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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since eed4139 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 8.6 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2018 Ondrej Hlavaty
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <errno.h>
31#include <str_error.h>
32#include <usb/debug.h>
33#include <usbvirt/device.h>
34#include <usb/host/bandwidth.h>
35#include <usb/host/endpoint.h>
36#include <usb/host/usb_transfer_batch.h>
37#include <usbvirt/ipc.h>
38#include "vhcd.h"
39#include "hub/virthub.h"
40
41static bool is_set_address_transfer(vhc_transfer_t *transfer)
42{
43 if (transfer->batch.target.endpoint != 0) {
44 return false;
45 }
46 if (transfer->batch.ep->transfer_type != USB_TRANSFER_CONTROL) {
47 return false;
48 }
49 if (transfer->batch.dir != USB_DIRECTION_OUT) {
50 return false;
51 }
52 const usb_device_request_setup_packet_t *setup =
53 &transfer->batch.setup.packet;
54 if (setup->request_type != 0) {
55 return false;
56 }
57 if (setup->request != USB_DEVREQ_SET_ADDRESS) {
58 return false;
59 }
60
61 return true;
62}
63
64static errno_t process_transfer_local(usb_transfer_batch_t *batch,
65 usbvirt_device_t *dev, size_t *actual_data_size)
66{
67 errno_t rc;
68
69 const usb_direction_t dir = batch->dir;
70
71 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
72 if (dir == USB_DIRECTION_IN) {
73 rc = usbvirt_control_read(dev,
74 batch->setup.buffer, USB_SETUP_PACKET_SIZE,
75 batch->dma_buffer.virt, batch->size,
76 actual_data_size);
77 } else {
78 assert(dir == USB_DIRECTION_OUT);
79 rc = usbvirt_control_write(dev,
80 batch->setup.buffer, USB_SETUP_PACKET_SIZE,
81 batch->dma_buffer.virt, batch->size);
82 }
83 } else {
84 if (dir == USB_DIRECTION_IN) {
85 rc = usbvirt_data_in(dev, batch->ep->transfer_type,
86 batch->ep->endpoint,
87 batch->dma_buffer.virt, batch->size,
88 actual_data_size);
89 } else {
90 assert(dir == USB_DIRECTION_OUT);
91 rc = usbvirt_data_out(dev, batch->ep->transfer_type,
92 batch->ep->endpoint,
93 batch->dma_buffer.virt, batch->size);
94 }
95 }
96
97 return rc;
98}
99
100static errno_t process_transfer_remote(usb_transfer_batch_t *batch,
101 async_sess_t *sess, size_t *actual_data_size)
102{
103 errno_t rc;
104
105 const usb_direction_t dir = batch->dir;
106
107 if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
108 if (dir == USB_DIRECTION_IN) {
109 rc = usbvirt_ipc_send_control_read(sess,
110 batch->setup.buffer, USB_SETUP_PACKET_SIZE,
111 batch->dma_buffer.virt, batch->size,
112 actual_data_size);
113 } else {
114 assert(dir == USB_DIRECTION_OUT);
115 rc = usbvirt_ipc_send_control_write(sess,
116 batch->setup.buffer, USB_SETUP_PACKET_SIZE,
117 batch->dma_buffer.virt, batch->size);
118 }
119 } else {
120 if (dir == USB_DIRECTION_IN) {
121 rc = usbvirt_ipc_send_data_in(sess, batch->ep->endpoint,
122 batch->ep->transfer_type,
123 batch->dma_buffer.virt, batch->size,
124 actual_data_size);
125 } else {
126 assert(dir == USB_DIRECTION_OUT);
127 rc = usbvirt_ipc_send_data_out(sess, batch->ep->endpoint,
128 batch->ep->transfer_type,
129 batch->dma_buffer.virt, batch->size);
130 }
131 }
132
133 return rc;
134}
135
136static vhc_transfer_t *dequeue_first_transfer(vhc_virtdev_t *dev)
137{
138 assert(fibril_mutex_is_locked(&dev->guard));
139 assert(!list_empty(&dev->transfer_queue));
140
141 vhc_transfer_t *transfer =
142 list_get_instance(list_first(&dev->transfer_queue),
143 vhc_transfer_t, link);
144 list_remove(&transfer->link);
145
146 return transfer;
147}
148
149static void execute_transfer_callback_and_free(vhc_transfer_t *transfer,
150 size_t data_transfer_size, errno_t outcome)
151{
152 assert(outcome != ENAK);
153 assert(transfer);
154 transfer->batch.error = outcome;
155 transfer->batch.transferred_size = data_transfer_size;
156 usb_transfer_batch_finish(&transfer->batch);
157}
158
159static usb_transfer_batch_t *batch_create(endpoint_t *ep)
160{
161 vhc_transfer_t *transfer = calloc(1, sizeof(vhc_transfer_t));
162 usb_transfer_batch_init(&transfer->batch, ep);
163 link_initialize(&transfer->link);
164 return &transfer->batch;
165}
166
167static int device_enumerate(device_t *device)
168{
169 vhc_data_t *vhc = bus_to_vhc(device->bus);
170 return usb2_bus_device_enumerate(&vhc->bus_helper, device);
171}
172
173static int endpoint_register(endpoint_t *endpoint)
174{
175 vhc_data_t *vhc = bus_to_vhc(endpoint->device->bus);
176 return usb2_bus_endpoint_register(&vhc->bus_helper, endpoint);
177}
178
179static void endpoint_unregister(endpoint_t *endpoint)
180{
181 vhc_data_t *vhc = bus_to_vhc(endpoint->device->bus);
182 usb2_bus_endpoint_unregister(&vhc->bus_helper, endpoint);
183
184 // TODO: abort transfer?
185}
186
187static const bus_ops_t vhc_bus_ops = {
188 .batch_create = batch_create,
189 .batch_schedule = vhc_schedule,
190
191 .device_enumerate = device_enumerate,
192 .endpoint_register = endpoint_register,
193 .endpoint_unregister = endpoint_unregister,
194};
195
196errno_t vhc_init(vhc_data_t *instance)
197{
198 assert(instance);
199 list_initialize(&instance->devices);
200 fibril_mutex_initialize(&instance->guard);
201 bus_init(&instance->bus, sizeof(device_t));
202 usb2_bus_helper_init(&instance->bus_helper, &bandwidth_accounting_usb11);
203 instance->bus.ops = &vhc_bus_ops;
204 return virthub_init(&instance->hub, "root hub");
205}
206
207errno_t vhc_schedule(usb_transfer_batch_t *batch)
208{
209 assert(batch);
210 vhc_transfer_t *transfer = (vhc_transfer_t *) batch;
211 vhc_data_t *vhc = bus_to_vhc(endpoint_get_bus(batch->ep));
212 assert(vhc);
213
214 fibril_mutex_lock(&vhc->guard);
215
216 int targets = 0;
217
218 list_foreach(vhc->devices, link, vhc_virtdev_t, dev) {
219 fibril_mutex_lock(&dev->guard);
220 if (dev->address == transfer->batch.target.address) {
221 if (!targets) {
222 list_append(&transfer->link, &dev->transfer_queue);
223 }
224 ++targets;
225 }
226 fibril_mutex_unlock(&dev->guard);
227 }
228
229 fibril_mutex_unlock(&vhc->guard);
230
231 if (targets > 1)
232 usb_log_warning("Transfer would be accepted by more devices!");
233
234 return targets ? EOK : ENOENT;
235}
236
237errno_t vhc_transfer_queue_processor(void *arg)
238{
239 vhc_virtdev_t *dev = arg;
240 fibril_mutex_lock(&dev->guard);
241 while (dev->plugged) {
242 if (list_empty(&dev->transfer_queue)) {
243 fibril_mutex_unlock(&dev->guard);
244 async_usleep(10 * 1000);
245 fibril_mutex_lock(&dev->guard);
246 continue;
247 }
248
249 vhc_transfer_t *transfer = dequeue_first_transfer(dev);
250 fibril_mutex_unlock(&dev->guard);
251
252 errno_t rc = EOK;
253 size_t data_transfer_size = 0;
254 if (dev->dev_sess) {
255 rc = process_transfer_remote(&transfer->batch,
256 dev->dev_sess, &data_transfer_size);
257 } else if (dev->dev_local != NULL) {
258 rc = process_transfer_local(&transfer->batch,
259 dev->dev_local, &data_transfer_size);
260 } else {
261 usb_log_warning("Device has no remote phone "
262 "nor local node.");
263 rc = ESTALL;
264 }
265
266 usb_log_debug2("Transfer %p processed: %s.",
267 transfer, str_error(rc));
268
269 fibril_mutex_lock(&dev->guard);
270 if (rc == EOK) {
271 if (is_set_address_transfer(transfer)) {
272 usb_device_request_setup_packet_t *setup =
273 (void *) transfer->batch.setup.buffer;
274 dev->address = setup->value;
275 usb_log_debug2("Address changed to %d",
276 dev->address);
277 }
278 }
279 if (rc == ENAK) {
280 // FIXME: this will work only because we do
281 // not NAK control transfers but this is generally
282 // a VERY bad idea indeed
283 list_append(&transfer->link, &dev->transfer_queue);
284 }
285 fibril_mutex_unlock(&dev->guard);
286
287 if (rc != ENAK) {
288 execute_transfer_callback_and_free(transfer,
289 data_transfer_size, rc);
290 }
291
292 async_usleep(1000 * 100);
293 fibril_mutex_lock(&dev->guard);
294 }
295
296 /* Immediately fail all remaining transfers. */
297 while (!list_empty(&dev->transfer_queue)) {
298 vhc_transfer_t *transfer = dequeue_first_transfer(dev);
299 execute_transfer_callback_and_free(transfer, 0, EBADCHECKSUM);
300 }
301
302 fibril_mutex_unlock(&dev->guard);
303
304 return EOK;
305}
Note: See TracBrowser for help on using the repository browser.