diff options
author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-03-19 14:58:44 +0000 |
---|---|---|
committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-03-19 14:58:44 +0000 |
commit | f2c6a6f2debcccfb51d239834c3cf91a2c3a6c40 (patch) | |
tree | 2a83d19fc07684f6713dd901f46f04858a78bc3d /input | |
parent | 0ac929228aa1eb823f37776e2bbb84855417c66e (diff) |
Moving input functions to the right files
Diffstat (limited to 'input')
-rw-r--r-- | input/device.c | 1011 | ||||
-rw-r--r-- | input/device.h | 9 | ||||
-rw-r--r-- | input/error.c | 64 | ||||
-rw-r--r-- | input/error.h | 22 | ||||
-rw-r--r-- | input/main.c | 5 | ||||
-rw-r--r-- | input/manager.c | 854 | ||||
-rw-r--r-- | input/manager.h | 4 | ||||
-rw-r--r-- | input/storage.c | 31 | ||||
-rw-r--r-- | input/storage.h | 3 |
9 files changed, 1029 insertions, 974 deletions
diff --git a/input/device.c b/input/device.c index fb1e5e5d..8488b406 100644 --- a/input/device.c +++ b/input/device.c @@ -34,12 +34,8 @@ #include <sys/socket.h> #include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> #include <bluetooth/l2cap.h> #include <bluetooth/rfcomm.h> -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> #include <bluetooth/hidp.h> #include <glib.h> @@ -52,12 +48,11 @@ #include "uinput.h" #include "storage.h" +#include "error.h" +#include "manager.h" #include "device.h" -#define INPUT_PATH "/org/bluez/input" -#define INPUT_MANAGER_INTERFACE "org.bluez.input.Manager" #define INPUT_DEVICE_INTERFACE "org.bluez.input.Device" -#define INPUT_ERROR_INTERFACE "org.bluez.Error" #define L2CAP_PSM_HIDP_CTRL 0x11 #define L2CAP_PSM_HIDP_INTR 0x13 @@ -66,12 +61,6 @@ #define UPDOWN_ENABLED 1 -static DBusConnection *connection = NULL; - -const char *pnp_uuid = "00001200-0000-1000-8000-00805f9b34fb"; -const char *hid_uuid = "00001124-0000-1000-8000-00805f9b34fb"; -const char *headset_uuid = "00001108-0000-1000-8000-00805f9b34fb"; - struct fake_input { GIOChannel *io; int rfcomm; /* RFCOMM socket */ @@ -95,35 +84,22 @@ struct input_device { struct pending_connect *pending_connect; }; -struct input_manager { - bdaddr_t src; /* Local adapter BT address */ - GSList *paths; /* Input registered paths */ -}; - -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_record_t *pnp_rec; - sdp_record_t *hid_rec; -}; - -static struct input_device *input_device_new(bdaddr_t *src, bdaddr_t *dst, uint32_t cls) +static struct input_device *input_device_new(bdaddr_t *src, bdaddr_t *dst) { struct input_device *idev; + uint32_t cls; idev = g_new0(struct input_device, 1); bacpy(&idev->src, src); bacpy(&idev->dst, dst); + read_device_name(src, dst, &idev->name); + read_device_class(src, dst, &cls); + idev->major = (cls >> 8) & 0x1f; idev->minor = (cls >> 2) & 0x3f; - read_device_name(src, dst, &idev->name); - /* FIXME: hidp could be alloc dynamically */ snprintf(idev->hidp.name, 128, "%s", idev->name); @@ -157,178 +133,6 @@ static void input_device_free(struct input_device *idev) g_free(idev); } -static struct pending_req *pending_req_new(DBusConnection *conn, - DBusMessage *msg, const char *adapter_path, - bdaddr_t *src, bdaddr_t *dst) -{ - struct pending_req *pr; - pr = g_try_new0(struct pending_req, 1); - if (!pr) - return NULL; - - 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_rec) - sdp_record_free(pr->pnp_rec); - if (pr->hid_rec) - sdp_record_free(pr->hid_rec); - g_free(pr); -} - -/* - * Common D-Bus BlueZ input error functions - */ -static DBusHandlerResult err_unknown_device(DBusConnection *conn, DBusMessage *msg) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE ".UnknownDevice", - "Invalid device")); -} - -static DBusHandlerResult err_unknown_method(DBusConnection *conn, DBusMessage *msg) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE ".UnknownMethod", - "Unknown input method")); -} - -static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *str) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE ".Failed", str)); -} - -static DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE ".NotSupported", - "The service is not supported by the remote device")); -} - -static DBusHandlerResult err_connection_failed(DBusConnection *conn, - DBusMessage *msg, const char *str) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE".ConnectionAttemptFailed", - str)); -} - -static DBusHandlerResult err_already_exists(DBusConnection *conn, - DBusMessage *msg, const char *str) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE ".AlreadyExists", str)); -} - -static DBusHandlerResult err_does_not_exist(DBusConnection *conn, - DBusMessage *msg, const char *str) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, - INPUT_ERROR_INTERFACE ".DoesNotExist", str)); -} - -static DBusHandlerResult err_generic(DBusConnection *conn, DBusMessage *msg, - const char *name, const char *str) -{ - return send_message_and_unref(conn, - dbus_message_new_error(msg, name, str)); - -} - -static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req) -{ - sdp_data_t *pdlist, *pdlist2; - uint8_t attr_val; - - pdlist = sdp_data_get(rec, 0x0101); - pdlist2 = sdp_data_get(rec, 0x0102); - if (pdlist) { - if (pdlist2) { - if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) { - strncpy(req->name, pdlist2->val.str, 127); - strcat(req->name, " "); - } - strncat(req->name, pdlist->val.str, 127 - strlen(req->name)); - } else - strncpy(req->name, pdlist->val.str, 127); - } else { - pdlist2 = sdp_data_get(rec, 0x0100); - if (pdlist2) - strncpy(req->name, pdlist2->val.str, 127); - } - - pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION); - req->parser = pdlist ? pdlist->val.uint16 : 0x0100; - - pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); - req->subclass = pdlist ? pdlist->val.uint8 : 0; - - pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); - req->country = pdlist ? pdlist->val.uint8 : 0; - - pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE); - attr_val = pdlist ? pdlist->val.uint8 : 0; - if (attr_val) - req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG); - - pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); - attr_val = pdlist ? pdlist->val.uint8 : 0; - if (attr_val) - req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); - - pdlist = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); - if (pdlist) { - pdlist = pdlist->val.dataseq; - pdlist = pdlist->val.dataseq; - pdlist = pdlist->next; - - req->rd_data = g_try_malloc0(pdlist->unitSize); - if (req->rd_data) { - memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize); - req->rd_size = pdlist->unitSize; - } - } -} - -static void extract_pnp_record(sdp_record_t *rec, struct hidp_connadd_req *req) -{ - sdp_data_t *pdlist; - - pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); - req->vendor = pdlist ? pdlist->val.uint16 : 0x0000; - - pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); - req->product = pdlist ? pdlist->val.uint16 : 0x0000; - - pdlist = sdp_data_get(rec, SDP_ATTR_VERSION); - req->version = pdlist ? pdlist->val.uint16 : 0x0000; -} - static int uinput_create(char *name) { struct uinput_dev dev; @@ -615,7 +419,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, reply = dbus_message_new_method_return(idev->pending_connect->msg); if (reply) { - dbus_connection_send(connection, reply, NULL); + dbus_connection_send(idev->pending_connect->conn, reply, NULL); dbus_message_unref(reply); } @@ -1199,12 +1003,16 @@ static DBusHandlerResult device_message(DBusConnection *conn, if (strcmp(member, "SetTimeout") == 0) return device_set_timeout(conn, msg, data); - return err_unknown_method(conn, msg); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } static void device_unregister(DBusConnection *conn, void *data) { - input_device_free(data); + struct input_device *idev = data; + + /* Disconnect if applied */ + disconnect(idev, (1 << HIDP_VIRTUAL_CABLE_UNPLUG)); + input_device_free(idev); } /* Virtual table to handle device object path hierarchy */ @@ -1214,819 +1022,98 @@ static const DBusObjectPathVTable device_table = { }; /* - * Input Manager methods + * Input registration functions */ -static void input_manager_free(struct input_manager *mgr) +int input_device_register(DBusConnection *conn, bdaddr_t *src, bdaddr_t *dst, + struct hidp_connadd_req *hid, const char **path) { - if (!mgr) - return; + DBusMessage *msg; + struct input_device *idev; - if (mgr->paths) { - g_slist_foreach(mgr->paths, (GFunc) free, NULL); - g_slist_free(mgr->paths); - } + idev = input_device_new(src, dst); + *path = create_input_path(idev->major, idev->minor); - g_free(mgr); -} + memcpy(&idev->hidp, hid, sizeof(struct hidp_connadd_req)); -static int register_input_device(DBusConnection *conn, - struct input_device *idev, const char *path) -{ - DBusMessage *msg; - struct input_manager *mgr; + /* FIXME: rd_data is a pointer - hacking */ if (!dbus_connection_register_object_path(conn, - path, &device_table, idev)) { + *path, &device_table, idev)) { error("Input device path registration failed"); return -1; } - dbus_connection_get_object_path_data(conn, INPUT_PATH, (void *) &mgr); - mgr->paths = g_slist_append(mgr->paths, g_strdup(path)); - msg = dbus_message_new_signal(INPUT_PATH, INPUT_MANAGER_INTERFACE, "DeviceCreated"); if (!msg) return -1; dbus_message_append_args(msg, - DBUS_TYPE_STRING, &path, + DBUS_TYPE_STRING, &*path, DBUS_TYPE_INVALID); send_message_and_unref(conn, msg); - info("Created input device: %s", path); + info("Created input device: %s", *path); return 0; } -static int unregister_input_device(DBusConnection *conn, const char *path) +int fake_input_register(DBusConnection *conn, bdaddr_t *src, + bdaddr_t *dst, uint8_t ch, const char **path) { DBusMessage *msg; + struct input_device *idev; - if (!dbus_connection_unregister_object_path(conn, path)) { - error("Input device path unregister failed"); + idev = input_device_new(src, dst); + *path = create_input_path(idev->major, idev->minor); + + idev->fake = g_new0(struct fake_input, 1); + idev->fake->ch = ch; + + if (!dbus_connection_register_object_path(conn, + *path, &device_table, idev)) { + error("Fake input device path registration failed"); return -1; } + /* FIXME: dupplicated code */ msg = dbus_message_new_signal(INPUT_PATH, - INPUT_MANAGER_INTERFACE, "DeviceRemoved"); + INPUT_MANAGER_INTERFACE, "DeviceCreated"); if (!msg) return -1; dbus_message_append_args(msg, - DBUS_TYPE_STRING, &path, + DBUS_TYPE_STRING, &*path, DBUS_TYPE_INVALID); send_message_and_unref(conn, msg); - return 0; -} - -static int path_bdaddr_cmp(const char *path, const bdaddr_t *bdaddr) -{ - struct input_device *idev; + info("Created input device: %s", *path); - if (!dbus_connection_get_object_path_data(connection, path, - (void *) &idev)) - return -1; - - if (!idev) - return -1; + return 0; - return bacmp(&idev->dst, bdaddr); } -static int get_record(struct pending_req *pr, uint32_t handle, - DBusPendingCallNotifyFunction cb) +int input_device_unregister(DBusConnection *conn, const char *path) { DBusMessage *msg; - DBusPendingCall *pending; - char addr[18]; - const char *paddr = addr; - - msg = dbus_message_new_method_call("org.bluez", pr->adapter_path, - "org.bluez.Adapter", "GetRemoteServiceRecord"); - if (!msg) - return -1; - ba2str(&pr->dst, addr); - dbus_message_append_args(msg, - DBUS_TYPE_STRING, &paddr, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - - if (dbus_connection_send_with_reply(pr->conn, msg, &pending, -1) == FALSE) { - error("Can't send D-Bus message."); + if (!dbus_connection_unregister_object_path(conn, path)) { + error("Input device path unregister failed"); return -1; } - dbus_pending_call_set_notify(pending, cb, pr, NULL); - dbus_message_unref(msg); - - return 0; -} - -static int get_class(bdaddr_t *src, bdaddr_t *dst, uint32_t *cls) -{ - char filename[PATH_MAX + 1], *str; - char addr[18]; - - ba2str(src, addr); - create_name(filename, PATH_MAX, STORAGEDIR, addr, "classes"); - - ba2str(dst, addr); - str = textfile_get(filename, addr); - if (!str) - return -ENOENT; - - if (sscanf(str, "%x", cls) != 1) { - g_free(str); - return -ENOENT; - } - - g_free(str); - - return 0; -} - -static int get_handles(struct pending_req *pr, const char *uuid, - DBusPendingCallNotifyFunction cb) -{ - DBusMessage *msg; - DBusPendingCall *pending; - char addr[18]; - const char *paddr = addr; - - msg = dbus_message_new_method_call("org.bluez", pr->adapter_path, - "org.bluez.Adapter", "GetRemoteServiceHandles"); + msg = dbus_message_new_signal(INPUT_PATH, + INPUT_MANAGER_INTERFACE, "DeviceRemoved"); if (!msg) return -1; - ba2str(&pr->dst, addr); dbus_message_append_args(msg, - DBUS_TYPE_STRING, &paddr, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - if (dbus_connection_send_with_reply(pr->conn, msg, &pending, -1) == FALSE) { - error("Can't send D-Bus message."); - return -1; - } - - dbus_pending_call_set_notify(pending, cb, pr, NULL); - dbus_message_unref(msg); - - return 0; -} - -static void hid_record_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusMessage *pr_reply; - struct pending_req *pr = data; - struct input_device *idev; - DBusError derr; - uint8_t *rec_bin; - const char *path; - int len, scanned; - uint32_t cls; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - err_generic(pr->conn, pr->msg, derr.name, derr.message); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len, - DBUS_TYPE_INVALID)) { - err_not_supported(pr->conn, pr->msg); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (len == 0) { - err_not_supported(pr->conn, pr->msg); - error("Invalid HID service record length"); - goto fail; - } - - pr->hid_rec = sdp_extract_pdu(rec_bin, &scanned); - if (!pr->hid_rec) { - err_not_supported(pr->conn, pr->msg); - goto fail; - } - - if (get_class(&pr->src, &pr->dst, &cls) < 0) { - err_not_supported(pr->conn, pr->msg); - error("Device class not available"); - goto fail; - } - - idev = input_device_new(&pr->src, &pr->dst, cls); - - extract_hid_record(pr->hid_rec, &idev->hidp); - if (pr->pnp_rec) - extract_pnp_record(pr->pnp_rec, &idev->hidp); - - path = create_input_path(idev->major, idev->minor); - - if (register_input_device(pr->conn, idev, path) < 0) { - err_failed(pr->conn, pr->msg, "D-Bus path registration failed"); - input_device_free(idev); - goto fail; - } - - pr_reply = dbus_message_new_method_return(pr->msg); - dbus_message_append_args(pr_reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - send_message_and_unref(pr->conn, pr_reply); - - store_device_info(&pr->src, &pr->dst, &idev->hidp); -fail: - dbus_error_free(&derr); - pending_req_free(pr); - dbus_message_unref(reply); - dbus_pending_call_unref(call); -} - -static void hid_handle_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - struct pending_req *pr = data; - uint32_t *phandle; - DBusError derr; - int len; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - err_generic(pr->conn, pr->msg, derr.name, derr.message); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, &len, - DBUS_TYPE_INVALID)) { - err_not_supported(pr->conn, pr->msg); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (len == 0) { - err_not_supported(pr->conn, pr->msg); - error("HID record handle not found"); - goto fail; - } - - if (get_record(pr, *phandle, hid_record_reply) < 0) { - err_not_supported(pr->conn, pr->msg); - error("HID service attribute request failed"); - goto fail; - } else { - /* Wait record reply */ - goto done; - } -fail: - dbus_error_free(&derr); - pending_req_free(pr); -done: - dbus_message_unref(reply); - dbus_pending_call_unref(call); -} - -static void pnp_record_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - struct pending_req *pr = data; - DBusError derr; - uint8_t *rec_bin; - int len, scanned; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - err_generic(pr->conn, pr->msg, derr.name, derr.message); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len, - DBUS_TYPE_INVALID)) { - err_not_supported(pr->conn, pr->msg); - error("%s: %s", derr.name, derr.message); - goto fail; - } - if (len == 0) { - err_not_supported(pr->conn, pr->msg); - error("Invalid PnP service record length"); - goto fail; - } - - pr->pnp_rec = sdp_extract_pdu(rec_bin, &scanned); - if (get_handles(pr, hid_uuid, hid_handle_reply) < 0) { - err_not_supported(pr->conn, pr->msg); - error("HID service search request failed"); - goto fail; - } else { - /* Wait handle reply */ - goto done; - } - -fail: - dbus_error_free(&derr); - pending_req_free(pr); -done: - dbus_message_unref(reply); - dbus_pending_call_unref(call); -} - -static void pnp_handle_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - struct pending_req *pr = data; - DBusError derr; - uint32_t *phandle; - int len; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - err_generic(pr->conn, pr->msg, derr.name, derr.message); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, &len, - DBUS_TYPE_INVALID)) { - err_not_supported(pr->conn, pr->msg); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (len == 0) { - /* PnP is optional: Ignore it and request the HID handle */ - if (get_handles(pr, hid_uuid, hid_handle_reply) < 0) { - err_not_supported(pr->conn, pr->msg); - error("HID service search request failed"); - goto fail; - } - } else { - /* Request PnP record */ - if (get_record(pr, *phandle, pnp_record_reply) < 0) { - err_not_supported(pr->conn, pr->msg); - error("PnP service attribute request failed"); - goto fail; - } - } - - /* Wait HID handle reply or PnP record reply */ - goto done; - -fail: - dbus_error_free(&derr); - pending_req_free(pr); -done: - dbus_message_unref(reply); - dbus_pending_call_unref(call); -} - -static void headset_record_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusMessage *pr_reply; - DBusError derr; - struct pending_req *pr = data; - struct input_device *idev; - uint8_t *rec_bin; - sdp_record_t *rec; - sdp_list_t *protos; - const char *path; - int len, scanned; - uint32_t cls; - uint8_t ch; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - err_generic(pr->conn, pr->msg, derr.name, derr.message); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len, - DBUS_TYPE_INVALID)) { - err_not_supported(pr->conn, pr->msg); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (len == 0) { - err_not_supported(pr->conn, pr->msg); - error("Invalid headset service record length"); - goto fail; - } - - rec = sdp_extract_pdu(rec_bin, &scanned); - if (!rec) { - err_not_supported(pr->conn, pr->msg); - goto fail; - } - - if (sdp_get_access_protos(rec, &protos) < 0) { - err_not_supported(pr->conn, pr->msg); - goto fail; - } - - 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) { - err_not_supported(pr->conn, pr->msg); - error("Invalid RFCOMM channel"); - goto fail; - } - - if (get_class(&pr->src, &pr->dst, &cls) < 0) { - err_not_supported(pr->conn, pr->msg); - error("Device class not available"); - goto fail; - } - - idev = input_device_new(&pr->src, &pr->dst, cls); - - idev->fake = g_new0(struct fake_input, 1); - idev->fake->ch = ch; - - /* FIXME: Store the fake input data */ - - path = create_input_path(idev->major, idev->minor); - if (register_input_device(pr->conn, idev, path) < 0) { - error("D-Bus path registration failed:%s", path); - err_failed(pr->conn, pr->msg, "Path registration failed"); - input_device_free(idev); - goto fail; - } - - pr_reply = dbus_message_new_method_return(pr->msg); - dbus_message_append_args(pr_reply, - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - send_message_and_unref(pr->conn, pr_reply); -fail: - dbus_error_free(&derr); - pending_req_free(pr); - dbus_message_unref(reply); - dbus_pending_call_unref(call); -} - -static void headset_handle_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - struct pending_req *pr = data; - DBusError derr; - uint32_t *phandle; - int len; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - err_generic(pr->conn, pr->msg, derr.name, derr.message); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, &len, - DBUS_TYPE_INVALID)) { - err_not_supported(pr->conn, pr->msg); - error("%s: %s", derr.name, derr.message); - goto fail; - } - - if (len == 0) { - err_not_supported(pr->conn, pr->msg); - error("Headset record handle not found"); - goto fail; - } - - if (get_record(pr, *phandle, headset_record_reply) < 0) { - err_not_supported(pr->conn, pr->msg); - error("Headset service attribute request failed"); - goto fail; - } else { - /* Wait record reply */ - goto done; - } -fail: - dbus_error_free(&derr); - pending_req_free(pr); -done: - dbus_message_unref(reply); - dbus_pending_call_unref(call); -} - -static DBusHandlerResult manager_create_device(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct input_manager *mgr = data; - struct pending_req *pr; - DBusError derr; - char adapter[18], adapter_path[32]; - const char *addr; - GSList *l; - bdaddr_t dst; - uint32_t cls = 0; - int dev_id; - - dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &addr, - DBUS_TYPE_INVALID)) { - err_generic(conn, msg, derr.name, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } - - str2ba(addr, &dst); - l = g_slist_find_custom(mgr->paths, &dst, - (GCompareFunc) path_bdaddr_cmp); - if (l) - return err_already_exists(conn, msg, "Input Already exists"); - - ba2str(&mgr->src, adapter); - dev_id = hci_devid(adapter); - snprintf(adapter_path, 32, "/org/bluez/hci%d", dev_id); - - if (get_class(&mgr->src, &dst, &cls) < 0) { - error("Device class not available"); - return err_not_supported(conn, msg); - } - - pr = pending_req_new(conn, msg, adapter_path, &mgr->src, &dst); - if (!pr) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - switch (cls & 0x1f00) { - case 0x0500: /* Peripheral */ - if (get_handles(pr, pnp_uuid, pnp_handle_reply) < 0) { - pending_req_free(pr); - return err_not_supported(conn, msg); - } - break; - case 0x0400: /* Fake input */ - if (get_handles(pr, headset_uuid, - headset_handle_reply) < 0) { - pending_req_free(pr); - return err_not_supported(conn, msg); - } - break; - default: - pending_req_free(pr); - return err_not_supported(conn, msg); - } - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult manager_remove_device(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct input_manager *mgr = data; - struct input_device *idev; - DBusMessage *reply; - DBusError derr; - GSList *l; - const char *path; - - dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID)) { - err_generic(conn, msg, derr.name, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } - - l = g_slist_find_custom(mgr->paths, path, (GCompareFunc) strcmp); - if (!l) - return err_does_not_exist(conn, msg, "Input doesn't exist"); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - /* Try disconnect */ - if (dbus_connection_get_object_path_data(conn, path, (void *) &idev) && idev) - disconnect(idev, (1 << HIDP_VIRTUAL_CABLE_UNPLUG)); - - del_stored_device_info(&mgr->src, &idev->dst); - - if (unregister_input_device(conn, path) < 0) { - dbus_message_unref(reply); - return err_failed(conn, msg, "D-Bus path unregistration failed"); - } - - g_free(l->data); - mgr->paths = g_slist_remove(mgr->paths, l->data); - - return send_message_and_unref(conn, reply); -} - -static DBusHandlerResult manager_list_devices(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct input_manager *mgr = data; - DBusMessageIter iter, iter_array; - DBusMessage *reply; - GSList *paths; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - 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 = mgr->paths; paths != NULL; paths = paths->next) { - const char *ppath = paths->data; - dbus_message_iter_append_basic(&iter_array, - DBUS_TYPE_STRING, &ppath); - } - - dbus_message_iter_close_container(&iter, &iter_array); - - return send_message_and_unref(conn, reply); -} - -static DBusHandlerResult manager_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *path, *iface, *member; - - path = dbus_message_get_path(msg); - iface = dbus_message_get_interface(msg); - member = dbus_message_get_member(msg); - - /* Catching fallback paths */ - if (strcmp(INPUT_PATH, path) != 0) - return err_unknown_device(conn, msg); - - /* Accept messages from the input manager interface only */ - if (strcmp(INPUT_MANAGER_INTERFACE, iface)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (strcmp(member, "ListDevices") == 0) - return manager_list_devices(conn, msg, data); - - if (strcmp(member, "CreateDevice") == 0) - return manager_create_device(conn, msg, data); - - if (strcmp(member, "RemoveDevice") == 0) - return manager_remove_device(conn, msg, data); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static void manager_unregister(DBusConnection *conn, void *data) -{ - struct input_manager *mgr = data; - - info("Unregistered manager path"); - - input_manager_free(mgr); -} - -/* Virtual table to handle manager object path hierarchy */ -static const DBusObjectPathVTable manager_table = { - .message_function = manager_message, - .unregister_function = manager_unregister, -}; - -static void stored_input(char *key, char *value, void *data) -{ - bdaddr_t *src = data; - struct input_device *idev; - const char *path; - bdaddr_t dst; - uint32_t cls; - - str2ba(key, &dst); - - if (get_class(src, &dst, &cls) < 0) - return; - - idev = input_device_new(src, &dst, cls); - - if (parse_stored_device_info(value, &idev->hidp) < 0) { - input_device_free(idev); - return; - } - - path = create_input_path(idev->major, idev->minor); - if (register_input_device(connection, idev, path) < 0) - input_device_free(idev); -} - -static int register_stored_inputs(bdaddr_t *src) -{ - char filename[PATH_MAX + 1]; - char addr[18]; - - ba2str(src, addr); - create_name(filename, PATH_MAX, STORAGEDIR, addr, "input"); - textfile_foreach(filename, stored_input, src); - - return 0; -} - -int input_dbus_init(void) -{ - struct input_manager *mgr; - bdaddr_t src; - int dev_id; - - connection = init_dbus(NULL, NULL, NULL); - if (!connection) - return -1; - - dbus_connection_set_exit_on_disconnect(connection, TRUE); - - mgr = g_new0(struct input_manager, 1); - - /* Fallback to catch invalid device path */ - if (!dbus_connection_register_fallback(connection, INPUT_PATH, - &manager_table, mgr)) { - error("D-Bus failed to register %s path", INPUT_PATH); - goto fail; - } - - info("Registered input manager path:%s", INPUT_PATH); - - /* Set the default adapter */ - bacpy(&src, BDADDR_ANY); - dev_id = hci_get_route(&src); - if (dev_id < 0) { - error("Bluetooth device not available"); - goto fail; - } - - if (hci_devba(dev_id, &src) < 0) { - error("Can't get local adapter device info"); - goto fail; - } - - bacpy(&mgr->src, &src); - /* Register well known HID devices */ - register_stored_inputs(&src); + send_message_and_unref(conn, msg); return 0; - -fail: - input_manager_free(mgr); - - return -1; -} - -void input_dbus_exit(void) -{ - dbus_connection_unregister_object_path(connection, INPUT_PATH); - - dbus_connection_unref(connection); -} - -void internal_service(const char *identifier) -{ - DBusMessage *msg, *reply; - const char *name = "Input Service Debug", *desc = ""; - - info("Registering service"); - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", "RegisterService"); - if (!msg) { - error("Can't create service register method"); - return; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &identifier, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &desc, DBUS_TYPE_INVALID); - - reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, NULL); - if (!reply) { - error("Can't register service"); - return; - } - - dbus_message_unref(msg); - dbus_message_unref(reply); - - dbus_connection_flush(connection); } diff --git a/input/device.h b/input/device.h index 957756fd..b51056e2 100644 --- a/input/device.h +++ b/input/device.h @@ -21,7 +21,8 @@ * */ -int input_dbus_init(void); -void input_dbus_exit(void); - -void internal_service(const char *identifier); +int input_device_register(DBusConnection *conn, bdaddr_t *src, bdaddr_t *dst, + struct hidp_connadd_req *hidp, const char **path); +int fake_input_register(DBusConnection *conn, bdaddr_t *src, + bdaddr_t *dst, uint8_t ch, const char **path); +int input_device_unregister(DBusConnection *conn, const char *path); diff --git a/input/error.c b/input/error.c index 55c459da..658bdeff 100644 --- a/input/error.c +++ b/input/error.c @@ -25,4 +25,68 @@ #include <config.h> #endif +#include <stdlib.h> +#include <dbus.h> + #include "error.h" + +#define INPUT_ERROR_INTERFACE "org.bluez.input.Error" + +DBusHandlerResult err_unknown_device(DBusConnection *conn, + DBusMessage *msg) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + INPUT_ERROR_INTERFACE ".UnknownDevice", + "Invalid device")); +} + +DBusHandlerResult err_generic(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, name, str)); + +} + +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + INPUT_ERROR_INTERFACE ".Failed", str)); +} + +DBusHandlerResult err_connection_failed(DBusConnection *conn, + DBusMessage *msg, const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + INPUT_ERROR_INTERFACE".ConnectionAttemptFailed", + str)); +} + +DBusHandlerResult err_already_exists(DBusConnection *conn, + DBusMessage *msg, const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + INPUT_ERROR_INTERFACE ".AlreadyExists", str)); +} + +DBusHandlerResult err_does_not_exist(DBusConnection *conn, + DBusMessage *msg, const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + INPUT_ERROR_INTERFACE ".DoesNotExist", str)); +} + +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + INPUT_ERROR_INTERFACE ".NotSupported", + "The service is not supported by the remote device")); +} + diff --git a/input/error.h b/input/error.h index e87dd676..9f767d1a 100644 --- a/input/error.h +++ b/input/error.h @@ -20,3 +20,25 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ + +DBusHandlerResult err_unknown_device(DBusConnection *conn, + DBusMessage *msg); + +DBusHandlerResult err_generic(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *str); + +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *str); + +DBusHandlerResult err_connection_failed(DBusConnection *conn, + DBusMessage *msg, const char *str); + +DBusHandlerResult err_already_exists(DBusConnection *conn, + DBusMessage *msg, const char *str); + +DBusHandlerResult err_does_not_exist(DBusConnection *conn, + DBusMessage *msg, const char *str); + +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); + + diff --git a/input/main.c b/input/main.c index 688c8a58..2a6b3b59 100644 --- a/input/main.c +++ b/input/main.c @@ -41,7 +41,6 @@ #include "manager.h" #include "server.h" -#include "device.h" static GMainLoop *main_loop; @@ -71,7 +70,7 @@ int main(int argc, char *argv[]) /* Create event loop */ main_loop = g_main_loop_new(NULL, FALSE); - if (input_dbus_init() < 0) { + if (input_init() < 0) { error("Unable to get on D-Bus"); exit(1); } @@ -85,7 +84,7 @@ int main(int argc, char *argv[]) server_stop(); - input_dbus_exit(); + input_exit(); g_main_loop_unref(main_loop); diff --git a/input/manager.c b/input/manager.c index ab45339f..143471bc 100644 --- a/input/manager.c +++ b/input/manager.c @@ -25,13 +25,867 @@ #include <config.h> #endif +#include <stdlib.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/hidp.h> + +#include <glib.h> + +#include <dbus/dbus.h> + +#include "dbus.h" +#include "logging.h" +#include "textfile.h" + +#include "error.h" +#include "storage.h" +#include "device.h" #include "manager.h" +const char *pnp_uuid = "00001200-0000-1000-8000-00805f9b34fb"; +const char *hid_uuid = "00001124-0000-1000-8000-00805f9b34fb"; +const char *headset_uuid = "00001108-0000-1000-8000-00805f9b34fb"; + +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_record_t *pnp_rec; + sdp_record_t *hid_rec; +}; + +struct input_manager { + bdaddr_t src; /* Local adapter BT address */ + GSList *paths; /* Input registered paths */ +}; + +static DBusConnection *connection = NULL; + +static struct pending_req *pending_req_new(DBusConnection *conn, + DBusMessage *msg, const char *adapter_path, + bdaddr_t *src, bdaddr_t *dst) +{ + struct pending_req *pr; + pr = g_try_new0(struct pending_req, 1); + if (!pr) + return NULL; + + 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_rec) + sdp_record_free(pr->pnp_rec); + if (pr->hid_rec) + sdp_record_free(pr->hid_rec); + g_free(pr); +} + +#if 0 +static int path_bdaddr_cmp(const char *path, const bdaddr_t *bdaddr) +{ + struct input_device *idev; + + if (!dbus_connection_get_object_path_data(connection, path, + (void *) &idev)) + return -1; + + if (!idev) + return -1; + + return bacmp(&idev->dst, bdaddr); +} +#endif +static int get_record(struct pending_req *pr, uint32_t handle, + DBusPendingCallNotifyFunction cb) +{ + DBusMessage *msg; + DBusPendingCall *pending; + char addr[18]; + const char *paddr = addr; + + msg = dbus_message_new_method_call("org.bluez", pr->adapter_path, + "org.bluez.Adapter", "GetRemoteServiceRecord"); + if (!msg) + return -1; + + ba2str(&pr->dst, addr); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &paddr, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(pr->conn, msg, &pending, -1) == FALSE) { + error("Can't send D-Bus message."); + return -1; + } + + dbus_pending_call_set_notify(pending, cb, pr, NULL); + dbus_message_unref(msg); + + return 0; +} + +static int get_handles(struct pending_req *pr, const char *uuid, + DBusPendingCallNotifyFunction cb) +{ + DBusMessage *msg; + DBusPendingCall *pending; + char addr[18]; + const char *paddr = addr; + + msg = dbus_message_new_method_call("org.bluez", pr->adapter_path, + "org.bluez.Adapter", "GetRemoteServiceHandles"); + if (!msg) + return -1; + + ba2str(&pr->dst, addr); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &paddr, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(pr->conn, msg, &pending, -1) == FALSE) { + error("Can't send D-Bus message."); + return -1; + } + + dbus_pending_call_set_notify(pending, cb, pr, NULL); + dbus_message_unref(msg); + + return 0; +} + +static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req) +{ + sdp_data_t *pdlist, *pdlist2; + uint8_t attr_val; + + pdlist = sdp_data_get(rec, 0x0101); + pdlist2 = sdp_data_get(rec, 0x0102); + if (pdlist) { + if (pdlist2) { + if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) { + strncpy(req->name, pdlist2->val.str, 127); + strcat(req->name, " "); + } + strncat(req->name, pdlist->val.str, 127 - strlen(req->name)); + } else + strncpy(req->name, pdlist->val.str, 127); + } else { + pdlist2 = sdp_data_get(rec, 0x0100); + if (pdlist2) + strncpy(req->name, pdlist2->val.str, 127); + } + + pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION); + req->parser = pdlist ? pdlist->val.uint16 : 0x0100; + + pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); + req->subclass = pdlist ? pdlist->val.uint8 : 0; + + pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); + req->country = pdlist ? pdlist->val.uint8 : 0; + + pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE); + attr_val = pdlist ? pdlist->val.uint8 : 0; + if (attr_val) + req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG); + + pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); + attr_val = pdlist ? pdlist->val.uint8 : 0; + if (attr_val) + req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); + + pdlist = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); + if (pdlist) { + pdlist = pdlist->val.dataseq; + pdlist = pdlist->val.dataseq; + pdlist = pdlist->next; + + req->rd_data = g_try_malloc0(pdlist->unitSize); + if (req->rd_data) { + memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize); + req->rd_size = pdlist->unitSize; + } + } +} + +static void extract_pnp_record(sdp_record_t *rec, struct hidp_connadd_req *req) +{ + sdp_data_t *pdlist; + + pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); + req->vendor = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); + req->product = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, SDP_ATTR_VERSION); + req->version = pdlist ? pdlist->val.uint16 : 0x0000; +} + +static void hid_record_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusMessage *pr_reply; + struct input_manager *mgr; + struct pending_req *pr = data; + struct hidp_connadd_req hidp; + DBusError derr; + uint8_t *rec_bin; + const char *path; + int len, scanned; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + err_generic(pr->conn, pr->msg, derr.name, derr.message); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len, + DBUS_TYPE_INVALID)) { + err_not_supported(pr->conn, pr->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + err_not_supported(pr->conn, pr->msg); + error("Invalid HID service record length"); + goto fail; + } + + pr->hid_rec = sdp_extract_pdu(rec_bin, &scanned); + if (!pr->hid_rec) { + err_not_supported(pr->conn, pr->msg); + goto fail; + } + + memset(&hidp, 0, sizeof(struct hidp_connadd_req)); + extract_hid_record(pr->hid_rec, &hidp); + if (pr->pnp_rec) + extract_pnp_record(pr->pnp_rec, &hidp); + + store_device_info(&pr->src, &pr->dst, &hidp); + + if (input_device_register(pr->conn, &pr->src, + &pr->dst, &hidp, &path) < 0) { + err_failed(pr->conn, pr->msg, "D-Bus path registration failed"); + goto fail; + } + + dbus_connection_get_object_path_data(pr->conn, INPUT_PATH, (void *) &mgr); + mgr->paths = g_slist_append(mgr->paths, g_strdup(path)); + + pr_reply = dbus_message_new_method_return(pr->msg); + dbus_message_append_args(pr_reply, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + send_message_and_unref(pr->conn, pr_reply); +fail: + dbus_error_free(&derr); + pending_req_free(pr); + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void hid_handle_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct pending_req *pr = data; + uint32_t *phandle; + DBusError derr; + int len; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + err_generic(pr->conn, pr->msg, derr.name, derr.message); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, &len, + DBUS_TYPE_INVALID)) { + err_not_supported(pr->conn, pr->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + err_not_supported(pr->conn, pr->msg); + error("HID record handle not found"); + goto fail; + } + + if (get_record(pr, *phandle, hid_record_reply) < 0) { + err_not_supported(pr->conn, pr->msg); + error("HID service attribute request failed"); + goto fail; + } else { + /* Wait record reply */ + goto done; + } +fail: + dbus_error_free(&derr); + pending_req_free(pr); +done: + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void pnp_record_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct pending_req *pr = data; + DBusError derr; + uint8_t *rec_bin; + int len, scanned; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + err_generic(pr->conn, pr->msg, derr.name, derr.message); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len, + DBUS_TYPE_INVALID)) { + err_not_supported(pr->conn, pr->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + err_not_supported(pr->conn, pr->msg); + error("Invalid PnP service record length"); + goto fail; + } + + pr->pnp_rec = sdp_extract_pdu(rec_bin, &scanned); + if (get_handles(pr, hid_uuid, hid_handle_reply) < 0) { + err_not_supported(pr->conn, pr->msg); + error("HID service search request failed"); + goto fail; + } else { + /* Wait handle reply */ + goto done; + } + +fail: + dbus_error_free(&derr); + pending_req_free(pr); +done: + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void pnp_handle_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct pending_req *pr = data; + DBusError derr; + uint32_t *phandle; + int len; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + err_generic(pr->conn, pr->msg, derr.name, derr.message); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, &len, + DBUS_TYPE_INVALID)) { + err_not_supported(pr->conn, pr->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + /* PnP is optional: Ignore it and request the HID handle */ + if (get_handles(pr, hid_uuid, hid_handle_reply) < 0) { + err_not_supported(pr->conn, pr->msg); + error("HID service search request failed"); + goto fail; + } + } else { + /* Request PnP record */ + if (get_record(pr, *phandle, pnp_record_reply) < 0) { + err_not_supported(pr->conn, pr->msg); + error("PnP service attribute request failed"); + goto fail; + } + } + + /* Wait HID handle reply or PnP record reply */ + goto done; + +fail: + dbus_error_free(&derr); + pending_req_free(pr); +done: + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void headset_record_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusMessage *pr_reply; + DBusError derr; + struct input_manager *mgr; + struct pending_req *pr = data; + uint8_t *rec_bin; + sdp_record_t *rec; + sdp_list_t *protos; + const char *path; + int len, scanned; + uint8_t ch; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + err_generic(pr->conn, pr->msg, derr.name, derr.message); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &rec_bin, &len, + DBUS_TYPE_INVALID)) { + err_not_supported(pr->conn, pr->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + err_not_supported(pr->conn, pr->msg); + error("Invalid headset service record length"); + goto fail; + } + + rec = sdp_extract_pdu(rec_bin, &scanned); + if (!rec) { + err_not_supported(pr->conn, pr->msg); + goto fail; + } + + if (sdp_get_access_protos(rec, &protos) < 0) { + err_not_supported(pr->conn, pr->msg); + goto fail; + } + + 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) { + err_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); + err_failed(pr->conn, pr->msg, "Path registration failed"); + goto fail; + } + + dbus_connection_get_object_path_data(pr->conn, INPUT_PATH, (void *) &mgr); + mgr->paths = g_slist_append(mgr->paths, g_strdup(path)); + + pr_reply = dbus_message_new_method_return(pr->msg); + dbus_message_append_args(pr_reply, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + send_message_and_unref(pr->conn, pr_reply); +fail: + dbus_error_free(&derr); + pending_req_free(pr); + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static void headset_handle_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct pending_req *pr = data; + DBusError derr; + uint32_t *phandle; + int len; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + err_generic(pr->conn, pr->msg, derr.name, derr.message); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, &len, + DBUS_TYPE_INVALID)) { + err_not_supported(pr->conn, pr->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + err_not_supported(pr->conn, pr->msg); + error("Headset record handle not found"); + goto fail; + } + + if (get_record(pr, *phandle, headset_record_reply) < 0) { + err_not_supported(pr->conn, pr->msg); + error("Headset service attribute request failed"); + goto fail; + } else { + /* Wait record reply */ + goto done; + } +fail: + dbus_error_free(&derr); + pending_req_free(pr); +done: + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + +static DBusHandlerResult manager_create_device(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct input_manager *mgr = data; + struct pending_req *pr; + DBusError derr; + char adapter[18], adapter_path[32]; + const char *addr; + bdaddr_t dst; + uint32_t cls = 0; + int dev_id; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &addr, + DBUS_TYPE_INVALID)) { + err_generic(conn, msg, derr.name, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + str2ba(addr, &dst); +#if 0 + /* FIXME */ + l = g_slist_find_custom(mgr->paths, &dst, + (GCompareFunc) path_bdaddr_cmp); + if (l) + return err_already_exists(conn, msg, "Input Already exists"); +#endif + /* FIXME: Move the following code to pending_req_new() */ + ba2str(&mgr->src, adapter); + dev_id = hci_devid(adapter); + snprintf(adapter_path, 32, "/org/bluez/hci%d", dev_id); + + if (read_device_class(&mgr->src, &dst, &cls) < 0) { + error("Device class not available"); + return err_not_supported(conn, msg); + } + + pr = pending_req_new(conn, msg, adapter_path, &mgr->src, &dst); + if (!pr) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + switch (cls & 0x1f00) { + case 0x0500: /* Peripheral */ + if (get_handles(pr, pnp_uuid, pnp_handle_reply) < 0) { + pending_req_free(pr); + return err_not_supported(conn, msg); + } + break; + case 0x0400: /* Fake input */ + if (get_handles(pr, headset_uuid, + headset_handle_reply) < 0) { + pending_req_free(pr); + return err_not_supported(conn, msg); + } + break; + default: + pending_req_free(pr); + return err_not_supported(conn, msg); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult manager_remove_device(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct input_manager *mgr = data; + DBusMessage *reply; + DBusError derr; + GSList *l; + const char *path; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID)) { + err_generic(conn, msg, derr.name, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + l = g_slist_find_custom(mgr->paths, path, (GCompareFunc) strcmp); + if (!l) + return err_does_not_exist(conn, msg, "Input doesn't exist"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + g_free(l->data); + mgr->paths = g_slist_remove(mgr->paths, l->data); + + /* FIXME: how retrieve the destination address */ + //del_stored_device_info(&mgr->src, &idev->dst); + + if (input_device_unregister(conn, path) < 0) { + dbus_message_unref(reply); + return err_failed(conn, msg, "D-Bus path unregistration failed"); + } + + return send_message_and_unref(conn, reply); +} + +static DBusHandlerResult manager_list_devices(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct input_manager *mgr = data; + DBusMessageIter iter, iter_array; + DBusMessage *reply; + GSList *paths; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + 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 = mgr->paths; paths != NULL; paths = paths->next) { + const char *ppath = paths->data; + dbus_message_iter_append_basic(&iter_array, + DBUS_TYPE_STRING, &ppath); + } + + dbus_message_iter_close_container(&iter, &iter_array); + + return send_message_and_unref(conn, reply); +} +static DBusHandlerResult manager_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const char *path, *iface, *member; + + path = dbus_message_get_path(msg); + iface = dbus_message_get_interface(msg); + member = dbus_message_get_member(msg); + + /* Catching fallback paths */ + if (strcmp(INPUT_PATH, path) != 0) + return err_unknown_device(conn, msg); + + /* Accept messages from the input manager interface only */ + if (strcmp(INPUT_MANAGER_INTERFACE, iface)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strcmp(member, "ListDevices") == 0) + return manager_list_devices(conn, msg, data); + + if (strcmp(member, "CreateDevice") == 0) + return manager_create_device(conn, msg, data); + + if (strcmp(member, "RemoveDevice") == 0) + return manager_remove_device(conn, msg, data); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void input_manager_free(struct input_manager *mgr) +{ + if (!mgr) + return; + + if (mgr->paths) { + g_slist_foreach(mgr->paths, (GFunc) free, NULL); + g_slist_free(mgr->paths); + } + + g_free(mgr); +} + +static void manager_unregister(DBusConnection *conn, void *data) +{ + struct input_manager *mgr = data; + + info("Unregistered manager path"); + + input_manager_free(mgr); +} + +/* Virtual table to handle manager object path hierarchy */ +static const DBusObjectPathVTable manager_table = { + .message_function = manager_message, + .unregister_function = manager_unregister, +}; + +/* + * Stored inputs registration functions + */ + +static void stored_input(char *key, char *value, void *data) +{ + struct input_manager *mgr = data; + const char *path; + struct hidp_connadd_req hidp; + bdaddr_t dst; + + str2ba(key, &dst); + + memset(&hidp, 0, sizeof(struct hidp_connadd_req)); + if (parse_stored_device_info(value, &hidp) < 0) { + return; + } + + if (input_device_register(connection, &mgr->src, &dst, &hidp, &path) < 0) + return; + + mgr->paths = g_slist_append(mgr->paths, g_strdup(path)); +} + +static int register_stored_inputs(struct input_manager *mgr) +{ + char filename[PATH_MAX + 1]; + char addr[18]; + + ba2str(&mgr->src, addr); + create_name(filename, PATH_MAX, STORAGEDIR, addr, "input"); + textfile_foreach(filename, stored_input, mgr); + + return 0; +} + int input_init(void) { + struct input_manager *mgr; + bdaddr_t src; + int dev_id; + + connection = init_dbus(NULL, NULL, NULL); + if (!connection) + return -1; + + dbus_connection_set_exit_on_disconnect(connection, TRUE); + + mgr = g_new0(struct input_manager, 1); + + /* Fallback to catch invalid device path */ + if (!dbus_connection_register_fallback(connection, INPUT_PATH, + &manager_table, mgr)) { + error("D-Bus failed to register %s path", INPUT_PATH); + goto fail; + } + + info("Registered input manager path:%s", INPUT_PATH); + + /* Set the default adapter */ + bacpy(&src, BDADDR_ANY); + dev_id = hci_get_route(&src); + if (dev_id < 0) { + error("Bluetooth device not available"); + goto fail; + } + + if (hci_devba(dev_id, &src) < 0) { + error("Can't get local adapter device info"); + goto fail; + } + + bacpy(&mgr->src, &src); + /* Register well known HID devices */ + register_stored_inputs(mgr); + return 0; + +fail: + input_manager_free(mgr); + + return -1; } void input_exit(void) { + dbus_connection_unregister_object_path(connection, INPUT_PATH); + + dbus_connection_unref(connection); +} + +void internal_service(const char *identifier) +{ + DBusMessage *msg, *reply; + const char *name = "Input Service Debug", *desc = ""; + + info("Registering service"); + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", "RegisterService"); + if (!msg) { + error("Can't create service register method"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &identifier, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &desc, DBUS_TYPE_INVALID); + + reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, NULL); + if (!reply) { + error("Can't register service"); + return; + } + + dbus_message_unref(msg); + dbus_message_unref(reply); + + dbus_connection_flush(connection); } diff --git a/input/manager.h b/input/manager.h index 98e6d2d8..9eee22aa 100644 --- a/input/manager.h +++ b/input/manager.h @@ -21,5 +21,9 @@ * */ +#define INPUT_PATH "/org/bluez/input" +#define INPUT_MANAGER_INTERFACE "org.bluez.input.Manager" + int input_init(void); void input_exit(void); +void internal_service(const char *identifier); diff --git a/input/storage.c b/input/storage.c index fd8d4bcb..3793a275 100644 --- a/input/storage.c +++ b/input/storage.c @@ -44,8 +44,8 @@ #include <glib.h> -#include "textfile.h" #include "logging.h" +#include "textfile.h" #include "storage.h" @@ -171,14 +171,14 @@ int store_device_info(bdaddr_t *src, bdaddr_t *dst, struct hidp_connadd_req *req return err; } -int read_device_name(bdaddr_t *local, bdaddr_t *peer, char **name) +int read_device_name(bdaddr_t *src, bdaddr_t *dst, char **name) { char filename[PATH_MAX + 1], addr[18], *str; int len; - create_filename(filename, PATH_MAX, local, "names"); + create_filename(filename, PATH_MAX, src, "names"); - ba2str(peer, addr); + ba2str(dst, addr); str = textfile_get(filename, addr); if (!str) return -ENOENT; @@ -202,6 +202,29 @@ int read_device_name(bdaddr_t *local, bdaddr_t *peer, char **name) return 0; } +int read_device_class(bdaddr_t *src, bdaddr_t *dst, uint32_t *cls) +{ + char filename[PATH_MAX + 1], *str; + char addr[18]; + + ba2str(src, addr); + create_name(filename, PATH_MAX, STORAGEDIR, addr, "classes"); + + ba2str(dst, addr); + str = textfile_get(filename, addr); + if (!str) + return -ENOENT; + + if (sscanf(str, "%x", cls) != 1) { + g_free(str); + return -ENOENT; + } + + g_free(str); + + return 0; +} + int encrypt_link(bdaddr_t *src, bdaddr_t *dst) { char filename[PATH_MAX + 1]; diff --git a/input/storage.h b/input/storage.h index 60df915d..81ad2599 100644 --- a/input/storage.h +++ b/input/storage.h @@ -32,6 +32,7 @@ int store_device_info(bdaddr_t *src, bdaddr_t *dst, int parse_stored_device_info(const char *str, struct hidp_connadd_req *req); -int read_device_name(bdaddr_t *local, bdaddr_t *peer, char **name); +int read_device_name(bdaddr_t *src, bdaddr_t *dst, char **name); +int read_device_class(bdaddr_t *src, bdaddr_t *dst, uint32_t *cls); int encrypt_link(bdaddr_t *src, bdaddr_t *dst); |