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

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

Add atomic control transfers to local USBHC

Also this commit might have serious BUGS.

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