创建设备文件节点_使用device_create实例分析

Linux 的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev 目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。

查看/proc/devices 文件可以获知系统中注册的设备,第 1 列为主设备号,第 2 列为设备名,主设备号是与驱动对应的概念,同一类设备一般使用相同的主设备号,不同类的设备一般使用不同的主设备号(但是也不排除在同一主设备号下包含有一定差异的设备)。因为同一驱动可支持多个同类设备,因此用次设备号来描述使用该驱动的设备的序号,序号一般从0开始。

adb命令:cat /proc/devices 可以查看到主设备号和对应的设备

130|rk3399_all:/ $ cat /proc/devices
Character devices:
  1 mem
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
 10 misc
 13 input
 21 sg
 29 fb
 81 video4linux
 86 ch
 89 i2c
108 ppp
116 alsa
128 ptm
136 pts
153 spi
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
216 rfcomm
226 drm
234 rkvr
235 roccat
236 hidraw
237 rtk_btusb

adb命令:ls -l dev/ 可以查看到所有的设备节点

rk3399_all:/ $ ls -l dev/
total 0
drwxr-xr-x 2 root      root              820 1970-01-01 00:00 __properties__
crw-rw-rw- 1 root      root         238,   0 1970-01-01 00:00 alpum
crw-rw-rw- 1 root      root          10,  57 1970-01-01 00:00 ashmem
crw-rw-rw- 1 root      root          10,  55 1970-01-01 00:00 binder
drwxr-xr-x 4 root      root              960 1970-01-01 00:00 block
drwxr-xr-x 3 root      root               60 1970-01-01 00:00 bus
crw-rw-r-- 1 media     camera        10,  59 1970-01-01 00:00 camsys_marvin
crw-rw-r-- 1 media     camera        10,  58 1970-01-01 00:00 camsys_marvin1
crw-rw-r-- 1 root      system       250,   0 1970-01-01 00:00 cec0
drwxr-xr-x 2 root      shell              40 1970-01-01 00:00 com.koushikdutta.superuser.daemon
crw------- 1 root      root           5,   1 1970-01-01 00:00 console
crw------- 1 root      root          10,  51 1970-01-01 00:00 cpu_dma_latency
dr-xr-xr-x 3 system    system              0 1970-01-01 00:00 cpuctl
dr-xr-xr-x 6 system    system              0 1970-01-01 00:00 cpuset
crw------- 1 root      root          10, 236 1970-01-01 00:00 device-mapper
drwxr-xr-x 2 root      root              100 1970-01-01 00:00 dri
lrwxrwxrwx 1 root      root               13 1970-01-01 00:00 fd -> /proc/self/fd
drwxrwx--- 2 root      system             40 1970-01-01 00:00 fscklogs
crw-rw-rw- 1 root      root           1,   7 1970-01-01 00:00 full
crw------- 1 root      root          10, 229 1970-01-01 00:00 fuse
drwxr-xr-x 2 root      root               60 1970-01-01 00:00 graphics
crw------- 1 root      root          10,  43 1970-01-01 00:00 hdcp2_node
crw------- 1 root      root          10,  46 1970-01-01 00:00 hdmi_hdcp1x
crw------- 1 root      root          10,  44 1970-01-01 00:00 hl_dev
crw------- 1 root      root          10,  54 1970-01-01 00:00 hwbinder
crw------- 1 root      root          89,   0 1970-01-01 00:00 i2c-0
crw-rw---- 1 system    system        89,   1 1970-01-01 00:00 i2c-1
crw------- 1 root      root          89,  10 1970-01-01 00:00 i2c-10
crw------- 1 root      root          89,   4 1970-01-01 00:00 i2c-4
crw------- 1 root      root          89,   9 1970-01-01 00:00 i2c-9
crw-rw-rw- 1 media     media         10,  61 1970-01-01 00:00 iep
crw-rw---- 1 system    system       247,   0 1970-01-01 00:00 iio:device0

创建设备节点可以手动创建也可以自动创建。

一,手动创建:可以通过mknod命令手动创建。

  1. mknod命令
    命令的格式是:
    mknod /dev/设备名 设备类型(字符:c,块:b) 主设备号 从设备号
    其中,主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。
    因此,想要创建设备节点,需要知道设备类型,及其主从设备号。
    例如:mknod /dev/nodetest c 244 10 就创建了一个nodetest的字符设备节点,主设备号是244,次设备号是10
    手动创建设备节点的缺点是容易导致设备号冲突。

二,自动创建:

利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置。在驱动初始化代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备。

驱动加载时完成如下工作:分配设备号-------------注册字符设备------------动态创建设备节点。
驱动卸载时完成如下工作:删除设备节点-------------取消字符设备的注册-----------删除设备号。

1、头文件的添加

	#include <linux/init.h>
	#include <linux/slab.h>
	#include <linux/module.h>
	#include <linux/kernel.h>
	#include <linux/fs.h>
	#include <linux/cdev.h>
	#include <linux/ioctl.h>
	#include <linux/uaccess.h>
	#include <linux/string.h>
	#include <linux/wait.h>
	#include <linux/types.h>
	#include <linux/proc_fs.h>

xxxx包含一些必要的头文件,具体要添加的头文件是哪些,可以根据你的接口函数是否在该头文件中有声明或者有extern引用;

2、定义一个类和一个设备的结构指针,test_class 和test_class_dev 可随意命名;

static struct class *test_class = NULL;
static struct device *test_class_dev = NULL;

3、在入口函数(module_init)里添加
3.1 调用alloc_chrdev_region动态分配设备号,函数具体用法可以自己去linux kernel找原函数;

3.2 初始化设备,关联对应的file_operation,注册字符设备,具体用法可以参考Linux kernel里面的原函数:
int error;

	dev_t devno = MKDEV(test_major, test_minor);

  cdev_init (&cdev, &test_fops);
	cdev.owner = THIS_MODULE;
	cdev.ops = &test_fops;        
 
/*注册字符设备*/
	error = cdev_add (&cdev, devno, 1);
	if(error)
	printk("Error %d adding test_setup_cdev", error);

3.3 调用test_class = class_create(THIS_MODULE, “test_class”);会在sys/class目录下生成test_class文件夹,test_class可以随意命名;

3.4 调用test_class_dev = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, “xxx”);会在/dev目录下生成xxx的设备文件,major是主设备号,可以在前面宏定义指定,也可以直接指定为0~254中的没被使用的一个。

4、在出口函数(module_exit)里添加删除字符设备(cdev_del),注销设备和设备类别(device_destroy和class_destroy),释放设备号(unregister_chrdev_region):

dev_t devno = MKDEV (test_major, test_minor);

   cdev_del(&cdev);
	device_destroy(test_class, devno); 
	class_destroy(test_class); 
	unregister_chrdev_region (devno, number_of_devices);

这样就可以通过adb工具查看自动创建的设备节点文件和类文件:

root@inwatch_portal:/dev # ls -l
root@inwatch_portal:/sys/class # ls -l

对生成的节点进行读写操作:

C:\Users\asus>adb shell
root@inwatch_portal:/ # cd dev
cd dev
root@inwatch_portal:/dev # echo 11111 > test
echo 11111 > test
root@inwatch_portal:/dev # cat test
cat test
11111
root@inwatch_portal:/dev #

device_create动态创建设备节点具体实现代码如下:

	#include <linux/init.h>
	#include <linux/slab.h>
	#include <linux/module.h>
	#include <linux/kernel.h>
	#include <linux/fs.h>
	#include <linux/cdev.h>
	#include <linux/ioctl.h>
	#include <linux/uaccess.h>
	#include <linux/string.h>
	#include <linux/wait.h>
	#include <linux/types.h>
	#include <linux/proc_fs.h>

	#define BUFSIZE  1024 

	/*主设备和从设备号变量*/
	static int test_major = 0;
	static int test_minor = 0;

	/*设备类别和设备变量*/
	static struct cdev cdev;
	static struct class *test_class = NULL;
	static struct device *test_class_dev = NULL;

	int number_of_devices = 1;
	dev_t dev = 0;
	static char *buf;
	static unsigned int len;



	/*打开设备方法,空实现*/
	static int test_open(struct inode *inode, struct file *filp) {

	return 0;
	}

	/*设备文件释放时调用,空实现*/
	static int test_release(struct inode *inode, struct file *filp) {

	return 0;
	}

	/************************
	 * file_operations->read
	 * 函数原型:static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos)
	 ************************/

	static ssize_t test_read(struct file *file, char __user *buffer,size_t count, loff_t *f_pos) 
	{
	if(*f_pos > 0)
		return 0;
	 
	printk("---start read---\n");
	printk("the	string is >>>>> %s\n", buf);
 
	if(copy_to_user(buffer, buf, len))
		return -EFAULT;
	*f_pos = *f_pos + len;
	return len;
	}


	/************************
	 * file_operations->write
	 * 函数原型:static ssize_t write(struct file *file, const char *buf, size_t count, loff_t *ppos)
	 ************************/

	static ssize_t test_write(struct file *file, const char __user *buffer,size_t count, loff_t *f_pos) 
	{
	
	if(count <= 0)
		return -EFAULT;
	printk("---start write---\n");
	
	len = count > BUFSIZE ? BUFSIZE : count;
 
	// kfree memory by kmalloc before
	if(buf != NULL)
		kfree(buf);
	buf = (char*)kmalloc(len+1, GFP_KERNEL);
	if(buf == NULL)
	{
		printk("device_create kmalloc fail!\n");
		return -EFAULT;
	}
 
	//memset(buf, 0, sizeof(buf));
	memset(buf, 0, len+1);
 
	if(copy_from_user(buf, buffer, len))
		return -EFAULT;
	printk("device_create writing :%s",buf);
	return len;
	}



	/*设备文件操作方法表*/
	static struct file_operations test_fops = {
	.owner = THIS_MODULE,
	.open = test_open,
	.release = test_release,
	.read = test_read,
	.write = test_write, 
	};



	static void test_setup_cdev (void)
	{
	int error;
	dev_t devno = MKDEV(test_major, test_minor);

	cdev_init (&cdev, &test_fops);
	cdev.owner = THIS_MODULE;
	cdev.ops = &test_fops;        
 
	/*注册字符设备*/
	error = cdev_add (&cdev, devno, 1);
	if(error)
	printk("Error %d adding test_setup_cdev", error);  

	}



	static int __init test_init (void)
    {		
	int err = -1;
	dev_t dev = 0;
 
	printk("---czd--- Initializing test device.\n");        
 
	/*动态分配主设备和从设备号*/
	err = alloc_chrdev_region(&dev, 0, number_of_devices, "test");
	if(err < 0) {
		printk("Failed to alloc char dev region.\n");
		return err;
	}
 
	test_major = MAJOR(dev);
	test_minor = MINOR(dev);
	printk("---czd--- test_major ==> %d ; test_minor ==> %d \n",test_major,test_minor);


	/*初始化设备*/
	test_setup_cdev();       
 
	/*在/sys/class/目录下创建设备类test_class别目录test*/
	test_class = class_create(THIS_MODULE, "test_class");
	if(IS_ERR(test_class)) 
        {
            printk("Err: failed in creating class.\n");
            return -1; 
        }       

	/*
	 * device_create - creates a device and registers it with sysfs
	 * @class: pointer to the struct class that this device should be registered to
	 * @parent: pointer to the parent struct device of this new device, if any
	 * @devt: the dev_t for the char device to be added
	 * @drvdata: the data to be added to the device for callbacks
	 * @fmt: string for the device's name
	 *
 	 * struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const 			char *fmt, ...)
	 *
	 */

	/*在/dev/目录和/sys/class/test目录下分别创建设备文件test*/
	test_class_dev = device_create(test_class, NULL, MKDEV(test_major, 0), NULL, "test");
	if(IS_ERR(test_class_dev)) 
        {
		printk("Err: failed in creating device.\n");
		return -1; 
        }

	printk("---czd--- Succedded to initialize test device.\n");  
	return 0;
 
	   }


	/*模块卸载方法*/
	static void __exit test_exit (void)
    {
	dev_t devno = MKDEV (test_major, test_minor);

	cdev_del(&cdev);
	device_destroy(test_class, devno); //delete device node under /dev
	class_destroy(test_class); //delete class created by us
	unregister_chrdev_region (devno, number_of_devices);
	printk ("Char driver test_exit.\n");

	   }



	module_init (test_init);
	module_exit (test_exit);

	MODULE_AUTHOR("czd");
	MODULE_DESCRIPTION("Device_create Driver");
	MODULE_LICENSE("GPL");

---------- 爱生活,爱安卓,爱Linux ----------

  • 0
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零意@

您的打赏将是我继续创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值