(2.3)file_operation 实现具体操作:思考二

【思考二;内核怎样创建设备,主设备号具体应用是什么呢?】

major = register_chrdev(0, "leds_dev", &jz2440_leds_fops); //注册设备 告诉内核

> 第一个参数是主设备号,0代表动态分配。第二个参数是设备的名字,第三个参数是文件操作指针。

__register_chrdev(0, 0, 256, "leds_dev", &jz2440_leds_fops);

 

int __register_chrdev(unsigned int major, unsigned int baseminor,

unsigned int count, const char *name,

const struct file_operations *fops)

{

struct char_device_struct *cd;

struct cdev *cdev;

...

cd = __register_chrdev_region(major, baseminor, count, name);

/* __register_chrdev_region(0, 0, 255, "leds_dev");

调用了 __register_chrdev_region 并强制指定了起始次设备号为0,256个,把一个主设备号下的所有次设备号都申请光了。同时它还封装了 cdev_init 和 cdev_add ,倒是很省事。 */

...

cdev = cdev_alloc(); /* 分配了一个cdev结构体,并把它添加进链表 */

... // 下面几行起到了 cdev_init() 的作用

cdev->owner = fops->owner;

cdev->ops = fops;

kobject_set_name(&cdev->kobj, "%s", name);

 

err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);

/**

* cdev_add() - add a char device to the system

* @p: the cdev structure for the device

* @dev: the first device number for which this device is responsible

* @count: the number of consecutive minor numbers corresponding to this

* device

*

* cdev_add() adds the device represented by @p to the system, making it

* live immediately. A negative error code is returned on failure.

*/

...

cd->cdev = cdev;

 

return major ? 0 : cd->major; /* 返回主设备号 */

}

 

简单来说就是分配一个主设备号,并把相对应的file_operation等信息存放在一个cdev结构体中。

 

上面涉及到的其他函数,主要是因为:Linux主设备号有限内核最多支持 256 个字符设备驱动程序。在 2.6 的内核之后,新增了一个 register_chrdev_region 函数,它支持将同一个主设备号下的次设备号进行分段,每一段供给一个字符设备驱动程序使用,使得资源利用率大大提升。

下图很清晰的表明了其中关系,和各个函数的用处。

下面用一个示例程序表明两种注册驱动的异同:

#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;

static int hello_open(struct inode *inode, struct file *file)
{
    printk("hello_open\n");
    return 0;
}

static int hello2_open(struct inode *inode, struct file *file)
{
    printk("hello2_open\n");
    return 0;
}

/* 2. 构造file_operations */
static struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,
};

static struct file_operations hello2_fops = {
    .owner = THIS_MODULE,
    .open = hello2_open,
};

#define HELLO_CNT 2
static struct cdev hello_cdev;
static struct cdev hello2_cdev;
static struct class *cls;

static int hello_init(void)
{
    dev_t devid;

    /* 3. 告诉内核 */
    #if 0 // 老方法
        /* (major, 0), (major, 1), ..., (major, 255)都对应hello_fops */
        major = register_chrdev(0, "hello", &hello_fops);

    #else // 新方法
        if (major) {
            devid = MKDEV(major, 0);
            /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops
                给定主设备号 */
            register_chrdev_region(devid, HELLO_CNT, "hello");
        } 
        else
        {
            /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops
                自动分配主设备号 */
            alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");
            major = MAJOR(devid);
        }

    cdev_init(&hello_cdev, &hello_fops);
    cdev_add(&hello_cdev, devid, HELLO_CNT);

    /* 同一个主设备号下的不同次设备号,采用不同的fops */
    devid = MKDEV(major, 2);
    register_chrdev_region(devid, 1, "hello2");
    cdev_init(&hello2_cdev, &hello2_fops);
    cdev_add(&hello2_cdev, devid, 1);

    #endif

    cls = class_create(THIS_MODULE, "hello");
    device_create(cls, NULL, MKDEV(major, 0), NULL, "hello0"); /* /dev/hello0 */
    device_create(cls, NULL, MKDEV(major, 1), NULL, "hello1"); /* /dev/hello1 */
    device_create(cls, NULL, MKDEV(major, 2), NULL, "hello2"); /* /dev/hello2 */
    device_create(cls, NULL, MKDEV(major, 3), NULL, "hello3"); /* /dev/hello3 */

    return 0;
}

static void hello_exit(void)
{
    device_destroy(cls, MKDEV(major, 0));
    device_destroy(cls, MKDEV(major, 1));
    device_destroy(cls, MKDEV(major, 2));
    device_destroy(cls, MKDEV(major, 3));
    class_destroy(cls);

    cdev_del(&hello_cdev);
    unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);
    cdev_del(&hello2_cdev);
    unregister_chrdev_region(MKDEV(major, 2), 1);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值