Merge "Make stdout be line buffered."
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 912dffb..c388625 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -156,7 +156,8 @@
refs = __sync_add_and_fetch(&attrib->refs, 1);
- DBG("%p: ref=%d", attrib, refs);
+ // suppress very common debug message
+ if (refs == 1 || refs > 20) DBG("%p: ref=%d", attrib, refs);
return attrib;
}
@@ -230,7 +231,8 @@
refs = __sync_sub_and_fetch(&attrib->refs, 1);
- DBG("%p: ref=%d", attrib, refs);
+ // suppress very common debug message
+ if (refs == 0 || refs > 20) DBG("%p: ref=%d", attrib, refs);
if (refs > 0)
return;
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 1bbaa48..58be9fd 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -37,6 +37,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/param.h>
#include <fcntl.h>
#include <bluetooth/bluetooth.h>
@@ -61,8 +62,9 @@
#define BATT_UUID "0000180f-0000-1000-8000-00805f9b34fb"
#define BATT_LEVEL_UUID "00002a19-0000-1000-8000-00805f9b34fb"
-//#define CHECK_TIME (24*60*60)
-#define CHECK_TIME 10
+#define BATTERY_DIR "/tmp/batteries"
+
+#define CHECK_TIME (8*60*60)
struct service {
guint attio_id;
@@ -79,7 +81,9 @@
struct service level;
- time_t lastCheck;
+ int batt_level;
+
+ time_t next_check;
};
static GSList *devices = NULL;
@@ -89,7 +93,6 @@
{
struct batt_device *battdev;
- DBG("BATT trace");
battdev = g_try_new0(struct batt_device, 1);
if (!battdev)
return NULL;
@@ -102,7 +105,6 @@
static void batt_free_device(struct batt_device *battdev)
{
- DBG("BATT trace");
btd_device_unref(battdev->device);
g_attrib_unref(battdev->attrib);
g_free(battdev->batt_primary);
@@ -119,23 +121,67 @@
return ts.tv_sec;
}
+/* should really be done in the dbus agent, I think */
+static void batt_update_level_file(struct batt_device* battdev)
+{
+ char path[MAXPATHLEN], pathnew[MAXPATHLEN];
+
+ const char* addr = NULL;
+ const char* id = device_get_path(battdev->device);
+
+ /* id == /org/bluez/hci0/dev_D0_5F_B8_29_10_8D */
+
+ if (id != NULL) {
+ addr = rindex(id, '/');
+ if (addr == NULL) {
+ addr = id;
+ } else {
+ addr++;
+ }
+ }
+ if (addr == NULL || *addr == '\0') {
+ addr = "UNKNOWN";
+ }
+
+ snprintf(path, sizeof path, "%s/%s", BATTERY_DIR, addr);
+ snprintf(pathnew, sizeof pathnew, "%s.new", path);
+
+ if (mkdir(BATTERY_DIR, 0777) < 0 && errno != EEXIST) {
+ error("BATT mkdir: %s: %s", BATTERY_DIR, strerror(errno));
+ return;
+ }
+
+ FILE* fp = fopen(pathnew, "w");
+ if (fp == NULL) {
+ error("BATT fopen: %s: %s", pathnew, strerror(errno));
+ return;
+ }
+ fprintf(fp, "%d\n", battdev->batt_level);
+ fclose(fp);
+
+ if (rename(pathnew, path) != 0) {
+ error("BATT rename: %s -> %s: %s", pathnew, path, strerror(errno));
+ return;
+ }
+}
+
static void level_read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
struct batt_device *battdev = user_data;
- DBG("BATT trace");
if (status != 0) {
error("BATT %s failed: %s", __func__, att_ecode2str(status));
return;
}
- int level = pdu[1];
- DBG("BATT level=%d", level);
+ battdev->batt_level = pdu[1];
+ DBG("BATT level=%d", battdev->batt_level);
+ batt_update_level_file(battdev);
}
-static void checkLevel(struct batt_device *battdev)
+static void check_level(struct batt_device *battdev)
{
- battdev->lastCheck = wallclock();
+ battdev->next_check = wallclock() + CHECK_TIME;
if (battdev->level.value_handle == 0)
return;
@@ -143,13 +189,11 @@
level_read_char_cb, battdev);
}
-static int isTimeForLevelCheck(struct batt_device* battdev)
+static int is_time_for_level_check(struct batt_device* battdev)
{
- DBG("BATT trace");
time_t now = wallclock();
- time_t dt = now - battdev->lastCheck;
- if (dt < CHECK_TIME) {
+ if (now < battdev->next_check) {
DBG("BATT not time for level check yet");
return 0;
}
@@ -190,8 +234,8 @@
battdev->level.value_handle = chr->value_handle;
DBG("BATT found BATT_LEVEL_UUID value_handle=0x%04x", chr->value_handle);
//discover_desc(battdev, chr, next);
- if (isTimeForLevelCheck(battdev)) {
- checkLevel(battdev);
+ if (is_time_for_level_check(battdev)) {
+ check_level(battdev);
}
}
}
@@ -203,7 +247,6 @@
struct gatt_primary *prim = battdev->batt_primary;
GSList *l;
- DBG("BATT trace");
DBG("BATT connected");
battdev->attrib = g_attrib_ref(attrib);
@@ -216,8 +259,8 @@
prim->range.end, NULL,
char_discovered_cb, battdev);
} else {
- if (isTimeForLevelCheck(battdev)) {
- checkLevel(battdev);
+ if (is_time_for_level_check(battdev)) {
+ check_level(battdev);
}
}
}
@@ -227,7 +270,6 @@
struct batt_device *battdev = user_data;
GSList *l;
- DBG("BATT trace");
DBG("BATT disconnected");
if (battdev->level.attio_id > 0) {
@@ -246,7 +288,6 @@
GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
GIOChannel *io;
- DBG("BATT trace");
battdev = batt_new_device(device, prim->range.start);
if (!battdev)
return NULL;
@@ -263,7 +304,6 @@
static int batt_unregister_device(struct batt_device *battdev)
{
- DBG("BATT trace");
btd_device_remove_attio_callback(battdev->device, battdev->attioid);
batt_free_device(battdev);
@@ -276,7 +316,6 @@
const char *path = device_get_path(device);
GSList *primaries, *l;
- DBG("BATT trace");
DBG("BATT path %s", path);
primaries = btd_device_get_primaries(device);
@@ -307,7 +346,6 @@
struct batt_device *battdev = a;
struct btd_device *device = b;
- DBG("BATT trace");
if (battdev->device != device)
return;
@@ -320,7 +358,6 @@
struct btd_device *device = btd_service_get_device(service);
const char *path = device_get_path(device);
- DBG("BATT trace");
DBG("BATT path %s", path);
g_slist_foreach(devices, remove_device, device);
@@ -337,13 +374,11 @@
{
int err;
- DBG("BATT trace");
return btd_profile_register(&batt_profile);
}
static void batt_exit(void)
{
- DBG("BATT trace");
btd_profile_unregister(&batt_profile);
}
diff --git a/profiles/oad/oad.c b/profiles/oad/oad.c
index 2936b6a..fd37cd7 100644
--- a/profiles/oad/oad.c
+++ b/profiles/oad/oad.c
@@ -37,6 +37,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/param.h>
#include <fcntl.h>
#include <bluetooth/bluetooth.h>
@@ -62,25 +63,31 @@
#define OAD_IMG_IDENTITY_UUID "f000ffc1-0451-4000-b000-000000000000"
#define OAD_IMG_BLOCK_UUID "f000ffc2-0451-4000-b000-000000000000"
-#define MAJOR_VERSION(x) ((x) >> 8)
-#define MINOR_VERSION(x) ((x) & 0xff)
-#define SLOT_VERSION(x) (!(MINOR_VERSION(x) & 0x1))
+#define OAD_MAJOR_VERSION(x) ((x) >> 8)
+#define OAD_MINOR_VERSION(x) ((x) & 0xff)
-#define FIRMWARE_SLOT0 "/tmp/gfrm200.slot0.bin"
-#define FIRMWARE_SLOT1 "/tmp/gfrm200.slot1.bin"
+/* remote has 2 processors, 2 firmwares. */
+#define OAD_GP_FIRMWARE "gfrm200.gp.bin"
+#define OAD_TI_FIRMWARE "gfrm200.ti.bin"
-//#define CHECK_TIME (24*60*60)
-#define CHECK_TIME 10
+/* remote checks in every 15 minutes */
+#define OAD_UPGRADE_DELAY_SECS 10 // delay between wake and upgrade check
+#define OAD_DELAY_NO_UPGRADE (8*60*60) // check for new ACS files (not likely)
+#define OAD_DELAY_UPGRADING 60 // recheck in 1 minute (in case of 2 firmware update)
+
+#define OAD_HEADER_LEN 8 // on-disk header. See OAD_Formatv7.xlsx
enum OAD_Status {
OADSTATUS_None = 0,
OADSTATUS_DataTransfer = 1,
OADSTATUS_TransferSuccess = 2,
+ OADSTATUS_Unknown81 = 0x81, // maybe flashing
+ OADSTATUS_Unknown82 = 0x82, // maybe rebooting
OADSTATUS_CRCError = 0xE0,
OADSTATUS_LVD = 0xE1, // low voltage detection
OADSTATUS_KeyPress = 0xE2, // user interrupted (not idle)
OADSTATUS_GP = 0xE3, // bootloader said no
- OADSTATUS_Host = 0xE4, // host did something wrong (eg, timeout)
+ OADSTATUS_Host = 0xE4, // host error (eg, timeout)
};
enum OAD_Action {
@@ -89,15 +96,21 @@
OADACTION_StartUpdate = 2,
};
-struct fwinfo {
- uint16_t gp_version;
- uint16_t ti_version;
- uint32_t gp_size;
- uint32_t ti_size;
- int slot;
+enum OAD_FirmwareType {
+ OADFW_Unknown = -1,
+ OADFW_GP = 0,
+ OADFW_TI = 1,
+ OADFW_Total = 2,
};
-struct service {
+struct oad_fwinfo {
+ uint16_t version;
+ uint32_t size;
+ uint8_t header[OAD_HEADER_LEN];
+ char file[MAXPATHLEN];
+};
+
+struct oad_service {
guint attio_id;
uint16_t ccc_handle;
uint16_t value_handle;
@@ -111,35 +124,34 @@
guint attioid;
struct gatt_primary *oad_primary;
- struct service identity;
- struct service block;
+ struct oad_service identity;
+ struct oad_service block;
- time_t lastCheck;
- struct fwinfo remote;
+ time_t nextCheck;
+ struct oad_fwinfo disk[OADFW_Total];
+ struct oad_fwinfo remote[OADFW_Total];
- uint8_t header[8];
+ int force_upgrade;
+ int retries;
+ int num_blocks;
+ int progress;
+
+ uint8_t header[OAD_HEADER_LEN];
int fd;
- guint tid; /* Timer id to delay starting upgrade */
+ guint tid; // Timer id to delay starting upgrade
};
-struct characteristic {
+struct oad_characteristic {
struct oad_device *oaddev;
char uuid[MAX_LEN_UUID_STR + 1];
};
-static GSList *devices = NULL;
+static GSList *oad_devices = NULL;
-//#define FAKEFW 1
-#ifdef FAKEFW
-static unsigned char HEADER[] = { 0xaa, 0x55, 0x01, 0x02, 0x0d, 0x04, 0x00, 0x54, 0, 0, 0, 0, 0, 0, 0, 0, };
-#endif
-
-static struct oad_device *oad_new_device(struct btd_device *device,
- uint16_t id)
+static struct oad_device *oad_new_device(struct btd_device *device, uint16_t id)
{
struct oad_device *oaddev;
- DBG("OAD trace");
oaddev = g_try_new0(struct oad_device, 1);
if (!oaddev)
return NULL;
@@ -153,7 +165,6 @@
static void oad_free_device(struct oad_device *oaddev)
{
- DBG("OAD trace");
if (oaddev->fd != -1) {
close(oaddev->fd);
oaddev->fd = -1;
@@ -164,36 +175,43 @@
g_free(oaddev);
}
-static void block_char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
+static void oad_block_char_write_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data)
{
struct oad_device *oaddev = user_data;
- DBG("OAD trace");
+ if (status != 0) {
+ error("OAD %s failed: %s", __func__, att_ecode2str(status));
+ return;
+ }
+ oaddev->retries = 0; // successful write
+}
+
+static void oad_identity_char_write_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data)
+{
+ struct oad_device *oaddev = user_data;
+
if (status != 0) {
error("OAD %s failed: %s", __func__, att_ecode2str(status));
return;
}
}
-static void identity_char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
+static void oad_start_transfer(struct oad_device* oaddev)
{
- struct oad_device *oaddev = user_data;
+ uint8_t attr_val[OAD_HEADER_LEN+1]; // + 1 byte action
- DBG("OAD trace");
- if (status != 0) {
- error("OAD %s failed: %s", __func__, att_ecode2str(status));
- return;
- }
+ memcpy(attr_val, oaddev->header, sizeof oaddev->header);
+ attr_val[OAD_HEADER_LEN] = OADACTION_TransferOADData; /* start OAD image transfer */
+
+ gatt_write_char(oaddev->attrib, oaddev->identity.value_handle, attr_val,
+ sizeof(attr_val), oad_identity_char_write_cb, oaddev);
}
-static void block_notify_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
+static void oad_block_notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
{
struct oad_device *oaddev = user_data;
+ int retry = 0;
- DBG("OAD trace");
/* should be at least opcode (1b) + handle (7a00) */
if (len < 3) {
@@ -215,32 +233,27 @@
case OADSTATUS_DataTransfer: // send a block
{
uint8_t block[18]; // 2 bytes block_num + 16 data bytes
+
memset(block, 0, sizeof block);
put_le16(block_num, block);
if (lseek(oaddev->fd, 16 * block_num, SEEK_SET) < 0) {
- perror("OAD seek");
- close(oaddev->fd);
- oaddev->fd = -1;
- return;
+ perror("OAD firmware seek");
+ goto error;
}
- if (read(oaddev->fd, block+2, sizeof block - 2) != sizeof block - 2) {
- perror("OAD short read");
- close(oaddev->fd);
- oaddev->fd = -1;
- return;
+ if (read(oaddev->fd, block+2, sizeof block - 2) < 0) {
+ perror("OAD firmware read");
+ goto error;
}
-#ifdef FAKEFW
-if (block_num == 0) {
- memcpy(block+2, HEADER, sizeof block - 2);
-} else {
- memset(block+2, 0, sizeof block - 2);
-}
-#endif
- DBG("OAD sending block %d", block_num);
+ int progress = 0;
+ if (oaddev->num_blocks > 0) progress = 100 * block_num / oaddev->num_blocks;
+ if (oaddev->progress != progress) {
+ oaddev->progress = progress;
+ DBG("OAD sending block %d (%d%%)", block_num, progress);
+ }
gatt_write_char(oaddev->attrib, oaddev->block.value_handle, block,
- sizeof(block), block_char_write_cb, oaddev);
+ sizeof(block), oad_block_char_write_cb, oaddev);
}
break;
case OADSTATUS_TransferSuccess: // OK, successful transfer
@@ -254,27 +267,48 @@
attr_val[8] = OADACTION_StartUpdate; /* commit fw image */
DBG("OAD committing firmware");
gatt_write_char(oaddev->attrib, oaddev->identity.value_handle, attr_val,
- sizeof(attr_val), identity_char_write_cb, oaddev);
+ sizeof(attr_val), oad_identity_char_write_cb, oaddev);
}
break;
- case OADSTATUS_CRCError: // checksum error
- DBG("OAD firmware transfer checksum error");
- close(oaddev->fd);
- oaddev->fd = -1;
+ case OADSTATUS_Unknown81:
+ case OADSTATUS_Unknown82:
+ DBG("OAD block notify status 0x%02x (might be a good thing)", status);
break;
- case OADSTATUS_LVD: // ???
- case OADSTATUS_KeyPress: // ???
- case OADSTATUS_GP: // ???
- case OADSTATUS_Host: // ???
+
+ case OADSTATUS_CRCError:
+ DBG("OAD firmware transfer checksum error");
+ goto error;
+ case OADSTATUS_LVD:
+ DBG("OAD firmware upgrade low battery error");
+ goto error;
+ case OADSTATUS_KeyPress:
+ DBG("OAD firmware upgrade user interrupted");
+ goto error;
+ case OADSTATUS_GP:
+ DBG("OAD firmware upgrade GP error");
+ goto error;
+ case OADSTATUS_Host:
+ DBG("OAD firmware upgrade host error (eg, timeout)");
+ if (oaddev->retries++ < 3) {
+ DBG("OAD retrying upgrade...", status);
+ oad_start_transfer(oaddev);
+ return;
+ }
+ goto error;
default:
DBG("OAD unexpected block notify status 0x%02x", status);
- close(oaddev->fd);
- oaddev->fd = -1;
- break;
+ goto error;
}
+ /* success so far*/
+ return;
+
+error:
+ DBG("OAD upgrade stopping due to error", status);
+ close(oaddev->fd);
+ oaddev->fd = -1;
}
-static time_t wallclock(void)
+static time_t oad_wallclock(void)
{
struct timespec ts;
@@ -284,14 +318,11 @@
return ts.tv_sec;
}
-static int isTimeForUpgradeCheck(struct oad_device* oaddev)
+static int oad_is_time_for_upgrade_check(struct oad_device* oaddev)
{
- DBG("OAD trace");
+ time_t now = oad_wallclock();
- time_t now = wallclock();
-
- time_t dt = now - oaddev->lastCheck;
- if (dt < CHECK_TIME) {
+ if (now < oaddev->nextCheck) {
DBG("OAD not time for upgrade check yet");
return 0;
}
@@ -299,96 +330,162 @@
return 1;
}
-static void checkForUpgrade(struct oad_device *oaddev)
+static void oad_get_firmware_info(struct oad_device *oaddev, const char* file)
{
- DBG("OAD trace");
+ uint8_t header[OAD_HEADER_LEN];
- if (!isTimeForUpgradeCheck(oaddev)) {
- return;
- }
-
- oaddev->lastCheck = wallclock();
-
- int slot2 = !oaddev->remote.slot; // alternate between slot 0 and 1
-
- char* fw_file = slot2 ? FIRMWARE_SLOT1 : FIRMWARE_SLOT0;
- int fd = open(fw_file, O_RDONLY);
+ int fd = open(file, O_RDONLY);
if (fd < 0) {
- error("OAD open: %s: %s", fw_file, strerror(errno));
+ error("OAD open: %s: %s", file, strerror(errno));
return;
}
- int len = read(fd, oaddev->header, sizeof (oaddev->header));
-#ifdef FAKEFW
-memcpy(oaddev->header, HEADER, sizeof oaddev->header);
-#endif
+ int len = read(fd, header, sizeof header);
if (len < 0) {
- error("OAD read: %s: %s", fw_file, strerror(errno));
+ error("OAD read: %s: %s", file, strerror(errno));
close(fd);
return;
}
-
- if (len < sizeof oaddev->header) {
- error("OAD short read: %s: wanted %d, got %d", fw_file, sizeof oaddev->header, len);
+ if (len < sizeof header) {
+ error("OAD short read: %s: wanted %d, got %d", file, sizeof header, len);
close(fd);
return;
}
+ close(fd);
+ fd = -1;
- /* 8 bytes of header appear to be:
+ /* See OAD_Formatv7.xlsx, 'Image Info' tab. 8 bytes are:
* 2 bytes checksum
* 2 bytes version
* 3 bytes size
- * 1 byte type: "T" TI image, "G" GP image
+ * 1 byte id: "T" TI image, "G" GP image
*/
- uint16_t version = get_le16(oaddev->header + 2);
- int type = oaddev->header[7];
- if (type == 'T') {
- int slot = SLOT_VERSION(version);
- DBG("OAD firmware on disk: file=%s ti_version=%d.%d slot=%d",
- fw_file, MAJOR_VERSION(version), MINOR_VERSION(version), slot);
+ uint16_t version = get_le16(header + 2);
+ uint32_t size = get_le32(header+4) & 0x00ffffff;
+ int id = header[7];
+ enum OAD_FirmwareType type = id == 'T' ? OADFW_TI : id == 'G' ? OADFW_GP : OADFW_Unknown;
+ int index = (int)type;
- if (version <= oaddev->remote.ti_version) {
- DBG("OAD TI firmware is up to date");
- close(fd);
- return;
- }
- } else if (type == 'G') {
- DBG("OAD firmware on disk: file=%s gp_version=%d.%d",
- fw_file, MAJOR_VERSION(version), MINOR_VERSION(version));
- if (version <= oaddev->remote.gp_version) {
- DBG("OAD GP firmware is up to date");
- close(fd);
- return;
- }
- } else {
- error("OAD firmware on disk: unknown type '%c': file=%s version=%d.%d",
- type, fw_file, MAJOR_VERSION(version), MINOR_VERSION(version));
- close(fd);
+ if (type == OADFW_Unknown) {
+ error("OAD unknown firmware id '%c': file=%s version=%d.%d",
+ id, file, OAD_MAJOR_VERSION(version), OAD_MINOR_VERSION(version));
+ return;
+ }
+ if (version <= oaddev->disk[index].version) {
+ error("OAD ignoring older firmware: file=%s type=%s version=%d.%d",
+ file, type, OAD_MAJOR_VERSION(version), OAD_MINOR_VERSION(version));
+ return;
+ }
+ if (strlen(file) >= sizeof oaddev->disk[index].file) {
+ error("OAD firmware file name too long: %s", file);
return;
}
- DBG("OAD starting upgrade");
+ oaddev->disk[index].version = version;
+ oaddev->disk[index].size = size;
+ memcpy(oaddev->disk[index].header, header, sizeof oaddev->disk[index].header);
+ strncpy(oaddev->disk[index].file, file, sizeof oaddev->disk[index].file);
+ oaddev->disk[index].file[sizeof oaddev->disk[index].file - 1] = '\0';
+}
+
+static const char* oad_firmware_type_string(enum OAD_FirmwareType type)
+{
+ switch (type) {
+ case OADFW_GP: return "GP";
+ case OADFW_TI: return "TI";
+ }
+ return "Unknown";
+}
+
+static void oad_find_firmware(struct oad_device *oaddev)
+{
+ memset(oaddev->disk, 0, sizeof oaddev->disk);
+
+ /* check in tmp first, to make debugging easier */
+ if (access("/tmp/" OAD_GP_FIRMWARE, R_OK) == 0 ||
+ access("/tmp/" OAD_TI_FIRMWARE, R_OK) == 0) {
+ DBG("OAD found firmware in /tmp, ignoring /etc/remote, enabling downgrade.");
+ oaddev->force_upgrade = 1; // allow downgrade
+ oad_get_firmware_info(oaddev, "/tmp/" OAD_GP_FIRMWARE);
+ oad_get_firmware_info(oaddev, "/tmp/" OAD_TI_FIRMWARE);
+ } else {
+ oaddev->force_upgrade = 0;
+ oad_get_firmware_info(oaddev, "/etc/remote/" OAD_GP_FIRMWARE);
+ oad_get_firmware_info(oaddev, "/etc/remote/" OAD_TI_FIRMWARE);
+ }
+
+ int i;
+ for (i = 0; i < OADFW_Total; i++) {
+ enum OAD_FirmwareType type = (enum OAD_FirmwareType) i;
+ const char* typeStr = oad_firmware_type_string(type);
+ if (strlen(oaddev->disk[type].file) == 0) {
+ error("OAD no %s firmware found on disk", typeStr);
+ } else {
+ DBG("OAD found %s firmware: file=%s version=%d.%d",
+ typeStr, oaddev->disk[i].file,
+ OAD_MAJOR_VERSION(oaddev->disk[i].version),
+ OAD_MINOR_VERSION(oaddev->disk[i].version));
+ }
+ }
+}
+
+static void oad_check_for_upgrade(struct oad_device *oaddev)
+{
+ if (!oad_is_time_for_upgrade_check(oaddev)) {
+ return;
+ }
+
+ oad_find_firmware(oaddev);
+
+ struct oad_fwinfo* fp = NULL;
+ int i;
+ for (i = 0; i < OADFW_Total; i++) {
+ enum OAD_FirmwareType type = (enum OAD_FirmwareType) i;
+ const char* typeStr = oad_firmware_type_string(type);
+ if (strlen(oaddev->disk[i].file) == 0) {
+ continue;
+ }
+ if (oaddev->disk[i].version == oaddev->remote[i].version ||
+ (!oaddev->force_upgrade &&
+ oaddev->disk[i].version < oaddev->remote[i].version)) {
+ DBG("OAD %s firmware is up to date", typeStr);
+ } else {
+ fp = &oaddev->disk[i];
+ break;
+ }
+ }
+
+ if (fp == NULL) {
+ oaddev->nextCheck = oad_wallclock() + OAD_DELAY_NO_UPGRADE;
+ return;
+ }
+ oaddev->nextCheck = oad_wallclock() + OAD_DELAY_UPGRADING;
+
+ DBG("OAD starting upgrade with %s", fp->file);
+
if (oaddev->fd != -1) {
close(oaddev->fd);
}
- oaddev->fd = fd; /* fd is left open */
+ oaddev->fd = open(fp->file, O_RDONLY);
+ if (oaddev->fd < 0) {
+ error("OAD open: %s: %s", fp->file, strerror(errno));
+ return;
+ }
if (oaddev->tid) {
g_source_remove(oaddev->tid);
+ oaddev->tid = 0;
}
- uint8_t attr_val[9];
- memcpy(attr_val, oaddev->header, sizeof oaddev->header);
- attr_val[8] = OADACTION_TransferOADData; /* start OAD image transfer */
-
- gatt_write_char(oaddev->attrib, oaddev->identity.value_handle, attr_val,
- sizeof(attr_val), identity_char_write_cb, oaddev);
+ oaddev->progress = 0;
+ oaddev->retries = 0;
+ oaddev->num_blocks = (fp->size + 15) / 16;
+ memcpy(oaddev->header, fp->header, sizeof oaddev->header);
+ oad_start_transfer(oaddev);
}
-static void identity_notify_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
+static void oad_identity_notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
{
struct oad_device *oaddev = user_data;
- DBG("OAD trace");
/* should be at least opcode (1b) + handle (76 00) */
if (len < 3) {
@@ -396,7 +493,7 @@
return;
}
- /* Next 10 bytes appear to be:
+ /* See OAD_Formatv7.xlsx, 'Image Info' tab. 10 bytes are:
* char gp_version[2]; // 09 00 is 0.9
* char gp_size[3]; // 00 00 01 is 0x010000 == 64k
* char ti_version[2]; // 2a 00 is 0.42
@@ -408,31 +505,30 @@
return;
}
- oaddev->remote.gp_version = get_le16(pdu+3);
- oaddev->remote.gp_size = get_le32(pdu+5) & 0x00ffffff;
- oaddev->remote.slot = SLOT_VERSION(oaddev->remote.gp_version);
- oaddev->remote.ti_version = get_le16(pdu+8);
+ oaddev->remote[OADFW_GP].version = get_le16(pdu+3);
+ oaddev->remote[OADFW_GP].size = get_le32(pdu+5) & 0x00ffffff;
+
+ oaddev->remote[OADFW_TI].version = get_le16(pdu+8);
unsigned char size[4];
memcpy(size, pdu+10, 3);
size[3] = 0; // need 4 bytes for get_le32
- oaddev->remote.ti_size = get_le32(size);
+ oaddev->remote[OADFW_TI].size = get_le32(size) & 0x00ffffff;
- DBG("OAD firmware on remote: gp_version=%d.%d gp_size=%d ti_version=%d.%d ti_size=%d slot=%d",
- MAJOR_VERSION(oaddev->remote.gp_version), MINOR_VERSION(oaddev->remote.gp_version),
- oaddev->remote.gp_size,
- MAJOR_VERSION(oaddev->remote.ti_version), MINOR_VERSION(oaddev->remote.ti_version),
- oaddev->remote.ti_size,
- oaddev->remote.slot);
-
- checkForUpgrade(oaddev);
+ int i;
+ for (i = 0; i < OADFW_Total; i++) {
+ enum OAD_FirmwareType type = (enum OAD_FirmwareType) i;
+ const char* typeStr = oad_firmware_type_string(type);
+ DBG("OAD %s firmware on remote: version=%d.%d",
+ typeStr, OAD_MAJOR_VERSION(oaddev->remote[i].version),
+ OAD_MINOR_VERSION(oaddev->remote[i].version));
+ }
+ oad_check_for_upgrade(oaddev);
}
-static void check_fwversion(struct oad_device *oaddev)
+static void oad_check_fwversion(struct oad_device *oaddev)
{
uint8_t attr_val[9]; // 8 byte header + 1 command byte (all zeros)
- DBG("OAD trace");
-
if (!oaddev->identity.notify_enabled || !oaddev->block.notify_enabled) {
return;
}
@@ -441,42 +537,37 @@
memset(attr_val, 0, sizeof attr_val);
attr_val[8] = OADACTION_GetRemoteInfo; /* trigger fw version notify */
gatt_write_char(oaddev->attrib, oaddev->identity.value_handle, attr_val,
- sizeof(attr_val), identity_char_write_cb, oaddev);
+ sizeof(attr_val), oad_identity_char_write_cb, oaddev);
}
-static void ccc_char_write_identity_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
+static void oad_ccc_char_write_identity_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data)
{
struct oad_device *oaddev = user_data;
- DBG("OAD trace");
if (status != 0) {
error("OAD %s failed: %s", __func__, att_ecode2str(status));
return;
}
oaddev->identity.notify_enabled = 1;
- check_fwversion(oaddev);
+ oad_check_fwversion(oaddev);
}
-static void ccc_char_write_block_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
+static void oad_ccc_char_write_block_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data)
{
struct oad_device *oaddev = user_data;
- DBG("OAD trace");
if (status != 0) {
error("OAD %s failed: %s", __func__, att_ecode2str(status));
return;
}
oaddev->block.notify_enabled = 1;
- check_fwversion(oaddev);
+ oad_check_fwversion(oaddev);
}
-static void enableNotify(struct oad_device* oaddev)
+static void oad_enable_notify(struct oad_device* oaddev)
{
- DBG("OAD trace");
if (oaddev->identity.ccc_handle == 0 || oaddev->block.ccc_handle == 0 ||
oaddev->identity.value_handle == 0 || oaddev->block.value_handle == 0) {
error("OAD discovery not complete, cannot enable notifiers");
@@ -486,23 +577,23 @@
oaddev->identity.attio_id =
g_attrib_register(oaddev->attrib,
ATT_OP_HANDLE_NOTIFY, oaddev->identity.value_handle,
- identity_notify_handler, oaddev, NULL);
+ oad_identity_notify_handler, oaddev, NULL);
oaddev->block.attio_id =
g_attrib_register(oaddev->attrib,
ATT_OP_HANDLE_NOTIFY, oaddev->block.value_handle,
- block_notify_handler, oaddev, NULL);
+ oad_block_notify_handler, oaddev, NULL);
uint8_t attr_val[2];
put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
DBG("OAD enabling notify for OAD_IMG_IDENTITY_UUID");
gatt_write_char(oaddev->attrib, oaddev->identity.ccc_handle, attr_val,
- sizeof(attr_val), ccc_char_write_identity_cb, oaddev);
+ sizeof(attr_val), oad_ccc_char_write_identity_cb, oaddev);
DBG("OAD enabling notify for OAD_IMG_BLOCK_UUID");
gatt_write_char(oaddev->attrib, oaddev->block.ccc_handle, attr_val,
- sizeof(attr_val), ccc_char_write_block_cb, oaddev);
+ sizeof(attr_val), oad_ccc_char_write_block_cb, oaddev);
}
-static gboolean upgrade_timer(gpointer data)
+static gboolean oad_upgrade_timer(gpointer data)
{
struct oad_device *oaddev = data;
@@ -510,16 +601,15 @@
oaddev->tid = 0;
DBG("OAD timer fired");
- enableNotify(oaddev);
+ oad_enable_notify(oaddev);
}
-static void desc_discovered_cb(uint8_t status, GSList *descs, void* user_data)
+static void oad_desc_discovered_cb(uint8_t status, GSList *descs, void* user_data)
{
- struct characteristic *ch = user_data;
+ struct oad_characteristic *ch = user_data;
struct oad_device *oaddev = ch->oaddev;
struct GSList *list = NULL;
- DBG("OAD trace");
if (status != 0) {
error("OAD %s failed: %s", __func__, att_ecode2str(status));
goto done;
@@ -541,20 +631,18 @@
}
}
if (oaddev->identity.ccc_handle && oaddev->block.ccc_handle)
- oaddev->tid = g_timeout_add_seconds(10, upgrade_timer, oaddev);
+ oaddev->tid = g_timeout_add_seconds(OAD_UPGRADE_DELAY_SECS, oad_upgrade_timer, oaddev);
done:
g_free(ch);
}
-static void discover_desc(struct oad_device *oaddev, struct gatt_char *c,
- struct gatt_char *c_next)
+static void oad_discover_desc(struct oad_device *oaddev, struct gatt_char *c, struct gatt_char *c_next)
{
struct gatt_primary *prim = oaddev->oad_primary;
- struct characteristic *ch;
+ struct oad_characteristic *ch;
uint16_t start, end;
- DBG("OAD trace");
start = c->value_handle + 1;
if (c_next != NULL) {
@@ -567,16 +655,16 @@
return;
}
- ch = g_new0(struct characteristic, 1);
+ ch = g_new0(struct oad_characteristic, 1);
ch->oaddev = oaddev;
memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
DBG("OAD discovering descriptors start=0x%04x end=0x%04x", start, end);
- gatt_discover_desc(oaddev->attrib, start, end, NULL, desc_discovered_cb, ch);
+ gatt_discover_desc(oaddev->attrib, start, end, NULL, oad_desc_discovered_cb, ch);
}
-static void char_discovered_cb(uint8_t status, GSList *chars, void* user_data)
+static void oad_char_discovered_cb(uint8_t status, GSList *chars, void* user_data)
{
struct oad_device *oaddev = user_data;
struct gatt_primary *prim = oaddev->oad_primary;
@@ -598,8 +686,7 @@
chr = l->data;
next = l->next ? l->next->data : NULL;
- DBG("OAD 0x%04x UUID: %s properties: %02x",
- chr->handle, chr->uuid, chr->properties);
+ DBG("OAD 0x%04x UUID: %s properties: %02x", chr->handle, chr->uuid, chr->properties);
start = chr->value_handle + 1;
@@ -608,23 +695,22 @@
if (g_strcmp0(chr->uuid, OAD_IMG_IDENTITY_UUID) == 0) {
oaddev->identity.value_handle = chr->value_handle;
DBG("OAD found OAD_IMG_IDENTITY_UUID value_handle=0x%04x", chr->value_handle);
- discover_desc(oaddev, chr, next);
+ oad_discover_desc(oaddev, chr, next);
} else if (g_strcmp0(chr->uuid, OAD_IMG_BLOCK_UUID) == 0) {
oaddev->block.value_handle = chr->value_handle;
DBG("OAD found OAD_IMG_BLOCK_UUID value_handle=0x%04x", chr->value_handle);
- discover_desc(oaddev, chr, next);
+ oad_discover_desc(oaddev, chr, next);
}
}
}
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+static void oad_attio_connected_cb(GAttrib *attrib, gpointer user_data)
{
struct oad_device *oaddev = user_data;
struct gatt_primary *prim = oaddev->oad_primary;
GSList *l;
- DBG("OAD trace");
DBG("OAD connected");
oaddev->attrib = g_attrib_ref(attrib);
@@ -635,22 +721,20 @@
oaddev->identity.value_handle == 0 || oaddev->block.value_handle == 0) {
DBG("OAD discovering characteristics");
- gatt_discover_char(oaddev->attrib, prim->range.start,
- prim->range.end, NULL,
- char_discovered_cb, oaddev);
+ gatt_discover_char(oaddev->attrib, prim->range.start, prim->range.end, NULL,
+ oad_char_discovered_cb, oaddev);
} else {
- if (isTimeForUpgradeCheck(oaddev)) {
- enableNotify(oaddev);
+ if (oad_is_time_for_upgrade_check(oaddev)) {
+ oad_enable_notify(oaddev);
}
}
}
-static void attio_disconnected_cb(gpointer user_data)
+static void oad_attio_disconnected_cb(gpointer user_data)
{
struct oad_device *oaddev = user_data;
GSList *l;
- DBG("OAD trace");
DBG("OAD disconnected");
if (oaddev->identity.attio_id > 0) {
@@ -666,14 +750,12 @@
oaddev->attrib = NULL;
}
-static struct oad_device *oad_register_device(struct btd_device *device,
- struct gatt_primary *prim)
+static struct oad_device *oad_register_device(struct btd_device *device, struct gatt_primary *prim)
{
struct oad_device *oaddev;
GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
GIOChannel *io;
- DBG("OAD trace");
oaddev = oad_new_device(device, prim->range.start);
if (!oaddev)
return NULL;
@@ -681,16 +763,13 @@
oaddev->oad_primary = g_memdup(prim, sizeof(*prim));
oaddev->attioid = btd_device_add_attio_callback(device,
- attio_connected_cb,
- attio_disconnected_cb,
- oaddev);
+ oad_attio_connected_cb, oad_attio_disconnected_cb, oaddev);
return oaddev;
}
static int oad_unregister_device(struct oad_device *oaddev)
{
- DBG("OAD trace");
btd_device_remove_attio_callback(oaddev->device, oaddev->attioid);
oad_free_device(oaddev);
@@ -703,7 +782,6 @@
const char *path = device_get_path(device);
GSList *primaries, *l;
- DBG("OAD trace");
DBG("OAD path %s", path);
primaries = btd_device_get_primaries(device);
@@ -723,22 +801,21 @@
if (oaddev == NULL)
continue;
- devices = g_slist_append(devices, oaddev);
+ oad_devices = g_slist_append(oad_devices, oaddev);
}
return 0;
}
-static void remove_device(gpointer a, gpointer b)
+static void oad_remove_device(gpointer a, gpointer b)
{
struct oad_device *oaddev = a;
struct btd_device *device = b;
- DBG("OAD trace");
if (oaddev->device != device)
return;
- devices = g_slist_remove(devices, oaddev);
+ oad_devices = g_slist_remove(oad_devices, oaddev);
oad_unregister_device(oaddev);
}
@@ -747,10 +824,9 @@
struct btd_device *device = btd_service_get_device(service);
const char *path = device_get_path(device);
- DBG("OAD trace");
DBG("OAD path %s", path);
- g_slist_foreach(devices, remove_device, device);
+ g_slist_foreach(oad_devices, oad_remove_device, device);
}
static struct btd_profile oad_profile = {
@@ -764,15 +840,12 @@
{
int err;
- DBG("OAD trace");
return btd_profile_register(&oad_profile);
}
static void oad_exit(void)
{
- DBG("OAD trace");
btd_profile_unregister(&oad_profile);
}
-BLUETOOTH_PLUGIN_DEFINE(oad, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- oad_init, oad_exit)
+BLUETOOTH_PLUGIN_DEFINE(oad, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, oad_init, oad_exit)
diff --git a/src/attrib-server.c b/src/attrib-server.c
index e65fff2..810cec5 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -993,7 +993,8 @@
size_t vlen;
uint8_t *value = g_attrib_get_buffer(channel->attrib, &vlen);
- DBG("op 0x%02x", ipdu[0]);
+ // suppress very common debug message
+ // DBG("op 0x%02x", ipdu[0]);
if (len > vlen) {
error("Too much data on ATT socket");