嵌入式Linux驱动
1 简介
  设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样使硬件对应用程序来说是透明的,在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。设备驱动程序是嵌入式Linux内核的一部分,它完成以下的功能:
· 对硬件设备初始化和释放
· 把数据从内核传送到硬件,或从硬件读取数据
· 读取应用程序传送给设备文件的数据和回送应用程序请求的数据
·检测和处理设备出现的错误和异常
    Linux系统的设备分为字符设备,块设备和网络设备。用户进程通过设备文件实现与硬件的交流。每个设备文件都有其文件属性,表示是字符设备还是块设备。另外每个文件都有两个设备号:第一个是主设备号,标识驱动程序;第二个是从设备号,标识使用同一个设备驱动程序的不同硬件设备。
    设备驱动程序可以分为3个主要组成部分:
  (1) 自动配置和初始化
    用于负责检测所要驱动的硬件设备是否存在和是否能正常工作。如果该设备正常,则对这个设备及其相关设备驱动程序需要的软件状态进行初始化,如设置寄存器的值,初始化驱动程序用到的数据结构。这部分驱动程序仅在初始化的时候被调用一次。
  (2)服务于I/O请求的子程序
    调用服务于I/O请求的子程序是由于系统调用的结果,如readwrite调用。这部分程序在执行的时候,系统仍认为是和进行调用的进程属于同一个进程,只是由用户态变成了核心态,它们的运行环境和进行此系统调用的用户程序一样,因此可以在其中调用sleep()等与进程运行环境有关得函数。
  (3)中断服务子程序
    UNIX系统中,并不是直接从中断向量表中调用设备驱动程序的中断服务子程序,而是由UNIX系统来接收硬件中断,再由系统调用中断服务子程序。中断可以发生在任何一个进程运行的时候,因此在中断服务程序被调用的时候,不能依赖于任何进程的状态,也就不能调用任何与进程运行环境有关的函数。因为设备驱动程序一般支持同种类型的若干设备,所以一般在系统调用中断服务子程序的时候,都带有一个或多个参数,以唯一标识请求服务的设备。
2 一个简单的Linux2.6内核驱动模块(hello world)
/* hello.c */
#include <linux/module.h> /* Needed by all modules */
#include <linux/config.h> /* Needed for KERN_ALERT */
#include <linux/init.h> /* Needed for the module-macros */
static int __init hello_init(void) // Module entry function specified by module_init()
{
printk(KERN_ALERT "Hello,world!n");
return 0;
}
static void __exit hello_exit(void) //Module exit function specified by module_exit()
{
printk(KERN_ALERT "Goodbye,cruel world!n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL"); //should always exist or you’ll get a warning
MODULE_AUTHOR("BENSON"); //optional
MODULE_DESCRIPTION("STUDY_MODULE"); //optional
/* Makefile */
# Makefile 2.6
obj-m = hello.o
KDIR:=/lib/modules/$(shell uname -r)/build
#PWD=$(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
************************************************
obj-m := hello.o表示编译后生成hello.o模块。
$(KDIR) 指定了内核源码的路径,M=”表示这是个外部模块,M=$(PWD) 指定了该模块文件所在的路径。
注: makefile预定义了$(PWD)变量,此处可以不必重复定义。
执行#make编译成功后
加载模块
#insmod hello.ko 字串7
#lsmod 输出内核已加载模块信息,可以查看到刚刚加载成功的hello模块
……
Module Size Used by
hello 5632 0
可以在日志里查看加载模块时的信息
#vi /var/log/messages
……
Sep 27 13:25:21 localhost kernel: Hello,world!
卸载模块
#rmmod hello.ko
#lsmod 发现hello模块已经被卸载
查看日志信息
#vi /var/log/messages
……
Sep 27 13:26:58 localhost kernel: Goodbye,cruel world!
3 一个简单Linux2.6内核的字符驱动程序
    Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open ()close ()read ()write () 等。
Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单。
//globalvar.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
#define MAJOR_NUM 254 //主设备号
static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
//初始化字符设备驱动的file_operations结构体
struct file_operations globalvar_fops =
linux下的sleep函数{
 read: globalvar_read,
write: globalvar_write,
};
static int global_var = 0; //"globalvar"设备的全局变量
static int __init globalvar_init(void)
{
 int ret;
 //注册设备驱动
 ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
 if (ret)
 {
  printk("globalvar register failure");
 }
 else
 {
  printk("globalvar register success");
 }
 return ret;
}
static void __exit globalvar_exit(void)
{
 int ret;
 //注销设备驱动
 ret = unregister_chrdev(MAJOR_NUM, "globalvar");
 if (ret)
 {
  printk("globalvar unregister failure");
 }
 else
 {
  printk("globalvar unregister success");
 }
}
static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
 //global_var从内核空间复制到用户空间
 if (copy_to_user(buf, &global_var, sizeof(int)))
 {
  return - EFAULT;
 }
 return sizeof(int);
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。