source: mainline/uspace/drv/hid/usbhid/multimedia/multimedia.c@ 3be9d10

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be9d10 was 3be9d10, checked in by Jakub Jermar <jakub@…>, 8 years ago

Get rid of ipc_callid_t

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/*
2 * Copyright (c) 2011 Lubos Slovak
3 * Copyright (c) 2011 Vojtech Horky
4 * Copyright (c) 2018 Ondrej Hlavaty
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup drvusbhid
32 * @{
33 */
34/**
35 * @file
36 * USB Keyboard multimedia keys subdriver.
37 */
38
39
40#include "multimedia.h"
41#include "../usbhid.h"
42#include "keymap.h"
43
44#include <usb/hid/hidparser.h>
45#include <usb/debug.h>
46#include <usb/hid/usages/core.h>
47#include <usb/hid/usages/consumer.h>
48
49#include <errno.h>
50#include <async.h>
51#include <str_error.h>
52
53#include <ipc/kbdev.h>
54#include <io/console.h>
55
56#define NAME "multimedia-keys"
57
58
59/**
60 * Logitech UltraX device type.
61 */
62typedef struct usb_multimedia_t {
63 /** Previously pressed keys (not translated to key codes). */
64 //int32_t *keys_old;
65 /** Currently pressed keys (not translated to key codes). */
66 //int32_t *keys;
67 /** Count of stored keys (i.e. number of keys in the report). */
68 //size_t key_count;
69 /** IPC session to the console device (for sending key events). */
70 async_sess_t *console_sess;
71} usb_multimedia_t;
72
73
74
75/**
76 * Default handler for IPC methods not handled by DDF.
77 *
78 * Currently recognizes only one method (IPC_M_CONNECT_TO_ME), in which case it
79 * assumes the caller is the console and thus it stores IPC session to it for
80 * later use by the driver to notify about key events.
81 *
82 * @param fun Device function handling the call.
83 * @param icallid Call id.
84 * @param icall Call data.
85 */
86static void default_connection_handler(ddf_fun_t *fun,
87 cap_call_handle_t icallid, ipc_call_t *icall)
88{
89 usb_log_debug(NAME " default_connection_handler()");
90
91 usb_multimedia_t *multim_dev = ddf_fun_data_get(fun);
92
93 async_sess_t *sess =
94 async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
95 if (sess != NULL) {
96 if (multim_dev->console_sess == NULL) {
97 multim_dev->console_sess = sess;
98 usb_log_debug(NAME " Saved session to console: %p",
99 sess);
100 async_answer_0(icallid, EOK);
101 } else
102 async_answer_0(icallid, ELIMIT);
103 } else
104 async_answer_0(icallid, EINVAL);
105}
106
107static ddf_dev_ops_t multimedia_ops = {
108 .default_handler = default_connection_handler
109};
110
111/**
112 * Processes key events.
113 *
114 * @note This function was copied from AT keyboard driver and modified to suit
115 * USB keyboard.
116 *
117 * @note Lock keys are not sent to the console, as they are completely handled
118 * in the driver. It may, however, be required later that the driver
119 * sends also these keys to application (otherwise it cannot use those
120 * keys at all).
121 *
122 * @param hid_dev
123 * @param multim_dev
124 * @param type Type of the event (press / release). Recognized values:
125 * KEY_PRESS, KEY_RELEASE
126 * @param key Key code of the key according to HID Usage Tables.
127 */
128static void usb_multimedia_push_ev(
129 usb_multimedia_t *multim_dev, int type, unsigned int key)
130{
131 assert(multim_dev != NULL);
132
133 const kbd_event_t ev = {
134 .type = type,
135 .key = key,
136 .mods = 0,
137 .c = 0,
138 };
139
140 usb_log_debug2(NAME " Sending key %d to the console", ev.key);
141 if (multim_dev->console_sess == NULL) {
142 usb_log_warning(
143 "Connection to console not ready, key discarded.\n");
144 return;
145 }
146
147 async_exch_t *exch = async_exchange_begin(multim_dev->console_sess);
148 if (exch != NULL) {
149 async_msg_4(exch, KBDEV_EVENT, ev.type, ev.key, ev.mods, ev.c);
150 async_exchange_end(exch);
151 } else {
152 usb_log_warning("Failed to send multimedia key.");
153 }
154}
155
156errno_t usb_multimedia_init(struct usb_hid_dev *hid_dev, void **data)
157{
158 if (hid_dev == NULL || hid_dev->usb_dev == NULL) {
159 return EINVAL;
160 }
161
162 usb_log_debug(NAME " Initializing HID/multimedia structure...");
163
164 /* Create the exposed function. */
165 ddf_fun_t *fun = usb_device_ddf_fun_create(
166 hid_dev->usb_dev, fun_exposed, NAME);
167 if (fun == NULL) {
168 usb_log_error("Could not create DDF function node.");
169 return ENOMEM;
170 }
171
172 ddf_fun_set_ops(fun, &multimedia_ops);
173
174 usb_multimedia_t *multim_dev =
175 ddf_fun_data_alloc(fun, sizeof(usb_multimedia_t));
176 if (multim_dev == NULL) {
177 ddf_fun_destroy(fun);
178 return ENOMEM;
179 }
180
181 multim_dev->console_sess = NULL;
182
183 //todo Autorepeat?
184
185 errno_t rc = ddf_fun_bind(fun);
186 if (rc != EOK) {
187 usb_log_error("Could not bind DDF function: %s.",
188 str_error(rc));
189 ddf_fun_destroy(fun);
190 return rc;
191 }
192
193 usb_log_debug(NAME " function created (handle: %" PRIun ").",
194 ddf_fun_get_handle(fun));
195
196 rc = ddf_fun_add_to_category(fun, "keyboard");
197 if (rc != EOK) {
198 usb_log_error(
199 "Could not add DDF function to category 'keyboard': %s.\n",
200 str_error(rc));
201 if (ddf_fun_unbind(fun) != EOK) {
202 usb_log_error("Failed to unbind %s, won't destroy.",
203 ddf_fun_get_name(fun));
204 } else {
205 ddf_fun_destroy(fun);
206 }
207 return rc;
208 }
209
210 /* Save the KBD device structure into the HID device structure. */
211 *data = fun;
212
213 usb_log_debug(NAME " HID/multimedia structure initialized.");
214 return EOK;
215}
216
217void usb_multimedia_deinit(struct usb_hid_dev *hid_dev, void *data)
218{
219 ddf_fun_t *fun = data;
220
221 usb_multimedia_t *multim_dev = ddf_fun_data_get(fun);
222
223 /* Hangup session to the console */
224 if (multim_dev->console_sess)
225 async_hangup(multim_dev->console_sess);
226 if (ddf_fun_unbind(fun) != EOK) {
227 usb_log_error("Failed to unbind %s, won't destroy.",
228 ddf_fun_get_name(fun));
229 } else {
230 usb_log_debug2("%s unbound.", ddf_fun_get_name(fun));
231 /* This frees multim_dev too as it was stored in
232 * fun->data */
233 ddf_fun_destroy(fun);
234 }
235}
236
237bool usb_multimedia_polling_callback(struct usb_hid_dev *hid_dev, void *data)
238{
239 // TODO: checks
240 ddf_fun_t *fun = data;
241 if (hid_dev == NULL) {
242 return false;
243 }
244
245 usb_multimedia_t *multim_dev = ddf_fun_data_get(fun);
246
247 usb_hid_report_path_t *path = usb_hid_report_path();
248 if (path == NULL)
249 return true; /* This might be a temporary failure. */
250
251 errno_t ret =
252 usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
253 if (ret != EOK) {
254 usb_hid_report_path_free(path);
255 return true; /* This might be a temporary failure. */
256 }
257
258 usb_hid_report_path_set_report_id(path, hid_dev->report_id);
259
260 usb_hid_report_field_t *field = usb_hid_report_get_sibling(
261 &hid_dev->report, NULL, path, USB_HID_PATH_COMPARE_END
262 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
263 USB_HID_REPORT_TYPE_INPUT);
264
265 //FIXME Is this iterating OK if done multiple times?
266 //FIXME The parsing is not OK. (what's wrong?)
267 while (field != NULL) {
268 if (field->value != 0) {
269 usb_log_debug(NAME " KEY VALUE(%X) USAGE(%X)",
270 field->value, field->usage);
271 const unsigned key =
272 usb_multimedia_map_usage(field->usage);
273 const char *key_str =
274 usbhid_multimedia_usage_to_str(field->usage);
275 usb_log_info("Pressed key: %s", key_str);
276 usb_multimedia_push_ev(multim_dev, KEY_PRESS, key);
277 }
278
279 field = usb_hid_report_get_sibling(
280 &hid_dev->report, field, path, USB_HID_PATH_COMPARE_END
281 | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY,
282 USB_HID_REPORT_TYPE_INPUT);
283 }
284
285 usb_hid_report_path_free(path);
286
287 return true;
288}
289/**
290 * @}
291 */
Note: See TracBrowser for help on using the repository browser.