转载地址:http://blog.csdn.net/xie0812/article/details/22984527
这一篇主要是在友善的Smart210开发板上写一个符合linux的iic驱动模型的设备驱动程序,这样能有一个更感性的认识。
开发环境介绍:
主机linux版本:fedora14
开发板:友善的Smart210
嵌入式linux版本:linux-3.0.8(友善光盘自带的)
交叉编译器:arm-linux-gcc-4.5.1(友善光盘自带的)
硬件简单介绍:
、
这是从友善的原理图上截下的图,这个图没有什么复杂的,从图可以看出来EEPROM是和s5pv210上的第0个iic适配器连接的,但我是用第1个适配器写的,所以用线直接把适配器1的引脚和EEPROM相连的,我这边正好有这个项目需要,所以这样写了,你可以直接用适配器0就行了,在写驱动的时候我会说怎么选iic适配器0和iic适配器1。
我们前面说过iic驱动模型是采用分层思想的,也即总线驱动和设备驱动是分开的。那它们怎么相互联系了?总得要一个什么东西来做个匹配吧,就像以前的地下工作者,需要接头暗号,要不然就乱套了,哈哈!iic总线和设备之间是用名字做匹配的,那好了,那就先得把设备的名字告诉总线吧,下面就是如何在总线上注册设备信息了。
注册设备信息
阅读linux下的Documentation/i2c/instantiating-devices 文档可以知道有两种方式可以注册,咱们只说前一种。打开:linux-3.0.8/arch/arm/mach-s5pv210/mach-mini210.c这个.c文件。就是在这个文件中填写咱们设备的信息的,这就是所说的bsp文件。首先添加头文件#include
因为linux专门问iic接口的eeprom提供了相应的数据结构,要是不加,肯定要报错。接下来添加如下信息:
[cpp]
view plain
copy
- static struct at24_platform_data at24c08 = {
- .byte_len = SZ_8K / 8,
- .page_size = 16,
[cpp]
view plain
copy
- };
然后添加如下的信息,主要把eeprom的信息包装成符合iic模型中的设备信息的格式
[cpp]
view plain
copy
- static struct i2c_board_info i2c_devices[] __initdata = {
- {
- I2C_BOARD_INFO("at24c08b", 0x50),
- .platform_data = &at24c08,
- },
最后在mini210_machine_init函数中把上面写的信息注册到iic总线上
[cpp]
view plain
copy
- static void __init mini210_machine_init(void)
- {
- ...
- s3c_i2c2_set_platdata(&i2c2_data);
- i2c_register_board_info(0, mini210_i2c_devs0,
- ARRAY_SIZE(mini210_i2c_devs0));
-
-
- "color:#ff0000;">i2c_register_board_info(1, i2c_devices,
- ARRAY_SIZE(i2c_devices));
- i2c_register_board_info(2, mini210_i2c_devs2,
- ARRAY_SIZE(mini210_i2c_devs2));
- }
这就算把设备信息注册上了,重新编译一下你的linux内核吧,然后把编译好的内核烧进开发板,下面开始就是真真的驱动部分了。
设备驱动编写
首先咱们是用eeprom读写一些数据,数据量不会很大,所以它应该是个字符设备,尽管它从iic驱动模型的角度说,是iic设备,起始这并不矛盾。因为字符设备里包括了一部分的iic设备,下面就是整个驱动了
[cpp]
view plain
copy
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define DEVICE_NAME "at24c08"
-
-
- struct At24c08_dev
- {
- char name[30];
- struct i2c_client *at24c08_client;
- struct miscdevice at24c08_miscdev;
- unsigned short current_pointer;
- };
-
- struct At24c08_dev *At24c08_devp;
[cpp]
view plain
copy
-
- static int at24c08_open(struct inode *inode,struct file *file){
-
- file->private_data = At24c08_devp;
- return 0;
- }
[cpp]
view plain
copy
-
- static ssize_t
- at24c08_read(struct file *file,char *buf,size_t count,loff_t *ppos)
- {
- int i = 0;
- int transferred = 0;
- char value;
- char my_buff[50];
-
- struct At24c08_dev *dev = (struct At24c08_dev *)file->private_data;
-
- dev->current_pointer = 0;
- if(i2c_check_functionality(dev->at24c08_client->adapter,I2C_FUNC_SMBUS_READ_BYTE_DATA))
- {
- while(transferred < count)
- {
- msleep(10);
- value = i2c_smbus_read_byte_data(dev->at24c08_client,dev->current_pointer +i);
- my_buff[i++] = value;
- transferred ++;
- }
- if(!copy_to_user(buf,(void *)my_buff,transferred))
- printk("The data copying from kernel to userspace success!
");
- else
- printk("Mybe some errors has occured
");
- dev->current_pointer +=transferred;
- }
- return transferred;
- }
[cpp]
view plain
copy
-
- static ssize_t
- at24c08_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
- {
- int i = 0;
- int transferred = 0;
- char my_buff[50];
-
- struct At24c08_dev *dev = (struct At24c08_dev *)file->private_data;
- dev->current_pointer = 0;
- if(i2c_check_functionality(dev->at24c08_client->adapter,I2C_FUNC_SMBUS_BYTE_DATA))
- {
- if(!copy_from_user(my_buff,buf,count))
- {
- printk("The data copying from userspace to kernel success!
");
- while(transferred < count)
- {
- msleep(10);
- i2c_smbus_write_byte_data(dev->at24c08_client,dev->current_pointer + i,my_buff[i]);
- i ++;
- transferred ++;
- }
- dev->current_pointer +=transferred;
- }
- else
- printk("Mybe some errors has occured
");
- }
- return transferred;
- }
-
- static const struct file_operations at24c08_fops ={
- .owner = THIS_MODULE,
- .open = at24c08_open,
- .read = at24c08_read,
- .write = at24c08_write,
- };
[cpp]
view plain
copy
-
- static int __devinit at24c08b_probe(struct i2c_client *client,const struct i2c_device_id *id)
- {
- int ret;
-
- #ifdef DEBUG
- printk("The routine of probe has started(for binding device)
");
- #endif
-
- At24c08_devp = kmalloc(sizeof(struct At24c08_dev),GFP_KERNEL);
- if(!At24c08_devp)
- {
- return ret = -ENOMEM;
- }
- memset(At24c08_devp,0,sizeof(struct At24c08_dev));
-
- At24c08_devp->at24c08_client = client;
-
- At24c08_devp->at24c08_miscdev.minor = MISC_DYNAMIC_MINOR;
- At24c08_devp->at24c08_miscdev.name = DEVICE_NAME;
- At24c08_devp->at24c08_miscdev.fops = &at24c08_fops;
-
- ret = misc_register(&At24c08_devp->at24c08_miscdev);
- #ifdef DEBUG
- printk("The driver of at24c08 has registered!
");
- #endif
- return ret;
- }
-
- static int __devexit at24c08b_remove(struct i2c_client *client)
- {
- misc_deregister(&At24c08_devp->at24c08_miscdev);
- #ifdef DEBUG
- printk("The routine of remove has implemented!
");
- #endif
- return 0;
- }
- static const struct i2c_device_id at24c08b_id[]={
- {"at24c08",0},
- {}
- };
-
- MODULE_DEVICE_TABLE(i2c,at24c08b_id);
-
- static struct i2c_driver at24c08b_driver = {
-
- .driver = {
- .name = "at24c08",
- .owner=THIS_MODULE,
- },
- .probe = at24c08b_probe,
- .remove=__devexit_p(at24c08b_remove),
- .id_table =at24c08b_id,
- };
-
- static int __init at24c08b_init(void)
- {
- #ifdef DEBUG
- printk(KERN_NOTICE"The driver of at24c08 is insmod!
");
- #endif
- return i2c_add_driver(&at24c08b_driver);
- }
-
- void at24c08b_exit(void)
- {
- #ifdef DEBUG
- printk(KERN_NOTICE"at24c0b is rmmod!
");
- #endif
- i2c_del_driver(&at24c08b_driver);
- }
-
-
- MODULE_DESCRIPTION("at24c08b eeprom driver");
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_AUTHOR("xie yingdong");
-
- module_init(at24c08b_init);
- module_exit(at24c08b_exit);
上面就是完整的eeprom驱动,当然驱动写完了,需要写个简单的Makefile来编译这个驱动,好吧,下面就是Makefile文件的内容
[cpp]
view plain
copy
- obj-m:=eeprom-driver.o
- KDIR = /tmp/linux-3.0.8
-
- all:
- $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules ARCH=arm CROSS_COMPILE=arm-linux-
- clean:
- @rm -rf eeprom-driver*.o
上面的Makefile文件很是简单,就不做过多的解释了。当把驱动编译好了,用动态的方式挂载到了linux内核上后,你还得做个简单的测试程序,来验证咱们写的驱动工作是否正常,下面就直接贴出来吧。
[cpp]
view plain
copy
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main(void)
- {
- int i;
- char value[19] = "eeprom-driver test!";
- char backvalue[19];
-
- int fd;
- fd = open("/dev/at24c08",O_RDWR);
- if(fd<0){
- printf("Open at24c08 device failed!
");
- exit(1);
- }
- write(fd,value,19);
- printf("The string writing to eeprom : %s
",value);
- printf("##################################################
");
- sleep(1);
- read(fd,backvalue,19);
- printf("The string reading from eeprom : %s
",backvalue);
- close(fd);
- return 0;}
哈哈,驱动就写完了,我自己测试了,没问题,你可以试试,下一篇我们会分析iic总线驱动。