source: mainline/uspace/drv/hid/usbhid/mouse/mousedev.c@ 999efa9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 999efa9 was 999efa9, checked in by Jakub Jermar <jakub@…>, 8 years ago

Reformat copyright messages

The goal is to have one copyright-holder per line so that it is easier
to do some sort of automatic processing.

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