source: mainline/uspace/lib/drv/generic/remote_usbhc.c@ 1728134

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1728134 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: 9.8 KB
Line 
1/*
2 * Copyright (c) 2010-2011 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libdrv
31 * @{
32 */
33/** @file
34 */
35
36#include <async.h>
37#include <errno.h>
38#include <assert.h>
39#include <macros.h>
40
41#include "usbhc_iface.h"
42#include "ddf/driver.h"
43
44#define USB_MAX_PAYLOAD_SIZE 1020
45
46/** IPC methods for communication with HC through DDF interface.
47 *
48 * Notes for async methods:
49 *
50 * Methods for sending data to device (OUT transactions)
51 * - e.g. IPC_M_USBHC_INTERRUPT_OUT -
52 * always use the same semantics:
53 * - first, IPC call with given method is made
54 * - argument #1 is target address
55 * - argument #2 is target endpoint
56 * - argument #3 is max packet size of the endpoint
57 * - this call is immediately followed by IPC data write (from caller)
58 * - the initial call (and the whole transaction) is answer after the
59 * transaction is scheduled by the HC and acknowledged by the device
60 * or immediately after error is detected
61 * - the answer carries only the error code
62 *
63 * Methods for retrieving data from device (IN transactions)
64 * - e.g. IPC_M_USBHC_INTERRUPT_IN -
65 * also use the same semantics:
66 * - first, IPC call with given method is made
67 * - argument #1 is target address
68 * - argument #2 is target endpoint
69 * - this call is immediately followed by IPC data read (async version)
70 * - the call is not answered until the device returns some data (or until
71 * error occurs)
72 *
73 * Some special methods (NO-DATA transactions) do not send any data. These
74 * might behave as both OUT or IN transactions because communication parts
75 * where actual buffers are exchanged are omitted.
76 **
77 * For all these methods, wrap functions exists. Important rule: functions
78 * for IN transactions have (as parameters) buffers where retrieved data
79 * will be stored. These buffers must be already allocated and shall not be
80 * touch until the transaction is completed
81 * (e.g. not before calling usb_wait_for() with appropriate handle).
82 * OUT transactions buffers can be freed immediately after call is dispatched
83 * (i.e. after return from wrapping function).
84 *
85 */
86typedef enum {
87 /** Get data from device.
88 * See explanation at usb_iface_funcs_t (IN transaction).
89 */
90 IPC_M_USBHC_READ,
91
92 /** Send data to device.
93 * See explanation at usb_iface_funcs_t (OUT transaction).
94 */
95 IPC_M_USBHC_WRITE,
96} usbhc_iface_funcs_t;
97
98int usbhc_read(async_exch_t *exch, usb_address_t address,
99 usb_endpoint_t endpoint, uint64_t setup, void *data, size_t size,
100 size_t *rec_size)
101{
102 if (!exch)
103 return EBADMEM;
104
105 if (size == 0 && setup == 0)
106 return EOK;
107
108 const usb_target_t target =
109 {{ .address = address, .endpoint = endpoint }};
110
111 /* Make call identifying target USB device and type of transfer. */
112 aid_t opening_request = async_send_4(exch,
113 DEV_IFACE_ID(USBHC_DEV_IFACE),
114 IPC_M_USBHC_READ, target.packed,
115 (setup & UINT32_MAX), (setup >> 32), NULL);
116
117 if (opening_request == 0) {
118 return ENOMEM;
119 }
120
121 /* Retrieve the data. */
122 ipc_call_t data_request_call;
123 aid_t data_request =
124 async_data_read(exch, data, size, &data_request_call);
125
126 if (data_request == 0) {
127 // FIXME: How to let the other side know that we want to abort?
128 async_forget(opening_request);
129 return ENOMEM;
130 }
131
132 /* Wait for the answer. */
133 int data_request_rc;
134 int opening_request_rc;
135 async_wait_for(data_request, &data_request_rc);
136 async_wait_for(opening_request, &opening_request_rc);
137
138 if (data_request_rc != EOK) {
139 /* Prefer the return code of the opening request. */
140 if (opening_request_rc != EOK) {
141 return (int) opening_request_rc;
142 } else {
143 return (int) data_request_rc;
144 }
145 }
146 if (opening_request_rc != EOK) {
147 return (int) opening_request_rc;
148 }
149
150 *rec_size = IPC_GET_ARG2(data_request_call);
151 return EOK;
152}
153
154int usbhc_write(async_exch_t *exch, usb_address_t address,
155 usb_endpoint_t endpoint, uint64_t setup, const void *data, size_t size)
156{
157 if (!exch)
158 return EBADMEM;
159
160 if (size == 0 && setup == 0)
161 return EOK;
162
163 const usb_target_t target =
164 {{ .address = address, .endpoint = endpoint }};
165
166 aid_t opening_request = async_send_5(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
167 IPC_M_USBHC_WRITE, target.packed, size,
168 (setup & UINT32_MAX), (setup >> 32), NULL);
169
170 if (opening_request == 0) {
171 return ENOMEM;
172 }
173
174 /* Send the data if any. */
175 if (size > 0) {
176 const int ret = async_data_write_start(exch, data, size);
177 if (ret != EOK) {
178 async_forget(opening_request);
179 return ret;
180 }
181 }
182
183 /* Wait for the answer. */
184 int opening_request_rc;
185 async_wait_for(opening_request, &opening_request_rc);
186
187 return (int) opening_request_rc;
188}
189
190static void remote_usbhc_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
191static void remote_usbhc_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
192
193/** Remote USB host controller interface operations. */
194static const remote_iface_func_ptr_t remote_usbhc_iface_ops[] = {
195 [IPC_M_USBHC_READ] = remote_usbhc_read,
196 [IPC_M_USBHC_WRITE] = remote_usbhc_write,
197};
198
199/** Remote USB host controller interface structure.
200 */
201const remote_iface_t remote_usbhc_iface = {
202 .method_count = ARRAY_SIZE(remote_usbhc_iface_ops),
203 .methods = remote_usbhc_iface_ops
204};
205
206typedef struct {
207 ipc_callid_t caller;
208 ipc_callid_t data_caller;
209 void *buffer;
210} async_transaction_t;
211
212static void async_transaction_destroy(async_transaction_t *trans)
213{
214 if (trans == NULL)
215 return;
216
217 if (trans->buffer != NULL)
218 free(trans->buffer);
219
220 free(trans);
221}
222
223static async_transaction_t *async_transaction_create(ipc_callid_t caller)
224{
225 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
226 if (trans == NULL) {
227 return NULL;
228 }
229
230 trans->caller = caller;
231 trans->data_caller = 0;
232 trans->buffer = NULL;
233
234 return trans;
235}
236
237static void callback_out(int outcome, void *arg)
238{
239 async_transaction_t *trans = arg;
240
241 async_answer_0(trans->caller, outcome);
242
243 async_transaction_destroy(trans);
244}
245
246static void callback_in(int outcome, size_t actual_size, void *arg)
247{
248 async_transaction_t *trans = (async_transaction_t *)arg;
249
250 if (outcome != EOK) {
251 async_answer_0(trans->caller, outcome);
252 if (trans->data_caller) {
253 async_answer_0(trans->data_caller, EINTR);
254 }
255 async_transaction_destroy(trans);
256 return;
257 }
258
259 if (trans->data_caller) {
260 async_data_read_finalize(trans->data_caller,
261 trans->buffer, actual_size);
262 }
263
264 async_answer_0(trans->caller, EOK);
265
266 async_transaction_destroy(trans);
267}
268
269void remote_usbhc_read(
270 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
271{
272 assert(fun);
273 assert(iface);
274 assert(call);
275
276 const usbhc_iface_t *hc_iface = iface;
277
278 if (!hc_iface->read) {
279 async_answer_0(callid, ENOTSUP);
280 return;
281 }
282
283 const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
284 const uint64_t setup =
285 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
286 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
287
288 async_transaction_t *trans = async_transaction_create(callid);
289 if (trans == NULL) {
290 async_answer_0(callid, ENOMEM);
291 return;
292 }
293
294 size_t size = 0;
295 if (!async_data_read_receive(&trans->data_caller, &size)) {
296 async_answer_0(callid, EPARTY);
297 return;
298 }
299
300 trans->buffer = malloc(size);
301 if (trans->buffer == NULL) {
302 async_answer_0(trans->data_caller, ENOMEM);
303 async_answer_0(callid, ENOMEM);
304 async_transaction_destroy(trans);
305 return;
306 }
307
308 const int rc = hc_iface->read(
309 fun, target, setup, trans->buffer, size, callback_in, trans);
310
311 if (rc != EOK) {
312 async_answer_0(trans->data_caller, rc);
313 async_answer_0(callid, rc);
314 async_transaction_destroy(trans);
315 }
316}
317
318void remote_usbhc_write(
319 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
320{
321 assert(fun);
322 assert(iface);
323 assert(call);
324
325 const usbhc_iface_t *hc_iface = iface;
326
327 if (!hc_iface->write) {
328 async_answer_0(callid, ENOTSUP);
329 return;
330 }
331
332 const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
333 const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
334 const uint64_t setup =
335 ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
336 (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
337
338 async_transaction_t *trans = async_transaction_create(callid);
339 if (trans == NULL) {
340 async_answer_0(callid, ENOMEM);
341 return;
342 }
343
344 size_t size = 0;
345 if (data_buffer_len > 0) {
346 const int rc = async_data_write_accept(&trans->buffer, false,
347 1, USB_MAX_PAYLOAD_SIZE,
348 0, &size);
349
350 if (rc != EOK) {
351 async_answer_0(callid, rc);
352 async_transaction_destroy(trans);
353 return;
354 }
355 }
356
357 const int rc = hc_iface->write(
358 fun, target, setup, trans->buffer, size, callback_out, trans);
359
360 if (rc != EOK) {
361 async_answer_0(callid, rc);
362 async_transaction_destroy(trans);
363 }
364}
365/**
366 * @}
367 */
Note: See TracBrowser for help on using the repository browser.