source: mainline/uspace/drv/bus/usb/usbhid/multimedia/multimedia.c@ 03e3029

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

keep the drivers source tree tidy by using logical subdirectories

  • Property mode set to 100644
File size: 9.1 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#include <usb/hid/usages/consumer.h>
46
47#include <errno.h>
48#include <async.h>
49#include <async_obsolete.h>
50#include <str_error.h>
51
52#include <ipc/kbdev.h>
53#include <io/console.h>
54
55// FIXME: remove this header
56#include <kernel/ipc/ipc_methods.h>
57
58#define NAME "multimedia-keys"
59
60/*----------------------------------------------------------------------------*/
61/**
62 * Logitech UltraX device type.
63 */
64typedef struct usb_multimedia_t {
65 /** Previously pressed keys (not translated to key codes). */
66 //int32_t *keys_old;
67 /** Currently pressed keys (not translated to key codes). */
68 //int32_t *keys;
69 /** Count of stored keys (i.e. number of keys in the report). */
70 //size_t key_count;
71 /** IPC phone to the console device (for sending key events). */
72 int console_phone;
73} usb_multimedia_t;
74
75
76/*----------------------------------------------------------------------------*/
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
95 usb_multimedia_t *multim_dev = (usb_multimedia_t *)fun->driver_data;
96
97 if (multim_dev == NULL) {
98 async_answer_0(icallid, EINVAL);
99 return;
100 }
101
102 if (method == IPC_M_CONNECT_TO_ME) {
103 int callback = IPC_GET_ARG5(*icall);
104
105 if (multim_dev->console_phone != -1) {
106 async_answer_0(icallid, ELIMIT);
107 return;
108 }
109
110 multim_dev->console_phone = callback;
111 usb_log_debug(NAME " Saved phone to console: %d\n", callback);
112 async_answer_0(icallid, EOK);
113 return;
114 }
115
116 async_answer_0(icallid, EINVAL);
117}
118
119/*----------------------------------------------------------------------------*/
120
121static ddf_dev_ops_t multimedia_ops = {
122 .default_handler = default_connection_handler
123};
124
125/*----------------------------------------------------------------------------*/
126/**
127 * Processes key events.
128 *
129 * @note This function was copied from AT keyboard driver and modified to suit
130 * USB keyboard.
131 *
132 * @note Lock keys are not sent to the console, as they are completely handled
133 * in the driver. It may, however, be required later that the driver
134 * sends also these keys to application (otherwise it cannot use those
135 * keys at all).
136 *
137 * @param hid_dev
138 * @param lgtch_dev
139 * @param type Type of the event (press / release). Recognized values:
140 * KEY_PRESS, KEY_RELEASE
141 * @param key Key code of the key according to HID Usage Tables.
142 */
143static void usb_multimedia_push_ev(usb_hid_dev_t *hid_dev,
144 usb_multimedia_t *multim_dev, int type, unsigned int key)
145{
146 assert(hid_dev != NULL);
147 assert(multim_dev != NULL);
148
149 kbd_event_t ev;
150
151 ev.type = type;
152 ev.key = key;
153 ev.mods = 0;
154 ev.c = 0;
155
156 usb_log_debug2(NAME " Sending key %d to the console\n", ev.key);
157 if (multim_dev->console_phone < 0) {
158 usb_log_warning(
159 "Connection to console not ready, key discarded.\n");
160 return;
161 }
162
163 async_obsolete_msg_4(multim_dev->console_phone, KBDEV_EVENT, ev.type, ev.key,
164 ev.mods, ev.c);
165}
166
167/*----------------------------------------------------------------------------*/
168
169static void usb_multimedia_free(usb_multimedia_t **multim_dev)
170{
171 if (multim_dev == NULL || *multim_dev == NULL) {
172 return;
173 }
174
175 // hangup phone to the console
176 async_obsolete_hangup((*multim_dev)->console_phone);
177
178 free(*multim_dev);
179 *multim_dev = NULL;
180}
181
182/*----------------------------------------------------------------------------*/
183
184static int usb_multimedia_create_function(usb_hid_dev_t *hid_dev,
185 usb_multimedia_t *multim_dev)
186{
187 /* Create the function exposed under /dev/devices. */
188 ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
189 NAME);
190 if (fun == NULL) {
191 usb_log_error("Could not create DDF function node.\n");
192 return ENOMEM;
193 }
194
195 fun->ops = &multimedia_ops;
196 fun->driver_data = multim_dev; // TODO: maybe change to hid_dev->data
197
198 int rc = ddf_fun_bind(fun);
199 if (rc != EOK) {
200 usb_log_error("Could not bind DDF function: %s.\n",
201 str_error(rc));
202 // TODO: Can / should I destroy the DDF function?
203 ddf_fun_destroy(fun);
204 return rc;
205 }
206
207 usb_log_debug("%s function created (jandle: %" PRIun ").\n",
208 NAME, fun->handle);
209
210 rc = ddf_fun_add_to_class(fun, "keyboard");
211 if (rc != EOK) {
212 usb_log_error(
213 "Could not add DDF function to class 'keyboard': %s.\n",
214 str_error(rc));
215 // TODO: Can / should I destroy the DDF function?
216 ddf_fun_destroy(fun);
217 return rc;
218 }
219
220 return EOK;
221}
222
223/*----------------------------------------------------------------------------*/
224
225int usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
226{
227 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
228 return EINVAL; /*! @todo Other return code? */
229 }
230
231 usb_log_debug(NAME " Initializing HID/multimedia structure...\n");
232
233 usb_multimedia_t *multim_dev = (usb_multimedia_t *)malloc(
234 sizeof(usb_multimedia_t));
235 if (multim_dev == NULL) {
236 return ENOMEM;
237 }
238
239 multim_dev->console_phone = -1;
240
241 /*! @todo Autorepeat */
242
243 // save the KBD device structure into the HID device structure
244 *data = multim_dev;
245
246 usb_log_debug(NAME " HID/multimedia device structure initialized.\n");
247
248 int rc = usb_multimedia_create_function(hid_dev, multim_dev);
249 if (rc != EOK) {
250 usb_multimedia_free(&multim_dev);
251 return rc;
252 }
253
254 usb_log_debug(NAME " HID/multimedia structure initialized.\n");
255
256 return EOK;
257}
258
259/*----------------------------------------------------------------------------*/
260
261void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
262{
263 if (hid_dev == NULL) {
264 return;
265 }
266
267 if (data != NULL) {
268 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
269 usb_multimedia_free(&multim_dev);
270 }
271}
272
273/*----------------------------------------------------------------------------*/
274
275bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data)
276{
277 // TODO: checks
278 if (hid_dev == NULL || data == NULL) {
279 return false;
280 }
281
282 usb_multimedia_t *multim_dev = (usb_multimedia_t *)data;
283
284 usb_hid_report_path_t *path = usb_hid_report_path();
285 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
286
287 usb_hid_report_path_set_report_id(path, hid_dev->report_id);
288
289 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
290 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
291 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
292 USB_HID_REPORT_TYPE_INPUT);
293
294 /*! @todo Is this iterating OK if done multiple times?
295 * @todo The parsing is not OK
296 */
297 while (field != NULL) {
298 if(field->value != 0) {
299 usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)\n",
300 field->value, field->usage);
301 unsigned int key =
302 usb_multimedia_map_usage(field->usage);
303 const char *key_str =
304 usbhid_multimedia_usage_to_str(field->usage);
305 usb_log_info("Pressed key: %s\n", key_str);
306 usb_multimedia_push_ev(hid_dev, multim_dev, KEY_PRESS,
307 key);
308 }
309
310 field = usb_hid_report_get_sibling(
311 hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
312 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
313 USB_HID_REPORT_TYPE_INPUT);
314 }
315
316 usb_hid_report_path_free(path);
317
318 return true;
319}
320
321/**
322 * @}
323 */
Note: See TracBrowser for help on using the repository browser.