static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct hid_device *dev = user_data; uint8_t state; DBG(""); if (conn_err) { error("%s", conn_err->message); state = HAL_HIDHOST_STATE_FAILED; goto failed; } if (uhid_create(dev) < 0) { state = HAL_HIDHOST_STATE_NO_HID; goto failed; } dev->intr_watch = g_io_add_watch(dev->intr_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, intr_watch_cb, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); return; failed: bt_hid_notify_state(dev, state); hid_device_free(dev); }
static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct hid_device *dev = data; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) goto error; if (cond & G_IO_IN) return ctrl_io_watch_cb(chan, data); error: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); /* Checking for intr_watch avoids a double g_io_channel_shutdown since * it's likely that intr_watch_cb has been queued for dispatching in * this mainloop iteration */ if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch) g_io_channel_shutdown(chan, TRUE, NULL); if (dev->intr_io && !(cond & G_IO_NVAL)) g_io_channel_shutdown(dev->intr_io, TRUE, NULL); hid_device_free(dev); return FALSE; }
static void bt_hid_connect(const void *buf, uint16_t len) { const struct hal_cmd_hidhost_connect *cmd = buf; struct hid_device *dev; uint8_t status; char addr[18]; bdaddr_t dst; GSList *l; uuid_t uuid; DBG(""); android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); if (l) { status = HAL_STATUS_FAILED; goto failed; } dev = g_new0(struct hid_device, 1); bacpy(&dev->dst, &dst); dev->uhid_fd = -1; ba2str(&dev->dst, addr); DBG("connecting to %s", addr); bt_string2uuid(&uuid, HID_UUID); if (bt_search_service(&adapter_addr, &dev->dst, &uuid, hid_sdp_search_cb, dev, NULL, 0) < 0) { error("Failed to search sdp details"); hid_device_free(dev); status = HAL_STATUS_FAILED; goto failed; } devices = g_slist_append(devices, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); status = HAL_STATUS_SUCCESS; failed: ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, status); }
static void control_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct hid_device *dev = user_data; GError *err = NULL; DBG(""); if (conn_err) { bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); error("%s", conn_err->message); goto failed; } /* Connect to the HID interrupt channel */ dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!dev->intr_io) { error("%s", err->message); g_error_free(err); goto failed; } dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, ctrl_watch_cb, dev); return; failed: hid_device_free(dev); }
static void hid_device_remove(struct hid_device *dev) { devices = g_slist_remove(devices, dev); hid_device_free(dev); }
static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hid_device *dev = data; sdp_list_t *list; GError *gerr = NULL; DBG(""); if (err < 0) { error("Unable to get SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("No SDP records found"); goto fail; } for (list = recs; list != NULL; list = list->next) { sdp_record_t *rec = list->data; sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); if (data) dev->vendor = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); if (data) dev->product = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_VERSION); if (data) dev->version = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); if (data) dev->country = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); if (data) dev->subclass = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); if (data) dev->boot_dev = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); if (data) { if (!SDP_IS_SEQ(data->dtd)) goto fail; /* First HIDDescriptor */ data = data->val.dataseq; if (!SDP_IS_SEQ(data->dtd)) goto fail; /* ClassDescriptorType */ data = data->val.dataseq; if (data->dtd != SDP_UINT8) goto fail; /* ClassDescriptorData */ data = data->next; if (!data || !SDP_IS_TEXT_STR(data->dtd)) goto fail; dev->rd_size = data->unitSize; dev->rd_data = g_memdup(data->val.str, data->unitSize); } } if (dev->ctrl_io) { if (uhid_create(dev) < 0) goto fail; return; } dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); goto fail; } return; fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_free(dev); }
static void free_hid_devices(gpointer data, gpointer user_data) { struct hid_device *dev = data; hid_device_free(dev); }
static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct hid_device *dev; bdaddr_t src, dst; char address[18]; uint16_t psm; GError *gerr = NULL; GSList *l; uuid_t uuid; if (err) { error("%s", err->message); return; } bt_io_get(chan, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_PSM, &psm, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_io_channel_shutdown(chan, TRUE, NULL); g_error_free(gerr); return; } ba2str(&dst, address); DBG("Incoming connection from %s on PSM %d", address, psm); switch (psm) { case L2CAP_PSM_HIDP_CTRL: l = g_slist_find_custom(devices, &dst, device_cmp); if (l) return; dev = g_new0(struct hid_device, 1); bacpy(&dev->dst, &dst); dev->ctrl_io = g_io_channel_ref(chan); dev->uhid_fd = -1; bt_string2uuid(&uuid, HID_UUID); if (bt_search_service(&src, &dev->dst, &uuid, hid_sdp_search_cb, dev, NULL, 0) < 0) { error("failed to search sdp details"); hid_device_free(dev); return; } devices = g_slist_append(devices, dev); dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, ctrl_watch_cb, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); break; case L2CAP_PSM_HIDP_INTR: l = g_slist_find_custom(devices, &dst, device_cmp); if (!l) return; dev = l->data; dev->intr_io = g_io_channel_ref(chan); dev->intr_watch = g_io_add_watch(dev->intr_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, intr_watch_cb, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); break; } }