Embedded Linux development: writing EEPROM driver_using miscellaneous character device framework

1. Environmental introduction

Host:  Redhat 6.3 32 bit

Target development board model:  Friendly arm Tiny4412

Target development board operating system:   use busybox production, the kernel uses the official 3.5 kernel.

Compiler version used:   Cross compiler version 4.5.1 included in the Friendly Arm CD

Two, EEPROM chip introduction

Three, drive code

   The following uses the IIC subsystem framework to write the EEPROM driver, the driver code uses the miscellaneous character device framework, and realizes the file pointer offset; in the application layer, the EEPROM can be used as a 255-byte file for programming and reading.

3.1 Device code

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static struct i2c_client *i2c_dev=NULL;
static struct i2c_adapter *adap=NULL;
static struct i2c_board_info info=
{
	.type="tiny4412_eeprom",
	.addr=0x50, /*设备地址*/
};

static int __init tiny4412_drv_init(void)
{	
	/*根据总线编号获取是适配器*/
	adap=i2c_get_adapter(0);
	/*注册IIC设备端*/
	i2c_dev=i2c_new_device(adap,&info);
    printk("IIC设备端: 驱动安装成功\n");
    return 0;
}

static void __exit tiny4412_drv_cleanup(void)
{
	/*注销IIC设备*/
	i2c_unregister_device(i2c_dev);
	i2c_put_adapter(adap);
    printk("IIC设备端: 驱动卸载成功\n");
}

module_init(tiny4412_drv_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(tiny4412_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/

MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/

3.2 Driver code

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
static struct work_struct work;
static struct i2c_client *eeprom_client;

#define MAX_SIZE 255  //EEPROM大小
#define EEPROM_PAGE  16 //页字节大小

static u8 eeprom_buff[255];
static int tiny4412_open(struct inode *inode, struct file *file)
{
	printk("tiny4412_open-->ok\n");
	return 0;
}

static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t size, loff_t *seek)
{	
	unsigned long err;
	//判断位置是否超出范围
	if(*seek+size>MAX_SIZE)
	{
		size=MAX_SIZE-*seek;
	}
	//读取数据
	i2c_smbus_read_i2c_block_data(eeprom_client,*seek,size,eeprom_buff);
	err=copy_to_user(buf,eeprom_buff,size);
	if(err!=0)return -1;
	*seek+=size;
	return size;
}

static ssize_t tiny4412_write(struct file *file, const char __user *buf, size_t size, loff_t *seek)
{
	size_t write_ok_cnt=0;
	unsigned long err;
	err=copy_from_user(eeprom_buff,buf,size);
	if(err!=0)return -1;
	//判断位置是否超出范围
	if(*seek+size>MAX_SIZE)
	{
		size=MAX_SIZE-*seek;
	}
	
	int write_byte=0;
	u8 *write_p=eeprom_buff;
	while(1)
	{
		if(size>EEPROM_PAGE)
		{
			write_byte=EEPROM_PAGE;
			size-=EEPROM_PAGE;
		}
		else
		{
			write_byte=size;
		}
		
		//写数据
		i2c_smbus_write_i2c_block_data(eeprom_client,*seek,write_byte,write_p);
		*seek+=write_byte;
		write_p+=write_byte;
		write_ok_cnt+=write_byte;  //记录写成功的字节数
		//等待写完成
		msleep(10);
		if(write_byte==size)break; //写完毕
	}
	return write_ok_cnt;
}

/*
filp:待操作的设备文件file结构体指针
off:待操作的定位偏移值(可正可负)
whence:待操作的定位起始位置
返回:返回移位后的新文件读、写位置,并且新位置总为正值
定位起始位置
  SEEK_SET:0,表示文件开头
  SEEK_CUR:1,表示当前位置
  SEEK_END:2,表示文件尾
*/
static loff_t tiny4412_llseek(struct file *filp, loff_t offset, int whence)
{
	loff_t newpos = 0;
	switch(whence)
	{
		case SEEK_SET:
			newpos = offset;
			break;
		case SEEK_CUR:
			newpos = filp->f_pos + offset;
			break;
		case SEEK_END:
			if(MAX_SIZE+offset>=MAX_SIZE)
			{
				newpos=MAX_SIZE;
			}
			else
			{
				newpos = MAX_SIZE + offset;
			}
			break;
		default:
			return -EINVAL;//无效的参数
	}
	filp->f_pos = newpos;
	return newpos;
}

static int tiny4412_release(struct inode *inode, struct file *file)
{
	printk("tiny4412_release-->ok\n");
	return 0;
}

static struct file_operations fops=
{
	.open=tiny4412_open,
	.read=tiny4412_read,
	.write=tiny4412_write,
	.release=tiny4412_release,
	.llseek=tiny4412_llseek
};

/*
Linux内核管理驱动---设备号
设备号是一个unsigned int 的变量--32位。
设备号=主设备号+次设备号
*/
static struct miscdevice misc=
{
	.minor = MISC_DYNAMIC_MINOR,  /*次设备号填255表示自动分配     主设备号固定为10*/
	.name = "tiny4412_eeprom",  /*/dev目录下文件名称*/
	.fops = &fops, /*文件操作接口*/
};


static int tiny4412_probe(struct i2c_client *client, const struct i2c_device_id *device_id)
{
	printk("probe调用成功:%#X\n",client->addr);
	eeprom_client=client;
	
	/*1. 杂项设备的注册函数*/
	misc_register(&misc);
	
	return 0;
}

static int tiny4412_remove(struct i2c_client *client)
{
	/*2. 杂项设备的注销函数*/
	misc_deregister(&misc);
	printk("remove调用成功.\n");
	return 0;
}

static struct i2c_device_id id_table[]=
{
	{"tiny4412_eeprom",0},
	{}
};

static struct i2c_driver drv=
{
	.probe=tiny4412_probe,
	.remove=tiny4412_remove,
	.driver=
	{
		.name="eeprom_iic"
	},
	.id_table=id_table
};

static int __init tiny4412_drv_init(void)
{	
	/*注册IIC驱动端*/
	i2c_add_driver(&drv);
    printk("IIC驱动端: 驱动安装成功\n");
    return 0;
}

static void __exit tiny4412_drv_cleanup(void)
{
	/*注销IIC驱动端*/
	i2c_del_driver(&drv);
    printk("IIC驱动端: 驱动卸载成功\n");
}

module_init(tiny4412_drv_init);    /*驱动入口--安装驱动的时候执行*/
module_exit(tiny4412_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/

MODULE_LICENSE("GPL");  /*设置模块的许可证--GPL*/

3.3 Application-side test code

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define EEPROM_DEV "/dev/tiny4412_eeprom"

int main(int argc,char **argv)
{
    /*1. 打开设备文件*/
    int fd=open(EEPROM_DEV,O_RDWR);
    if(fd<0)
    {
        printf("%s 设备驱动打开失败.\n",EEPROM_DEV);
        return 0;
	}
    /*3.读写数据*/
	unsigned char buff[255];
	int cnt;
	int i;
	for(i=0;i<255;i++)buff[i]=i;
	cnt=write(fd,buff,255);
    printf("write成功:%d Byte\n",cnt);
	
	//偏移文件指针
	lseek(fd,SEEK_SET,0);
	
	unsigned char buff_r[255];
	cnt=read(fd,buff_r,255);
	printf("read成功:%d Byte\n",cnt);
	for(i=0;i<cnt;i++)
	{
		printf("%d ",buff_r[i]);
	}
	printf("\n");
    return 0;
}

3.4 Compile Makefile

KER_DRI=/home/wbyq/work/linux-3.5/linux-3.5
all:
	make -C $(KER_DRI) M=`pwd` modules
	cp *.ko /home/wbyq/work/rootfs/code -f
	make -C $(KER_DRI) M=`pwd` modules clean
	arm-linux-gcc app.c -o app
	cp app /home/wbyq/work/rootfs/code -f
	rm app -f
obj-m += iic_drv.o
obj-m += iic_dev.o

3.5 Test results

 

 

The following WeChat public account has a full set of C language, C++, MCU, Internet of Things, QT, Python, Linux tutorials, welcome to follow:

 

Guess you like

Origin blog.csdn.net/xiaolong1126626497/article/details/109683147