hid.c revision 137868
1128080Semax/*
2128080Semax * hid.c
3128080Semax *
4128080Semax * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5128080Semax * All rights reserved.
6128080Semax *
7128080Semax * Redistribution and use in source and binary forms, with or without
8128080Semax * modification, are permitted provided that the following conditions
9128080Semax * are met:
10128080Semax * 1. Redistributions of source code must retain the above copyright
11128080Semax *    notice, this list of conditions and the following disclaimer.
12128080Semax * 2. Redistributions in binary form must reproduce the above copyright
13128080Semax *    notice, this list of conditions and the following disclaimer in the
14128080Semax *    documentation and/or other materials provided with the distribution.
15128080Semax *
16128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26128080Semax * SUCH DAMAGE.
27128080Semax *
28137868Semax * $Id: hid.c,v 1.4 2004/11/17 21:59:42 max Exp $
29128080Semax * $FreeBSD: head/usr.sbin/bluetooth/bthidd/hid.c 137868 2004-11-18 18:05:15Z emax $
30128080Semax */
31128080Semax
32128080Semax#include <sys/consio.h>
33128080Semax#include <sys/mouse.h>
34128080Semax#include <sys/queue.h>
35128080Semax#include <assert.h>
36128080Semax#include <bluetooth.h>
37128080Semax#include <errno.h>
38128080Semax#include <dev/usb/usb.h>
39128080Semax#include <dev/usb/usbhid.h>
40128080Semax#include <stdio.h>
41128080Semax#include <string.h>
42128080Semax#include <syslog.h>
43137868Semax#include <unistd.h>
44128080Semax#include <usbhid.h>
45128080Semax#include "bthidd.h"
46128080Semax#include "bthid_config.h"
47137868Semax#include "kbd.h"
48128080Semax
49128080Semax#undef	min
50128080Semax#define	min(x, y)	(((x) < (y))? (x) : (y))
51128080Semax
52137868Semax#undef	ASIZE
53137868Semax#define	ASIZE(a)	(sizeof(a)/sizeof(a[0]))
54137868Semax
55128080Semax/*
56128080Semax * Process data from control channel
57128080Semax */
58128080Semax
59128080Semaxint
60128080Semaxhid_control(bthid_session_p s, char *data, int len)
61128080Semax{
62128080Semax	assert(s != NULL);
63128080Semax	assert(data != NULL);
64128080Semax	assert(len > 0);
65128080Semax
66128080Semax	switch (data[0] >> 4) {
67128080Semax        case 0: /* Handshake (response to command) */
68128080Semax		if (data[0] & 0xf)
69128080Semax			syslog(LOG_ERR, "Got handshake message with error " \
70128080Semax				"response 0x%x from %s",
71128080Semax				data[0], bt_ntoa(&s->bdaddr, NULL));
72128080Semax		break;
73128080Semax
74128080Semax	case 1: /* HID Control */
75128080Semax		switch (data[0] & 0xf) {
76128080Semax		case 0: /* NOP */
77128080Semax			break;
78128080Semax
79128080Semax		case 1: /* Hard reset */
80128080Semax		case 2: /* Soft reset */
81128080Semax			syslog(LOG_WARNING, "Device %s requested %s reset",
82128080Semax				bt_ntoa(&s->bdaddr, NULL),
83128080Semax				((data[0] & 0xf) == 1)? "hard" : "soft");
84128080Semax			break;
85128080Semax
86128080Semax		case 3: /* Suspend */
87128080Semax			syslog(LOG_NOTICE, "Device %s requested Suspend",
88128080Semax				bt_ntoa(&s->bdaddr, NULL));
89128080Semax			break;
90128080Semax
91128080Semax		case 4: /* Exit suspend */
92128080Semax			syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
93128080Semax				bt_ntoa(&s->bdaddr, NULL));
94128080Semax			break;
95128080Semax
96128080Semax		case 5: /* Virtual cable unplug */
97128080Semax			syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
98128080Semax				bt_ntoa(&s->bdaddr, NULL));
99128080Semax			session_close(s);
100128080Semax			break;
101128080Semax
102128080Semax		default:
103128080Semax			syslog(LOG_WARNING, "Device %s sent unknown " \
104128080Semax                                "HID_Control message 0x%x",
105128080Semax				bt_ntoa(&s->bdaddr, NULL), data[0]);
106128080Semax			break;
107128080Semax		}
108128080Semax		break;
109128080Semax
110128080Semax	default:
111128080Semax		syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
112128080Semax			"channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
113128080Semax		break;
114128080Semax	}
115128080Semax
116128080Semax	return (0);
117128080Semax}
118128080Semax
119128080Semax/*
120128080Semax * Process data from the interrupt channel
121128080Semax */
122128080Semax
123128080Semaxint
124128080Semaxhid_interrupt(bthid_session_p s, char *data, int len)
125128080Semax{
126128080Semax	hid_device_p	hid_device = NULL;
127128080Semax	hid_data_t	d;
128128080Semax	hid_item_t	h;
129128080Semax	int		report_id, usage, page, val,
130128080Semax			mouse_x, mouse_y, mouse_z, mouse_butt,
131137868Semax			mevents, kevents;
132128080Semax
133128080Semax	assert(s != NULL);
134137868Semax	assert(s->srv != NULL);
135128080Semax	assert(data != NULL);
136128080Semax
137128080Semax	if (len < 3) {
138128080Semax		syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
139128080Semax			"channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
140128080Semax		return (-1);
141128080Semax	}
142128080Semax
143128080Semax	if ((unsigned char) data[0] != 0xa1) {
144128080Semax		syslog(LOG_ERR, "Got unexpected message 0x%x on " \
145128080Semax			"Interrupt channel from %s",
146128080Semax			data[0], bt_ntoa(&s->bdaddr, NULL));
147128080Semax		return (-1);
148128080Semax	}
149128080Semax
150128080Semax	report_id = data[1];
151128080Semax	data += 2;
152128080Semax	len -= 2;
153128080Semax
154128080Semax	hid_device = get_hid_device(&s->bdaddr);
155128080Semax	assert(hid_device != NULL);
156128080Semax
157137868Semax	mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0;
158128080Semax
159128080Semax	for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
160128080Semax	     hid_get_item(d, &h) > 0; ) {
161128080Semax		if ((h.flags & HIO_CONST) || (h.report_ID != report_id))
162128080Semax			continue;
163128080Semax
164128080Semax		page = HID_PAGE(h.usage);
165128080Semax		usage = HID_USAGE(h.usage);
166128080Semax		val = hid_get_data(data, &h);
167128080Semax
168128080Semax		switch (page) {
169128080Semax		case HUP_GENERIC_DESKTOP:
170128080Semax			switch (usage) {
171128080Semax			case HUG_X:
172128080Semax				mouse_x = val;
173137868Semax				mevents ++;
174128080Semax				break;
175128080Semax
176128080Semax			case HUG_Y:
177128080Semax				mouse_y = val;
178137868Semax				mevents ++;
179128080Semax				break;
180128080Semax
181128080Semax			case HUG_WHEEL:
182128080Semax				mouse_z = -val;
183137868Semax				mevents ++;
184128080Semax				break;
185128080Semax
186128080Semax			case HUG_SYSTEM_SLEEP:
187128080Semax				if (val)
188128080Semax					syslog(LOG_NOTICE, "Sleep button pressed");
189128080Semax				break;
190128080Semax			}
191128080Semax			break;
192128080Semax
193128080Semax		case HUP_KEYBOARD:
194137868Semax			kevents ++;
195137868Semax
196128080Semax			if (h.flags & HIO_VARIABLE) {
197137868Semax				if (val && usage < kbd_maxkey())
198137868Semax					bit_set(s->srv->keys, usage);
199128080Semax			} else {
200137868Semax				if (val && val < kbd_maxkey())
201137868Semax					bit_set(s->srv->keys, val);
202137868Semax
203128080Semax				data ++;
204128080Semax				len --;
205128080Semax
206128080Semax				len = min(len, h.report_size);
207128080Semax				while (len > 0) {
208128080Semax					val = hid_get_data(data, &h);
209137868Semax					if (val && val < kbd_maxkey())
210137868Semax						bit_set(s->srv->keys, val);
211137868Semax
212128080Semax					data ++;
213128080Semax					len --;
214128080Semax				}
215128080Semax			}
216128080Semax			break;
217128080Semax
218128080Semax		case HUP_BUTTON:
219128080Semax			mouse_butt |= (val << (usage - 1));
220137868Semax			mevents ++;
221128080Semax			break;
222128080Semax
223137868Semax		case HUP_CONSUMER:
224137868Semax			if (!val)
225137868Semax				break;
226137868Semax
227137868Semax			switch (usage) {
228137868Semax			case 0xb5: /* Scan Next Track */
229137868Semax				val = 0x19;
230137868Semax				break;
231137868Semax
232137868Semax			case 0xb6: /* Scan Previous Track */
233137868Semax				val = 0x10;
234137868Semax				break;
235137868Semax
236137868Semax			case 0xb7: /* Stop */
237137868Semax				val = 0x24;
238137868Semax				break;
239137868Semax
240137868Semax			case 0xcd: /* Play/Pause */
241137868Semax				val = 0x22;
242137868Semax				break;
243137868Semax
244137868Semax			case 0xe2: /* Mute */
245137868Semax				val = 0x20;
246137868Semax				break;
247137868Semax
248137868Semax			case 0xe9: /* Volume Up */
249137868Semax				val = 0x30;
250137868Semax				break;
251137868Semax
252137868Semax			case 0xea: /* Volume Down */
253137868Semax				val = 0x2E;
254137868Semax				break;
255137868Semax
256137868Semax			case 0x183: /* Media Select */
257137868Semax				val = 0x6D;
258137868Semax				break;
259137868Semax
260137868Semax			case 0x018a: /* Mail */
261137868Semax				val = 0x6C;
262137868Semax				break;
263137868Semax
264137868Semax			case 0x192: /* Calculator */
265137868Semax				val = 0x21;
266137868Semax				break;
267137868Semax
268137868Semax			case 0x194: /* My Computer */
269137868Semax				val = 0x6B;
270137868Semax				break;
271137868Semax
272137868Semax			case 0x221: /* WWW Search */
273137868Semax				val = 0x65;
274137868Semax				break;
275137868Semax
276137868Semax			case 0x223: /* WWW Home */
277137868Semax				val = 0x32;
278137868Semax				break;
279137868Semax
280137868Semax			case 0x224: /* WWW Back */
281137868Semax				val = 0x6A;
282137868Semax				break;
283137868Semax
284137868Semax			case 0x225: /* WWW Forward */
285137868Semax				val = 0x69;
286137868Semax				break;
287137868Semax
288137868Semax			case 0x226: /* WWW Stop */
289137868Semax				val = 0x68;
290137868Semax				break;
291137868Semax
292137868Semax			case 0227: /* WWW Refresh */
293137868Semax				val = 0x67;
294137868Semax				break;
295137868Semax
296137868Semax			case 0x22a: /* WWW Favorites */
297137868Semax				val = 0x66;
298137868Semax				break;
299137868Semax
300137868Semax			default:
301137868Semax				val = 0;
302137868Semax				break;
303137868Semax			}
304137868Semax
305137868Semax			/* XXX FIXME - UGLY HACK */
306137868Semax			if (val != 0) {
307137868Semax				int	buf[4] = { 0xe0, val, 0xe0, val|0x80 };
308137868Semax
309137868Semax				write(s->srv->vkbd, buf, sizeof(buf));
310137868Semax			}
311137868Semax			break;
312137868Semax
313128080Semax		case HUP_MICROSOFT:
314128080Semax			switch (usage) {
315128080Semax			case 0xfe01:
316128080Semax				if (!hid_device->battery_power)
317128080Semax					break;
318128080Semax
319128080Semax				switch (val) {
320128080Semax				case 1:
321128080Semax					syslog(LOG_INFO, "Battery is OK on %s",
322128080Semax						bt_ntoa(&s->bdaddr, NULL));
323128080Semax					break;
324128080Semax
325128080Semax				case 2:
326128080Semax					syslog(LOG_NOTICE, "Low battery on %s",
327128080Semax						bt_ntoa(&s->bdaddr, NULL));
328128080Semax					break;
329128080Semax
330128080Semax				case 3:
331128080Semax					syslog(LOG_WARNING, "Very low battery "\
332128080Semax                                                "on %s",
333128080Semax						bt_ntoa(&s->bdaddr, NULL));
334128080Semax					break;
335128080Semax                                }
336128080Semax				break;
337128080Semax			}
338128080Semax			break;
339128080Semax		}
340128080Semax	}
341128080Semax	hid_end_parse(d);
342128080Semax
343137868Semax	/* Feed keyboard events into kernel */
344137868Semax	if (kevents > 0)
345137868Semax		kbd_process_keys(s);
346137868Semax
347128080Semax	/*
348137868Semax	 * XXX FIXME Feed mouse events into kernel.
349137868Semax	 * The code block below works, but it is not good enough.
350137868Semax	 * Need to track double-clicks etc.
351128080Semax	 */
352128080Semax
353137868Semax	if (mevents > 0) {
354128080Semax		struct mouse_info	mi;
355128080Semax
356128080Semax		mi.operation = MOUSE_ACTION;
357128080Semax		mi.u.data.x = mouse_x;
358128080Semax		mi.u.data.y = mouse_y;
359128080Semax		mi.u.data.z = mouse_z;
360128080Semax		mi.u.data.buttons = mouse_butt;
361128080Semax
362128080Semax		if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
363128080Semax			syslog(LOG_ERR, "Could not process mouse events from " \
364128080Semax				"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
365128080Semax				strerror(errno), errno);
366128080Semax	}
367128080Semax
368128080Semax	return (0);
369128080Semax}
370128080Semax
371