16490c2ffSMaksim Yevmenkin /*
26490c2ffSMaksim Yevmenkin * hid.c
37aebfa93SMaksim Yevmenkin */
47aebfa93SMaksim Yevmenkin
57aebfa93SMaksim Yevmenkin /*-
6*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
71de7b4b8SPedro F. Giffuni *
87aebfa93SMaksim Yevmenkin * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
96490c2ffSMaksim Yevmenkin * All rights reserved.
106490c2ffSMaksim Yevmenkin *
116490c2ffSMaksim Yevmenkin * Redistribution and use in source and binary forms, with or without
126490c2ffSMaksim Yevmenkin * modification, are permitted provided that the following conditions
136490c2ffSMaksim Yevmenkin * are met:
146490c2ffSMaksim Yevmenkin * 1. Redistributions of source code must retain the above copyright
156490c2ffSMaksim Yevmenkin * notice, this list of conditions and the following disclaimer.
166490c2ffSMaksim Yevmenkin * 2. Redistributions in binary form must reproduce the above copyright
176490c2ffSMaksim Yevmenkin * notice, this list of conditions and the following disclaimer in the
186490c2ffSMaksim Yevmenkin * documentation and/or other materials provided with the distribution.
196490c2ffSMaksim Yevmenkin *
206490c2ffSMaksim Yevmenkin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
216490c2ffSMaksim Yevmenkin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
226490c2ffSMaksim Yevmenkin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
236490c2ffSMaksim Yevmenkin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
246490c2ffSMaksim Yevmenkin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
256490c2ffSMaksim Yevmenkin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
266490c2ffSMaksim Yevmenkin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
276490c2ffSMaksim Yevmenkin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
286490c2ffSMaksim Yevmenkin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
296490c2ffSMaksim Yevmenkin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
306490c2ffSMaksim Yevmenkin * SUCH DAMAGE.
316490c2ffSMaksim Yevmenkin *
327aebfa93SMaksim Yevmenkin * $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $
336490c2ffSMaksim Yevmenkin */
346490c2ffSMaksim Yevmenkin
356490c2ffSMaksim Yevmenkin #include <sys/consio.h>
366490c2ffSMaksim Yevmenkin #include <sys/mouse.h>
376490c2ffSMaksim Yevmenkin #include <sys/queue.h>
386490c2ffSMaksim Yevmenkin #include <assert.h>
398d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
406490c2ffSMaksim Yevmenkin #include <bluetooth.h>
416490c2ffSMaksim Yevmenkin #include <dev/usb/usb.h>
426490c2ffSMaksim Yevmenkin #include <dev/usb/usbhid.h>
437aebfa93SMaksim Yevmenkin #include <errno.h>
446490c2ffSMaksim Yevmenkin #include <stdio.h>
45a9a8884bSVladimir Kondratyev #include <stdlib.h>
466490c2ffSMaksim Yevmenkin #include <string.h>
476490c2ffSMaksim Yevmenkin #include <syslog.h>
483adfd74aSMaksim Yevmenkin #include <unistd.h>
496490c2ffSMaksim Yevmenkin #include <usbhid.h>
506490c2ffSMaksim Yevmenkin #include "bthid_config.h"
517aebfa93SMaksim Yevmenkin #include "bthidd.h"
5244af5666SVladimir Kondratyev #include "btuinput.h"
533adfd74aSMaksim Yevmenkin #include "kbd.h"
546490c2ffSMaksim Yevmenkin
556490c2ffSMaksim Yevmenkin /*
56a9a8884bSVladimir Kondratyev * Inoffical and unannounced report ids for Apple Mice and trackpad
57a9a8884bSVladimir Kondratyev */
58a9a8884bSVladimir Kondratyev #define TRACKPAD_REPORT_ID 0x28
59a9a8884bSVladimir Kondratyev #define AMM_REPORT_ID 0x29
60a9a8884bSVladimir Kondratyev #define BATT_STAT_REPORT_ID 0x30
61a9a8884bSVladimir Kondratyev #define BATT_STRENGTH_REPORT_ID 0x47
62a9a8884bSVladimir Kondratyev #define SURFACE_REPORT_ID 0x61
63a9a8884bSVladimir Kondratyev
64a9a8884bSVladimir Kondratyev /*
65a9a8884bSVladimir Kondratyev * Apple magic mouse (AMM) specific device state
66a9a8884bSVladimir Kondratyev */
67a9a8884bSVladimir Kondratyev #define AMM_MAX_BUTTONS 16
68a9a8884bSVladimir Kondratyev struct apple_state {
69a9a8884bSVladimir Kondratyev int y [AMM_MAX_BUTTONS];
70a9a8884bSVladimir Kondratyev int button_state;
71a9a8884bSVladimir Kondratyev };
72a9a8884bSVladimir Kondratyev
73a9a8884bSVladimir Kondratyev #define MAGIC_MOUSE(D) (((D)->vendor_id == 0x5ac) && ((D)->product_id == 0x30d))
74a9a8884bSVladimir Kondratyev #define AMM_BASIC_BLOCK 5
75a9a8884bSVladimir Kondratyev #define AMM_FINGER_BLOCK 8
76a9a8884bSVladimir Kondratyev #define AMM_VALID_REPORT(L) (((L) >= AMM_BASIC_BLOCK) && \
77a9a8884bSVladimir Kondratyev ((L) <= 16*AMM_FINGER_BLOCK + AMM_BASIC_BLOCK) && \
78a9a8884bSVladimir Kondratyev ((L) % AMM_FINGER_BLOCK) == AMM_BASIC_BLOCK)
79a9a8884bSVladimir Kondratyev #define AMM_WHEEL_SPEED 100
80a9a8884bSVladimir Kondratyev
81a9a8884bSVladimir Kondratyev /*
82a9a8884bSVladimir Kondratyev * Probe for per-device initialisation
83a9a8884bSVladimir Kondratyev */
84a9a8884bSVladimir Kondratyev void
hid_initialise(bthid_session_p s)85a9a8884bSVladimir Kondratyev hid_initialise(bthid_session_p s)
86a9a8884bSVladimir Kondratyev {
87a9a8884bSVladimir Kondratyev hid_device_p hid_device = get_hid_device(&s->bdaddr);
88a9a8884bSVladimir Kondratyev
89a9a8884bSVladimir Kondratyev if (hid_device && MAGIC_MOUSE(hid_device)) {
90a9a8884bSVladimir Kondratyev /* Magic report to enable trackpad on Apple's Magic Mouse */
91a9a8884bSVladimir Kondratyev static uint8_t rep[] = {0x53, 0xd7, 0x01};
92a9a8884bSVladimir Kondratyev
93a9a8884bSVladimir Kondratyev if ((s->ctx = calloc(1, sizeof(struct apple_state))) == NULL)
94a9a8884bSVladimir Kondratyev return;
95a9a8884bSVladimir Kondratyev write(s->ctrl, rep, 3);
96a9a8884bSVladimir Kondratyev }
97a9a8884bSVladimir Kondratyev }
98a9a8884bSVladimir Kondratyev
99a9a8884bSVladimir Kondratyev /*
1006490c2ffSMaksim Yevmenkin * Process data from control channel
1016490c2ffSMaksim Yevmenkin */
1026490c2ffSMaksim Yevmenkin
1037aebfa93SMaksim Yevmenkin int32_t
hid_control(bthid_session_p s,uint8_t * data,int32_t len)1047aebfa93SMaksim Yevmenkin hid_control(bthid_session_p s, uint8_t *data, int32_t len)
1056490c2ffSMaksim Yevmenkin {
1066490c2ffSMaksim Yevmenkin assert(s != NULL);
1076490c2ffSMaksim Yevmenkin assert(data != NULL);
1086490c2ffSMaksim Yevmenkin assert(len > 0);
1096490c2ffSMaksim Yevmenkin
1106490c2ffSMaksim Yevmenkin switch (data[0] >> 4) {
1116490c2ffSMaksim Yevmenkin case 0: /* Handshake (response to command) */
1126490c2ffSMaksim Yevmenkin if (data[0] & 0xf)
1136490c2ffSMaksim Yevmenkin syslog(LOG_ERR, "Got handshake message with error " \
1146490c2ffSMaksim Yevmenkin "response 0x%x from %s",
1156490c2ffSMaksim Yevmenkin data[0], bt_ntoa(&s->bdaddr, NULL));
1166490c2ffSMaksim Yevmenkin break;
1176490c2ffSMaksim Yevmenkin
1186490c2ffSMaksim Yevmenkin case 1: /* HID Control */
1196490c2ffSMaksim Yevmenkin switch (data[0] & 0xf) {
1206490c2ffSMaksim Yevmenkin case 0: /* NOP */
1216490c2ffSMaksim Yevmenkin break;
1226490c2ffSMaksim Yevmenkin
1236490c2ffSMaksim Yevmenkin case 1: /* Hard reset */
1246490c2ffSMaksim Yevmenkin case 2: /* Soft reset */
1256490c2ffSMaksim Yevmenkin syslog(LOG_WARNING, "Device %s requested %s reset",
1266490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL),
1276490c2ffSMaksim Yevmenkin ((data[0] & 0xf) == 1)? "hard" : "soft");
1286490c2ffSMaksim Yevmenkin break;
1296490c2ffSMaksim Yevmenkin
1306490c2ffSMaksim Yevmenkin case 3: /* Suspend */
1316490c2ffSMaksim Yevmenkin syslog(LOG_NOTICE, "Device %s requested Suspend",
1326490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
1336490c2ffSMaksim Yevmenkin break;
1346490c2ffSMaksim Yevmenkin
1356490c2ffSMaksim Yevmenkin case 4: /* Exit suspend */
1366490c2ffSMaksim Yevmenkin syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
1376490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
1386490c2ffSMaksim Yevmenkin break;
1396490c2ffSMaksim Yevmenkin
1406490c2ffSMaksim Yevmenkin case 5: /* Virtual cable unplug */
1416490c2ffSMaksim Yevmenkin syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
1426490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
1436490c2ffSMaksim Yevmenkin session_close(s);
1446490c2ffSMaksim Yevmenkin break;
1456490c2ffSMaksim Yevmenkin
1466490c2ffSMaksim Yevmenkin default:
1476490c2ffSMaksim Yevmenkin syslog(LOG_WARNING, "Device %s sent unknown " \
1486490c2ffSMaksim Yevmenkin "HID_Control message 0x%x",
1496490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL), data[0]);
1506490c2ffSMaksim Yevmenkin break;
1516490c2ffSMaksim Yevmenkin }
1526490c2ffSMaksim Yevmenkin break;
1536490c2ffSMaksim Yevmenkin
1546490c2ffSMaksim Yevmenkin default:
1556490c2ffSMaksim Yevmenkin syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
1566490c2ffSMaksim Yevmenkin "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
1576490c2ffSMaksim Yevmenkin break;
1586490c2ffSMaksim Yevmenkin }
1596490c2ffSMaksim Yevmenkin
1606490c2ffSMaksim Yevmenkin return (0);
1616490c2ffSMaksim Yevmenkin }
1626490c2ffSMaksim Yevmenkin
1636490c2ffSMaksim Yevmenkin /*
1646490c2ffSMaksim Yevmenkin * Process data from the interrupt channel
1656490c2ffSMaksim Yevmenkin */
1666490c2ffSMaksim Yevmenkin
1677aebfa93SMaksim Yevmenkin int32_t
hid_interrupt(bthid_session_p s,uint8_t * data,int32_t len)1687aebfa93SMaksim Yevmenkin hid_interrupt(bthid_session_p s, uint8_t *data, int32_t len)
1696490c2ffSMaksim Yevmenkin {
1707aebfa93SMaksim Yevmenkin hid_device_p hid_device;
1716490c2ffSMaksim Yevmenkin hid_data_t d;
1726490c2ffSMaksim Yevmenkin hid_item_t h;
1737aebfa93SMaksim Yevmenkin int32_t report_id, usage, page, val,
174c9eb7bf7SVladimir Kondratyev mouse_x, mouse_y, mouse_z, mouse_t, mouse_butt,
175ef6de0e1SKai Wang mevents, kevents, i;
1766490c2ffSMaksim Yevmenkin
1776490c2ffSMaksim Yevmenkin assert(s != NULL);
1783adfd74aSMaksim Yevmenkin assert(s->srv != NULL);
1796490c2ffSMaksim Yevmenkin assert(data != NULL);
1806490c2ffSMaksim Yevmenkin
1816490c2ffSMaksim Yevmenkin if (len < 3) {
1826490c2ffSMaksim Yevmenkin syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
1836490c2ffSMaksim Yevmenkin "channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
1846490c2ffSMaksim Yevmenkin return (-1);
1856490c2ffSMaksim Yevmenkin }
1866490c2ffSMaksim Yevmenkin
1877aebfa93SMaksim Yevmenkin if (data[0] != 0xa1) {
1886490c2ffSMaksim Yevmenkin syslog(LOG_ERR, "Got unexpected message 0x%x on " \
1896490c2ffSMaksim Yevmenkin "Interrupt channel from %s",
1906490c2ffSMaksim Yevmenkin data[0], bt_ntoa(&s->bdaddr, NULL));
1916490c2ffSMaksim Yevmenkin return (-1);
1926490c2ffSMaksim Yevmenkin }
1936490c2ffSMaksim Yevmenkin
1946490c2ffSMaksim Yevmenkin report_id = data[1];
195ef6de0e1SKai Wang data ++;
196ef6de0e1SKai Wang len --;
1976490c2ffSMaksim Yevmenkin
1986490c2ffSMaksim Yevmenkin hid_device = get_hid_device(&s->bdaddr);
1996490c2ffSMaksim Yevmenkin assert(hid_device != NULL);
2006490c2ffSMaksim Yevmenkin
201c9eb7bf7SVladimir Kondratyev mouse_x = mouse_y = mouse_z = mouse_t = mouse_butt = 0;
202c9eb7bf7SVladimir Kondratyev mevents = kevents = 0;
2036490c2ffSMaksim Yevmenkin
2046490c2ffSMaksim Yevmenkin for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
2056490c2ffSMaksim Yevmenkin hid_get_item(d, &h) > 0; ) {
206fc780ca1SMaksim Yevmenkin if ((h.flags & HIO_CONST) || (h.report_ID != report_id) ||
207fc780ca1SMaksim Yevmenkin (h.kind != hid_input))
2086490c2ffSMaksim Yevmenkin continue;
2096490c2ffSMaksim Yevmenkin
2106490c2ffSMaksim Yevmenkin page = HID_PAGE(h.usage);
2116490c2ffSMaksim Yevmenkin val = hid_get_data(data, &h);
2126490c2ffSMaksim Yevmenkin
213822a51b3SRaphael Kubo da Costa /*
214822a51b3SRaphael Kubo da Costa * When the input field is an array and the usage is specified
215822a51b3SRaphael Kubo da Costa * with a range instead of an ID, we have to derive the actual
216822a51b3SRaphael Kubo da Costa * usage by using the item value as an index in the usage range
217822a51b3SRaphael Kubo da Costa * list.
218822a51b3SRaphael Kubo da Costa */
219822a51b3SRaphael Kubo da Costa if ((h.flags & HIO_VARIABLE)) {
220822a51b3SRaphael Kubo da Costa usage = HID_USAGE(h.usage);
221822a51b3SRaphael Kubo da Costa } else {
222822a51b3SRaphael Kubo da Costa const uint32_t usage_offset = val - h.logical_minimum;
223822a51b3SRaphael Kubo da Costa usage = HID_USAGE(h.usage_minimum + usage_offset);
224822a51b3SRaphael Kubo da Costa }
225822a51b3SRaphael Kubo da Costa
2266490c2ffSMaksim Yevmenkin switch (page) {
2276490c2ffSMaksim Yevmenkin case HUP_GENERIC_DESKTOP:
2286490c2ffSMaksim Yevmenkin switch (usage) {
2296490c2ffSMaksim Yevmenkin case HUG_X:
2306490c2ffSMaksim Yevmenkin mouse_x = val;
2313adfd74aSMaksim Yevmenkin mevents ++;
2326490c2ffSMaksim Yevmenkin break;
2336490c2ffSMaksim Yevmenkin
2346490c2ffSMaksim Yevmenkin case HUG_Y:
2356490c2ffSMaksim Yevmenkin mouse_y = val;
2363adfd74aSMaksim Yevmenkin mevents ++;
2376490c2ffSMaksim Yevmenkin break;
2386490c2ffSMaksim Yevmenkin
2396490c2ffSMaksim Yevmenkin case HUG_WHEEL:
2406490c2ffSMaksim Yevmenkin mouse_z = -val;
2413adfd74aSMaksim Yevmenkin mevents ++;
2426490c2ffSMaksim Yevmenkin break;
2436490c2ffSMaksim Yevmenkin
2446490c2ffSMaksim Yevmenkin case HUG_SYSTEM_SLEEP:
2456490c2ffSMaksim Yevmenkin if (val)
2466490c2ffSMaksim Yevmenkin syslog(LOG_NOTICE, "Sleep button pressed");
2476490c2ffSMaksim Yevmenkin break;
2486490c2ffSMaksim Yevmenkin }
2496490c2ffSMaksim Yevmenkin break;
2506490c2ffSMaksim Yevmenkin
2516490c2ffSMaksim Yevmenkin case HUP_KEYBOARD:
2523adfd74aSMaksim Yevmenkin kevents ++;
2533adfd74aSMaksim Yevmenkin
2546490c2ffSMaksim Yevmenkin if (h.flags & HIO_VARIABLE) {
2553adfd74aSMaksim Yevmenkin if (val && usage < kbd_maxkey())
2567aebfa93SMaksim Yevmenkin bit_set(s->keys1, usage);
2576490c2ffSMaksim Yevmenkin } else {
2583adfd74aSMaksim Yevmenkin if (val && val < kbd_maxkey())
2597aebfa93SMaksim Yevmenkin bit_set(s->keys1, val);
2603adfd74aSMaksim Yevmenkin
261ef6de0e1SKai Wang for (i = 1; i < h.report_count; i++) {
262ef6de0e1SKai Wang h.pos += h.report_size;
2636490c2ffSMaksim Yevmenkin val = hid_get_data(data, &h);
2643adfd74aSMaksim Yevmenkin if (val && val < kbd_maxkey())
2657aebfa93SMaksim Yevmenkin bit_set(s->keys1, val);
2666490c2ffSMaksim Yevmenkin }
2676490c2ffSMaksim Yevmenkin }
2686490c2ffSMaksim Yevmenkin break;
2696490c2ffSMaksim Yevmenkin
2706490c2ffSMaksim Yevmenkin case HUP_BUTTON:
2717aebfa93SMaksim Yevmenkin if (usage != 0) {
2727aebfa93SMaksim Yevmenkin if (usage == 2)
2737aebfa93SMaksim Yevmenkin usage = 3;
2747aebfa93SMaksim Yevmenkin else if (usage == 3)
2757aebfa93SMaksim Yevmenkin usage = 2;
2767aebfa93SMaksim Yevmenkin
2777aebfa93SMaksim Yevmenkin mouse_butt |= (val << (usage - 1));
2783adfd74aSMaksim Yevmenkin mevents ++;
2797aebfa93SMaksim Yevmenkin }
2803adfd74aSMaksim Yevmenkin break;
2813adfd74aSMaksim Yevmenkin
2823adfd74aSMaksim Yevmenkin case HUP_CONSUMER:
28344af5666SVladimir Kondratyev if (hid_device->keyboard && s->srv->uinput) {
28444af5666SVladimir Kondratyev if (h.flags & HIO_VARIABLE) {
28544af5666SVladimir Kondratyev uinput_rep_cons(s->ukbd, usage, !!val);
28644af5666SVladimir Kondratyev } else {
28744af5666SVladimir Kondratyev if (s->consk > 0)
28844af5666SVladimir Kondratyev uinput_rep_cons(s->ukbd,
28944af5666SVladimir Kondratyev s->consk, 0);
29044af5666SVladimir Kondratyev if (uinput_rep_cons(s->ukbd, val, 1)
29144af5666SVladimir Kondratyev == 0)
29244af5666SVladimir Kondratyev s->consk = val;
29344af5666SVladimir Kondratyev }
29444af5666SVladimir Kondratyev }
29544af5666SVladimir Kondratyev
2963adfd74aSMaksim Yevmenkin if (!val)
2973adfd74aSMaksim Yevmenkin break;
2983adfd74aSMaksim Yevmenkin
2993adfd74aSMaksim Yevmenkin switch (usage) {
300a28cc643SMaksim Yevmenkin case HUC_AC_PAN:
301a28cc643SMaksim Yevmenkin /* Horizontal scroll */
302c9eb7bf7SVladimir Kondratyev mouse_t = val;
303a28cc643SMaksim Yevmenkin mevents ++;
304a28cc643SMaksim Yevmenkin val = 0;
305a28cc643SMaksim Yevmenkin break;
306a28cc643SMaksim Yevmenkin
3073adfd74aSMaksim Yevmenkin case 0xb5: /* Scan Next Track */
3083adfd74aSMaksim Yevmenkin val = 0x19;
3093adfd74aSMaksim Yevmenkin break;
3103adfd74aSMaksim Yevmenkin
3113adfd74aSMaksim Yevmenkin case 0xb6: /* Scan Previous Track */
3123adfd74aSMaksim Yevmenkin val = 0x10;
3133adfd74aSMaksim Yevmenkin break;
3143adfd74aSMaksim Yevmenkin
3153adfd74aSMaksim Yevmenkin case 0xb7: /* Stop */
3163adfd74aSMaksim Yevmenkin val = 0x24;
3173adfd74aSMaksim Yevmenkin break;
3183adfd74aSMaksim Yevmenkin
3193adfd74aSMaksim Yevmenkin case 0xcd: /* Play/Pause */
3203adfd74aSMaksim Yevmenkin val = 0x22;
3213adfd74aSMaksim Yevmenkin break;
3223adfd74aSMaksim Yevmenkin
3233adfd74aSMaksim Yevmenkin case 0xe2: /* Mute */
3243adfd74aSMaksim Yevmenkin val = 0x20;
3253adfd74aSMaksim Yevmenkin break;
3263adfd74aSMaksim Yevmenkin
3273adfd74aSMaksim Yevmenkin case 0xe9: /* Volume Up */
3283adfd74aSMaksim Yevmenkin val = 0x30;
3293adfd74aSMaksim Yevmenkin break;
3303adfd74aSMaksim Yevmenkin
3313adfd74aSMaksim Yevmenkin case 0xea: /* Volume Down */
3323adfd74aSMaksim Yevmenkin val = 0x2E;
3333adfd74aSMaksim Yevmenkin break;
3343adfd74aSMaksim Yevmenkin
3353adfd74aSMaksim Yevmenkin case 0x183: /* Media Select */
3363adfd74aSMaksim Yevmenkin val = 0x6D;
3373adfd74aSMaksim Yevmenkin break;
3383adfd74aSMaksim Yevmenkin
3393adfd74aSMaksim Yevmenkin case 0x018a: /* Mail */
3403adfd74aSMaksim Yevmenkin val = 0x6C;
3413adfd74aSMaksim Yevmenkin break;
3423adfd74aSMaksim Yevmenkin
3433adfd74aSMaksim Yevmenkin case 0x192: /* Calculator */
3443adfd74aSMaksim Yevmenkin val = 0x21;
3453adfd74aSMaksim Yevmenkin break;
3463adfd74aSMaksim Yevmenkin
3473adfd74aSMaksim Yevmenkin case 0x194: /* My Computer */
3483adfd74aSMaksim Yevmenkin val = 0x6B;
3493adfd74aSMaksim Yevmenkin break;
3503adfd74aSMaksim Yevmenkin
3513adfd74aSMaksim Yevmenkin case 0x221: /* WWW Search */
3523adfd74aSMaksim Yevmenkin val = 0x65;
3533adfd74aSMaksim Yevmenkin break;
3543adfd74aSMaksim Yevmenkin
3553adfd74aSMaksim Yevmenkin case 0x223: /* WWW Home */
3563adfd74aSMaksim Yevmenkin val = 0x32;
3573adfd74aSMaksim Yevmenkin break;
3583adfd74aSMaksim Yevmenkin
3593adfd74aSMaksim Yevmenkin case 0x224: /* WWW Back */
3603adfd74aSMaksim Yevmenkin val = 0x6A;
3613adfd74aSMaksim Yevmenkin break;
3623adfd74aSMaksim Yevmenkin
3633adfd74aSMaksim Yevmenkin case 0x225: /* WWW Forward */
3643adfd74aSMaksim Yevmenkin val = 0x69;
3653adfd74aSMaksim Yevmenkin break;
3663adfd74aSMaksim Yevmenkin
3673adfd74aSMaksim Yevmenkin case 0x226: /* WWW Stop */
3683adfd74aSMaksim Yevmenkin val = 0x68;
3693adfd74aSMaksim Yevmenkin break;
3703adfd74aSMaksim Yevmenkin
37113d38e1aSMarkus Brueffer case 0x227: /* WWW Refresh */
3723adfd74aSMaksim Yevmenkin val = 0x67;
3733adfd74aSMaksim Yevmenkin break;
3743adfd74aSMaksim Yevmenkin
3753adfd74aSMaksim Yevmenkin case 0x22a: /* WWW Favorites */
3763adfd74aSMaksim Yevmenkin val = 0x66;
3773adfd74aSMaksim Yevmenkin break;
3783adfd74aSMaksim Yevmenkin
3793adfd74aSMaksim Yevmenkin default:
3803adfd74aSMaksim Yevmenkin val = 0;
3813adfd74aSMaksim Yevmenkin break;
3823adfd74aSMaksim Yevmenkin }
3833adfd74aSMaksim Yevmenkin
3843adfd74aSMaksim Yevmenkin /* XXX FIXME - UGLY HACK */
3853adfd74aSMaksim Yevmenkin if (val != 0) {
3867aebfa93SMaksim Yevmenkin if (hid_device->keyboard) {
3877aebfa93SMaksim Yevmenkin int32_t buf[4] = { 0xe0, val,
3887aebfa93SMaksim Yevmenkin 0xe0, val|0x80 };
3893adfd74aSMaksim Yevmenkin
3907aebfa93SMaksim Yevmenkin assert(s->vkbd != -1);
3917aebfa93SMaksim Yevmenkin write(s->vkbd, buf, sizeof(buf));
3927aebfa93SMaksim Yevmenkin } else
3937aebfa93SMaksim Yevmenkin syslog(LOG_ERR, "Keyboard events " \
3947aebfa93SMaksim Yevmenkin "received from non-keyboard " \
3957aebfa93SMaksim Yevmenkin "device %s. Please report",
3967aebfa93SMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
3973adfd74aSMaksim Yevmenkin }
3986490c2ffSMaksim Yevmenkin break;
3996490c2ffSMaksim Yevmenkin
4006490c2ffSMaksim Yevmenkin case HUP_MICROSOFT:
4016490c2ffSMaksim Yevmenkin switch (usage) {
4026490c2ffSMaksim Yevmenkin case 0xfe01:
4036490c2ffSMaksim Yevmenkin if (!hid_device->battery_power)
4046490c2ffSMaksim Yevmenkin break;
4056490c2ffSMaksim Yevmenkin
4066490c2ffSMaksim Yevmenkin switch (val) {
4076490c2ffSMaksim Yevmenkin case 1:
4086490c2ffSMaksim Yevmenkin syslog(LOG_INFO, "Battery is OK on %s",
4096490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
4106490c2ffSMaksim Yevmenkin break;
4116490c2ffSMaksim Yevmenkin
4126490c2ffSMaksim Yevmenkin case 2:
4136490c2ffSMaksim Yevmenkin syslog(LOG_NOTICE, "Low battery on %s",
4146490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
4156490c2ffSMaksim Yevmenkin break;
4166490c2ffSMaksim Yevmenkin
4176490c2ffSMaksim Yevmenkin case 3:
4186490c2ffSMaksim Yevmenkin syslog(LOG_WARNING, "Very low battery "\
4196490c2ffSMaksim Yevmenkin "on %s",
4206490c2ffSMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
4216490c2ffSMaksim Yevmenkin break;
4226490c2ffSMaksim Yevmenkin }
4236490c2ffSMaksim Yevmenkin break;
4246490c2ffSMaksim Yevmenkin }
4256490c2ffSMaksim Yevmenkin break;
4266490c2ffSMaksim Yevmenkin }
4276490c2ffSMaksim Yevmenkin }
4286490c2ffSMaksim Yevmenkin hid_end_parse(d);
4296490c2ffSMaksim Yevmenkin
4307aebfa93SMaksim Yevmenkin /*
431a9a8884bSVladimir Kondratyev * Apple adheres to no standards and sends reports it does
432a9a8884bSVladimir Kondratyev * not introduce in its hid descriptor for its magic mouse.
433a9a8884bSVladimir Kondratyev * Handle those reports here.
434a9a8884bSVladimir Kondratyev */
435a9a8884bSVladimir Kondratyev if (MAGIC_MOUSE(hid_device) && s->ctx) {
436a9a8884bSVladimir Kondratyev struct apple_state *c = (struct apple_state *)s->ctx;
437a9a8884bSVladimir Kondratyev int firm = 0, middle = 0;
438a9a8884bSVladimir Kondratyev int16_t v;
439a9a8884bSVladimir Kondratyev
440a9a8884bSVladimir Kondratyev data++, len--; /* Chomp report_id */
441a9a8884bSVladimir Kondratyev
442a9a8884bSVladimir Kondratyev if (report_id != AMM_REPORT_ID || !AMM_VALID_REPORT(len))
443a9a8884bSVladimir Kondratyev goto check_middle_button;
444a9a8884bSVladimir Kondratyev
445a9a8884bSVladimir Kondratyev /*
446a9a8884bSVladimir Kondratyev * The basics. When touches are detected, no normal mouse
447a9a8884bSVladimir Kondratyev * reports are sent. Collect clicks and dx/dy
448a9a8884bSVladimir Kondratyev */
449a9a8884bSVladimir Kondratyev if (data[2] & 1)
450a9a8884bSVladimir Kondratyev mouse_butt |= 0x1;
451a9a8884bSVladimir Kondratyev if (data[2] & 2)
452a9a8884bSVladimir Kondratyev mouse_butt |= 0x4;
453a9a8884bSVladimir Kondratyev
454a9a8884bSVladimir Kondratyev if ((v = data[0] + ((data[2] & 0x0C) << 6)))
455a9a8884bSVladimir Kondratyev mouse_x += ((int16_t)(v << 6)) >> 6, mevents++;
456a9a8884bSVladimir Kondratyev if ((v = data[1] + ((data[2] & 0x30) << 4)))
457a9a8884bSVladimir Kondratyev mouse_y += ((int16_t)(v << 6)) >> 6, mevents++;
458a9a8884bSVladimir Kondratyev
459a9a8884bSVladimir Kondratyev /*
460a9a8884bSVladimir Kondratyev * The hard part: accumulate touch events and emulate middle
461a9a8884bSVladimir Kondratyev */
462a9a8884bSVladimir Kondratyev for (data += AMM_BASIC_BLOCK, len -= AMM_BASIC_BLOCK;
463a9a8884bSVladimir Kondratyev len >= AMM_FINGER_BLOCK;
464a9a8884bSVladimir Kondratyev data += AMM_FINGER_BLOCK, len -= AMM_FINGER_BLOCK) {
465a9a8884bSVladimir Kondratyev int x, y, z, force, id;
466a9a8884bSVladimir Kondratyev
467a9a8884bSVladimir Kondratyev v = data[0] | ((data[1] & 0xf) << 8);
468a9a8884bSVladimir Kondratyev x = ((int16_t)(v << 4)) >> 4;
469a9a8884bSVladimir Kondratyev
470a9a8884bSVladimir Kondratyev v = (data[1] >> 4) | (data[2] << 4);
471a9a8884bSVladimir Kondratyev y = -(((int16_t)(v << 4)) >> 4);
472a9a8884bSVladimir Kondratyev
473a9a8884bSVladimir Kondratyev force = data[5] & 0x3f;
474a9a8884bSVladimir Kondratyev id = 0xf & ((data[5] >> 6) | (data[6] << 2));
475a9a8884bSVladimir Kondratyev z = (y - c->y[id]) / AMM_WHEEL_SPEED;
476a9a8884bSVladimir Kondratyev
477a9a8884bSVladimir Kondratyev switch ((data[7] >> 4) & 0x7) { /* Phase */
478a9a8884bSVladimir Kondratyev case 3: /* First touch */
479a9a8884bSVladimir Kondratyev c->y[id] = y;
480a9a8884bSVladimir Kondratyev break;
481a9a8884bSVladimir Kondratyev case 4: /* Touch dragged */
482a9a8884bSVladimir Kondratyev if (z) {
483a9a8884bSVladimir Kondratyev mouse_z += z;
484a9a8884bSVladimir Kondratyev c->y[id] += z * AMM_WHEEL_SPEED;
485a9a8884bSVladimir Kondratyev mevents++;
486a9a8884bSVladimir Kondratyev }
487a9a8884bSVladimir Kondratyev break;
488a9a8884bSVladimir Kondratyev default:
489a9a8884bSVladimir Kondratyev break;
490a9a8884bSVladimir Kondratyev }
491a9a8884bSVladimir Kondratyev /* Count firm touches vs. firm+middle touches */
492a9a8884bSVladimir Kondratyev if (force >= 8 && ++firm && x > -350 && x < 350)
493a9a8884bSVladimir Kondratyev ++middle;
494a9a8884bSVladimir Kondratyev }
495a9a8884bSVladimir Kondratyev
496a9a8884bSVladimir Kondratyev /*
497a9a8884bSVladimir Kondratyev * If a new click is registered by mouse and there are firm
498a9a8884bSVladimir Kondratyev * touches which are all in center, make it a middle click
499a9a8884bSVladimir Kondratyev */
500a9a8884bSVladimir Kondratyev if (mouse_butt && !c->button_state && firm && middle == firm)
501a9a8884bSVladimir Kondratyev mouse_butt = 0x2;
502a9a8884bSVladimir Kondratyev
503a9a8884bSVladimir Kondratyev /*
504a9a8884bSVladimir Kondratyev * If we're still clicking and have converted the click
505a9a8884bSVladimir Kondratyev * to a middle click, keep it middle clicking
506a9a8884bSVladimir Kondratyev */
507a9a8884bSVladimir Kondratyev check_middle_button:
508a9a8884bSVladimir Kondratyev if (mouse_butt && c->button_state == 0x2)
509a9a8884bSVladimir Kondratyev mouse_butt = 0x2;
510a9a8884bSVladimir Kondratyev
511a9a8884bSVladimir Kondratyev if (mouse_butt != c->button_state)
512a9a8884bSVladimir Kondratyev c->button_state = mouse_butt, mevents++;
513a9a8884bSVladimir Kondratyev }
514a9a8884bSVladimir Kondratyev
515a9a8884bSVladimir Kondratyev /*
5167aebfa93SMaksim Yevmenkin * XXX FIXME Feed keyboard events into kernel.
5177aebfa93SMaksim Yevmenkin * The code below works, bit host also needs to track
5187aebfa93SMaksim Yevmenkin * and handle repeat.
5197aebfa93SMaksim Yevmenkin *
5207aebfa93SMaksim Yevmenkin * Key repeat currently works in X, but not in console.
5217aebfa93SMaksim Yevmenkin */
5227aebfa93SMaksim Yevmenkin
5237aebfa93SMaksim Yevmenkin if (kevents > 0) {
5247aebfa93SMaksim Yevmenkin if (hid_device->keyboard) {
5257aebfa93SMaksim Yevmenkin assert(s->vkbd != -1);
5263adfd74aSMaksim Yevmenkin kbd_process_keys(s);
5277aebfa93SMaksim Yevmenkin } else
5287aebfa93SMaksim Yevmenkin syslog(LOG_ERR, "Keyboard events received from " \
5297aebfa93SMaksim Yevmenkin "non-keyboard device %s. Please report",
5307aebfa93SMaksim Yevmenkin bt_ntoa(&s->bdaddr, NULL));
5317aebfa93SMaksim Yevmenkin }
5323adfd74aSMaksim Yevmenkin
5336490c2ffSMaksim Yevmenkin /*
5343adfd74aSMaksim Yevmenkin * XXX FIXME Feed mouse events into kernel.
5353adfd74aSMaksim Yevmenkin * The code block below works, but it is not good enough.
5363adfd74aSMaksim Yevmenkin * Need to track double-clicks etc.
5377aebfa93SMaksim Yevmenkin *
5387aebfa93SMaksim Yevmenkin * Double click currently works in X, but not in console.
5396490c2ffSMaksim Yevmenkin */
5406490c2ffSMaksim Yevmenkin
5413adfd74aSMaksim Yevmenkin if (mevents > 0) {
5426490c2ffSMaksim Yevmenkin struct mouse_info mi;
5436490c2ffSMaksim Yevmenkin
544c9eb7bf7SVladimir Kondratyev memset(&mi, 0, sizeof(mi));
5456490c2ffSMaksim Yevmenkin mi.operation = MOUSE_ACTION;
546c9eb7bf7SVladimir Kondratyev mi.u.data.buttons = mouse_butt;
547c9eb7bf7SVladimir Kondratyev
548c9eb7bf7SVladimir Kondratyev /* translate T-axis into button presses */
549c9eb7bf7SVladimir Kondratyev if (mouse_t != 0) {
550c9eb7bf7SVladimir Kondratyev mi.u.data.buttons |= 1 << (mouse_t > 0 ? 6 : 5);
551c9eb7bf7SVladimir Kondratyev if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
552c9eb7bf7SVladimir Kondratyev syslog(LOG_ERR, "Could not process mouse " \
553c9eb7bf7SVladimir Kondratyev "events from %s. %s (%d)",
554c9eb7bf7SVladimir Kondratyev bt_ntoa(&s->bdaddr, NULL),
555c9eb7bf7SVladimir Kondratyev strerror(errno), errno);
556c9eb7bf7SVladimir Kondratyev }
557c9eb7bf7SVladimir Kondratyev
5586490c2ffSMaksim Yevmenkin mi.u.data.x = mouse_x;
5596490c2ffSMaksim Yevmenkin mi.u.data.y = mouse_y;
5606490c2ffSMaksim Yevmenkin mi.u.data.z = mouse_z;
5616490c2ffSMaksim Yevmenkin mi.u.data.buttons = mouse_butt;
5626490c2ffSMaksim Yevmenkin
5636490c2ffSMaksim Yevmenkin if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
5646490c2ffSMaksim Yevmenkin syslog(LOG_ERR, "Could not process mouse events from " \
5656490c2ffSMaksim Yevmenkin "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
5666490c2ffSMaksim Yevmenkin strerror(errno), errno);
56744af5666SVladimir Kondratyev
56844af5666SVladimir Kondratyev if (hid_device->mouse && s->srv->uinput &&
56944af5666SVladimir Kondratyev uinput_rep_mouse(s->umouse, mouse_x, mouse_y, mouse_z,
57044af5666SVladimir Kondratyev mouse_t, mouse_butt, s->obutt) < 0)
57144af5666SVladimir Kondratyev syslog(LOG_ERR, "Could not process mouse events from " \
57244af5666SVladimir Kondratyev "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
57344af5666SVladimir Kondratyev strerror(errno), errno);
57444af5666SVladimir Kondratyev s->obutt = mouse_butt;
5756490c2ffSMaksim Yevmenkin }
5766490c2ffSMaksim Yevmenkin
5776490c2ffSMaksim Yevmenkin return (0);
5786490c2ffSMaksim Yevmenkin }
579