Command Code: 0x0009
Command Parameters: Controller_Index (2 Octets)
UUID (16 Octets)
+ SVC_Hint (1 octet)
Return Paramters: Controller_Index (2 Octets)
Return Paramters: Controller_Index (2 Octets)
+Set Device Class
+================
+
+ Command Code: 0x000B
+ Command Parameters: Controller_Index (2 Octets)
+ Major_Class (1 octet)
+ Minor_Class (1 octed)
+ Return Paramters: Controller_Index (2 Octets)
+
+
+Set Service Cache Command
+=========================
+
+ Command Code: 0x000C
+ Command Parameters: Controller_Index (2 Octets)
+ Enable (1 octet)
+ Return Paramters: Controller_Index (2 Octets)
+
+
Read Tracing Buffer Size Command
================================
#define MGMT_OP_SET_PAIRABLE 0x0008
-struct mgmt_cp_uuid {
+#define MGMT_OP_ADD_UUID 0x0009
+struct mgmt_cp_add_uuid {
uint16_t index;
uint8_t uuid[16];
+ uint8_t svc_hint;
} __packed;
-#define MGMT_OP_ADD_UUID 0x0009
-
#define MGMT_OP_REMOVE_UUID 0x000A
+struct mgmt_cp_remove_uuid {
+ uint16_t index;
+ uint8_t uuid[16];
+} __packed;
+
+#define MGMT_OP_SET_DEV_CLASS 0x000B
+struct mgmt_cp_set_dev_class {
+ uint16_t index;
+ uint8_t major;
+ uint8_t minor;
+} __packed;
+
+#define MGMT_OP_SET_SERVICE_CACHE 0x000C
+struct mgmt_cp_set_service_cache {
+ uint16_t index;
+ uint8_t enable;
+} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
PENDING_NAME,
};
+struct uuid_info {
+ uuid_t uuid;
+ uint8_t svc_hint;
+};
+
static int max_dev = -1;
static struct dev_info {
int sk;
uuid128 = ptr + 2;
for (; list; list = list->next) {
- uuid_t *uuid = list->data;
- uint8_t *uuid128_data = uuid->value.uuid128.data;
+ struct uuid_info *uuid = list->data;
+ uint8_t *uuid128_data = uuid->uuid.value.uuid128.data;
- if (uuid->type != SDP_UUID128)
+ if (uuid->uuid.type != SDP_UUID128)
continue;
/* Stop if not enough space to put next UUID128 */
/* Group all UUID16 types */
for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
- uuid_t *uuid = l->data;
+ struct uuid_info *uuid = l->data;
- if (uuid->type != SDP_UUID16)
+ if (uuid->uuid.type != SDP_UUID16)
continue;
- if (uuid->value.uuid16 < 0x1100)
+ if (uuid->uuid.value.uuid16 < 0x1100)
continue;
- if (uuid->value.uuid16 == PNP_INFO_SVCLASS_ID)
+ if (uuid->uuid.value.uuid16 == PNP_INFO_SVCLASS_ID)
continue;
/* Stop if not enough space to put next UUID16 */
/* Check for duplicates */
for (i = 0; i < uuid_count; i++)
- if (uuid16[i] == uuid->value.uuid16)
+ if (uuid16[i] == uuid->uuid.value.uuid16)
break;
if (i < uuid_count)
continue;
- uuid16[uuid_count++] = uuid->value.uuid16;
+ uuid16[uuid_count++] = uuid->uuid.value.uuid16;
eir_len += sizeof(uint16_t);
}
return 0;
}
-static uint8_t get_uuid_mask(uint16_t uuid16)
-{
- switch (uuid16) {
- case DIALUP_NET_SVCLASS_ID:
- case CIP_SVCLASS_ID:
- return 0x42; /* Telephony & Networking */
- case IRMC_SYNC_SVCLASS_ID:
- case OBEX_OBJPUSH_SVCLASS_ID:
- case OBEX_FILETRANS_SVCLASS_ID:
- case IRMC_SYNC_CMD_SVCLASS_ID:
- case PBAP_PSE_SVCLASS_ID:
- return 0x10; /* Object Transfer */
- case HEADSET_SVCLASS_ID:
- case HANDSFREE_SVCLASS_ID:
- return 0x20; /* Audio */
- case CORDLESS_TELEPHONY_SVCLASS_ID:
- case INTERCOM_SVCLASS_ID:
- case FAX_SVCLASS_ID:
- case SAP_SVCLASS_ID:
- /*
- * Setting the telephony bit for the handsfree audio gateway
- * role is not required by the HFP specification, but the
- * Nokia 616 carkit is just plain broken! It will refuse
- * pairing without this bit set.
- */
- case HANDSFREE_AGW_SVCLASS_ID:
- return 0x40; /* Telephony */
- case AUDIO_SOURCE_SVCLASS_ID:
- case VIDEO_SOURCE_SVCLASS_ID:
- return 0x08; /* Capturing */
- case AUDIO_SINK_SVCLASS_ID:
- case VIDEO_SINK_SVCLASS_ID:
- return 0x04; /* Rendering */
- case PANU_SVCLASS_ID:
- case NAP_SVCLASS_ID:
- case GN_SVCLASS_ID:
- return 0x02; /* Networking */
- default:
- return 0;
- }
-}
-
static uint8_t generate_service_class(int index)
{
struct dev_info *dev = &devs[index];
uint8_t val = 0;
for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
- uuid_t *uuid = l->data;
+ struct uuid_info *uuid = l->data;
- if (uuid->type != SDP_UUID16)
- continue;
-
- val |= get_uuid_mask(uuid->value.uuid16);
+ val |= uuid->svc_hint;
}
return val;
return err;
}
-static int hciops_add_uuid(int index, uuid_t *uuid)
+static int hciops_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
{
struct dev_info *dev = &devs[index];
+ struct uuid_info *info;
DBG("hci%d", index);
- dev->uuids = g_slist_append(dev->uuids, g_memdup(uuid, sizeof(*uuid)));
+ info = g_new0(struct uuid_info, 1);
+ memcpy(&info->uuid, uuid, sizeof(*uuid));
+ info->svc_hint = svc_hint;
+
+ dev->uuids = g_slist_append(dev->uuids, info);
return update_service_classes(index);
}
memcpy(uuid128, uuid, sizeof(*uuid));
}
-static int mgmt_uuid_op(int index, uint16_t op, uuid_t *uuid)
+static int mgmt_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
{
- char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_uuid)];
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_uuid)];
struct mgmt_hdr *hdr = (void *) buf;
- struct mgmt_cp_uuid *cp = (void *) &buf[sizeof(*hdr)];
+ struct mgmt_cp_add_uuid *cp = (void *) &buf[sizeof(*hdr)];
uuid_t uuid128;
+ DBG("index %d", index);
+
uuid_to_uuid128(&uuid128, uuid);
memset(buf, 0, sizeof(buf));
- hdr->opcode = htobs(op);
+ hdr->opcode = htobs(MGMT_OP_ADD_UUID);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp->index = htobs(index);
+ memcpy(cp->uuid, uuid128.value.uuid128.data, 16);
+ cp->svc_hint = svc_hint;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_remove_uuid(int index, uuid_t *uuid)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_uuid)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_remove_uuid *cp = (void *) &buf[sizeof(*hdr)];
+ uuid_t uuid128;
+
+ DBG("index %d", index);
+
+ uuid_to_uuid128(&uuid128, uuid);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_REMOVE_UUID);
hdr->len = htobs(sizeof(*cp));
cp->index = htobs(index);
memset(&uuid_any, 0, sizeof(uuid_any));
uuid_any.type = SDP_UUID128;
- return mgmt_uuid_op(index, MGMT_OP_REMOVE_UUID, &uuid_any);
+ return mgmt_remove_uuid(index, &uuid_any);
}
static void read_index_list_complete(int sk, void *buf, size_t len)
return;
}
+ mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 1);
+
info = &controllers[index];
info->type = rp->type;
info->enabled = rp->powered;
case MGMT_OP_REMOVE_UUID:
DBG("remove_uuid complete");
break;
+ case MGMT_OP_SET_DEV_CLASS:
+ DBG("set_dev_class complete");
+ break;
+ case MGMT_OP_SET_SERVICE_CACHE:
+ DBG("set_service_cache complete");
+ break;
default:
error("Unknown command complete for opcode %u", opcode);
break;
static int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor)
{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_dev_class)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_dev_class *cp = (void *) &buf[sizeof(*hdr)];
+
DBG("index %d major %u minor %u", index, major, minor);
- return -ENOSYS;
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_DEV_CLASS);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp->index = htobs(index);
+ cp->major = major;
+ cp->minor = minor;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
}
static int mgmt_set_limited_discoverable(int index, gboolean limited)
return -ENOSYS;
}
-static int mgmt_add_uuid(int index, uuid_t *uuid)
-{
- DBG("index %d", index);
- return mgmt_uuid_op(index, MGMT_OP_ADD_UUID, uuid);
-}
-
-static int mgmt_remove_uuid(int index, uuid_t *uuid)
-{
- DBG("index %d", index);
- return mgmt_uuid_op(index, MGMT_OP_REMOVE_UUID, uuid);
-}
-
static int mgmt_disable_cod_cache(int index)
{
DBG("index %d", index);
- return -ENOSYS;
+ return mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 0);
}
static int mgmt_restore_powered(int index)
g_strfreev(uuids);
}
+static uint8_t get_uuid_mask(uuid_t *uuid)
+{
+ if (uuid->type != SDP_UUID16)
+ return 0;
+
+ switch (uuid->value.uuid16) {
+ case DIALUP_NET_SVCLASS_ID:
+ case CIP_SVCLASS_ID:
+ return 0x42; /* Telephony & Networking */
+ case IRMC_SYNC_SVCLASS_ID:
+ case OBEX_OBJPUSH_SVCLASS_ID:
+ case OBEX_FILETRANS_SVCLASS_ID:
+ case IRMC_SYNC_CMD_SVCLASS_ID:
+ case PBAP_PSE_SVCLASS_ID:
+ return 0x10; /* Object Transfer */
+ case HEADSET_SVCLASS_ID:
+ case HANDSFREE_SVCLASS_ID:
+ return 0x20; /* Audio */
+ case CORDLESS_TELEPHONY_SVCLASS_ID:
+ case INTERCOM_SVCLASS_ID:
+ case FAX_SVCLASS_ID:
+ case SAP_SVCLASS_ID:
+ /*
+ * Setting the telephony bit for the handsfree audio gateway
+ * role is not required by the HFP specification, but the
+ * Nokia 616 carkit is just plain broken! It will refuse
+ * pairing without this bit set.
+ */
+ case HANDSFREE_AGW_SVCLASS_ID:
+ return 0x40; /* Telephony */
+ case AUDIO_SOURCE_SVCLASS_ID:
+ case VIDEO_SOURCE_SVCLASS_ID:
+ return 0x08; /* Capturing */
+ case AUDIO_SINK_SVCLASS_ID:
+ case VIDEO_SINK_SVCLASS_ID:
+ return 0x04; /* Rendering */
+ case PANU_SVCLASS_ID:
+ case NAP_SVCLASS_ID:
+ case GN_SVCLASS_ID:
+ return 0x02; /* Networking */
+ default:
+ return 0;
+ }
+}
+
static int uuid_cmp(const void *a, const void *b)
{
const sdp_record_t *rec = a;
adapter->services = sdp_list_insert_sorted(adapter->services, rec,
record_sort);
- if (new_uuid)
- adapter_ops->add_uuid(adapter->dev_id, &rec->svclass);
+ if (new_uuid) {
+ uint8_t svc_hint = get_uuid_mask(&rec->svclass);
+ adapter_ops->add_uuid(adapter->dev_id, &rec->svclass, svc_hint);
+ }
adapter_emit_uuids_updated(adapter);
}
gpointer user_data);
int (*set_did) (int index, uint16_t vendor, uint16_t product,
uint16_t version);
- int (*add_uuid) (int index, uuid_t *uuid);
+ int (*add_uuid) (int index, uuid_t *uuid, uint8_t svc_hint);
int (*remove_uuid) (int index, uuid_t *uuid);
int (*disable_cod_cache) (int index);
int (*restore_powered) (int index);