www.pudn.com > canbus4linux.rar > cantronik.c


/*
 * cantronik.c
 * Copyright (c) 2001
 *
 * The hardware driver for the cantronik CAN Card.
 * http://www.cantronik.com
 *
 * 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 "cantronik.h"

#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif


static int minimum_num=-1;
static int maximum_num=-1;
static int frequency=24000000;
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);	// set data port to output
	out2Data(lpDIOCParms,adr);                                  	// addresse on data port
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x01);	// ALE=0
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x04);	// RD/WR\=0
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x02);	// E=1
	out2Data(lpDIOCParms,wert);                                 	// value on data port
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x02);	// E=0
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x20);	// set data port to input
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x01);	// 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 | 0x01);    // ALE=0
	out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x04);    // 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 & ~0x01);   // 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;

		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);
	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,0x00); // Schnittstelle initialisieren
		parport_enable_irq(lpDIOCParms->parport->port);
#else
		if (lpDIOCParms->ecp == 1)  // ECP Modus umschalten
			out2ECPControlport(lpDIOCParms,inECPControlport(lpDIOCParms)|0x20);

		out2Controlport(lpDIOCParms,0x10); // Schnittstelle initialisieren/IRQ ein
#endif

		lpDIOCParms->open = 1;
		TRACE("open device finished -> ok");

		// Start value
		out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x20);	// Datenport auf Eingang
		out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x01);	// ALE=1
		out2Controlport(lpDIOCParms,lpDIOCParms->statusport | 0x02);	// E=0
		out2Controlport(lpDIOCParms,lpDIOCParms->statusport & ~0x04);	// RD/WR\=0
		wr_can(lpDIOCParms, 31, 0x40 | rd_can(lpDIOCParms, 31)); // Set CBP: "Receive Bypass Comparator"

		return 0;
	}
	TRACE("Invalid data or no parport");
	return -ENODEV;
}

/***************************************************************************************/
static int canpar_close_device(PCANPARDEVICE lpDIOCParms)
{
	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, "cantronik", 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,"cantronik 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 = "cantronik";
	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 CANTRONIK Card");