edouard@629: /* edouard@629: This file is part of CanFestival, a library implementing CanOpen Stack. edouard@629: edouard@629: Copyright (C): Cosateq GmbH & Co.KG edouard@629: http://www.cosateq.com/ edouard@629: http://www.scale-rt.com/ edouard@629: edouard@629: See COPYING file for copyrights details. edouard@629: edouard@629: This library is free software; you can redistribute it and/or edouard@629: modify it under the terms of the GNU Lesser General Public edouard@629: License as published by the Free Software Foundation; either edouard@629: version 2.1 of the License, or (at your option) any later version. edouard@629: edouard@629: This library is distributed in the hope that it will be useful, edouard@629: but WITHOUT ANY WARRANTY; without even the implied warranty of edouard@629: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU edouard@629: Lesser General Public License for more details. edouard@629: edouard@629: You should have received a copy of the GNU Lesser General Public edouard@629: License along with this library; if not, write to the Free Software edouard@629: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA edouard@629: */ edouard@629: edouard@629: /* edouard@629: COMEDI interface for CO-PCICAN card. edouard@629: */ edouard@629: edouard@629: #include edouard@629: #include edouard@629: #include edouard@629: edouard@629: #define NEED_PRINT_MESSAGE edouard@629: #include "can_driver.h" edouard@629: #include "def.h" edouard@629: #include "co_pcicanops.h" edouard@629: edouard@629: MODULE_LICENSE("GPL"); edouard@629: edouard@629: /* at the moment not threadsafe :-( */ edouard@629: static unsigned char selectedChannelRx = 0, selectedChannelTx = 0; edouard@629: edouard@629: static int rt_strcmp( const char *src, const char *dst ) edouard@629: { edouard@629: int ret = 0; edouard@629: edouard@629: while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) edouard@629: { edouard@629: src++; edouard@629: dst++; edouard@629: } edouard@629: edouard@629: if( ret < 0 ) edouard@629: { edouard@629: ret = -1 ; edouard@629: } edouard@629: else edouard@629: { edouard@629: if( ret > 0 ) edouard@629: ret = 1 ; edouard@629: } edouard@629: edouard@629: return ret; edouard@629: } edouard@629: edouard@629: static int TranslateBaudRate( char* optarg ) edouard@629: { edouard@629: /* values see documentation of CO-PCICAN */ edouard@629: if( !rt_strcmp( optarg, "1M" ) ) return 0; edouard@629: if( !rt_strcmp( optarg, "800K" ) ) return 1; edouard@629: if( !rt_strcmp( optarg, "500K" ) ) return 2; edouard@629: if( !rt_strcmp( optarg, "250K" ) ) return 3; edouard@629: if( !rt_strcmp( optarg, "125K" ) ) return 4; edouard@629: if( !rt_strcmp( optarg, "100K" ) ) return 5; edouard@629: if( !rt_strcmp( optarg, "83.3K" ) ) return 6; edouard@629: if( !rt_strcmp( optarg, "10K" ) ) return 7; edouard@629: edouard@629: return -1; edouard@629: } edouard@629: edouard@629: /*********CO-PCICAN specific functions to communicate with the board**********/ edouard@629: typedef struct edouard@629: { edouard@629: char used; edouard@629: CAN_HANDLE fd; edouard@629: void* receiveTask; edouard@629: void* d; edouard@629: } CANPort; /* taken from drivers/unix.c */ edouard@629: edouard@629: comedi_t* get_dev_of_port( CAN_PORT port ) edouard@629: { edouard@629: CANPort *thisPort = (CANPort*)port; edouard@629: CAN_HANDLE thisHandle; edouard@629: comedi_t *dev; edouard@629: edouard@629: if( thisPort == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: get_dev_of_port(): thisPort is NULL\n"); edouard@629: return NULL; edouard@629: } edouard@629: edouard@629: thisHandle = thisPort->fd; edouard@629: edouard@629: if( thisHandle == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: get_dev_of_port(): thisHandle is NULL\n"); edouard@629: return NULL; edouard@629: } edouard@629: edouard@629: dev = (comedi_t*)thisHandle; edouard@629: edouard@629: /*MSG("can_copcican_comedi: get_dev_of_port(): handle is 0x%08x\n", (unsigned int)dev);*/ edouard@629: edouard@629: return dev; edouard@629: } edouard@629: edouard@629: int co_pcican_enter_run_mode( comedi_t *dev ) edouard@629: { edouard@629: unsigned int opcmd[15]; edouard@629: unsigned int opcode = CMDQ_OPC_ENTER_RUN_MODE; edouard@629: comedi_insn insn; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_enter_run_mode(): dev is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: memset( opcmd, 0x00, sizeof(opcmd) ); edouard@629: opcmd[0] = 0; edouard@629: edouard@629: memset( &insn, 0x00, sizeof(insn) ); edouard@629: insn.insn = INSN_CONFIG; edouard@629: insn.n = 1; edouard@629: insn.data = (void*)opcmd; edouard@629: insn.subdev = 0; edouard@629: insn.unused[0] = opcode; /* in use for CO-PCICAN */ edouard@629: edouard@629: comedi_do_insn( dev, &insn ); edouard@629: edouard@629: opcmd[0] = 2; edouard@629: insn.subdev = 2; edouard@629: insn.unused[0] = opcode; /* in use for CO-PCICAN */ edouard@629: edouard@629: return comedi_do_insn( dev, &insn ); edouard@629: } edouard@629: edouard@629: int co_pcican_enter_config_mode( comedi_t *dev ) edouard@629: { edouard@629: unsigned int opcmd[15]; edouard@629: unsigned int opcode = CMDQ_OPC_ENTER_CONFIG_MODE; edouard@629: comedi_insn insn; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_enter_config_mode(): dev is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: memset( opcmd, 0x00, sizeof(opcmd) ); edouard@629: opcmd[0] = 0; edouard@629: edouard@629: memset( &insn, 0x00, sizeof(insn) ); edouard@629: insn.insn = INSN_CONFIG; edouard@629: insn.n = 1; edouard@629: insn.data = (void*)opcmd; edouard@629: insn.subdev = 0; edouard@629: insn.unused[0] = opcode; /* in use for CO-PCICAN */ edouard@629: edouard@629: comedi_do_insn( dev, &insn ); edouard@629: edouard@629: opcmd[0] = 2; edouard@629: insn.subdev = 2; edouard@629: insn.unused[0] = opcode; /* in use for CO-PCICAN */ edouard@629: edouard@629: return comedi_do_insn( dev, &insn ); edouard@629: } edouard@629: edouard@629: int co_pcican_select_channel( const unsigned char channel, const unsigned int direction ) edouard@629: { edouard@629: if( channel >= NUM_CAN_CHANNELS ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_select_channel(): invalid channel\n"); edouard@629: return -1; edouard@629: } edouard@629: edouard@629: /* at the moment not threadsafe :-( */ edouard@629: switch( direction ) edouard@629: { edouard@629: case RX: selectedChannelRx = channel; edouard@629: break; edouard@629: case TX: selectedChannelTx = channel; edouard@629: break; edouard@629: default: return -1; edouard@629: } edouard@629: edouard@629: return 0; edouard@629: } edouard@629: edouard@629: int co_pcican_configure_selected_channel( comedi_t *dev, s_BOARD *board, const unsigned int direction ) edouard@629: { edouard@629: unsigned int opcmd[15]; edouard@629: unsigned int opcode = CMDQ_OPC_SET_CONFIG_CHANNEL; edouard@629: unsigned int selectedChannel; edouard@629: comedi_insn insn; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): dev is NULL\n"); edouard@629: return -1; edouard@629: } edouard@629: edouard@629: if( board == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): board is NULL\n"); edouard@629: return -1; edouard@629: } edouard@629: edouard@629: if( board->baudrate == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): baudrate is NULL\n"); edouard@629: return -1; edouard@629: } edouard@629: edouard@629: switch( direction ) edouard@629: { edouard@629: case RX: selectedChannel = selectedChannelRx; edouard@629: break; edouard@629: case TX: selectedChannel = selectedChannelTx; edouard@629: break; edouard@629: default: selectedChannel = 0xff; edouard@629: } edouard@629: edouard@629: if( selectedChannel >= NUM_CAN_CHANNELS ) edouard@629: { edouard@629: MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): invalid channel selected\n"); edouard@629: return -1; edouard@629: } edouard@629: edouard@629: memset( opcmd, 0x00, sizeof(opcmd) ); edouard@629: opcmd[0] = selectedChannel; edouard@629: opcmd[1] = TranslateBaudRate( board->baudrate ); edouard@629: edouard@629: memset( &insn, 0x00, sizeof(insn) ); edouard@629: insn.insn = INSN_CONFIG; edouard@629: insn.n = 1; edouard@629: insn.data = (void*)opcmd; edouard@629: edouard@629: if( selectedChannel < 2 ) edouard@629: insn.subdev = 0; edouard@629: else edouard@629: insn.subdev = 2; edouard@629: edouard@629: insn.unused[0] = opcode; /* in use for CO-PCICAN */ edouard@629: edouard@629: return comedi_do_insn( dev, &insn ); edouard@629: } edouard@629: edouard@629: /*********functions which permit to communicate with the board****************/ edouard@629: UNS8 canReceive_driver( CAN_HANDLE fd0, Message *m ) edouard@629: { edouard@629: MESSAGE_T canmsg; edouard@629: comedi_insn insn; edouard@629: UNS8 ret = 0; edouard@629: comedi_t *dev = (comedi_t*)fd0; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canReceive_driver(): dev is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: if( selectedChannelRx >= NUM_CAN_CHANNELS ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canReceive_driver(): invalid channel selected\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: if( m == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canReceive_driver(): message is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: memset( &canmsg, 0x00, sizeof(MESSAGE_T) ); edouard@629: edouard@629: memset( &insn, 0x00, sizeof(insn) ); edouard@629: insn.insn = INSN_READ; edouard@629: insn.n = 1; edouard@629: insn.data = (void*)&canmsg; edouard@629: edouard@629: if( selectedChannelRx < 2 ) edouard@629: insn.subdev = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DI, 0); edouard@629: else edouard@629: insn.subdev = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DI, 2); edouard@629: edouard@629: insn.unused[1] = selectedChannelRx; /* in use for CO-PCICAN */ edouard@629: insn.unused[2] = sizeof(MESSAGE_T); /* in use for CO-PCICAN */ edouard@629: edouard@629: comedi_do_insn( dev, &insn ); edouard@629: edouard@629: if( canmsg.timestamp_lo == 0 ) edouard@629: { edouard@629: memset( m, 0x00, sizeof(Message) ); edouard@629: edouard@629: m->cob_id = 0xffff; /* set to invalid so nothing happens */ edouard@629: } edouard@629: else edouard@629: { edouard@629: m->len = canmsg.size; edouard@629: m->cob_id = canmsg.id; edouard@629: m->rtr = canmsg.type & MSG_RTR; edouard@629: edouard@629: if( !m->rtr ) edouard@629: { edouard@629: /* this is for safety */ edouard@629: if( m->len > 8 ) edouard@629: m->len = 8; edouard@629: edouard@629: memcpy( m->data, canmsg.data, m->len); edouard@629: } edouard@629: } edouard@629: edouard@629: return ret; edouard@629: } edouard@629: edouard@629: /***************************************************************************/ edouard@629: UNS8 canSend_driver( CAN_HANDLE fd0, Message *m ) edouard@629: { edouard@629: MESSAGE_T canmsg; edouard@629: comedi_insn insn; edouard@629: UNS8 ret = 0; edouard@629: comedi_t *dev = (comedi_t*)fd0; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canSend_driver(): dev is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: if( selectedChannelTx >= NUM_CAN_CHANNELS ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canSend_driver(): invalid channel selected\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: if( m == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canSend_driver(): message is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: memset( &canmsg, 0x00, sizeof(MESSAGE_T) ); edouard@629: canmsg.size = m->len; edouard@629: canmsg.id = m->cob_id; edouard@629: edouard@629: if( canmsg.id >= 0x800 ) edouard@629: canmsg.type |= MSG_EXT; edouard@629: edouard@629: if( m->rtr ) edouard@629: { edouard@629: canmsg.type |= MSG_RTR; edouard@629: } edouard@629: else edouard@629: { edouard@629: /* this is for safety */ edouard@629: if( canmsg.size > 8 ) edouard@629: canmsg.size = 8; edouard@629: edouard@629: memcpy( canmsg.data, m->data, canmsg.size); edouard@629: } edouard@629: edouard@629: memset( &insn, 0x00, sizeof(insn) ); edouard@629: insn.insn = INSN_WRITE; edouard@629: insn.n = 1; edouard@629: insn.data = (void*)&canmsg; edouard@629: edouard@629: if( selectedChannelTx < 2 ) edouard@629: insn.subdev = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DO, 0); edouard@629: else edouard@629: insn.subdev = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DO, 2); edouard@629: edouard@629: insn.unused[1] = selectedChannelTx; /* in use for CO-PCICAN */ edouard@629: insn.unused[2] = sizeof(MESSAGE_T) - sizeof(unsigned long); /* in use for CO-PCICAN, no timestamp for tx */ edouard@629: edouard@629: if( comedi_do_insn( dev, &insn ) < 0 ) edouard@629: ret = 1; edouard@629: edouard@629: return ret; edouard@629: } edouard@629: edouard@629: /***************************************************************************/ edouard@629: CAN_HANDLE canOpen_driver( s_BOARD *board ) edouard@629: { edouard@629: comedi_t *dev; edouard@629: edouard@629: if( board == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canOpen_driver(): board is NULL\n"); edouard@629: return NULL; edouard@629: } edouard@629: edouard@629: if( board->busname == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canOpen_driver(): busname is NULL\n"); edouard@629: return NULL; edouard@629: } edouard@629: edouard@629: dev = comedi_open( board->busname ); edouard@629: edouard@629: /*MSG("can_copcican_comedi: canOpen_driver(): handle is 0x%08x\n", (unsigned int)dev);*/ edouard@629: edouard@629: return dev; edouard@629: } edouard@629: edouard@629: /***************************************************************************/ edouard@629: int canClose_driver( CAN_HANDLE fd0 ) edouard@629: { edouard@629: comedi_t *dev = (comedi_t*)fd0; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canClose_driver(): dev is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: comedi_close( dev ); edouard@629: edouard@629: selectedChannelRx = 0; edouard@629: selectedChannelTx = 0; edouard@629: edouard@629: return 0; edouard@629: } edouard@629: edouard@629: /***************************************************************************/ edouard@629: UNS8 canChangeBaudRate_driver( CAN_HANDLE fd0, char* baud ) edouard@629: { edouard@629: s_BOARD board; edouard@629: UNS8 ret = 0; edouard@629: comedi_t *dev = (comedi_t*)fd0; edouard@629: edouard@629: if( dev == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canChangeBaudRate_driver(): dev is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: if( baud == NULL ) edouard@629: { edouard@629: MSG("can_copcican_comedi: canChangeBaudRate_driver(): baud is NULL\n"); edouard@629: return 1; edouard@629: } edouard@629: edouard@629: memset( &board, 0x00, sizeof(s_BOARD) ); edouard@629: board.baudrate = baud; edouard@629: edouard@629: if( co_pcican_configure_selected_channel( dev, &board, RX ) < 0 ) edouard@629: ret = 1; edouard@629: edouard@629: if( co_pcican_configure_selected_channel( dev, &board, TX ) < 0 ) edouard@629: ret = 1; edouard@629: edouard@629: return ret; edouard@629: } edouard@629: edouard@629: static int init(void) edouard@629: { edouard@629: printk("can_copcican_comedi for CanFestival loaded\n"); edouard@629: edouard@629: return 0; edouard@629: } edouard@629: edouard@629: static void exit(void) edouard@629: { edouard@629: printk("can_copcican_comedi unloaded\n"); edouard@629: } edouard@629: edouard@629: module_init(init); edouard@629: module_exit(exit); edouard@629: edouard@629: EXPORT_SYMBOL(get_dev_of_port); edouard@629: EXPORT_SYMBOL(co_pcican_enter_run_mode); edouard@629: EXPORT_SYMBOL(co_pcican_enter_config_mode); edouard@629: EXPORT_SYMBOL(co_pcican_select_channel); edouard@629: EXPORT_SYMBOL(co_pcican_configure_selected_channel); edouard@629: EXPORT_SYMBOL(canOpen_driver); edouard@629: EXPORT_SYMBOL(canClose_driver); edouard@629: EXPORT_SYMBOL(canSend_driver); edouard@629: EXPORT_SYMBOL(canReceive_driver); edouard@629: EXPORT_SYMBOL(canChangeBaudRate_driver);