source: mainline/uspace/drv/usbhid/multimedia/multimedia.c@ ef90ffb3

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

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

  • Property mode set to 100644
File size: 10.4 KB
RevLine 
[e3b5129]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
[d3b6d5e]34 * USB Keyboard multimedia keys subdriver.
[e3b5129]35 */
36
37
[d3b6d5e]38#include "multimedia.h"
[e3b5129]39#include "../usbhid.h"
[b20de1d]40#include "keymap.h"
[e3b5129]41
[faa44e58]42#include <usb/hid/hidparser.h>
[e3b5129]43#include <usb/debug.h>
[faa44e58]44#include <usb/hid/usages/core.h>
[b20de1d]45
[e3b5129]46#include <errno.h>
47#include <str_error.h>
48
[b20de1d]49#include <ipc/kbd.h>
50#include <io/console.h>
51
[d3b6d5e]52#define NAME "multimedia-keys"
[b20de1d]53
[31cfee16]54/*----------------------------------------------------------------------------*/
55/**
56 * Logitech UltraX device type.
57 */
[d3b6d5e]58typedef struct usb_multimedia_t {
[31cfee16]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). */
[d3b6d5e]64 size_t key_count;
[31cfee16]65 /** IPC phone to the console device (for sending key events). */
66 int console_phone;
[d3b6d5e]67} usb_multimedia_t;
[31cfee16]68
[b20de1d]69
[e3b5129]70/*----------------------------------------------------------------------------*/
[b20de1d]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
[65b458c4]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;
[b20de1d]91
[65b458c4]92 if (multim_dev == NULL) {
[b20de1d]93 async_answer_0(icallid, EINVAL);
94 return;
95 }
[e3b5129]96
[b20de1d]97 if (method == IPC_M_CONNECT_TO_ME) {
98 int callback = IPC_GET_ARG5(*icall);
[e3b5129]99
[d3b6d5e]100 if (multim_dev->console_phone != -1) {
[b20de1d]101 async_answer_0(icallid, ELIMIT);
102 return;
103 }
104
[d3b6d5e]105 multim_dev->console_phone = callback;
[b20de1d]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
[d3b6d5e]116static ddf_dev_ops_t multimedia_ops = {
[b20de1d]117 .default_handler = default_connection_handler
[e3b5129]118};
119
[b20de1d]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 */
[65b458c4]138static void usb_multimedia_push_ev(usb_hid_dev_t *hid_dev,
139 usb_multimedia_t *multim_dev, int type, unsigned int key)
[e3b5129]140{
[b20de1d]141 assert(hid_dev != NULL);
[65b458c4]142 assert(multim_dev != NULL);
[b20de1d]143
[65b458c4]144// usb_multimedia_t *multim_dev = (usb_multimedia_t *)hid_dev->data;
[b20de1d]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);
[d3b6d5e]154 if (multim_dev->console_phone < 0) {
[b20de1d]155 usb_log_warning(
156 "Connection to console not ready, key discarded.\n");
157 return;
158 }
[e3b5129]159
[d3b6d5e]160 async_msg_4(multim_dev->console_phone, KBD_EVENT, ev.type, ev.key,
[b20de1d]161 ev.mods, ev.c);
162}
163
164/*----------------------------------------------------------------------------*/
165
[d3b6d5e]166static void usb_multimedia_free(usb_multimedia_t **multim_dev)
[b20de1d]167{
[d3b6d5e]168 if (multim_dev == NULL || *multim_dev == NULL) {
[b20de1d]169 return;
170 }
171
172 // hangup phone to the console
[d3b6d5e]173 async_hangup((*multim_dev)->console_phone);
[b20de1d]174
175 // free all buffers
[d3b6d5e]176 if ((*multim_dev)->keys != NULL) {
177 free((*multim_dev)->keys);
[b20de1d]178 }
[d3b6d5e]179 if ((*multim_dev)->keys_old != NULL) {
180 free((*multim_dev)->keys_old);
[b20de1d]181 }
182
[d3b6d5e]183 free(*multim_dev);
184 *multim_dev = NULL;
[b20de1d]185}
186
187/*----------------------------------------------------------------------------*/
188
[65b458c4]189static int usb_multimedia_create_function(usb_hid_dev_t *hid_dev,
190 usb_multimedia_t *multim_dev)
[31cfee16]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
[d3b6d5e]200 fun->ops = &multimedia_ops;
[65b458c4]201 fun->driver_data = multim_dev; // TODO: maybe change to hid_dev->data
[31cfee16]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
[65b458c4]227int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
[b20de1d]228{
229 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
230 return EINVAL; /*! @todo Other return code? */
231 }
232
[d3b6d5e]233 usb_log_debug(NAME " Initializing HID/multimedia structure...\n");
[b20de1d]234
[d3b6d5e]235 usb_multimedia_t *multim_dev = (usb_multimedia_t *)malloc(
236 sizeof(usb_multimedia_t));
237 if (multim_dev == NULL) {
[b20de1d]238 return ENOMEM;
239 }
240
[d3b6d5e]241 multim_dev->console_phone = -1;
[b20de1d]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
[d59d0bb]248 multim_dev->key_count = usb_hid_report_size(
[3a6e423]249 hid_dev->report, 0, USB_HID_REPORT_TYPE_INPUT);
[d59d0bb]250
[b20de1d]251 usb_hid_report_path_free(path);
252
253 usb_log_debug(NAME " Size of the input report: %zu\n",
[d3b6d5e]254 multim_dev->key_count);
[b20de1d]255
[d3b6d5e]256 multim_dev->keys = (int32_t *)calloc(multim_dev->key_count,
[b20de1d]257 sizeof(int32_t));
258
[d3b6d5e]259 if (multim_dev->keys == NULL) {
[b20de1d]260 usb_log_fatal("No memory!\n");
[d3b6d5e]261 free(multim_dev);
[b20de1d]262 return ENOMEM;
263 }
264
[d3b6d5e]265 multim_dev->keys_old =
266 (int32_t *)calloc(multim_dev->key_count, sizeof(int32_t));
[b20de1d]267
[d3b6d5e]268 if (multim_dev->keys_old == NULL) {
[b20de1d]269 usb_log_fatal("No memory!\n");
[d3b6d5e]270 free(multim_dev->keys);
271 free(multim_dev);
[b20de1d]272 return ENOMEM;
273 }
274
275 /*! @todo Autorepeat */
276
277 // save the KBD device structure into the HID device structure
[65b458c4]278 *data = multim_dev;
[b20de1d]279
[d3b6d5e]280 usb_log_debug(NAME " HID/multimedia device structure initialized.\n");
[b20de1d]281
[65b458c4]282 int rc = usb_multimedia_create_function(hid_dev, multim_dev);
[b20de1d]283 if (rc != EOK) {
[d3b6d5e]284 usb_multimedia_free(&multim_dev);
[b20de1d]285 return rc;
286 }
287
[d3b6d5e]288 usb_log_debug(NAME " HID/multimedia structure initialized.\n");
[b20de1d]289
290 return EOK;
291}
292
293/*----------------------------------------------------------------------------*/
294
[65b458c4]295void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
[b20de1d]296{
297 if (hid_dev == NULL) {
298 return;
299 }
300
[65b458c4]301 if (data != NULL) {
302 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
[d3b6d5e]303 usb_multimedia_free(&multim_dev);
[b20de1d]304 }
[e3b5129]305}
306
307/*----------------------------------------------------------------------------*/
308
[65b458c4]309bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data,
[e3b5129]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);
[65b458c4]316
317 if (data == NULL) {
318 return EINVAL; // TODO: other error code?
319 }
320
321 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
[e3b5129]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();
[d3b6d5e]327 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
[cfbbe1d3]328
329 uint8_t report_id;
[6513110]330
331 int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size,
332 &report_id);
[e67399e]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
[cfbbe1d3]340 usb_hid_report_path_set_report_id(path, report_id);
[e50cd7f]341
[6513110]342 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
[b20de1d]343 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
344 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
[6513110]345 USB_HID_REPORT_TYPE_INPUT);
346
[3a6e423]347// unsigned int key;
[b20de1d]348
349 /*! @todo Is this iterating OK if done multiple times?
350 * @todo The parsing is not OK
351 */
[6513110]352 while (field != NULL) {
[3a6e423]353 if(field->value != 0) {
[97cb542]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);
[d59d0bb]358 const char *key_str =
359 usb_multimedia_usage_to_str(field->usage);
360 usb_log_info("Pressed key: %s\n", key_str);
[65b458c4]361 usb_multimedia_push_ev(hid_dev, multim_dev, KEY_PRESS,
362 key);
[3a6e423]363 }
[b20de1d]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 }
[e3b5129]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.