字符设备驱动(2)

字符设备驱动加载函数

有了《内核模块编写及编译》《字符设备驱动(1)》的基础,并且字符设备驱动对上注册给内核,对下要操作硬件,所以分成2层:

                     

//加载函数(必须)
static int hello_init(void)//返回值是int类型,函数名自定义,参数是void
{
    printk(KERN_ALERT "Hello World.\n");
    //up kernel


    //down hardware

    return 0;
}

备注:上面只写了加载函数。

其中对上注册给内核,又分成3步:1、注册设备号,2、初始化字符设备结构体,3、添加字符设备结构体给内核。类似去体检,先要预约个体检号(第一步),去之前要准备一下,比如前一天饮食清淡点,早上空腹等等(第二步),最后在去体检(第三步)。

这三步分别有3个函数对应:

#include <linux/fs.h> 
//1、注册设备号,下面2个二选一
//1.1、提前规划好要用的设备号,向内核单独申请
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//或者
//1.2、不知道使用哪些设备号,向内核动态申请
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)

#include <linux/cdev.h>
//2、初始化字符设备结构体
void cdev_init(struct cdev *cdev , const struct file_operations *fops)
//3、添加字符设备结构体给内核
int cdev_add(struct cdev *p, dev_t dev, unsigned count)

所以字符设备驱动加载函数如下:

//需要是全局变量
struct cdev cdevice;
struct file_operations cdev_ops;

//加载
static int hello_init(void)
{ 
    dev_t devno = MKDEV(MAJOR_CHAR,MINOR_CHAR);
    int ret = -1;
	
    //up kernel
        //1、注册设备号
        ret = register_chrdev_region(devno, 1, "hello");
        if (0 != ret)
        {
            printk("Register char device failed.\n");
            return ret;
        }

        printk("Register char device ok.\n");
    
        //2、初始化字符设备结构体
        cdev_init(&cdevice, &cdev_ops);
    
        cdevice.owner = THIS_MODULE;
	
        //3、添加字符设备结构体给内核
        ret = cdev_add(&cdevice,devno , 1);
        if (0 != ret)
        {   
            //注意释放设备号
            unregister_chrdev_region(devno,1);
            printk("Unregister char device.\n");
            return ret;
        }

    //down hardware

    return 0;
} 

其中cdevice、cdev_ops不能定义为局部变量,因为这2个结构体是要在驱动(模块)加载完成后也需要长期存在,要挂载进内核,即要在hello_init函数执行完后也存在,若定义为hello_init函数中的局部变量,则函数执行完就释放掉了。

另外在cdev_add的异常分支注意需要释放设备号。

字符设备驱动卸载函数

字符设备驱动卸载函数也分成2层,和加载函数流程相反,先释放硬件资源,再从内核中注销,注销分成2部:1、从内核中删除字符设备结构体,2、注销设备号

其中从内核中删除字符设备结构体如下

#include <linux/cdev.h>
void cdev_del(struct cdev *cdevice)

卸载函数如下:

//卸载函数(必须)
static void hello_exit(void)//返回值是void类型,函数名自定义,参数是void
{
    cdev_t devno = MKDEV(MA, MI);

    printk(KERN_ALERT "Goodbye World.\n");
    // down hardware

    // up kernel
        //1、从内核中删除字符设备结构体
        cdev_del(cdevice);

        //2、注销设备号
        unregister_chrdev_region(devno, 1);
}

字符设备操作

要对字符设备进行操作,可以像对文件操作,比如打开、关闭、读写等等,也就是对struct file_operations cdev_ops赋值,其成员是函数指针,赋值如下:

static int my_open(struct inode *pnode, struct file *pfile)
{
     printk("Open cdev.\n");
     return 0;
}
            
static int my_close(struct inode *pnode, struct file *pfile)
{
     printk("Close cdev.\n");
     return 0;
}
            
struct file_operations cdev_ops = { 
     .open    = my_open,
     .release = my_close,
};

那怎么把字符设备、操作、设备文件、应用(App)联系起来呢,明天更新。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值