自制简单字符型设备驱动程序——LED驱动
来源:互联网 发布:csgo正在检索游戏数据 编辑:程序博客网 时间:2024/06/12 01:20
这周抽空学以致用,参考LDD3及嵌入式系统接口设计与Linux驱动程序开发这两本书,结合自己的开发板,做了LED驱动的程序,自己写程序和看书感觉真的是不一样啊,过程中犯了不少错误,但最终还是完成了程序,很高兴!
硬件平台:tq2440
内核版本:2.6.30.4
1. 硬件介绍
下面首先介绍一下我的开发板的接线,我的开发板是天嵌公司的tq2440,它的LED接线如图所示:
其中,nLED_1接S3C2440的GPB5引脚,其他依次接GPB6,GPB7,GPB8,低电平时LED点亮。这些条件就够我们写驱动使用了。
2. LED驱动程序
把程序分成几块分别说明一下。首先是最前面的一部分,这一部分主要是头文件及调试用的预定义,同时定义了一些参数,默认情况下,主次设备号均为0,有4个LED,同时给出了一个关键的结构,我的LED设备的结构,它包含了LED所接的引脚信息,LED的状态,信号量及一个标志他是字符型设备的cdev结构。
- #include <linux/module.h>
- #include <linux/init.h>
- #include <mach/regs-gpio.h> /*S3C2410_GPB5_OUTP and S3C2410_GPB5*/
- #include <linux/kernel.h> /*printk*/
- #include <linux/fs.h>
- #include <linux/types.h> /*size_t and atomic_t*/
- #include <linux/cdev.h> /*cdev*/
- #include <mach/hardware.h> /*s3c2410_gpio_cfgpin and s3c2410_gpio_setpin*/
- #include <asm/uaccess.h> /*copy_*_user*/
- /*for debug use*/
- #undef DEBUG
- //#define DEBUG
- #ifdef DEBUG
- #define PRINTK printk("Success!\n")
- #else
- #define PRINTK
- #endif
- /*LED start pin*/
- #define LEDSTARTPIN S3C2410_GPB5
- int yjpLED_major = 0;
- int yjpLED_minor = 0;
- int nr_LED = 4;
- module_param(yjpLED_major, int, S_IRUGO);
- module_param(yjpLED_minor, int, S_IRUGO);
- module_param(nr_LED, int, S_IRUGO);
- struct yjpLED
- {
- unsigned int LEDpin;
- unsigned char status;
- struct semaphore sem;
- struct cdev cdev;
- };
- struct yjpLED *yjpLEDs;
最后面的一部分,是一些协议信息及作者信息,但最关键的在与还指出了初始化函数及退出函数。
- module_init(yjpLED_init);
- module_exit(yjpLED_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Yjp");
下面先看我的初始化函数
- int __init yjpLED_init(void)
- {
- int result, i;
- dev_t dev;
- if(yjpLED_major){
- dev = MKDEV(yjpLED_major, yjpLED_minor);
- result = register_chrdev_region(dev, nr_LED, "yjpLED");
- }else{
- result = alloc_chrdev_region(&dev, yjpLED_minor, nr_LED, "yjpLED");
- yjpLED_major=MAJOR(dev);
- }
- if(result < 0){
- printk(KERN_ALERT "yjpLED: can't get major %d\n", yjpLED_major);
- return result;
- }
- yjpLEDs = kmalloc(nr_LED * sizeof(struct yjpLED), GFP_KERNEL);
- if (!yjpLEDs) {
- result = -ENOMEM;
- goto fail;
- memset(yjpLEDs, 0, nr_LED * sizeof(struct yjpLED));
- }
- for(i = 0; i < nr_LED; i++){
- yjpLEDs[i].LEDpin= LEDSTARTPIN + i;
- yjpLEDs[i].status = 1;
- }
- for(i = 0; i < nr_LED; i++){
- s3c2410_gpio_cfgpin(yjpLEDs[i].LEDpin, S3C2410_GPB5_OUTP << (i*2));
- s3c2410_gpio_setpin(yjpLEDs[i].LEDpin, yjpLEDs[i].status);
- init_MUTEX(&yjpLEDs[i].sem);
- setup_yjpLED_cdev(&yjpLEDs[i].cdev, i);
- }
- if (!yjpLEDs) {
- result = -ENOMEM;
- goto fail;
- }
- printk(KERN_ALERT "yjpLED init!\n");
- return result;
- fail:
- yjpLED_exit();
- return result;
- }
该函数完成的工作为:
1. 注册设备,为设备分配设备号。注册成功后,在/proc/devices下就可以看到设备了。
2. 为设备结构分配空间,并初始化为0.
3. 硬件初始化。
4. cdev结构的初始化。
其中,cdev结构初始化由下面的函数完成:
- void setup_yjpLED_cdev(struct cdev *cdev, int index)
- {
- int err, devno = MKDEV(yjpLED_major, yjpLED_minor + index);
- cdev_init(cdev, &yjpLED_fops);
- (*cdev).owner = THIS_MODULE;
- err = cdev_add(cdev, devno, 1);
- PRINTK;
- if(err){
- printk(KERN_ALERT "Error %d adding yjpLED%d", err, index);
- }
- }
初始化了cdev的必要的成员,并激活cdev。LED已经“活”了。下面看退出函数:
- void __exit yjpLED_exit(void)
- {
- int i, devno = MKDEV(yjpLED_major, yjpLED_minor);
- if(yjpLEDs){
- for(i = 0; i < nr_LED; i++){
- cdev_del(&yjpLEDs[i].cdev);
- PRINTK;
- }
- kfree(yjpLEDs);
- }
- printk(KERN_ALERT "yjpLED exit!\n");
- unregister_chrdev_region(devno, nr_LED);
- }
删除每个设备的cdev结构并注销设备。
然后看一下关键的结构体,file_operations
- struct file_operations yjpLED_fops = {
- .owner = THIS_MODULE,
- .write = yjpLED_write,
- .open = yjpLED_open,
- .release = yjpLED_release,
- };
定义了设备的打开,读及写操作。open操作的函数:
- int yjpLED_open(struct inode *inode, struct file *filp)
- {
- struct yjpLED *dev;
- dev = container_of(inode->i_cdev, struct yjpLED, cdev);
- filp->private_data = dev;
- PRINTK;
- return 0;
- }
将所打开的设备的指针存放在代表打开文件的指针filp的private_data成员中,以便其他操作函数使用。
release操作:
- int yjpLED_release(struct inode *inode, struct file *filp)
- {
- filp->private_data = NULL;
- return 0;
- }
释放由open分配的保存在filp->private_data中的所用内容。
write操作:
- ssize_t yjpLED_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
- {
- ssize_t retval;
- struct yjpLED *dev;
- dev = filp->private_data;
- if(down_interruptible(&dev->sem))
- return -ERESTARTSYS;
- if(copy_from_user(&dev->status, buf, sizeof(dev->status)))
- {
- printk(KERN_ALERT "after copy but error!\n");
- retval = -EFAULT;
- goto out;
- }
- retval = sizeof(dev->status);
- dev->status =~ (dev->status) & 1;
- s3c2410_gpio_setpin(dev->LEDpin, dev->status);
- PRINTK;
- out:
- up(&dev->sem);
- return retval;
- }
完成的工作主要有:
1. 获取设备信号量,防止竞态发生。
2. 从用户空间获取数据,如果成功则置位相应的引脚,否则释放信号量并退出。
以上就是整个程序,将其复制到内核目录下的drivers/char/,然后修改该目录下的Kconfig文件和Makefile文件,编译出模块yjpLED.ko,将其复制到文件系统的/lib/目录下,注意要修改权限为可执行。
这里为了方便加载模块,还修改了LDD3中的scull_load脚本,也将其复制到文件系统/lib目录下,修改权限为可执行,该脚本内容如下:
- #!/bin/sh
- # $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $
- module="yjpLED"
- device="yjpLED"
- mode="664"
- group="root"
- # invoke insmod with all arguments we got
- # and use a pathname, as insmod doesn't look in . by default
- /sbin/insmod ./$module.ko $* || exit 1
- # retrieve major number
- major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
- # Remove stale nodes and replace them, then give gid and perms
- # Usually the script is shorter, it's scull that has several devices in it.
- rm -f /dev/${device}[0-3]
- mknod /dev/${device}0 c $major 0
- mknod /dev/${device}1 c $major 1
- mknod /dev/${device}2 c $major 2
- mknod /dev/${device}3 c $major 3
- chgrp $group /dev/${device}[0-3]
- chmod $mode /dev/${device}[0-3]
启动开发板,进入/lib目录,加载模块并运行,测试使用echo命令向设备文件写入,写入单数点亮对应的LED,写入双数关闭对应的LED。
如echo -n 1 > /dev/yjpLED2 点亮第三个LED(从0开始编号)
而echo -n 2 > /dev/yjpLED2 则关闭第三个LED。
- 自制简单字符型设备驱动程序——LED驱动
- 自制简单字符型设备驱动程序——LED驱动
- 字符设备驱动----LED驱动程序
- 字符设备驱动--LED驱动程序
- 简单字符设备驱动——LED驱动
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
- LED字符设备驱动程序
- Linux设备驱动--简单字符设备驱动程序
- Tiny6410 简单的LED字符设备驱动
- Tiny6410 简单的LED字符设备驱动
- Linux设备驱动程序——简单字符设备驱动程序
- 第一篇 字符设备驱动程序之LED流水灯驱动
- Linux设备驱动工程师之路——简单字符设备驱动程序
- LED字符设备驱动
- led字符设备驱动
- 字符设备驱动---Led
- TI的omap3530如何实现GPIO口的中断.
- String,StringBuffer,StringBuild的区别
- ZABKAT.xplorer2.Pro.v2.2.0.1.Multilingual.Incl.Keymaker-ZWT
- Qt程序崩溃之自定义类型
- spring事务 spring事务代理
- 自制简单字符型设备驱动程序——LED驱动
- gridEh自动列宽 OptimizeWidth
- Java HashMap实现详解
- ZABKAT.xplorer2.Pro.v2.2.0.1.x64.Multilingual.Incl.Keymaker-ZWT
- VC延时函数
- VMWARE 系列:VMware打开VMDK格式文件
- Zigbee协议栈OSAL层API函数
- 跟小静学MVC3[01]--创建第一个MVC3项目
- 黑龙江集贤办公楼坍塌事故至少两人被埋-黑龙江-自来水公司-办公楼坍塌