⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcan_sja1000.c

📁 linux下的CAN BUS驱动代码。适合在arm平台使用。
💻 C
📖 第 1 页 / 共 2 页
字号:
//****************************************************************************// Copyright (C) 2001-2007  PEAK System-Technik GmbH//// linux@peak-system.com// www.peak-system.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; either version 2 of the License, or// (at your option) any later version.//// This program 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 this program; if not, write to the Free Software// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.//// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)//// Major contributions by://                Edouard Tisserant (edouard.tisserant@lolitech.fr) XENOMAI//                Laurent Bessard   (laurent.bessard@lolitech.fr)   XENOMAI//                Oliver Hartkopp   (oliver.hartkopp@volkswagen.de) socketCAN//// Contributions: Arnaud Westenberg (arnaud@wanadoo.nl)//                Matt Waters (Matt.Waters@dynetics.com)//                Benjamin Kolb (Benjamin.Kolb@bigfoot.de)//****************************************************************************//****************************************************************************//// pcan_sja1000.c - all about sja1000 init and data handling//// $Id: pcan_sja1000.c 527 2007-11-04 15:02:32Z khitschler $////****************************************************************************//****************************************************************************// INCLUDES#include <src/pcan_common.h>#include <linux/sched.h>#include <asm/errno.h>#include <asm/byteorder.h>  // because of little / big endian#include <linux/delay.h>#include <src/pcan_main.h>#include <src/pcan_fifo.h>#include <src/pcan_sja1000.h>#ifdef NETDEV_SUPPORT#include <src/pcan_netdev.h>#endif//****************************************************************************// DEFINES// sja1000 registers, only PELICAN mode - TUX like it#define MODE                   0      // mode register#define COMMAND                1#define CHIPSTATUS             2#define INTERRUPT_STATUS       3#define INTERRUPT_ENABLE       4      // acceptance code#define TIMING0                6      // bus timing 0#define TIMING1                7      // bus timing 1#define OUTPUT_CONTROL         8      // output control#define TESTREG                9#define ARBIT_LOST_CAPTURE    11      // transmit buffer: Identifier#define ERROR_CODE_CAPTURE    12      // RTR bit und data length code#define ERROR_WARNING_LIMIT   13      // start byte of data field#define RX_ERROR_COUNTER      14#define TX_ERROR_COUNTER      15#define ACCEPTANCE_CODE_BASE  16#define RECEIVE_FRAME_BASE    16#define TRANSMIT_FRAME_BASE   16#define ACCEPTANCE_MASK_BASE  20#define RECEIVE_MSG_COUNTER   29#define RECEIVE_START_ADDRESS 30#define CLKDIVIDER            31      // set bit rate and pelican mode// important sja1000 register contents, MODE register#define SLEEP_MODE             0x10#define ACCEPT_FILTER_MODE     0x08#define SELF_TEST_MODE         0x04#define LISTEN_ONLY_MODE       0x02#define RESET_MODE             0x01#define NORMAL_MODE            0x00// COMMAND register#define CLEAR_DATA_OVERRUN     0x08#define RELEASE_RECEIVE_BUFFER 0x04#define ABORT_TRANSMISSION     0x02#define TRANSMISSION_REQUEST   0x01// CHIPSTATUS register#define BUS_STATUS             0x80#define ERROR_STATUS           0x40#define TRANSMIT_STATUS        0x20#define RECEIVE_STATUS         0x10#define TRANS_COMPLETE_STATUS  0x08#define TRANS_BUFFER_STATUS    0x04#define DATA_OVERRUN_STATUS    0x02#define RECEIVE_BUFFER_STATUS  0x01// INTERRUPT STATUS register#define BUS_ERROR_INTERRUPT    0x80#define ARBIT_LOST_INTERRUPT   0x40#define ERROR_PASSIV_INTERRUPT 0x20#define WAKE_UP_INTERRUPT      0x10#define DATA_OVERRUN_INTERRUPT 0x08#define ERROR_WARN_INTERRUPT   0x04#define TRANSMIT_INTERRUPT     0x02#define RECEIVE_INTERRUPT      0x01// INTERRUPT ENABLE register#define BUS_ERROR_INTERRUPT_ENABLE    0x80#define ARBIT_LOST_INTERRUPT_ENABLE   0x40#define ERROR_PASSIV_INTERRUPT_ENABLE 0x20#define WAKE_UP_INTERRUPT_ENABLE      0x10#define DATA_OVERRUN_INTERRUPT_ENABLE 0x08#define ERROR_WARN_INTERRUPT_ENABLE   0x04#define TRANSMIT_INTERRUPT_ENABLE     0x02#define RECEIVE_INTERRUPT_ENABLE      0x01// OUTPUT CONTROL register#define OUTPUT_CONTROL_TRANSISTOR_P1  0x80#define OUTPUT_CONTROL_TRANSISTOR_N1  0x40#define OUTPUT_CONTROL_POLARITY_1     0x20#define OUTPUT_CONTROL_TRANSISTOR_P0  0x10#define OUTPUT_CONTROL_TRANSISTOR_N0  0x08#define OUTPUT_CONTROL_POLARITY_0     0x04#define OUTPUT_CONTROL_MODE_1         0x02#define OUTPUT_CONTROL_MODE_0         0x01// TRANSMIT or RECEIVE BUFFER#define BUFFER_EFF                    0x80 // set for 29 bit identifier#define BUFFER_RTR                    0x40 // set for RTR request#define BUFFER_DLC_MASK               0x0f// CLKDIVIDER register#define CAN_MODE                      0x80#define CAN_BYPASS                    0x40#define RXINT_OUTPUT_ENABLE           0x20#define CLOCK_OFF                     0x08#define CLOCK_DIVIDER_MASK            0x07// additional informations#define CLOCK_HZ                  16000000 // crystal frequency// time for mode register to change mode#define MODE_REGISTER_SWITCH_TIME 100 // msec // some CLKDIVIDER register contents, hardware architecture dependend #define PELICAN_SINGLE  (CAN_MODE | CAN_BYPASS | 0x07 | CLOCK_OFF)#define PELICAN_MASTER  (CAN_MODE | CAN_BYPASS | 0x07            )#define PELICAN_DEFAULT (CAN_MODE                                )#define CHIP_RESET      PELICAN_SINGLE // hardware depended setup for OUTPUT_CONTROL register#define OUTPUT_CONTROL_SETUP (OUTPUT_CONTROL_TRANSISTOR_P0 | OUTPUT_CONTROL_TRANSISTOR_N0 | OUTPUT_CONTROL_MODE_1)// the interrupt enables#define INTERRUPT_ENABLE_SETUP (RECEIVE_INTERRUPT_ENABLE | TRANSMIT_INTERRUPT_ENABLE | DATA_OVERRUN_INTERRUPT_ENABLE | BUS_ERROR_INTERRUPT_ENABLE | ERROR_PASSIV_INTERRUPT_ENABLE | ERROR_WARN_INTERRUPT_ENABLE)// the maximum number of handled messages in one interrupt #define MAX_MESSAGES_PER_INTERRUPT 8// the maximum number of handled sja1000 interrupts in 1 handler entry#define MAX_INTERRUPTS_PER_ENTRY   4// constants from Arnaud Westenberg email:arnaud@wanadoo.nl#define MAX_TSEG1  15#define MAX_TSEG2  7#define BTR1_SAM   (1<<1)//****************************************************************************// GLOBALS//****************************************************************************// LOCALS//****************************************************************************// CODE#ifdef NO_RT  #include "pcan_sja1000_linux.c"#else  #include "pcan_sja1000_rt.c"#endif//----------------------------------------------------------------------------// switches the chip into reset modestatic int set_reset_mode(struct pcandev *dev){  u32 dwStart = get_mtime();  u8  tmp;  tmp = dev->readreg(dev, MODE);  while (!(tmp & RESET_MODE) && ((get_mtime() - dwStart) < MODE_REGISTER_SWITCH_TIME))  {    dev->writereg(dev, MODE, RESET_MODE); // force into reset mode    wmb();    udelay(1);    //schedule();    tmp = dev->readreg(dev, MODE);  }  if (!(tmp & RESET_MODE))    return -EIO;  else    return 0;}//----------------------------------------------------------------------------// switches the chip back from reset modestatic int set_normal_mode(struct pcandev *dev, u8 ucModifier){  u32 dwStart = get_mtime();  u8  tmp;  tmp = dev->readreg(dev, MODE);  while ((tmp != ucModifier) && ((get_mtime() - dwStart) < MODE_REGISTER_SWITCH_TIME))  {    dev->writereg(dev, MODE, ucModifier); // force into normal mode    wmb();    udelay(1);    //schedule();    tmp = dev->readreg(dev, MODE);  }  if (tmp != ucModifier)    return -EIO;  else    return 0;} //----------------------------------------------------------------------------// interrupt enable and disablestatic inline void sja1000_irq_enable(struct pcandev *dev){  dev->writereg(dev, INTERRUPT_ENABLE, INTERRUPT_ENABLE_SETUP);}static inline void sja1000_irq_disable(struct pcandev *dev){  dev->writereg(dev, INTERRUPT_ENABLE, 0);}//----------------------------------------------------------------------------// find the proper clock dividerstatic inline u8 clkdivider(struct pcandev *dev){  if (!dev->props.ucExternalClock)  // crystal based    return PELICAN_DEFAULT;  // configure clock divider register, switch into pelican mode, depended of of type  switch (dev->props.ucMasterDevice)  {    case CHANNEL_SLAVE:    case CHANNEL_SINGLE:      return PELICAN_SINGLE;  // neither a slave nor a single device distribute the clock ...    default:      return PELICAN_MASTER;  // ... but a master does  }}//----------------------------------------------------------------------------// init CAN-chipint sja1000_open(struct pcandev *dev, u16 btr0btr1, u8 bExtended, u8 bListenOnly){  int result      = 0;  u8  _clkdivider = clkdivider(dev);  u8  ucModifier  = (bListenOnly) ? LISTEN_ONLY_MODE : NORMAL_MODE;  DPRINTK(KERN_DEBUG "%s: sja1000_open(), minor = %d.\n", DEVICE_NAME, dev->nMinor);  // switch to reset   result = set_reset_mode(dev);  if (result)    goto fail;  // take a fresh status  dev->wCANStatus = 0;  // store extended mode (standard still accepted)  dev->bExtended = bExtended;  // configure clock divider register, switch into pelican mode, depended of of type  dev->writereg(dev, CLKDIVIDER, _clkdivider);  // configure acceptance code registers  dev->writereg(dev, ACCEPTANCE_CODE_BASE,     0);  dev->writereg(dev, ACCEPTANCE_CODE_BASE + 1, 0);  dev->writereg(dev, ACCEPTANCE_CODE_BASE + 2, 0);  dev->writereg(dev, ACCEPTANCE_CODE_BASE + 3, 0);  // configure all acceptance mask registers to don't care  dev->writereg(dev, ACCEPTANCE_MASK_BASE,     0xff);  dev->writereg(dev, ACCEPTANCE_MASK_BASE + 1, 0xff);  dev->writereg(dev, ACCEPTANCE_MASK_BASE + 2, 0xff);  dev->writereg(dev, ACCEPTANCE_MASK_BASE + 3, 0xff);  // configure bus timing registers  dev->writereg(dev, TIMING0, (u8)((btr0btr1 >> 8) & 0xff));  dev->writereg(dev, TIMING1, (u8)((btr0btr1     ) & 0xff));  // configure output control registers  dev->writereg(dev, OUTPUT_CONTROL, OUTPUT_CONTROL_SETUP);  // clear any pending interrupt  dev->readreg(dev, INTERRUPT_STATUS);  // enter normal operating mode  result = set_normal_mode(dev, ucModifier);  if (result)    goto fail;  // enable CAN interrupts  sja1000_irq_enable(dev);  fail:  return result;}//----------------------------------------------------------------------------// release CAN-chipvoid sja1000_release(struct pcandev *dev){  DPRINTK(KERN_DEBUG "%s: sja1000_release()\n", DEVICE_NAME);  // abort pending transmissions  dev->writereg(dev, COMMAND, ABORT_TRANSMISSION);  // disable CAN interrupts and set chip in reset mode  sja1000_irq_disable(dev);  set_reset_mode(dev);}//----------------------------------------------------------------------------// read CAN-data from chip, supposed a message is availablestatic int sja1000_read_frames(SJA1000_METHOD_ARGS){  int msgs   = MAX_MESSAGES_PER_INTERRUPT;  u8 fi;  u8 dreg;  u8 dlc;  ULCONV localID;  struct can_frame frame;  struct timeval tv;  int i;  int result = 0;  // DPRINTK(KERN_DEBUG "%s: sja1000_read_frames()\n", DEVICE_NAME);  do  {    DO_GETTIMEOFDAY(tv); /* create timestamp */    fi  = dev->readreg(dev, RECEIVE_FRAME_BASE);    dlc = fi & BUFFER_DLC_MASK;    if (dlc > 8)      dlc = 8;    if (fi & BUFFER_EFF) {      /* extended frame format (EFF) */      dreg = RECEIVE_FRAME_BASE + 5;      #if defined(__LITTLE_ENDIAN)      localID.uc[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);      localID.uc[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);      localID.uc[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 3);      localID.uc[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 4);      #elif defined(__BIG_ENDIAN)      localID.uc[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);      localID.uc[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);      localID.uc[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 3);      localID.uc[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 4);      #else        #error  "Please fix the endianness defines in <asm/byteorder.h>"      #endif      // frame.can_id = (dev->readreg(dev, RECEIVE_FRAME_BASE + 1) << (5+16))      //              | (dev->readreg(dev, RECEIVE_FRAME_BASE + 2) << (5+8))      //              | (dev->readreg(dev, RECEIVE_FRAME_BASE + 3) << 5)      //              | (dev->readreg(dev, RECEIVE_FRAME_BASE + 4) >> 3);      frame.can_id = (localID.ul >> 3) | CAN_EFF_FLAG;    } else {      /* standard frame format (EFF) */      dreg = RECEIVE_FRAME_BASE + 3;            localID.ul    = 0;      #if defined(__LITTLE_ENDIAN)      localID.uc[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);      localID.uc[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);      #elif defined(__BIG_ENDIAN)      localID.uc[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);      localID.uc[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);      #else        #error  "Please fix the endianness defines in <asm/byteorder.h>"      #endif      frame.can_id = (localID.ul >> 21);      //frame.can_id = (dev->readreg(dev, RECEIVE_FRAME_BASE + 1) << 3)      //             | (dev->readreg(dev, RECEIVE_FRAME_BASE + 2) >> 5);    }    if (fi & BUFFER_RTR)      frame.can_id |= CAN_RTR_FLAG;    *(__u64 *) &frame.data[0] = (__u64) 0; /* clear aligned data section */    for (i = 0; i < dlc; i++)      frame.data[i] = dev->readreg(dev, dreg++);    frame.can_dlc = dlc;    SJA1000_LOCK_IRQSAVE(in_lock);    if ((i = pcan_xxxdev_rx(dev, &frame, &tv))) // put into specific data sink      result = i; // save the last result    SJA1000_UNLOCK_IRQRESTORE(in_lock);    // Any error processing on result =! 0 here?    // Indeed we have to read from the controller as long as we receive data to    // unblock the controller. If we have problems to fill the CAN frames into    // the receive queues, this cannot be handled inside the interrupt.    // release the receive buffer    dev->writereg(dev, COMMAND, RELEASE_RECEIVE_BUFFER);    wmb();    // give time to settle    udelay(1);  } while (dev->readreg(dev, CHIPSTATUS) & RECEIVE_BUFFER_STATUS && (msgs--));  return result;}//----------------------------------------------------------------------------// write CAN-data to chip, int sja1000_write_frame(struct pcandev *dev, struct can_frame *cf){  u8 fi;  u8 dlc;  canid_t id;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -