Add missing calls to refcount_init()
[helenos.git] / uspace / lib / drv / generic / remote_usbhid.c
blob0ee9b0b7887270d0a091c43b8469f3748f784fe1
1 /*
2 * Copyright (c) 2010-2011 Vojtech Horky
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup libdrv
30 * @{
32 /** @file
35 #include <async.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <stdio.h>
39 #include <macros.h>
41 #include "usbhid_iface.h"
42 #include "ddf/driver.h"
44 /** IPC methods for USB HID device interface. */
45 typedef enum {
46 /** Get number of events reported in single burst.
47 * Parameters: none
48 * Answer:
49 * - Size of one report in bytes.
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)
63 * It is okay if the client requests less data. Extra data must
64 * be truncated by the driver.
66 * @todo Change this comment.
68 IPC_M_USBHID_GET_EVENT,
70 /** Get the size of the report descriptor from the HID device.
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.
79 IPC_M_USBHID_GET_REPORT_DESCRIPTOR_LENGTH,
81 /** Get the report descriptor from the HID device.
83 * Parameters:
84 * - none
85 * The call is followed by data read expecting the descriptor itself.
86 * Answer:
87 * - EOK - report descriptor returned.
89 IPC_M_USBHID_GET_REPORT_DESCRIPTOR
90 } usbhid_iface_funcs_t;
92 /** Ask for event array length.
94 * @param dev_sess Session to DDF device providing USB HID interface.
96 * @return Number of usages returned or an error code.
99 errno_t usbhid_dev_get_event_length(async_sess_t *dev_sess, size_t *size)
101 if (!dev_sess)
102 return EINVAL;
104 async_exch_t *exch = async_exchange_begin(dev_sess);
106 sysarg_t len;
107 errno_t rc = async_req_1_1(exch, DEV_IFACE_ID(USBHID_DEV_IFACE),
108 IPC_M_USBHID_GET_EVENT_LENGTH, &len);
110 async_exchange_end(exch);
112 if (rc == EOK) {
113 if (size != NULL)
114 *size = (size_t) len;
117 return rc;
120 /** Request for next event from HID device.
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_*).
131 * @return Error code.
134 errno_t 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)
137 if (!dev_sess)
138 return EINVAL;
140 if (buf == NULL)
141 return ENOMEM;
143 if (size == 0)
144 return EINVAL;
146 size_t buffer_size = size;
147 uint8_t *buffer = malloc(buffer_size);
148 if (buffer == NULL)
149 return ENOMEM;
151 async_exch_t *exch = async_exchange_begin(dev_sess);
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);
158 if (opening_request == 0) {
159 async_exchange_end(exch);
160 free(buffer);
161 return ENOMEM;
164 ipc_call_t data_request_call;
165 aid_t data_request = async_data_read(exch, buffer, buffer_size,
166 &data_request_call);
168 async_exchange_end(exch);
170 if (data_request == 0) {
171 async_forget(opening_request);
172 free(buffer);
173 return ENOMEM;
176 errno_t data_request_rc;
177 errno_t opening_request_rc;
178 async_wait_for(data_request, &data_request_rc);
179 async_wait_for(opening_request, &opening_request_rc);
181 if (data_request_rc != EOK) {
182 /* Prefer return code of the opening request. */
183 if (opening_request_rc != EOK)
184 return (errno_t) opening_request_rc;
185 else
186 return (errno_t) data_request_rc;
189 if (opening_request_rc != EOK)
190 return (errno_t) opening_request_rc;
192 size_t act_size = IPC_GET_ARG2(data_request_call);
194 /* Copy the individual items. */
195 memcpy(buf, buffer, act_size);
197 if (actual_size != NULL)
198 *actual_size = act_size;
200 if (event_nr != NULL)
201 *event_nr = IPC_GET_ARG1(opening_request_call);
203 return EOK;
206 errno_t usbhid_dev_get_report_descriptor_length(async_sess_t *dev_sess,
207 size_t *size)
209 if (!dev_sess)
210 return EINVAL;
212 async_exch_t *exch = async_exchange_begin(dev_sess);
214 sysarg_t arg_size;
215 errno_t rc = async_req_1_1(exch, DEV_IFACE_ID(USBHID_DEV_IFACE),
216 IPC_M_USBHID_GET_REPORT_DESCRIPTOR_LENGTH, &arg_size);
218 async_exchange_end(exch);
220 if (rc == EOK) {
221 if (size != NULL)
222 *size = (size_t) arg_size;
225 return rc;
228 errno_t usbhid_dev_get_report_descriptor(async_sess_t *dev_sess, uint8_t *buf,
229 size_t size, size_t *actual_size)
231 if (!dev_sess)
232 return EINVAL;
234 if (buf == NULL)
235 return ENOMEM;
237 if (size == 0)
238 return EINVAL;
240 async_exch_t *exch = async_exchange_begin(dev_sess);
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;
250 ipc_call_t data_request_call;
251 aid_t data_request = async_data_read(exch, buf, size,
252 &data_request_call);
254 async_exchange_end(exch);
256 if (data_request == 0) {
257 async_forget(opening_request);
258 return ENOMEM;
261 errno_t data_request_rc;
262 errno_t opening_request_rc;
263 async_wait_for(data_request, &data_request_rc);
264 async_wait_for(opening_request, &opening_request_rc);
266 if (data_request_rc != EOK) {
267 /* Prefer return code of the opening request. */
268 if (opening_request_rc != EOK)
269 return (errno_t) opening_request_rc;
270 else
271 return (errno_t) data_request_rc;
274 if (opening_request_rc != EOK)
275 return (errno_t) opening_request_rc;
277 size_t act_size = IPC_GET_ARG2(data_request_call);
279 if (actual_size != NULL)
280 *actual_size = act_size;
282 return EOK;
285 static void remote_usbhid_get_event_length(ddf_fun_t *, void *, ipc_call_t *);
286 static void remote_usbhid_get_event(ddf_fun_t *, void *, ipc_call_t *);
287 static void remote_usbhid_get_report_descriptor_length(ddf_fun_t *, void *, ipc_call_t *);
288 static void remote_usbhid_get_report_descriptor(ddf_fun_t *, void *, ipc_call_t *);
290 /** Remote USB HID interface operations. */
291 static const remote_iface_func_ptr_t remote_usbhid_iface_ops [] = {
292 [IPC_M_USBHID_GET_EVENT_LENGTH] = remote_usbhid_get_event_length,
293 [IPC_M_USBHID_GET_EVENT] = remote_usbhid_get_event,
294 [IPC_M_USBHID_GET_REPORT_DESCRIPTOR_LENGTH] =
295 remote_usbhid_get_report_descriptor_length,
296 [IPC_M_USBHID_GET_REPORT_DESCRIPTOR] = remote_usbhid_get_report_descriptor
299 /** Remote USB HID interface structure.
301 const remote_iface_t remote_usbhid_iface = {
302 .method_count = ARRAY_SIZE(remote_usbhid_iface_ops),
303 .methods = remote_usbhid_iface_ops
306 void remote_usbhid_get_event_length(ddf_fun_t *fun, void *iface,
307 ipc_call_t *call)
309 printf("remote_usbhid_get_event_length()\n");
311 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
313 if (!hid_iface->get_event_length) {
314 printf("Get event length not set!\n");
315 async_answer_0(call, ENOTSUP);
316 return;
319 size_t len = hid_iface->get_event_length(fun);
320 async_answer_1(call, EOK, len);
323 void remote_usbhid_get_event(ddf_fun_t *fun, void *iface,
324 ipc_call_t *call)
326 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
328 if (!hid_iface->get_event) {
329 async_answer_0(call, ENOTSUP);
330 return;
333 unsigned int flags = DEV_IPC_GET_ARG1(*call);
335 ipc_call_t data;
336 size_t len;
337 if (!async_data_read_receive(&data, &len)) {
338 async_answer_0(call, EPARTY);
339 return;
342 if (len == 0) {
343 async_answer_0(&data, EINVAL);
344 async_answer_0(call, EINVAL);
345 return;
348 errno_t rc;
350 uint8_t *event = malloc(len);
351 if (event == NULL) {
352 async_answer_0(&data, ENOMEM);
353 async_answer_0(call, ENOMEM);
354 return;
357 size_t act_length;
358 int event_nr;
359 rc = hid_iface->get_event(fun, event, len, &act_length, &event_nr, flags);
360 if (rc != EOK) {
361 free(event);
362 async_answer_0(&data, rc);
363 async_answer_0(call, rc);
364 return;
366 if (act_length >= len) {
367 /* This shall not happen. */
368 // FIXME: how about an assert here?
369 act_length = len;
372 async_data_read_finalize(&data, event, act_length);
374 free(event);
376 async_answer_1(call, EOK, event_nr);
379 void remote_usbhid_get_report_descriptor_length(ddf_fun_t *fun, void *iface,
380 ipc_call_t *call)
382 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
384 if (!hid_iface->get_report_descriptor_length) {
385 async_answer_0(call, ENOTSUP);
386 return;
389 size_t len = hid_iface->get_report_descriptor_length(fun);
390 async_answer_1(call, EOK, (sysarg_t) len);
393 void remote_usbhid_get_report_descriptor(ddf_fun_t *fun, void *iface,
394 ipc_call_t *call)
396 usbhid_iface_t *hid_iface = (usbhid_iface_t *) iface;
398 if (!hid_iface->get_report_descriptor) {
399 async_answer_0(call, ENOTSUP);
400 return;
403 ipc_call_t data;
404 size_t len;
405 if (!async_data_read_receive(&data, &len)) {
406 async_answer_0(call, EINVAL);
407 return;
410 if (len == 0) {
411 async_answer_0(&data, EINVAL);
412 async_answer_0(call, EINVAL);
413 return;
416 uint8_t *descriptor = malloc(len);
417 if (descriptor == NULL) {
418 async_answer_0(&data, ENOMEM);
419 async_answer_0(call, ENOMEM);
420 return;
423 size_t act_len = 0;
424 errno_t rc = hid_iface->get_report_descriptor(fun, descriptor, len,
425 &act_len);
426 if (act_len > len) {
427 rc = ELIMIT;
429 if (rc != EOK) {
430 free(descriptor);
431 async_answer_0(&data, rc);
432 async_answer_0(call, rc);
433 return;
436 async_data_read_finalize(&data, descriptor, act_len);
437 async_answer_0(call, EOK);
439 free(descriptor);
443 * @}