Apple Magic Mouse
Dirk Engling
erdgeist at erdgeist.org
Mon Sep 14 00:03:48 UTC 2015
On 12.09.15 19:53, Maksim Yevmenkin wrote:
> i think it should be possible to teach bthidd to make another sdp
> request to pull device id sdp record (if available). if apple mouse
> answers it then it should very easy to identify it and enable all the
> features.
Apples magic mouse indeed answers the SDP request for its Device ID
Service Record.
Find attached a first patch that adds a vendor, product and version
member in the bthid_session object that is filled after both connections
for the session were attached.
This is done in a new function session_get_devid that issues and handles
the SDP requests.
There's also a new hid_initialise function, that gets a chance to take a
look at those new members and initialise devices based on those information.
Caveats:
Only the first DevIDService record is parsed without regard for the
PrimaryRecord flag. This is not necessarily correct for devices that
expose multiple services. For most HID it still should be good enough
and checking for multiple records and writing code to handle devices
with no PrimaryRecord is way too complex for what we want to achieve.
The libsdp actually issues a blocking write and, worse: a blocking read
on the bluetooth connection, potentially blocking the whole bthidd until
the read times out. However, since this happens after both channels were
successfully established, chances are pretty good that the device will
not die just when we send the request.
Depending on lingual preferences, you might want to rename initialise to
initialize.
Comments on the patch are appreciated.
I will now go on and blatantly rip off Iain Hibberts code from the
NetBSD driver to make use of what the mouse sends me.
Regards,
erdgeist
-------------- next part --------------
diff -ur bthidd/Makefile /usr/src/usr.sbin/bluetooth/bthidd/Makefile
--- bthidd/Makefile 2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/Makefile 2015-09-14 01:23:58.745842317 +0200
@@ -11,7 +11,7 @@
DEBUG_FLAGS= -g
DPADD= ${LIBBLUETOOTH} ${LIBUSBHID}
-LDADD= -lbluetooth -lusbhid
+LDADD= -lbluetooth -lusbhid -lsdp
NO_WMISSING_VARIABLE_DECLARATIONS=
diff -ur bthidd/bthidd.h /usr/src/usr.sbin/bluetooth/bthidd/bthidd.h
--- bthidd/bthidd.h 2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/bthidd.h 2015-09-14 01:20:49.863880887 +0200
@@ -61,6 +61,9 @@
int32_t intr; /* interrupt channel */
int32_t vkbd; /* virual keyboard */
bdaddr_t bdaddr;/* remote bdaddr */
+ uint16_t vendor;/* remote vendor id */
+ uint16_t product;/*remote product id */
+ uint16_t version;/*remote version id */
uint16_t state; /* session state */
#define CLOSED 0
#define W4CTRL 1
@@ -84,8 +87,10 @@
bthid_session_p session_open (bthid_server_p srv, hid_device_p const d);
bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
bthid_session_p session_by_fd (bthid_server_p srv, int32_t fd);
+void session_get_devid(bthid_session_p s);
void session_close (bthid_session_p s);
+void hid_initialise (bthid_session_p s);
int32_t hid_control (bthid_session_p s, uint8_t *data, int32_t len);
int32_t hid_interrupt (bthid_session_p s, uint8_t *data, int32_t len);
diff -ur bthidd/hid.c /usr/src/usr.sbin/bluetooth/bthidd/hid.c
--- bthidd/hid.c 2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/hid.c 2015-09-14 01:43:48.644754678 +0200
@@ -49,6 +49,19 @@
#include "kbd.h"
/*
+ * Probe for per-device initialisation
+ */
+void
+hid_initialise(bthid_session_p s)
+{
+ /* Magic report to enable trackpad on Apple's Magic Mouse
+ static uint8_t rep[] = { 0x53, 0xd7, 0x01 };
+ if(s->vendor == 0x5ac && s->product == 0x30d )
+ write(s->ctrl, rep, 3 );
+ */
+}
+
+/*
* Process data from control channel
*/
diff -ur bthidd/server.c /usr/src/usr.sbin/bluetooth/bthidd/server.c
--- bthidd/server.c 2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/server.c 2015-09-14 01:44:12.843765049 +0200
@@ -286,6 +286,12 @@
srv->maxfd = s->vkbd;
}
+ /* Get VendorID and ProductID after both channels are established */
+ if (s->state == OPEN) {
+ session_get_devid(s);
+ hid_initialise(s);
+ }
+
return (0);
}
diff -ur bthidd/session.c /usr/src/usr.sbin/bluetooth/bthidd/session.c
--- bthidd/session.c 2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/session.c 2015-09-14 01:50:19.862726418 +0200
@@ -36,6 +36,7 @@
#include <bluetooth.h>
#include <errno.h>
#include <fcntl.h>
+#include <sdp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -61,7 +62,7 @@
if ((s = (bthid_session_p) malloc(sizeof(*s))) == NULL)
return (NULL);
- s->srv = srv;
+ s->srv = srv;
memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr));
s->ctrl = -1;
s->intr = -1;
@@ -80,6 +81,7 @@
s->vkbd = -1;
s->state = CLOSED;
+ s->vendor = s->product = s->version = 0;
s->keys1 = bit_alloc(kbd_maxkey());
if (s->keys1 == NULL) {
@@ -138,6 +140,89 @@
}
/*
+ * Get Device ID Service Record for session
+
+ Hard coded attibute IDs taken from the
+ DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
+ */
+
+#define SDP_DEVICE_ID_SERVICE_ATTR_VENDORID 0x0201
+#define SDP_DEVICE_ID_SERVICE_ATTR_PRODUCTID 0x0202
+#define SDP_DEVICE_ID_SERVICE_ATTR_VERSION 0x0203
+#define SDP_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
+ SDP_DEVICE_ID_SERVICE_ATTR_VENDORID, SDP_DEVICE_ID_SERVICE_ATTR_VERSION )
+
+void
+session_get_devid(bthid_session_p s)
+{
+ sdp_attr_t val[3];
+ uint8_t buf[16];
+ uint16_t devid_servrec_uuid = SDP_SERVICE_CLASS_PNP_INFORMATION;
+ uint32_t devid_servrec_attr_range = SDP_DEVICE_ID_RANGE;
+ uint32_t type;
+ void *xs;
+ uint8_t *v;
+ int i;
+
+ assert(s != NULL);
+ assert(s->state == OPEN);
+
+ xs = sdp_open(NG_HCI_BDADDR_ANY, &s->bdaddr);
+ if (!xs)
+ return;
+
+ /* Initialise return array */
+ for (i=0; i<3; ++i) {
+ val[i].flags = SDP_ATTR_INVALID;
+ val[i].attr = 0;
+ val[i].value = buf + i*4;
+ val[i].vlen = 4; /* Max size should be 3 */
+ }
+
+ /* Getting only the first set of attributes, assuming the
+ primary record to come first. TODO. */
+ if (sdp_search(xs, 1, &devid_servrec_uuid,
+ 1, &devid_servrec_attr_range,
+ 3, val) != 0 ) {
+ sdp_close(xs);
+ return;
+ }
+
+
+ /* If search is successful, scan through return vals */
+ for (i=0; i<3; ++i) {
+ if (val[i].flags == SDP_ATTR_INVALID )
+ continue;
+
+ /* Expecting tag + uint16_t on all 3 attributes */
+ if (val[i].vlen != 3)
+ continue;
+
+ /* Make sure, we're reading a uint16_t */
+ v = val[i].value;
+ SDP_GET8(type, v);
+ if (type != SDP_DATA_UINT16 )
+ continue;
+
+ switch (val[i].attr) {
+ case SDP_DEVICE_ID_SERVICE_ATTR_VENDORID:
+ SDP_GET16(s->vendor, v);
+ break;
+ case SDP_DEVICE_ID_SERVICE_ATTR_PRODUCTID:
+ SDP_GET16(s->product, v);
+ break;
+ case SDP_DEVICE_ID_SERVICE_ATTR_VERSION:
+ SDP_GET16(s->version, v);
+ break;
+ default:
+ break;
+ }
+ }
+
+ sdp_close(xs);
+}
+
+/*
* Close session
*/
More information about the freebsd-bluetooth
mailing list