栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux驱动开发(一)--字符驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux驱动开发(一)--字符驱动

Linux驱动开发–字符驱动

好记性不如烂笔头,学习做一下笔记记录在此。
写驱动之前的目的导向:

编写驱动的第一步是定义驱动将要提供给用户程序的能力(机制).因为我们的"设备"是计算机内存的一部分, 我们可自由做我们想做的事情. 它可以是一个顺序的或者随机存取的设备, 一个或多个设备, 等等.
scull源码 主次编号

dev_t是32位量,其中12位用作主编号,20位用作次编号

获取主次编号
MAJOR(dev_t dev)
MINOR(dev_t dev)
将主次编号转换为dev_t
MKDEV(int major, int minor)
分配和释放设备编号

建立字符驱动时第一件事:获取一个或多个设备编号来使用。


int register_chrdev_region(dev_t first, unsigned int count, char *name);
	first 是你要分配的起始设备编号. first 的次编号部分常常是 0, 但是没有要求是那个效果. 
	count 是你请求的连续设备编号的总数. 注意, 如果 count 太大, 你要求的范围可能溢出到下一个次编号; 但是只要你要求的编号
范围可用, 一切都仍然会正确工作.
	name 是应当连接到这个编号范围的设备的名子; 它会出现在 /proc/devices 和 sysfs 中.
	返回值 0 成功,失败返回错误码

动态分配设备编号:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
	dev 是一个只输出的参数, 它在函数成功完成时持有你的分配范围的第一个数. 
	fisetminor 应当是请求的第一个要用的次编号; 它常常是 0.
	count 和 name 与register_chrdev_region一致
	
设备编号的释放:
void unregister_chrdev_region(dev_t first, unsigned int count);
file_operations 结构体注解
struct file_operations {
	struct module *owner;
	指向拥有这个结构的模块的指针. 这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在  中定义的宏.
	
	loff_t (*llseek) (struct file *, loff_t, int);
	llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32 位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器。
	
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以 -EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型).
	
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数.
	
	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 *);
	int (*iterate_shared) (struct file *, struct dir_context *);
	__poll_t (*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 *);
	mmap 用来请求将设备内存映射到进程的地址空间. 如果这个方法是 NULL, mmap 系统调用返回 -ENODEV.
	
	unsigned long mmap_supported_flags;
	int (*open) (struct inode *, struct file *);
	打开设备,若NULL,则驱动得不到通知
	
	int (*flush) (struct file *, fl_owner_t id);
	flush 操作在进程关闭它的设备文件描述符的拷贝时调用; 它应当执行(并且等待)设备的任何未完成的操作. 这个必须不要和用户查询请求的 fsync 操作混淆了. 当前, flush 在很少驱动中使用; SCSI 磁带驱动使用它, 例如, 为确保所有写的数据在设备关闭前写到磁带上. 如果 flush 为 NULL, 内核简单地忽略用户应用程序的请求.
	
	int (*release) (struct inode *, struct file *);
	在文件结构被释放时引用这个操作.
	
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	这个方法是 fsync 系统调用的后端, 用户调用来刷新任何挂着的数据. 如果这个指针是 NULL, 系统调用返回 -EINVAL.
	
	int (*fasync) (int, struct file *, int);
	这个操作用来通知设备它的 FASYNC 标志的改变. 异步通知
	
	int (*lock) (struct file *, int, struct file_lock *);
	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
	ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
			loff_t, size_t, unsigned int);
	loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
				   struct file *file_out, loff_t pos_out,
				   loff_t len, unsigned int remap_flags);
	int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;

scull 设备驱动只实现最重要的设备方法. 它的 file_operations 结构是如下初始化的:

struct file_operations scull_fops = { 
 		.owner = THIS_MODULE, 
 		.llseek = scull_llseek, 
 		.read = scull_read, 
 		.write = scull_write, 
 		.ioctl = scull_ioctl, 
 		.open = scull_open, 
 		.release = scull_release, 
 }; 
文件结构
定义与
struct file {
	union {
		struct llist_node	fu_llist;
		struct rcu_head 	fu_rcuhead;
	} f_u;
	struct path		f_path;
	struct inode		*f_inode;	
	const struct file_operations	*f_op;
	和文件关联的操作. 内核安排指针作为它的 open 实现的一部分, 接着读取它,当它需要分派任何的操作时. filp->f_op 中的值从不由内核保存为后面的引用; 这意味着你可改变你的文件关联的文件操作, 在你返回调用者之后新方法会起作用. 例如, 关联到主编号 1 (/dev/null, /dev/zero, 等等)的 open 代码根据打开的次编号来替代 filp->f_op 中的操作. 这个做法允许实现几种行为, 在同一个主编号下而不必在每个系统调用中引入开销. 替换文件操作的能力是面向对象编程的"方法重载"的内核对等体.

	
	spinlock_t		f_lock;
	enum rw_hint		f_write_hint;
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	文件标志,如O_NONBLOCK此类
	
	fmode_t			f_mode;
	文件模式确定文件是可读的或者是可写的(或者都是), 通过位FMODE_READ 和 FMODE_WRITE. 你可能想在你的 open 或者 ioctl 函数
中检查这个成员的读写许可, 但是你不需要检查读写许可, 因为内核在调用你的方法之前检查. 当文件还没有为那种存取而打开时读或写的企
图被拒绝, 驱动甚至不知道这个情况.
	
	struct mutex		f_pos_lock;
	loff_t			f_pos;
	当前读写位置. loff_t 在所有平台都是 64 位( 在 gcc 术语里是 long long ). 驱动可以读这个值, 如果它需要知道文件中的当前位置, 但是正常地不应该改变它; 读和写应当使用它们作为最后参数而收到的指针来更新一个位置, 代替直接作用于 filp->f_pos. 这个规则的一个例外是在 llseek 方法中, 它的目的就是改变文件位置.
	
	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
	
	void			*private_data;
	open 系统调用设置这个指针为 NULL, 在为驱动调用 open 方法之前. 你可自由使用这个成员或者忽略它; 你可以使用这个成员来指向分配的数据, 但是接着你必须记住在内核销毁文件结构之前, 在 release 方法中释放那个内存. private_data 是一个有用的资源, 在系统调用间保留状态信息, 我们大部分例子模块都使用它.

#ifdef CONFIG_EPOLL
	
	struct list_head	f_ep_links;
	struct list_head	f_tfile_llink;
#endif 
	struct address_space	*f_mapping;
	errseq_t		f_wb_err;
} __randomize_layout
  __attribute__((aligned(4)));	
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/679356.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号