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


/*
 * canbuscore.c
 * Copyright (c) 2001 Jürgen Eder 
 *
 * A general canbus driver. To use this, you need also a
 * hardware driver (for example: sja1000.o + can200par.o).
 * Tested under Linux 2.4.20 on i386 PC architecture.
 * 
 * 		installation:
 * 		
 * 		make all
 * 		make install
 * 		
 * 		char-major numbers up to 240 are "LOCAL/EXPERIMENTAL" (see /usr/src/linux/Documentation/devices.txt)
 * 		char-major 91 is reserved for CAN BUS
 * 		insert following lines into /etc/modules.conf:
 * 		
 * 		only ELEKTOR CAN Card
 * 		--------------------------------------------------
 * 		alias char-major-91     elektor_canpar
 *
 *
 * 		or only CAN200 Card
 * 	    --------------------------------------------------
 * 		alias char-major-91     can200par
 *
 *
 * 		or both CAN Cards (kernel < 2.4.0)
 * 	    --------------------------------------------------
 * 		alias char-major-91     elektor_canpar
 * 		post-install elektor_canpar /sbin/modprobe "-k" can200par
 *
 *
 * 		or both CAN Cards (kernel >= 2.4.0, this also work with 2.2.x)
 * 	    --------------------------------------------------
 * 		alias char-major-91     canbus
 * 		probeall canbus elektor_canpar can200par
 * 	    --------------------------------------------------
 * 	    
 * 	    Also set the IRQ for the parport driver
 * 	    for example (IRQ 7 for LPT1):
 * 	    --------------------------------------------------
 *      options parport_pc io=0x378  irq=7,none
 * 	    --------------------------------------------------
 * 	    
 * 	    After changing "modules.conf" do a "depmod -a"
 * 
 *      Kernel > 2.5 need a "generate-modprobe.conf"
 *
 *      Reload the parport driver:
 *      rmmod parport_pc
 *      modprobe parport_pc io=0x378 irq=7
 *
 *
 * 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
#define CANBUS4LINUX_CLEARTEXT_COMMANDS
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,67)
#include 
#include 
#endif

#include "trace.h"
#include "canbus4linux.h"

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


static int major=91;
static int virtualize=1;
static char *version = "0.2.1";

#define CANBUS4LINUX_PROC_DIR "canbus"

#define NUM_CANBUS_DEVICES 10
#define MAX_DEVICE_INFO (2*MAX_DEVICE_NAME_LENGTH)
#define NUMBER_OF_WRITE_BUFFER (100)
#define MAX_EVENTS (2*NUMBER_OF_WRITE_BUFFER)

int canbus4linux_fasync(int fd, struct file *file, int mode);
void *Allocate_Memory(unsigned long s);
void Free_Memory(void *p);
struct canbus_time GetTime(void);
static struct canbus_admin canbus4linux_admin[NUM_CANBUS_DEVICES];
static struct proc_dir_entry *canbus4linux_proc_dir=NULL;

struct canbus_intern_transmit 
{
	struct canbus_transmit_data data;
	struct file *fa_file;
};

struct proc_registers
{
	struct canbus_admin *pAdmin;
	u16 register_nr;
};

struct canbus_file;
struct canbus_admin
{
	/* 			W I C H T I G
	 * 			Bei Änderungen muß evtl. auch "canbus4linux_register_device()" 
	 * 			geändert werden
	 */
	struct canbus_access access;
	int driver_registered;
	void *pDeviceParm;
	char cDeviceName[MAX_DEVICE_INFO];
	int bCan_2B; // 0=CAN 2.0 A   1=CAN 2.0B
	int bFrameFmt; // 0=11 bit if CAN 2.0A and 29 bit if CAN 2.0B,  1=always 11 bit,  2=always 29bit (see: CANBUS_TRANS_...)
	int bOpen;
	unsigned long iBaudrate;
	struct canbus_acceptance_filter acceptance_filter; // hardwarefilter
	struct canbus_acceptance_filter soft_acceptance_filter; // softwarefilter
	int standby;
	int virtualize;
	unsigned long lost;
	int busload;
	short   transmitting;			// 1=Daten wurden in das SJA1000 geschrieben, warte jetzt auf IRQ

	struct canbus_properties props;
	struct canbus_file *cf_first;

	struct fasync_struct *async_queue;
	struct proc_dir_entry *proc_dir;
	struct proc_dir_entry *proc_register_dir;
	struct proc_registers *proc_regs;

	struct canbus_intern_transmit send_data;
	struct  canbus_intern_transmit *sndISRInfo;	// CAN-Botschaften in der Warteschlange, die noch an den SJA übergeben werden müssen (FIFO)
	volatile short   sndIn;  // Nummer (Daten wurden in die Warteschlange eingereiht)
	volatile short   sndOut; // Nummer (bis "hier" wurden die Daten bereits abgeholt)
};

struct canbus_file
{
	struct canbus_file *next;
	struct file *file;
	struct canbus_admin *adm;
	char write_buffer[NUMBER_OF_WRITE_BUFFER];
	int write_buffer_pos;
	int sleeping_send_wait;
	int sleeping_send_read;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	wait_queue_head_t sended;
	wait_queue_head_t send_wait;
	wait_queue_head_t send_read;
#else
	struct  wait_queue *sended;
	struct  wait_queue *send_wait;
	struct  wait_queue *send_read;
#endif
	int send_counter;
	struct  canbus_event *admISRInfo;	// Daten, die an die USER-Mode Applikation übergeben werden (FIFO-Struktur)
	short   admIn;  // Nummer (Daten wurden in die Warteschlange eingereiht)
	short   admOut; // Nummer (bis "hier" wurden die Daten bereits abgeholt)
};

/***************************************************************************************/
int canbus4linux_open_device(struct canbus_admin *pPar)
{
		int ret=0;

		TRACE("canbus4linux_open_device -1- %d",pPar->bOpen);
		if(!pPar->bOpen)
		{
			TRACE("canbus4linux_open_device -2-");
			pPar->transmitting=0;
			pPar->lost=0;

			pPar->sndIn=0;
			pPar->sndOut=0;

			pPar->sndISRInfo = (struct canbus_intern_transmit *)Allocate_Memory(MAX_EVENTS*sizeof(struct canbus_intern_transmit));
			if (!pPar->sndISRInfo) goto error;

			if(pPar->access.pOpen)
				ret = (*pPar->access.pOpen)(pPar->pDeviceParm);

			//if(pPar->access.pInit)
			// 	(*pPar->access.pInit)(pPar->pDeviceParm);
		}

		if(ret >= 0)
		{
			pPar->bOpen++;
			TRACE("canbus4linux_open_device -3-");
			if (canbus4linux_test_device(pPar) == 0)
			{
				INFO_TRACE("Warning: no device detected");
			}

		}

		TRACE("canbus4linux_open_device -> ok");
		return ret;

	
error:
	INFO_TRACE("out of memory while allocation buffer");

	Free_Memory(pPar->sndISRInfo);
	pPar->sndISRInfo = NULL;
	pPar->sndIn = 0;
	pPar->sndOut = 0;

	return -ENOMEM;
}
/***************************************************************************************/
int canbus4linux_close_device(struct canbus_admin *pPar)
{
		if(pPar->bOpen <= 0)  // already closed
			return 0;
			
		pPar->bOpen--;

		if(pPar->bOpen == 0)  // now ready to close ?
		{
			int ret = 0;

			if(pPar->access.pClose)
				(*pPar->access.pClose)(pPar->pDeviceParm);

			Free_Memory(pPar->sndISRInfo);
			pPar->sndISRInfo = NULL;
			pPar->sndIn = 0;
			pPar->sndOut = 0;

			TRACE("canbus4linux_close_device -> ok");
			return ret;
		}

		return 0;
}
/***************************************************************************************/
int canbus4linux_init_device(struct canbus_admin *pPar)
{
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pInit)
	{
		TRACE("canbus4linux_init_device");
		return (*pPar->access.pInit)(pPar->pDeviceParm);
	}
	INFO_TRACE("error in canbus4linux_init_device %p %p %d %d",pPar,pPar->access.pInit,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_test_device(struct canbus_admin *pPar)
{
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pTestDevice)
	{
		TRACE("sja1000_test_device");
		return (*pPar->access.pTestDevice)(pPar->pDeviceParm);
	}
	INFO_TRACE("error in sja1000_test_device %p %p %d %d",pPar,pPar->access.pInit,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_set_baudrate(struct canbus_admin *pPar, unsigned long baudrate)
{
	pPar->iBaudrate = baudrate;
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pSetBaudrate)
	{
		return (*pPar->access.pSetBaudrate)(pPar->pDeviceParm,baudrate);
	}
	//INFO_TRACE("error in canbus4linux_set_baudrate %p %p %d %d",pPar,pPar->access.pSetBaudrate,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
unsigned long canbus4linux_get_baudrate(struct canbus_admin *pPar)
{
	return pPar->iBaudrate;
}
/***************************************************************************************/
unsigned long canbus4linux_get_baudrate_by_constant(struct canbus_admin *pPar)
{
	return pPar->iBaudrate;
}
/***************************************************************************************/
int canbus4linux_get_properties(struct canbus_admin *pPar, struct canbus_properties *props)
{
	if(/*pPar->bOpen &&*/ pPar->driver_registered && pPar->access.pGetProperty)
	{
		props->version = 0;
		strcpy(props->device_name,pPar->cDeviceName);
		return (*pPar->access.pGetProperty)(pPar->pDeviceParm,props);
	}
	INFO_TRACE("error in canbus4linux_get_properties %p %p %d %d",pPar,pPar->access.pGetProperty,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_set_register(struct canbus_admin *pPar, int addresse, unsigned int value)
{
	if(/*pPar->bOpen &&*/ pPar->driver_registered && pPar->access.pSetRegister)
	{
		return (*pPar->access.pSetRegister)(pPar->pDeviceParm,addresse,value);
	}
	INFO_TRACE("error in canbus4linux_set_register %p %p %d %d",pPar,pPar->access.pSetRegister,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_get_register(struct canbus_admin *pPar, int addresse, int *value)
{
	if(/*pPar->bOpen &&*/ pPar->driver_registered && pPar->access.pGetRegister)
	{
		return (*pPar->access.pGetRegister)(pPar->pDeviceParm,addresse,value);
	}
	INFO_TRACE("error in canbus4linux_get_register %p %p %d %d",pPar,pPar->access.pGetRegister,pPar->driver_registered,pPar->bOpen);
	return 0;
}
/***************************************************************************************/
int canbus4linux_set_can_mode(struct canbus_admin *pPar, int can_2b)
{
	pPar->bCan_2B = can_2b;
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pSetCanMode)
	{
		int ret;
		ret = (*pPar->access.pSetCanMode)(pPar->pDeviceParm,can_2b);
		canbus4linux_init_device(pPar);
		return ret;
	}
	INFO_TRACE("error in canbus4linux_set_can_mode %p %p %d %d",pPar,pPar->access.pSetCanMode,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_set_baudrate_by_constant(struct canbus_admin *pPar, unsigned long constante)
{
	pPar->iBaudrate = constante;
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pSetBaudrateByConstant)
	{
		return (*pPar->access.pSetBaudrateByConstant)(pPar->pDeviceParm,constante);
	}
	INFO_TRACE("error in canbus4linux_set_baudrate_by_constant %p %p %d %d",pPar,pPar->access.pSetBaudrateByConstant,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_set_acceptance_filter(struct canbus_admin *pPar, struct canbus_acceptance_filter *filter)
{
	memcpy(&pPar->acceptance_filter,filter,sizeof(struct canbus_acceptance_filter));
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pSetAcceptanceFilter)
	{
		return (*pPar->access.pSetAcceptanceFilter)(pPar->pDeviceParm,&pPar->acceptance_filter);
	}
	INFO_TRACE("error in canbus4linux_set_acceptance_filter %p %p %d %d",pPar,pPar->access.pSetAcceptanceFilter,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
struct canbus_acceptance_filter canbus4linux_get_acceptance_filter(struct canbus_admin *pPar)
{
	return pPar->acceptance_filter;
}
/***************************************************************************************/
int canbus4linux_set_soft_acceptance_filter(struct canbus_admin *pPar, struct canbus_acceptance_filter *filter)
{
	memcpy(&pPar->soft_acceptance_filter,filter,sizeof(struct canbus_acceptance_filter));
	pPar->soft_acceptance_filter.code &= pPar->soft_acceptance_filter.mask;
	return 0;
}
/***************************************************************************************/
struct canbus_acceptance_filter canbus4linux_get_soft_acceptance_filter(struct canbus_admin *pPar)
{
	return pPar->soft_acceptance_filter;
}
/***************************************************************************************/
int canbus4linux_set_command(struct canbus_admin *pPar, int command)
{
	if(command == CANBUS_CMD_ENTER_STANDBY)
	{
		pPar->standby=1;
	}
	if(command == CANBUS_CMD_LEAVE_STANDBY)
	{
		pPar->standby=0;
	}
	if(command == CANBUS_CMD_ABORT_TRANSMISSION)
	{
		pPar->sndIn = pPar->sndOut;
		pPar->transmitting = 0;
	}
	if(command == CANBUS_CMD_VIRTUALIZE_ON)
	{
		pPar->virtualize = 1;
		return 0;
	}
	if(command == CANBUS_CMD_VIRTUALIZE_OFF)
	{
		pPar->virtualize = 0;
		return 0;
	}
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pSetCommand)
	{
		return (*pPar->access.pSetCommand)(pPar->pDeviceParm,command);
	}
	INFO_TRACE("error in canbus4linux_set_command %p %p %d %d",pPar,pPar->access.pSetCommand,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_set_default_frame_format(struct canbus_admin *pPar, int fmt)
{
	pPar->bFrameFmt=fmt;
	return 0;
}
/***************************************************************************************/
int canbus4linux_transmit_data(struct canbus_admin *pPar, struct canbus_transmit_data *trans, struct file *fa_file)
{
	if(pPar->bOpen && pPar->driver_registered && pPar->access.pTransmitData && pPar->sndISRInfo)
	{
		int fmt=trans->fmt;

		// error check
		if(fmt < CANBUS_TRANS_FMT_DEFAULT)
			fmt = CANBUS_TRANS_FMT_DEFAULT;
		if(fmt > CANBUS_TRANS_FMT_EXT)
			fmt = CANBUS_TRANS_FMT_EXT;

		// format check
		if(!pPar->bCan_2B)	// mode is set to CAN 2.0A
		{
			fmt = CANBUS_TRANS_FMT_STD;	// send data always as standard frame
		}
		else
		{
			if(!fmt)
			{
				if(!pPar->bFrameFmt)
					fmt = CANBUS_TRANS_FMT_EXT;
				else
					fmt = pPar->bFrameFmt;
			}
		}
		
		if(pPar->transmitting)
		{
			int t;
			memcpy(&pPar->sndISRInfo[pPar->sndIn].data,trans,sizeof(struct canbus_transmit_data));
			pPar->sndISRInfo[pPar->sndIn].data.fmt = fmt;
			pPar->sndISRInfo[pPar->sndIn].fa_file = fa_file;
			t = (1+pPar->sndIn)%MAX_EVENTS;

			if(t == pPar->sndOut)
			{
				if (fa_file && !(fa_file->f_flags & O_NONBLOCK) && fa_file->private_data)
				{
					struct canbus_file *cf = (struct canbus_file *)fa_file->private_data;
					cf->sleeping_send_wait=1;
					interruptible_sleep_on(&cf->send_wait);
					if(t == pPar->sndOut)
					{
						return -EAGAIN;
					}
				}
				else
				{
					return -EAGAIN;
				}
			}
			
			if (fa_file && fa_file->private_data)
			{
				struct canbus_file *cf = (struct canbus_file *)fa_file->private_data;
				cf->send_counter++;
			}
			pPar->sndIn = t;
			return 0;
		}
		pPar->transmitting = 1;

		memcpy(&pPar->send_data.data,trans,sizeof(struct canbus_transmit_data));
		pPar->send_data.data.fmt = fmt;
		pPar->send_data.fa_file = fa_file;
		if (fa_file && fa_file->private_data)
		{
			struct canbus_file *cf = (struct canbus_file *)fa_file->private_data;
			cf->send_counter++;
		}
		if(pPar->standby)
		{
			canbus4linux_set_command(pPar,CANBUS_CMD_LEAVE_STANDBY);
		}

		return (*pPar->access.pTransmitData)(pPar->pDeviceParm,&pPar->send_data.data);
	}
	INFO_TRACE("error in canbus4linux_transmit_data %p %p %d %d",pPar,pPar->access.pTransmitData,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
int canbus4linux_get_event(struct canbus_admin *pPar, struct canbus_event *trans, struct canbus_file *cf)
{
	if(pPar->bOpen && cf/*&& pPar->driver_registered && pPar->admISRInfo*/)
	{
		TRACE("canbus4linux_get_event -1-");
		if(cf->admIn != cf->admOut)
		{
			TRACE("canbus4linux_get_event -2-");
			memcpy(trans,&cf->admISRInfo[cf->admOut],sizeof(struct canbus_event));
			cf->admOut = (1+cf->admOut)%MAX_EVENTS;
			return 1;
		}
		return 0;
	}
	INFO_TRACE("error in canbus4linux_get_event %p %d %d",pPar,pPar->driver_registered,pPar->bOpen);
	return -ENODEV;
}
/***************************************************************************************/
//int canbus4linux_check_received_msg(struct canbus_admin *pPar, struct canbus_event *pEvent)
//{
//	unsigned long tmp = (pPar->acceptance_filter.code ^ pEvent->data.identifier) | pPar->acceptance_filter.mask | 0xc0000000;
//
//	INFO_TRACE("check %lx %lx %lx => %lx => %lx",pPar->acceptance_filter.code,pEvent->data.identifier,pPar->acceptance_filter.mask,pPar->acceptance_filter.code ^ pEvent->data.identifier,tmp);
//	if (tmp == 0xffffffff)
//		return 1;
//	return 0;
//}
/***************************************************************************************/
int canbus4linux_interrupt(void *pSpecificPar, struct canbus_admin *pPar, struct canbus_event *pEvent)
{
	TRACE("canbus4linux_interrupt -1-");
	if(pPar && pEvent && /*pPar->async_queue && */pPar->bOpen)
	{
		//struct fasync_struct *fa=pPar->async_queue;
		struct canbus_file *cf;
		int t;
		int max=1000;
		
		TRACE("canbus4linux_interrupt -2-");

		pEvent->time = GetTime();
		if(pEvent->event == CANBUS_EVENT_LEAVING_STANDBY)
		{
			pPar->standby = 0; // every event means "wake up"
		}

		for(cf = pPar->cf_first,max=1000;cf && max;cf=cf->next,max--)
		{
				memcpy(&cf->admISRInfo[cf->admIn],pEvent,sizeof(struct canbus_event));
				if(pEvent->event == CANBUS_EVENT_TRANSMITTED)
				{
					// sended data are in pPar->send_data !!!
					memcpy(&cf->admISRInfo[cf->admIn].data,&pPar->send_data.data,sizeof(struct canbus_transmit_data));

					if(cf->file == pPar->send_data.fa_file)
					{
						cf->send_counter--;
					}
					else if(pPar->virtualize)
					{
						// only the application, who sent the data get the CANBUS_EVENT_TRANSMITTED message
						// all other apps gets CANBUS_EVENT_RECEIVED (virtual can bus)
						cf->admISRInfo[cf->admIn].event = CANBUS_EVENT_RECEIVED;
						cf->admISRInfo[cf->admIn].lost = 0;
					}


					if(cf->sleeping_send_wait)
					{
						cf->sleeping_send_wait=0;
						wake_up_interruptible(&cf->send_wait);
					}
					
				}

				// Test Software acceptance filter
				if(cf->adm && (cf->admISRInfo[cf->admIn].event == CANBUS_EVENT_RECEIVED))
				{
					if(  !(cf->adm->soft_acceptance_filter.code == (cf->admISRInfo[cf->admIn].data.identifier & cf->adm->soft_acceptance_filter.mask)))
						continue;
				}

				t = (1+cf->admIn)%MAX_EVENTS;
				if(t == cf->admOut)
				{
					if(pEvent->event == CANBUS_EVENT_RECEIVED)
				    	pPar->lost++;
				}
				else
				{
					if(pEvent->event == CANBUS_EVENT_RECEIVED)
					{
						cf->admISRInfo[cf->admIn].lost = pPar->lost;
						pPar->lost = 0;
					}

					cf->admIn = t;
				}

				if(cf->sleeping_send_read)
				{
					TRACE("canbus4linux_irq -> read sleep -> wakeup");
					cf->sleeping_send_read=0;
					wake_up_interruptible(&cf->send_read);
				}
		}
		// if there are any data to send, send it now and only once (because of that, this code mustn't be in the loop above !!!)
		if(pPar->sndIn != pPar->sndOut)
		{
			memcpy(&pPar->send_data,&pPar->sndISRInfo[pPar->sndOut],sizeof(struct canbus_intern_transmit));
			pPar->sndOut = (1+pPar->sndOut)%MAX_EVENTS;
			if(pPar->standby)
				canbus4linux_set_command(pPar,CANBUS_CMD_LEAVE_STANDBY);
			if(pPar->access.pTransmitData)
				(*pPar->access.pTransmitData)(pPar->pDeviceParm,&pPar->send_data.data);
		}
		else
		{
			for(cf = pPar->cf_first,max=1000;cf && max;cf=cf->next,max--)
			{
				wake_up_interruptible(&cf->sended);
			}
			pPar->transmitting = 0;
		}

		if(pPar->async_queue)
		{
			TRACE("canbus4linux_interrupt -3-");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
			kill_fasync(&pPar->async_queue,SIGIO,POLL_IN); // 2.4.0
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18)
			kill_fasync(pPar->async_queue,SIGIO,POLL_IN); // 2.2.18
#else
			kill_fasync(pPar->async_queue,SIGIO);   // 2.2.16
#endif
#endif
		}
		return 1;
	}
	return 0;
}
/***************************************************************************************/
static const char zeichen_toupper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char zeichen_tolower[] = "abcdefghijklmnopqrstuvwxyz";
/***************************************************************************************/
char mytoupper(char data)
{
	int p;
	if ((data >= 'a') && (data <= 'z'))
	{
		p = data-'a';
		data = zeichen_toupper[p];
	}
	return data;
}
/***************************************************************************************/
char mytolower(char data)
{
	int p;
	if ((data >= 'A') && (data <= 'Z'))
	{
		p = data-'A';
		data = zeichen_tolower[p];
	}
	return data;
}
/***************************************************************************************/
int myAtoi(const char *buffer)
{
	int ret=0;
	int m=1,t,x=0;

	// searching the first digit
	for(t=0;t= '0') && (buffer[t] <= '9') )
		{
			x = t;
			break;
		}
	}

	for(t=x+1;t '9')
			break;
		if(buffer[t] < '0')
			break;
		m *= 10;
	}
	for(t=x;t '9')
			break;
		if(buffer[t] < '0')
			break;
		ret = ret + (buffer[t]-'0')*m;
		m /= 10;
	}
	return ret;
}
/***************************************************************************************/
int htoi(unsigned long *x, const char *buffer)
{
	int ret=0;
	int t;
	char z;
	
	if(!buffer[0])
		return 0;

	// searching the first digit
	for(t=0;t= '0') && (z <= '9')) || ((z >= 'a') && (z <= 'f')) )
		{
			break;
		}
	}

	for(;t= '0') && (z <= '9')) || ((z >= 'a') && (z <= 'f')) )
		{
			if((z >= '0') && (z <= '9'))
				ret = (ret<<4) + (z-'0');
			else
				ret = (ret<<4) + (z-'a'+10);
		}
		else
			break;
	}
	*x = ret;
	return t;
}
/***************************************************************************************/
int canbus4linux_read_baudrate_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%lu\n",pPar->iBaudrate);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_baudrate_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	unsigned long baudrate;
	baudrate = myAtoi(page);
	if(canbus4linux_open_device(pPar) >= 0)
	{
		canbus4linux_set_baudrate(pPar, baudrate);
		canbus4linux_close_device(pPar);
	}
	return count;
}
/***************************************************************************************/
int canbus4linux_read_can_mode_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%d [%s]\n",pPar->bCan_2B,(pPar->bCan_2B==CANBUS_FORMAT_CAN_2_0_A)?"CAN 2.0 A":"CAN 2.0 B");

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_can_mode_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int m;
	m = myAtoi(page);
	if(canbus4linux_open_device(pPar) >= 0)
	{
		canbus4linux_set_can_mode(pPar, m);
		canbus4linux_close_device(pPar);
	}
	return count;
}
/***************************************************************************************/
int canbus4linux_read_can_format_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	const char *fmt;
	int len;

	switch(pPar->bFrameFmt)
	{
	default:
	case CANBUS_TRANS_FMT_DEFAULT:
		fmt = "default (2.0 A => 11 bit, 2.0 B => 29 bit)";
		break;
	case CANBUS_TRANS_FMT_STD:
		fmt = "standard (11 bit)";
		break;
	case CANBUS_TRANS_FMT_EXT:
		fmt = "extended (29 bit)";
		break;
	}
	sprintf(page,"%d [%s]\n",pPar->bFrameFmt,fmt);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_can_format_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	pPar->bFrameFmt = myAtoi(page);
	return count;
}
/***************************************************************************************/
int canbus4linux_read_can_filter_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%08lx %08lx\n",pPar->acceptance_filter.code,pPar->acceptance_filter.mask);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_can_filter_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	struct canbus_acceptance_filter filter;
	int x;
	memset(&filter,0,sizeof(filter));
	x = htoi(&filter.code, page);
	htoi(&filter.mask,&page[x]);
	if(canbus4linux_open_device(pPar) >= 0)
	{
		canbus4linux_set_acceptance_filter(pPar, &filter);
		canbus4linux_close_device(pPar);
	}
	return count;
}
/***************************************************************************************/
int canbus4linux_read_can_soft_filter_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%08lx %08lx\n",pPar->soft_acceptance_filter.code,pPar->soft_acceptance_filter.mask);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_can_soft_filter_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	struct canbus_acceptance_filter filter;
	int x;
	memset(&filter,0,sizeof(filter));
	x = htoi(&filter.code, page);
	htoi(&filter.mask,&page[x]);
	if(canbus4linux_open_device(pPar) >= 0)
	{
		canbus4linux_set_soft_acceptance_filter(pPar, &filter);
		canbus4linux_close_device(pPar);
	}
	return count;
}
/***************************************************************************************/
int canbus4linux_read_register_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	int len=0;
	int value;
	struct proc_registers *pProc= (struct proc_registers *)data;
	if(pProc)
	{
		struct canbus_admin *pPar  = pProc->pAdmin;

		if(pPar)
		{
			if(canbus4linux_open_device(pPar) >= 0)
			{
				canbus4linux_get_register(pPar, pProc->register_nr,&value);
				TRACE("read register %03d : %02x",pProc->register_nr,value);
				canbus4linux_close_device(pPar);
			}
			else
				INFO_TRACE("Can't open device while reading register");

			sprintf(page,"%02x\n",value);
			len = strlen(page);
			if (len <= off+count) *eof = 1;
			*start = page + off;
			len -= off;
			if (len > count) len = count;
			if (len < 0) len = 0;
		}
		else
			INFO_TRACE("Error pPar = NULL while read_register_proc()");
	}
	else
		INFO_TRACE("Error pProc = NULL while read_register_proc()");
	return len;
}
/***************************************************************************************/
int canbus4linux_write_register_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	unsigned long value;
	struct proc_registers *pProc= (struct proc_registers *)data;
	if(pProc)
	{
		struct canbus_admin *pPar  = pProc->pAdmin;

		if(pPar)
		{
			if(canbus4linux_open_device(pPar) >= 0)
			{
				htoi(&value, page);
				canbus4linux_set_register(pPar, pProc->register_nr,(int)value);
				TRACE("write register %03d : %02lx",pProc->register_nr,value);
				canbus4linux_close_device(pPar);
			}
			else
				INFO_TRACE("Can't open device while writing register");
		}
		else
			INFO_TRACE("Error pPar = NULL while write_register_proc()");
	}
	else
		INFO_TRACE("Error pProc = NULL while write_register_proc()");
	return count;
}


/***************************************************************************************/
int canbus4linux_read_standby_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%d\n",pPar->standby);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_standby_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int m = myAtoi(page);
	if(canbus4linux_open_device(pPar) >= 0)
	{
		if(m)
			canbus4linux_set_command(pPar, CANBUS_CMD_ENTER_STANDBY);
		else
			canbus4linux_set_command(pPar, CANBUS_CMD_LEAVE_STANDBY);
		canbus4linux_close_device(pPar);
	}
	return count;
}
/***************************************************************************************/
int canbus4linux_read_virtualize_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%d\n",pPar->virtualize);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_write_virtualize_proc(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	pPar->virtualize = myAtoi(page);
	return count;
}
/***************************************************************************************/
int canbus4linux_read_lost_msg_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%lu\n",pPar->lost);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_read_busload_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	sprintf(page,"%d\n",pPar->busload);

	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_transmit_from_char(const char *page, unsigned long count, struct canbus_admin *pPar, struct file *file)
{
	struct canbus_transmit_data d;
	int x=0;
	int ret=0;
	unsigned long t;
	memset(&d,0,sizeof(d));
	x += htoi(&t, &page[x]); // Can ID
	d.identifier = t;
	x += htoi(&t, &page[x]); // rtr
	d.rtr = (unsigned char)t;
	x += htoi(&t, &page[x]); // dlc
	d.dlc = (unsigned char)t;
	x += htoi(&t, &page[x]); // 8 Data Bytes
	d.msg[0] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[1] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[2] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[3] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[4] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[5] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[6] = (unsigned char)t;
	x += htoi(&t, &page[x]);
	d.msg[7] = (unsigned char)t;
	ret = canbus4linux_transmit_data(pPar, &d, file);
	return ret;
}
/***************************************************************************************/
int canbus4linux_write_transmit(struct file *file, const char *page, unsigned long count, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	if(canbus4linux_open_device(pPar) >= 0)
	{
		canbus4linux_transmit_from_char(page, count, pPar, NULL);
		canbus4linux_close_device(pPar);
	}
	return count;
}
/***************************************************************************************/
int canbus4linux_read_settings_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct canbus_admin *pPar= (struct canbus_admin *)data;
	int len;
	int p;

	strcpy(page,pPar->cDeviceName);
	for(p=0;pbOpen)?"opened":"closed");
	canbus4linux_get_properties(pPar, &pPar->props);
	sprintf(&page[strlen(page)],"baudrates: %ld - %ld\n",pPar->props.min,pPar->props.max);
	sprintf(&page[strlen(page)],"baudrate mode: %s\n",(pPar->props.number_baudrates!=0)?"fix baudrates (list)":"calculating bit timing registers");
	if(pPar->props.number_baudrates)
	{
		for(p=0;(pprops.number_baudrates) && (pprops.baudrates[p]);
		}
	}
	sprintf(&page[strlen(page)],"supported chipset flags: ");
	if(pPar->props.chipset_flags & CANBUS_CFS_CAN_2_0_A)
		sprintf(&page[strlen(page)],"CANBUS_CFS_CAN_2_0_A ");
	if(pPar->props.chipset_flags & CANBUS_CFS_CAN_2_0_B)
		sprintf(&page[strlen(page)],"CANBUS_CFS_CAN_2_0_B ");
	if(pPar->props.chipset_flags & CANBUS_CFS_EXT_FRAME)
		sprintf(&page[strlen(page)],"CANBUS_CFS_EXT_FRAME ");
	if(pPar->props.chipset_flags & CANBUS_CFS_POLLING)
		sprintf(&page[strlen(page)],"CANBUS_CFS_POLLING ");
	sprintf(&page[strlen(page)],"\n");
	sprintf(&page[strlen(page)],"can mode: %s\n",(pPar->bCan_2B==0)?"2.0A":"2.0B");
	for(p=0;pprops.number_commands;p++)
	{
		sprintf(&page[strlen(page)],"supported command: %s\n",canbus4linux_commands[pPar->props.commands[p]]);
	}
	sprintf(&page[strlen(page)],"lowlevel access: register 0x0000...0x%04x\n",pPar->props.number_registers-1);

	if(canbus4linux_open_device(pPar) >= 0)
	{
		if(canbus4linux_test_device(pPar) > 0)
			sprintf(&page[strlen(page)],"hardware detected\n");
		else
			sprintf(&page[strlen(page)],"hardware not detected\n");
		canbus4linux_close_device(pPar);
	}
	else
	{
		sprintf(&page[strlen(page)],"hardware not detected\n");
	}


	sprintf(&page[strlen(page)],"\n");


	len = strlen(page);
	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len > count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
int canbus4linux_read_number_drv_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	// Code aus drivers/char/rtc.c
	//
	// Calculating the number of registered drivers
	int len,t,number=0;
	for(t=0;t count) len = count;
	if (len < 0) len = 0;
	return len;
}
/***************************************************************************************/
void canbus4linux_proc_driver_dir(struct canbus_admin *pPar, int num, int remove)
{
	char name[10];
	struct proc_dir_entry *res;
	if(canbus4linux_proc_dir)
	{
		sprintf(name,"%d",num);
		if(!remove)
		{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18)
			pPar->proc_dir = proc_mkdir(name,canbus4linux_proc_dir);
#else
			pPar->proc_dir = create_proc_entry(name,S_IFDIR,canbus4linux_proc_dir);
#endif
			if(pPar->proc_dir)
			{
				if(pPar->proc_regs)
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18)
					pPar->proc_register_dir = proc_mkdir("register",pPar->proc_dir);
#else
					pPar->proc_register_dir = create_proc_entry("register",S_IFDIR,pPar->proc_dir);
#endif
				}
				else
					pPar->proc_register_dir = NULL;
					
				if(pPar->proc_register_dir)
				{
					char register_name[10];
					int n;
					for(n=0;nprops.number_registers;n++)
					{
						sprintf(register_name,"%03d",n);
    					res = create_proc_entry(register_name,0777,pPar->proc_register_dir); 
    					if(res)
    					{
    						res->read_proc = canbus4linux_read_register_proc;
    						res->write_proc = canbus4linux_write_register_proc;
    						res->data = &pPar->proc_regs[n];
    					}
					}
				}

    			res = create_proc_entry("information",0,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_settings_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("baudrate",0777,pPar->proc_dir);
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_baudrate_proc;
    				res->write_proc = canbus4linux_write_baudrate_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("mode",0777,pPar->proc_dir);
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_can_mode_proc;
    				res->write_proc = canbus4linux_write_can_mode_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("format",0777,pPar->proc_dir); // 11 bit, or 29 bit Frame format
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_can_format_proc;
    				res->write_proc = canbus4linux_write_can_format_proc;
    				res->data = pPar;
    			}

				// Old name for Hardware filter, maybe this entry will be deleted in the future
    			res = create_proc_entry("filter",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_can_filter_proc;
    				res->write_proc = canbus4linux_write_can_filter_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("hardware_filter",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_can_filter_proc;
    				res->write_proc = canbus4linux_write_can_filter_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("software_filter",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_can_soft_filter_proc;
    				res->write_proc = canbus4linux_write_can_soft_filter_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("standby",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_standby_proc;
    				res->write_proc = canbus4linux_write_standby_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("lost",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_lost_msg_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("busload",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc = canbus4linux_read_busload_proc;
    				res->data = pPar;
    			}

    			res = create_proc_entry("transmit",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->write_proc = canbus4linux_write_transmit;
    				res->data = pPar;
    			}

    			res = create_proc_entry("virtualize",0777,pPar->proc_dir); 
    			if(res)
    			{
    				res->read_proc  = canbus4linux_read_virtualize_proc;
    				res->write_proc = canbus4linux_write_virtualize_proc;
    				res->data = pPar;
    			}
  			}
		}
		else
		{
			if(pPar->proc_dir)
			{
				TRACE("remove proc/canbus/%s/baudrate",name);
				remove_proc_entry("baudrate",pPar->proc_dir);

				TRACE("remove proc/canbus/%s/mode",name);
    			remove_proc_entry("mode",pPar->proc_dir);

				TRACE("remove proc/canbus/%s/format",name);
    			remove_proc_entry("format",pPar->proc_dir); // 11 bit, or 29 bit Frame format

				TRACE("remove proc/canbus/%s/filter",name);
    			remove_proc_entry("filter",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/hardware_filter",name);
    			remove_proc_entry("hardware_filter",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/software_filter",name);
    			remove_proc_entry("software_filter",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/standby",name);
    			remove_proc_entry("standby",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/lost",name);
    			remove_proc_entry("lost",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/busload",name);
    			remove_proc_entry("busload",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/transmit",name);
    			remove_proc_entry("transmit",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/virtualize",name);
    			remove_proc_entry("virtualize",pPar->proc_dir); 

				TRACE("remove proc/canbus/%s/information",name);
				remove_proc_entry("information",pPar->proc_dir);

				if(pPar->proc_register_dir)
				{
					char register_name[10];
					int n;
					TRACE("remove proc/canbus/%s/register/xxx",name);
					for(n=0;nprops.number_registers;n++)
					{
						sprintf(register_name,"%03d",n);
						remove_proc_entry(register_name,pPar->proc_register_dir);
					}
					TRACE("remove proc/canbus/%s/register (directory)",name);
					remove_proc_entry("register",pPar->proc_dir);
					pPar->proc_register_dir = 0;
				}
				TRACE("remove proc/canbus/%s (directory)",name);
				remove_proc_entry(name,canbus4linux_proc_dir);
			}
			pPar->proc_dir = NULL;
		}
  	}
}
/***************************************************************************************/
int canbus4linux_register_device(char *name, int version, void *pSpecificPar, struct canbus_access *access, int prefered_min, int prefered_max)
{
	int t;
	if (prefered_min < 0) prefered_min = 0;
	if (prefered_max < 0) prefered_max = NUM_CANBUS_DEVICES;
	TRACE("registering can device: %s",name);
	for(t=prefered_min;(tpRegisterIsr) 
				(access->pRegisterIsr)(pSpecificPar, canbus4linux_interrupt, &canbus4linux_admin[t]);

			canbus4linux_admin[t].driver_registered = 1;
			canbus4linux_get_properties(&canbus4linux_admin[t], &canbus4linux_admin[t].props);
			if(canbus4linux_admin[t].props.number_registers)
			{
				int n;
	
				canbus4linux_admin[t].proc_regs = Allocate_Memory(sizeof(struct proc_registers)*canbus4linux_admin[t].props.number_registers);
				for(n=0;n= 0)
				{
					struct canbus_event ev;
					TRACE("Initializing can device -2-");
					if(!canbus4linux_test_device(&canbus4linux_admin[t]))
					{
						INFO_TRACE("Warning: no device detected");
					}
					canbus4linux_get_properties(&canbus4linux_admin[t], &canbus4linux_admin[t].props);
					canbus4linux_set_baudrate(&canbus4linux_admin[t], canbus4linux_admin[t].iBaudrate);
					canbus4linux_set_acceptance_filter(&canbus4linux_admin[t], &canbus4linux_admin[t].acceptance_filter);
					canbus4linux_set_can_mode(&canbus4linux_admin[t], canbus4linux_admin[t].bCan_2B);
					canbus4linux_set_default_frame_format(&canbus4linux_admin[t], canbus4linux_admin[t].bFrameFmt);
					if(canbus4linux_admin[t].standby)
						canbus4linux_set_command(&canbus4linux_admin[t], CANBUS_CMD_ENTER_STANDBY);

					ev.event = CANBUS_EVENT_DEVICE_CHANGED;
					canbus4linux_interrupt(canbus4linux_admin[t].pDeviceParm, &canbus4linux_admin[t], &ev);
				}

			}

			TRACE("canbus4linux_register_device() - finished");
			return t;
		}
	}
	return -1;
}

/***************************************************************************************/
int canbus4linux_unregister_device(int can_num)
{
	if (canbus4linux_admin[can_num].driver_registered != 0)
	{
		TRACE("unregistering can device: %s",canbus4linux_admin[can_num].cDeviceName);

		canbus4linux_proc_driver_dir(&canbus4linux_admin[can_num],can_num, 1);

		if(canbus4linux_admin[can_num].bOpen)
		{
			struct canbus_event ev;

			if(canbus4linux_admin[can_num].access.pClose)
				(*canbus4linux_admin[can_num].access.pClose)(canbus4linux_admin[can_num].pDeviceParm);

			ev.event = CANBUS_EVENT_DEVICE_CHANGED;
			canbus4linux_interrupt(canbus4linux_admin[can_num].pDeviceParm, &canbus4linux_admin[can_num], &ev);
			canbus4linux_admin[can_num].access.pTransmitData = 0;
		}

		if (canbus4linux_admin[can_num].access.pUnregisterIsr)
			(canbus4linux_admin[can_num].access.pUnregisterIsr)(canbus4linux_admin[can_num].pDeviceParm);
		memset(&canbus4linux_admin[can_num].access, 0,sizeof(struct canbus_access));
		canbus4linux_admin[can_num].transmitting = 0;
		canbus4linux_admin[can_num].lost = 0;
		canbus4linux_admin[can_num].cDeviceName[0] = 0;
		canbus4linux_admin[can_num].pDeviceParm = NULL;
		if(canbus4linux_admin[can_num].proc_regs)
			Free_Memory(canbus4linux_admin[can_num].proc_regs);
		canbus4linux_admin[can_num].proc_regs = NULL;
		canbus4linux_admin[can_num].driver_registered = 0;
	}
	return 0;
}

/***************************************************************************************/


/***************************************************************************************/
void *Allocate_Memory(unsigned long s)
{
	unsigned char *x;
	unsigned long t;
	x = kmalloc(s,GFP_KERNEL);
	if (x)
	{
		for(t=0;tadm;

	if(!a->cf_first)
	{
		TRACE("SetCanbusFileEntry -1-");
		a->cf_first = f;
	}
	else
	{
		TRACE("SetCanbusFileEntry -2-");
		n = a->cf_first;
		while(n->next)
			n = n->next;
		n->next = f;
	}
	f->next = NULL;
}
/***************************************************************************************/
void DeleteCanbusFileEntry(struct canbus_file *f)
{
	struct canbus_file *n;
	struct canbus_admin *a = f->adm;

	if(f == a->cf_first)
	{
		TRACE("DeleteCanbusFileEntry -1-");
		a->cf_first = f->next;
	}
	else
	{
		TRACE("DeleteCanbusFileEntry -2-");
		n = a->cf_first;
		while(n->next)
		{
			if(f == n->next)
			{
				TRACE("DeleteCanbusFileEntry -3-");
				n->next = n->next->next;
				break;
			}
			n = n->next;
		}
	}
	f->next = NULL;
}
/***************************************************************************************/
int canbus4linux_open(struct inode * inode, struct file * file)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	unsigned int minor = MINOR(inode->i_rdev);
#else
    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,67)
	unsigned int minor = MINOR(inode->i_rdev.value);
    #else
	unsigned int minor = MINOR(inode->i_rdev);
    #endif
#endif
	struct canbus_file *cf=NULL;

	if(!file)
	{
		INFO_TRACE("no file structure !!!");
		return -ENODEV;
	}

	if(minor >= NUM_CANBUS_DEVICES)
	{
		INFO_TRACE("minor number exceeds internal list");
		return -ENODEV;
	}
	cf = (struct canbus_file *)Allocate_Memory(sizeof(struct canbus_file));
	file->private_data = cf;
	if(!cf)
		goto error;

	cf->adm = &canbus4linux_admin[minor];
	cf->sleeping_send_wait = 0;
	cf->sleeping_send_read = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
	init_waitqueue_head(&cf->sended);
	init_waitqueue_head(&cf->send_wait);
	init_waitqueue_head(&cf->send_read);
#else
	cf->sended = NULL;
	cf->send_wait = NULL;
	cf->send_read = NULL;
#endif
	cf->file = file;
	cf->send_counter = 0;
	cf->write_buffer_pos=0;
	cf->admIn=0;
	cf->admOut=0;
	cf->admISRInfo = (struct canbus_event *)Allocate_Memory(MAX_EVENTS*sizeof(struct canbus_event));
	if (!cf->admISRInfo)
		goto error;

	SetCanbusFileEntry(cf);
	
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,67)
	MOD_INC_USE_COUNT;
#endif
	TRACE("canbus4linux_open %d",minor);
	return canbus4linux_open_device(&canbus4linux_admin[minor]);
	
error:
	INFO_TRACE("out of memory while allocation buffer");

	if(cf)
	{
		Free_Memory(cf->admISRInfo);
	}
	Free_Memory(cf);

	return -ENOMEM;
}

/***************************************************************************************/
int canbus4linux_release(struct inode * inode, struct file * file)
{
	struct canbus_file *cf=NULL;

	if(file)
		cf = (struct canbus_file *)file->private_data;
		
	if(cf)
	{
		if (!(file->f_flags & O_NONBLOCK) && (cf->adm->sndIn != cf->adm->sndOut))
		{
			interruptible_sleep_on(&cf->sended);
		}

		canbus4linux_close_device(cf->adm);
		if(file->f_flags & FASYNC)
			canbus4linux_fasync(-1,file,0);

		DeleteCanbusFileEntry(cf);

		Free_Memory(cf->admISRInfo);
		Free_Memory(cf);
	}
	file->private_data = NULL;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,67)
	MOD_DEC_USE_COUNT;
#endif
	return 0;
}

/***************************************************************************************/
int canbus4linux_ioctl(struct inode *inode, struct file *file,
		    unsigned int cmd, unsigned long arg)
{
	int status=0;
	struct canbus_file *cf=NULL;
	struct canbus_acceptance_filter canbus_acceptance_filter;
	struct canbus_transmit_data canbus_transmit_data;
	struct canbus_event canbus_event;
	struct canbus_time canbus_time;
	struct canbus_properties canbus_properties;


	if(file)
		cf = (struct canbus_file *)file->private_data;
		
	if(!cf)
	{
		TRACE("no canbus_file structure");
		return -ENODEV;
	}

	if(cf->adm->bOpen <= 0)
	{
		INFO_TRACE("ioctl - device not open");
		return -ENODEV;
	}
		
	status = -EINVAL;
	TRACE("canbus4linux_ioctl cmd:%x %x",(int)cmd,(int)CANBUS4LINUX_WRITE_TRANSMIT_DATA);
	switch(cmd)
	{
	case CANBUS4LINUX_WRITE_ACCEPTANCE_FILTER:
		TRACE("CANBUS4LINUX_WRITE_ACCEPTANCE_FILTER");
		if(verify_area(VERIFY_READ,(struct canbus_acceptance_filter *)arg,sizeof(struct canbus_acceptance_filter)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		copy_from_user(&canbus_acceptance_filter,(struct canbus_acceptance_filter *)arg,sizeof(struct canbus_acceptance_filter));
		status = canbus4linux_set_acceptance_filter(cf->adm, &canbus_acceptance_filter);
		break;
	case CANBUS4LINUX_READ_ACCEPTANCE_FILTER:
		TRACE("CANBUS4LINUX_READ_ACCEPTANCE_FILTER");
		if(verify_area(VERIFY_WRITE,(struct canbus_acceptance_filter *)arg,sizeof(struct canbus_acceptance_filter)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		canbus_acceptance_filter = canbus4linux_get_acceptance_filter(cf->adm);
		copy_to_user((struct canbus_acceptance_filter *)arg,&canbus_acceptance_filter,sizeof(struct canbus_acceptance_filter));
		status = 0; // = ok
		break;
	case CANBUS4LINUX_SET_BAUDRATE:
		TRACE("CANBUS4LINUX_SET_BAUDRATE");
		status = canbus4linux_set_baudrate(cf->adm, arg);
		break;
	case CANBUS4LINUX_GET_BAUDRATE:
		TRACE("CANBUS4LINUX_GET_BAUDRATE");
		status = canbus4linux_get_baudrate(cf->adm);
		break;
	case CANBUS4LINUX_INIT:
		TRACE("CANBUS4LINUX_INIT");
		status =  canbus4linux_init_device(cf->adm);
		break;
	case CANBUS4LINUX_SET_BAUDRATE_BY_CONSTANT:
		TRACE("CANBUS4LINUX_SET_BAUDRATE_BY_CONSTANT");
		status = canbus4linux_set_baudrate_by_constant(cf->adm, arg);
		break;
	case CANBUS4LINUX_GET_BAUDRATE_BY_CONSTANT:
		TRACE("CANBUS4LINUX_GET_BAUDRATE_BY_CONSTANT");
		status = canbus4linux_get_baudrate_by_constant(cf->adm);
		break;
	case CANBUS4LINUX_SET_CAN_MODE:
		TRACE("CANBUS4LINUX_SET_CAN_MODE");
		status = canbus4linux_set_can_mode(cf->adm,arg);
		break;
	case CANBUS4LINUX_SET_COMMAND:
		TRACE("CANBUS4LINUX_SET_COMMAND");
		status = canbus4linux_set_command(cf->adm,arg);
		break;
	case CANBUS4LINUX_WRITE_TRANSMIT_DATA:
		TRACE("CANBUS4LINUX_WRITE_TRANSMIT_DATA");
		if(verify_area(VERIFY_READ, (struct canbus_transmit_data *)arg,sizeof(struct canbus_transmit_data)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		copy_from_user(&canbus_transmit_data, (struct canbus_transmit_data *)arg,sizeof(struct canbus_transmit_data));
		status = canbus4linux_transmit_data(cf->adm, &canbus_transmit_data,file);
		break;
	case CANBUS4LINUX_READ_EVENT_DATA:
		TRACE("CANBUS4LINUX_READ_EVENT_DATA");
		if(verify_area(VERIFY_WRITE,(struct canbus_event *)arg,sizeof(struct canbus_event)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		status = canbus4linux_get_event(cf->adm,&canbus_event,cf);
		copy_to_user((struct canbus_event *)arg,&canbus_event,sizeof(struct canbus_event));
		break;
	case CANBUS4LINUX_READ_TIME:
		TRACE("CANBUS4LINUX_READ_TIME");
		if(verify_area(VERIFY_WRITE,(struct canbus_time *)arg,sizeof(struct canbus_time)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		canbus_time = GetTime();
		copy_to_user((struct canbus_time *)arg,&canbus_time,sizeof(struct canbus_time));
		status = 0;
		break;
	case CANBUS4LINUX_SET_DEFAULT_FRAME_FORMAT:
		TRACE("CANBUS4LINUX_SET_DEFAULT_FRAME_FORMAT");
		status = canbus4linux_set_default_frame_format(cf->adm, arg);
		status = 0;
		break;
	case CANBUS4LINUX_TEST_DEVICE:
		TRACE("CANBUS4LINUX_TEST_DEVICE");
		status = canbus4linux_test_device(cf->adm);
		break;
	case CANBUS4LINUX_READ_PROPERTIES:
		TRACE("CANBUS4LINUX_READ_PROPERTIES");
		if(verify_area(VERIFY_WRITE,(struct canbus_properties *)arg,sizeof(struct canbus_properties)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		status = canbus4linux_get_properties(cf->adm,&canbus_properties);
		copy_to_user((struct canbus_properties *)arg,&canbus_properties,sizeof(struct canbus_properties));
		break;
	case CANBUS4LINUX_WRITE_SOFT_ACCEPTANCE_FILTER:
		TRACE("CANBUS4LINUX_WRITE_SOFTWARE_ACCEPTANCE_FILTER");
		if(verify_area(VERIFY_READ,(struct canbus_acceptance_filter *)arg,sizeof(struct canbus_acceptance_filter)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		copy_from_user(&canbus_acceptance_filter,(struct canbus_acceptance_filter *)arg,sizeof(struct canbus_acceptance_filter));
		status = canbus4linux_set_soft_acceptance_filter(cf->adm, &canbus_acceptance_filter);
		break;
	case CANBUS4LINUX_READ_SOFT_ACCEPTANCE_FILTER:
		TRACE("CANBUS4LINUX_READ_SOFTWARE_ACCEPTANCE_FILTER");
		if(verify_area(VERIFY_WRITE,(struct canbus_acceptance_filter *)arg,sizeof(struct canbus_acceptance_filter)) < 0)
		{
			TRACE("error in verify_area");
			break;
		}
		canbus_acceptance_filter = canbus4linux_get_soft_acceptance_filter(cf->adm);
		copy_to_user((struct canbus_acceptance_filter *)arg,&canbus_acceptance_filter,sizeof(struct canbus_acceptance_filter));
		status = 0; // = ok
		break;
	}
	return status;
}

/***************************************************************************************/
int canbus4linux_fasync(int fd, struct file *file, int mode)
{
	struct canbus_file *cf=NULL;

	if(file)
		cf = (struct canbus_file *)file->private_data;
		
	if(!cf)
	{
		TRACE("no canbus_file structure");
		return -ENODEV;
	}
					
	TRACE("canbus4linux_fasync %d %p %d",fd,file,mode);
	return fasync_helper(fd,file,mode,&cf->adm->async_queue);
}

/***************************************************************************************/
unsigned int canbus4linux_poll(struct file *file, poll_table *wait)
{
	struct canbus_file *cf = (struct canbus_file *)file->private_data;
	unsigned int mask=0;

	poll_wait(file,&cf->send_wait,wait);
	poll_wait(file,&cf->send_read,wait);

	if (cf->admIn != cf->admOut) mask |= POLLIN | POLLRDNORM;
	if (cf->adm->sndIn != cf->adm->sndOut) mask |= POLLOUT | POLLWRNORM;
	
	return mask;
}

/***************************************************************************************/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
int canbus4linux_fsync(struct file *file, struct dentry *dt, int datasync)
#else
int canbus4linux_fsync(struct file *file, struct dentry *dt)
#endif
{
	struct canbus_file *cf = (struct canbus_file *)file->private_data;

	if (!(file->f_flags & O_NONBLOCK) && (cf->adm->sndIn != cf->adm->sndOut))
	{
		interruptible_sleep_on(&cf->sended);
	}
	
	return 0;
}

/***************************************************************************************/
ssize_t canbus4linux_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
	int x, y=-ENODEV;
	struct canbus_file *cf = (struct canbus_file *)file->private_data;

	if(!cf)
		return -ENODEV;
		
	if (cf->write_buffer_pos >= NUMBER_OF_WRITE_BUFFER)
	{
		cf->write_buffer_pos=0; // internal error
		return -ENOMEM;
	}
	for(x=0;xwrite_buffer[cf->write_buffer_pos] = buf[x];
		if ((cf->write_buffer_pos > 1) && ((cf->write_buffer[cf->write_buffer_pos] == '\n')  ||  (cf->write_buffer[cf->write_buffer_pos] == '\r')) )
		{
			cf->write_buffer[cf->write_buffer_pos] = 0;
			if(canbus4linux_open_device(cf->adm) >= 0)
			{
				y = canbus4linux_transmit_from_char(cf->write_buffer, cf->write_buffer_pos, cf->adm, file);
				canbus4linux_close_device(cf->adm);
			}
			cf->write_buffer_pos = 0;
			if(y < 0)
				return y;
			y = x+1;
		}
		else
		{
			cf->write_buffer_pos++;
			if (cf->write_buffer_pos >= sizeof(cf->write_buffer))
			{
				cf->write_buffer_pos = 0;
				INFO_TRACE("error buffer overflow / writing wrong data");
				return -ENOMEM;
			}
		}
	}
	return count;
}

/***************************************************************************************/
ssize_t canbus4linux_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
	int x,status;
	char buffer[100];
	struct canbus_file *cf = (struct canbus_file *)file->private_data;
	struct canbus_event ev;

	if(!cf || (count < 20))
		return 0;

	buf[0] = 0;

	x=2;
	while(x)
	{
		status = canbus4linux_get_event(cf->adm,&ev,cf);
		if(status)
			break;

		if (!(file->f_flags & O_NONBLOCK))
		{
			TRACE("canbus4linux_read -> sleep");
			cf->sleeping_send_read=1;
			interruptible_sleep_on(&cf->send_read);
		}
		else
			return -EAGAIN;
		x--;
	}
	switch(ev.event)
	{
	case CANBUS_EVENT_RECEIVED:
		sprintf(buffer,"received\t%lu:%lu\t%08lx\t%d\t%d",ev.time.high,ev.time.low,ev.data.identifier,ev.data.rtr,ev.data.dlc);
		for(x=0;(x<8) && (x= KERNEL_VERSION(2,4,0)
	THIS_MODULE,
#endif
	read:canbus4linux_read,      // read
	write:canbus4linux_write,     // write
	poll:canbus4linux_poll,      // poll
	ioctl:canbus4linux_ioctl,     // ioctl
	open:canbus4linux_open,      // open
	release:canbus4linux_release,   // release
	fsync:canbus4linux_fsync,     // fsync
	fasync:canbus4linux_fasync,    // fasync
};

/***************************************************************************************/
int init_module(void)
{
	int result;

	INFO_TRACE("Version %s / %s / %s",version,__DATE__,__TIME__);
    result = register_chrdev(major,"canbus",&canbus4linux_fops);
    if (result < 0)
    {
    	INFO_TRACE("Can't get MAJOR Number: %d",major);
    	return result;
    }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,18)
	canbus4linux_proc_dir = proc_mkdir(CANBUS4LINUX_PROC_DIR,&proc_root);
#else
	canbus4linux_proc_dir = create_proc_entry(CANBUS4LINUX_PROC_DIR,S_IFDIR,&proc_root);
#endif
	if(canbus4linux_proc_dir)
	{
		struct proc_dir_entry *res;

    	res = create_proc_entry(CANBUS4LINUX_PROC_DIR"/devices",0,0); 
    	if(res)
    	{
    		res->read_proc = canbus4linux_read_number_drv_proc;
    		res->data = NULL;
    	}
    }
  	return 0;
}

/***************************************************************************************/
void cleanup_module(void)
{
	int t;
  	TRACE("cleanup_module");
	for(t=0;t");
MODULE_DESCRIPTION("canbus4linux main driver");