Exemple #1
0
/*! net_fd_start_recv_ecm - start recv urb(s)
 *
 * @param function_instance - pointer to this function instance
 * @return none
 */
int net_fd_start_recv_ecm(struct usbd_function_instance *function_instance)
{
        struct usb_network_private *npd = function_instance->privdata;
        int i;
        int network_start_urbs = NETWORK_START_URBS;
        int hs = usbd_high_speed(function_instance);
        int endpoint_index = BULK_OUT_A;

        if (hs)
                network_start_urbs = NETWORK_START_URBS * 3;


        for (i = 0; i < network_start_urbs; i++) {
                struct usbd_urb *urb;
                BREAK_IF(!(urb = usbd_alloc_urb(function_instance, endpoint_index,
                                                usbd_endpoint_transferSize(function_instance, endpoint_index, hs),
                                                net_fd_recv_urb)));
                TRACE_MSG5(NTT,"i: %d urb: %p priv: %p npd: %p function: %p",
                                i, urb, urb->function_privdata, npd, function_instance);

                //urb->function_privdata = npd;
                if (usbd_start_out_urb(urb)) {
                        urb->function_privdata = NULL;
                        usbd_free_urb(urb);
                }
        }
        return 0;
}
/*!
 * generic_cl_device_request - called to process a request to endpoint or interface
 * @param function
 * @param request
 * @return non-zero for failure, will cause endpoint zero stall
 */
static int generic_cl_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
{
        TRACE_MSG5(GCLASS, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x wLength: %04x",
                        request->bmRequestType, request->bRequest, request->wValue,
                        request->wIndex, request->wLength);

        return -EINVAL;
}
/*!
 * inteltest_cl_device_request - called to process a request to endpoint or interface
 * @param function
 * @param request
 * @return non-zero for failure, will cause endpoint zero stall
 */
static int inteltest_cl_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
{
        struct usbd_urb *urb;
        u16 wLength = le16_to_cpu(request->wLength);


        TRACE_MSG5(GCLASS, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x wLength: %04x",
                        request->bmRequestType, request->bRequest, request->wValue,
                        request->wIndex, request->wLength);

        /* XXX it should be this, need to verify
         */
        RETURN_EINVAL_UNLESS(USB_REQ_RECIPIENT_DEVICE == (request->bmRequestType & USB_REQ_RECIPIENT_MASK));

        switch (ctrl->bRequest) {
        switch (request->bmRequestType & USB_REQ_DIRECTION_MASK) {
        case USB_REQ_DEVICE2HOST:

                switch (request->bRequest) {
                case 0x5c:      /* read test */
                        RETURN_EINVAL_UNLESS((urb = usbd_alloc_urb_ep0 (function, wLength, NULL)));
                        RETURN_ZERO_UNLESS(rc || usbd_start_in_urb(urb));
                        break;
                }
                break;

        case USB_REQ_HOST2DEVICE:

                switch (request->bRequest) {
                case 0x5b:      /* write test */
                        RETURN_EINVAL_UNLESS((urb = usbd_alloc_urb_ep0 (function, wLength, NULL)));
                        RETURN_ZERO_UNLESS(rc || usbd_start_out_urb(urb));
                        break;
                }
                break;
        }

        return -EINVAL;
}
/* ********************************************************************************************* */
#if !defined(OTG_C99)
/*! function_ops - operations table for the USB Device Core
 */
static struct usbd_function_operations inteltest_function_ops;

/*! inteltest_class_driver - USB Device Core function driver definition
 */
struct usbd_class_driver inteltest_class_driver;

#else /* defined(OTG_C99) */
/*! function_ops - operations table for the USB Device Core
 */
static struct usbd_function_operations inteltest_function_ops = {
        .device_request = inteltest_cl_device_request,           /*!< called for each received device request */
};

/*! inteltest_class_driver - USB Device Core function driver definition
 */
struct usbd_class_driver inteltest_class_driver = {
        .driver.name = "inteltest-class",                            /*!< driver name */
        .driver.fops = &inteltest_function_ops,                             /*!< operations table */
};
#endif /* defined(OTG_C99) */


/* USB Module init/exit ***************************************************** */

//#if OTG_EPILOGUE
/*!
 * inteltest_cl_modexit() - module init
 *
 * This is called by the Linux kernel; when the module is being unloaded
 * if compiled as a module. This function is never called if the
 * driver is linked into the kernel.
 * @return none
 */
static void inteltest_cl_modexit (void)
{
        usbd_deregister_class_function (&inteltest_class_driver);
        otg_trace_invalidate_tag(GCLASS);
}

module_exit (inteltest_cl_modexit);
//#endif

/*!
 * inteltest_cl_modinit() - module init
 *
 * This is called by the Linux kernel; either when the module is loaded
 * if compiled as a module, or during the system intialization if the
 * driver is linked into the kernel.
 *
 * This function will parse module parameters if required and then register
 * the inteltest driver with the USB Device software.
 *
 */
static int inteltest_cl_modinit (void)
{
        #if !defined(OTG_C99)
        /*! function_ops - operations table for the USB Device Core
         */
        ZERO(inteltest_function_ops);
        inteltest_function_ops.device_request=inteltest_cl_device_request;          /*! called for each received device request */

        /*! class_driver - USB Device Core function driver definition
         */
        ZERO(inteltest_class_driver);
        inteltest_class_driver.driver.name = "inteltest-class";                            /*! driver name */
        inteltest_class_driver.driver.fops = &inteltest_function_ops;                             /*! operations table */
        #endif /* defined(OTG_C99) */

        GCLASS = otg_trace_obtain_tag(NULL, "inteltest-cf");

        // register as usb function driver
        TRACE_MSG0(GCLASS, "REGISTER CLASS");
        THROW_IF (usbd_register_class_function (&inteltest_class_driver, "inteltest-class", NULL), error);

        TRACE_MSG0(GCLASS, "REGISTER FINISHED");

        CATCH(error) {
                inteltest_cl_modexit();
                return -EINVAL;
        }
        return 0;
}
Exemple #4
0
/*! 
 * generic_cf_modinit() - module init
 *
 * This is called by the Linux kernel; either when the module is loaded
 * if compiled as a module, or during the system intialization if the 
 * driver is linked into the kernel.
 *
 * This function will parse module parameters if required and then register
 * the generic driver with the USB Device software.
 *
 */
static int generic_cf_modinit (void)
{
        int i;

        #if !defined(OTG_C99)
        //generic_cf_global_init();
        #endif /* defined(OTG_C99) */


        GENERIC = otg_trace_obtain_tag();

        i = MODPARM(idVendor);

        printk (KERN_INFO "Model ID is %s",MODPARM(iProduct));

#if 0
        TRACE_MSG4(GENERIC, "config_name: \"%s\" load_all: %d class_name: \"%s\" interface_names: \"%s\"",
                        MODPARM(config_name) ? MODPARM(config_name) : "", 
                        MODPARM(load_all), 
                        MODPARM(class_name) ? MODPARM(class_name) : "", 
                        MODPARM(interface_names) ? MODPARM(interface_names) : "");
#else

        TRACE_MSG5(GENERIC, "config_name: \"%s\" load_all: %d class_name: \"%s\" interface_names: \"%s\" Serial: \"%s\"",
                        generic_config_name(), 
                        MODPARM(load_all), 
                        MODPARM(class_name) ? MODPARM(class_name) : "", 
                        MODPARM(interface_names) ? MODPARM(interface_names) : "",
			MODPARM(iSerialNumber) ? MODPARM(iSerialNumber) : "");

#endif

        /* load config or configs
         */
        
        if (preset_config_name()  || MODPARM(load_all)) {

		if (preset_config_name()){
                        MODPARM(load_all) = 0;
		}

                printk (KERN_INFO "%s: config_name: \"%s\" load_all: %d\n", 
                                __FUNCTION__, generic_config_name() , MODPARM(load_all));

                /* search for named config
                 */
                for (i = 0; ; i++) {
                        struct generic_config *config = generic_configs + i;
                        BREAK_UNLESS(config->interface_names);
                        printk(KERN_INFO"%s: checking[%d] \"%s\"\n", __FUNCTION__, i, config->composite_driver.driver.name);

			if (MODPARM(iSerialNumber) && strlen(MODPARM(iSerialNumber)) &&
                            /* For the moment, we will only use serial number for msc and mtp.  I suggest we come up with a more generic
                               way to determine if a function driver needs to use the serial number. (for instance another function member. */
                            ( ( !strcmp(config->composite_driver.driver.name, "mtp")) || !strcmp(config->composite_driver.driver.name, "msc")) ){
                              config->device_description.iSerialNumber = MODPARM(iSerialNumber);
			}

                        config->device_description.iProduct = MODPARM(iProduct);

                        generic_cf_register(config, generic_config_name());
                        //printk(KERN_INFO"%s: loaded  %s\n", __FUNCTION__, config->composite_driver.driver.name);
                }
        }
        else {
                struct generic_config *config = &generic_config;

                //printk (KERN_INFO "%s: idVendor: %04x idProduct: %04x\n", __FUNCTION__, MODPARM(idVendor), MODPARM(idProduct));
                //printk (KERN_INFO "%s: class_name: \"%s\" _interface_names: \"%s\"\n", 
                //                __FUNCTION__, MODPARM(class_name), MODPARM(interface_names));

                if (MODPARM(driver_name) && strlen(MODPARM(driver_name)))
                        config->composite_driver.driver.name = MODPARM(driver_name);

                if (MODPARM(class_name) && strlen(MODPARM(class_name)))
                        config->class_name = MODPARM(class_name);

                if (MODPARM(interface_names) && strlen(MODPARM(interface_names)))
                        config->interface_names = MODPARM(interface_names);

                if (MODPARM(iConfiguration) && strlen(MODPARM(iConfiguration)))
                        config->configuration_description.iConfiguration = MODPARM(iConfiguration);

                if (MODPARM(bDeviceClass))
                        config->device_description.bDeviceClass = MODPARM(bDeviceClass);

                if (MODPARM(bDeviceSubClass))
                        config->device_description.bDeviceSubClass = MODPARM(bDeviceSubClass);

                if (MODPARM(bDeviceProtocol))
                        config->device_description.bDeviceProtocol = MODPARM(bDeviceProtocol);

                if (MODPARM(idVendor)) 
                        config->device_description.idVendor = MODPARM(idVendor);
                else
                        config->device_description.idVendor = CONFIG_OTG_GENERIC_VENDORID;

                if (MODPARM(idProduct))
                        config->device_description.idProduct = MODPARM(idProduct);
                else
                        config->device_description.idProduct = CONFIG_OTG_GENERIC_PRODUCTID;

                if (MODPARM(bcdDevice))
                        config->device_description.bcdDevice = MODPARM(bcdDevice);
                else
                        config->device_description.bcdDevice = CONFIG_OTG_GENERIC_BCDDEVICE;

                if (MODPARM(iManufacturer) && strlen(MODPARM(iManufacturer)))
                        config->device_description.iManufacturer = MODPARM(iManufacturer);
                else
                        config->device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER;

                if (MODPARM(iProduct) && strlen(MODPARM(iProduct)))
                        config->device_description.iProduct = MODPARM(iProduct);
                else
                        config->device_description.iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME;

                if (MODPARM(iSerialNumber) && strlen(MODPARM(iSerialNumber))){
                        config->device_description.iSerialNumber = MODPARM(iSerialNumber);
		}
                if (MODPARM(interface_names))
                        config->interface_names = MODPARM(interface_names);

                generic_cf_register(config, NULL);
        }
        return 0;
}
Exemple #5
0
/*!
 * generic_cf_register()
 *
 */
void generic_cf_register(struct generic_config *config, char *match)
{
        char *cp, *sp, *lp;
        int i;
        char **interface_list = NULL; 
        int interfaces = 0;

        //printk(KERN_INFO"%s: Driver: \"%s\" idVendor: %04x idProduct: %04x interface_names: \"%s\" match: \"%s\"\n", 
        //                __FUNCTION__,
        //                config->composite_driver.driver.name, MODPARM(idVendor), MODPARM(idProduct), config->interface_names, 
        //                match ? match : "");

        TRACE_MSG5(GENERIC, "Driver: \"%s\" idVendor: %04x idProduct: %04x interface_names: \"%s\" match: \"%s\"",
                        config->composite_driver.driver.name, MODPARM(idVendor), MODPARM(idProduct), config->interface_names, 
                        match ? match : "");


        RETURN_IF (match && strlen(match) && strcmp(match, config->composite_driver.driver.name));

        //printk(KERN_INFO"%s: MATCHED\n", __FUNCTION__); 

        /* decompose interface names to construct interface_list 
         */
        RETURN_UNLESS (config->interface_names && strlen(config->interface_names));

        /* count interface names and allocate _interface_names array
         */
        for (cp = sp = config->interface_names, interfaces = 0; cp && *cp; ) {
                for (; *cp && *cp == ':'; cp++);        // skip white space
                sp = cp;                                // save start of token
                for (; *cp && *cp != ':'; cp++);        // find end of token
                BREAK_IF (sp == cp);
                if (*cp) cp++;
                interfaces++;
        }

        THROW_UNLESS(interfaces, error);

        TRACE_MSG1(GENERIC, "interfaces: %d", interfaces);

        THROW_UNLESS((interface_list = (char **) CKMALLOC (sizeof (char *) * (interfaces + 1), GFP_KERNEL)), error);

        for (cp = sp = config->interface_names, interfaces = 0; cp && *cp; interfaces++) {
                for (; *cp && *cp == ':'; cp++);        // skip white space
                sp = cp;                                // save start of token
                for (; *cp && *cp != ':'; cp++);        // find end of token
                BREAK_IF (sp == cp);
                lp = cp;
                if (*cp) cp++;
                *lp = '\0';
                lp = CKMALLOC(strlen(sp), GFP_KERNEL);
                strcpy(lp, sp);
                interface_list[interfaces] = lp;

                TRACE_MSG3(GENERIC, "INTERFACE[%2d] %x \"%s\"", 
                                interfaces, interface_list[interfaces], interface_list[interfaces]);
        }

        config->composite_driver.device_description = &config->device_description;
        config->composite_driver.configuration_description = &config->configuration_description;
        config->composite_driver.driver.fops = &generic_function_ops;
        config->interface_list = interface_list;

        THROW_IF (usbd_register_composite_function (
                                &config->composite_driver,
                                config->composite_driver.driver.name,
                                config->class_name,
                                config->interface_list, NULL), error);

        config->registered++;

	TRACE_MSG0(GENERIC, "REGISTER FINISHED");

        CATCH(error) {
                otg_trace_invalidate_tag(GENERIC);
        }

}
Exemple #6
0
/*! blan_fd_device_request
 *  @brief process a received SETUP URB
 *
 * Processes a received setup packet and CONTROL WRITE data.
 * Results for a CONTROL READ are placed in urb->buffer.
 *
 * @param function_instance - pointer to this function instance
 * @param request - received request to interface or endpoint
 * @return non-zero for failure.
 */
int blan_fd_device_request (struct usbd_function_instance *function_instance, struct usbd_device_request *request)
{
        struct usb_network_private *npd = function_instance->privdata;
        struct usbd_urb *urb;
        int index;

        /* Verify that this is a USB Class request per CDC specification or a vendor request.
         */
        RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));

        /* Determine the request direction and process accordingly
         */
        switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {

        case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR:

                switch (request->bRequest) {
                case MCCI_ENABLE_CRC:
                        //if (make_crc_table())
                        //        return -EINVAL;
                        //npd->encapsulation = simple_crc;
                        return 0;

                case BELCARRA_PING:
                        TRACE_MSG1(NTT,"H2D VENDOR IP: %08x", npd->ip_addr);
                        if ((npd->network_type == network_blan))
                                net_os_send_notification_later(function_instance);
                        break;

                #if !defined(CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME) || !defined(CONFIG_OTG_NETWORK_SAFE_DO_NOT_SETTIME)
                case BELCARRA_SETTIME:
                        npd->rfc868time = ntohl( request->wValue << 16 | request->wIndex);
                        net_os_settime(function_instance, npd->rfc868time);
                        break;
                #endif
                case BELCARRA_SETIP:
                        #ifdef OTG_BIG_ENDIAN
                        npd->ip_addr = ntohl( request->wValue | request->wIndex<< 16 );
                        #else /* OTG_BIG_ENDIAN */
                        npd->ip_addr = ntohl( request->wValue << 16 | request->wIndex);
                        #endif /* OTG_BIG_ENDIAN */
                        TRACE_MSG1(NTT, "npd->ip_addr: %08x", npd->ip_addr);
                        // XXX need to get in correct order here
                        // XXX what about locally set mac addr
                        // XXX UNLESS(npd->local_dev_set) {
                        if (!(npd->override_MAC)) {
                                npd->local_dev_addr[0] = 0xfe | 0x02;           /* locally administered */
                                npd->local_dev_addr[1] = 0x00;
                                npd->local_dev_addr[2] = (npd->ip_addr >> 24) & 0xff;
                                npd->local_dev_addr[3] = (npd->ip_addr >> 16) & 0xff;
                                npd->local_dev_addr[4] = (npd->ip_addr >> 8) & 0xff;
                                npd->local_dev_addr[5] = (npd->ip_addr >> 0) & 0xff;
                         }
                        // XXX }
                        break;

                case BELCARRA_SETMSK:
                        #ifdef OTG_BIG_ENDIAN
                        npd->network_mask = ntohl( request->wValue | request->wIndex << 16);
                        #else /* OTG_BIG_ENDIAN */
                        npd->network_mask = ntohl( request->wValue << 16 | request->wIndex);
                        #endif /* OTG_BIG_ENDIAN */
                        TRACE_MSG1(NTT, "npd->network_mask: %08x", npd->network_mask);
                        break;

                case BELCARRA_SETROUTER:
                        #ifdef OTG_BIG_ENDIAN
                        npd->router_ip = ntohl( request->wValue | request->wIndex << 16);
                        #else /* OTG_BIG_ENDIAN */
                        npd->router_ip = ntohl( request->wValue << 16 | request->wIndex);
                        #endif /* OTG_BIG_ENDIAN */
                        TRACE_MSG1(NTT, "npd->router_ip: %08x", npd->router_ip);
                        break;

                case BELCARRA_SETDNS:
                        #ifdef OTG_BIG_ENDIAN
                        npd->dns_server_ip = ntohl( request->wValue | request->wIndex < 16);
                        #else /* OTG_BIG_ENDIAN */
                        npd->dns_server_ip = ntohl( request->wValue << 16 | request->wIndex);
                        #endif /* OTG_BIG_ENDIAN */
                        break;
                #ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT
                case BELCARRA_SETFERMAT:
                        npd->fermat = 1;
                        break;
                #endif
                #ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME
                case BELCARRA_HOSTNAME:
                        TRACE_MSG0(NTT,"HOSTNAME");
                        RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function_instance, le16_to_cpu(request->wLength),
                                                       blant_fd_urb_received_ep0) ));

                        RETURN_ZERO_IF(!usbd_start_out_urb(urb));                  // return if no error
                        usbd_free_urb(urb);                                  // de-alloc if error
                        return -EINVAL;
                #endif
                #ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK
                case BELCARRA_DATA_NOTIFY:
                        TRACE_MSG0(NTT,"DATA NOTIFY");
                        npd->data_notify = 1;
                        return -EINVAL;
                #endif
                }
                switch (request->bRequest) {
                case BELCARRA_SETIP:
                case BELCARRA_SETMSK:
                case BELCARRA_SETROUTER:
                        TRACE_MSG5(NTT, "npd->network_mask: %08x npd->ip_addr: %08x npd->router_ip: %08x flags: %08x %s",
                                        npd->network_mask, npd->ip_addr, npd->router_ip, npd->flags,
                                        (npd->flags & NETWORK_CONFIGURED) ? "CONFIGURED" : "");

                        BREAK_UNLESS (npd->network_mask && npd->ip_addr && npd->router_ip && (npd->flags & NETWORK_CONFIGURED));
                        // let the os layer know, if it's interested.
                        #ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG
                        net_os_config(function_instance);
                        net_os_hotplug(function_instance);
                        #endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */
                        break;
                }
                return 0;
        default:
                break;
        }
Exemple #7
0
/*!
 * otg_pci_probe() - otg pci probe function
 *
 * Get the standard PCI resources allocated.
 *
 */
int __devinit otg_pci_probe (struct pci_dev *pci_dev, const struct pci_device_id *id, struct otg_pci_driver *otg_pci_driver)
{

        struct otg_driver       *otg_driver;
        struct otg_dev          *otg_dev = NULL;
        int                     enabled = 0;
        int                     irq = 0;
        int                     region;

        u8                      latency, limit;


        /* allocate otg_dev structure and fill in standard fields */
        THROW_UNLESS((otg_dev = kmalloc(sizeof(struct otg_dev), SLAB_KERNEL)), error);

        memset(otg_dev, 0, sizeof(struct otg_dev));
        otg_dev->PCI = otg_trace_obtain_tag();


        //printk(KERN_INFO"%s: PCI %d\n", __FUNCTION__, otg_dev->PCI); 
        //TRACE_MSG0(otg_dev->PCI, "TEST");

        THROW_UNLESS((enabled = !pci_enable_device(pci_dev)), error);

        otg_dev->otg_pci_driver = otg_pci_driver;
        otg_dev->pci_regions = otg_pci_driver->pci_regions;
        pci_set_drvdata(pci_dev, otg_dev);
        printk(KERN_INFO"%s: pci_dev: %x otg_dev: %x drv_data: %x\n", __FUNCTION__, pci_dev, otg_dev, pci_get_drvdata(pci_dev)); 

        for (region = 0; region < DEVICE_COUNT_RESOURCE; region++) {
                unsigned long           resource_start;
                unsigned long           resource_len;
                TRACE_MSG5(otg_dev->PCI, "[%2d] flags: %08x start: %08x end: %08x len: %08x", region,
                                pci_resource_flags(pci_dev, region),
                                pci_resource_start(pci_dev, region),
                                pci_resource_end(pci_dev, region),
                                pci_resource_len(pci_dev, region)
                                ); 

                CONTINUE_UNLESS(otg_dev->pci_regions & (1 << region));

                resource_start = pci_resource_start(pci_dev, region);
                resource_len = pci_resource_len(pci_dev, region);

                TRACE_MSG5(otg_dev->PCI, "pci_dev: %x otg_dev: %x start: %lx len: %lx name: %s", 
                                pci_dev, otg_dev, resource_start, resource_len, otg_pci_driver->name); 

                THROW_UNLESS(request_mem_region(resource_start, resource_len, otg_pci_driver->name), error);
                THROW_UNLESS((otg_dev->regs[region] = ioremap_nocache(resource_start, resource_len)), error);
                TRACE_MSG2(otg_dev->PCI, "regs[%d] %x", region, otg_dev->regs[region]);
        }

        THROW_UNLESS((irq = !request_irq(pci_dev->irq, otg_pci_isr, SA_SHIRQ, otg_pci_driver->name, pci_dev)), error);
        TRACE_MSG1(otg_dev->PCI, "irq: %d", pci_dev->irq);

        /* bad pci latencies can contribute to overruns  - but where ?? */
        pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &latency);
        pci_read_config_byte (pci_dev, PCI_MAX_LAT, &limit);
        TRACE_MSG2(otg_dev->PCI, "latency: %02x limit: %02x", latency, limit);

        if (latency && /* limit &&*/ (limit < latency)) {

                pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, limit);
                pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &latency);
                TRACE_MSG2(otg_dev->PCI, "latency: %02x limit: %02x", latency, limit);
        }



	/* XXX lock? */
        otg_dev->id = otg_get_id(pci_dev);
        TRACE_MSG1(otg_dev->PCI, "id: %d", otg_dev->id); 
        if (otg_devs) {
                TRACE_MSG2(otg_dev->PCI, "otg_devs: %x new: %x", otg_devs, otg_dev); 
                otg_dev->next = otg_devs;
        }
        otg_devs = otg_dev;

        return 0;

        CATCH(error) {

                printk(KERN_INFO"%s: FAILED\n", __FUNCTION__); 
                pci_set_drvdata(pci_dev, NULL);

                if (irq) free_irq(pci_dev->irq, otg_dev);

                otg_pci_free_dev(pci_dev, otg_dev);

                if (otg_dev) kfree(otg_dev);

                if (enabled) pci_disable_device(pci_dev);
                
                return -EINVAL;
        }
}