All of lore.kernel.org
 help / color / mirror / Atom feed
From: "george cewe" <gcewe@copper.net>
To: <linux-kernel@vger.kernel.org>
Subject: submission of CAN driver for i82527 chip
Date: Thu, 20 Mar 2003 12:54:36 -0500	[thread overview]
Message-ID: <005501c2ef09$c6f93480$54491cd8@CEVE> (raw)

[-- 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

             reply	other threads:[~2003-03-20 17:43 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-03-20 17:54 george cewe [this message]
2003-03-20 18:08 ` submission of CAN driver for i82527 chip Jeff Garzik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='005501c2ef09$c6f93480$54491cd8@CEVE' \
    --to=gcewe@copper.net \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.