diff options
author | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2014-05-22 17:07:05 +0300 |
---|---|---|
committer | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2014-05-23 16:40:29 +0300 |
commit | e93eeb6cff1b1874e44472bd65f251b2922a0f40 (patch) | |
tree | 0d40c606caf36b230535386e82af7bad7a83c045 /profiles | |
parent | 00d4b9d89fc3d20d38fff8f5a6be03f3a2aac7fb (diff) | |
download | bluez-e93eeb6cff1b1874e44472bd65f251b2922a0f40.tar.gz |
input/device: Rework uHID code to use bt_uhid
Diffstat (limited to 'profiles')
-rw-r--r-- | profiles/input/device.c | 255 |
1 files changed, 71 insertions, 184 deletions
diff --git a/profiles/input/device.c b/profiles/input/device.c index 52222eaad..fa1f5f2b6 100644 --- a/profiles/input/device.c +++ b/profiles/input/device.c @@ -52,15 +52,13 @@ #include "src/dbus-common.h" #include "src/error.h" #include "src/sdp-client.h" +#include "src/shared/uhid.h" #include "device.h" #include "hidp_defs.h" -#include "uhid_copy.h" #define INPUT_INTERFACE "org.bluez.Input1" -#define UHID_DEVICE_FILE "/dev/uhid" - enum reconnect_mode_t { RECONNECT_NONE = 0, RECONNECT_DEVICE, @@ -86,9 +84,7 @@ struct input_device { enum reconnect_mode_t reconnect_mode; guint reconnect_timer; uint32_t reconnect_attempt; - bool uhid_enabled; - int uhid_fd; - guint uhid_watch; + struct bt_uhid *uhid; bool uhid_created; uint8_t report_req_pending; guint report_req_timer; @@ -116,6 +112,22 @@ static void input_device_free(struct input_device *idev) if (idev->dc_id) device_remove_disconnect_watch(idev->device, idev->dc_id); + if (idev->uhid) { + if (idev->uhid_created) { + int err; + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + err = bt_uhid_send(idev->uhid, &ev); + if (err < 0) + error("bt_uhid_send: %s (%d)", strerror(-err), + -err); + } + + bt_uhid_unref(idev->uhid); + } + btd_service_unref(idev->service); btd_device_unref(idev->device); g_free(idev->path); @@ -198,7 +210,7 @@ static bool uhid_send_feature_answer(struct input_device *idev, uint32_t id, uint16_t err) { struct uhid_event ev; - ssize_t len; + int ret; if (data == NULL) size = 0; @@ -220,20 +232,13 @@ static bool uhid_send_feature_answer(struct input_device *idev, if (size > 0) memcpy(ev.u.feature_answer.data, data, size); - len = write(idev->uhid_fd, &ev, sizeof(ev)); - if (len < 0) { - error("uHID dev write error: %s (%d)", strerror(errno), errno); + ret = bt_uhid_send(idev->uhid, &ev); + if (ret < 0) { + error("bt_uhid_send: %s (%d)", strerror(-ret), -ret); return false; } - /* uHID kernel driver does not handle partial writes */ - if ((size_t) len < sizeof(ev)) { - error("uHID dev write error: partial write (%zd of %zu bytes)", - len, sizeof(ev)); - return false; - } - - DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd); + DBG("HID report (%zu bytes)", size); return true; } @@ -242,7 +247,7 @@ static bool uhid_send_input_report(struct input_device *idev, const uint8_t *data, size_t size) { struct uhid_event ev; - ssize_t len; + int err; if (data == NULL) size = 0; @@ -262,20 +267,13 @@ static bool uhid_send_input_report(struct input_device *idev, if (size > 0) memcpy(ev.u.input.data, data, size); - len = write(idev->uhid_fd, &ev, sizeof(ev)); - if (len < 0) { - error("uHID dev write error: %s (%d)", strerror(errno), errno); - return false; - } - - /* uHID kernel driver does not handle partial writes */ - if ((size_t) len < sizeof(ev)) { - error("uHID dev write error: partial write (%zd of %zu bytes)", - len, sizeof(ev)); + 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) -> uHID fd %d", size, idev->uhid_fd); + DBG("HID report (%zu bytes)", size); return true; } @@ -580,9 +578,9 @@ static gboolean hidp_report_req_timeout(gpointer data) return FALSE; } -static void hidp_send_set_report(struct input_device *idev, - struct uhid_event *ev) +static void hidp_send_set_report(struct uhid_event *ev, void *user_data) { + struct input_device *idev = user_data; uint8_t hdr; bool sent; @@ -618,9 +616,9 @@ static void hidp_send_set_report(struct input_device *idev, } } -static void hidp_send_get_report(struct input_device *idev, - struct uhid_event *ev) +static void hidp_send_get_report(struct uhid_event *ev, void *user_data) { + struct input_device *idev = user_data; uint8_t hdr; bool sent; @@ -660,91 +658,6 @@ static void hidp_send_get_report(struct input_device *idev, } } -static gboolean uhid_watch_cb(GIOChannel *chan, GIOCondition cond, - gpointer user_data) -{ - struct input_device *idev = user_data; - int fd; - ssize_t len; - struct uhid_event ev; - - if (cond & (G_IO_ERR | G_IO_NVAL)) - goto failed; - - fd = g_io_channel_unix_get_fd(chan); - memset(&ev, 0, sizeof(ev)); - - len = read(fd, &ev, sizeof(ev)); - if (len < 0) { - error("uHID dev read error: %s (%d)", strerror(errno), errno); - goto failed; - } - - if ((size_t) len < sizeof(ev.type)) { - error("uHID dev read returned too few bytes"); - goto failed; - } - - DBG("uHID event type %u received (%zd bytes)", ev.type, len); - - switch (ev.type) { - case UHID_START: - case UHID_STOP: - /* These are called to start and stop the underlying hardware. - * For HID we open the channels before creating the device so - * the hardware is always ready. No need to handle these. - * Note that these are also called when the kernel switches - * between device-drivers loaded on the HID device. But we can - * simply keep the hardware alive during transitions and it - * works just fine. - * The kernel never destroys a device itself! Only an explicit - * UHID_DESTROY request can remove a device. - */ - break; - case UHID_OPEN: - case UHID_CLOSE: - /* OPEN/CLOSE are sent whenever user-space opens any interface - * provided by the kernel HID device. Whenever the open-count - * is non-zero we must be ready for I/O. As long as it is zero, - * we can decide to drop all I/O and put the device - * asleep This is optional, though. Moreover, some - * special device drivers are buggy in that regard, so - * maybe we just keep I/O always awake like HIDP in the - * kernel does. - */ - break; - case UHID_OUTPUT: - hidp_send_set_report(idev, &ev); - break; - case UHID_FEATURE: - hidp_send_get_report(idev, &ev); - break; - case UHID_OUTPUT_EV: - /* This is only sent by kernels prior to linux-3.11. It - * requires us to parse HID-descriptors in user-space to - * properly handle it. This is redundant as the kernel - * does it already. That's why newer kernels assemble - * the output-reports and send it to us via UHID_OUTPUT. - * We never implemented this, so we rely on users to use - * recent-enough kernels if they want this feature. No reason - * to implement this for older kernels. - */ - DBG("Unsupported uHID output event: type %u code %u value %d", - ev.u.output_ev.type, ev.u.output_ev.code, - ev.u.output_ev.value); - break; - default: - warn("unexpected uHID event"); - break; - } - - return TRUE; - -failed: - idev->uhid_watch = 0; - return FALSE; -} - static void epox_endian_quirk(unsigned char *data, int size) { /* USAGE_PAGE (Keyboard) 05 07 @@ -949,33 +862,38 @@ static int ioctl_disconnect(struct input_device *idev, uint32_t flags) static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req) { - int err = 0; + int err; struct uhid_event ev; - if (!idev->uhid_created) { - /* create uHID device */ - memset(&ev, 0, sizeof(ev)); - ev.type = UHID_CREATE; - strncpy((char *) ev.u.create.name, req->name, + 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; - - if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) { - err = -errno; - error("Failed to create uHID device: %s (%d)", - strerror(-err), -err); - } else - idev->uhid_created = true; + 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; } @@ -987,7 +905,7 @@ static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition, DBG(""); - if (idev->uhid_enabled) + if (idev->uhid) err = uhid_connadd(idev, idev->req); else err = ioctl_connadd(idev->req); @@ -1089,7 +1007,7 @@ static int hidp_add_connection(struct input_device *idev) return 0; } - if (idev->uhid_enabled) + if (idev->uhid) err = uhid_connadd(idev, req); else err = ioctl_connadd(req); @@ -1103,7 +1021,7 @@ cleanup: static bool is_connected(struct input_device *idev) { - if (idev->uhid_enabled) + if (idev->uhid) return (idev->intr_io != NULL && idev->ctrl_io != NULL); else return ioctl_is_connected(idev); @@ -1120,7 +1038,7 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags) if (idev->ctrl_io) g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL); - if (idev->uhid_enabled) + if (idev->uhid) return 0; else return ioctl_disconnect(idev, flags); @@ -1174,7 +1092,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, if (err < 0) goto failed; - if (idev->uhid_enabled) + if (idev->uhid) cond |= G_IO_IN; idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb, @@ -1229,7 +1147,7 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err, idev->intr_io = io; - if (idev->uhid_enabled) + if (idev->uhid) cond |= G_IO_IN; idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb, @@ -1435,7 +1353,6 @@ static struct input_device *input_device_new(struct btd_service *service) idev->path = g_strdup(path); idev->handle = rec->handle; idev->disable_sdp = is_device_sdp_disable(rec); - idev->uhid_enabled = uhid_enabled; /* Initialize device properties */ extract_hid_props(idev, rec); @@ -1465,8 +1382,6 @@ int input_device_register(struct btd_service *service) struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct input_device *idev; - int err; - GIOChannel *io; DBG("%s", path); @@ -1474,22 +1389,13 @@ int input_device_register(struct btd_service *service) if (!idev) return -EINVAL; - if (idev->uhid_enabled) { - idev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC); - if (idev->uhid_fd < 0) { - err = errno; - error("Failed to open uHID device: %s (%d)", - strerror(err), err); + if (uhid_enabled) { + idev->uhid = bt_uhid_new_default(); + if (!idev->uhid) { + error("bt_uhid_new_default: failed"); input_device_free(idev); - return -err; + return -EIO; } - - io = g_io_channel_unix_new(idev->uhid_fd); - g_io_channel_set_encoding(io, NULL, NULL); - idev->uhid_watch = g_io_add_watch(io, - G_IO_IN | G_IO_ERR | G_IO_NVAL, - uhid_watch_cb, idev); - g_io_channel_unref(io); } if (g_dbus_register_interface(btd_get_dbus_connection(), @@ -1529,31 +1435,12 @@ void input_device_unregister(struct btd_service *service) struct btd_device *device = btd_service_get_device(service); const char *path = device_get_path(device); struct input_device *idev = btd_service_get_user_data(service); - struct uhid_event ev; DBG("%s", path); g_dbus_unregister_interface(btd_get_dbus_connection(), idev->path, INPUT_INTERFACE); - if (idev->uhid_enabled) { - if (idev->uhid_watch) { - g_source_remove(idev->uhid_watch); - idev->uhid_watch = 0; - } - - if (idev->uhid_created) { - memset(&ev, 0, sizeof(ev)); - ev.type = UHID_DESTROY; - if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) - error("Failed to destroy uHID device: %s (%d)", - strerror(errno), errno); - } - - close(idev->uhid_fd); - idev->uhid_fd = -1; - } - input_device_free(idev); } @@ -1599,7 +1486,7 @@ int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, if (!idev) return -ENOENT; - if (idev->uhid_enabled) + if (uhid_enabled) cond |= G_IO_IN; switch (psm) { |