Linux驱动学习记录

基本模板

驱动模板

#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。

  1. 内核模块支持的参数类型有:bool、invbool、charp、int、short、long、uint、ushort
    和 ulong。
  2. 访问掩码的值在<linux/stat.h>定义,S_IRUGO 表示任何人都可以读取该参数,但不
    能修改。
  3. 支持传参的模块需包含 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)MakefileM=(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");

上午截止。。。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值