source: mainline/uspace/drv/usbhid/lgtch-ultrax/lgtch-ultrax.c@ 252cf2a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 252cf2a was 252cf2a, checked in by Matej Klonfar <maklf@…>, 14 years ago

Development changes merge
Correct parsing of usages in case of array items

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