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

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

usb2_bus: no longer be a bus

As the number of implemented functions got to 3, it's not so beneficial
to inherit usb2 bus to get the functionality. Overall, four trampolines
needed to be added, which is an acceptable number.

Now, the usb2_bus has become a usb2_bus_helper, to be used as
a companion to the common bus.

This is mostly a preparation to remove the runtime binding of the bus
methods.

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