source: mainline/uspace/drv/usbhid/multimedia/multimedia.c@ 4e78236

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

Removed generic subdriver from mappings + some debug output

  • Property mode set to 100644
File size: 10.5 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 usb_log_debug("%s function created. Handle: %d\n", NAME, fun->handle);
213
214 rc = ddf_fun_add_to_class(fun, "keyboard");
215 if (rc != EOK) {
216 usb_log_error(
217 "Could not add DDF function to class 'keyboard': %s.\n",
218 str_error(rc));
219 // TODO: Can / should I destroy the DDF function?
220 ddf_fun_destroy(fun);
221 return rc;
222 }
223
224 return EOK;
225}
226
227/*----------------------------------------------------------------------------*/
228
229int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
230{
231 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
232 return EINVAL; /*! @todo Other return code? */
233 }
234
235 usb_log_debug(NAME " Initializing HID/multimedia structure...\n");
236
237 usb_multimedia_t *multim_dev = (usb_multimedia_t *)malloc(
238 sizeof(usb_multimedia_t));
239 if (multim_dev == NULL) {
240 return ENOMEM;
241 }
242
243 multim_dev->console_phone = -1;
244
245 usb_hid_report_path_t *path = usb_hid_report_path();
246 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
247
248 usb_hid_report_path_set_report_id(path, 1);
249
250 multim_dev->key_count = usb_hid_report_size(
251 hid_dev->report, 0, USB_HID_REPORT_TYPE_INPUT);
252
253 usb_hid_report_path_free(path);
254
255 usb_log_debug(NAME " Size of the input report: %zu\n",
256 multim_dev->key_count);
257
258 multim_dev->keys = (int32_t *)calloc(multim_dev->key_count,
259 sizeof(int32_t));
260
261 if (multim_dev->keys == NULL) {
262 usb_log_fatal("No memory!\n");
263 free(multim_dev);
264 return ENOMEM;
265 }
266
267 multim_dev->keys_old =
268 (int32_t *)calloc(multim_dev->key_count, sizeof(int32_t));
269
270 if (multim_dev->keys_old == NULL) {
271 usb_log_fatal("No memory!\n");
272 free(multim_dev->keys);
273 free(multim_dev);
274 return ENOMEM;
275 }
276
277 /*! @todo Autorepeat */
278
279 // save the KBD device structure into the HID device structure
280 *data = multim_dev;
281
282 usb_log_debug(NAME " HID/multimedia device structure initialized.\n");
283
284 int rc = usb_multimedia_create_function(hid_dev, multim_dev);
285 if (rc != EOK) {
286 usb_multimedia_free(&multim_dev);
287 return rc;
288 }
289
290 usb_log_debug(NAME " HID/multimedia structure initialized.\n");
291
292 return EOK;
293}
294
295/*----------------------------------------------------------------------------*/
296
297void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
298{
299 if (hid_dev == NULL) {
300 return;
301 }
302
303 if (data != NULL) {
304 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
305 usb_multimedia_free(&multim_dev);
306 }
307}
308
309/*----------------------------------------------------------------------------*/
310
311bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data,
312 uint8_t *buffer, size_t buffer_size)
313{
314 // TODO: checks
315
316 usb_log_debug(NAME " usb_lgtch_polling_callback(%p, %p, %zu)\n",
317 hid_dev, buffer, buffer_size);
318
319 if (data == NULL) {
320 return EINVAL; // TODO: other error code?
321 }
322
323 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
324
325 usb_log_debug(NAME " Calling usb_hid_parse_report() with "
326 "buffer %s\n", usb_debug_str_buffer(buffer, buffer_size, 0));
327
328 usb_hid_report_path_t *path = usb_hid_report_path();
329 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
330
331 uint8_t report_id;
332
333 int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size,
334 &report_id);
335
336 if (rc != EOK) {
337 usb_log_warning(NAME "Error in usb_hid_parse_report(): %s\n",
338 str_error(rc));
339 return true;
340 }
341
342 usb_hid_report_path_set_report_id(path, report_id);
343
344 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
345 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
346 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
347 USB_HID_REPORT_TYPE_INPUT);
348
349// unsigned int key;
350
351 /*! @todo Is this iterating OK if done multiple times?
352 * @todo The parsing is not OK
353 */
354 while (field != NULL) {
355 if(field->value != 0) {
356 usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)\n",
357 field->value, field->usage);
358 unsigned int key =
359 usb_multimedia_map_usage(field->usage);
360 const char *key_str =
361 usb_multimedia_usage_to_str(field->usage);
362 usb_log_info("Pressed key: %s\n", key_str);
363 usb_multimedia_push_ev(hid_dev, multim_dev, KEY_PRESS,
364 key);
365 }
366
367 field = usb_hid_report_get_sibling(
368 hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
369 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
370 USB_HID_REPORT_TYPE_INPUT);
371 }
372
373 usb_hid_report_path_free(path);
374
375 return true;
376}
377
378/**
379 * @}
380 */
Note: See TracBrowser for help on using the repository browser.