source: mainline/uspace/drv/bus/usb/vhc/transfer.c@ dcf0597

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dcf0597 was c0e4b5b2, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usbhost refactoring: vhc

Now, please, review.

  • Property mode set to 100644
File size: 7.7 KB
Line 
1/*
2 * Copyright (c) 2011 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#include <errno.h>
30#include <str_error.h>
31#include <usb/debug.h>
32#include <usbvirt/device.h>
33#include <usb/host/bandwidth.h>
34#include <usbvirt/ipc.h>
35#include "vhcd.h"
36#include "hub/virthub.h"
37
38static bool is_set_address_transfer(vhc_transfer_t *transfer)
39{
40 if (transfer->batch->ep->target.endpoint != 0) {
41 return false;
42 }
43 if (transfer->batch->ep->transfer_type != USB_TRANSFER_CONTROL) {
44 return false;
45 }
46 if (usb_transfer_batch_direction(transfer->batch) != USB_DIRECTION_OUT) {
47 return false;
48 }
49 const usb_device_request_setup_packet_t *setup =
50 (void*)transfer->batch->setup_buffer;
51 if (setup->request_type != 0) {
52 return false;
53 }
54 if (setup->request != USB_DEVREQ_SET_ADDRESS) {
55 return false;
56 }
57
58 return true;
59}
60
61static int process_transfer_local(usb_transfer_batch_t *batch,
62 usbvirt_device_t *dev, size_t *actual_data_size)
63{
64 int rc;
65
66 const usb_direction_t dir = usb_transfer_batch_direction(batch);
67
68 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
69 if (dir == USB_DIRECTION_IN) {
70 rc = usbvirt_control_read(dev,
71 batch->setup_buffer, batch->setup_size,
72 batch->buffer, batch->buffer_size,
73 actual_data_size);
74 } else {
75 assert(dir == USB_DIRECTION_OUT);
76 rc = usbvirt_control_write(dev,
77 batch->setup_buffer, batch->setup_size,
78 batch->buffer, batch->buffer_size);
79 }
80 } else {
81 if (dir == USB_DIRECTION_IN) {
82 rc = usbvirt_data_in(dev, batch->ep->transfer_type,
83 batch->ep->target.endpoint,
84 batch->buffer, batch->buffer_size,
85 actual_data_size);
86 } else {
87 assert(dir == USB_DIRECTION_OUT);
88 rc = usbvirt_data_out(dev, batch->ep->transfer_type,
89 batch->ep->target.endpoint,
90 batch->buffer, batch->buffer_size);
91 }
92 }
93
94 return rc;
95}
96
97static int process_transfer_remote(usb_transfer_batch_t *batch,
98 async_sess_t *sess, size_t *actual_data_size)
99{
100 int rc;
101
102 const usb_direction_t dir = usb_transfer_batch_direction(batch);
103
104 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
105 if (dir == USB_DIRECTION_IN) {
106 rc = usbvirt_ipc_send_control_read(sess,
107 batch->setup_buffer, batch->setup_size,
108 batch->buffer, batch->buffer_size,
109 actual_data_size);
110 } else {
111 assert(dir == USB_DIRECTION_OUT);
112 rc = usbvirt_ipc_send_control_write(sess,
113 batch->setup_buffer, batch->setup_size,
114 batch->buffer, batch->buffer_size);
115 }
116 } else {
117 if (dir == USB_DIRECTION_IN) {
118 rc = usbvirt_ipc_send_data_in(sess, batch->ep->target.endpoint,
119 batch->ep->transfer_type,
120 batch->buffer, batch->buffer_size,
121 actual_data_size);
122 } else {
123 assert(dir == USB_DIRECTION_OUT);
124 rc = usbvirt_ipc_send_data_out(sess, batch->ep->target.endpoint,
125 batch->ep->transfer_type,
126 batch->buffer, batch->buffer_size);
127 }
128 }
129
130 return rc;
131}
132
133static vhc_transfer_t *dequeue_first_transfer(vhc_virtdev_t *dev)
134{
135 assert(fibril_mutex_is_locked(&dev->guard));
136 assert(!list_empty(&dev->transfer_queue));
137
138 vhc_transfer_t *transfer = list_get_instance(
139 list_first(&dev->transfer_queue), vhc_transfer_t, link);
140 list_remove(&transfer->link);
141
142 return transfer;
143}
144
145static void execute_transfer_callback_and_free(vhc_transfer_t *transfer,
146 size_t data_transfer_size, int outcome)
147{
148 assert(outcome != ENAK);
149 assert(transfer);
150 assert(transfer->batch);
151 usb_transfer_batch_finish_error(transfer->batch, NULL,
152 data_transfer_size, outcome);
153 usb_transfer_batch_destroy(transfer->batch);
154 free(transfer);
155}
156
157int vhc_init(vhc_data_t *instance)
158{
159 assert(instance);
160 list_initialize(&instance->devices);
161 fibril_mutex_initialize(&instance->guard);
162 usb2_bus_init(&instance->bus, BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
163 instance->magic = 0xDEADBEEF;
164 return virthub_init(&instance->hub, "root hub");
165}
166
167int vhc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
168{
169 assert(hcd);
170 assert(batch);
171 vhc_data_t *vhc = hcd_get_driver_data(hcd);
172 assert(vhc);
173
174 vhc_transfer_t *transfer = malloc(sizeof(vhc_transfer_t));
175 if (!transfer)
176 return ENOMEM;
177 link_initialize(&transfer->link);
178 transfer->batch = batch;
179
180 fibril_mutex_lock(&vhc->guard);
181
182 int targets = 0;
183
184 list_foreach(vhc->devices, link, vhc_virtdev_t, dev) {
185 fibril_mutex_lock(&dev->guard);
186 if (dev->address == transfer->batch->ep->target.address) {
187 if (!targets) {
188 list_append(&transfer->link, &dev->transfer_queue);
189 }
190 ++targets;
191 }
192 fibril_mutex_unlock(&dev->guard);
193 }
194
195 fibril_mutex_unlock(&vhc->guard);
196
197 if (targets > 1)
198 usb_log_warning("Transfer would be accepted by more devices!\n");
199
200 return targets ? EOK : ENOENT;
201}
202
203int vhc_transfer_queue_processor(void *arg)
204{
205 vhc_virtdev_t *dev = arg;
206 fibril_mutex_lock(&dev->guard);
207 while (dev->plugged) {
208 if (list_empty(&dev->transfer_queue)) {
209 fibril_mutex_unlock(&dev->guard);
210 async_usleep(10 * 1000);
211 fibril_mutex_lock(&dev->guard);
212 continue;
213 }
214
215 vhc_transfer_t *transfer = dequeue_first_transfer(dev);
216 fibril_mutex_unlock(&dev->guard);
217
218 int rc = EOK;
219 size_t data_transfer_size = 0;
220 if (dev->dev_sess) {
221 rc = process_transfer_remote(transfer->batch,
222 dev->dev_sess, &data_transfer_size);
223 } else if (dev->dev_local != NULL) {
224 rc = process_transfer_local(transfer->batch,
225 dev->dev_local, &data_transfer_size);
226 } else {
227 usb_log_warning("Device has no remote phone nor local node.\n");
228 rc = ESTALL;
229 }
230
231 usb_log_debug2("Transfer %p processed: %s.\n",
232 transfer, str_error(rc));
233
234 fibril_mutex_lock(&dev->guard);
235 if (rc == EOK) {
236 if (is_set_address_transfer(transfer)) {
237 usb_device_request_setup_packet_t *setup =
238 (void*) transfer->batch->setup_buffer;
239 dev->address = setup->value;
240 usb_log_debug2("Address changed to %d\n",
241 dev->address);
242 }
243 }
244 if (rc == ENAK) {
245 // FIXME: this will work only because we do
246 // not NAK control transfers but this is generally
247 // a VERY bad idea indeed
248 list_append(&transfer->link, &dev->transfer_queue);
249 }
250 fibril_mutex_unlock(&dev->guard);
251
252 if (rc != ENAK) {
253 execute_transfer_callback_and_free(transfer,
254 data_transfer_size, rc);
255 }
256
257 async_usleep(1000 * 100);
258 fibril_mutex_lock(&dev->guard);
259 }
260
261 /* Immediately fail all remaining transfers. */
262 while (!list_empty(&dev->transfer_queue)) {
263 vhc_transfer_t *transfer = dequeue_first_transfer(dev);
264 execute_transfer_callback_and_free(transfer, 0, EBADCHECKSUM);
265 }
266
267 fibril_mutex_unlock(&dev->guard);
268
269 return EOK;
270}
Note: See TracBrowser for help on using the repository browser.