X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/1fd6ebf194739bee96aa5696cdd24622ef658130..4a1f32c3421f6e8f1ea90192e4ef4074f09db891:/lincan/src/unican.c diff --git a/lincan/src/unican.c b/lincan/src/unican.c index a64aeb3..2347a67 100644 --- a/lincan/src/unican.c +++ b/lincan/src/unican.c @@ -10,6 +10,24 @@ #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 + +static void unican_delay(long msdelay) +{ + #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); + } + +} long unican_bus_latency(struct msgobj_t *obj) @@ -44,6 +62,57 @@ int unican_disable_configuration(struct chip_t *chip) */ int unican_chip_config(struct chip_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; } @@ -76,6 +145,30 @@ int unican_extended_mask(struct chip_t *chip, unsigned long code, unsigned long int unican_baud_rate(struct chip_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 +177,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) { + 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); } /** @@ -163,6 +323,15 @@ int unican_check_tx_stat(struct chip_t *chip) int unican_set_btregs(struct chip_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; } @@ -244,6 +413,7 @@ int unican_clear_objects(struct chip_t *chip) */ int unican_config_irqs(struct chip_t *chip, short irqs) { + CANMSG("unican_config_irqs not implemented\n"); return -ENOSYS; } @@ -261,9 +431,128 @@ int unican_config_irqs(struct chip_t *chip, short irqs) */ void unican_irq_write_handler(struct chip_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 chip_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 /** @@ -283,55 +572,52 @@ void unican_irq_write_handler(struct chip_t *chip, struct msgobj_t *obj) */ can_irqreturn_t unican_irq_handler(int irq, void *dev_id, struct pt_regs *regs) { - return CAN_IRQ_HANDLED; -} + struct chip_t *chip=(struct chip_t *)dev_id; + 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 CAN_IRQ_NONE; + } + if (cl2_get_status(chipext, &status) == CL2_NO_REQUEST) + return CAN_IRQ_NONE; -void unican_schedule_next(struct msgobj_t *obj) -{ - int cmd; + cl2_clear_interrupt(chipext); - can_preempt_disable(); - can_msgobj_set_fl(obj,TX_REQUEST); - - while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){ + 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_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"); + } + if(status & CL2_SYNC_QUEUE_EMPTY) { + can_msgobj_set_fl(obj,TX_REQUEST); - } 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"); + /* 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); } - can_preempt_enable(); + cl2_gen_interrupt(chipext); + + return CAN_IRQ_HANDLED; } -void unican_do_tx_timeout(unsigned long data) +/*void unican_do_tx_timeout(unsigned long data) { 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); - - unican_schedule_next(obj); -} +}*/ /** * unican_wakeup_tx: - wakeups TX processing @@ -343,30 +629,15 @@ void unican_do_tx_timeout(unsigned long data) */ int unican_wakeup_tx(struct chip_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 +645,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 +657,19 @@ int unican_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj) */ int unican_request_io(struct candevice_t *candev) { + unsigned long 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 = (long) 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 +682,8 @@ int unican_request_io(struct candevice_t *candev) */ int unican_release_io(struct candevice_t *candev) { + iounmap((void*)candev->dev_base_addr); + can_release_mem_region(candev->io_addr,IO_RANGE); return 0; } @@ -408,6 +696,48 @@ int unican_release_io(struct candevice_t *candev) */ int unican_reset(struct candevice_t *candev) { + int ret; + int i; + struct chip_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; } @@ -448,7 +778,8 @@ int unican_init_chip_data(struct candevice_t *candev, int chipnr) chip->int_clk_reg = 0x0; chip->int_bus_reg = 0x0; chip->max_objects = 1; - + chip->chip_base_addr=candev->io_addr; + CANMSG("initializing unican chip operations\n"); chip->chipspecops->chip_config=unican_chip_config; chip->chipspecops->baud_rate=unican_baud_rate; @@ -468,7 +799,7 @@ int unican_init_chip_data(struct candevice_t *candev, int chipnr) chip->chipspecops->set_btregs=unican_set_btregs; 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; return 0; } @@ -485,8 +816,8 @@ int unican_init_obj_data(struct chip_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 +846,121 @@ 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) +{ + #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))*/ + + return 0; +} + + +int unican_pci_release_io(struct candevice_t *candev) +{ + #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; + + do { + pcidev = pci_find_device(UNICAN_PCI_VENDOR, UNICAN_PCI_ID, pcidev); + if(pcidev == NULL) return -ENODEV; + } while(can_check_dev_taken(pcidev)); + + if (pci_enable_device (pcidev)){ + printk(KERN_CRIT "Setup of Unican PCI failed\n"); + 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"); + return -EIO; + } + candev->dev_base_addr=pci_resource_start(pcidev,0); /*S5920*/ + candev->io_addr=candev->dev_base_addr; + candev->res_addr=candev->dev_base_addr; + + /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/ + + candev->nr_82527_chips=0; + candev->nr_sja1000_chips=0; + candev->nr_all_chips=1; + + return 0; +} + + +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_pci_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->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 + +int unican_vme_reset(struct candevice_t *candev) +{ + struct chip_t *chip = candev->chip[0]; + unican_writew(chip->chip_irq,chip->chip_base_addr+CL2_VME_INT_VECTOR); + return unican_reset(candev); +} + + +int unican_vme_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = unican_request_io; + hwspecops->release_io = unican_release_io; + hwspecops->reset = unican_vme_reset; + hwspecops->init_hw_data = unican_init_hw_data; + hwspecops->init_chip_data = unican_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_VME_SUPPORT*/