hid.c revision 163185
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: head/usr.sbin/bluetooth/bthidd/hid.c 163185 2006-10-09 22:27:23Z markus $
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#undef	min
52128080Semax#define	min(x, y)	(((x) < (y))? (x) : (y))
53128080Semax
54137868Semax#undef	ASIZE
55137868Semax#define	ASIZE(a)	(sizeof(a)/sizeof(a[0]))
56137868Semax
57128080Semax/*
58128080Semax * Process data from control channel
59128080Semax */
60128080Semax
61162128Semaxint32_t
62162128Semaxhid_control(bthid_session_p s, uint8_t *data, int32_t len)
63128080Semax{
64128080Semax	assert(s != NULL);
65128080Semax	assert(data != NULL);
66128080Semax	assert(len > 0);
67128080Semax
68128080Semax	switch (data[0] >> 4) {
69128080Semax        case 0: /* Handshake (response to command) */
70128080Semax		if (data[0] & 0xf)
71128080Semax			syslog(LOG_ERR, "Got handshake message with error " \
72128080Semax				"response 0x%x from %s",
73128080Semax				data[0], bt_ntoa(&s->bdaddr, NULL));
74128080Semax		break;
75128080Semax
76128080Semax	case 1: /* HID Control */
77128080Semax		switch (data[0] & 0xf) {
78128080Semax		case 0: /* NOP */
79128080Semax			break;
80128080Semax
81128080Semax		case 1: /* Hard reset */
82128080Semax		case 2: /* Soft reset */
83128080Semax			syslog(LOG_WARNING, "Device %s requested %s reset",
84128080Semax				bt_ntoa(&s->bdaddr, NULL),
85128080Semax				((data[0] & 0xf) == 1)? "hard" : "soft");
86128080Semax			break;
87128080Semax
88128080Semax		case 3: /* Suspend */
89128080Semax			syslog(LOG_NOTICE, "Device %s requested Suspend",
90128080Semax				bt_ntoa(&s->bdaddr, NULL));
91128080Semax			break;
92128080Semax
93128080Semax		case 4: /* Exit suspend */
94128080Semax			syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
95128080Semax				bt_ntoa(&s->bdaddr, NULL));
96128080Semax			break;
97128080Semax
98128080Semax		case 5: /* Virtual cable unplug */
99128080Semax			syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
100128080Semax				bt_ntoa(&s->bdaddr, NULL));
101128080Semax			session_close(s);
102128080Semax			break;
103128080Semax
104128080Semax		default:
105128080Semax			syslog(LOG_WARNING, "Device %s sent unknown " \
106128080Semax                                "HID_Control message 0x%x",
107128080Semax				bt_ntoa(&s->bdaddr, NULL), data[0]);
108128080Semax			break;
109128080Semax		}
110128080Semax		break;
111128080Semax
112128080Semax	default:
113128080Semax		syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
114128080Semax			"channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
115128080Semax		break;
116128080Semax	}
117128080Semax
118128080Semax	return (0);
119128080Semax}
120128080Semax
121128080Semax/*
122128080Semax * Process data from the interrupt channel
123128080Semax */
124128080Semax
125162128Semaxint32_t
126162128Semaxhid_interrupt(bthid_session_p s, uint8_t *data, int32_t len)
127128080Semax{
128162128Semax	hid_device_p	hid_device;
129128080Semax	hid_data_t	d;
130128080Semax	hid_item_t	h;
131162128Semax	int32_t		report_id, usage, page, val,
132128080Semax			mouse_x, mouse_y, mouse_z, mouse_butt,
133137868Semax			mevents, kevents;
134128080Semax
135128080Semax	assert(s != NULL);
136137868Semax	assert(s->srv != NULL);
137128080Semax	assert(data != NULL);
138128080Semax
139128080Semax	if (len < 3) {
140128080Semax		syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
141128080Semax			"channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
142128080Semax		return (-1);
143128080Semax	}
144128080Semax
145162128Semax	if (data[0] != 0xa1) {
146128080Semax		syslog(LOG_ERR, "Got unexpected message 0x%x on " \
147128080Semax			"Interrupt channel from %s",
148128080Semax			data[0], bt_ntoa(&s->bdaddr, NULL));
149128080Semax		return (-1);
150128080Semax	}
151128080Semax
152128080Semax	report_id = data[1];
153128080Semax	data += 2;
154128080Semax	len -= 2;
155128080Semax
156128080Semax	hid_device = get_hid_device(&s->bdaddr);
157128080Semax	assert(hid_device != NULL);
158128080Semax
159137868Semax	mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0;
160128080Semax
161128080Semax	for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
162128080Semax	     hid_get_item(d, &h) > 0; ) {
163128080Semax		if ((h.flags & HIO_CONST) || (h.report_ID != report_id))
164128080Semax			continue;
165128080Semax
166128080Semax		page = HID_PAGE(h.usage);
167128080Semax		usage = HID_USAGE(h.usage);
168128080Semax		val = hid_get_data(data, &h);
169128080Semax
170128080Semax		switch (page) {
171128080Semax		case HUP_GENERIC_DESKTOP:
172128080Semax			switch (usage) {
173128080Semax			case HUG_X:
174128080Semax				mouse_x = val;
175137868Semax				mevents ++;
176128080Semax				break;
177128080Semax
178128080Semax			case HUG_Y:
179128080Semax				mouse_y = val;
180137868Semax				mevents ++;
181128080Semax				break;
182128080Semax
183128080Semax			case HUG_WHEEL:
184128080Semax				mouse_z = -val;
185137868Semax				mevents ++;
186128080Semax				break;
187128080Semax
188128080Semax			case HUG_SYSTEM_SLEEP:
189128080Semax				if (val)
190128080Semax					syslog(LOG_NOTICE, "Sleep button pressed");
191128080Semax				break;
192128080Semax			}
193128080Semax			break;
194128080Semax
195128080Semax		case HUP_KEYBOARD:
196137868Semax			kevents ++;
197137868Semax
198128080Semax			if (h.flags & HIO_VARIABLE) {
199137868Semax				if (val && usage < kbd_maxkey())
200162128Semax					bit_set(s->keys1, usage);
201128080Semax			} else {
202137868Semax				if (val && val < kbd_maxkey())
203162128Semax					bit_set(s->keys1, val);
204137868Semax
205128080Semax				data ++;
206128080Semax				len --;
207128080Semax
208128080Semax				len = min(len, h.report_size);
209128080Semax				while (len > 0) {
210128080Semax					val = hid_get_data(data, &h);
211137868Semax					if (val && val < kbd_maxkey())
212162128Semax						bit_set(s->keys1, val);
213137868Semax
214128080Semax					data ++;
215128080Semax					len --;
216128080Semax				}
217128080Semax			}
218128080Semax			break;
219128080Semax
220128080Semax		case HUP_BUTTON:
221162128Semax			if (usage != 0) {
222162128Semax				if (usage == 2)
223162128Semax					usage = 3;
224162128Semax				else if (usage == 3)
225162128Semax					usage = 2;
226162128Semax
227162128Semax				mouse_butt |= (val << (usage - 1));
228162128Semax				mevents ++;
229162128Semax			}
230128080Semax			break;
231128080Semax
232137868Semax		case HUP_CONSUMER:
233137868Semax			if (!val)
234137868Semax				break;
235137868Semax
236137868Semax			switch (usage) {
237137868Semax			case 0xb5: /* Scan Next Track */
238137868Semax				val = 0x19;
239137868Semax				break;
240137868Semax
241137868Semax			case 0xb6: /* Scan Previous Track */
242137868Semax				val = 0x10;
243137868Semax				break;
244137868Semax
245137868Semax			case 0xb7: /* Stop */
246137868Semax				val = 0x24;
247137868Semax				break;
248137868Semax
249137868Semax			case 0xcd: /* Play/Pause */
250137868Semax				val = 0x22;
251137868Semax				break;
252137868Semax
253137868Semax			case 0xe2: /* Mute */
254137868Semax				val = 0x20;
255137868Semax				break;
256137868Semax
257137868Semax			case 0xe9: /* Volume Up */
258137868Semax				val = 0x30;
259137868Semax				break;
260137868Semax
261137868Semax			case 0xea: /* Volume Down */
262137868Semax				val = 0x2E;
263137868Semax				break;
264137868Semax
265137868Semax			case 0x183: /* Media Select */
266137868Semax				val = 0x6D;
267137868Semax				break;
268137868Semax
269137868Semax			case 0x018a: /* Mail */
270137868Semax				val = 0x6C;
271137868Semax				break;
272137868Semax
273137868Semax			case 0x192: /* Calculator */
274137868Semax				val = 0x21;
275137868Semax				break;
276137868Semax
277137868Semax			case 0x194: /* My Computer */
278137868Semax				val = 0x6B;
279137868Semax				break;
280137868Semax
281137868Semax			case 0x221: /* WWW Search */
282137868Semax				val = 0x65;
283137868Semax				break;
284137868Semax
285137868Semax			case 0x223: /* WWW Home */
286137868Semax				val = 0x32;
287137868Semax				break;
288137868Semax
289137868Semax			case 0x224: /* WWW Back */
290137868Semax				val = 0x6A;
291137868Semax				break;
292137868Semax
293137868Semax			case 0x225: /* WWW Forward */
294137868Semax				val = 0x69;
295137868Semax				break;
296137868Semax
297137868Semax			case 0x226: /* WWW Stop */
298137868Semax				val = 0x68;
299137868Semax				break;
300137868Semax
301163185Smarkus			case 0x227: /* WWW Refresh */
302137868Semax				val = 0x67;
303137868Semax				break;
304137868Semax
305137868Semax			case 0x22a: /* WWW Favorites */
306137868Semax				val = 0x66;
307137868Semax				break;
308137868Semax
309137868Semax			default:
310137868Semax				val = 0;
311137868Semax				break;
312137868Semax			}
313137868Semax
314137868Semax			/* XXX FIXME - UGLY HACK */
315137868Semax			if (val != 0) {
316162128Semax				if (hid_device->keyboard) {
317162128Semax					int32_t	buf[4] = { 0xe0, val,
318162128Semax							   0xe0, val|0x80 };
319137868Semax
320162128Semax					assert(s->vkbd != -1);
321162128Semax					write(s->vkbd, buf, sizeof(buf));
322162128Semax				} else
323162128Semax					syslog(LOG_ERR, "Keyboard events " \
324162128Semax						"received from non-keyboard " \
325162128Semax						"device %s. Please report",
326162128Semax						bt_ntoa(&s->bdaddr, NULL));
327137868Semax			}
328137868Semax			break;
329137868Semax
330128080Semax		case HUP_MICROSOFT:
331128080Semax			switch (usage) {
332128080Semax			case 0xfe01:
333128080Semax				if (!hid_device->battery_power)
334128080Semax					break;
335128080Semax
336128080Semax				switch (val) {
337128080Semax				case 1:
338128080Semax					syslog(LOG_INFO, "Battery is OK on %s",
339128080Semax						bt_ntoa(&s->bdaddr, NULL));
340128080Semax					break;
341128080Semax
342128080Semax				case 2:
343128080Semax					syslog(LOG_NOTICE, "Low battery on %s",
344128080Semax						bt_ntoa(&s->bdaddr, NULL));
345128080Semax					break;
346128080Semax
347128080Semax				case 3:
348128080Semax					syslog(LOG_WARNING, "Very low battery "\
349128080Semax                                                "on %s",
350128080Semax						bt_ntoa(&s->bdaddr, NULL));
351128080Semax					break;
352128080Semax                                }
353128080Semax				break;
354128080Semax			}
355128080Semax			break;
356128080Semax		}
357128080Semax	}
358128080Semax	hid_end_parse(d);
359128080Semax
360162128Semax	/*
361162128Semax	 * XXX FIXME Feed keyboard events into kernel.
362162128Semax	 * The code below works, bit host also needs to track
363162128Semax	 * and handle repeat.
364162128Semax	 *
365162128Semax	 * Key repeat currently works in X, but not in console.
366162128Semax	 */
367137868Semax
368162128Semax	if (kevents > 0) {
369162128Semax		if (hid_device->keyboard) {
370162128Semax			assert(s->vkbd != -1);
371162128Semax			kbd_process_keys(s);
372162128Semax		} else
373162128Semax			syslog(LOG_ERR, "Keyboard events received from " \
374162128Semax				"non-keyboard device %s. Please report",
375162128Semax				bt_ntoa(&s->bdaddr, NULL));
376162128Semax	}
377162128Semax
378128080Semax	/*
379137868Semax	 * XXX FIXME Feed mouse events into kernel.
380137868Semax	 * The code block below works, but it is not good enough.
381137868Semax	 * Need to track double-clicks etc.
382162128Semax	 *
383162128Semax	 * Double click currently works in X, but not in console.
384128080Semax	 */
385128080Semax
386137868Semax	if (mevents > 0) {
387128080Semax		struct mouse_info	mi;
388128080Semax
389128080Semax		mi.operation = MOUSE_ACTION;
390128080Semax		mi.u.data.x = mouse_x;
391128080Semax		mi.u.data.y = mouse_y;
392128080Semax		mi.u.data.z = mouse_z;
393128080Semax		mi.u.data.buttons = mouse_butt;
394128080Semax
395128080Semax		if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
396128080Semax			syslog(LOG_ERR, "Could not process mouse events from " \
397128080Semax				"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
398128080Semax				strerror(errno), errno);
399128080Semax	}
400128080Semax
401128080Semax	return (0);
402128080Semax}
403