嵌入式LINUX驱动学习之4.字符设备驱动编程(一)

一、字符设备操作接口数据结构:truct file_operations

1.1 头文件

//头文件位置:linux/fs.h
#include <linux/fs.h>
struct file_operations {
    
    
struct module *owner; // 指向宏:THIS_MODULE,用于表示当前进程调用
//当用户执行读文件操作时,调用
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//当用户执行写文件操作时,调用
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//当用户执行打开文件操作时,调用
int (*open) (struct inode *, struct file *);
//当用户执行关闭文件操作时,调用
int (*release) (struct inode *, struct file *);
//..........以上只是少部分,基于部分可以查看内核源文件...........
}

1.1.1THIS_MODULE宏实现

//include/linux/export.h 
#ifdef MODULE
extern struct module __this_module;
#define THIS_MODULE (&__this_module)
#else
#define THIS_MODULE ((struct module *)0)
#endif

1.2 struct file_operations结构体使用

#include <linux/fs.h>
ssize_t mychar_dev_open(struct inode * inode, struct file * file){
    
    
/*填写文件打开操作执行的代码*/
}
ssize_t mychar_dev_close(struct inode * inode,struct file * file){
    
    
/*填写文件关闭操作执行的代码*/
}
ssize_t mychar_dev_read(struct file * file,char __user * buf,\ 
                        size_t count,loff_t * ppos){
    
    
    char kbuf[count];
    memset(kbuf,0,count); 
    /*填写执行读操作执行的代码*/
    copy_to_user(buf,kbuf,count);
}
ssize_t mychar_dev_write(struct file * file ,const char __user *buf,\
                         size_t count, loff_t *ppos)
{
    
    
    char kbuf[count];
    memset(kbuf,0,count);
    copy_from_user(kbuf,buf,count);
/*填写文件写操作执行的代码*/
}
struct file_operations mychar_dev_fops = {
    
    
    .owner = THIS_MODULE,
    .open = mychar_dev_open,
    .release = mychar_dev_close,
    .read = mychar_dev_read,
    .write = mychar_dev_write,
};
/*
形参说明:
     file : 文件指错
     buf  : 用户空间的读(写)数据首地址
     count: 读(写)数据大小
     ppos : 记录上一次读(写)位置,由内核维护,不用管
*/

1.2.1用户空间和内核之间进行数据传输的函数:copy_to_user() 、 copy_from_user()

//头文件定义位置:arch/um/include/asm/uaccess.h
//函数实现:arch/um/kernel/skas/uaccess.c
#include <asm/uaccess.h>
//功能:拷贝内核空间数据到用户空间
int copy_to_user(void __user *to, const void *from, int n)EXPORT_SYMBOL(copy_to_user);//表示此函数对全部内核代码公开,即:内核中随时随地可以调用

//功能拷贝用户空间数据到内核空间
int copy_from_user(void *to, const void __user *from, int n)EXPORT_SYMBOL(copy_from_user);
/*
参数说明:
    参数一:数据的目的地址
    参数二:数据的源地址
    参数三:希望拷拷贝的数据大小
返回值:
    实现拷贝的数据大小
/*

二、定义初始化字符设备对象

2.1头文件

//头文件位置:include/linux/cdev.h
//函数实现:fs/char_dev.c
#include <linux/cdev.h>
void cdev_init(struct cdev *cdev, const struct file_operations *fops);

2.2函数实现(了解即可)

//函数实现:fs/char_dev.c
#include <linux/cdev.h>
struct cdev file_dev;
void cdev_init(struct cdev *cdev, const struct file_operations *fops)

{
    
    
        memset(cdev, 0, sizeof *cdev);  // 将cdev数据清空
        INIT_LIST_HEAD(&cdev->list);  //初始化cdev结构体列表
        
        //初始化kobj,使得&cdev->kobj->ktype = &ktype_cdev_default
        //实现代码见:lib/kobject.c
        kobject_init(&cdev->kobj, &ktype_cdev_default);
        cdev->ops = fops;  //也就是1.2中的结构体:mychar_dev_fops
}
/*
软件代码在初始代函数中这样写:
cdev_init(&file_dev,&mychar_dev_fops) ;
*/
// INIT_LIST_HEAD函数实现:
头文件位置:include/linux/list.h
#include <linux/list.h>
static inline void INIT_LIST_HEAD(struct list_head *list)
{
    
    
        list->next = list;
        list->prev = list;
}

三、向内核添加注册字符设备对象:cdev_add()

1.4.1 头文件

//头文件位置:include/linux/cdev.h
//函数实现:fs/char_dev.c
#include <linux/cdev.h>
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
/*
参数说明:
    p:指向字符设备对象
    dev:设备号
    count :次设备号个数
*/

1.4.2 函数实现

//  vim fs/char_dev.c
#include <linux/cdev.h>
static struct kobj_map *cdev_map;// 结构体在drivers/base/map.c中实现
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    
    
        p->dev = dev;  // 设备号
        p->count = count; //次设备号个数
        return kobj_map(cdev_map, dev, count,\
                        NULL, exact_match, exact_lock, p);
}
/*
软件代码在初始代函数中这样写:
cdev_add(file_dev,dev,1);
*/

四、向内核添加卸载字符设备对象:cdev_del()

4.1 头文件

//头文件位置:include/linux/cdev.h
//函数实现:fs/char_dev.c
#include <linux/cdev.h>
void cdev_del(struct cdev *);
/*
参数说明:
    p:指向字符设备对象;
软件代码在卸载代函数中这样写:
cdev_del(file_dev);
*/

猜你喜欢

转载自blog.csdn.net/weixin_47273317/article/details/107747342