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

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

Connecting to "parent" host controller

USB drivers can now connect to host controller drivers they physically
belong to (so far, everything connected to VHC).

Each USB device must implement the USB interface of libdrv to allow
this (it is absolutely neccesary for hubs).

USB drivers can use usb_drv_hc_connect() to connect to "their" host
controller.

Child devices created with usb_drv_register_child_in_devman()are set
to handle this connection automatically.

UHCI and VHC drivers were updated.

  • Property mode set to 100644
File size: 13.0 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 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/** Start control read transfer. */
498int usb_drv_async_control_read_setup(int phone, usb_target_t target,
499 void *buffer, size_t size,
500 usb_handle_t *handle)
501{
502 return async_send_buffer(phone,
503 IPC_M_USBHC_CONTROL_READ_SETUP,
504 target,
505 buffer, size,
506 handle);
507}
508
509/** Read data during control read transfer. */
510int usb_drv_async_control_read_data(int phone, usb_target_t target,
511 void *buffer, size_t size, size_t *actual_size,
512 usb_handle_t *handle)
513{
514 return async_recv_buffer(phone,
515 IPC_M_USBHC_CONTROL_READ_DATA,
516 target,
517 buffer, size, actual_size,
518 handle);
519}
520
521/** Finalize control read transfer. */
522int usb_drv_async_control_read_status(int phone, usb_target_t target,
523 usb_handle_t *handle)
524{
525 return async_send_buffer(phone,
526 IPC_M_USBHC_CONTROL_READ_STATUS,
527 target,
528 NULL, 0,
529 handle);
530}
531
532/**
533 * @}
534 */
Note: See TracBrowser for help on using the repository browser.