source: mainline/uspace/drv/usbhid/multimedia/multimedia.c@ 02cacce

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 02cacce was ef90ffb3, checked in by Lubos Slovak <lubos.slovak@…>, 14 years ago

HID parser improved significantly, more subdrivers fix, mouse wheel.

  • Property mode set to 100644
File size: 10.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 Keyboard multimedia keys subdriver.
35 */
36
37
38#include "multimedia.h"
39#include "../usbhid.h"
40#include "keymap.h"
41
42#include <usb/hid/hidparser.h>
43#include <usb/debug.h>
44#include <usb/hid/usages/core.h>
45
46#include <errno.h>
47#include <str_error.h>
48
49#include <ipc/kbd.h>
50#include <io/console.h>
51
52#define NAME "multimedia-keys"
53
54/*----------------------------------------------------------------------------*/
55/**
56 * Logitech UltraX device type.
57 */
58typedef struct usb_multimedia_t {
59 /** Previously pressed keys (not translated to key codes). */
60 int32_t *keys_old;
61 /** Currently pressed keys (not translated to key codes). */
62 int32_t *keys;
63 /** Count of stored keys (i.e. number of keys in the report). */
64 size_t key_count;
65 /** IPC phone to the console device (for sending key events). */
66 int console_phone;
67} usb_multimedia_t;
68
69
70/*----------------------------------------------------------------------------*/
71/**
72 * Default handler for IPC methods not handled by DDF.
73 *
74 * Currently recognizes only one method (IPC_M_CONNECT_TO_ME), in which case it
75 * assumes the caller is the console and thus it stores IPC phone to it for
76 * later use by the driver to notify about key events.
77 *
78 * @param fun Device function handling the call.
79 * @param icallid Call id.
80 * @param icall Call data.
81 */
82static void default_connection_handler(ddf_fun_t *fun,
83 ipc_callid_t icallid, ipc_call_t *icall)
84{
85 usb_log_debug(NAME " default_connection_handler()\n");
86
87 sysarg_t method = IPC_GET_IMETHOD(*icall);
88
89 usb_multimedia_t *multim_dev = (usb_multimedia_t *)fun->driver_data;
90 //usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)fun->driver_data;
91
92 if (multim_dev == NULL) {
93 async_answer_0(icallid, EINVAL);
94 return;
95 }
96
97 if (method == IPC_M_CONNECT_TO_ME) {
98 int callback = IPC_GET_ARG5(*icall);
99
100 if (multim_dev->console_phone != -1) {
101 async_answer_0(icallid, ELIMIT);
102 return;
103 }
104
105 multim_dev->console_phone = callback;
106 usb_log_debug(NAME " Saved phone to console: %d\n", callback);
107 async_answer_0(icallid, EOK);
108 return;
109 }
110
111 async_answer_0(icallid, EINVAL);
112}
113
114/*----------------------------------------------------------------------------*/
115
116static ddf_dev_ops_t multimedia_ops = {
117 .default_handler = default_connection_handler
118};
119
120/*----------------------------------------------------------------------------*/
121/**
122 * Processes key events.
123 *
124 * @note This function was copied from AT keyboard driver and modified to suit
125 * USB keyboard.
126 *
127 * @note Lock keys are not sent to the console, as they are completely handled
128 * in the driver. It may, however, be required later that the driver
129 * sends also these keys to application (otherwise it cannot use those
130 * keys at all).
131 *
132 * @param hid_dev
133 * @param lgtch_dev
134 * @param type Type of the event (press / release). Recognized values:
135 * KEY_PRESS, KEY_RELEASE
136 * @param key Key code of the key according to HID Usage Tables.
137 */
138static void usb_multimedia_push_ev(usb_hid_dev_t *hid_dev,
139 usb_multimedia_t *multim_dev, int type, unsigned int key)
140{
141 assert(hid_dev != NULL);
142 assert(multim_dev != NULL);
143
144// usb_multimedia_t *multim_dev = (usb_multimedia_t *)hid_dev->data;
145
146 console_event_t ev;
147
148 ev.type = type;
149 ev.key = key;
150 ev.mods = 0;
151 ev.c = 0;
152
153 usb_log_debug2(NAME " Sending key %d to the console\n", ev.key);
154 if (multim_dev->console_phone < 0) {
155 usb_log_warning(
156 "Connection to console not ready, key discarded.\n");
157 return;
158 }
159
160 async_msg_4(multim_dev->console_phone, KBD_EVENT, ev.type, ev.key,
161 ev.mods, ev.c);
162}
163
164/*----------------------------------------------------------------------------*/
165
166static void usb_multimedia_free(usb_multimedia_t **multim_dev)
167{
168 if (multim_dev == NULL || *multim_dev == NULL) {
169 return;
170 }
171
172 // hangup phone to the console
173 async_hangup((*multim_dev)->console_phone);
174
175 // free all buffers
176 if ((*multim_dev)->keys != NULL) {
177 free((*multim_dev)->keys);
178 }
179 if ((*multim_dev)->keys_old != NULL) {
180 free((*multim_dev)->keys_old);
181 }
182
183 free(*multim_dev);
184 *multim_dev = NULL;
185}
186
187/*----------------------------------------------------------------------------*/
188
189static int usb_multimedia_create_function(usb_hid_dev_t *hid_dev,
190 usb_multimedia_t *multim_dev)
191{
192 /* Create the function exposed under /dev/devices. */
193 ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
194 NAME);
195 if (fun == NULL) {
196 usb_log_error("Could not create DDF function node.\n");
197 return ENOMEM;
198 }
199
200 fun->ops = &multimedia_ops;
201 fun->driver_data = multim_dev; // TODO: maybe change to hid_dev->data
202
203 int rc = ddf_fun_bind(fun);
204 if (rc != EOK) {
205 usb_log_error("Could not bind DDF function: %s.\n",
206 str_error(rc));
207 // TODO: Can / should I destroy the DDF function?
208 ddf_fun_destroy(fun);
209 return rc;
210 }
211
212 rc = ddf_fun_add_to_class(fun, "keyboard");
213 if (rc != EOK) {
214 usb_log_error(
215 "Could not add DDF function to class 'keyboard': %s.\n",
216 str_error(rc));
217 // TODO: Can / should I destroy the DDF function?
218 ddf_fun_destroy(fun);
219 return rc;
220 }
221
222 return EOK;
223}
224
225/*----------------------------------------------------------------------------*/
226
227int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
228{
229 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
230 return EINVAL; /*! @todo Other return code? */
231 }
232
233 usb_log_debug(NAME " Initializing HID/multimedia structure...\n");
234
235 usb_multimedia_t *multim_dev = (usb_multimedia_t *)malloc(
236 sizeof(usb_multimedia_t));
237 if (multim_dev == NULL) {
238 return ENOMEM;
239 }
240
241 multim_dev->console_phone = -1;
242
243 usb_hid_report_path_t *path = usb_hid_report_path();
244 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
245
246 usb_hid_report_path_set_report_id(path, 1);
247
248 multim_dev->key_count = usb_hid_report_size(
249 hid_dev->report, 0, USB_HID_REPORT_TYPE_INPUT);
250
251 usb_hid_report_path_free(path);
252
253 usb_log_debug(NAME " Size of the input report: %zu\n",
254 multim_dev->key_count);
255
256 multim_dev->keys = (int32_t *)calloc(multim_dev->key_count,
257 sizeof(int32_t));
258
259 if (multim_dev->keys == NULL) {
260 usb_log_fatal("No memory!\n");
261 free(multim_dev);
262 return ENOMEM;
263 }
264
265 multim_dev->keys_old =
266 (int32_t *)calloc(multim_dev->key_count, sizeof(int32_t));
267
268 if (multim_dev->keys_old == NULL) {
269 usb_log_fatal("No memory!\n");
270 free(multim_dev->keys);
271 free(multim_dev);
272 return ENOMEM;
273 }
274
275 /*! @todo Autorepeat */
276
277 // save the KBD device structure into the HID device structure
278 *data = multim_dev;
279
280 usb_log_debug(NAME " HID/multimedia device structure initialized.\n");
281
282 int rc = usb_multimedia_create_function(hid_dev, multim_dev);
283 if (rc != EOK) {
284 usb_multimedia_free(&multim_dev);
285 return rc;
286 }
287
288 usb_log_debug(NAME " HID/multimedia structure initialized.\n");
289
290 return EOK;
291}
292
293/*----------------------------------------------------------------------------*/
294
295void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
296{
297 if (hid_dev == NULL) {
298 return;
299 }
300
301 if (data != NULL) {
302 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
303 usb_multimedia_free(&multim_dev);
304 }
305}
306
307/*----------------------------------------------------------------------------*/
308
309bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data,
310 uint8_t *buffer, size_t buffer_size)
311{
312 // TODO: checks
313
314 usb_log_debug(NAME " usb_lgtch_polling_callback(%p, %p, %zu)\n",
315 hid_dev, buffer, buffer_size);
316
317 if (data == NULL) {
318 return EINVAL; // TODO: other error code?
319 }
320
321 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
322
323 usb_log_debug(NAME " Calling usb_hid_parse_report() with "
324 "buffer %s\n", usb_debug_str_buffer(buffer, buffer_size, 0));
325
326 usb_hid_report_path_t *path = usb_hid_report_path();
327 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
328
329 uint8_t report_id;
330
331 int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size,
332 &report_id);
333
334 if (rc != EOK) {
335 usb_log_warning(NAME "Error in usb_hid_parse_report(): %s\n",
336 str_error(rc));
337 return true;
338 }
339
340 usb_hid_report_path_set_report_id(path, report_id);
341
342 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
343 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
344 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
345 USB_HID_REPORT_TYPE_INPUT);
346
347// unsigned int key;
348
349 /*! @todo Is this iterating OK if done multiple times?
350 * @todo The parsing is not OK
351 */
352 while (field != NULL) {
353 if(field->value != 0) {
354 usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)\n",
355 field->value, field->usage);
356 unsigned int key =
357 usb_multimedia_map_usage(field->usage);
358 const char *key_str =
359 usb_multimedia_usage_to_str(field->usage);
360 usb_log_info("Pressed key: %s\n", key_str);
361 usb_multimedia_push_ev(hid_dev, multim_dev, KEY_PRESS,
362 key);
363 }
364
365 field = usb_hid_report_get_sibling(
366 hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
367 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
368 USB_HID_REPORT_TYPE_INPUT);
369 }
370
371 usb_hid_report_path_free(path);
372
373 return true;
374}
375
376/**
377 * @}
378 */
Note: See TracBrowser for help on using the repository browser.