ohci.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/usb.h>
00040 #include <usb/ddfiface.h>
00041 #include <usb/debug.h>
00042 
00043 #include "ohci.h"
00044 #include "iface.h"
00045 #include "pci.h"
00046 #include "hc.h"
00047 #include "root_hub.h"
00048 
00049 typedef struct ohci {
00050         ddf_fun_t *hc_fun;
00051         ddf_fun_t *rh_fun;
00052 
00053         hc_t hc;
00054         rh_t rh;
00055 } ohci_t;
00056 
00057 static inline ohci_t * dev_to_ohci(ddf_dev_t *dev)
00058 {
00059         assert(dev);
00060         assert(dev->driver_data);
00061         return dev->driver_data;
00062 }
00063 
00070 static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
00071 {
00072         hc_t *hc = &dev_to_ohci(dev)->hc;
00073         assert(hc);
00074         const uint16_t status = IPC_GET_ARG1(*call);
00075         hc_interrupt(hc, status);
00076 }
00077 /*----------------------------------------------------------------------------*/
00084 static int usb_iface_get_address(
00085     ddf_fun_t *fun, devman_handle_t handle, usb_address_t *address)
00086 {
00087         assert(fun);
00088         usb_device_keeper_t *manager = &dev_to_ohci(fun->dev)->hc.manager;
00089 
00090         usb_address_t addr = usb_device_keeper_find(manager, handle);
00091         if (addr < 0) {
00092                 return addr;
00093         }
00094 
00095         if (address != NULL) {
00096                 *address = addr;
00097         }
00098 
00099         return EOK;
00100 }
00101 /*----------------------------------------------------------------------------*/
00108 static int usb_iface_get_hc_handle(
00109     ddf_fun_t *fun, devman_handle_t *handle)
00110 {
00111         assert(fun);
00112         ddf_fun_t *hc_fun = dev_to_ohci(fun->dev)->hc_fun;
00113         assert(hc_fun);
00114 
00115         if (handle != NULL)
00116                 *handle = hc_fun->handle;
00117         return EOK;
00118 }
00119 /*----------------------------------------------------------------------------*/
00121 static usb_iface_t usb_iface = {
00122         .get_hc_handle = usb_iface_get_hc_handle,
00123         .get_address = usb_iface_get_address
00124 };
00125 /*----------------------------------------------------------------------------*/
00127 static ddf_dev_ops_t hc_ops = {
00128         .interfaces[USBHC_DEV_IFACE] = &hc_iface, /* see iface.h/c */
00129 };
00130 /*----------------------------------------------------------------------------*/
00132 static ddf_dev_ops_t rh_ops = {
00133         .interfaces[USB_DEV_IFACE] = &usb_iface,
00134 };
00135 /*----------------------------------------------------------------------------*/
00147 int device_setup_ohci(ddf_dev_t *device)
00148 {
00149         ohci_t *instance = malloc(sizeof(ohci_t));
00150         if (instance == NULL) {
00151                 usb_log_error("Failed to allocate OHCI driver.\n");
00152                 return ENOMEM;
00153         }
00154 
00155 #define CHECK_RET_DEST_FREE_RETURN(ret, message...) \
00156 if (ret != EOK) { \
00157         if (instance->hc_fun) { \
00158                 instance->hc_fun->ops = NULL; \
00159                 instance->hc_fun->driver_data = NULL; \
00160                 ddf_fun_destroy(instance->hc_fun); \
00161         } \
00162         if (instance->rh_fun) { \
00163                 instance->rh_fun->ops = NULL; \
00164                 instance->rh_fun->driver_data = NULL; \
00165                 ddf_fun_destroy(instance->rh_fun); \
00166         } \
00167         free(instance); \
00168         usb_log_error(message); \
00169         return ret; \
00170 } else (void)0
00171 
00172         instance->rh_fun = NULL;
00173         instance->hc_fun = ddf_fun_create(device, fun_exposed, "ohci-hc");
00174         int ret = instance->hc_fun ? EOK : ENOMEM;
00175         CHECK_RET_DEST_FREE_RETURN(ret, "Failed to create OHCI HC function.\n");
00176         instance->hc_fun->ops = &hc_ops;
00177         instance->hc_fun->driver_data = &instance->hc;
00178 
00179         instance->rh_fun = ddf_fun_create(device, fun_inner, "ohci-rh");
00180         ret = instance->rh_fun ? EOK : ENOMEM;
00181         CHECK_RET_DEST_FREE_RETURN(ret, "Failed to create OHCI RH function.\n");
00182         instance->rh_fun->ops = &rh_ops;
00183 
00184         uintptr_t reg_base = 0;
00185         size_t reg_size = 0;
00186         int irq = 0;
00187 
00188         ret = pci_get_my_registers(device, &reg_base, &reg_size, &irq);
00189         CHECK_RET_DEST_FREE_RETURN(ret,
00190             "Failed to get memory addresses for %" PRIun ": %s.\n",
00191             device->handle, str_error(ret));
00192         usb_log_debug("Memory mapped regs at %p (size %zu), IRQ %d.\n",
00193             (void *) reg_base, reg_size, irq);
00194 
00195         bool interrupts = false;
00196 #ifdef CONFIG_USBHC_NO_INTERRUPTS
00197         usb_log_warning("Interrupts disabled in OS config, "
00198             "falling back to polling.\n");
00199 #else
00200         ret = pci_enable_interrupts(device);
00201         if (ret != EOK) {
00202                 usb_log_warning("Failed to enable interrupts: %s.\n",
00203                     str_error(ret));
00204                 usb_log_info("HW interrupts not available, "
00205                     "falling back to polling.\n");
00206         } else {
00207                 usb_log_debug("Hw interrupts enabled.\n");
00208                 interrupts = true;
00209         }
00210 #endif
00211 
00212         ret = hc_init(&instance->hc, reg_base, reg_size, interrupts);
00213         CHECK_RET_DEST_FREE_RETURN(ret, "Failed(%d) to init ohci-hcd.\n", ret);
00214 
00215 #define CHECK_RET_FINI_RETURN(ret, message...) \
00216 if (ret != EOK) { \
00217         hc_fini(&instance->hc); \
00218         CHECK_RET_DEST_FREE_RETURN(ret, message); \
00219 } else (void)0
00220 
00221         /* It does no harm if we register this on polling */
00222         ret = register_interrupt_handler(device, irq, irq_handler,
00223             &instance->hc.interrupt_code);
00224         CHECK_RET_FINI_RETURN(ret,
00225             "Failed(%d) to register interrupt handler.\n", ret);
00226 
00227         ret = ddf_fun_bind(instance->hc_fun);
00228         CHECK_RET_FINI_RETURN(ret,
00229             "Failed(%d) to bind OHCI device function: %s.\n",
00230             ret, str_error(ret));
00231 
00232         ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
00233         CHECK_RET_FINI_RETURN(ret,
00234             "Failed to add OHCI to HC class: %s.\n", str_error(ret));
00235 
00236         device->driver_data = instance;
00237 
00238         hc_start_hw(&instance->hc);
00239         hc_register_hub(&instance->hc, instance->rh_fun);
00240         return EOK;
00241 
00242 #undef CHECK_RET_DEST_FUN_RETURN
00243 #undef CHECK_RET_FINI_RETURN
00244 }

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