summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-05-22 17:07:05 +0300
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-05-23 16:40:29 +0300
commite93eeb6cff1b1874e44472bd65f251b2922a0f40 (patch)
tree0d40c606caf36b230535386e82af7bad7a83c045
parent00d4b9d89fc3d20d38fff8f5a6be03f3a2aac7fb (diff)
downloadbluez-e93eeb6cff1b1874e44472bd65f251b2922a0f40.tar.gz
input/device: Rework uHID code to use bt_uhid
-rw-r--r--Makefile.plugins3
-rw-r--r--profiles/input/device.c255
2 files changed, 73 insertions, 185 deletions
diff --git a/Makefile.plugins b/Makefile.plugins
index f0eada962..7a02d9fd5 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -56,7 +56,8 @@ builtin_modules += input
builtin_sources += profiles/input/manager.c \
profiles/input/server.h profiles/input/server.c \
profiles/input/device.h profiles/input/device.c \
- profiles/input/uhid_copy.h profiles/input/hidp_defs.h
+ src/shared/uhid.h src/shared/uhid.c \
+ profiles/input/hidp_defs.h
builtin_modules += hog
builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
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) {