Linux字符设备驱动之Tiny6410 LED驱动分析
来源:互联网 发布:数据库备份类型 编辑:程序博客网 时间:2024/05/08 11:27
摘要 : 驱动程序是应用程序和底层硬件之间的桥梁,非常重要。字符设备是一种可以当做一个字节流来存取的设备,这样的设备只能一个字节一个字节的进行数据传输,这样的驱动常常至少实现open、close、read、和write系统条用,常见的有串口、LED、文本控制台等,字符设备通过文件系统节点来存取,例如/dev/tty1和/dev/lp0.在一个字符设备和一个普通文件之间唯一相关的不同就是,你可以在普通的文件中移来移去,但是大部分字符社诶仅仅是数据通道,只能顺序存取。
重要概念
1. 用户空间和内核空间
一个驱动模块在内核空间运行,而应用程序则是在用户空间运行,这个概念是操作系统的理论基础。Linux为这两种空间之间的数据传输定义了两个函数,分别为copy_to_user()和copy_from_user(),从字面意思可以知道函数的意义。比如在编写驱动程序时,很显然驱动程序属于内核空间,会经常使用copy_from_user()函数,从应用程序中获取数据给驱动程序。
2. 编译模块
使用make xxxx modules 生成xxx.ko模块。
3. 加载和卸载模块(驱动)
驱动生成的模块需要加载到内核中,加载模块使用insmod指令,
如:insmod xxx.ko
卸载驱动则用rmmod命令。
4. 所需头文件
#include<linux/module.h> //包含大量加载模块所需的函数和符号定义
#include<linux/init.h> //制定初始化和清理函数
许可凭证指令:MODULE_LICENSE(“GPL”);
5. 初始化和关停
初始化指令:static int __init initialization_function(void)
{
}
module_init(initialization_function);
初始化函数应声明为静态,因为他们不会再特定文件之外可见,毕竟static的重点应用是“隐藏”。声明中的__init标识是给内核一个暗示,该函数只是在初始化时使用,一旦模块加载者在模块加载后会丢掉这个初始化函数,使它的内存用作他用。
module_init是强制的,该宏定义增加了特别的段到模块目标代码中,表明在哪里找到模块的初始化函数,没有这个定义,初始化函数不会被调用。
清理函数:static void__exit cleanup_function(void)
{
}
module_exit(cleanup_function);
清理函数没有返回值,用于模块的卸载。
6. 主、次设备号
字符设备的索引节点,是操作系统访问该设备驱动的入口点,尤其主设备号尤为重要。
在linux中,设备号是一个dev_t类型数据,其中dev_t其实就是一个32位数,低12位为主设备号,高20位为次设备号。获取主次设备号,使用宏定义:
MAJOR(dev_t dev);
MINOR(dev_t dev);
而如果有了主次设备号,转换成dev_t数据,也是用宏定义
MKDEV(intmajor,int minor);
7. 注册和释放设备
在建立一个字符驱动时,第一件重要的事情就是注册设备编号,常见的有3个函数完成这一功能,分别为:register_chrdev_region()、alloc_chrdev_region()和register_chrdev().
在内核中所有已经分配的字符设备驱动都是记录在一个名为chrdevs的列表中。
alloc_chrdev_region()是动态注册分配主次设备号。
register_chrdev_region()是已经知道设备主次设备号进行注册。
register_chrdev()是老版本的设备号注册方式。
这3个函数一方面是完成设备注册到内核中,另外一方面是将该设备与对应的file_operations结构体进行链接,这样就实现了驱动程序“接口”的链接。
该3个函数一般用于设备初始化函数 __init func(void)中。
8. 重要数据结构
file_operations :文件操作数据结构,定义了字符设备驱动的接口,由于所有的驱动接口名称是相同的,都是open,read,write等,所以要求不同的设备对应不同的xxx_open,xxx_read,xxx_write等,这些的接口就定义在file_openations结构体中。
常见的如:
struct file_operaions xxx_fops = {
.owner =THIS_MODULE,
.llseek =xxx_llseek,
.read = xxx_read,
.write =xxx_write,
.ioctl =xxx_ioctl,
.open = xxx_open,
.release =xxx_release,
};
其中open为设备的打开程序,如串口打开,LED接口定义等。而release函数的功能与open相反,用于设备的释放或者关闭,如串口关闭等。
Tiny6410开发板的LED驱动
下面是针对tiny6410开发板的LED进行开发的驱动文件tiny6410_leds.c的详细分析:
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-k.h>
#define LED_MAJOR 243
#define LED_ON 1
#define LED_OFF 0
#define LED_1_ON 2
#define LED_1_OFF 3
#define LED_2_ON 4
#define LED_2_OFF 5
#define LED_3_ON 6
#define LED_3_OFF 7
#define LED_4_ON 8
#define LED_4_OFF 9
/*************.open函数*****************/
static int led_open(struct inode *inode,struct file *file)
{
unsigned tmp;
tmp =readl(S3C64XX_GPKCON);
tmp =(tmp&0x0000ffff)|0x1111ffff;
writel(tmp,S3C64XX_GPKCON);
printk("*************open************");
return 0;
}
/*************.read函数******************/
static int led_read(struct file *file,char __user *buf,size_tcount,loff_t *f_pos)
{
return count;
}
/*************.write函数******************/
static int led_write(struct file *filp,const char __user *buf,size_tcount,loff_t *f_pos)
{
char wbuf[10];
unsigned tmp;
if (copy_from_user(wbuf,buf,count))
return -EFAULT;
switch(wbuf[0])
{
case LED_ON:
tmp =readl(S3C64XX_GPKDAT);
tmp &=(0x0f);
writel(tmp,S3C64XX_GPKDAT);
printk("turn on!\n");
break;
case LED_OFF:
tmp =readl(S3C64XX_GPKDAT);
tmp &=(0xf0);
writel(tmp,S3C64XX_GPKDAT);
printk("turn off!\n");
break;
case LED_1_ON:
tmp = readl(S3C64XX_GPKDAT);
tmp &=(0xef);
writel(tmp,S3C64XX_GPKDAT);
printk("turn LED1 on!\n");
break;
case LED_1_OFF:
tmp =readl(S3C64XX_GPKDAT);
tmp |= (0xf0);
writel(tmp,S3C64XX_GPKDAT);
printk("turnLED1 off!\n");
break;
case LED_2_ON:
tmp =readl(S3C64XX_GPKDAT);
tmp &=(0xdf);
writel(tmp,S3C64XX_GPKDAT);
printk("turnled2 off!\n");
break;
case LED_2_OFF:
tmp =readl(S3C64XX_GPKDAT);
tmp |= (0xf0);
writel(tmp,S3C64XX_GPKDAT);
printk("turnled2 on!\n");
break;
case LED_3_ON:
tmp =readl(S3C64XX_GPKDAT);
tmp &=(0xbf);
writel(tmp,S3C64XX_GPKDAT);
printk("turnled3 off!\n");
break;
case LED_3_OFF:
tmp =readl(S3C64XX_GPKDAT);
tmp |= (0xf0);
writel(tmp,S3C64XX_GPKDAT);
printk("turn led3 on!\n");
break;
case LED_4_ON:
tmp =readl(S3C64XX_GPKDAT);
tmp &=(0x7f);
writel(tmp,S3C64XX_GPKDAT);
printk("turnled4 off!\n");
break;
case LED_4_OFF:
tmp = readl(S3C64XX_GPKDAT);
tmp |= (0xf0);
writel(tmp,S3C64XX_GPKDAT);
printk("turnled4 on!\n");
break;
default:
break;
}
return 0;
}
/*************.releae函数******************/
int led_release(struct inode *inode,struct file *file)
{
printk("**************release*********");
return 0;
}
/*************.file_operations 结构体******************/
struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
/*************init初始化函数 ******************/
int __init led_init(void)
{
int rc;
printk("test leddev\n");
rc =register_chrdev(LED_MAJOR,"led",&led_fops); //重要
if(rc<0)
{
printk("register%s char dev error\n","led");
return -1;
}
printk("ok!\n");
return 0;
}
void __exit led_exit(void)
{
unregister_chrdev(LED_MAJOR,"lde");
printk("moduleexit\n");
return ;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JSQ");
生成tiny6410_led.ko所需的Makefile脚本
obj-m := tiny6410_leds.o
KDIR :=/opt/FriendlyARM/mini6410/linux/linux-2.6.38
all:
make -C $(KDIR) M=$(shellpwd) modules
install:
cp tiny6410_leds.ko /opt/FriendlyARM/mini6410/linux/linux-2.6.38/drivers/char
clean:
make -C $(KDIR) M=$(shellpwd) clean
其中:make -C $(KDIR)M=$(shell pwd) modules 命令是make modules(生成模块.ko)命令的扩展,它是改变它的目录到-C选项提供的目录下(内核源码目录),它在哪里会发现内核的顶层makefile,这个M=选项使makefile在试图建立模块钱汇到你的模块源码目录,这个目标,依次的是指在obj-m变量中发现的模块列表,在本例中是tiny6410_leds.o
加载设备驱动模块,并挂载设备驱动
加载驱动:insmod tiny6410_leds.ko
挂载设备节点: mknod /dev/leds c 243 0
测试程序tiny6410_leds_test.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_OFF 0
#define LED_ON 1
#define LED_1_ON 2
#define LED_1_OFF 3
#define LED_2_ON 4
#define LED_2_OFF 5
#define LED_3_ON 6
#define LED_3_OFF 7
#define LED_4_ON 8
#define LED_4_OFF 9
int main (void)
{
int i=0;
int fd;
char buf[10]={
LED_ON , LED_OFF ,
LED_1_ON, LED_1_OFF,
LED_2_ON, LED_2_OFF,
LED_3_ON, LED_3_OFF,
LED_4_ON, LED_4_OFF,
};
fd =open("/dev/led",O_RDWR); //打开驱动,开始使用驱动
if (fd < 0)
{
printf ("Open/dev/led file error\n");
return -1;
}
while(i<10)
{
write(fd,&buf[i],4); //使用驱动接口函数write
sleep(1);
i++;
}
close (fd);
return 0;
}
应用程序tiny6410_leds_test.c的编译 Makefile
ifndef DESTDIR
DESTDIR ?= /tmp/FriendlyARM/mini6410/rootfs
endif
CFLAGS =-Wall -O2
CC =arm-linux-gcc
INSTALL =install
TARGET =led_test
all: $(TARGET)
led_test: tiny6410_leds_test.c
$(CC) $(CFLAGS) $< -o $@
install: $(TARGET)
$(INSTALL) $^$(DESTDIR)/usr/bin
clean distclean:
rm -rf *.o $(TARGET)
#----------------------------------------------------------------------------
.PHONY: $(PHONY) install clean distclean
# End of file
# vim: syntax=make
运行测试程序:./tiny610_leds_test
测试结果---串口输出:
其中tiny6410开发板指示灯与测试程序定义相同,而且通过这个测试发现,release函数还是很有必要的,能够终止该进程,否则会一直死循环。
- Linux字符设备驱动之Tiny6410 LED驱动分析
- Linux字符设备驱动之LED驱动
- Tiny6410 简单的LED字符设备驱动
- Tiny6410 简单的LED字符设备驱动
- tiny6410 linux混杂设备 led驱动
- linux字符设备驱动之LED
- linux设备驱动(一)---字符设备之led驱动
- Linux 字符设备驱动 LED
- Tiny6410杂项设备驱动之——led驱动
- 字符设备驱动之LED
- linux字符设备驱动-LED驱动
- linux设备驱动之LED驱动代码分析
- Tiny6410 led 驱动实现分析
- Tiny6410 led 驱动实现分析
- Tiny6410 led 驱动实现分析
- Linux下驱动开发之二(LED驱动)-------Tiny6410
- linux设备驱动之LED驱动测试
- LED字符设备驱动
- MSSQL数据库测试连接的快捷方法
- Java获取时间范围: 当前季度,上个季度,昨天,当前月,上个月
- Codeforces 567E President and Roads (用Dijkstra求最短路条数 + 强连通桥)
- HDU 5428 The Factor
- 好好学语文
- Linux字符设备驱动之Tiny6410 LED驱动分析
- 常见笔试题记录
- jsp页面的字段要求:存整数、存二位小数等
- include的使用
- KMP算法
- InputStream InputStreamReader BufferedReader
- 当推荐算法开源包多如牛毛,为什么我们还要专门的推荐算法工程师
- jQuery Ajax 跨域请求
- LayoutInflater三种模式的差别