字符设备驱动
基础框架
- 驱动模块框架
- 设备号审请
- cdev初始化和
file_operations
初始化 - class初始化
- device自动分配设备文件
模块框架
源文件
#include <linux/init.h>
#include <linux/module.h>
int __init XXX_init(void);
void __exit XXX_exit(void);
module_init(XXX_init);
module_exit(XXX_exit);
MODULE_LICENSE("GPL");
int __init XXX_init(void){
return 0;
}
void __exit XXX_exit(void){
}
Makefile
CONFIG_MODULE_SIG=n
KERNDIR:=/lib/modules/`uname -r`/build
PWD:= $(shell pwd)
obj-m:= XXX.o
all:
make -C $(KERNDIR) M=$(PWD) modules
clean:
make -C $(KERNDIR) M=$(PWD) clean
设备号审请
/*
* 设备号是系统资源因此每次要使用设备号都要向系统审请
* 静态审请函数:
* register_chrdev_region(device_id, device_count, "device_name");
* 动态宴请函数:
* alloc_chrdev_region(device_id, device_minor, device_count, "device_name")
* 设备号注销:
* unregister_chrdev_region(device_id);
* 相关宏定义
* MKDEV(主设备号,次设备号)
* MAJOR(设备号)
* MINOR(设备号)
* cat /proc/devices
*/
// 设备号
dev_t dev_id;
#define DEVICE_MAJOR 220
#define DEVICE_MINOR 0
#define DEVICE_COUNT 1
// 静态审请设备号
int get_device_id_static(void){
int ret = 0;
dev_id = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);
// 向内核请求设备号
// 静态审请设备号的第一个参数是dev_t类型
ret = register_chrdev_region(dev_id,DEVICE_COUNT, "cdd_demo");
if(ret < 0){
printk("KERN_INFO ""alloc device id static error\n");
return ret;
}
return 0;
}
// 动态审请设备号
int get_device_id_automatic(void){
int ret = 0;
// 向内核动态审请设备号
// 动态审请设备号的第一个参数是dev_t *类型
ret = alloc_chrdev_region(&dev_id, DEVICE_MINOR, DEVICE_COUNT, "cdd_demon");
if(ret < 0)
return ret;
printk("KERN_INFO ""alloc chrdev region OK\n");
printk("KERN_INFO ""alloc a char device id %d\n",dev_id);
return 0;
}
void __exit device_id_exit(void){
unregister_chrdev_region(dev_id, DEVICE_COUNT);
printk("KERN_INFO ""device id module exit\n");
}
cdev初始化和file_operations
初始化
/*
* cdev初始化:
* void cdev_init(struct cdev *cdev, const struct file_operations *fops)
* 两个参数都要自己声明
* cdev加入内核:
* void cdev_add(struct cdev *cdev, dev_t device_id, unsigned count);
* 手动创建设备文件: mknod device_name device_type device_major device_minor
*/
#include <linux/fs.h>
#include <linux/cdev.h>
static int device_open(struct inode *node, struct file *file){
printk("KERN_INFO device id module opening\n");
return 0;
}
static int device_close(struct inode *node, struct file *file){
printk("KERN_INFO device id module closing\n");
return 0;
}
ssize_t device_read(struct file *file, char __user *buf, size_t size, loff_t *len){
printk("KERN_INFO device id module reading\n");
return 0;
}
ssize_t device_write(struct file *file, const char __user *buf, size_t size, loff_t *len){
printk("KERN_INFO device id module writing\n");
return 0;
}
// struct cdev
struct cdev device_cdev;
// struct file_operations
struct file_operations device_fops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_close,
.read = device_read,
.write = device_write
};
// cdev 初始化和注册到内核
cdev_init(&device_cdev, &device_fops);
ret = cdev_add(&device_cdev, dev_id, DEVICE_COUNT);
// 卸装cdev
cdev_del(&device_cdev);
class 初始化
/*
* 自动生成设备文件面不使用mknod命令
* 先建立类:
* struct class *class_create(模块所有者, 设备类名);
* 销毁类:
* void class_destroy(struct class *);
* 出错检测:
* PTR_ERR(void *)
* ERR_PTR(int)
* IS_ERR(void *)
* IS_ERR_OR_NULL(void *)
*/
// device class
struct class *device_class;
// create class
device_class = class_create(THIS_MODULE, "cdd_class");
if(IS_ERR(&device_class)){
//放cdev
cdev_del(&device_cdev);
//放设备号
unregister_chrdev_region(dev_id, DEVICE_COUNT);
ret = PTR_ERR(device_close);
printk("KERN_INFO class create failed\n");
return ret;
}
// 释放class
class_destroy(device_class);
device自动分配设备文件
/*
* 建立设备文件:
* struct device *device_create(设备类指针,父设备指针,设备号,额外数据,“设备文件名”);
* 销毁设备文件:
* void device_destory(设备类指针,设备号);
* 出错检测:
* PTR_ERR(void *)
* ERR_PTR(int)
* IS_ERR(void *)
* IS_ERR_OR_NULL(void *)
*/
struct device *device_device;
device_device = device_create(device_class, NULL, dev_id, NULL, "cdd");
if(IS_ERR(device_device)){
//放class
class_destroy(device_class);
//放cdev
cdev_del(&device_cdev);
//放设备号
unregister_chrdev_region(dev_id, DEVICE_COUNT);
ret = PTR_ERR(device_device);
printk("KERN_INFO device create failed\n");
return ret;
}
void __exit device_id_exit(void){
// 放device
device_destroy(device_class, dev_id);
// 放class
class_destroy(device_class);
// 卸装cdev
cdev_del(&device_cdev);
// 释放使用的设备号
unregister_chrdev_region(dev_id, DEVICE_COUNT);
printk("KERN_INFO ""device id module exit\n");
}