xref: /freebsd/usr.sbin/bluetooth/bthidd/hid.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * hid.c
3  *
4  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: hid.c,v 1.3 2004/02/26 21:47:35 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/consio.h>
33 #include <sys/mouse.h>
34 #include <sys/queue.h>
35 #include <assert.h>
36 #include <bluetooth.h>
37 #include <errno.h>
38 #include <dev/usb/usb.h>
39 #include <dev/usb/usbhid.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <usbhid.h>
44 #include "bthidd.h"
45 #include "bthid_config.h"
46 
47 #undef	min
48 #define	min(x, y)	(((x) < (y))? (x) : (y))
49 
50 /*
51  * Process data from control channel
52  */
53 
54 int
55 hid_control(bthid_session_p s, char *data, int len)
56 {
57 	assert(s != NULL);
58 	assert(data != NULL);
59 	assert(len > 0);
60 
61 	switch (data[0] >> 4) {
62         case 0: /* Handshake (response to command) */
63 		if (data[0] & 0xf)
64 			syslog(LOG_ERR, "Got handshake message with error " \
65 				"response 0x%x from %s",
66 				data[0], bt_ntoa(&s->bdaddr, NULL));
67 		break;
68 
69 	case 1: /* HID Control */
70 		switch (data[0] & 0xf) {
71 		case 0: /* NOP */
72 			break;
73 
74 		case 1: /* Hard reset */
75 		case 2: /* Soft reset */
76 			syslog(LOG_WARNING, "Device %s requested %s reset",
77 				bt_ntoa(&s->bdaddr, NULL),
78 				((data[0] & 0xf) == 1)? "hard" : "soft");
79 			break;
80 
81 		case 3: /* Suspend */
82 			syslog(LOG_NOTICE, "Device %s requested Suspend",
83 				bt_ntoa(&s->bdaddr, NULL));
84 			break;
85 
86 		case 4: /* Exit suspend */
87 			syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
88 				bt_ntoa(&s->bdaddr, NULL));
89 			break;
90 
91 		case 5: /* Virtual cable unplug */
92 			syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
93 				bt_ntoa(&s->bdaddr, NULL));
94 			session_close(s);
95 			break;
96 
97 		default:
98 			syslog(LOG_WARNING, "Device %s sent unknown " \
99                                 "HID_Control message 0x%x",
100 				bt_ntoa(&s->bdaddr, NULL), data[0]);
101 			break;
102 		}
103 		break;
104 
105 	default:
106 		syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
107 			"channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
108 		break;
109 	}
110 
111 	return (0);
112 }
113 
114 /*
115  * Process data from the interrupt channel
116  */
117 
118 int
119 hid_interrupt(bthid_session_p s, char *data, int len)
120 {
121 	hid_device_p	hid_device = NULL;
122 	hid_data_t	d;
123 	hid_item_t	h;
124 	int		report_id, usage, page, val,
125 			mouse_x, mouse_y, mouse_z, mouse_butt,
126 			nkeys, keys[32]; /* XXX how big keys[] should be? */
127 
128 	assert(s != NULL);
129 	assert(data != NULL);
130 
131 	if (len < 3) {
132 		syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
133 			"channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
134 		return (-1);
135 	}
136 
137 	if ((unsigned char) data[0] != 0xa1) {
138 		syslog(LOG_ERR, "Got unexpected message 0x%x on " \
139 			"Interrupt channel from %s",
140 			data[0], bt_ntoa(&s->bdaddr, NULL));
141 		return (-1);
142 	}
143 
144 	report_id = data[1];
145 	data += 2;
146 	len -= 2;
147 
148 	hid_device = get_hid_device(&s->bdaddr);
149 	assert(hid_device != NULL);
150 
151 	mouse_x = mouse_y = mouse_z = mouse_butt = nkeys = 0;
152 
153 	for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
154 	     hid_get_item(d, &h) > 0; ) {
155 		if ((h.flags & HIO_CONST) || (h.report_ID != report_id))
156 			continue;
157 
158 		page = HID_PAGE(h.usage);
159 		usage = HID_USAGE(h.usage);
160 		val = hid_get_data(data, &h);
161 
162 		switch (page) {
163 		case HUP_GENERIC_DESKTOP:
164 			switch (usage) {
165 			case HUG_X:
166 				mouse_x = val;
167 				break;
168 
169 			case HUG_Y:
170 				mouse_y = val;
171 				break;
172 
173 			case HUG_WHEEL:
174 				mouse_z = -val;
175 				break;
176 
177 			case HUG_SYSTEM_SLEEP:
178 				if (val)
179 					syslog(LOG_NOTICE, "Sleep button pressed");
180 				break;
181 			}
182 			break;
183 
184 		case HUP_KEYBOARD:
185 			if (h.flags & HIO_VARIABLE) {
186 				if (val && nkeys < sizeof(keys))
187 					keys[nkeys ++] = usage;
188 			} else {
189 				if (val && nkeys < sizeof(keys))
190 					keys[nkeys ++] = val;
191 				data ++;
192 				len --;
193 
194 				len = min(len, h.report_size);
195 				while (len > 0) {
196 					val = hid_get_data(data, &h);
197 					if (val && nkeys < sizeof(keys))
198 						keys[nkeys ++] = val;
199 					data ++;
200 					len --;
201 				}
202 			}
203 			break;
204 
205 		case HUP_BUTTON:
206 			mouse_butt |= (val << (usage - 1));
207 			break;
208 
209 		case HUP_MICROSOFT:
210 			switch (usage) {
211 			case 0xfe01:
212 				if (!hid_device->battery_power)
213 					break;
214 
215 				switch (val) {
216 				case 1:
217 					syslog(LOG_INFO, "Battery is OK on %s",
218 						bt_ntoa(&s->bdaddr, NULL));
219 					break;
220 
221 				case 2:
222 					syslog(LOG_NOTICE, "Low battery on %s",
223 						bt_ntoa(&s->bdaddr, NULL));
224 					break;
225 
226 				case 3:
227 					syslog(LOG_WARNING, "Very low battery "\
228                                                 "on %s",
229 						bt_ntoa(&s->bdaddr, NULL));
230 					break;
231                                 }
232 				break;
233 			}
234 			break;
235 		}
236 	}
237 	hid_end_parse(d);
238 
239 	/*
240 	 * XXX FIXME	Feed mouse and keyboard events into kernel
241 	 *		The code block below works, but it is not
242 	 *		good enough
243 	 */
244 
245 	if (mouse_x != 0 || mouse_y != 0 || mouse_z != 0 || mouse_butt != 0) {
246 		struct mouse_info	mi;
247 
248 		mi.operation = MOUSE_ACTION;
249 		mi.u.data.x = mouse_x;
250 		mi.u.data.y = mouse_y;
251 		mi.u.data.z = mouse_z;
252 		mi.u.data.buttons = mouse_butt;
253 
254 		if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
255 			syslog(LOG_ERR, "Could not process mouse events from " \
256 				"%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
257 				strerror(errno), errno);
258 	}
259 
260 	return (0);
261 }
262 
263