目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
6.1 I/O体系结构
1. 扩展硬件
总线的作用:
用于连接CPU和外设。
总线分为:
系统总线:
PCI-E,ISA,EISA
内部总线:
SPI,I2C
外部总线:
USB,RS-232,RS-485,SCSI
SCSI:
特点:吞吐非常高。
使用场景:服务器中寻址硬盘。
2. 与外设的交互
1. IO端口:
1. 端口号用于识别设备。
2. 每个端口未使用或分配给一个设备。
3. 多个设备不能共享同一外设。
4. 可将两个连续8位端口合并成一个16位端口。
使用的体系:X86
端口分为:只读端口,只写端口,读写端口。
相应函数:outb,outw,inb
2. IO内存映射:
将IO端口进行内存映射,像操作内存一样读写外设。
使用场景:
显卡,PCI。
相应函数:
ioremap,iounmap。
作用:
将外设的物理地址映射到内核的虚拟地址空间。
轮询polling:
反复向设备查询是否有可用数据。
缺点:浪费CPU。
中断:
多个设备可共享同一中断。
3. 通过总线控制设备
如PCI,USB等
6.2 访问设备
6.2.1 设备文件
设备文件的作用:
建立了与设备驱动程序的连接。
如/dev/ttyS0。
创建设备文件时,创建对应struct inode。
打开设备文件时,创建对应struct file。
6.2.2 字符设备、块设备和其他设备
1. 标识设备文件
#ls -l /dev/ttyS0
crw-rw---- 1 root dialout 4, 64 4月 4 15:18 /dev/ttyS0
#ls -l /dev/sda1
brw-rw---- 1 root disk 8, 1 4月 4 15:18 /dev/sda1
c:表示字符设备。
b:表示块设备。
创建设备文件时需指定主从设备号。
主设备号:用于寻找设备驱动。
从设备号:用于区分该驱动中的不同设备。
2. 动态创建设备文件
设备文件创建方法有:
1. 制作文件系统时生成。(静态)
2. mknod命令创建。
3. udevd守护进程。 (用户层动态创建)
udevd实现原理:
1. 内核通过netlink将设备热插拔消息(包含主从设备号信息)发送到用户空间udevd。
2. udevd监听netlink uevent事件,并创建设备文件。
struct kset { //表示一组具有相似特性kobject集合
struct list_head list;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
static const struct kset_uevent_ops device_uevent_ops = {
.uevent = dev_uevent,
};
6.2.3 ioctl 寻址设备
ioctl:
配置和修改特定设备的特殊属性。
sysfs文件系统:
层次化表示系统设备。
设置设备的参数。
ioctl系统调用:
sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
网卡及其他设备
应用层使用socket与网卡通信。
网络相关函数都使用socketcall系统调用与内核通信,从而访问网卡。
哪些设备没有对应设备文件?
网卡。
USB设备。
6.2.4 主从设备号的表示
数据结构:dev_t
共32位,12位主设备号,20从设备号。
API与宏:
MAJOR,MINOR宏:
从dev_t提取主,从设备号。
MKDEV(major, minor):
根据主从,设备号生成dev_t。
6.2.5 注册
1. 数据结构
字符设备与块设备:都用唯一设备号标识。
struct cdev:
表示一个字符设备。
struct genhd:
表示一个块设备分区。
如何跟踪内核所有cdev和genhd实例?
全局数组:
struct kobj_map *bdev_map;
struct kobj_map *cdev_map;
一个用于管理系统所有设备的散列表。
散列方法:主设备号 % 255
struct kobj_map { //设备数据库
struct probe {
struct probe *next;
dev_t dev; //设备号
unsigned long range; //从设备号范围
struct module *owner;
kobj_probe_t *get; //函数指针,用于返回设备的kobject实例
int (*lock)(dev_t, void *);
void *data;
//字符设备指向struct cdev。
//块设备指向struct genhd。
} *probes[255];
struct mutex *lock;
};
字符设备范围数据库
字符设备专用,用于管理设备号的范围。
同样使用主设备为散列键。
struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
int minorct; //从设备数量
unsigned int baseminor; //包含minorct个从设备号的连续范围中最小值
char name[64];
struct cdev *cdev;
} *chrdevs[255];
2. 注册过程
注册字符设备
步骤1. 分配一个设备号范围。
两种方法:
方法1.
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//指定设备号范围。
使用举例:
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0);
方法2.
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, char *name)
//由内核选择合适的设备号范围。
参数:
baseminor:指定最小从设备号。
count:设备号范围的长度。
步骤2. 将设备添加到字符设备数据库。
1. void cdev_init(struct cdev *cdev, const struct file_operations *fops)
//初始化一个struct cdev。
2. int cdev_add(struct cdev *p, dev_t dev, unsigned count)
//加入到设备数据库(即struct kobj_map *cdev_map)
参数:
count:从设备数量。
cdev_add成功返回后,设备进入活动状态。
注册块设备
void add_disk(struct gendisk *disk);
老版本内核也可用:
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
int register_blkdev(unsigned int major, const char *name);
标签:struct,int,dev,cdev,6.2,设备,驱动程序 From: https://blog.csdn.net/qingwangheni/article/details/139765487