1128080Semax/*
2128080Semax * hid.c
3162128Semax */
4162128Semax
5162128Semax/*-
6162128Semax * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7128080Semax * All rights reserved.
8128080Semax *
9128080Semax * Redistribution and use in source and binary forms, with or without
10128080Semax * modification, are permitted provided that the following conditions
11128080Semax * are met:
12128080Semax * 1. Redistributions of source code must retain the above copyright
13128080Semax *    notice, this list of conditions and the following disclaimer.
14128080Semax * 2. Redistributions in binary form must reproduce the above copyright
15128080Semax *    notice, this list of conditions and the following disclaimer in the
16128080Semax *    documentation and/or other materials provided with the distribution.
17128080Semax *
18128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28128080Semax * SUCH DAMAGE.
29128080Semax *
30162128Semax * $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $
31128080Semax * $FreeBSD: releng/10.2/usr.sbin/bluetooth/bthidd/hid.c 281567 2015-04-15 22:07:51Z rakuco $
32128080Semax */
33128080Semax
34128080Semax#include <sys/consio.h>
35128080Semax#include <sys/mouse.h>
36128080Semax#include <sys/queue.h>
37128080Semax#include <assert.h>
38128080Semax#include <bluetooth.h>
39128080Semax#include <dev/usb/usb.h>
40128080Semax#include <dev/usb/usbhid.h>
41162128Semax#include <errno.h>
42128080Semax#include <stdio.h>
43128080Semax#include <string.h>
44128080Semax#include <syslog.h>
45137868Semax#include <unistd.h>
46128080Semax#include <usbhid.h>
47162128Semax#include "bthid_config.h"
48128080Semax#include "bthidd.h"
49137868Semax#include "kbd.h"
50128080Semax
51128080Semax/*
52128080Semax * Process data from control channel
53128080Semax */
54128080Semax
55162128Semaxint32_t
56162128Semaxhid_control(bthid_session_p s, uint8_t *data, int32_t len)
57128080Semax{
58128080Semax	assert(s != NULL);
59128080Semax	assert(data != NULL);
60128080Semax	assert(len > 0);
61128080Semax
62128080Semax	switch (data[0] >> 4) {
63128080Semax        case 0: /* Handshake (response to command) */
64128080Semax		if (data[0] & 0xf)
65128080Semax			syslog(LOG_ERR, "Got handshake message with error " \
66128080Semax				"response 0x%x from %s",
67128080Semax				data[0], bt_ntoa(&s->bdaddr, NULL));
68128080Semax		break;
69128080Semax
70128080Semax	case 1: /* HID Control */
71128080Semax		switch (data[0] & 0xf) {
72128080Semax		case 0: /* NOP */
73128080Semax			break;
74128080Semax
75128080Semax		case 1: /* Hard reset */
76128080Semax		case 2: /* Soft reset */
77128080Semax			syslog(LOG_WARNING, "Device %s requested %s reset",
78128080Semax				bt_ntoa(&s->bdaddr, NULL),
79128080Semax				((data[0] & 0xf) == 1)? "hard" : "soft");
80128080Semax			break;
81128080Semax
82128080Semax		case 3: /* Suspend */
83128080Semax			syslog(LOG_NOTICE, "Device %s requested Suspend",
84128080Semax				bt_ntoa(&s->bdaddr, NULL));
85128080Semax			break;
86128080Semax
87128080Semax		case 4: /* Exit suspend */
88128080Semax			syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
89128080Semax				bt_ntoa(&s->bdaddr, NULL));
90128080Semax			break;
91128080Semax
92128080Semax		case 5: /* Virtual cable unplug */
93128080Semax			syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
94128080Semax				bt_ntoa(&s->bdaddr, NULL));
95128080Semax			session_close(s);
96128080Semax			break;
97128080Semax
98128080Semax		default:
99128080Semax			syslog(LOG_WARNING, "Device %s sent unknown " \
100128080Semax                                "HID_Control message 0x%x",
101128080Semax				bt_ntoa(&s->bdaddr, NULL), data[0]);
102128080Semax			break;
103128080Semax		}
104128080Semax		break;
105128080Semax
106128080Semax	default:
107128080Semax		syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
108128080Semax			"channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
109128080Semax		break;
110128080Semax	}
111128080Semax
112128080Semax	return (0);
113128080Semax}
114128080Semax
115128080Semax/*
116128080Semax * Process data from the interrupt channel
117128080Semax */
118128080Semax
119162128Semaxint32_t
120162128Semaxhid_interrupt(bthid_session_p s, uint8_t *data, int32_t len)
121128080Semax{
122162128Semax	hid_device_p	hid_device;
123128080Semax	hid_data_t	d;
124128080Semax	hid_item_t	h;
125162128Semax	int32_t		report_id, usage, page, val,
126128080Semax			mouse_x, mouse_y, mouse_z, mouse_butt,
127207812Skaiw			mevents, kevents, i;
128128080Semax
129128080Semax	assert(s != NULL);
130137868Semax	assert(s->srv != NULL);
131128080Semax	assert(data != NULL);
132128080Semax
133128080Semax	if (len < 3) {
134128080Semax		syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
135128080Semax			"channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
136128080Semax		return (-1);
137128080Semax	}
138128080Semax
139162128Semax	if (data[0] != 0xa1) {
140128080Semax		syslog(LOG_ERR, "Got unexpected message 0x%x on " \
141128080Semax			"Interrupt channel from %s",
142128080Semax			data[0], bt_ntoa(&s->bdaddr, NULL));
143128080Semax		return (-1);
144128080Semax	}
145128080Semax
146128080Semax	report_id = data[1];
147207812Skaiw	data ++;
148207812Skaiw	len --;
149128080Semax
150128080Semax	hid_device = get_hid_device(&s->bdaddr);
151128080Semax	assert(hid_device != NULL);
152128080Semax
153137868Semax	mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0;
154128080Semax
155128080Semax	for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
156128080Semax	     hid_get_item(d, &h) > 0; ) {
157212335Semax		if ((h.flags & HIO_CONST) || (h.report_ID != report_id) ||
158212335Semax		    (h.kind != hid_input))
159128080Semax			continue;
160128080Semax
161128080Semax		page = HID_PAGE(h.usage);
162128080Semax		val = hid_get_data(data, &h);
163128080Semax
164281566Srakuco		/*
165281566Srakuco		 * When the input field is an array and the usage is specified
166281566Srakuco		 * with a range instead of an ID, we have to derive the actual
167281566Srakuco		 * usage by using the item value as an index in the usage range
168281566Srakuco		 * list.
169281566Srakuco		 */
170281566Srakuco		if ((h.flags & HIO_VARIABLE)) {
171281566Srakuco			usage = HID_USAGE(h.usage);
172281566Srakuco		} else {
173281566Srakuco			const uint32_t usage_offset = val - h.logical_minimum;
174281566Srakuco			usage = HID_USAGE(h.usage_minimum + usage_offset);
175281566Srakuco		}
176281566Srakuco
177128080Semax		switch (page) {
178128080Semax		case HUP_GENERIC_DESKTOP:
179128080Semax			switch (usage) {
180128080Semax			case HUG_X:
181128080Semax				mouse_x = val;
182137868Semax				mevents ++;
183128080Semax				break;
184128080Semax
185128080Semax			case HUG_Y:
186128080Semax				mouse_y = val;
187137868Semax				mevents ++;
188128080Semax				break;
189128080Semax
190128080Semax			case HUG_WHEEL:
191128080Semax				mouse_z = -val;
192137868Semax				mevents ++;
193128080Semax				break;
194128080Semax
195128080Semax			case HUG_SYSTEM_SLEEP:
196128080Semax				if (val)
197128080Semax					syslog(LOG_NOTICE, "Sleep button pressed");
198128080Semax				break;
199128080Semax			}
200128080Semax			break;
201128080Semax
202128080Semax		case HUP_KEYBOARD:
203137868Semax			kevents ++;
204137868Semax
205128080Semax			if (h.flags & HIO_VARIABLE) {
206137868Semax				if (val && usage < kbd_maxkey())
207162128Semax					bit_set(s->keys1, usage);
208128080Semax			} else {
209137868Semax				if (val && val < kbd_maxkey())
210162128Semax					bit_set(s->keys1, val);
211137868Semax
212207812Skaiw				for (i = 1; i < h.report_count; i++) {
213207812Skaiw					h.pos += h.report_size;
214128080Semax					val = hid_get_data(data, &h);
215137868Semax					if (val && val < kbd_maxkey())
216162128Semax						bit_set(s->keys1, val);
217128080Semax				}
218128080Semax			}
219128080Semax			break;
220128080Semax
221128080Semax		case HUP_BUTTON:
222162128Semax			if (usage != 0) {
223162128Semax				if (usage == 2)
224162128Semax					usage = 3;
225162128Semax				else if (usage == 3)
226162128Semax					usage = 2;
227162128Semax
228162128Semax				mouse_butt |= (val << (usage - 1));
229162128Semax				mevents ++;
230162128Semax			}
231128080Semax			break;
232128080Semax
233137868Semax		case HUP_CONSUMER:
234137868Semax			if (!val)
235137868Semax				break;
236137868Semax
237137868Semax			switch (usage) {
238221448Semax			case HUC_AC_PAN:
239221448Semax				/* Horizontal scroll */
240221448Semax				if (val < 0)
241221448Semax					mouse_butt |= (1 << 5);
242221448Semax				else
243221448Semax					mouse_butt |= (1 << 6);
244221448Semax
245221448Semax				mevents ++;
246221448Semax				val = 0;
247221448Semax				break;
248221448Semax
249137868Semax			case 0xb5: /* Scan Next Track */
250137868Semax				val = 0x19;
251137868Semax				break;
252137868Semax
253137868Semax			case 0xb6: /* Scan Previous Track */
254137868Semax				val = 0x10;
255137868Semax				break;
256137868Semax
257137868Semax			case 0xb7: /* Stop */
258137868Semax				val = 0x24;
259137868Semax				break;
260137868Semax
261137868Semax			case 0xcd: /* Play/Pause */
262137868Semax				val = 0x22;
263137868Semax				break;
264137868Semax
265137868Semax			case 0xe2: /* Mute */
266137868Semax				val = 0x20;
267137868Semax				break;
268137868Semax
269137868Semax			case 0xe9: /* Volume Up */
270137868Semax				val = 0x30;
271137868Semax				break;
272137868Semax
273137868Semax			case 0xea: /* Volume Down */
274137868Semax				val = 0x2E;
275137868Semax				break;
276137868Semax
277137868Semax			case 0x183: /* Media Select */
278137868Semax				val = 0x6D;
279137868Semax				break;
280137868Semax
281137868Semax			case 0x018a: /* Mail */
282137868Semax				val = 0x6C;
283137868Semax				break;
284137868Semax
285137868Semax			case 0x192: /* Calculator */
286137868Semax				val = 0x21;
287137868Semax				break;
288137868Semax
289137868Semax			case 0x194: /* My Computer */
290137868Semax				val = 0x6B;
291137868Semax				break;
292137868Semax
293137868Semax			case 0x221: /* WWW Search */
294137868Semax				val = 0x65;
295137868Semax				break;
296137868Semax
297137868Semax			case 0x223: /* WWW Home */
298137868Semax				val = 0x32;
299137868Semax				break;
300137868Semax
301137868Semax			case 0x224: /* WWW Back */
302137868Semax				val = 0x6A;
303137868Semax				break;
304137868Semax
305137868Semax			case 0x225: /* WWW Forward */
306137868Semax				val = 0x69;
307137868Semax				break;
308137868Semax
309137868Semax			case 0x226: /* WWW Stop */
310137868Semax				val = 0x68;
311137868Semax				break;
312137868Semax
313163185Smarkus			case 0x227: /* WWW Refresh */
314137868Semax				val = 0x67;
315137868Semax				break;
316137868Semax
317137868Semax			case 0x22a: /* WWW Favorites */
318137868Semax				val = 0x66;
319137868Semax				break;
320137868Semax
321137868Semax			default:
322137868Semax				val = 0;
323137868Semax				break;
324137868Semax			}
325137868Semax
326137868Semax			/* XXX FIXME - UGLY HACK */
327137868Semax			if (val != 0) {
328162128Semax				if (hid_device->keyboard) {
329162128Semax					int32_t	buf[4] = { 0xe0, val,
330162128Semax							   0xe0, val|0x80 };
331137868Semax
332162128Semax					assert(s->vkbd != -1);
333162128Semax					write(s->vkbd, buf, sizeof(buf));
334162128Semax				} else
335162128Semax					syslog(LOG_ERR, "Keyboard events " \
336162128Semax						"received from non-keyboard " \
337162128Semax						"device %s. Please report",
338162128Semax						bt_ntoa(&s->bdaddr, NULL));
339137868Semax			}
340137868Semax			break;
341137868Semax
342128080Semax		case HUP_MICROSOFT:
343128080Semax			switch (usage) {
344128080Semax			case 0xfe01:
345128080Semax				if (!hid_device->battery_power)
346128080Semax					break;
347128080Semax
348128080Semax				switch (val) {
349128080Semax				case 1:
350128080Semax					syslog(LOG_INFO, "Battery is OK on %s",
351128080Semax						bt_ntoa(&s->bdaddr, NULL));
352128080Semax					break;
353128080Semax
354128080Semax				case 2:
355128080Semax					syslog(LOG_NOTICE, "Low battery on %s",
356128080Semax						bt_ntoa(&s->bdaddr, NULL));
357128080Semax					break;
358128080Semax
359128080Semax				case 3:
360128080Semax					syslog(LOG_WARNING, "Very low battery "\
361128080Semax                                                "on %s",
362128080Semax						bt_ntoa(&s->bdaddr, NULL));
363128080Semax					break;
364128080Semax                                }
365128080Semax				break;
366128080Semax			}
367128080Semax			break;
368128080Semax		}
369128080Semax	}
370128080Semax	hid_end_parse(d);
371128080Semax
372162128Semax	/*
373162128Semax	 * XXX FIXME Feed keyboard events into kernel.
374162128Semax	 * The code below works, bit host also needs to track
375162128Semax	 * and handle repeat.
376162128Semax	 *
377162128Semax	 * Key repeat currently works in X, but not in console.
378162128Semax	 */
379137868Semax
380162128Semax	if (kevents > 0) {
381162128Semax		if (hid_device->keyboard) {
382162128Semax			assert(s->vkbd != -1);
383162128Semax			kbd_process_keys(s);
384162128Semax		} else
385162128Semax			syslog(LOG_ERR, "Keyboard events received from " \
386162128Semax				"non-keyboard device %s. Please report",
387162128Semax				bt_ntoa(&s->bdaddr, NULL));
388162128Semax	}
389162128Semax
390128080Semax	/*
391137868Semax	 * XXX FIXME Feed mouse events into kernel.
392137868Semax	 * The code block below works, but it is not good enough.
393137868Semax	 * Need to track double-clicks etc.
394162128Semax	 *
395162128Semax	 * Double click currently works in X, but not in console.
396128080Semax	 */
397128080Semax
398137868Semax	if (mevents > 0) {
399128080Semax		struct mouse_info	mi;
400128080Semax
401128080Semax		mi.operation = MOUSE_ACTION;
402128080Semax		mi.u.data.x = mouse_x;
403128080Semax		mi.u.data.y = mouse_y;
404128080Semax		mi.u.data.z = mouse_z;
405128080Semax		mi.u.data.buttons = mouse_butt;
406128080Semax
407128080Semax		if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
408128080Semax			syslog(LOG_ERR, "Could not process mouse events from " \
409128080Semax				"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
410128080Semax				strerror(errno), errno);
411128080Semax	}
412128080Semax
413128080Semax	return (0);
414128080Semax}
415