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——字符设备驱动的更多相关文章

  1. Linux驱动开发之字符设备驱动模型之file_operations

    90%的驱动模型都是按照下图开发的 下面来说下设备描述结构是什么东西 打开Linux-2.6.32.2的Source Insight 工程,搜索cdev 比如一个应用程序需要调用read和write这 ...

  2. LCD驱动分析(一)字符设备驱动框架分析

    参考:S3C2440 LCD驱动(FrameBuffer)实例开发<一>   S3C2440 LCD驱动(FrameBuffer)实例开发<二> LCD驱动也是字符设备驱动,也 ...

  3. Linux应用程序访问字符设备驱动详细过程【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51346532 下面先通过一个编写好的内核驱动模块来体验以下字符设备驱动 可以暂时 ...

  4. 初入android驱动开发之字符设备(一)

    大学毕业,初入公司,招进去的是android驱动开发工程师的岗位,那时候刚进去,首先学到的就是如何搭建kernel.android的编译环境,然后就是了解如何刷设备以及一些最基本的工具.如adb.fa ...

  5. Linux 驱动框架---cdev字符设备驱动和misc杂项设备驱动

    字符设备 Linux中设备常见分类是字符设备,块设备.网络设备,其中字符设备也是Linux驱动中最常用的设备类型.因此开发Linux设备驱动肯定是要先学习一下字符设备的抽象的.在内核中使用struct ...

  6. Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程

    /*********************************************************************************** * * 字符设备驱动基本操作及 ...

  7. 驱动开发--【字符设备、块设备简介】【sky原创】

    驱动开发   字符设备,块设备,网络设备   字符设备 以字节流的方式访问, 不能随机访问 有例外,显卡.EEPROM可以随机访问   EEPROM可以擦写1亿次,是一种字符设备,可以随机访问 读写是 ...

  8. 【Linux 驱动】简单字符设备驱动架构(LED驱动)

    本文基于icool210开发板,内核版本:linux2.6.35: 驱动代码: (1)头文件:led.h #ifndef __LED_H__ #define __LED_H__ #define LED ...

  9. 初入android驱动开发之字符设备(四-中断)

    上一篇讲到android驱动开发中,应用是怎样去操作底层硬件的整个流程,实现了按键控制led的亮灭.当然,这是一个非常easy的实例,只是略微演变一下,就能够得到广泛的应用. 如开发扫描头,应用透过监 ...

随机推荐

  1. linux下安装phpunit

    安装pear 的命令如下: $ wget http://pear.php.net/go-pear.phar $ php go-pear.phar 如果报出PHP Warning:  file_exis ...

  2. Building and booting Nexus 5 kernel

    1. Downloading toolchain and setup. git clone https://android.googlesource.com/platform/prebuilts/gc ...

  3. 爬虫之selenium 安装与 chromedriver安装

    今天学到一个有意思的插件,就是chromedriver,在爬虫的时候,如果网站反爬虫做的很好,自己又很想爬去里面的数据,那就可以用这个插件,虽然笨笨的,慢的一批,但是还有别的办法就不会用他啦, 这个东 ...

  4. 字符串与List互转

    List转字符串,用逗号隔开 List<string> list = new List<string>(); list.Add("a"); list.Add ...

  5. php内置函数分析之current()、next()、prev()、reset()、end()

    current()初始指向插入到数组中的第一个单元 next() 将数组的内部指针向前移动一位 prev() 将数组的内部指针倒回一位 reset() 将数组的内部指针指向第一个单元 end() 将数 ...

  6. APIO2019 题解

    APIO2019 题解 T1 奇怪装置 题目传送门 https://loj.ac/problem/3144 题解 很容易发现,这个东西一定会形成一个环.我们只需要求出环的长度就解决了一切问题. 设环的 ...

  7. linux下实现web数据同步的四种方式(性能比较)

    实现web数据同步的四种方式 ======================================= 1.nfs实现web数据共享2.rsync +inotify实现web数据同步3.rsyn ...

  8. C++ GUI Qt4编程-创建自定义窗口部件

    C++ GUI Qt4编程-创建自定义窗口部件   Qtqt4 通过Qt窗口部件进行子类化或者直接对QWidget进行子类化,就可以创建自定义窗口部件,下面示范两种方式,并且也会说明如何把自定义窗口部 ...

  9. Chrome设置--disable-web-security解决跨域问题

    这里介绍的是--disable-web-security参数.这个参数可以降低chrome浏览器的安全性,禁用同源策略,利于开发人员本地调试. (1)新建一个chrome快捷方式,右键“属性”,“快捷 ...

  10. Python---协程---重写多线程

    一. # 用协程的方式,修改播放电影和音乐的练习题 # 用协程的方式完成播放 movie_list = ["斗破.mp4", "复仇者联盟.avi", &quo ...