source: mainline/uspace/drv/bus/usb/uhci/uhci.c@ ffa96c2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ffa96c2 was b5111c46, checked in by Jiri Svoboda <jiri@…>, 11 years ago

Convert OHCI and UHCI away from DDF_DATA_IMPLANT.

  • Property mode set to 100644
File size: 7.6 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 drvusbuhci
30 * @{
31 */
32/** @file
33 * @brief UHCI driver
34 */
35
36#include <errno.h>
37#include <stdbool.h>
38#include <str_error.h>
39#include <ddf/interrupt.h>
40#include <usb_iface.h>
41#include <usb/ddfiface.h>
42#include <usb/debug.h>
43
44#include "uhci.h"
45
46#include "res.h"
47#include "hc.h"
48#include "root_hub.h"
49
50/** Structure representing both functions of UHCI hc, USB host controller
51 * and USB root hub */
52typedef struct uhci {
53 /** Pointer to DDF representation of UHCI host controller */
54 ddf_fun_t *hc_fun;
55 /** Pointer to DDF representation of UHCI root hub */
56 ddf_fun_t *rh_fun;
57
58 /** Internal driver's representation of UHCI host controller */
59 hc_t hc;
60 /** Internal driver's representation of UHCI root hub */
61 rh_t *rh;
62} uhci_t;
63
64static inline uhci_t *dev_to_uhci(ddf_dev_t *dev)
65{
66 return ddf_dev_data_get(dev);
67}
68
69/** IRQ handling callback, forward status from call to diver structure.
70 *
71 * @param[in] dev DDF instance of the device to use.
72 * @param[in] iid (Unused).
73 * @param[in] call Pointer to the call from kernel.
74 */
75static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
76{
77 assert(dev);
78 uhci_t *uhci = dev_to_uhci(dev);
79 if (!uhci) {
80 usb_log_error("Interrupt on not yet initialized device.\n");
81 return;
82 }
83 const uint16_t status = IPC_GET_ARG1(*call);
84 hc_interrupt(&uhci->hc, status);
85}
86
87/** Operations supported by the HC driver */
88static ddf_dev_ops_t hc_ops = {
89 .interfaces[USBHC_DEV_IFACE] = &hcd_iface, /* see iface.h/c */
90};
91
92/** Gets handle of the respective hc.
93 *
94 * @param[in] fun DDF function of uhci device.
95 * @param[out] handle Host cotnroller handle.
96 * @return Error code.
97 */
98static int usb_iface_get_hc_handle(ddf_fun_t *fun, devman_handle_t *handle)
99{
100 ddf_fun_t *hc_fun = dev_to_uhci(ddf_fun_get_dev(fun))->hc_fun;
101 assert(hc_fun);
102
103 if (handle != NULL)
104 *handle = ddf_fun_get_handle(hc_fun);
105 return EOK;
106}
107
108/** USB interface implementation used by RH */
109static usb_iface_t usb_iface = {
110 .get_hc_handle = usb_iface_get_hc_handle,
111};
112
113/** Get root hub hw resources (I/O registers).
114 *
115 * @param[in] fun Root hub function.
116 * @return Pointer to the resource list used by the root hub.
117 */
118static hw_resource_list_t *get_resource_list(ddf_fun_t *fun)
119{
120 rh_t *rh = ddf_fun_data_get(fun);
121 assert(rh);
122 return &rh->resource_list;
123}
124
125/** Interface to provide the root hub driver with hw info */
126static hw_res_ops_t hw_res_iface = {
127 .get_resource_list = get_resource_list,
128 .enable_interrupt = NULL,
129};
130
131static pio_window_t *get_pio_window(ddf_fun_t *fun)
132{
133 rh_t *rh = ddf_fun_data_get(fun);
134
135 if (rh == NULL)
136 return NULL;
137 return &rh->pio_window;
138}
139
140static pio_window_ops_t pio_window_iface = {
141 .get_pio_window = get_pio_window
142};
143
144/** RH function support for uhci_rhd */
145static ddf_dev_ops_t rh_ops = {
146 .interfaces[USB_DEV_IFACE] = &usb_iface,
147 .interfaces[HW_RES_DEV_IFACE] = &hw_res_iface,
148 .interfaces[PIO_WINDOW_DEV_IFACE] = &pio_window_iface
149};
150
151/** Initialize hc and rh DDF structures and their respective drivers.
152 *
153 * @param[in] device DDF instance of the device to use.
154 *
155 * This function does all the preparatory work for hc and rh drivers:
156 * - gets device's hw resources
157 * - disables UHCI legacy support (PCI config space)
158 * - attempts to enable interrupts
159 * - registers interrupt handler
160 */
161int device_setup_uhci(ddf_dev_t *device)
162{
163 bool ih_registered = false;
164 bool hc_inited = false;
165 bool fun_bound = false;
166 int rc;
167
168 if (!device)
169 return EBADMEM;
170
171 uhci_t *instance = ddf_dev_data_alloc(device, sizeof(uhci_t));
172 if (instance == NULL) {
173 usb_log_error("Failed to allocate OHCI driver.\n");
174 return ENOMEM;
175 }
176
177 instance->hc_fun = ddf_fun_create(device, fun_exposed, "uhci_hc");
178 if (instance->hc_fun == NULL) {
179 usb_log_error("Failed to create UHCI HC function.\n");
180 rc = ENOMEM;
181 goto error;
182 }
183
184 ddf_fun_set_ops(instance->hc_fun, &hc_ops);
185
186 instance->rh_fun = ddf_fun_create(device, fun_inner, "uhci_rh");
187 if (instance->rh_fun == NULL) {
188 usb_log_error("Failed to create UHCI RH function.\n");
189 rc = ENOMEM;
190 goto error;
191 }
192
193 ddf_fun_set_ops(instance->rh_fun, &rh_ops);
194 instance->rh = ddf_fun_data_alloc(instance->rh_fun, sizeof(rh_t));
195
196 addr_range_t regs;
197 int irq = 0;
198
199 rc = get_my_registers(device, &regs, &irq);
200 if (rc != EOK) {
201 usb_log_error("Failed to get I/O addresses for %" PRIun ": %s.\n",
202 ddf_dev_get_handle(device), str_error(rc));
203 goto error;
204 }
205 usb_log_debug("I/O regs at %p (size %zu), IRQ %d.\n",
206 RNGABSPTR(regs), RNGSZ(regs), irq);
207
208 rc = disable_legacy(device);
209 if (rc != EOK) {
210 usb_log_error("Failed to disable legacy USB: %s.\n",
211 str_error(rc));
212 goto error;
213 }
214
215 rc = hc_register_irq_handler(device, &regs, irq, irq_handler);
216 if (rc != EOK) {
217 usb_log_error("Failed to register interrupt handler: %s.\n",
218 str_error(rc));
219 goto error;
220 }
221
222 ih_registered = true;
223
224 bool interrupts = false;
225 rc = enable_interrupts(device);
226 if (rc != EOK) {
227 usb_log_warning("Failed to enable interrupts: %s."
228 " Falling back to polling.\n", str_error(rc));
229 } else {
230 usb_log_debug("Hw interrupts enabled.\n");
231 interrupts = true;
232 }
233
234 rc = hc_init(&instance->hc, instance->hc_fun, &regs, interrupts);
235 if (rc != EOK) {
236 usb_log_error("Failed to init uhci_hcd: %s.\n", str_error(rc));
237 goto error;
238 }
239
240 hc_inited = true;
241
242 rc = ddf_fun_bind(instance->hc_fun);
243 if (rc != EOK) {
244 usb_log_error("Failed to bind UHCI device function: %s.\n",
245 str_error(rc));
246 goto error;
247 }
248
249 fun_bound = true;
250
251 rc = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
252 if (rc != EOK) {
253 usb_log_error("Failed to add UHCI to HC class: %s.\n",
254 str_error(rc));
255 goto error;
256 }
257
258 rc = rh_init(instance->rh, instance->rh_fun, &regs, 0x10, 4);
259 if (rc != EOK) {
260 usb_log_error("Failed to setup UHCI root hub: %s.\n",
261 str_error(rc));
262 goto error;
263 }
264
265 rc = ddf_fun_bind(instance->rh_fun);
266 if (rc != EOK) {
267 usb_log_error("Failed to register UHCI root hub: %s.\n",
268 str_error(rc));
269 goto error;
270 }
271
272 return EOK;
273
274error:
275 if (fun_bound)
276 ddf_fun_unbind(instance->hc_fun);
277 if (hc_inited)
278 hc_fini(&instance->hc);
279 if (ih_registered)
280 unregister_interrupt_handler(device, irq);
281 if (instance->hc_fun != NULL)
282 ddf_fun_destroy(instance->hc_fun);
283 if (instance->rh_fun != NULL) {
284 ddf_fun_destroy(instance->rh_fun);
285 }
286 return rc;
287}
288/**
289 * @}
290 */
Note: See TracBrowser for help on using the repository browser.