linux内核学习笔记:字符设备驱动学习①

学习视频:linux内核开发第4讲:编写最简单的字符设备驱动
学习文章:链接
源码:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include<linux/fs.h>
#include <linux/wait.h>
#include<linux/poll.h>
#include <linux/sched.h>
#include<linux/slab.h>
#define BUFFER_MAX    (10)
#define OK (0)
#define ERROR (-1)
struct cdev *gDev;
struct file_operations *gFile;
dev_t devNum;
unsigned int subDevNum = 1;
int reg_major = 232;
int reg_minor = 0;
char *buffer;
int flag = 0;
int hello_open(struct inode *p, struct file *f)
{
    printk(KERN_EMERG "hello_open\r\n");
    return 0;
}
ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l)
{
    printk(KERN_EMERG "hello_write\r\n");
    return 0;
}
ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
{
    printk(KERN_EMERG "hello_read\r\n");
    return 0;
}
int hello_init(void)
{
    devNum = MKDEV(reg_major, reg_minor);
    if (OK == register_chrdev_region(devNum, subDevNum, "helloworld"))
    {
        printk(KERN_EMERG "register_chrdev_region ok \n");
    }
    else
    {
        printk(KERN_EMERG "register_chrdev_region error n");
        return ERROR;
    }
    printk(KERN_EMERG " hello driver init \n");
    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
    gFile->open = hello_open;
    gFile->read = hello_read;
    gFile->write = hello_write;
    gFile->owner = THIS_MODULE;
    cdev_init(gDev, gFile);
    cdev_add(gDev, devNum, 1);
    return 0;
}
void __exit hello_exit(void)
{
    cdev_del(gDev);
    unregister_chrdev_region(devNum, subDevNum);
    return;
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

linux下的驱动具有两个序列号:主序列号和次序列号。

其中,主序列号直接关联我们的驱动,每种的驱动都具有它唯一的主序列号,而在一种的驱动之下,我们可能会同时连接好几个这种驱动对应的设备,于是,次序列号就是用来标注同一驱动下每个的设备的序列号。
比如假设雷蛇鼠标的驱动拥有主序列号 238,而现在这台电脑比较鬼畜,连了3个雷蛇鼠标,那么我们就可以设这3个鼠标的次序列号分别为:0,1,2.那么,他们的序列号就分别为:238 0,238 1,238 2.

现在我们讲一下代码,最底下的

module_init(hello_init);
module_exit(hello_exit);

为我们标注了这个驱动的入口函数和出口函数。入口函数好理解,就是起始点嘛。那出口函数的作用是什么呢?我的理解是,这个函数的作用类似于我们底层的ret之前或之后,所需要做的那个清栈工作。其目的在于正确地退出程序。

然后是我们的入口(初始化)函数:

int hello_init(void)
{
    devNum = MKDEV(reg_major, reg_minor);
    if (OK == register_chrdev_region(devNum, subDevNum, "helloworld"))
    {
        printk(KERN_EMERG "register_chrdev_region ok \n");
    }
    else
    {
        printk(KERN_EMERG "register_chrdev_region error n");
        return ERROR;
    }
    printk(KERN_EMERG " hello driver init \n");
    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
    gFile->open = hello_open;
    gFile->read = hello_read;
    gFile->write = hello_write;
    gFile->owner = THIS_MODULE;
    cdev_init(gDev, gFile);
    cdev_add(gDev, devNum, 1);
    return 0;
}

这里的第一句就是我们前面提到的序列号初始化。这里我们不需要关注它的实现原理,只需要值得其是这样用的就行了。

    devNum = MKDEV(reg_major, reg_minor);

而后就是一些调试语句:

    if (OK == register_chrdev_region(devNum, subDevNum, "helloworld"))
    {
        printk(KERN_EMERG "register_chrdev_region ok \n");
    }
    else
    {
        printk(KERN_EMERG "register_chrdev_region error n");
        return ERROR;
    }
    printk(KERN_EMERG " hello driver init \n");

接下来就是初始化函数里的核心部分(前面那个序列号其实也算核心):

    gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
    gFile->open = hello_open;
    gFile->read = hello_read;
    gFile->write = hello_write;
    gFile->owner = THIS_MODULE;
    cdev_init(gDev, gFile);
    cdev_add(gDev, devNum, 1);

这里我们同样也不需要在意一些底层的实现,这里我们需要知道是,
①gFile的oepn,read,write成员存储了我们对应三个功能函数的地址,同样,owner对应的是本体文件模块。
②我们已经设置好了序列号,初始化好了gDev(cdev),初始化并赋参了gFile(file_operations),那么我们需要为这三者之间建立一个联系 – 即为gDev赋参,也就是:

    cdev_init(gDev, gFile);
    cdev_add(gDev, devNum, 1);

那么到这里我们的一个最简单的字符设备驱动代码编写也就完事了,剩下的后边写。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值