source: mainline/uspace/lib/usb/src/usbdrv.c@ 47c573a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 47c573a was 3937bda, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Allow control write transfers with no data stage

  • Property mode set to 100644
File size: 14.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
30 * @{
31 */
32/** @file
33 * @brief USB driver (implementation).
34 */
35#include <usb/usbdrv.h>
36#include <usbhc_iface.h>
37#include <usb_iface.h>
38#include <errno.h>
39#include <str_error.h>
40
41/** Information about pending transaction on HC. */
42typedef struct {
43 /** Phone to host controller driver. */
44 int phone;
45 /** Data buffer. */
46 void *buffer;
47 /** Buffer size. */
48 size_t size;
49 /** Storage for actual number of bytes transferred. */
50 size_t *size_transferred;
51 /** Initial call reply data. */
52 ipc_call_t reply;
53 /** Initial call identifier. */
54 aid_t request;
55 /** Reply data for data read call. */
56 ipc_call_t read_reply;
57 /** Data read call identifier. */
58 aid_t read_request;
59} transfer_info_t;
60
61/** Find handle of host controller the device is physically attached to.
62 *
63 * @param[in] dev Device looking for its host controller.
64 * @param[out] handle Host controller devman handle.
65 * @return Error code.
66 */
67int usb_drv_find_hc(device_t *dev, devman_handle_t *handle)
68{
69 if (dev == NULL) {
70 return EBADMEM;
71 }
72 if (handle == NULL) {
73 return EBADMEM;
74 }
75
76 int parent_phone = devman_parent_device_connect(dev->handle,
77 IPC_FLAG_BLOCKING);
78 if (parent_phone < 0) {
79 return parent_phone;
80 }
81
82 devman_handle_t h;
83 int rc = async_req_1_1(parent_phone, DEV_IFACE_ID(USB_DEV_IFACE),
84 IPC_M_USB_GET_HOST_CONTROLLER_HANDLE, &h);
85
86 async_hangup(parent_phone);
87
88 if (rc != EOK) {
89 return rc;
90 }
91
92 *handle = h;
93
94 return EOK;
95}
96
97/** Connect to host controller the device is physically attached to.
98 *
99 * @param dev Device asking for connection.
100 * @param hc_handle Devman handle of the host controller.
101 * @param flags Connection flags (blocking connection).
102 * @return Phone to the HC or error code.
103 */
104int usb_drv_hc_connect(device_t *dev, devman_handle_t hc_handle,
105 unsigned int flags)
106{
107 return devman_device_connect(hc_handle, flags);
108}
109
110/** Connect to host controller the device is physically attached to.
111 *
112 * @param dev Device asking for connection.
113 * @param flags Connection flags (blocking connection).
114 * @return Phone to corresponding HC or error code.
115 */
116int usb_drv_hc_connect_auto(device_t *dev, unsigned int flags)
117{
118 int rc;
119 devman_handle_t hc_handle;
120
121 /*
122 * Call parent hub to obtain device handle of respective HC.
123 */
124 rc = usb_drv_find_hc(dev, &hc_handle);
125 if (rc != EOK) {
126 return rc;
127 }
128
129 return usb_drv_hc_connect(dev, hc_handle, flags);
130}
131
132/** Tell USB address assigned to given device.
133 *
134 * @param phone Phone to my HC.
135 * @param dev Device in question.
136 * @return USB address or error code.
137 */
138usb_address_t usb_drv_get_my_address(int phone, device_t *dev)
139{
140 sysarg_t address;
141 int rc = async_req_2_1(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
142 IPC_M_USBHC_GET_ADDRESS,
143 dev->handle, &address);
144
145 if (rc != EOK) {
146 return rc;
147 }
148
149 return (usb_address_t) address;
150}
151
152/** Tell HC to reserve default address.
153 *
154 * @param phone Open phone to host controller driver.
155 * @return Error code.
156 */
157int usb_drv_reserve_default_address(int phone)
158{
159 return async_req_1_0(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
160 IPC_M_USBHC_RESERVE_DEFAULT_ADDRESS);
161}
162
163/** Tell HC to release default address.
164 *
165 * @param phone Open phone to host controller driver.
166 * @return Error code.
167 */
168int usb_drv_release_default_address(int phone)
169{
170 return async_req_1_0(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
171 IPC_M_USBHC_RELEASE_DEFAULT_ADDRESS);
172}
173
174/** Ask HC for free address assignment.
175 *
176 * @param phone Open phone to host controller driver.
177 * @return Assigned USB address or negative error code.
178 */
179usb_address_t usb_drv_request_address(int phone)
180{
181 sysarg_t address;
182 int rc = async_req_1_1(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
183 IPC_M_USBHC_REQUEST_ADDRESS, &address);
184 if (rc != EOK) {
185 return rc;
186 } else {
187 return (usb_address_t) address;
188 }
189}
190
191/** Inform HC about binding address with devman handle.
192 *
193 * @param phone Open phone to host controller driver.
194 * @param address Address to be binded.
195 * @param handle Devman handle of the device.
196 * @return Error code.
197 */
198int usb_drv_bind_address(int phone, usb_address_t address,
199 devman_handle_t handle)
200{
201 int rc = async_req_3_0(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
202 IPC_M_USBHC_BIND_ADDRESS,
203 address, handle);
204
205 return rc;
206}
207
208/** Inform HC about address release.
209 *
210 * @param phone Open phone to host controller driver.
211 * @param address Address to be released.
212 * @return Error code.
213 */
214int usb_drv_release_address(int phone, usb_address_t address)
215{
216 return async_req_2_0(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
217 IPC_M_USBHC_RELEASE_ADDRESS, address);
218}
219
220/** Send data to HCD.
221 *
222 * @param phone Phone to HC.
223 * @param method Method used for calling.
224 * @param target Targeted device.
225 * @param buffer Data buffer (NULL to skip data transfer phase).
226 * @param size Buffer size (must be zero when @p buffer is NULL).
227 * @param handle Storage for transaction handle (cannot be NULL).
228 * @return Error status.
229 * @retval EINVAL Invalid parameter.
230 * @retval ENOMEM Not enough memory to complete the operation.
231 */
232static int async_send_buffer(int phone, int method,
233 usb_target_t target,
234 void *buffer, size_t size,
235 usb_handle_t *handle)
236{
237 if (phone < 0) {
238 return EINVAL;
239 }
240
241 if ((buffer == NULL) && (size > 0)) {
242 return EINVAL;
243 }
244
245 if (handle == NULL) {
246 return EINVAL;
247 }
248
249 transfer_info_t *transfer
250 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
251 if (transfer == NULL) {
252 return ENOMEM;
253 }
254
255 transfer->read_request = 0;
256 transfer->size_transferred = NULL;
257 transfer->buffer = NULL;
258 transfer->size = 0;
259 transfer->phone = phone;
260
261 int rc;
262
263 transfer->request = async_send_4(phone,
264 DEV_IFACE_ID(USBHC_DEV_IFACE),
265 method,
266 target.address, target.endpoint,
267 size,
268 &transfer->reply);
269
270 if (size > 0) {
271 rc = async_data_write_start(phone, buffer, size);
272 if (rc != EOK) {
273 async_wait_for(transfer->request, NULL);
274 return rc;
275 }
276 }
277
278 *handle = (usb_handle_t) transfer;
279
280 return EOK;
281}
282
283/** Prepare data retrieval.
284 *
285 * @param phone Opened phone to HCD.
286 * @param method Method used for calling.
287 * @param target Targeted device.
288 * @param buffer Buffer where to store retrieved data
289 * (NULL to skip data transfer phase).
290 * @param size Buffer size (must be zero when @p buffer is NULL).
291 * @param actual_size Storage where actual number of bytes transferred will
292 * be stored.
293 * @param handle Storage for transaction handle (cannot be NULL).
294 * @return Error status.
295 * @retval EINVAL Invalid parameter.
296 * @retval ENOMEM Not enough memory to complete the operation.
297 */
298static int async_recv_buffer(int phone, int method,
299 usb_target_t target,
300 void *buffer, size_t size, size_t *actual_size,
301 usb_handle_t *handle)
302{
303 if (phone < 0) {
304 return EINVAL;
305 }
306
307 if ((buffer == NULL) && (size > 0)) {
308 return EINVAL;
309 }
310
311 if (handle == NULL) {
312 return EINVAL;
313 }
314
315 transfer_info_t *transfer
316 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
317 if (transfer == NULL) {
318 return ENOMEM;
319 }
320
321 transfer->read_request = 0;
322 transfer->size_transferred = actual_size;
323 transfer->buffer = buffer;
324 transfer->size = size;
325 transfer->phone = phone;
326
327 transfer->request = async_send_4(phone,
328 DEV_IFACE_ID(USBHC_DEV_IFACE),
329 method,
330 target.address, target.endpoint,
331 size,
332 &transfer->reply);
333
334 if (buffer != NULL) {
335 transfer->read_request = async_data_read(phone, buffer, size,
336 &transfer->read_reply);
337 }
338
339 *handle = (usb_handle_t) transfer;
340
341 return EOK;
342}
343
344
345/** Blocks caller until given USB transaction is finished.
346 * After the transaction is finished, the user can access all output data
347 * given to initial call function.
348 *
349 * @param handle Transaction handle.
350 * @return Error status.
351 * @retval EOK No error.
352 * @retval EBADMEM Invalid handle.
353 * @retval ENOENT Data buffer associated with transaction does not exist.
354 */
355int usb_drv_async_wait_for(usb_handle_t handle)
356{
357 if (handle == 0) {
358 return EBADMEM;
359 }
360
361 int rc = EOK;
362
363 transfer_info_t *transfer = (transfer_info_t *) handle;
364
365 sysarg_t answer_rc;
366
367 /*
368 * If the buffer is not NULL, we must accept some data.
369 */
370 if ((transfer->buffer != NULL) && (transfer->size > 0)) {
371 async_wait_for(transfer->read_request, &answer_rc);
372
373 if (answer_rc != EOK) {
374 rc = (int) answer_rc;
375 goto leave;
376 }
377
378 if (transfer->size_transferred != NULL) {
379 *(transfer->size_transferred)
380 = IPC_GET_ARG2(transfer->read_reply);
381 }
382 }
383
384 async_wait_for(transfer->request, &answer_rc);
385
386 if (answer_rc != EOK) {
387 rc = (int) answer_rc;
388 goto leave;
389 }
390
391leave:
392 free(transfer);
393
394 return rc;
395}
396
397/** Send interrupt data to device. */
398int usb_drv_async_interrupt_out(int phone, usb_target_t target,
399 void *buffer, size_t size,
400 usb_handle_t *handle)
401{
402 return async_send_buffer(phone,
403 IPC_M_USBHC_INTERRUPT_OUT,
404 target,
405 buffer, size,
406 handle);
407}
408
409/** Request interrupt data from device. */
410int usb_drv_async_interrupt_in(int phone, usb_target_t target,
411 void *buffer, size_t size, size_t *actual_size,
412 usb_handle_t *handle)
413{
414 return async_recv_buffer(phone,
415 IPC_M_USBHC_INTERRUPT_IN,
416 target,
417 buffer, size, actual_size,
418 handle);
419}
420
421/** Start control write transfer. */
422int usb_drv_async_control_write_setup(int phone, usb_target_t target,
423 void *buffer, size_t size,
424 usb_handle_t *handle)
425{
426 return async_send_buffer(phone,
427 IPC_M_USBHC_CONTROL_WRITE_SETUP,
428 target,
429 buffer, size,
430 handle);
431}
432
433/** Send data during control write transfer. */
434int usb_drv_async_control_write_data(int phone, usb_target_t target,
435 void *buffer, size_t size,
436 usb_handle_t *handle)
437{
438 return async_send_buffer(phone,
439 IPC_M_USBHC_CONTROL_WRITE_DATA,
440 target,
441 buffer, size,
442 handle);
443}
444
445/** Finalize control write transfer. */
446int usb_drv_async_control_write_status(int phone, usb_target_t target,
447 usb_handle_t *handle)
448{
449 return async_recv_buffer(phone,
450 IPC_M_USBHC_CONTROL_WRITE_STATUS,
451 target,
452 NULL, 0, NULL,
453 handle);
454}
455
456/** Issue whole control write transfer. */
457int usb_drv_async_control_write(int phone, usb_target_t target,
458 void *setup_packet, size_t setup_packet_size,
459 void *buffer, size_t buffer_size,
460 usb_handle_t *handle)
461{
462 // FIXME - check input parameters instead of asserting them
463 assert(phone > 0);
464 assert(setup_packet != NULL);
465 assert(setup_packet_size > 0);
466 assert(((buffer != NULL) && (buffer_size > 0))
467 || ((buffer == NULL) && (buffer_size == 0)));
468 assert(handle != NULL);
469
470 transfer_info_t *transfer
471 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
472 if (transfer == NULL) {
473 return ENOMEM;
474 }
475
476 transfer->read_request = 0;
477 transfer->size_transferred = NULL;
478 transfer->buffer = NULL;
479 transfer->size = 0;
480 transfer->phone = phone;
481
482 int rc;
483
484 transfer->request = async_send_3(phone,
485 DEV_IFACE_ID(USBHC_DEV_IFACE),
486 IPC_M_USBHC_CONTROL_WRITE,
487 target.address, target.endpoint,
488 &transfer->reply);
489
490 rc = async_data_write_start(phone, setup_packet, setup_packet_size);
491 if (rc != EOK) {
492 async_wait_for(transfer->request, NULL);
493 return rc;
494 }
495
496 if (buffer_size > 0) {
497 rc = async_data_write_start(phone, buffer, buffer_size);
498 if (rc != EOK) {
499 async_wait_for(transfer->request, NULL);
500 return rc;
501 }
502 }
503
504 *handle = (usb_handle_t) transfer;
505
506 return EOK;
507}
508
509/** Start control read transfer. */
510int usb_drv_async_control_read_setup(int phone, usb_target_t target,
511 void *buffer, size_t size,
512 usb_handle_t *handle)
513{
514 return async_send_buffer(phone,
515 IPC_M_USBHC_CONTROL_READ_SETUP,
516 target,
517 buffer, size,
518 handle);
519}
520
521/** Read data during control read transfer. */
522int usb_drv_async_control_read_data(int phone, usb_target_t target,
523 void *buffer, size_t size, size_t *actual_size,
524 usb_handle_t *handle)
525{
526 return async_recv_buffer(phone,
527 IPC_M_USBHC_CONTROL_READ_DATA,
528 target,
529 buffer, size, actual_size,
530 handle);
531}
532
533/** Finalize control read transfer. */
534int usb_drv_async_control_read_status(int phone, usb_target_t target,
535 usb_handle_t *handle)
536{
537 return async_send_buffer(phone,
538 IPC_M_USBHC_CONTROL_READ_STATUS,
539 target,
540 NULL, 0,
541 handle);
542}
543
544/** Issue whole control read transfer. */
545int usb_drv_async_control_read(int phone, usb_target_t target,
546 void *setup_packet, size_t setup_packet_size,
547 void *buffer, size_t buffer_size, size_t *actual_size,
548 usb_handle_t *handle)
549{
550 // FIXME - check input parameters instead of asserting them
551 assert(phone > 0);
552 assert(setup_packet != NULL);
553 assert(setup_packet_size > 0);
554 assert(buffer != NULL);
555 assert(buffer_size > 0);
556 assert(handle != NULL);
557
558 transfer_info_t *transfer
559 = (transfer_info_t *) malloc(sizeof(transfer_info_t));
560 if (transfer == NULL) {
561 return ENOMEM;
562 }
563
564 transfer->size_transferred = actual_size;
565 transfer->buffer = buffer;
566 transfer->size = buffer_size;
567 transfer->phone = phone;
568
569 int rc;
570
571 transfer->request = async_send_4(phone,
572 DEV_IFACE_ID(USBHC_DEV_IFACE),
573 IPC_M_USBHC_CONTROL_READ,
574 target.address, target.endpoint,
575 buffer_size,
576 &transfer->reply);
577
578 rc = async_data_write_start(phone, setup_packet, setup_packet_size);
579 if (rc != EOK) {
580 async_wait_for(transfer->request, NULL);
581 return rc;
582 }
583
584 transfer->read_request = async_data_read(phone, buffer, buffer_size,
585 &transfer->read_reply);
586
587 *handle = (usb_handle_t) transfer;
588
589 return EOK;
590}
591
592/**
593 * @}
594 */
Note: See TracBrowser for help on using the repository browser.