Author: wulf
Date: Sat Aug 12 21:23:33 2017
New Revision: 322440
URL: https://svnweb.freebsd.org/changeset/base/322440

Log:
  bthidd(8): Add support for Apple's magic mouse
  
  Note: Mouse's bthidd.conf record should contain vendor and device IDs
  to make proper device detection. If it does not contain IDs,
  regenerate record with "Query" command of recent bthidcontrol(8).
  
  Submitted by:         Dirk Engling <erdge...@erdgeist.org>
  Reviewed by:          emax
  Approved by:          bapt (mentor), gonzo (mentor)
  Differential Revision:        https://reviews.freebsd.org/D3702

Modified:
  head/usr.sbin/bluetooth/bthidd/bthidd.h
  head/usr.sbin/bluetooth/bthidd/hid.c
  head/usr.sbin/bluetooth/bthidd/server.c
  head/usr.sbin/bluetooth/bthidd/session.c

Modified: head/usr.sbin/bluetooth/bthidd/bthidd.h
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/bthidd.h     Sat Aug 12 21:20:51 2017        
(r322439)
+++ head/usr.sbin/bluetooth/bthidd/bthidd.h     Sat Aug 12 21:23:33 2017        
(r322440)
@@ -60,6 +60,7 @@ struct bthid_session
        int32_t                          ctrl;  /* control channel */
        int32_t                          intr;  /* interrupt channel */
        int32_t                          vkbd;  /* virual keyboard */
+       void                            *ctx;   /* product specific dev state */
        bdaddr_t                         bdaddr;/* remote bdaddr */
        uint16_t                         state; /* session state */
 #define CLOSED 0
@@ -86,6 +87,7 @@ bthid_session_p       session_by_bdaddr(bthid_server_p srv, 
 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);
 

Modified: head/usr.sbin/bluetooth/bthidd/hid.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/hid.c        Sat Aug 12 21:20:51 2017        
(r322439)
+++ head/usr.sbin/bluetooth/bthidd/hid.c        Sat Aug 12 21:23:33 2017        
(r322440)
@@ -41,6 +41,7 @@
 #include <dev/usb/usbhid.h>
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
@@ -50,6 +51,50 @@
 #include "kbd.h"
 
 /*
+ * Inoffical and unannounced report ids for Apple Mice and trackpad
+ */
+#define TRACKPAD_REPORT_ID     0x28
+#define AMM_REPORT_ID          0x29
+#define BATT_STAT_REPORT_ID    0x30
+#define BATT_STRENGTH_REPORT_ID        0x47
+#define SURFACE_REPORT_ID      0x61
+
+/*
+ * Apple magic mouse (AMM) specific device state
+ */
+#define AMM_MAX_BUTTONS 16
+struct apple_state {
+       int     y   [AMM_MAX_BUTTONS];
+       int     button_state;
+};
+
+#define MAGIC_MOUSE(D) (((D)->vendor_id == 0x5ac) && ((D)->product_id == 
0x30d))
+#define AMM_BASIC_BLOCK   5
+#define AMM_FINGER_BLOCK  8
+#define AMM_VALID_REPORT(L) (((L) >= AMM_BASIC_BLOCK) && \
+    ((L) <= 16*AMM_FINGER_BLOCK    + AMM_BASIC_BLOCK) && \
+    ((L)  % AMM_FINGER_BLOCK)     == AMM_BASIC_BLOCK)
+#define AMM_WHEEL_SPEED 100
+
+/*
+ * Probe for per-device initialisation
+ */
+void
+hid_initialise(bthid_session_p s)
+{
+       hid_device_p hid_device = get_hid_device(&s->bdaddr);
+
+       if (hid_device && MAGIC_MOUSE(hid_device)) {
+               /* Magic report to enable trackpad on Apple's Magic Mouse */
+               static uint8_t rep[] = {0x53, 0xd7, 0x01};
+
+               if ((s->ctx = calloc(1, sizeof(struct apple_state))) == NULL)
+                       return;
+               write(s->ctrl, rep, 3);
+       }
+}
+
+/*
  * Process data from control channel
  */
 
@@ -369,6 +414,91 @@ hid_interrupt(bthid_session_p s, uint8_t *data, int32_
                }
        }
        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 (MAGIC_MOUSE(hid_device) && s->ctx) {
+               struct apple_state *c = (struct apple_state *)s->ctx;
+               int firm = 0, middle = 0;
+               int16_t v;
+
+               data++, len--;          /* Chomp report_id */
+
+               if (report_id != AMM_REPORT_ID || !AMM_VALID_REPORT(len))
+                       goto check_middle_button;
+
+               /*
+                * The basics. When touches are detected, no normal mouse
+                * reports are sent. Collect clicks and dx/dy
+                */
+               if (data[2] & 1)
+                       mouse_butt |= 0x1;
+               if (data[2] & 2)
+                       mouse_butt |= 0x4;
+
+               if ((v = data[0] + ((data[2] & 0x0C) << 6)))
+                       mouse_x += ((int16_t)(v << 6)) >> 6, mevents++;
+               if ((v = data[1] + ((data[2] & 0x30) << 4)))
+                       mouse_y += ((int16_t)(v << 6)) >> 6, mevents++;
+
+               /*
+                * The hard part: accumulate touch events and emulate middle
+                */
+               for (data += AMM_BASIC_BLOCK,  len -= AMM_BASIC_BLOCK;
+                    len >=  AMM_FINGER_BLOCK;
+                    data += AMM_FINGER_BLOCK, len -= AMM_FINGER_BLOCK) {
+                       int x, y, z, force, id;
+
+                       v = data[0] | ((data[1] & 0xf) << 8);
+                       x = ((int16_t)(v << 4)) >> 4;
+
+                       v = (data[1] >> 4) | (data[2] << 4);
+                       y = -(((int16_t)(v << 4)) >> 4);
+
+                       force = data[5] & 0x3f;
+                       id = 0xf & ((data[5] >> 6) | (data[6] << 2));
+                       z = (y - c->y[id]) / AMM_WHEEL_SPEED;
+
+                       switch ((data[7] >> 4) & 0x7) { /* Phase */
+                       case 3: /* First touch */
+                               c->y[id] = y;
+                               break;
+                       case 4: /* Touch dragged */
+                               if (z) {
+                                       mouse_z += z;
+                                       c->y[id] += z * AMM_WHEEL_SPEED;
+                                       mevents++;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+                       /* Count firm touches vs. firm+middle touches */
+                       if (force >= 8 && ++firm && x > -350 && x < 350)
+                               ++middle;
+               }
+
+               /*
+                * If a new click is registered by mouse and there are firm
+                * touches which are all in center, make it a middle click
+                */
+               if (mouse_butt && !c->button_state && firm && middle == firm)
+                       mouse_butt = 0x2;
+
+               /*
+                * If we're still clicking and have converted the click
+                * to a middle click, keep it middle clicking
+                */
+check_middle_button:
+               if (mouse_butt && c->button_state == 0x2)
+                       mouse_butt = 0x2;
+
+               if (mouse_butt != c->button_state)
+                       c->button_state = mouse_butt, mevents++;
+       }
 
        /*
         * XXX FIXME Feed keyboard events into kernel.

Modified: head/usr.sbin/bluetooth/bthidd/server.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/server.c     Sat Aug 12 21:20:51 2017        
(r322439)
+++ head/usr.sbin/bluetooth/bthidd/server.c     Sat Aug 12 21:23:33 2017        
(r322440)
@@ -289,6 +289,10 @@ server_accept(bthid_server_p srv, int32_t fd)
                        srv->maxfd = s->vkbd;
        }
 
+       /* Pass device for probing after both channels are established */
+       if (s->state == OPEN)
+               hid_initialise(s);
+
        return (0);
 }
 

Modified: head/usr.sbin/bluetooth/bthidd/session.c
==============================================================================
--- head/usr.sbin/bluetooth/bthidd/session.c    Sat Aug 12 21:20:51 2017        
(r322439)
+++ head/usr.sbin/bluetooth/bthidd/session.c    Sat Aug 12 21:23:33 2017        
(r322440)
@@ -66,6 +66,7 @@ session_open(bthid_server_p srv, hid_device_p const d)
        memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr));
        s->ctrl = -1;
        s->intr = -1;
+       s->ctx = NULL;
 
        if (d->keyboard) {
                /* Open /dev/vkbdctl */
@@ -176,6 +177,7 @@ session_close(bthid_session_p s)
                        s->srv->maxfd --;
        }
 
+       free(s->ctx);
        free(s->keys1);
        free(s->keys2);
 
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to