基本模板
驱动模板
#include <linux/module.h> //头文件调用,包含moduleparam.h传参模块头文件
#include <linux/init.h> //必须包含的头文件
static int num = 3; //参数定义及默认
static char *whom = "master"; //参数定义及默认
module_param(num, int, S_IRUGO); //传参定义
module_param(whom, charp, S_IRUGO);
static int __init hello_init(void) //注册模块函数
{
printk(KERN_INFO "%s, I get %d\n", whom, num);
return 0;
}
static void __exit hello_exit(void) //卸载模块函数
{
printk("I'll be leaving, bye!\n");
}
module_init(hello_init); //注册模块
module_exit(hello_exit); //卸载模块
MODULE_LICENSE("GPL"); //开源许可证
模块参数必须使用 module_param 宏来声明,通常放在文件头部。module_param 需要 3个参数:变量名称、类型以及用于 sysfs 入口的访问掩码。模块最好为参数指定一个默认值,以防加载模块的时候忘记传参而带来错误。如下的示例在插入模块时候没有指定 num 参数的话,模块将会使用默认值 5。
- 内核模块支持的参数类型有:bool、invbool、charp、int、short、long、uint、ushort
和 ulong。 - 访问掩码的值在<linux/stat.h>定义,S_IRUGO 表示任何人都可以读取该参数,但不
能修改。 - 支持传参的模块需包含 moduleparam.h 头文件。
安装模块 # insmod hellop.ko
卸载模块 # rmmod hellop
Makefile模板
# Makefile2.6
# 第一次读取Makefile时,KERNELRELEASE没有被定义,就执行else之后的内容
ifneq ($(KERNELRELEASE),)
#KERNELRELEASE 定义的一个变量没有定义则执行else后内容,定义后则执行下面内容
param-objs := file1.o file2.o
#所需要的编译的.C文件编译后为.O文件
obj-m := file.o
#将编译后的文件汇总为一个文件
else
KDIR := /lib/modules/2.6.18-53.el5/build
#定义路径
all:
make -C $(KDIR) M=$(PWD) modules
#目标为all则跳转到指定目录Makefile运行
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
#目标为clean则清除操作
endif
ifneq ($(KERNELRELEASE),)
KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容,如果make的目标是clean,直接执行clean操作,然后结束。当make的目标为all时,-C
(
K
D
I
R
)
指
明
跳
转
到
内
核
源
码
目
录
下
读
取
那
里
的
M
a
k
e
f
i
l
e
;
M
=
(KDIR)指明跳转到内核源码目录下读取那里的Makefile;M=
(KDIR)指明跳转到内核源码目录下读取那里的Makefile;M=(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句,指明模块源码中各文件的依赖关系,以及要生成的目标模块名。param-objs := file1.o file2.o 表示param.o由file1.o与file2.o 连接生成,obj-m := param.o表示编译连接后将生成param.o模块。
设备文件
:~$ ls -l /dev/ttyS0 /dev/sda1
brw-rw---- 1 root disk 8, 1 2011-01-07 17:48 /dev/sda1
crw-rw---- 1 root dialout 4, 64 2011-01-07 17:48 /dev/ttyS0
/dev/ttyS0 是设备节点名称,c 表示该设备是字符设备,主设备号为 4,从设备号为 64,该设备节点对应于系统的串口 0。
在设备节点属性中,分别以 c 和 b 来表示,即 c表示字符设备节点文件,b 表示块设备节点文件
动态获取设备号
ret = alloc_chrdev_region(&devno, minor, 1, "char_cdev"); /* 从系统获取主设备号 */
major = MAJOR(devno); /* 保存获得的主设备号 */
if (ret < 0) {
printk(KERN_ERR "cannot get major %d \n", major);
return -1;
}
释放设备号
void unregister_chrdev_region(dev_t from, unsigned count);
两种注册设备号方式说明
extern int register_chrdev_region(dev_t, unsigned, const char *); //静态的申请和注册设备号
extern int alloc_chrdev_region(dev_t, unsigned, const char *); //动态的申请注册一个设备号
extern int register_chrdev(unsigned int, const char *,struct file_operations *);//int为0时候动态注册,非零时候静态注册。
extern int unregister_chrdev(unsigned int, const char *); //注销设备号
extern void unregister_chrdev_region(dev_t, unsigned); //注销设备号
设备注册与注销
在注册设备之前,必须分配并注册一个或者多个 cdev 结构,可用 cdev_alloc 实现,如:
struct cdev *char_cdev = cdev_alloc(); /* 分配 char_cdev 结构 */
初始化 cdev 结构通过调用 cdev_init()实现,cdev_init()函数原型
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
参数 fops 用于指定设备的操作方法,fops结构体
struct file_operations char_old_fops = {
.owner = THIS_MODULE,
.read = char_old_read,
.write = char_old_write,
.open = char_old_open,
.release = char_old_release,
.ioctl = char_old_ioctl
};
设置owner成员
char_cdev->owner = THIS_MODULE;
通过调用 cdev_add 将 cdev 添加到系统中
if (cdev_add(char_cdev, devno, 1) != 0) { /* 增加 char_cdev 到系统中 */
printk(KERN_ERR "add cdev error!\n");
goto error1;
}
必须检查 cdev_add 的返回值,因为 cdev_add 不一定保证成功,添加成功返回 0,失败返回返回错误码
删除cdev
cdev_del(char_cdev); /* 移除字符设备 */
驱动大概运作情况
驱动程序示例代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#define DEVICE_NAME "char_null"
static int major = 232; /* 保存主设备号的全局变量 */
static int __init char_null_init(void)
{
int ret;
ret = register_chrdev(major, DEVICE_NAME, &major); /* 申请设备号和注册 */
if (major > 0) { /* 静态设备号 */
if (ret < 0) {
printk(KERN_INFO " Can't get major number!\n");
return ret;
}
} else { /* 动态设备号 */
printk(KERN_INFO " ret is %d\n", ret);
major = ret; /* 保存动态获取到的主设备号 */
}
printk(KERN_INFO "%s ok!\n", __func__);
return ret;
}
static void __exit char_null_exit(void)
{
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_INFO "%s\n", __func__);
}
module_init(char_null_init);
module_exit(char_null_exit);
MODULE_LICENSE("GPL");
完整字符设备驱动框架
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
//...
static int major = 232; /* 静态设备号方式的默认值 */
static int minor = 0; /* 静态设备号方式的默认值 */
module_param(major, int, S_IRUGO); /*S_IRUGO 表示任何人都可以读取该参数,但不能修改*/
module_param(minor, int, S_IRUGO);
struct cdev *char_cdev; /* cdev 数据结构 */
static dev_t devno; /* 设备编号 */
static struct class *char_cdev_class;
#define DEVICE_NAME "char_cdev"
static int char_cdev_open(struct inode *inode, struct file *file )//open方法
{
}
static int char_cdev_release(struct inode *inode, struct file *file )//release方法
{
}
static ssize_t char_cdev_read(struct file *file, char *buf,size_t count, loff_t *f_pos)//read方法
{
}
static ssize_t char_cdev_write(struct file *file, const char *buf, size_t count, loff_t *f_pos)//write方法
{
}
static int char_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)//ioctl方法
{
}
struct file_operations char_cdev_fops = { //fops定义
.owner = THIS_MODULE,
.read = char_cdev_read,
.write = char_cdev_write,
.open = char_cdev_open,
.release = char_cdev_release,
.ioctl = char_cdev_ioctl
};
static int __init char_cdev_init(void)//模块初始化代码
{
}
module_init(char_cdev_init);//初始化函数
static void __exit char_cdev_exit(void) //模块退出代码
{
}
module_exit(char_cdev_exit); //退出函数
MODULE_LICENSE("GPL");//协议声明 模块描述
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
static int major = 232; /* 静态设备号方式的默认值 */
static int minor = 0; /* 静态设备号方式的默认值 */
module_param(major, int, S_IRUGO);
module_param(minor, int, S_IRUGO);
struct cdev *char_cdev; /* cdev 数据结构 */
static dev_t devno; /* 设备编号 */
static struct class *char_cdev_class;
#define DEVICE_NAME "char_cdev"
static int char_cdev_open(struct inode *inode, struct file *file )
{
try_module_get(THIS_MODULE);
printk(KERN_INFO DEVICE_NAME " opened!\n");
return 0;
}
static int char_cdev_release(struct inode *inode, struct file *file )
{
printk(KERN_INFO DEVICE_NAME " closed!\n");
module_put(THIS_MODULE);
return 0;
}
static ssize_t char_cdev_read(struct file *file, char *buf,size_t count, loff_t *f_pos)
{
printk(KERN_INFO DEVICE_NAME " read method!\n");
return count;
}
static ssize_t char_cdev_write(struct file *file, const char *buf, size_t count, loff_t *f_pos)
{
printk(KERN_INFO DEVICE_NAME " write method!\n");
return count;
}
static int char_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
printk(KERN_INFO DEVICE_NAME " ioctl method!\n");
return 0;
}
struct file_operations char_cdev_fops = {
.owner = THIS_MODULE,
.read = char_cdev_read,
.write = char_cdev_write,
.open = char_cdev_open,
.release = char_cdev_release,
.ioctl = char_cdev_ioctl
};
static int __init char_cdev_init(void)
{
int ret;
if (major > 0) { /* 静态设备号 */
devno = MKDEV(major, minor);
ret = register_chrdev_region(devno, 1, "char_cdev");
} else { /* 动态设备号 */
ret = alloc_chrdev_region(&devno, minor, 1, "char_cdev"); /* 从系统获取主设备号 */
major = MAJOR(devno);
}
if (ret < 0) {
printk(KERN_ERR "cannot get major %d \n", major);
return -1;
}
char_cdev = cdev_alloc(); /* 分配 char_cdev 结构 */
if (char_cdev != NULL) {
cdev_init(char_cdev, &char_cdev_fops); /* 初始化 char_cdev 结构 */
char_cdev->owner = THIS_MODULE;
if (cdev_add(char_cdev, devno, 1) != 0) { /* 增加 char_cdev 到系统中 */
printk(KERN_ERR "add cdev error!\n");
goto error;
}
} else {
printk(KERN_ERR "cdev_alloc error!\n");
return -1;
}
char_cdev_class = class_create(THIS_MODULE, "char_cdev_class");
if (IS_ERR(char_cdev_class)) {
printk(KERN_INFO "create class error\n");
return -1;
}
//device_create(char_cdev_class, NULL, devno, NULL, "char_cdev" "%d", MINOR(devno));
device_create(char_cdev_class, NULL, devno, NULL, "char_cdev", NULL);
return 0;
error:
unregister_chrdev_region(devno, 1); /* 释放已经获得的设备号 */
return ret;
}
static void __exit char_cdev_exit(void)
{
cdev_del(char_cdev); /* 移除字符设备 */
unregister_chrdev_region(devno, 1); /* 释放设备号 */
device_destroy(char_cdev_class, devno);
class_destroy(char_cdev_class);
}
module_init(char_cdev_init);
module_exit(char_cdev_exit);
MODULE_LICENSE("GPL");
今天就到这。。。
今天继续
ioctl命令
生成命令
1.包含<linux/ioctl.h>头文件
_IO(type, nr) 构造无参数的命令编号
_IOW(type, nr, size) 构造往驱动写入数据的命令编号
_IOR(type, nr, size) 构造从驱动中读取数据的命令编号
_IOWR(type, nr, size) 构造双向传输的命令编号
type:幻数,驱动的特征码,区分不同的驱动的命令,专有,已用<Documentation/ioctl/ioctl-number.txt>,通常以字符定义,如#define LED_IOC_MAGIC ‘Z’
nr:功能号
size:数据大小
驱动解析命令
_IOC_DIR(nr) 解析命令的传输方向
_IOC_TYPE(nr) 解析命令类型
_IOC_NR(nr) 解析命令序号
_IOC_SIZE(nr) 解析参数大小
LED驱动范例
.H
#ifndef _LED_DRV_H
#define _LED_DRV_H
#define LED_IOC_MAGIC 'L'
#define LED_ON _IO(LED_IOC_MAGIC, 0)
#define LED_OFF _IO(LED_IOC_MAGIC, 1)
#define LED_IOCTL_MAXNR 2
#endif /*_LED_DRV_H*/
.C
#include <linux/init.h> //必要头文件
#include <linux/module.h> //必要头文件
#include <linux/fs.h> //设备号头文件
#include <linux/cdev.h> //字符设备数据结构头文件
#include <linux/device.h> //设备头文件
#include <linux/version.h> //内核版本识别头文件
#include <asm/mach/arch.h>
#include <mach/hardware.h> //硬件
#include <mach/gpio.h>
#include <asm/gpio.h>
#include "led_drv.h"
static int major;
static int minor;
struct cdev *led; /* cdev 数据结构 */
static dev_t devno; /* 设备编号 */
static struct class *led_class;
#define DEVICE_NAME "led"
#define GPIO_LED_PIN_NUM 55 /* gpio 1_23 */
static int
led_open(struct inode *inode, struct file *file )
{
try_module_get(THIS_MODULE);
gpio_direction_output(GPIO_LED_PIN_NUM, 1);
return 0;
}
static int
led_release(struct inode *inode, struct file *file )
{
module_put(THIS_MODULE);
gpio_direction_output(GPIO_LED_PIN_NUM, 1);
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
int led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
#else
static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
#endif
{
if (_IOC_TYPE(cmd) != LED_IOC_MAGIC)
{
return -ENOTTY;
}
if (_IOC_NR(cmd) > LED_IOCTL_MAXNR)
{
return -ENOTTY;
}
switch(cmd)
{
case LED_ON:
gpio_set_value(GPIO_LED_PIN_NUM, 0);
break;
case LED_OFF:
gpio_set_value(GPIO_LED_PIN_NUM, 1);
break;
default:
gpio_set_value(27, 0);
break;
}
return 0;
}
struct file_operations led_fops =
{
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
.unlocked_ioctl = led_ioctl
#else
.ioctl = led_ioctl
#endif
};
static int __init led_init(void)
{
int ret;
gpio_free(GPIO_LED_PIN_NUM);
if (gpio_request(GPIO_LED_PIN_NUM, "led_run"))
{
printk("request %s gpio faile \n", "led_run");
return -1;
}
ret = alloc_chrdev_region(&devno, minor, 1, "led"); /* 从系统获取主设备号 */
major = MAJOR(devno);
if (ret < 0)
{
printk(KERN_ERR "cannot get major %d \n", major);
return -1;
}
led = cdev_alloc(); /* 分配 led 结构 */
if (led != NULL)
{
cdev_init(led, &led_fops); /* 初始化 led 结构 */
led->owner = THIS_MODULE;
if (cdev_add(led, devno, 1) != 0) /* 增加 led 到系统中 */
{
printk(KERN_ERR "add cdev error!\n");
goto error;
}
}
else
{
printk(KERN_ERR "cdev_alloc error!\n");
return -1;
}
led_class = class_create(THIS_MODULE, "led_class");
if (IS_ERR(led_class))
{
printk(KERN_INFO "create class error\n");
return -1;
}
device_create(led_class, NULL, devno, NULL, "led");
return 0;
error:
unregister_chrdev_region(devno, 1); /* 释放已经获得的设备号 */
return ret;
}
static void __exit led_exit(void)
{
cdev_del(led); /* 移除字符设备 */
unregister_chrdev_region(devno, 1); /* 释放设备号 */
device_destroy(led_class, devno);
class_destroy(led_class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
上午截止。。。