uhci.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Jan Vesely
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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, /* see iface.h/c */
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, &reg_base, &reg_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         /* It does no harm if we register this on polling */
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 }

Generated on Thu Jun 2 07:45:44 2011 for HelenOS/USB by  doxygen 1.4.7