字符设备驱动
之前的框架
- 确定主设备号
- file_operation结构体
- register_chrdecv
- 入口
- 出口
缺点:只有255个字符设备驱动
之前:以主设备号为下标,在chardev里面找到之前注册的file_operations
现在:以主设备号和次设备号来找到file_operation结构体
现在的框架
register_chardev拆分为以下几个部分:
__register_chrdev_region
cdev_alloc
cdev_init
cdev_add
具体框架分析
- 构造file_operation结构体
.owner = THIS_MODULE
.open = hello
- 注册字符设备驱动
if major
MKDEV
register_chrdev_region
else
alloc_chrdev_region //自动分配主设备号
- 定义一个cdev结构体,并初始化
cdev_init
cdev_add
- 创建类,自动创建设备节点
struct class cls;
cls = class_create
class_device_create
- 出口函数
class_device_destroy
class_destroy
cdev_del
unregister_chrdev_region
- 测试程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
* hello_test /dev/hello
*/
void print_usage(char *file)
{
printf("%s <dev> \n",file);
}
int main(int argc,char *argv[])
{
int fd;
if(argc != 2)
{
print_usage(argv[0]);
return 0;
}
fd = open(argv[1],O_RDWR);
if(fd<0)
printf("can not open %s \n",argv[1]);
else
printf("can open %s \n",argv[1]);
return 0;
}
驱动部分代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/cdev.h>
/* 1 确定主设备号 */
static int major = 0;
static struct cdev cdev_hello;
static struct cdev cdev2_hello;
#define MAX_HELLO 2
static dev_t devid;
static struct class *cls;
int hello_open(struct inode *node,struct file *file)
{
static count = 0;
printk("hello1 is open %d \n",++count);
return 0;
}
int hello2_open(struct inode *node,struct file *file)
{
static count = 0;
printk("hello2 is open %d \n",++count);
return 0;
}
/* 2 构造file_operation结构体*/
static struct file_operations file_ops=
{
.owner = THIS_MODULE,
.open = hello_open,
};
static struct file_operations file_ops2=
{
.owner = THIS_MODULE,
.open = hello2_open,
};
static int hello_init(void)
{
/*3 代替register_chrdev的方式 */
/* 创建次设备号为0 1的节点 */
if (major) {
devid = MKDEV(major, 0);
register_chrdev_region(devid, MAX_HELLO, "hello");/* major(0~1)对应file_ops,其他的都不对应file_ops */
}
else {
alloc_chrdev_region(&devid, 0, MAX_HELLO, "hello");/* major(0~1)对应file_ops,其他的都不对应file_ops */
major = MAJOR(devid);
}
cdev_init(&cdev_hello,&file_ops);
cdev_add(&cdev_hello,devid,MAX_HELLO);
/* 创建次设备号为2的节点 */
devid = MKDEV(major, 2); // 从2开始
register_chrdev_region(devid, 1, "hello"); // 需要1个节点
cdev_init(&cdev2_hello,&file_ops2);
cdev_add(&cdev2_hello,devid,1);
/*4 自动创建设备节点 */
cls = class_create(THIS_MODULE,"hello");
class_device_create(cls,NULL,MKDEV(major,0),NULL,"hello0");/*dev/hello0*/
class_device_create(cls,NULL,MKDEV(major,1),NULL,"hello1");/*dev/hello1*/
class_device_create(cls,NULL,MKDEV(major,2),NULL,"hello2");/*dev/hello2*/
class_device_create(cls,NULL,MKDEV(major,3),NULL,"hello3");/*dev/hello2*/
return 0;
}
static void hello_exit(void)
{
class_device_destroy(cls,MKDEV(major,0));
class_device_destroy(cls,MKDEV(major,1));
class_device_destroy(cls,MKDEV(major,2));
class_device_destroy(cls,MKDEV(major,3));
class_destroy(cls);
cdev_del(&cdev_hello);
unregister_chrdev_region(MKDEV(major,0),MAX_HELLO);
cdev_del(&cdev2_hello);
unregister_chrdev_region(MKDEV(major,2),1);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
以上是字符设备驱动的框架以及测试代码,仅仅是说明了字符设备驱动的一种新的注册方式,距离一个真正的字符设备驱动还有一定的距离
~
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!