Linux驱动开发2——字符设备驱动
1、申请设备号
#include <linux/fs.h> int register_chrdev_region(dev_t first, unsigned int count, char *name);
静态分配字符设备号,从fist开始的count个,name为设备名称(name会出现在/proc/devices和sysfs中),成功返回0,失败返回一个负的错误码 int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
动态分配字符设备号,主设备号动态分配,次设备号从firstminor开始的count个,name为设备名称(动态分配的主设备号可以在/proc/devices中获取) void unregister_chrdev_region(dev_t first, unsigned int count);
注销字符设备号,从first开始的count个
2、初始化字符设备
#include <linux/cdev.h> 动态创建
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops; 静态创建
void cdev_init(struct cdev *cdev, struct file_operations *fops);
3、添加和删除字符设备
#include <linux/cdev.h> 添加字符设备
int cdev_add(struct cdev *dev, dev_t num, unsigned int count); 删除字符设备
void cdev_del(struct cdev *dev);
4、关键数据结构
4.1、file_operations结构体
struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
        unsigned (*mmap_capabilities)(struct file *);
#endif
};
4.2、file结构体
struct file {
        union {
                struct llist_node       fu_llist;
                struct rcu_head         fu_rcuhead;
        } f_u;
        struct path             f_path;
        struct inode            *f_inode;       /* cached value */
        const struct file_operations    *f_op;
        /*
         * Protects f_ep_links, f_flags.
         * Must not be taken from IRQ context.
         */
        spinlock_t              f_lock;
        atomic_long_t           f_count;
        unsigned int            f_flags;
        fmode_t                 f_mode;
        struct mutex            f_pos_lock;
        loff_t                  f_pos;
        struct fown_struct      f_owner;
        const struct cred       *f_cred;
        struct file_ra_state    f_ra;
        u64                     f_version;
#ifdef CONFIG_SECURITY
        void                    *f_security;
#endif
        /* needed for tty driver, and maybe others */
        void                    *private_data;
#ifdef CONFIG_EPOLL
        /* Used by fs/eventpoll.c to link all the hooks to this file */
        struct list_head        f_ep_links;
        struct list_head        f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
        struct address_space    *f_mapping;
} __attribute__((aligned()));  /* lest something weird decides that 2 is OK */
4.3、inode结构体
struct inode {
        umode_t                 i_mode;
        unsigned short          i_opflags;
        kuid_t                  i_uid;
        kgid_t                  i_gid;
        unsigned int            i_flags;
#ifdef CONFIG_FS_POSIX_ACL
        struct posix_acl        *i_acl;
        struct posix_acl        *i_default_acl;
#endif
        const struct inode_operations   *i_op;
        struct super_block      *i_sb;
        struct address_space    *i_mapping;
#ifdef CONFIG_SECURITY
        void                    *i_security;
#endif
        /* Stat data, not accessed from path walking */
        unsigned long           i_ino;
        /*
         * Filesystems may only read i_nlink directly.  They shall use the
         * following functions for modification:
         *
         *    (set|clear|inc|drop)_nlink
         *    inode_(inc|dec)_link_count
         */
        union {
                const unsigned int i_nlink;
                unsigned int __i_nlink;
        };
        dev_t                   i_rdev;
        loff_t                  i_size;
        struct timespec         i_atime;
        struct timespec         i_mtime;
        struct timespec         i_ctime;
        spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
        unsigned short          i_bytes;
        unsigned int            i_blkbits;
        blkcnt_t                i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
        seqcount_t              i_size_seqcount;
#endif
        /* Misc */
        unsigned long           i_state;
        struct mutex            i_mutex;
        unsigned long           dirtied_when;   /* jiffies of first dirtying */
        unsigned long           dirtied_time_when;
        struct hlist_node       i_hash;
        struct list_head        i_io_list;      /* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
        struct bdi_writeback    *i_wb;          /* the associated cgroup wb */
        /* foreign inode detection, see wbc_detach_inode() */
        int                     i_wb_frn_winner;
        u16                     i_wb_frn_avg_time;
        u16                     i_wb_frn_history;
#endif
        struct list_head        i_lru;          /* inode LRU list */
        struct list_head        i_sb_list;
        union {
                struct hlist_head       i_dentry;
                struct rcu_head         i_rcu;
        };
        u64                     i_version;
        atomic_t                i_count;
        atomic_t                i_dio_count;
        atomic_t                i_writecount;
#ifdef CONFIG_IMA
        atomic_t                i_readcount; /* struct files open RO */
#endif
        const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
        struct file_lock_context        *i_flctx;
        struct address_space    i_data;
        struct list_head        i_devices;
        union {
                struct pipe_inode_info  *i_pipe;
                struct block_device     *i_bdev;
                struct cdev             *i_cdev;
                char                    *i_link;
        };
        __u32                   i_generation;
#ifdef CONFIG_FSNOTIFY
        __u32                   i_fsnotify_mask; /* all events this inode cares about */
        struct hlist_head       i_fsnotify_marks;
#endif
        void                    *i_private; /* fs or device private pointer */
};
Linux中一切皆文件,对于字符设备驱动而言,都是通过设备文件进行交互的。Linux内核中,针对每一个文件有一个唯一的inode结构体描述,但是一个文件可以打开多次,所以内核使用file结构体作为文件描述符,每次打开同一个文件,均生成一个新的文件描述符,但是所有的文件描述符都指向同一个inode,表示打开的是同一个文件。inode和file结构体中均有file_operation结构体成员,它就是指向驱动实现的file_operations结构体,为文件操作提供具体实现函数。
5、container_of宏
#include <linux/kernel.h> container_of(pointer, container_type, container_field);
6、open和release
6.1、int open(struct inode *inode, struct file *filp);
open函数中应当进行以下工作:
1)检查设备特定的错误(如设备未准备好,或者硬件错误)
2)如果第一次打开,初始化设备
3)如果需要,更新f_op指针
4)分配并填充要放入filp_private_data的任何数据结构
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* contain cdev member */ dev = contailer_of(inode->i_cdev, struct scull_dev, cdev); filp->private_data = dev; return ;
}
6.2、int release(struct inode *inode, struct file *filp);
release函数应当进行以下工作:
1)释放open分配在filp->private_data中的任何东西
2)在最后close关闭设备
int scull_release(struct inode *inode, struct file *filp)
{
return ;
}
7、read和write
#include <linux/fs.h> ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp); ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
8、ioctl
#include <linux/ioctl.h> long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);
long (*compat_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg); cmd - 操作码(四字节,type|nr|direction|size)
类型:魔数,通常为一个字符,表示特定设备,自定义
序号:命令序号
方向:__IOC_NONE|__IOC_READ|__IOC_WRITE
大小:数据结构大小 根据方向,可以使用_IO(type,nr)
_IOR(type,nr,datatype)
_IOW(type,nr,datatype)
组合生成cmd操作码,size成员通过sizeof(datatype)获取 反之,可以通过宏解析出cmd操作码的不同字段:
类型:_IOC_TYPE(cmd)
方向:_IOC_DIR(cmd)
序号:_IOC_NR(cmd)
大小:_IOC_SIZE(cmd)
arg - 可选参数(可以是一个整数或者一个指针) 返回值:失败返回-ENOTTY或者-ENIVAL给用户 ioctl函数注册时,需要注意按照以下形式注册:
struct file_operations globalvar_fops = {
.owner = THIS_MODULE,
.open = globalvar_open,
.release = globalvar_release,
.read = globalvar_read,
.write = globalvar_write,
.unlocked_ioctl = globalvar_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = globalvar_ioctl,
#endif
};
9、access_ok()
#include <asm/uaccess.h> 检查用户空间地址是否可以存取,成功返回1,失败返回0,失败时,应该给调用者返回-EFAULT错误码
int access_ok(int type, const void *addr, unsigned long size); type: VERIFY_READ|VERIFY_WRITE
addr: 用户空间地址
size: 大小
Linux驱动开发2——字符设备驱动的更多相关文章
- Linux驱动开发之字符设备驱动模型之file_operations
		
90%的驱动模型都是按照下图开发的 下面来说下设备描述结构是什么东西 打开Linux-2.6.32.2的Source Insight 工程,搜索cdev 比如一个应用程序需要调用read和write这 ...
 - LCD驱动分析(一)字符设备驱动框架分析
		
参考:S3C2440 LCD驱动(FrameBuffer)实例开发<一> S3C2440 LCD驱动(FrameBuffer)实例开发<二> LCD驱动也是字符设备驱动,也 ...
 - Linux应用程序访问字符设备驱动详细过程【转】
		
本文转载自:http://blog.csdn.net/coding__madman/article/details/51346532 下面先通过一个编写好的内核驱动模块来体验以下字符设备驱动 可以暂时 ...
 - 初入android驱动开发之字符设备(一)
		
大学毕业,初入公司,招进去的是android驱动开发工程师的岗位,那时候刚进去,首先学到的就是如何搭建kernel.android的编译环境,然后就是了解如何刷设备以及一些最基本的工具.如adb.fa ...
 - Linux 驱动框架---cdev字符设备驱动和misc杂项设备驱动
		
字符设备 Linux中设备常见分类是字符设备,块设备.网络设备,其中字符设备也是Linux驱动中最常用的设备类型.因此开发Linux设备驱动肯定是要先学习一下字符设备的抽象的.在内核中使用struct ...
 - Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程
		
/*********************************************************************************** * * 字符设备驱动基本操作及 ...
 - 驱动开发--【字符设备、块设备简介】【sky原创】
		
驱动开发 字符设备,块设备,网络设备 字符设备 以字节流的方式访问, 不能随机访问 有例外,显卡.EEPROM可以随机访问 EEPROM可以擦写1亿次,是一种字符设备,可以随机访问 读写是 ...
 - 【Linux 驱动】简单字符设备驱动架构(LED驱动)
		
本文基于icool210开发板,内核版本:linux2.6.35: 驱动代码: (1)头文件:led.h #ifndef __LED_H__ #define __LED_H__ #define LED ...
 - 初入android驱动开发之字符设备(四-中断)
		
上一篇讲到android驱动开发中,应用是怎样去操作底层硬件的整个流程,实现了按键控制led的亮灭.当然,这是一个非常easy的实例,只是略微演变一下,就能够得到广泛的应用. 如开发扫描头,应用透过监 ...
 
随机推荐
- linux下安装phpunit
			
安装pear 的命令如下: $ wget http://pear.php.net/go-pear.phar $ php go-pear.phar 如果报出PHP Warning: file_exis ...
 - Building and booting Nexus 5 kernel
			
1. Downloading toolchain and setup. git clone https://android.googlesource.com/platform/prebuilts/gc ...
 - 爬虫之selenium 安装与 chromedriver安装
			
今天学到一个有意思的插件,就是chromedriver,在爬虫的时候,如果网站反爬虫做的很好,自己又很想爬去里面的数据,那就可以用这个插件,虽然笨笨的,慢的一批,但是还有别的办法就不会用他啦, 这个东 ...
 - 字符串与List互转
			
List转字符串,用逗号隔开 List<string> list = new List<string>(); list.Add("a"); list.Add ...
 - php内置函数分析之current()、next()、prev()、reset()、end()
			
current()初始指向插入到数组中的第一个单元 next() 将数组的内部指针向前移动一位 prev() 将数组的内部指针倒回一位 reset() 将数组的内部指针指向第一个单元 end() 将数 ...
 - APIO2019 题解
			
APIO2019 题解 T1 奇怪装置 题目传送门 https://loj.ac/problem/3144 题解 很容易发现,这个东西一定会形成一个环.我们只需要求出环的长度就解决了一切问题. 设环的 ...
 - linux下实现web数据同步的四种方式(性能比较)
			
实现web数据同步的四种方式 ======================================= 1.nfs实现web数据共享2.rsync +inotify实现web数据同步3.rsyn ...
 - C++ GUI Qt4编程-创建自定义窗口部件
			
C++ GUI Qt4编程-创建自定义窗口部件 Qtqt4 通过Qt窗口部件进行子类化或者直接对QWidget进行子类化,就可以创建自定义窗口部件,下面示范两种方式,并且也会说明如何把自定义窗口部 ...
 - Chrome设置--disable-web-security解决跨域问题
			
这里介绍的是--disable-web-security参数.这个参数可以降低chrome浏览器的安全性,禁用同源策略,利于开发人员本地调试. (1)新建一个chrome快捷方式,右键“属性”,“快捷 ...
 - Python---协程---重写多线程
			
一. # 用协程的方式,修改播放电影和音乐的练习题 # 用协程的方式完成播放 movie_list = ["斗破.mp4", "复仇者联盟.avi", &quo ...