source: mainline/uspace/lib/drv/generic/remote_usbhid.c@ 3c7702c0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3c7702c0 was 25a179e, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

IPC return values are always errno constants. Adjust types to reflect that.

In principle, IPC server is not allowed to return non-errno values via
the "main" return value, because kernel interprets it (e.g. EHANGUP).
It's still possible to return arbitrary additional return values alongside EOK,
which are not interpreted in normal communication.

  • Property mode set to 100644
File size: 11.5 KB
Line 
1/*
2 * Copyright (c) 2010-2011 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 libdrv
30 * @{
31 */
32/** @file
33 */
34
35#include <async.h>
36#include <errno.h>
37#include <assert.h>
38#include <stdio.h>
39#include <macros.h>
40
41#include "usbhid_iface.h"
42#include "ddf/driver.h"
43
44/** IPC methods for USB HID device interface. */
45typedef enum {
46 /** Get number of events reported in single burst.
47 * Parameters: none
48 * Answer:
49 * - Size of one report in bytes.
50 */
51 IPC_M_USBHID_GET_EVENT_LENGTH,
52 /** Get single event from the HID device.
53 * The word single refers to set of individual events that were
54 * available at particular point in time.
55 * Parameters:
56 * - flags
57 * The call is followed by data read expecting two concatenated
58 * arrays.
59 * Answer:
60 * - EOK - events returned
61 * - EAGAIN - no event ready (only in non-blocking mode)
62 *
63 * It is okay if the client requests less data. Extra data must
64 * be truncated by the driver.
65 *
66 * @todo Change this comment.
67 */
68 IPC_M_USBHID_GET_EVENT,
69
70 /** Get the size of the report descriptor from the HID device.
71 *
72 * Parameters:
73 * - none
74 * Answer:
75 * - EOK - method is implemented (expected always)
76 * Parameters of the answer:
77 * - Size of the report in bytes.
78 */
79 IPC_M_USBHID_GET_REPORT_DESCRIPTOR_LENGTH,
80
81 /** Get the report descriptor from the HID device.
82 *
83 * Parameters:
84 * - none
85 * The call is followed by data read expecting the descriptor itself.
86 * Answer:
87 * - EOK - report descriptor returned.
88 */
89 IPC_M_USBHID_GET_REPORT_DESCRIPTOR
90} usbhid_iface_funcs_t;
91
92/** Ask for event array length.
93 *
94 * @param dev_sess Session to DDF device providing USB HID interface.
95 *
96 * @return Number of usages returned or negative error code.
97 *
98 */
99int usbhid_dev_get_event_length(async_sess_t *dev_sess, size_t *size)
100{
101 if (!dev_sess)
102 return EINVAL;
103
104 async_exch_t *exch = async_exchange_begin(dev_sess);
105
106 sysarg_t len;
107 int rc = async_req_1_1(exch, DEV_IFACE_ID(USBHID_DEV_IFACE),
108 IPC_M_USBHID_GET_EVENT_LENGTH, &len);
109
110 async_exchange_end(exch);
111
112 if (rc == EOK) {
113 if (size != NULL)
114 *size = (size_t) len;
115 }
116
117 return rc;
118}
119
120/** Request for next event from HID device.
121 *
122 * @param[in] dev_sess Session to DDF device providing USB HID interface.
123 * @param[out] usage_pages Where to store usage pages.
124 * @param[out] usages Where to store usages (actual data).
125 * @param[in] usage_count Length of @p usage_pages and @p usages buffer
126 * (in items, not bytes).
127 * @param[out] actual_usage_count Number of usages actually returned by the
128 * device driver.
129 * @param[in] flags Flags (see USBHID_IFACE_FLAG_*).
130 *
131 * @return Error code.
132 *
133 */
134int usbhid_dev_get_event(async_sess_t *dev_sess, uint8_t *buf,
135 size_t size, size_t *actual_size, int *event_nr, unsigned int flags)
136{
137 if (!dev_sess)
138 return EINVAL;
139
140 if (buf == NULL)
141 return ENOMEM;
142
143 if (size == 0)
144 return EINVAL;
145
146 size_t buffer_size = size;
147 uint8_t *buffer = malloc(buffer_size);
148 if (buffer == NULL)
149 return ENOMEM;
150
151 async_exch_t *exch = async_exchange_begin(dev_sess);
152
153 ipc_call_t opening_request_call;
154 aid_t opening_request = async_send_2(exch,
155 DEV_IFACE_ID(USBHID_DEV_IFACE), IPC_M_USBHID_GET_EVENT,
156 flags, &opening_request_call);
157
158 if (opening_request == 0) {
159 async_exchange_end(exch);
160 free(buffer);
161 return ENOMEM;
162 }
163
164 ipc_call_t data_request_call;
165 aid_t data_request = async_data_read(exch, buffer, buffer_size,
166 &data_request_call);
167
168 async_exchange_end(exch);
169
170 if (data_request == 0) {
171 async_forget(opening_request);
172 free(buffer);
173 return ENOMEM;
174 }
175
176 int data_request_rc;
177 int opening_request_rc;
178 async_wait_for(data_request, &data_request_rc);
179 async_wait_for(opening_request, &opening_request_rc);
180
181 if (data_request_rc != EOK) {
182 /* Prefer return code of the opening request. */
183 if (opening_request_rc != EOK)
184 return (int) opening_request_rc;
185 else
186 return (int) data_request_rc;
187 }
188
189 if (opening_request_rc != EOK)
190 return (int) opening_request_rc;
191
192 size_t act_size = IPC_GET_ARG2(data_request_call);
193
194 /* Copy the individual items. */
195 memcpy(buf, buffer, act_size);
196
197 if (actual_size != NULL)
198 *actual_size = act_size;
199
200 if (event_nr != NULL)
201 *event_nr = IPC_GET_ARG1(opening_request_call);
202
203 return EOK;
204}
205
206int usbhid_dev_get_report_descriptor_length(async_sess_t *dev_sess,
207 size_t *size)
208{
209 if (!dev_sess)
210 return EINVAL;
211
212 async_exch_t *exch = async_exchange_begin(dev_sess);
213
214 sysarg_t arg_size;
215 int rc = async_req_1_1(exch, DEV_IFACE_ID(USBHID_DEV_IFACE),
216 IPC_M_USBHID_GET_REPORT_DESCRIPTOR_LENGTH, &arg_size);
217
218 async_exchange_end(exch);
219
220 if (rc == EOK) {
221 if (size != NULL)
222 *size = (size_t) arg_size;
223 }
224
225 return rc;
226}
227
228int usbhid_dev_get_report_descriptor(async_sess_t *dev_sess, uint8_t *buf,
229 size_t size, size_t *actual_size)
230{
231 if (!dev_sess)
232 return EINVAL;
233
234 if (buf == NULL)
235 return ENOMEM;
236
237 if (size == 0)
238 return EINVAL;
239
240 async_exch_t *exch = async_exchange_begin(dev_sess);
241
242 aid_t opening_request = async_send_1(exch,
243 DEV_IFACE_ID(USBHID_DEV_IFACE), IPC_M_USBHID_GET_REPORT_DESCRIPTOR,
244 NULL);
245 if (opening_request == 0) {
246 async_exchange_end(exch);
247 return ENOMEM;
248 }
249
250 ipc_call_t data_request_call;
251 aid_t data_request = async_data_read(exch, buf, size,
252 &data_request_call);
253
254 async_exchange_end(exch);
255
256 if (data_request == 0) {
257 async_forget(opening_request);
258 return ENOMEM;
259 }
260
261 int data_request_rc;
262 int opening_request_rc;
263 async_wait_for(data_request, &data_request_rc);
264 async_wait_for(opening_request, &opening_request_rc);
265
266 if (data_request_rc != EOK) {
267 /* Prefer return code of the opening request. */
268 if (opening_request_rc != EOK)
269 return (int) opening_request_rc;
270 else
271 return (int) data_request_rc;
272 }
273
274 if (opening_request_rc != EOK)
275 return (int) opening_request_rc;
276
277 size_t act_size = IPC_GET_ARG2(data_request_call);
278
279 if (actual_size != NULL)
280 *actual_size = act_size;
281
282 return EOK;
283}
284
285static void remote_usbhid_get_event_length(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
286static void remote_usbhid_get_event(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
287static void remote_usbhid_get_report_descriptor_length(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
288static void remote_usbhid_get_report_descriptor(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
289// static void remote_usbhid_(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
290
291/** Remote USB HID interface operations. */
292static const remote_iface_func_ptr_t remote_usbhid_iface_ops [] = {
293 [IPC_M_USBHID_GET_EVENT_LENGTH] = remote_usbhid_get_event_length,
294 [IPC_M_USBHID_GET_EVENT] = remote_usbhid_get_event,
295 [IPC_M_USBHID_GET_REPORT_DESCRIPTOR_LENGTH] =
296 remote_usbhid_get_report_descriptor_length,
297 [IPC_M_USBHID_GET_REPORT_DESCRIPTOR] = remote_usbhid_get_report_descriptor
298};
299
300/** Remote USB HID interface structure.
301 */
302const remote_iface_t remote_usbhid_iface = {
303 .method_count = ARRAY_SIZE(remote_usbhid_iface_ops),
304 .methods = remote_usbhid_iface_ops
305};
306
307//usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
308
309
310void remote_usbhid_get_event_length(ddf_fun_t *fun, void *iface,
311 ipc_callid_t callid, ipc_call_t *call)
312{
313 printf("remote_usbhid_get_event_length()\n");
314
315 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
316
317 if (!hid_iface->get_event_length) {
318 printf("Get event length not set!\n");
319 async_answer_0(callid, ENOTSUP);
320 return;
321 }
322
323 size_t len = hid_iface->get_event_length(fun);
324// if (len == 0) {
325// len = EEMPTY;
326// }
327 async_answer_1(callid, EOK, len);
328
329// if (len < 0) {
330// async_answer_0(callid, len);
331// } else {
332// async_answer_1(callid, EOK, len);
333// }
334}
335
336void remote_usbhid_get_event(ddf_fun_t *fun, void *iface,
337 ipc_callid_t callid, ipc_call_t *call)
338{
339 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
340
341 if (!hid_iface->get_event) {
342 async_answer_0(callid, ENOTSUP);
343 return;
344 }
345
346 unsigned int flags = DEV_IPC_GET_ARG1(*call);
347
348 size_t len;
349 ipc_callid_t data_callid;
350 if (!async_data_read_receive(&data_callid, &len)) {
351 async_answer_0(callid, EPARTY);
352 return;
353 }
354// /* Check that length is even number. Truncate otherwise. */
355// if ((len % 2) == 1) {
356// len--;
357// }
358 if (len == 0) {
359 async_answer_0(data_callid, EINVAL);
360 async_answer_0(callid, EINVAL);
361 return;
362 }
363
364 int rc;
365
366 uint8_t *data = malloc(len);
367 if (data == NULL) {
368 async_answer_0(data_callid, ENOMEM);
369 async_answer_0(callid, ENOMEM);
370 return;
371 }
372
373 size_t act_length;
374 int event_nr;
375 rc = hid_iface->get_event(fun, data, len, &act_length, &event_nr, flags);
376 if (rc != EOK) {
377 free(data);
378 async_answer_0(data_callid, rc);
379 async_answer_0(callid, rc);
380 return;
381 }
382 if (act_length >= len) {
383 /* This shall not happen. */
384 // FIXME: how about an assert here?
385 act_length = len;
386 }
387
388 async_data_read_finalize(data_callid, data, act_length);
389
390 free(data);
391
392 async_answer_1(callid, EOK, event_nr);
393}
394
395void remote_usbhid_get_report_descriptor_length(ddf_fun_t *fun, void *iface,
396 ipc_callid_t callid, ipc_call_t *call)
397{
398 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
399
400 if (!hid_iface->get_report_descriptor_length) {
401 async_answer_0(callid, ENOTSUP);
402 return;
403 }
404
405 size_t len = hid_iface->get_report_descriptor_length(fun);
406 async_answer_1(callid, EOK, (sysarg_t) len);
407}
408
409void remote_usbhid_get_report_descriptor(ddf_fun_t *fun, void *iface,
410 ipc_callid_t callid, ipc_call_t *call)
411{
412 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
413
414 if (!hid_iface->get_report_descriptor) {
415 async_answer_0(callid, ENOTSUP);
416 return;
417 }
418
419 size_t len;
420 ipc_callid_t data_callid;
421 if (!async_data_read_receive(&data_callid, &len)) {
422 async_answer_0(callid, EINVAL);
423 return;
424 }
425
426 if (len == 0) {
427 async_answer_0(data_callid, EINVAL);
428 async_answer_0(callid, EINVAL);
429 return;
430 }
431
432 uint8_t *descriptor = malloc(len);
433 if (descriptor == NULL) {
434 async_answer_0(data_callid, ENOMEM);
435 async_answer_0(callid, ENOMEM);
436 return;
437 }
438
439 size_t act_len = 0;
440 int rc = hid_iface->get_report_descriptor(fun, descriptor, len,
441 &act_len);
442 if (act_len > len) {
443 rc = ELIMIT;
444 }
445 if (rc != EOK) {
446 free(descriptor);
447 async_answer_0(data_callid, rc);
448 async_answer_0(callid, rc);
449 return;
450 }
451
452 async_data_read_finalize(data_callid, descriptor, act_len);
453 async_answer_0(callid, EOK);
454
455 free(descriptor);
456}
457
458
459
460/**
461 * @}
462 */
Note: See TracBrowser for help on using the repository browser.