Android系统中, 驱动程序因商业需求分为运行在用户空间的hardware层以及运行在内核空间的驱动程序, 大多情况下内核驱动都需要提供用户空间访问的接口。
Linux内核空间到用户空间的接口有主要有以下几种类型
1.系统调用
系统调用是指系统实现的所有系统调用所构成的集合,即程序接口。
linux系统调用分为:进程控制,文件系统控制,系统控制,内存管理,网络管理,用户管理,进程管理等类型
linux操作系统中,系统调用的ID通常在arch/{体系结构}/include/asm/目录的unistd.h文件中.
可增加系统调用为用户空间提供访问接口,用于系统调用是UNIX标准的内容,所以一般情况下不这样做。
2.字符设备节点
字符设备进行I/O操作不经过操作系统的缓冲区,每次操作只传输一个字符,可以通过字符设备文件来访问
文件操作file_operations表示对一个文件的操作,其定义在/kernel/include/linux/fs.h
........
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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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 (*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);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};
........
// 字符设备的注册函数
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
// 字符设备的销毁函数
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
........
对于一般的文件,可以通过通用的文件操作,如open,read,write,ioctl,mmap,release等,
对于简单的字符设备,也是驱动的设备节点,它的文件操作与一般的文件不一样,需要单独实现。
字符设备的驱动程序,可以基于字符设备的驱动程序框架来构建,也可基于特定的字符设备架构来构建.
基于字符设备的驱动程序架构来建设:
直接实现对字符设备的注册函数进行注册,此时定义的字符设备一般具有一个唯一的主设备号.
在注册具体的字符设备时,构建file_operations中的各个成员函数指针
基于特定的字符设备驱动程序架构来构建:(Misc驱动程序,帧缓存驱动程序,TTY驱动程序等)
这些驱动程序的架构通常对字符设备驱动架构进行封装,实现自己的架构,并且具有了自己的注册机制。
在具体的驱动程序的实现中,通过这种驱动程序架构的注册函数进行注册,实现相关的函数。
此时,驱动程序的架构一般已经实现了主设备号,具体的驱动程序实现的是次设备号。
在用户空间通过/dev/中设备节点调用驱动程序顺序:用户空间-->字符设备驱动程序的架构-->该类驱动程序的框架-->具体驱动程序的实现。
3.块设备节点(光盘,硬盘,软盘)
块设备采用随机访问的方式传输数据,并且数据总是具有固定大小的快,为了提高数据传输效率,块设备驱动程序内部采用块缓冲技术.
块设备的操作在/kernel/include/linux/blkdev.h中定义
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
void (*release) (struct gendisk *, fmode_t);
int (*rw_page)(struct block_device *, sector_t, struct page *, int rw);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*direct_access) (struct block_device *, sector_t,
void **, unsigned long *);
unsigned int (*check_events) (struct gendisk *disk,
unsigned int clearing);
/* ->media_changed() is DEPRECATED, use ->check_events() instead */
int (*media_changed) (struct gendisk *);
void (*unlock_native_capacity) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
int (*getgeo)(struct block_device *, struct hd_geometry *);
/* this callback is with swap_lock and sometimes page table lock held */
void (*swap_slot_free_notify) (struct block_device *, unsigned long);
struct module *owner;
};
注册与注销/kernel/include/linux/fs.h
extern int register_blkdev(unsigned int, const char *);
extern void unregister_blkdev(unsigned int, const char *);
对于块设备,由于也是设备节点,也可以使用文件接口来访问,一般不直接访问块设备节点,而是使用文件系统来访问。可用df或mount来查看挂载情况
Android块设备在/dev/block/目录中
4.网络设备
网络设备没有文件系统的节点,使用socket机制,通过专有的数据结构进行访问,系统内部支持数据的收发。
网络设备与字符设备和块设备最大的区别在于网络设备没有文件系统的节点。
一般网络驱动程序不会由应用程序调用,而是由linux内核的网络模块调用,用户空间程序通过socket等通用网络接口,间接调用网络驱动程序。
访问网络设备时,需要用到socket相关的几个函数:socket(),bind(),listen(),accept(),connect()等
socket()和open一样,返回类型也是一个文件描述符,表示一个在进程中打开的文件。可以用通常的文件操作来对这个文件描述符进行操作。
网络设备的核心定义在/kernel/include/linux/netdevice.h
........
struct net_device {
char name[IFNAMSIZ];
struct hlist_node name_hlist;
char *ifalias;
........
};
........
int register_netdev(struct net_device *dev); // 注册函数
void unregister_netdev(struct net_device *dev);// 撤销函数
........
5.proc文件系统(ramconsole驱动)
proc文件在/proc目录中,用于查看有关硬件,进程的状态,也可以通过操作proc文件系统进行控制工作
proc文件系统的创建函数定义在/kernel/include/linux/proc_fs.h
struct proc_dir_entry { // 表示一个目录项的入口
unsigned int low_ino;
umode_t mode;
nlink_t nlink;
kuid_t uid;
kgid_t gid;
loff_t size;
const struct inode_operations *proc_iops; // 文件系统操作
const struct file_operations *proc_fops; // 文件系统节点操作
........
};
6.sys文件系统
sys文件系统与proc有些类似,除了查看和设定内核参数功能之外,还有位linux统一设备模型作为管理之用
sys文件系统只有读和写两个接口,不支持参数传递,大规模数据块操作等复杂的操作
sys文件系统在linux文件系统的/sys/目录中,如/sys/bus/为总线目录结构,/sys/class为按功能分类设备模型
通过cat与echo重定向来查看与设置/sys目录
7.无用户空间接口