目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
6.6 资源分配
6.6.1 资源管理
1. 树数据结构
内核所有资源以树形式组织。
树根节点表示整个系统资源。
每个设备有自己的资源子树,表示该设备资源和子设备的资源。
资源类型有:
IORESOURCE_IO
IORESOURCE_MEM
IORESOURCE_REG
IORESOURCE_IRQ
IORESOURCE_DMA
IORESOURCE_BUS
资源的结构体:
struct resource {
resource_size_t start;
resource_size_t end;
char *name;
unsigned long flags;
struct resource *parent, *sibling, *child; // 用于组织树。
};
2. 请求和释放资源
请求、释放资源:
即向资源树中添加、删除项。
请求资源
struct resource * __request_resource(struct resource *root, struct resource *new)
扫描资源树,将资源添加到以root为根节点的树中合适位置。
如请求一段内存、一个中断、一个 I/O 端口等资源。
释放资源
int release_resource(struct resource *old)
6.6.2 I/O 内存
物理地址的大部分给系统内存,其余部分给其他设备,如显存。
IO端口:用于X86。
IO内存:用于非X86。
查看系统IO内存信息:
cat /proc/iomem
通过resource的parent sibling child成员,形成资源树。
全局变量iomem_resource:
系统所有IO内存的资源树。
struct resource iomem_resource = {
.name = "PCI mem",
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
void __iomem *ioremap(phys_addr_t offset, size_t size)
将外设的物理地址映射为内核的虚拟地址。
实现方法:修改页表。
访问IO内存区域的函数:
readb(addr)
readw(addr)
readl(addr)
writeb(addr)
writew(addr)
writel(addr)
memcpy_fromio(dest, src, sum)
memcpy_toio(dest, src, num);
6.6.3 I/O端口
I/O端口:适用于x86。
访问区域之前先注册。
ioport_resource:系统IO端口资源树的根结点。
struct resource ioport_resource = {
.name = "PCI IO",
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
查看系统IO端口:
cat /proc/ioports
一个设备可同时映射到IO端口,IO内存。
访问IO端口函数
insb(port, addr, num)
insl(port, addr, num)
insw(port, addr, num)
从端口port读(字节、字、长整型)数据到addr。
outsb(port, addr, num)
outsl(port, addr, num)
outsw(port, addr, num)
从addr向端口port写数据(字节、字、长整型)。
6.7 总线系统
6.7.1 通用驱动程序模型
- 设备的表示
struct device {
struct kobject kobj;
struct device *parent; //父设备
struct device_private *p;
struct device_type *type;
struct klist_node knode_class;
struct bus_type *bus; //所在总线
struct device_driver *driver;
struct class *class;
void *platform_data; //通常为BSP信息
void *driver_data; //驱动程序的私有数据。
dev_t devt;
void (*release)(struct device *dev);
};
注册设备:
int device_register(struct device *dev)
通用的驱动程序模型:
struct device_driver {
const char *name; // 驱动程序名称
struct bus_type *bus;
struct module *owner;
const struct of_device_id *of_match_table;
int (*probe) (struct device *dev); // 检测是否存在能用该驱动处理的设备
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
// 这三个函数用于电源管理。
struct driver_private *p;
};
驱动程序注册:
int driver_register(struct device_driver *drv)
2. 总线的表示
总线作用:
将device和device_driver两者关联起来。
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
int (*match)(struct device *dev, struct device_driver *drv);
// 用于寻找设备与设备驱动,找到后调用device_driver的probe
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
//热插拔 或 删除device 和driver联系
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
//电源管理
};
3. 注册过程
下面讲总线,设备,驱动三者注册过程。
注册总线
以PCI为例
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
.shutdown = pci_device_shutdown,
};
bus_register(&pci_bus_type);
int bus_register(struct bus_type *bus)
{
kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
kset_register(&bus->devices);
kset_register(&bus->drivers);
//注册device的kset和drivers的kset。
}
注册设备
int device_register(struct device *dev)
{
device_initialize(dev);
device_add(dev);
}
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
}
int device_add(struct device *dev)
{
parent = get_device(dev->parent);
kobj_parent = get_device_parent(dev, parent);
dev->kobj.parent = kobj_parent;
kobject_add(&dev->kobj, dev->kobj.parent, NULL);
bus_add_device(dev);
//在sysyfs总线目录下添加设备
//在sysfs设备中添加链接指向总线。
}
注册设备驱动程序
int driver_register(struct device_driver *drv)
{
struct device_driver *other;
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, aborting...\n", drv->name);
return -EBUSY;
}
bus_add_driver(drv);
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
//触发uevent事件,用于热插拔
}