Linux嵌入式驱动初体验(七)--- LED驱动之字符设备篇
2010-09-26 21:58
513 查看
Linux中的设备可以分为三类:字符设备、块设备、网络设备,对于上一篇文章中的驱动编写的方法,是基于platform结构的,下面我们把它改变成字符设备的驱动编写模式,原理和方法基本是一样的,只是换了一个外壳而已。
首先还是看一下依照字符设备驱动编写所设计的数据结构:
很眼熟也很简单,对于后面的两个东西的定义,你可以理解成是我多此一举了,不过其实这只是因为这个设备比较简单的原因,如果是其他设备,led_dev_t里面的内容会更多的。对于cdev这个结构体大家可能不熟悉,我先贴出来给大家看看:
它在/include/linux/cdev.h中定义,其实也都是一些大家“喜闻乐见”的东西了。好了,下面我们就又要开始填充这些函数了:
其实只看上一篇文章就可以知道这些函数的实现原理和过程了,而且上面的这些函数写的更加简短了,因为我们的目的明确,而且“不拘小节”(就是忽略了一些除错处理,不要学我,呵呵。。。)。好了,其实上面也不是重点,它们还体现不出来我们在写一个字符驱动,真正能让我们有所区别的是init和exit的编写:
MKDEV,用来给设备分配设备号,然后使用register_chrdev_region来进行字符设备的注册,使用cdev_add向系统中添加这个字符设备,使用cdev_del删除字符设备,这些也都是系统中已经有的宏定义。
最后我再把代码的剩余部分贴出来:
不用说明,只是为了保持代码的完整性。当然,这还不是字符驱动开发的全部,我们还要有测试程序:
其实和上一篇的那个测试程序是一样的,只是更好看了一点。。。不过还有一点不同时体现在代码之外的,就是对于一个字符设备来说,它还需要一个/dev下的对应的结点文件,这个文件是自己手动加入的,而且重启开发板后,这个手动创建的文件就没有了,所以为了方便起见,我写了一个简单的shell脚本,在运行这个测试程序之前运行脚本文件,就可以了:
其中的248是我自己设置的主设备号,这个号码要和驱动程序中的LED_MAJOR保持一致,而且要保证这个主设备号是空闲的。
好了,一个LED的字符驱动就写好了,之所以写的比较简单,分析的比较少,是因为其实它们和上一篇的驱动是比较类似的,在那里已经做了比较详细的分析,这里只是说明了一下字符设备驱动的编写和platform体系驱动的编写的不同,两者可以达到同样的目的,只是platform有一些面向对象的感觉,更加有章法一些。现在的学习都是在学习方法,其实对于简单的事物,我们千万不要看不起它,如果我们可以很重视的把它解决,那么遇到难的问题,用同样的方法还是可以行得通的。综上所述吧,就是学会举一反三,有很多东西,解决的模式是一定的,而且是一样的,都有相通的地方,我们学习的时候就要抓住重点,学习方法,而不是只针对一个事物学习,这样就真的是要“活到老学到老了”。
首先还是看一下依照字符设备驱动编写所设计的数据结构:
static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, .release = led_release, .write = led_write, .ioctl = led_ioctl, }; struct led_dev_t { struct cdev cdev; }; struct led_dev_t dev;
很眼熟也很简单,对于后面的两个东西的定义,你可以理解成是我多此一举了,不过其实这只是因为这个设备比较简单的原因,如果是其他设备,led_dev_t里面的内容会更多的。对于cdev这个结构体大家可能不熟悉,我先贴出来给大家看看:
struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
它在/include/linux/cdev.h中定义,其实也都是一些大家“喜闻乐见”的东西了。好了,下面我们就又要开始填充这些函数了:
static ssize_t led_write(struct file *filp, const char __user *buff, size_t s, loff_t *l) { int ret = 0; int i; unsigned char ctrl = 0; get_user(ctrl, (u8 *)buff); i = (ctrl - 0x30) & 0x03; if(i == 0) __raw_writel(_BIT(5), GPIO_P3_OUTP_CLR(GPIO_IOBASE)); else __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE)); return ret; } static int led_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long ul) { switch(cmd) { case LED_ON : __raw_writel(_BIT(5), GPIO_P3_OUTP_CLR(GPIO_IOBASE)); break; case LED_OFF : __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE)); break; default : __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE)); break; } return 0; } static int led_open(struct inode *si, struct file *filp) { __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE)); printk(DEV_NAME " success open!/n"); return 0; } static int led_release(struct inode *si, struct file *filp) { __raw_writel(_BIT(5), GPIO_P3_OUTP_SET(GPIO_IOBASE)); printk(DEV_NAME " success close!/n"); return 0; }
其实只看上一篇文章就可以知道这些函数的实现原理和过程了,而且上面的这些函数写的更加简短了,因为我们的目的明确,而且“不拘小节”(就是忽略了一些除错处理,不要学我,呵呵。。。)。好了,其实上面也不是重点,它们还体现不出来我们在写一个字符驱动,真正能让我们有所区别的是init和exit的编写:
static void led_setup_cdev(void) { int err, devno = MKDEV(led_major, 0); cdev_init(&dev.cdev, &led_fops); dev.cdev.owner = THIS_MODULE; dev.cdev.ops = &led_fops; err = cdev_add(&dev.cdev, devno, 1); if(err) printk(KERN_NOTICE "Error %d adding led", err); } static int __init led_init(void) { int result; dev_t devno = MKDEV(led_major, 0); if(led_major) result = register_chrdev_region(devno, 1, "leddrv"); else { result = alloc_chrdev_region(&devno, 0, 1, "leddrv"); led_major = MAJOR(devno); } if(result < 0) return result; led_setup_cdev(); printk(KERN_ALERT "led driver init/n"); return 0; } static void __exit led_exit(void) { unregister_chrdev_region(MKDEV(led_major, 0), 1); cdev_del(&dev.cdev); printk(KERN_ALERT "led driver exit/n"); }
MKDEV,用来给设备分配设备号,然后使用register_chrdev_region来进行字符设备的注册,使用cdev_add向系统中添加这个字符设备,使用cdev_del删除字符设备,这些也都是系统中已经有的宏定义。
最后我再把代码的剩余部分贴出来:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/cdev.h> #include <linux/mm.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/ioctl.h> #include <linux/delay.h> #include <asm/io.h> #include <asm/uaccess.h> #include <../arch/arm/mach-lpc32xx/include/mach/hardware.h> #include <../arch/arm/mach-lpc32xx/include/mach/lpc32xx_gpio.h> #include "led.h" int led_major = LED_MAJOR; module_init(led_init); module_exit(led_exit); MODULE_AUTHOR("wzy"); MODULE_LICENSE("GPL");
不用说明,只是为了保持代码的完整性。当然,这还不是字符驱动开发的全部,我们还要有测试程序:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include "../led.h" #define NODE_NAME "/dev/led" int main(int argc, char *argv[]) { int fd; int dat = 0; int i; fd = open(NODE_NAME, O_RDWR); if(fd < 0) { printf("can not open the device/n"); exit(1); } printf("Test ioctl:/n"); for(i = 0; i < 3; i++) { printf("light %d time/n", i + 1); ioctl(fd, LED_ON); usleep(300000); ioctl(fd, LED_OFF); usleep(300000); } printf("ioctl test finish/n"); usleep(30000); printf("Test write:/n"); for(i = 0; i < 3; i++) { printf("light %d time/n", i + 1); dat = 0; write(fd, &dat, 1); usleep(300000); dat = 1; write(fd, &dat, 1); usleep(300000); } printf("write test finish/n"); close(fd); return 0; }
其实和上一篇的那个测试程序是一样的,只是更好看了一点。。。不过还有一点不同时体现在代码之外的,就是对于一个字符设备来说,它还需要一个/dev下的对应的结点文件,这个文件是自己手动加入的,而且重启开发板后,这个手动创建的文件就没有了,所以为了方便起见,我写了一个简单的shell脚本,在运行这个测试程序之前运行脚本文件,就可以了:
#!/bin/sh mod=led.ko mknod /dev/led c 248 0 rmmod $mod insmod $mod
其中的248是我自己设置的主设备号,这个号码要和驱动程序中的LED_MAJOR保持一致,而且要保证这个主设备号是空闲的。
好了,一个LED的字符驱动就写好了,之所以写的比较简单,分析的比较少,是因为其实它们和上一篇的驱动是比较类似的,在那里已经做了比较详细的分析,这里只是说明了一下字符设备驱动的编写和platform体系驱动的编写的不同,两者可以达到同样的目的,只是platform有一些面向对象的感觉,更加有章法一些。现在的学习都是在学习方法,其实对于简单的事物,我们千万不要看不起它,如果我们可以很重视的把它解决,那么遇到难的问题,用同样的方法还是可以行得通的。综上所述吧,就是学会举一反三,有很多东西,解决的模式是一定的,而且是一样的,都有相通的地方,我们学习的时候就要抓住重点,学习方法,而不是只针对一个事物学习,这样就真的是要“活到老学到老了”。
相关文章推荐
- 嵌入式Linux字符设备驱动LED驱动编写
- 07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-LED字符设备驱动
- 嵌入式Linux字符设备驱动LED驱动编写
- 嵌入式Linux字符设备入门之--LED驱动详解
- 嵌入式Linux驱动学习之路(十)字符设备驱动-my_led
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- linux-3.0.1下ok6410的led字符设备驱动
- linux4.10.8 内核移植(四)---字符设备驱动_led驱动程序
- 【嵌入式Linux驱动开发】三、字符设备驱动(二)
- Linux驱动开发-OK6410-LED字符设备驱动实现过程
- “手把手教你学linux驱动开发”OK6410系列之03---LED字符设备驱动
- s3c2440基于linux的gpio led字符设备驱动实践 [转]
- 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入
- Linux字符设备驱动之LED驱动
- 小学生学嵌入式:)Linux_字符设备驱动_流水灯的实现
- 嵌入式Linux驱动学习之路(二十七)字符设备驱动的另一种写法
- 手把手教你学linux驱动开发:模块编程、虚拟字符设备编程、LED字符设备驱动
- 嵌入式学习-驱动开发-lesson2-LED字符设备驱动
- TQ2440 LINUX 2.6.30.4 LED驱动感言,从最初的打印字符,到自动分配设备号,到自动创建设备节点,到次设备号分控led
- linux字符设备驱动之LED