⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c.c

📁 自己编写的linux下的i2c驱动程序,是基于lpc2200编写的,已经测试ok
💻 C
字号:

#define IN_I2C

#ifndef __KERNEL__
    #define __KERNEL__
#endif
#ifndef MODULE
    #define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>

#include <linux/kernel.h>       /* printk() */
//#include <linux/malloc.h>       /* kmalloc() */
#include <linux/slab.h>         /* kmalloc() */
#include <linux/fs.h>           /* everything... */
#include <linux/errno.h>        /* error codes */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>        /* O_ACCMODE */

#include <asm/system.h>         /* cli(), *_flags */
#include <linux/init.h>
#include <asm/uaccess.h>

#include <asm/arch/irqs.h>
#include <asm/arch/hardware.h>
#include <asm/io.h>

#include "i2c.h"

#define I2C_MAJOR_NR	122
#define MAJOR_NR 	major
#define DEVICE_NAME	"I2C"
						
#define i2cStart()	outl(I2CON_STA, I2CONSET)
#define i2cStop()	outl(I2CON_STO, I2CONSET)
#define i2c_enInt()	outl(1 << IRQ_I2C, VICIntEnable)
#define i2c_disInt()	outl(1 << IRQ_I2C, VICIntEnClr)

static int i2c_open(struct inode *inode, struct file *filp);
static int i2c_release(struct inode *inode, struct file *filp);
static ssize_t i2c_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static ssize_t i2c_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
static int i2c_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param);
static void i2c_irq_handle(int irq, void *dev_id, struct pt_regs *regs);

int  i2c_init(void);
void i2c_cleanup(void);


module_init(i2c_init);
module_exit(i2c_cleanup);

MODULE_PARM(major, "i");

MODULE_LICENSE("Proprietary");
MODULE_DESCRIPTION("");
MODULE_SUPPORTED_DEVICE("uClinux2.4.x LPC2200 i2c");
MODULE_AUTHOR("WuWengang");

static int major = I2C_MAJOR_NR;
static u16 i2c_opentimes = 0;
static u32 pinsel0Save;
static u8 i2c_addr;
static volatile size_t i2c_count;
static size_t i2c_max;
static u8 *i2c_buf;
static struct semaphore i2c_devSem;
static struct semaphore i2c_intSem;

static struct file_operations i2c_fops =
{
	owner:		THIS_MODULE,
#if 0
	llseek:		i2c_llseek,
#endif
	read:		i2c_read,
	write:		i2c_write,
	ioctl:		i2c_ioctl,
	open:		i2c_open,
	release:	i2c_release,
};

int i2c_init(void)
{
	int result;

	result = register_chrdev(MAJOR_NR, DEVICE_NAME, &i2c_fops);
	if(result < 0)
	{
		printk(KERN_ERR DEVICE_NAME ":Unable to get major %d):\n", MAJOR_NR);
		return result;
	}

	if(0 == MAJOR_NR)
	{
		MAJOR_NR = result;
	}

	
	printk(KERN_INFO DEVICE_NAME "wwg's i2c driver:init OK(: \n");
	return 0;
}

void i2c_cleanup(void)
{
	unregister_chrdev(MAJOR_NR, DEVICE_NAME);
}

static int i2c_open(struct inode *inode, struct file *filp)
{
	unsigned long flag;
	
	local_irq_save(flag);
	if(0 == i2c_opentimes++)
	{
		request_irq(IRQ_I2C, i2c_irq_handle, SA_INTERRUPT, DEVICE_NAME, NULL);
		sema_init(&i2c_devSem, 1);
		pinsel0Save = (inl(PINSEL0) & 0x000000f0);
		outl((inl(PINSEL0)&(~0x000000f0))|0x00000050, PINSEL0);
		outl(0x2c, I2CONCLR);
		outl(I2CON_I2EN, I2CONSET);
	}
	local_irq_restore(flag);
	
	filp->private_data = (void *)MINOR(inode->i_rdev);
	MOD_INC_USE_COUNT;
	printk(KERN_INFO "open i2c address(%d)\n", (u32)(filp->private_data));
	return 0;
}

static int i2c_release(struct inode *inode, struct file *filp)
{
	unsigned long flag;
	
	MOD_DEC_USE_COUNT;
	
	local_irq_save(flag);
	if(0 == --i2c_opentimes)
	{
		outl((inl(PINSEL0)&(~0x000000f0))|pinsel0Save, PINSEL0);
		free_irq(IRQ_I2C, NULL);
	}
	local_irq_restore(flag);
	
	return 0;
}

static ssize_t i2c_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	ssize_t retVal;
	
	if(!access_ok(VERIFY_READ, (void *)buf, count))
	{
		return -EFAULT;
	}
	
	down_interruptible(&i2c_devSem);
	i2c_buf = (void *)buf;
	i2c_addr = ((u32)filp->private_data) & 0xfe;
	i2c_max = count;
	i2c_count = 0;
	
	sema_init(&i2c_intSem, 0);
	i2c_enInt();
	i2cStart();
	if(down_interruptible(&i2c_intSem))
    {
		up(&i2c_devSem);
    	return -ERESTARTSYS;
    }

	retVal = i2c_count;
	up(&i2c_devSem);
	return retVal;
}

static ssize_t i2c_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	ssize_t retVal;
	
	if(!access_ok(VERIFY_WRITE, (void *)buf, count))
	{
		return -EFAULT;
	}
	
	down_interruptible(&i2c_devSem);
	i2c_buf = buf;
	i2c_addr = ((u32)filp->private_data) | 0x01;
	i2c_max = count;
	i2c_count = 0;
	
	sema_init(&i2c_intSem, 0);
	i2c_enInt();
	i2cStart();
	if(down_interruptible(&i2c_intSem))
    {
    	return -ERESTARTSYS;
    }

	retVal = i2c_count;
	up(&i2c_devSem);
	return retVal;
}

static int i2c_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	if (arg < 4)
	{
		arg = 4;
	}
	
	switch(cmd)
	{
		case I2C_SET_CLH:
			outl(arg, I2SCLH);
			break;
		case I2C_SET_CLL:
			outl(arg, I2SCLL);
			break;
		default:
			return -ENOTTY;
	}

	return 0;
}

static void i2c_irq_handle(int irq, void *dev_id, struct pt_regs *regs)
{
	u8 data;
	data = inl(I2STAT);
	switch(data)
	{
		case 0x08:                     /* 已发送起始条件,与0x18相同处理 */
			//break;
		case 0x10:                     /* 已发送重复起始条件 */
			outl(i2c_addr, I2DAT);
			outl(I2CON_STA, I2CONCLR);
			break;
		case 0x18:                     /* 已发送SLA+W,并已接收应答 */
		case 0x28:                     /* 已发送I2C数据,并接收到应答 */
			if(i2c_count < i2c_max)
			{
				get_user(data, &i2c_buf[i2c_count++]);
				outl(data, I2DAT);
				break;
			}
		case 0x20:                     /* 已发送SLA+W;已接收非ACK, 与0x48处理相同 */
		case 0x30:                     /* 已发送I2DAT中的数据字节;已接收非ACK, 与0x48处理相同 */
		case 0x48:                     /* 已发送SLA+R;已接收非ACK */
			i2cStop();
		case 0x38:                     /* 在SLA+R/W或数据字节中丢失仲裁 */
			i2c_disInt();
			outl(I2CON_STA, I2CONCLR);
			up(&i2c_intSem);
			break;
		case 0x40:                     /* 已发送SLA+R;已接收ACK */
			if(i2c_max > 1)
			{
				outl(I2CON_AA, I2CONSET);
			}
			else
			{
				outl(I2CON_AA, I2CONCLR);
			}
			
			break;
		case 0x50:                     /* 已接收数据字节;已发送ACK */
			data = inl(I2DAT);
			put_user(data, &i2c_buf[i2c_count++]);
			if(i2c_count < (i2c_max - 1))
			{
				outl(I2CON_AA, I2CONSET);
			}
			else
			{
				outl(I2CON_AA, I2CONCLR);
			}
			
			break;
		case 0x58:                     /* 已接收数据字节;已发送非ACK */
			data = inl(I2DAT);
			put_user(data, &i2c_buf[i2c_count++]);
			i2cStop();
			i2c_disInt();
			up(&i2c_intSem);
			break;
		default:
			break;
	}
	
	outl(I2CON_SI, I2CONCLR);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -