source: mainline/uspace/lib/usb/hcd.c@ 63b4f90

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63b4f90 was c7137738, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

DDF proposal

Proposal for new API using the device driver framework.

  • Property mode set to 100644
File size: 10.5 KB
Line 
1/*
2 * Copyright (c) 2010 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 libusb usb
30 * @{
31 */
32/** @file
33 * @brief USB Host Controller Driver (implementation).
34 */
35#include "hcd.h"
36#include <devmap.h>
37#include <stdlib.h>
38#include <fcntl.h>
39#include <vfs/vfs.h>
40#include <errno.h>
41
42/** Information about pending transaction on HC. */
43typedef struct {
44 /** Phone to host controller driver. */
45 int phone;
46 /** Data buffer. */
47 void *buffer;
48 /** Buffer size. */
49 size_t size;
50 /** Storage for actual number of bytes transferred. */
51 size_t *size_transferred;
52 /** Initial call replay data. */
53 ipc_call_t reply;
54 /** Initial call identifier. */
55 aid_t request;
56} transfer_info_t;
57
58#define NAMESPACE "usb"
59
60
61/** String representation of USB transaction outcome. */
62const char * usb_str_transaction_outcome(usb_transaction_outcome_t o)
63{
64 switch (o) {
65 case USB_OUTCOME_OK:
66 return "ok";
67 case USB_OUTCOME_CRCERROR:
68 return "CRC error";
69 case USB_OUTCOME_BABBLE:
70 return "babble";
71 default:
72 return "unknown";
73 }
74}
75
76/** Create necessary phones for communicating with HCD.
77 * This function wraps following calls:
78 * -# open <code>/dev/usb/<i>hcd_path</i></code> for reading
79 * -# access phone of file opened in previous step
80 * -# return the (outgoing) phone
81 *
82 * @warning This function is wrapper for several actions and therefore
83 * it is not possible - in case of error - to determine at which point
84 * error occurred.
85 *
86 * @param hcd_path HCD identification under devfs
87 * (without <code>/dev/usb/</code>).
88 * @return Phone for communicating with HCD or error code from errno.h.
89 */
90int usb_hcd_connect(const char * hcd_path)
91{
92 char dev_path[DEVMAP_NAME_MAXLEN + 1];
93 snprintf(dev_path, DEVMAP_NAME_MAXLEN,
94 "/dev/%s/%s", NAMESPACE, hcd_path);
95
96 int fd = open(dev_path, O_RDONLY);
97 if (fd < 0) {
98 return fd;
99 }
100
101 int hcd_phone = fd_phone(fd);
102
103 if (hcd_phone < 0) {
104 return hcd_phone;
105 }
106
107 return hcd_phone;
108}
109
110/** Send data to HCD.
111 *
112 * @param phone Opened phone to HCD.
113 * @param method Method used for calling.
114 * @param target Target device.
115 * @param buffer Data buffer (NULL to skip data transfer phase).
116 * @param size Buffer size (must be zero when @p buffer is NULL).
117 * @param handle Storage for transaction handle (cannot be NULL).
118 * @return Error status.
119 * @retval EINVAL Invalid parameter.
120 * @retval ENOMEM Not enough memory to complete the operation.
121 */
122static int async_send_buffer(int phone, int method,
123 usb_target_t target,
124 void *buffer, size_t size,
125 usb_handle_t *handle)
126{
127 if (phone < 0) {
128 return EINVAL;
129 }
130
131 if ((buffer == NULL) && (size > 0)) {
132 return EINVAL;
133 }
134
135 if (handle == NULL) {
136 return EINVAL;
137 }
138
139 transfer_info_t *transfer
140 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
141 if (transfer == NULL) {
142 return ENOMEM;
143 }
144
145 transfer->size_transferred = NULL;
146 transfer->buffer = NULL;
147 transfer->size = 0;
148 transfer->phone = phone;
149
150 int rc;
151
152 transfer->request = async_send_3(phone,
153 method,
154 target.address, target.endpoint,
155 size,
156 &transfer->reply);
157
158 if (size > 0) {
159 rc = async_data_write_start(phone, buffer, size);
160 if (rc != EOK) {
161 async_wait_for(transfer->request, NULL);
162 return rc;
163 }
164 }
165
166 *handle = (usb_handle_t) transfer;
167
168 return EOK;
169}
170
171/** Prepare data retrieval.
172 *
173 * @param phone Opened phone to HCD.
174 * @param method Method used for calling.
175 * @param target Target device.
176 * @param buffer Buffer where to store retrieved data
177 * (NULL to skip data transfer phase).
178 * @param size Buffer size (must be zero when @p buffer is NULL).
179 * @param actual_size Storage where actual number of bytes transferred will
180 * be stored.
181 * @param handle Storage for transaction handle (cannot be NULL).
182 * @return Error status.
183 * @retval EINVAL Invalid parameter.
184 * @retval ENOMEM Not enough memory to complete the operation.
185 */
186static int async_recv_buffer(int phone, int method,
187 usb_target_t target,
188 void *buffer, size_t size, size_t *actual_size,
189 usb_handle_t *handle)
190{
191 if (phone < 0) {
192 return EINVAL;
193 }
194
195 if ((buffer == NULL) && (size > 0)) {
196 return EINVAL;
197 }
198
199 if (handle == NULL) {
200 return EINVAL;
201 }
202
203 transfer_info_t *transfer
204 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
205 if (transfer == NULL) {
206 return ENOMEM;
207 }
208
209 transfer->size_transferred = actual_size;
210 transfer->buffer = buffer;
211 transfer->size = size;
212 transfer->phone = phone;
213
214 transfer->request = async_send_3(phone,
215 method,
216 target.address, target.endpoint,
217 size,
218 &transfer->reply);
219
220 *handle = (usb_handle_t) transfer;
221
222 return EOK;
223}
224
225/** Read buffer from HCD.
226 *
227 * @param phone Opened phone to HCD.
228 * @param hash Buffer hash (obtained after completing IN transaction).
229 * @param buffer Buffer where to store data data.
230 * @param size Buffer size.
231 * @param actual_size Storage where actual number of bytes transferred will
232 * be stored.
233 * @return Error status.
234 */
235static int read_buffer_in(int phone, ipcarg_t hash,
236 void *buffer, size_t size, size_t *actual_size)
237{
238 ipc_call_t answer_data;
239 ipcarg_t answer_rc;
240 aid_t req;
241 int rc;
242
243 req = async_send_1(phone,
244 IPC_M_USB_HCD_GET_BUFFER_ASYNC,
245 hash,
246 &answer_data);
247
248 rc = async_data_read_start(phone, buffer, size);
249 if (rc != EOK) {
250 async_wait_for(req, NULL);
251 return EINVAL;
252 }
253
254 async_wait_for(req, &answer_rc);
255 rc = (int)answer_rc;
256
257 if (rc != EOK) {
258 return rc;
259 }
260
261 *actual_size = IPC_GET_ARG1(answer_data);
262
263 return EOK;
264}
265
266/** Blocks caller until given USB transaction is finished.
267 * After the transaction is finished, the user can access all output data
268 * given to initial call function.
269 *
270 * @param handle Transaction handle.
271 * @return Error status.
272 * @retval EOK No error.
273 * @retval EBADMEM Invalid handle.
274 * @retval ENOENT Data buffer associated with transaction does not exist.
275 */
276int usb_hcd_async_wait_for(usb_handle_t handle)
277{
278 if (handle == 0) {
279 return EBADMEM;
280 }
281
282 int rc = EOK;
283
284 transfer_info_t *transfer = (transfer_info_t *) handle;
285
286 ipcarg_t answer_rc;
287 async_wait_for(transfer->request, &answer_rc);
288
289 if (answer_rc != EOK) {
290 rc = (int) answer_rc;
291 goto leave;
292 }
293
294 /*
295 * If the buffer is not NULL, we must accept some data.
296 */
297 if ((transfer->buffer != NULL) && (transfer->size > 0)) {
298 /*
299 * The buffer hash identifies the data on the server
300 * side.
301 * We will use it when actually reading-in the data.
302 */
303 ipcarg_t buffer_hash = IPC_GET_ARG1(transfer->reply);
304 if (buffer_hash == 0) {
305 rc = ENOENT;
306 goto leave;
307 }
308
309 size_t actual_size;
310 rc = read_buffer_in(transfer->phone, buffer_hash,
311 transfer->buffer, transfer->size, &actual_size);
312
313 if (rc != EOK) {
314 goto leave;
315 }
316
317 if (transfer->size_transferred) {
318 *(transfer->size_transferred) = actual_size;
319 }
320 }
321
322leave:
323 free(transfer);
324
325 return rc;
326}
327
328/** Send interrupt data to device. */
329int usb_hcd_async_transfer_interrupt_out(int hcd_phone,
330 usb_target_t target,
331 void *buffer, size_t size,
332 usb_handle_t *handle)
333{
334 return async_send_buffer(hcd_phone,
335 IPC_M_USB_HCD_INTERRUPT_OUT_ASYNC,
336 target,
337 buffer, size,
338 handle);
339}
340
341/** Request interrupt data from device. */
342int usb_hcd_async_transfer_interrupt_in(int hcd_phone,
343 usb_target_t target,
344 void *buffer, size_t size, size_t *actual_size,
345 usb_handle_t *handle)
346{
347 return async_recv_buffer(hcd_phone,
348 IPC_M_USB_HCD_INTERRUPT_IN_ASYNC,
349 target,
350 buffer, size, actual_size,
351 handle);
352}
353
354/** Start WRITE control transfer. */
355int usb_hcd_async_transfer_control_write_setup(int hcd_phone,
356 usb_target_t target,
357 void *buffer, size_t size,
358 usb_handle_t *handle)
359{
360 return async_send_buffer(hcd_phone,
361 IPC_M_USB_HCD_CONTROL_WRITE_SETUP_ASYNC,
362 target,
363 buffer, size,
364 handle);
365}
366
367/** Send data during WRITE control transfer. */
368int usb_hcd_async_transfer_control_write_data(int hcd_phone,
369 usb_target_t target,
370 void *buffer, size_t size,
371 usb_handle_t *handle)
372{
373 return async_send_buffer(hcd_phone,
374 IPC_M_USB_HCD_CONTROL_WRITE_DATA_ASYNC,
375 target,
376 buffer, size,
377 handle);
378}
379
380/** Terminate WRITE control transfer. */
381int usb_hcd_async_transfer_control_write_status(int hcd_phone,
382 usb_target_t target,
383 usb_handle_t *handle)
384{
385 return async_recv_buffer(hcd_phone,
386 IPC_M_USB_HCD_CONTROL_WRITE_STATUS_ASYNC,
387 target,
388 NULL, 0, NULL,
389 handle);
390}
391
392/** Start READ control transfer. */
393int usb_hcd_async_transfer_control_read_setup(int hcd_phone,
394 usb_target_t target,
395 void *buffer, size_t size,
396 usb_handle_t *handle)
397{
398 return async_send_buffer(hcd_phone,
399 IPC_M_USB_HCD_CONTROL_READ_SETUP_ASYNC,
400 target,
401 buffer, size,
402 handle);
403}
404
405/** Request data during READ control transfer. */
406int usb_hcd_async_transfer_control_read_data(int hcd_phone,
407 usb_target_t target,
408 void *buffer, size_t size, size_t *actual_size,
409 usb_handle_t *handle)
410{
411 return async_recv_buffer(hcd_phone,
412 IPC_M_USB_HCD_CONTROL_READ_DATA_ASYNC,
413 target,
414 buffer, size, actual_size,
415 handle);
416}
417
418/** Terminate READ control transfer. */
419int usb_hcd_async_transfer_control_read_status(int hcd_phone,
420 usb_target_t target,
421 usb_handle_t *handle)
422{
423 return async_send_buffer(hcd_phone,
424 IPC_M_USB_HCD_CONTROL_READ_STATUS_ASYNC,
425 target,
426 NULL, 0,
427 handle);
428}
429
430/**
431 * @}
432 */
Note: See TracBrowser for help on using the repository browser.