source: mainline/uspace/lib/usbhost/src/hcd.c@ 5dfb70c9

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

usbhost: polling fibril gets bus, not hcd

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
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 libusbhost
30 * @{
31 */
32/** @file
33 *
34 */
35
36#include <assert.h>
37#include <async.h>
38#include <ddf/interrupt.h>
39#include <errno.h>
40#include <macros.h>
41#include <str_error.h>
42#include <usb/debug.h>
43#include <usb/descriptor.h>
44#include <usb/request.h>
45#include <usb_iface.h>
46
47#include "bus.h"
48#include "ddf_helpers.h"
49#include "endpoint.h"
50#include "usb_transfer_batch.h"
51
52#include "hcd.h"
53
54int hc_dev_add(ddf_dev_t *);
55int hc_dev_remove(ddf_dev_t *);
56int hc_dev_gone(ddf_dev_t *);
57int hc_fun_online(ddf_fun_t *);
58int hc_fun_offline(ddf_fun_t *);
59
60static driver_ops_t hc_driver_ops = {
61 .dev_add = hc_dev_add,
62 .dev_remove = hc_dev_remove,
63 .dev_gone = hc_dev_gone,
64 .fun_online = hc_fun_offline,
65 .fun_offline = hc_fun_offline,
66};
67
68static const hc_driver_t *hc_driver;
69
70int hc_driver_main(const hc_driver_t *driver)
71{
72 driver_t ddf_driver = {
73 .name = driver->name,
74 .driver_ops = &hc_driver_ops,
75 };
76
77 /* Remember ops to call. */
78 hc_driver = driver;
79
80 return ddf_driver_main(&ddf_driver);
81}
82
83/** IRQ handling callback, forward status from call to diver structure.
84 *
85 * @param[in] dev DDF instance of the device to use.
86 * @param[in] iid (Unused).
87 * @param[in] call Pointer to the call from kernel.
88 */
89static void irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
90{
91 assert(dev);
92 hc_device_t *hcd = dev_to_hcd(dev);
93
94 const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, interrupt);
95 assert(ops);
96
97 const uint32_t status = IPC_GET_ARG1(*call);
98 ops->interrupt(hcd->bus, status);
99}
100
101/** Worker for the HW interrupt replacement fibril.
102 */
103static int interrupt_polling(void *arg)
104{
105 bus_t *bus = arg;
106 assert(bus);
107
108 const bus_ops_t *interrupt_ops = BUS_OPS_LOOKUP(bus->ops, interrupt);
109 const bus_ops_t *status_ops = BUS_OPS_LOOKUP(bus->ops, status);
110 if (!interrupt_ops || !status_ops)
111 return ENOTSUP;
112
113 uint32_t status = 0;
114 while (status_ops->status(bus, &status) == EOK) {
115 interrupt_ops->interrupt(bus, status);
116 status = 0;
117 /* We should wait 1 frame - 1ms here, but this polling is a
118 * lame crutch anyway so don't hog the system. 10ms is still
119 * good enough for emergency mode */
120 async_usleep(10000);
121 }
122 return EOK;
123}
124
125static inline void irq_code_clean(irq_code_t *code)
126{
127 if (code) {
128 free(code->ranges);
129 free(code->cmds);
130 code->ranges = NULL;
131 code->cmds = NULL;
132 code->rangecount = 0;
133 code->cmdcount = 0;
134 }
135}
136
137/** Register interrupt handler
138 *
139 * @param[in] device Host controller DDF device
140 * @param[in] regs Register range
141 * @param[in] irq Interrupt number
142 * @paran[in] handler Interrupt handler
143 * @param[in] gen_irq_code IRQ code generator.
144 *
145 * @return IRQ capability handle on success.
146 * @return Negative error code.
147 */
148static int hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
149{
150 assert(hcd);
151 irq_code_t irq_code = {0};
152
153 if (!hc_driver->irq_code_gen)
154 return ENOTSUP;
155
156 const int irq = hc_driver->irq_code_gen(&irq_code, hcd, hw_res);
157 if (irq < 0) {
158 usb_log_error("Failed to generate IRQ code: %s.\n",
159 str_error(irq));
160 return irq;
161 }
162
163 /* Register handler to avoid interrupt lockup */
164 const int irq_cap = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code);
165 irq_code_clean(&irq_code);
166 if (irq_cap < 0) {
167 usb_log_error("Failed to register interrupt handler: %s.\n",
168 str_error(irq_cap));
169 return irq_cap;
170 }
171
172 /* Enable interrupts */
173 int ret = hcd_ddf_enable_interrupt(hcd, irq);
174 if (ret != EOK) {
175 usb_log_error("Failed to enable interrupts: %s.\n",
176 str_error(ret));
177 unregister_interrupt_handler(hcd->ddf_dev, irq_cap);
178 return ret;
179 }
180 return irq_cap;
181}
182
183/** Initialize HC in memory of the driver.
184 *
185 * @param device DDF instance of the device to use
186 * @return Error code
187 *
188 * This function does all the preparatory work for hc and rh drivers:
189 * - gets device's hw resources
190 * - attempts to enable interrupts
191 * - registers interrupt handler
192 * - calls driver specific initialization
193 * - registers root hub
194 */
195int hc_dev_add(ddf_dev_t *device)
196{
197 int ret = EOK;
198 assert(device);
199
200 if (!hc_driver->hc_add) {
201 usb_log_error("Driver '%s' does not support adding devices.", hc_driver->name);
202 return ENOTSUP;
203 }
204
205 ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size);
206 if (ret != EOK) {
207 usb_log_error("Failed to setup HC device.\n");
208 return ret;
209 }
210
211 hc_device_t *hcd = dev_to_hcd(device);
212
213 hw_res_list_parsed_t hw_res;
214 ret = hcd_ddf_get_registers(hcd, &hw_res);
215 if (ret != EOK) {
216 usb_log_error("Failed to get register memory addresses "
217 "for `%s': %s.\n", ddf_dev_get_name(device),
218 str_error(ret));
219 goto err_hcd;
220 }
221
222 ret = hc_driver->hc_add(hcd, &hw_res);
223 if (ret != EOK) {
224 usb_log_error("Failed to init HCD.\n");
225 goto err_hw_res;
226 }
227
228 assert(hcd->bus);
229
230 /* Setup interrupts */
231 hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res);
232 if (hcd->irq_cap >= 0) {
233 usb_log_debug("Hw interrupts enabled.\n");
234 }
235
236 /* Claim the device from BIOS */
237 if (hc_driver->claim)
238 ret = hc_driver->claim(hcd);
239 if (ret != EOK) {
240 usb_log_error("Failed to claim `%s' for `%s': %s",
241 ddf_dev_get_name(device), hc_driver->name, str_error(ret));
242 goto err_irq;
243 }
244
245 /* Start hw */
246 if (hc_driver->start)
247 ret = hc_driver->start(hcd);
248 if (ret != EOK) {
249 usb_log_error("Failed to start HCD: %s.\n", str_error(ret));
250 goto err_irq;
251 }
252
253 const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, status);
254
255 /* Need working irq replacement to setup root hub */
256 if (hcd->irq_cap < 0 && ops) {
257 hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus);
258 if (!hcd->polling_fibril) {
259 usb_log_error("Failed to create polling fibril\n");
260 ret = ENOMEM;
261 goto err_started;
262 }
263 fibril_add_ready(hcd->polling_fibril);
264 usb_log_warning("Failed to enable interrupts: %s."
265 " Falling back to polling.\n", str_error(hcd->irq_cap));
266 }
267
268 /*
269 * Creating root hub registers a new USB device so HC
270 * needs to be ready at this time.
271 */
272 if (hc_driver->setup_root_hub)
273 ret = hc_driver->setup_root_hub(hcd);
274 if (ret != EOK) {
275 usb_log_error("Failed to setup HC root hub: %s.\n",
276 str_error(ret));
277 goto err_polling;
278 }
279
280 usb_log_info("Controlling new `%s' device `%s'.\n",
281 hc_driver->name, ddf_dev_get_name(device));
282 return EOK;
283
284err_polling:
285 // TODO: Stop the polling fibril (refactor the interrupt_polling func)
286 //
287err_started:
288 if (hc_driver->stop)
289 hc_driver->stop(hcd);
290err_irq:
291 unregister_interrupt_handler(device, hcd->irq_cap);
292 if (hc_driver->hc_remove)
293 hc_driver->hc_remove(hcd);
294err_hw_res:
295 hw_res_list_parsed_clean(&hw_res);
296err_hcd:
297 hcd_ddf_clean_hc(hcd);
298 return ret;
299}
300
301int hc_dev_remove(ddf_dev_t *dev)
302{
303 int err;
304 hc_device_t *hcd = dev_to_hcd(dev);
305
306 if (hc_driver->stop)
307 if ((err = hc_driver->stop(hcd)))
308 return err;
309
310 unregister_interrupt_handler(dev, hcd->irq_cap);
311
312 if (hc_driver->hc_remove)
313 if ((err = hc_driver->hc_remove(hcd)))
314 return err;
315
316 hcd_ddf_clean_hc(hcd);
317
318 // TODO probably not complete
319
320 return EOK;
321}
322
323int hc_dev_gone(ddf_dev_t *dev)
324{
325 int err = ENOTSUP;
326 hc_device_t *hcd = dev_to_hcd(dev);
327
328 if (hc_driver->hc_gone)
329 err = hc_driver->hc_gone(hcd);
330
331 hcd_ddf_clean_hc(hcd);
332
333 return err;
334}
335
336int hc_fun_online(ddf_fun_t *fun)
337{
338 assert(fun);
339
340 device_t *dev = ddf_fun_data_get(fun);
341 assert(dev);
342
343 usb_log_info("Device(%d): Requested to be brought online.", dev->address);
344 return bus_device_online(dev);
345}
346
347int hc_fun_offline(ddf_fun_t *fun)
348{
349 assert(fun);
350
351 device_t *dev = ddf_fun_data_get(fun);
352 assert(dev);
353
354 usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
355 return bus_device_offline(dev);
356}
357
358/** Get max packet size for the control endpoint 0.
359 *
360 * For LS, HS, and SS devices this value is fixed. For FS devices we must fetch
361 * the first 8B of the device descriptor to determine it.
362 *
363 * @return Max packet size for EP 0
364 */
365int hcd_get_ep0_max_packet_size(uint16_t *mps, bus_t *bus, device_t *dev)
366{
367 assert(mps);
368
369 static const uint16_t mps_fixed [] = {
370 [USB_SPEED_LOW] = 8,
371 [USB_SPEED_HIGH] = 64,
372 [USB_SPEED_SUPER] = 512,
373 };
374
375 if (dev->speed < ARRAY_SIZE(mps_fixed) && mps_fixed[dev->speed] != 0) {
376 *mps = mps_fixed[dev->speed];
377 return EOK;
378 }
379
380 const usb_target_t control_ep = {{
381 .address = dev->address,
382 .endpoint = 0,
383 }};
384
385 usb_standard_device_descriptor_t desc = { 0 };
386 const usb_device_request_setup_packet_t get_device_desc_8 =
387 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
388
389 usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
390 ssize_t got = bus_device_send_batch_sync(dev, control_ep, USB_DIRECTION_IN,
391 (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
392 "read first 8 bytes of dev descriptor");
393
394 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
395 const int err = got < 0 ? got : EOVERFLOW;
396 usb_log_error("Failed to get 8B of dev descr: %s.", str_error(err));
397 return err;
398 }
399
400 if (desc.descriptor_type != USB_DESCTYPE_DEVICE) {
401 usb_log_error("The device responded with wrong device descriptor.");
402 return EIO;
403 }
404
405 uint16_t version = uint16_usb2host(desc.usb_spec_version);
406 if (version < 0x0300) {
407 /* USB 2 and below have MPS raw in the field */
408 *mps = desc.max_packet_size;
409 } else {
410 /* USB 3 have MPS as an 2-based exponent */
411 *mps = (1 << desc.max_packet_size);
412 }
413 return EOK;
414}
415
416/**
417 * Setup devices Transaction Translation.
418 *
419 * This applies for Low/Full speed devices under High speed hub only. Other
420 * devices just inherit TT from the hub.
421 *
422 * Roothub must be handled specially.
423 */
424void hcd_setup_device_tt(device_t *dev)
425{
426 if (!dev->hub)
427 return;
428
429 if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
430 /* For LS devices under HS hub */
431 dev->tt.address = dev->hub->address;
432 dev->tt.port = dev->port;
433 }
434 else {
435 /* Inherit hub's TT */
436 dev->tt = dev->hub->tt;
437 }
438}
439
440/** Check setup packet data for signs of toggle reset.
441 *
442 * @param[in] requst Setup requst data.
443 *
444 * @retval -1 No endpoints need reset.
445 * @retval 0 All endpoints need reset.
446 * @retval >0 Specified endpoint needs reset.
447 *
448 */
449toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
450 const usb_device_request_setup_packet_t *request)
451{
452 assert(request);
453 switch (request->request)
454 {
455 /* Clear Feature ENPOINT_STALL */
456 case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
457 /* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
458 if ((request->request_type == 0x2) &&
459 (request->value == USB_FEATURE_ENDPOINT_HALT))
460 return RESET_EP;
461 break;
462 case USB_DEVREQ_SET_CONFIGURATION:
463 case USB_DEVREQ_SET_INTERFACE:
464 /* Recipient must be device, this resets all endpoints,
465 * In fact there should be no endpoints but EP 0 registered
466 * as different interfaces use different endpoints,
467 * unless you're changing configuration or alternative
468 * interface of an already setup device. */
469 if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
470 return RESET_ALL;
471 break;
472 default:
473 break;
474 }
475
476 return RESET_NONE;
477}
478
479
480/**
481 * @}
482 */
Note: See TracBrowser for help on using the repository browser.