+/* ul_usb1.c
+ * Linux CAN-bus device driver.
+ * Written by Jan Kriz email:johen@post.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3 17 Jun 2004
+ *
+ * Based on
+ * USB Skeleton driver - 2.2
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
+ * but has been rewritten to be easier to read and use.
+ *
+ */
+
+#include "./can/ul_usb1.h"
+#include "./can/can.h"
+#include "./can/can_sysdep.h"
+#include "./can/main.h"
+#include "./can/devcommon.h"
+#include "./can/setup.h"
+#include "./can/finish.h"
+#include "./can/i82527.h"
+//#include "../include/sja1000.h"
+#include "./can/sja1000p.h"
+
+#include "./can/errno.h"
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_SKEL_MINOR_BASE 192
+
+#define CAN_OP_MASK 0x80
+#define CAN_OP_READ 0x80
+#define CAN_OP_WRITE 0x00
+
+
+/* our private defines. if this grows any larger, use your own .h file */
+#define MAX_TRANSFER (PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+ allocations > PAGE_SIZE and the number of packets in a page
+ is an integer 512 is the largest possible packet on EHCI */
+#define WRITES_IN_FLIGHT 8
+/* arbitrarily chosen */
+
+/* Define these values to match your devices */
+#define USB_SKEL_VENDOR_ID 0xDEAD
+#define USB_SKEL_PRODUCT_ID 0x1001
+
+/* table of devices that work with this driver */
+// static struct usb_device_id ul_usb1_table [] = {
+// { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
+// { } /* Terminating entry */
+// };
+// MODULE_DEVICE_TABLE(usb, ul_usb1_table);
+
+extern struct file_operations can_fops;
+
+struct ul_usb1_combo {
+ struct usb_ul_usb1 * dev;
+ struct urb * urb;
+};
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x100
+
+/* Structure to hold all of our device specific stuff */
+struct usb_ul_usb1 {
+ struct usb_device *udev; /* the usb device for this device */
+ struct usb_interface *interface; /* the interface for this device */
+ struct semaphore limit_sem; /* limiting the number of writes in progress */
+ struct usb_anchor submitted; /* in case we need to retract our submissions */
+ unsigned char *bulk_in_buffer; /* the buffer to receive data */
+ size_t bulk_in_size; /* the size of the receive buffer */
+ unsigned char *int_in_buffer; /* the buffer to receive data */
+ size_t int_in_size; /* the size of the receive buffer */
+ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
+ __u8 int_in_endpointAddr; /* the address of the interrupt in endpoint */
+ int int_in_interval;
+ int errors; /* the last request tanked */
+ int open_count; /* count the number of openers */
+ spinlock_t err_lock; /* lock for errors */
+ struct mutex io_mutex; /* synchronize I/O with disconnect */
+ struct urb *irq;
+ struct candevice_t *candev;
+};
+
+static struct usb_driver ul_usb1_driver;
+
+/** ul_usb1_request_io
+ * ul_usb1_request_io: - reserve io or memory range for can board
+ * @candev: pointer to candevice/board which asks for io. Field @io_addr
+ * of @candev is used in most cases to define start of the range
+ *
+ * The function ul_usb1_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_request_io(struct candevice_t *candev)
+{
+ ((struct usb_ul_usb1*)candev->sysdevptr.anydev)->candev=candev;
+ return 0;
+}
+
+/** ul_usb1_release_io
+ * ul_usb1_release_io - free reserved io memory range
+ * @candev: pointer to candevice/board which releases io
+ *
+ * The function ul_usb1_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_release_io(struct candevice_t *candev)
+{
+ struct usb_ul_usb1 *dev;
+ if (candev->sysdevptr.anydev){
+ dev=(struct usb_ul_usb1*) candev->sysdevptr.anydev;
+ usb_put_dev(dev->udev);
+ usb_kill_urb(dev->irq);
+ usb_free_urb(dev->irq);
+ kfree(dev->bulk_in_buffer);
+ kfree(dev->int_in_buffer);
+ if (dev->candev){
+ dev->candev->sysdevptr.anydev=NULL;
+ //cleanup_usbdev(dev->candev);
+ }
+ kfree(dev);
+ }
+ return 0;
+}
+
+/** ul_usb1_reset
+ * ul_usb1_reset - hardware reset routine
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function ul_usb1_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_reset(struct candevice_t *candev)
+{
+ return 0;
+}
+
+#define RESET_ADDR 0x0
+#define NR_82527 0
+#define NR_SJA1000 1
+
+/** ul_usb1_init_hw_data
+ * ul_usb1_init_hw_data - Initialize hardware cards
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function ul_usb1_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of Intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of Philips sja1000 chips on the board.
+ * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_init_hw_data(struct candevice_t *candev)
+{
+ candev->res_addr=RESET_ADDR;
+ candev->nr_82527_chips=NR_82527;
+ candev->nr_sja1000_chips=NR_SJA1000;
+ candev->nr_all_chips=NR_82527+NR_SJA1000;
+ //candev->flags |= CANDEV_PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+/** ul_usb1_init_chip_data
+ * ul_usb1_init_chip_data - Initialize chips
+ * @candev: Pointer to candevice/board structure
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function ul_usb1_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK,
+ * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * The entry @int_cpu_reg holds hardware specific options for the cpu interface
+ * register. Options defined in the %i82527.h file:
+ * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST.
+ * Return Value: The function always returns zero
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+ /*i82527_fill_chipspecops(candev->chip[chipnr]);*/
+ /*sja1000_fill_chipspecops(candev->chip[chipnr]);*/
+ sja1000p_fill_chipspecops(candev->chip[chipnr]);
+
+ candev->chip[chipnr]->flags|= CHIP_IRQ_CUSTOM;
+
+ candev->chip[chipnr]->chip_base_addr=0;
+ candev->chip[chipnr]->clock = 24000000;
+ candev->chip[chipnr]->int_cpu_reg = iCPU_DSC;
+ candev->chip[chipnr]->int_clk_reg = iCLK_SL1;
+ candev->chip[chipnr]->int_bus_reg = iBUS_CBY;
+ candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF;
+ candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL |
+ sjaOCR_TX0_LH;
+
+ return 0;
+}
+
+/** ul_usb1_init_obj_data
+ * ul_usb1_init_obj_data - Initialize message buffers
+ * @chip: Pointer to chip specific structure
+ * @objnr: Number of the message buffer
+ *
+ * The function ul_usb1_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_init_obj_data(struct canchip_t *chip, int objnr)
+{
+ chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr+(objnr+1)*0x10;
+
+ return 0;
+}
+
+/** ul_usb1_program_irq
+ * ul_usb1_program_irq - program interrupts
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function ul_usb1_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/ul_usb1.c
+ */
+int ul_usb1_program_irq(struct candevice_t *candev)
+{
+ return 0;
+}
+
+/** ul_usb1_write_register
+ * ul_usb1_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function ul_usb1_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/ul_usb1.c
+ */
+void ul_usb1_write_register(struct candevice_t *candev,unsigned data, unsigned long address)
+{
+ struct usb_ul_usb1 *dev;
+ int retval;
+ int bytes_transferred;
+ unsigned char buffer[2];
+ buffer[0]=((unsigned char)address & ~CAN_OP_MASK)+CAN_OP_WRITE;
+ buffer[1]=(unsigned char)data;
+
+ dev = (struct usb_ul_usb1 *)candev->sysdevptr.anydev;
+
+ mutex_lock(&dev->io_mutex);
+ if (!dev) { /* disconnect() was called */
+ CANMSG("Sending %lu:%X : ERR No device\n",address,(uint8_t)data);
+ retval = -ENODEV;
+ goto exit;
+ }
+ if (!dev->interface) { /* disconnect() was called */
+ CANMSG("Sending %lu:%X : ERR No interface\n",address,(uint8_t)data);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* do a blocking bulk write to send data to the device */
+ retval = usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+ buffer,
+ 2,
+ &bytes_transferred, 10000);
+ CANMSG("Sending %lu:%X : retval %d, transferred %d bytes\n",address,(uint8_t)data,retval,bytes_transferred);
+
+exit:
+ mutex_unlock(&dev->io_mutex);
+}
+
+/** ul_usb1_read_register
+ * ul_usb1_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function ul_usb1_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/ul_usb1.c
+ */
+unsigned ul_usb1_read_register(struct candevice_t *candev,unsigned long address)
+{
+ struct usb_ul_usb1 *dev;
+ int retval;
+ int bytes_transferred;
+ unsigned char buffer[2];
+ buffer[0]=((unsigned char)address & ~CAN_OP_MASK)+CAN_OP_READ;
+ buffer[1]=0x00;
+
+ dev = (struct usb_ul_usb1 *)candev->sysdevptr.anydev;
+
+ mutex_lock(&dev->io_mutex);
+ if (!dev) { /* disconnect() was called */
+ retval = -ENODEV;
+ goto exit;
+ }
+ if (!dev->interface) { /* disconnect() was called */
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* do a blocking bulk write to send data to the device */
+ retval = usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+ buffer,
+ 2,
+ &bytes_transferred, 10000);
+
+ CANMSG("Requested: %ld : retval %d, transferred %d bytes\n",address,retval,bytes_transferred);
+ if ((retval)||(bytes_transferred!=2)){
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ /* do a blocking bulk read to get data from the device */
+ retval = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
+ dev->bulk_in_buffer,
+ dev->bulk_in_size,
+ &bytes_transferred, 10000);
+
+ /* if the read was successful, copy the data to userspace */
+ CANMSG("Received %d bytes : %u:%X\n",bytes_transferred,(dev->bulk_in_buffer[0] & 0x7F),dev->bulk_in_buffer[1]);
+ if (!retval) {
+ if (bytes_transferred!=2)
+ retval = -EFAULT;
+ else
+ retval = dev->bulk_in_buffer[1];
+ }
+
+exit:
+ mutex_unlock(&dev->io_mutex);
+ return retval;
+}
+
+/* !!! Don't change this function !!! */
+int ul_usb1_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = ul_usb1_request_io;
+ hwspecops->release_io = ul_usb1_release_io;
+ hwspecops->reset = ul_usb1_reset;
+ hwspecops->init_hw_data = ul_usb1_init_hw_data;
+ hwspecops->init_chip_data = ul_usb1_init_chip_data;
+ hwspecops->init_obj_data = ul_usb1_init_obj_data;
+ hwspecops->write_register = ul_usb1_write_register;
+ hwspecops->read_register = ul_usb1_read_register;
+ hwspecops->program_irq = ul_usb1_program_irq;
+ return 0;
+}
+
+
+
+
+/* --------------------------------------------------------------------------------------------------- */
+
+
+static void ul_usb1_irq(struct urb *urb)
+{
+ struct usb_ul_usb1 *dev = urb->context;
+ struct ul_usb1_combo devc;
+ int retval;
+
+ CANMSG("Interrupt poll\n");
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ CANMSG("%s - urb shutting down with status: %d\n", __FUNCTION__, urb->status);
+ return;
+ default:
+ CANMSG("%s - nonzero urb status received: %d\n", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ devc.dev = dev;
+ devc.urb = urb;
+
+ dev->candev->chip[0]->chipspecops->irq_handler(0,dev->candev->chip[0]);
+ CANMSG("Interrupt caught\n");
+
+ exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ CANMSG("%s - usb_submit_urb failed with result %d\n",
+ __FUNCTION__, retval);
+}
+
+static void ul_usb1_delete(struct usb_ul_usb1 *dev)
+{
+ usb_put_dev(dev->udev);
+ usb_kill_urb(dev->irq);
+ usb_free_urb(dev->irq);
+ kfree(dev->bulk_in_buffer);
+ kfree(dev->int_in_buffer);
+ if (dev->candev){
+ dev->candev->sysdevptr.anydev=NULL;
+ cleanup_usbdev(dev->candev);
+ }
+ kfree(dev);
+}
+
+static int ul_usb1_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_ul_usb1 *dev;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct candevice_t *candev;
+ size_t buffer_size;
+ int i;
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err("Out of memory");
+ goto error;
+ }
+ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+ mutex_init(&dev->io_mutex);
+ spin_lock_init(&dev->err_lock);
+ init_usb_anchor(&dev->submitted);
+
+// dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->udev = interface_to_usbdev(interface);
+ dev->interface = interface;
+
+ /* set up the endpoint information */
+ /* use only the first bulk-in and bulk-out endpoints */
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (!dev->bulk_in_endpointAddr &&
+ usb_endpoint_is_bulk_in(endpoint)) {
+ /* we found a bulk in endpoint */
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->bulk_in_size = buffer_size;
+ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+ dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!dev->bulk_in_buffer) {
+ err("Could not allocate bulk_in_buffer");
+ goto error;
+ }
+ }
+
+ if (!dev->bulk_out_endpointAddr &&
+ usb_endpoint_is_bulk_out(endpoint)) {
+ /* we found a bulk out endpoint */
+ dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+ }
+
+ if (!dev->int_in_endpointAddr &&
+ usb_endpoint_is_int_in(endpoint)) {
+ /* we found an interrupt in endpoint */
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->int_in_size = buffer_size;
+ dev->int_in_endpointAddr = endpoint->bEndpointAddress;
+ dev->int_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ dev->int_in_interval = endpoint->bInterval;
+ if (!dev->int_in_buffer) {
+ err("Could not allocate int_in_buffer");
+ goto error;
+ }
+ }
+ }
+ if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr && dev->int_in_endpointAddr)) {
+ err("Could not find all bulk-in, bulk-out and interrupt endpoints");
+ goto error;
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ if (main_init_done==1)
+ register_usbdev("ul_usb1",(void *) dev);
+ else {
+ mutex_lock(&usbdev_reg_mutex);
+ if (main_init_done==1)
+ register_usbdev("ul_usb1",(void *) dev);
+ else {
+ for (i=0;i<MAX_HW_CARDS;i++){
+ if (usbregq[i]==NULL){
+ usbregq[i]=(struct usbdev_reg_query *)can_checked_malloc(sizeof(struct usbdev_reg_query));
+ if (!usbregq[i]){
+ CANMSG("Error allocating usbdev_reg_query");
+ mutex_unlock(&usbdev_reg_mutex);
+ goto error;
+ }
+ sprintf (usbregq[i]->hwname,"ul_usb1");
+ usbregq[i]->anydev=(void *) dev;
+ break;
+ }
+ }
+ if (i==MAX_HW_CARDS){
+ CANMSG("No free space to register new card");
+ mutex_unlock(&usbdev_reg_mutex);
+ goto error;
+ }
+ }
+ mutex_unlock(&usbdev_reg_mutex);
+ }
+
+ dev->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->irq){
+ CANMSG("Error allocating usb urb\n");
+ goto error;
+ }
+ dev->irq->dev = dev->udev;
+ usb_fill_int_urb(dev->irq, dev->udev,
+ usb_rcvintpipe(dev->udev, dev->int_in_endpointAddr),
+ dev->int_in_buffer, dev->int_in_size,
+ ul_usb1_irq, dev, dev->int_in_interval);
+/* usb_fill_bulk_urb(dev->irq, dev->udev,
+ usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
+ dev->int_in_buffer, dev->int_in_size,
+ ul_usb1_irq, dev);*/
+
+/* dev->irq->transfer_dma = wacom->data_dma;
+ dev->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;*/
+ retval=usb_submit_urb(dev->irq, GFP_KERNEL);
+ if (retval){
+ CANMSG("INT URB %d\n",retval);
+ return -EIO;
+ }else
+ CANMSG("INT URB SUCCCESS\n");
+
+ /* let the user know what node this device is now attached to */
+ info("USB Skeleton device now attached");
+ return 0;
+
+error:
+ ul_usb1_delete(dev);
+ return retval;
+}
+
+static void ul_usb1_disconnect(struct usb_interface *interface)
+{
+ struct usb_ul_usb1 *dev;
+ int minor = interface->minor;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ /* prevent more I/O from starting */
+ mutex_lock(&dev->io_mutex);
+ dev->interface = NULL;
+ mutex_unlock(&dev->io_mutex);
+
+ //usb_kill_anchored_urbs(&dev->submitted);
+
+ ul_usb1_delete(dev);
+
+ info("USB Skeleton now disconnected");
+}
+
+// static struct usb_driver ul_usb1_driver = {
+// .name = "ul_usb1-can",
+// .id_table = ul_usb1_table,
+// .probe = ul_usb1_probe,
+// .disconnect = ul_usb1_disconnect,
+// .id_table = ul_usb1_table,
+// };
+
+// int ul_usb1_init(void){
+// return usb_register(&ul_usb1_driver);
+// }
+//
+// void ul_usb1_exit(void){
+// usb_deregister(&ul_usb1_driver);
+// }