--- /dev/null
+I would like to thank all who have contributed to this project and especially:
+(Random order)
+
+Tomasz Motylewski, T.Motylewski@bfad.de
+BFAD GmbH http://www.bfad.de/, http://www.getembedded.de/
+ Tester, bugfixes, hints and PeliCAN mode!
--- /dev/null
+# Makefile for the Linux CAN-bus driver.
+# Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+# This software is released under the GPL-License.
+# Version 0.7 6 Aug 2001
+
+########## The following options can be changed ##########
+
+# Include directory
+INCLUDEDIR = /usr/src/linux/include
+# Compiler
+CC = gcc
+# Enable debugging messages
+DEBUG = y
+# Enable module version support
+MODVERSIONS = y
+
+# You can comment out the hardware you don't need. This will result in a smaller
+# driver. By default, all hardware is supported in the driver. See the README
+# file for a description of the supported hardware.
+PIP = y
+PCCAN = y # Not to be confused with PCCCAN!!!
+SMARTCAN = y
+NSI = y
+CC104 = y
+AIM104 = y
+PCI03 = y
+PCM3680 = y
+M437 = y
+PCCCAN = y # Not to be confused with PCCAN!!!
+SSV = y
+TEMPLATE = y
+
+########## Don't change anything under this line please ##########
+
+KERNEL_VERSION := $(shell awk -F\" '/REL/ {print $$2}' \
+ $(INCLUDEDIR)/linux/version.h | awk -F\- '{print $$1}')
+
+PROC_FS := $(shell awk -F\ '/PROC_FS/ {print $$3}' \
+ $(INCLUDEDIR)/linux/autoconf.h)
+
+VPATH = ./src:./include:./utils
+
+CFLAGS = -D__KERNEL__ -DMODULE -O2 -Wall -Wstrict-prototypes \
+ -Wpointer-arith -I $(INCLUDEDIR)
+
+
+ifdef DEBUG
+ CFLAGS += -DCAN_DEBUG
+endif
+
+ifndef MODVERSIONS
+ CFLAGS += -DNOVER
+endif
+
+ifdef PROC_FS
+ SRCS += proc.c
+ OBJS += proc.o
+endif
+ifdef PIP
+ SRCS += pip.c
+ OBJS += pip.o
+endif
+ifdef PCCAN
+ SRCS += pccan.c
+ OBJS += pccan.o
+endif
+ifdef SMARTCAN
+ SRCS += smartcan.c
+ OBJS += smartcan.o
+endif
+ifdef NSI
+ SRCS += nsi.c
+ OBJS += nsi.o
+endif
+ifdef CC104
+ SRCS += cc_can104.c
+ OBJS += cc_can104.o
+endif
+ifdef PCI03
+ SRCS += pc-i03.c
+ OBJS += pc-i03.o
+endif
+ifdef PCM3680
+ SRCS += pcm3680.c
+ OBJS += pcm3680.o
+endif
+ifdef AIM104
+ SRCS += aim104.c
+ OBJS += aim104.o
+endif
+ifdef M437
+ SRCS += m437.c
+ OBJS += m437.o
+endif
+ifdef PCCCAN
+ SRCS += pcccan.c
+ OBJS += pcccan.o
+endif
+ifdef SSV
+ SRCS += ssv.c
+ OBJS += ssv.o
+endif
+ifdef TEMPLATE
+ SRCS += template.c
+ OBJS += template.o
+endif
+
+SRCS += main.c modparms.c setup.c sja1000.c i82527.c close.c ioctl.c\
+ open.c write.c read.c sja1000p.c irq.c
+
+OBJS += main.o modparms.o setup.o sja1000.o i82527.o close.o ioctl.o\
+ open.o write.o read.o sja1000p.o irq.o
+
+all : mesg main.ver can.o utils
+
+mesg :
+ @echo
+ @echo Compiling for kernel version: $(KERNEL_VERSION)
+ifdef MODVERSIONS
+ @echo Compiling with module version support
+else
+ @echo Compiling without module version support
+endif
+ifdef PROC_FS
+ @echo Compiling with proc filesystem support
+else
+ @echo Compiling without proc filesystem support
+endif
+ @echo
+
+ifdef MODVERSIONS
+main.ver : main.c
+ $(CC) -I $(INCLUDEDIR) -E -O2 -D__GENKSYMS__ $^ | \
+ /sbin/genksyms -k $(KERNEL_VERSION) > ./include/$@
+else
+main.ver:
+endif
+
+can.o : $(OBJS)
+ (cd ./src; ld -o ../can.o $(OBJS) -E -O2 -r)
+
+main.o : main.c main.h proc.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+proc.o : proc.c main.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+modparms.o : modparms.c modparms.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+setup.o : setup.c setup.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+pip.o : pip.c pip.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define PIP 1" >> .support
+pccan.o : pccan.c pccan.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define PCCAN 1" >> .support
+smartcan.o : smartcan.c smartcan.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define SMARTCAN 1" >> .support
+nsi.o : nsi.c nsi.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define NSI 1" >> .support
+cc_can104.o : cc_can104.c cc_can104.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define CC104 1" >> .support
+aim104.o : aim104.c aim104.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define AIM104 1" >> .support
+pc-i03.o : pc-i03.c pc-i03.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define PCI03 1" >> .support
+pcm3680.o : pcm3680.c pcm3680.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define PCM3680 1" >> .support
+m437.o : m437.c m437.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define M437 1" >> .support
+pcccan.o : pcccan.c pcccan.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define PCCCAN 1" >> .support
+ssv.o : ssv.c ssv.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define SSV 1" >> .support
+template.o : template.c template.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ @echo "#define TEMPLATE 1" >> .support
+open.o : open.c open.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+i82527.o : i82527.c i82527.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+close.o : close.c close.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+read.o : read.c read.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+irq.o : irq.c irq.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+ioctl.o : ioctl.c ioctl.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+write.o : write.c write.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+sja1000.o : sja1000.c sja1000.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+sja1000p.o : sja1000p.c sja1000p.h
+ $(CC) -c -o $(<D)/$@ $< $(CFLAGS)
+
+utils : rxtx sendburst readburst
+
+rxtx : rxtx.c
+ $(CC) -o $(<D)/$@ $< -Wall
+sendburst : sendburst.c
+ $(CC) -o $(<D)/$@ $< -Wall
+readburst : readburst.c
+ $(CC) -o $(<D)/$@ $< -Wall
+
+
+clean :
+ rm -f *.o
+ rm -f .support
+ (cd src; rm -f *.o)
+ (cd include; rm -f *.ver)
+ (cd utils; rm -f rxtx sendburst readburst)
-Linux CAN Driver
-================
+README for the linux CAN-bus driver.
+Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+This software is released under the GPL-License.
+Version 0.7 6 Aug 2001
+
+COMPILATION
+Just type 'make' at the command line and it should compile without errors.
+
+INSTALLATION.
+Currently there's no installation procedure, just compile and load the driver.
+
+LOADING
+To load the driver type:
+# insmod can.o hw='your hardware' irq='irq number' io='io address' <options>
+Example: # insmod can.o hw=pip5 irq=4 io=0x8000
+
+The hw argument can be one of:
+- pip5, for the pip5 computer by MPL
+- pip6, for the pip6 computer by MPL
+- pccan-q, for the PCcan-Q ISA card by KVASER
+- pccan-f, for the PCcan-F ISA card by KVASER
+- pccan-s, for the PCcan-S ISA card by KVASER
+- pccan-d, for the PCcan-D ISA card by KVASER
+- nsican, for the CAN104 PC/104 card by NSI
+- cc104, for the CAN104 PC/104 card by Contemporary Controls
+- aim104, for the AIM104CAN PC/104 card by Arcom Control Systems
+- pc-i03, for the PC-I03 ISA card by IXXAT
+- pcm3680, for the PCM-3680 PC/104 card by Advantech
+- m437, for the M436 PC/104 card by SECO
+- template, for yet unsupported hardware (you need to edit src/template.c)
+
+options can be one of:
+- major=<nr>, major specifies the major number of the driver.
+- minor=<nr>, you can specify wich minor numbers the driver should use for your
+ hardware.
+- extended=[1|0], configures the driver to use extended message format.
+- pelican=[1|0], configures the driver to set the CAN chips into pelican mode.
+- baudrate=<nr>, sets the baudrate of the device(s)
+- stdmask=<nr>, sets the standard mask of the device
+- extmask=<nr>, sets the extended mask of the device
+- mo15mask=<nr>, sets the mask for message object 15 (i82527 only)
+
+There's still a lot of work to do, have a look at the TODO file for more
+information.
--- /dev/null
+Things that still have to be done in version 0.7:
+Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+This software is released under the GPL-License.
+Version 0.7 6 Aug 2001
+
+- Add proper comment to the code
+- Error handling/reporting
+- Proc directory
+- Lot of ioctl's
+- Poll/select
--- /dev/null
+Unsupported-hardware
+Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+This software is released under the GPL-License.
+Version 0.7 6 Aug 2001
+
+If your hardware is currently not supported there are two things you can do:
+1) Edit the file src/template.c
+2) Send an email to arnaud@wanadoo.nl
+
+If you choose to add the code yourself, all you have to do is edit the file
+template.c. This file contains hardware specific template functions wich you can
+edit to make the hardware work with the driver. When you have edited this file
+you can simply load the driver with the module parameter hw=template and all
+should work fine. IMO the functions in template.c are well commented and you
+should have little trouble in adding/changing the correct code. If it still
+doesn't work feel free to contact me at arnaud@wanadoo.nl. In case you've
+sucessfully changed the template.c file, please send this file (no patches
+please) to me so I can extend the drivers supported hardware base.
+
+If you don't have the skills to add/change the code yourself, feel free to
+contact me at arnaud@wanadoo.nl
+
+
--- /dev/null
+/* aim104.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int aim104_request_io(unsigned long io_addr);
+int aim104_release_io(unsigned long io_addr);
+int aim104_reset(int card);
+int aim104_init_hw_data(int card);
+int aim104_init_chip_data(int card, int chipnr);
+int aim104_init_obj_data(int chipnr, int objnr);
+void aim104_write_register(unsigned char data, unsigned long address);
+unsigned aim104_read_register(unsigned long address);
+int aim104_program_irq(int card);
+
--- /dev/null
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
+#define CAN_MSG_LENGTH 8
+
+struct canmsg_t {
+ short flags;
+ int cob;
+ unsigned long id;
+ unsigned long timestamp;
+ unsigned int length;
+ char data[CAN_MSG_LENGTH];
+} PACKED;
+
+/* Definitions to use for canmsg_t flags */
+#define MSG_RTR (1<<0)
+#define MSG_OVR (1<<1)
+#define MSG_EXT (1<<2)
+
+/* CAN ioctl magic number */
+#define CAN_IOC_MAGIC 'd'
+
+unsigned long bittiming;
+unsigned short channel;
+
+/* CAN ioctl functions */
+#define CMD_START _IOW(CAN_IOC_MAGIC, 1, channel)
+#define CMD_STOP _IOW(CAN_IOC_MAGIC, 2, channel)
+//#define CMD_RESET 3
+
+#define CONF_BAUD _IOW(CAN_IOC_MAGIC, 4, bittiming)
+//#define CONF_ACCM
+//#define CONF_XTDACCM
+//#define CONF_TIMING
+//#define CONF_OMODE
+#define CONF_FILTER _IOW(CAN_IOC_MAGIC, 8, unsigned char)
+//#define CONF_FENABLE
+//#define CONF_FDISABLE
+
+
+#define STAT _IO(CAN_IOC_MAGIC, 9)
--- /dev/null
+/* cc_can104.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int cc104_request_io(unsigned long io_addr);
+int cc104_release_io(unsigned long io_addr);
+int cc104_reset(int card);
+int cc104_init_hw_data(int card);
+int cc104_init_chip_data(int card, int chipnr);
+int cc104_init_obj_data(int chipnr, int objnr);
+void cc104_write_register(unsigned char data, unsigned long address);
+unsigned cc104_read_register(unsigned long address);
+int cc104_program_irq(int card);
+
--- /dev/null
+/* close.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int can_close(struct inode *inode, struct file *file);
+
--- /dev/null
+/* constants.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#ifdef __CONSTANTS_H__
+#else
+#define __CONSTANTS_H__
+
+/* Device name as it will appear in /proc/devices */
+#define DEVICE_NAME "can"
+
+/* Default driver major number, see /usr/src/linux/Documentation/devices.txt */
+#define CAN_MAJOR 91
+
+/* Timeout in jiffies before the system calls return with an error */
+#define CANTIMEOUT (4*HZ)
+
+/* Definition of the maximum number of concurrent supported hardware boards,
+ * chips per board, total number of chips, interrupts and message objects.
+ * Obviously there are no 32 different interrupts, but each chip can have its
+ * own interrupt so we have to check for it MAX_IRQ == MAX_TOT_CHIPS times.
+ */
+#define MAX_HW_CARDS 8
+#define MAX_HW_CHIPS 4
+#define MAX_TOT_CHIPS (MAX_HW_CHIPS*MAX_HW_CARDS)
+#define MAX_IRQ 32
+#define MAX_MSGOBJS 15
+#define MAX_TOT_MSGOBJS (MAX_TOT_CHIPS*MAX_MSGOBJS)
+#define MAX_BUF_LENGTH 64
+
+#define IE (1<<1)
+#define SIE (1<<2)
+#define EIE (1<<3)
+
+#define SPACING 0x3c0
+
+/* These flags can be used for the msgobj_t structure flags data entry */
+#define OPENED (1<<0)
+
+/* These flags can be used for the chip_t structure flags data entry */
+#define CONFIGURED (1<<0)
+#define SEGMENTED (1<<1)
+
+/* These flags can be used for the candevices_t structure flags data entry */
+#define PROGRAMMABLE_IRQ (1<<0)
+
+enum timing_BTR1 {
+ MAX_TSEG1 = 15,
+ MAX_TSEG2 = 7
+};
+
+/* Flags for baud_rate function */
+#define BTR1_SAM (1<<1)
+
+#endif
--- /dev/null
+/* i82527.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int i82527_enable_configuration(struct chip_t *chip);
+int i82527_disable_configuration(struct chip_t *chip);
+int i82527_chip_config(struct chip_t *chip);
+int i82527_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags);
+int i82527_standard_mask(struct chip_t *chip, unsigned short code,
+ unsigned short mask);
+int i82527_extended_mask(struct chip_t *chip, unsigned long code,
+ unsigned long mask);
+int i82527_message15_mask(struct chip_t *chip, unsigned long code,
+ unsigned long mask);
+int i82527_clear_objects(struct chip_t *chip);
+int i82527_config_irqs(struct chip_t *chip, short irqs);
+int i82527_pre_read_config(struct chip_t *chip, struct msgobj_t *obj);
+int i82527_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int i82527_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int i82527_remote_request(struct chip_t *chip, struct msgobj_t *obj);
+int i82527_set_btregs(struct chip_t *chip, unsigned short btr0,
+ unsigned short btr1);
+int i82527_start_chip(struct chip_t *chip);
+int i82527_stop_chip(struct chip_t *chip);
+int i82527_check_tx_stat(struct chip_t *chip);
+
+#define iCTL 0x00 // Control Register
+#define iSTAT 0x01 // Status Register
+#define iCPU 0x02 // CPU Interface Register
+#define iHSR 0x04 // High Speed Read
+#define iSGM0 0x06 // Standard Global Mask byte 0
+#define iSGM1 0x07
+#define iEGM0 0x08 // Extended Global Mask byte 0
+#define iEGM1 0x09
+#define iEGM2 0x0a
+#define iEGM3 0x0b
+#define i15M0 0x0c // Message 15 Mask byte 0
+#define i15M1 0x0d
+#define i15M2 0x0e
+#define i15M3 0x0f
+#define iCLK 0x1f // Clock Out Register
+#define iBUS 0x2f // Bus Configuration Register
+#define iBT0 0x3f // Bit Timing Register byte 0
+#define iBT1 0x4f
+#define iIRQ 0x5f // Interrupt Register
+#define iP1C 0x9f // Port 1 Register
+#define iP2C 0xaf // Port 2 Register
+#define iP1I 0xbf // Port 1 Data In Register
+#define iP2I 0xcf // Port 2 Data In Register
+#define iP1O 0xdf // Port 1 Data Out Register
+#define iP2O 0xef // Port 2 Data Out Register
+#define iSRA 0xff // Serial Reset Address
+
+#define iMSGCTL0 0x00 /* First Control register */
+#define iMSGCTL1 0x01 /* Second Control register */
+#define iMSGID0 0x02 /* First Byte of Message ID */
+#define iMSGID1 0x03
+#define iMSGID2 0x04
+#define iMSGID3 0x05
+#define iMSGCFG 0x06 /* Message Configuration */
+#define iMSGDAT0 0x07 /* First Data Byte */
+#define iMSGDAT1 0x08
+#define iMSGDAT2 0x09
+#define iMSGDAT3 0x0a
+#define iMSGDAT4 0x0b
+#define iMSGDAT5 0x0c
+#define iMSGDAT6 0x0d
+#define iMSGDAT7 0x0e
+
+/* Control Register (0x00) */
+enum i82527_iCTL {
+ iCTL_INI = 1, // Initialization
+ iCTL_IE = 1<<1, // Interrupt Enable
+ iCTL_SIE = 1<<2, // Status Interrupt Enable
+ iCTL_EIE = 1<<3, // Error Interrupt Enable
+ iCTL_CCE = 1<<6 // Change Configuration Enable
+};
+
+/* Status Register (0x01) */
+enum i82527_iSTAT {
+ iSTAT_TXOK = 1<<3, // Transmit Message Successfully
+ iSTAT_RXOK = 1<<4, // Receive Message Successfully
+ iSTAT_WAKE = 1<<5, // Wake Up Status
+ iSTAT_WARN = 1<<6, // Warning Status
+ iSTAT_BOFF = 1<<7 // Bus Off Status
+};
+
+/* CPU Interface Register (0x02) */
+enum i82527_iCPU {
+ iCPU_CEN = 1, // Clock Out Enable
+ iCPU_MUX = 1<<2, // Multiplex
+ iCPU_SLP = 1<<3, // Sleep
+ iCPU_PWD = 1<<4, // Power Down Mode
+ iCPU_DMC = 1<<5, // Divide Memory Clock
+ iCPU_DSC = 1<<6, // Divide System Clock
+ iCPU_RST = 1<<7 // Hardware Reset Status
+};
+
+/* Clock Out Register (0x1f) */
+enum i82527_iCLK {
+ iCLK_CD0 = 1, // Clock Divider bit 0
+ iCLK_CD1 = 1<<1,
+ iCLK_CD2 = 1<<2,
+ iCLK_CD3 = 1<<3,
+ iCLK_SL0 = 1<<4, // Slew Rate bit 0
+ iCLK_SL1 = 1<<5
+};
+
+/* Bus Configuration Register (0x2f) */
+enum i82527_iBUS {
+ iBUS_DR0 = 1, // Disconnect RX0 Input
+ iBUS_DR1 = 1<<1, // Disconnect RX1 Input
+ iBUS_DT1 = 1<<3, // Disconnect TX1 Output
+ iBUS_POL = 1<<5, // Polarity
+ iBUS_CBY = 1<<6 // Comparator Bypass
+};
+
+#define RESET 1 // Bit Pair Reset Status
+#define SET 2 // Bit Pair Set Status
+#define UNCHANGED 3 // Bit Pair Unchanged
+
+/* Message Control Register 0 (Base Address + 0x0) */
+enum i82527_iMSGCTL0 {
+ INTPD_SET = SET, // Interrupt pending
+ INTPD_RES = RESET, // No Interrupt pending
+ INTPD_UNC = UNCHANGED,
+ RXIE_SET = SET<<2, // Receive Interrupt Enable
+ RXIE_RES = RESET<<2, // Receive Interrupt Disable
+ RXIE_UNC = UNCHANGED<<2,
+ TXIE_SET = SET<<4, // Transmit Interrupt Enable
+ TXIE_RES = RESET<<4, // Transmit Interrupt Disable
+ TXIE_UNC = UNCHANGED<<4,
+ MVAL_SET = SET<<6, // Message Valid
+ MVAL_RES = RESET<<6, // Message Invalid
+ MVAL_UNC = UNCHANGED<<6
+};
+
+/* Message Control Register 1 (Base Address + 0x01) */
+enum i82527_iMSGCTL1 {
+ NEWD_SET = SET, // New Data
+ NEWD_RES = RESET, // No New Data
+ NEWD_UNC = UNCHANGED,
+ MLST_SET = SET<<2, // Message Lost
+ MLST_RES = RESET<<2, // No Message Lost
+ MLST_UNC = UNCHANGED<<2,
+ CPUU_SET = SET<<2, // CPU Updating
+ CPUU_RES = RESET<<2, // No CPU Updating
+ CPUU_UNC = UNCHANGED<<2,
+ TXRQ_SET = SET<<4, // Transmission Request
+ TXRQ_RES = RESET<<4, // No Transmission Request
+ TXRQ_UNC = UNCHANGED<<4,
+ RMPD_SET = SET<<6, // Remote Request Pending
+ RMPD_RES = RESET<<6, // No Remote Request Pending
+ RMPD_UNC = UNCHANGED<<6
+};
+
+/* Message Configuration Register (Base Address + 0x06) */
+enum i82527_iMSGCFG {
+ MCFG_XTD = 1<<2, // Extended Identifier
+ MCFG_DIR = 1<<3 // Direction is Transmit
+};
--- /dev/null
+/* ioctl.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int can_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
--- /dev/null
+/* irq.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+void i82527_irq_handler(int irq, void *dev_id, struct pt_regs *regs);
+void sja1000_irq_handler(int irq, void *dev_id, struct pt_regs *regs);
--- /dev/null
+/* m437.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ *
+ * Header file for the SECO M437 (see m437.c for details)
+ */
+
+int m437_request_io(unsigned long io_addr);
+int m437_release_io(unsigned long io_addr);
+int m437_reset(int card);
+int m437_init_hw_data(int card);
+int m437_init_chip_data(int card, int chipnr);
+int m437_init_obj_data(int chipnr, int objnr);
+void m437_write_register(unsigned char data, unsigned long address);
+unsigned m437_read_register(unsigned long address);
+int m437_program_irq(int card);
+int m437_register(struct hwspecops_t *hwspecops);
--- /dev/null
+/* main.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "./can.h"
+#include "./constants.h"
+
+#ifdef CAN_DEBUG
+ #define DEBUGMSG(fmt,args...) printk(KERN_ERR "can.o (debug): " fmt,\
+ ##args)
+#else
+ #define DEBUGMSG(fmt,args...)
+#endif
+
+#define CANMSG(fmt,args...) printk(KERN_ERR "can.o: " fmt,##args)
+
+#define MINOR_NR \
+ (MINOR(file->f_dentry->d_inode->i_rdev))
+
+#define MSG_OFFSET(object) ((object)*0x10)
+
+struct canhardware_t {
+ int nr_boards;
+ struct rtr_id *rtr_queue;
+ spinlock_t rtr_lock;
+ struct candevice_t *candevice[MAX_HW_CARDS];
+};
+
+struct candevice_t {
+ char *hwname;
+ unsigned long io_addr;
+ unsigned long res_addr;
+ unsigned int flags;
+
+ /* Hardware chip configuration. In case of multiple chips *chip
+ * is the first in an array of chip_t structures.
+ */
+ int nr_82527_chips;
+ int nr_sja1000_chips;
+ struct chip_t *chip[MAX_HW_CHIPS];
+
+ struct hwspecops_t *hwspecops;
+
+ struct canhardware_t *hosthardware_p;
+};
+
+struct chip_t {
+ char *chip_type;
+ int chip_irq;
+ unsigned long chip_base_addr;
+ unsigned int flags;
+ int clock; // Chip clock in Hz
+
+ /* sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the sja1000.h file:
+ * CDR_CLKOUT_MASK, CDR_CLK_OFF, CDR_RXINPEN, CDR_CBP, CDR_PELICAN
+ *
+ * sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the sja1000.h file:
+ * OCR_MODE_BIPHASE, OCR_MODE_TEST, OCR_MODE_NORMAL, OCR_MODE_CLOCK,
+ * OCR_TX0_LH, OCR_TX1_ZZ.
+ *
+ * int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the i82527.h file:
+ * iCLK_CD0, iCLK_CD1, iCLK_CD2, iCLK_CD3, iCLK_SL0, iCLK_SL1.
+ *
+ * int_bus_reg holds hardware specific options for the Bus Configuration
+ * register. Options defined in the i82527.h file:
+ * iBUS_DR0, iBUS_DR1, iBUS_DT1, iBUS_POL, iBUS_CBY.
+ *
+ * int_cpu_reg holds hardware specific options for the CPU Interface
+ * register. Options defined in the i82527.h file:
+ * iCPU_CEN, iCPU_MUX, iCPU_SLP, iCPU_PWD, iCPU_DMC, iCPU_DSC, iCPU_RST.
+ */
+ unsigned short sja_cdr_reg; // sja1000 only!
+ unsigned short sja_ocr_reg; // sja1000 only!
+ unsigned short int_cpu_reg; // intel 82527 only!
+ unsigned short int_clk_reg; // intel 82527 only!
+ unsigned short int_bus_reg; // intel 82527 only!
+
+ struct msgobj_t *msgobj[MAX_MSGOBJS];
+
+ struct chipspecops_t *chipspecops;
+
+ struct candevice_t *hostdevice;
+};
+
+struct msgobj_t {
+ unsigned long obj_base_addr;
+ unsigned int minor;
+ unsigned int object;
+ unsigned int flags;
+ int ret;
+
+ struct canfifo_t *fifo;
+
+ struct chip_t *hostchip;
+};
+
+struct hwspecops_t {
+ int (*request_io)(unsigned long io_addr);
+ int (*release_io)(unsigned long io_addr);
+ int (*reset)(int card);
+ int (*init_hw_data)(int card);
+ int (*init_chip_data)(int card, int chipnr);
+ int (*init_obj_data)(int chipnr, int objnr);
+ void (*write_register)(unsigned char data,unsigned long address);
+ unsigned (*read_register)(unsigned long address);
+ int (*program_irq)(int card);
+};
+
+struct chipspecops_t {
+ int (*chip_config)(struct chip_t *chip);
+ int (*baud_rate)(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags);
+ int (*standard_mask)(struct chip_t *chip, unsigned short code,
+ unsigned short mask);
+ int (*extended_mask)(struct chip_t *chip, unsigned long code,
+ unsigned long mask);
+ int (*message15_mask)(struct chip_t *chip, unsigned long code,
+ unsigned long mask);
+ int (*clear_objects)(struct chip_t *chip);
+ int (*config_irqs)(struct chip_t *chip, short irqs);
+ int (*pre_read_config)(struct chip_t *chip, struct msgobj_t *obj);
+ int (*pre_write_config)(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+ int (*send_msg)(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+ int (*remote_request)(struct chip_t *chip, struct msgobj_t *obj);
+ int (*check_tx_stat)(struct chip_t *chip);
+ int (*enable_configuration)(struct chip_t *chip);
+ int (*disable_configuration)(struct chip_t *chip);
+ int (*set_btregs)(struct chip_t *chip, unsigned short btr0,
+ unsigned short btr1);
+ int (*start_chip)(struct chip_t *chip);
+ int (*stop_chip)(struct chip_t *chip);
+ void (*irq_handler)(int irq, void *dev_id, struct pt_regs *regs);
+};
+
+struct mem_addr {
+ void *address;
+ struct mem_addr *next;
+};
+
+/* Structure for the drivers main input and output buffers. The readq, writeq
+ * entries are the wait queues for the driver to sleep on in case of blocking
+ * read/write calls. buf_rx_entry and buf_tx_entry are pointers to the input and
+ * output buffers. The buffers are dynamically allocated. The tx_readp,
+ * tx_writep, rx_readp and rx_writep pointers are the various read/write
+ * pointers used when reading or writing the input and output buffers. The
+ * rx/tx_size entries are the dynamically allocated input and output buffer size
+ * The rx/tx_in_progress entries are used to determine whether the device is
+ * already set up for transmission.
+ */
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+struct canfifo_t {
+ struct wait_queue *readq, *writeq;
+ struct canmsg_t *buf_tx_entry;
+ struct canmsg_t *buf_rx_entry;
+ struct canmsg_t *tx_readp;
+ struct canmsg_t *rx_writep;
+ struct canmsg_t *tx_writep;
+ struct canmsg_t *rx_readp;
+ int rx_size, tx_size;
+ volatile int rx_in_progress, tx_in_progress;
+ int head, tail; //TEMP!!!
+};
+#else
+struct canfifo_t {
+ struct __wait_queue_head readq;
+ struct __wait_queue_head writeq;
+ struct canmsg_t *buf_tx_entry;
+ struct canmsg_t *buf_rx_entry;
+ struct canmsg_t *tx_readp;
+ struct canmsg_t *rx_writep;
+ struct canmsg_t *tx_writep;
+ struct canmsg_t *rx_readp;
+ int rx_size, tx_size;
+ volatile int rx_in_progress, tx_in_progress;
+ int head, tail; //TEMP!!!
+};
+#endif
+
+/* Structure for the RTR queue */
+struct rtr_id {
+ unsigned long id;
+ struct canmsg_t *rtr_message;
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ struct wait_queue *rtr_wq;
+#else
+ struct __wait_queue_head rtr_wq;
+#endif
+ struct rtr_id *next;
+};
+
+extern int major;
+extern int minor[MAX_TOT_CHIPS];
+extern int extended;
+extern int baudrate;
+extern char *hw[MAX_HW_CARDS];
+extern int irq[MAX_IRQ];
+extern unsigned long io[MAX_HW_CARDS];
+
+extern struct canhardware_t *hardware_p;
+extern struct candevice_t *candevices_p[MAX_HW_CARDS];
+extern struct chip_t *chips_p[MAX_TOT_CHIPS];
+extern struct msgobj_t *objects_p[MAX_TOT_MSGOBJS];
+
+extern struct mem_addr *mem_head;
+
+/* Inline function to write to the hardware registers. The argument address is
+ * relative to the memory map of the chip and not the absolute memory address.
+ */
+extern inline void can_write_reg(struct chip_t *chip, unsigned char data, unsigned short address)
+{
+ unsigned short segment_number;
+ unsigned long address_to_write;
+
+ address_to_write = chip->chip_base_addr+address;
+
+ if ( (chip->flags & SEGMENTED) != 0) {
+ segment_number = (unsigned short)(address >> 6);
+ address_to_write += SPACING * segment_number;
+ }
+
+ chip->hostdevice->hwspecops->write_register(data, address_to_write);
+}
+
+extern inline unsigned can_read_reg(struct chip_t *chip, unsigned short address)
+{
+ unsigned short segment_number;
+ unsigned long address_to_read;
+
+ address_to_read = chip->chip_base_addr+address;
+
+ if ( (chip->flags & SEGMENTED) != 0) {
+ segment_number = (unsigned short)(address >> 6);
+ address_to_read += SPACING * segment_number;
+ }
+ return chip->hostdevice->hwspecops->read_register(address_to_read);
+}
--- /dev/null
+/* mod_parms.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int parse_mod_parms(void);
--- /dev/null
+/* nsi.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int nsi_request_io(unsigned long io_addr);
+int nsi_release_io(unsigned long io_addr);
+int nsi_reset(int card);
+int nsi_init_hw_data(int card);
+int nsi_init_chip_data(int card, int chipnr);
+int nsi_init_obj_data(int chipnr, int objnr);
+void nsi_write_register(unsigned char data, unsigned long address);
+unsigned nsi_read_register(unsigned long address);
+int nsi_program_irq(int card);
+
--- /dev/null
+/* open.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int can_open(struct inode *inode, struct file *file);
--- /dev/null
+/* pc-i03.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int pci03_request_io(unsigned long io_addr);
+int pci03_release_io(unsigned long io_addr);
+int pci03_reset(int card);
+int pci03_init_hw_data(int card);
+int pci03_init_chip_data(int card, int chipnr);
+int pci03_init_obj_data(int chipnr, int objnr);
+void pci03_write_register(unsigned char data, unsigned long address);
+unsigned pci03_read_register(unsigned long address);
+int pci03_program_irq(int card);
+
--- /dev/null
+/* pccan.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int pccanf_request_io(unsigned long io_addr);
+int pccanf_release_io(unsigned long io_addr);
+int pccand_request_io(unsigned long io_addr);
+int pccand_release_io(unsigned long io_addr);
+int pccanq_request_io(unsigned long io_addr);
+int pccanq_release_io(unsigned long io_addr);
+int pccanf_reset(int card);
+int pccand_reset(int card);
+int pccanq_reset(int card);
+int pccan_init_hw_data(int card);
+int pccan_init_chip_data(int card, int chipnr);
+int pccan_init_obj_data(int chipnr, int objnr);
+void pccan_write_register(unsigned char data, unsigned long address);
+unsigned pccan_read_register(unsigned long address);
+int pccan_program_irq(int card);
+
+
--- /dev/null
+/* pcccan.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int pcccan_request_io(unsigned long io_addr);
+int pcccan_release_io(unsigned long io_addr);
+int pcccan_reset(int card);
+int pcccan_init_hw_data(int card);
+int pcccan_init_chip_data(int card, int chipnr);
+int pcccan_init_obj_data(int chipnr, int objnr);
+void pcccan_write_register(unsigned char data, unsigned long address);
+unsigned pcccan_read_register(unsigned long address);
+int pcccan_program_irq(int card);
+
--- /dev/null
+/* pcm3680.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int pcm3680_request_io(unsigned long io_addr);
+int pcm3680_release_io(unsigned long io_addr);
+int pcm3680_reset(int card);
+int pcm3680_init_hw_data(int card);
+int pcm3680_init_chip_data(int card, int chipnr);
+int pcm3680_init_obj_data(int chipnr, int objnr);
+void pcm3680_write_register(unsigned char data, unsigned long address);
+unsigned pcm3680_read_register(unsigned long address);
+int pcm3680_program_irq(int card);
+
--- /dev/null
+/* pip.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int pip5_request_io(unsigned long io_addr);
+int pip5_release_io(unsigned long io_addr);
+int pip5_reset(int card);
+int pip5_init_hw_data(int card);
+int pip5_init_chip_data(int card, int chipnr);
+int pip5_init_obj_data(int chipnr, int objnr);
+void pip5_write_register(unsigned char data, unsigned long address);
+unsigned pip5_read_register(unsigned long address);
+int pip5_program_irq(int card);
+
--- /dev/null
+/* proc.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/proc_fs.h>
+#include "./constants.h"
+
+int can_init_procdir(void);
+int can_delete_procdir(void);
+
+struct canproc_t {
+ struct proc_dir_entry *can_proc_entry;
+ struct channelproc_t *channel[MAX_TOT_CHIPS];
+};
+
+struct channelproc_t {
+ char ch_name[20];
+ struct proc_dir_entry *ch_entry;
+ struct objectproc_t *object[MAX_MSGOBJS];
+};
+
+
+struct objectproc_t {
+ char obj_name[20];
+ struct proc_dir_entry *obj_entry;
+ char lnk_name[20];
+ char lnk_dev[20];
+ struct proc_dir_entry *lnk;
+};
--- /dev/null
+/* read.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+ssize_t can_read(struct file *file,char *buffer,size_t length,loff_t *offset);
--- /dev/null
+/* setup.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int init_hw_struct(void);
+int list_hw(void);
+int add_mem_to_list(void *address_p);
+int del_mem_from_list(void *address_p);
+int del_mem_list(void);
--- /dev/null
+/* sja1000.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int sja1000_enable_configuration(struct chip_t *chip);
+int sja1000_disable_configuration(struct chip_t *chip);
+int sja1000_chip_config(struct chip_t *chip);
+int sja1000_standard_mask(struct chip_t *chip, unsigned short code, unsigned short mask);
+int sja1000_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags);
+int sja1000_pre_read_config(struct chip_t *chip, struct msgobj_t *obj);
+int sja1000_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int sja1000_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int sja1000_check_tx_stat(struct chip_t *chip);
+int sja1000_set_btregs(struct chip_t *chip, unsigned short btr0,
+ unsigned short btr1);
+int sja1000_start_chip(struct chip_t *chip);
+int sja1000_stop_chip(struct chip_t *chip);
+void sja1000_irq_handler(int irq, void *dev_id, struct pt_regs *regs);
+
+/* BasicCAN mode address map */
+#define SJACR 0x00 /* Control register */
+#define SJACMR 0x01 /* Command register */
+#define SJASR 0x02 /* Status register */
+#define SJAIR 0x03 /* Interrupt register */
+#define SJAACR 0x04 /* Acceptance Code register */
+#define SJAAMR 0x05 /* Acceptance Mask Register */
+#define SJABTR0 0x06 /* Bus Timing register 0 */
+#define SJABTR1 0x07 /* Bus Timing register 1 */
+#define SJAOCR 0x08 /* Output Control register */
+#define SJACDR 0x1f /* Clock Divider register */
+
+#define SJATXID1 0x0a /* Identifier byte 1 */
+#define SJATXID0 0x0b /* Identifier byte 0 */
+#define SJATXDAT0 0x0c /* First data byte */
+#define SJATXDAT1 0x0d
+#define SJATXDAT2 0x0e
+#define SJATDDAT3 0x0f
+#define SJATXDAT4 0x10
+#define SJATXDAT5 0x11
+#define SJATXDAT6 0x12
+#define SJATXDAT7 0x13
+
+#define SJARXID1 0x14 /* Identifier byte 1 */
+#define SJARXID0 0x15 /* Identifier byte 0 */
+#define SJARXDAT0 0x16 /* First data byte */
+#define SJARXDAT1 0x17
+#define SJARXDAT2 0x18
+#define SJARXDAT3 0x19
+#define SJARXDAT4 0x1a
+#define SJARXDAT5 0x1b
+#define SJARXDAT6 0x1c
+#define SJARXDAT7 0x1d
+
+/* Command register */
+enum sja1000_BASIC_CMR {
+ CMR_TR = 1, // Transmission request
+ CMR_AT = 1<<1, // Abort Transmission
+ CMR_RRB = 1<<2, // Release Receive Buffer
+ CMR_CDO = 1<<3, // Clear Data Overrun
+ CMR_GTS = 1<<4 // Go To Sleep
+};
+
+/* Status Register */
+enum sja1000_BASIC_SR {
+ SR_RBS = 1, // Receive Buffer Status
+ SR_DOS = 1<<1, // Data Overrun Status
+ SR_TBS = 1<<2, // Transmit Buffer Status
+ SR_TCS = 1<<3, // Transmission Complete Status
+ SR_RS = 1<<4, // Receive Status
+ SR_TS = 1<<5, // Transmit Status
+ SR_ES = 1<<6, // Error Status
+ SR_BS = 1<<7 // Bus Status
+};
+
+/* Control Register */
+enum sja1000_BASIC_CR {
+ CR_RR = 1, // Reset Request
+ CR_RIE = 1<<1, // Receive Interrupt Enable
+ CR_TIE = 1<<2, // Transmit Interrupt Enable
+ CR_EIE = 1<<3, // Error Interrupt Enable
+ CR_OIE = 1<<4 // Overrun Interrupt Enable
+};
+
+/* Interrupt (status) Register */
+enum sja1000_BASIC_IR {
+ IR_RI = 1, // Receive Interrupt
+ IR_TI = 1<<1, // Transmit Interrupt
+ IR_EI = 1<<2, // Error Interrupt
+ IR_DOI = 1<<3, // Data Overrun Interrupt
+ IR_WUI = 1<<4 // Wake-Up Interrupt
+};
+
+/* Clock Divider Register */
+enum sja1000_CDR {
+ /* f_out = f_osc/(2*(CDR[2:0]+1)) or f_osc if CDR[2:0]==7 */
+ CDR_CLKOUT_MASK = 7,
+ CDR_CLK_OFF = 1<<3, // Clock Off
+ CDR_RXINPEN = 1<<5, // TX1 output is RX irq output
+ CDR_CBP = 1<<6, // Input Comparator By-Pass
+ CDR_PELICAN = 1<<7 // PeliCAN Mode
+};
+
+/* Output Control Register */
+enum sja1000_OCR {
+ OCR_MODE_BIPHASE = 0,
+ OCR_MODE_TEST = 1,
+ OCR_MODE_NORMAL = 2,
+ OCR_MODE_CLOCK = 3,
+// TX0 push-pull not inverted
+ OCR_TX0_LH = 0x18,
+// TX0 push-pull inverted
+ OCR_TX0_HL = 0x1c,
+// TX1 floating (off)
+ OCR_TX1_ZZ = 0,
+// TX1 pull-down not inverted
+ OCR_TX1_LZ = 0x40
+};
+
+/** Frame format information 0x11 */
+enum sja1000_BASIC_ID0 {
+ ID0_RTR = 1<<4, // Remote request
+ ID0_DLC_M = (1<<4)-1 // Length Mask
+};
--- /dev/null
+/* sja1000p.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.6.1 T.Motylewski@bfad.de 13.03.2001
+ * See app. note an97076.pdf from Philips Semiconductors
+ * and SJA1000 data sheet
+ * PELICAN mode
+ */
+
+int sja1000p_chip_config(struct chip_t *chip);
+int sja1000p_extended_mask(struct chip_t *chip, unsigned long code, unsigned long mask);
+int sja1000p_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags);
+int sja1000p_pre_read_config(struct chip_t *chip, struct msgobj_t *obj);
+int sja1000p_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int sja1000p_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+
+/* PeliCAN mode */
+enum SJA1000_PeliCAN_regs {
+ SJAMOD = 0x00,
+/// Command register
+ SJACMR = 0x01,
+/// Status register
+ SJASR = 0x02,
+/// Interrupt register
+ SJAIR = 0x03,
+/// Interrupt Enable
+ SJAIER = 0x04,
+/// Bus Timing register 0
+ SJABTR0 = 0x06,
+/// Bus Timing register 1
+ SJABTR1 = 0x07,
+/// Output Control register
+ SJAOCR = 0x08,
+/// Arbitration Lost Capture
+ SJAALC = 0x0b,
+/// Error Code Capture
+ SJAECC = 0x0c,
+/// Error Warning Limit
+ SJAEWLR = 0x0d,
+/// RX Error Counter
+ SJARXERR = 0x0e,
+/// TX Error Counter
+ SJATXERR0 = 0x0e,
+ SJATXERR1 = 0x0f,
+/// Rx Message Counter (number of msgs. in RX FIFO
+ SJARMC = 0x1d,
+/// Rx Buffer Start Addr. (address of current MSG)
+ SJARBSA = 0x1e,
+/// Transmit Buffer (write) Receive Buffer (read) Frame Information
+ SJAFRM = 0x10,
+/// ID bytes (11 bits in 0 and 1 or 16 bits in 0,1 and 13 bits in 2,3 (extended))
+ SJAID0 = 0x11, SJAID1 = 0x12,
+/// ID cont. for extended frames
+ SJAID2 = 0x13, SJAID3 = 0x14,
+/// Data start standard frame
+ SJADATS = 0x13,
+/// Data start extended frame
+ SJADATE = 0x15,
+/// Acceptance Code (4 bytes) in RESET mode
+ SJAACR0 = 0x10,
+/// Acceptance Mask (4 bytes) in RESET mode
+ SJAAMR0 = 0x14,
+/// 4 bytes
+ SJA_PeliCAN_AC_LEN = 4,
+/// Clock Divider
+ SJACDR = 0x1f
+};
+
+/** Mode Register 0x00 */
+enum sja1000_PeliCAN_MOD {
+ MOD_SM = 1<<4, // Sleep Mode (writable only in OPERATING mode)
+ MOD_AFM= 1<<3, // Acceptance Filter Mode (writable only in RESET)
+ MOD_STM= 1<<2, // Self Test Mode (writable only in RESET)
+ MOD_LOM= 1<<1, // Listen Only Mode (writable only in RESET)
+ MOD_RM = 1 // Reset Mode
+};
+
+/** Command Register 0x01 */
+enum sja1000_PeliCAN_CMR {
+ CMR_SRR = 1<<4, // Self Reception Request (GoToSleep in BASIC mode)
+ CMR_CDO = 1<<3, // Clear Data Overrun
+ CMR_RRB = 1<<2, // Release Receive Buffer
+ CMR_AT = 1<<1, // Abort Transmission
+ CMR_TR = 1 }; // Transmission Request
+
+/** Status Register 0x02 */
+enum sja1000_SR {
+ SR_BS = 1<<7, // Bus Status
+ SR_ES = 1<<6, // Error Status
+ SR_TS = 1<<5, // Transmit Status
+ SR_RS = 1<<4, // Receive Status
+ SR_TCS = 1<<3, // Transmission Complete Status
+ SR_TBS = 1<<2, // Transmit Buffer Status
+ SR_DOS = 1<<1, // Data Overrun Status
+ SR_RBS = 1 }; // Receive Buffer Status
+
+/** Interrupt Enable Register 0x04 */
+enum sja1000_PeliCAN_IER {
+ IER_BEIE= 1<<7, // Bus Error Interrupt Enable
+ IER_ALIE= 1<<6, // Arbitration Lost Interrupt Enable
+ IER_EPIE= 1<<5, // Error Passive Interrupt Enable
+ IER_WUIE= 1<<4, // Wake-Up Interrupt Enable
+ IER_DOIE = 1<<3,// Data Overrun Interrupt Enable
+ IER_EIE = 1<<2, // Error Warning Interrupt Enable
+ IER_TIE = 1<<1, // Transmit Interrupt Enable
+ IER_RIE = 1, // Receive Interrupt Enable
+ ENABLE_INTERRUPTS = IER_BEIE|IER_EPIE|IER_DOIE|IER_EIE|IER_TIE|IER_RIE,
+ DISABLE_INTERRUPTS = 0
+// WARNING: the chip automatically enters RESET (bus off) mode when
+ // error counter > 255
+};
+
+/** Arbitration Lost Capture Register 0x0b.
+ * Counting starts from 0 (bit1 of ID). Bits 5-7 reserved*/
+enum sja1000_PeliCAN_ALC {
+ ALC_SRTR = 0x0b, // Arbitration lost in bit SRTR
+ ALC_IDE = 0x1c, // Arbitration lost in bit IDE
+ ALC_RTR = 0x1f, // Arbitration lost in RTR
+};
+
+/** Error Code Capture Register 0x0c*/
+enum sja1000_PeliCAN_ECC {
+ ECC_ERCC1 = 1<<7,
+ ECC_ERCC0 = 1<<6,
+ ECC_BIT = 0,
+ ECC_FORM = ECC_ERCC0,
+ ECC_STUFF = ECC_ERCC1,
+ ECC_OTHER = ECC_ERCC0 | ECC_ERCC1,
+ ECC_DIR = 1<<5, // 1 == RX, 0 == TX
+ ECC_SEG_M = (1<<5) -1 // Segment mask, see page 37 of SJA1000 Data Sheet
+};
+
+/** Frame format information 0x10 */
+enum sja1000_PeliCAN_FRM {
+ FRM_FF = 1<<7, // Frame Format 1 == extended, 0 == standard
+ FRM_RTR = 1<<6, // Remote request
+ FRM_DLC_M = (1<<4)-1 // Length Mask
+};
+
+
+/** Interrupt (status) Register 0x03 */
+enum sja1000_PeliCAN_IR {
+ IR_BEI = 1<<7, // Bus Error Interrupt
+ IR_ALI = 1<<6, // Arbitration Lost Interrupt
+ IR_EPI = 1<<5, // Error Passive Interrupt (entered error passive state or error active state)
+ IR_WUI = 1<<4, // Wake-Up Interrupt
+ IR_DOI = 1<<3, // Data Overrun Interrupt
+ IR_EI = 1<<2, // Error Interrupt
+ IR_TI = 1<<1, // Transmit Interrupt
+ IR_RI = 1 // Receive Interrupt
+};
+
+#if 0
+/** Bus Timing 1 Register 0x07 */
+enum sja1000_BTR1 {
+ MAX_TSEG1 = 15,
+ MAX_TSEG2 = 7
+};
+#endif
+
+/** Output Control Register 0x08 */
+enum sja1000_OCR {
+ OCR_MODE_BIPHASE = 0,
+ OCR_MODE_TEST = 1,
+ OCR_MODE_NORMAL = 2,
+ OCR_MODE_CLOCK = 3,
+/// TX0 push-pull not inverted
+ OCR_TX0_LH = 0x18,
+/// TX1 floating (off)
+ OCR_TX1_ZZ = 0
+};
+
+/** Clock Divider register 0x1f */
+enum sja1000_CDR {
+ CDR_PELICAN = 1<<7,
+/// bypass input comparator
+ CDR_CBP = 1<<6,
+/// switch TX1 to generate RX INT
+ CDR_RXINPEN = 1<<5,
+ CDR_CLK_OFF = 1<<3,
+/// f_out = f_osc/(2*(CDR[2:0]+1)) or f_osc if CDR[2:0]==7
+ CDR_CLKOUT_MASK = 7
+};
+
+/** flags for sja1000_baud_rate */
+#define BTR1_SAM (1<<1)
--- /dev/null
+/* smartcan.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int smartcan_request_io(unsigned long io_addr);
+int smartcan_release_io(unsigned long io_addr);
+int smartcan_reset(int card);
+int smartcan_init_hw_data(int card);
+int smartcan_init_chip_data(int card, int chipnr);
+int smartcan_init_obj_data(int chipnr,int objnr);
+void smartcan_write_register(unsigned char data, unsigned long address);
+unsigned smartcan_read_register(unsigned long address);
+
--- /dev/null
+/* ssv.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@casema.net
+ * This software is released under the GPL-License.
+ * Version 0.6 18 Sept 2000
+ */
+
+int ssv_request_io(unsigned long io_addr);
+int ssv_release_io(unsigned long io_addr);
+int ssv_reset(int card);
+int ssv_init_hw_data(int card);
+int ssv_init_chip_data(int card, int chipnr);
+int ssv_init_obj_data(int chipnr, int objnr);
+void ssv_write_register(unsigned char data, unsigned long address);
+unsigned ssv_read_register(unsigned long address);
+int ssv_program_irq(int card);
+
--- /dev/null
+/* template.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+int template_request_io(unsigned long io_addr);
+int template_release_io(unsigned long io_addr);
+int template_reset(int card);
+int template_init_hw_data(int card);
+int template_init_chip_data(int card, int chipnr);
+int template_init_obj_data(int chipnr, int objnr);
+void template_write_register(unsigned char data, unsigned long address);
+unsigned template_read_register(unsigned long address);
+int template_program_irq(int card);
+
--- /dev/null
+/* write.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+ssize_t can_write(struct file *file, const char *buffer, size_t length, loff_t *offset);
--- /dev/null
+/* aim104.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/aim104.h"
+#include "../include/sja1000.h"
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x20
+
+/**
+ * template_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function template_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int aim104_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/**
+ * template_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function template_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int aim104_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/**
+ * template_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function template_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int aim104_reset(int card)
+{
+ int i=0;
+
+ DEBUGMSG("Resetting aim104 hardware ...\n");
+
+ aim104_write_register(0x00, candevices_p[card]->io_addr + SJACR);
+
+ /* Check hardware reset status chip 0 */
+ i=0;
+ while ( (aim104_read_register(candevices_p[card]->io_addr + SJACR)
+ & CR_RR) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip reset status ok.\n");
+
+ return 0;
+}
+
+#define RESET_ADDR 0x0
+#define NR_82527 0
+#define NR_SJA1000 1
+
+/**
+ * template_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function template_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int aim104_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=0;
+ candevices_p[card]->nr_sja1000_chips=1;
+ candevices_p[card]->flags |= ~PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "sja1000"
+/**
+ * template_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function template_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int aim104_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->flags = 0;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = 0x08;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = 0xfa;
+
+ return 0;
+}
+
+/**
+ * template_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function template_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int aim104_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * template_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function template_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int aim104_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * template_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function template_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/template.c
+ */
+void aim104_write_register(unsigned char data, unsigned long address)
+{
+ outb(data,address);
+}
+
+/**
+ * template_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function template_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/template.c
+ */
+unsigned aim104_read_register(unsigned long address)
+{
+ return inb(address);
+}
+
+/* !!! Don't change this function !!! */
+int aim104_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = aim104_request_io;
+ hwspecops->release_io = aim104_release_io;
+ hwspecops->reset = aim104_reset;
+ hwspecops->init_hw_data = aim104_init_hw_data;
+ hwspecops->init_chip_data = aim104_init_chip_data;
+ hwspecops->init_obj_data = aim104_init_obj_data;
+ hwspecops->write_register = aim104_write_register;
+ hwspecops->read_register = aim104_read_register;
+ hwspecops->program_irq = aim104_program_irq;
+ return 0;
+}
--- /dev/null
+/* cc_can104.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/cc_can104.h"
+#include "../include/i82527.h"
+#include "../include/sja1000.h"
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x80
+
+/**
+ * template_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function template_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int cc104_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/**
+ * template_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function template_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int cc104_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/**
+ * template_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function template_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int cc104_reset(int card)
+{
+ return 0;
+}
+
+#define RESET_ADDR 0x0
+#define NR_82527 0
+#define NR_SJA1000 1
+
+/**
+ * template_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function template_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int cc104_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=0;
+ candevices_p[card]->nr_sja1000_chips=1;
+ candevices_p[card]->flags |= ~PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "sja1000"
+/**
+ * template_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function template_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int cc104_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->flags = 0;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = CDR_CBP | CDR_CLK_OFF;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = OCR_MODE_NORMAL |
+ OCR_TX0_LH;
+
+ return 0;
+}
+
+/**
+ * template_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function template_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int cc104_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * template_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function template_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int cc104_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * template_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function template_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/template.c
+ */
+void cc104_write_register(unsigned char data, unsigned long address)
+{
+ outb(data,address);
+}
+
+/**
+ * template_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function template_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/template.c
+ */
+unsigned cc104_read_register(unsigned long address)
+{
+ return inb(address);
+}
+
+/* !!! Don't change this function !!! */
+int cc104_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = cc104_request_io;
+ hwspecops->release_io = cc104_release_io;
+ hwspecops->reset = cc104_reset;
+ hwspecops->init_hw_data = cc104_init_hw_data;
+ hwspecops->init_chip_data = cc104_init_chip_data;
+ hwspecops->init_obj_data = cc104_init_obj_data;
+ hwspecops->write_register = cc104_write_register;
+ hwspecops->read_register = cc104_read_register;
+ hwspecops->program_irq = cc104_program_irq;
+ return 0;
+}
--- /dev/null
+/* close.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/fs.h>
+
+#include "../include/main.h"
+#include "../include/close.h"
+#include "../include/i82527.h"
+#include "../include/setup.h"
+
+int can_close(struct inode *inode, struct file *file)
+{
+ /* Give up message buffer memory */
+ if (objects_p[MINOR_NR]->fifo->buf_tx_entry)
+ del_mem_from_list(objects_p[MINOR_NR]->fifo->buf_tx_entry);
+ else
+ CANMSG("objects_p[MINOR_NR]->fifo->buf_tx_entry is NULL\n");
+ if (objects_p[MINOR_NR]->fifo->buf_rx_entry)
+ del_mem_from_list(objects_p[MINOR_NR]->fifo->buf_rx_entry);
+ else
+ CANMSG("objects_p[MINOR_NR]->fifo->buf_rx_entry is NULL\n");
+
+/* FIXME: what about clearing chip HW status, stopping sending messages etc? */
+
+ objects_p[MINOR_NR]->flags &= ~OPENED;
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
--- /dev/null
+/* i82527.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/fs.h>
+
+#include "../include/main.h"
+#include "../include/i82527.h"
+
+extern int stdmask;
+extern int extmask;
+extern int mo15mask;
+
+int i82527_enable_configuration(struct chip_t *chip)
+{
+ unsigned short flags=0;
+
+ flags = can_read_reg(chip, iCTL) & (iCTL_IE|iCTL_SIE|iCTL_EIE);
+ can_write_reg(chip, flags|iCTL_CCE, iCTL);
+
+ return 0;
+}
+
+int i82527_disable_configuration(struct chip_t *chip)
+{
+ unsigned short flags=0;
+
+ flags = can_read_reg(chip, iCTL) & (iCTL_IE|iCTL_SIE|iCTL_EIE);
+ can_write_reg(chip, flags, iCTL);
+
+ return 0;
+}
+
+int i82527_chip_config(struct chip_t *chip)
+{
+ can_write_reg(chip,chip->int_cpu_reg,iCPU); // Configure cpu interface
+ can_write_reg(chip,(iCTL_CCE|iCTL_INI),iCTL); // Enable configuration
+ can_write_reg(chip,chip->int_clk_reg,iCLK); // Set clock out slew rates
+ can_write_reg(chip,chip->int_bus_reg,iBUS); /* Bus configuration */
+ can_write_reg(chip,0x00,iSTAT); /* Clear error status register */
+
+ /* Check if we can at least read back some arbitrary data from the
+ * card. If we can not, the card is not properly configured!
+ */
+ can_write_reg(chip,0x25,MSG_OFFSET(1)+iMSGDAT1);
+ can_write_reg(chip,0x52,MSG_OFFSET(2)+iMSGDAT3);
+ can_write_reg(chip,0xc3,MSG_OFFSET(10)+iMSGDAT6);
+ if ( (can_read_reg(chip,MSG_OFFSET(1)+iMSGDAT1) != 0x25) ||
+ (can_read_reg(chip,MSG_OFFSET(2)+iMSGDAT3) != 0x52) ||
+ (can_read_reg(chip,MSG_OFFSET(10)+iMSGDAT6) != 0xc3) ) {
+ CANMSG("Could not read back from the hardware.\n");
+ CANMSG("This probably means that your hardware is not correctly configured!\n");
+ return -1;
+ }
+ else
+ DEBUGMSG("Could read back, hardware is probably configured correctly\n");
+
+ if (baudrate == 0)
+ baudrate=1000;
+
+ if (i82527_baud_rate(chip,baudrate*1000,chip->clock,0,75,0)) {
+ CANMSG("Error configuring baud rate\n");
+ return -ENODEV;
+ }
+ if (i82527_standard_mask(chip,0x0000,stdmask)) {
+ CANMSG("Error configuring standard mask\n");
+ return -ENODEV;
+ }
+ if (i82527_extended_mask(chip,0x00000000,extmask)) {
+ CANMSG("Error configuring extended mask\n");
+ return -ENODEV;
+ }
+ if (i82527_message15_mask(chip,0x00000000,mo15mask)) {
+ CANMSG("Error configuring message 15 mask\n");
+ return -ENODEV;
+ }
+ if (i82527_clear_objects(chip)) {
+ CANMSG("Error clearing message objects\n");
+ return -ENODEV;
+ }
+ if (i82527_config_irqs(chip,0x0a)) {
+ CANMSG("Error configuring interrupts\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Set communication parameters.
+ * param rate baud rate in Hz
+ * param clock frequency of i82527 clock in Hz (ISA osc is 14318000)
+ * param sjw synchronization jump width (0-3) prescaled clock cycles
+ * param sampl_pt sample point in % (0-100) sets (TSEG1+2)/(TSEG1+TSEG2+3) ratio
+ * param flags fields BTR1_SAM, OCMODE, OCPOL, OCTP, OCTN, CLK_OFF, CBP
+ */
+int i82527_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags)
+{
+ int best_error = 1000000000, error;
+ int best_tseg=0, best_brp=0, best_rate=0, brp=0;
+ int tseg=0, tseg1=0, tseg2=0;
+
+ if (i82527_enable_configuration(chip))
+ return -ENODEV;
+
+ clock /=2;
+
+ /* tseg even = round down, odd = round up */
+ for (tseg=(0+0+2)*2; tseg<=(MAX_TSEG2+MAX_TSEG1+2)*2+1; tseg++) {
+ brp = clock/((1+tseg/2)*rate)+tseg%2;
+ if (brp == 0 || brp > 64)
+ continue;
+ error = rate - clock/(brp*(1+tseg/2));
+ if (error < 0)
+ error = -error;
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg/2;
+ best_brp = brp-1;
+ best_rate = clock/(brp*(1+tseg/2));
+ }
+ }
+ if (best_error && (rate/best_error < 10)) {
+ CANMSG("baud rate %d is not possible with %d Hz clock\n",
+ rate, 2*clock);
+ CANMSG("%d bps. brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d\n",
+ best_rate, best_brp, best_tseg, tseg1, tseg2);
+ return -EINVAL;
+ }
+ tseg2 = best_tseg-(sampl_pt*(best_tseg+1))/100;
+ if (tseg2 < 0)
+ tseg2 = 0;
+ if (tseg2 > MAX_TSEG2)
+ tseg2 = MAX_TSEG2;
+
+ tseg1 = best_tseg-tseg2-2;
+ if (tseg1>MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg-tseg1-2;
+ }
+
+ DEBUGMSG("Setting %d bps.\n", best_rate);
+ DEBUGMSG("brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d, sampl_pt=%d\n",
+ best_brp, best_tseg, tseg1, tseg2,
+ (100*(best_tseg-tseg2)/(best_tseg+1)));
+
+
+ can_write_reg(chip, sjw<<6 | best_brp, iBT0);
+ can_write_reg(chip, ((flags & BTR1_SAM) != 0)<<7 | tseg2<<4 | tseg1,
+ iBT1);
+ DEBUGMSG("Writing 0x%x to iBT0\n",(sjw<<6 | best_brp));
+ DEBUGMSG("Writing 0x%x to iBT1\n",((flags & BTR1_SAM) != 0)<<7 |
+ tseg2<<4 | tseg1);
+
+ i82527_disable_configuration(chip);
+
+ return 0;
+}
+
+int i82527_standard_mask(struct chip_t *chip, unsigned short code, unsigned short mask)
+{
+ unsigned char mask0, mask1;
+
+ mask0 = (unsigned char) (mask >> 3);
+ mask1 = (unsigned char) (mask << 5);
+
+ can_write_reg(chip,mask0,iSGM0);
+ can_write_reg(chip,mask1,iSGM1);
+
+ DEBUGMSG("Setting standard mask to 0x%lx\n",(unsigned long)mask);
+
+ return 0;
+}
+
+int i82527_extended_mask(struct chip_t *chip, unsigned long code, unsigned long mask)
+{
+ unsigned char mask0, mask1, mask2, mask3;
+
+ mask0 = (unsigned char) (mask >> 21);
+ mask1 = (unsigned char) (mask >> 13);
+ mask2 = (unsigned char) (mask >> 5);
+ mask3 = (unsigned char) (mask << 3);
+
+ can_write_reg(chip,mask0,iEGM0);
+ can_write_reg(chip,mask1,iEGM1);
+ can_write_reg(chip,mask2,iEGM2);
+ can_write_reg(chip,mask3,iEGM3);
+
+ DEBUGMSG("Setting extended mask to 0x%lx\n",(unsigned long)mask);
+
+ return 0;
+}
+
+int i82527_message15_mask(struct chip_t *chip, unsigned long code, unsigned long mask)
+{
+ unsigned char mask0, mask1, mask2, mask3;
+
+ mask0 = (unsigned char) (mask >> 21);
+ mask1 = (unsigned char) (mask >> 13);
+ mask2 = (unsigned char) (mask >> 5);
+ mask3 = (unsigned char) (mask << 3);
+
+ can_write_reg(chip,mask0,i15M0);
+ can_write_reg(chip,mask1,i15M1);
+ can_write_reg(chip,mask2,i15M2);
+ can_write_reg(chip,mask3,i15M3);
+
+ DEBUGMSG("Setting message 15 mask to 0x%lx\n",mask);
+
+ return 0;
+
+
+}
+
+int i82527_clear_objects(struct chip_t *chip)
+{
+ int i=0,id=0,data=0;
+
+ DEBUGMSG("Cleared all message objects on chip\n");
+
+ for (i=1; i<=15; i++) {
+ can_write_reg(chip,(INTPD_RES|RXIE_RES|TXIE_RES|MVAL_RES) ,
+ MSG_OFFSET(i)+iMSGCTL0);
+ can_write_reg(chip,(NEWD_RES|MLST_RES|TXRQ_RES|RMPD_RES) ,
+ MSG_OFFSET(i)+iMSGCTL1);
+ for (data=0x07; data<0x0f; data++)
+ can_write_reg(chip,0x00,MSG_OFFSET(i)+data);
+ for (id=2; id<6; id++) {
+ can_write_reg(chip,0x00,MSG_OFFSET(i)+id);
+ }
+ if (extended==0) {
+ can_write_reg(chip,0x00,MSG_OFFSET(i)+iMSGCFG);
+ }
+ else {
+ can_write_reg(chip,MCFG_XTD,MSG_OFFSET(i)+iMSGCFG);
+ }
+ }
+ if (extended==0)
+ DEBUGMSG("All message ID's set to standard\n");
+ else
+ DEBUGMSG("All message ID's set to extended\n");
+
+ return 0;
+}
+
+int i82527_config_irqs(struct chip_t *chip, short irqs)
+{
+ can_write_reg(chip,irqs,iCTL);
+ DEBUGMSG("Configured hardware interrupt delivery\n");
+ return 0;
+}
+
+int i82527_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
+{
+ if (extended) {
+ can_write_reg(chip,MCFG_XTD,MSG_OFFSET(obj->object)+iMSGCFG);
+ }
+ else {
+ can_write_reg(chip,0x00,MSG_OFFSET(obj->object)+iMSGCFG);
+ }
+ can_write_reg(chip ,(NEWD_RES|MLST_RES|TXRQ_RES|RMPD_RES),
+ MSG_OFFSET(obj->object)+iMSGCTL1);
+ can_write_reg(chip ,(MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),
+ MSG_OFFSET(obj->object)+iMSGCTL0);
+
+ return 0;
+}
+
+int i82527_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ int i=0,id0=0,id1=0,id2=0,id3=0;
+
+ can_write_reg(chip,(RMPD_RES|TXRQ_RES|CPUU_SET|NEWD_RES),
+ MSG_OFFSET(obj->object)+iMSGCTL1);
+ can_write_reg(chip,(MVAL_SET|TXIE_SET|RXIE_RES|INTPD_RES),
+ MSG_OFFSET(obj->object)+iMSGCTL0);
+ if (extended) {
+ can_write_reg(chip,(msg->length<<4)+(MCFG_DIR|MCFG_XTD),
+ MSG_OFFSET(obj->object)+iMSGCFG);
+ }
+ else {
+ can_write_reg(chip,(msg->length<<4)+MCFG_DIR,
+ MSG_OFFSET(obj->object)+iMSGCFG);
+ }
+ if (extended) {
+ id0 = (unsigned char) (msg->id<<3);
+ id1 = (unsigned char) (msg->id>>5);
+ id2 = (unsigned char) (msg->id>>13);
+ id3 = (unsigned char) (msg->id>>21);
+ can_write_reg(chip,id0,MSG_OFFSET(obj->object)+iMSGID3);
+ can_write_reg(chip,id1,MSG_OFFSET(obj->object)+iMSGID2);
+ can_write_reg(chip,id2,MSG_OFFSET(obj->object)+iMSGID1);
+ can_write_reg(chip,id3,MSG_OFFSET(obj->object)+iMSGID0);
+ }
+ else {
+ id1 = (unsigned char) (msg->id<<5);
+ id0 = (unsigned char) (msg->id>>3);
+ can_write_reg(chip,id1,MSG_OFFSET(obj->object)+iMSGID1);
+ can_write_reg(chip,id0,MSG_OFFSET(obj->object)+iMSGID0);
+ }
+ can_write_reg(chip,0xfa,MSG_OFFSET(obj->object)+iMSGCTL1);
+ for (i=0; i<msg->length; i++) {
+ can_write_reg(chip,msg->data[i],MSG_OFFSET(obj->object)+
+ iMSGDAT0+i);
+ }
+
+ return 0;
+}
+
+int i82527_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ if (msg->flags & MSG_RTR) {
+ can_write_reg(chip,(RMPD_RES|TXRQ_RES|CPUU_RES|NEWD_SET),
+ MSG_OFFSET(obj->object)+iMSGCTL1);
+ }
+ else {
+ can_write_reg(chip,(RMPD_RES|TXRQ_SET|CPUU_RES|NEWD_SET),
+ MSG_OFFSET(obj->object)+iMSGCTL1);
+ }
+
+ return 0;
+}
+
+int i82527_check_tx_stat(struct chip_t *chip)
+{
+ if (can_read_reg(chip,iSTAT) & iSTAT_TXOK) {
+ can_write_reg(chip,0x0,iSTAT);
+ return 0;
+ }
+ else {
+ can_write_reg(chip,0x0,iSTAT);
+ return 1;
+ }
+}
+
+int i82527_remote_request(struct chip_t *chip, struct msgobj_t *obj)
+{
+ can_write_reg(chip, (MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),
+ MSG_OFFSET(obj->object)+iMSGCTL0);
+ can_write_reg(chip, (RMPD_RES|TXRQ_SET|MLST_RES|NEWD_RES),
+ MSG_OFFSET(obj->object)+iMSGCTL1);
+
+ return 0;
+}
+
+int i82527_set_btregs(struct chip_t *chip, unsigned short btr0,
+ unsigned short btr1)
+{
+ if (i82527_enable_configuration(chip))
+ return -ENODEV;
+
+ can_write_reg(chip, btr0, iBT0);
+ can_write_reg(chip, btr1, iBT1);
+
+ i82527_disable_configuration(chip);
+
+ return 0;
+}
+
+int i82527_start_chip(struct chip_t *chip)
+{
+ unsigned short flags = 0;
+
+ flags = can_read_reg(chip, iCTL) & (iCTL_IE|iCTL_SIE|iCTL_EIE);
+ can_write_reg(chip, flags, iCTL);
+
+ return 0;
+}
+
+int i82527_stop_chip(struct chip_t *chip)
+{
+ unsigned short flags = 0;
+
+ flags = can_read_reg(chip, iCTL) & (iCTL_IE|iCTL_SIE|iCTL_EIE);
+ can_write_reg(chip, flags|(iCTL_CCE|iCTL_INI), iCTL);
+
+ return 0;
+}
+
+int i82527_register(struct chipspecops_t *chipspecops)
+{
+ chipspecops->chip_config = i82527_chip_config;
+ chipspecops->baud_rate = i82527_baud_rate;
+ chipspecops->standard_mask = i82527_standard_mask;
+ chipspecops->extended_mask = i82527_extended_mask;
+ chipspecops->message15_mask = i82527_message15_mask;
+ chipspecops->clear_objects = i82527_clear_objects;
+ chipspecops->config_irqs = i82527_config_irqs;
+ chipspecops->pre_read_config = i82527_pre_read_config;
+ chipspecops->pre_write_config = i82527_pre_write_config;
+ chipspecops->send_msg = i82527_send_msg;
+ chipspecops->check_tx_stat = i82527_check_tx_stat;
+ chipspecops->remote_request = i82527_remote_request;
+ chipspecops->enable_configuration = i82527_enable_configuration;
+ chipspecops->disable_configuration = i82527_disable_configuration;
+ chipspecops->set_btregs = i82527_set_btregs;
+ chipspecops->start_chip = i82527_start_chip;
+ chipspecops->stop_chip = i82527_stop_chip;
+ return 0;
+}
--- /dev/null
+/* ioctl.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/fs.h>
+#include <linux/version.h>
+
+#include "../include/main.h"
+#include "../include/ioctl.h"
+#include "../include/i82527.h"
+
+int can_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int i=0;
+ unsigned short channel=0;
+ unsigned btr0=0, btr1=0;
+ struct msgobj_t *obj;
+ struct chip_t *chip = objects_p[MINOR_NR]->hostchip;
+ struct canfifo_t *fifo = objects_p[MINOR_NR]->fifo;
+
+ /* Initialize hardware pointers */
+ if ( (obj = objects_p[MINOR_NR]) == NULL) {
+ CANMSG("Could not assign buffer structure\n");
+ return -1;
+ }
+ if ( (chip = obj->hostchip) == NULL) {
+ CANMSG("Device is not correctly configured.\n");
+ CANMSG("Please reload the driver.\n");
+ return -1;
+ }
+
+ switch (cmd) {
+ case STAT: {
+ for (i=0x0; i<0x100; i++)
+ CANMSG("0x%x is 0x%x\n",i,can_read_reg(chip,i));
+ break;
+ }
+ case CMD_START: {
+ if (chips_p[arg]->chipspecops->start_chip(chip))
+ return -1;
+ break;
+ }
+ case CMD_STOP: {
+ if (chips_p[arg]->chipspecops->stop_chip(chip))
+ return -1;
+ break;
+ }
+ case CONF_FILTER: {
+ if (!strcmp(chip->chip_type,"i82527")) {
+
+ unsigned char id1, id0;
+ id1 = (unsigned char) (arg << 5);
+ id0 = (unsigned char) (arg >> 3);
+
+ DEBUGMSG("configuring ID=%lx in message object:
+ %02x, %02x\n", arg, id0, id1);
+ can_write_reg(chip,id1,MSG_OFFSET(obj->object) +
+ iMSGID1);
+ can_write_reg(chip,id0,MSG_OFFSET(obj->object) +
+ iMSGID0);
+ }
+
+ /* In- and output buffer re-initialization */
+
+ fifo->tx_readp = fifo->buf_tx_entry;
+ fifo->tx_writep = fifo->buf_tx_entry;
+ fifo->rx_readp = fifo->buf_rx_entry;
+ fifo->rx_writep = fifo->buf_rx_entry;
+ fifo->rx_size= MAX_BUF_LENGTH * sizeof(struct canmsg_t);
+ fifo->tx_size = fifo->rx_size;
+
+ #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ init_waitqueue(&fifo->readq);
+ init_waitqueue(&fifo->writeq);
+ #else
+ init_waitqueue_head(&fifo->readq);
+ init_waitqueue_head(&fifo->writeq);
+ #endif
+
+ fifo->rx_in_progress = 0;
+ fifo->tx_in_progress = 0;
+
+ fifo->head = fifo->tail = 0; //TEMP!!
+
+ break;
+ }
+
+ case CONF_BAUD: {
+ channel = arg & 0xff;
+ btr0 = (arg >> 8) & 0xff;
+ btr1 = (arg >> 16) & 0xff;
+
+ if (chips_p[channel]->chipspecops->set_btregs(chip,
+ btr0, btr1)) {
+ CANMSG("Error setting bit timing registers\n");
+ return -1;
+ }
+ break;
+ }
+ default: {
+ CANMSG("Not a valid ioctl command\n");
+ return -EINVAL;
+ }
+
+ }
+
+ return 0;
+}
--- /dev/null
+/* irq.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+
+#include "../include/main.h"
+#include "../include/irq.h"
+#include "../include/i82527.h"
+#include "../include/sja1000.h"
+
+void i82527_irq_rtr_handler(void);
+void sja1000_irq_read_handler(void);
+void sja1000_irq_write_handler(void);
+
+void (*put_reg)(unsigned char data, unsigned long address);
+unsigned (*get_reg)(unsigned long address);
+
+struct chip_t *chip=NULL;
+struct candevice_t *device=NULL;
+unsigned object=0,irq_register=0;
+unsigned long msgbase=0;
+struct canfifo_t *fifo=NULL;
+unsigned long message_id=0;
+struct rtr_id *rtr_search;
+
+inline void i82527_irq_write_handler(void)
+{
+ put_reg((MVAL_RES|TXIE_RES|RXIE_RES|INTPD_RES),msgbase+iMSGCTL0);
+
+ fifo->tx_readp++;
+ if (fifo->tx_readp >= fifo->buf_tx_entry + MAX_BUF_LENGTH)
+ fifo->tx_readp = fifo->buf_tx_entry;
+ if (fifo->tx_readp == fifo->tx_writep) { // Output buffer is empty
+ fifo->tx_in_progress = 0;
+ if (waitqueue_active(&fifo->writeq)) {
+ chip->msgobj[object]->ret = 0;
+ wake_up_interruptible(&fifo->writeq);
+ }
+ return;
+ }
+ if (chip->chipspecops->pre_write_config(chip, chip->msgobj[object],
+ fifo->tx_readp)) {
+ if (waitqueue_active(&fifo->writeq)) {
+ chip->msgobj[object]->ret = -1;
+ wake_up_interruptible(&fifo->writeq);
+ return;
+ }
+ }
+ if (chip->chipspecops->send_msg(chip, chip->msgobj[object],
+ fifo->tx_readp)) {
+ if (waitqueue_active(&fifo->writeq)) {
+ chip->msgobj[object]->ret = -1;
+ wake_up_interruptible(&fifo->writeq);
+ return;
+ }
+ }
+}
+
+inline void i82527_irq_read_handler(void)
+{
+ int i=0, tmp=1 ;
+
+ while (tmp) {
+ put_reg((RMPD_RES|TXRQ_RES|MLST_RES|NEWD_RES), msgbase +
+ iMSGCTL1);
+ put_reg((MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES), msgbase +
+ iMSGCTL0);
+
+ fifo->rx_writep->length =(get_reg(msgbase+iMSGCFG) & 0xf0) >> 4;
+ fifo->rx_writep->id = message_id;
+ for (i=0; i < fifo->rx_writep->length; i++)
+ fifo->rx_writep->data[i] = get_reg(msgbase+iMSGDAT0+i);
+
+//FIXME: Add buffer overflow check, currently it's silently over written!
+
+ fifo->rx_writep++;
+ if (fifo->rx_writep >= fifo->buf_rx_entry + MAX_BUF_LENGTH)
+ fifo->rx_writep = fifo->buf_rx_entry;
+
+ if (!((tmp=get_reg(msgbase + iMSGCTL1)) & NEWD_SET)) {
+ break;
+ }
+
+ if (tmp & MLST_SET)
+ CANMSG("Message lost!\n");
+
+ }
+ if (waitqueue_active(&fifo->readq)) {
+ wake_up_interruptible(&fifo->readq);
+ }
+}
+
+void i82527_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int id0=0, id1=0, id2=0, id3=0;
+
+ chip=(struct chip_t *)dev_id;
+ device=(struct candevice_t *)chip->hostdevice;
+
+ put_reg=device->hwspecops->write_register;
+ get_reg=device->hwspecops->read_register;
+
+ if ( (chip->flags & SEGMENTED) != 0)
+ irq_register = get_reg(chip->chip_base_addr+iIRQ+SPACING);
+ else
+ irq_register = get_reg(chip->chip_base_addr+iIRQ);
+
+ while (irq_register) {
+
+ if (irq_register == 0x01) {
+ DEBUGMSG("Status register: 0x%x\n",get_reg(chip->chip_base_addr+iSTAT));
+ return;
+ }
+
+ if (irq_register == 0x02)
+ object = 14;
+ else
+ object = irq_register-3;
+
+ fifo=chip->msgobj[object]->fifo;
+ msgbase=chip->msgobj[object]->obj_base_addr;
+
+ if (get_reg(msgbase+iMSGCFG) & MCFG_DIR) {
+ i82527_irq_write_handler();
+ }
+ else {
+
+ if (extended) {
+ id0=get_reg(msgbase+iMSGID3);
+ id1=get_reg(msgbase+iMSGID2)<<8;
+ id2=get_reg(msgbase+iMSGID1)<<16;
+ id3=get_reg(msgbase+iMSGID0)<<24;
+ message_id=(id0|id1|id2|id3)>>3;
+ }
+ else {
+ id0=get_reg(msgbase+iMSGID1);
+ id1=get_reg(msgbase+iMSGID0)<<8;
+ message_id=(id0|id1)>>5;
+ }
+
+ spin_lock(&hardware_p->rtr_lock);
+ rtr_search=hardware_p->rtr_queue;
+ while (rtr_search != NULL) {
+ if (rtr_search->id == message_id)
+ break;
+ rtr_search=rtr_search->next;
+ }
+ spin_unlock(&hardware_p->rtr_lock);
+ if ((rtr_search!=NULL) && (rtr_search->id==message_id))
+ i82527_irq_rtr_handler();
+ else
+ i82527_irq_read_handler();
+ }
+
+ if ( (chip->flags & SEGMENTED) != 0)
+ irq_register=get_reg(chip->chip_base_addr+iIRQ+SPACING);
+ else
+ irq_register=get_reg(chip->chip_base_addr+iIRQ);
+ }
+
+}
+
+void i82527_irq_rtr_handler(void)
+{
+ short int i=0;
+
+ put_reg((MVAL_RES|TXIE_RES|RXIE_RES|INTPD_RES),msgbase + iMSGCTL0);
+ put_reg((RMPD_RES|TXRQ_RES|MLST_RES|NEWD_RES),msgbase + iMSGCTL1);
+
+ spin_lock(&hardware_p->rtr_lock);
+
+ rtr_search->rtr_message->id=message_id;
+ rtr_search->rtr_message->length=(get_reg(msgbase + iMSGCFG) & 0xf0)>>4;
+ for (i=0; i<rtr_search->rtr_message->length; i++)
+ rtr_search->rtr_message->data[i]=get_reg(msgbase+iMSGDAT0+i);
+
+ spin_unlock(&hardware_p->rtr_lock);
+
+ if (waitqueue_active(&rtr_search->rtr_wq))
+ wake_up_interruptible(&rtr_search->rtr_wq);
+}
+
+void sja1000_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ chip=(struct chip_t *)dev_id;
+ device=(struct candevice_t *)chip->hostdevice;
+
+ put_reg=device->hwspecops->write_register;
+ get_reg=device->hwspecops->read_register;
+
+ irq_register=get_reg(chip->chip_base_addr+SJAIR);
+// DEBUGMSG("sja1000_irq_handler: SJAIR:%02x\n",irq_register);
+// DEBUGMSG("sja1000_irq_handler: SJASR:%02x\n",
+// get_reg(chip->chip_base_addr+SJASR));
+
+ if ((irq_register & (IR_WUI|IR_DOI|IR_EI|IR_TI|IR_RI)) == 0)
+ return;
+
+ fifo=chip->msgobj[0]->fifo;
+ msgbase=chip->msgobj[0]->obj_base_addr;
+
+ if ((irq_register & IR_RI) != 0)
+ sja1000_irq_read_handler();
+ if ((irq_register & IR_TI) != 0)
+ sja1000_irq_write_handler();
+ if ((irq_register & (IR_EI|IR_DOI)) != 0) {
+ // Some error happened
+// FIXME: chip should be brought to usable state. Transmission cancelled if in progress.
+// Reset flag set to 0 if chip is already off the bus. Full state report
+ CANMSG("Error: status register: 0x%x irq_register: 0x%02x\n",
+ get_reg(chip->chip_base_addr+SJASR), irq_register);
+ chip->msgobj[0]->ret=-1;
+ if (waitqueue_active(&fifo->writeq))
+ wake_up_interruptible(&fifo->writeq);
+ if (waitqueue_active(&fifo->readq))
+ wake_up_interruptible(&fifo->readq);
+ }
+
+ return;
+}
+void sja1000_irq_read_handler(void)
+{
+ int i=0, id=0, tmp=1;
+
+ while (tmp) {
+ id = get_reg(msgbase+SJARXID0) | (get_reg(msgbase+SJARXID1)<<8);
+ fifo->buf_rx_entry[fifo->head].length = id & 0x0f;
+ fifo->buf_rx_entry[fifo->head].flags = id&ID0_RTR ? MSG_RTR : 0;
+ fifo->buf_rx_entry[fifo->head].timestamp = 0;
+ fifo->buf_rx_entry[fifo->head].cob = 0;
+ fifo->buf_rx_entry[fifo->head].id = id>>5;
+
+ for (i=0; i<fifo->buf_rx_entry[fifo->head].length; i++)
+ fifo->buf_rx_entry[fifo->head].data[i]=get_reg(msgbase +
+ SJARXDAT0 + i);
+
+ put_reg(CMR_RRB,msgbase+SJACMR);
+
+ fifo->head++;
+ if (fifo->head >= MAX_BUF_LENGTH-1)
+ fifo->head=0;
+
+ tmp=(get_reg(msgbase+SJASR) & SR_RBS);
+ }
+ if (waitqueue_active(&fifo->readq))
+ wake_up_interruptible(&fifo->readq);
+}
+
+void sja1000_irq_write_handler(void)
+{
+ fifo->tx_readp++;
+ if (fifo->tx_readp >= fifo->buf_tx_entry + MAX_BUF_LENGTH)
+ fifo->tx_readp = fifo->buf_tx_entry;
+ if (fifo->tx_readp == fifo->tx_writep) { // Output buffer is empty
+ fifo->tx_in_progress = 0;
+ if (waitqueue_active(&fifo->writeq)) {
+ chip->msgobj[object]->ret = 0;
+ wake_up_interruptible(&fifo->writeq);
+ }
+ return;
+ }
+ if (chip->chipspecops->pre_write_config(chip, chip->msgobj[object],
+ fifo->tx_readp)) {
+ if (waitqueue_active(&fifo->writeq)) {
+ chip->msgobj[object]->ret = -1;
+ wake_up_interruptible(&fifo->writeq);
+ return;
+ }
+ }
+ if (chip->chipspecops->send_msg(chip, chip->msgobj[object],
+ fifo->tx_readp)) {
+ if (waitqueue_active(&fifo->writeq)) {
+ chip->msgobj[object]->ret = -1;
+ wake_up_interruptible(&fifo->writeq);
+ return;
+ }
+ }
+}
--- /dev/null
+/* m437.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+/*
+ * Support for the SECO M437
+ *
+ * SECO M437 is a pc104 format, i82527 controller based card
+ * produced by SECO http://www.seco.it
+ * This driver uses the Memory Mapped I/O mode, and should be
+ * working with all cards supporting this mode.
+ *
+ * Written by Fabio Parodi (fabio.parodi@iname.com)
+ * Additional changes by Giampiero Giancipoli (gianci@freemail.it)
+ *
+ * Version 0.1 08 Jun 2001
+ *
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/m437.h"
+#include "../include/i82527.h"
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x100
+
+
+static long base = 0L;
+
+/**
+ * m437_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function m437_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/m437.c
+ */
+int m437_request_io(unsigned long io_addr)
+{
+
+ if ( !( base = (long) ioremap( io_addr, IO_RANGE ) ) ) {
+ CANMSG("Unable to access I/O memory at: 0x%lx\n", io_addr);
+ return -ENODEV;
+
+ }
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ return 0;
+}
+
+/**
+ * m437_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function m437_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/m437.c
+ */
+int m437_release_io(unsigned long io_addr)
+{
+ unsigned i;
+
+ /* disable IRQ generation */
+ m437_write_register(iCTL_CCE, iCTL);
+
+ /* clear all message objects */
+ for (i=1; i<=15; i++) {
+ m437_write_register(
+ INTPD_RES |
+ RXIE_RES |
+ TXIE_RES |
+ MVAL_RES,
+ i*0x10+iMSGCTL0);
+ m437_write_register(
+ NEWD_RES |
+ MLST_RES |
+ CPUU_RES |
+ TXRQ_RES |
+ RMPD_RES,
+ i*0x10+iMSGCTL1);
+ }
+
+ /* power down i82527 */
+ m437_write_register(iCPU_PWD, iCPU);
+
+ /* release I/O memory mapping */
+ iounmap((void*)base);
+
+ return 0;
+}
+
+/**
+ * m437_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function m437_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/m437.c
+ */
+int m437_reset(int card)
+{
+ return 0;
+}
+
+#define RESET_ADDR 0x0
+#define NR_82527 1
+#define NR_SJA1000 0
+
+/**
+ * m437_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function m437_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/m437.c
+ */
+int m437_init_hw_data(int card)
+{
+ DEBUGMSG("m437_init_hw_data()\n");
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=1;
+ candevices_p[card]->nr_sja1000_chips=0;
+ candevices_p[card]->flags &= ~PROGRAMMABLE_IRQ;
+ /* The M437 has no programmable IRQ */
+
+ return 0;
+}
+
+#define CHIP_TYPE "i82527"
+/**
+ * m437_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function m437_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * The entry @int_cpu_reg holds hardware specific options for the cpu interface
+ * register. Options defined in the %i82527.h file:
+ * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST.
+ * Return Value: The function always returns zero
+ * File: src/m437.c
+ */
+int m437_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC | iCPU_CEN;
+ candevices_p[card]->chip[chipnr]->int_clk_reg =
+ iCLK_CD0 | iCLK_CD1 | iCLK_CD2 | iCLK_SL0 | iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY;
+
+ return 0;
+}
+
+/**
+ * m437_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function m437_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/m437.c
+ */
+int m437_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr+(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * m437_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function m437_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/m437.c
+ */
+int m437_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * m437_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function m437_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/m437.c
+ */
+void m437_write_register(unsigned char data, unsigned long address)
+{
+ writeb(data,base+address);
+}
+
+/**
+ * m437_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function m437_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/m437.c
+ */
+unsigned m437_read_register(unsigned long address)
+{
+ return readb(base+address);
+}
+
+/* !!! Don't change this function !!! */
+int m437_register(struct hwspecops_t *hwspecops)
+{
+ DEBUGMSG("m437_register()\n");
+ hwspecops->request_io = m437_request_io;
+ hwspecops->release_io = m437_release_io;
+ hwspecops->reset = m437_reset;
+ hwspecops->init_hw_data = m437_init_hw_data;
+ hwspecops->init_chip_data = m437_init_chip_data;
+ hwspecops->init_obj_data = m437_init_obj_data;
+ hwspecops->write_register = m437_write_register;
+ hwspecops->read_register = m437_read_register;
+ hwspecops->program_irq = m437_program_irq;
+ return 0;
+}
--- /dev/null
+/* main.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#define EXPORT_SYMTAB
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#include <linux/module.h>
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/wrapper.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/autoconf.h>
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+#include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
+
+#if !defined (__GENKSYMS__)
+#if (defined (MODVERSIONS) && !defined(NOVER))
+#include <linux/modversions.h>
+#include "../include/main.ver"
+#endif
+#endif
+
+#include "../include/main.h"
+#include "../include/modparms.h"
+#include "../include/setup.h"
+#include "../include/proc.h"
+#include "../include/open.h"
+#include "../include/close.h"
+#include "../include/read.h"
+#include "../include/irq.h"
+#include "../include/ioctl.h"
+#include "../include/write.h"
+
+#define EXPORT_SYMTAB
+
+/* Module parameters, some must be supplied at module loading time */
+int major=CAN_MAJOR;
+MODULE_PARM(major,"1i");
+int minor[MAX_TOT_CHIPS]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+MODULE_PARM(minor, "1-" __MODULE_STRING(MAX_TOT_CHIPS)"i");
+int extended=0;
+MODULE_PARM(extended,"1i");
+int pelican=0;
+MODULE_PARM(pelican,"1i");
+int baudrate=0;
+MODULE_PARM(baudrate,"1i");
+char *hw[MAX_HW_CARDS]={NULL,};
+MODULE_PARM(hw, "1-" __MODULE_STRING(MAX_HW_CARDS)"s");
+int irq[MAX_IRQ]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_IRQ)"i");
+unsigned long io[MAX_HW_CARDS]={-1,-1,-1,-1,-1,-1,-1,-1};
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_HW_CARDS)"i");
+int stdmask=0;
+MODULE_PARM(stdmask, "1i");
+int extmask=0;
+MODULE_PARM(extmask, "1i");
+int mo15mask=0;
+MODULE_PARM(mo15mask, "1i");
+
+/* Global structures, used to describe the installed hardware. */
+struct canhardware_t canhardware;
+struct canhardware_t *hardware_p=&canhardware;
+struct candevice_t *candevices_p[MAX_HW_CARDS];
+struct chip_t *chips_p[MAX_TOT_CHIPS];
+struct msgobj_t *objects_p[MAX_TOT_MSGOBJS];
+
+/* Pointers to dynamically allocated memory are maintained in a linked list
+ * to ease memory deallocation.
+ */
+struct mem_addr *mem_head=NULL;
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+struct file_operations can_fops=
+{
+ NULL, /* llseek */
+ read: can_read,
+ write: can_write,
+ NULL, /* readdir */
+ NULL, /* poll */
+ ioctl: can_ioctl,
+ NULL, /* mmap */
+ open: can_open,
+ NULL, /* flush */
+ release: can_close,
+ NULL, /* fsync */
+};
+#else
+struct file_operations can_fops=
+{
+ owner: THIS_MODULE,
+ read: can_read,
+ write: can_write,
+ ioctl: can_ioctl,
+ open: can_open,
+ release: can_close,
+};
+#endif
+
+EXPORT_SYMBOL(can_fops);
+
+int init_module(void)
+{
+ int res=0,i=0;
+
+ if (parse_mod_parms())
+ return -EINVAL;
+
+ if (init_hw_struct())
+ return -ENODEV;
+
+ #ifdef CAN_DEBUG
+ list_hw();
+ #endif
+
+ res=register_chrdev(major,DEVICE_NAME, &can_fops);
+ if (res<0) {
+ CANMSG("Error registering driver.\n");
+ return -ENODEV;
+ }
+
+ for (i=0; i<hardware_p->nr_boards; i++) {
+ if (candevices_p[i]->hwspecops->request_io(candevices_p[i]->io_addr))
+ goto memory_error;
+ }
+
+ for (i=0; i<hardware_p->nr_boards; i++) {
+ if (candevices_p[i]->hwspecops->reset(i))
+ goto reset_error;
+ }
+
+ i=0;
+ while ( (chips_p[i] != NULL) && (i < MAX_TOT_CHIPS) ) {
+ if (!strcmp(chips_p[i]->chip_type,"i82527")) {
+ if (request_irq(chips_p[i]->chip_irq,i82527_irq_handler,SA_SHIRQ,DEVICE_NAME,chips_p[i]))
+ goto interrupt_error;
+ else
+ DEBUGMSG("Registered interrupt %d\n",chips_p[i]->chip_irq);
+ }
+ if (!strcmp(chips_p[i]->chip_type,"sja1000p") ||
+ !strcmp(chips_p[i]->chip_type,"sja1000")) {
+ if (request_irq(chips_p[i]->chip_irq,
+ chips_p[i]->chipspecops->irq_handler,SA_SHIRQ,DEVICE_NAME,chips_p[i]))
+ goto interrupt_error;
+ else
+ DEBUGMSG("Registered interrupt %d\n",chips_p[i]->chip_irq);
+ }
+ i++;
+ }
+
+ for (i=0; i<hardware_p->nr_boards; i++) {
+ if (candevices_p[i]->flags & PROGRAMMABLE_IRQ)
+ if (candevices_p[i]->hwspecops->program_irq(i))
+ goto interrupt_error;
+ }
+
+ spin_lock_init(&hardware_p->rtr_lock);
+ hardware_p->rtr_queue=NULL;
+
+#ifdef CONFIG_PROC_FS
+ if (can_init_procdir())
+ goto proc_error;
+#endif
+
+ return 0;
+
+#ifdef CONFIG_PROC_FS
+ proc_error: ;
+ CANMSG("Error registering /proc entry.\n");
+ goto memory_error;
+#endif
+
+ interrupt_error: ;
+ CANMSG("Error registering interrupt line.\n");
+ goto memory_error;
+
+ reset_error: ;
+ goto memory_error;
+
+ memory_error: ;
+ for (i=0; i<hardware_p->nr_boards; i++)
+ candevices_p[i]->hwspecops->release_io(candevices_p[i]->io_addr);
+ goto register_error;
+
+ register_error: ;
+ res=unregister_chrdev(major,DEVICE_NAME);
+ if (res<0)
+ CANMSG("Error unloading CAN driver, error: %d\n",res);
+ else
+ CANMSG("Successfully unloaded CAN driver.\n");
+ return -ENODEV;
+
+}
+
+void cleanup_module(void)
+{
+ int res=0,i=0;
+
+#ifdef CONFIG_PROC_FS
+ if (can_delete_procdir())
+ CANMSG("Error unregistering /proc/can entry.\n");
+#endif
+
+ while ( (chips_p[i] != NULL) & (i < MAX_TOT_CHIPS) ) {
+ free_irq(chips_p[i]->chip_irq, chips_p[i]);
+ i++;
+ }
+
+ for (i=0; i<hardware_p->nr_boards; i++)
+ candevices_p[i]->hwspecops->release_io(candevices_p[i]->io_addr);
+
+ if ( del_mem_list() )
+ CANMSG("Error deallocating memory\n");
+
+ res=unregister_chrdev(major,DEVICE_NAME);
+ if (res<0)
+ CANMSG("Error unregistering CAN driver, error: %d\n",res);
+}
--- /dev/null
+/* mod_parms.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include "../.support"
+#ifndef PIP
+#define PIP 0
+#endif
+#ifndef SMARTCAN
+#define SMARTCAN 0
+#endif
+#ifndef PCCAN
+#define PCCAN 0
+#endif
+#ifndef NSI
+#define NSI 0
+#endif
+#ifndef CC104
+#define CC104 0
+#endif
+#ifndef AIM104
+#define AIM104 0
+#endif
+#ifndef PCI03
+#define PCI03 0
+#endif
+#ifndef PCM3680
+#define PCM3680 0
+#endif
+#ifndef M437
+#define M437 0
+#endif
+#ifndef PCCCAN
+#define PCCCAN 0
+#endif
+#ifndef SSV
+#define SSV 0
+#endif
+#ifndef TEMPLATE
+#define TEMPLATE 0
+#endif
+
+#include <linux/string.h>
+#include <linux/fs.h>
+
+#include "../include/main.h"
+#include "../include/modparms.h"
+
+int parse_mod_parms(void)
+{
+ int i=0,j=0,irq_needed=0,irq_supplied=0,io_needed=0,io_supplied=0,minor_needed=0,minor_supplied=0;
+
+ if ( (hw[0] == NULL) | (irq[0] == -1) | (io[0] == -1) ) {
+ CANMSG("You must supply your type of hardware, interrupt numbers and io address.\n");
+ CANMSG("Example: # insmod can.o hw=pip5 irq=4 io=0x8000\n");
+ return -ENODEV;
+ }
+
+ while ( (hw[i] != NULL) && (i < MAX_HW_CARDS) ) {
+ if ( !strcmp(hw[i],"pip5") && PIP )
+ irq_needed++;
+ else if (!strcmp(hw[i],"pip6") && PIP)
+ irq_needed++;
+ else if (!strcmp(hw[i],"smartcan") && SMARTCAN)
+ irq_needed++;
+ else if (!strcmp(hw[i],"pccan-q") && PCCAN)
+ irq_needed=irq_needed+4;
+ else if (!strcmp(hw[i],"pccan-f") && PCCAN)
+ irq_needed++;
+ else if (!strcmp(hw[i],"pccan-s") && PCCAN)
+ irq_needed++;
+ else if (!strcmp(hw[i],"pccan-d") && PCCAN)
+ irq_needed=irq_needed+2;
+ else if (!strcmp(hw[i],"nsican") && NSI)
+ irq_needed++;
+ else if (!strcmp(hw[i],"cc104") && CC104)
+ irq_needed++;
+ else if (!strcmp(hw[i],"aim104") && AIM104)
+ irq_needed++;
+ else if (!strcmp(hw[i],"pc-i03") && PCI03)
+ irq_needed++;
+ else if (!strcmp(hw[i],"pcm3680") && PCM3680)
+ irq_needed=irq_needed+2;
+ else if (!strcmp(hw[i],"m437") && M437)
+ irq_needed++;
+ else if (!strcmp(hw[i],"pcccan") && PCCCAN)
+ irq_needed++;
+ else if (!strcmp(hw[i],"ssv") && SSV)
+ irq_needed=irq_needed+2;
+ else if (!strcmp(hw[i],"template") && TEMPLATE);
+ else {
+ CANMSG("Sorry, hardware \"%s\" is currently not supported.\n",hw[i]);
+ return -EINVAL;
+ }
+ i++;
+ }
+
+ /* Check wether the supplied number of io addresses is correct. */
+ io_needed=i;
+ while ( (io[io_supplied] != -1) & (io_supplied<MAX_HW_CARDS) )
+ io_supplied++;
+ if (io_needed != io_supplied) {
+ CANMSG("Invalid number of io addresses.\n");
+ CANMSG("Supplied hardware needs %d io address(es).\n",io_needed);
+ return -EINVAL;
+ }
+
+ /* Check wether the supplied number of irq's is correct. */
+ while ( (irq[irq_supplied] != -1) & (irq_supplied<MAX_IRQ) )
+ irq_supplied++;
+ while ( (hw[j] != NULL) && (j<MAX_HW_CARDS) ) {
+ if (!strcmp(hw[j],"template") && TEMPLATE)
+ irq_needed = irq_supplied;
+ j++;
+ }
+ if (irq_needed != irq_supplied) {
+ CANMSG("Invalid number of interrupts.\n");
+ CANMSG("Supplied harware needs %d irq number(s).\n",irq_needed);
+ return -EINVAL;
+ }
+
+ /* In case minor numbers were assigned check wether the correct number
+ * of minor numbers was supplied.
+ */
+ if (minor[0] != -1) {
+ minor_needed=irq_needed;
+ while ((minor[minor_supplied] != -1) & (minor_supplied<MAX_IRQ))
+ minor_supplied++;
+ if (minor_supplied != minor_needed) {
+ CANMSG("Invalid number of minor numbers.\n");
+ CANMSG("Supplied hardware needs %d minor number(s).\n",minor_needed);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+/* list_hw is used when debugging is on to show the hardware layout */
+int list_hw(void)
+{
+ int i=0,j=0,k=0;
+
+ DEBUGMSG("Number of boards : %d\n",hardware_p->nr_boards);
+
+ while ( (hw[i] != NULL) & (i<=MAX_HW_CARDS-1) ) {
+ printk(KERN_ERR "\n");
+ DEBUGMSG("Hardware : %s\n",hardware_p->candevice[i]->hwname);
+ DEBUGMSG("IO address : 0x%lx\n",hardware_p->candevice[i]->io_addr);
+ DEBUGMSG("Nr. of i82527 : %d\n",hardware_p->candevice[i]->nr_82527_chips);
+ DEBUGMSG("Nr. of sja1000 : %d\n",hardware_p->candevice[i]->nr_sja1000_chips);
+ for (j=0; j<hardware_p->candevice[i]->nr_82527_chips+hardware_p->candevice[i]->nr_sja1000_chips; j++) {
+ DEBUGMSG("Chip%d type : %s\n", j+1, hardware_p->candevice[i]->chip[j]->chip_type);
+ DEBUGMSG("Chip base : 0x%lx\n",hardware_p->candevice[i]->chip[j]->chip_base_addr);
+ DEBUGMSG("Interrupt : %d\n",hardware_p->candevice[i]->chip[j]->chip_irq);
+
+
+ if (!strcmp(hardware_p->candevice[i]->chip[j]->chip_type,"i82527")) {
+ for (k=0; k<15; k++)
+ DEBUGMSG("Obj%d: minor: %d base: 0x%lx\n",k,hardware_p->candevice[i]->chip[j]->msgobj[k]->minor,hardware_p->candevice[i]->chip[j]->msgobj[k]->obj_base_addr);
+ }
+
+ if (!strcmp(hardware_p->candevice[i]->chip[j]->chip_type,"sja1000")) {
+ for (k=0; k<1; k++)
+ DEBUGMSG("Obj%d: minor: %d base: 0x%lx\n",k,hardware_p->candevice[i]->chip[j]->msgobj[k]->minor,hardware_p->candevice[i]->chip[j]->msgobj[k]->obj_base_addr);
+ }
+
+
+ }
+ i++;
+ }
+ return 0;
+}
--- /dev/null
+/* nsi.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/nsi.h"
+#include "../include/i82527.h"
+
+int nsican_irq=-1;
+unsigned long nsican_base=0x0;
+
+/* IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips.
+ */
+#define IO_RANGE 0x04
+
+/* The function template_request_io is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * The reserved memory starts at io_addr, wich is the module parameter io.
+ */
+int nsi_request_io(unsigned long io_addr)
+{
+
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr,
+ io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/* The function template_release_io is used to free the previously reserved
+ * io-memory. In case you reserved more memory, don't forget to free it here.
+ */
+int nsi_release_io(unsigned long io_addr)
+{
+
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/* The function template_reset is used to give a hardware reset. This is rather
+ * hardware specific so I haven't included example code. Don't forget to check
+ * the reset status of the chip before returning.
+ */
+int nsi_reset(int card)
+{
+ int i;
+
+ DEBUGMSG("Resetting nsi hardware ...\n");
+ /* we don't use template_write_register because we don't use the two first
+ register of the card but the third in order to make a hard reset */
+ outb (1, nsican_base + candevices_p[card]->res_addr);
+ outb (0, nsican_base + candevices_p[card]->res_addr);
+ for (i = 1; i < 1000; i++)
+ udelay (1000);
+
+
+ /* Check hardware reset status */
+ i=0;
+ while ( (nsi_read_register(nsican_base+iCPU) & iCPU_RST) && (i<=15)) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip0 reset status ok.\n");
+
+ return 0;
+}
+
+/* The function template_init_hw_data is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * RESET_ADDR represents the io-address of the hardware reset register.
+ * NR_82527 represents the number of intel 82527 chips on the board.
+ * NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ */
+#define RESET_ADDR 0x02
+#define NR_82527 1
+#define NR_SJA1000 0
+
+int nsi_init_hw_data(int card)
+ {
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=1;
+ candevices_p[card]->nr_sja1000_chips=0;
+ candevices_p[card]->flags |= PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+/* The function template_init_chip_data is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * CHIP_TYPE represents the type of CAN chip. CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the io_addr
+ * argument supplied at module loading time.
+ * The clock argument holds the chip clock value in Hz.
+ */
+#define CHIP_TYPE "i82527"
+
+int nsi_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=
+ candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ nsican_irq=candevices_p[card]->chip[chipnr]->chip_irq;
+ nsican_base=candevices_p[card]->chip[chipnr]->chip_base_addr;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY;
+
+ return 0;
+}
+
+ /* The function template_init_obj_data is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ */
+int nsi_init_obj_data(int chipnr, int objnr)
+{
+
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=
+ chips_p[chipnr]->chip_base_addr+(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/* The function template_program_irq is used for hardware that uses programmable
+ * interrupts. If your hardware doesn't use programmable interrupts you should
+ * not set the candevices_t->flags entry to PROGRAMMABLE_IRQ and leave this
+ * function unedited. Again this function is hardware specific so there's no
+ * example code.
+ */
+int nsi_program_irq(int card)
+{
+ return 0;
+}
+
+/* The function template_write_register is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ */
+void nsi_write_register(unsigned char data, unsigned long address)
+{
+ /* address is an absolute address */
+
+ /* the nsi card has two registers, the address register at 0x0
+ and the data register at 0x01 */
+
+ /* write the relative address on the eight LSB bits
+ and the data on the eight MSB bits in one time */
+ outw(address-nsican_base + (256 * data), nsican_base);
+}
+
+/* The function template_read_register is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ */
+unsigned nsi_read_register(unsigned long address)
+{
+ /* this is the same thing that the function write_register.
+ We use the two register, we write the address where we
+ want to read in a first time. In a second time we read the
+ data */
+ unsigned char ret;
+
+ disable_irq(nsican_irq);
+ outb(address-nsican_base, nsican_base);
+ ret=inb(nsican_base+1);
+ enable_irq(nsican_irq);
+ return ret;
+}
+
+
+ /* !!! Don't change this function !!! */
+int nsi_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = nsi_request_io;
+ hwspecops->release_io = nsi_release_io;
+ hwspecops->reset = nsi_reset;
+ hwspecops->init_hw_data = nsi_init_hw_data;
+ hwspecops->init_chip_data = nsi_init_chip_data;
+ hwspecops->init_obj_data = nsi_init_obj_data;
+ hwspecops->write_register = nsi_write_register;
+ hwspecops->read_register = nsi_read_register;
+ hwspecops->program_irq = nsi_program_irq;
+ return 0;
+}
--- /dev/null
+/* open.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/version.h>
+
+#include "../include/main.h"
+#include "../include/open.h"
+#include "../include/i82527.h"
+#include "../include/setup.h"
+
+int can_open(struct inode *inode, struct file *file)
+{
+ struct msgobj_t *obj;
+ struct chip_t *chip;
+ struct canfifo_t *fifo;
+
+ if ( ((obj=objects_p[MINOR_NR]) == NULL) ||
+ ((chip=objects_p[MINOR_NR]->hostchip) == NULL) ) {
+ CANMSG("There is no hardware support for the device file with minor nr.: %d\n",MINOR_NR);
+ return -ENODEV;
+ }
+
+ if (objects_p[MINOR_NR]->flags & OPENED) {
+ CANMSG("Sorry, only single open per device file.\n");
+ return -EBUSY;
+ }
+ else
+ objects_p[MINOR_NR]->flags |= OPENED;
+
+ if (chip->flags & CONFIGURED)
+ DEBUGMSG("Device is already configured.\n");
+ else {
+ if (chip->chipspecops->chip_config(chip))
+ CANMSG("Error configuring chip.\n");
+ else
+ chip->flags |= CONFIGURED;
+ } /* End of chip configuration */
+
+ /* Allocate output buffer memory for the opened device */
+ fifo = objects_p[MINOR_NR]->fifo;
+ fifo->buf_tx_entry=(struct canmsg_t *)kmalloc(MAX_BUF_LENGTH * sizeof(struct canmsg_t), GFP_KERNEL);
+ if (fifo->buf_tx_entry == NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(fifo->buf_tx_entry) )
+ return -ENOMEM;
+ /* Allocate input buffer memory for the opened device */
+ fifo->buf_rx_entry=(struct canmsg_t *)kmalloc(MAX_BUF_LENGTH * sizeof(struct canmsg_t), GFP_KERNEL);
+ if (fifo->buf_rx_entry == NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(fifo->buf_rx_entry) )
+ return -ENOMEM;
+
+ /* In- and output buffer initialization */
+ fifo->tx_readp = fifo->buf_tx_entry;
+ fifo->tx_writep = fifo->buf_tx_entry;
+ fifo->rx_readp = fifo->buf_rx_entry;
+ fifo->rx_writep = fifo->buf_rx_entry;
+ fifo->rx_size = MAX_BUF_LENGTH * sizeof(struct canmsg_t);
+ fifo->tx_size = fifo->rx_size;
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ init_waitqueue(&fifo->readq);
+ init_waitqueue(&fifo->writeq);
+#else
+ init_waitqueue_head(&fifo->readq);
+ init_waitqueue_head(&fifo->writeq);
+#endif
+
+ fifo->rx_in_progress = 0;
+ fifo->tx_in_progress = 0;
+
+ fifo->head = fifo->tail = 0; //TEMP!!
+
+ if (chip->chipspecops->pre_read_config(chip,obj)<0)
+ CANMSG("Error initializing chip for receiving\n");
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
--- /dev/null
+/* pc-i03.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wnadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/pc-i03.h"
+#include "../include/sja1000.h"
+
+/* Basic hardware io address. This is also stored in the hardware structure but
+ * we need it global, else we have to change many internal functions.
+ * pc-i03_base_addr is initialized in pc-i03_init_chip_data().
+ */
+unsigned int pci03_base_addr;
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x200 // The pc-i03 uses an additional 0x100 bytes reset space
+
+/**
+ * pci03_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function pci03_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/pc-i03.c
+ */
+int pci03_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/**
+ * pci03_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function pci03_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/pc-i03.c
+ */
+int pci03_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/**
+ * pci03_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function pci03_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/pc-i03.c
+ */
+int pci03_reset(int card)
+{
+ int i=0;
+
+ DEBUGMSG("Resetting pc-i03 hardware ...\n");
+ pci03_write_register(0x01,pci03_base_addr +
+ 0x100); // Write arbitrary data to reset mem
+ udelay(20000);
+
+ pci03_write_register(0x00, pci03_base_addr + SJACR);
+
+ /* Check hardware reset status */
+ i=0;
+ while ( (pci03_read_register(pci03_base_addr + SJACR) & CR_RR)
+ && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip[0] reset status ok.\n");
+
+ return 0;
+}
+
+#define RESET_ADDR 0x100
+#define NR_82527 0
+#define NR_SJA1000 1
+
+/**
+ * pci03_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function pci03_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/pc-i03.c
+ */
+int pci03_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=NR_82527;
+ candevices_p[card]->nr_sja1000_chips=NR_SJA1000;
+ candevices_p[card]->flags |= ~PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "sja1000"
+/**
+ * pci03_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function pci03_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * Return Value: The function always returns zero
+ * File: src/pc-i03.c
+ */
+int pci03_init_chip_data(int card, int chipnr)
+{
+ pci03_base_addr = candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = CDR_CBP | CDR_CLK_OFF;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = OCR_MODE_NORMAL |
+ OCR_TX0_HL | OCR_TX1_LZ;
+
+ return 0;
+}
+
+/**
+ * pci03_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function pci03_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/pc-i03.c
+ */
+int pci03_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * pci03_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function pci03_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/pc-i03.c
+ */
+int pci03_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * pci03_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function pci03_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/pc-i03.c
+ */
+void pci03_write_register(unsigned char data, unsigned long address)
+{
+ unsigned int *pci03_base_ptr;
+ unsigned short address_to_write;
+
+ /* The read/write functions are called by an extra abstract function.
+ * This extra function adds the basic io address of the card to the
+ * memory address we want to write to, so we substract the basic io
+ * address again to obtain the offset into the hardware's memory map.
+ */
+ address_to_write = address - pci03_base_addr; // Offset
+ pci03_base_ptr = (unsigned int *)(pci03_base_addr * 0x100001);
+ (*(pci03_base_ptr+address_to_write)) = data;
+}
+
+/**
+ * pci03_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function pci03_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/pc-i03.c
+ */
+unsigned pci03_read_register(unsigned long address)
+{
+ unsigned int *pci03_base_ptr;
+ unsigned short address_to_read;
+
+ /* The read/write functions are called by an extra abstract function.
+ * This extra function adds the basic io address of the card to the
+ * memory address we want to write to, so we substract the basic io
+ * address again to obtain the offset into the hardware's memory map.
+ */
+ address_to_read = address - pci03_base_addr;
+ pci03_base_ptr = (unsigned int *)(pci03_base_addr * 0x100001);
+ return (*(pci03_base_ptr+address_to_read));
+}
+
+int pci03_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pci03_request_io;
+ hwspecops->release_io = pci03_release_io;
+ hwspecops->reset = pci03_reset;
+ hwspecops->init_hw_data = pci03_init_hw_data;
+ hwspecops->init_chip_data = pci03_init_chip_data;
+ hwspecops->init_obj_data = pci03_init_obj_data;
+ hwspecops->write_register = pci03_write_register;
+ hwspecops->read_register = pci03_read_register;
+ hwspecops->program_irq = pci03_program_irq;
+ return 0;
+}
--- /dev/null
+/* pccan.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/pccan.h"
+#include "../include/i82527.h"
+#include "../include/sja1000.h"
+
+int pccanf_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr+0x4000,0x20)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x4000);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x6000,0x04)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x6000);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr+0x4000,0x20,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x4000, io_addr+0x4000+0x20-1);
+ request_region(io_addr+0x6000,0x04,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x6000, io_addr+0x6000+0x04-1);
+ }
+ return 0;
+}
+
+int pccand_request_io(unsigned long io_addr)
+{
+ if (pccanf_request_io(io_addr))
+ return -ENODEV;
+
+ if (check_region(io_addr+0x5000,0x20)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x5000);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr+0x5000,0x20,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x5000, io_addr+0x5000+0x20-1);
+ }
+ return 0;
+}
+
+int pccanq_request_io(unsigned long io_addr)
+{
+ if (pccand_request_io(io_addr))
+ return -ENODEV;
+
+ if (check_region(io_addr+0x2000,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x2000);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x2400,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x2400);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x2800,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x2800);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x2C00,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x2C00);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x3000,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x3000);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x3400,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x3400);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x3800,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x3800);
+ return -ENODEV;
+ }
+ else if (check_region(io_addr+0x3C00,0x40)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x3C00);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr+0x2000,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x2000, io_addr+0x2000+0x40-1);
+ request_region(io_addr+0x2400,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x2400, io_addr+0x2400+0x40-1);
+ request_region(io_addr+0x2800,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x2800, io_addr+0x2800+0x40-1);
+ request_region(io_addr+0x2C00,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x2C00, io_addr+0x2C00+0x40-1);
+ request_region(io_addr+0x3000,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x3000, io_addr+0x3000+0x40-1);
+ request_region(io_addr+0x3400,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x3400, io_addr+0x3400+0x40-1);
+ request_region(io_addr+0x3800,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x3800, io_addr+0x3800+0x40-1);
+ request_region(io_addr+0x3C00,0x40,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr+0x3C00, io_addr+0x3C00+0x40-1);
+ }
+
+ return 0;
+}
+
+int pccanf_release_io(unsigned long io_addr)
+{
+ release_region(io_addr+0x4000,0x20);
+ release_region(io_addr+0x6000,0x04);
+
+ return 0;
+}
+
+int pccand_release_io(unsigned long io_addr)
+{
+ pccanf_release_io(io_addr);
+ release_region(io_addr+0x5000,0x20);
+
+ return 0;
+}
+
+int pccanq_release_io(unsigned long io_addr)
+{
+ pccand_release_io(io_addr);
+ release_region(io_addr+0x2000,0x40);
+ release_region(io_addr+0x2400,0x40);
+ release_region(io_addr+0x2800,0x40);
+ release_region(io_addr+0x2C00,0x40);
+ release_region(io_addr+0x3000,0x40);
+ release_region(io_addr+0x3400,0x40);
+ release_region(io_addr+0x3800,0x40);
+ release_region(io_addr+0x3C00,0x40);
+
+ return 0;
+}
+
+int pccanf_reset(int card)
+{
+ int i=0;
+
+ DEBUGMSG("Resetting pccanf/s hardware ...\n");
+ while (i < 1000000) {
+ i++;
+ outb(0x00,candevices_p[card]->res_addr);
+ }
+ outb(0x01,candevices_p[card]->res_addr);
+ outb(0x00,candevices_p[card]->chip[0]->chip_base_addr+SJACR);
+
+ /* Check hardware reset status */
+ i=0;
+ while ( (inb(candevices_p[card]->chip[0]->chip_base_addr+SJACR) & CR_RR)
+ && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip[0] reset status ok.\n");
+
+ return 0;
+}
+
+int pccand_reset(int card)
+{
+ int i=0,chip_nr=0;
+
+ DEBUGMSG("Resetting pccan-d hardware ...\n");
+ while (i < 1000000) {
+ i++;
+ outb(0x00,candevices_p[card]->res_addr);
+ }
+ outb(0x01,candevices_p[card]->res_addr);
+ outb(0x00,candevices_p[card]->chip[0]->chip_base_addr+SJACR);
+ outb(0x00,candevices_p[card]->chip[1]->chip_base_addr+SJACR);
+
+ /* Check hardware reset status */
+ i=0;
+ for (chip_nr=0; chip_nr<2; chip_nr++) {
+ i=0;
+ while ( (inb(candevices_p[card]->chip[chip_nr]->chip_base_addr +
+ SJACR) & CR_RR) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip%d reset status ok.\n",chip_nr);
+ }
+ return 0;
+}
+
+int pccanq_reset(int card)
+{
+ int i=0,chip_nr=0;
+
+ for (i=0; i<4; i++)
+ disable_irq(candevices_p[card]->chip[i]->chip_irq);
+
+ DEBUGMSG("Resetting pccan-q hardware ...\n");
+ while (i < 100000) {
+ i++;
+ outb(0x00,candevices_p[card]->res_addr);
+ }
+ outb_p(0x01,candevices_p[card]->res_addr);
+
+ outb(0x00,candevices_p[card]->chip[2]->chip_base_addr+SJACR);
+ outb(0x00,candevices_p[card]->chip[3]->chip_base_addr+SJACR);
+
+ /* Check hardware reset status */
+ for (chip_nr=0; chip_nr<2; chip_nr++) {
+ i=0;
+ while( (inb(candevices_p[card]->chip[chip_nr]->chip_base_addr +
+ iCPU) & iCPU_RST) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip%d reset status ok.\n",chip_nr);
+ }
+ for (chip_nr=2; chip_nr<4; chip_nr++) {
+ i=0;
+ while( (inb(candevices_p[card]->chip[chip_nr]->chip_base_addr +
+ SJACR) & CR_RR) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip%d reset status ok.\n",chip_nr);
+ }
+
+ for (i=0; i<4; i++)
+ enable_irq(candevices_p[card]->chip[i]->chip_irq);
+
+ return 0;
+}
+
+int pccan_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=candevices_p[card]->io_addr+0x6001;
+ candevices_p[card]->flags |= PROGRAMMABLE_IRQ;
+
+ if (!strcmp(candevices_p[card]->hwname,"pccan-q")) {
+ candevices_p[card]->nr_82527_chips=2;
+ candevices_p[card]->nr_sja1000_chips=2;
+ }
+ if (!strcmp(candevices_p[card]->hwname,"pccan-f") |
+ !strcmp(candevices_p[card]->hwname,"pccan-s")) {
+ candevices_p[card]->nr_82527_chips=0;
+ candevices_p[card]->nr_sja1000_chips=1;
+ }
+ if (!strcmp(candevices_p[card]->hwname,"pccan-d")) {
+ candevices_p[card]->nr_82527_chips=0;
+ candevices_p[card]->nr_sja1000_chips=2;
+ }
+
+ return 0;
+}
+
+int pccan_init_chip_data(int card, int chipnr)
+{
+ if (!strcmp(candevices_p[card]->hwname,"pccan-q")) {
+ if (chipnr<2) {
+ candevices_p[card]->chip[chipnr]->chip_type="i82527";
+ candevices_p[card]->chip[chipnr]->flags = SEGMENTED;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg=iCPU_DSC;
+ candevices_p[card]->chip[chipnr]->int_clk_reg=iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg=iBUS_CBY;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = 0;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = 0;
+ }
+ else{
+ candevices_p[card]->chip[chipnr]->chip_type="sja1000";
+ candevices_p[card]->chip[chipnr]->flags = 0;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = 0;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = 0;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = 0;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg =
+ CDR_CLK_OFF;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg =
+ OCR_MODE_NORMAL | OCR_TX0_LH;
+ }
+ candevices_p[card]->chip[chipnr]->chip_base_addr=0x1000*chipnr+0x2000+candevices_p[card]->io_addr;
+ }
+ else {
+ candevices_p[card]->chip[chipnr]->chip_type="sja1000";
+ candevices_p[card]->chip[chipnr]->chip_base_addr=0x1000*chipnr+0x4000+candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->flags = 0;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = 0;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = 0;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = 0;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = CDR_CLK_OFF;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg =
+ OCR_MODE_NORMAL | OCR_TX0_LH;
+ }
+
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+
+ return 0;
+}
+
+int pccan_init_obj_data(int chipnr, int objnr)
+{
+ if (!strcmp(chips_p[chipnr]->chip_type,"sja1000")) {
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+ }
+ else {
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr+(objnr+1)*0x10+(int)((objnr+1)/4)*0x3c0;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+ }
+
+ return 0;
+}
+
+int pccan_program_irq(int card)
+{
+ #define IRQ9 0x01
+ #define IRQ3 0x02
+ #define IRQ5 0x03
+
+ unsigned char irq_reg_value=0;
+ int i;
+
+ for (i=0; i<4; i++) {
+ switch (candevices_p[card]->chip[i]->chip_irq) {
+ case 0: {
+ break;
+ }
+ case 3: {
+ irq_reg_value |= (IRQ3<<(i*2));
+ break;
+ }
+ case 5: {
+ irq_reg_value |= (IRQ5<<(i*2));
+ break;
+ }
+ case 9: {
+ irq_reg_value |= (IRQ9<<(i*2));
+ break;
+ }
+ default: {
+ CANMSG("Supplied interrupt is not supported by the hardware\n");
+ return -ENODEV;
+ }
+ }
+ }
+ outb(irq_reg_value,0x6000+candevices_p[card]->io_addr);
+ DEBUGMSG("Configured pccan hardware interrupts\n");
+ outb(0x80,0x6000+candevices_p[card]->io_addr+0x02);
+ DEBUGMSG("Selected pccan on-board 16 MHz oscillator\n");
+
+ return 0;
+}
+
+inline void pccan_write_register(unsigned char data, unsigned long address)
+{
+ outb(data,address);
+}
+
+unsigned pccan_read_register(unsigned long address)
+{
+ return inb(address);
+}
+
+int pccanf_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pccanf_request_io;
+ hwspecops->release_io = pccanf_release_io;
+ hwspecops->reset = pccanf_reset;
+ hwspecops->init_hw_data = pccan_init_hw_data;
+ hwspecops->init_chip_data = pccan_init_chip_data;
+ hwspecops->init_obj_data = pccan_init_obj_data;
+ hwspecops->write_register = pccan_write_register;
+ hwspecops->read_register = pccan_read_register;
+ hwspecops->program_irq = pccan_program_irq;
+ return 0;
+}
+
+
+int pccand_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pccand_request_io;
+ hwspecops->release_io = pccand_release_io;
+ hwspecops->reset = pccand_reset;
+ hwspecops->init_hw_data = pccan_init_hw_data;
+ hwspecops->init_chip_data = pccan_init_chip_data;
+ hwspecops->init_obj_data = pccan_init_obj_data;
+ hwspecops->write_register = pccan_write_register;
+ hwspecops->read_register = pccan_read_register;
+ hwspecops->program_irq = pccan_program_irq;
+ return 0;
+}
+
+
+int pccanq_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pccanq_request_io;
+ hwspecops->release_io = pccanq_release_io;
+ hwspecops->reset = pccanq_reset;
+ hwspecops->init_hw_data = pccan_init_hw_data;
+ hwspecops->init_chip_data = pccan_init_chip_data;
+ hwspecops->init_obj_data = pccan_init_obj_data;
+ hwspecops->write_register = pccan_write_register;
+ hwspecops->read_register = pccan_read_register;
+ hwspecops->program_irq = pccan_program_irq;
+ return 0;
+}
--- /dev/null
+/* pcccan.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+/* This file contains the low level functions for the pcccan-1 card from Gespac.
+ * You can probably find more information at http://www.gespac.com
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/pcccan.h"
+#include "../include/i82527.h"
+
+int pcccan_irq=-1;
+unsigned long pcccan_base=0x0;
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+
+/* The pcccan card uses indexed addressing hence the need to only reserve
+ * eight bytes of memory.
+ * base + 0 = Reset
+ * base + 1 = Address loading
+ * base + 2 = Read register
+ * base + 3 = Read register + increment loaded address (saves a write operation
+ * when accessing consecutive registers)
+ * base + 4 = Unused
+ * base + 5 = Address read
+ * base + 6 = Write register
+ * base + 7 = Write register + increment loaded address
+ */
+#define IO_RANGE 0x8
+
+/**
+ * pcccan_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function pcccan_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/pcccan.c
+ */
+int pcccan_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/**
+ * pcccan_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function pcccan_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/pcccan.c
+ */
+int pcccan_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/**
+ * pcccan_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function pcccan_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/pcccan.c
+ */
+int pcccan_reset(int card)
+{
+ int i=0;
+
+ DEBUGMSG("Resetting pcccan-1 hardware ...\n");
+ while (i < 1000000) {
+ i++;
+ outb(0x0,candevices_p[card]->res_addr);
+ }
+
+ /* Check hardware reset status */
+ i=0;
+ outb(iCPU,candevices_p[card]->io_addr+0x1);
+ while ( (inb(candevices_p[card]->io_addr+0x2)&0x80) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip reset status ok.\n");
+
+ return 0;
+}
+
+#define NR_82527 1
+#define NR_SJA1000 0
+
+/**
+ * pcccan_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function pcccan_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/pcccan.c
+ */
+int pcccan_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->nr_82527_chips=NR_82527;
+ candevices_p[card]->nr_sja1000_chips=NR_SJA1000;
+ candevices_p[card]->flags |= ~PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "i82527"
+/**
+ * pcccan_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function pcccan_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * The entry @int_cpu_reg holds hardware specific options for the cpu interface
+ * register. Options defined in the %i82527.h file:
+ * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST.
+ * Return Value: The function always returns zero
+ * File: src/pcccan.c
+ */
+int pcccan_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC | iCPU_DMC;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = iCLK_SL1 | iCLK_CD0;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY | iBUS_DR1;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = 0;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = 0;
+ pcccan_irq=candevices_p[card]->chip[chipnr]->chip_irq;
+ pcccan_base=candevices_p[card]->chip[chipnr]->chip_base_addr;
+
+ return 0;
+}
+
+/**
+ * pcccan_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function pcccan_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/pcccan.c
+ */
+int pcccan_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * pcccan_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function pcccan_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/pcccan.c
+ */
+int pcccan_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * pcccan_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function pcccan_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/pcccan.c
+ */
+void pcccan_write_register(unsigned char data, unsigned long address)
+{
+ disable_irq(pcccan_irq);
+ outb(address - pcccan_base, pcccan_base+1);
+ outb(data, pcccan_base+6);
+ enable_irq(pcccan_irq);
+}
+
+/**
+ * pcccan_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function pcccan_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/pcccan.c
+ */
+unsigned pcccan_read_register(unsigned long address)
+{
+ unsigned ret;
+ disable_irq(pcccan_irq);
+ outb(address - pcccan_base, pcccan_base+1);
+ ret=inb(pcccan_base+2);
+ enable_irq(pcccan_irq);
+ return ret;
+
+}
+
+/* !!! Don't change this function !!! */
+int pcccan_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pcccan_request_io;
+ hwspecops->release_io = pcccan_release_io;
+ hwspecops->reset = pcccan_reset;
+ hwspecops->init_hw_data = pcccan_init_hw_data;
+ hwspecops->init_chip_data = pcccan_init_chip_data;
+ hwspecops->init_obj_data = pcccan_init_obj_data;
+ hwspecops->write_register = pcccan_write_register;
+ hwspecops->read_register = pcccan_read_register;
+ hwspecops->program_irq = pcccan_program_irq;
+ return 0;
+}
--- /dev/null
+/* pcm3680.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/pcm3680.h"
+#include "../include/i82527.h"
+#include "../include/sja1000p.h"
+
+/* Basic hardware io address. This is also stored in the hardware structure but
+ * we need it global, else we have to change many internal functions.
+ * pcm3680_base_addr is initialized in pcm3680_init_chip_data().
+ */
+unsigned int pcm3680_base_addr;
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x200
+
+/**
+ * template_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function template_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int pcm3680_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/**
+ * template_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function template_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcm3680_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/**
+ * template_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function template_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int pcm3680_reset(int card)
+{
+// int i=0;
+
+// DEBUGMSG("Resetting pcm3680 hardware ...\n");
+// pcm3680_write_register(0x01, candevices_p[card]->io_addr +
+// 0x100); // Write arbitrary data to reset mem
+// pcm3680_write_register(0x01, candevices_p[card]->io_addr +
+// 0x300); // Write arbitrary data to reset mem
+// udelay(20000);
+
+// pcm3680_write_register(0x00, candevices_p[card]->io_addr + SJACR);
+// pcm3680_write_register(0x00, candevices_p[card]->io_addr + SJACR+0x200);
+
+ /* Check hardware reset status chip 0 */
+/* i=0;
+ while ( (pcm3680_read_register(candevices_p[card]->io_addr + SJACR)
+ & CR_RR) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip[0] reset status ok.\n");
+*/
+ /* Check hardware reset status chip 1 */
+/* i=0;
+ while ( (pcm3680_read_register( candevices_p[card]->io_addr + SJACR +
+ 0x200) & CR_RR) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip[1] reset status ok.\n");
+*/
+ return 0;
+}
+
+#define RESET_ADDR 0x100
+#define NR_82527 0
+#define NR_SJA1000 1
+
+/**
+ * template_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function template_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcm3680_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=NR_82527;
+ candevices_p[card]->nr_sja1000_chips=NR_SJA1000;
+ candevices_p[card]->flags &= ~PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "sja1000p"
+/**
+ * template_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function template_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcm3680_init_chip_data(int card, int chipnr)
+{
+ pcm3680_base_addr = candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = 0x0;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = 0x0;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = CDR_CBP | CDR_CLK_OFF;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = OCR_MODE_NORMAL |
+ OCR_TX0_LH;
+
+ return 0;
+}
+
+/**
+ * template_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function template_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int pcm3680_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * template_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function template_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int pcm3680_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * template_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function template_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/template.c
+ */
+void pcm3680_write_register(unsigned char data, unsigned long address)
+{
+ writeb(data,address);
+}
+
+/**
+ * template_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function template_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/template.c
+ */
+unsigned pcm3680_read_register(unsigned long address)
+{
+ return readb(address);
+}
+
+/* !!! Don't change this function !!! */
+int pcm3680_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pcm3680_request_io;
+ hwspecops->release_io = pcm3680_release_io;
+ hwspecops->reset = pcm3680_reset;
+ hwspecops->init_hw_data = pcm3680_init_hw_data;
+ hwspecops->init_chip_data = pcm3680_init_chip_data;
+ hwspecops->init_obj_data = pcm3680_init_obj_data;
+ hwspecops->write_register = pcm3680_write_register;
+ hwspecops->read_register = pcm3680_read_register;
+ hwspecops->program_irq = pcm3680_program_irq;
+ return 0;
+}
--- /dev/null
+/* pip.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/pip.h"
+#include "../include/i82527.h"
+
+int pip5_request_io(unsigned long io_addr)
+{
+ if (io_addr != 0x8000) {
+ CANMSG("Invalid base io address\n");
+ CANMSG("The PIP5 uses a fixed base address of 0x8000,\n");
+ CANMSG("please consult your user manual.\n");
+ return -ENODEV;
+ }
+ if (check_region(io_addr,0x100)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else if(check_region(io_addr+0x102,0x01)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr+0x102);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,0x100,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + 0x100 - 1);
+ request_region(io_addr+0x102,0x01,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx\n", io_addr+0x102);
+ }
+ return 0;
+}
+
+int pip6_request_io(unsigned long io_addr)
+{
+ if ( (io_addr != 0x1000)&&(io_addr != 0x8000)&&(io_addr != 0xe000)) {
+ CANMSG("Invalid base io address\n");
+ CANMSG("Valid values for the PIP6 are: 0x1000, 0x8000 or 0xe000\n");
+ CANMSG("Please consult your user manual.\n");
+ return -ENODEV;
+ }
+ if (check_region(io_addr,0x100)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else if (check_region(0x804, 0x02)) {
+ CANMSG("Unable to open port: 0x%x\n", 0x804);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,0x100, DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + 0x100 -1);
+ request_region(0x804,0x02,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory : 0x%x - 0x%x\n",0x804,0x805);
+ }
+ return 0;
+}
+
+int pip5_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,0x100);
+ release_region(io_addr+0x102,0x01);
+
+ return 0;
+}
+
+int pip6_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,0x100);
+ release_region(0x804,0x02);
+
+ return 0;
+}
+
+int pip_reset(int card)
+{
+ int i=0, res_value=0;
+
+ DEBUGMSG("Resetting %s hardware ...\n", candevices_p[card]->hwname);
+ if (!strcmp(candevices_p[card]->hwname,"pip5"))
+ res_value = 0xcf;
+ else
+ res_value = 0x01;
+ while (i < 1000000) {
+ i++;
+ outb(res_value,candevices_p[card]->res_addr);
+ }
+ outb(0x0,candevices_p[card]->res_addr);
+
+ /* Check hardware reset status */
+ i=0;
+ while ( (inb(candevices_p[card]->io_addr+iCPU) & iCPU_RST) && (i<=15)) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip0 reset status ok.\n");
+
+
+ return 0;
+}
+
+int pip_init_hw_data(int card)
+{
+ if (!strcmp(candevices_p[card]->hwname,"pip5"))
+ candevices_p[card]->res_addr=candevices_p[card]->io_addr+0x102;
+ else
+ candevices_p[card]->res_addr=0x805;
+ candevices_p[card]->nr_82527_chips=1;
+ candevices_p[card]->nr_sja1000_chips=0;
+ candevices_p[card]->flags |= PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+int pip_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type="i82527";
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ if (!strcmp(candevices_p[card]->hwname,"pip5"))
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC;
+ else
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = 0x0;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = 0;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = 0;
+
+ return 0;
+}
+
+int pip_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr+(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+int pip5_program_irq(int card)
+{
+ outb(0x01, candevices_p[card]->res_addr);
+ switch (candevices_p[card]->chip[0]->chip_irq) {
+ case 3: { outb(0x03, candevices_p[card]->res_addr); break; }
+ case 4: { outb(0x05, candevices_p[card]->res_addr); break; }
+ case 5: { outb(0x07, candevices_p[card]->res_addr); break; }
+ case 10: { outb(0x09, candevices_p[card]->res_addr); break; }
+ case 11: { outb(0x0c, candevices_p[card]->res_addr); break; }
+ case 15: { outb(0x0d, candevices_p[card]->res_addr); break; }
+ default: {
+ CANMSG("Supplied interrupt is not supported by the hardware\n");
+ CANMSG("Please consult your user manual.\n");
+ return -ENODEV;
+ }
+ }
+ outb(0x00, candevices_p[card]->res_addr);
+
+ return 0;
+}
+
+int pip6_program_irq(int card)
+{
+ unsigned char can_int = 0, can_addr = 0;
+
+ can_int = candevices_p[card]->chip[0]->chip_irq;
+ if ((can_int != 3) && (can_int != 4) && (can_int != 5) && (can_int != 6)
+ && (can_int != 7) && (can_int != 9) && (can_int != 10) &&
+ (can_int != 11) && (can_int != 12) && (can_int != 14) &&
+ (can_int != 15)) {
+ CANMSG("Invalid interrupt number\n");
+ CANMSG("Valid interrupt numbers for the PIP6: 3,4,5,6,7,9,10,11,12,14 or 15\n");
+ CANMSG("Please consult your user manual.\n");
+ return -ENODEV;
+ }
+ switch (candevices_p[card]->io_addr) {
+ case 0x1000: { can_addr = 0x01; break; }
+ case 0x8000: { can_addr = 0x02; break; }
+ case 0xe000: { can_addr = 0x03; break; }
+ default: {
+ CANMSG("Supplied io address is not valid, please check your manual\n");
+ return -ENODEV;
+ }
+ }
+ outb( (can_int<<4)|can_addr, 0x804);
+
+ return 0;
+}
+
+void pip_write_register(unsigned char data, unsigned long address)
+{
+ outb(data,address);
+}
+
+unsigned pip_read_register(unsigned long address)
+{
+ return inb(address);
+}
+
+/* !!! Don't change these functions !!! */
+int pip5_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pip5_request_io;
+ hwspecops->release_io = pip5_release_io;
+ hwspecops->reset = pip_reset;
+ hwspecops->init_hw_data = pip_init_hw_data;
+ hwspecops->init_chip_data = pip_init_chip_data;
+ hwspecops->init_obj_data = pip_init_obj_data;
+ hwspecops->write_register = pip_write_register;
+ hwspecops->read_register = pip_read_register;
+ hwspecops->program_irq = pip5_program_irq;
+ return 0;
+}
+
+int pip6_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = pip6_request_io;
+ hwspecops->release_io = pip6_release_io;
+ hwspecops->reset = pip_reset;
+ hwspecops->init_hw_data = pip_init_hw_data;
+ hwspecops->init_chip_data = pip_init_chip_data;
+ hwspecops->init_obj_data = pip_init_obj_data;
+ hwspecops->write_register = pip_write_register;
+ hwspecops->read_register = pip_read_register;
+ hwspecops->program_irq = pip6_program_irq;
+ return 0;
+}
--- /dev/null
+/* proc.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+
+#include "../include/main.h"
+#include "../include/proc.h"
+#include "../include/setup.h"
+
+int add_channel_to_procdir(void);
+int remove_channel_from_procdir(void);
+int add_object_to_procdir(void);
+int remove_object_from_procdir(void);
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+static int candev_readlink(struct proc_dir_entry *de, char *page);
+#endif
+
+static int bc=0; /* static counter for each hardware board */
+static int cc=0; /* static counter for each CAN chip */
+static int oc=0; /* static counter for each message object */
+
+struct canproc_t can_proc_base;
+struct canproc_t *base=&can_proc_base;
+
+/* The following functions are needed only for kernel version 2.2. Kernel
+ * version 2.4 already defines them for us.
+ */
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+static void can_fill_inode(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+static struct proc_dir_entry * new_can_proc_entry(unsigned short inode,
+ const char *name, mode_t mode, nlink_t nlink, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *new_entry = NULL;
+
+ new_entry = (struct proc_dir_entry *) kmalloc(sizeof(struct
+ proc_dir_entry), GFP_KERNEL);
+ if (new_entry == NULL)
+ return NULL;
+
+ memset(new_entry, 0, sizeof(struct proc_dir_entry));
+
+ new_entry->low_ino = inode;
+ new_entry->namelen = strlen(name);
+ new_entry->name = name;
+ new_entry->mode = mode;
+ new_entry->nlink = nlink;
+ new_entry->fill_inode = can_fill_inode;
+ new_entry->parent = parent;
+
+ proc_register(parent, new_entry);
+
+ return new_entry;
+}
+
+int can_remove_proc_entry(struct proc_dir_entry *del, struct proc_dir_entry *parent)
+{
+ if (del != NULL) {
+ proc_unregister(parent, del->low_ino);
+ kfree(del);
+ del = NULL;
+ return 0;
+ }
+ else return -ENODEV;
+}
+#endif // Functions required for kernel 2.2
+
+/* can_init_procdir registers the entire CAN directory tree recursively at
+ * the proc system.
+ */
+int can_init_procdir(void)
+{
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ base->can_proc_entry = new_can_proc_entry(0, "can", S_IFDIR | S_IRUGO |
+ S_IXUGO, 0, &proc_root);
+#else
+ base->can_proc_entry = create_proc_entry("can", S_IFDIR | S_IRUGO |
+ S_IXUGO, &proc_root);
+#endif
+ if (base->can_proc_entry == NULL)
+ return -ENODEV;
+
+ for (bc=0; bc<hardware_p->nr_boards; bc++) {
+ add_channel_to_procdir();
+ }
+
+ return 0;
+}
+
+/* can_delete_procdir removes the entire CAN tree from the proc system */
+int can_delete_procdir(void)
+{
+ if (remove_channel_from_procdir())
+ return -ENODEV;
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ if (can_remove_proc_entry(base->can_proc_entry, &proc_root))
+ return -ENODEV;
+#else
+ remove_proc_entry("can", &proc_root);
+#endif
+
+ return 0;
+}
+
+int add_channel_to_procdir(void)
+{
+ int i=0;
+
+ for (i=0; i < candevices_p[bc]->nr_82527_chips +
+ candevices_p[bc]->nr_sja1000_chips; i++) {
+
+ base->channel[cc] = (struct channelproc_t *)
+ kmalloc(sizeof(struct channelproc_t), GFP_KERNEL);
+ if (base->channel[cc] == NULL)
+ return -ENOMEM;
+ else if (add_mem_to_list(base->channel[cc]))
+ return -ENOMEM;
+
+ sprintf(base->channel[cc]->ch_name, "channel%d",cc);
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ base->channel[cc]->ch_entry = new_can_proc_entry(0,
+ base->channel[cc]->ch_name,
+ S_IFDIR | S_IRUGO | S_IXUGO, 0,
+ base->can_proc_entry);
+#else
+ base->channel[cc]->ch_entry = create_proc_entry(
+ base->channel[cc]->ch_name,
+ S_IFDIR | S_IRUGO |S_IXUGO,
+ base->can_proc_entry);
+#endif
+ if (base->channel[cc]->ch_entry == NULL)
+ return -ENODEV;
+
+ add_object_to_procdir();
+
+ cc++;
+ }
+
+ return 0;
+}
+
+int remove_channel_from_procdir(void)
+{
+
+ while (cc != 0) {
+ cc--;
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ if (can_remove_proc_entry(base->channel[cc]->ch_entry,
+ base->can_proc_entry))
+ return -ENODEV;
+#else
+ remove_proc_entry(base->channel[cc]->ch_name,
+ base->can_proc_entry);
+#endif
+ if (remove_object_from_procdir())
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+int add_object_to_procdir(void)
+{
+ int i=0, obj=0;
+
+ if (!strcmp(chips_p[cc]->chip_type,"i82527"))
+ obj=15;
+ if (!strcmp(chips_p[cc]->chip_type,"sja1000"))
+ obj=1;
+
+ for (i=0; i<obj; i++) {
+ oc=i;
+ base->channel[cc]->object[i] = (struct objectproc_t *)
+ kmalloc(sizeof(struct objectproc_t),GFP_KERNEL);
+
+
+ if (base->channel[cc]->object[i] == NULL)
+ return -ENOMEM;
+ else if (add_mem_to_list( base->channel[cc]->object[i]))
+ return -ENOMEM;
+
+ sprintf(base->channel[cc]->object[i]->obj_name,"object%d",i);
+ sprintf(base->channel[cc]->object[i]->lnk_name,"dev");
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ base->channel[cc]->object[i]->obj_entry=new_can_proc_entry(
+ 0, base->channel[cc]->object[i]->obj_name,
+ S_IFDIR | S_IRUGO | S_IXUGO, 0,
+ base->channel[cc]->ch_entry);
+ if (base->channel[cc]->object[i]->obj_entry == NULL)
+ return -ENODEV;
+ base->channel[cc]->object[i]->lnk = new_can_proc_entry(
+ 0, base->channel[cc]->object[i]->lnk_name,
+ S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO,
+ 0, base->channel[cc]->object[i]->obj_entry);
+ if (base->channel[cc]->object[i]->lnk == NULL)
+ return -ENODEV;
+ sprintf(base->channel[cc]->object[i]->lnk_dev,"/dev/can");
+ base->channel[cc]->object[i]->lnk->readlink_proc =
+ candev_readlink;
+
+#else
+ base->channel[cc]->object[i]->obj_entry = create_proc_entry(
+ base->channel[cc]->object[i]->obj_name,
+ S_IFDIR | S_IRUGO | S_IXUGO,
+ base->channel[cc]->ch_entry);
+ if (base->channel[cc]->object[i]->obj_entry == NULL)
+ return -ENODEV;
+ sprintf(base->channel[cc]->object[i]->lnk_dev,"/dev/can%d",
+ chips_p[cc]->msgobj[i]->minor);
+ base->channel[cc]->object[i]->lnk = proc_symlink(
+ base->channel[cc]->object[i]->lnk_name,
+ base->channel[cc]->object[i]->obj_entry,
+ base->channel[cc]->object[i]->lnk_dev);
+ if (base->channel[cc]->object[i]->lnk == NULL)
+ return -ENODEV;
+#endif
+
+ }
+ return 0;
+}
+
+int remove_object_from_procdir(void)
+{
+ int i=0, obj=0;
+
+ if (!strcmp(chips_p[cc]->chip_type,"i82527"))
+ obj=15;
+ if (!strcmp(chips_p[cc]->chip_type,"sja1000"))
+ obj=1;
+
+ for (i=0; i<obj; i++) {
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ if (can_remove_proc_entry( base->channel[cc]->object[i]->lnk,
+ base->channel[cc]->object[i]->obj_entry))
+ return -ENODEV;
+ if (can_remove_proc_entry(
+ base->channel[cc]->object[i]->obj_entry,
+ base->channel[cc]->ch_entry))
+ return -ENODEV;
+#else
+ remove_proc_entry(base->channel[cc]->object[i]->lnk_name,
+ base->channel[cc]->object[i]->obj_entry);
+ remove_proc_entry(base->channel[cc]->object[i]->obj_name,
+ base->channel[cc]->ch_entry);
+#endif
+
+ }
+ return 0;
+}
+
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+static int candev_readlink(struct proc_dir_entry *de, char *page)
+{
+ int i=0, nchip=0, nobj=0;
+ char chip[20], object[20], tmp[6];
+
+ sprintf(chip, de->parent->parent->name+7);
+ sprintf(object, de->parent->name+6);
+
+ for (i=0; i<MAX_TOT_CHIPS; i++) {
+ sprintf(tmp,"%d",i);
+ if (!strcmp(chip,tmp)) {
+ nchip=i;
+ break;
+ }
+ }
+ for (i=0; i<MAX_MSGOBJS; i++) {
+ sprintf(tmp,"%d",i);
+ if (!strcmp(object,tmp)) {
+ nobj=i;
+ break;
+ }
+ }
+
+ return sprintf(page,"/dev/can%d",chips_p[nchip]->msgobj[nobj]->minor );
+}
+#endif //End of candev_readlink for kernel 2.2
--- /dev/null
+/* read.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/malloc.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/read.h"
+#include "../include/ioctl.h"
+
+/* This is the 'Normal' read handler for normal transmission messages */
+inline ssize_t can_std_read(struct file *file, struct canfifo_t *fifo,
+ struct msgobj_t *obj, char *buffer, size_t length)
+{
+ int can_timeout, ret;
+ int bytes_avail = 0, bytes_to_copy = 0;
+
+ cli();
+ if (fifo->rx_readp == fifo->rx_writep) { // Buffer is empty
+ if (file->f_flags & O_NONBLOCK) {
+ sti();
+ return -EAGAIN;
+ }
+ obj->ret = 0;
+ can_timeout = interruptible_sleep_on_timeout(&fifo->readq,
+ CANTIMEOUT);
+ sti();
+ if (signal_pending(current)) {
+ DEBUGMSG("Rx interrupted\n");
+ return -EINTR;
+ }
+ if (!can_timeout) {
+ DEBUGMSG("Rx timeout\n");
+ return -EIO;
+ }
+ if (obj->ret < 0)
+ return obj->ret;
+ }
+ /* Calculate available bytes in the buffer */
+ cli();
+ bytes_avail = ((int)fifo->rx_readp < (int)fifo->rx_writep) ?
+ ((int)fifo->rx_writep - (int)fifo->rx_readp) :
+ ((int)fifo->rx_writep - (int)fifo->rx_readp +
+ (int)fifo->rx_size);
+ sti();
+
+ bytes_to_copy = (length < bytes_avail) ? length : bytes_avail;
+ ret = bytes_to_copy;
+
+ /* Copy the data to user space */
+ while (bytes_to_copy > 0) {
+ copy_to_user(buffer, fifo->rx_readp, sizeof(struct canmsg_t));
+ buffer += sizeof(struct canmsg_t);
+ bytes_to_copy -= sizeof(struct canmsg_t);
+ fifo->rx_readp++;
+ if (fifo->rx_readp >= fifo->buf_rx_entry + MAX_BUF_LENGTH)
+ fifo->rx_readp = fifo->buf_rx_entry;
+ }
+
+ return ret;
+}
+
+/* This is the 'RTR' read handler for remote transmission request messages */
+inline ssize_t can_rtr_read(struct chip_t *chip, struct msgobj_t *obj,
+ char *buffer)
+{
+ unsigned long flags;
+ struct rtr_id *rtr_current, *new_rtr_entry;
+ struct canmsg_t read_msg;
+
+ DEBUGMSG("Remote transmission request\n");
+ spin_lock_irqsave(&hardware_p->rtr_lock, flags);
+ if (hardware_p->rtr_queue == NULL) { //No remote messages pending
+ new_rtr_entry=(struct rtr_id *)kmalloc(sizeof(struct rtr_id),GFP_ATOMIC);
+ if (new_rtr_entry == NULL) {
+ spin_unlock_irqrestore(&hardware_p->rtr_lock,
+ flags);
+ return -ENOMEM;
+ }
+ hardware_p->rtr_queue=new_rtr_entry;
+ }
+ else {
+ rtr_current=hardware_p->rtr_queue;
+ while (rtr_current->next != NULL)
+ rtr_current=rtr_current->next;
+ new_rtr_entry=(struct rtr_id *)kmalloc(sizeof(struct rtr_id),GFP_ATOMIC);
+ rtr_current->next=new_rtr_entry;
+ }
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19))
+ init_waitqueue(&new_rtr_entry->rtr_wq);
+#else
+ init_waitqueue_head(&new_rtr_entry->rtr_wq);
+#endif
+ new_rtr_entry->id = read_msg.id;
+ new_rtr_entry->rtr_message = &read_msg;
+ new_rtr_entry->next=NULL;
+
+ spin_unlock_irqrestore(&hardware_p->rtr_lock, flags);
+
+ /* Send remote transmission request */
+ chip->chipspecops->remote_request(chip,obj);
+ obj->ret = 0;
+ interruptible_sleep_on(&new_rtr_entry->rtr_wq);
+
+ spin_lock_irqsave(&hardware_p->rtr_lock, flags);
+ copy_to_user(buffer, &read_msg, sizeof(struct canmsg_t));
+ if (hardware_p->rtr_queue == new_rtr_entry) {
+ if (new_rtr_entry->next != NULL)
+ hardware_p->rtr_queue=new_rtr_entry->next;
+ else
+ hardware_p->rtr_queue=NULL;
+ }
+ else {
+ rtr_current=hardware_p->rtr_queue;
+ while (rtr_current->next != new_rtr_entry)
+ rtr_current=rtr_current->next;
+ if (new_rtr_entry->next != NULL)
+ rtr_current->next=new_rtr_entry->next;
+ else
+ rtr_current->next=NULL;
+ }
+ spin_unlock_irqrestore(&hardware_p->rtr_lock, flags);
+ kfree(new_rtr_entry);
+
+ return obj->ret;
+}
+
+ssize_t can_read(struct file *file, char *buffer, size_t length, loff_t *offset)
+{
+ struct msgobj_t *obj;
+ struct chip_t *chip;
+ struct canfifo_t *fifo;
+ struct canmsg_t read_msg;
+ int ret=0;
+
+ if (length < sizeof(struct canmsg_t)) {
+ DEBUGMSG("Trying to read less bytes than a CAN message, \n");
+ DEBUGMSG("this will always return zero.\n");
+ return 0;
+ }
+ if (length > 8 * sizeof(struct canmsg_t)) {
+ DEBUGMSG("Reading more than 8 CAN messages, this is not supported.\n");
+ DEBUGMSG("Defaulting to 8 messages.\n");
+ length = 8 * sizeof(struct canmsg_t);
+ }
+ /* Initialize hardware pointers */
+ if ( (obj = objects_p[MINOR_NR]) == NULL) {
+ CANMSG("Could not assign buffer structure\n");
+ return -1;
+ }
+ if ( (chip = obj->hostchip) == NULL) {
+ CANMSG("Device is not correctly configured,\n");
+ CANMSG("please reload the driver.\n");
+ return -1;
+ }
+ if ( (fifo = obj->fifo) == NULL) {
+ CANMSG("Could not assign buffer memory.\n");
+ return -1;
+ }
+
+ copy_from_user(&read_msg, buffer, sizeof(struct canmsg_t));
+ if (read_msg.flags & MSG_RTR)
+ ret = can_rtr_read(chip, obj, buffer);
+ else
+ ret = can_std_read(file, fifo, obj, buffer, length);
+
+ return ret;
+}
+
+
--- /dev/null
+/* setup.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/malloc.h>
+#include <linux/fs.h>
+
+#include "../.support"
+
+#include "../include/main.h"
+#include "../include/setup.h"
+#include "../include/pip.h"
+#include "../include/pccan.h"
+#include "../include/smartcan.h"
+#include "../include/pc-i03.h"
+#include "../include/pcm3680.h"
+#include "../include/m437.h"
+#include "../include/template.h"
+#include "../include/i82527.h"
+#include "../include/aim104.h"
+#include "../include/pcccan.h"
+
+extern int sja1000_register(struct chipspecops_t *chipspecops);
+extern int sja1000p_register(struct chipspecops_t *chipspecops);
+extern int i82527_register(struct chipspecops_t *chipspecops);
+extern int template_register(struct hwspecops_t *hwspecops);
+extern int pip5_register(struct hwspecops_t *hwspecops);
+extern int pip6_register(struct hwspecops_t *hwspecops);
+extern int smartcan_register(struct hwspecops_t *hwspecops);
+extern int pccanf_register(struct hwspecops_t *hwspecops);
+extern int pccand_register(struct hwspecops_t *hwspecops);
+extern int pccanq_register(struct hwspecops_t *hwspecops);
+extern int nsi_register(struct hwspecops_t *hwspecops);
+extern int cc104_register(struct hwspecops_t *hwspecops);
+extern int pci03_register(struct hwspecops_t *hwspecops);
+extern int pcm3680_register(struct hwspecops_t *hwspecops);
+extern int aim104_register(struct hwspecops_t *hwspecops);
+extern int pcccan_register(struct hwspecops_t *hwspecops);
+extern int ssv_register(struct hwspecops_t *hwspecops);
+
+int init_device_struct(int card);
+int init_hwspecops(int card);
+int init_chip_struct(int card);
+int init_obj_struct(int card, int chip);
+int init_chipspecops(int card, int chipnr);
+
+int add_mem_to_list(void *address_p)
+{
+ struct mem_addr *mem_new;
+
+#ifdef DEBUG_MEM
+ DEBUGMSG("add_mem_to_list %p, mem_head=%p\n",address_p, mem_head);
+ return 0;
+#endif
+
+ mem_new=(struct mem_addr *)kmalloc(sizeof(struct mem_addr),GFP_KERNEL);
+ if (mem_new == NULL) {
+ CANMSG("Memory list error.\n");
+ return -ENOMEM;
+ }
+ mem_new->next=mem_head;
+ mem_new->address=address_p;
+ mem_head=mem_new;
+
+ return 0;
+}
+
+int del_mem_from_list(void *address_p)
+{
+ struct mem_addr *mem_search=NULL;
+ struct mem_addr *mem_delete=NULL;
+
+#ifdef DEBUG_MEM
+ DEBUGMSG("del_mem_from_list %p, mem_head=%p\n", address_p, mem_head);
+ return 0;
+#endif
+
+ mem_search = mem_head;
+
+ if (mem_head->address == address_p) {
+ kfree(mem_head->address);
+ mem_head=mem_head->next;
+ kfree(mem_search);
+ }
+ else {
+ while (mem_search->next->address != address_p)
+ mem_search=mem_search->next;
+ kfree(mem_search->next->address);
+ mem_delete=mem_search->next;
+ mem_search->next=mem_search->next->next;
+ kfree(mem_delete);
+ }
+ return 0;
+}
+
+
+int del_mem_list(void)
+{
+ struct mem_addr *mem_old;
+
+#ifdef DEBUG_MEM
+ DEBUGMSG("del_mem_list, mem_head=%p\n", mem_head);
+ return 0;
+#endif
+
+ while (mem_head->next != NULL) {
+ mem_old=mem_head;
+ kfree(mem_old->address);
+ mem_head=mem_old->next;
+ kfree(mem_old);
+ }
+
+ return 0;
+}
+
+/* The function init_hw_struct is used to initialize the hardware structure. */
+int init_hw_struct(void)
+{
+ int i=0;
+
+ hardware_p->nr_boards=0;
+ while ( (hw[i] != NULL) & (i < MAX_HW_CARDS) ) {
+ hardware_p->nr_boards++;
+
+ if (init_device_struct(i)) {
+ CANMSG("Error initializing candevice_t structures.\n");
+ return -ENODEV;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+/* The function init_device_struct is used to initialize a single device
+ * structure.
+ */
+int init_device_struct(int card)
+{
+ hardware_p->candevice[card]=(struct candevice_t *)kmalloc(sizeof(struct candevice_t),GFP_KERNEL);
+ if (hardware_p->candevice[card]==NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(hardware_p->candevice[card]) )
+ return -ENOMEM;
+
+ candevices_p[card]=hardware_p->candevice[card];
+
+ hardware_p->candevice[card]->hwname=hw[card];
+ hardware_p->candevice[card]->io_addr=io[card];
+
+ hardware_p->candevice[card]->hwspecops=(struct hwspecops_t *)kmalloc(sizeof(struct hwspecops_t),GFP_KERNEL);
+ if (hardware_p->candevice[card]->hwspecops==NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(hardware_p->candevice[card]->hwspecops) )
+ return -ENOMEM;
+
+ if (init_hwspecops(card))
+ return -ENODEV;
+
+ if (candevices_p[card]->hwspecops->init_hw_data(card))
+ return -ENODEV;
+
+ if (init_chip_struct(card))
+ return -ENODEV;
+
+ return 0;
+}
+
+/* The function init_chip_struct is used to initialize all chip_t structures
+ * on one hardware board.
+ */
+int init_chip_struct(int card)
+{
+ static int irq_count=0;
+ int i=0;
+
+ /* Alocate and initialize the chip structures */
+ for (i=0; i < candevices_p[card]->nr_82527_chips+candevices_p[card]->nr_sja1000_chips; i++) {
+ candevices_p[card]->chip[i]=(struct chip_t *)kmalloc(sizeof(struct chip_t),GFP_KERNEL);
+ if (candevices_p[card]->chip[i]==NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(candevices_p[card]->chip[i]) )
+ return -ENOMEM;
+
+ candevices_p[card]->chip[i]->chipspecops=(struct chipspecops_t *)kmalloc(sizeof(struct chipspecops_t),GFP_KERNEL);
+ if (candevices_p[card]->chip[i]->chipspecops==NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(candevices_p[card]->chip[i]->chipspecops) )
+ return -ENOMEM;
+
+ chips_p[irq_count]=candevices_p[card]->chip[i];
+ candevices_p[card]->chip[i]->hostdevice=candevices_p[card];
+ candevices_p[card]->chip[i]->chip_irq=irq[irq_count];
+ candevices_p[card]->chip[i]->flags=0x0;
+
+ candevices_p[card]->hwspecops->init_chip_data(card,i);
+
+ if (init_chipspecops(card,i))
+ return -ENODEV;
+
+ init_obj_struct(card, irq_count);
+
+ irq_count++;
+ }
+
+ return 0;
+}
+
+int init_obj_struct(int card, int chip)
+{
+ static int obj_count=0;
+ int i=0,max_objects=0;
+
+ if (!strcmp(chips_p[chip]->chip_type,"i82527"))
+ max_objects=15;
+ else
+ max_objects=1;
+ for (i=0; i<max_objects; i++) {
+ chips_p[chip]->msgobj[i]=(struct msgobj_t *)kmalloc(sizeof(struct msgobj_t),GFP_KERNEL);
+ if (chips_p[chip]->msgobj[i] == NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(chips_p[chip]->msgobj[i]) )
+ return -ENOMEM;
+
+ chips_p[chip]->msgobj[i]->fifo=(struct canfifo_t *)kmalloc(sizeof(struct canfifo_t),GFP_KERNEL);
+ if (chips_p[chip]->msgobj[i]->fifo == NULL)
+ return -ENOMEM;
+ else
+ if ( add_mem_to_list(chips_p[chip]->msgobj[i]->fifo) )
+ return -ENOMEM;
+
+ if (minor[0] == -1) {
+ objects_p[obj_count]=chips_p[chip]->msgobj[i];
+ objects_p[obj_count]->hostchip=chips_p[chip];
+ objects_p[obj_count]->object=i+1;
+ objects_p[obj_count]->minor=obj_count;
+ }
+ else {
+ objects_p[minor[chip]+i]=chips_p[chip]->msgobj[i];
+ objects_p[minor[chip]+i]->hostchip=chips_p[chip];
+ objects_p[minor[chip]+i]->object=i+1;
+ objects_p[minor[chip]+i]->minor=minor[chip]+i;
+ }
+
+ chips_p[chip]->msgobj[i]->flags = 0x0;
+
+ candevices_p[card]->hwspecops->init_obj_data(chip,i);
+
+ obj_count++;
+ }
+ return 0;
+}
+
+
+int init_hwspecops(int card)
+{
+ #ifdef TEMPLATE
+ if (!strcmp(candevices_p[card]->hwname,"template")) {
+ template_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef PIP
+ if (!strcmp(candevices_p[card]->hwname,"pip5")) {
+ pip5_register(candevices_p[card]->hwspecops);
+ }
+ else if (!strcmp(candevices_p[card]->hwname,"pip6")) {
+ pip6_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef SMARTCAN
+ if (!strcmp(candevices_p[card]->hwname,"smartcan")) {
+ smartcan_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef NSI
+ if (!strcmp(candevices_p[card]->hwname,"nsican")) {
+ nsi_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef CC104
+ if (!strcmp(candevices_p[card]->hwname,"cc104")) {
+ cc104_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef AIM104
+ if (!strcmp(candevices_p[card]->hwname,"aim104")) {
+ aim104_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef PCI03
+ if (!strcmp(candevices_p[card]->hwname,"pc-i03")) {
+ pci03_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef PCM3680
+ if (!strcmp(candevices_p[card]->hwname,"pcm3680")) {
+ pcm3680_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef PCCAN
+ if (!strcmp(candevices_p[card]->hwname,"pccan-f") |
+ !strcmp(candevices_p[card]->hwname,"pccan-s") ) {
+ pccanf_register(candevices_p[card]->hwspecops);
+ }
+ if (!strcmp(candevices_p[card]->hwname,"pccan-d")) {
+ pccand_register(candevices_p[card]->hwspecops);
+ }
+ if (!strcmp(candevices_p[card]->hwname,"pccan-q")) {
+ pccanq_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef M437
+ if (!strcmp(candevices_p[card]->hwname,"m437")) {
+ m437_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef PCCCAN
+ if (!strcmp(candevices_p[card]->hwname,"pcccan")) {
+ pcccan_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ #ifdef SSV
+ if (!strcmp(candevices_p[card]->hwname,"ssv")) {
+ ssv_register(candevices_p[card]->hwspecops);
+ }
+ #endif
+ return 0;
+}
+
+int init_chipspecops(int card, int chipnr)
+{
+ if (!strcmp(candevices_p[card]->chip[chipnr]->chip_type,"i82527")) {
+ i82527_register(candevices_p[card]->chip[chipnr]->chipspecops);
+ }
+ if (!strcmp(candevices_p[card]->chip[chipnr]->chip_type,"sja1000")) {
+ sja1000_register(candevices_p[card]->chip[chipnr]->chipspecops);
+ }
+ if (!strcmp(candevices_p[card]->chip[chipnr]->chip_type,"sja1000p")) {
+ sja1000p_register(candevices_p[card]->chip[chipnr]->chipspecops);
+ }
+
+ return 0;
+}
--- /dev/null
+/* sja1000.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.6 18 Sept 2000
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/delay.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/sja1000.h"
+
+int sja1000_enable_configuration(struct chip_t *chip)
+{
+ int i=0;
+ unsigned flags;
+
+ disable_irq(chip->chip_irq);
+
+ flags=can_read_reg(chip,SJACR);
+
+ while ((!(flags & CR_RR)) && (i<=10)) {
+ can_write_reg(chip,flags|CR_RR,SJACR);
+ udelay(100);
+ i++;
+ flags=can_read_reg(chip,SJACR);
+ }
+ if (i>=10) {
+ CANMSG("Reset error\n");
+ enable_irq(chip->chip_irq);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int sja1000_disable_configuration(struct chip_t *chip)
+{
+ int i=0;
+ unsigned flags;
+
+ flags=can_read_reg(chip,SJACR);
+
+ while ( (flags & CR_RR) && (i<=10) ) {
+ can_write_reg(chip,flags & (CR_RIE|CR_TIE|CR_EIE|CR_OIE),SJACR);
+ udelay(100);
+ i++;
+ flags=can_read_reg(chip,SJACR);
+ }
+ if (i>=10) {
+ CANMSG("Error leaving reset status\n");
+ return -ENODEV;
+ }
+
+ enable_irq(chip->chip_irq);
+
+ return 0;
+}
+
+int sja1000_chip_config(struct chip_t *chip)
+{
+ if (sja1000_enable_configuration(chip))
+ return -ENODEV;
+
+ /* Set mode, clock out, comparator */
+ can_write_reg(chip,chip->sja_cdr_reg,SJACDR);
+ /* Set driver output configuration */
+ can_write_reg(chip,chip->sja_ocr_reg,SJAOCR);
+
+ if (sja1000_standard_mask(chip,0x0000, 0xffff))
+ return -ENODEV;
+
+ if (!baudrate)
+ baudrate=1000;
+ if (sja1000_baud_rate(chip,1000*baudrate,chip->clock,0,75,0))
+ return -ENODEV;
+
+ /* Enable hardware interrupts */
+ can_write_reg(chip,(CR_RIE|CR_TIE|CR_EIE|CR_OIE),SJACR);
+
+ sja1000_disable_configuration(chip);
+
+ return 0;
+}
+
+int sja1000_standard_mask(struct chip_t *chip, unsigned short code, unsigned short mask)
+{
+ unsigned char write_code, write_mask;
+
+ if (sja1000_enable_configuration(chip))
+ return -ENODEV;
+
+ /* The acceptance code bits (SJAACR bits 0-7) and the eight most
+ * significant bits of the message identifier (id.10 to id.3) must be
+ * equal to those bit positions which are marked relevant by the
+ * acceptance mask bits (SJAAMR bits 0-7).
+ * (id.10 to id.3) = (SJAACR.7 to SJAACR.0) v (SJAAMR.7 to SJAAMR.0)
+ * (Taken from Philips sja1000 Data Sheet)
+ */
+ write_code = (unsigned char) code >> 3;
+ write_mask = (unsigned char) mask >> 3;
+
+ can_write_reg(chip,write_code,SJAACR);
+ can_write_reg(chip,write_mask,SJAAMR);
+
+ DEBUGMSG("Setting acceptance code to 0x%lx\n",(unsigned long)code);
+ DEBUGMSG("Setting acceptance mask to 0x%lx\n",(unsigned long)mask);
+
+ sja1000_disable_configuration(chip);
+
+ return 0;
+}
+
+/* Set communication parameters.
+ * param rate baud rate in Hz
+ * param clock frequency of sja1000 clock in Hz (ISA osc is 14318000)
+ * param sjw synchronization jump width (0-3) prescaled clock cycles
+ * param sampl_pt sample point in % (0-100) sets (TSEG1+2)/(TSEG1+TSEG2+3) ratio
+ * param flags fields BTR1_SAM, OCMODE, OCPOL, OCTP, OCTN, CLK_OFF, CBP
+ */
+int sja1000_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags)
+{
+ int best_error = 1000000000, error;
+ int best_tseg=0, best_brp=0, best_rate=0, brp=0;
+ int tseg=0, tseg1=0, tseg2=0;
+
+ if (sja1000_enable_configuration(chip))
+ return -ENODEV;
+
+ clock /=2;
+
+ /* tseg even = round down, odd = round up */
+ for (tseg=(0+0+2)*2; tseg<=(MAX_TSEG2+MAX_TSEG1+2)*2+1; tseg++) {
+ brp = clock/((1+tseg/2)*rate)+tseg%2;
+ if (brp == 0 || brp > 64)
+ continue;
+ error = rate - clock/(brp*(1+tseg/2));
+ if (error < 0)
+ error = -error;
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg/2;
+ best_brp = brp-1;
+ best_rate = clock/(brp*(1+tseg/2));
+ }
+ }
+ if (best_error && (rate/best_error < 10)) {
+ CANMSG("baud rate %d is not possible with %d Hz clock\n",
+ rate, 2*clock);
+ CANMSG("%d bps. brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d\n",
+ best_rate, best_brp, best_tseg, tseg1, tseg2);
+ return -EINVAL;
+ }
+ tseg2 = best_tseg-(sampl_pt*(best_tseg+1))/100;
+ if (tseg2 < 0)
+ tseg2 = 0;
+ if (tseg2 > MAX_TSEG2)
+ tseg2 = MAX_TSEG2;
+ tseg1 = best_tseg-tseg2-2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg-tseg1-2;
+ }
+
+ DEBUGMSG("Setting %d bps.\n", best_rate);
+ DEBUGMSG("brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d, sampl_pt=%d\n",
+ best_brp, best_tseg, tseg1, tseg2,
+ (100*(best_tseg-tseg2)/(best_tseg+1)));
+
+
+ can_write_reg(chip, sjw<<6 | best_brp, SJABTR0);
+ can_write_reg(chip, ((flags & BTR1_SAM) != 0)<<7 | tseg2<<4 | tseg1,
+ SJABTR1);
+// can_write_reg(chip, OCR_MODE_NORMAL | OCR_TX0_LH | OCR_TX1_ZZ, SJAOCR);
+ /* BASIC mode, bypass input comparator */
+// can_write_reg(chip, CDR_CBP| /* CDR_CLK_OFF | */ 7, SJACDR);
+
+ sja1000_disable_configuration(chip);
+
+ return 0;
+}
+
+int sja1000_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
+{
+ int i;
+ struct canfifo_t *fifo = chip->msgobj[0]->fifo;
+ int id;
+ i=can_read_reg(chip,SJASR);
+
+ if (!(i&SR_RBS)) {
+//Temp
+ for (i=0; i<0x20; i++)
+ CANMSG("0x%x is 0x%x\n",i,can_read_reg(chip,i));
+ return 0;
+ }
+ sja1000_start_chip(chip);
+
+ can_write_reg(chip, 0, SJACR); // disable interrupts for a moment
+// TODO: this would be best sja1000_irq_read_handler(chip);
+// now just duplicate the code.
+ do {
+ id=(can_read_reg(chip, SJARXID1)<<8) + can_read_reg(chip,
+ SJARXID0);
+ fifo->buf_rx_entry[fifo->head].length = (id>>8) & 0x0f;
+ fifo->buf_rx_entry[fifo->head].id = id>>5;
+ fifo->buf_rx_entry[fifo->head].flags = id&ID0_RTR ?
+ MSG_RTR : 0;
+ fifo->buf_rx_entry[fifo->head].timestamp = 0;
+ fifo->buf_rx_entry[fifo->head].cob = 0;
+ for (i=0; i<fifo->buf_rx_entry[fifo->head].length; i++) {
+ fifo->buf_rx_entry[fifo->head].data[i] =
+ can_read_reg(chip,SJARXDAT0 + i);
+ }
+ fifo->head++;
+ if (fifo->head == MAX_BUF_LENGTH -1)
+ fifo->head = 0;
+ can_write_reg(chip, CMR_RRB, SJACMR);
+ } while (can_read_reg(chip, SJASR) & SR_RBS);
+
+// enable interrupts
+ can_write_reg(chip, CR_OIE | CR_EIE | CR_TIE | CR_RIE, SJACR);
+
+
+ return 1;
+}
+
+#define MAX_TRANSMIT_WAIT_LOOPS 200
+int sja1000_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ int i=0, id=0;
+
+ sja1000_start_chip(chip); //sja1000 goes automatically into reset mode on errors
+
+ /* Wait until Transmit Buffer Status is released */
+ while ( !(can_read_reg(chip, SJASR) & SR_TBS) &&
+ i++<MAX_TRANSMIT_WAIT_LOOPS) {
+ udelay(i);
+ }
+
+ if (!(can_read_reg(chip, SJASR) & SR_TBS)) {
+ CANMSG("Transmit timed out, cancelling\n");
+ can_write_reg(chip, CMR_AT, SJACMR);
+ i=0;
+ while ( !(can_read_reg(chip, SJASR) & SR_TBS) &&
+ i++<MAX_TRANSMIT_WAIT_LOOPS) {
+ udelay(i);
+ }
+ if (!(can_read_reg(chip, SJASR) & SR_TBS)) {
+ CANMSG("Could not cancel, please reset\n");
+ return -EIO;
+ }
+ }
+
+ id = (msg->id<<5) | ((msg->flags&MSG_RTR)?ID0_RTR:0) | msg->length;
+
+ can_write_reg(chip, id>>8, SJATXID1);
+ can_write_reg(chip, id & 0xff , SJATXID0);
+
+ for (i=0; i<msg->length; i++)
+ can_write_reg(chip, msg->data[i], SJATXDAT0+i);
+
+ return 0;
+}
+
+int sja1000_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ can_write_reg(chip, CMR_TR, SJACMR);
+
+ return 0;
+}
+
+int sja1000_check_tx_stat(struct chip_t *chip)
+{
+ if (can_read_reg(chip,SJASR) & SR_TCS)
+ return 0;
+ else
+ return 1;
+}
+
+int sja1000_set_btregs(struct chip_t *chip, unsigned short btr0,
+ unsigned short btr1)
+{
+ if (sja1000_enable_configuration(chip))
+ return -ENODEV;
+
+ can_write_reg(chip, btr0, SJABTR0);
+ can_write_reg(chip, btr1, SJABTR1);
+
+ sja1000_disable_configuration(chip);
+
+ return 0;
+}
+
+int sja1000_start_chip(struct chip_t *chip)
+{
+ unsigned short flags = 0;
+
+ flags = can_read_reg(chip, SJACR) & (CR_RIE|CR_TIE|CR_EIE|CR_OIE);
+ can_write_reg(chip, flags, SJACR);
+
+ return 0;
+}
+
+int sja1000_stop_chip(struct chip_t *chip)
+{
+ unsigned short flags = 0;
+
+ flags = can_read_reg(chip, SJACR) & (CR_RIE|CR_TIE|CR_EIE|CR_OIE);
+ can_write_reg(chip, flags|CR_RR, SJACR);
+
+ return 0;
+}
+
+int sja1000_remote_request(struct chip_t *chip, struct msgobj_t *obj)
+{
+ CANMSG("sja1000_remote_request not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000_extended_mask(struct chip_t *chip, unsigned long code,
+ unsigned long mask)
+{
+ CANMSG("sja1000_extended_mask not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000_clear_objects(struct chip_t *chip)
+{
+ CANMSG("sja1000_clear_objects not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000_config_irqs(struct chip_t *chip, short irqs)
+{
+ CANMSG("sja1000_config_irqs not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000_register(struct chipspecops_t *chipspecops)
+{
+ chipspecops->chip_config = sja1000_chip_config;
+ chipspecops->baud_rate = sja1000_baud_rate;
+ chipspecops->standard_mask = sja1000_standard_mask;
+ chipspecops->extended_mask = sja1000_extended_mask;
+ chipspecops->message15_mask = sja1000_extended_mask;
+ chipspecops->clear_objects = sja1000_clear_objects;
+ chipspecops->config_irqs = sja1000_config_irqs;
+ chipspecops->pre_read_config = sja1000_pre_read_config;
+ chipspecops->pre_write_config = sja1000_pre_write_config;
+ chipspecops->send_msg = sja1000_send_msg;
+ chipspecops->check_tx_stat = sja1000_check_tx_stat;
+ chipspecops->remote_request = sja1000_remote_request;
+ chipspecops->enable_configuration = sja1000_enable_configuration;
+ chipspecops->disable_configuration = sja1000_disable_configuration;
+ chipspecops->set_btregs = sja1000_set_btregs;
+ chipspecops->start_chip = sja1000_start_chip;
+ chipspecops->stop_chip = sja1000_stop_chip;
+ chipspecops->irq_handler = sja1000_irq_handler;
+ return 0;
+}
--- /dev/null
+/* sja1000.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.6 18 Sept 2000
+ * Changed for PeliCan mode SJA1000 by Tomasz Motylewski (BFAD GmbH)
+ * T.Motylewski@bfad.de
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/sja1000p.h"
+
+struct chip_t *chip_irq=NULL;
+struct candevice_t *device_irq=NULL;
+struct canfifo_t *fifo_irq=NULL;
+void (*put_reg)(unsigned char data, unsigned long address);
+unsigned (*get_reg)(unsigned long address);
+
+
+
+int sja1000p_enable_configuration(struct chip_t *chip)
+{
+ int i=0;
+ enum sja1000_PeliCAN_MOD flags;
+
+ disable_irq(chip->chip_irq);
+
+ flags=can_read_reg(chip,SJAMOD);
+
+ while ((!(flags & MOD_RM)) && (i<=10)) {
+ can_write_reg(chip, MOD_RM, SJAMOD);
+// TODO: chinfigurable MOD_AFM (32/16 bit acceptance filter)
+// config MOD_LOM (listen only)
+ udelay(100);
+ i++;
+ flags=can_read_reg(chip, SJAMOD);
+ }
+ if (i>=10) {
+ CANMSG("Reset error\n");
+ enable_irq(chip->chip_irq);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int sja1000p_disable_configuration(struct chip_t *chip)
+{
+ int i=0;
+ enum sja1000_PeliCAN_MOD flags;
+
+ flags=can_read_reg(chip,SJAMOD);
+
+ while ( (flags & MOD_RM) && (i<=10) ) {
+ can_write_reg(chip, 0, SJAMOD);
+// TODO: chinfigurable MOD_AFM (32/16 bit acceptance filter)
+// config MOD_LOM (listen only)
+ udelay(100);
+ i++;
+ flags=can_read_reg(chip, SJAMOD);
+ }
+ if (i>=10) {
+ CANMSG("Error leaving reset status\n");
+ return -ENODEV;
+ }
+
+ enable_irq(chip->chip_irq);
+
+ return 0;
+}
+
+int sja1000p_chip_config(struct chip_t *chip)
+{
+ if (sja1000p_enable_configuration(chip))
+ return -ENODEV;
+
+ /* Set mode, clock out, comparator */
+ can_write_reg(chip,CDR_PELICAN|chip->sja_cdr_reg,SJACDR);
+ /* Set driver output configuration */
+ can_write_reg(chip,chip->sja_ocr_reg,SJAOCR);
+
+ if (sja1000p_extended_mask(chip,0x00000000, 0xffffffff))
+ return -ENODEV;
+
+ if (!baudrate)
+ baudrate=1000;
+ if (sja1000p_baud_rate(chip,1000*baudrate,chip->clock,0,75,0))
+ return -ENODEV;
+
+ /* Enable hardware interrupts */
+ can_write_reg(chip, ENABLE_INTERRUPTS, SJAIER);
+
+ sja1000p_disable_configuration(chip);
+
+ return 0;
+}
+
+int sja1000p_extended_mask(struct chip_t *chip, unsigned long code, unsigned long mask)
+{
+ int i;
+
+ if (sja1000p_enable_configuration(chip))
+ return -ENODEV;
+
+// LSB to +3, MSB to +0
+ for(i=SJA_PeliCAN_AC_LEN; --i>=0;) {
+ can_write_reg(chip,code&0xff,SJAACR0+i);
+ can_write_reg(chip,mask&0xff,SJAAMR0+i);
+ code >>= 8;
+ mask >>= 8;
+ }
+
+ DEBUGMSG("Setting acceptance code to 0x%lx\n",(unsigned long)code);
+ DEBUGMSG("Setting acceptance mask to 0x%lx\n",(unsigned long)mask);
+
+ sja1000p_disable_configuration(chip);
+
+ return 0;
+}
+
+/* Set communication parameters.
+ * param rate baud rate in Hz
+ * param clock frequency of sja1000 clock in Hz (ISA osc is 14318000)
+ * param sjw synchronization jump width (0-3) prescaled clock cycles
+ * param sampl_pt sample point in % (0-100) sets (TSEG1+1)/(TSEG1+TSEG2+2) ratio
+ * param flags fields BTR1_SAM, OCMODE, OCPOL, OCTP, OCTN, CLK_OFF, CBP
+ */
+int sja1000p_baud_rate(struct chip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags)
+{
+ int best_error = 1000000000, error;
+ int best_tseg=0, best_brp=0, best_rate=0, brp=0;
+ int tseg=0, tseg1=0, tseg2=0;
+
+ if (sja1000p_enable_configuration(chip))
+ return -ENODEV;
+
+ clock /=2;
+
+ /* tseg even = round down, odd = round up */
+ for (tseg=(0+0+2)*2; tseg<=(MAX_TSEG2+MAX_TSEG1+2)*2+1; tseg++) {
+ brp = clock/((1+tseg/2)*rate)+tseg%2;
+ if (brp == 0 || brp > 64)
+ continue;
+ error = rate - clock/(brp*(1+tseg/2));
+ if (error < 0)
+ error = -error;
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg/2;
+ best_brp = brp-1;
+ best_rate = clock/(brp*(1+tseg/2));
+ }
+ }
+ if (best_error && (rate/best_error < 10)) {
+ CANMSG("baud rate %d is not possible with %d Hz clock\n",
+ rate, 2*clock);
+ CANMSG("%d bps. brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d\n",
+ best_rate, best_brp, best_tseg, tseg1, tseg2);
+ return -EINVAL;
+ }
+ tseg2 = best_tseg-(sampl_pt*(best_tseg+1))/100;
+ if (tseg2 < 0)
+ tseg2 = 0;
+ if (tseg2 > MAX_TSEG2)
+ tseg2 = MAX_TSEG2;
+ tseg1 = best_tseg-tseg2-2;
+ if (tseg1>MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg-tseg1-2;
+ }
+
+ DEBUGMSG("Setting %d bps.\n", best_rate);
+ DEBUGMSG("brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d, sampl_pt=%d\n",
+ best_brp, best_tseg, tseg1, tseg2,
+ (100*(best_tseg-tseg2)/(best_tseg+1)));
+
+
+ can_write_reg(chip, sjw<<6 | best_brp, SJABTR0);
+ can_write_reg(chip, ((flags & BTR1_SAM) != 0)<<7 | (tseg2<<4)
+ | tseg1, SJABTR1);
+
+ sja1000p_disable_configuration(chip);
+
+ return 0;
+}
+
+void sja1000p_read(struct chip_t *chip, struct canfifo_t *fifo) {
+ int i, flags, len, datastart;
+ do {
+ flags = can_read_reg(chip,SJAFRM);
+ if(flags&FRM_FF) {
+ fifo->buf_rx_entry[fifo->head].id =
+ (can_read_reg(chip,SJAID0)<<21) +
+ (can_read_reg(chip,SJAID1)<<13) +
+ (can_read_reg(chip,SJAID2)<<5) +
+ (can_read_reg(chip,SJAID3)>>3);
+ datastart = SJADATE;
+ } else {
+ fifo->buf_rx_entry[fifo->head].id =
+ (can_read_reg(chip,SJAID0)<<3) +
+ (can_read_reg(chip,SJAID1)>>5);
+ datastart = SJADATS;
+ }
+ fifo->buf_rx_entry[fifo->head].flags =
+ ((flags & FRM_RTR) ? MSG_RTR : 0) |
+ ((flags & FRM_FF) ? MSG_EXT : 0);
+ len = flags & FRM_DLC_M;
+ for(i=0; i< len; i++) {
+ fifo->buf_rx_entry[fifo->head].data[i]=
+ can_read_reg(chip,datastart+i);
+ }
+ fifo->buf_rx_entry[fifo->head].length = len;
+ fifo->head++; fifo->head %= MAX_BUF_LENGTH;
+// FIXME: what if fifo->head == fifo->tail again ?
+ can_write_reg(chip, CMR_RRB, SJACMR);
+ } while (can_read_reg(chip, SJASR) & SR_RBS);
+}
+
+int sja1000p_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
+{
+ int i;
+ i=can_read_reg(chip,SJASR);
+
+ if (!(i&SR_RBS)) {
+ return 0;
+ }
+
+ can_write_reg(chip, DISABLE_INTERRUPTS, SJAIER); //disable interrupts for a moment
+ sja1000p_read(chip, obj->fifo);
+ can_write_reg(chip, ENABLE_INTERRUPTS, SJAIER); //enable interrupts
+ return 1;
+}
+
+#define MAX_TRANSMIT_WAIT_LOOPS 200
+int sja1000p_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ int i=0;
+ unsigned int id;
+
+ /* Wait until Transmit Buffer Status is released */
+ while ( !(can_read_reg(chip, SJASR) & SR_TBS) &&
+ i++<MAX_TRANSMIT_WAIT_LOOPS) {
+ udelay(i);
+ }
+
+ if (!(can_read_reg(chip, SJASR) & SR_TBS)) {
+ CANMSG("Transmit timed out, cancelling\n");
+// here we should check if there is no write/select waiting for this
+// transmit. If so, set error ret and wake up.
+// CHECKME: if we do not disable IER_TIE (TX IRQ) here we get interrupt
+// immediately
+ can_write_reg(chip, CMR_AT, SJACMR);
+ i=0;
+ while ( !(can_read_reg(chip, SJASR) & SR_TBS) &&
+ i++<MAX_TRANSMIT_WAIT_LOOPS) {
+ udelay(i);
+ }
+ if (!(can_read_reg(chip, SJASR) & SR_TBS)) {
+ CANMSG("Could not cancel, please reset\n");
+ return -EIO;
+ }
+ }
+ msg->length &= FRM_DLC_M;
+ can_write_reg(chip, ((msg->flags&MSG_EXT)?FRM_FF:0) |
+ ((msg->flags & MSG_RTR) ? FRM_RTR : 0) |
+ msg->length, SJAFRM);
+ if(msg->flags&MSG_EXT) {
+ id=msg->id<<3;
+ can_write_reg(chip, id & 0xff, SJAID3);
+ id >>= 8;
+ can_write_reg(chip, id & 0xff, SJAID2);
+ id >>= 8;
+ can_write_reg(chip, id & 0xff, SJAID1);
+ id >>= 8;
+ can_write_reg(chip, id, SJAID0);
+ for(i=0; i < msg->length; i++) {
+ can_write_reg(chip, msg->data[i], SJADATE+i);
+ }
+ } else {
+ id=msg->id >> 5;
+ can_write_reg(chip, id & 0xff, SJAID0);
+ id >>= 8;
+ can_write_reg(chip, id & 0xff, SJAID1);
+ for(i=0; i < msg->length; i++) {
+ can_write_reg(chip, msg->data[i], SJADATS+i);
+ }
+ }
+ return 0;
+}
+
+int sja1000p_send_msg(struct chip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ can_write_reg(chip, CMR_TR, SJACMR);
+
+ return 0;
+}
+
+int sja1000p_check_tx_stat(struct chip_t *chip)
+{
+ if (can_read_reg(chip,SJASR) & SR_TCS)
+ return 0;
+ else
+ return 1;
+}
+
+int sja1000p_set_btregs(struct chip_t *chip, unsigned short btr0,
+ unsigned short btr1)
+{
+ if (sja1000p_enable_configuration(chip))
+ return -ENODEV;
+
+ can_write_reg(chip, btr0, SJABTR0);
+ can_write_reg(chip, btr1, SJABTR1);
+
+ sja1000p_disable_configuration(chip);
+
+ return 0;
+}
+
+int sja1000p_start_chip(struct chip_t *chip)
+{
+ enum sja1000_PeliCAN_MOD flags;
+
+ flags = can_read_reg(chip, SJAMOD) & (MOD_LOM|MOD_STM|MOD_AFM|MOD_SM);
+ can_write_reg(chip, flags, SJAMOD);
+
+ return 0;
+}
+
+int sja1000p_stop_chip(struct chip_t *chip)
+{
+ enum sja1000_PeliCAN_MOD flags;
+
+ flags = can_read_reg(chip, SJAMOD) & (MOD_LOM|MOD_STM|MOD_AFM|MOD_SM);
+ can_write_reg(chip, flags|MOD_RM, SJAMOD);
+
+ return 0;
+}
+
+
+int sja1000p_remote_request(struct chip_t *chip, struct msgobj_t *obj)
+{
+ CANMSG("sja1000p_remote_request not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000p_standard_mask(struct chip_t *chip, unsigned short code,
+ unsigned short mask)
+{
+ CANMSG("sja1000p_standard_mask not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000p_clear_objects(struct chip_t *chip)
+{
+ CANMSG("sja1000p_clear_objects not implemented\n");
+ return -ENOSYS;
+}
+
+int sja1000p_config_irqs(struct chip_t *chip, short irqs)
+{
+ CANMSG("sja1000p_config_irqs not implemented\n");
+ return -ENOSYS;
+}
+
+void sja1000p_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int irq_register;
+ chip_irq=(struct chip_t *)dev_id;
+ device_irq=(struct candevice_t *)chip_irq->hostdevice;
+
+ put_reg=device_irq->hwspecops->write_register;
+ get_reg=device_irq->hwspecops->read_register;
+
+ irq_register=get_reg(chip_irq->chip_base_addr+SJAIR);
+// DEBUGMSG("sja1000_irq_handler: SJAIR:%02x\n",irq_register);
+// DEBUGMSG("sja1000_irq_handler: SJASR:%02x\n",
+// get_reg(chip_irq->chip_base_addr+SJASR));
+
+ if ((irq_register & (IR_BEI|IR_EPI|IR_DOI|IR_EI|IR_TI|IR_RI)) == 0)
+ return;
+
+ fifo_irq=chip_irq->msgobj[0]->fifo;
+
+ if ((irq_register & IR_RI) != 0) {
+ sja1000p_read(chip_irq,fifo_irq);
+ chip_irq->msgobj[0]->ret = 0;
+ if (waitqueue_active(&fifo_irq->readq))
+ wake_up_interruptible(&fifo_irq->readq);
+ }
+ if ((irq_register & IR_TI) != 0) {
+ chip_irq->msgobj[0]->ret = 0;
+ if (waitqueue_active(&fifo_irq->writeq))
+ wake_up_interruptible(&fifo_irq->writeq);
+ }
+ if ((irq_register & (IR_EI|IR_BEI|IR_EPI|IR_DOI)) != 0) {
+ // Some error happened
+ CANMSG("Error: status register: 0x%x irq_register: 0x%02x\n",
+ get_reg(chip_irq->chip_base_addr+SJASR), irq_register);
+// FIXME: chip should be brought to usable state. Transmission cancelled if in progress.
+// Reset flag set to 0 if chip is already off the bus. Full state report
+ chip_irq->msgobj[0]->ret=-1;
+ if (waitqueue_active(&fifo_irq->writeq))
+ wake_up_interruptible(&fifo_irq->writeq);
+ if (waitqueue_active(&fifo_irq->readq))
+ wake_up_interruptible(&fifo_irq->readq);
+ }
+
+ return;
+}
+
+int sja1000p_register(struct chipspecops_t *chipspecops)
+{
+ CANMSG("initializing sja1000p chip operations\n");
+ chipspecops->chip_config=sja1000p_chip_config;
+ chipspecops->baud_rate=sja1000p_baud_rate;
+ chipspecops->standard_mask=sja1000p_standard_mask;
+ chipspecops->extended_mask=sja1000p_extended_mask;
+ chipspecops->message15_mask=sja1000p_extended_mask;
+ chipspecops->clear_objects=sja1000p_clear_objects;
+ chipspecops->config_irqs=sja1000p_config_irqs;
+ chipspecops->pre_read_config=sja1000p_pre_read_config;
+ chipspecops->pre_write_config=sja1000p_pre_write_config;
+ chipspecops->send_msg=sja1000p_send_msg;
+ chipspecops->check_tx_stat=sja1000p_check_tx_stat;
+ chipspecops->remote_request=sja1000p_remote_request;
+ chipspecops->enable_configuration=sja1000p_enable_configuration;
+ chipspecops->disable_configuration=sja1000p_disable_configuration;
+ chipspecops->set_btregs=sja1000p_set_btregs;
+ chipspecops->start_chip=sja1000p_start_chip;
+ chipspecops->stop_chip=sja1000p_stop_chip;
+ chipspecops->irq_handler=sja1000p_irq_handler;
+ return 0;
+}
--- /dev/null
+/* smartcan.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/smartcan.h"
+#include "../include/i82527.h"
+
+int smartcan_irq=-1;
+unsigned long smartcan_base=0x0;
+
+int smartcan_request_io(unsigned long io_addr)
+{
+ int err=0;
+
+ if ( (err=check_region(io_addr,0x04)) < 0 ) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,0x04,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + 0x04 - 1);
+ }
+ return 0;
+}
+
+int smartcan_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,0x04);
+
+ return 0;
+}
+
+int smartcan_reset(int card)
+{
+ int i=0;
+
+ DEBUGMSG("Resetting smartcan hardware ...\n");
+ outb(0x00,candevices_p[card]->res_addr);
+ while (i < 1000000) {
+ i++;
+ outb(0x01,candevices_p[card]->res_addr);
+ }
+ outb(0x00,candevices_p[card]->res_addr);
+
+ /* Check hardware reset status */
+ i=0;
+ outb(candevices_p[card]->io_addr+iCPU,candevices_p[card]->io_addr);
+ while ( (inb(candevices_p[card]->io_addr+1)&0x80) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip0 reset status ok.\n");
+
+ return 0;
+}
+
+int smartcan_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=candevices_p[card]->io_addr+0x02;
+ candevices_p[card]->nr_82527_chips=1;
+ candevices_p[card]->nr_sja1000_chips=0;
+
+ return 0;
+}
+
+int smartcan_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type="i82527";
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = 0;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = 0;
+ smartcan_irq=candevices_p[card]->chip[chipnr]->chip_irq;
+ smartcan_base=candevices_p[card]->chip[chipnr]->chip_base_addr;
+
+ return 0;
+}
+
+int smartcan_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+
+void smartcan_write_register(unsigned char data, unsigned long address)
+{
+ disable_irq(smartcan_irq);
+ outb(address-smartcan_base,smartcan_base);
+ outb(data,smartcan_base+1);
+ enable_irq(smartcan_irq);
+}
+
+unsigned smartcan_read_register(unsigned long address)
+{
+ unsigned ret;
+ disable_irq(smartcan_irq);
+ outb(address-smartcan_base,smartcan_base);
+ ret=inb(smartcan_base+1);
+ enable_irq(smartcan_irq);
+ return ret;
+}
+
+int smartcan_program_irq(int card)
+{
+ CANMSG("The 'smartcan' card doesn't have programmable interrupts\n");
+ return 0;
+}
+
+/* !!! Don't change this function !!! */
+int smartcan_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = smartcan_request_io;
+ hwspecops->release_io = smartcan_release_io;
+ hwspecops->reset = smartcan_reset;
+ hwspecops->init_hw_data = smartcan_init_hw_data;
+ hwspecops->init_chip_data = smartcan_init_chip_data;
+ hwspecops->init_obj_data = smartcan_init_obj_data;
+ hwspecops->write_register = smartcan_write_register;
+ hwspecops->read_register = smartcan_read_register;
+ hwspecops->program_irq = smartcan_program_irq;
+ return 0;
+}
--- /dev/null
+/* ssv.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@casema.net
+ * This software is released under the GPL-License.
+ * Version 0.6 18 Sept 2000
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "../include/main.h"
+#include "../include/ssv.h"
+#include "../include/i82527.h"
+
+int ssvcan_irq[2]={-1,-1};
+unsigned long ssvcan_base=0x0;
+
+/* IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips.
+ */
+#define IO_RANGE 0x04
+
+/* The function template_request_io is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * The reserved memory starts at io_addr, wich is the module parameter io.
+ */
+int ssv_request_io(unsigned long io_addr)
+{
+
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr,
+ io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/* The function template_release_io is used to free the previously reserved
+ * io-memory. In case you reserved more memory, don't forget to free it here.
+ */
+int ssv_release_io(unsigned long io_addr)
+{
+
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/* The function template_reset is used to give a hardware reset. This is rather
+ * hardware specific so I haven't included example code. Don't forget to check
+ * the reset status of the chip before returning.
+ */
+int ssv_reset(int card)
+{
+ int i;
+
+ DEBUGMSG("Resetting ssv hardware ...\n");
+ ssv_write_register(1,ssvcan_base+iCPU);
+ ssv_write_register(0,ssvcan_base+iCPU);
+ ssv_write_register(1,ssvcan_base+0x100+iCPU);
+ ssv_write_register(0,ssvcan_base+0x100+iCPU);
+
+ for (i = 1; i < 1000; i++)
+ udelay (1000);
+
+ /* Check hardware reset status */
+ i=0;
+ while ( (ssv_read_register(ssvcan_base+iCPU) & iCPU_RST) && (i<=15)) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip0 reset status ok.\n");
+
+ /* Check hardware reset status */
+ i=0;
+ while ( (ssv_read_register(ssvcan_base+0x100+iCPU) & iCPU_RST) && (i<=15)) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip1 reset status ok.\n");
+
+
+
+ return 0;
+}
+
+/* The function template_init_hw_data is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * RESET_ADDR represents the io-address of the hardware reset register.
+ * NR_82527 represents the number of intel 82527 chips on the board.
+ * NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ */
+#define RESET_ADDR 0x02
+#define NR_82527 2
+#define NR_SJA1000 0
+
+int ssv_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=NR_82527;
+ candevices_p[card]->nr_sja1000_chips=0;
+ candevices_p[card]->flags |= PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+/* The function template_init_chip_data is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * CHIP_TYPE represents the type of CAN chip. CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the io_addr
+ * argument supplied at module loading time.
+ * The clock argument holds the chip clock value in Hz.
+ */
+#define CHIP_TYPE "i82527"
+
+int ssv_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=
+ candevices_p[card]->io_addr+0x100*chipnr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ ssvcan_irq[chipnr]=candevices_p[card]->chip[chipnr]->chip_irq;
+
+ ssvcan_base=candevices_p[card]->io_addr;
+
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY;
+ return 0;
+}
+
+ /* The function template_init_obj_data is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ */
+int ssv_init_obj_data(int chipnr, int objnr)
+{
+
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=
+ chips_p[chipnr]->chip_base_addr+(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/* The function template_program_irq is used for hardware that uses programmable
+ * interrupts. If your hardware doesn't use programmable interrupts you should
+ * not set the candevices_t->flags entry to PROGRAMMABLE_IRQ and leave this
+ * function unedited. Again this function is hardware specific so there's no
+ * example code.
+ */
+int ssv_program_irq(int card)
+{
+ return 0;
+}
+
+/* The function template_write_register is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ */
+void ssv_write_register(unsigned char data, unsigned long address)
+{
+ /* address is an absolute address */
+
+ /* the ssv card has two registers, the address register at 0x0
+ and the data register at 0x01 */
+
+ /* write the relative address on the eight LSB bits
+ and the data on the eight MSB bits in one time */
+ if((address-ssvcan_base)<0x100)
+ outw(address-ssvcan_base + (256 * data), ssvcan_base);
+ else
+ outw(address-ssvcan_base-0x100 + (256 * data), ssvcan_base+0x02);
+}
+
+/* The function template_read_register is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ */
+unsigned ssv_read_register(unsigned long address)
+{
+ /* this is the same thing that the function write_register.
+ We use the two register, we write the address where we
+ want to read in a first time. In a second time we read the
+ data */
+ unsigned char ret;
+
+
+ if((address-ssvcan_base)<0x100)
+ {
+ disable_irq(ssvcan_irq[0]);
+ outb(address-ssvcan_base, ssvcan_base);
+ ret=inb(ssvcan_base+1);
+ enable_irq(ssvcan_irq[0]);
+ }
+ else
+ {
+ disable_irq(ssvcan_irq[1]);
+ outb(address-ssvcan_base-0x100, ssvcan_base+0x02);
+ ret=inb(ssvcan_base+1+0x02);
+ enable_irq(ssvcan_irq[1]);
+ }
+
+ return ret;
+}
+
+
+ /* !!! Don't change this function !!! */
+int ssv_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = ssv_request_io;
+ hwspecops->release_io = ssv_release_io;
+ hwspecops->reset = ssv_reset;
+ hwspecops->init_hw_data = ssv_init_hw_data;
+ hwspecops->init_chip_data = ssv_init_chip_data;
+ hwspecops->init_obj_data = ssv_init_obj_data;
+ hwspecops->write_register = ssv_write_register;
+ hwspecops->read_register = ssv_read_register;
+ hwspecops->program_irq = ssv_program_irq;
+ return 0;
+}
--- /dev/null
+{
+ int i=0;
+
+ DEBUGMSG("Resetting smartcan hardware ...\n");
+ outb(0x00,candevices_p[card]->res_addr);
+ while (i < 1000000) {
+ i++;
+ outb(0x01,candevices_p[card]->res_addr);
+ }
+ outb(0x00,candevices_p[card]->res_addr);
+
+ /* Check hardware reset status */
+ i=0;
+ outb(candevices_p[card]->io_addr+iCPU,candevices_p[card]->io_addr);
+ while ( (inb(candevices_p[card]->io_addr+1)&0x80) && (i<=15) ) {
+ udelay(20000);
+ i++;
+ }
+ if (i>=15) {
+ CANMSG("Reset status timeout!\n");
+ CANMSG("Please check your hardware.\n");
+ return -ENODEV;
+ }
+ else
+ DEBUGMSG("Chip0 reset status ok.\n");
+
+ return 0;
+}
--- /dev/null
+/* template.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+/* This file is intended as a template file for currently unsupported hardware.
+ * Once you've changed/added the functions specific to your hardware it is
+ * possible to load the driver with the hardware option hw=template.
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "../include/main.h"
+#include "../include/template.h"
+#include "../include/i82527.h"
+#include "../include/sja1000.h"
+
+/*
+ * IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ */
+#define IO_RANGE 0x100
+
+/**
+ * template_request_io: - reserve io memory
+ * @io_addr: The reserved memory starts at @io_addr, wich is the module
+ * parameter @io.
+ *
+ * The function template_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int template_request_io(unsigned long io_addr)
+{
+ if (check_region(io_addr,IO_RANGE)) {
+ CANMSG("Unable to open port: 0x%lx\n",io_addr);
+ return -ENODEV;
+ }
+ else {
+ request_region(io_addr,IO_RANGE,DEVICE_NAME);
+ DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", io_addr, io_addr + IO_RANGE - 1);
+ }
+ return 0;
+}
+
+/**
+ * template_release_io - free reserved io-memory
+ * @io_addr: Start of the memory range to be released.
+ *
+ * The function template_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int template_release_io(unsigned long io_addr)
+{
+ release_region(io_addr,IO_RANGE);
+
+ return 0;
+}
+
+/**
+ * template_reset - hardware reset routine
+ * @card: Number of the hardware card.
+ *
+ * The function template_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int template_reset(int card)
+{
+ return 0;
+}
+
+#define RESET_ADDR 0x0
+#define NR_82527 0
+#define NR_SJA1000 0
+
+/**
+ * template_init_hw_data - Initialze hardware cards
+ * @card: Number of the hardware card.
+ *
+ * The function template_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of philips sja1000 chips on the board.
+ * The flags entry can currently only be %PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int template_init_hw_data(int card)
+{
+ candevices_p[card]->res_addr=RESET_ADDR;
+ candevices_p[card]->nr_82527_chips=1;
+ candevices_p[card]->nr_sja1000_chips=0;
+ candevices_p[card]->flags |= PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+#define CHIP_TYPE "i82527"
+/**
+ * template_init_chip_data - Initialize chips
+ * @card: Number of the hardware card
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function template_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %CDR_CLKOUT_MASK, %CDR_CLK_OFF, %CDR_RXINPEN, %CDR_CBP, %CDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %OCR_MODE_BIPHASE, %OCR_MODE_TEST, %OCR_MODE_NORMAL, %OCR_MODE_CLOCK,
+ * %OCR_TX0_LH, %OCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * The entry @int_cpu_reg holds hardware specific options for the cpu interface
+ * register. Options defined in the %i82527.h file:
+ * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int template_init_chip_data(int card, int chipnr)
+{
+ candevices_p[card]->chip[chipnr]->chip_type=CHIP_TYPE;
+ candevices_p[card]->chip[chipnr]->chip_base_addr=candevices_p[card]->io_addr;
+ candevices_p[card]->chip[chipnr]->clock = 16000000;
+ candevices_p[card]->chip[chipnr]->int_cpu_reg = iCPU_DSC;
+ candevices_p[card]->chip[chipnr]->int_clk_reg = iCLK_SL1;
+ candevices_p[card]->chip[chipnr]->int_bus_reg = iBUS_CBY;
+ candevices_p[card]->chip[chipnr]->sja_cdr_reg = CDR_CBP | CDR_CLK_OFF;
+ candevices_p[card]->chip[chipnr]->sja_ocr_reg = OCR_MODE_NORMAL |
+ OCR_TX0_LH;
+
+ return 0;
+}
+
+/**
+ * template_init_obj_data - Initialize message buffers
+ * @chipnr: Number of the CAN chip
+ * @objnr: Number of the message buffer
+ *
+ * The function template_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/template.c
+ */
+int template_init_obj_data(int chipnr, int objnr)
+{
+ chips_p[chipnr]->msgobj[objnr]->obj_base_addr=chips_p[chipnr]->chip_base_addr+(objnr+1)*0x10;
+ chips_p[chipnr]->msgobj[objnr]->flags=0;
+
+ return 0;
+}
+
+/**
+ * template_program_irq - program interrupts
+ * @card: Number of the hardware card.
+ *
+ * The function template_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/template.c
+ */
+int template_program_irq(int card)
+{
+ return 0;
+}
+
+/**
+ * template_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function template_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/template.c
+ */
+void template_write_register(unsigned char data, unsigned long address)
+{
+ outb(data,address);
+}
+
+/**
+ * template_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function template_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/template.c
+ */
+unsigned template_read_register(unsigned long address)
+{
+ return inb(address);
+}
+
+/* !!! Don't change this function !!! */
+int template_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = template_request_io;
+ hwspecops->release_io = template_release_io;
+ hwspecops->reset = template_reset;
+ hwspecops->init_hw_data = template_init_hw_data;
+ hwspecops->init_chip_data = template_init_chip_data;
+ hwspecops->init_obj_data = template_init_obj_data;
+ hwspecops->write_register = template_write_register;
+ hwspecops->read_register = template_read_register;
+ hwspecops->program_irq = template_program_irq;
+ return 0;
+}
--- /dev/null
+/* write.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ * Version 0.7 6 Aug 2001
+ */
+
+#include <linux/autoconf.h>
+#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
+#define MODVERSIONS
+#endif
+
+#if defined (MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "../include/main.h"
+
+ssize_t can_write(struct file *file, const char *buffer, size_t length, loff_t *offset)
+{
+ struct msgobj_t *obj;
+ struct chip_t *chip;
+ struct canmsg_t *write_msg;
+ struct canfifo_t *fifo;
+ int ret = 0;
+ int space = 0;
+ int written = 0;
+ int bytes_to_copy = 0;
+ long can_timeout = 1;
+
+ if (length < sizeof(struct canmsg_t)) {
+ DEBUGMSG("Trying to write less bytes than a CAN message,\n");
+ DEBUGMSG("this will always return 0 !\n");
+ return 0;
+ }
+ if (length > 8 * sizeof(struct canmsg_t)) {
+ CANMSG("Trying to write more than is supported.\n");
+ return -1;
+ }
+ if (length % sizeof(struct canmsg_t)) {
+ CANMSG("The number of bytes requested to be written is not a multiple of\n");
+ CANMSG("'sizeof(struct canmsg_t)', currently this is not allowed.\n");
+ return -1;
+ }
+
+ /* Initialize hardware pointers */
+ if ( (obj = objects_p[MINOR_NR]) == NULL) {
+ CANMSG("Could not assign buffer structure\n");
+ return -1;
+ }
+ if ( (chip = obj->hostchip) == NULL) {
+ CANMSG("Device is not correctly configured,\n");
+ CANMSG("please reload the driver\n");
+ return -1;
+ }
+ if ( (fifo = obj->fifo) == NULL) {
+ CANMSG("Could not assign buffer memory\n");
+ return -1;
+ }
+
+ /* Initialize pointer to the first message to be sent */
+ write_msg = fifo->tx_writep;
+
+ /* Calculate free buffer space */
+ cli();
+ space = ((int)fifo->tx_writep < (int)fifo->tx_readp) ?
+ ((int)fifo->tx_readp - (int)fifo->tx_writep) :
+ ((int)fifo->tx_readp - (int)fifo->tx_writep +
+ (int)fifo->tx_size);
+ sti();
+
+ /* If the output buffer is full, return immediately in case O_NONBLOCK
+ * has been specified or loop until space becomes available.
+ */
+ while (space < sizeof(struct canmsg_t)) {
+ DEBUGMSG("Buffer is full\n");
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ can_timeout = interruptible_sleep_on_timeout(&fifo->writeq,
+ CANTIMEOUT);
+ if (signal_pending(current))
+ return -EINTR;
+ if (!can_timeout)
+ return -EIO;
+ cli();
+ space = ((int)fifo->tx_writep < (int)fifo->tx_readp) ?
+ ((int)fifo->tx_readp - (int)fifo->tx_writep) :
+ ((int)fifo->tx_readp - (int)fifo->tx_writep +
+ (int)fifo->tx_size);
+ sti();
+ }
+
+ /* There's space available in the kernel output buffer.
+ * Find out wich is smaller: 'length', the number of bytes requested to
+ * be written or 'space', the number of bytes available in the kernel
+ * buffer. We copy the least of the two to kernel space.
+ */
+// space = space - (space % sizeof(struct canmsg_t)); // round it
+ bytes_to_copy = space < length ? space : length;
+ copy_from_user(fifo->tx_writep, buffer, bytes_to_copy);
+ written = bytes_to_copy;
+ while (bytes_to_copy > 0) {
+ fifo->tx_writep++;
+ if (fifo->tx_writep >= fifo->buf_tx_entry + MAX_BUF_LENGTH)
+ fifo->tx_writep = fifo->buf_tx_entry;
+ bytes_to_copy -= sizeof(struct canmsg_t);
+ }
+
+ /* Copy the data to be transmitted into the output buffer */
+/* while ( (written < length) && (space >= sizeof(struct canmsg_t)) ) {
+ copy_from_user(fifo->tx_writep,buffer, sizeof(struct canmsg_t));
+ cli();
+ fifo->tx_writep++;
+ if (fifo->tx_writep >= fifo->buf_tx_entry + MAX_BUF_LENGTH)
+ fifo->tx_writep = fifo->buf_tx_entry;
+ buffer += sizeof(struct canmsg_t);
+ written += sizeof(struct canmsg_t);
+ space = ((int)fifo->tx_writep < (int)fifo->tx_readp) ?
+ ((int)fifo->tx_readp - (int)fifo->tx_writep) :
+ ((int)fifo->tx_readp - (int)fifo->tx_writep +
+ (int)fifo->tx_size);
+ sti();
+ } */
+
+ /* Initiate transmission in case we are not already transmitting */
+ cli();
+ if (!fifo->tx_in_progress) {
+ fifo->tx_in_progress = 1;
+ sti();
+ if ( (ret = chip->chipspecops->pre_write_config(chip, obj,
+ write_msg)) < 0) {
+ CANMSG("Error initializing hardware for sending\n");
+ return -EIO;
+ }
+ obj->ret = 0;
+ if ( (ret = chip->chipspecops->send_msg(chip, obj,
+ write_msg)) < 0) {
+ CANMSG("Error sending message\n");
+ return -EIO;
+ }
+ /* If O_SYNC is specified wait for successfull transmission */
+/* while (1) {
+ cli();
+ if ( (!file->f_flags & O_SYNC) ||
+ (!fifo->tx_in_progress)) {
+ sti();
+ if (obj->ret < 0)
+ return obj->ret;
+ else
+ return written;
+ }
+ cli();
+ if (fifo->tx_in_progress) {
+ can_timeout = interruptible_sleep_on_timeout(
+ &fifo->writeq, CANTIMEOUT);
+ }
+ sti();
+ if (signal_pending(current))
+ return -EINTR;
+ if (!can_timeout)
+ return -EIO;
+ } */
+ }
+ sti();
+
+ return written;
+}
--- /dev/null
+README for the rxtx sample program to use with the Linux CAN-bus driver.
+Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+This software is released under the GPL-License.
+Version 0.6 18 Sept 2000
+
+This program is automatically compiled when compiling the driver. It's a very
+simple program wich asks some questions about what you want to do. The program
+allows you to send or receive standard or 'remote transmission' messages.
+
+Just give it a try.
--- /dev/null
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "../include/can.h"
+
+int fd;
+
+/*--- handler on SIGINT signal : the program quit with CTL-C ---*/
+void sortie(int sig)
+ {
+ close(fd);
+ printf("Terminated by user\n");
+ exit(0);
+ }
+
+int main(void)
+ {
+ int n,ret;
+ unsigned long i=0;
+ struct canmsg_t readmsg={0,0,5,0,0,{0,}};
+ struct sigaction act;
+
+ /*------- register handler on SIGINT signal -------*/
+ act.sa_handler=sortie;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags=0;
+ sigaction(SIGINT,&act,0);
+ /*---------------------------------------*/
+
+ if ((fd=open("/dev/can0",O_RDWR)) < 0)
+ {
+ perror("open");
+ printf("Error opening /dev/can0\n");
+ exit(1);
+ }
+
+ while (1)
+ {
+ readmsg.flags=0;
+ readmsg.cob=0;
+ readmsg.timestamp=0;
+ ret=read(fd,&readmsg,sizeof(struct canmsg_t));
+ if(ret <0)
+ {
+ printf("Error reading message\n");
+ }
+ else
+ {
+ printf("Received message #%lu: id=%lX dlc=%u",i,readmsg.id,readmsg.length);
+ for(n=0 ; n<readmsg.length ; n++)
+ printf(" %.2X",(unsigned char)readmsg.data[n]);
+ printf("\n");
+ i++;
+ }
+ }
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "../include/can.h"
+#define MAXL 40
+
+int main(void)
+{
+ int i=0, fd=0, ret=0, count=0;
+ char loop=0;
+ char ch, transmission[MAXL+1], specialfile[MAXL+1]="/dev/can0", emptystring[MAXL+1]="", buf[MAXL+1];
+ char remote[MAXL+1];
+ struct canmsg_t message;
+
+ printf("\nThis program allows you to send or receive Can messages.\n");
+ printf("Please answer the following questions:\n\n");
+
+ while ( (*transmission!='s') && (*transmission!='r') ) {
+ printf("Would you like to send or receive a message?\n");
+ printf("send: <s> | receive: <r> ");
+ strcpy(transmission,emptystring);
+ count=0;
+ while ( (ch=getchar()) != '\n' )
+ transmission[count++]=ch;
+ transmission[count]='\0';
+ }
+
+ while ( *remote!='y' && *remote!='n' ) {
+ printf("Should the message be configured for Remote Transmission Requests?\n");
+ printf("yes: <y> | no: <n> ");
+ strcpy(remote,emptystring);
+ count=0;
+ while ( (ch=getchar()) != '\n' )
+ remote[count++]=ch;
+ remote[count]='\0';
+ }
+ if (remote[0]=='y')
+ message.flags |= MSG_RTR;
+ else
+ message.flags = 0;
+// message.flags |= MSG_EXT; hard code EXT for now
+
+ if (transmission[0]=='s') {
+ printf("From wich device file would you like to send the message?\n");
+ printf(specialfile);
+ *buf='\0';
+ fgets(buf,MAXL,stdin);
+ buf[strcspn(buf,"\n")]='\0';
+ if(*buf)
+ strncpy(specialfile,buf,MAXL);
+ specialfile[MAXL]='\0';
+ specialfile[MAXL]='\0';
+ printf("Enter the Message ID ");
+ scanf("%lx",&message.id);
+ printf("Enter the Message Length ");
+ scanf("%d",&message.length);
+ for (i=0; i<message.length; i++) {
+ printf("Enter data byte [%d] ",i);
+ scanf("%x",(int *)&message.data[i]);
+ }
+ }
+ if (*transmission=='r') {
+ printf("At which device file would you like to receive the message?\n");
+ printf(specialfile);
+ *buf='\0';
+ fgets(buf,MAXL,stdin);
+ buf[strcspn(buf,"\n")]='\0';
+ if(*buf)
+ strncpy(specialfile,buf,MAXL);
+ specialfile[MAXL]='\0';
+ printf("Enter the Message ID ");
+ scanf("%ld",&message.id);
+ getchar();
+ }
+
+ fd=open(specialfile,O_RDWR);
+ if (fd<0) {
+ printf("Error opening %s\n",specialfile);
+ return -1;
+ }
+
+ if (transmission[0]=='s') {
+ printf("Press enter to send message\n");
+ getchar();
+ while (getchar() != '\n');
+ ret=write(fd, &message, sizeof(struct canmsg_t));
+ if (ret<0)
+ printf("Error sending message from %s\n",specialfile);
+ else
+ printf("Message successfully sent from %s\n",specialfile);
+ }
+
+ if (*transmission=='r') {
+ ioctl(fd,CONF_FILTER,message.id);
+ printf("Press enter to receive message or <l>oop\n");
+ loop = 'l';
+ while ( loop == 'l') {
+ loop = getchar();
+ ret=read(fd, &message, sizeof(struct canmsg_t));
+ if (ret<0)
+ printf("Error receiving message on %s\n",
+ specialfile);
+ else {
+ printf("Id : %lx\n",message.id);
+ printf("length : %d\n",message.length);
+ printf("flags : 0x%02x\n", message.flags);
+ printf("time : %ld\n", message.timestamp);
+ for (i=0; i<message.length; i++)
+ printf("data%d : %02x\n",i,
+ message.data[i]);
+ }
+ }
+ }
+
+ if (close(fd)) {
+ printf("Error closing %s\n",specialfile);
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#include "../include/can.h"
+
+int main(void)
+ {
+ struct canmsg_t sendmsg={0,0,5,0,8,{1,2,3,4,5,6,7,8}};
+ int fd, ret,i,j;
+
+ if ((fd=open("/dev/can0",O_RDWR)) < 0 )
+ {
+ perror("open");
+ printf("Error opening /dev/can0\n");
+ exit(1);
+ }
+ j=0;
+ while (1)
+ {
+ for(i=0;i<10;i++)
+ {
+ sendmsg.data[0]=i;
+ sendmsg.data[1]=j;
+ if ((ret=write(fd,&sendmsg,sizeof(struct canmsg_t))) < 0)
+ {
+ perror("write");
+ printf("Error sending message\n");
+ break;
+ }
+ }
+ printf("Sent block of 10 messages #: %u\n",j);
+ j++;
+ usleep(500000);
+ }
+ close(fd);
+ return 0;
+ }
+
+