]> rtime.felk.cvut.cz Git - lincan.git/commitdiff
Adding device side of can queue
authorJan Kriz <krizj1@fel.cvut.cz>
Mon, 7 Jul 2008 21:11:32 +0000 (23:11 +0200)
committerppisa <pisa@cmp.felk.cvut.cz>
Sun, 23 Nov 2008 23:12:54 +0000 (00:12 +0100)
embedded/app/usbcan/can/i82527.h [new file with mode: 0644]
embedded/app/usbcan/can/modparms.h [new file with mode: 0644]
embedded/app/usbcan/can/setup.h [new file with mode: 0644]
embedded/app/usbcan/can/ul_usb1.h [new file with mode: 0644]
embedded/app/usbcan/setup.c [new file with mode: 0644]
embedded/app/usbcan/ul_usb1.c [new file with mode: 0644]

diff --git a/embedded/app/usbcan/can/i82527.h b/embedded/app/usbcan/can/i82527.h
new file mode 100644 (file)
index 0000000..1747396
--- /dev/null
@@ -0,0 +1,177 @@
+/* i82527.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3  17 Jun 2004
+ */
+
+int i82527_enable_configuration(struct canchip_t *chip);
+int i82527_disable_configuration(struct canchip_t *chip);
+int i82527_chip_config(struct canchip_t *chip);
+int i82527_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw, 
+                                               int sampl_pt, int flags);
+int i82527_standard_mask(struct canchip_t *chip, unsigned short code, 
+                                                       unsigned short mask);
+int i82527_extended_mask(struct canchip_t *chip, unsigned long code, 
+                                                       unsigned long mask);
+int i82527_message15_mask(struct canchip_t *chip, unsigned long code, 
+                                                       unsigned long mask);
+int i82527_clear_objects(struct canchip_t *chip);
+int i82527_config_irqs(struct canchip_t *chip, short irqs);
+int i82527_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj);
+int i82527_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
+                                                       struct canmsg_t *msg);
+int i82527_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
+                                                       struct canmsg_t *msg);
+int i82527_remote_request(struct canchip_t *chip, struct msgobj_t *obj);
+int i82527_set_btregs(struct canchip_t *chip, unsigned short btr0, 
+                                                       unsigned short btr1);
+int i82527_start_chip(struct canchip_t *chip);
+int i82527_stop_chip(struct canchip_t *chip);
+int i82527_check_tx_stat(struct canchip_t *chip);
+int i82527_irq_handler(int irq, struct canchip_t *chip);
+int i82527_fill_chipspecops(struct canchip_t *chip);
+
+
+#define MSG_OFFSET(object) ((object)*0x10)
+
+#define iCTL 0x00              // Control Register
+#define iSTAT 0x01             // Status Register
+#define iCPU 0x02              // CPU Interface Register
+#define iHSR 0x04              // High Speed Read
+#define iSGM0 0x06             // Standard Global Mask byte 0
+#define iSGM1 0x07
+#define iEGM0 0x08             // Extended Global Mask byte 0
+#define iEGM1 0x09
+#define iEGM2 0x0a
+#define iEGM3 0x0b
+#define i15M0 0x0c             // Message 15 Mask byte 0
+#define i15M1 0x0d
+#define i15M2 0x0e
+#define i15M3 0x0f
+#define iCLK 0x1f              // Clock Out Register
+#define iBUS 0x2f              // Bus Configuration Register
+#define iBT0 0x3f              // Bit Timing Register byte 0
+#define iBT1 0x4f
+#define iIRQ 0x5f              // Interrupt Register
+#define iP1C 0x9f              // Port 1 Register
+#define iP2C 0xaf              // Port 2 Register
+#define iP1I 0xbf              // Port 1 Data In Register
+#define iP2I 0xcf              // Port 2 Data In Register
+#define iP1O 0xdf              // Port 1 Data Out Register
+#define iP2O 0xef              // Port 2 Data Out Register
+#define iSRA 0xff              // Serial Reset Address
+
+#define iMSGCTL0       0x00    /* First Control register */
+#define iMSGCTL1       0x01    /* Second Control register */
+#define iMSGID0                0x02    /* First Byte of Message ID */
+#define iMSGID1                0x03
+#define iMSGID2                0x04
+#define iMSGID3                0x05
+#define iMSGCFG                0x06    /* Message Configuration */
+#define iMSGDAT0       0x07    /* First Data Byte */
+#define iMSGDAT1       0x08
+#define iMSGDAT2       0x09
+#define iMSGDAT3       0x0a
+#define iMSGDAT4       0x0b
+#define iMSGDAT5       0x0c
+#define iMSGDAT6       0x0d
+#define iMSGDAT7       0x0e
+
+/* Control Register (0x00) */
+enum i82527_iCTL {
+       iCTL_INI = 1,           // Initialization
+       iCTL_IE  = 1<<1,        // Interrupt Enable
+       iCTL_SIE = 1<<2,        // Status Interrupt Enable
+       iCTL_EIE = 1<<3,        // Error Interrupt Enable
+       iCTL_CCE = 1<<6         // Change Configuration Enable
+};
+
+/* Status Register (0x01) */
+enum i82527_iSTAT {
+       iSTAT_TXOK = 1<<3,      // Transmit Message Successfully
+       iSTAT_RXOK = 1<<4,      // Receive Message Successfully
+       iSTAT_WAKE = 1<<5,      // Wake Up Status
+       iSTAT_WARN = 1<<6,      // Warning Status
+       iSTAT_BOFF = 1<<7       // Bus Off Status
+};
+
+/* CPU Interface Register (0x02) */
+enum i82527_iCPU {
+       iCPU_CEN = 1,           // Clock Out Enable
+       iCPU_MUX = 1<<2,        // Multiplex
+       iCPU_SLP = 1<<3,        // Sleep
+       iCPU_PWD = 1<<4,        // Power Down Mode
+       iCPU_DMC = 1<<5,        // Divide Memory Clock
+       iCPU_DSC = 1<<6,        // Divide System Clock
+       iCPU_RST = 1<<7         // Hardware Reset Status
+};
+
+/* Clock Out Register (0x1f) */
+enum i82527_iCLK {
+       iCLK_CD0 = 1,           // Clock Divider bit 0
+       iCLK_CD1 = 1<<1,
+       iCLK_CD2 = 1<<2,
+       iCLK_CD3 = 1<<3,
+       iCLK_SL0 = 1<<4,        // Slew Rate bit 0
+       iCLK_SL1 = 1<<5
+};
+
+/* Bus Configuration Register (0x2f) */
+enum i82527_iBUS {
+       iBUS_DR0 = 1,           // Disconnect RX0 Input
+       iBUS_DR1 = 1<<1,        // Disconnect RX1 Input
+       iBUS_DT1 = 1<<3,        // Disconnect TX1 Output
+       iBUS_POL = 1<<5,        // Polarity
+       iBUS_CBY = 1<<6         // Comparator Bypass
+};
+
+#define RESET 1                        // Bit Pair Reset Status
+#define SET 2                  // Bit Pair Set Status
+#define UNCHANGED 3            // Bit Pair Unchanged
+
+/* Message Control Register 0 (Base Address + 0x0) */
+enum i82527_iMSGCTL0 {
+       INTPD_SET = SET,                // Interrupt pending
+       INTPD_RES = RESET,              // No Interrupt pending
+       INTPD_UNC = UNCHANGED,
+       RXIE_SET  = SET<<2,             // Receive Interrupt Enable
+       RXIE_RES  = RESET<<2,           // Receive Interrupt Disable
+       RXIE_UNC  = UNCHANGED<<2,
+       TXIE_SET  = SET<<4,             // Transmit Interrupt Enable
+       TXIE_RES  = RESET<<4,           // Transmit Interrupt Disable
+       TXIE_UNC  = UNCHANGED<<4,
+       MVAL_SET  = SET<<6,             // Message Valid
+       MVAL_RES  = RESET<<6,           // Message Invalid
+       MVAL_UNC  = UNCHANGED<<6
+};
+
+/* Message Control Register 1 (Base Address + 0x01) */
+enum i82527_iMSGCTL1 {
+       NEWD_SET = SET,                 // New Data
+       NEWD_RES = RESET,               // No New Data
+       NEWD_UNC = UNCHANGED,
+       MLST_SET = SET<<2,              // Message Lost
+       MLST_RES = RESET<<2,            // No Message Lost
+       MLST_UNC = UNCHANGED<<2,
+       CPUU_SET = SET<<2,              // CPU Updating
+       CPUU_RES = RESET<<2,            // No CPU Updating
+       CPUU_UNC = UNCHANGED<<2,
+       TXRQ_SET = SET<<4,              // Transmission Request
+       TXRQ_RES = RESET<<4,            // No Transmission Request
+       TXRQ_UNC = UNCHANGED<<4,
+       RMPD_SET = SET<<6,              // Remote Request Pending
+       RMPD_RES = RESET<<6,            // No Remote Request Pending
+       RMPD_UNC = UNCHANGED<<6
+};
+
+/* Message Configuration Register (Base Address + 0x06) */
+enum i82527_iMSGCFG {
+       MCFG_XTD = 1<<2,                // Extended Identifier
+       MCFG_DIR = 1<<3                 // Direction is Transmit
+};
+
+void i82527_seg_write_reg(const struct canchip_t *chip, unsigned char data, unsigned address);
+unsigned i82527_seg_read_reg(const struct canchip_t *chip, unsigned address);
diff --git a/embedded/app/usbcan/can/modparms.h b/embedded/app/usbcan/can/modparms.h
new file mode 100644 (file)
index 0000000..6691931
--- /dev/null
@@ -0,0 +1,10 @@
+/* mod_parms.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3  17 Jun 2004
+ */
+
+int parse_mod_parms(void);
diff --git a/embedded/app/usbcan/can/setup.h b/embedded/app/usbcan/can/setup.h
new file mode 100644 (file)
index 0000000..d35a8b1
--- /dev/null
@@ -0,0 +1,16 @@
+/* setup.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3  17 Jun 2004
+ */
+
+int init_hw_struct(void);
+int list_hw(void);
+void *can_checked_malloc(size_t size);
+int can_checked_free(void *address_p);
+int can_del_mem_list(void);
+int can_chip_setup_irq(struct canchip_t *chip);
+void can_chip_free_irq(struct canchip_t *chip);
diff --git a/embedded/app/usbcan/can/ul_usb1.h b/embedded/app/usbcan/can/ul_usb1.h
new file mode 100644 (file)
index 0000000..818bb24
--- /dev/null
@@ -0,0 +1,21 @@
+/* ul_usb1.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3  17 Jun 2004
+ */
+
+// int ul_usb1_request_io(struct candevice_t *candev);
+// int ul_usb1_release_io(struct candevice_t *candev);
+// int ul_usb1_reset(struct candevice_t *candev);
+// int ul_usb1_init_hw_data(struct candevice_t *candev);
+// int ul_usb1_init_chip_data(struct candevice_t *candev, int chipnr);
+// int ul_usb1_init_obj_data(struct canchip_t *chip, int objnr);
+// void ul_usb1_write_register(struct candevice_t *candev,unsigned data, unsigned long address);
+// unsigned ul_usb1_read_register(struct candevice_t *candev,unsigned long address);
+// int ul_usb1_program_irq(struct candevice_t *candev);
+
+int ul_usb1_init(void);
+void ul_usb1_exit(void);
diff --git a/embedded/app/usbcan/setup.c b/embedded/app/usbcan/setup.c
new file mode 100644 (file)
index 0000000..59ab8b5
--- /dev/null
@@ -0,0 +1,395 @@
+/* setup.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3  17 Jun 2004
+ */
+
+#include "./can/can.h"
+#include "./can/can_sysless.h"
+#include "./can/main.h"
+#include "./can/devcommon.h"
+#include "./can/setup.h"
+//#include "./can/finish.h"
+
+int init_hwspecops(struct candevice_t *candev, int *irqnum_p);
+int init_device_struct(int card, int *chan_param_idx_p, int *irq_param_idx_p);
+int init_chip_struct(struct candevice_t *candev, int chipnr, int irq, long baudrate);
+int init_obj_struct(struct candevice_t *candev, struct canchip_t *hostchip, int objnr);
+
+/**
+ * can_base_addr_fixup - relocates board physical memory addresses to the CPU accessible ones
+ * @candev: pointer to the previously filled device/board, chips and message objects structures
+ * @new_base: @candev new base address
+ *
+ * This function adapts base addresses of all structures of one board
+ * to the new board base address.
+ * It is required for translation between physical and virtual address mappings.
+ * This function is prepared to simplify board specific xxx_request_io() function
+ * for memory mapped devices.
+ */
+int can_base_addr_fixup(struct candevice_t *candev, unsigned long new_base)
+{
+       unsigned long offs;
+       int i, j;
+
+       offs=new_base-candev->dev_base_addr;
+       candev->dev_base_addr=new_base;
+       for(i=0;i<candev->nr_all_chips;i++){
+               candev->chip[i]->chip_base_addr += offs;
+               for(j=0;j<candev->chip[i]->max_objects;j++)
+                       candev->chip[i]->msgobj[j]->obj_base_addr += offs;
+       }
+       return 0;
+}
+
+/**
+ * can_check_dev_taken - checks if bus device description is already taken by driver
+ * @anydev:    pointer to bus specific Linux device description
+ *
+ * Returns: Returns 1 if device is already used by LinCAN driver, 0 otherwise.
+ */
+int can_check_dev_taken(void *anydev)
+{
+       int board_nr;
+       struct candevice_t *candev;
+       void *boarddev;
+
+       for (board_nr=hardware_p->nr_boards; board_nr--; ) {
+               if((candev=hardware_p->candevice[board_nr])==NULL)
+                       continue;
+               boarddev=candev->sysdevptr.anydev;
+               if(boarddev == anydev)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * register_obj_struct - registers message object into global array
+ * @obj: the initialized message object being registered
+ * @minorbase: wanted minor number, if (-1) automatically selected
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int register_obj_struct(struct msgobj_t *obj, int minorbase)
+{
+       static int next_minor=0;
+       int i;
+
+       if(minorbase>=0)
+               next_minor=minorbase;
+       if(next_minor>=MAX_TOT_MSGOBJS)
+               next_minor=0;
+       i=next_minor;
+       do{
+               if(objects_p[i]==NULL){
+                       objects_p[i]=obj;
+                       obj->minor=i;
+                       next_minor=i+1;
+                       return 0;
+               }
+               if(++i >= MAX_TOT_MSGOBJS) i=0;
+       }while(i!=next_minor);
+       obj->minor=-1;
+       return -1;
+}
+
+
+/**
+ * register_chip_struct - registers chip into global array
+ * @chip: the initialized chip structure being registered
+ * @minorbase: wanted minor number base, if (-1) automatically selected
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int register_chip_struct(struct canchip_t *chip, int minorbase)
+{
+       static int next_chip_slot=0;
+       int i;
+
+       if(next_chip_slot>=MAX_TOT_CHIPS)
+               next_chip_slot=0;
+       i=next_chip_slot;
+       do{
+               if(chips_p[i]==NULL){
+                       chips_p[i]=chip;
+
+                       next_chip_slot=i+1;
+                       return 0;
+               }
+               if(++i >= MAX_TOT_CHIPS) i=0;
+       }while(i!=next_chip_slot);
+       return -1;
+}
+
+
+
+/**
+ * init_hw_struct - initializes driver hardware description structures
+ *
+ * The function init_hw_struct() is used to initialize the hardware structure.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int init_hw_struct(void)
+{
+       int i=0;
+       int irq_param_idx=0;
+       int chan_param_idx=0;
+
+       hardware_p->nr_boards=0;
+       while ( (hw[i] != NULL) & (i < MAX_HW_CARDS) ) {
+               hardware_p->nr_boards++;
+
+               if (init_device_struct(i, &chan_param_idx, &irq_param_idx)) {
+                       CANMSG("Error initializing candevice_t structures.\n");
+                       return -ENODEV;
+               }
+               i++;
+       }
+
+       return 0;
+}
+
+/**
+ * init_device_struct - initializes single CAN device/board
+ * @card: index into @hardware_p HW description
+ * @chan_param_idx_p: pointer to the index into arrays of the CAN channel parameters
+ * @irq_param_idx_p: pointer to the index into arrays of the per CAN channel IRQ parameters
+ *
+ * The function builds representation of the one board from parameters provided
+ * in the module parameters arrays:
+ *     @hw[card] .. hardware type,
+ *     @io[card] .. base IO address,
+ *     @baudrate[chan_param_idx] .. per channel baudrate,
+ *     @minor[chan_param_idx] .. optional specification of requested channel minor base,
+ *     @irq[irq_param_idx] .. one or more board/chips IRQ parameters.
+ * The indexes are advanced after consumed parameters if the registration is successful.
+ *
+ * The hardware specific operations of the device/board are initialized by call to
+ * init_hwspecops() function. Then board data are initialized by board specific
+ * init_hw_data() function. Then chips and objects representation is build by
+ * init_chip_struct() function. If all above steps are successful, chips and
+ * message objects are registered into global arrays.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int init_device_struct(int card, int *chan_param_idx_p, int *irq_param_idx_p)
+{
+       struct candevice_t *candev;
+       int ret;
+       int irqnum;
+       int chipnr;
+       long bd;
+       int irqsig=-1;
+
+       candev=(struct candevice_t *)can_checked_malloc(sizeof(struct candevice_t));
+       if (candev==NULL)
+               return -ENOMEM;
+
+        memset(candev, 0, sizeof(struct candevice_t));
+
+       hardware_p->candevice[card]=candev;
+       candev->candev_idx=card;
+
+       candev=candev;
+
+       candev->hwname=hw[card];
+       candev->io_addr=io[card];
+       candev->dev_base_addr=io[card];
+
+       candev->hwspecops=(struct hwspecops_t *)can_checked_malloc(sizeof(struct hwspecops_t));
+       if (candev->hwspecops==NULL)
+               goto error_nomem;
+
+       memset(candev->hwspecops, 0, sizeof(struct hwspecops_t));
+
+       if (init_hwspecops(candev, &irqnum))
+               goto error_nodev;
+
+       if (candev->hwspecops->init_hw_data(candev))
+               goto error_nodev;
+
+       /* Alocate and initialize the chip structures */
+       for (chipnr=0; chipnr < candev->nr_all_chips; chipnr++) {
+
+               if(chipnr<irqnum)
+                       irqsig=irq[*irq_param_idx_p+chipnr];
+
+               bd=baudrate[*chan_param_idx_p+chipnr];
+               if(!bd) bd=baudrate[0];
+
+               if ((ret=init_chip_struct(candev, chipnr, irqsig, bd*1000)))
+                       goto error_chip;
+       }
+
+
+
+       for (chipnr=0; chipnr < candev->nr_all_chips; chipnr++) {
+               int m=minor[*chan_param_idx_p+chipnr];
+               struct canchip_t *chip=candev->chip[chipnr];
+               int objnr;
+
+               register_chip_struct(chip, m);
+
+               for (objnr=0; objnr<chip->max_objects; objnr++) {
+                       register_obj_struct(chip->msgobj[objnr], m);
+                       if(m>=0) m++;
+               }
+       }
+
+       *irq_param_idx_p += irqnum;
+       *chan_param_idx_p += candev->nr_all_chips;
+
+       return 0;
+
+    error_nodev:
+       ret=-ENODEV;
+    error_chip:
+       candevice_done(candev);
+       goto error_both;
+
+    error_nomem:
+       ret=-ENOMEM;
+
+    error_both:
+       hardware_p->candevice[card]=NULL;
+       can_checked_free(candev);
+       return ret;
+
+}
+
+/**
+ * init_chip_struct - initializes one CAN chip structure
+ * @candev: pointer to the corresponding CAN device/board
+ * @chipnr: index of the chip in the corresponding device/board structure
+ * @irq: chip IRQ number or (-1) if not appropriate
+ * @baudrate: baudrate in the units of 1Bd
+ *
+ * Chip structure is allocated and chip specific operations are filled by
+ * call to board specific init_chip_data() which calls chip specific
+ * fill_chipspecops(). The message objects are generated by
+ * calls to init_obj_struct() function.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int init_chip_struct(struct candevice_t *candev, int chipnr, int irq, long baudrate)
+{
+       struct canchip_t *chip;
+       int objnr;
+       int ret;
+
+       candev->chip[chipnr]=(struct canchip_t *)can_checked_malloc(sizeof(struct canchip_t));
+       if ((chip=candev->chip[chipnr])==NULL)
+               return -ENOMEM;
+
+        memset(chip, 0, sizeof(struct canchip_t));
+
+       chip->write_register=candev->hwspecops->write_register;
+       chip->read_register=candev->hwspecops->read_register;
+
+       chip->chipspecops=can_checked_malloc(sizeof(struct chipspecops_t));
+       if (chip->chipspecops==NULL)
+               return -ENOMEM;
+       memset(chip->chipspecops,0,sizeof(struct chipspecops_t));
+
+       chip->chip_idx=chipnr;
+       chip->hostdevice=candev;
+       chip->chip_irq=irq;
+       chip->baudrate=baudrate;
+       chip->flags=0x0;
+
+       if(candev->hwspecops->init_chip_data(candev,chipnr)<0)
+               return -ENODEV;
+
+       for (objnr=0; objnr<chip->max_objects; objnr++) {
+               ret=init_obj_struct(candev, chip, objnr);
+               if(ret<0) return ret;
+       }
+
+       return 0;
+}
+
+
+/**
+ * init_obj_struct - initializes one CAN message object structure
+ * @candev: pointer to the corresponding CAN device/board
+ * @hostchip: pointer to the chip containing this object
+ * @objnr: index of the builded object in the chip structure
+ *
+ * The function initializes message object structure and allocates and initializes
+ * CAN queue chip ends structure.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int init_obj_struct(struct candevice_t *candev, struct canchip_t *hostchip, int objnr)
+{
+       struct canque_ends_t *qends;
+       struct msgobj_t *obj;
+       int ret;
+
+       obj=(struct msgobj_t *)can_checked_malloc(sizeof(struct msgobj_t));
+       hostchip->msgobj[objnr]=obj;
+       if (obj == NULL)
+               return -ENOMEM;
+
+        memset(obj, 0, sizeof(struct msgobj_t));
+       obj->minor=-1;
+
+       atomic_set(&obj->obj_used,0);
+       INIT_LIST_HEAD(&obj->obj_users);
+       init_timer(&obj->tx_timeout);
+
+       qends = (struct canque_ends_t *)can_checked_malloc(sizeof(struct canque_ends_t));
+       if(qends == NULL) return -ENOMEM;
+       memset(qends, 0, sizeof(struct canque_ends_t));
+       obj->hostchip=hostchip;
+       obj->object=objnr+1;
+       obj->qends=qends;
+       obj->tx_qedge=NULL;
+       obj->tx_slot=NULL;
+       obj->obj_flags = 0x0;
+
+       ret=canqueue_ends_init_chip(qends, hostchip, obj);
+       if(ret<0) return ret;
+
+       ret=candev->hwspecops->init_obj_data(hostchip,objnr);
+       if(ret<0) return ret;
+
+       return 0;
+}
+
+
+/**
+ * init_hwspecops - finds and initializes board/device specific operations
+ * @candev: pointer to the corresponding CAN device/board
+ * @irqnum_p: optional pointer to the number of interrupts required by board
+ *
+ * The function searches board @hwname in the list of supported boards types.
+ * The board type specific board_register() function is used to initialize
+ * @hwspecops operations.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int init_hwspecops(struct candevice_t *candev, int *irqnum_p)
+{
+       const struct boardtype_t *brp;
+
+       brp = boardtype_find(candev->hwname);
+
+       if(!brp) {
+               CANMSG("Sorry, hardware \"%s\" is currently not supported.\n",candev->hwname);
+               return -EINVAL;
+       }
+
+       if(irqnum_p)
+               *irqnum_p=brp->irqnum;
+       brp->board_register(candev->hwspecops);
+
+       return 0;
+}
diff --git a/embedded/app/usbcan/ul_usb1.c b/embedded/app/usbcan/ul_usb1.c
new file mode 100644 (file)
index 0000000..b777b14
--- /dev/null
@@ -0,0 +1,637 @@
+/* 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);
+// }