diff options
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); | 
