diff options
-rw-r--r-- | input/device.c | 242 | ||||
-rw-r--r-- | input/device.h | 4 | ||||
-rw-r--r-- | input/main.c | 74 | ||||
-rw-r--r-- | input/manager.c | 654 | ||||
-rw-r--r-- | input/manager.h | 4 |
5 files changed, 114 insertions, 864 deletions
diff --git a/input/device.c b/input/device.c index 75acaa82..632b5564 100644 --- a/input/device.c +++ b/input/device.c @@ -49,14 +49,14 @@ #include "device.h" #include "error.h" -#include "manager.h" #include "storage.h" #include "fakehid.h" #include "glib-helper.h" -#define INPUT_DEVICE_INTERFACE "org.bluez.input.Device" +#define INPUT_DEVICE_INTERFACE "org.bluez.input.Device" +#define INPUT_HEADSET_INTERFACE "org.bluez.input.Headset" -#define BUF_SIZE 16 +#define BUF_SIZE 16 #define UPDOWN_ENABLED 1 @@ -77,6 +77,7 @@ struct device { DBusMessage *pending_connect; DBusConnection *conn; char *path; + char *iface; int ctrl_sk; int intr_sk; guint ctrl_watch; @@ -193,85 +194,6 @@ static int uinput_create(char *name) return fd; } -static const char *create_input_path(uint8_t major, uint8_t minor) -{ - static char path[48]; - char subpath[32]; - static int next_id = 0; - - switch (major) { - case 0x02: /* Phone */ - strcpy(subpath, "phone"); - break; - case 0x04: /* Audio */ - switch (minor) { - /* FIXME: Testing required */ - case 0x01: /* Wearable Headset Device */ - strcpy(subpath, "wearable"); - break; - case 0x02: /* Hands-free */ - strcpy(subpath, "handsfree"); - break; - case 0x06: /* Headphone */ - strcpy(subpath, "headphone"); - break; - default: - return NULL; - } - break; - case 0x05: /* Peripheral */ - switch (minor & 0x30) { - case 0x10: - strcpy(subpath, "keyboard"); - break; - case 0x20: - strcpy(subpath, "pointing"); - break; - case 0x30: - strcpy(subpath, "combo"); - break; - default: - subpath[0] = '\0'; - break; - } - - if ((minor & 0x0f) && (strlen(subpath) > 0)) - strcat(subpath, "/"); - - switch (minor & 0x0f) { - case 0x00: - break; - case 0x01: - strcat(subpath, "joystick"); - break; - case 0x02: - strcat(subpath, "gamepad"); - break; - case 0x03: - strcat(subpath, "remotecontrol"); - break; - case 0x04: - strcat(subpath, "sensing"); - break; - case 0x05: - strcat(subpath, "digitizertablet"); - break; - case 0x06: - strcat(subpath, "cardreader"); - break; - default: - strcat(subpath, "reserved"); - break; - } - break; - default: - return NULL; - } - - snprintf(path, 48, "%s/%s%d", INPUT_PATH, subpath, next_id++); - return path; -} - static int decode_key(const char *str) { static int mode = UPDOWN_ENABLED, gain = 0; @@ -425,7 +347,7 @@ static void rfcomm_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, /* Sending the Connected signal */ path = dbus_message_get_path(idev->pending_connect); g_dbus_emit_signal(idev->conn, path, - INPUT_DEVICE_INTERFACE, "Connected", + idev->iface, "Connected", DBUS_TYPE_INVALID); dbus_message_unref(idev->pending_connect); @@ -462,11 +384,8 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data if (cond & (G_IO_HUP | G_IO_ERR)) g_io_channel_close(chan); - g_dbus_emit_signal(idev->conn, - idev->path, - INPUT_DEVICE_INTERFACE, - "Disconnected", - DBUS_TYPE_INVALID); + g_dbus_emit_signal(idev->conn, idev->path, idev->iface, + "Disconnected", DBUS_TYPE_INVALID); g_source_remove(idev->ctrl_watch); idev->ctrl_watch = 0; @@ -489,11 +408,8 @@ static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data if (cond & (G_IO_HUP | G_IO_ERR)) g_io_channel_close(chan); - g_dbus_emit_signal(idev->conn, - idev->path, - INPUT_DEVICE_INTERFACE, - "Disconnected", - DBUS_TYPE_INVALID); + g_dbus_emit_signal(idev->conn, idev->path, idev->iface, + "Disconnected", DBUS_TYPE_INVALID); g_source_remove(idev->intr_watch); idev->intr_watch = 0; @@ -594,11 +510,8 @@ static void interrupt_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, idev->intr_watch = create_watch(idev->intr_sk, intr_watch_cb, idev); idev->ctrl_watch = create_watch(idev->ctrl_sk, ctrl_watch_cb, idev); - g_dbus_emit_signal(idev->conn, - idev->path, - INPUT_DEVICE_INTERFACE, - "Connected", - DBUS_TYPE_INVALID); + g_dbus_emit_signal(idev->conn, idev->path, idev->iface, + "Connected", DBUS_TYPE_INVALID); /* Replying to the requestor */ g_dbus_send_reply(idev->conn, idev->pending_connect, DBUS_TYPE_INVALID); @@ -826,64 +739,12 @@ static DBusMessage *device_is_connected(DBusConnection *conn, DBUS_TYPE_INVALID); } -static DBusMessage *device_get_adapter(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct device *idev = data; - char addr[18]; - const char *paddr = addr; - - ba2str(&idev->src, addr); - - return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &paddr, - DBUS_TYPE_INVALID); -} - -static DBusMessage *device_get_address(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct device *idev = data; - char addr[18]; - const char *paddr = addr; - - ba2str(&idev->dst, addr); - - return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &paddr, - DBUS_TYPE_INVALID); -} - -static DBusMessage *device_get_name(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct device *idev = data; - const char *pname = (idev->name ? idev->name : ""); - - return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &pname, - DBUS_TYPE_INVALID); -} - -static DBusMessage *device_get_product_id(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct device *idev = data; - - return g_dbus_create_reply(msg, DBUS_TYPE_UINT16, &idev->product, - DBUS_TYPE_INVALID); -} - -static DBusMessage *device_get_vendor_id(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct device *idev = data; - - return g_dbus_create_reply(msg, DBUS_TYPE_UINT16, &idev->vendor, - DBUS_TYPE_INVALID); -} - static void device_unregister(void *data) { struct device *idev = data; + info("Unregistered interface %s on path %s", idev->iface, idev->path); + /* Disconnect if applied */ disconnect(idev, (1 << HIDP_VIRTUAL_CABLE_UNPLUG)); device_free(idev); @@ -894,11 +755,6 @@ static GDBusMethodTable device_methods[] = { G_DBUS_METHOD_FLAG_ASYNC }, { "Disconnect", "", "", device_disconnect }, { "IsConnected", "", "b", device_is_connected }, - { "GetAdapter", "", "s", device_get_adapter }, - { "GetAddress", "", "s", device_get_address }, - { "GetName", "", "s", device_get_name }, - { "GetProductId", "", "q", device_get_product_id }, - { "GetVendorId", "", "q", device_get_vendor_id }, { } }; @@ -911,71 +767,53 @@ static GDBusSignalTable device_signals[] = { /* * Input registration functions */ -static int register_path(DBusConnection *conn, const char *path, struct device *idev) +static int register_path(DBusConnection *conn, struct device *idev) { - if (g_dbus_register_interface(conn, path, INPUT_DEVICE_INTERFACE, + if (g_dbus_register_interface(conn, idev->path, idev->iface, device_methods, device_signals, NULL, idev, device_unregister) == FALSE) { - error("Failed to register %s interface to %s", - INPUT_DEVICE_INTERFACE, path); + error("Failed to register interface %s on path %s", + idev->iface, idev->path); + device_free(idev); return -1; } devices = g_slist_append(devices, idev); - info("Created input device: %s", path); + info("Registered interface %s on path %s", idev->iface, idev->path); return 0; } int input_device_register(DBusConnection *conn, bdaddr_t *src, bdaddr_t *dst, - struct hidp_connadd_req *hid, const char **ppath) + struct hidp_connadd_req *hid, const char *path) { struct device *idev; - const char *path; - int err; idev = device_new(src, dst, hid->subclass, hid->idle_to); if (!idev) return -EINVAL; - path = create_input_path(idev->major, idev->minor); - if (!path) { - device_free(idev); - return -EINVAL; - } - - idev->path = g_strdup(path); - idev->product = hid->product; - idev->vendor = hid->vendor; - idev->conn = dbus_connection_ref(conn); - - err = register_path(conn, path, idev); - - if (!err && ppath) - *ppath = path; + idev->path = g_strdup(path); + idev->iface = INPUT_DEVICE_INTERFACE; + idev->product = hid->product; + idev->vendor = hid->vendor; + idev->conn = dbus_connection_ref(conn); - return err; + return register_path(conn, idev); } int fake_input_register(DBusConnection *conn, bdaddr_t *src, - bdaddr_t *dst, uint8_t ch, const char **ppath) + bdaddr_t *dst, uint8_t ch, const char *path) { struct device *idev; - const char *path; - int err; idev = device_new(src, dst, 0, 0); if (!idev) return -EINVAL; - path = create_input_path(idev->major, idev->minor); - if (!path) { - device_free(idev); - return -EINVAL; - } - idev->path = g_strdup(path); + idev->iface = INPUT_HEADSET_INTERFACE; idev->conn = dbus_connection_ref(conn); /* FIXME: Missing set product and vendor */ @@ -985,12 +823,7 @@ int fake_input_register(DBusConnection *conn, bdaddr_t *src, idev->fake->connect = rfcomm_connect; idev->fake->disconnect = fake_disconnect; - err = register_path(conn, path, idev); - - if (!err && ppath) - *ppath = path; - - return err; + return register_path(conn, idev); } static struct device *find_device(const bdaddr_t *src, const bdaddr_t *dst) @@ -1048,17 +881,11 @@ int input_device_unregister(DBusConnection *conn, const char *path) if (idev->intr_watch) { g_source_remove(idev->intr_watch); - g_dbus_emit_signal(conn, - path, INPUT_DEVICE_INTERFACE, + g_dbus_emit_signal(conn, path, idev->iface, "Disconnected", DBUS_TYPE_INVALID); } - g_dbus_emit_signal(conn, INPUT_PATH, - INPUT_MANAGER_INTERFACE, "DeviceRemoved" , - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - - g_dbus_unregister_interface(conn, path, INPUT_DEVICE_INTERFACE); + g_dbus_unregister_interface(conn, path, idev->iface); return 0; } @@ -1154,11 +981,8 @@ int input_device_connadd(bdaddr_t *src, bdaddr_t *dst) idev->intr_watch = create_watch(idev->intr_sk, intr_watch_cb, idev); idev->ctrl_watch = create_watch(idev->ctrl_sk, ctrl_watch_cb, idev); - g_dbus_emit_signal(idev->conn, - idev->path, - INPUT_DEVICE_INTERFACE, - "Connected", - DBUS_TYPE_INVALID); + g_dbus_emit_signal(idev->conn, idev->path, idev->iface, + "Connected", DBUS_TYPE_INVALID); return 0; error: diff --git a/input/device.h b/input/device.h index c9296ad0..d71e7a73 100644 --- a/input/device.h +++ b/input/device.h @@ -38,9 +38,9 @@ struct fake_input { }; int input_device_register(DBusConnection *conn, bdaddr_t *src, bdaddr_t *dst, - struct hidp_connadd_req *hidp, const char **ppath); + struct hidp_connadd_req *hid, const char *path); int fake_input_register(DBusConnection *conn, bdaddr_t *src, - bdaddr_t *dst, uint8_t ch, const char **ppath); + bdaddr_t *dst, uint8_t ch, const char *path); int input_device_unregister(DBusConnection *conn, const char *path); gboolean input_device_is_registered(bdaddr_t *src, bdaddr_t *dst); diff --git a/input/main.c b/input/main.c index 8462967a..d70bf2a0 100644 --- a/input/main.c +++ b/input/main.c @@ -33,77 +33,10 @@ #include <gdbus.h> #include "plugin.h" -#include "../hcid/device.h" #include "logging.h" #include "dbus-service.h" #include "manager.h" -#define INPUT_INTERFACE "org.bluez.Input" - -static DBusMessage *input_connect(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static DBusMessage *input_disconnect(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static DBusMessage *input_is_connected(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - dbus_bool_t connected = FALSE; - - return g_dbus_create_reply(msg, DBUS_TYPE_BOOLEAN, &connected, - DBUS_TYPE_INVALID); -} - -static GDBusMethodTable input_methods[] = { - { "Connect", "", "", input_connect }, - { "Disconnect", "", "", input_disconnect }, - { "IsConnected", "", "b", input_is_connected }, - { } -}; - -static GDBusSignalTable input_signals[] = { - { "Connected", "" }, - { "Disconnected", "" }, - { } -}; - -static DBusConnection *conn; - -static int input_probe(struct btd_device *device, GSList *records) -{ - const gchar *path = device_get_path(device); - DBG("path %s", path); - - if (g_dbus_register_interface(conn, path, INPUT_INTERFACE, - input_methods, input_signals, NULL, - device, NULL) == FALSE) - return -1; - - return 0; -} - -static void input_remove(struct btd_device *device) -{ - const gchar *path = device_get_path(device); - DBG("path %s", path); - - g_dbus_unregister_interface(conn, path, INPUT_INTERFACE); -} - -static struct btd_device_driver input_driver = { - .name = "input", - .uuids = BTD_UUIDS("00001124-0000-1000-8000-00805f9b34fb"), - .probe = input_probe, - .remove = input_remove, -}; - static GKeyFile *load_config_file(const char *file) { GKeyFile *keyfile; @@ -124,6 +57,7 @@ static GKeyFile *load_config_file(const char *file) static int input_init(void) { GKeyFile *config; + DBusConnection *conn; conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); if (conn == NULL) @@ -139,18 +73,12 @@ static int input_init(void) if (config) g_key_file_free(config); - btd_register_device_driver(&input_driver); - return 0; } static void input_exit(void) { - btd_unregister_device_driver(&input_driver); - input_manager_exit(); - - dbus_connection_unref(conn); } BLUETOOTH_PLUGIN_DEFINE("input", input_init, input_exit) diff --git a/input/manager.c b/input/manager.c index 2dfb9b2d..dc7440cd 100644 --- a/input/manager.c +++ b/input/manager.c @@ -44,6 +44,8 @@ #include "logging.h" #include "textfile.h" +#include "../hcid/adapter.h" +#include "../hcid/device.h" #include "device.h" #include "server.h" @@ -52,70 +54,10 @@ #include "storage.h" #include "glib-helper.h" -struct pending_req { - char *adapter_path; /* Local adapter D-Bus path */ - bdaddr_t src; /* Local adapter BT address */ - bdaddr_t dst; /* Peer BT address */ - DBusConnection *conn; - DBusMessage *msg; - sdp_list_t *pnp_recs; - sdp_list_t *hid_recs; - GIOChannel *ctrl_channel; -}; - static int idle_timeout = 0; -static GSList *device_paths = NULL; /* Input registered paths */ - static DBusConnection *connection = NULL; -static struct pending_req *pending_req_new(DBusConnection *conn, - DBusMessage *msg, bdaddr_t *src, bdaddr_t *dst) -{ - char adapter[18], adapter_path[32]; - struct pending_req *pr; - int dev_id; - - pr = g_try_new0(struct pending_req, 1); - if (!pr) - return NULL; - - ba2str(src, adapter); - dev_id = hci_devid(adapter); - snprintf(adapter_path, 32, "/org/bluez/hci%d", dev_id); - - pr->adapter_path = g_strdup(adapter_path); - bacpy(&pr->src, src); - bacpy(&pr->dst, dst); - pr->conn = dbus_connection_ref(conn); - pr->msg = dbus_message_ref(msg); - - return pr; -} - -static void pending_req_free(struct pending_req *pr) -{ - if (!pr) - return; - - if (pr->adapter_path) - g_free(pr->adapter_path); - - if (pr->conn) - dbus_connection_unref(pr->conn); - - if (pr->msg) - dbus_message_unref(pr->msg); - - if (pr->pnp_recs) - sdp_list_free(pr->pnp_recs, (sdp_free_func_t) sdp_record_free); - - if (pr->hid_recs) - sdp_list_free(pr->hid_recs, (sdp_free_func_t) sdp_record_free); - - g_free(pr); -} - static void epox_endian_quirk(unsigned char *data, int size) { /* USAGE_PAGE (Keyboard) 05 07 @@ -198,567 +140,133 @@ static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req) } } -static void extract_pnp_record(sdp_record_t *rec, struct hidp_connadd_req *req) +/* + * Stored inputs registration functions + */ + +static int load_stored(const char *source, const char *destination, + struct hidp_connadd_req *hidp) { - sdp_data_t *pdlist; + char filename[PATH_MAX + 1]; + char *value; + + /* load the input stored */ + create_name(filename, PATH_MAX, STORAGEDIR, destination, "input"); - pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); - req->vendor = pdlist ? pdlist->val.uint16 : 0x0000; + value = textfile_get(filename, destination); + if (!value) + return -EINVAL; - pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); - req->product = pdlist ? pdlist->val.uint16 : 0x0000; + memset(&hidp, 0, sizeof(hidp)); - pdlist = sdp_data_get(rec, SDP_ATTR_VERSION); - req->version = pdlist ? pdlist->val.uint16 : 0x0000; + return parse_stored_device_info(value, hidp); } -static void interrupt_connect_cb(GIOChannel *chan, int err, - const bdaddr_t *src, const bdaddr_t *dst, - gpointer user_data) +int input_probe(struct btd_device *device, GSList *records) { - struct pending_req *pr = user_data; + struct adapter *adapter = device_get_adapter(device); + const gchar *path = device_get_path(device); + const char *source, *destination; struct hidp_connadd_req hidp; - const char *path; + bdaddr_t src, dst; + + DBG("path %s", path); memset(&hidp, 0, sizeof(hidp)); - if (err < 0) { - error("connect(): %s (%d)", strerror(-err), -err); - goto failed; - } + source = adapter->address; + destination = device_get_address(device); - g_io_channel_close(chan); - g_io_channel_unref(chan); + if (load_stored(source, destination, &hidp) == 0) + goto done; hidp.idle_to = idle_timeout * 60; - extract_hid_record(pr->hid_recs->data, &hidp); - if (pr->pnp_recs) - extract_pnp_record(pr->pnp_recs->data, &hidp); - - store_device_info(&pr->src, &pr->dst, &hidp); - - if (input_device_register(pr->conn, &pr->src, - &pr->dst, &hidp, &path) < 0) { - error_failed(pr->conn, pr->msg, "path registration failed"); - goto cleanup; - } - - g_dbus_emit_signal(pr->conn, INPUT_PATH, - INPUT_MANAGER_INTERFACE, "DeviceCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - - device_paths = g_slist_append(device_paths, g_strdup(path)); - - g_dbus_send_reply(pr->conn, pr->msg, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + extract_hid_record(records->data, &hidp); - goto cleanup; +done: + str2ba(source, &src); + str2ba(destination, &dst); -failed: - error_connection_attempt_failed(pr->conn, pr->msg, err); - -cleanup: - g_io_channel_close(pr->ctrl_channel); - g_io_channel_unref(pr->ctrl_channel); - pending_req_free(pr); + store_device_info(&src, &dst, &hidp); if (hidp.rd_data) g_free(hidp.rd_data); -} - -static void control_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, - const bdaddr_t *dst, gpointer user_data) -{ - struct pending_req *pr = user_data; - - if (err < 0) { - error("connect(): %s (%d)", strerror(-err), -err); - goto failed; - } - /* Set HID control channel */ - pr->ctrl_channel = chan; - - /* Connect to the HID interrupt channel */ - err = bt_l2cap_connect(&pr->src, &pr->dst, L2CAP_PSM_HIDP_INTR, 0, - interrupt_connect_cb, pr); - if (err < 0) { - error("L2CAP connect failed:%s (%d)", strerror(-err), -err); - goto failed; - } - - return; - -failed: - error_connection_attempt_failed(pr->conn, pr->msg, -err); - pending_req_free(pr); + return input_device_register(connection, &src, &dst, &hidp, path); } -static void create_bonding_reply(DBusPendingCall *call, void *data) +void input_remove(struct btd_device *device) { - DBusMessage *reply = dbus_pending_call_steal_reply(call); - struct pending_req *pr = data; - DBusError derr; - int err; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("CreateBonding failed: %s(%s)", - derr.name, derr.message); - error_failed(pr->conn, pr->msg, "Authentication failed (CreateBonding)"); - dbus_error_free(&derr); - dbus_message_unref(reply); - pending_req_free(pr); - return; - } + const gchar *path = device_get_path(device); - dbus_message_unref(reply); + DBG("path %s", path); - err = bt_l2cap_connect(&pr->src, &pr->dst, L2CAP_PSM_HIDP_CTRL, 0, - control_connect_cb, pr); - if (err < 0) { - error("L2CAP connect failed:%s (%d)", strerror(-err), -err); - error_connection_attempt_failed(pr->conn, pr->msg, -err); - pending_req_free(pr); - } -} - -static int create_bonding(struct pending_req *pr) -{ - DBusPendingCall *pending; - DBusMessage *msg; - char address[18], *addr_ptr = address; - - msg = dbus_message_new_method_call("org.bluez", pr->adapter_path, - "org.bluez.Adapter", "CreateBonding"); - if (!msg) { - error("Unable to allocate new method call"); - return -1; - } - - ba2str(&pr->dst, address); - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_INVALID); - if (dbus_connection_send_with_reply(pr->conn, msg, &pending, -1) == FALSE) { - error("Can't send D-Bus message."); - dbus_message_unref(msg); - return -1; - } - dbus_pending_call_set_notify(pending, create_bonding_reply, pr, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(msg); - return 0; + input_device_unregister(connection, path); } -static void hid_record_cb(sdp_list_t *recs, int err, gpointer user_data) +int headset_input_probe(struct btd_device *device, GSList *records) { - struct pending_req *pr = user_data; - - if (err < 0) { - error_not_supported(pr->conn, pr->msg); - error("SDP search error: %s (%d)", strerror(-err), -err); - goto fail; - } - - if (!recs || !recs->data) { - error_not_supported(pr->conn, pr->msg); - error("Invalid HID service record length"); - goto fail; - } - - pr->hid_recs = recs; - - if (strcmp("CreateSecureDevice", dbus_message_get_member(pr->msg)) == 0) { - sdp_data_t *d; - - /* Pairing mandatory for keyboard and combo */ - d = sdp_data_get(pr->hid_recs->data, - SDP_ATTR_HID_DEVICE_SUBCLASS); - if (d && (d->val.uint8 & 0x40) && - !has_bonding(&pr->src, &pr->dst)) { - if (create_bonding(pr) < 0) { - error_failed(pr->conn, pr->msg, - "Unable to initialize bonding process"); - goto fail; - } - /* Wait bonding reply */ - return; - } - - /* Otherwise proceede L2CAP connection */ - } - - /* No encryption or link key already exists -- connect control channel */ - err = bt_l2cap_connect(&pr->src, &pr->dst, L2CAP_PSM_HIDP_CTRL, 0, - control_connect_cb, pr); - if (err < 0) { - error("L2CAP connect failed:%s (%d)", strerror(-err), -err); - error_connection_attempt_failed(pr->conn, pr->msg, -err); - goto fail; - } - - /* Wait L2CAP connect */ - return; - -fail: - pending_req_free(pr); -} - -static void pnp_record_cb(sdp_list_t *recs, int err, gpointer user_data) -{ - struct pending_req *pr = user_data; - uuid_t uuid; - - if (err < 0) { - error_not_supported(pr->conn, pr->msg); - error("SDP search error: %s (%d)", strerror(-err), -err); - goto fail; - } - - if (!recs || !recs->data) { - error_not_supported(pr->conn, pr->msg); - error("Invalid PnP service record length"); - goto fail; - } - - pr->pnp_recs = recs; - sdp_uuid16_create(&uuid, HID_SVCLASS_ID); - err = bt_search_service(&pr->src, &pr->dst, &uuid, hid_record_cb, - pr, NULL); - if (err < 0) { - error_not_supported(pr->conn, pr->msg); - error("HID service search request failed"); - goto fail; - } - - return; - -fail: - pending_req_free(pr); -} - -static void headset_record_cb(sdp_list_t *recs, int err, gpointer user_data) -{ - struct pending_req *pr = user_data; - sdp_record_t *rec; + struct adapter *adapter = device_get_adapter(device); + const gchar *path = device_get_path(device); + sdp_record_t *record = records->data; sdp_list_t *protos; - const char *path; uint8_t ch; + const char *source, *destination; + bdaddr_t src, dst; - if (err < 0) { - error_not_supported(pr->conn, pr->msg); - error("SDP search error: %s (%d)", strerror(-err), -err); - goto fail; - } - - if (!recs || !recs->data) { - error_not_supported(pr->conn, pr->msg); - error("Invalid headset service record length"); - goto fail; - } - - rec = recs->data; + DBG("path %s", path); - if (sdp_get_access_protos(rec, &protos) < 0) { - error_not_supported(pr->conn, pr->msg); - goto fail; + if (sdp_get_access_protos(record, &protos) < 0) { + error("Invalid record"); + return -EINVAL; } ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); - sdp_record_free(rec); if (ch <= 0) { - error_not_supported(pr->conn, pr->msg); error("Invalid RFCOMM channel"); - goto fail; - } - - /* FIXME: Store the fake input data */ - - if (fake_input_register(pr->conn, &pr->src, &pr->dst, ch, &path) < 0) { - error("D-Bus path registration failed:%s", path); - error_failed(pr->conn, pr->msg, "Path registration failed"); - goto fail; - } - - g_dbus_emit_signal(pr->conn, INPUT_PATH, - INPUT_MANAGER_INTERFACE, "DeviceCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - - device_paths = g_slist_append(device_paths, g_strdup(path)); - - g_dbus_send_reply(pr->conn, pr->msg, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - -fail: - pending_req_free(pr); -} - -static inline DBusMessage *adapter_not_available(DBusMessage *msg) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", - "Adapter not available"); -} - -static inline DBusMessage *already_exists(DBusMessage *msg) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists", - "Input Already exists"); -} - -static inline DBusMessage *not_supported(DBusMessage *msg) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported", - "Not supported"); -} - -static inline DBusMessage *does_not_exist(DBusMessage *msg) -{ - return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists", - "Input doesn't exist"); -} - -static DBusMessage *create_device(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct pending_req *pr; - const char *addr; - bdaddr_t src, dst; - uint32_t cls = 0; - int dev_id, err; - uuid_t uuid; - bt_callback_t cb; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr, - DBUS_TYPE_INVALID)) - return NULL; - - /* Get the default adapter */ - dev_id = hci_get_route(NULL); - if (dev_id < 0) { - error("Bluetooth adapter not available"); - return adapter_not_available(msg); - } - - if (hci_devba(dev_id, &src) < 0) { - error("Can't get local adapter device info"); - return adapter_not_available(msg); - } - - str2ba(addr, &dst); - if (input_device_is_registered(&src, &dst)) - return already_exists(msg); - - if (read_device_class(&src, &dst, &cls) < 0) { - error("Device class not available"); - return not_supported(msg); + return -EINVAL; } - pr = pending_req_new(conn, msg, &src, &dst); - if (!pr) - return NULL; - - switch (cls & 0x1f00) { - case 0x0500: /* Peripheral */ - case 0x0200: /* Phone */ - sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); - cb = pnp_record_cb; - break; - case 0x0400: /* Fake input */ - sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID); - cb = headset_record_cb; - break; - default: - pending_req_free(pr); - return not_supported(msg); - } - - err = bt_search_service(&src, &dst, &uuid, cb, pr, NULL); - if (err < 0) { - pending_req_free(pr); - return not_supported(msg); - } - - return NULL; -} - -static DBusMessage *remove_device(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - GSList *l; - const char *path; - int err; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID)) - return NULL; - - l = g_slist_find_custom(device_paths, path, (GCompareFunc) strcmp); - if (!l) - return does_not_exist(msg); - - err = input_device_unregister(conn, path); - if (err < 0) - return create_errno_message(msg, -err); - - g_free(l->data); - device_paths = g_slist_remove(device_paths, l->data); - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static DBusMessage *list_devices(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessageIter iter, iter_array; - DBusMessage *reply; - GSList *paths; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &iter_array); - - for (paths = device_paths; paths != NULL; paths = paths->next) { - const char *ppath = paths->data; - dbus_message_iter_append_basic(&iter_array, - DBUS_TYPE_STRING, &ppath); - } + source = adapter->address; + destination = device_get_address(device); - dbus_message_iter_close_container(&iter, &iter_array); + str2ba(source, &src); + str2ba(destination, &dst); - return reply; + return fake_input_register(connection, &src, &dst, ch, path); } -static void manager_unregister(void *data) +void headset_input_remove(struct btd_device *device) { - info("Unregistered manager path"); + const gchar *path = device_get_path(device); - g_slist_foreach(device_paths, (GFunc) free, NULL); + DBG("path %s", path); - g_slist_free(device_paths); + input_device_unregister(connection, path); } -/* - * Stored inputs registration functions - */ - -static void stored_input(char *key, char *value, void *data) -{ - const char *path; - struct hidp_connadd_req hidp; - bdaddr_t dst, *src = data; - - str2ba(key, &dst); - - memset(&hidp, 0, sizeof(hidp)); - - if (parse_stored_device_info(value, &hidp) < 0) - return; - - /* - * Repeated entries for the same remote device are - * acceptable since the source is different. - */ - if (input_device_register(connection, src, &dst, &hidp, &path) < 0) - goto cleanup; - - device_paths = g_slist_append(device_paths, g_strdup(path)); -cleanup: - if (hidp.rd_data) - g_free(hidp.rd_data); -} - -/* hidd to input transition function */ -static void stored_hidd(char *key, char *value, void *data) -{ - struct hidp_connadd_req hidp; - char *str, filename[PATH_MAX + 1], addr[18]; - bdaddr_t dst, *src = data; - - ba2str(src, addr); - create_name(filename, PATH_MAX, STORAGEDIR, addr, "input"); - - str = textfile_get(filename, key); - if (str) { - /* Skip: entry found in input file */ - free(str); - return; - } - - memset(&hidp, 0, sizeof(hidp)); - - if (parse_stored_hidd(value, &hidp) < 0) - return; - - str2ba(key, &dst); - store_device_info(src, &dst, &hidp); - if (hidp.rd_data) - g_free(hidp.rd_data); -} - -static void register_stored_inputs(void) -{ - char dirname[PATH_MAX + 1]; - char filename[PATH_MAX + 1]; - struct dirent *de; - DIR *dir; - bdaddr_t src; - - snprintf(dirname, PATH_MAX, "%s", STORAGEDIR); - - dir = opendir(dirname); - if (!dir) - return; - - while ((de = readdir(dir)) != NULL) { - if (!isdigit(de->d_name[0])) - continue; - - str2ba(de->d_name, &src); - - /* move the hidd entries to the input storage */ - create_name(filename, PATH_MAX, STORAGEDIR, - de->d_name, "hidd"); - textfile_foreach(filename, stored_hidd, &src); - - /* load the input stored devices */ - create_name(filename, PATH_MAX, STORAGEDIR, - de->d_name, "input"); - - textfile_foreach(filename, stored_input, &src); - } - - closedir(dir); -} - -static GDBusMethodTable manager_methods[] = { - { "ListDevices", "", "as", list_devices }, - { "CreateDevice", "s", "s", create_device, - G_DBUS_METHOD_FLAG_ASYNC }, - { "CreateSecureDevice", "s", "s", create_device, - G_DBUS_METHOD_FLAG_ASYNC }, - { "RemoveDevice", "s", "", remove_device }, - { } +static struct btd_device_driver input_driver = { + .name = "input", + .uuids = BTD_UUIDS(HID_UUID), + .probe = input_probe, + .remove = input_remove, }; -static GDBusSignalTable manager_signals[] = { - { "DeviceCreated", "s" }, - { "DeviceRemoved", "s" }, - { } +static struct btd_device_driver input_headset_driver = { + .name = "input-headset", + .uuids = BTD_UUIDS(HSP_HS_UUID), + .probe = headset_input_probe, + .remove = headset_input_remove, }; int input_manager_init(DBusConnection *conn, GKeyFile *config) { GError *err = NULL; - + if (config) { idle_timeout = g_key_file_get_integer(config, "General", "IdleTimeout", &err); @@ -768,33 +276,23 @@ int input_manager_init(DBusConnection *conn, GKeyFile *config) } } - if (g_dbus_register_interface(conn, INPUT_PATH, INPUT_MANAGER_INTERFACE, - manager_methods, manager_signals, NULL, - NULL, manager_unregister) == FALSE) { - error("Failed to register %s interface to %s", - INPUT_MANAGER_INTERFACE, INPUT_PATH); - return -1; - } - connection = dbus_connection_ref(conn); - info("Registered input manager path:%s", INPUT_PATH); - - /* Register well known HID devices */ - register_stored_inputs(); - server_start(); + btd_register_device_driver(&input_driver); + btd_register_device_driver(&input_headset_driver); + return 0; } void input_manager_exit(void) { - g_dbus_unregister_interface(connection, INPUT_PATH, - INPUT_MANAGER_INTERFACE); - server_stop(); + btd_unregister_device_driver(&input_driver); + btd_unregister_device_driver(&input_headset_driver); + dbus_connection_unref(connection); connection = NULL; diff --git a/input/manager.h b/input/manager.h index 8f9d999e..f50975c8 100644 --- a/input/manager.h +++ b/input/manager.h @@ -21,8 +21,8 @@ * */ -#define INPUT_PATH "/org/bluez/input" -#define INPUT_MANAGER_INTERFACE "org.bluez.input.Manager" +#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" +#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb" int input_manager_init(DBusConnection *conn, GKeyFile *config); void input_manager_exit(void); |