summaryrefslogtreecommitdiffstats
path: root/input/manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'input/manager.c')
-rw-r--r--input/manager.c654
1 files changed, 76 insertions, 578 deletions
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;