From: ppisa Date: Tue, 29 Apr 2003 21:56:59 +0000 (+0000) Subject: The original version of Arnaud Westenberg Linux CAN-bus driver X-Git-Tag: CLT_COMM_CAN_pre_canmsg_change~32 X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/commitdiff_plain/4cf24de229090b1ab6279570a564d224e13dd706?hp=4ddb8468e0688f83f24fc041cfb62512ee45194a The original version of Arnaud Westenberg Linux CAN-bus driver can-0.7.1 (http://home.wanadoo.nl/arnaud/) --- diff --git a/lincan/CREDITS b/lincan/CREDITS new file mode 100644 index 0000000..7d84faa --- /dev/null +++ b/lincan/CREDITS @@ -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 index 0000000..75a1182 --- /dev/null +++ b/lincan/Makefile @@ -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 $(> .support +pccan.o : pccan.c pccan.h + $(CC) -c -o $(> .support +smartcan.o : smartcan.c smartcan.h + $(CC) -c -o $(> .support +nsi.o : nsi.c nsi.h + $(CC) -c -o $(> .support +cc_can104.o : cc_can104.c cc_can104.h + $(CC) -c -o $(> .support +aim104.o : aim104.c aim104.h + $(CC) -c -o $(> .support +pc-i03.o : pc-i03.c pc-i03.h + $(CC) -c -o $(> .support +pcm3680.o : pcm3680.c pcm3680.h + $(CC) -c -o $(> .support +m437.o : m437.c m437.h + $(CC) -c -o $(> .support +pcccan.o : pcccan.c pcccan.h + $(CC) -c -o $(> .support +ssv.o : ssv.c ssv.h + $(CC) -c -o $(> .support +template.o : template.c template.h + $(CC) -c -o $(> .support +open.o : open.c open.h + $(CC) -c -o $( +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=, major specifies the major number of the driver. +- minor=, 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=, sets the baudrate of the device(s) +- stdmask=, sets the standard mask of the device +- extmask=, sets the extended mask of the device +- mo15mask=, 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 index 0000000..f0d17cd --- /dev/null +++ b/lincan/TODO @@ -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 index 0000000..a127595 --- /dev/null +++ b/lincan/Unsupported-hardware @@ -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 index 0000000..5e9c0b8 --- /dev/null +++ b/lincan/include/aim104.h @@ -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 index 0000000..071deae --- /dev/null +++ b/lincan/include/can.h @@ -0,0 +1,45 @@ +#include +#include + +#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 index 0000000..64da797 --- /dev/null +++ b/lincan/include/cc_can104.h @@ -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 index 0000000..f164b31 --- /dev/null +++ b/lincan/include/close.h @@ -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 index 0000000..f662a98 --- /dev/null +++ b/lincan/include/constants.h @@ -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 index 0000000..14df44e --- /dev/null +++ b/lincan/include/i82527.h @@ -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 index 0000000..794b779 --- /dev/null +++ b/lincan/include/ioctl.h @@ -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 index 0000000..6391bf9 --- /dev/null +++ b/lincan/include/irq.h @@ -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 index 0000000..869dc1e --- /dev/null +++ b/lincan/include/m437.h @@ -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 index 0000000..53fb783 --- /dev/null +++ b/lincan/include/main.h @@ -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 +#include +#include +#include +#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 index 0000000..18879fc --- /dev/null +++ b/lincan/include/modparms.h @@ -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 index 0000000..fd69113 --- /dev/null +++ b/lincan/include/nsi.h @@ -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 index 0000000..997bd9f --- /dev/null +++ b/lincan/include/open.h @@ -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 index 0000000..beb8aed --- /dev/null +++ b/lincan/include/pc-i03.h @@ -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 index 0000000..f5b8901 --- /dev/null +++ b/lincan/include/pccan.h @@ -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 index 0000000..77cdd80 --- /dev/null +++ b/lincan/include/pcccan.h @@ -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 index 0000000..c0d5957 --- /dev/null +++ b/lincan/include/pcm3680.h @@ -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 index 0000000..fefe48d --- /dev/null +++ b/lincan/include/pip.h @@ -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 index 0000000..8a3d004 --- /dev/null +++ b/lincan/include/proc.h @@ -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 +#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 index 0000000..9716105 --- /dev/null +++ b/lincan/include/read.h @@ -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 index 0000000..3e9a35a --- /dev/null +++ b/lincan/include/setup.h @@ -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 index 0000000..9c6a6c6 --- /dev/null +++ b/lincan/include/sja1000.h @@ -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 index 0000000..9b1f5d8 --- /dev/null +++ b/lincan/include/sja1000p.h @@ -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 index 0000000..939df87 --- /dev/null +++ b/lincan/include/smartcan.h @@ -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 index 0000000..d6a1151 --- /dev/null +++ b/lincan/include/ssv.h @@ -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 index 0000000..410a46b --- /dev/null +++ b/lincan/include/template.h @@ -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 index 0000000..d421036 --- /dev/null +++ b/lincan/include/write.h @@ -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 index 0000000..d679b52 --- /dev/null +++ b/lincan/src/aim104.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..8530a76 --- /dev/null +++ b/lincan/src/cc_can104.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..7ac3574 --- /dev/null +++ b/lincan/src/close.c @@ -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 + +#include +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include + +#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 index 0000000..5ab2dcf --- /dev/null +++ b/lincan/src/i82527.c @@ -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 + +#include +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include + +#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; ilength; 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 index 0000000..0f54c86 --- /dev/null +++ b/lincan/src/ioctl.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include + +#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 index 0000000..92ce53e --- /dev/null +++ b/lincan/src/irq.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) +#include +#else +#include +#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; irtr_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; ibuf_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 index 0000000..3be6f65 --- /dev/null +++ b/lincan/src/m437.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..4ff1523 --- /dev/null +++ b/lincan/src/main.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#include + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19)) +#include +#else +#include +#endif + +#if !defined (__GENKSYMS__) +#if (defined (MODVERSIONS) && !defined(NOVER)) +#include +#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; inr_boards; i++) { + if (candevices_p[i]->hwspecops->request_io(candevices_p[i]->io_addr)) + goto memory_error; + } + + for (i=0; inr_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; inr_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; inr_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; inr_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 index 0000000..6f9a0de --- /dev/null +++ b/lincan/src/modparms.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#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 +#include + +#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_suppliednr_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; jcandevice[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 index 0000000..3fefebc --- /dev/null +++ b/lincan/src/nsi.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include +#include + +#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 index 0000000..862a297 --- /dev/null +++ b/lincan/src/open.c @@ -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 + +#include +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include + +#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 index 0000000..bf64ff6 --- /dev/null +++ b/lincan/src/pc-i03.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..bf5a8a8 --- /dev/null +++ b/lincan/src/pccan.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include +#include + +#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 index 0000000..e7b5b22 --- /dev/null +++ b/lincan/src/pcccan.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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 index 0000000..cfb816a --- /dev/null +++ b/lincan/src/pcm3680.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..09f0ae4 --- /dev/null +++ b/lincan/src/pip.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..401148e --- /dev/null +++ b/lincan/src/proc.c @@ -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 + +#include +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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; bcnr_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; ichannel[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; ichannel[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; imsgobj[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 index 0000000..4e02801 --- /dev/null +++ b/lincan/src/read.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..74ba10f --- /dev/null +++ b/lincan/src/setup.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include + +#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; imsgobj[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 index 0000000..e1da4c1 --- /dev/null +++ b/lincan/src/sja1000.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include + +#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; ibuf_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++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; ilength; 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 index 0000000..f95f1a7 --- /dev/null +++ b/lincan/src/sja1000p.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include + +#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++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 index 0000000..c41c2d6 --- /dev/null +++ b/lincan/src/smartcan.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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 index 0000000..bc54d63 --- /dev/null +++ b/lincan/src/ssv.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#ifdef MODVERSIONS +#include +#endif + +#include +#include +#include +#include +#include + +#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 index 0000000..dceec0c --- /dev/null +++ b/lincan/src/temp.c @@ -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 index 0000000..1c638ae --- /dev/null +++ b/lincan/src/template.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#include +#include +#include +#include + +#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 index 0000000..98ad046 --- /dev/null +++ b/lincan/src/write.c @@ -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 +#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) +#define MODVERSIONS +#endif + +#if defined (MODVERSIONS) +#include +#endif + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include + +#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 index 0000000..22a8afe --- /dev/null +++ b/lincan/utils/README @@ -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 index 0000000..139e558 --- /dev/null +++ b/lincan/utils/readburst.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#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: | receive: "); + 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: | no: "); + 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; ioop\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 +#include +#include +#include +#include + + +#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; + } + +