mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 00:34:39 +08:00
input/device: Rework uHID code to use bt_uhid
This commit is contained in:
parent
00d4b9d89f
commit
e93eeb6cff
@ -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 \
|
||||
|
@ -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);
|
||||
err = bt_uhid_send(idev->uhid, &ev);
|
||||
if (err < 0) {
|
||||
error("bt_uhid_send: %s (%d)", strerror(-err), -err);
|
||||
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;
|
||||
}
|
||||
@ -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,
|
||||
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 (idev->uhid_created)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user