source: mainline/uspace/lib/usbhost/src/hcd.c@ 296d22fc

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

usbhub: revert the runtime binding of bus methods

It was just a dead end.

  • Property mode set to 100644
File size: 9.1 KB
RevLine 
[4daee7a]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 *
[1102eca]34 * Host controller driver framework. Encapsulates DDF device of HC to an
35 * hc_device_t, which is passed to driver implementing hc_driver_t.
[4daee7a]36 */
37
[8d2e251]38#include <assert.h>
39#include <async.h>
[32fb6bce]40#include <ddf/interrupt.h>
[8d2e251]41#include <errno.h>
[306a36d]42#include <macros.h>
[2b61945]43#include <str_error.h>
[64fea02]44#include <usb/debug.h>
45#include <usb/descriptor.h>
46#include <usb/request.h>
47#include <usb_iface.h>
48
49#include "bus.h"
[32fb6bce]50#include "ddf_helpers.h"
[64fea02]51#include "endpoint.h"
52#include "usb_transfer_batch.h"
[8d2e251]53
[b4c1c95]54#include "hcd.h"
55
[32fb6bce]56int hc_dev_add(ddf_dev_t *);
57int hc_dev_remove(ddf_dev_t *);
58int hc_dev_gone(ddf_dev_t *);
59int hc_fun_online(ddf_fun_t *);
60int hc_fun_offline(ddf_fun_t *);
[41924f30]61
[32fb6bce]62static driver_ops_t hc_driver_ops = {
63 .dev_add = hc_dev_add,
64 .dev_remove = hc_dev_remove,
65 .dev_gone = hc_dev_gone,
[8eb7095]66 .fun_online = hc_fun_online,
[32fb6bce]67 .fun_offline = hc_fun_offline,
68};
69
70static const hc_driver_t *hc_driver;
71
[1102eca]72/**
73 * The main HC driver routine.
74 */
[32fb6bce]75int hc_driver_main(const hc_driver_t *driver)
76{
77 driver_t ddf_driver = {
78 .name = driver->name,
79 .driver_ops = &hc_driver_ops,
80 };
81
82 /* Remember ops to call. */
83 hc_driver = driver;
84
85 return ddf_driver_main(&ddf_driver);
86}
87
[1102eca]88/**
89 * IRQ handling callback. Call the bus operation.
[21be46a]90 *
[1102eca]91 * Currently, there is a bus ops lookup to find the interrupt handler. So far,
92 * the mechanism is too flexible, as it allows different instances of HC to
93 * have different IRQ handlers, disallowing us to optimize the lookup here.
94 * TODO: Make the bus mechanism less flexible in irq handling and remove the
95 * lookup.
[32fb6bce]96 */
97static void irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
98{
99 assert(dev);
100 hc_device_t *hcd = dev_to_hcd(dev);
101
102 const uint32_t status = IPC_GET_ARG1(*call);
[296d22fc]103 hcd->bus->ops->interrupt(hcd->bus, status);
[32fb6bce]104}
105
[1102eca]106/**
107 * Worker for the HW interrupt replacement fibril.
[21be46a]108 */
[32fb6bce]109static int interrupt_polling(void *arg)
110{
[f98f4b7]111 bus_t *bus = arg;
112 assert(bus);
[32fb6bce]113
[296d22fc]114 if (!bus->ops->interrupt || !bus->ops->status)
[32fb6bce]115 return ENOTSUP;
116
117 uint32_t status = 0;
[296d22fc]118 while (bus->ops->status(bus, &status) == EOK) {
119 bus->ops->interrupt(bus, status);
[32fb6bce]120 status = 0;
121 /* We should wait 1 frame - 1ms here, but this polling is a
122 * lame crutch anyway so don't hog the system. 10ms is still
123 * good enough for emergency mode */
124 async_usleep(10000);
125 }
126 return EOK;
127}
[21be46a]128
[1102eca]129/**
130 * Clean the IRQ code bottom-half.
131 */
[32fb6bce]132static inline void irq_code_clean(irq_code_t *code)
133{
134 if (code) {
135 free(code->ranges);
136 free(code->cmds);
137 code->ranges = NULL;
138 code->cmds = NULL;
139 code->rangecount = 0;
140 code->cmdcount = 0;
141 }
142}
143
[1102eca]144/**
145 * Register an interrupt handler. If there is a callback to setup the bottom half,
146 * invoke it and register it. Register for notifications.
147 *
148 * If this method fails, a polling fibril is started instead.
[32fb6bce]149 *
[1102eca]150 * @param[in] hcd Host controller device.
151 * @param[in] hw_res Resources to be used.
[32fb6bce]152 *
153 * @return IRQ capability handle on success.
154 * @return Negative error code.
155 */
156static int hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
157{
158 assert(hcd);
159 irq_code_t irq_code = {0};
160
161 if (!hc_driver->irq_code_gen)
162 return ENOTSUP;
163
164 const int irq = hc_driver->irq_code_gen(&irq_code, hcd, hw_res);
165 if (irq < 0) {
[a1732929]166 usb_log_error("Failed to generate IRQ code: %s.",
[32fb6bce]167 str_error(irq));
168 return irq;
169 }
170
171 /* Register handler to avoid interrupt lockup */
172 const int irq_cap = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code);
173 irq_code_clean(&irq_code);
174 if (irq_cap < 0) {
[a1732929]175 usb_log_error("Failed to register interrupt handler: %s.",
[32fb6bce]176 str_error(irq_cap));
177 return irq_cap;
178 }
179
180 /* Enable interrupts */
181 int ret = hcd_ddf_enable_interrupt(hcd, irq);
182 if (ret != EOK) {
[a1732929]183 usb_log_error("Failed to enable interrupts: %s.",
[32fb6bce]184 str_error(ret));
185 unregister_interrupt_handler(hcd->ddf_dev, irq_cap);
186 return ret;
187 }
188 return irq_cap;
189}
190
[1102eca]191/**
192 * Initialize HC in memory of the driver.
[32fb6bce]193 *
194 * This function does all the preparatory work for hc and rh drivers:
195 * - gets device's hw resources
196 * - attempts to enable interrupts
197 * - registers interrupt handler
198 * - calls driver specific initialization
199 * - registers root hub
[1102eca]200 *
201 * @param device DDF instance of the device to use
202 * @return Error code
[32fb6bce]203 */
204int hc_dev_add(ddf_dev_t *device)
205{
206 int ret = EOK;
207 assert(device);
208
209 if (!hc_driver->hc_add) {
210 usb_log_error("Driver '%s' does not support adding devices.", hc_driver->name);
211 return ENOTSUP;
212 }
213
214 ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size);
215 if (ret != EOK) {
[a1732929]216 usb_log_error("Failed to setup HC device.");
[32fb6bce]217 return ret;
218 }
219
220 hc_device_t *hcd = dev_to_hcd(device);
221
222 hw_res_list_parsed_t hw_res;
223 ret = hcd_ddf_get_registers(hcd, &hw_res);
224 if (ret != EOK) {
225 usb_log_error("Failed to get register memory addresses "
[a1732929]226 "for `%s': %s.", ddf_dev_get_name(device),
[32fb6bce]227 str_error(ret));
228 goto err_hcd;
229 }
230
231 ret = hc_driver->hc_add(hcd, &hw_res);
232 if (ret != EOK) {
[a1732929]233 usb_log_error("Failed to init HCD.");
[32fb6bce]234 goto err_hw_res;
235 }
236
237 assert(hcd->bus);
238
239 /* Setup interrupts */
240 hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res);
241 if (hcd->irq_cap >= 0) {
[a1732929]242 usb_log_debug("Hw interrupts enabled.");
[32fb6bce]243 }
244
245 /* Claim the device from BIOS */
246 if (hc_driver->claim)
247 ret = hc_driver->claim(hcd);
248 if (ret != EOK) {
249 usb_log_error("Failed to claim `%s' for `%s': %s",
250 ddf_dev_get_name(device), hc_driver->name, str_error(ret));
251 goto err_irq;
252 }
253
254 /* Start hw */
255 if (hc_driver->start)
256 ret = hc_driver->start(hcd);
257 if (ret != EOK) {
[a1732929]258 usb_log_error("Failed to start HCD: %s.", str_error(ret));
[32fb6bce]259 goto err_irq;
260 }
261
[296d22fc]262 const bus_ops_t *ops = hcd->bus->ops;
[32fb6bce]263
264 /* Need working irq replacement to setup root hub */
[296d22fc]265 if (hcd->irq_cap < 0 && ops->status) {
[32fb6bce]266 hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus);
267 if (!hcd->polling_fibril) {
[a1732929]268 usb_log_error("Failed to create polling fibril");
[32fb6bce]269 ret = ENOMEM;
270 goto err_started;
271 }
272 fibril_add_ready(hcd->polling_fibril);
273 usb_log_warning("Failed to enable interrupts: %s."
[a1732929]274 " Falling back to polling.", str_error(hcd->irq_cap));
[32fb6bce]275 }
276
277 /*
278 * Creating root hub registers a new USB device so HC
279 * needs to be ready at this time.
280 */
281 if (hc_driver->setup_root_hub)
282 ret = hc_driver->setup_root_hub(hcd);
283 if (ret != EOK) {
[a1732929]284 usb_log_error("Failed to setup HC root hub: %s.",
[32fb6bce]285 str_error(ret));
286 goto err_polling;
287 }
288
[a1732929]289 usb_log_info("Controlling new `%s' device `%s'.",
[32fb6bce]290 hc_driver->name, ddf_dev_get_name(device));
291 return EOK;
292
293err_polling:
294 // TODO: Stop the polling fibril (refactor the interrupt_polling func)
295 //
296err_started:
297 if (hc_driver->stop)
298 hc_driver->stop(hcd);
299err_irq:
300 unregister_interrupt_handler(device, hcd->irq_cap);
301 if (hc_driver->hc_remove)
302 hc_driver->hc_remove(hcd);
303err_hw_res:
304 hw_res_list_parsed_clean(&hw_res);
305err_hcd:
306 hcd_ddf_clean_hc(hcd);
307 return ret;
308}
309
310int hc_dev_remove(ddf_dev_t *dev)
311{
312 int err;
313 hc_device_t *hcd = dev_to_hcd(dev);
314
315 if (hc_driver->stop)
316 if ((err = hc_driver->stop(hcd)))
317 return err;
318
319 unregister_interrupt_handler(dev, hcd->irq_cap);
320
321 if (hc_driver->hc_remove)
322 if ((err = hc_driver->hc_remove(hcd)))
323 return err;
324
325 hcd_ddf_clean_hc(hcd);
326
327 // TODO probably not complete
328
329 return EOK;
330}
331
332int hc_dev_gone(ddf_dev_t *dev)
333{
334 int err = ENOTSUP;
335 hc_device_t *hcd = dev_to_hcd(dev);
336
337 if (hc_driver->hc_gone)
338 err = hc_driver->hc_gone(hcd);
339
340 hcd_ddf_clean_hc(hcd);
341
342 return err;
343}
344
345int hc_fun_online(ddf_fun_t *fun)
346{
347 assert(fun);
348
349 device_t *dev = ddf_fun_data_get(fun);
350 assert(dev);
351
352 usb_log_info("Device(%d): Requested to be brought online.", dev->address);
353 return bus_device_online(dev);
354}
355
356int hc_fun_offline(ddf_fun_t *fun)
357{
358 assert(fun);
359
360 device_t *dev = ddf_fun_data_get(fun);
361 assert(dev);
362
363 usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
364 return bus_device_offline(dev);
[21be46a]365}
366
[237df2f]367
[4daee7a]368/**
369 * @}
370 */
Note: See TracBrowser for help on using the repository browser.