static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data) { struct hid_device *dev = data; uint8_t buf[UHID_DATA_MAX]; struct uhid_event ev; int fd, bread, err; /* Wait uHID if not ready */ if (!dev->uhid) return TRUE; fd = g_io_channel_unix_get_fd(chan); bread = read(fd, buf, sizeof(buf)); if (bread < 0) { error("hidhost: read from interrupt failed: %s(%d)", strerror(errno), -errno); return TRUE; } /* Discard non-data packets */ if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) return TRUE; /* send data to uHID device skipping HIDP header byte */ memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; ev.u.input.size = bread - 1; memcpy(ev.u.input.data, &buf[1], ev.u.input.size); err = bt_uhid_send(dev->uhid, &ev); if (err < 0) DBG("bt_uhid_send: %s (%d)", strerror(-err), -err); return TRUE; }
static bool uhid_send_input_report(struct input_device *idev, const uint8_t *data, size_t size) { struct uhid_event ev; int err; if (data == NULL) size = 0; if (size > sizeof(ev.u.input.data)) size = sizeof(ev.u.input.data); if (!idev->uhid_created) { DBG("HID report (%zu bytes) dropped", size); return false; } memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; ev.u.input.size = size; if (size > 0) memcpy(ev.u.input.data, data, size); err = bt_uhid_send(idev->uhid, &ev); if (err < 0) { error("bt_uhid_send: %s (%d)", strerror(-err), -err); return false; } DBG("HID report (%zu bytes)", size); return true; }
static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct hog_device *hogdev = user_data; struct uhid_event rsp; int err; hogdev->getrep_att = 0; memset(&rsp, 0, sizeof(rsp)); rsp.type = UHID_GET_REPORT_REPLY; rsp.u.get_report_reply.id = hogdev->getrep_id; if (status != 0) { error("Error reading Report value: %s", att_ecode2str(status)); goto exit; } if (len == 0) { error("Error reading Report, length %d", len); status = EIO; goto exit; } if (pdu[0] != 0x0b) { error("Error reading Report, invalid response: %02x", pdu[0]); status = EPROTO; goto exit; } --len; ++pdu; if (hogdev->has_report_id && len > 0) { --len; ++pdu; } rsp.u.get_report_reply.size = len; memcpy(rsp.u.get_report_reply.data, pdu, len); exit: rsp.u.get_report_reply.err = status; err = bt_uhid_send(hogdev->uhid, &rsp); if (err < 0) error("bt_uhid_send: %s", strerror(-err)); }
static void set_report_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct hog_device *hogdev = user_data; struct uhid_event rsp; int err; hogdev->setrep_att = 0; memset(&rsp, 0, sizeof(rsp)); rsp.type = UHID_SET_REPORT_REPLY; rsp.u.set_report_reply.id = hogdev->setrep_id; rsp.u.set_report_reply.err = status; if (status != 0) error("Error setting Report value: %s", att_ecode2str(status)); err = bt_uhid_send(hogdev->uhid, &rsp); if (err < 0) error("bt_uhid_send: %s", strerror(-err)); }
static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data) { struct report *report = user_data; struct hog_device *hogdev = report->hogdev; struct uhid_event ev; uint8_t *buf; int err; if (len < ATT_NOTIFICATION_HEADER_SIZE) { error("Malformed ATT notification"); return; } pdu += ATT_NOTIFICATION_HEADER_SIZE; len -= ATT_NOTIFICATION_HEADER_SIZE; memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; buf = ev.u.input.data; if (hogdev->has_report_id) { buf[0] = report->id; len = MIN(len, sizeof(ev.u.input.data) - 1); memcpy(buf + 1, pdu, len); ev.u.input.size = ++len; } else { len = MIN(len, sizeof(ev.u.input.data)); memcpy(buf, pdu, len); ev.u.input.size = len; } err = bt_uhid_send(hogdev->uhid, &ev); if (err < 0) { error("bt_uhid_send: %s (%d)", strerror(-err), -err); return; } DBG("HoG report (%u bytes)", ev.u.input.size); }
static bool uhid_send_feature_answer(struct input_device *idev, const uint8_t *data, size_t size, uint32_t id, uint16_t err) { struct uhid_event ev; int ret; if (data == NULL) size = 0; if (size > sizeof(ev.u.feature_answer.data)) size = sizeof(ev.u.feature_answer.data); if (!idev->uhid_created) { DBG("HID report (%zu bytes) dropped", size); return false; } memset(&ev, 0, sizeof(ev)); ev.type = UHID_FEATURE_ANSWER; ev.u.feature_answer.id = id; ev.u.feature_answer.err = err; ev.u.feature_answer.size = size; if (size > 0) memcpy(ev.u.feature_answer.data, data, size); ret = bt_uhid_send(idev->uhid, &ev); if (ret < 0) { error("bt_uhid_send: %s (%d)", strerror(-ret), -ret); return false; } DBG("HID report (%zu bytes)", size); return true; }
static int uhid_create(struct hid_device *dev) { struct uhid_event ev; int err; dev->uhid = bt_uhid_new_default(); if (!dev->uhid) { err = -errno; error("hidhost: Failed to create bt_uhid instance"); return err; } memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; strcpy((char *) ev.u.create.name, "bluez-input-device"); ev.u.create.bus = BUS_BLUETOOTH; ev.u.create.vendor = dev->vendor; ev.u.create.product = dev->product; ev.u.create.version = dev->version; ev.u.create.country = dev->country; ev.u.create.rd_size = dev->rd_size; ev.u.create.rd_data = dev->rd_data; err = bt_uhid_send(dev->uhid, &ev); if (err < 0) { error("hidhost: Failed to create uHID device: %s", strerror(-err)); bt_uhid_unref(dev->uhid); dev->uhid = NULL; return err; } bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev); bt_hid_set_info(dev); return 0; }
static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req) { int err; struct uhid_event ev; if (idev->uhid_created) return 0; /* create uHID device */ memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; strncpy((char *) ev.u.create.name, req->name, sizeof(ev.u.create.name) - 1); ba2str(&idev->src, (char *) ev.u.create.phys); ba2str(&idev->dst, (char *) ev.u.create.uniq); ev.u.create.vendor = req->vendor; ev.u.create.product = req->product; ev.u.create.version = req->version; ev.u.create.country = req->country; ev.u.create.bus = BUS_BLUETOOTH; ev.u.create.rd_data = req->rd_data; ev.u.create.rd_size = req->rd_size; err = bt_uhid_send(idev->uhid, &ev); if (err < 0) { error("bt_uhid_send: %s", strerror(-err)); return err; } bt_uhid_register(idev->uhid, UHID_OUTPUT, hidp_send_set_report, idev); bt_uhid_register(idev->uhid, UHID_FEATURE, hidp_send_get_report, idev); idev->uhid_created = true; return err; }
static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct hog_device *hogdev = user_data; struct btd_adapter *adapter = device_get_adapter(hogdev->device); uint8_t value[HOG_REPORT_MAP_MAX_SIZE]; struct uhid_event ev; uint16_t vendor_src, vendor, product, version; ssize_t vlen; char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */ int i, err; if (status != 0) { error("Report Map read failed: %s", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, plen, value, sizeof(value)); if (vlen < 0) { error("ATT protocol error"); return; } DBG("Report MAP:"); for (i = 0; i < vlen;) { ssize_t ilen = 0; bool long_item = false; if (get_descriptor_item_info(&value[i], vlen - i, &ilen, &long_item)) { /* Report ID is short item with prefix 100001xx */ if (!long_item && (value[i] & 0xfc) == 0x84) hogdev->has_report_id = TRUE; DBG("\t%s", item2string(itemstr, &value[i], ilen)); i += ilen; } else { error("Report Map parsing failed at %d", i); /* Just print remaining items at once and break */ DBG("\t%s", item2string(itemstr, &value[i], vlen - i)); break; } } vendor_src = btd_device_get_vendor_src(hogdev->device); vendor = btd_device_get_vendor(hogdev->device); product = btd_device_get_product(hogdev->device); version = btd_device_get_version(hogdev->device); DBG("DIS information: vendor_src=0x%X, vendor=0x%X, product=0x%X, " "version=0x%X", vendor_src, vendor, product, version); /* create uHID device */ memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; if (device_name_known(hogdev->device)) device_get_name(hogdev->device, (char *) ev.u.create.name, sizeof(ev.u.create.name)); else strcpy((char *) ev.u.create.name, "bluez-hog-device"); ba2str(btd_adapter_get_address(adapter), (char *) ev.u.create.phys); ba2str(device_get_address(hogdev->device), (char *) ev.u.create.uniq); ev.u.create.vendor = vendor; ev.u.create.product = product; ev.u.create.version = version; ev.u.create.country = hogdev->bcountrycode; ev.u.create.bus = BUS_BLUETOOTH; ev.u.create.rd_data = value; ev.u.create.rd_size = vlen; err = bt_uhid_send(hogdev->uhid, &ev); if (err < 0) { error("bt_uhid_send: %s", strerror(-err)); return; } bt_uhid_register(hogdev->uhid, UHID_OUTPUT, forward_report, hogdev); bt_uhid_register(hogdev->uhid, UHID_SET_REPORT, set_report, hogdev); bt_uhid_register(hogdev->uhid, UHID_GET_REPORT, get_report, hogdev); }
static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct bt_hog *hog = user_data; uint8_t value[HOG_REPORT_MAP_MAX_SIZE]; struct uhid_event ev; ssize_t vlen; char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */ int i, err; GError *gerr = NULL; if (status != 0) { error("Report Map read failed: %s", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, plen, value, sizeof(value)); if (vlen < 0) { error("ATT protocol error"); return; } DBG("Report MAP:"); for (i = 0; i < vlen;) { ssize_t ilen = 0; bool long_item = false; if (get_descriptor_item_info(&value[i], vlen - i, &ilen, &long_item)) { /* Report ID is short item with prefix 100001xx */ if (!long_item && (value[i] & 0xfc) == 0x84) hog->has_report_id = TRUE; DBG("\t%s", item2string(itemstr, &value[i], ilen)); i += ilen; } else { error("Report Map parsing failed at %d", i); /* Just print remaining items at once and break */ DBG("\t%s", item2string(itemstr, &value[i], vlen - i)); break; } } /* create uHID device */ memset(&ev, 0, sizeof(ev)); ev.type = UHID_CREATE; bt_io_get(g_attrib_get_channel(hog->attrib), &gerr, BT_IO_OPT_SOURCE, ev.u.create.phys, BT_IO_OPT_DEST, ev.u.create.uniq, BT_IO_OPT_INVALID); if (gerr) { error("Failed to connection details: %s", gerr->message); g_error_free(gerr); return; } strcpy((char *) ev.u.create.name, hog->name); ev.u.create.vendor = hog->vendor; ev.u.create.product = hog->product; ev.u.create.version = hog->version; ev.u.create.country = hog->bcountrycode; ev.u.create.bus = BUS_BLUETOOTH; ev.u.create.rd_data = value; ev.u.create.rd_size = vlen; err = bt_uhid_send(hog->uhid, &ev); if (err < 0) { error("bt_uhid_send: %s", strerror(-err)); return; } bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog); bt_uhid_register(hog->uhid, UHID_FEATURE, forward_report, hog); }