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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 19b3cc6 was 1b973dc, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

merge libdrv cleanup

  • 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 sysarg_t data_request_rc;
134 sysarg_t 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 sysarg_t 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
221 free(trans);
222}
223
224static async_transaction_t *async_transaction_create(ipc_callid_t caller)
225{
226 async_transaction_t *trans = malloc(sizeof(async_transaction_t));
227 if (trans == NULL) {
228 return NULL;
229 }
230
231 trans->caller = caller;
232 trans->data_caller = 0;
233 trans->buffer = NULL;
234
235 return trans;
236}
237
238static void callback_out(int outcome, void *arg)
239{
240 async_transaction_t *trans = arg;
241
242 async_answer_0(trans->caller, outcome);
243
244 async_transaction_destroy(trans);
245}
246
247static void callback_in(int outcome, size_t actual_size, void *arg)
248{
249 async_transaction_t *trans = (async_transaction_t *)arg;
250
251 if (outcome != EOK) {
252 async_answer_0(trans->caller, outcome);
253 if (trans->data_caller) {
254 async_answer_0(trans->data_caller, EINTR);
255 }
256 async_transaction_destroy(trans);
257 return;
258 }
259
260 if (trans->data_caller) {
261 async_data_read_finalize(trans->data_caller,
262 trans->buffer, actual_size);
263 }
264
265 async_answer_0(trans->caller, EOK);
266
267 async_transaction_destroy(trans);
268}
269
270void remote_usbhc_read(
271 ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
272{
273 assert(fun);
274 assert(iface);
275 assert(call);
276
277 const usbhc_iface_t *hc_iface = iface;
278
279 if (!hc_iface->read) {
280 async_answer_0(callid, ENOTSUP);
281 return;
282 }
283
284 const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
285 const uint64_t setup =
286 ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
287 (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
288
289 async_transaction_t *trans = async_transaction_create(callid);
290 if (trans == NULL) {
291 async_answer_0(callid, ENOMEM);
292 return;
293 }
294
295 size_t size = 0;
296 if (!async_data_read_receive(&trans->data_caller, &size)) {
297 async_answer_0(callid, EPARTY);
298 return;
299 }
300
301 trans->buffer = malloc(size);
302 if (trans->buffer == NULL) {
303 async_answer_0(trans->data_caller, ENOMEM);
304 async_answer_0(callid, ENOMEM);
305 async_transaction_destroy(trans);
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.