source: mainline/uspace/drv/usbhid/multimedia/multimedia.c@ 79ae36dd

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

new async framework with integrated exchange tracking

  • strict isolation between low-level IPC and high-level async framework with integrated exchange tracking
    • each IPC connection is represented by an async_sess_t structure
    • each IPC exchange is represented by an async_exch_t structure
    • exchange management is either based on atomic messages (EXCHANGE_ATOMIC), locking (EXCHANGE_SERIALIZE) or connection cloning (EXCHANGE_CLONE)
  • async_obsolete: temporary compatibility layer to keep old async clients working (several pieces of code are currently broken, but only non-essential functionality)
  • IPC_M_PHONE_HANGUP is now method no. 0 (for elegant boolean evaluation)
  • IPC_M_DEBUG_ALL has been renamed to IPC_M_DEBUG
  • IPC_M_PING has been removed (VFS protocol now has VFS_IN_PING)
  • console routines in libc have been rewritten for better abstraction
  • additional use for libc-private header files (FILE structure opaque to the client)
  • various cstyle changes (typos, indentation, missing externs in header files, improved comments, etc.)
  • Property mode set to 100644
File size: 9.6 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>
[d7c72db]45#include <usb/hid/usages/consumer.h>
[b20de1d]46
[e3b5129]47#include <errno.h>
[79ae36dd]48#include <async.h>
49#include <async_obsolete.h>
[e3b5129]50#include <str_error.h>
51
[b20de1d]52#include <ipc/kbd.h>
53#include <io/console.h>
54
[79ae36dd]55// FIXME: remove this header
56#include <kernel/ipc/ipc_methods.h>
57
[d3b6d5e]58#define NAME "multimedia-keys"
[b20de1d]59
[31cfee16]60/*----------------------------------------------------------------------------*/
61/**
62 * Logitech UltraX device type.
63 */
[d3b6d5e]64typedef struct usb_multimedia_t {
[31cfee16]65 /** Previously pressed keys (not translated to key codes). */
[3e95cd7]66 //int32_t *keys_old;
[31cfee16]67 /** Currently pressed keys (not translated to key codes). */
[3e95cd7]68 //int32_t *keys;
[31cfee16]69 /** Count of stored keys (i.e. number of keys in the report). */
[3e95cd7]70 //size_t key_count;
[31cfee16]71 /** IPC phone to the console device (for sending key events). */
72 int console_phone;
[d3b6d5e]73} usb_multimedia_t;
[31cfee16]74
[b20de1d]75
[e3b5129]76/*----------------------------------------------------------------------------*/
[b20de1d]77/**
78 * Default handler for IPC methods not handled by DDF.
79 *
80 * Currently recognizes only one method (IPC_M_CONNECT_TO_ME), in which case it
81 * assumes the caller is the console and thus it stores IPC phone to it for
82 * later use by the driver to notify about key events.
83 *
84 * @param fun Device function handling the call.
85 * @param icallid Call id.
86 * @param icall Call data.
87 */
88static void default_connection_handler(ddf_fun_t *fun,
89 ipc_callid_t icallid, ipc_call_t *icall)
90{
91 usb_log_debug(NAME " default_connection_handler()\n");
92
93 sysarg_t method = IPC_GET_IMETHOD(*icall);
94
[65b458c4]95 usb_multimedia_t *multim_dev = (usb_multimedia_t *)fun->driver_data;
96 //usb_hid_dev_t *hid_dev = (usb_hid_dev_t *)fun->driver_data;
[b20de1d]97
[65b458c4]98 if (multim_dev == NULL) {
[b20de1d]99 async_answer_0(icallid, EINVAL);
100 return;
101 }
[e3b5129]102
[b20de1d]103 if (method == IPC_M_CONNECT_TO_ME) {
104 int callback = IPC_GET_ARG5(*icall);
[e3b5129]105
[d3b6d5e]106 if (multim_dev->console_phone != -1) {
[b20de1d]107 async_answer_0(icallid, ELIMIT);
108 return;
109 }
110
[d3b6d5e]111 multim_dev->console_phone = callback;
[b20de1d]112 usb_log_debug(NAME " Saved phone to console: %d\n", callback);
113 async_answer_0(icallid, EOK);
114 return;
115 }
116
117 async_answer_0(icallid, EINVAL);
118}
119
120/*----------------------------------------------------------------------------*/
121
[d3b6d5e]122static ddf_dev_ops_t multimedia_ops = {
[b20de1d]123 .default_handler = default_connection_handler
[e3b5129]124};
125
[b20de1d]126/*----------------------------------------------------------------------------*/
127/**
128 * Processes key events.
129 *
130 * @note This function was copied from AT keyboard driver and modified to suit
131 * USB keyboard.
132 *
133 * @note Lock keys are not sent to the console, as they are completely handled
134 * in the driver. It may, however, be required later that the driver
135 * sends also these keys to application (otherwise it cannot use those
136 * keys at all).
137 *
138 * @param hid_dev
139 * @param lgtch_dev
140 * @param type Type of the event (press / release). Recognized values:
141 * KEY_PRESS, KEY_RELEASE
142 * @param key Key code of the key according to HID Usage Tables.
143 */
[65b458c4]144static void usb_multimedia_push_ev(usb_hid_dev_t *hid_dev,
145 usb_multimedia_t *multim_dev, int type, unsigned int key)
[e3b5129]146{
[b20de1d]147 assert(hid_dev != NULL);
[65b458c4]148 assert(multim_dev != NULL);
[b20de1d]149
[79ae36dd]150 kbd_event_t ev;
[b20de1d]151
152 ev.type = type;
153 ev.key = key;
154 ev.mods = 0;
155 ev.c = 0;
156
157 usb_log_debug2(NAME " Sending key %d to the console\n", ev.key);
[d3b6d5e]158 if (multim_dev->console_phone < 0) {
[b20de1d]159 usb_log_warning(
160 "Connection to console not ready, key discarded.\n");
161 return;
162 }
[e3b5129]163
[79ae36dd]164 async_obsolete_msg_4(multim_dev->console_phone, KBD_EVENT, ev.type, ev.key,
[b20de1d]165 ev.mods, ev.c);
166}
167
168/*----------------------------------------------------------------------------*/
169
[d3b6d5e]170static void usb_multimedia_free(usb_multimedia_t **multim_dev)
[b20de1d]171{
[d3b6d5e]172 if (multim_dev == NULL || *multim_dev == NULL) {
[b20de1d]173 return;
174 }
175
176 // hangup phone to the console
[79ae36dd]177 async_obsolete_hangup((*multim_dev)->console_phone);
[b20de1d]178
[d3b6d5e]179 free(*multim_dev);
180 *multim_dev = NULL;
[b20de1d]181}
182
183/*----------------------------------------------------------------------------*/
184
[65b458c4]185static int usb_multimedia_create_function(usb_hid_dev_t *hid_dev,
186 usb_multimedia_t *multim_dev)
[31cfee16]187{
188 /* Create the function exposed under /dev/devices. */
189 ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
190 NAME);
191 if (fun == NULL) {
192 usb_log_error("Could not create DDF function node.\n");
193 return ENOMEM;
194 }
195
[d3b6d5e]196 fun->ops = &multimedia_ops;
[65b458c4]197 fun->driver_data = multim_dev; // TODO: maybe change to hid_dev->data
[31cfee16]198
199 int rc = ddf_fun_bind(fun);
200 if (rc != EOK) {
201 usb_log_error("Could not bind DDF function: %s.\n",
202 str_error(rc));
203 // TODO: Can / should I destroy the DDF function?
204 ddf_fun_destroy(fun);
205 return rc;
206 }
207
[0d59d0e9]208 usb_log_debug("%s function created (jandle: %" PRIun ").\n",
209 NAME, fun->handle);
[4939490]210
[31cfee16]211 rc = ddf_fun_add_to_class(fun, "keyboard");
212 if (rc != EOK) {
213 usb_log_error(
214 "Could not add DDF function to class 'keyboard': %s.\n",
215 str_error(rc));
216 // TODO: Can / should I destroy the DDF function?
217 ddf_fun_destroy(fun);
218 return rc;
219 }
220
221 return EOK;
222}
223
224/*----------------------------------------------------------------------------*/
225
[65b458c4]226int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
[b20de1d]227{
228 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
229 return EINVAL; /*! @todo Other return code? */
230 }
231
[d3b6d5e]232 usb_log_debug(NAME " Initializing HID/multimedia structure...\n");
[b20de1d]233
[d3b6d5e]234 usb_multimedia_t *multim_dev = (usb_multimedia_t *)malloc(
235 sizeof(usb_multimedia_t));
236 if (multim_dev == NULL) {
[b20de1d]237 return ENOMEM;
238 }
239
[d3b6d5e]240 multim_dev->console_phone = -1;
[b20de1d]241
242 /*! @todo Autorepeat */
243
244 // save the KBD device structure into the HID device structure
[65b458c4]245 *data = multim_dev;
[b20de1d]246
[d3b6d5e]247 usb_log_debug(NAME " HID/multimedia device structure initialized.\n");
[b20de1d]248
[65b458c4]249 int rc = usb_multimedia_create_function(hid_dev, multim_dev);
[b20de1d]250 if (rc != EOK) {
[d3b6d5e]251 usb_multimedia_free(&multim_dev);
[b20de1d]252 return rc;
253 }
254
[d3b6d5e]255 usb_log_debug(NAME " HID/multimedia structure initialized.\n");
[b20de1d]256
257 return EOK;
258}
259
260/*----------------------------------------------------------------------------*/
261
[65b458c4]262void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
[b20de1d]263{
264 if (hid_dev == NULL) {
265 return;
266 }
267
[65b458c4]268 if (data != NULL) {
269 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
[d3b6d5e]270 usb_multimedia_free(&multim_dev);
[b20de1d]271 }
[e3b5129]272}
273
274/*----------------------------------------------------------------------------*/
275
[65b458c4]276bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data,
[e3b5129]277 uint8_t *buffer, size_t buffer_size)
278{
279 // TODO: checks
[19e0560e]280 if (hid_dev == NULL || data == NULL || buffer == NULL) {
281 return false;
282 }
[e3b5129]283
284 usb_log_debug(NAME " usb_lgtch_polling_callback(%p, %p, %zu)\n",
285 hid_dev, buffer, buffer_size);
[65b458c4]286
287 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
[e3b5129]288
289 usb_log_debug(NAME " Calling usb_hid_parse_report() with "
290 "buffer %s\n", usb_debug_str_buffer(buffer, buffer_size, 0));
291
292 usb_hid_report_path_t *path = usb_hid_report_path();
[d3b6d5e]293 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
[cfbbe1d3]294
295 uint8_t report_id;
[6513110]296
297 int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size,
298 &report_id);
[e67399e]299
300 if (rc != EOK) {
301 usb_log_warning(NAME "Error in usb_hid_parse_report(): %s\n",
302 str_error(rc));
303 return true;
304 }
305
[cfbbe1d3]306 usb_hid_report_path_set_report_id(path, report_id);
[e50cd7f]307
[6513110]308 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
[b20de1d]309 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
310 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
[6513110]311 USB_HID_REPORT_TYPE_INPUT);
[19e0560e]312
[b20de1d]313 /*! @todo Is this iterating OK if done multiple times?
314 * @todo The parsing is not OK
315 */
[6513110]316 while (field != NULL) {
[3a6e423]317 if(field->value != 0) {
[97cb542]318 usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)\n",
319 field->value, field->usage);
320 unsigned int key =
321 usb_multimedia_map_usage(field->usage);
[d59d0bb]322 const char *key_str =
[d7c72db]323 usbhid_multimedia_usage_to_str(field->usage);
[d59d0bb]324 usb_log_info("Pressed key: %s\n", key_str);
[65b458c4]325 usb_multimedia_push_ev(hid_dev, multim_dev, KEY_PRESS,
326 key);
[3a6e423]327 }
[b20de1d]328
329 field = usb_hid_report_get_sibling(
330 hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
331 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
332 USB_HID_REPORT_TYPE_INPUT);
333 }
[e3b5129]334
335 usb_hid_report_path_free(path);
336
337 return true;
338}
339
340/**
341 * @}
342 */
Note: See TracBrowser for help on using the repository browser.