On 14.09.15 19:09, Iain Hibbert wrote:

> My advice is, to put that code in bthidcontrol, which already queries the 
> device, and add product-id and vendor-id fields to the bthidd.conf file.

Thanks for the advice, find attach an implementation. The only problem I
found is that the bthidd.conf format is not backward compatible, the
three additional parameters will fail on old versions of bthidcontrol
and bthidd.

Also contained in the patch is successful probing for my Magic Mouse and
extraction of basic mouse features when track pad events are sent by the
mouse. Next up: translation of finger tracking to z-scroll events.

Looking forward to your reviews.

  erdgeist
diff -rwu bthidcontrol/sdp.c /usr/src/usr.sbin/bluetooth/bthidcontrol/sdp.c
--- bthidcontrol/sdp.c  2015-08-12 16:21:37.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidcontrol/sdp.c      2015-09-15 
04:14:53.413719623 +0200
@@ -46,7 +46,20 @@
 static int32_t hid_sdp_parse_hid_descriptor            (sdp_attr_p a);
 static int32_t hid_sdp_parse_boolean                   (sdp_attr_p a);
 
+/*
+ * 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 )
+
 static uint16_t                service = 
SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
+static uint16_t                service_devid = 
SDP_SERVICE_CLASS_PNP_INFORMATION;
+static uint32_t        attrs_devid   = SDP_DEVICE_ID_RANGE;
 
 static uint32_t                attrs[] = {
 SDP_ATTR_RANGE(        SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
@@ -84,27 +97,34 @@
        return (((e) == 0)? 0 : -1);    \
 }
 
+static void
+hid_init_return_values() {
+       int i;
+       for (i = 0; i < nvalues; i ++) {
+               values[i].flags = SDP_ATTR_INVALID;
+               values[i].attr = 0;
+               values[i].vlen = sizeof(buffer[i]);
+               values[i].value = buffer[i];
+       }
+}
+
 static int32_t
 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
 {
        void    *ss = NULL;
-       uint8_t *hid_descriptor = NULL;
+       uint8_t *hid_descriptor = NULL, *v;
        int32_t  i, control_psm = -1, interrupt_psm = -1,
                 reconnect_initiate = -1,
                 normally_connectable = 0, battery_power = 0,
-                hid_descriptor_length = -1;
+                hid_descriptor_length = -1, type;
+       int16_t  vendor_id, product_id, version;
 
        if (local == NULL)
                local = NG_HCI_BDADDR_ANY;
        if (hd == NULL)
                hid_sdp_query_exit(EINVAL);
 
-       for (i = 0; i < nvalues; i ++) {
-               values[i].flags = SDP_ATTR_INVALID;
-               values[i].attr = 0;
-               values[i].vlen = sizeof(buffer[i]);
-               values[i].value = buffer[i];
-       }
+       hid_init_return_values();
 
        if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
                hid_sdp_query_exit(ENOMEM);
@@ -113,9 +133,6 @@
        if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
                 hid_sdp_query_exit(sdp_error(ss));
 
-        sdp_close(ss);
-        ss = NULL;
-
        for (i = 0; i < nvalues; i ++) {
                if (values[i].flags != SDP_ATTR_OK)
                        continue;
@@ -150,11 +167,51 @@
                }
        }
 
+       hid_init_return_values();
+
+       if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) 
!= 0)
+                hid_sdp_query_exit(sdp_error(ss));
+
+        sdp_close(ss);
+        ss = NULL;
+
+       /* If search is successful, scan through return vals */
+       for (i = 0; i < 3; i ++ ) {
+               if (values[i].flags == SDP_ATTR_INVALID )
+                       continue;
+
+               /* Expecting tag + uint16_t on all 3 attributes */
+               if (values[i].vlen != 3)
+                       continue;
+
+               /* Make sure, we're reading a uint16_t */
+               v = values[i].value;
+               SDP_GET8(type, v);
+               if (type != SDP_DATA_UINT16 )
+                       continue;
+
+               switch (values[i].attr) {
+                       case SDP_DEVICE_ID_SERVICE_ATTR_VENDORID:
+                               SDP_GET16(vendor_id, v);
+                               break;
+                       case SDP_DEVICE_ID_SERVICE_ATTR_PRODUCTID:
+                               SDP_GET16(product_id, v);
+                               break;
+                       case SDP_DEVICE_ID_SERVICE_ATTR_VERSION:
+                               SDP_GET16(version, v);
+                               break;
+                       default:
+                               break;
+               }
+       }
+
        if (control_psm == -1 || interrupt_psm == -1 ||
            reconnect_initiate == -1 ||
            hid_descriptor == NULL || hid_descriptor_length == -1)
                hid_sdp_query_exit(ENOATTR);
-
+       hd->vendor_id = vendor_id;
+       hd->product_id = product_id;
+       hd->version = version;
        hd->control_psm = control_psm;
        hd->interrupt_psm = interrupt_psm;
        hd->reconnect_initiate = reconnect_initiate? 1 : 0;
diff -rwu bthidd/bthid_config.h 
/usr/src/usr.sbin/bluetooth/bthidd/bthid_config.h
--- bthidd/bthid_config.h       2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/bthid_config.h   2015-09-15 
02:47:06.046363156 +0200
@@ -42,6 +42,9 @@
        bdaddr_t                bdaddr;         /* HID device BDADDR */
        uint16_t                control_psm;    /* control PSM */
        uint16_t                interrupt_psm;  /* interrupt PSM */
+       uint16_t                vendor_id;      /* primary vendor id */
+       uint16_t                product_id;
+       uint16_t                version;
        unsigned                new_device           : 1;
        unsigned                reconnect_initiate   : 1;
        unsigned                battery_power        : 1;
diff -rwu bthidd/bthidd.conf.sample 
/usr/src/usr.sbin/bluetooth/bthidd/bthidd.conf.sample
--- bthidd/bthidd.conf.sample   2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/bthidd.conf.sample       2015-09-15 
05:08:15.624443300 +0200
@@ -2,6 +2,9 @@
 
 device {
        bdaddr                  00:50:f2:e5:68:84;
+       vendor_id               0x0000;
+       product_id              0x0000;
+       version                 0x0000;
        control_psm             0x11;
        interrupt_psm           0x13;
        reconnect_initiate      true;
@@ -24,6 +27,9 @@
 
 device {
        bdaddr                  00:50:f2:e3:fb:e1;
+       vendor_id               0x0000;
+       product_id              0x0000;
+       version                 0x0000;
        control_psm             0x11;
        interrupt_psm           0x13;
        reconnect_initiate      true;
diff -rwu 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-15 04:31:18.791591870 
+0200
@@ -86,6 +86,7 @@
 bthid_session_p        session_by_fd    (bthid_server_p srv, int32_t fd);
 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 -rwu 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-15 05:09:01.009438308 
+0200
@@ -49,6 +49,30 @@
 #include "kbd.h"
 
 /*
+ * Inoffical and unannounced report ids for Apple Mice and trackpad
+ */
+#define TRACKPAD_REPORT_ID     0x28
+#define MOUSE_REPORT_ID                0x29
+#define BATT_STAT_REPORT_ID    0x30
+#define BATT_STRENGHT_REPORT_ID        0x47
+#define SURFACE_REPORT_ID      0x61
+
+/*
+ * Probe for per-device initialisation
+ */
+void
+hid_initialise(bthid_session_p s)
+{
+       hid_device_p    hid_device = get_hid_device(&s->bdaddr);
+       assert(hid_device != NULL);
+
+       /* Magic report to enable trackpad on Apple's Magic Mouse */
+       static uint8_t rep[] = { 0x53, 0xd7, 0x01 };
+       if (hid_device->vendor_id == 0x5ac && hid_device->product_id == 0x30d)
+               write(s->ctrl, rep, 3 );
+}
+
+/*
  * Process data from control channel
  */
 
@@ -370,6 +394,41 @@
        hid_end_parse(d);
 
        /*
+        * Apple adheres to no standards and sends reports it does
+        * not introduce in its hid descriptor for its magic mouse
+        * handle those reports here
+        */
+       if (report_id == MOUSE_REPORT_ID &&
+               hid_device->vendor_id == 0x5ac &&
+               hid_device->product_id == 0x30d &&
+               len > 5 && len <= 6 + 8*15 && ( (len - 6) % 8) == 0 ) {
+
+               /* The basics. When touches are detected, no normal mouse
+                  reports are sent. Collect clicks and dx/dy */
+               int16_t delta;
+               ++data, --len; /* Chomp report_id */
+
+               if (data[2] & 1)
+                       mouse_butt |= 0x4, mevents++;
+               if (data[2] & 2)
+                       mouse_butt |= 0x1, mevents++;
+
+               delta = data[0] + ((data[2] & 0x0C) << 6);
+               if (delta) /* add and sign extend */
+                       mouse_x += ((int16_t)(delta << 6)) >> 6, mevents++;
+
+               delta = data[1] + ((data[2] & 0x30) << 4);
+               if (delta) /* add and sign extend */
+                       mouse_y += ((int16_t)(delta << 6)) >> 6, mevents++;
+
+               data += 5, len -= 5; /* Chomp fixed header */
+
+               /* The harder part: accumulate touch events */
+               /* TODO */
+       }
+               
+
+       /*
         * XXX FIXME Feed keyboard events into kernel.
         * The code below works, bit host also needs to track
         * and handle repeat.
@@ -403,7 +462,6 @@
                mi.u.data.y = mouse_y;
                mi.u.data.z = mouse_z;
                mi.u.data.buttons = mouse_butt;
-
                if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
                        syslog(LOG_ERR, "Could not process mouse events from " \
                                "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
diff -rwu bthidd/lexer.l /usr/src/usr.sbin/bluetooth/bthidd/lexer.l
--- bthidd/lexer.l      2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/lexer.l  2015-09-15 05:05:49.762445199 
+0200
@@ -50,9 +50,13 @@
 
 hexdigit                       [0-9a-fA-F]
 hexbyte                                {hexdigit}{hexdigit}?
+hexword                                
{hexdigit}{hexdigit}?{hexdigit}?{hexdigit}?
 
 device_word                    device
 bdaddr_word                    bdaddr
+vendor_id_word                 vendor_id
+product_id_word                        product_id
+version_word                   version
 control_psm_word               control_psm
 interrupt_psm_word             interrupt_psm
 reconnect_initiate_word                reconnect_initiate
@@ -64,6 +68,7 @@
 
 bdaddrstring                   
{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}
 hexbytestring                  0x{hexbyte}
+hexwordstring                  0x{hexword}
 
 %%
 
@@ -78,6 +83,9 @@
 
 {device_word}                  return (T_DEVICE);
 {bdaddr_word}                  return (T_BDADDR);
+{vendor_id_word}               return (T_VENDOR_ID);
+{product_id_word}              return (T_PRODUCT_ID);
+{version_word}                 return (T_VERSION);
 {control_psm_word}             return (T_CONTROL_PSM);
 {interrupt_psm_word}           return (T_INTERRUPT_PSM);
 {reconnect_initiate_word}      return (T_RECONNECT_INITIATE);
@@ -100,6 +108,14 @@
                                return (*ep == '\0'? T_HEXBYTE : T_ERROR);
                                }
 
+{hexwordstring}                        {
+                               char    *ep;
+
+                               yylval.num = strtoul(yytext, &ep, 16);
+
+                               return (*ep == '\0'? T_HEXWORD : T_ERROR);
+                               }
+
 .                              return (T_ERROR);
 
 %%
diff -rwu bthidd/parser.y /usr/src/usr.sbin/bluetooth/bthidd/parser.y
--- bthidd/parser.y     2015-08-12 16:21:36.000000000 +0200
+++ /usr/src/usr.sbin/bluetooth/bthidd/parser.y 2015-09-15 05:03:43.183458248 
+0200
@@ -86,8 +86,10 @@
 
 %token <bdaddr> T_BDADDRSTRING
 %token <num>   T_HEXBYTE
-%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
-%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
+%token <num>   T_HEXWORD
+%token T_DEVICE T_BDADDR T_VENDOR_ID T_PRODUCT_ID T_VERSION T_CONTROL_PSM
+%token T_INTERRUPT_PSM T_RECONNECT_INITIATE T_BATTERY_POWER
+%token T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
 %token T_TRUE T_FALSE T_ERROR
 
 %%
@@ -123,6 +125,9 @@
                ;
 
 option:                bdaddr
+               | vendor_id
+               | product_id
+               | version
                | control_psm
                | interrupt_psm
                | reconnect_initiate
@@ -138,6 +143,24 @@
                        }
                ;
 
+vendor_id:     T_VENDOR_ID T_HEXWORD
+                       {
+                       hid_device->vendor_id = $2;
+                       }
+               ;
+
+product_id:    T_PRODUCT_ID T_HEXWORD
+                       {
+                       hid_device->product_id = $2;
+                       }
+               ;
+
+version:       T_VERSION T_HEXWORD
+                       {
+                       hid_device->version = $2;
+                       }
+               ;
+
 control_psm:   T_CONTROL_PSM T_HEXBYTE
                        {
                        hid_device->control_psm = $2;
@@ -306,6 +329,9 @@
        fprintf(f,
 "device {\n"                                   \
 "      bdaddr                  %s;\n"          \
+"      vendor_id               0x%04x;\n"      \
+"      product_id              0x%04x;\n"      \
+"      version                 0x%04x;\n"      \
 "      control_psm             0x%x;\n"        \
 "      interrupt_psm           0x%x;\n"        \
 "      reconnect_initiate      %s;\n"          \
@@ -313,6 +339,7 @@
 "      normally_connectable    %s;\n"          \
 "      hid_descriptor          {",
                bt_ntoa(&d->bdaddr, NULL),
+               d->vendor_id, d->product_id, d->version,
                d->control_psm, d->interrupt_psm,
                 d->reconnect_initiate? "true" : "false",
                 d->battery_power? "true" : "false",
diff -rwu 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-15 05:15:15.664459031 
+0200
@@ -286,6 +286,10 @@
                        srv->maxfd = s->vkbd;
        }
 
+       /* Pass device for probing after both channels are established */
+       if (s->state == OPEN)
+               hid_initialise(s);
+
        return (0);
 }
 
_______________________________________________
freebsd-bluetooth@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-bluetooth
To unsubscribe, send any mail to "freebsd-bluetooth-unsubscr...@freebsd.org"

Reply via email to