【IMX6ULL驱动开发学习】02.hello驱动程序之cdev注册字符设备驱动程序和设置次设备号

目录

一、register_chrdev

二、解决方法

2.1 alloc_chrdev_region函数:注册一系列字符设备编号

2.2 cdev_init函数:初始化cdev结构体 

2.3  cdev_add函数:将字符设备添加到系统中

 三、驱动程序


一、register_chrdev

major = register_chrdev(0, "100ask_hello", &hello_drv);

【IMX6ULL驱动开发学习】01.编写第一个hello驱动+自动创建设备节点(不涉及硬件操作)_阿龙还在写代码的博客-CSDN博客

在之前的hello驱动程序中,入口函数会用 register_chrdev来注册字符设备驱动程序,好处是方便快捷,缺点是霸占了主设备号下的所有此设备号。当我们手动创建一个设备节点(主设备号相同),因为有多个次设备号,所以用上面的设备节点也可以访问hello驱动程序。

Linux内核提供的主设备号是有限的,如果设备很多的情况下主设备号就可能不够用了,那怎么办呢?
解决办法:可以在注册驱动设备的时候,给设备分配好固定的次设备号。

二、解决方法

先定义两个静态全局变量

static struct cdev hello_cdev;
static dev_t dev;

2.1 alloc_chrdev_region函数:注册一系列字符设备编号

int ret;
// major = register_chrdev(0, "100ask_hello", &hello_drv);

ret = alloc_chrdev_region(&dev, 0, 2, "hello");  // dev/hello c 245 0
if (ret < 0) {
	printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");
	return -EINVAL;
}
  • register_chrdev 该函数会把主设备号下所有次设备号都霸占了

  • dev为输出变量,这个结构体里会含有设备的主次设备号

  • 0为次设备号,2为想获得几个次设备号,hello为名字

  • 如果自己创建设备节点 mknod /dev/xyz c 245(具体数字看对应的主设备号) 1

  • 因为有两个次设备号,所以用上面的设备节点也可以访问驱动程序

2.2 cdev_init函数:初始化cdev结构体 

cdev_init(&hello_cdev, &hello_drv);
  • 初始化hello_cdev,让hello_cdev与hello_drv结构体挂钩

2.3  cdev_add函数:将字符设备添加到系统中

ret = cdev_add(&hello_cdev, dev, 2);
if (ret)
{
	printk(KERN_ERR "cdev_add() failed for hello\n");
	return -EINVAL;
}
  • 添加hello_cdev 2为此设备号个数

 三、驱动程序

hello_drv.c

#include "asm-generic/errno-base.h"
#include "asm/cacheflush.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>


static struct class *hello_class;
static struct cdev hello_cdev;
static dev_t dev;

static unsigned char hello_buf[100];

static int hello_open (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    copy_to_user(buf, hello_buf, len);

    return len;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    unsigned long len = size > 100 ? 100 : size;

    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    copy_from_user(hello_buf, buf, len);

    return len;
}

static int hello_release (struct inode *node, struct file *filp)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* 1. create file_operations */
static const struct file_operations hello_drv = {
    .owner      = THIS_MODULE,
	.read		= hello_read,
	.write		= hello_write,
	.open		= hello_open,
    .release    = hello_release,
};


/* 2. register_chrdev */

/* 3. entry function */
static int hello_init(void)
{
    int ret;

    //申请主次设备号的空间 主次设备号放在dev结构体中
    //dev为输出变量,这个结构体里会含有设备的主次设备号
    //0为次设备号,2为想获得几个次设备号,hello为名字
	ret = alloc_chrdev_region(&dev, 0, 2, "hello");// dev/hello c 245 0
	if (ret < 0) {
		printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");
		return -EINVAL;
	}

    //初始化hello_cdev,让hello_cdev与hello_drv结构体挂钩
    cdev_init(&hello_cdev, &hello_drv);

    //添加hello_cdev 2为此设备号个数
    ret = cdev_add(&hello_cdev, dev, 2);
	if (ret)
    {
		printk(KERN_ERR "cdev_add() failed for hello\n");
		return -EINVAL;
    }
		    
	hello_class = class_create(THIS_MODULE, "hello_class");
	if (IS_ERR(hello_class)) {
		printk("failed to allocate class\n");
		return PTR_ERR(hello_class);
	}

    device_create(hello_class, NULL, dev, NULL, "hello");  /* /dev/hello */
    return 0;
}


/* 4. exit function */
static void hello_exit(void)
{
    device_destroy(hello_class, dev);

    class_destroy(hello_class);

    //unregister_chrdev(major, "100ask_hello");
    cdev_del(&hello_cdev);
    unregister_chrdev_region(dev, 2);
}


module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
这里提供一个简单的 IMX6ULL 键盘驱动程序,适用于 Linux 操作系统: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/interrupt.h> #define DRIVER_NAME "imx6ull-keyboard" #define MAX_KEYS 4 struct imx6ull_key { const char *name; int gpio; int keycode; int irq; int down; }; static struct imx6ull_key keys[MAX_KEYS] = { { "KEY1", IMX_GPIO_NR(1, 11), KEY_A, -1, 1 }, { "KEY2", IMX_GPIO_NR(1, 12), KEY_B, -1, 1 }, { "KEY3", IMX_GPIO_NR(1, 13), KEY_C, -1, 1 }, { "KEY4", IMX_GPIO_NR(1, 14), KEY_D, -1, 1 }, }; static irqreturn_t imx6ull_key_irq(int irq, void *dev_id) { struct input_dev *input = dev_id; struct imx6ull_key *key = input->private; int down = gpio_get_value(key->gpio); if (down != key->down) { key->down = down; input_report_key(input, key->keycode, down); input_sync(input); } return IRQ_HANDLED; } static int imx6ull_key_probe(struct platform_device *pdev) { struct input_dev *input; int i, ret; input = input_allocate_device(); if (!input) { dev_err(&pdev->dev, "Failed to allocate input device\n"); return -ENOMEM; } input->name = DRIVER_NAME; input->dev.parent = &pdev->dev; input->id.bustype = BUS_GPIO; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; for (i = 0; i < MAX_KEYS; i++) { input->private = &keys[i]; ret = gpio_request(keys[i].gpio, keys[i].name); if (ret) { dev_err(&pdev->dev, "Failed to request GPIO %d\n", keys[i].gpio); goto err_free_input; } gpio_direction_input(keys[i].gpio); keys[i].irq = gpio_to_irq(keys[i].gpio); ret = request_irq(keys[i].irq, imx6ull_key_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, keys[i].name, input); if (ret) { dev_err(&pdev->dev, "Failed to request IRQ %d\n", keys[i].irq); goto err_free_gpio; } } ret = input_register_device(input); if (ret) { dev_err(&pdev->dev, "Failed to register input device\n"); goto err_free_irq; } platform_set_drvdata(pdev, input); return 0; err_free_irq: for (i = 0; i < MAX_KEYS; i++) { if (keys[i].irq >= 0) free_irq(keys[i].irq, input); } err_free_gpio: for (i = 0; i < MAX_KEYS; i++) { if (gpio_is_valid(keys[i].gpio)) gpio_free(keys[i].gpio); } err_free_input: input_free_device(input); return ret; } static int imx6ull_key_remove(struct platform_device *pdev) { struct input_dev *input = platform_get_drvdata(pdev); int i; input_unregister_device(input); for (i = 0; i < MAX_KEYS; i++) { if (keys[i].irq >= 0) free_irq(keys[i].irq, input); if (gpio_is_valid(keys[i].gpio)) gpio_free(keys[i].gpio); } input_free_device(input); return 0; } static const struct of_device_id imx6ull_key_of_match[] = { { .compatible = "fsl,imx6ull-keyboard", }, {}, }; MODULE_DEVICE_TABLE(of, imx6ull_key_of_match); static struct platform_driver imx6ull_key_driver = { .probe = imx6ull_key_probe, .remove = imx6ull_key_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = imx6ull_key_of_match, }, }; module_platform_driver(imx6ull_key_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("IMX6ULL Keyboard Driver"); ``` 这个驱动程序使用了 GPIO 作为输入,并使用了 Linux 内核提供的 input 子系统来产生键盘事件。驱动程序使用了 `input_allocate_device()` 函数来分配输入设备,之后使用 `gpio_request()` 函数来请求 GPIO,并使用 `request_irq()` 函数来请求 IRQ。当 IRQ 发生时,调用 `imx6ull_key_irq()` 函数来处理事件,并使用 `input_report_key()` 函数来报告按键状态。最后,使用 `input_register_device()` 函数来注册输入设备驱动程序还实现了 `probe()` 和 `remove()` 函数,用于驱动程序的安装和卸载。 请注意,这个驱动程序仅仅是一个简单的例子,并不适用于所有情况。在实际应用中,您需要更加详细的实现和错误处理,以确保驱动程序的稳定性和正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿龙还在写代码

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值