i2c驱动之普通设备驱动1

        这篇博客分析i2c的普通设备驱动,像往常的字符驱动一样,先module_init(),和module_exit(),来完成模块的加载与卸载,然后在初始化函数中注册设备号和关联文件结构体file_operaion,最后再去实现file_operation中的read,write,open,release等操作,在操作read,write,open,release时,直接控制s3c2440的i2c相关的寄存器来与i2c设备进行通信,而没有记住linux2.6.32.2内核提供的i2c-core.c文件里面的i2c_transfer方法来实现数据通信。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <mach/hardware.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>   /* printk() */
#include <linux/slab.h>   /* kmalloc() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/mm.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <asm/irq.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/regs-irq.h>
#include <mach/regs-clock.h>
#include <asm/signal.h>

volatile int f_nGetACK;
#define UINT unsigned int
#define I2C_MAGIC 'k'  
#define I2C_set _IO(I2C_MAGIC,1)
#define I2C_MAJOR 259
#define DEVICE_NAME "s3c2410_I2C"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("farsight");
MODULE_DESCRIPTION("s3c2410 I2C");

char data[128]="\0";
#define rGPECON  *(volatile unsigned int *)S3C2410_GPECON

#if 0
#define S3C2410_I2C(x) (S3C2410_IICREG(x))
#define S3C2410_IICCON	   S3C2410_I2C(0x00)
#define S3C2410_IICSTAT	   S3C2410_I2C(0x04)
#define S3C2410_IICADD	   S3C2410_I2C(0x08)
#define S3C2410_IICDS	   S3C2410_I2C(0x0c)

#define rIICCON  *(volatile unsigned int *)S3C2410_IICCON
#define rIICSTAT *(volatile unsigned int *)S3C2410_IICSTAT
#define rIICADD  *(volatile unsigned int *)S3C2410_IICADD
#define rIICDS   *(volatile unsigned int *)S3C2410_IICDS
#define rGPECON  *(volatile unsigned int *)S3C2410_GPECON
#else

#define rIICCON  *(volatile unsigned int *)i2c_base
#define rIICSTAT *(volatile unsigned int *)((unsigned int)i2c_base + 4)
#define rIICADD *(volatile unsigned int *)((unsigned int)i2c_base + 8)
#define rIICDS *(volatile unsigned int *)((unsigned int)i2c_base + 0xc)

static volatile void __iomem *i2c_base;
static struct resource		*area = NULL;
#endif

#define CLKCON 0x4c00000c
static volatile unsigned int *clkcon;
static int I2C_major = I2C_MAJOR;
static struct cdev I2C_cdev;

/*********************************************************************************************
 * name:		iic_int_24c04()
 * func:		IIC interrupt handler
 * para:		none
 * ret:		none
 * modify:
 * comment:		
 *********************************************************************************************/
static irqreturn_t  iic_int_24c04(int irq,void *dev_id,struct pt_regs *regs)
{
	f_nGetACK = 1;
	return IRQ_HANDLED ;
}

/*********************************************************************************************
 * name:		iic_write_24c040 
 * func:		write data to 24C040
 * para:		unSlaveAddr --- input, chip slave address
 *			unAddr		--- input, data address
 *			ucData    	--- input, data value
 * ret:		none
 * modify:
 * comment:		
 *********************************************************************************************/
void iic_write_24c040(UINT unSlaveAddr,UINT unAddr,UINT ucData)
{
	f_nGetACK = 0;

	// Send control byte
	rIICDS = unSlaveAddr;			// 0xa0
	rIICSTAT = 0xf0;			// Master Tx,Start
	while(f_nGetACK == 0);// Wait ACK					
	f_nGetACK = 0;
	//Send address
	rIICDS = unAddr;
	rIICCON = 0xaf;				// Resumes IIC operation.
	while(f_nGetACK == 0);// Wait ACK
	f_nGetACK = 0;

	// Send data 
	rIICDS = ucData;
	rIICCON = 0xaf;				// Resumes IIC operation.
	while(f_nGetACK == 0);// Wait ACK
	f_nGetACK = 0;

	// End send
	rIICSTAT = 0xd0;			// Stop Master Tx condition
	rIICCON = 0xaf;				// Resumes IIC operation.
	mdelay(10);				// Wait until stop condtion is in effect.
}

/*********************************************************************************************
 * name:		iic_read_24c040
 * func:		read data from 24C040
 * para:		unSlaveAddr --- input, chip slave address
 *			unAddr		--- input, data address
 *			pData    	--- output, data pointer
 * ret:		none
 * modify:
 * comment:		
 *********************************************************************************************/
void iic_read_24c040(UINT unSlaveAddr,UINT unAddr,unsigned char *pData)
{
	char cRecvByte;

	f_nGetACK = 0;

	//Send control byte 
	rIICDS = unSlaveAddr;				// 0xa0
	rIICSTAT = 0xf0;				// Master Tx,Start
	while(f_nGetACK == 0);// Wait ACK
	f_nGetACK = 0;

	// Send address
	rIICDS = unAddr;
	rIICCON = 0xaf;					// Resumes IIC operation.
	while(f_nGetACK == 0);// Wait ACK
	f_nGetACK = 0;

	//Send control byte 
	rIICDS = unSlaveAddr;				// 0xa0
	rIICSTAT = 0xb0;				// Master Rx,Start
	rIICCON = 0xaf;					// Resumes IIC operation.   
	mdelay(100);
	while(f_nGetACK == 0);// Wait ACK
	f_nGetACK = 0;

	//Get data 
	rIICCON = 0x2f;
	mdelay(1);

	// Get data 
	cRecvByte = rIICDS;

	// End receive 
	rIICSTAT = 0x90;				// Stop Master Rx condition 
	rIICCON = 0xaf;					// Resumes IIC operation.
	mdelay(10);					// Wait until stop condtion is in effect.

	*pData = cRecvByte;
}

ssize_t I2C_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
	ssize_t result = 0;
	int i;

	for(i=0; i<count; i++) 
		data[i]=0;   
	// Read 16 byte from 24C04
	for(i=0; i<count; i++)
	{
		iic_read_24c040(0xa0, i, &(data[i])); 
	}
	data[count]='\0';

	if (copy_to_user (buff, data, count)) 
		result = -EFAULT;
	
	result=count;
	return result;
}

ssize_t I2C_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	int i;
	ssize_t ret = 0;
	//printk ("Writing %d bytes\n", count);
	if (count>127) return -ENOMEM;
	if (count<0) return -EINVAL;
	if (copy_from_user (data, buf, count))
	{
		ret = -EFAULT;
	}
	else {
		data[127]='\0';
		//printk ("Received: %s\n", data);


		// Write 0 - 16 to 24C04
		for(i=0; i<count; i++)
		{
			iic_write_24c040(0xa0, i, data[i]); 
			//mdelay(100);
		}
		//printk("write end\n");
		ret = count;
	}
	return ret;
}

static int I2C_open(struct inode *inode ,struct file *file)	
{
	int result;
	// Initialize iic
	rIICADD = 0x10;					// S3C2410X slave address 
	rIICCON = 0xaf;					// Enable ACK, interrupt, SET IICCLK=MCLK/16
	rIICSTAT = 0x10;				// Enable TX/RX 

	rGPECON =(rGPECON&((~0xf)<<28))+(0xa<<28);
	//printk("rGPECON=%x\n",rGPECON);

	result = request_irq (IRQ_IIC, iic_int_24c04, IRQF_DISABLED, DEVICE_NAME, NULL);
	if (result) {
		printk(KERN_INFO "I2C: can't get assigned irq\n");
	}

	//printk(KERN_NOTICE"open the I2C now!\n");
	return 0;
}

static int I2C_release(struct inode *inode,struct file *file)
{
	free_irq(IRQ_IIC, NULL);//ÊÍ·ÅÖжÏ×ÊÔŽ
	//printk("I2C closed\n");
	return 0;
}

static int I2C_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg)
{	
	return 0;	
}
//œ«É豞ע²áµœÏµÍ³Ö®ÖÐ
static void I2C_setup_dev(struct cdev *dev,int minor,struct file_operations *fops)
{
	int err;
	int devno=MKDEV(I2C_major,minor);
	cdev_init(dev,fops); 
	dev->owner=THIS_MODULE;
	dev->ops=fops; 
	err=cdev_add(dev,devno,1);
	if(err)
		printk(KERN_INFO"Error %d adding I2C %d\n",err,minor);
}

static struct file_operations I2C_remap_ops={
	.owner=THIS_MODULE,
	.open=I2C_open, 
	.write = I2C_write,
	.read = I2C_read,
	.release=I2C_release, 
	.ioctl=I2C_ioctl,
};

//×¢²áÉ豞Çý¶¯³ÌÐò£¬Ö÷ÒªÍê³ÉÖ÷É豞ºÅµÄ×¢²á
static int __init s3c2410_I2C_init(void)
{
	int result;

	dev_t dev = MKDEV(I2C_major,0);
	if(I2C_major)
		result = register_chrdev_region(dev,1,DEVICE_NAME);
	else
	{	
		result = alloc_chrdev_region(&dev,0,1,DEVICE_NAME);
		I2C_major = MAJOR(dev);
	}

	if(result<0)
	{
		printk(KERN_WARNING"I2C:unable to get major %d\n",I2C_major);		
		return result;
	}

	if(I2C_major == 0)
		I2C_major = result;

	printk(KERN_NOTICE"[DEBUG] I2C device major is %d\n",I2C_major);

	__raw_writel( (__raw_readl(S3C2410_CLKCON) | (1 << 16)), S3C2410_CLKCON);

#if 0
	printk("\n  S3C2410_CLKCON = %x \n", __raw_readl(S3C2410_CLKCON));
	area = request_mem_region(0x54000000, 16,"I2C");
#endif

	i2c_base = ioremap(0x54000000, 16);
	clkcon = ioremap(CLKCON, 0x4);

	printk(KERN_INFO"i2c clock = %d\n", *clkcon & (0x1 << 16));
	*clkcon |= 0x1 << 16;

	I2C_setup_dev(&I2C_cdev,0,&I2C_remap_ops);

	return 0;
}
//Çý¶¯Ä£¿éжÔØ
static void s3c2410_I2C_exit(void)
{
#if 0
	if (area) {
		release_resource(area);
		kfree(area);
	}
#endif
	cdev_del(&I2C_cdev);
	unregister_chrdev_region(MKDEV(I2C_major,0),1);
	printk("I2C device uninstalled\n");
}

module_init(s3c2410_I2C_init);
module_exit(s3c2410_I2C_exit);
在编译驱动的时候提示出错:I2C_drv.c:7:26: error: asm/hardware.h: No such file or directory        
        原因是是早期版本的头文件,在该版本位置已经换掉了。
        解决办法是使用find -name "xxx.h"查找到头文件对应的位置,然后将头文件添加进去。

       另外一个错误:2410_I2C/I2C_drv.c:285: error: 'SA_INTERRUPT' undeclared (first use in this function
       解决办法是SA_INTERRUPT定义在早期版本的内核,当前的内核没有定义该变量,而采用IRQF_DISABLE来代替

       以上归根结底是版本的问题。


测试应用程序

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ioctl.h>

#define WATCHDOG_MAGIC 'k' 
#define FEED_DOG _IO(WATCHDOG_MAGIC,1)

int main(int argc,char **argv)
{
	int fd;
	char buff[]="farsight";
	//Žò¿ªI2C
	fd=open("/dev/i2c",O_RDWR);
	 
	if(fd<0)
	{
		printf("cannot open the I2C device\n");
		return -1;
	}
	
   	sleep(1);
	printf("buff_write=%s\n",buff);
	write (fd, buff, sizeof(buff));

	//printf(" read  now!\n");
	memset (buff, '\0', sizeof(buff));
	//printf ("Read returns %d\n", read (fd, buff, sizeof(buff)));
   	read (fd, buff, sizeof(buff));
	//read (fd, buff, 3);
	printf ("buff_read = %s\n", buff);
	
	close(fd);
      	//  while(1);
   	//   printf("end\n");
	return 0;

}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckywang1103

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值