]> rtime.felk.cvut.cz Git - lincan.git/commitdiff
The original version of Arnaud Westenberg Linux CAN-bus driver
authorppisa <ppisa>
Tue, 29 Apr 2003 21:56:59 +0000 (21:56 +0000)
committerppisa <ppisa>
Tue, 29 Apr 2003 21:56:59 +0000 (21:56 +0000)
can-0.7.1 (http://home.wanadoo.nl/arnaud/)

62 files changed:
lincan/CREDITS [new file with mode: 0644]
lincan/Makefile [new file with mode: 0644]
lincan/README
lincan/TODO [new file with mode: 0644]
lincan/Unsupported-hardware [new file with mode: 0644]
lincan/include/aim104.h [new file with mode: 0644]
lincan/include/can.h [new file with mode: 0644]
lincan/include/cc_can104.h [new file with mode: 0644]
lincan/include/close.h [new file with mode: 0644]
lincan/include/constants.h [new file with mode: 0644]
lincan/include/i82527.h [new file with mode: 0644]
lincan/include/ioctl.h [new file with mode: 0644]
lincan/include/irq.h [new file with mode: 0644]
lincan/include/m437.h [new file with mode: 0644]
lincan/include/main.h [new file with mode: 0644]
lincan/include/modparms.h [new file with mode: 0644]
lincan/include/nsi.h [new file with mode: 0644]
lincan/include/open.h [new file with mode: 0644]
lincan/include/pc-i03.h [new file with mode: 0644]
lincan/include/pccan.h [new file with mode: 0644]
lincan/include/pcccan.h [new file with mode: 0644]
lincan/include/pcm3680.h [new file with mode: 0644]
lincan/include/pip.h [new file with mode: 0644]
lincan/include/proc.h [new file with mode: 0644]
lincan/include/read.h [new file with mode: 0644]
lincan/include/setup.h [new file with mode: 0644]
lincan/include/sja1000.h [new file with mode: 0644]
lincan/include/sja1000p.h [new file with mode: 0644]
lincan/include/smartcan.h [new file with mode: 0644]
lincan/include/ssv.h [new file with mode: 0644]
lincan/include/template.h [new file with mode: 0644]
lincan/include/write.h [new file with mode: 0644]
lincan/src/aim104.c [new file with mode: 0644]
lincan/src/cc_can104.c [new file with mode: 0644]
lincan/src/close.c [new file with mode: 0644]
lincan/src/i82527.c [new file with mode: 0644]
lincan/src/ioctl.c [new file with mode: 0644]
lincan/src/irq.c [new file with mode: 0644]
lincan/src/m437.c [new file with mode: 0644]
lincan/src/main.c [new file with mode: 0644]
lincan/src/modparms.c [new file with mode: 0644]
lincan/src/nsi.c [new file with mode: 0644]
lincan/src/open.c [new file with mode: 0644]
lincan/src/pc-i03.c [new file with mode: 0644]
lincan/src/pccan.c [new file with mode: 0644]
lincan/src/pcccan.c [new file with mode: 0644]
lincan/src/pcm3680.c [new file with mode: 0644]
lincan/src/pip.c [new file with mode: 0644]
lincan/src/proc.c [new file with mode: 0644]
lincan/src/read.c [new file with mode: 0644]
lincan/src/setup.c [new file with mode: 0644]
lincan/src/sja1000.c [new file with mode: 0644]
lincan/src/sja1000p.c [new file with mode: 0644]
lincan/src/smartcan.c [new file with mode: 0644]
lincan/src/ssv.c [new file with mode: 0644]
lincan/src/temp.c [new file with mode: 0644]
lincan/src/template.c [new file with mode: 0644]
lincan/src/write.c [new file with mode: 0644]
lincan/utils/README [new file with mode: 0644]
lincan/utils/readburst.c [new file with mode: 0644]
lincan/utils/rxtx.c [new file with mode: 0644]
lincan/utils/sendburst.c [new file with mode: 0644]

diff --git a/lincan/CREDITS b/lincan/CREDITS
new file mode 100644 (file)
index 0000000..7d84faa
--- /dev/null
@@ -0,0 +1,6 @@
+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!
diff --git a/lincan/Makefile b/lincan/Makefile
new file mode 100644 (file)
index 0000000..75a1182
--- /dev/null
@@ -0,0 +1,220 @@
+# 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)
index 34573b919976fea2573a8501e9acffedea5fa39d..0a8ffb80f8c130fa475883f6e3b4a20d0fcca002 100644 (file)
@@ -1,2 +1,44 @@
-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.
diff --git a/lincan/TODO b/lincan/TODO
new file mode 100644 (file)
index 0000000..f0d17cd
--- /dev/null
@@ -0,0 +1,10 @@
+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
diff --git a/lincan/Unsupported-hardware b/lincan/Unsupported-hardware
new file mode 100644 (file)
index 0000000..a127595
--- /dev/null
@@ -0,0 +1,23 @@
+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
+
+
diff --git a/lincan/include/aim104.h b/lincan/include/aim104.h
new file mode 100644 (file)
index 0000000..5e9c0b8
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/can.h b/lincan/include/can.h
new file mode 100644 (file)
index 0000000..071deae
--- /dev/null
@@ -0,0 +1,45 @@
+#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)
diff --git a/lincan/include/cc_can104.h b/lincan/include/cc_can104.h
new file mode 100644 (file)
index 0000000..64da797
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/close.h b/lincan/include/close.h
new file mode 100644 (file)
index 0000000..f164b31
--- /dev/null
@@ -0,0 +1,9 @@
+/* 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);
+
diff --git a/lincan/include/constants.h b/lincan/include/constants.h
new file mode 100644 (file)
index 0000000..f662a98
--- /dev/null
@@ -0,0 +1,58 @@
+/* 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
diff --git a/lincan/include/i82527.h b/lincan/include/i82527.h
new file mode 100644 (file)
index 0000000..14df44e
--- /dev/null
@@ -0,0 +1,167 @@
+/* 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
+};
diff --git a/lincan/include/ioctl.h b/lincan/include/ioctl.h
new file mode 100644 (file)
index 0000000..794b779
--- /dev/null
@@ -0,0 +1,8 @@
+/* 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);
diff --git a/lincan/include/irq.h b/lincan/include/irq.h
new file mode 100644 (file)
index 0000000..6391bf9
--- /dev/null
@@ -0,0 +1,9 @@
+/* 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);
diff --git a/lincan/include/m437.h b/lincan/include/m437.h
new file mode 100644 (file)
index 0000000..869dc1e
--- /dev/null
@@ -0,0 +1,19 @@
+/* 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);
diff --git a/lincan/include/main.h b/lincan/include/main.h
new file mode 100644 (file)
index 0000000..53fb783
--- /dev/null
@@ -0,0 +1,248 @@
+/* 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);
+}
diff --git a/lincan/include/modparms.h b/lincan/include/modparms.h
new file mode 100644 (file)
index 0000000..18879fc
--- /dev/null
@@ -0,0 +1,8 @@
+/* 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);
diff --git a/lincan/include/nsi.h b/lincan/include/nsi.h
new file mode 100644 (file)
index 0000000..fd69113
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/open.h b/lincan/include/open.h
new file mode 100644 (file)
index 0000000..997bd9f
--- /dev/null
@@ -0,0 +1,8 @@
+/* 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);
diff --git a/lincan/include/pc-i03.h b/lincan/include/pc-i03.h
new file mode 100644 (file)
index 0000000..beb8aed
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/pccan.h b/lincan/include/pccan.h
new file mode 100644 (file)
index 0000000..f5b8901
--- /dev/null
@@ -0,0 +1,24 @@
+/* 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);
+
+
diff --git a/lincan/include/pcccan.h b/lincan/include/pcccan.h
new file mode 100644 (file)
index 0000000..77cdd80
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/pcm3680.h b/lincan/include/pcm3680.h
new file mode 100644 (file)
index 0000000..c0d5957
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/pip.h b/lincan/include/pip.h
new file mode 100644 (file)
index 0000000..fefe48d
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/proc.h b/lincan/include/proc.h
new file mode 100644 (file)
index 0000000..8a3d004
--- /dev/null
@@ -0,0 +1,32 @@
+/* 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;
+}; 
diff --git a/lincan/include/read.h b/lincan/include/read.h
new file mode 100644 (file)
index 0000000..9716105
--- /dev/null
@@ -0,0 +1,8 @@
+/* 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);
diff --git a/lincan/include/setup.h b/lincan/include/setup.h
new file mode 100644 (file)
index 0000000..3e9a35a
--- /dev/null
@@ -0,0 +1,12 @@
+/* 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);
diff --git a/lincan/include/sja1000.h b/lincan/include/sja1000.h
new file mode 100644 (file)
index 0000000..9c6a6c6
--- /dev/null
@@ -0,0 +1,129 @@
+/* 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
+};
diff --git a/lincan/include/sja1000p.h b/lincan/include/sja1000p.h
new file mode 100644 (file)
index 0000000..9b1f5d8
--- /dev/null
@@ -0,0 +1,190 @@
+/* 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)
diff --git a/lincan/include/smartcan.h b/lincan/include/smartcan.h
new file mode 100644 (file)
index 0000000..939df87
--- /dev/null
@@ -0,0 +1,16 @@
+/* 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);
+
diff --git a/lincan/include/ssv.h b/lincan/include/ssv.h
new file mode 100644 (file)
index 0000000..d6a1151
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/template.h b/lincan/include/template.h
new file mode 100644 (file)
index 0000000..410a46b
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
+
diff --git a/lincan/include/write.h b/lincan/include/write.h
new file mode 100644 (file)
index 0000000..d421036
--- /dev/null
@@ -0,0 +1,8 @@
+/* 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);
diff --git a/lincan/src/aim104.c b/lincan/src/aim104.c
new file mode 100644 (file)
index 0000000..d679b52
--- /dev/null
@@ -0,0 +1,271 @@
+/* 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;
+}
diff --git a/lincan/src/cc_can104.c b/lincan/src/cc_can104.c
new file mode 100644 (file)
index 0000000..8530a76
--- /dev/null
@@ -0,0 +1,252 @@
+/* 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;
+}
diff --git a/lincan/src/close.c b/lincan/src/close.c
new file mode 100644 (file)
index 0000000..7ac3574
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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;
+}
diff --git a/lincan/src/i82527.c b/lincan/src/i82527.c
new file mode 100644 (file)
index 0000000..5ab2dcf
--- /dev/null
@@ -0,0 +1,417 @@
+/* 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;
+}
diff --git a/lincan/src/ioctl.c b/lincan/src/ioctl.c
new file mode 100644 (file)
index 0000000..0f54c86
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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;
+}
diff --git a/lincan/src/irq.c b/lincan/src/irq.c
new file mode 100644 (file)
index 0000000..92ce53e
--- /dev/null
@@ -0,0 +1,298 @@
+/* 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;
+               }
+       }
+}
diff --git a/lincan/src/m437.c b/lincan/src/m437.c
new file mode 100644 (file)
index 0000000..3be6f65
--- /dev/null
@@ -0,0 +1,300 @@
+/* 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;
+}
diff --git a/lincan/src/main.c b/lincan/src/main.c
new file mode 100644 (file)
index 0000000..4ff1523
--- /dev/null
@@ -0,0 +1,235 @@
+/* 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);
+}
diff --git a/lincan/src/modparms.c b/lincan/src/modparms.c
new file mode 100644 (file)
index 0000000..6f9a0de
--- /dev/null
@@ -0,0 +1,185 @@
+/* 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;
+}
diff --git a/lincan/src/nsi.c b/lincan/src/nsi.c
new file mode 100644 (file)
index 0000000..3fefebc
--- /dev/null
@@ -0,0 +1,229 @@
+/* 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;
+}
diff --git a/lincan/src/open.c b/lincan/src/open.c
new file mode 100644 (file)
index 0000000..862a297
--- /dev/null
@@ -0,0 +1,100 @@
+/* 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;
+}
diff --git a/lincan/src/pc-i03.c b/lincan/src/pc-i03.c
new file mode 100644 (file)
index 0000000..bf64ff6
--- /dev/null
@@ -0,0 +1,300 @@
+/* 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;
+}
diff --git a/lincan/src/pccan.c b/lincan/src/pccan.c
new file mode 100644 (file)
index 0000000..bf5a8a8
--- /dev/null
@@ -0,0 +1,441 @@
+/* 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;
+}
diff --git a/lincan/src/pcccan.c b/lincan/src/pcccan.c
new file mode 100644 (file)
index 0000000..e7b5b22
--- /dev/null
@@ -0,0 +1,310 @@
+/* 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;
+}
diff --git a/lincan/src/pcm3680.c b/lincan/src/pcm3680.c
new file mode 100644 (file)
index 0000000..cfb816a
--- /dev/null
@@ -0,0 +1,302 @@
+/* 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;
+}
diff --git a/lincan/src/pip.c b/lincan/src/pip.c
new file mode 100644 (file)
index 0000000..09f0ae4
--- /dev/null
@@ -0,0 +1,249 @@
+/* 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;
+}
diff --git a/lincan/src/proc.c b/lincan/src/proc.c
new file mode 100644 (file)
index 0000000..401148e
--- /dev/null
@@ -0,0 +1,306 @@
+/* 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
diff --git a/lincan/src/read.c b/lincan/src/read.c
new file mode 100644 (file)
index 0000000..4e02801
--- /dev/null
@@ -0,0 +1,185 @@
+/* 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;
+}
+
+
diff --git a/lincan/src/setup.c b/lincan/src/setup.c
new file mode 100644 (file)
index 0000000..74ba10f
--- /dev/null
@@ -0,0 +1,360 @@
+/* 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;
+}
diff --git a/lincan/src/sja1000.c b/lincan/src/sja1000.c
new file mode 100644 (file)
index 0000000..e1da4c1
--- /dev/null
@@ -0,0 +1,373 @@
+/* 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;
+}
diff --git a/lincan/src/sja1000p.c b/lincan/src/sja1000p.c
new file mode 100644 (file)
index 0000000..f95f1a7
--- /dev/null
@@ -0,0 +1,450 @@
+/* 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;
+}
diff --git a/lincan/src/smartcan.c b/lincan/src/smartcan.c
new file mode 100644 (file)
index 0000000..c41c2d6
--- /dev/null
@@ -0,0 +1,154 @@
+/* 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;
+}
diff --git a/lincan/src/ssv.c b/lincan/src/ssv.c
new file mode 100644 (file)
index 0000000..bc54d63
--- /dev/null
@@ -0,0 +1,261 @@
+/* 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;
+}
diff --git a/lincan/src/temp.c b/lincan/src/temp.c
new file mode 100644 (file)
index 0000000..dceec0c
--- /dev/null
@@ -0,0 +1,28 @@
+{
+       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;
+} 
diff --git a/lincan/src/template.c b/lincan/src/template.c
new file mode 100644 (file)
index 0000000..1c638ae
--- /dev/null
@@ -0,0 +1,262 @@
+/* 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;
+}
diff --git a/lincan/src/write.c b/lincan/src/write.c
new file mode 100644 (file)
index 0000000..98ad046
--- /dev/null
@@ -0,0 +1,175 @@
+/* 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;
+}      
diff --git a/lincan/utils/README b/lincan/utils/README
new file mode 100644 (file)
index 0000000..22a8afe
--- /dev/null
@@ -0,0 +1,10 @@
+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.
diff --git a/lincan/utils/readburst.c b/lincan/utils/readburst.c
new file mode 100644 (file)
index 0000000..139e558
--- /dev/null
@@ -0,0 +1,61 @@
+#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;
+}
diff --git a/lincan/utils/rxtx.c b/lincan/utils/rxtx.c
new file mode 100644 (file)
index 0000000..0adcef1
--- /dev/null
@@ -0,0 +1,124 @@
+#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;
+}      
diff --git a/lincan/utils/sendburst.c b/lincan/utils/sendburst.c
new file mode 100644 (file)
index 0000000..084a4b2
--- /dev/null
@@ -0,0 +1,43 @@
+#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;
+       }
+
+