📄 can200.c
字号:
/* * can200.c * version 0.2 * * An interrupt driven can 82c200 handler for EPP mode parport. * Tested under Linux 2.2.14 on i386 PC architecture. * * * Copyright (c) 2000 Martin Homuth-Rosemann <homuth-rosemann@gmx.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/kernel.h>#include <linux/module.h>#if CONFIG_MODVERSIONS==1#define MODVERSIONS#include <linux/modversions.h>#endif#include <linux/sched.h>#include <linux/tqueue.h>#include <linux/interrupt.h>#include <asm/atomic.h>#include <linux/fs.h>#include <linux/wrapper.h>#include <linux/ioport.h>#include <linux/delay.h>#include <asm/io.h>/* In 2.2.3 /usr/include/linux/version.h includes a * macro for this, but 2.0.35 doesn't - so I add it * here if necessary. */#ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))#endif/* Conditional compilation. LINUX_VERSION_CODE is * the code (as per KERNEL_VERSION) of this version. */#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)#include <asm/uaccess.h> /* for put_user */#endif#include "can200.h"#include "82c200.h"/* Init parameter, usage: * "insmod can200.o can200_io=0x278 can200_irq=5 can200_major = 60" */int can200_io = LP_PORT;int can200_irq = LP_IRQ;int can200_major = CAN_MAJOR;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)MODULE_PARM( can200_io, "i" );MODULE_PARM( can200_irq, "i" );MODULE_PARM( can200_major, "i" );#endif/* Queues of waiting processes */static struct wait_queue *RdWaitQ = NULL;static struct wait_queue *WrWaitQ = NULL;/* The minor device number for the device. This is * global (well, static, which in this context is global * within this file) because it has to be accessible * both for registration and for release. */static int Minor;/* The length of a message from the can device */#define RD_FIFO_LEN (200*CAN_MSG_LEN)#define WR_FIFO_LEN (50*CAN_MSG_LEN)/* the read and write fifos */typedef struct { volatile short head; volatile short tail; volatile short status; volatile short size; volatile unsigned char buffer[ RD_FIFO_LEN ];} can_rd_fifo_t;typedef struct { volatile short head; volatile short tail; volatile short status; volatile short size; volatile unsigned char buffer[ WR_FIFO_LEN ];} can_wr_fifo_t;#define FIFO_EMPTY -1#define FIFO_OK 0#define FIFO_FULL 1#define FIFO_OVERFLOW 2/* these fifos hold our data */static can_rd_fifo_t RdFifo;static can_wr_fifo_t WrFifo;static void can_init_fifos( void ){ RdFifo.head = RdFifo.tail = RdFifo.size = 0; RdFifo.status = FIFO_EMPTY; WrFifo.head = WrFifo.tail = WrFifo.size = 0; WrFifo.status = FIFO_EMPTY;}/*******************************************************************//*******************************************************************//*******************************************************************//* The lowest level access routines for the CAN controller chip. * This level handles the physical connection to out PC architecture. * (ISA card, parallel port in PS/2 or EPP mode, etc. (PCI?) * Default mode is the Enhanced Parallel Port*/#define LP_STAT (can200_io+1)#define LP_CTRL (can200_io+2)#ifdef USE_EPP_MODE/**************************//* Enhanced Parallel Port *//**************************/#define EPP_ADDR (can200_io+3)#define EPP_DATA (can200_io+4)static void can_put_reg( unsigned char reg, unsigned char value ){ outb( reg, EPP_ADDR ); outb( value, EPP_DATA );}static unsigned char can_get_reg( unsigned char reg ){ outb( reg, EPP_ADDR ); return inb( EPP_DATA );}static void can_reset( void ){ outb( LP_RESET_ON, LP_CTRL ); udelay( 5 );}static void can_ena_irq( int irq_enable ){ if ( irq_enable ) outb( LP_RESET_OFF | LP_IRQ_ENA, LP_CTRL ); else outb( LP_RESET_OFF, LP_CTRL );}#endif#ifdef USE_PS2_MODE/*****************************************//* PS/2 mode bidirectional parallel port *//*****************************************/static unsigned char Port2;#define LP_DATA (can200_io)static void can_put_reg( unsigned char reg, unsigned char value ){ outb( Port2+1, LP_CTRL ); // R/W = 0 (write) outb( reg, LP_DATA ); outb( Port2+8+1, LP_CTRL ); // /ADDRSTB = 0 (act). outb( Port2+1, LP_CTRL ); // /ADDRSTB = 1 (inact.) outb( value, LP_DATA ); outb( Port2+2+1, LP_CTRL ); // /DATASTB = 0 (act). outb( Port2+1, LP_CTRL ); // /DATASTB = 1 (inact.) outb( Port2, LP_CTRL ); // R/W, /DATSTB, /ADDRSTB = 1}static unsigned char can_get_reg( unsigned char reg ){ unsigned char value; outb( Port2+1, LP_CTRL ); // R/W = 0 (write) outb( reg, LP_DATA ); outb( Port2+8+1, LP_CTRL ); // /ADDRSTB = 0 (act). outb( Port2+1, LP_CTRL ); // /ADDRSTB = 1 (inact.) outb( Port2+32, LP_CTRL ); // R/W = 1 (read) outb( Port2+32+2, LP_CTRL ); // /DATASTB = 0 (act). value = inb( LP_DATA ); outb( Port2+32, LP_CTRL ); // /DATASTB = 1 (inact). outb( Port2, LP_CTRL ); // R/W, /DATSTB, /ADDRSTB = 1 return value;}static void can_reset( void ){ //printk( "can_reset()\n" ); outb( Port2 = LP_RESET_ON, LP_CTRL ); udelay( 5 );}static void can_ena_irq( int irq_enable ){ //printk( "can_ena_int( %d )\n", irq_enable ); if ( irq_enable ) outb( Port2 = LP_RESET_OFF | LP_IRQ_ENA, LP_CTRL ); else outb( Port2 = LP_RESET_OFF, LP_CTRL );}#endifstatic int stuck_interrupt( void ){ return inb( LP_STAT ) & 0x40;}/*******************************************************************//*******************************************************************//*******************************************************************//* Hardware detection and init function * Independent of PC interface type *//* check if there is a CAN controller connected and powered *//* TODO: check type (82C200 or SJA1000) */static int can_find_chip( void ){ can_reset(); /* Control Register, Reset Request bit */ if ( 0x21 != ( can_get_reg( CAN_CR ) & 0x21 ) ) return -ENXIO; /* Status Register, TBS and TCS bits */ if ( 0x0c != can_get_reg( CAN_SR ) ) return -ENXIO; /* Interrupt Register, unused bits 5..7 = 1 */ if ( 0xe0 != can_get_reg( CAN_IR ) ) return -ENXIO; /* Clock Divider Register, divide by 12 (Motorola mode) */ if ( 0x05 != can_get_reg( CAN_CDR ) ) return -ENXIO; can_ena_irq( 0 ); return SUCCESS;}/* Init the CAN controller for a specific bit rate. * Enable TX and RX interrupts. * Push/Pull output for 82C250/251 driver circuit. */#define CAN_IRQ_ENA \ ( CAN_RECEIVE_INT_ENABLE \ | CAN_TRANSMIT_INT_ENABLE \ | CAN_ERROR_INT_ENABLE )static int can_setup( int speed ){ static int btr0 = -1; static int btr1 = -1; int err; if ( ( err = can_find_chip() ) < 0 ) return err; switch ( speed ) { case CAN_RESET: if ( -1 == btr0 ) return -1; if ( -1 == btr1 ) return -EINVAL; break; case CAN_10K: btr0 = CAN_TIM0_10K; btr1 = CAN_TIM1_10K; break; case CAN_20K: btr0 = CAN_TIM0_20K; btr1 = CAN_TIM1_20K; break; case CAN_40K: btr0 = CAN_TIM0_40K; btr1 = CAN_TIM1_40K; break; case CAN_50K: btr0 = CAN_TIM0_50K; btr1 = CAN_TIM1_50K; break; case CAN_100K: btr0 = CAN_TIM0_100K; btr1 = CAN_TIM1_100K; break; case CAN_125K: btr0 = CAN_TIM0_125K; btr1 = CAN_TIM1_125K; break; default: return -EINVAL; } can_put_reg( CAN_CR, CAN_RESET_REQUEST ); can_put_reg( CAN_ACR, 0xFF ); // dummy
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -