Messages in this thread | | | From | "george cewe" <> | Subject | submission of CAN driver for i82527 chip | Date | Thu, 20 Mar 2003 12:54:36 -0500 |
| |
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.
/* * 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__ */
/* * 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
| |