字符设备驱动程序框架

字符设备驱动程序框架

  1. 定义字符设备结构体变量
  2. 创建设备号,也可以让内核分配alloc_chrdev_region
  3. 定义自己的file_operations结构体
  4. 实现对应的drv_open/drv_read/drv_write等函数,填入file_operations结构体
  5. 初始化cdev把file_operations结构体告诉内核
  6. 添加注册cdev
  7. 创建类
  8. 创建设备
  9. 提供入口函数
  10. 提供出口函数

用到的函数说明:

自动分配设备号并注册:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

参数dev是设备ID;baseminor次设备号开始编号;参数count是要申请的数量,一般都是一个;参数name是设备名字。

 

注册设备号:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

参数from是要申请的起始设备号,也就是给定的设备号;参数count是要申请的数量,一般都是一个;参数name是设备名字。

 

释放设备号:

void unregister_chrdev_region(dev_t from, unsigned count)

 

cdev结构体初始化函数:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

 

注册字符设备:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数p指向要添加的字符设备(cdev结构体变量),参数dev就是设备所使用的设备号,参数count是要添加的设备数量。完善示例代码

 

删除字符设备:

void cdev_del(struct cdev *p)

参数p就是要删除的字符设备。

 

创建类:

struct class *class_create (struct module *owner, const char *name)

class_create一共有两个参数,参数owner一般为THIS_MODULE,参数name是类名字。返回值是个指向结构体class的指针,也就是创建的类。

 

删除类:

void class_destroy(struct class *cls);

参数cls就是要删除的类。

 

创建设备:

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)

device_create是个可变参数函数,参数class就是设备要创建哪个类下面;参数parent是父设备,一般为NULL,也就是没有父设备;参数devt是设备号;参数drvdata是设备可能会使用的一些数据,一般为NULL;参数fmt是设备名字,如果设置fmt=xxx的话,就会生成/dev/xxx这个设备文件。返回值就是创建好的设备。

 

卸载设备:

void device_destroy(struct class *class, dev_t devt)

参数classs是要删除的设备所处的类,参数devt是要删除的设备号。

 

 

设备号相关示例代码:

int major; /* 主设备号 */ 
int minor; /* 次设备号 */ 
dev_t devid; /* 设备号 */ 

if (major) { /* 定义了主设备号 */ 
devid = MKDEV(major, 0); /* 大部分驱动次设备号都选择0 */ 
register_chrdev_region(devid, 1, "test"); 
} else { /* 没有定义设备号 */ 
alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */ 
major = MAJOR(devid); /* 获取分配号的主设备号 */ 
minor = MINOR(devid); /* 获取分配号的次设备号 */ 
} 

cdev初始化和注册示例代码:

cdev结构体说明

struct cdev { 
struct kobject kobj; 
struct module *owner; 
const struct file_operations *ops; 
struct list_head list; 
dev_t dev; 
unsigned int count; 
}; 

在cdev中有两个重要的成员变量:ops和dev,这两个就是字符设备文件操作函数集合file_operations以及设备号dev_t。编写字符设备驱动之前需要定义一个cdev结构体变量。

示例代码:

struct cdev testcdev; 
/* 设备操作函数 */ 
static struct file_operations test_fops = { 
.owner = THIS_MODULE, 
/* 其他具体的初始项 */ 
}; 
testcdev.owner = THIS_MODULE; 
cdev_init(&testcdev, &test_fops); /* 初始化cdev结构体变量 */
cdev_add(&testcdev, devid, 1); 	   /* 添加字符设备 */

自动创建设备节点示例代码:

struct class *class; /* 类 */ 
struct device *device; /* 设备 */ 
dev_t devid; /* 设备号 */ 

/* 驱动入口函数 */ 
static int __init xxx_init(void) 
{ 
/* 创建类 */ 
class = class_create(THIS_MODULE, "xxx"); 
/* 创建设备 */ 
device = device_create(class, NULL, devid, NULL, "xxx"); 
return 0; 
} 

/* 驱动出口函数 */ 
static void __exit led_exit(void) 
{ 
/* 删除设备 */ 
device_destroy(newchrled.class, newchrled.devid); 
/* 删除类 */ 
class_destroy(newchrled.class); 
} 
 
module_init(led_init); 
module_exit(led_exit);

完整的字符设备驱动程序框架示例代码:

#include <linux/types.h> 
#include <linux/kernel.h> 
#include <linux/delay.h> 
#include <linux/ide.h> 
#include <linux/init.h>
#include <linux/module.h> 
#include <linux/errno.h> 
#include <linux/gpio.h> 
#include <linux/cdev.h> 
#include <linux/device.h> 
#include <asm/mach/map.h> 
#include <asm/uaccess.h> 
#include <asm/io.h>

/*设备结构体 */ 
struct chr_dev{ 
dev_t devid; /* 设备号 */ 
struct cdev cdev; /* cdev */ 
struct class *class; /* 类 */
struct device *device; /* 设备 */ 
int major; /* 主设备号 */ 
int minor; /* 次设备号 */ 
}; 
struct chr_dev chrtest; /* 字符设备 */ 
 
/* 
* @description : 打开设备 
* @param – inode : 传递给驱动的inode 
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量 
* 一般在open的时候将private_data指向设备结构体。 
* @return : 0 成功;其他 失败 
*/ 
static int test_open(struct inode *inode, struct file *filp) 
{ 
filp->private_data = &chrtest; /* 设置私有数据 */ 
return 0; 
} 

static ssize_t test_read(struct file *filp, char __user *buf, 
size_t cnt, loff_t *offt) 
{ 
return 0; 
} 

static ssize_t test_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 
{ 
return 0;
} 

static int test_release(struct inode *inode, struct file *filp) 
{ 
return 0; 
} 

/* 设备操作函数 */ 
static struct file_operations chr_fops = { 
.owner = THIS_MODULE, 
.open = test_open, 
.read = test_read, 
.write = test_write, 
.release = test_release, 
}; 
 
/* 
* @description : 驱动入口函数 
*/ 
static int __init test_init(void) 
{ 
u32 val = 0; 

/* 注册字符设备驱动 */ 
/* 1、创建设备号 */ 
if (chrtest.major) { /* 定义了设备号 */ 
chrtest.devid = MKDEV(chrtest.major, 0); 
register_chrdev_region(chrtest.devid,1,”test”); 
} else { /* 没有定义设备号 */ 
alloc_chrdev_region(&chrtest.devid, 0, 1,”test”); /* 申请设备号 */ 
chrtest.major = MAJOR(chrtest.devid); /* 获取主设备号 */ 
chrtest.minor = MINOR(chrtest.devid); /* 获取次设备号 */ 
} 
 
/* 2、初始化cdev */ 
chrtest.cdev.owner = THIS_MODULE; 
cdev_init(&chrtest.cdev, &chr_fops); 

/* 3、添加一个cdev */ 
cdev_add(&chrtest.cdev, chrtest.devid, 1); 

/* 4、创建类 */ 
chrtest.class = class_create(THIS_MODULE,”test”); 
 
/* 5、创建设备 */ 
chrtest.device = device_create(chrtest.class, NULL, 
chrtest.devid, NULL,”test”); 
 
return 0; 
} 

/* 
* @description : 驱动出口函数 
*/ 
static void __exit test_exit(void) 
{ 

/* 注销字符设备 */ 
cdev_del(&chrtest.cdev);/* 删除cdev */ 
unregister_chrdev_region(chrtest.devid, 1); 
device_destroy(chrtest.class, chrtest.devid); 
class_destroy(chrtest.class); 
} 
 
module_init(test_init); 
module_exit(test_exit); 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("wb");

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值