source: mainline/uspace/drv/bus/usb/usbhid/mouse/mousedev.c@ e3c78efc

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e3c78efc was e3c78efc, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

usbhid: Remove redundant array size enums.

  • Property mode set to 100644
File size: 16.9 KB
Line 
1/*
2 * Copyright (c) 2011 Lubos Slovak, Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbhid
30 * @{
31 */
32/**
33 * @file
34 * USB Mouse driver API.
35 */
36
37#include <usb/debug.h>
38#include <usb/classes/classes.h>
39#include <usb/hid/hid.h>
40#include <usb/hid/request.h>
41#include <usb/hid/usages/core.h>
42#include <errno.h>
43#include <async.h>
44#include <str_error.h>
45#include <ipc/mouseev.h>
46#include <io/console.h>
47
48#include <ipc/kbdev.h>
49#include <io/keycode.h>
50
51#include "mousedev.h"
52#include "../usbhid.h"
53
54/** Number of simulated arrow-key presses for singel wheel step. */
55#define ARROWS_PER_SINGLE_WHEEL 3
56
57#define NAME "mouse"
58
59/*----------------------------------------------------------------------------*/
60
61const usb_endpoint_description_t usb_hid_mouse_poll_endpoint_description = {
62 .transfer_type = USB_TRANSFER_INTERRUPT,
63 .direction = USB_DIRECTION_IN,
64 .interface_class = USB_CLASS_HID,
65 .interface_subclass = USB_HID_SUBCLASS_BOOT,
66 .interface_protocol = USB_HID_PROTOCOL_MOUSE,
67 .flags = 0
68};
69
70const char *HID_MOUSE_FUN_NAME = "mouse";
71const char *HID_MOUSE_WHEEL_FUN_NAME = "mouse-wheel";
72const char *HID_MOUSE_CATEGORY = "mouse";
73const char *HID_MOUSE_WHEEL_CATEGORY = "keyboard";
74
75/** Default idle rate for mouses. */
76static const uint8_t IDLE_RATE = 0;
77
78/*----------------------------------------------------------------------------*/
79static const uint8_t USB_MOUSE_BOOT_REPORT_DESCRIPTOR[] = {
80 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
81 0x09, 0x02, // USAGE (Mouse)
82 0xa1, 0x01, // COLLECTION (Application)
83 0x09, 0x01, // USAGE (Pointer)
84 0xa1, 0x00, // COLLECTION (Physical)
85 0x95, 0x03, // REPORT_COUNT (3)
86 0x75, 0x01, // REPORT_SIZE (1)
87 0x05, 0x09, // USAGE_PAGE (Button)
88 0x19, 0x01, // USAGE_MINIMUM (Button 1)
89 0x29, 0x03, // USAGE_MAXIMUM (Button 3)
90 0x15, 0x00, // LOGICAL_MINIMUM (0)
91 0x25, 0x01, // LOGICAL_MAXIMUM (1)
92 0x81, 0x02, // INPUT (Data,Var,Abs)
93 0x95, 0x01, // REPORT_COUNT (1)
94 0x75, 0x05, // REPORT_SIZE (5)
95 0x81, 0x01, // INPUT (Cnst)
96 0x75, 0x08, // REPORT_SIZE (8)
97 0x95, 0x02, // REPORT_COUNT (2)
98 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
99 0x09, 0x30, // USAGE (X)
100 0x09, 0x31, // USAGE (Y)
101 0x15, 0x81, // LOGICAL_MINIMUM (-127)
102 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
103 0x81, 0x06, // INPUT (Data,Var,Rel)
104 0xc0, // END_COLLECTION
105 0xc0 // END_COLLECTION
106};
107
108/*----------------------------------------------------------------------------*/
109
110/** Default handler for IPC methods not handled by DDF.
111 *
112 * @param fun Device function handling the call.
113 * @param icallid Call id.
114 * @param icall Call data.
115 */
116static void default_connection_handler(ddf_fun_t *fun,
117 ipc_callid_t icallid, ipc_call_t *icall)
118{
119 usb_mouse_t *mouse_dev = fun->driver_data;
120
121 if (mouse_dev == NULL) {
122 usb_log_debug("%s: Missing parameters.\n", __FUNCTION__);
123 async_answer_0(icallid, EINVAL);
124 return;
125 }
126
127 usb_log_debug("%s: fun->name: %s\n", __FUNCTION__, fun->name);
128 usb_log_debug("%s: mouse_sess: %p, wheel_sess: %p\n",
129 __FUNCTION__, mouse_dev->mouse_sess, mouse_dev->wheel_sess);
130
131 async_sess_t **sess_ptr = (fun == mouse_dev->mouse_fun) ?
132 &mouse_dev->mouse_sess : &mouse_dev->wheel_sess;
133
134 async_sess_t *sess =
135 async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
136 if (sess != NULL) {
137 if (*sess_ptr == NULL) {
138 *sess_ptr = sess;
139 usb_log_debug("Console session to %s set ok (%p).\n",
140 fun->name, sess);
141 async_answer_0(icallid, EOK);
142 } else {
143 usb_log_error("Console session to %s already set.\n",
144 fun->name);
145 async_answer_0(icallid, ELIMIT);
146 }
147 } else {
148 usb_log_debug("%s: Invalid function.\n", __FUNCTION__);
149 async_answer_0(icallid, EINVAL);
150 }
151}
152
153/*----------------------------------------------------------------------------*/
154
155static void usb_mouse_send_wheel(const usb_mouse_t *mouse_dev, int wheel)
156{
157 unsigned int key = (wheel > 0) ? KC_UP : KC_DOWN;
158
159 if (mouse_dev->wheel_sess == NULL) {
160 usb_log_warning(
161 "Connection to console not ready, wheel roll discarded.\n");
162 return;
163 }
164
165 const unsigned count =
166 ((wheel < 0) ? -wheel : wheel) * ARROWS_PER_SINGLE_WHEEL;
167 for (unsigned i = 0; i < count; i++) {
168 /* Send arrow press and release. */
169 usb_log_debug2("Sending key %d to the console\n", key);
170
171 async_exch_t *exch = async_exchange_begin(mouse_dev->wheel_sess);
172
173 async_msg_4(exch, KBDEV_EVENT, KEY_PRESS, key, 0, 0);
174 async_msg_4(exch, KBDEV_EVENT, KEY_RELEASE, key, 0, 0);
175
176 async_exchange_end(exch);
177 }
178}
179
180/*----------------------------------------------------------------------------*/
181
182static int get_mouse_axis_move_value(uint8_t rid, usb_hid_report_t *report,
183 int32_t usage)
184{
185 int result = 0;
186
187 usb_hid_report_path_t *path = usb_hid_report_path();
188 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_GENERIC_DESKTOP,
189 usage);
190
191 usb_hid_report_path_set_report_id(path, rid);
192
193 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
194 report, NULL, path, USB_HID_PATH_COMPARE_END,
195 USB_HID_REPORT_TYPE_INPUT);
196
197 if (field != NULL) {
198 result = field->value;
199 }
200
201 usb_hid_report_path_free(path);
202
203 return result;
204}
205
206static bool usb_mouse_process_report(usb_hid_dev_t *hid_dev,
207 usb_mouse_t *mouse_dev)
208{
209 assert(mouse_dev != NULL);
210
211 if (mouse_dev->mouse_sess == NULL) {
212 usb_log_warning(NAME " No console session.\n");
213 return true;
214 }
215
216 const int shift_x = get_mouse_axis_move_value(hid_dev->report_id,
217 &hid_dev->report, USB_HIDUT_USAGE_GENERIC_DESKTOP_X);
218 const int shift_y = get_mouse_axis_move_value(hid_dev->report_id,
219 &hid_dev->report, USB_HIDUT_USAGE_GENERIC_DESKTOP_Y);
220 const int wheel = get_mouse_axis_move_value(hid_dev->report_id,
221 &hid_dev->report, USB_HIDUT_USAGE_GENERIC_DESKTOP_WHEEL);
222
223 if ((shift_x != 0) || (shift_y != 0)) {
224 async_exch_t *exch =
225 async_exchange_begin(mouse_dev->mouse_sess);
226 if (exch != NULL) {
227 async_req_2_0(exch, MOUSEEV_MOVE_EVENT, shift_x, shift_y);
228 async_exchange_end(exch);
229 }
230 }
231
232 if (wheel != 0)
233 usb_mouse_send_wheel(mouse_dev, wheel);
234
235 /* Buttons */
236 usb_hid_report_path_t *path = usb_hid_report_path();
237 if (path == NULL) {
238 usb_log_warning("Failed to create USB HID report path.\n");
239 return true;
240 }
241 int ret =
242 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_BUTTON, 0);
243 if (ret != EOK) {
244 usb_hid_report_path_free(path);
245 usb_log_warning("Failed to add buttons to report path.\n");
246 return true;
247 }
248 usb_hid_report_path_set_report_id(path, hid_dev->report_id);
249
250 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
251 &hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
252 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, USB_HID_REPORT_TYPE_INPUT);
253
254 while (field != NULL) {
255 usb_log_debug2(NAME " VALUE(%X) USAGE(%X)\n", field->value,
256 field->usage);
257 assert(field->usage > field->usage_minimum);
258 const unsigned index = field->usage - field->usage_minimum;
259 assert(index < mouse_dev->buttons_count);
260
261 if (mouse_dev->buttons[index] == 0 && field->value != 0) {
262 async_exch_t *exch =
263 async_exchange_begin(mouse_dev->mouse_sess);
264 if (exch != NULL) {
265 async_req_2_0(exch, MOUSEEV_BUTTON_EVENT,
266 field->usage, 1);
267 async_exchange_end(exch);
268 mouse_dev->buttons[index] = field->value;
269 }
270
271 } else if (mouse_dev->buttons[index] != 0 && field->value == 0) {
272 async_exch_t *exch =
273 async_exchange_begin(mouse_dev->mouse_sess);
274 if (exch != NULL) {
275 async_req_2_0(exch, MOUSEEV_BUTTON_EVENT,
276 field->usage, 0);
277 async_exchange_end(exch);
278 mouse_dev->buttons[index] = field->value;
279 }
280 }
281
282 field = usb_hid_report_get_sibling(
283 &hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
284 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
285 USB_HID_REPORT_TYPE_INPUT);
286 }
287
288 usb_hid_report_path_free(path);
289
290 return true;
291}
292/*----------------------------------------------------------------------------*/
293#define FUN_UNBIND_DESTROY(fun) \
294if (fun) { \
295 if (ddf_fun_unbind((fun)) == EOK) { \
296 (fun)->driver_data = NULL; \
297 ddf_fun_destroy((fun)); \
298 } else { \
299 usb_log_error("Could not unbind function `%s', it " \
300 "will not be destroyed.\n", (fun)->name); \
301 } \
302} else (void)0
303/*----------------------------------------------------------------------------*/
304static int usb_mouse_create_function(usb_hid_dev_t *hid_dev, usb_mouse_t *mouse)
305{
306 assert(hid_dev != NULL);
307 assert(mouse != NULL);
308
309 /* Create the exposed function. */
310 usb_log_debug("Creating DDF function %s...\n", HID_MOUSE_FUN_NAME);
311 ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
312 HID_MOUSE_FUN_NAME);
313 if (fun == NULL) {
314 usb_log_error("Could not create DDF function node `%s'.\n",
315 HID_MOUSE_FUN_NAME);
316 return ENOMEM;
317 }
318
319 fun->ops = &mouse->ops;
320 fun->driver_data = mouse;
321
322 int rc = ddf_fun_bind(fun);
323 if (rc != EOK) {
324 usb_log_error("Could not bind DDF function `%s': %s.\n",
325 fun->name, str_error(rc));
326 fun->driver_data = NULL;
327 ddf_fun_destroy(fun);
328 return rc;
329 }
330
331 usb_log_debug("Adding DDF function `%s' to category %s...\n",
332 fun->name, HID_MOUSE_CATEGORY);
333 rc = ddf_fun_add_to_category(fun, HID_MOUSE_CATEGORY);
334 if (rc != EOK) {
335 usb_log_error(
336 "Could not add DDF function to category %s: %s.\n",
337 HID_MOUSE_CATEGORY, str_error(rc));
338 FUN_UNBIND_DESTROY(fun);
339 return rc;
340 }
341 mouse->mouse_fun = fun;
342
343 /*
344 * Special function for acting as keyboard (wheel)
345 */
346 usb_log_debug("Creating DDF function %s...\n",
347 HID_MOUSE_WHEEL_FUN_NAME);
348 fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
349 HID_MOUSE_WHEEL_FUN_NAME);
350 if (fun == NULL) {
351 usb_log_error("Could not create DDF function node `%s'.\n",
352 HID_MOUSE_WHEEL_FUN_NAME);
353 FUN_UNBIND_DESTROY(mouse->mouse_fun);
354 mouse->mouse_fun = NULL;
355 return ENOMEM;
356 }
357
358 /*
359 * Store the initialized HID device and HID ops
360 * to the DDF function.
361 */
362 fun->ops = &mouse->ops;
363 fun->driver_data = mouse;
364
365 rc = ddf_fun_bind(fun);
366 if (rc != EOK) {
367 usb_log_error("Could not bind DDF function `%s': %s.\n",
368 fun->name, str_error(rc));
369 FUN_UNBIND_DESTROY(mouse->mouse_fun);
370 mouse->mouse_fun = NULL;
371
372 fun->driver_data = NULL;
373 ddf_fun_destroy(fun);
374 return rc;
375 }
376
377 usb_log_debug("Adding DDF function to category %s...\n",
378 HID_MOUSE_WHEEL_CATEGORY);
379 rc = ddf_fun_add_to_category(fun, HID_MOUSE_WHEEL_CATEGORY);
380 if (rc != EOK) {
381 usb_log_error(
382 "Could not add DDF function to category %s: %s.\n",
383 HID_MOUSE_WHEEL_CATEGORY, str_error(rc));
384
385 FUN_UNBIND_DESTROY(mouse->mouse_fun);
386 mouse->mouse_fun = NULL;
387 FUN_UNBIND_DESTROY(fun);
388 return rc;
389 }
390 mouse->wheel_fun = fun;
391
392 return EOK;
393}
394
395/*----------------------------------------------------------------------------*/
396
397/** Get highest index of a button mentioned in given report.
398 *
399 * @param report HID report.
400 * @param report_id Report id we are interested in.
401 * @return Highest button mentioned in the report.
402 * @retval 1 No button was mentioned.
403 *
404 */
405static size_t usb_mouse_get_highest_button(usb_hid_report_t *report, uint8_t report_id)
406{
407 size_t highest_button = 0;
408
409 usb_hid_report_path_t *path = usb_hid_report_path();
410 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_BUTTON, 0);
411 usb_hid_report_path_set_report_id(path, report_id);
412
413 usb_hid_report_field_t *field = NULL;
414
415 /* Break from within. */
416 while (1) {
417 field = usb_hid_report_get_sibling(
418 report, field, path,
419 USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
420 USB_HID_REPORT_TYPE_INPUT);
421 /* No more buttons? */
422 if (field == NULL) {
423 break;
424 }
425
426 size_t current_button = field->usage - field->usage_minimum;
427 if (current_button > highest_button) {
428 highest_button = current_button;
429 }
430 }
431
432 usb_hid_report_path_free(path);
433
434 return highest_button;
435}
436/*----------------------------------------------------------------------------*/
437int usb_mouse_init(usb_hid_dev_t *hid_dev, void **data)
438{
439 usb_log_debug("Initializing HID/Mouse structure...\n");
440
441 if (hid_dev == NULL) {
442 usb_log_error("Failed to init keyboard structure: no structure"
443 " given.\n");
444 return EINVAL;
445 }
446
447 usb_mouse_t *mouse_dev = calloc(1, sizeof(usb_mouse_t));
448 if (mouse_dev == NULL) {
449 usb_log_error("Error while creating USB/HID Mouse device "
450 "structure.\n");
451 return ENOMEM;
452 }
453
454 // FIXME: This may not be optimal since stupid hardware vendor may
455 // use buttons 1, 2, 3 and 6000 and we would allocate array of
456 // 6001*4B and use only 4 items in it.
457 // Since I doubt that hardware producers would do that, I think
458 // that the current solution is good enough.
459 /* Adding 1 because we will be accessing buttons[highest]. */
460 mouse_dev->buttons_count = 1 + usb_mouse_get_highest_button(
461 &hid_dev->report, hid_dev->report_id);
462 mouse_dev->buttons = calloc(mouse_dev->buttons_count, sizeof(int32_t));
463
464 if (mouse_dev->buttons == NULL) {
465 usb_log_error(NAME ": out of memory, giving up on device!\n");
466 free(mouse_dev);
467 return ENOMEM;
468 }
469
470 // set handler for incoming calls
471 mouse_dev->ops.default_handler = default_connection_handler;
472
473 // TODO: how to know if the device supports the request???
474 usbhid_req_set_idle(&hid_dev->usb_dev->ctrl_pipe,
475 hid_dev->usb_dev->interface_no, IDLE_RATE);
476
477 int rc = usb_mouse_create_function(hid_dev, mouse_dev);
478 if (rc != EOK) {
479 free(mouse_dev->buttons);
480 free(mouse_dev);
481 return rc;
482 }
483
484 /* Save the Mouse device structure into the HID device structure. */
485 *data = mouse_dev;
486
487 return EOK;
488}
489/*----------------------------------------------------------------------------*/
490bool usb_mouse_polling_callback(usb_hid_dev_t *hid_dev, void *data)
491{
492 if (hid_dev == NULL || data == NULL) {
493 usb_log_error(
494 "Missing argument to the mouse polling callback.\n");
495 return false;
496 }
497
498 usb_mouse_t *mouse_dev = data;
499
500 return usb_mouse_process_report(hid_dev, mouse_dev);
501}
502/*----------------------------------------------------------------------------*/
503void usb_mouse_deinit(usb_hid_dev_t *hid_dev, void *data)
504{
505 if (data == NULL)
506 return;
507
508 usb_mouse_t *mouse_dev = data;
509
510 /* Hangup session to the console */
511 if (mouse_dev->mouse_sess != NULL) {
512 const int ret = async_hangup(mouse_dev->mouse_sess);
513 if (ret != EOK)
514 usb_log_warning("Failed to hang up mouse session: "
515 "%p, %s.\n", mouse_dev->mouse_sess, str_error(ret));
516 }
517
518 if (mouse_dev->wheel_sess != NULL) {
519 const int ret = async_hangup(mouse_dev->wheel_sess);
520 if (ret != EOK)
521 usb_log_warning("Failed to hang up wheel session: "
522 "%p, %s.\n", mouse_dev->wheel_sess, str_error(ret));
523 }
524
525 FUN_UNBIND_DESTROY(mouse_dev->mouse_fun);
526 FUN_UNBIND_DESTROY(mouse_dev->wheel_fun);
527
528 free(mouse_dev->buttons);
529 free(mouse_dev);
530}
531/*----------------------------------------------------------------------------*/
532int usb_mouse_set_boot_protocol(usb_hid_dev_t *hid_dev)
533{
534 int rc = usb_hid_parse_report_descriptor(
535 &hid_dev->report, USB_MOUSE_BOOT_REPORT_DESCRIPTOR,
536 sizeof(USB_MOUSE_BOOT_REPORT_DESCRIPTOR));
537
538 if (rc != EOK) {
539 usb_log_error("Failed to parse boot report descriptor: %s\n",
540 str_error(rc));
541 return rc;
542 }
543
544 rc = usbhid_req_set_protocol(&hid_dev->usb_dev->ctrl_pipe,
545 hid_dev->usb_dev->interface_no, USB_HID_PROTOCOL_BOOT);
546
547 if (rc != EOK) {
548 usb_log_warning("Failed to set boot protocol to the device: "
549 "%s\n", str_error(rc));
550 return rc;
551 }
552
553 return EOK;
554}
555
556/**
557 * @}
558 */
Note: See TracBrowser for help on using the repository browser.