]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/unican.c
LinCAN sources go through big white-space cleanup.
[lincan.git] / lincan / src / unican.c
index a64aeb3c4917a2bde70720a7d4f5afbb067b0a82..ae2934a1cb1a0465201c4c618c3eaa2c8d006f1d 100644 (file)
@@ -1,36 +1,68 @@
-/* unican.c
- * Linux CAN-bus device driver.
- * Written for new CAN driver version by Pavel Pisa - OCERA team member
- * email:pisa@cmp.felk.cvut.cz
- * This software is released under the GPL-License.
- * Version lincan-0.2  9 Jul 2003
- */ 
+/**************************************************************************/
+/* File: unican.c - Unicontrols PCAN,PCAN-PCI, VCAN boards support        */
+/*                                                                        */
+/* LinCAN - (Not only) Linux CAN bus driver                               */
+/* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz>   */
+/* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
+/* Funded by OCERA and FRESCOR IST projects                               */
+/* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl>      */
+/*                                                                        */
+/* LinCAN is free software; you can redistribute it and/or modify it      */
+/* under terms of the GNU General Public License as published by the      */
+/* Free Software Foundation; either version 2, or (at your option) any    */
+/* later version.  LinCAN is distributed in the hope that it will be      */
+/* useful, but WITHOUT ANY WARRANTY; without even the implied warranty    */
+/* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU    */
+/* General Public License for more details. You should have received a    */
+/* copy of the GNU General Public License along with LinCAN; see file     */
+/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,  */
+/* Cambridge, MA 02139, USA.                                              */
+/*                                                                        */
+/* To allow use of LinCAN in the compact embedded systems firmware        */
+/* and RT-executives (RTEMS for example), main authors agree with next    */
+/* special exception:                                                     */
+/*                                                                        */
+/* Including LinCAN header files in a file, instantiating LinCAN generics */
+/* or templates, or linking other files with LinCAN objects to produce    */
+/* an application image/executable, does not by itself cause the          */
+/* resulting application image/executable to be covered by                */
+/* the GNU General Public License.                                        */
+/* This exception does not however invalidate any other reasons           */
+/* why the executable file might be covered by the GNU Public License.    */
+/* Publication of enhanced or derived LinCAN files is required although.  */
+/**************************************************************************/
 
 #include "../include/can.h"
 #include "../include/can_sysdep.h"
 #include "../include/main.h"
 #include "../include/unican_cl2.h"
+#include "../include/setup.h"
 
+#define UNICAN_PCI_VENDOR  0xFA3C
+#define UNICAN_PCI_ID      0x0101
 
-long unican_bus_latency(struct msgobj_t *obj)
+static void unican_delay(long msdelay)
 {
-       long latency;
-       latency=obj->hostchip->baudrate;
-       if(latency){
-               latency=(long)HZ*1000/latency;
+    #ifdef CAN_WITH_RTL
+       if(!rtl_rt_system_is_idle()) {
+               rtl_delay(1000000l*msdelay);
+       }else
+    #endif /*CAN_WITH_RTL*/
+       {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout((msdelay*HZ)/1000+1);
        }
-       return latency;
-}
 
+}
 
 /* * * unican Chip Functionality * * */
 
-int unican_enable_configuration(struct chip_t *chip)
+int unican_enable_configuration(struct canchip_t *chip)
 {
        return 0;
 }
 
-int unican_disable_configuration(struct chip_t *chip)
+int unican_disable_configuration(struct canchip_t *chip)
 {
        return 0;
 }
@@ -42,8 +74,59 @@ int unican_disable_configuration(struct chip_t *chip)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_chip_config(struct chip_t *chip)
+int unican_chip_config(struct canchip_t *chip)
 {
+       int ret;
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+
+       unican_delay(10);
+
+       /* disable all card interrupts */
+       ret = cl2_int_mode(chipext, INT_MODE_ALL*0);
+       if(ret != CL2_OK) {
+               CANMSG("disable interrupts by cl2_iit_mode returned %d\n",ret);
+               return -ENODEV;
+       }
+       unican_delay(1);
+
+        if (chip->baudrate == 0)
+                chip->baudrate=1000000;
+
+       ret = chip->chipspecops->baud_rate(chip,chip->baudrate,chip->clock,0,75,0);
+       if(ret < 0){
+               CANMSG("can not set baudrate\n");
+               return ret;
+       }
+
+       unican_delay(2);
+       /* set interrupt inhibit time to 1 ms */
+       ret = cl2_set_iit(chipext, 10);
+       if(ret != CL2_OK) {
+               CANMSG("cl2_set_iit returned %d\n",ret);
+               return -ENODEV;
+       }
+       unican_delay(1);
+
+       /* enable start interrupt inhibit time command */
+       ret = cl2_iit_mode(chipext, 1);
+       if(ret != CL2_OK) {
+               CANMSG("cl2_iit_mode returned %d\n",ret);
+               return -ENODEV;
+       }
+       unican_delay(1);
+
+       /* enable all card interrupts */
+       ret = cl2_int_mode(chipext, INT_MODE_ALL);
+       if(ret != CL2_OK) {
+               CANMSG("cl2_iit_mode returned %d\n",ret);
+               return -ENODEV;
+       }
+       unican_delay(1);
+
+       /* generate interrupt command */
+       cl2_gen_interrupt(chipext);
+
+
        return 0;
 }
 
@@ -56,7 +139,7 @@ int unican_chip_config(struct chip_t *chip)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_extended_mask(struct chip_t *chip, unsigned long code, unsigned  long mask)
+int unican_extended_mask(struct canchip_t *chip, unsigned long code, unsigned  long mask)
 {
        return 0;
 }
@@ -73,9 +156,33 @@ int unican_extended_mask(struct chip_t *chip, unsigned long code, unsigned  long
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+int unican_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw,
                                                        int sampl_pt, int flags)
 {
+       int ret;
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+       int bt_val;
+
+       switch (rate) {
+               case 5000:   bt_val = CL2_BITRATE_5K; break;
+               case 10000:  bt_val = CL2_BITRATE_10K; break;
+               case 20000:  bt_val = CL2_BITRATE_20K; break;
+               case 50000:  bt_val = CL2_BITRATE_50K; break;
+               case 100000: bt_val = CL2_BITRATE_100K; break;
+               case 125000: bt_val = CL2_BITRATE_125K; break;
+               case 200000: bt_val = CL2_BITRATE_200K; break;
+               case 250000: bt_val = CL2_BITRATE_250K; break;
+               case 500000: bt_val = CL2_BITRATE_500K; break;
+               case 800000: bt_val = CL2_BITRATE_800K; break;
+               case 1000000:bt_val = CL2_BITRATE_1M; break;
+               default: return -EINVAL;
+       }
+
+       ret=cl2_set_bitrate(chipext,bt_val);
+       if(ret == CL2_COMMAND_BUSY) return -EBUSY;
+       if(ret != CL2_OK) return -EINVAL;
+       unican_delay(2);
+
        return 0;
 }
 
@@ -84,10 +191,77 @@ int unican_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
  * @chip: pointer to chip state structure
  * @obj: pinter to CAN message queue information
  *
+ * This is rewritten cl2_receive_data function. The direct use of CL2
+ * function would require one more message data copy to reformat message
+ * data into different structure layout. Other way is to rewrite CL2 sources.
+ * No of these solutions is perfect.
+ *
  * File: src/unican.c
  */
-void unican_read(struct chip_t *chip, struct msgobj_t *obj) {
+void unican_read(struct canchip_t *chip, struct msgobj_t *obj) {
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+       __u16 *ptr16;
+       __u16 u;
+       unsigned long timestamp;
+       int i;
+
+       do {
+               ptr16 = (__u16*)chipext->rxBufPtr;
+               u = unican_readw(ptr16++);
+               if ( !(u & CL2_MESSAGE_VALID) ) break; /* No more messages in the queue */
+
+               obj->rx_msg.id = ((__u32)(u & 0xFF00 )) << 16;
+               u = unican_readw(ptr16++);
+               obj->rx_msg.id |= ((__u32)( u & 0x00FF )) << 16;
+               obj->rx_msg.id |= (__u32)( u & 0xFF00 );
+               u = unican_readw(ptr16++);
+               obj->rx_msg.id |= (__u32)( u & 0x00FF );
 
+
+               u >>= 8;
+
+               if ( u & CL2_EXT_FRAME ) {      /* 2.0B frame */
+                       obj->rx_msg.id >>= 3;
+                       obj->rx_msg.flags = MSG_EXT;
+               } else {                        /* 2.0A frame */
+                       obj->rx_msg.id >>= 21;
+                       obj->rx_msg.flags = 0;
+               }
+
+               /*if ( !(u & (CL2_REMOTE_FRAME<<8)) )
+                       obj->rx_msg.flags |= MSG_RTR;*/
+
+               obj->rx_msg.length = ( (u >> 4) & 0x000F );
+               if(obj->rx_msg.length > CAN_MSG_LENGTH) obj->rx_msg.length = CAN_MSG_LENGTH;
+
+               for ( i = 0; i < obj->rx_msg.length; ) {
+                       u = unican_readw(ptr16++);
+                       obj->rx_msg.data[i++] = (__u8)( u );
+                       obj->rx_msg.data[i++] = (__u8)( u >> 8 );
+               }
+               if ( obj->rx_msg.length & 0x01 ) {      /* odd */
+                       timestamp = ( (unican_readw(ptr16++) & 0x00FF) | (u & 0xFF00) );
+               } else {                                /* even */
+                       u = unican_readw(ptr16++);
+                       timestamp = (u << 8) | (u >> 8);
+               }
+               unican_writew(0x000,(__u16*)chipext->rxBufPtr);
+
+              #ifdef CAN_MSG_VERSION_2
+               obj->rx_msg.timestamp.tv_sec = 0;
+               obj->rx_msg.timestamp.tv_usec = timestamp;
+               #else /* CAN_MSG_VERSION_2 */
+               obj->rx_msg.timestamp = timestamp;
+               #endif /* CAN_MSG_VERSION_2 */
+
+               /* increment rx-buffer pointer */
+               if ( (chipext->rxBufBase + chipext->rxBufSize*16 ) <= (chipext->rxBufPtr += 16) ) {
+                       chipext->rxBufPtr = chipext->rxBufBase;
+               }
+
+               canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+
+       } while (1);
 }
 
 /**
@@ -99,7 +273,7 @@ void unican_read(struct chip_t *chip, struct msgobj_t *obj) {
  *     Positive value indicates immediate reception of message.
  * File: src/unican.c
  */
-int unican_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
+int unican_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj)
 {
        return 0;
 }
@@ -114,7 +288,7 @@ int unican_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_pre_write_config(struct chip_t *chip, struct msgobj_t *obj, 
+int unican_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
                                                        struct canmsg_t *msg)
 {
        return 0;
@@ -131,7 +305,7 @@ int unican_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_send_msg(struct chip_t *chip, struct msgobj_t *obj, 
+int unican_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
                                                        struct canmsg_t *msg)
 {
        return 0;
@@ -146,7 +320,7 @@ int unican_send_msg(struct chip_t *chip, struct msgobj_t *obj,
  *     Zero value indicates finishing of all issued transmission requests.
  * File: src/unican.c
  */
-int unican_check_tx_stat(struct chip_t *chip)
+int unican_check_tx_stat(struct canchip_t *chip)
 {
        return 0;
 }
@@ -160,9 +334,18 @@ int unican_check_tx_stat(struct chip_t *chip)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_set_btregs(struct chip_t *chip, unsigned short btr0, 
+int unican_set_btregs(struct canchip_t *chip, unsigned short btr0,
                                                        unsigned short btr1)
 {
+       int ret;
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+       int bt_val;
+
+       bt_val=btr0 | (btr1<<8);
+       ret=cl2_set_bitrate(chipext,bt_val);
+       if(ret == CL2_COMMAND_BUSY) return -EBUSY;
+       if(ret != CL2_OK) return -EINVAL;
+
        return 0;
 }
 
@@ -173,7 +356,7 @@ int unican_set_btregs(struct chip_t *chip, unsigned short btr0,
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_start_chip(struct chip_t *chip)
+int unican_start_chip(struct canchip_t *chip)
 {
        return 0;
 }
@@ -185,11 +368,39 @@ int unican_start_chip(struct chip_t *chip)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_stop_chip(struct chip_t *chip)
+int unican_stop_chip(struct canchip_t *chip)
 {
        return 0;
 }
 
+/**
+ * unican_attach_to_chip: - attaches to the chip, setups registers and state
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/sja1000p.c
+ */
+int unican_attach_to_chip(struct canchip_t *chip)
+{
+       return 0;
+}
+
+/**
+ * unican_release_chip: - called before chip structure removal if %CHIP_ATTACHED is set
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/sja1000p.c
+ */
+int unican_release_chip(struct canchip_t *chip)
+{
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+
+       unican_stop_chip(chip);
+       cl2_clear_interrupt(chipext);
+
+       return 0;
+}
 
 /**
  * unican_remote_request: - configures message object and asks for RTR message
@@ -199,7 +410,7 @@ int unican_stop_chip(struct chip_t *chip)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_remote_request(struct chip_t *chip, struct msgobj_t *obj)
+int unican_remote_request(struct canchip_t *chip, struct msgobj_t *obj)
 {
        CANMSG("unican_remote_request not implemented\n");
        return -ENOSYS;
@@ -214,7 +425,7 @@ int unican_remote_request(struct chip_t *chip, struct msgobj_t *obj)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_standard_mask(struct chip_t *chip, unsigned short code,
+int unican_standard_mask(struct canchip_t *chip, unsigned short code,
                unsigned short mask)
 {
        CANMSG("unican_standard_mask not implemented\n");
@@ -228,7 +439,7 @@ int unican_standard_mask(struct chip_t *chip, unsigned short code,
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_clear_objects(struct chip_t *chip)
+int unican_clear_objects(struct canchip_t *chip)
 {
        CANMSG("unican_clear_objects not implemented\n");
        return -ENOSYS;
@@ -242,8 +453,9 @@ int unican_clear_objects(struct chip_t *chip)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_config_irqs(struct chip_t *chip, short irqs)
+int unican_config_irqs(struct canchip_t *chip, short irqs)
 {
+
        CANMSG("unican_config_irqs not implemented\n");
        return -ENOSYS;
 }
@@ -259,21 +471,137 @@ int unican_config_irqs(struct chip_t *chip, short irqs)
  * unican_irq_write_handler() for transmit events.
  * File: src/unican.c
  */
-void unican_irq_write_handler(struct chip_t *chip, struct msgobj_t *obj)
+void unican_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj)
+{
+       int cmd;
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+       __u16 *ptr16 = (__u16*)chipext->rxBufPtr;
+       __u16 u;
+       unsigned long timestamp=0;
+       unsigned long cobid;
+       int i;
+       int len;
+
+       #if 0
+       if(obj->tx_slot){
+               /* Do local transmitted message distribution if enabled */
+               if (processlocal){
+                       obj->tx_slot->msg.flags |= MSG_LOCAL;
+                       canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
+               }
+               /* Free transmitted slot */
+               canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+               obj->tx_slot=NULL;
+       }
+       #endif
+
+       if ( chipext->asyncTxBufSize==0 ) {
+               canque_notify_inends(obj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_PREP);
+               return; /* No asynchronous queue configured */
+       }
+
+       do {
+               ptr16 = (__u16*)chipext->asyncTxBufPtr;
+               if(unican_readw(ptr16) & CL2_MESSAGE_VALID)
+                       return;         /* No free space in asynchronous Tx queue */
+
+               cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
+               if(cmd<0)
+                       return;         /* No more messages to send */
+
+
+               cobid = obj->tx_slot->msg.id;
+
+               if ( (obj->tx_slot->msg.flags & MSG_EXT) ) {    /* 2.0B frame */
+                       cobid <<= 3;
+               } else {                                        /* 2.0A frame */
+                       cobid <<= 5+16;
+               }
+               ptr16++;
+               u = ((cobid>>16) & 0x00FF ) + (cobid & 0xFF00);
+               unican_writew(u,ptr16++);
+
+               len = obj->tx_slot->msg.length;
+               if(len > CAN_MSG_LENGTH)
+                       len = CAN_MSG_LENGTH;
+               u = (len << 12) | (cobid & 0x00FF);
+
+               if ( !(obj->tx_slot->msg.flags & MSG_RTR) )
+                       u |= CL2_REMOTE_FRAME<<8;
+               if ( obj->tx_slot->msg.flags & MSG_EXT )
+                       u |= CL2_EXT_FRAME<<8;
+
+               unican_writew(u,ptr16++);
+
+               for ( i = 0; i < len-1; )       {
+                       u = obj->tx_slot->msg.data[i++];
+                       u |= ((__u16)obj->tx_slot->msg.data[i]<<8); i++;
+                       unican_writew(u,ptr16++);
+               }
+               if(i == len) {
+                       unican_writew(timestamp,ptr16);
+               } else {
+                       u = obj->tx_slot->msg.data[i++];
+                       u |= ((timestamp & 0x00FF)<<8);
+                       unican_writew(u,ptr16++);
+                       unican_writew(timestamp & 0x00FF, ptr16);
+               }
+
+               u = ((cobid>>16) & 0xFF00) | CL2_MESSAGE_VALID;
+               unican_writew(u,(__u16*)chipext->asyncTxBufPtr);
+
+               if ( (chipext->asyncTxBufBase + chipext->asyncTxBufSize*16) <=
+                                               (chipext->asyncTxBufPtr += 16) ) {
+                       chipext->asyncTxBufPtr = chipext->asyncTxBufBase;
+               }
+
+
+               /* Do local transmitted message distribution if enabled. */
+               /* This code should not be called directly there, because it breaks strict
+                  behavior of queues if O_SYNC is set. */
+               if (processlocal){
+                       obj->tx_slot->msg.flags |= MSG_LOCAL;
+                       canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
+               }
+               /* Free transmitted slot */
+               canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+               obj->tx_slot=NULL;
+
+       }while(1);
+
+       return;
+
+}
+
+void unican_irq_sync_activities(struct canchip_t *chip, struct msgobj_t *obj)
 {
+       while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)) {
+
+               if(can_msgobj_test_and_clear_fl(obj,TX_REQUEST)) {
+                       unican_irq_write_handler(chip, obj);
+               }
+
+               /*if(can_msgobj_test_and_clear_fl(obj,FILTCH_REQUEST)) {
+                       unican_irq_update_filter(chip, obj);
+               }*/
 
+               can_msgobj_clear_fl(obj,TX_LOCK);
+               if(can_msgobj_test_fl(obj,TX_REQUEST))
+                       continue;
+               if(can_msgobj_test_fl(obj,FILTCH_REQUEST) && !obj->tx_slot)
+                       continue;
+               break;
+       }
 }
 
+
 #define MAX_RETR 10
 
 /**
  * unican_irq_handler: - interrupt service routine
  * @irq: interrupt vector number, this value is system specific
- * @dev_id: driver private pointer registered at time of request_irq() call.
- *     The CAN driver uses this pointer to store relationship of interrupt
- *     to chip state structure - @struct chip_t
- * @regs: system dependent value pointing to registers stored in exception frame
- * 
+ * @chip: pointer to chip state structure
+ *
  * Interrupt handler is activated when state of CAN controller chip changes,
  * there is message to be read or there is more space for new messages or
  * error occurs. The receive events results in reading of the message from
@@ -281,58 +609,78 @@ void unican_irq_write_handler(struct chip_t *chip, struct msgobj_t *obj)
  * message queues.
  * File: src/unican.c
  */
-can_irqreturn_t unican_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+int unican_irq_handler(int irq, struct canchip_t *chip)
 {
-       return CAN_IRQ_HANDLED;
-}
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+       struct msgobj_t *obj=chip->msgobj[0];
+       __u16 status;
+       __u16 error;
 
+       if(!(chip->flags&CHIP_CONFIGURED)) {
+               CANMSG("unican_irq_handler: called for non-configured device\n");
+               return CANCHIP_IRQ_NONE;
+       }
 
-void unican_schedule_next(struct msgobj_t *obj)
-{
-       int cmd;
+       if (cl2_get_status(chipext, &status) == CL2_NO_REQUEST) {
+               /* Reenable interrupts generation, this has to be even there,
+                * because irq_accept disables interrupts
+                */
+               cl2_gen_interrupt(chipext);
+               return CANCHIP_IRQ_NONE;
+       }
 
-       can_preempt_disable();
+       cl2_clear_interrupt(chipext);
 
-       can_msgobj_set_fl(obj,TX_REQUEST);
-       
-       while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
 
-               can_msgobj_clear_fl(obj,TX_REQUEST);
-               
-               cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
-               if(cmd>=0) {
-                       mod_timer(&obj->tx_timeout,
-                               jiffies+unican_bus_latency(obj));
-                       DEBUGMSG("unican: scheduled delivery\n");
-
-               } else          
-                       can_msgobj_clear_fl(obj,TX_LOCK);
-               
-               if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
-               DEBUGMSG("TX looping in unican_schedule_next\n");
+       if(status & CL2_CARD_ERROR) {
+               cl2_get_error(chipext, &error);
+               CANMSG("unican_irq_handler: card status=0x%04x error=0x%04x \n",status,error);
        }
+       if(status & CL2_ASYNC_QUEUE_EMPTY) {
 
-       can_preempt_enable();
+       }
+       if(status & CL2_SYNC_QUEUE_EMPTY) {
+               can_msgobj_set_fl(obj,TX_REQUEST);
+
+               /* calls unican_irq_write_handler synchronized with other invocations */
+               unican_irq_sync_activities(chip, obj);
+
+       }
+       if(status & CL2_DATA_IN_RBUF) {
+               unican_read(chip, obj);
+       }
+
+       /* Reenable interrupts generation */
+       cl2_gen_interrupt(chipext);
+
+       return CANCHIP_IRQ_HANDLED;
 }
 
 
-void unican_do_tx_timeout(unsigned long data)
+/**
+ * unican_irq_accept: - fast irq accept routine, blocks further interrupts
+ * @irq: interrupt vector number, this value is system specific
+ * @chip: pointer to chip state structure
+ *
+ * This routine only accepts interrupt reception and stops further
+ * incoming interrupts, but does not handle situation causing interrupt.
+ * File: src/unican.c
+ */
+int unican_irq_accept(int irq, struct canchip_t *chip)
 {
-       struct msgobj_t *obj=(struct msgobj_t *)data;
-       
-       if(obj->tx_slot) {
-               /* Deliver message to edges */
-               canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
-               /* Free transmitted slot */
-               canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
-               obj->tx_slot=NULL;
-               DEBUGMSG("unican: delayed delivery\n");
-       }
-       can_msgobj_clear_fl(obj,TX_LOCK);
+       sCAN_CARD *chipext = (sCAN_CARD *)chip->chip_data;
+
+       cl2_clear_interrupt(chipext);
 
-       unican_schedule_next(obj);
+       return CANCHIP_IRQ_ACCEPTED;
 }
 
+/*void unican_do_tx_timeout(unsigned long data)
+{
+       struct msgobj_t *obj=(struct msgobj_t *)data;
+
+}*/
+
 /**
  * unican_wakeup_tx: - wakeups TX processing
  * @chip: pointer to chip state structure
@@ -341,32 +689,17 @@ void unican_do_tx_timeout(unsigned long data)
  * Return Value: negative value reports error.
  * File: src/unican.c
  */
-int unican_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj)
+int unican_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
 {
-       /* can_msgobj_set_fl(obj,TX_REQUEST); */
-       
-       struct canque_edge_t *qedge;
-       struct canque_slot_t *slot;
-       int cmd;
+       can_preempt_disable();
 
-       can_msgobj_clear_fl(obj,TX_REQUEST);
+       can_msgobj_set_fl(obj,TX_REQUEST);
 
-    #ifndef CAN_WITH_RTL
-       if(!unican_bus_latency(obj)) {
-    #endif /*CAN_WITH_RTL*/
-               /* Ensure delivery of all ready slots */
-               while((cmd=canque_test_outslot(obj->qends, &qedge, &slot)) >= 0){
-                       if(cmd==0) {
-                               canque_filter_msg2edges(obj->qends, &slot->msg);
-                               DEBUGMSG("unican: direct delivery\n");
-                       }
-                       canque_free_outslot(obj->qends, qedge, slot);
-               }
-    #ifndef CAN_WITH_RTL
-       } else {
-               unican_schedule_next(obj);
-       }
-    #endif /*CAN_WITH_RTL*/
+       /* calls unican_irq_write_handler synchronized with other invocations
+         from kernel and IRQ context */
+       unican_irq_sync_activities(chip, obj);
+
+       can_preempt_enable();
 
        return 0;
 }
@@ -374,6 +707,8 @@ int unican_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj)
 
 /* * * unican Board Functionality * * */
 
+#define IO_RANGE 0x1000
+
 /**
  * unican_request_io: - reserve io or memory range for can board
  * @candev: pointer to candevice/board which asks for io. Field @io_addr
@@ -384,6 +719,19 @@ int unican_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj)
  */
 int unican_request_io(struct candevice_t *candev)
 {
+        can_ioptr_t remap_addr;
+       if (!can_request_mem_region(candev->io_addr,IO_RANGE,DEVICE_NAME " - unican")) {
+               CANMSG("Unable to request IO-memory: 0x%lx\n",candev->io_addr);
+               return -ENODEV;
+       }
+       if ( !( remap_addr = ioremap( candev->io_addr, IO_RANGE ) ) ) {
+               CANMSG("Unable to access I/O memory at: 0x%lx\n", candev->io_addr);
+               can_release_mem_region(candev->io_addr,IO_RANGE);
+               return -ENODEV;
+
+       }
+       can_base_addr_fixup(candev, remap_addr);
+       DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", candev->io_addr, candev->io_addr + IO_RANGE - 1);
        return 0;
 }
 
@@ -396,6 +744,8 @@ int unican_request_io(struct candevice_t *candev)
  */
 int unican_release_io(struct candevice_t *candev)
 {
+       iounmap(candev->dev_base_addr);
+       can_release_mem_region(candev->io_addr,IO_RANGE);
        return 0;
 }
 
@@ -408,6 +758,48 @@ int unican_release_io(struct candevice_t *candev)
  */
 int unican_reset(struct candevice_t *candev)
 {
+       int ret;
+       int i;
+       struct canchip_t *chip = candev->chip[0];
+       sCAN_CARD *chipext;
+
+
+       if(chip->chip_data == NULL) {
+               chip->chip_data = can_checked_malloc(sizeof(sCAN_CARD));
+               if(!chip->chip_data) return -ENOMEM;
+               memset(chip->chip_data,0,sizeof(sCAN_CARD));
+               ret = cl2_init_card(chip->chip_data,(void*)chip->chip_base_addr,chip->chip_irq);
+               if(ret != CL2_OK){
+                       CANMSG("cl2_init_card returned %d\n",ret);
+                       return -ENODEV;
+               }
+       }
+
+       chipext = (sCAN_CARD *)chip->chip_data;
+
+       i = 0;
+       /* reset and test whether the card is present */
+       do {
+               cl2_reset_card(chipext);
+               unican_delay(10);
+               i++;
+               ret = cl2_test_card(chipext);
+       } while((ret != CL2_OK)&&(i<10));
+
+       if(ret != CL2_OK) {
+               CANMSG("card check failed %d\n",ret);
+               return -ENODEV;
+       }
+
+       /* start card firmware */
+       ret = cl2_start_firmware(chipext);
+       if(ret != CL2_OK){
+               CANMSG("cl2_start_firmware returned %d\n",ret);
+               return -ENODEV;
+       }
+
+        unican_delay(100);
+
        return 0;
 }
 
@@ -418,7 +810,7 @@ int unican_reset(struct candevice_t *candev)
  * Return Value: The function always returns zero
  * File: src/unican.c
  */
-int unican_init_hw_data(struct candevice_t *candev) 
+int unican_init_hw_data(struct candevice_t *candev)
 {
        candev->res_addr=0;
        candev->nr_82527_chips=0;
@@ -429,8 +821,6 @@ int unican_init_hw_data(struct candevice_t *candev)
        return 0;
 }
 
-#define CHIP_TYPE "unican"
-
 /**
  * unican_init_chip_data - Initialize chips
  * @candev: Pointer to candevice/board structure
@@ -441,13 +831,14 @@ int unican_init_hw_data(struct candevice_t *candev)
  */
 int unican_init_chip_data(struct candevice_t *candev, int chipnr)
 {
-       struct chip_t *chip = candev->chip[chipnr];
-       chip->chip_type = CHIP_TYPE;
+       struct canchip_t *chip = candev->chip[chipnr];
+       chip->chip_type = "unican";
        chip->chip_base_addr = 0;
        chip->clock = 10000000;
        chip->int_clk_reg = 0x0;
        chip->int_bus_reg = 0x0;
        chip->max_objects = 1;
+       chip->chip_base_addr=candev->dev_base_addr;
 
        CANMSG("initializing unican chip operations\n");
        chip->chipspecops->chip_config=unican_chip_config;
@@ -466,9 +857,12 @@ int unican_init_chip_data(struct candevice_t *candev, int chipnr)
        chip->chipspecops->enable_configuration=unican_enable_configuration;
        chip->chipspecops->disable_configuration=unican_disable_configuration;
        chip->chipspecops->set_btregs=unican_set_btregs;
+       chip->chipspecops->attach_to_chip=unican_attach_to_chip;
+       chip->chipspecops->release_chip=unican_release_chip;
        chip->chipspecops->start_chip=unican_start_chip;
        chip->chipspecops->stop_chip=unican_stop_chip;
-       chip->chipspecops->irq_handler=NULL;
+       chip->chipspecops->irq_handler=unican_irq_handler;
+       chip->chipspecops->irq_accept=unican_irq_accept;
 
        return 0;
 }
@@ -481,12 +875,12 @@ int unican_init_chip_data(struct candevice_t *candev, int chipnr)
  * Return Value: The function always returns zero
  * File: src/unican.c
  */
-int unican_init_obj_data(struct chip_t *chip, int objnr)
+int unican_init_obj_data(struct canchip_t *chip, int objnr)
 {
        struct msgobj_t *obj=chip->msgobj[objnr];
        obj->obj_base_addr=chip->chip_base_addr;
-       obj->tx_timeout.function=unican_do_tx_timeout;
-       obj->tx_timeout.data=(unsigned long)obj;
+       /*obj->tx_timeout.function=unican_do_tx_timeout;
+       obj->tx_timeout.data=(unsigned long)obj;*/
        return 0;
 }
 
@@ -515,3 +909,130 @@ int unican_register(struct hwspecops_t *hwspecops)
        hwspecops->program_irq = unican_program_irq;
        return 0;
 }
+
+
+/* Unicontrols PCI board specific functions */
+
+#ifdef CAN_ENABLE_PCI_SUPPORT
+
+int unican_pci_request_io(struct candevice_t *candev)
+{
+        can_ioptr_t remap_addr;
+
+    #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+       if(pci_request_region(candev->sysdevptr.pcidev, 0, "unican_pci") != 0){
+               CANMSG("Request of Unican PCI range failed\n");
+               return -ENODEV;
+       }
+    #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+       if(pci_request_regions(candev->sysdevptr.pcidev, "kv_pcican") != 0){
+               CANMSG("Request of Unican PCI range failed\n");
+               return -ENODEV;
+       }
+    #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+
+       candev->io_addr=pci_resource_start(candev->sysdevptr.pcidev,0);
+       candev->res_addr=candev->io_addr;
+
+       if ( !( remap_addr = ioremap( candev->io_addr, IO_RANGE ) ) ) {
+               CANMSG("Unable to access I/O memory at: 0x%lx\n", candev->io_addr);
+           #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+               pci_release_region(candev->sysdevptr.pcidev, 0);
+           #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+               pci_release_regions(candev->sysdevptr.pcidev);
+           #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+               return -ENODEV;
+
+       }
+       can_base_addr_fixup(candev, remap_addr);
+       DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", candev->io_addr, candev->io_addr + IO_RANGE - 1);
+       DEBUGMSG("VMA: dev_base_addr: 0x%lx chip_base_addr: 0x%lx\n",
+               can_ioptr2ulong(candev->dev_base_addr),
+               can_ioptr2ulong(candev->chip[0]->chip_base_addr));
+
+       return 0;
+}
+
+
+int unican_pci_release_io(struct candevice_t *candev)
+{
+       iounmap(candev->dev_base_addr);
+    #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+       pci_release_region(candev->sysdevptr.pcidev, 0);
+    #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+       pci_release_regions(candev->sysdevptr.pcidev);
+    #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+       return 0;
+}
+
+
+int unican_pci_init_hw_data(struct candevice_t *candev)
+{
+       struct pci_dev *pcidev = NULL;
+
+       pcidev = can_pci_get_next_untaken_device(UNICAN_PCI_VENDOR, UNICAN_PCI_ID);
+       if(pcidev == NULL)
+               return -ENODEV;
+
+       if (pci_enable_device (pcidev)){
+               printk(KERN_CRIT "Setup of Unican PCI failed\n");
+               can_pci_dev_put(pcidev);
+               return -EIO;
+       }
+       candev->sysdevptr.pcidev=pcidev;
+
+       if(!(pci_resource_flags(pcidev,0)&IORESOURCE_MEM)){
+               printk(KERN_CRIT "Unican PCI region 0 is not MEM\n");
+               can_pci_dev_put(pcidev);
+               return -EIO;
+       }
+       candev->io_addr=pci_resource_start(pcidev,0);
+       candev->res_addr=candev->io_addr;
+       candev->dev_base_addr=NULL;
+
+       /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/
+
+       candev->nr_82527_chips=0;
+       candev->nr_sja1000_chips=0;
+       candev->nr_all_chips=1;
+
+       return 0;
+}
+
+void unican_pci_done_hw_data(struct candevice_t *candev)
+{
+       struct pci_dev *pcidev = candev->sysdevptr.pcidev;
+       can_pci_dev_put(pcidev);
+}
+
+int unican_pci_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+       int ret;
+       candev->chip[chipnr]->chip_irq=candev->sysdevptr.pcidev->irq;
+       ret = unican_init_chip_data(candev, chipnr);
+       candev->chip[chipnr]->flags |= CHIP_IRQ_PCI;
+       return ret;
+}
+
+int unican_pci_register(struct hwspecops_t *hwspecops)
+{
+       hwspecops->request_io = unican_pci_request_io;
+       hwspecops->release_io = unican_pci_release_io;
+       hwspecops->reset = unican_reset;
+       hwspecops->init_hw_data = unican_pci_init_hw_data;
+       hwspecops->done_hw_data = unican_pci_done_hw_data;
+       hwspecops->init_chip_data = unican_pci_init_chip_data;
+       hwspecops->init_obj_data = unican_init_obj_data;
+       hwspecops->write_register = NULL;
+       hwspecops->read_register = NULL;
+       hwspecops->program_irq = unican_program_irq;
+       return 0;
+}
+
+#endif /*CAN_ENABLE_PCI_SUPPORT*/
+
+#ifdef CAN_ENABLE_VME_SUPPORT
+
+#include "unican_vme.c"
+
+#endif /*CAN_ENABLE_VME_SUPPORT*/