[RFC 2/2] profiles/input: Add support for SDP fallback for DualShock 3 clones

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Some DualShock 3 clones were reported to not provide any SDP record.
In such case provide fallback SDP record (based on genuine DS3 record)
and retry setting input device channels before disconnecting.
---
 profiles/input/server.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 184 insertions(+), 7 deletions(-)

diff --git a/profiles/input/server.c b/profiles/input/server.c
index eb3fcf8..4b7b1e1 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -35,6 +35,7 @@
 #include "lib/bluetooth.h"
 #include "lib/sdp.h"
 #include "lib/uuid.h"
+#include "lib/sdp_lib.h"
 
 #include "src/log.h"
 #include "src/uuid-helper.h"
@@ -72,29 +73,205 @@ struct sixaxis_data {
 	uint16_t psm;
 };
 
+static sdp_record_t *get_sixaxis_record(struct btd_device *device)
+{
+	sdp_record_t *record;
+	uint16_t vid, pid, hid_release, hid_parser, version, timeout;
+	uint8_t sdp_disable, battery, remote_wakeup, norm_connect, boot_device;
+	uint8_t subclass, country, virtual_cable, reconnect;
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[3];
+	sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+	uint8_t dtd = SDP_UINT16;
+	uint8_t dtd2 = SDP_UINT8;
+	uint8_t dtd_data = SDP_TEXT_STR8;
+	void *dtds[2];
+	void *values[2];
+	void *dtds2[2];
+	void *values2[2];
+	int leng[2];
+	uint8_t hid_spec_type = 0x22;
+	uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+	static const uint16_t ctrl = 0x11;
+	static const uint16_t intr = 0x13;
+	uint8_t hid_spec[] = {
+		0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, 0x85, 0x01,
+		0x75, 0x08, 0x95, 0x01, 0x15, 0x00, 0x26, 0xff, 0x00, 0x81,
+		0x03, 0x75, 0x01, 0x95, 0x13, 0x15, 0x00, 0x25, 0x01, 0x35,
+		0x00, 0x45, 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81,
+		0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, 0x81, 0x03,
+		0x15, 0x00, 0x26, 0xff, 0x00, 0x05, 0x01, 0x09, 0x01, 0xa1,
+		0x00, 0x75, 0x08, 0x95, 0x04, 0x35, 0x00, 0x46, 0xff, 0x00,
+		0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02,
+		0xc0, 0x05, 0x01, 0x75, 0x08, 0x95, 0x27, 0x09, 0x01, 0x81,
+		0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0x91, 0x02, 0x75,
+		0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02,
+		0x85, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02,
+		0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, 0x30, 0x09,
+		0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0xef, 0x75, 0x08,
+		0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xc0, 0x00
+	};
+
+	vid = btd_device_get_vendor(device);
+	pid = btd_device_get_product(device);
+
+	/* only for DualShock 3 clones */
+	if (vid != 0x054c || pid != 0x0268)
+		return NULL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_add_lang_attr(record);
+
+	sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &hidkb_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HID_PROFILE_ID);
+	profile.version = 0x0100;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	/* protocols */
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	/* additional protocols */
+	proto[1] = sdp_list_append(0, &l2cap_uuid);
+	psm = sdp_data_alloc(SDP_UINT16, &intr);
+	proto[1] = sdp_list_append(proto[1], psm);
+	apseq = sdp_list_append(0, proto[1]);
+
+	sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+	proto[2] = sdp_list_append(0, &hidp_uuid);
+	apseq = sdp_list_append(apseq, proto[2]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_add_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Wireless Controller",
+						"Sony Computer Entertainment",
+						"Wireless Controller");
+
+	hid_release = 0x0100;
+	sdp_attr_add_new(record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, SDP_UINT16,
+								&hid_release);
+
+	hid_parser = 0x0100;
+	sdp_attr_add_new(record, SDP_ATTR_HID_PARSER_VERSION, SDP_UINT16,
+								&hid_parser);
+
+	subclass = 0x00;
+	sdp_attr_add_new(record, SDP_ATTR_HID_DEVICE_SUBCLASS, SDP_UINT8,
+								&subclass);
+
+	country = 0x21;
+	sdp_attr_add_new(record, SDP_ATTR_HID_COUNTRY_CODE, SDP_UINT8,
+								&country);
+
+	virtual_cable = 0x01;
+	sdp_attr_add_new(record, SDP_ATTR_HID_VIRTUAL_CABLE, SDP_BOOL,
+								&virtual_cable);
+
+	reconnect = 0x01;
+	sdp_attr_add_new(record, SDP_ATTR_HID_RECONNECT_INITIATE, SDP_BOOL,
+								&reconnect);
+
+	dtds[0] = &dtd2;
+	values[0] = &hid_spec_type;
+	dtds[1] = &dtd_data;
+	values[1] = hid_spec;
+	leng[0] = 0;
+	leng[1] = sizeof(hid_spec);
+	hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+	hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+	sdp_attr_add(record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+	dtds2[0] = &dtd;
+	values2[0] = &hid_attr_lang[0];
+	dtds2[1] = &dtd;
+	values2[1] = &hid_attr_lang[1];
+	lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+	lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+	sdp_attr_add(record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+	sdp_disable = 0x00;
+	sdp_attr_add_new(record, SDP_ATTR_HID_SDP_DISABLE, SDP_BOOL,
+								&sdp_disable);
+
+	battery = 0x01;
+	sdp_attr_add_new(record, SDP_ATTR_HID_BATTERY_POWER, SDP_BOOL,
+								&battery);
+
+	remote_wakeup = 0x01;
+	sdp_attr_add_new(record, SDP_ATTR_HID_REMOTE_WAKEUP, SDP_BOOL,
+								&remote_wakeup);
+
+	version = 0x0100;
+	sdp_attr_add_new(record, SDP_ATTR_HID_PROFILE_VERSION, SDP_UINT16,
+								&version);
+
+	timeout = 0x3e80;
+	sdp_attr_add_new(record, SDP_ATTR_HID_SUPERVISION_TIMEOUT, SDP_UINT16,
+								&timeout);
+
+	norm_connect = 0x00;
+	sdp_attr_add_new(record, SDP_ATTR_HID_NORMALLY_CONNECTABLE, SDP_BOOL,
+								&norm_connect);
+
+	boot_device = 0x00;
+	sdp_attr_add_new(record, SDP_ATTR_HID_BOOT_DEVICE, SDP_BOOL,
+								&boot_device);
+
+	return record;
+}
+
 static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data)
 {
+	const bdaddr_t *src = btd_adapter_get_address(device_get_adapter(dev));
 	struct sixaxis_data *data = user_data;
-	const bdaddr_t *src;
 
 	DBG("err %d (%s)", err, strerror(-err));
 
 	if (err < 0)
-		goto fail;
-
-	src = btd_adapter_get_address(device_get_adapter(dev));
+		goto fallback;
 
 	if (input_device_set_channel(src, device_get_address(dev), data->psm,
 								data->chan) < 0)
-		goto fail;
+		goto fallback;
 
 	g_io_channel_unref(data->chan);
 	g_free(data);
 
 	return;
 
-fail:
-	g_io_channel_shutdown(data->chan, TRUE, NULL);
+fallback:
+	DBG("SDP search failed, attempting fallback");
+
+	btd_device_set_record(dev, HID_UUID, get_sixaxis_record(dev));
+
+	if (input_device_set_channel(src, device_get_address(dev),
+						data->psm, data->chan) < 0)
+		g_io_channel_shutdown(data->chan, TRUE, NULL);
+
 	g_io_channel_unref(data->chan);
 	g_free(data);
 }
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux