svn commit: r322440 - head/usr.sbin/bluetooth/bthidd

Vladimir Kondratyev wulf at FreeBSD.org
Sat Aug 12 21:23:35 UTC 2017


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 <erdgeist at 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);
 


More information about the svn-src-head mailing list