00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00035 #include <errno.h>
00036 #include <str_error.h>
00037 #include <ddf/interrupt.h>
00038 #include <usb_iface.h>
00039 #include <usb/ddfiface.h>
00040 #include <usb/debug.h>
00041
00042 #include "uhci.h"
00043 #include "iface.h"
00044 #include "pci.h"
00045
00046 #include "hc.h"
00047 #include "root_hub.h"
00048
00051 typedef struct uhci {
00053 ddf_fun_t *hc_fun;
00055 ddf_fun_t *rh_fun;
00056
00058 hc_t hc;
00060 rh_t rh;
00061 } uhci_t;
00062
00063 static inline uhci_t * dev_to_uhci(const ddf_dev_t *dev)
00064 {
00065 assert(dev);
00066 assert(dev->driver_data);
00067 return dev->driver_data;
00068 }
00069
00076 static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
00077 {
00078 assert(dev);
00079 uhci_t *uhci = dev_to_uhci(dev);
00080 hc_t *hc = &uhci->hc;
00081 const uint16_t status = IPC_GET_ARG1(*call);
00082 assert(hc);
00083 hc_interrupt(hc, status);
00084 }
00085
00087 static ddf_dev_ops_t hc_ops = {
00088 .interfaces[USBHC_DEV_IFACE] = &hc_iface,
00089 };
00090
00097 static int usb_iface_get_address(
00098 ddf_fun_t *fun, devman_handle_t handle, usb_address_t *address)
00099 {
00100 assert(fun);
00101 usb_device_keeper_t *manager = &dev_to_uhci(fun->dev)->hc.manager;
00102 usb_address_t addr = usb_device_keeper_find(manager, handle);
00103
00104 if (addr < 0) {
00105 return addr;
00106 }
00107
00108 if (address != NULL) {
00109 *address = addr;
00110 }
00111
00112 return EOK;
00113 }
00114
00121 static int usb_iface_get_hc_handle(ddf_fun_t *fun, devman_handle_t *handle)
00122 {
00123 assert(fun);
00124 ddf_fun_t *hc_fun = dev_to_uhci(fun->dev)->hc_fun;
00125 assert(hc_fun);
00126
00127 if (handle != NULL)
00128 *handle = hc_fun->handle;
00129 return EOK;
00130 }
00131
00133 static usb_iface_t usb_iface = {
00134 .get_hc_handle = usb_iface_get_hc_handle,
00135 .get_address = usb_iface_get_address
00136 };
00137
00143 static hw_resource_list_t *get_resource_list(ddf_fun_t *fun)
00144 {
00145 assert(fun);
00146 rh_t *rh = fun->driver_data;
00147 assert(rh);
00148 return &rh->resource_list;
00149 }
00150
00152 static hw_res_ops_t hw_res_iface = {
00153 .get_resource_list = get_resource_list,
00154 .enable_interrupt = NULL,
00155 };
00156
00158 static ddf_dev_ops_t rh_ops = {
00159 .interfaces[USB_DEV_IFACE] = &usb_iface,
00160 .interfaces[HW_RES_DEV_IFACE] = &hw_res_iface
00161 };
00162
00173 int device_setup_uhci(ddf_dev_t *device)
00174 {
00175 assert(device);
00176 uhci_t *instance = malloc(sizeof(uhci_t));
00177 if (instance == NULL) {
00178 usb_log_error("Failed to allocate OHCI driver.\n");
00179 return ENOMEM;
00180 }
00181
00182 #define CHECK_RET_DEST_FREE_RETURN(ret, message...) \
00183 if (ret != EOK) { \
00184 if (instance->hc_fun) \
00185 instance->hc_fun->ops = NULL; \
00186 instance->hc_fun->driver_data = NULL; \
00187 ddf_fun_destroy(instance->hc_fun); \
00188 if (instance->rh_fun) {\
00189 instance->rh_fun->ops = NULL; \
00190 instance->rh_fun->driver_data = NULL; \
00191 ddf_fun_destroy(instance->rh_fun); \
00192 } \
00193 free(instance); \
00194 usb_log_error(message); \
00195 return ret; \
00196 } else (void)0
00197
00198 instance->rh_fun = NULL;
00199 instance->hc_fun = ddf_fun_create(device, fun_exposed, "uhci-hc");
00200 int ret = (instance->hc_fun == NULL) ? ENOMEM : EOK;
00201 CHECK_RET_DEST_FREE_RETURN(ret, "Failed to create UHCI HC function.\n");
00202 instance->hc_fun->ops = &hc_ops;
00203 instance->hc_fun->driver_data = &instance->hc;
00204
00205 instance->rh_fun = ddf_fun_create(device, fun_inner, "uhci-rh");
00206 ret = (instance->rh_fun == NULL) ? ENOMEM : EOK;
00207 CHECK_RET_DEST_FREE_RETURN(ret, "Failed to create UHCI RH function.\n");
00208 instance->rh_fun->ops = &rh_ops;
00209 instance->rh_fun->driver_data = &instance->rh;
00210
00211 uintptr_t reg_base = 0;
00212 size_t reg_size = 0;
00213 int irq = 0;
00214
00215 ret = pci_get_my_registers(device, ®_base, ®_size, &irq);
00216 CHECK_RET_DEST_FREE_RETURN(ret,
00217 "Failed to get I/O addresses for %" PRIun ": %s.\n",
00218 device->handle, str_error(ret));
00219 usb_log_debug("I/O regs at 0x%p (size %zu), IRQ %d.\n",
00220 (void *) reg_base, reg_size, irq);
00221
00222 ret = pci_disable_legacy(device);
00223 CHECK_RET_DEST_FREE_RETURN(ret,
00224 "Failed(%d) to disable legacy USB: %s.\n", ret, str_error(ret));
00225
00226 bool interrupts = false;
00227 #ifdef CONFIG_USBHC_NO_INTERRUPTS
00228 usb_log_warning("Interrupts disabled in OS config, " \
00229 "falling back to polling.\n");
00230 #else
00231 ret = pci_enable_interrupts(device);
00232 if (ret != EOK) {
00233 usb_log_warning("Failed to enable interrupts: %s.\n",
00234 str_error(ret));
00235 usb_log_info("HW interrupts not available, " \
00236 "falling back to polling.\n");
00237 } else {
00238 usb_log_debug("Hw interrupts enabled.\n");
00239 interrupts = true;
00240 }
00241 #endif
00242
00243
00244 ret = hc_init(&instance->hc, (void*)reg_base, reg_size, interrupts);
00245 CHECK_RET_DEST_FREE_RETURN(ret,
00246 "Failed(%d) to init uhci-hcd: %s.\n", ret, str_error(ret));
00247
00248 #define CHECK_RET_FINI_RETURN(ret, message...) \
00249 if (ret != EOK) { \
00250 hc_fini(&instance->hc); \
00251 CHECK_RET_DEST_FREE_RETURN(ret, message); \
00252 return ret; \
00253 } else (void)0
00254
00255
00256 ret = register_interrupt_handler(device, irq, irq_handler,
00257 &instance->hc.interrupt_code);
00258 CHECK_RET_FINI_RETURN(ret,
00259 "Failed(%d) to register interrupt handler: %s.\n",
00260 ret, str_error(ret));
00261
00262 ret = ddf_fun_bind(instance->hc_fun);
00263 CHECK_RET_FINI_RETURN(ret,
00264 "Failed(%d) to bind UHCI device function: %s.\n",
00265 ret, str_error(ret));
00266
00267 ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
00268 CHECK_RET_FINI_RETURN(ret,
00269 "Failed to add UHCI to HC class: %s.\n", str_error(ret));
00270
00271 ret = rh_init(&instance->rh, instance->rh_fun,
00272 (uintptr_t)instance->hc.registers + 0x10, 4);
00273 CHECK_RET_FINI_RETURN(ret,
00274 "Failed(%d) to setup UHCI root hub: %s.\n", ret, str_error(ret));
00275
00276 ret = ddf_fun_bind(instance->rh_fun);
00277 CHECK_RET_FINI_RETURN(ret,
00278 "Failed(%d) to register UHCI root hub: %s.\n", ret, str_error(ret));
00279
00280 device->driver_data = instance;
00281 return EOK;
00282 #undef CHECK_RET_FINI_RETURN
00283 }