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

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

Creating DDF functions in subdrivers + saving input report.

  • Keyboard function.
  • Mouse function.
  • Lgtch UltraX function - added to class keyboard.
  • Saving input report in hid dev structure.
  • Property mode set to 100644
File size: 11.6 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 Logitech UltraX Keyboard sample driver.
35 */
36
37
38#include "lgtch-ultrax.h"
39#include "../usbhid.h"
40#include "keymap.h"
41
42#include <usb/classes/hidparser.h>
43#include <usb/debug.h>
44#include <usb/classes/hidut.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 "lgtch-ultrax"
53
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
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
90
91/*----------------------------------------------------------------------------*/
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;
120
121 if (method == IPC_M_CONNECT_TO_ME) {
122 int callback = IPC_GET_ARG5(*icall);
123
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
142};
143
144/*----------------------------------------------------------------------------*/
145
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 */
182static void usb_lgtch_push_ev(usb_hid_dev_t *hid_dev, int type,
183 unsigned int key)
184{
185 assert(hid_dev != NULL);
186 assert(hid_dev->data != NULL);
187
188 usb_lgtch_ultrax_t *lgtch_dev = (usb_lgtch_ultrax_t *)hid_dev->data;
189
190 console_event_t ev;
191
192 ev.type = type;
193 ev.key = key;
194 ev.mods = 0;
195
196 ev.c = 0;
197
198 usb_log_debug2(NAME " Sending key %d to the console\n", ev.key);
199 if (lgtch_dev->console_phone < 0) {
200 usb_log_warning(
201 "Connection to console not ready, key discarded.\n");
202 return;
203 }
204
205 async_msg_4(lgtch_dev->console_phone, KBD_EVENT, ev.type, ev.key,
206 ev.mods, ev.c);
207}
208
209/*----------------------------------------------------------------------------*/
210
211static void usb_lgtch_free(usb_lgtch_ultrax_t **lgtch_dev)
212{
213 if (lgtch_dev == NULL || *lgtch_dev == NULL) {
214 return;
215 }
216
217 // hangup phone to the console
218 async_hangup((*lgtch_dev)->console_phone);
219
220// if ((*lgtch_dev)->repeat_mtx != NULL) {
221// /* TODO: replace by some check and wait */
222// assert(!fibril_mutex_is_locked((*lgtch_dev)->repeat_mtx));
223// free((*lgtch_dev)->repeat_mtx);
224// }
225
226 // free all buffers
227 if ((*lgtch_dev)->keys != NULL) {
228 free((*lgtch_dev)->keys);
229 }
230 if ((*lgtch_dev)->keys_old != NULL) {
231 free((*lgtch_dev)->keys_old);
232 }
233
234 free(*lgtch_dev);
235 *lgtch_dev = NULL;
236}
237
238/*----------------------------------------------------------------------------*/
239
240static int usb_lgtch_create_function(usb_hid_dev_t *hid_dev)
241{
242 /* Create the function exposed under /dev/devices. */
243 ddf_fun_t *fun = ddf_fun_create(hid_dev->usb_dev->ddf_dev, fun_exposed,
244 NAME);
245 if (fun == NULL) {
246 usb_log_error("Could not create DDF function node.\n");
247 return ENOMEM;
248 }
249
250 /*
251 * Store the initialized HID device and HID ops
252 * to the DDF function.
253 */
254 fun->ops = &lgtch_ultrax_ops;
255 fun->driver_data = hid_dev; // TODO: maybe change to hid_dev->data
256
257 int rc = ddf_fun_bind(fun);
258 if (rc != EOK) {
259 usb_log_error("Could not bind DDF function: %s.\n",
260 str_error(rc));
261 // TODO: Can / should I destroy the DDF function?
262 ddf_fun_destroy(fun);
263 return rc;
264 }
265
266 rc = ddf_fun_add_to_class(fun, "keyboard");
267 if (rc != EOK) {
268 usb_log_error(
269 "Could not add DDF function to class 'keyboard': %s.\n",
270 str_error(rc));
271 // TODO: Can / should I destroy the DDF function?
272 ddf_fun_destroy(fun);
273 return rc;
274 }
275
276 return EOK;
277}
278
279/*----------------------------------------------------------------------------*/
280
281int usb_lgtch_init(struct usb_hid_dev *hid_dev)
282{
283 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
284 return EINVAL; /*! @todo Other return code? */
285 }
286
287 usb_log_debug(NAME " Initializing HID/lgtch_ultrax structure...\n");
288
289 usb_lgtch_ultrax_t *lgtch_dev = (usb_lgtch_ultrax_t *)malloc(
290 sizeof(usb_lgtch_ultrax_t));
291 if (lgtch_dev == NULL) {
292 return ENOMEM;
293 }
294
295 lgtch_dev->console_phone = -1;
296
297 usb_hid_report_path_t *path = usb_hid_report_path();
298 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
299
300 usb_hid_report_path_set_report_id(path, 1);
301
302 lgtch_dev->key_count = usb_hid_report_input_length(
303 hid_dev->report, path,
304 USB_HID_PATH_COMPARE_END | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY);
305 usb_hid_report_path_free(path);
306
307 usb_log_debug(NAME " Size of the input report: %zu\n",
308 lgtch_dev->key_count);
309
310 lgtch_dev->keys = (int32_t *)calloc(lgtch_dev->key_count,
311 sizeof(int32_t));
312
313 if (lgtch_dev->keys == NULL) {
314 usb_log_fatal("No memory!\n");
315 free(lgtch_dev);
316 return ENOMEM;
317 }
318
319 lgtch_dev->keys_old =
320 (int32_t *)calloc(lgtch_dev->key_count, sizeof(int32_t));
321
322 if (lgtch_dev->keys_old == NULL) {
323 usb_log_fatal("No memory!\n");
324 free(lgtch_dev->keys);
325 free(lgtch_dev);
326 return ENOMEM;
327 }
328
329 /*! @todo Autorepeat */
330
331 // save the KBD device structure into the HID device structure
332 hid_dev->data = lgtch_dev;
333
334 lgtch_dev->initialized = USB_LGTCH_STATUS_INITIALIZED;
335 usb_log_debug(NAME " HID/lgtch_ultrax device structure initialized.\n");
336
337 int rc = usb_lgtch_create_function(hid_dev);
338 if (rc != EOK) {
339 usb_lgtch_free(&lgtch_dev);
340 return rc;
341 }
342
343 usb_log_debug(NAME " HID/lgtch_ultrax structure initialized.\n");
344
345 return EOK;
346}
347
348/*----------------------------------------------------------------------------*/
349
350void usb_lgtch_deinit(struct usb_hid_dev *hid_dev)
351{
352 if (hid_dev == NULL) {
353 return;
354 }
355
356 if (hid_dev->data != NULL) {
357 usb_lgtch_ultrax_t *lgtch_dev =
358 (usb_lgtch_ultrax_t *)hid_dev->data;
359// if (usb_kbd_is_initialized(kbd_dev)) {
360// usb_kbd_mark_unusable(kbd_dev);
361// } else {
362 usb_lgtch_free(&lgtch_dev);
363 hid_dev->data = NULL;
364// }
365 }
366}
367
368/*----------------------------------------------------------------------------*/
369
370bool usb_lgtch_polling_callback(struct usb_hid_dev *hid_dev,
371 uint8_t *buffer, size_t buffer_size)
372{
373 // TODO: checks
374
375 usb_log_debug(NAME " usb_lgtch_polling_callback(%p, %p, %zu)\n",
376 hid_dev, buffer, buffer_size);
377
378 usb_log_debug(NAME " Calling usb_hid_parse_report() with "
379 "buffer %s\n", usb_debug_str_buffer(buffer, buffer_size, 0));
380
381 usb_hid_report_path_t *path = usb_hid_report_path();
382 usb_hid_report_path_append_item(path, 0xc, 0);
383
384 uint8_t report_id;
385
386 int rc = usb_hid_parse_report(hid_dev->report, buffer, buffer_size,
387 &report_id);
388
389 if (rc != EOK) {
390 usb_log_warning(NAME "Error in usb_hid_parse_report(): %s\n",
391 str_error(rc));
392 return true;
393 }
394
395 usb_hid_report_path_set_report_id(path, report_id);
396
397 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
398 hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
399 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
400 USB_HID_REPORT_TYPE_INPUT);
401
402 unsigned int key;
403
404 /*! @todo Is this iterating OK if done multiple times?
405 * @todo The parsing is not OK
406 */
407 while (field != NULL) {
408 usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)\n", field->value,
409 field->usage);
410
411 key = usb_lgtch_map_usage(field->usage);
412 usb_lgtch_push_ev(hid_dev, KEY_PRESS, key);
413
414 field = usb_hid_report_get_sibling(
415 hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
416 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
417 USB_HID_REPORT_TYPE_INPUT);
418 }
419
420 usb_hid_report_path_free(path);
421
422 return true;
423}
424
425/**
426 * @}
427 */
Note: See TracBrowser for help on using the repository browser.