diff --git a/Makefile.am b/Makefile.am
index e1daf44..8266def 100644
--- a/Makefile.am
+++ b/Makefile.am
if HOGPLUGIN
builtin_modules += hog
builtin_sources += profiles/input/hog_manager.c profiles/input/hog_device.h \
- profiles/input/hog_device.c
+ profiles/input/hog_device.c profiles/input/uhid_copy.h
endif
if NETWORKPLUGIN
diff --git a/profiles/input/hog_device.c b/profiles/input/hog_device.c
index 8e5e758..437aeb8 100644
--- a/profiles/input/hog_device.c
+++ b/profiles/input/hog_device.c
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "uhid_copy.h"
#include <bluetooth/bluetooth.h>
#include <bluetooth/uuid.h>
#define HOG_REPORT_MAP_UUID 0x2A4B
#define HOG_REPORT_UUID 0x2A4D
+#define UHID_DEVICE_FILE "/dev/uhid"
#define HOG_REPORT_MAP_MAX_SIZE 512
guint report_cb_id;
struct gatt_primary *hog_primary;
GSList *reports;
+ int uhid_fd;
};
static GSList *devices = NULL;
static void report_value_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
{
- uint16_t handle;
+ struct hog_device *hogdev = user_data;
+ struct uhid_event ev;
+ uint16_t report_size = len - 3;
if (len < 3) { /* 1-byte opcode + 2-byte handle */
error("Malformed ATT notification");
return;
}
- handle = att_get_u16(&pdu[1]);
- DBG("Report notification on handle 0x%04x", handle);
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_INPUT;
+ ev.u.input.size = MIN(report_size, UHID_DATA_MAX);
+ memcpy(ev.u.input.data, &pdu[3], MIN(report_size, UHID_DATA_MAX));
+
+ if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+ error("uHID write failed: %s", strerror(errno));
+ else
+ DBG("Report from HoG device %s written to uHID fd %d",
+ hogdev->path, hogdev->uhid_fd);
}
static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
gpointer user_data)
{
+ struct hog_device *hogdev = user_data;
uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
+ struct uhid_event ev;
ssize_t vlen;
int i;
else
DBG("\t %02x %02x", value[i], value[i + 1]);
}
+
+ /* create uHID device */
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_CREATE;
+ /* TODO: get info from DIS */
+ strcpy((char *) ev.u.create.name, "bluez-hog-device");
+ ev.u.create.vendor = 0xBEBA;
+ ev.u.create.product = 0xCAFE;
+ ev.u.create.version = 0;
+ ev.u.create.country = 0;
+ ev.u.create.bus = BUS_BLUETOOTH;
+ ev.u.create.rd_data = value;
+ ev.u.create.rd_size = vlen;
+
+ if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+ error("Failed to create uHID device: %s", strerror(errno));
}
static void char_discovered_cb(GSList *chars, guint8 status, gpointer user_data)
static void attio_disconnected_cb(gpointer user_data)
{
struct hog_device *hogdev = user_data;
+ struct uhid_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_DESTROY;
+ if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+ error("Failed to destroy uHID device: %s", strerror(errno));
g_attrib_unregister(hogdev->attrib, hogdev->report_cb_id);
hogdev->report_cb_id = 0;
return (l ? l->data : NULL);
}
+static void report_free(void *data)
+{
+ struct report *report = data;
+ g_free(report->decl);
+ g_free(report);
+}
+
+static void hog_device_free(struct hog_device *hogdev)
+{
+ btd_device_unref(hogdev->device);
+ g_slist_free_full(hogdev->reports, report_free);
+ g_free(hogdev->path);
+ g_free(hogdev->hog_primary);
+ g_free(hogdev);
+}
+
int hog_device_register(struct btd_device *device, const char *path)
{
struct hog_device *hogdev;
if (!hogdev)
return -ENOMEM;
+ hogdev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+ if (hogdev->uhid_fd < 0) {
+ int err = -errno;
+ error("Failed to open uHID device: %s", strerror(-err));
+ hog_device_free(hogdev);
+ return err;
+ }
+
hogdev->hog_primary = g_memdup(prim, sizeof(*prim));
hogdev->attioid = btd_device_add_attio_callback(device,
return 0;
}
-static void report_free(void *data)
-{
- struct report *report = data;
- g_free(report->decl);
- g_free(report);
-}
-
-static void hog_device_free(struct hog_device *hogdev)
-{
- btd_device_unref(hogdev->device);
- g_slist_free_full(hogdev->reports, report_free);
- g_free(hogdev->path);
- g_free(hogdev->hog_primary);
- g_free(hogdev);
-}
-
int hog_device_unregister(const char *path)
{
struct hog_device *hogdev;
return -EINVAL;
btd_device_remove_attio_callback(hogdev->device, hogdev->attioid);
+
+ close(hogdev->uhid_fd);
+ hogdev->uhid_fd = -1;
+
devices = g_slist_remove(devices, hogdev);
hog_device_free(hogdev);
diff --git a/profiles/input/uhid_copy.h b/profiles/input/uhid_copy.h
new file mode 100644
index 0000000..381b062
--- /dev/null
+++ b/profiles/input/uhid_copy.h
+#ifndef __UHID_H_
+#define __UHID_H_
+
+/*
+ * User-space I/O driver support for HID subsystem
+ * Copyright (c) 2012 David Herrmann
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * Public header for user-space communication. We try to keep every structure
+ * aligned but to be safe we also use __attribute__((__packed__)). Therefore,
+ * the communication should be ABI compatible even between architectures.
+ */
+
+#include <linux/input.h>
+#include <linux/types.h>
+
+enum uhid_event_type {
+ UHID_CREATE,
+ UHID_DESTROY,
+ UHID_START,
+ UHID_STOP,
+ UHID_OPEN,
+ UHID_CLOSE,
+ UHID_OUTPUT,
+ UHID_OUTPUT_EV,
+ UHID_INPUT,
+ UHID_FEATURE,
+ UHID_FEATURE_ANSWER,
+};
+
+struct uhid_create_req {
+ __u8 name[128];
+ __u8 phys[64];
+ __u8 uniq[64];
+ __u8 *rd_data;
+ __u16 rd_size;
+
+ __u16 bus;
+ __u32 vendor;
+ __u32 product;
+ __u32 version;
+ __u32 country;
+} __attribute__((__packed__));
+
+#define UHID_DATA_MAX 4096
+
+enum uhid_report_type {
+ UHID_FEATURE_REPORT,
+ UHID_OUTPUT_REPORT,
+ UHID_INPUT_REPORT,
+};
+
+struct uhid_input_req {
+ __u8 data[UHID_DATA_MAX];
+ __u16 size;
+} __attribute__((__packed__));
+
+struct uhid_output_req {
+ __u8 data[UHID_DATA_MAX];
+ __u16 size;
+ __u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_output_ev_req {
+ __u16 type;
+ __u16 code;
+ __s32 value;
+} __attribute__((__packed__));
+
+struct uhid_feature_req {
+ __u32 id;
+ __u8 rnum;
+ __u8 rtype;
+} __attribute__((__packed__));
+
+struct uhid_feature_answer_req {
+ __u32 id;
+ __u16 err;
+ __u16 size;
+ __u8 data[UHID_DATA_MAX];
+};
+
+struct uhid_event {
+ __u32 type;
+
+ union {
+ struct uhid_create_req create;
+ struct uhid_input_req input;
+ struct uhid_output_req output;
+ struct uhid_output_ev_req output_ev;
+ struct uhid_feature_req feature;
+ struct uhid_feature_answer_req feature_answer;
+ } u;
+} __attribute__((__packed__));
+
+#endif /* __UHID_H_ */