public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* submission of CAN driver for i82527 chip
@ 2003-03-20 17:54 george cewe
  2003-03-20 18:08 ` Jeff Garzik
  0 siblings, 1 reply; 2+ messages in thread
From: george cewe @ 2003-03-20 17:54 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 477 bytes --]

Dear Kernel Maintainer;

Please find enclosed 2 files of a driver for Intel 82527 CAN Controller,
This driver is a simplified version of 'ocan' driver by Allesandro Rubini.

'jcan' is a character driver for kernel 2.4 and up, I inserted it in
linux_root/drivers/char directory.
I tested it on embedded MPC823 platform, but it should be running on other
platforms as well.

This is first time I submit a Linux driver and I would appreciate your
comments.

regards George Cewe.


[-- Attachment #2: jcan.h --]
[-- Type: text/plain, Size: 4681 bytes --]

/*
 * jcan.h -- header for the CAN driver
 *
 * Copyright (C) 2003 George Cewe <gcewe@copper.net>
 * Copyright (C) 2003 Advanced Concepts Inc.
 * Copyright (C) 2002 Alessandro Rubini <rubini@linux.it>
 * Copyright (C) 2001   Ascensit S.p.A <support@ascensit.com>
 * Author: Rodolfo Giometti <r.giometti@ascensit.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.
 */

#ifndef __JCAN_H__
#define __JCAN_H__

#include <linux/types.h>

#define CAN_READ_BUFS 64       /* read buffer has 64 messages  */


#define I527_XTD_XTD	      0x04
#define I527_XTD_STD	      0x00

/*
 * data structures used in both kernel and user space (mostly for ioctl)
 */
typedef enum
{  B_100K
  ,B_125K
  ,B_250K
  ,B_500K
  ,B_1M
  ,BPS_MAX
} BPS;

typedef struct 
{
  __u8 reg;
  __u8 val;
} CAN_REG;

#define CAN_MULTIREG_MAX 16
typedef struct  
{
  __u8 first, last;
  __u8 regs[CAN_MULTIREG_MAX];
} CAN_MULTIREG;

typedef struct
{
  __u8 data[256];
} CAN_ALLREGS;

typedef struct
{
  __u32 m_msg15;
  __u32 m_xtd;
  __u16 m_std;
} CAN_MASKS;

/* This structure is both part of the device structure and arg of ioctl */
typedef struct
{
  __u32 id;
  __u8  msgobj;   /* 1..15 */
  __u8  flags;
  __u8  config;   /* same as config register */
  __u8  dlc;      /* data length counter */
  __u8  data[8];
  __u16 error;    /* also for alignment */
} CAN_MSG;

/* And these are the message flags */
#define CAN_MSGFLAG_WRITE    0x01
#define CAN_MSGFLAG_READ     0x02 /* set either write read */
#define CAN_MSGFLAG_DOREMOTE 0x04 /* send a remote frame, for reading */
#define CAN_MSGFLAG_PEEKONLY 0x08 /* used by IOCPEEK */

#define CAN_ERROR_MASK	     0x07   /* Tx error code */
#define CAN_ERROR_MSGLST     0x10   /* Message lost error */
#define CAN_ERROR_OVRFLW     0x80   /* Read buffer overflow */


/*
 * ioctl() definitons
 */
 
/* Use 'z' as magic number */
#define CAN_IOC_MAGIC  'z' /* like other CAN drivers, per ioctl-number.txt */

#define CAN_IOCRESET            _IO(CAN_IOC_MAGIC,  0)

/* individual registers */
#define CAN_IOCREADREG        _IOWR(CAN_IOC_MAGIC,  1, CAN_REG)
#define CAN_IOCWRITEREG        _IOW(CAN_IOC_MAGIC,  2, CAN_REG)

/* range of registers */
#define CAN_IOCREADMULTI      _IOWR(CAN_IOC_MAGIC,  3, CAN_MULTIREG)
#define CAN_IOCWRITEMULTI      _IOW(CAN_IOC_MAGIC,  4, CAN_MULTIREG)

/* one message */
#define CAN_IOCSETUPMSG        _IOW(CAN_IOC_MAGIC,  5, CAN_MSG)
#define CAN_IOCWRITEMSG        _IOW(CAN_IOC_MAGIC,  6, CAN_MSG)
#define CAN_IOCTXMSG           _IOW(CAN_IOC_MAGIC,  7, unsigned long)
#define CAN_IOCRXMSG          _IOWR(CAN_IOC_MAGIC,  8, CAN_MSG)
#define CAN_IOCRELEASEMSG      _IOW(CAN_IOC_MAGIC,  9, unsigned long)

/* all registers */
#define CAN_IOCREADALL         _IOR(CAN_IOC_MAGIC, 10, CAN_ALLREGS)

/* masks, times, bus configuration */
#define CAN_IOCGETMASKS        _IOR(CAN_IOC_MAGIC, 11, CAN_MASKS)
#define CAN_IOCSETMASKS        _IOW(CAN_IOC_MAGIC, 12, CAN_MASKS)

#define CAN_IOCGETBPS          _IOR(CAN_IOC_MAGIC, 13, unsigned long)  
#define CAN_IOCSETBPS          _IOW(CAN_IOC_MAGIC, 14, unsigned long)

#define CAN_IOCGETBUSCONF      _IOR(CAN_IOC_MAGIC, 15, CAN_REG)
#define CAN_IOCSETBUSCONF      _IOW(CAN_IOC_MAGIC, 16, CAN_REG)

/* misc */
#define CAN_IOCSOFTRESET       _IO(CAN_IOC_MAGIC,  17)

#define CAN_IOCWRITEQ          _IOW(CAN_IOC_MAGIC, 18, CAN_MSG)
#define CAN_IOCGIRQCOUNT       _IOR(CAN_IOC_MAGIC, 19, unsigned long)

#define CAN_IOCREQSIGNAL       _IOW(CAN_IOC_MAGIC, 20, unsigned long)

/* I/O ports */
#define CAN_IOCINPUT          _IOWR(CAN_IOC_MAGIC, 21, CAN_REG)
#define CAN_IOCOUTPUT          _IOW(CAN_IOC_MAGIC, 22, CAN_REG)
#define CAN_IOCIOCFG           _IOW(CAN_IOC_MAGIC, 23, CAN_REG)

/* peek (like rx) */
#define CAN_IOCPEEKMSG        _IOWR(CAN_IOC_MAGIC, 24, CAN_MSG)

#define CAN_IOC_MAXNR                              24

#endif /* __JCAN_H__ */




[-- Attachment #3: jcan.c --]
[-- Type: text/plain, Size: 49540 bytes --]

/*
 * jcan.c -- CAN driver for i82527
 *
 * Copyright (C) 2003 George Cewe <gcewe@copper.net>
 * Copyright (C) 2003 Advanced Concepts Inc.
 * Copyright (C) 2002 Alessandro Rubini <rubini@linux.it>
 * Copyright (C) 2002 System SpA <info.electronics@system-group.it>
 *
 *   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.
 *
 *
 * This driver is a simplified version of "ocan" driver by Alessandro Rubini.
 * It supports one memory mapped Intel i82527 CAN chip, running under J1939
 * protocol (250kBps, extended message id).
 * 
 */

#define  __NO_VERSION__
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/interrupt.h>

#include <asm/irq.h>
#include <asm/io.h>
#ifdef __powerpc__
#include <asm-ppc/8xx_immap.h>
#endif
#include "jcan.h"

/*=============================================================================
 * Messaging stuff
 */
#undef PDEBUG        /* undef it, just in case */

#ifdef CAN_DEBUG
#ifdef __KERNEL__
/* This one if debugging is on, and kernel space */
#define PDEBUG(fmt, args...) printk(KERN_DEBUG CAN_TEXT fmt,##args)
#else
/* This one for user space */
#define PDEBUG(fmt, args...) fprintf(stdout, fmt, ##args)
#endif
#else
/* nothing */
#define PDEBUG(fmt, args...) /* nothing */
#endif /* CAN_DEBUG */

#ifdef CAN_DEBUG
#define debug 1
#else
#define debug 0
#endif

// base address and interrupt number
#define IO_ADDR  0xffff0000
#define IO_RANGE 0x100
#define IRQ_NUM  SIU_IRQ3

#ifndef CAN_MAJOR
#define CAN_MAJOR   91   
#endif

#define CAN_NAME "jcan"
#define CAN_TEXT "jcan: "

/*
 * Sysctl constants
 */
#define DEV_CAN  2658        /* random for /proc/sys/dev/jcan/ */

/* inside /proc/sys/dev/jcan, each backend can use 100 keys */

/*
 * minor allocation: each device has 1 general entry point, 15
 * message-buffer entry points and a few information/error files.
 * So it will need 5 bits. This accounts for up to eight devices
 */

#define CAN_DEV_TYPE(minor)     ((minor)&0x1f)
#define CAN_DEV_TYPE_GENERIC    0  /* 1..15 are the message objects */
#define CAN_DEV_TYPE_ERROR     16
#define CAN_DEV_TYPE_IO1       17
#define CAN_DEV_TYPE_IO2       18

#define CAN_DEV_NUM(minor)    ((minor)>>5) 

/* 
 * Device structures
 */

#define CAN_BUFSIZE   64   /* 64 pointers for msg packets */

// i82527 related definitions =============================================

#define I527_MSG_OBJS	16 /* 0 is the whole device, 1..15 are objects */
#define I527_FIRST_OBJ    1

/* 82527 address map (referred from base address) & internal bits.
   See the 82527 data-sheet page 9 and following */
#define I527_MAP_SIZE 	           0x100
#define I527_CTRL_REG		   0x00
#define I527_CCE		   0x40
#define I527_EIE		   0x08
#define I527_SIE		   0x04
#define I527_IE		           0x02
#define I527_INIT		   0x01

#define I527_STAT_REG		   0x01
#define I527_STAT_REG_DEFAULT      0x07

#define I527_CPU_INT_REG	   0x02
#define I527_RSTST		   0x80
#define I527_DSC		   0x40
#define I527_DMC		   0x20
#define I527_PWD		   0x10
#define I527_SLEEP		   0x08
#define I527_MUX		   0x04
#define I527_CEN		   0x01

/* Reserved                     0x03 */
#define I527_HI_SPEED_RD	0x04
#define I527_GMASK_STD		0x06
#define I527_GMASK_XTD		0x08
#define I527_MSG15_MASK		0x0c

/* Message 1			0x10 */
#define I527_MSG_OFF		0x10   /* No size definition here */
#define I527_MSG_CTRL	   0x00
#define I527_MSGVAL_R	  0xff7f   /* *********************** */
#define I527_MSGVAL_S	  0xffbf   /* WARNING!!!              */
#define I527_TXIE_R	  0xffdf   /* These masks must be     */
#define I527_TXIE_S	  0xffef   /* &-ed and *NOT* |-ed     */
#define I527_RXIE_R	  0xfff7   /*                         */
#define I527_RXIE_S	  0xfffb   /*                         */
#define I527_INTPND_R	  0xfffd   /*                         */
#define I527_INTPND_S	  0xfffe   /*                         */
#define I527_RMTPND_R	  0x7fff   /* WARNING!!!              */
#define I527_RMTPND_S	  0xbfff   /* These masks must be     */
#define I527_TXRQST_R	  0xdfff   /* &-ed and *NOT* |-ed     */
#define I527_TXRQST_S	  0xefff   /*                         */
#define I527_MSGLST_R	  0xf7ff   /*                         */
#define I527_MSGLST_S	  0xfbff   /*                         */
#define I527_CPUUPD_R	  0xf7ff   /* WARNING!!!              */
#define I527_CPUUPD_S	  0xfbff   /* These masks must be     */
#define I527_NEWDAT_R	  0xfdff   /* &-ed and *NOT* |-ed     */
#define I527_NEWDAT_S	  0xfeff   /* *********************** */
#define I527_MSG_ARBIT	  0x02
#define I527_MSG_CFG	   0x06
#define I527_DLC_MASK	   0xf0
#define I527_DLC_SHIFT	   4
#define I527_DLC_MASK0	   0x0f
#define I527_DIR		   0x08
#define I527_DIR_TX	      0x08
#define I527_DIR_RX	      0x00
#define I527_XTD		   0x04
#define I527_MSG_DATA	0x07   /* 8 bytes */
#define I527_CLKOUT_REG		0x1f
#define I527_SL_MASK		   0x30
#define I527_SL_SHIFT	   4
#define I527_SL_MASK0	   0x03
#define I527_CLKDIV_MASK	   0x0f
#define I527_CLKDIV_SHIFT	   0
#define I527_CLKDIV_MASK0	   0x0f

/* Message 2			0x20 */
#define I527_BUSCFG_REG		0x2f
#define I527_COBY		   0x40
#define I527_POL		   0x20
#define I527_DCT1		   0x08
#define I527_DCR1		   0x02
#define I527_DCR0		   0x01

/* Message 3			0x30 */
#define I527_BITT0_REG		0x3f
#define I527_SJW_MASK           0xc0
#define I527_SJW_SHIFT          0x06
#define I527_SJW_MASK0          0x03
#define I527_BRP_MASK           0x3f
#define I527_BRP_SHIFT          0
#define I527_BRP_MASK0          0x3f

/* Message 4			0x40 */
#define I527_BITT1_REG		0x4f
#define I527_SPL_MASK           0x80
#define I527_SPL_SHIFT          7
#define I527_SPL_MASK0          0x01
#define I527_TSEG2_MASK         0x70
#define I527_TSEG2_SHIFT        4
#define I527_TSEG2_MASK0        0x07
#define I527_TSEG1_MASK         0x0f
#define I527_TSEG1_SHIFT        0
#define I527_TSEG1_MASK0        0x0f

/* Message 5			0x50 */
#define I527_INT_REG		0x5f

/* Message 6			0x60 */
/* Reserved                     0x6f */

/* Message 7			0x70 */
/* Reserved                     0x7f */

/* Message 8			0x80 */
/* Reserved                     0x8f */

/* Message 9			0x90 */
#define I527_P1CONF		0x9f

/* Message 10			0xa0 */
#define I527_P2CONF		0xaf

/* Message 11			0xb0 */
#define I527_P1IN		0xbf

/* Message 12			0xc0 */
#define I527_P2IN		0xcf

/* Message 13			0xd0 */
#define I527_P1OUT		0xdf

/* Message 14			0xe0 */
#define I527_P2OUT		0xef

/* Message 15			0xf0 */
#define I527_SER_RST_ADD	0xff

typedef struct
{
  __u8 cpuint;
  __u8 clkout;
  __u8 bittm0;
  __u8 bittm1;
} CAN_BPS; 

/*
 * WARNING:  can_times fields must all be __u8, because I sometimes
 * iniitalize it from arrays of bytes (for example dev->hal->h_defaults)
 */
typedef struct  
{
  __u8  t_dsc;
  __u8  t_dmc;
  __u8  t_clkout_div;
  __u8  t_clkout_slew;
  __u8  t_sjw;
  __u8  t_spl;
  __u8  t_brp;
  __u8  t_tseg1;
  __u8  t_tseg2;
} CAN_TIMES;

/* This is the MSGOBJ structure */
typedef struct 
{
  u32                          obj_id;  /* current id */
  struct file                  *owner;  /* someone is waiting for a message */
  volatile u16              obj_flags;  /* object-specific flags */
  u8                          regbase;  /* register offset */
  volatile u8                   error;  /* error on message */
  spinlock_t                     lock;
  unsigned long                 flags;          
  wait_queue_head_t            read_q;  /* read queue */
  struct fasync_struct  *async_read_q;  /* async read queue */
  wait_queue_head_t           write_q;  /* write queue */
  struct fasync_struct *async_write_q;  /* async write queue*/
  volatile int                   head;
  volatile int                   tail;
  CAN_MSG                       **msg;   /* allocated using CAN_BUFSIZE */;
} CAN_MSGOBJ;

/* object flags and error flags, used internally */
#define CAN_OBJFLAG_WRITE        0x01   /* being used to write/read */
#define CAN_OBJFLAG_READ         0x02   /* (same values as MSGFLAG) */
#define CAN_OBJFLAG_REMOTE       0x04   /* remote flag on */
#define CAN_OBJFLAG_HWBUSY       0x40   /* message is being transmitted */
#define CAN_OBJFLAG_BUSY         0x80   /* message is in use */


/* error notification via signals */
typedef struct  
{
  struct file *file;
  int signal;
  pid_t pid;
} CAN_ERROR;
#define CAN_NR_ERROR_FILES 4 /* at most 4 processes can get a signal */

typedef struct  
{
  u32               base;
  volatile u32      irq_count;
  u8                usecount;
  volatile u8       statusbyte;
  u8                registered;
  u8                gotirq;
  CAN_MSGOBJ       *objs[I527_MSG_OBJS];
  CAN_ERROR         errorinfo[CAN_NR_ERROR_FILES];
  wait_queue_head_t error_q;
  BPS               eBps;
} CAN_DEV;


static kmem_cache_t *gpsCache;     // The memory cache for msg packets
static CAN_DEV gsDev;              // The device data 

static const CAN_BPS gasBps[BPS_MAX] =
{  {0x40, 0x31, 0x03, 0x4d}        // 100k
  ,{0x40, 0x31, 0x03, 0x49}        // 125k
  ,{0x40, 0x31, 0x01, 0x49}        // 250k
  ,{0x40, 0x31, 0x00, 0x49}        // 500k
  ,{0x40, 0x31, 0x00, 0x14}        // 1M
};

static spinlock_t can_lock = SPIN_LOCK_UNLOCKED;

/////////////////////////////////////////////////////////////////////////////
static int can_readb(CAN_DEV *dev, u_char reg)
{
  return readb((volatile unsigned char *)(dev->base + reg));
}

//---------------------------------------------------------------------------
static int can_writeb(CAN_DEV *dev, u_char reg, unsigned char val)
{
  writeb(val, (volatile unsigned char *)(dev->base + reg));
  return 0;
}

/*===========================================================================
 * basic functions use direct methods
 */
#define i527_read_reg(dev, reg) \
                           (can_readb((dev), (reg)))

#define i527_write_reg(dev, reg, value) \
                           (can_writeb((dev), (reg), (value)))

/*
 * bit-setting operation and its use
 */
extern inline int i527_bits(CAN_DEV *dev, int reg,
				 int bits, int value)
{
    int res = i527_read_reg(dev, reg);
    if (res < 0) return res;
    res = (res & ~bits) | (bits & value);
    return i527_write_reg(dev, reg, res);
}

#define can_enable_irq(dev) i527_bits((dev), I527_CTRL_REG, \
                             I527_EIE|I527_IE, I527_EIE|I527_IE)

#define can_disable_irq(dev) i527_bits((dev), I527_CTRL_REG, \
                              I527_EIE|I527_IE, 0)

#define can_read_msg_dlc(dev, n) \
                ((i527_read_msgcfg(dev, n) & I527_DLC_MASK) \
                      >> I527_DLC_SHIFT)

#define can_write_msg_dlc(dev, n, dlc) \
                (i527_bits((dev), n * I527_MSG_OFF + I527_MSG_CFG, \
				 I527_DLC_MASK, (dlc) << I527_DLC_SHIFT))

/*
 * Enable/disable configuration changes
 */

/* enable and return previous value */
extern inline int can_enable_cfg(CAN_DEV *dev)
{
    int res =  i527_read_reg(dev, I527_CTRL_REG);
    if (res < 0) return res;
    if (res & I527_CCE) return I527_CCE;
    res = i527_write_reg(dev, I527_CTRL_REG, res | I527_CCE);
    if (res < 0) return res;
    return 0;
}

/* disable and return previous value */
extern inline int can_disable_cfg(CAN_DEV *dev)
{
    int res =  i527_read_reg(dev, I527_CTRL_REG);
    if (res < 0) return res;
    if (!(res & I527_CCE)) return 0;
    res = i527_write_reg(dev, I527_CTRL_REG, res & ~I527_CCE);
    if (res < 0) return res;
    return I527_CCE;
}

/* restore previous value */
extern inline int can_restore_cfg(CAN_DEV *dev, int prev)
{
    return i527_bits(dev, I527_CTRL_REG, I527_CCE, prev);
}

/*
 * higher level functions (can't be in i82527.h, for strange dependencies)
 */

extern inline int i527_read_msgcfg(CAN_DEV *dev,
				   CAN_MSGOBJ *obj)
{
    return i527_read_reg(dev, obj->regbase + I527_MSG_CFG);
}

extern inline int i527_write_msgcfg(CAN_DEV *dev,
				    CAN_MSGOBJ *obj, int val)
{
    if (val & ~0xff) return -EINVAL;
    return  i527_write_reg(dev, obj->regbase + I527_MSG_CFG, val);
}

extern inline u16 i527_read_std_mask(CAN_DEV *dev)
{
    /* errors are ignored, hmm... */
    u16 ret = i527_read_reg(dev, I527_GMASK_STD) << 8
	|  i527_read_reg(dev, I527_GMASK_STD + 1);
    return ret;
}
 
extern inline void i527_write_std_mask(CAN_DEV *dev, u16 val)
{
    /* errors are ignored, hmm... */
    i527_write_reg(dev, I527_GMASK_STD,     val >> 8);
    i527_write_reg(dev, I527_GMASK_STD + 1, val & 0xff);
}

extern inline u32 i527_read_x_mask(CAN_DEV *dev)
{
    /* errors are ignored, hmm... */
    u32 ret = i527_read_reg(dev, I527_GMASK_XTD)  << 24
	|  i527_read_reg(dev, I527_GMASK_XTD + 1) << 16
	|  i527_read_reg(dev, I527_GMASK_XTD + 2) << 8
	|  i527_read_reg(dev, I527_GMASK_XTD + 3);
    return ret;
}

extern inline void i527_write_x_mask(CAN_DEV *dev, u32 val)
{
    /* errors are ignored, hmm... */
    i527_write_reg(dev, I527_GMASK_XTD,      val >> 24);
    i527_write_reg(dev, I527_GMASK_XTD + 1, (val >> 16) & 0xff);
    i527_write_reg(dev, I527_GMASK_XTD + 2, (val >>  8) & 0xff);
    i527_write_reg(dev, I527_GMASK_XTD + 3, (val >>  0) & 0xff);
}


extern inline u32 i527_read_15_mask(CAN_DEV *dev)
{
    /* errors are ignored, hmm... */
    u32 ret = i527_read_reg(dev, I527_MSG15_MASK)  << 24
	|  i527_read_reg(dev, I527_MSG15_MASK + 1) << 16
	|  i527_read_reg(dev, I527_MSG15_MASK + 2) << 8
	|  i527_read_reg(dev, I527_MSG15_MASK + 3);
    return ret;
}
 
extern inline void i527_write_15_mask(CAN_DEV *dev,
					     u32 val)
{
    /* errors are ignored, hmm... */
    i527_write_reg(dev, I527_MSG15_MASK,      val >> 24);
    i527_write_reg(dev, I527_MSG15_MASK + 1, (val >> 16) & 0xff);
    i527_write_reg(dev, I527_MSG15_MASK + 2, (val >>  8) & 0xff);
    i527_write_reg(dev, I527_MSG15_MASK + 3, (val >>  0) & 0xff);
}

extern inline u16 i527_read_msgctrl(CAN_DEV *dev,
				    CAN_MSGOBJ *obj)
{
    /* FIXME: this is used little-endian, but doesn't need to be 16b */

    /* errors are ignored, hmm... */
    u16 ret = i527_read_reg(dev, obj->regbase + I527_MSG_CTRL)
	| (i527_read_reg(dev, obj->regbase + I527_MSG_CTRL +1 )
	   << 8);
    return ret;
}
 
extern inline void i527_write_msgctrl(CAN_DEV *dev,
				      CAN_MSGOBJ *obj, u16 val)
{
    /* FIXME: this is used little-endian, but doesn't need to be 16b */

    /* errors are ignored, hmm... */
    i527_write_reg(dev, obj->regbase + I527_MSG_CTRL, val & 0xff);
    i527_write_reg(dev, obj->regbase + I527_MSG_CTRL + 1, val >> 8);
}

/* write a single byte of msgctrl, twice as fast as the function above */
extern inline void i527_msgflag(CAN_DEV *dev,
				CAN_MSGOBJ *obj, u16 act)
{
    if ((act & 0xff) == 0xff)
	i527_write_reg(dev, obj->regbase + I527_MSG_CTRL + 1, act >> 8);
    else
	i527_write_reg(dev, obj->regbase + I527_MSG_CTRL, act & 0xff);
}
/* same, but for unallocated objects */
extern inline void i527_msgflag_noobj(CAN_DEV *dev,
				      int msgnum, u16 act)
{
    if ((act & 0xff) == 0xff)
	i527_write_reg(dev, msgnum*I527_MSG_OFF + I527_MSG_CTRL + 1,
			    act >> 8);
    else
	i527_write_reg(dev,  msgnum*I527_MSG_OFF + I527_MSG_CTRL,
			    act & 0xff);
}
/* read a single byte of msgctrl (use the _S flag. Hm....) */
extern inline int i527_check_msgflag(CAN_DEV *dev,
				     CAN_MSGOBJ *obj, u16 act)
{
    u16 reg;
    if ((act & 0xff) == 0xff) {
	reg = i527_read_reg(dev, obj->regbase + I527_MSG_CTRL + 1);
	return !((reg << 8) & ~act);
    }
    reg = i527_read_reg(dev, obj->regbase + I527_MSG_CTRL);
    return !(reg & ~act);
}

extern inline u32 i527_read_msgarb(CAN_DEV *dev,
				   CAN_MSGOBJ *obj)
{
    int port = obj->regbase + I527_MSG_ARBIT;

    /* errors are ignored, hmm... */
    u32 ret = i527_read_reg(dev, port)  << 24
	|  i527_read_reg(dev, port + 1) << 16
	|  i527_read_reg(dev, port + 2) << 8
	|  i527_read_reg(dev, port + 3);
    return ret;
}
 
extern inline void i527_write_msgarb(CAN_DEV *dev,
				     CAN_MSGOBJ *obj, u32 val)
{
    int port = obj->regbase + I527_MSG_ARBIT;

    i527_write_reg(dev, port,      val >> 24);
    i527_write_reg(dev, port + 1, (val >> 16) & 0xff);
    i527_write_reg(dev, port + 2, (val >>  8) & 0xff);
    i527_write_reg(dev, port + 3, (val >>  0) & 0xff);
}

extern inline void i527_read_msgdata(CAN_DEV *dev,
				     CAN_MSGOBJ *obj,
				     int n, u8 *data)
{
    int i;
    u8 reg = obj->regbase + I527_MSG_DATA;

    for (i=0; i<n; i++)
	data[i] = i527_read_reg(dev, reg++);
}

extern inline void i527_write_msgdata(CAN_DEV *dev,
				      CAN_MSGOBJ *obj,
				      int n, u8 *data)
{
    int i;
    u8 reg = obj->regbase + I527_MSG_DATA;
    
    if (!n) return;
    i527_msgflag(dev, obj, I527_CPUUPD_S); /* CPU updating */
    for (i=0; i<n && i<8; i++)
	i527_write_reg(dev, reg++, data[i]);
    i527_msgflag(dev, obj, I527_CPUUPD_R);
}

/*===========================================================================
 * a collection of quick helper functions (all of them inline)
 */

/* objects can become allocated items, and allocation is hidden here */
extern inline CAN_MSGOBJ *lib_findobj(CAN_DEV *dev, int msgnum, int prio)
{
  /* msgnum already checked */
  return dev->objs[msgnum];
}

/* if/when allocation is there, release them here */
extern inline void lib_releaseobj(CAN_DEV *dev, int msgnum, CAN_MSGOBJ *obj)
{
  int i;
  if (!obj) return;
  spin_lock(&obj->lock);
  obj->owner = NULL;
  obj->obj_flags &= ~(CAN_OBJFLAG_BUSY | CAN_OBJFLAG_HWBUSY);
  i527_msgflag(dev, obj, I527_MSGVAL_R & I527_TXIE_R & I527_RXIE_R);
  for (i=0; i<CAN_BUFSIZE; i++)
    if (obj->msg[i]) {
      PDEBUG("discarding packet in obj %i\n", msgnum);
      kmem_cache_free(gpsCache, obj->msg[i]);
      obj->msg[i] = NULL;
    }
  obj-> head = obj->tail = 0;
  spin_unlock(&obj->lock);
}


/* returns 0 or -EINVAL */
extern inline int lib_checkmsgnum(int msgnum, int flags)
{
  if (msgnum > 0 && msgnum < 15)
    return 0;
  if ( (msgnum == 15) && !(flags & CAN_MSGFLAG_WRITE) )
    return 0;
  printk("lib_checkmsgnum error, num= %d\n", msgnum);
  return -EINVAL;
}

/* returns either 0 or -EBUSY, adjusts ownership */
extern inline int lib_setowner(CAN_MSGOBJ *obj,
			       struct file *filp, int rdwr)
{
  spin_lock(&obj->lock);
  if ( (obj->obj_flags & CAN_OBJFLAG_BUSY) && (obj->owner != filp) ) {
    spin_unlock(&obj->lock);
    return -EBUSY;
  }
  obj->owner = filp;
  obj->obj_flags =
    (obj->obj_flags & ~(CAN_MSGFLAG_WRITE | CAN_MSGFLAG_READ))
    | CAN_OBJFLAG_BUSY | rdwr;
  spin_unlock(&obj->lock);
  return 0;
}


/* find this file in the errorfiles */
extern inline CAN_ERROR *lib_findfile(CAN_DEV *dev, struct file *file)
{
  CAN_ERROR *info = dev->errorinfo;
  int i;

  for (i = 0; i<CAN_NR_ERROR_FILES; i++, info++)
    if (info->file == file)
      return info;
  return NULL;
}

/*===========================================================================
 * Normal management message objects functions
 */

//---------------------------------------------------------------------------
static void can_setbps(CAN_DEV *dev, BPS eBps )
{
  int j;
  __u8 nTmp;
  const CAN_BPS *psBps = gasBps + eBps;

  j = can_enable_cfg(dev);

  nTmp = i527_read_reg(dev, I527_CPU_INT_REG) & ~(I527_DSC|I527_DMC);
  nTmp |= psBps->cpuint;
  i527_write_reg(dev, I527_CPU_INT_REG, nTmp);
  i527_write_reg(dev, I527_CLKOUT_REG, psBps->clkout);
  i527_write_reg(dev, I527_BITT0_REG, psBps->bittm0);
  i527_write_reg(dev, I527_BITT1_REG, psBps->bittm1);

  PDEBUG("libdrv: index        %lx\n", eBps);
  PDEBUG("libdrv: cpu_int_reg  %x\n", nTmp);
  PDEBUG("libdrv: cpu_clk_out  %x\n", psBps->clkout);
  PDEBUG("libdrv: cpu_bit0_reg %x\n", psBps->bittm0);
  PDEBUG("libdrv: cpu_bit1_reg %x\n", psBps->bittm1);

  can_restore_cfg(dev, j);
  dev->eBps = eBps;
}

/*---------------------------------------------------------------------------
 * full-reset function, called by deviceinit or by ioctl(RESET)
 --------------------------------------------------------------------------*/
static void can_reset_i82527(CAN_DEV *dev)
{
  int i;

  PDEBUG("init/reset of i82527 at 0x%08lx\n", (unsigned long)dev->base);

  i527_write_reg(dev,I527_CTRL_REG,   I527_CCE|I527_INIT);          //41
  i527_write_reg(dev,I527_CLKOUT_REG, 3 << I527_SL_SHIFT);          //30
  i527_write_reg(dev, I527_STAT_REG, 0x00);
  i527_write_reg(dev, I527_P1CONF, 0x00);    /* default is input */
  i527_write_reg(dev, I527_P2CONF, 0x00);

  i527_write_15_mask(dev, 0xffffffff);
  i527_write_std_mask(dev, 0xffff);
  i527_write_x_mask(dev, 0xffffffff);

  /* Turn off all message objects and clear message identifiers */
  for (i = I527_FIRST_OBJ; i < I527_MSG_OBJS; i++) 
  {
    if (dev->objs[i]) 
    {
      lib_releaseobj(dev, i, dev->objs[i]); /* useful for reset */
      i527_write_msgctrl(dev, dev->objs[i],
			      I527_MSGVAL_R & I527_TXIE_R &
			      I527_RXIE_R & I527_INTPND_R &
			      I527_RMTPND_R & I527_TXRQST_R &
			      I527_MSGLST_R & I527_NEWDAT_R);
      i527_write_msgarb(dev, dev->objs[i], 0); 
    }
  }

  /* set default values */
  i527_write_reg(dev, I527_BUSCFG_REG, 0x4a);
  can_setbps(dev, B_250K);

  /* Initialization end */
  i527_write_reg(dev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
  if (dev->usecount) 
    {
      can_enable_irq(dev); /* warm reset */
    } 
  else 
    {
      i527_write_reg(dev, I527_CTRL_REG, 0); /* cold reset */
    }
}

/*---------------------------------------------------------------------------
 * This is called from ioctl or from the bottom half handler.
 * It must configure and possibly transmit an object
 * Everything checkable has already been checked if we get here.
-------------------------------------------------------------------------- */
static void can_config_and_transmit(CAN_DEV    *dev,
			            CAN_MSGOBJ *obj,
			            CAN_MSG    *msg, int flag)
{
  int i;

  /* it looks like message 15 fires an interrupt when re-enabled */
  if (msg->msgobj != 15) 
    i527_msgflag(dev, obj, I527_MSGVAL_R); /* being updated */
    
  if (msg->id != obj->obj_id) {
    i527_write_msgarb(dev, obj, msg->id);
    obj->obj_id = msg->id;
    PDEBUG("message has new ID 0x%08lx\n", (unsigned long)obj->obj_id);
  }
	  
  /* write DLC and X flag */
  i = (msg->dlc << I527_DLC_SHIFT) | (msg->config & I527_XTD)
    | (msg->flags & CAN_MSGFLAG_WRITE ? I527_DIR_TX : I527_DIR_RX);
  i527_write_msgcfg(dev, obj, i);

  /* write data bytes */
  if (msg->flags & CAN_MSGFLAG_WRITE)
    i527_write_msgdata(dev, obj, msg->dlc, msg->data);

  /* now set flags: *XIE, MSGVAL; then clear CPUUpd and set newdat */
  i527_msgflag(dev, obj,
		    I527_MSGVAL_S & I527_TXIE_S & I527_RXIE_S);

  if (!flag) 
  { /* only setup, no tx */
    if (msg->flags & CAN_MSGFLAG_WRITE)
      i527_msgflag(dev, obj, I527_CPUUPD_R & I527_NEWDAT_S);
    else
      i527_msgflag(dev, obj, I527_RMTPND_R & I527_MSGLST_R
			& I527_NEWDAT_R);
    return;
  }
  /* else, tx too */
  i527_msgflag(dev, obj, I527_CPUUPD_R & I527_NEWDAT_S & I527_TXRQST_S);
}


//IRQ////////////////////////////////////////////////////////////////////////
/* this bottom half does the work of message handling and buffer popping */

//---------------------------------------------------------------------------
static void can_write_interrupt(CAN_DEV *dev,
				CAN_MSGOBJ *obj, int objnum)
{
  PDEBUG("can_wi: interrupt on objnum %d\n", objnum);

  /* write-irq flushes the queue and then awakes writers */

  i527_msgflag(dev, obj, I527_INTPND_R);
  /* head and tail access is atomic with HWBUSY access */
  spin_lock(&obj->lock);
  if (obj->head == obj->tail) 
    {
      obj->obj_flags &= ~CAN_OBJFLAG_HWBUSY;
      spin_unlock(&obj->lock);
      wake_up_interruptible(&obj->write_q);
      if (obj->async_write_q)
	kill_fasync(&obj->async_write_q, SIGIO, POLLOUT);
      return;
    }
  spin_unlock(&obj->lock);

  /* else push out another packet */
  can_config_and_transmit(dev, obj, obj->msg[obj->tail], 1 /* tx */);
  kmem_cache_free(gpsCache, obj->msg[obj->tail]);
  obj->msg[obj->tail] = NULL;
  obj->tail = (obj->tail + 1) % CAN_BUFSIZE;
  /* possibly awake writers */
  if ( ((obj->head + CAN_BUFSIZE - obj->tail) % CAN_BUFSIZE)
       == CAN_BUFSIZE/2) 
    {
      wake_up_interruptible(&obj->write_q);
      if (obj->async_write_q)
	kill_fasync(&obj->async_write_q, SIGIO, POLLOUT);
    }
}

//---------------------------------------------------------------------------
static void can_read_interrupt(CAN_DEV *dev,
			       CAN_MSGOBJ *obj, int objnum)
{
  CAN_MSG *msg;
  int newhead;

  PDEBUG("can_ri: interrupt on objnum %d\n", objnum);

  /* Check for transmission errors */
  obj->error = 0;
  if (i527_check_msgflag(dev, obj, I527_MSGLST_S)) 
    {
      obj->error = CAN_ERROR_MSGLST;
      i527_msgflag(dev, obj, I527_MSGLST_R);
    }
    
  if (objnum != 15 ) 
  { /* Argh!!! it wasn't clear *not* to do that for 15 */
    /* "The CPU should clear the bit before reading data and check it" */
    i527_msgflag(dev, obj, I527_NEWDAT_R);
  }

  newhead = (obj->head + 1) % CAN_BUFSIZE;
  if (newhead == obj->tail) 
    {
      /* overflow, discard this message and mark overlow in the previous */
      //	printk(KERN_ERR CAN_TEXT "overflow for msgobj %i\n", objnum);
      printk("ov\n");
      obj->msg[(obj->head+CAN_BUFSIZE-1)%CAN_BUFSIZE]->error
	|= CAN_ERROR_OVRFLW;
      goto ack_return;
    }
    
  msg = kmem_cache_alloc(gpsCache, GFP_ATOMIC);
  if (!msg) 
  {
    printk(KERN_ERR CAN_TEXT "out of memory for packet to msgobj %i\n",
	   objnum);
    goto ack_return;
  }
  obj->msg[obj->head] = msg;

  msg->id        = i527_read_msgarb(dev, obj);
  obj->obj_id    = 0; /* invalidate, so tx will set it */
  msg->msgobj    = objnum;
  msg->config    = i527_read_msgcfg(dev, obj);
  msg->dlc       = (msg->config & I527_DLC_MASK) >> I527_DLC_SHIFT;
  msg->flags     = CAN_MSGFLAG_READ;
  msg->error     = obj->error;
  i527_read_msgdata(dev, obj, msg->dlc, msg->data);

  obj->head = newhead;
  i527_msgflag(dev, obj, I527_INTPND_R);

  /* FIXME: should re-check NewDat according to data sheet */
  if (objnum == 15) 
    {
      i527_msgflag(dev, obj, I527_NEWDAT_R & I527_RMTPND_R);
    }
  PDEBUG("%i bytes from 0x%08lx\n", msg->dlc, (long)msg->id);

  /* Wake any reading process */
  wake_up_interruptible(&obj->read_q);
    
  /* Signal any asynchronous reader */
  if (obj->async_read_q != NULL)
    kill_fasync(&obj->async_read_q, SIGIO, POLLIN);
  return;

 ack_return:
  i527_msgflag(dev, obj, I527_INTPND_R);
  if (objnum == 15) 
    {
      i527_msgflag(dev, obj, I527_NEWDAT_R & I527_RMTPND_R);
    }
}


/* map the interrupt register value to the message object */
static const int irqtab[17] = {
  0, 0, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
};

//-------------------------------------------------------------------------
static void can_bh(unsigned long nNum)
{
  CAN_DEV *dev = &gsDev;
  CAN_MSGOBJ *obj = NULL;
  int objnum;
  int status, irqreg;
  unsigned long flags;

  spin_lock_irqsave(&can_lock, flags);

  /* Get the interrupt register -- even if we'd done it at hw irq */
  irqreg = i527_read_reg(dev, I527_INT_REG);
  status = i527_read_reg(dev, I527_STAT_REG);
  PDEBUG("can_bh: interrupt on irq %d\n", irqreg);

  dev->irq_count++;

#ifdef CAN_DEBUG /* needed to avoid "unused variable lec" */
  /* print some stupid message */
  if (1) {
    PDEBUG("{{{ irq for device\n");
    if (status & 0x80) PDEBUG("status: BusOff\n");
    if (status & 0x40) PDEBUG("status: Warning\n");
    if (status & 0x20) PDEBUG("status: Wake\n");
    if (status & 0x10) PDEBUG("status: RXOK\n");
    if (status & 0x08) PDEBUG("status: TXOK\n");
    if ((status & 0x07) != 7) {
      static char *lec[]={"No","Stuff","Form","Ack","B1","B0","CRC"};
      PDEBUG("status: LEC: %s Error\n", lec[status&7]);
    }
  }
#endif /* CAN_DEBUG */

  /* Reset the status register */
  i527_write_reg(dev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
   
  do {
    objnum = irqtab[irqreg];

    if (!objnum) 
    {
      /* awake sleeping readers and send signals */
      CAN_ERROR *info = dev->errorinfo;
      PDEBUG("Status Register IRQ (0x%02x)\n", status);
      dev->statusbyte = status;
      wake_up_interruptible(&dev->error_q);
	   
      for (objnum = 0; objnum < CAN_NR_ERROR_FILES; objnum++, info++)
	if (info->file)
	  kill_proc(info->pid, info->signal, 1);
      printk("jcan: irq objnum = 0\n");
      continue;
    }

    obj = dev->objs[objnum];
    if (!obj) 
      {
	printk("jcan: irq obj = 0\n");
	/* object not allocated, clear the interrupt anyways */
	i527_msgflag_noobj(dev, objnum, I527_INTPND_R);
	PDEBUG("resetted irq for unallocated device %i\n", objnum);
	continue;
      }
    else
      PDEBUG("jcan: irq objnum %d\n", objnum);


    if (obj->obj_flags & CAN_OBJFLAG_WRITE)
      can_write_interrupt(dev, obj, objnum);
    else
      can_read_interrupt(dev, obj, objnum);
    udelay(10);                          /* 14 MCLK max, be safe */
  } 
  while ( (irqreg = i527_read_reg(dev, I527_INT_REG)) ); 

  spin_unlock_irqrestore(&can_lock, flags);
  PDEBUG("end of irq}}}\n");
}

static DECLARE_TASKLET(can_task, can_bh, (unsigned long)0);

/* actual interrupt handling --------------------------------------------*/
static void can_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
  CAN_DEV *dev = (CAN_DEV *) dev_id;
  int irqreg;
   
  /* Get the interrupt register's status */
  irqreg = i527_read_reg(dev, I527_INT_REG);

  if (!irqreg) 
    {
      return;  /* Not us, probably a shared handler */
    }
  if (irqreg > 16) 
    {
      printk(KERN_ERR CAN_TEXT "irq register too high: %i\n", irqreg);
      return;
    }

  /* everything else is done at bottom-half time */
  PDEBUG("can_int  dev %p int_reg %d\n", dev, irqreg);
  PDEBUG("** HW irq for device %i:\n", irqtab[irqreg]);

  tasklet_schedule(&can_task);  
}

/*========================================================================
 * File operations definitions
 * ------------------------------------------------------- ioctl-------- */
static int can_ioctl (struct inode *inode, struct file *filp,
	 	       unsigned int cmd, unsigned long arg)
{
#define IOC_BUFSIZE (CAN_READ_BUFS * sizeof(CAN_MSG))

  static unsigned char localbuf[IOC_BUFSIZE];
  CAN_DEV *dev = filp->private_data;
  CAN_ERROR *info;
  int size = _IOC_SIZE(cmd); /* the size bitfield in cmd */
  int i, j, retval = 0; /* success by default */
  /* I'm lazy, and I define "alias" names for the buffer */
  void *karg = localbuf;
  unsigned long *klong = karg;
  CAN_REG *kreg = karg;
  CAN_MSG *kmsg = karg;
  CAN_MULTIREG *kmulti = karg;
  CAN_MASKS *kmasks = karg;
  CAN_MSGOBJ *obj;
  unsigned char *area;

  /*
   * extract the type and number bitfields, and don't decode
   * wrong cmds: return ENOTTY before verify_area()
   */
  if (_IOC_TYPE(cmd) != CAN_IOC_MAGIC) return -ENOTTY;
  if (_IOC_NR(cmd) > CAN_IOC_MAXNR) return -ENOTTY;

  /*
   * the direction is a bitmask, and VERIFY_WRITE catches R/W
   * transfers. `Dir' is user-oriented, while
   * verify_area is kernel-oriented, so the concept of "read" and
   * "write" is reversed
   */
  if (_IOC_DIR(cmd) & _IOC_READ) 
  {
    if (!access_ok(VERIFY_WRITE, (void *)arg, size))
      return -EFAULT;
  }
  else 
  if (_IOC_DIR(cmd) & _IOC_WRITE) 
  {
    if (!access_ok(VERIFY_READ, (void *)arg, size))
      return -EFAULT;
  }

  PDEBUG("ioctl: %08x %08lx: nr %i, size %i, dir %i\n",
	 cmd, arg, _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));

  /* first, retrieve data from userspace */
  if ((_IOC_DIR(cmd) & _IOC_WRITE) && (size <= IOC_BUFSIZE))
    copy_from_user(karg, (void *)arg, size);

  /* and now the big switch... */
  switch(cmd) 
    {
    case CAN_IOCRESET:
    case CAN_IOCSOFTRESET:
      can_disable_irq(dev);
      free_irq(IRQ_NUM, (void *)dev);
      can_reset_i82527(dev);
      i527_write_reg(dev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
#ifdef __powerpc__
      request_8xxirq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev); 
#else
      request_irq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev); 
#endif
      can_enable_irq(dev);
      break;

    case CAN_IOCGIRQCOUNT:
      *klong = dev->irq_count;
      break;

    case CAN_IOCREADREG:
      kreg->val = i527_read_reg(dev, kreg->reg & 0xff);
      break;

    case CAN_IOCWRITEREG:
      printk("jcan write reg %02x %02x\n", kreg->reg, kreg->val);
      i527_write_reg(dev, kreg->reg & 0xff, kreg->val);
      break;

    case CAN_IOCREADMULTI:
      if (kmulti->last < kmulti->first)
	return -EINVAL;
      if (kmulti->last - kmulti->first + 1 > CAN_MULTIREG_MAX)
	return -EINVAL;
      for (i=0, j=kmulti->first; j<=kmulti->last; i++, j++)
	kmulti->regs[i] = i527_read_reg(dev, j);
      break;

    case CAN_IOCWRITEMULTI:
      if (kmulti->last < kmulti->first)
	return -EINVAL;
      if (kmulti->last - kmulti->first + 1 > CAN_MULTIREG_MAX)
	return -EINVAL;
      i=0;
      for (i=0, j=kmulti->first; j <= kmulti->last; j++)
	i527_write_reg(dev, j, kmulti->regs[i++]);
      break;

    case CAN_IOCREADALL:
      area = kmalloc(256, GFP_USER);
      if (!area) return -ENOMEM;
      for (i=0; i<256; i++)
	((u8 *)area)[i] = i527_read_reg(dev, i);
      copy_to_user((void *)arg, area, 256);
      kfree(area);
      return 0;

    case CAN_IOCGETMASKS:
      kmasks->m_msg15 = i527_read_15_mask(dev);
      kmasks->m_xtd =   i527_read_x_mask(dev);
      kmasks->m_std =   i527_read_std_mask(dev);
      break;
	  
    case CAN_IOCSETMASKS:
      i527_write_15_mask(dev,  kmasks->m_msg15);
      i527_write_x_mask(dev,   kmasks->m_xtd); 
      i527_write_std_mask(dev, kmasks->m_std);
      break;
		
    case CAN_IOCGETBPS:
      *klong = dev->eBps;
      break;

    case CAN_IOCSETBPS:
      can_setbps(dev, (BPS)arg );
      break;

    case CAN_IOCGETBUSCONF:
      kreg->val = i527_read_reg(dev, I527_BUSCFG_REG);
      break;

    case CAN_IOCSETBUSCONF:
      j = can_enable_cfg(dev);
      i527_write_reg(dev, I527_BUSCFG_REG, kreg->val);
      can_restore_cfg(dev, j);
      break;

    //-----------------------------------------------------------------
    case CAN_IOCWRITEMSG:
    case CAN_IOCSETUPMSG: /* copy the msgobj struct to device registers */
      if (lib_checkmsgnum(kmsg->msgobj, kmsg->flags))
	return -EINVAL;

      obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
      if (!obj) return -ENOMEM;
      if (lib_setowner(obj, filp, kmsg->flags &
			    (CAN_MSGFLAG_WRITE | CAN_MSGFLAG_READ) ))
	return -EBUSY;

      /* before we touch the message, ensure it's not transmitting */
      if (obj->obj_flags & CAN_OBJFLAG_HWBUSY) {
	if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
	wait_event_interruptible(obj->write_q,
				 !(obj->obj_flags &CAN_OBJFLAG_HWBUSY));
	if (signal_pending(current)) return -ERESTARTSYS;
      }

      if (cmd == CAN_IOCWRITEMSG)
	obj->obj_flags |= CAN_OBJFLAG_HWBUSY;
      can_config_and_transmit(dev, obj, kmsg,
			       cmd == CAN_IOCWRITEMSG ? 1 : 0);
      break;

    case CAN_IOCWRITEQ: /* place it in the queue */
#if 0
      if (lib_checkmsgnum(kmsg->msgobj, kmsg->flags))
	return -EINVAL;

      obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
      if (!obj) return -ENOMEM;
      if (lib_setowner(obj, filp, kmsg->flags &
			    (CAN_MSGFLAG_WRITE | CAN_MSGFLAG_READ) ))
	return -EBUSY;

      /* place the message in the queue */
      i = (obj->head + 1) % CAN_BUFSIZE;
      if (i == obj->tail) {
	/* queue full: sleep -- no problem with concurrent access */
	if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
	wait_event_interruptible(obj->write_q, (i != obj->tail));
	if (signal_pending(current)) return -ERESTARTSYS;
      }
      newmsg = kmem_cache_alloc(gpsCache, GFP_USER);
      if (!newmsg) {
	printk(KERN_ERR "can't allocate outgoing packet for msg %i\n",
	       kmsg->msgobj);
	return -ENOMEM;
      }
      spin_lock_bh(obj->lock);
      /* if already tranmitting, just add this to the queue */
      if ( obj->obj_flags & CAN_OBJFLAG_HWBUSY ) {
	*newmsg = *kmsg;
	obj->msg[obj->head] = newmsg;
	obj->head = i;
	spin_unlock_bh(obj->lock);
	break;
      } 
      spin_unlock_bh(obj->lock);

      /* not transmitting, can safely begin transmission */
      obj->obj_flags |= CAN_OBJFLAG_HWBUSY;
      kmem_cache_free(gpsCache, newmsg);
      can_config_and_transmit(dev, obj, kmsg, 1);
#endif
      break;

    //-------------------------------------------------------------------
    case CAN_IOCTXMSG: /* tx only, no need to call config_and_transmit() */
      if (lib_checkmsgnum(*klong, CAN_MSGFLAG_WRITE))
	return -EINVAL;
      obj = lib_findobj(dev, *klong, GFP_USER);
      if (!obj) return -ENOMEM;
      if (obj->owner != filp) return -EPERM;
      if (obj->obj_flags & CAN_OBJFLAG_HWBUSY) {
	if (filp->f_flags & O_NONBLOCK) return -EAGAIN;
	wait_event_interruptible(obj->write_q,
				 !(obj->obj_flags &CAN_OBJFLAG_HWBUSY));
	if (signal_pending(current)) return -ERESTARTSYS;
      }
      obj->obj_flags |= CAN_OBJFLAG_HWBUSY;
      i527_msgflag(dev, obj, I527_CPUUPD_R & I527_NEWDAT_S
			& I527_TXRQST_S);
      break;

    //--------------------------------------------------------------------
    case CAN_IOCRXMSG:
      /* the message must be already configured and owned */
      if (lib_checkmsgnum(kmsg->msgobj, CAN_MSGFLAG_READ))
	return -EINVAL;
      obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
      if (!obj) return -ENOMEM;
      if (obj->owner != filp) return -EPERM;

      if (obj->head == obj->tail) {
	if (filp->f_flags & O_NONBLOCK) {
	  return -EAGAIN;
	}
	wait_event_interruptible(obj->read_q, obj->head != obj->tail);
	if (signal_pending(current))
	  return -ERESTARTSYS;
	if (obj->head == obj->tail) {
	  PDEBUG("Argh: no message\n");
	  return -EFAULT; /* Hmmm.... */
	}
      }

      PDEBUG("jcan: received msg\n");
      i=0;
      while((i < CAN_READ_BUFS) && (obj->head != obj->tail)) 
	{
	  /* ok, we have a message to pull from the buffer */
	  *kmsg++ = *(obj->msg[obj->tail]);
	  kmem_cache_free(gpsCache, obj->msg[obj->tail]);
	  obj->msg[obj->tail] = NULL;
	  obj->tail = (obj->tail+1) % CAN_BUFSIZE;
	  i++;
	}
      retval = i;
      size = retval * sizeof(CAN_MSG);
      break;

    case CAN_IOCPEEKMSG: /* similar, but never sleep and use an exact ID */
#if 0
      if (lib_checkmsgnum(kmsg->msgobj, CAN_MSGFLAG_READ))
	return -EINVAL;
      obj = lib_findobj(dev, kmsg->msgobj, GFP_USER);
      if (!obj) return -ENOMEM;
      if (obj->owner != filp) return -EPERM;

      for (i = obj->tail; i != obj->head; i = i+1 % CAN_BUFSIZE) {
	PDEBUG("peeking item %i (%x %x -- %x %x)\n", i,
	       obj->msg[i]->id, kmsg->id,
	       obj->msg[i]->config&I527_XTD,
	       kmsg->config&I527_XTD);
	if ((obj->msg[i]->id == kmsg->id) &&
	    ((obj->msg[i]->config&I527_XTD) == (kmsg->config&I527_XTD)))
	  break;
      }
      if (i == obj->head) return -EAGAIN;

      j = kmsg->flags;
      *kmsg = *(obj->msg[i]);
      PDEBUG("peekonly: %i\n", j&CAN_MSGFLAG_PEEKONLY);
      if (j & CAN_MSGFLAG_PEEKONLY)
	break; /* return the data but leave it there */
      kmem_cache_free(gpsCache, obj->msg[i]);

      /*
       * remove the packet by shifting pointers: don't touch head
       * as it is the active end, modified at interrupt time
       */
      while (i != obj->tail) {
	j = (i-1 + CAN_BUFSIZE) % CAN_BUFSIZE;
	PDEBUG("copy %i to %i\n", j, i);
	obj->msg[i] = obj->msg[j];
	i = j;
      }
      obj->tail = (i+1) % CAN_BUFSIZE;
#endif
      break;

    case CAN_IOCRELEASEMSG:
      if (lib_checkmsgnum(*klong, CAN_MSGFLAG_READ))
	return -EINVAL;
      obj = lib_findobj(dev, *klong, GFP_USER);
      if (!obj) return -ENOMEM;
      if (obj->owner != filp) return -EPERM;
      lib_releaseobj(dev, *klong, obj);
      break;
	  
    case CAN_IOCREQSIGNAL:
      if (*klong >= SIGRTMIN) return -EINVAL;
      /* change an entry, if already there */
      info = lib_findfile(dev, filp);
      if (info) {
	if (!*klong) info->file = NULL;
	else info->signal = *klong;
	break;
      }
      /* look for a free slot */
      info = dev->errorinfo;
      for (i = 0; i<CAN_NR_ERROR_FILES; i++, info++)
	if (!info->file)
	  break;
      if (i == CAN_NR_ERROR_FILES) return -EBUSY;

      info->file = filp;
      info->pid = current->pid;
      info->signal = *klong;
      break;

      /*
       * I/O ports
       */
    case CAN_IOCINPUT:
      if (kreg->reg != 1 && kreg->reg != 2) return -EINVAL;
      kreg->val = i527_read_reg(dev, kreg->reg == 1
				     ? I527_P1IN : I527_P2IN);
      break;

    case CAN_IOCOUTPUT:
      if (kreg->reg != 1 && kreg->reg != 2) return -EINVAL;
      i527_write_reg(dev, kreg->reg == 1
			  ? I527_P1OUT : I527_P2OUT, kreg->val);
      break;

    case CAN_IOCIOCFG:
      if (kreg->reg != 1 && kreg->reg != 2) return -EINVAL;
      i527_write_reg(dev, kreg->reg == 1
			  ? I527_P1CONF : I527_P2CONF, kreg->val);
      break;


    default:
      PDEBUG("ioctl: no such command\n");
      return -ENOTTY;
    }
  /* finally, copy data to user space and return */
  if (retval < 0) return retval;
  if ((_IOC_DIR(cmd) & _IOC_READ) && (size <= IOC_BUFSIZE))
    copy_to_user((void *)arg, karg, size);
  return retval; /* sometimes, positive is what I want */
}


//-------------------------------------------------------------------------
static ssize_t can_read(struct file *file, char *buf, 
                         size_t count, loff_t *ppos)
{
  //  CAN_DEV *dev = (CAN_DEV *) file->private_data;
  //  int minor = MINOR(file->f_dentry->d_inode->i_rdev);
  //  int num = CAN_DEV_TYPE(minor);
  //  unsigned char status;

  return -ENOSYS; /* FIXME: reimplement write */
}

//-------------------------------------------------------------------------
static ssize_t can_write(struct file *file, const char *buf, 
                          size_t count, loff_t *ppos)
{
  //  CAN_DEV *dev = (CAN_DEV *) file->private_data;
  //  int minor = MINOR(file->f_dentry->d_inode->i_rdev);
  //  int num = CAN_DEV_TYPE(minor);

  return -ENOSYS; /* FIXME: reimplement write */

}


//-------------------------------------------------------------------------
static int can_open(struct inode *inode, struct file *file)
{
  int minor = MINOR(inode->i_rdev); 
  int num = CAN_DEV_TYPE(minor);
  int accmode = file->f_flags & O_ACCMODE;

  /* We cannot open Message Objects 1-15 for both reading and writing */
  if ((num >= 1) && (num < 15) && accmode == O_RDWR)
    return -EACCES;

  /* We cannot open Message Object 15 for writing */
  if ((num == 15) && (accmode != O_RDONLY))
    return -EACCES;

  /* Set the device's private data */
  file->private_data = &gsDev;

  /* Check for first open for 82527 */
  if (++(gsDev.usecount) == 1) 
    {
      PDEBUG("first open, enable interrupts\n");
      i527_write_reg(&gsDev, I527_STAT_REG, I527_STAT_REG_DEFAULT);
      can_enable_irq(&gsDev);
    }

  MOD_INC_USE_COUNT;
  return 0;
}

//-------------------------------------------------------------------------
static int can_release(struct inode *inode, struct file *file)
{
  CAN_DEV *dev = (CAN_DEV *) file->private_data;
  CAN_MSGOBJ *obj;
  CAN_ERROR  *info;
  int i;

  /* independently of the minor number, look for any owned objects */
  for (i = I527_FIRST_OBJ; i < I527_MSG_OBJS; i++) 
    {
      obj = dev->objs[i];
      if (obj && obj->owner == file)
	lib_releaseobj(dev, i, obj);
    }
  /* similarly, release signal notification */
  info = lib_findfile(dev, file);
  if (info)
    info->file = 0;

  /* Check for last close  for 82527 */
  if (--(dev->usecount) == 0) 
    {
      PDEBUG("last close, disable interrupts\n");
      can_disable_irq(dev);
    }

  MOD_DEC_USE_COUNT;
  return 0;
}

/*-------------------------------------------------------------------------
 * Setup file operations
 */

static struct file_operations can_fops = {
  ioctl:	can_ioctl,
  read: 	can_read,
  write:	can_write,
  open:	        can_open,
  release:	can_release,
};

//-------------------------------------------------------------------------
static void cleanup_device(CAN_DEV *dev)
{
  int i;

  if (dev->gotirq)
    free_irq(IRQ_NUM, (void *)dev);

  for (i = 0; i<I527_MSG_OBJS; i++)
    if ( dev->objs[i]) 
      {
        if (dev->objs[i]->msg)
	  kfree(dev->objs[i]->msg);
	kfree(dev->objs[i]);
	dev->objs[i] = NULL;
      }   
}

//-------------------------------------------------------------------------
int __init init_device(void)
{
  int i;
  int nRet = 0;
  CAN_DEV *dev = &gsDev;

  printk(KERN_INFO CAN_TEXT "setting up device, base %x, irq %i\n",
	                                            IO_ADDR, IRQ_NUM);

  gpsCache = kmem_cache_create("jcan-packet", sizeof(CAN_MSG),
			       0, SLAB_HWCACHE_ALIGN, NULL, NULL);
  if (!gpsCache) 
    {
      printk(KERN_ERR CAN_TEXT "cannot allocate cache for packets\n");
      return -ENOMEM;
    }

  memset(dev, 0, sizeof(*dev));

  // Allocate all message objects
  for (i = 1; i<I527_MSG_OBJS; i++) 
    {
      CAN_MSGOBJ *obj;
      if (!(obj = kmalloc(sizeof(CAN_MSGOBJ), GFP_KERNEL)))
	break;
      memset(obj, 0, sizeof(*obj));
      obj->regbase = i * I527_MSG_OFF;
      spin_lock_init(&obj->lock);
   
      dev->objs[i] = obj;
      obj->msg = kmalloc(CAN_BUFSIZE * sizeof(CAN_MSG *),
			 GFP_KERNEL);
      if (!obj->msg) break;
      memset(obj->msg, 0, CAN_BUFSIZE * sizeof(CAN_MSG *));
    }
  if (i < I527_MSG_OBJS) 
    {
      nRet = -ENOMEM; goto fail;
    }

  // map device
  dev->base = (long) ioremap(IO_ADDR, IO_RANGE ); 
  if (! dev->base) 
    {
      printk(KERN_ERR CAN_TEXT "I/O map at 0x%x busy\n", IO_ADDR);   
      goto fail;
    }
  else
    printk("jcan: remapped to: 0x%lx\n", (long)dev->base);

    /* Wait queue initialization */
  init_waitqueue_head(&dev->error_q);
  for (i=0; i<I527_MSG_OBJS; i++) 
  {
    if (dev->objs[i]) 
    {
      init_waitqueue_head(&dev->objs[i]->read_q);
      init_waitqueue_head(&dev->objs[i]->write_q);
    }
  }
#ifdef __powerpc__
  // Make IRQ3 edge sensitive.
  ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= 0x02000000;
  nRet = request_8xxirq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev); 
#else
  nRet = request_irq(IRQ_NUM, can_interrupt, 0, CAN_NAME, (void *)dev); 
#endif
  if (nRet) 
    {
      printk(KERN_ERR CAN_TEXT "dev: can't get irq %i\n", IRQ_NUM);
      goto fail;
    }

  dev->gotirq = 1;
  can_reset_i82527(dev); /* can't fail */

  /* Register the char device */
  nRet = register_chrdev(CAN_MAJOR, CAN_NAME, &can_fops);
  if (nRet < 0) 
    {
      printk(KERN_ERR CAN_TEXT "can't get major %d\n", CAN_MAJOR);
      goto fail;
    }
  dev->registered = 1;   
  return 0; /* success */

 fail:
  cleanup_device(dev);
  return nRet;
}

#ifdef MODULE
//MODULE_LICENSE("GPL");
MODULE_AUTHOR("George Cewe");
MODULE_DESCRIPTION("Driver for i82527 CAN controller");

//------------------------------------------------------------------------
static int can_init(void)
{
  int nRet;

  nRet = init_device();

  if(!nRet)
    printk(KERN_INFO CAN_TEXT "loaded (%sdebugging mode)\n",
	                                debug ? "" : "non-");
  return nRet;
}


//------------------------------------------------------------------------
static void can_cleanup(void)
{
  cleanup_device(&gsDev);

  if (gsDev.registered) 
      unregister_chrdev(CAN_MAJOR, CAN_NAME);

  if (gpsCache) 
    kmem_cache_destroy(gpsCache);

  printk(KERN_INFO CAN_TEXT "driver removed\n");
}

module_init(can_init);
module_exit(can_cleanup);

#endif

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: submission of CAN driver for i82527 chip
  2003-03-20 17:54 submission of CAN driver for i82527 chip george cewe
@ 2003-03-20 18:08 ` Jeff Garzik
  0 siblings, 0 replies; 2+ messages in thread
From: Jeff Garzik @ 2003-03-20 18:08 UTC (permalink / raw)
  To: george cewe; +Cc: linux-kernel

On Thu, Mar 20, 2003 at 12:54:36PM -0500, george cewe wrote:
> Dear Kernel Maintainer;
> 
> Please find enclosed 2 files of a driver for Intel 82527 CAN Controller,
> This driver is a simplified version of 'ocan' driver by Allesandro Rubini.
> 
> 'jcan' is a character driver for kernel 2.4 and up, I inserted it in
> linux_root/drivers/char directory.
> I tested it on embedded MPC823 platform, but it should be running on other
> platforms as well.

Marcelo bounced this to me...  I've reviewed it and have some minor
cleanups and fixes for it.  I'll send when I get home.

Mostly it looks ok...

	Jeff




^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2003-03-20 17:57 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-20 17:54 submission of CAN driver for i82527 chip george cewe
2003-03-20 18:08 ` Jeff Garzik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox