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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2fe28ca1 was 1875a0c, checked in by Martin Decky <martin@…>, 14 years ago

input server improvements

  • integrate legacy port mouse drivers (PS/2, ADB) into the input server (to be on par with the legacy keyboard drivers)
  • create generic port/protocol layers for the mouse in the input server
  • rename the "hid_in" namespace to "hid" namespace (hid_in/input was rather redundant)
  • rename the mouse event IPC messages to be more consistent with the keyboard event messages
  • rename input server ops structure members to be more generic (parse_scancode → parse)
  • cstyle changes (newlines, comments, printouts)
  • Property mode set to 100644
File size: 14.4 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 <async_obsolete.h>
45#include <str_error.h>
46#include <ipc/mouseev.h>
47#include <io/console.h>
48
49#include <ipc/kbdev.h>
50#include <io/keycode.h>
51
52#include "mousedev.h"
53#include "../usbhid.h"
54
55/** Number of simulated arrow-key presses for singel wheel step. */
56#define ARROWS_PER_SINGLE_WHEEL 3
57
58// FIXME: remove this header
59#include <kernel/ipc/ipc_methods.h>
60
61#define NAME "mouse"
62
63/*----------------------------------------------------------------------------*/
64
65usb_endpoint_description_t usb_hid_mouse_poll_endpoint_description = {
66 .transfer_type = USB_TRANSFER_INTERRUPT,
67 .direction = USB_DIRECTION_IN,
68 .interface_class = USB_CLASS_HID,
69 .interface_subclass = USB_HID_SUBCLASS_BOOT,
70 .interface_protocol = USB_HID_PROTOCOL_MOUSE,
71 .flags = 0
72};
73
74const char *HID_MOUSE_FUN_NAME = "mouse";
75const char *HID_MOUSE_WHEEL_FUN_NAME = "mouse-wheel";
76const char *HID_MOUSE_CLASS_NAME = "mouse";
77const char *HID_MOUSE_WHEEL_CLASS_NAME = "keyboard";
78
79/** Default idle rate for mouses. */
80static const uint8_t IDLE_RATE = 0;
81static const size_t USB_MOUSE_BUTTON_COUNT = 3;
82
83/*----------------------------------------------------------------------------*/
84
85enum {
86 USB_MOUSE_BOOT_REPORT_DESCRIPTOR_SIZE = 63
87};
88
89static const uint8_t USB_MOUSE_BOOT_REPORT_DESCRIPTOR[
90 USB_MOUSE_BOOT_REPORT_DESCRIPTOR_SIZE] = {
91 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
92 0x09, 0x02, // USAGE (Mouse)
93 0xa1, 0x01, // COLLECTION (Application)
94 0x09, 0x01, // USAGE (Pointer)
95 0xa1, 0x00, // COLLECTION (Physical)
96 0x95, 0x03, // REPORT_COUNT (3)
97 0x75, 0x01, // REPORT_SIZE (1)
98 0x05, 0x09, // USAGE_PAGE (Button)
99 0x19, 0x01, // USAGE_MINIMUM (Button 1)
100 0x29, 0x03, // USAGE_MAXIMUM (Button 3)
101 0x15, 0x00, // LOGICAL_MINIMUM (0)
102 0x25, 0x01, // LOGICAL_MAXIMUM (1)
103 0x81, 0x02, // INPUT (Data,Var,Abs)
104 0x95, 0x01, // REPORT_COUNT (1)
105 0x75, 0x05, // REPORT_SIZE (5)
106 0x81, 0x01, // INPUT (Cnst)
107 0x75, 0x08, // REPORT_SIZE (8)
108 0x95, 0x02, // REPORT_COUNT (2)
109 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
110 0x09, 0x30, // USAGE (X)
111 0x09, 0x31, // USAGE (Y)
112 0x15, 0x81, // LOGICAL_MINIMUM (-127)
113 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
114 0x81, 0x06, // INPUT (Data,Var,Rel)
115 0xc0, // END_COLLECTION
116 0xc0 // END_COLLECTION
117};
118
119/*----------------------------------------------------------------------------*/
120
121/** Default handler for IPC methods not handled by DDF.
122 *
123 * @param fun Device function handling the call.
124 * @param icallid Call id.
125 * @param icall Call data.
126 */
127static void default_connection_handler(ddf_fun_t *fun,
128 ipc_callid_t icallid, ipc_call_t *icall)
129{
130 sysarg_t method = IPC_GET_IMETHOD(*icall);
131
132 usb_mouse_t *mouse_dev = (usb_mouse_t *)fun->driver_data;
133
134 if (mouse_dev == NULL) {
135 usb_log_debug("default_connection_handler: Missing "
136 "parameters.\n");
137 async_answer_0(icallid, EINVAL);
138 return;
139 }
140
141 usb_log_debug("default_connection_handler: fun->name: %s\n",
142 fun->name);
143 usb_log_debug("default_connection_handler: mouse_phone: %d, wheel "
144 "phone: %d\n", mouse_dev->mouse_phone, mouse_dev->wheel_phone);
145
146 int *phone = (str_cmp(fun->name, HID_MOUSE_FUN_NAME) == 0)
147 ? &mouse_dev->mouse_phone : &mouse_dev->wheel_phone;
148
149 if (method == IPC_M_CONNECT_TO_ME) {
150 int callback = IPC_GET_ARG5(*icall);
151
152 if (*phone != -1) {
153 usb_log_debug("default_connection_handler: Console "
154 "phone to mouse already set.\n");
155 async_answer_0(icallid, ELIMIT);
156 return;
157 }
158
159 *phone = callback;
160 usb_log_debug("Console phone to mouse set ok (%d).\n", *phone);
161 async_answer_0(icallid, EOK);
162 return;
163 }
164
165 usb_log_debug("default_connection_handler: Invalid function.\n");
166 async_answer_0(icallid, EINVAL);
167}
168
169/*----------------------------------------------------------------------------*/
170
171static usb_mouse_t *usb_mouse_new(void)
172{
173 usb_mouse_t *mouse = calloc(1, sizeof(usb_mouse_t));
174 if (mouse == NULL) {
175 return NULL;
176 }
177 mouse->mouse_phone = -1;
178 mouse->wheel_phone = -1;
179
180 return mouse;
181}
182
183/*----------------------------------------------------------------------------*/
184
185static void usb_mouse_free(usb_mouse_t **mouse_dev)
186{
187 assert(mouse_dev != NULL && *mouse_dev != NULL);
188
189 // hangup phone to the console
190 if ((*mouse_dev)->mouse_phone >= 0) {
191 async_obsolete_hangup((*mouse_dev)->mouse_phone);
192 }
193
194 if ((*mouse_dev)->wheel_phone >= 0) {
195 async_obsolete_hangup((*mouse_dev)->wheel_phone);
196 }
197
198 free(*mouse_dev);
199 *mouse_dev = NULL;
200}
201
202/*----------------------------------------------------------------------------*/
203
204static void usb_mouse_send_wheel(const usb_mouse_t *mouse_dev, int wheel)
205{
206 unsigned int key = (wheel > 0) ? KC_UP : KC_DOWN;
207
208 if (mouse_dev->wheel_phone < 0) {
209 usb_log_warning(
210 "Connection to console not ready, wheel roll discarded.\n");
211 return;
212 }
213
214 int count = ((wheel < 0) ? -wheel : wheel) * ARROWS_PER_SINGLE_WHEEL;
215 int i;
216
217 for (i = 0; i < count; i++) {
218 /* Send arrow press and release. */
219 usb_log_debug2("Sending key %d to the console\n", key);
220 async_obsolete_msg_4(mouse_dev->wheel_phone, KBDEV_EVENT,
221 KEY_PRESS, key, 0, 0);
222 async_obsolete_msg_4(mouse_dev->wheel_phone, KBDEV_EVENT,
223 KEY_RELEASE, key, 0, 0);
224 }
225}
226
227/*----------------------------------------------------------------------------*/
228
229static int get_mouse_axis_move_value(uint8_t rid, usb_hid_report_t *report,
230 int32_t usage)
231{
232 int result = 0;
233
234 usb_hid_report_path_t *path = usb_hid_report_path();
235 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_GENERIC_DESKTOP,
236 usage);
237
238 usb_hid_report_path_set_report_id(path, rid);
239
240 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
241 report, NULL, path, USB_HID_PATH_COMPARE_END,
242 USB_HID_REPORT_TYPE_INPUT);
243
244 if (field != NULL) {
245 result = field->value;
246 }
247
248 usb_hid_report_path_free(path);
249
250 return result;
251}
252
253static bool usb_mouse_process_report(usb_hid_dev_t *hid_dev,
254 usb_mouse_t *mouse_dev)
255{
256 assert(mouse_dev != NULL);
257
258 if (mouse_dev->mouse_phone < 0) {
259 usb_log_warning(NAME " No console phone.\n");
260 return true;
261 }
262
263 int shift_x = get_mouse_axis_move_value(hid_dev->report_id,
264 hid_dev->report, USB_HIDUT_USAGE_GENERIC_DESKTOP_X);
265 int shift_y = get_mouse_axis_move_value(hid_dev->report_id,
266 hid_dev->report, USB_HIDUT_USAGE_GENERIC_DESKTOP_Y);
267 int wheel = get_mouse_axis_move_value(hid_dev->report_id,
268 hid_dev->report, USB_HIDUT_USAGE_GENERIC_DESKTOP_WHEEL);
269
270 if ((shift_x != 0) || (shift_y != 0)) {
271 async_obsolete_req_2_0(mouse_dev->mouse_phone,
272 MOUSEEV_MOVE_EVENT, shift_x, shift_y);
273 }
274
275 if (wheel != 0) {
276 usb_mouse_send_wheel(mouse_dev, wheel);
277 }
278
279 /*
280 * Buttons
281 */
282 usb_hid_report_path_t *path = usb_hid_report_path();
283 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_BUTTON, 0);
284 usb_hid_report_path_set_report_id(path, hid_dev->report_id);
285
286 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
287 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
288 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
289 USB_HID_REPORT_TYPE_INPUT);
290
291 while (field != NULL) {
292 usb_log_debug2(NAME " VALUE(%X) USAGE(%X)\n", field->value,
293 field->usage);
294
295 if (mouse_dev->buttons[field->usage - field->usage_minimum] == 0
296 && field->value != 0) {
297 async_obsolete_req_2_0(mouse_dev->mouse_phone,
298 MOUSEEV_BUTTON_EVENT, field->usage, 1);
299 mouse_dev->buttons[field->usage - field->usage_minimum]
300 = field->value;
301 } else if (mouse_dev->buttons[field->usage - field->usage_minimum] != 0
302 && field->value == 0) {
303 async_obsolete_req_2_0(mouse_dev->mouse_phone,
304 MOUSEEV_BUTTON_EVENT, field->usage, 0);
305 mouse_dev->buttons[field->usage - field->usage_minimum] =
306 field->value;
307 }
308
309 field = usb_hid_report_get_sibling(
310 hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
311 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
312 USB_HID_REPORT_TYPE_INPUT);
313 }
314
315 usb_hid_report_path_free(path);
316
317 return true;
318}
319
320/*----------------------------------------------------------------------------*/
321
322static int usb_mouse_create_function(usb_hid_dev_t *hid_dev, usb_mouse_t *mouse)
323{
324 assert(hid_dev != NULL);
325 assert(mouse != NULL);
326
327 /* Create the function exposed under /dev/devices. */
328 usb_log_debug("Creating DDF function %s...\n", HID_MOUSE_FUN_NAME);
329 ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
330 HID_MOUSE_FUN_NAME);
331 if (fun == NULL) {
332 usb_log_error("Could not create DDF function node.\n");
333 return ENOMEM;
334 }
335
336 fun->ops = &mouse->ops;
337 fun->driver_data = mouse;
338
339 int rc = ddf_fun_bind(fun);
340 if (rc != EOK) {
341 usb_log_error("Could not bind DDF function: %s.\n",
342 str_error(rc));
343 ddf_fun_destroy(fun);
344 return rc;
345 }
346
347 usb_log_debug("Adding DDF function to class %s...\n",
348 HID_MOUSE_CLASS_NAME);
349 rc = ddf_fun_add_to_class(fun, HID_MOUSE_CLASS_NAME);
350 if (rc != EOK) {
351 usb_log_error(
352 "Could not add DDF function to class %s: %s.\n",
353 HID_MOUSE_CLASS_NAME, str_error(rc));
354 ddf_fun_destroy(fun);
355 return rc;
356 }
357
358 /*
359 * Special function for acting as keyboard (wheel)
360 */
361 usb_log_debug("Creating DDF function %s...\n",
362 HID_MOUSE_WHEEL_FUN_NAME);
363 fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
364 HID_MOUSE_WHEEL_FUN_NAME);
365 if (fun == NULL) {
366 usb_log_error("Could not create DDF function node.\n");
367 return ENOMEM;
368 }
369
370 /*
371 * Store the initialized HID device and HID ops
372 * to the DDF function.
373 */
374 fun->ops = &mouse->ops;
375 fun->driver_data = mouse;
376
377 rc = ddf_fun_bind(fun);
378 if (rc != EOK) {
379 usb_log_error("Could not bind DDF function: %s.\n",
380 str_error(rc));
381 ddf_fun_destroy(fun);
382 return rc;
383 }
384
385 usb_log_debug("Adding DDF function to class %s...\n",
386 HID_MOUSE_WHEEL_CLASS_NAME);
387 rc = ddf_fun_add_to_class(fun, HID_MOUSE_WHEEL_CLASS_NAME);
388 if (rc != EOK) {
389 usb_log_error(
390 "Could not add DDF function to class %s: %s.\n",
391 HID_MOUSE_WHEEL_CLASS_NAME, str_error(rc));
392 ddf_fun_destroy(fun);
393 return rc;
394 }
395
396 return EOK;
397}
398
399/*----------------------------------------------------------------------------*/
400
401int usb_mouse_init(usb_hid_dev_t *hid_dev, void **data)
402{
403 usb_log_debug("Initializing HID/Mouse structure...\n");
404
405 if (hid_dev == NULL) {
406 usb_log_error("Failed to init keyboard structure: no structure"
407 " given.\n");
408 return EINVAL;
409 }
410
411 usb_mouse_t *mouse_dev = usb_mouse_new();
412 if (mouse_dev == NULL) {
413 usb_log_error("Error while creating USB/HID Mouse device "
414 "structure.\n");
415 return ENOMEM;
416 }
417
418 mouse_dev->buttons = (int32_t *)calloc(USB_MOUSE_BUTTON_COUNT,
419 sizeof(int32_t));
420
421 if (mouse_dev->buttons == NULL) {
422 usb_log_fatal("No memory!\n");
423 free(mouse_dev);
424 return ENOMEM;
425 }
426
427 // save the Mouse device structure into the HID device structure
428 *data = mouse_dev;
429
430 // set handler for incoming calls
431 mouse_dev->ops.default_handler = default_connection_handler;
432
433 // TODO: how to know if the device supports the request???
434 usbhid_req_set_idle(&hid_dev->usb_dev->ctrl_pipe,
435 hid_dev->usb_dev->interface_no, IDLE_RATE);
436
437 int rc = usb_mouse_create_function(hid_dev, mouse_dev);
438 if (rc != EOK) {
439 usb_mouse_free(&mouse_dev);
440 return rc;
441 }
442
443 return EOK;
444}
445
446/*----------------------------------------------------------------------------*/
447
448bool usb_mouse_polling_callback(usb_hid_dev_t *hid_dev, void *data)
449{
450 if (hid_dev == NULL || data == NULL) {
451 usb_log_error("Missing argument to the mouse polling callback."
452 "\n");
453 return false;
454 }
455
456 usb_mouse_t *mouse_dev = (usb_mouse_t *)data;
457
458 return usb_mouse_process_report(hid_dev, mouse_dev);
459}
460
461/*----------------------------------------------------------------------------*/
462
463void usb_mouse_deinit(usb_hid_dev_t *hid_dev, void *data)
464{
465 if (data != NULL) {
466 usb_mouse_free((usb_mouse_t **)&data);
467 }
468}
469
470/*----------------------------------------------------------------------------*/
471
472int usb_mouse_set_boot_protocol(usb_hid_dev_t *hid_dev)
473{
474 int rc = usb_hid_parse_report_descriptor(hid_dev->report,
475 USB_MOUSE_BOOT_REPORT_DESCRIPTOR,
476 USB_MOUSE_BOOT_REPORT_DESCRIPTOR_SIZE);
477
478 if (rc != EOK) {
479 usb_log_error("Failed to parse boot report descriptor: %s\n",
480 str_error(rc));
481 return rc;
482 }
483
484 rc = usbhid_req_set_protocol(&hid_dev->usb_dev->ctrl_pipe,
485 hid_dev->usb_dev->interface_no, USB_HID_PROTOCOL_BOOT);
486
487 if (rc != EOK) {
488 usb_log_warning("Failed to set boot protocol to the device: "
489 "%s\n", str_error(rc));
490 return rc;
491 }
492
493 return EOK;
494}
495
496/**
497 * @}
498 */
Note: See TracBrowser for help on using the repository browser.