drivers/can_copcican_comedi/can_copcican_comedi.c
changeset 629 b9274b595650
equal deleted inserted replaced
628:9e496a2aadca 629:b9274b595650
       
     1 /*
       
     2 This file is part of CanFestival, a library implementing CanOpen Stack.
       
     3 
       
     4 Copyright (C): Cosateq GmbH & Co.KG
       
     5                http://www.cosateq.com/
       
     6                http://www.scale-rt.com/
       
     7 
       
     8 See COPYING file for copyrights details.
       
     9 
       
    10 This library is free software; you can redistribute it and/or
       
    11 modify it under the terms of the GNU Lesser General Public
       
    12 License as published by the Free Software Foundation; either
       
    13 version 2.1 of the License, or (at your option) any later version.
       
    14 
       
    15 This library is distributed in the hope that it will be useful,
       
    16 but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    18 Lesser General Public License for more details.
       
    19 
       
    20 You should have received a copy of the GNU Lesser General Public
       
    21 License along with this library; if not, write to the Free Software
       
    22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    23 */
       
    24 
       
    25 /*
       
    26 	COMEDI interface for CO-PCICAN card.
       
    27 */
       
    28 
       
    29 #include <linux/module.h>
       
    30 #include <linux/comedi.h>
       
    31 #include <linux/comedilib.h>
       
    32 
       
    33 #define NEED_PRINT_MESSAGE
       
    34 #include "can_driver.h"
       
    35 #include "def.h"
       
    36 #include "co_pcicanops.h"
       
    37 
       
    38 MODULE_LICENSE("GPL");
       
    39 
       
    40 /* at the moment not threadsafe :-( */
       
    41 static unsigned char selectedChannelRx = 0, selectedChannelTx = 0;
       
    42 
       
    43 static int rt_strcmp( const char *src, const char *dst )
       
    44 {
       
    45   int ret = 0;
       
    46 
       
    47   while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
       
    48   {
       
    49     src++;
       
    50     dst++;
       
    51   }
       
    52 
       
    53   if( ret < 0 )
       
    54   {
       
    55     ret = -1 ;
       
    56   }
       
    57   else
       
    58   {
       
    59     if( ret > 0 )
       
    60       ret = 1 ;
       
    61   }
       
    62 
       
    63   return ret;
       
    64 }
       
    65 
       
    66 static int TranslateBaudRate( char* optarg )
       
    67 {
       
    68   /* values see documentation of CO-PCICAN */
       
    69   if( !rt_strcmp( optarg, "1M"    ) ) return 0;
       
    70   if( !rt_strcmp( optarg, "800K"  ) ) return 1;
       
    71   if( !rt_strcmp( optarg, "500K"  ) ) return 2;
       
    72   if( !rt_strcmp( optarg, "250K"  ) ) return 3;
       
    73   if( !rt_strcmp( optarg, "125K"  ) ) return 4;
       
    74   if( !rt_strcmp( optarg, "100K"  ) ) return 5;
       
    75   if( !rt_strcmp( optarg, "83.3K" ) ) return 6;
       
    76   if( !rt_strcmp( optarg, "10K"   ) ) return 7;
       
    77 
       
    78   return -1;
       
    79 }
       
    80 
       
    81 /*********CO-PCICAN specific functions to communicate with the board**********/
       
    82 typedef struct
       
    83 {
       
    84   char used;
       
    85   CAN_HANDLE fd;
       
    86   void* receiveTask;
       
    87   void* d;
       
    88 } CANPort; /* taken from drivers/unix.c */
       
    89 
       
    90 comedi_t* get_dev_of_port( CAN_PORT port )
       
    91 {
       
    92   CANPort *thisPort = (CANPort*)port;
       
    93   CAN_HANDLE thisHandle;
       
    94   comedi_t *dev;
       
    95 
       
    96   if( thisPort == NULL )
       
    97   {
       
    98     MSG("can_copcican_comedi: get_dev_of_port(): thisPort is NULL\n");
       
    99     return NULL;
       
   100   }
       
   101 
       
   102   thisHandle = thisPort->fd;
       
   103 
       
   104   if( thisHandle == NULL )
       
   105   {
       
   106     MSG("can_copcican_comedi: get_dev_of_port(): thisHandle is NULL\n");
       
   107     return NULL;
       
   108   }
       
   109 
       
   110   dev = (comedi_t*)thisHandle;
       
   111 
       
   112   /*MSG("can_copcican_comedi: get_dev_of_port(): handle is 0x%08x\n", (unsigned int)dev);*/
       
   113 
       
   114   return dev;
       
   115 }
       
   116 
       
   117 int co_pcican_enter_run_mode( comedi_t *dev )
       
   118 {
       
   119   unsigned int opcmd[15];
       
   120   unsigned int opcode = CMDQ_OPC_ENTER_RUN_MODE;
       
   121   comedi_insn insn;
       
   122 
       
   123   if( dev == NULL )
       
   124   {
       
   125     MSG("can_copcican_comedi: co_pcican_enter_run_mode(): dev is NULL\n");
       
   126     return 1;
       
   127   }
       
   128 
       
   129   memset( opcmd, 0x00, sizeof(opcmd) );
       
   130   opcmd[0] = 0;
       
   131 
       
   132   memset( &insn, 0x00, sizeof(insn) );
       
   133   insn.insn      = INSN_CONFIG;
       
   134   insn.n         = 1;
       
   135   insn.data      = (void*)opcmd;
       
   136   insn.subdev    = 0;
       
   137   insn.unused[0] = opcode; /* in use for CO-PCICAN */
       
   138 
       
   139   comedi_do_insn( dev, &insn );
       
   140 
       
   141   opcmd[0] = 2;
       
   142   insn.subdev    = 2;
       
   143   insn.unused[0] = opcode; /* in use for CO-PCICAN */
       
   144 
       
   145   return comedi_do_insn( dev, &insn );
       
   146 }
       
   147 
       
   148 int co_pcican_enter_config_mode( comedi_t *dev )
       
   149 {
       
   150   unsigned int opcmd[15];
       
   151   unsigned int opcode = CMDQ_OPC_ENTER_CONFIG_MODE;
       
   152   comedi_insn insn;
       
   153 
       
   154   if( dev == NULL )
       
   155   {
       
   156     MSG("can_copcican_comedi: co_pcican_enter_config_mode(): dev is NULL\n");
       
   157     return 1;
       
   158   }
       
   159 
       
   160   memset( opcmd, 0x00, sizeof(opcmd) );
       
   161   opcmd[0] = 0;
       
   162 
       
   163   memset( &insn, 0x00, sizeof(insn) );
       
   164   insn.insn      = INSN_CONFIG;
       
   165   insn.n         = 1;
       
   166   insn.data      = (void*)opcmd;
       
   167   insn.subdev    = 0;
       
   168   insn.unused[0] = opcode; /* in use for CO-PCICAN */
       
   169 
       
   170   comedi_do_insn( dev, &insn );
       
   171 
       
   172   opcmd[0] = 2;
       
   173   insn.subdev    = 2;
       
   174   insn.unused[0] = opcode; /* in use for CO-PCICAN */
       
   175 
       
   176   return comedi_do_insn( dev, &insn );
       
   177 }
       
   178 
       
   179 int co_pcican_select_channel( const unsigned char channel, const unsigned int direction )
       
   180 {
       
   181   if( channel >= NUM_CAN_CHANNELS )
       
   182   {
       
   183     MSG("can_copcican_comedi: co_pcican_select_channel(): invalid channel\n");
       
   184     return -1;
       
   185   }
       
   186 
       
   187   /* at the moment not threadsafe :-( */
       
   188   switch( direction )
       
   189   {
       
   190     case RX: selectedChannelRx = channel;
       
   191              break;
       
   192     case TX: selectedChannelTx = channel;
       
   193              break;
       
   194     default: return -1;
       
   195   }
       
   196 
       
   197   return 0;
       
   198 }
       
   199 
       
   200 int co_pcican_configure_selected_channel( comedi_t *dev, s_BOARD *board, const unsigned int direction )
       
   201 {
       
   202   unsigned int opcmd[15];
       
   203   unsigned int opcode = CMDQ_OPC_SET_CONFIG_CHANNEL;
       
   204   unsigned int selectedChannel;
       
   205   comedi_insn insn;
       
   206 
       
   207   if( dev == NULL )
       
   208   {
       
   209     MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): dev is NULL\n");
       
   210     return -1;
       
   211   }
       
   212 
       
   213   if( board == NULL )
       
   214   {
       
   215     MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): board is NULL\n");
       
   216     return -1;
       
   217   }
       
   218 
       
   219   if( board->baudrate == NULL )
       
   220   {
       
   221     MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): baudrate is NULL\n");
       
   222     return -1;
       
   223   }
       
   224 
       
   225   switch( direction )
       
   226   {
       
   227     case RX: selectedChannel = selectedChannelRx;
       
   228              break;
       
   229     case TX: selectedChannel = selectedChannelTx;
       
   230              break;
       
   231     default: selectedChannel = 0xff;
       
   232   }
       
   233 
       
   234   if( selectedChannel >= NUM_CAN_CHANNELS )
       
   235   {
       
   236     MSG("can_copcican_comedi: co_pcican_configure_selected_channel(): invalid channel selected\n");
       
   237     return -1;
       
   238   }
       
   239 
       
   240   memset( opcmd, 0x00, sizeof(opcmd) );
       
   241   opcmd[0] = selectedChannel;
       
   242   opcmd[1] = TranslateBaudRate( board->baudrate );
       
   243 
       
   244   memset( &insn, 0x00, sizeof(insn) );
       
   245   insn.insn      = INSN_CONFIG;
       
   246   insn.n         = 1;
       
   247   insn.data      = (void*)opcmd;
       
   248 
       
   249   if( selectedChannel < 2 )
       
   250     insn.subdev    = 0;
       
   251   else
       
   252     insn.subdev    = 2;
       
   253 
       
   254   insn.unused[0] = opcode; /* in use for CO-PCICAN */
       
   255 
       
   256   return comedi_do_insn( dev, &insn );
       
   257 }
       
   258 
       
   259 /*********functions which permit to communicate with the board****************/
       
   260 UNS8 canReceive_driver( CAN_HANDLE fd0, Message *m )
       
   261 {
       
   262   MESSAGE_T canmsg;
       
   263   comedi_insn insn;
       
   264   UNS8 ret = 0;
       
   265   comedi_t *dev = (comedi_t*)fd0;
       
   266 
       
   267   if( dev == NULL )
       
   268   {
       
   269     MSG("can_copcican_comedi: canReceive_driver(): dev is NULL\n");
       
   270     return 1;
       
   271   }
       
   272 
       
   273   if( selectedChannelRx >= NUM_CAN_CHANNELS )
       
   274   {
       
   275     MSG("can_copcican_comedi: canReceive_driver(): invalid channel selected\n");
       
   276     return 1;
       
   277   }
       
   278 
       
   279   if( m == NULL )
       
   280   {
       
   281     MSG("can_copcican_comedi: canReceive_driver(): message is NULL\n");
       
   282     return 1;
       
   283   }
       
   284 
       
   285   memset( &canmsg, 0x00, sizeof(MESSAGE_T) );
       
   286 
       
   287   memset( &insn, 0x00, sizeof(insn) );
       
   288   insn.insn      = INSN_READ;
       
   289   insn.n         = 1;
       
   290   insn.data      = (void*)&canmsg;
       
   291 
       
   292   if( selectedChannelRx < 2 )
       
   293     insn.subdev    = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DI, 0);
       
   294   else
       
   295     insn.subdev    = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DI, 2);
       
   296 
       
   297   insn.unused[1] = selectedChannelRx; /* in use for CO-PCICAN */
       
   298   insn.unused[2] = sizeof(MESSAGE_T); /* in use for CO-PCICAN */
       
   299 
       
   300   comedi_do_insn( dev, &insn );
       
   301 
       
   302   if( canmsg.timestamp_lo == 0 )
       
   303   {
       
   304     memset( m, 0x00, sizeof(Message) );
       
   305 
       
   306     m->cob_id = 0xffff; /* set to invalid so nothing happens */
       
   307   }
       
   308   else
       
   309   {
       
   310     m->len = canmsg.size;
       
   311     m->cob_id = canmsg.id;
       
   312     m->rtr = canmsg.type & MSG_RTR;
       
   313 
       
   314     if( !m->rtr )
       
   315     {
       
   316       /* this is for safety */
       
   317       if( m->len > 8 )
       
   318         m->len = 8;
       
   319 
       
   320       memcpy( m->data, canmsg.data, m->len);
       
   321     }
       
   322   }
       
   323 
       
   324   return ret;
       
   325 }
       
   326 
       
   327 /***************************************************************************/
       
   328 UNS8 canSend_driver( CAN_HANDLE fd0, Message *m )
       
   329 {
       
   330   MESSAGE_T canmsg;
       
   331   comedi_insn insn;
       
   332   UNS8 ret = 0;
       
   333   comedi_t *dev = (comedi_t*)fd0;
       
   334 
       
   335   if( dev == NULL )
       
   336   {
       
   337     MSG("can_copcican_comedi: canSend_driver(): dev is NULL\n");
       
   338     return 1;
       
   339   }
       
   340 
       
   341   if( selectedChannelTx >= NUM_CAN_CHANNELS )
       
   342   {
       
   343     MSG("can_copcican_comedi: canSend_driver(): invalid channel selected\n");
       
   344     return 1;
       
   345   }
       
   346 
       
   347   if( m == NULL )
       
   348   {
       
   349     MSG("can_copcican_comedi: canSend_driver(): message is NULL\n");
       
   350     return 1;
       
   351   }
       
   352 
       
   353   memset( &canmsg, 0x00, sizeof(MESSAGE_T) );
       
   354   canmsg.size = m->len;
       
   355   canmsg.id = m->cob_id;
       
   356 
       
   357   if( canmsg.id >= 0x800 )
       
   358     canmsg.type |= MSG_EXT;
       
   359 
       
   360   if( m->rtr )
       
   361   {
       
   362     canmsg.type |= MSG_RTR;
       
   363   }
       
   364   else
       
   365   {
       
   366     /* this is for safety */
       
   367     if( canmsg.size > 8 )
       
   368       canmsg.size = 8;
       
   369 
       
   370     memcpy( canmsg.data, m->data, canmsg.size);
       
   371   }
       
   372 
       
   373   memset( &insn, 0x00, sizeof(insn) );
       
   374   insn.insn      = INSN_WRITE;
       
   375   insn.n         = 1;
       
   376   insn.data      = (void*)&canmsg;
       
   377 
       
   378   if( selectedChannelTx < 2 )
       
   379     insn.subdev    = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DO, 0);
       
   380   else
       
   381     insn.subdev    = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DO, 2);
       
   382 
       
   383   insn.unused[1] = selectedChannelTx; /* in use for CO-PCICAN */
       
   384   insn.unused[2] = sizeof(MESSAGE_T) - sizeof(unsigned long); /* in use for CO-PCICAN, no timestamp for tx */
       
   385 
       
   386   if( comedi_do_insn( dev, &insn ) < 0 )
       
   387     ret = 1;
       
   388 
       
   389   return ret;
       
   390 }
       
   391 
       
   392 /***************************************************************************/
       
   393 CAN_HANDLE canOpen_driver( s_BOARD *board )
       
   394 {
       
   395   comedi_t *dev;
       
   396 
       
   397   if( board == NULL )
       
   398   {
       
   399     MSG("can_copcican_comedi: canOpen_driver(): board is NULL\n");
       
   400     return NULL;
       
   401   }
       
   402 
       
   403   if( board->busname == NULL )
       
   404   {
       
   405     MSG("can_copcican_comedi: canOpen_driver(): busname is NULL\n");
       
   406     return NULL;
       
   407   }
       
   408 
       
   409   dev = comedi_open( board->busname );
       
   410 
       
   411   /*MSG("can_copcican_comedi: canOpen_driver(): handle is 0x%08x\n", (unsigned int)dev);*/
       
   412 
       
   413   return dev;
       
   414 }
       
   415 
       
   416 /***************************************************************************/
       
   417 int canClose_driver( CAN_HANDLE fd0 )
       
   418 {
       
   419   comedi_t *dev = (comedi_t*)fd0;
       
   420 
       
   421   if( dev == NULL )
       
   422   {
       
   423     MSG("can_copcican_comedi: canClose_driver(): dev is NULL\n");
       
   424     return 1;
       
   425   }
       
   426 
       
   427   comedi_close( dev );
       
   428 
       
   429   selectedChannelRx = 0;
       
   430   selectedChannelTx = 0;
       
   431 
       
   432   return 0;
       
   433 }
       
   434 
       
   435 /***************************************************************************/
       
   436 UNS8 canChangeBaudRate_driver( CAN_HANDLE fd0, char* baud )
       
   437 {
       
   438   s_BOARD board;
       
   439   UNS8 ret = 0;
       
   440   comedi_t *dev = (comedi_t*)fd0;
       
   441 
       
   442   if( dev == NULL )
       
   443   {
       
   444     MSG("can_copcican_comedi: canChangeBaudRate_driver(): dev is NULL\n");
       
   445     return 1;
       
   446   }
       
   447 
       
   448   if( baud == NULL )
       
   449   {
       
   450     MSG("can_copcican_comedi: canChangeBaudRate_driver(): baud is NULL\n");
       
   451     return 1;
       
   452   }
       
   453 
       
   454   memset( &board, 0x00, sizeof(s_BOARD) );
       
   455   board.baudrate = baud;
       
   456 
       
   457   if( co_pcican_configure_selected_channel( dev, &board, RX ) < 0 )
       
   458     ret = 1;
       
   459 
       
   460   if( co_pcican_configure_selected_channel( dev, &board, TX ) < 0 )
       
   461     ret = 1;
       
   462 
       
   463   return ret;
       
   464 }
       
   465 
       
   466 static int init(void)
       
   467 {
       
   468   printk("can_copcican_comedi for CanFestival loaded\n");
       
   469 
       
   470   return 0;
       
   471 }
       
   472 
       
   473 static void exit(void)
       
   474 {
       
   475   printk("can_copcican_comedi unloaded\n");
       
   476 }
       
   477 
       
   478 module_init(init);
       
   479 module_exit(exit);
       
   480 
       
   481 EXPORT_SYMBOL(get_dev_of_port);
       
   482 EXPORT_SYMBOL(co_pcican_enter_run_mode);
       
   483 EXPORT_SYMBOL(co_pcican_enter_config_mode);
       
   484 EXPORT_SYMBOL(co_pcican_select_channel);
       
   485 EXPORT_SYMBOL(co_pcican_configure_selected_channel);
       
   486 EXPORT_SYMBOL(canOpen_driver);
       
   487 EXPORT_SYMBOL(canClose_driver);
       
   488 EXPORT_SYMBOL(canSend_driver);
       
   489 EXPORT_SYMBOL(canReceive_driver);
       
   490 EXPORT_SYMBOL(canChangeBaudRate_driver);