Example #1
0
int CAN_VendorInit (int minor)
{
    DBGin("CAN_VendorInit");
/* 1. Vendor specific part ------------------------------------------------ */
    can_range[minor] = 0x180;

/* End: 1. Vendor specific part ------------------------------------------- */

    /* Request the controllers address space */

    /* looks like not needed in uClinux with internal ressources ? */
    /* It's Memory I/O */
    if(NULL == request_mem_region(Base[minor], can_range[minor], "CAN-IO")) {
	return -EBUSY;
    }

    /* not necessary in uClinux, but ... */
    can_base[minor] = ioremap(Base[minor], can_range[minor]);
    /* now the virtual address can be used for the register address macros */

/* 2. Vendor specific part ------------------------------------------------ */



/* End: 2. Vendor specific part ------------------------------------------- */

    if( IRQ[minor] > 0 ) {
        if( Can_RequestIrq( minor, IRQ[minor] , CAN_Interrupt) ) {
	     printk("Can[%d]: Can't request IRQ %d \n", minor, IRQ[minor]);
	     DBGout(); return -EBUSY;
        }
    }
    DBGout(); return 1;

}
Example #2
0
int CAN_StartChip (int board)
{
/* int i; */
    RxErr[board] = TxErr[board] = 0L;
    DBGin("CAN_StartChip");
    /* printk("CAN_StartChip\n"); */


    /* clear interrupts */
    CANinw(board, iflag);
    CANoutw(board, iflag, 0xffff);	/* 5282 overwrites with '1' */
    CANoutw(board, estat, 0); 		/* 5282 overwrites ESTAT with '0' */

    CANresetw(board, canmcr, CAN_MCR_HALT | CAN_MCR_FRZ);
    /* Interrupts on Rx, TX, any Status change and data overrun */
    /* Initialize the transmit and receive pin modes in control register 0 */
    CANset(board, canctrl0,
    	  CAN_CTRL0_ENABLE_BOFF_INT	/* enable Bus-Off Interrupt	*/
    	+ CAN_CTRL0_ENABLE_ERR_INT	/* enable Error Interrupt	*/
    	+ 0				/*  4 , logic RX level		*/
    	+ 0				/*  1 , logic TX level		*/
    	);

   CANoutw(board, imask, 
               (1 << RECEIVE_STD_OBJ)
              +(1 << RECEIVE_EXT_OBJ)
              +(1 << TRANSMIT_OBJ));

    /* to do */
    /* CAN_register_dump(); */

    DBGout();
    return 0;
}
Example #3
0
int Can_FilterSignal(int minor, unsigned id, unsigned signal) {
    DBGin("Can_FilterSignal");
       if( signal <= 3 )
       Rx_Filter[minor].filter[id].signal = signal;
    DBGout();
    return 0;
}
Example #4
0
int Can_FreeIrq(int minor, int irq )
{
    DBGin("Can_FreeIrq");
    IRQ_requested[minor] = 0;

#if defined(MCF5282)
    /* reset interrupt masks */
    mcf_irqreset();
#endif
#if defined(MCF5282)
    {
    int i;
    	/* 19 Int vectors are used on Interrupt Controller 1 */
	for(i = 136; i < 155; i++) {
	    free_irq(i, &Can_minors[minor]);
	}
    }
#else 
    /* release the vector from the kernel */
    free_irq(irq, &Can_minors[minor]);
#endif

    DBGout();
    return 0;
}
Example #5
0
int Can_FilterSigNo(int minor, unsigned signo, unsigned signal ) {
    DBGin("Can_FilterSigNo");
       if( signal < 3 )
       Rx_Filter[minor].signo[signal] = signo;
    DBGout();
    return 0;
}
Example #6
0
/* void cleanup_module(void) */
void can_exit(void)
{
    DBGin("cleanup_module");
#ifndef MODULE
    if (MOD_IN_USE) {
      printk("Can : device busy, remove delayed\n");
    }
    /* printk("CAN: removed successfully \n"); */
#endif
	

#if LDDK_USE_BLKREQUEST
    blk_dev[Can_major].request_fn = NULL ;
#endif
#ifdef LDDK_USE_REGISTER
    if( unregister_chrdev(Can_major, "Can") != 0 ){
        printk("can't unregister Can, device busy \n");
    } else {
        printk("Can successfully removed\n");
    }

#endif
#if LDDK_USE_PROCINFO
    unregister_procinfo();
#endif
#if LDDK_USE_SYSCTL
    unregister_systables();
#endif
    DBGout();
}
Example #7
0
int controller_available(unsigned long address, int offset)
{
unsigned long ptr = (unsigned long)ioremap(address, 32 * offset);

    DBGin("controller_available");
    /* printk("controller_available 0x%lx\n", address); */


    /* printk("0x%0x, ", readb(ptr + (0 * offset)) ); */
    /* printk("0x%0x, ", readb(ptr + (2 * offset)) ); */
    /* printk("0x%0x\n", readb(ptr + (3 * offset)) ); */

    if ( 0x21 == readb((void __iomem *)ptr))  {
	/* compare rest values of status and interrupt register */
	if(   0x0c == readb((void __iomem *)ptr + (2 * offset))
	   && 0xe0 == readb((void __iomem *)ptr + (3 * offset)) ) {
	    return 1;
	} else {
	    return 0;
	}
    } else {
	/* may be called after a 'soft reset' in 'PeliCAN' mode */
	/*   value     address                     mask    */
	if(   0x00 ==  readb((void __iomem *)ptr + (1 * offset))
	   && 0x34 == (readb((void __iomem *)ptr + (2 * offset))    & 0x37)
	   && 0x00 == (readb((void __iomem *)ptr + (3 * offset))    & 0xfb)
	  ) {
	    return 1;
	} else {
	    return 0;
	}

    }
}
Example #8
0
/*
 * PCM3680  Remarks
 *
 * Adresses used related to the Basde adress (set by dip switches) 
 * Base address (hex)      CAN controller
 * base:0000h - base:00FFh Basic- Port 1
 * base:0100h - base:01FFh HW reset Basic - Port 1
 * base:0200h - base:02FFh Basic- Port 2
 * base:0300h - base:03FFh HW reset Basic - Port 2
 * base:0400h - base:0FFFh Not used
 * 
 * Each CAN channel uses 0x200 bytes
 */
int CAN_VendorInit (int minor)
{
    DBGin("CAN_VendorInit");
    can_range[minor] = 0x200;
    

    /* Some LINUX systems, e.g. the PIP10 I tested on,
     * locate already the memory using the information
     * provided in the "Option ROM"
     * The memory is marked as "Adapter-ROM" in /proc/iomem.
     * In this case the drive should not try to allocate the IO mem */

#if !defined(PC104_OPTION_ROM)
    /* Request the controllers address space */
    if(NULL == request_mem_region(Base[minor], can_range[minor], "CAN-IO")) {
	DBGprint(DBG_DATA,("Request_mem-Region CAN-IO failed at 0x%x\n",
		Base[minor]));
	return -EBUSY;
    }
#endif

    controller_available(Base[minor], 1);

    can_base[minor] = ioremap(Base[minor], can_range[minor]);
    /* now the virtual address can be used for the register access macros */


    if( Base[minor] & 0x200 ) {
	    /* printk("Resetting Advantech Pcm-3680 [contr 1]\n"); */
	    /* perform HW reset 2. contr*/
	    writeb(0xff, can_base[minor] + 0x300);
    } else {
	    /* printk("Resetting Advantech Pcm-3680 [contr 0]\n"); */
	    /* perform HW reset 1. contr*/
	    writeb(0xff, can_base[minor] + 0x100);
    }
    mdelay(100);



    if( IRQ[minor] > 0 || IRQ[minor] > MAX_IRQNUMBER ){
        int err;
	err = request_irq( IRQ[minor], CAN_Interrupt, SA_SHIRQ, 
				"Can", &Can_minors[minor]);
        if( !err ){
	    DBGprint(DBG_BRANCH,("Requested IRQ: %d @ 0x%lx",
				    IRQ[minor], (unsigned long)CAN_Interrupt));
	    IRQ_requested[minor] = 1;
	} else {
	    release_mem_region(Base[minor], can_range[minor]);
	    DBGout(); return -EBUSY;
	}
    } else {
	/* Invalid IRQ number in /proc/.../IRQ */
	release_mem_region(Base[minor], can_range[minor]);
	DBGout(); return -EBUSY;
    }
    DBGout(); return 0;
}
Example #9
0
int Can_FreeIrq(int minor, int irq )
{
    DBGin();
    IRQ_requested[minor] = 0;
    free_irq(irq, &Can_minors[minor]);
    DBGout();
    return 0;
}
Example #10
0
/*
Listen-Only Mode
In listen-only mode, the CAN module is able to receive messages
without giving an acknowledgment.
Since the module does not influence the CAN bus in this mode
the host device is capable of functioning like a monitor
or for automatic bit-rate detection.

*/
int CAN_SetListenOnlyMode (int board, int arg)
{
    DBGin("CAN_SetListenOnlyMode");
    /* has to be filled for the FlexCAN */
    /* ================================ */
    DBGout();
    return 0;
}
Example #11
0
/* writing to the control range at BAR1 of the PCI board */
void reset_CPC_PCI(unsigned long address)
{
unsigned long ptr = (unsigned long)ioremap(address, 32);
    DBGin("reset_CPC_PCI");
    /* printk("reset_CPC_PCI\n"); */
    writeb(0x01, ptr);
    writeb(0x00, ptr);
}
Example #12
0
/* change the bit timings of the selected CAN channel */
int CAN_SetTiming (int board, int baud)
{
/* int custom=0; */
BTR_TAB_TOUCAN_T * table = (BTR_TAB_TOUCAN_T*)can_btr_tab_toucan;

    DBGin("CAN_SetTiming");
    DBGprint(DBG_DATA, ("baud[%d]=%d", board, baud));
    /* enable changing of bit timings */
    CANsetw(board, canmcr, CAN_MCR_HALT);
    /* search for data from table */
    while(1) {
        if (table->rate == 0 || table->rate == baud) {
    	    break;
    	}
    	table++;
    }
    if (table->rate == 0) {
	/* try to use baud  as custom specific bit rate
	 * not implemented yet
	 */
	return -ENXIO;
    }

    /*
     * Set Timing Register values.
     * Initialize the bit timing parameters PROPSEG, PSEG1, PSEG2 and RJW
     * in control registers 1 and 2.
     *
     * The FlexCAN module uses three 8-bit registers to set-up
     * the bit timing parameters required by the CAN protocol.
     * Control registers 1 and 2 contain the PROPSEG, PSEG1, PSEG2
     * and RJW fields which allow the user to configure
     * the bit timing parameters.
     * The prescaler divide register (PRESDIV) allows the user to select
     * the ratio used to derive the S-Clock from the system clock.
     * The time quanta clock operates at the S-clock frequency.
     */
    CANout(board, presdiv, table->presdiv);
    CANout(board, canctrl2, ((table->pseg1 << 3)+ table->pseg2));
    CANout(board, canctrl1, (
    	  0		/* SAMP = 0 , 0/0x80		*/	
    	+ 0		/* LOOP = 0 , 0/0x40		*/	
    	+ 0		/* TSYNC= 0 , 0/0x20		*/
    	+ 0		/* LBUF = 0 , 0/0x10		*/
    	+ 0		/* RSVD = 0 , 0/0x08		*/
    	+ table->propseg)
    	);

    /*
     * Stay in configuration mode; a call to Start-CAN() is necessary to
     * activate the CAN controller with the new bit rate
     */
    DBGprint(DBG_DATA,("canctrl2=0x%x presdiv=0x%x",
    		CANin(board, canctrl2), CANin(board, presdiv)) );

    DBGout();
    return 0;
}
Example #13
0
int Can_RequestIrq(int minor, int irq,
    irqreturn_t (*handler)(int, void *, struct pt_regs *))
{
int err=0;

    DBGin("Can_RequestIrq");
    /*

    int request_irq(unsigned int irq,			// interrupt number  
              void (*handler)(int, void *, struct pt_regs *), // pointer to ISR
		              irq, dev_id, registers on stack
              unsigned long irqflags, const char *devname,
              void *dev_id);

       dev_id - The device ID of this handler (see below).       
       This parameter is usually set to NULL,
       but should be non-null if you wish to do  IRQ  sharing.
       This  doesn't  matter when hooking the
       interrupt, but is required so  that,  when  free_irq()  is
       called,  the  correct driver is unhooked.  Since this is a
       void *, it can point to anything (such  as  a  device-spe­
       cific  structure,  or even empty space), but make sure you
       pass the same pointer to free_irq().

    */

    {
	int i;
	/* 19 Int vectors are used on Interrupt Controller 1 */
	for( i = 136; i < 155; i++) {
	    err = request_irq( i, handler, SA_INTERRUPT, "Can", &Can_minors[minor]);
	    if(err) {
    		DBGout();return err;
	    }
	}
    }
    if( !err ){
	/* printk("Requested IRQ[%d]: %d @ 0x%x", minor, irq, handler); */

/* Now the kernel has assigned a service to the Interruptvector,
   time to enable the hardware to generate an ISR.

   here should be used a generic function e.g. can_irqsetup(minor)
   and do whatever needed for the app. hardware
   to reduce #ifdef clutter
   */
	mcf_irqsetup();

	/* irq2minormap[irq] = minor; */

	/* irq2pidmap[irq] = current->pid; */
	DBGprint(DBG_BRANCH,("Requested IRQ: %d @ 0x%lx",
				irq, (unsigned long)handler));
	IRQ_requested[minor] = 1;
    }
    DBGout();return err;
}
Example #14
0
/* set value of the output control register
 * The register is not available, nothing happens here 
 * besides printing some debug information
 */
int CAN_SetOMode (int board, int arg)
{

    DBGin("CAN_SetOMode");
	DBGprint(DBG_DATA,("[%d] outc=0x%x", board, arg));

    DBGout();
    return 0;
}
Example #15
0
/* look if one of the receive message objects has something received */
int CAN_GetMessage (int board, canmsg_t *rx )
{
volatile unsigned int stat;
volatile unsigned int ctrl;
int i = 0;

    DBGin("CAN_GetMessage");
    stat = CANinw(board, estat);
    DBGprint(DBG_DATA,("0x%x: stat=0x%x iflag=0x%x imask=0x%x" ,
    			Base[board], stat,
    			CANinw(board, iflag),
    			CANinw(board, imask)));

    rx->flags  = 0;
    rx->length = 0;

    /* CAN_register_dump(); */
    /* CAN_object_dump(RECEIVE_STD_OBJ); */
    i = CANinw(board, iflag);
    if( i & (1 << RECEIVE_STD_OBJ)) {
	/* reading the control status word of the identified message
	 * buffer triggers a lock for that buffer.
	 * A new received message frame which maches the message buffer
	 * can not be written into this buffer while it is locked
	 */
        while (((ctrl = CAN_READ_CTRL(RECEIVE_STD_OBJ)) & (REC_CODE_BUSY << 4)) == (REC_CODE_BUSY << 4)) {
	    /* 20 cycles maximum wait */
	    /* printf1("CAN_int, rx REC_CODE_BUSY"); */
	}
	/* printk("message received 0x%x\n", ctrl); */
	rx->length = ctrl & 0x0f;
	rx->id =  CAN_READ_OID(RECEIVE_STD_OBJ);
	memcpy((void *)&rx->data[0],
	       (const void *)&(CAN_OBJ[RECEIVE_STD_OBJ].MSG0), 8);

	/* clear interrupts */
	/* Int is cleared when the CPU reads the intrerupt flag register iflag
	 * while the associated bit is set, and then writes it back as '1'
	 * (and no new event of the same type occurs
	 * between the read and write action.)
	 * ! This is opposit to the TouCAN module, where the iflag bit
	 * has to be written back with '0'
	 */
	CANsetw(board, iflag, (1 << RECEIVE_STD_OBJ));
	/* Reset message object */
	CAN_WRITE_CTRL(RECEIVE_STD_OBJ, REC_CODE_EMPTY, 8);
	/* reading the free running timer will unlock any message buffers */
	(void) CANinw(board, timer);
	i = 1; /* signal one received message */
    } else {
	i = 0;
    }

    DBGout();
    return i;
}
Example #16
0
/*
 * Configures bit timing registers directly. Chip must be in bus off state.
 */
int CAN_SetBTR (int board, int btr0, int btr1)
{
    DBGin("CAN_SetBTR");
    DBGprint(DBG_DATA, ("[%d] btr0=%d, btr1=%d", board, btr0, btr1));


    /* ToDO for MCF FlexCAN */

    DBGout();
    return 0;
}
Example #17
0
/* FlexCAN only knows a 'mask' value, code is ignored */
int CAN_SetMask (int board, unsigned int code, unsigned int mask)
{

    DBGin("CAN_SetMask");
    DBGprint(DBG_DATA,("[%d] acc=0x%x mask=0x%x",  board, code, mask));
    CANoutw(board, rxgmskhi, mask >> 16);
    CANoutw(board, rxgmsklo, mask && 16);

    DBGout();
    return 0;
}
Example #18
0
int Can_UnConfigRTR( int minor, unsigned message )
{
canmsg_t *tmp;

    DBGin("Can_UnConfigRTR");
    if( Rx_Filter[minor].filter[message].rtr_response != NULL ) {
	    kfree(Rx_Filter[minor].filter[message].rtr_response);
	    Rx_Filter[minor].filter[message].rtr_response = NULL;
    }	
    DBGout(); return 1;
    return 0;
}
Example #19
0
int Can_FilterCleanup(int minor)
{
int i;

    DBGin("Can_FilterCleanup");
    for(i=0;i<MAX_ID_NUMBER;i++) {
	    if( Rx_Filter[minor].filter[i].rtr_response != NULL )	
	       kfree( Rx_Filter[minor].filter[i].rtr_response);
	    Rx_Filter[minor].filter[i].rtr_response = NULL;
    }
    DBGout();
    return 0;
}
Example #20
0
int Can_ConfigRTR( int minor, unsigned message, canmsg_t *Tx )
{
canmsg_t *tmp;

    DBGin("Can_ConfigRTR");
    if( (tmp = kmalloc ( sizeof(canmsg_t), GFP_ATOMIC )) == NULL ){
	    DBGprint(DBG_BRANCH,("memory problem"));
	    DBGout(); return -1;
    }
    Rx_Filter[minor].filter[message].rtr_response = tmp;
    memcpy( Rx_Filter[minor].filter[message].rtr_response , Tx, sizeof(canmsg_t));	
    DBGout(); return 1;
    return 0;
}
Example #21
0
/* Disable all CAN activities */
int CAN_StopChip (int board)
{
    DBGin("CAN_StopChip");
    /* printk("CAN_StopChip\n"); */
    CANset(board, canmcr, CAN_MCR_HALT);
    /* disable all error interrupts */
    CANreset(board, canctrl0,
    	  CAN_CTRL0_ENABLE_BOFF_INT	/* disable Bus-Off Interrupt	*/
    	+ CAN_CTRL0_ENABLE_ERR_INT	/* disable Error Interrupt	*/
    	);
    /* disable all MB interrupts */
    CANoutw(board, imask, 0);
    DBGout();
    return 0;
}
Example #22
0
/*
initialize RX and TX Fifo's
*/
int Can_FifoInit(int minor)
{
int i;

    DBGin("Can_FifoInit");
       Tx_Buf[minor].head   = Rx_Buf[minor].head = 0;
       Tx_Buf[minor].tail   = Rx_Buf[minor].tail = 0;
       Tx_Buf[minor].status = Rx_Buf[minor].status = 0;
       Tx_Buf[minor].active = Rx_Buf[minor].active = 0;
       for(i = 0; i < MAX_BUFSIZE; i++) {
	   Tx_Buf[minor].free[i]  = BUF_EMPTY;
       }
    DBGout();
    return 0;
}
Example #23
0
int Can_WaitInit(int minor)
{
    DBGin("Can_WaitInit");
	/* reset the wait_queue pointer */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
/* soll so sein */
	init_waitqueue_head(&CanWait[minor]);
/* das geht */
	/* CanWait[minor] = NULL; */
#else
	CanWait[minor] = NULL;
#endif
    DBGout();
    return 0;
}
Example #24
0
int Can_FilterInit(int minor)
{
int i;

    DBGin("Can_FilterInit");
       Rx_Filter[minor].use      = 0;
       Rx_Filter[minor].signo[0] = 0;
       Rx_Filter[minor].signo[1] = 0;
       Rx_Filter[minor].signo[2] = 0;

       for(i=0;i<MAX_ID_NUMBER;i++)	
	  Rx_Filter[minor].filter[i].rtr_response = NULL;

    DBGout();
    return 0;
}
Example #25
0
int Can_FreeIrq(int minor, int irq )
{
    DBGin("Can_FreeIrq");
    IRQ_requested[minor] = 0;

    /* reset interrupt masks */
    mcf_irqreset();
    {
    int i;
    	/* 19 Int vectors are used on Interrupt Controller 1 */
	for(i = 136; i < 155; i++) {
	    free_irq(i, &Can_minors[minor]);
	}
    }
    DBGout();
    return 0;
}
Example #26
0
/* void cleanup_module(void) */
static void __exit can_exit(void)
{
    DBGin("cleanup_module");

#if LDDK_USE_BLKREQUEST
    blk_dev[Can_major].request_fn = NULL ;
#endif
#if LDDK_USE_REGISTER
    unregister_chrdev(Can_major, "can");
#endif
#if LDDK_USE_PROCINFO
    unregister_procinfo();
#endif
#if LDDK_USE_SYSCTL
    unregister_systables();
#endif
    DBGout();
}
Example #27
0
/* check memory region if there is a CAN controller
*  assume the controller was resetted before testing 
*/
int controller_available(unsigned long address, int offset)
{
unsigned long ptr = (unsigned long)ioremap(address, 32 * offset);

    DBGin("controller_available");
    /* printk("controller_available %ld\n", address); */


    /* printk("0x%0x, ", readb(ptr + (2 * offset)) ); */
    /* printk("0x%0x\n", readb(ptr + (3 * offset)) ); */

    if(   0x0c == readb(ptr + (2 * offset))
       && 0xe0 == readb(ptr + (3 * offset)) ) {
	return 1;
    } else {
	return 0;
    }
}
Example #28
0
/* Release IRQ and IO ressources */
int CAN_Release(int minor)
{
    DBGin("CAN_Release()");

    /* call this before freeing any memory or io area.
     * this can contain registers needed by Can_FreeIrq()
     */
    Can_FreeIrq(minor, IRQ[minor]);


printk("iounmap %p \n", can_base[minor]);
    iounmap(can_base[minor]);

    /* release_mem_region(Base[minor], can_range[minor]); */
    /* Release the memory region */
printk("release mem %x \n", Base[minor]);
    release_mem_region(Base[minor], can_range[minor]);

    DBGout(); return 0;
}
Example #29
0
/* Release IRQ and IO ressources */
int CAN_Release(int minor)
{
    DBGin("CAN_Release()");

    /* call this before freeing any memory or io area.
     * this can contain registers needed by Can_FreeIrq()
     */
    Can_FreeIrq(minor, IRQ[minor]);


   if(Base[minor] < 0xD8000) {
	/* By definition a EMS CPC-104 */
	/* the real address range is starting 0x100 below 
	   see CAN_VendorInit() */
	can_base[minor] = can_base[minor] - 0x100; 
    }
printk("iounmap %p \n", can_base[minor]);
    iounmap(can_base[minor]);



    if(Base[minor] < 0xD8000) {
	/* By definition a EMS CPC-104 */
#       if !defined(PC104_OPTION_ROM)
	/* release_mem_region(Base[minor], can_range[minor]); */
	/* Release the memory region */
printk("release mem %x \n", Base[minor]);
	release_mem_region(Base[minor], can_range[minor]);
#       else
	;
#       endif
    } else {
    	/* PCM3680, always reles io mem */
	/* release_mem_region(Base[minor], can_range[minor]); */
	/* Release the memory region */
printk("release mem %x \n", Base[minor]);
	release_mem_region(Base[minor], can_range[minor]);
    }

    DBGout(); return 0;
}
Example #30
0
/**
*
* \brief int close(int fd);
* close a file descriptor
* \param fd The descriptor to close.
*
* \b close closes a file descriptor, so that it no longer
*  refers to any device and may be reused.
* \returns
* close returns zero on success, or -1 if an error occurred.
* \par ERRORS
*
* the following errors can occur
*
* \arg \c BADF \b fd isn't a valid open file descriptor 
*

*/
__LDDK_CLOSE_TYPE can_close ( __LDDK_CLOSE_PARAM )
{
    DBGin("can_close");
    {
	unsigned int minor = __LDDK_MINOR;

	CAN_StopChip(minor);

        /* since Vx.y (2.4?) macros defined in ioport.h,
           called is  __release_region()  */
#if defined(CAN_PORT_IO) 
	release_region(Base[minor], can_range[minor] );
#else
	release_region(Base[minor], can_range[minor] );
#endif

#ifdef CAN_USE_FILTER
	Can_FilterCleanup(minor);
#endif
	Can_FreeIrq(minor, IRQ[minor]);
#if !defined(CONFIG_PPC)
	/* printk("CAN module %d has been closed\n",minor); */
#endif

	if(Can_isopen[minor] > 0) {
	    --Can_isopen[minor];		/* flag device as free */
	    MOD_DEC_USE_COUNT;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
	    return 0;
#endif
	}
	
    }
    DBGout();
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
    return -EBADF;
#endif
}