一、环境介绍
宿主机: Redhat 6.3 32位
目标开发板型号: 友善之臂Tiny4412
目标开发板操作系统: 使用busybox制作,内核使用官方3.5内核。
使用的编译器版本: 友善之臂光盘里自带的交叉编译器版本4.5.1
三、驱动代码
下面使用IIC子系统框架编写EEPROM的驱动,驱动端代码使用杂项字符设备框架,并且实现了文件指针偏移;在应用层可以将EEPROM当做一个255字节大小的文件进行编程读写。
#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 驱动端代码
#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 应用端测试代码
#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 编译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 测试效果