#include "./constants.h"
int can_init_procdir(void);
+int can_init_procentry(int board);
int can_delete_procdir(void);
+int can_delete_procentry(struct candevice_t *candev);
struct canproc_t {
struct proc_dir_entry *can_proc_entry;
*/
int init_hw_struct(void);
+int init_new_hw_struct(int devnr);
int list_hw(void);
void *can_checked_malloc(size_t size);
int can_checked_free(void *address_p);
* Version lincan-0.3 17 Jul 2008
*/
+#ifndef USBCAN_H
+#define USBCAN_H
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
int usbcan_request_io(struct candevice_t *candev);
int usbcan_release_io(struct candevice_t *candev);
int usbcan_reset(struct candevice_t *candev);
int usbcan_fill_chipspecops(struct canchip_t *chip);
int usbcan_irq_handler(int irq, struct canchip_t *chip);
+int usbcan_init(void);
+void usbcan_exit(void);
+#endif /*USBCAN_H*/
\ No newline at end of file
#include "../include/can_iortl.h"
#endif /*CAN_WITH_RTL*/
+#if defined(CONFIG_OC_LINCAN_CARD_usbcan)
+ #include "../include/usbcan.h"
+#endif
+
can_spinlock_t canuser_manipulation_lock;
int major=CAN_MAJOR;
}
}
#endif
+
+#if defined(CONFIG_OC_LINCAN_CARD_usbcan)
+ res = usbcan_init();
+ if (res){
+ CANMSG("usb_register for usbcan failed. Error number %d.\n", res);
+ return -ENODEV;
+ }
+#endif
+
return 0;
#ifdef CONFIG_PROC_FS
return -ENODEV;
}
+
+
+
+
+struct candevice_t* register_usbdev(const char *hwname,void *anydev){
+ int i=0, j, board;
+ struct candevice_t *candev;
+ struct canchip_t *chip;
+ struct boardtype_t *brp;
+
+ while ( (hw[board] != NULL) && (board < MAX_HW_CARDS) )
+ board++;
+ brp = boardtype_find(hwname);
+ if(!brp) {
+ CANMSG("Sorry, hardware \"%s\" is currently not supported.\n",hw[board]);
+ return NULL;
+ }
+ if (board==MAX_HW_CARDS){
+ CANMSG("Device \"%s\" could not be registered due to internal limits.\n",hw[board]);
+ return NULL;
+ }
+ hw[board]=brp->boardtype;
+
+ if (init_new_hw_struct(board))
+ return NULL;
+
+ #ifdef CAN_DEBUG
+ list_hw();
+ #endif
+
+ candev=hardware_p->candevice[board];
+
+ /* Adding link to usb device structure into can device */
+ candev->sysdevptr.anydev=anydev;
+
+ if (candev->hwspecops->request_io(candev))
+ goto request_io_error;
+ candev->flags|=CANDEV_IO_RESERVED;
+
+ if (candev->hwspecops->reset(candev))
+ goto reset_error;
+
+ for(j=0; j<candev->nr_all_chips; j++) {
+ if((chip=candev->chip[j])==NULL)
+ continue;
+
+ if(chip->chipspecops->attach_to_chip(chip)<0) {
+ CANMSG("Initial attach to the chip HW failed\n");
+ goto interrupt_error;
+ }
+
+ chip->flags |= CHIP_ATTACHED;
+
+ if(can_chip_setup_irq(chip)<0) {
+ CANMSG("Error to setup chip IRQ\n");
+ goto interrupt_error;
+ }
+ }
+
+ if (candev->flags & CANDEV_PROGRAMMABLE_IRQ)
+ if (candev->hwspecops->program_irq(candev)){
+ CANMSG("Error to program board interrupt\n");
+ goto interrupt_error;
+ }
+
+#ifdef CONFIG_PROC_FS
+ if (can_init_procentry(board))
+ goto proc_error;
+#endif
+
+#if defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ {
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0))
+ char dev_name[32];
+ #else
+ struct class_device *this_dev;
+ #endif
+ int dev_minor;
+ for(i=0;i<MAX_TOT_MSGOBJS;i++) {
+ if(!objects_p[i]) continue;
+ if(objects_p[i]->hostchip->hostdevice != candev) continue;
+
+ dev_minor=objects_p[i]->minor;
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0))
+ sprintf (dev_name, "can%d", dev_minor);
+ devfs_handles[i]=devfs_register(NULL, dev_name,
+ DEVFS_FL_DEFAULT, major, dev_minor,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+ &can_fops, (void*)objects_p[i]);
+ #else
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,14))
+ this_dev=class_device_create(can_class, MKDEV(major, dev_minor), NULL, "can%d", dev_minor);
+ #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,25) /* >= 2.6.15 */
+ this_dev=class_device_create(can_class, NULL, MKDEV(major, dev_minor), NULL, "can%d", dev_minor);
+ #else /* >= 2.6.26 */
+ this_dev=device_create_drvdata(can_class, NULL, MKDEV(major, dev_minor), objects_p[i], "can%d", dev_minor);
+ #endif /* >= 2.6.26 */
+ if(IS_ERR(this_dev)){
+ CANMSG("problem to create device \"can%d\" in the class \"can\"\n", dev_minor);
+ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,25)
+ }else{
+ /*this_dev->class_data=objects_p[i];*/
+ class_set_devdata(this_dev,objects_p[i]);
+ #endif /* <= 2.6.25 */
+ }
+ #ifdef CONFIG_DEVFS_FS
+ devfs_mk_cdev(MKDEV(major, dev_minor), S_IFCHR | S_IRUGO | S_IWUGO, "can%d", dev_minor);
+ #endif
+ #endif
+ }
+ }
+#endif
+ return candev;
+
+#ifdef CONFIG_PROC_FS
+ proc_error: ;
+ CANMSG("Error registering /proc entry.\n");
+ goto memory_error;
+#endif
+
+ interrupt_error: ;
+ goto memory_error;
+
+ reset_error: ;
+ CANMSG("Error resetting device.\n");
+ goto memory_error;
+
+ request_io_error: ;
+ CANMSG("Error to request IO resources for device.\n");
+ goto memory_error;
+
+ memory_error: ;
+
+ #ifdef CAN_WITH_RTL
+ rtldev_error:
+ #endif /*CAN_WITH_RTL*/
+
+// register_error:
+ if ( can_del_mem_list() )
+ CANMSG("Error deallocating memory\n");
+
+ return NULL;
+}
+
+
+
+
+
+
+
+void cleanup_usbdev(struct candevice_t *dev)
+{
+ int i=0;
+ int dev_minor;
+
+ if (!dev)
+ return;
+
+#ifdef CONFIG_PROC_FS
+ if (can_delete_procentry(dev))
+ CANMSG("Error unregistering /proc/can entry.\n");
+#endif
+
+#if defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ for(i=0;i<MAX_TOT_MSGOBJS;i++) {
+ if(!objects_p[i]) continue;
+ if(objects_p[i]->hostchip->hostdevice != dev) continue;
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0))
+ if(devfs_handles[i])
+ devfs_unregister(devfs_handles[i]);
+ #else
+ dev_minor=objects_p[i]->minor;
+ if(dev_minor>=0){
+ #ifdef CONFIG_DEVFS_FS
+ devfs_remove("can%d", dev_minor);
+ #endif
+ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,25)
+ class_device_destroy(can_class, MKDEV(major, dev_minor));
+ #else /* >= 2.6.26 */
+ device_destroy(can_class, MKDEV(major, dev_minor));
+ #endif /* >= 2.6.26 */
+ }
+ #endif
+ }
+#endif
+
+ for(i=0;i<MAX_TOT_MSGOBJS;i++) {
+ if(!objects_p[i]) continue;
+ if(objects_p[i]->hostchip->hostdevice != dev) continue;
+ //canqueue_ends_done_chip(objects_p[i]->qends);
+ //can_checked_free(objects_p[i]->qends);
+ //can_checked_free(objects_p[i]);
+ objects_p[i]=NULL;
+ }
+
+ for(i=0;i<MAX_TOT_CHIPS;i++){
+ if(!chips_p[i]) continue;
+ if(chips_p[i]->hostdevice != dev) continue;
+ //can_checked_free(chips_p[i]->chipspecops);
+ //can_checked_free(chips_p[i]);
+ chips_p[i]=NULL;
+ }
+
+ hardware_p->candevice[dev->candev_idx]=NULL;
+ hardware_p->nr_boards--;
+ //kfree(hw[dev->candev_idx]);
+ hw[dev->candev_idx]=NULL;
+ //can_checked_free(dev->hwspecops);
+ //can_checked_free(dev);
+
+ candevice_done(dev);
+ can_checked_free(dev);
+}
+
+
+
+
void cleanup_module(void)
{
#if defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
int i=0;
#endif
+#if defined(CONFIG_OC_LINCAN_CARD_usbcan)
+ usbcan_exit();
+#endif
+
#ifdef CONFIG_PROC_FS
if (can_delete_procdir())
CANMSG("Error unregistering /proc/can entry.\n");
#define __NO_VERSION__
#include <linux/module.h>
+#include <linux/mutex.h>
int add_channel_to_procdir(struct candevice_t *candev);
-int remove_channel_from_procdir(void);
+int remove_channels_from_procdir(void);
+int remove_channel_from_procdir(struct candevice_t *candev);
int add_object_to_procdir(int chip_nr);
int remove_object_from_procdir(int chip_nr);
static struct canproc_t can_proc_base;
static struct canproc_t *base=&can_proc_base;
+DEFINE_MUTEX(proc_mutex); /* synchronize access to canproc_t array */
/* The following functions are needed only for kernel version 2.2. Kernel
* version 2.4 already defines them for us.
{
int board;
struct candevice_t *candev;
+
+ mutex_init(&proc_mutex);
+
base->can_proc_entry = can_create_proc_entry("can", S_IFDIR | S_IRUGO |
S_IXUGO, CAN_PROC_ROOT);
if (base->can_proc_entry == NULL)
return 0;
}
+/* can_init_procentry registers entry of a new board in CAN directory tree at
+ * the proc system.
+ */
+int can_init_procentry(int board)
+{
+ struct candevice_t *candev;
+ candev=hardware_p->candevice[board];
+ if(candev)
+ return add_channel_to_procdir(candev);
+ return -ENODEV;
+}
+
/* can_delete_procdir removes the entire CAN tree from the proc system */
int can_delete_procdir(void)
{
- if (remove_channel_from_procdir())
+ if (remove_channels_from_procdir())
return -ENODEV;
/* name: "can" */
- if (can_remove_proc_entry(base->can_proc_entry, CAN_PROC_ROOT))
+ if (can_remove_proc_entry(base->can_proc_entry, CAN_PROC_ROOT))
+ return -ENODEV;
+
+ return 0;
+}
+
+/* can_delete_procentry removes device entries from CAN tree in the proc system */
+int can_delete_procentry(struct candevice_t *candev)
+{
+ if (remove_channel_from_procdir(candev))
return -ENODEV;
return 0;
{
int i=0;
- for (i=0; i < candev->nr_all_chips; i++) {
+ mutex_lock(&proc_mutex);
+ for (i=0; i < MAX_TOT_CHIPS; i++){
+ if (!chips_p[i]) continue;
+ if (chips_p[i]->hostdevice != candev) continue;
- base->channel[cc] = (struct channelproc_t *)
+ base->channel[i] = (struct channelproc_t *)
can_checked_malloc(sizeof(struct channelproc_t));
- if (base->channel[cc] == NULL)
+ if (base->channel[i] == NULL){
+ mutex_unlock(&proc_mutex);
return -ENOMEM;
+ }
- sprintf(base->channel[cc]->ch_name, "channel%d",cc);
+ sprintf(base->channel[i]->ch_name, "channel%d",i);
- base->channel[cc]->ch_entry = can_create_proc_entry(
- base->channel[cc]->ch_name,
+ base->channel[i]->ch_entry = can_create_proc_entry(
+ base->channel[i]->ch_name,
S_IFDIR | S_IRUGO |S_IXUGO,
base->can_proc_entry);
- if (base->channel[cc]->ch_entry == NULL)
+ if (base->channel[i]->ch_entry == NULL){
+ mutex_unlock(&proc_mutex);
return -ENODEV;
+ }
- add_object_to_procdir(cc);
+ add_object_to_procdir(i);
create_proc_read_entry("chip_info", /* proc entry name */
0, /* protection mask, 0->default */
- base->channel[cc]->ch_entry, /* parent dir, NULL->/proc */
+ base->channel[i]->ch_entry, /* parent dir, NULL->/proc */
can_chip_procinfo,
- candev->chip[i]);
-
+ chips_p[i]);
cc++;
}
+ mutex_unlock(&proc_mutex);
return 0;
}
-int remove_channel_from_procdir(void)
+int remove_channels_from_procdir(void)
{
+ int i=0;
+
+ mutex_lock(&proc_mutex);
+ for (i=0; i < MAX_TOT_CHIPS; i++){
+ if (!chips_p[i]) continue;
- while (cc != 0) {
cc--;
- if(!base->channel[cc]) continue;
+ if(!base->channel[i]) continue;
- remove_proc_entry("chip_info", base->channel[cc]->ch_entry);
+ remove_proc_entry("chip_info", base->channel[i]->ch_entry);
- if (remove_object_from_procdir(cc))
+ if (remove_object_from_procdir(i)){
+ mutex_unlock(&proc_mutex);
return -ENODEV;
+ }
/* name: base->channel[cc]->ch_name */
- if (can_remove_proc_entry(base->channel[cc]->ch_entry,
- base->can_proc_entry))
+ if (can_remove_proc_entry(base->channel[i]->ch_entry,
+ base->can_proc_entry)){
+ mutex_unlock(&proc_mutex);
return -ENODEV;
+ }
- can_checked_free(base->channel[cc]);
- base->channel[cc] = NULL;
+ can_checked_free(base->channel[i]);
+ base->channel[i] = NULL;
+ }
+ mutex_unlock(&proc_mutex);
+
+ return 0;
+}
+
+int remove_channel_from_procdir(struct candevice_t *candev)
+{
+ int i=0,j=0;
+
+ mutex_lock(&proc_mutex);
+ for (i=0; i < MAX_TOT_CHIPS; i++){
+ if (!chips_p[i]) continue;
+ if (chips_p[i]->hostdevice != candev) continue;
+ if (!base->channel[i]) continue;
+
+ remove_proc_entry("chip_info", base->channel[i]->ch_entry);
+
+ if (remove_object_from_procdir(i)){
+ mutex_unlock(&proc_mutex);
+ return -ENODEV;
+ }
+
+ /* name: base->channel[cc]->ch_name */
+ if (can_remove_proc_entry(base->channel[i]->ch_entry,
+ base->can_proc_entry)){
+ mutex_unlock(&proc_mutex);
+ return -ENODEV;
+ }
+
+ can_checked_free(base->channel[i]);
+ base->channel[i] = NULL;
+
+ cc--;
}
+ mutex_unlock(&proc_mutex);
return 0;
}
return 0;
}
+/**
+ * init_new_hw_struct - initializes driver description structures for new hardware
+ *
+ * The function init_new_hw_struct() is used to initialize the hardware structure.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int init_new_hw_struct(int devnr)
+{
+ int irq_param_idx=0;
+ int chan_param_idx=0;
+
+ if ( (hw[devnr] != NULL) & (devnr < MAX_HW_CARDS) ) {
+ hardware_p->nr_boards++;
+
+ if (init_device_struct(devnr, &chan_param_idx, &irq_param_idx)) {
+ CANMSG("Error initializing candevice_t structures.\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
/**
* init_device_struct - initializes single CAN device/board
* @card: index into @hardware_p HW description
{
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 usbcan_init(void){
+ return usb_register(&ul_usb1_driver);
+}
+
+void usbcan_exit(void){
+ usb_deregister(&ul_usb1_driver);
+}