字符设备驱动

之前的框架

- 确定主设备号
- 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 协议 ,转载请注明出处!

DMA驱动 上一篇
数组与指针的关系 下一篇