Linux字符设备驱动新写法

一、新老设备驱动写法差异

设备驱动基本写法可以参见之前的文章:Linux字符设备驱动基本写法

register_chrdevunregister_chrdev 这两个函数是老版本驱动使用的函数,现在新的字符设备驱动已经不再使用这两个函数。
主要原因是使用这两个函数我们需要确定哪些主设备号没有使用,而且注册的时候会将这个主设备号下面的所有次设备号都使用掉,而实际我们并没有这么多设备也不需要将所有次设备号都占住,这就造成了次设备号的浪费。
Linux 社区推荐使用动态分配设备号,在注册字符设备之前先申请一个设备号,系统会自动给你一个没有被使用的设备号,这样就避免了冲突。
设备号的申请函数如下:

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

dev:保存申请到的设备号。
baseminor: 次设备号起始地址, alloc_chrdev_region 可以申请一段连续的多个设备号,这些设备号的主设备号一样,但是次设备号不同,次设备号以 baseminor 为起始地址地址开始递增。一般 baseminor 为 0,也就是说次设备号从 0 开始。
count: 要申请的设备号数量。
name:设备名字。

对于主设备号已经确定,可以使用如下函数进行申请,dev_t from为所需设备号范围中的第一个,必须包括主设备号。

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

注销字符设备之后要释放掉设备号,设备号释放函数如下:

void unregister_chrdev_region(dev_t from, unsigned count)

from:要释放的设备号。
count: 表示从 from 开始,要释放的设备号数量

二、新驱动实例代码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>

#define NEWCHRLED_CNT 1
#define NEWCHRLED_NAME "my_leddrv"

static struct gpio_desc *led_gpio;


static int led_drv_open(struct inode *node, struct file *file)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	gpiod_direction_output(led_gpio, 0);
	return 0;
}

static int led_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int result;
	char status;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	result = copy_from_user(&status, buf, 1);
	gpiod_set_value(led_gpio, status);
	return 1;
}

static int led_drv_release(struct inode *node, struct file *file)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static struct file_operations led_drv_fops =
{
	.owner = THIS_MODULE,
	.open  = led_drv_open,
	.read  = led_drv_read,
	.write = led_drv_write,
	.release = led_drv_release,
};
		
struct newchar_dev
{
	dev_t devid; 		/* 设备号 */
	struct cdev cdev;   /* cdev */
	int major;			/* 主设备号 */
	int minor;			/* 次设备号 */
	struct class *class;	/* 类 */
	struct device *device;  /* 设备 */
};

struct newchar_dev myled;

static int led_gpio_probe(struct platform_device *pdev)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	led_gpio = gpiod_get(&pdev->dev, "led", 0);
	if(IS_ERR(led_gpio))
	{
		printk(KERN_ERR "gpiod_get is err\r\n");
		return -1;
	}

	/*
	major = register_chrdev(0, "my_leddrv", &led_drv);
	led_class = class_create(THIS_MODULE, "my_led_class");
	if(IS_ERR(led_class))
	{
		printk(KERN_ERR "class_create is err\r\n");
	}
	
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "my_led%d", 0);
	*/

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	alloc_chrdev_region(&myled.devid,  0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
	myled.major = MAJOR(myled.devid); /* 获取主设备号 */
	myled.minor = MINOR(myled.devid); /* 获取次设备号 */
	printk("myled major is %d, minor is %d\r\n",myled.major, myled.minor);

	/* 2、初始化 cdev */
	myled.cdev.owner = THIS_MODULE;
	cdev_init(&myled.cdev, &led_drv_fops);

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

	/* 4、创建类 */
	myled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);

	/* 5、创建设备 */
	myled.device = device_create(myled.class, NULL,myled.devid, NULL, NEWCHRLED_NAME);
	
	return 0;	
}



static int led_gpio_remove(struct platform_device *pdev)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);

	/* 注销字符设备 */
	cdev_del(&myled.cdev);/* 删除 cdev */
	unregister_chrdev_region(myled.devid, NEWCHRLED_CNT);

	/* 删除设备文件和对应类 */
	device_destroy(myled.class, myled.devid);
	class_destroy(myled.class);

	gpiod_put(led_gpio);
	
	return 0;	
}


static const struct of_device_id my_led[] =
{
	{.compatible = "my,led_driver"},
	{},
};


static struct platform_driver led_gpio_driver =
{
	.probe  = led_gpio_probe,
	.remove = led_gpio_remove,
	.driver = 
	{
		.name = "led_gpio",
		.of_match_table = my_led,
	},
};


static int __init led_init(void)
{
	int result;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	result = platform_driver_register(&led_gpio_driver);
	return result;
}


static void __exit led_exit(void)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&led_gpio_driver);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

上面的例子是用之前写的老led驱动程序改写的,主要看下probe函数新老差异,老的代码可以看下被注释掉的部分。

三、实例测试

手动加载驱动ko文件,可以看到/dev/目录下生成了主设备号为240,次设备号为0的字符设备,与我们在probe函数加的内核主次设备号打印相匹配,程序符合预期。

[root:/mnt]# insmod myleddrv_cdev.ko
[  495.380082] /home/book/code/test/myleddrv_cdev.c led_init line is 161
[  495.396821] /home/book/code/test/myleddrv_cdev.c led_gpio_probe line is 77
[root:/mnt]#
[root:/mnt]# ls -al /dev/my_leddrv
crw------- 1 root root 240, 0 Jan  1 00:08 /dev/my_leddrv

[root:/mnt]# echo 0 > /dev/my_leddrv
[  615.510401] /home/book/code/test/myleddrv_cdev.c led_drv_open line is 27
[  615.518102] /home/book/code/test/myleddrv_cdev.c led_drv_write line is 42
[  615.527225] /home/book/code/test/myleddrv_cdev.c led_drv_write line is 42
[  615.535099] /home/book/code/test/myleddrv_cdev.c led_drv_release line is 50
[root:/mnt]# echo 1 > /dev/my_leddrv
[  622.995421] /home/book/code/test/myleddrv_cdev.c led_drv_open line is 27
[  623.005806] /home/book/code/test/myleddrv_cdev.c led_drv_write line is 42
[  623.013132] /home/book/code/test/myleddrv_cdev.c led_drv_write line is 42
[  623.022285] /home/book/code/test/myleddrv_cdev.c led_drv_release line is 50

加载驱动之后,内核dmesg打印如下:

[  495.380082] /home/book/code/test/myleddrv_cdev.c led_init line is 161
[  495.396821] /home/book/code/test/myleddrv_cdev.c led_gpio_probe line is 77
[  495.404145] myled major is 240, minor is 0

与我们在/dev/目录下查询到的一致。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值