www.pudn.com > canbus4linux.rar > can200par.c
/* * can200par.c * Copyright (c) 2001 Jürgen Eder* * The hardware driver for the CAN200 CAN Card. The homepage of the CAN200 * project is: http://private.addcom.de/h/horo/can200 * * 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. */ //#define EXPORT_SYMTAB #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18) #include #else #include #endif #include #include #include "trace.h" #include "canbus4linux.h" #include "can200par.h" #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif static int minimum_num=-1; static int maximum_num=-1; static int frequency=8000000; static CANPARDEVICE candevice[PARPORT_MAX]; static struct parport_driver ParportDriver; static void wr_can(PCANPARDEVICE lpDIOCParms, unsigned char adr,unsigned char wert); static unsigned char rd_can(PCANPARDEVICE lpDIOCParms, unsigned char adr); static void out2Data(PCANPARDEVICE lpDIOCParms, int data); static void out2Controlport(PCANPARDEVICE lpDIOCParms, int data); static unsigned char inControlport(PCANPARDEVICE lpDIOCParms); static unsigned char inData(PCANPARDEVICE lpDIOCParms); //static void MaskIRQ(PCANPARDEVICE lpDIOCParms); //static void UnmaskIRQ(PCANPARDEVICE lpDIOCParms); //static void out2ECPControlport(PCANPARDEVICE lpDIOCParms, int data); //static unsigned char inECPControlport(PCANPARDEVICE lpDIOCParms); //static unsigned char inStatusport(PCANPARDEVICE lpDIOCParms); /***************************************************************************************/ static void wr_can(PCANPARDEVICE lpDIOCParms, unsigned char adr,unsigned char wert) // Wert in Register des 82C200 an Adresse adr schreiben { if(!lpDIOCParms->open) return; out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x20); // Datenport auf Ausgang schalten out2Data(lpDIOCParms,adr); // Adresse auf Datenport out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x08); // ALE=0 out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x01); // RD/WR\=0 out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x02); // E=1 out2Data(lpDIOCParms,wert); // Wert auf Datenport out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x02); // E=0 out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x28); // Datenport auf Eingang + ALE=1 } /***************************************************************************************/ static unsigned char rd_can(PCANPARDEVICE lpDIOCParms, unsigned char adr) // Wert aus Register des 82C200 auslesen { unsigned char bw; if(!lpDIOCParms->open) return 0; out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x20); // Datenport auf Ausgang out2Data(lpDIOCParms,adr); // Adresse auf Datenport out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x08); // ALE=0 out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x01); // RD/WR\=1 out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x20); // Datenport auf Eingang out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x02); // E=1 bw=inData(lpDIOCParms); // Datenport auslesen out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x02); // E=0 out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x08); // ALE=1 return(bw); } /***************************************************************************************/ /* static unsigned char inStatusport(PCANPARDEVICE lpDIOCParms) { return parport_read_status(lpDIOCParms->parport->port); } */ /***************************************************************************************/ static void out2Controlport(PCANPARDEVICE lpDIOCParms, int data) { if(!lpDIOCParms->parport || !lpDIOCParms->parport->port) return; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) parport_write_control(lpDIOCParms->parport->port,data & ~0x20); if(data & 0x20) parport_data_reverse(lpDIOCParms->parport->port); else parport_data_forward(lpDIOCParms->parport->port); #else parport_write_control(lpDIOCParms->parport->port,data); #endif lpDIOCParms->statusport = data; } /***************************************************************************************/ static unsigned char inControlport(PCANPARDEVICE lpDIOCParms) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) return lpDIOCParms->statusport; #else return parport_read_control(lpDIOCParms->parport->port); #endif } /***************************************************************************************/ static void out2Data(PCANPARDEVICE lpDIOCParms, int data) { parport_write_data(lpDIOCParms->parport->port,data); } /***************************************************************************************/ static unsigned char inData(PCANPARDEVICE lpDIOCParms) { return parport_read_data(lpDIOCParms->parport->port); } /***************************************************************************************/ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) static void out2ECPControlport(PCANPARDEVICE lpDIOCParms, int data) { parport_write_econtrol(lpDIOCParms->parport->port,data); } #endif /***************************************************************************************/ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) static unsigned char inECPControlport(PCANPARDEVICE lpDIOCParms) { return parport_read_econtrol(lpDIOCParms->parport->port); } #endif /***************************************************************************************/ static int can_preempt(void *handle) { return 1; // 1=don't release parport } /***************************************************************************************/ static void can_irq(int irq, void *dev_id, struct pt_regs *regs) { PCANPARDEVICE parport=(PCANPARDEVICE)dev_id; //TRACE("can_irq()"); if(parport && parport->pIsr) { // Aufruf der SJA Routine (*parport->pIsr)(parport,parport->pSja1000Par); } } /***************************************************************************************/ static int canpar_register_isr(PCANPARDEVICE lpDIOCParms, sja1000_isr pIsr, struct sja1000_admin *pSja1000Par) { TRACE("registering isr()"); if(lpDIOCParms) { lpDIOCParms->pIsr = pIsr; lpDIOCParms->pSja1000Par = pSja1000Par; return 0; } return -EINVAL; } /***************************************************************************************/ static int canpar_unregister_isr(PCANPARDEVICE lpDIOCParms) { TRACE("unregistering isr()"); if(lpDIOCParms) { lpDIOCParms->pIsr = 0; return 0; } return -EINVAL; } /***************************************************************************************/ static int canpar_open_device(PCANPARDEVICE lpDIOCParms) { TRACE("open device adr: %lx",(long)lpDIOCParms); //printk("canpar_open_device %d\n",frequency); if(lpDIOCParms && lpDIOCParms->parport) { if(parport_claim(lpDIOCParms->parport)) { TRACE("no access to PARPORT device"); return -ENODEV; } lpDIOCParms->ecp = 0; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) if(lpDIOCParms->parport->port->modes & (PARPORT_MODE_PCECP+PARPORT_MODE_PCECPEPP+PARPORT_MODE_PCECPPS2)) { INFO_TRACE("using ECP mode"); lpDIOCParms->ecp = 1; } else { INFO_TRACE("using Standard mode (Bidirectional, EPP)"); lpDIOCParms->ecp = 0; } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) out2Controlport(lpDIOCParms,0x04); // Schnittstelle initialisieren parport_enable_irq(lpDIOCParms->parport->port); #else if (lpDIOCParms->ecp == 1) // ECP Modus umschalten out2ECPControlport(lpDIOCParms,inECPControlport(lpDIOCParms)|0x20); out2Controlport(lpDIOCParms,0x14); // Schnittstelle initialisieren/IRQ ein #endif lpDIOCParms->open = 1; // Start value out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x20); // Datenport auf Eingang out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x08); // ALE=1 out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x02); // E=0 out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x01); // RD/WR\=0 return 0; } TRACE("Invalid data or no parport"); return -ENODEV; } /***************************************************************************************/ static int canpar_close_device(PCANPARDEVICE lpDIOCParms) { //INFO_TRACE("canpar_close_device"); TRACE("close device"); if(lpDIOCParms) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) out2Controlport(lpDIOCParms,inControlport(lpDIOCParms)&0xef); // Schnittstelle: IRQ aus parport_disable_irq(lpDIOCParms->parport->port); #else out2Controlport(lpDIOCParms,inControlport(lpDIOCParms)&0xef); // Schnittstelle: IRQ aus #endif parport_release(lpDIOCParms->parport); lpDIOCParms->open = 0; } return 0; } /***************************************************************************************/ static void cb_canpar_attach(struct parport *port) { // Search a free entry int num; for(num=0;num < PARPORT_MAX; num++) { if(candevice[num].parport == 0) { candevice[num].open = 0; candevice[num].pIsr = 0; candevice[num].num = num; candevice[num].parport = parport_register_device(port, "can200par", can_preempt, NULL, can_irq,0,&candevice[num]); if (!candevice[num].parport) { TRACE("Can't register device no.: %d name: %s",num, port->name); } else { struct sja1000_access access; char name[MAX_DEVICE_NAME_LENGTH]; TRACE("Register device no.: %d",num); access.chipset_frequency = frequency; access.output_control_register = 0x1a; //0xc2; access.pOpenCanDevice = (sja1000_openCanDevice)canpar_open_device; access.pCloseCanDevice = (sja1000_closeCanDevice)canpar_close_device; access.pWriteToRegister = (sja1000_writeToRegister)wr_can; access.pReadFromRegister = (sja1000_readFromRegister)rd_can; access.pRegisterIsr = (sja1000_registerIsr)canpar_register_isr; access.pUnregisterIsr = (sja1000_unregisterIsr)canpar_unregister_isr; access.bCanChipsetFlags=CANBUS_CFS_CAN_2_0_A+CANBUS_CFS_CAN_2_0_B+CANBUS_CFS_EXT_FRAME; sprintf(name,"can200 parallelport card%d",num); candevice[num].num = sja1000_register_device(name, CANBUS4LINUX_VERSION, &candevice[num], &access, minimum_num, maximum_num); if(candevice[num].num < 0) break; // not enough memory to register this device } break; } } } /***************************************************************************************/ static void cb_canpar_detach(struct parport *port) { int num; for(num=0;num < PARPORT_MAX; num++) { if(candevice[num].parport && (candevice[num].parport->port == port)) { TRACE("cb_canpar_detach: %d name: %s",num, port->name); parport_unregister_device(candevice[num].parport); return; } } INFO_TRACE("Can't detach parport: parport not found in candevice list %d: %s",num, port->name); } /***************************************************************************************/ int init_module(void) { #ifdef EXPORT_NO_SYMBOLS EXPORT_NO_SYMBOLS; #endif TRACE("init_module()"); memset(candevice,0x00,PARPORT_MAX*sizeof(CANPARDEVICE)); memset(&ParportDriver,0x00,sizeof(struct parport_driver)); ParportDriver.name = "can200par"; ParportDriver.attach = cb_canpar_attach; ParportDriver.detach = cb_canpar_detach; if (0 != parport_register_driver(&ParportDriver)) { INFO_TRACE("Error while calling parport_register_driver()"); } TRACE("Init module finished"); return 0; } /***************************************************************************************/ void cleanup_module(void) { int num; TRACE("cleanup"); for(num=0; num < PARPORT_MAX; num++) { if (candevice[num].parport) { TRACE("Unregister device no.: %d",num); if(candevice[num].open) canpar_close_device(&candevice[num]); candevice[num].num = sja1000_unregister_device(candevice[num].num); } } parport_unregister_driver(&ParportDriver); candevice[num].parport = 0; } /***************************************************************************************/ MODULE_PARM(minimum_num,"1i"); MODULE_PARM(maximum_num,"1i"); MODULE_PARM(frequency,"1i"); MODULE_AUTHOR("Juergen Eder "); MODULE_DESCRIPTION("CAN driver for CAN200 Card");