Linux 设备驱动 Edition 3(书摘)

主要是一重要的设备接口与内部API函数,以供查阅.

中断:

/proc/interrupts 的显示展示了有多少中断硬件递交给系统中的每个 CPU

/proc/stat 记录了几个关于系统活动的低级统计量, 包括(但是不限于)自系统启动以来收到的中断数. 

intr 5167833 5154006 2 0 2 4907 0 2 68 4 0 4406 9291 50 0 0 

第一个数是所有中断的总数, 而其他每一个代表一个单个 IRQ 线, 从中断 0 开始. 所有的计数跨系统中所有处理器而汇总的. 如果你在测试的驱动请求并释放中断在每个打开和关闭循环, 你可能发现 /proc/stat 比 /proc/interrupts 更加有用.

 

中断处理应当返回一个值指示是否真正有一个中断要处理. 如果处理者发现它的设备确实需要注意, 它应当返回 IRQ_HANDLED;否则返回值应当是 IRQ_NONE. 你也可产生返回值, 使用这个宏:

IRQ_RETVAL(handled)

这里, handled 是非零, 如果你能够处理中断. 内核用返回值来检测和抑制假中断. 如果你的设备没有给你方法来告知是否它确实中断, 你应当返回 IRQ_HANDLED.

 

void disable_irq(int irq);

void disable_irq_nosync(int irq);

void enable_irq(int irq);

void local_irq_save(unsigned long flags);

void local_irq_disable(void);

void local_irq_restore(unsigned long flags);

void local_irq_enable(void);

 

Linux (许多其他系统一起)解决这个问题通过将中断处理分为 2 半

tasklet 常常是后半部处理的首选机制,它们非常快, 但是所有的 tasklet 代码必须是原子的. tasklet 的可选项是工作队列, 它可能有一个更高的运行周期但是允许睡眠.

DECLARE_TASKLET(name, function, data);

INIT_WORK(&short_wq, (void (*)(void *)) short_do_tasklet, NULL);

 

TTY

struct termios tty_std_termios = {

 .c_iflag = ICRNL |IXON,

 .c_oflag = OPOST |ONLCR,

 .c_cflag = B38400 |CS8 | CREAD | HUPCL,

 .c_lflag = ISIG |ICANON | ECHO | ECHOE | ECHOK |

 ECHOCTL | ECHOKE |IEXTEN,

 .c_cc = INIT_C_CC

};

tty_flip_buffer_push(tty);

 

内存映射和 DMA

User virtualaddresses

这是被用户程序见到的常规地址.

Physicaladdresses

在处理器和系统内存之间使用的地址

Bus addresses

在外设和内存之间使用的地址

Kernel logicaladdresses

这些组成了正常的内核地址空间

Kernel virtualaddresses

内核虚拟地址类似于逻辑地址, 它们都是从内核空间地址到物理地址的映射.

宏 __pa() ( 在<asm/page.h> 中定义)返回它的关联的物理地址.

 物理地址可被映射回逻辑地址使用 __va(), 但是只给低内存页.

有些函数和宏被定义来在 struct page 指针和虚拟地址之间转换:

struct page*virt_to_page(void *kaddr);

这个宏, 定义在<asm/page.h>, 采用一个内核逻辑地址并返回它的被关联的 struct page 指针. 因为它需要一个逻辑地址, 它不使用来自 vmalloc 的内存或者高内存.

struct page*pfn_to_page(int pfn);

为给定的页帧号返回 struct page 指针. 如果需要, 它在传递给 pfn_to_page 之前使用 pfn_valid 来检查一个页帧号的有效性.

void*page_address(struct page *page);

返回这个页的内核虚拟地址, 如果这样一个地址存在. 对于高内存, 那个地址仅当这个页已被映射才存在. 这个函数在<linux/mm.h> 中定义. 大部分情况下, 你想使用 kmap 的一个版本而不是 page_address.

#include<linux/highmem.h>

void*kmap(struct page *page);

void kunmap(structpage *page);

void*kmap_atomic(struct page *page, enum km_type type);

voidkunmap_atomic(void *addr, enum km_type type);

一个机制来转换虚拟地址到它的对应物理地址. 这个机制被称为一个页表

cat /proc/1/maps look at init

7fbfffe000-7fc0000000 rw-p 7fbfffe000 00:00 0 stack

start-end perm offset major:minor inode image

 

vm_area_struct

/proc/iomem

 

建立新页来映射物理地址

intremap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsignedlong pfn, unsigned long size, pgprot_t prot);

intio_remap_page_range(struct vm_area_struct *vma, unsigned long virt_addr,unsigned long phys_addr, unsigned long size, pgprot_t prot);

使用 nopage 映射内存

morgana.root#./mapper /dev/mem 0x10000 0x1000 | od -Ax -t x1

mapped"/dev/mem" from 65536 to 69632

000000 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00

*

001000

 

PAGE_SIZE

PAGE_SHIFT

---进行直接 I/O

DMA

intdma_set_mask(struct device *dev, u64 mask);

对于无法寻址整个 32-位范围的外设, 这个函数通知内核可寻址的地址范围并且如果可进行 DMA 返回非零.

void*dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *bus_addr, intflag);

voiddma_free_coherent(struct device *dev, size_t size, void *cpuaddr, dma_handle_tbus_addr);

分配和释放一致 DMA 映射, 对一个将持续在驱动的生命周期中的缓冲.

#include<linux/dmapool.h>

struct dma_pool*dma_pool_create(const char *name, struct device *dev, size_t size, size_talign, size_t allocation);

voiddma_pool_destroy(struct dma_pool *pool);

void*dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);

voiddma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t handle);

创建, 销毁, 和使用 DMA 池来管理小 DMA 区的函数.

enumdma_data_direction;

DMA_TO_DEVICE

DMA_FROM_DEVICE

DMA_BIDIRECTIONAL

DMA_NONE

符号, 用来告知流映射函数在什么方向数据移入或出缓冲.

dma_addr_tdma_map_single(struct device *dev, void *buffer, size_t size, enumdma_data_direction direction);

voiddma_unmap_single(struct device *dev, dma_addr_t bus_addr, size_t size, enumdma_data_direction direction);

创建和销毁一个单使用, 流 DMA 映射.

voiddma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size,enum dma_data_direction direction);

voiddma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_tsize, enum dma_data_direction direction);

同步一个由一个流映射的缓冲. 必须使用这些函数, 如果处理器必须存取一个缓冲当使用流映射时.(即, 当设备拥有缓冲时).

#include<asm/scatterlist.h>

structscatterlist { /* ... */ };

dma_addr_tsg_dma_address(struct scatterlist *sg);

unsigned int sg_dma_len(structscatterlist *sg);

这个散布表结构描述一个涉及不止一个缓冲的 I/O 操作. 宏 sg_dma_addresshe sg_dma_len 可用来抽取总线地址和缓冲长度来传递给设备, 当实现发散/汇聚操作时.

dma_map_sg(structdevice *dev, struct scatterlist *list, int nents, enum dma_data_directiondirection);

dma_unmap_sg(structdevice *dev, struct scatterlist *list, int nents, enum dma_data_directiondirection);

voiddma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enumdma_data_direction direction);

voiddma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents,enum dma_data_direction direction);

dma_map_sg 映射一个 发散/汇聚 操作, 并且 dma_unmap_sg 恢复这些映射. 如果在这个映射被激活时缓冲必须被存取, dma_sync_sg_*可用来同步.

/proc/dma

包含在 DMA 控制器中的被分配的通道的文本快照的文件. 基于 PCI 的 DMA 不显示, 因为每个板独立工作, 不需要分配一个通道在 DMA 控制器中.

#include<asm/dma.h>

定义或者原型化所有和 DMA 相关的函数和宏定义. 它必须被包含来使用任何下面符号.

int request_dma(unsignedint channel, const char *name);

voidfree_dma(unsigned int channel);

存取 DMA 注册. 注册必须在使用 ISA DMA 通道之前进行.

unsigned longclaim_dma_lock( );

voidrelease_dma_lock(unsigned long flags);

获取和释放 DMA 自旋锁, 它必须被持有, 在调用其他的在这个列表中描述的 ISA DMA 函数之前. 它们在本地处理器上也关闭和重新使能中断

voidset_dma_mode(unsigned int channel, char mode);

voidset_dma_addr(unsigned int channel, unsigned int addr);

voidset_dma_count(unsigned int channel, unsigned int count);

编程 DMA 信息在 DMA 控制器中. addr 是一个总线地址.

voiddisable_dma(unsigned int channel);

voidenable_dma(unsigned int channel);

一个 DMA 通道必须被关闭在配置期间. 这些函数改变 DMA 通道的状态.

intget_dma_residue(unsigned int channel);

如果这驱动需要知道一个 DMA 传送在进行, 它可调用这个函数, 返回尚未完成的数据传输的数目. 在成功的 DMA 完成后, 这个函数返回 0; 值是不可预测的当数据仍然在传送时.

voidclear_dma_ff(unsigned int channel);

DMA flip-flop 被控制器用来传送 16-位值, 通过 2 个 8 位操作. 它必须被清除, 在发送任何数据给处理器之前

 

调试:

Linux 追踪工具Linux TraceToolkit (LTT) http://www.opersys.com/LTT

动态探针Dynamic Probes( DProbes )

kgdb

kdb

gdb

echo 0 >/proc/sys/kernel/sysrq系统挂起

oops 消息

strace ls /dev> /dev/scull0

重定向控制台消息

 

Linux 设备模型

Kobjects, Ksets和 Subsystems / sysfs 表示

voidkobject_init(struct kobject *kobj);

intkobject_set_name(struct kobject *kobj, const char *format, ...);

用作 kobject 初始化的函数

struct kobject*kobject_get(struct kobject *kobj);

voidkobject_put(struct kobject *kobj);

为 kobjects 管理引用计数的函数.

structkobj_type;

structkobj_type *get_ktype(struct kobject *kobj);

表示一个kobjct 被嵌入的结构类型. 使用 get_ktype 来获得关联到一个给定 kobject 的 kobj_type.

intkobject_add(struct kobject *kobj);

extern intkobject_register(struct kobject *kobj);

voidkobject_del(struct kobject *kobj);

voidkobject_unregister(struct kobject *kobj);

kobject_add 添加一个 kobject 到系统, 处理 kset 成员关系, sysfs 表示, 以及热插拔事件产生.kobject_register 是一个方便函数, 它结合 kobject_init 和 kobject_add. 使用 kobject_del 来去除一个 kobject 或者kobject_unregister, 它结合了 kobject_del 和 kobject_put.

voidkset_init(struct kset *kset);

intkset_add(struct kset *kset);

intkset_register(struct kset *kset);

voidkset_unregister(struct kset *kset);

为 ksets 初始化和注册的函数.

decl_subsys(name,type, hotplug_ops);

易于声明子系统的一个宏.

voidsubsystem_init(struct subsystem *subsys);

intsubsystem_register(struct subsystem *subsys);

voidsubsystem_unregister(struct subsystem *subsys);

structsubsystem *subsys_get(struct subsystem *subsys);

voidsubsys_put(struct subsystem *subsys);

对子系统的操作.

14.9.2. sysfs 操作

#include<linux/sysfs.h>

包含 sysfs 声明的包含文件.

intsysfs_create_file(struct kobject *kobj, struct attribute *attr);

intsysfs_remove_file(struct kobject *kobj, struct attribute *attr);

intsysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);

intsysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);

intsysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);

void sysfs_remove_link(structkobject *kobj, char *name);

创建和去除和一个 kobject 关联的属性文件的函数.

14.9.3. 总线, 设备, 和驱动

intbus_register(struct bus_type *bus);

voidbus_unregister(struct bus_type *bus);

在设备模型中进行注册和注销总线的函数.

intbus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int(*fn)(struct device *, void *));

intbus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data,int (*fn)(struct device_driver *, void *));

列举每个设备和驱动的函数, 特别地, 绑定到给定总线的设备.

BUS_ATTR(name,mode, show, store);

intbus_create_file(struct bus_type *bus, struct bus_attribute *attr);

voidbus_remove_file(struct bus_type *bus, struct bus_attribute *attr);

BUS_ATTR 宏可能用来声明一个 bus_attribute 结构, 它可能接着被添加和去除, 使用上面 2 个函数.

intdevice_register(struct device *dev);

voiddevice_unregister(struct device *dev);

处理设备注册的函数.

DEVICE_ATTR(name,mode, show, store);

intdevice_create_file(struct device *device, struct device_attribute *entry);

voiddevice_remove_file(struct device *dev, struct device_attribute *attr);

处理设备属性的宏和函数.

intdriver_register(struct device_driver *drv);

void driver_unregister(structdevice_driver *drv);

注册和注销一个设备驱动的函数.

DRIVER_ATTR(name,mode, show, store);

intdriver_create_file(struct device_driver *drv, struct driver_attribute *attr);

voiddriver_remove_file(struct device_driver *drv, struct driver_attribute *attr);

关联驱动属性的宏和函数.

14.9.4. 类

structclass_simple *class_simple_create(struct module *owner, char *name);

voidclass_simple_destroy(struct class_simple *cs);

structclass_device *class_simple_device_add(struct class_simple *cs, dev_t devnum,struct device *device, const char *fmt, ...);

voidclass_simple_device_remove(dev_t dev);

intclass_simple_set_hotplug(struct class_simple *cs, int (*hotplug)(structclass_device *dev, char **envp, int num_envp, char *buffer, int buffer_size));

实现 class_simple 接口的函数; 它们管理包含一个 dev 属性和很少其他属性的简单的类入口

intclass_register(struct class *cls);

voidclass_unregister(struct class *cls);

注册和注销类.

CLASS_ATTR(name,mode, show, store);

intclass_create_file(struct class *cls, const struct class_attribute *attr);

voidclass_remove_file(struct class *cls, const struct class_attribute *attr);

处理类属性的常用宏和函数.

intclass_device_register(struct class_device *cd);

voidclass_device_unregister(struct class_device *cd);

intclass_device_rename(struct class_device *cd, char *new_name);

CLASS_DEVICE_ATTR(name,mode, show, store);

intclass_device_create_file(struct class_device *cls, const structclass_device_attribute *attr);

属性类设备接口的函数和宏.

intclass_interface_register(struct class_interface *intf);

voidclass_interface_unregister(struct class_interface *intf);

添加一个接口到一个类(或去除它)的函数.

14.9.5. 固件

#include<linux/firmware.h>

intrequest_firmware(const struct firmware **fw, char *name, struct device*device);

intrequest_firmware_nowait(struct module *module, char *name, struct device*device, void *context, void (*cont)(const struct firmware *fw, void*context));

voidrelease_firmware(struct firmware *fw);

属性内核固件加载接口的函数.

 

并发和竞争情况

#include<asm/semaphore.h>

定义旗标和其上操作的包含文件.

DECLARE_MUTEX(name);

DECLARE_MUTEX_LOCKED(name);

2 个宏定义, 用来声明和初始化一个在互斥模式下使用的旗标.

void init_MUTEX(structsemaphore *sem);

voidinit_MUTEX_LOCKED(struct semaphore *sem);

这 2 函数用来在运行时初始化一个旗标.

voiddown(struct semaphore *sem);

intdown_interruptible(struct semaphore *sem);

intdown_trylock(struct semaphore *sem);

void up(structsemaphore *sem);

加锁和解锁旗标. down 使调用进程进入不可打断睡眠, 如果需要;down_interruptible, 相反, 可以被信号打断. down_trylock 不睡眠; 相反, 它立刻返回如果旗标不可用. 加锁旗标的代码必须最终使用 up 解锁它.

structrw_semaphore;

init_rwsem(structrw_semaphore *sem);

旗标的读者/写者版本和初始化它的函数.

voiddown_read(struct rw_semaphore *sem);

int down_read_trylock(structrw_semaphore *sem);

voidup_read(struct rw_semaphore *sem);

获得和释放对读者/写者旗标的读存取的函数.

voiddown_write(struct rw_semaphore *sem);

intdown_write_trylock(struct rw_semaphore *sem);

voidup_write(struct rw_semaphore *sem);

voiddowngrade_write(struct rw_semaphore *sem);

管理对读者/写者旗标写存取的函数.

#include<linux/completion.h>

DECLARE_COMPLETION(name);

init_completion(structcompletion *c);

INIT_COMPLETION(structcompletion c);

描述 Linuxcompletion 机制的包含文件, 已经初始化 completion 的正常方法.INIT_COMPLETION 应当只用来重新初始化一个之前已经使用过的 completion.

voidwait_for_completion(struct completion *c);

等待一个 completion 事件发出.

voidcomplete(struct completion *c);

voidcomplete_all(struct completion *c);

发出一个 completion 事件. completion 唤醒, 最多, 一个等待着的线程, 而 complete_all 唤醒全部等待者.

void complete_and_exit(structcompletion *c, long retval);

通过调用 complete 来发出一个 completion 事件, 并且为当前线程调用 exit.

#include<linux/spinlock.h>

spinlock_t lock= SPIN_LOCK_UNLOCKED;

spin_lock_init(spinlock_t*lock);

定义自旋锁接口的包含文件, 以及初始化锁的 2 个方法.

voidspin_lock(spinlock_t *lock);

voidspin_lock_irqsave(spinlock_t *lock, unsigned long flags);

voidspin_lock_irq(spinlock_t *lock);

voidspin_lock_bh(spinlock_t *lock);

加锁一个自旋锁的各种方法, 并且, 可能地, 禁止中断.

intspin_trylock(spinlock_t *lock);

intspin_trylock_bh(spinlock_t *lock);

上面函数的非自旋版本; 在获取锁失败时返回 0, 否则非零.

voidspin_unlock(spinlock_t *lock);

voidspin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);

voidspin_unlock_irq(spinlock_t *lock);

voidspin_unlock_bh(spinlock_t *lock);

释放一个自旋锁的相应方法.

rwlock_t lock =RW_LOCK_UNLOCKED

rwlock_init(rwlock_t*lock);

初始化读者/写者锁的 2 个方法.

voidread_lock(rwlock_t *lock);

voidread_lock_irqsave(rwlock_t *lock, unsigned long flags);

voidread_lock_irq(rwlock_t *lock);

voidread_lock_bh(rwlock_t *lock);

获得一个读者/写者锁的读存取的函数.

voidread_unlock(rwlock_t *lock);

voidread_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

voidread_unlock_irq(rwlock_t *lock);

voidread_unlock_bh(rwlock_t *lock);

释放一个读者/写者自旋锁的读存取.

voidwrite_lock(rwlock_t *lock);

voidwrite_lock_irqsave(rwlock_t *lock, unsigned long flags);

voidwrite_lock_irq(rwlock_t *lock);

voidwrite_lock_bh(rwlock_t *lock);

获得一个读者/写者锁的写存取的函数.

voidwrite_unlock(rwlock_t *lock);

voidwrite_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

voidwrite_unlock_irq(rwlock_t *lock);

voidwrite_unlock_bh(rwlock_t *lock);

释放一个读者/写者自旋锁的写存取的函数.

#include<asm/atomic.h>

atomic_t v =ATOMIC_INIT(value);

voidatomic_set(atomic_t *v, int i);

intatomic_read(atomic_t *v);

voidatomic_add(int i, atomic_t *v);

voidatomic_sub(int i, atomic_t *v);

voidatomic_inc(atomic_t *v);

voidatomic_dec(atomic_t *v);

intatomic_inc_and_test(atomic_t *v);

intatomic_dec_and_test(atomic_t *v);

intatomic_sub_and_test(int i, atomic_t *v);

intatomic_add_negative(int i, atomic_t *v);

intatomic_add_return(int i, atomic_t *v);

int atomic_sub_return(inti, atomic_t *v);

intatomic_inc_return(atomic_t *v);

intatomic_dec_return(atomic_t *v);

原子地存取整数变量. atomic_t 变量必须只通过这些函数存取.

#include<asm/bitops.h>

voidset_bit(nr, void *addr);

voidclear_bit(nr, void *addr);

voidchange_bit(nr, void *addr);

test_bit(nr,void *addr);

inttest_and_set_bit(nr, void *addr);

inttest_and_clear_bit(nr, void *addr);

inttest_and_change_bit(nr, void *addr);

原子地存取位值; 它们可用做标志或者锁变量. 使用这些函数阻止任何与并发存取这个位相关的竞争情况.

#include<linux/seqlock.h>

seqlock_t lock= SEQLOCK_UNLOCKED;

seqlock_init(seqlock_t*lock);

定义 seqlock 的包含文件, 已经初始化它们的 2 个方法.

unsigned intread_seqbegin(seqlock_t *lock);

unsigned intread_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);

intread_seqretry(seqlock_t *lock, unsigned int seq);

intread_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned longflags);

获得一个 seqlock-保护 的资源的读权限的函数.

void write_seqlock(seqlock_t*lock);

voidwrite_seqlock_irqsave(seqlock_t *lock, unsigned long flags);

voidwrite_seqlock_irq(seqlock_t *lock);

voidwrite_seqlock_bh(seqlock_t *lock);

获取一个 seqlock-保护的资源的写权限的函数.

voidwrite_sequnlock(seqlock_t *lock);

void write_sequnlock_irqrestore(seqlock_t*lock, unsigned long flags);

voidwrite_sequnlock_irq(seqlock_t *lock);

voidwrite_sequnlock_bh(seqlock_t *lock);

释放一个 seqlock-保护的资源的写权限的函数.

#include<linux/rcupdate.h>

需要使用读取-拷贝-更新(RCU)机制的包含文件.

voidrcu_read_lock;

void rcu_read_unlock;

获取对由 RCU 保护的资源的原子读权限的宏定义.

voidcall_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg);

安排一个回调在所有处理器已经被调度以及一个 RCU-保护的资源可用被安全的释放之后运行.

 

高级字符驱动操作

#include<linux/ioctl.h>

声明用来定义 ioctl 命令的宏定义. 当前被<linux/fs.h> 包含.

_IOC_NRBITS

_IOC_TYPEBITS

_IOC_SIZEBITS

_IOC_DIRBITS

ioctl 命令的不同位段所使用的位数. 还有 4 个宏来指定 MASK 和 4 个指定 SHIFT, 但是它们主要是给内部使用. _IOC_SIZEBIT 是一个要检查的重要的值, 因为它跨体系改变.

_IOC_NONE

_IOC_READ

_IOC_WRITE

"方向"位段可能的值."read" 和"write" 是不同的位并且可相或来指定 read/write. 这些值是基于 0 的.

_IOC(dir,type,nr,size)

_IO(type,nr)

_IOR(type,nr,size)

_IOW(type,nr,size)

_IOWR(type,nr,size)

用来创建 ioclt 命令的宏定义.

_IOC_DIR(nr)

_IOC_TYPE(nr)

_IOC_NR(nr)

_IOC_SIZE(nr)

用来解码一个命令的宏定义. 特别地, _IOC_TYPE(nr)是 _IOC_READ 和 _IOC_WRITE 的 OR 结合.

#include<asm/uaccess.h>

intaccess_ok(int type, const void *addr, unsigned long size);

检查一个用户空间的指针是可用的. access_ok 返回一个非零值, 如果应当允许存取.

VERIFY_READ

VERIFY_WRITE

access_ok 中 type 参数的可能取值. VERIFY_WRITE 是 VERIFY_READ 的超集.

#include<asm/uaccess.h>

intput_user(datum,ptr);

intget_user(local,ptr);

int__put_user(datum,ptr);

int__get_user(local,ptr);

用来存储或获取一个数据到或从用户空间的宏. 传送的字节数依赖 sizeof(*ptr). 常规的版本调用 access_ok , 而常规版本( __put_user 和 __get_user ) 假定 access_ok 已经被调用了.

#include<linux/capability.h>

定义各种 CAP_ 符号, 描述一个用户空间进程可有的能力.

int capable(intcapability);

返回非零值如果进程有给定的能力.

#include<linux/wait.h>

typedef struct{ /* ... */ } wait_queue_head_t;

voidinit_waitqueue_head(wait_queue_head_t *queue);

DECLARE_WAIT_QUEUE_HEAD(queue);

Linux 等待队列的定义类型. 一个 wait_queue_head_t必须被明确在运行时使用 init_waitqueue_head 或者编译时使用DEVLARE_WAIT_QUEUE_HEAD 进行初始化.

voidwait_event(wait_queue_head_t q, int condition);

intwait_event_interruptible(wait_queue_head_t q, int condition);

intwait_event_timeout(wait_queue_head_t q, int condition, int time);

intwait_event_interruptible_timeout(wait_queue_head_t q, int condition,int time);

使进程在给定队列上睡眠, 直到给定条件值为真值.

voidwake_up(struct wait_queue **q);

voidwake_up_interruptible(struct wait_queue **q);

voidwake_up_nr(struct wait_queue **q, int nr);

voidwake_up_interruptible_nr(struct wait_queue **q, int nr);

voidwake_up_all(struct wait_queue **q);

voidwake_up_interruptible_all(struct wait_queue **q);

voidwake_up_interruptible_sync(struct wait_queue **q);

唤醒在队列 q 上睡眠的进程._interruptible 的形式只唤醒可中断的进程. 正常地, 只有一个互斥等待者被唤醒, 但是这个行为可被 _nr 或者 _all 形式所改变. _sync 版本在返回之前不重新调度 CPU.

#include<linux/sched.h>

set_current_state(intstate);

设置当前进程的执行状态. TASK_RUNNING 意味着它已经运行, 而睡眠状态是TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE.

voidschedule(void);

选择一个可运行的进程从运行队列中. 被选中的进程可是当前进程或者另外一个.

typedef struct{ /* ... */ } wait_queue_t;

init_waitqueue_entry(wait_queue_t*entry, struct task_struct *task);

wait_queue_t 类型用来放置一个进程到一个等待队列.

voidprepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);

void prepare_to_wait_exclusive(wait_queue_head_t*queue, wait_queue_t *wait, int state);

voidfinish_wait(wait_queue_head_t *queue, wait_queue_t *wait);

帮忙函数, 可用来编码一个手工睡眠.

voidsleep_on(wiat_queue_head_t *queue);

voidinterruptible_sleep_on(wiat_queue_head_t *queue);

老式的不推荐的函数, 它们无条件地使当前进程睡眠.

#include<linux/poll.h>

voidpoll_wait(struct file *filp, wait_queue_head_t *q, poll_table *p);

将当前进程放入一个等待队列, 不立刻调度. 它被设计来被设备驱动的 poll 方法使用.

intfasync_helper(struct inode *inode, struct file *filp, int mode, structfasync_struct **fa);

一个"帮忙者", 来实现 fasync 设备方法. mode 参数是传递给方法的相同的值, 而 fa 指针指向一个设备特定的 fasync_struct*.

voidkill_fasync(struct fasync_struct *fa, int sig, int band);

如果这个驱动支持异步通知, 这个函数可用来发送一个信号到登记在 fa 中的进程.

intnonseekable_open(struct inode *inode, struct file *filp);

loff_tno_llseek(struct file *file, loff_t offset, int whence);

nonseekable_open应当在任何不支持移位的设备的 open 方法中被调用. 这样的设备应当使用 no_llseek 作为它们的 llseek 方法.

 

时间, 延时, 和延后工作

#include<linux/param.h>

HZ

HZ 符号指定了每秒产生的时钟嘀哒的数目.

#include<linux/jiffies.h>

volatileunsigned long jiffies;

u64 jiffies_64;

jiffies_64 变量每个时钟嘀哒时被递增; 因此, 它是每秒递增 HZ 次. 内核代码几乎常常引用 jiffies, 它在 64-位平台和 jiffies_64 相同并且在 32-位平台是它低有效的一半.

inttime_after(unsigned long a, unsigned long b);

inttime_before(unsigned long a, unsigned long b);

inttime_after_eq(unsigned long a, unsigned long b);

inttime_before_eq(unsigned long a, unsigned long b);

这些布尔表达式以一种安全的方式比较 jiffies, 没有万一计数器溢出的问题和不需要存取 jiffies_64.

u64get_jiffies_64(void);

获取 jiffies_64 而没有竞争条件.

#include<linux/time.h>

unsigned longtimespec_to_jiffies(struct timespec *value);

voidjiffies_to_timespec(unsigned long jiffies, struct timespec *value);

unsigned longtimeval_to_jiffies(struct timeval *value);

voidjiffies_to_timeval(unsigned long jiffies, struct timeval *value);

在 jiffies 和其他表示之间转换时间表示.

#include <asm/msr.h>

rdtsc(low32,high32);

rdtscl(low32);

rdtscll(var32);

x86-特定的宏定义来读取时戳计数器. 它们作为 2 半 32-位来读取, 只读低一半, 或者全部读到一个 long long 变量.

#include<linux/timex.h>

cycles_tget_cycles(void);

以平台独立的方式返回时戳计数器. 如果 CPU 没提供时戳特性, 返回 0.

#include<linux/time.h>

unsigned longmktime(year, mon, day, h, m, s);

返回自 Epoch 以来的秒数, 基于 6 个 unsigned int 参数.

voiddo_gettimeofday(struct timeval *tv);

返回当前时间, 作为自 Epoch 以来的秒数和微秒数, 用硬件能提供的最好的精度. 在大部分的平台这个解决方法是一个微秒或者更好, 尽管一些平台只提供 jiffies 精度.

struct timespeccurrent_kernel_time(void);

返回当前时间, 以一个 jiffy 的精度.

7.7.2. 延迟

#include<linux/wait.h>

longwait_event_interruptible_timeout(wait_queue_head_t *q, condition, signed longtimeout);

使当前进程在等待队列进入睡眠, 安装一个以 jiffies 表达的超时值. 使用schedule_timeout( 下面) 给不可中断睡眠.

#include<linux/sched.h>

signed long schedule_timeout(signedlong timeout);

调用调度器, 在确保当前进程在超时到的时候被唤醒后. 调用者首先必须调用set_curret_state 来使自己进入一个可中断的或者不可中断的睡眠状态.

#include<linux/delay.h>

voidndelay(unsigned long nsecs);

voidudelay(unsigned long usecs);

voidmdelay(unsigned long msecs);

引入一个整数纳秒, 微秒和毫秒的延迟. 获得的延迟至少是请求的值, 但是可能更多. 每个函数的参数必须不超过一个平台特定的限制(常常是几千).

voidmsleep(unsigned int millisecs);

unsigned longmsleep_interruptible(unsigned int millisecs);

voidssleep(unsigned int seconds);

使进程进入睡眠给定的毫秒数(或者秒, 如果使 ssleep).

7.7.3. 内核定时器

#include<asm/hardirq.h>

intin_interrupt(void);

intin_atomic(void);

返回一个布尔值告知是否调用代码在中断上下文或者原子上下文执行. 中断上下文是在一个进程上下文之外, 或者在硬件或者软件中断处理中. 原子上下文是当你不能调度一个中断上下文或者一个持有一个自旋锁的进程的上下文.

#include<linux/timer.h>

voidinit_timer(struct timer_list * timer);

structtimer_list TIMER_INITIALIZER(_function, _expires, _data);

这个函数和静态的定时器结构的声明是初始化一个 timer_list 数据结构的 2 个方法.

voidadd_timer(struct timer_list * timer);

注册定时器结构来在当前 CPU 上运行.

intmod_timer(struct timer_list *timer, unsigned long expires);

改变一个已经被调度的定时器结构的超时时间. 它也能作为一个 add_timer 的替代.

inttimer_pending(struct timer_list * timer);

宏定义, 返回一个布尔值说明是否这个定时器结构已经被注册运行.

voiddel_timer(struct timer_list * timer);

voiddel_timer_sync(struct timer_list * timer);

从激活的定时器链表中去除一个定时器. 后者保证这定时器当前没有在另一个 CPU 上运行.

7.7.4. Tasklets机制

#include<linux/interrupt.h>

DECLARE_TASKLET(name,func, data);

DECLARE_TASKLET_DISABLED(name,func, data);

voidtasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsignedlong data);

前 2 个宏定义声明一个 tasklet 结构, 而 tasklet_init 函数初始化一个已经通过分配或其他方式获得的 tasklet 结构. 第 2 个 DECLARE 宏标识这个 tasklet 为禁止的.

voidtasklet_disable(struct tasklet_struct *t);

voidtasklet_disable_nosync(struct tasklet_struct *t);

voidtasklet_enable(struct tasklet_struct *t);

禁止和使能一个 tasklet. 每个禁止必须配对一个使能( 你可以禁止这个 tasklet 即便它已经被禁止). 函数tasklet_disable 等待 tasklet 终止如果它在另一个 CPU 上运行. 这个非同步版本不采用这个额外的步骤.

voidtasklet_schedule(struct tasklet_struct *t);

voidtasklet_hi_schedule(struct tasklet_struct *t);

调度一个 tasklet 运行, 或者作为一个"正常" tasklet 或者一个高优先级的. 当软中断被执行, 高优先级 tasklets 被首先处理, 而正常 tasklet 最后执行.

void tasklet_kill(structtasklet_struct *t);

从激活的链表中去掉 tasklet, 如果它被调度执行. 如同tasklet_disable, 这个函数可能在 SMP 系统中阻塞等待 tasklet 终止, 如果它当前在另一个 CPU 上运行.

7.7.5. 工作队列

#include<linux/workqueue.h>

structworkqueue_struct;

structwork_struct;

这些结构分别表示一个工作队列和一个工作入口.

struct workqueue_struct*create_workqueue(const char *name);

structworkqueue_struct *create_singlethread_workqueue(const char *name);

voiddestroy_workqueue(struct workqueue_struct *queue);

创建和销毁工作队列的函数. 一个对create_workqueue 的调用创建一个有一个工作者线程在系统中每个处理器上的队列; 相反, create_singlethread_workqueue创建一个有一个单个工作者进程的工作队列.

DECLARE_WORK(name,void (*function)(void *), void *data);

INIT_WORK(structwork_struct *work, void (*function)(void *), void *data);

PREPARE_WORK(structwork_struct *work, void (*function)(void *), void *data);

声明和初始化工作队列入口的宏.

intqueue_work(struct workqueue_struct *queue, struct work_struct *work);

intqueue_delayed_work(struct workqueue_struct *queue, struct work_struct *work,unsigned long delay);

从一个工作队列对工作进行排队执行的函数.

intcancel_delayed_work(struct work_struct *work);

voidflush_workqueue(struct workqueue_struct *queue);

使用cancel_delayed_work 来从一个工作队列中去除入口;flush_workqueue 确保没有工作队列入口在系统中任何地方运行.

intschedule_work(struct work_struct *work);

intschedule_delayed_work(struct work_struct *work, unsigned long delay);

voidflush_scheduled_work(void);

使用共享队列的函数.

 

分配内存

#include<linux/slab.h>

void*kmalloc(size_t size, int flags);

void kfree(void*obj);

内存分配的最常用接口.

#include<linux/mm.h>

GFP_USER

GFP_KERNEL

GFP_NOFS

GFP_NOIO

GFP_ATOMIC

控制内存分配如何进行的标志, 从最少限制的到最多的. GFP_USER 和 GFP_KERNEL 优先级允许当前进程被置为睡眠来满足请求. GFP_NOFS 和 GFP_NOIO 禁止文件系统操作和所有的 I/O 操作, 分别地, 而 GFP_ATOMIC 分配根本不能睡眠.

__GFP_DMA

__GFP_HIGHMEM

__GFP_COLD

__GFP_NOWARN

__GFP_HIGH

__GFP_REPEAT

__GFP_NOFAIL

__GFP_NORETRY

这些标志修改内核的行为, 当分配内存时.

#include<linux/malloc.h>

kmem_cache_t*kmem_cache_create(char *name, size_t size, size_t offset, unsigned long flags,constructor(), destructor( ));

intkmem_cache_destroy(kmem_cache_t *cache);

创建和销毁一个 slab 缓存. 这个缓存可被用来分配几个相同大小的对象.

SLAB_NO_REAP

SLAB_HWCACHE_ALIGN

SLAB_CACHE_DMA

在创建一个缓存时可指定的标志.

SLAB_CTOR_ATOMIC

SLAB_CTOR_CONSTRUCTOR

分配器可用传递给构造函数和析构函数的标志.

void*kmem_cache_alloc(kmem_cache_t *cache, int flags);

voidkmem_cache_free(kmem_cache_t *cache, const void *obj);

从缓存中分配和释放一个单个对象./proc/slabinfo 一个包含对 slab 缓存使用情况统计的虚拟文件.

#include<linux/mempool.h>

mempool_t*mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn,void *data);

voidmempool_destroy(mempool_t *pool);

创建内存池的函数, 它试图避免内存分配设备, 通过保持一个已分配项的"紧急列表".

void*mempool_alloc(mempool_t *pool, int gfp_mask);

void mempool_free(void*element, mempool_t *pool);

从(并且返回它们给)内存池分配项的函数.

unsigned longget_zeroed_page(int flags);

unsigned long__get_free_page(int flags);

unsigned long__get_free_pages(int flags, unsigned long order);

面向页的分配函数.get_zeroed_page 返回一个单个的, 零填充的页. 这个调用的所有的其他版本不初始化返回页的内容.

intget_order(unsigned long size);

返回关联在当前平台的大小的分配级别, 根据 PAGE_SIZE. 这个参数必须是 2 的幂, 并且返回值至少是 0.

voidfree_page(unsigned long addr);

voidfree_pages(unsigned long addr, unsigned long order);

释放面向页分配的函数.

struct page*alloc_pages_node(int nid, unsigned int flags, unsigned int order);

struct page*alloc_pages(unsigned int flags, unsigned int order);

struct page*alloc_page(unsigned int flags);

Linux 内核中最底层页分配器的所有变体.

void__free_page(struct page *page);

void__free_pages(struct page *page, unsigned int order);

voidfree_hot_page(struct page *page);

使用一个 alloc_page 形式分配的页的各种释放方法.

#include<linux/vmalloc.h>

void *vmalloc(unsigned long size);

void vfree(void* addr);

#include<asm/io.h>

void *ioremap(unsigned long offset, unsigned long size);

voidiounmap(void *addr);

分配或释放一个连续虚拟地址空间的函数. iormap 存取物理内存通过虚拟地址, 而 vmalloc 分配空闲页. 使用 ioreamp 映射的区是 iounmap 释放, 而从 vmalloc 获得的页使用 vfree 来释放.

#include<linux/percpu.h>

DEFINE_PER_CPU(type,name);

DECLARE_PER_CPU(type,name);

定义和声明每-CPU变量的宏.

per_cpu(variable,int cpu_id)

get_cpu_var(variable)

put_cpu_var(variable)

提供对静态声明的每-CPU变量存取的宏.

void*alloc_percpu(type);

void*__alloc_percpu(size_t size, size_t align);

voidfree_percpu(void *variable);

进行运行时分配和释放每-CPU变量的函数.

int get_cpu( );

void put_cpu();

per_cpu_ptr(void*variable, int cpu_id)

get_cpu 获得对当前处理器的引用(因此, 阻止抢占和移动到另一个处理器)并且返回处理器的ID; put_cpu 返回这个引用. 为存取一个动态分配的每-CPU变量, 用应当被存取版本所在的 CPU 的 ID 来使用 per_cpu_ptr. 对一个动态的每-CPU 变量当前 CPU 版本的操作, 应当用对 get_cpu 和 put_cpu 的调用来包围.

#include<linux/bootmem.h>

void*alloc_bootmem(unsigned long size);

void*alloc_bootmem_low(unsigned long size);

void*alloc_bootmem_pages(unsigned long size);

void*alloc_bootmem_low_pages(unsigned long size);

voidfree_bootmem(unsigned long addr, unsigned long size);

在系统启动时进行分配和释放内存的函数(只能被直接连接到内核中去的驱动使用)

 

链表

structlist_head { struct list_head *next, *prev; };

#include<linux/list.h>

list_add(structlist_head *new, struct list_head *head);

list_add_tail(structlist_head *new, struct list_head *head);

list_del(structlist_head *entry);

list_del_init(structlist_head *entry);

list_empty(structlist_head *head);

list_entry(entry,type, member);

list_move(structlist_head *entry, struct list_head *head);

list_move_tail(structlist_head *entry, struct list_head *head);

list_splice(structlist_head *list, struct list_head *head);

操作环形, 双向链表的函数.

list_for_each(structlist_head *cursor, struct list_head *list)

list_for_each_prev(structlist_head *cursor, struct list_head *list)

list_for_each_safe(structlist_head *cursor, struct list_head *next, struct list_head *list)

list_for_each_entry(type*cursor, struct list_head *list, member)

list_for_each_entry_safe(type*cursor, type *next struct list_head *list, member)

 

USB 驱动

/sys/devices/pci0000:00/0000:00:09.0/usb2/2-1

|-- 2-1:1.0

|  |-- bAlternateSetting 

|  |-- bInterfaceClass 

|  |-- bInterfaceNumber 

|  |-- bInterfaceProtocol 

|  |-- bInterfaceSubClass 

|  |-- bNumEndpoints 

|  |-- detach_state 

|  |-- iInterface 

|  `-- power 

|  `-- state 

|--bConfigurationValue

|--bDeviceClass

|--bDeviceProtocol

|--bDeviceSubClass

|-- bMaxPower

|--bNumConfigurations

|--bNumInterfaces

|-- bcdDevice

|--bmAttributes

|--detach_state

|-- devnum

|-- idProduct

|-- idVendor

|-- maxchild

|-- power

| `-- state

|-- speed

`-- version

sysfs 设备命名方法是root_hub-hub_port:config.interface

root_hub-hub_port-hub_port:config.interface

/proc/bus/usb/devices

urb ( USBrequest block)

#include<linux/usb.h>

所有和 USB 相关的头文件. 它必须被所有的 USB 设备驱动包含.

structusb_driver;

描述 USB 驱动的结构.

structusb_device_id;

描述这个驱动支持的 USB 设备的结构.

intusb_register(struct usb_driver *d);

用来从USB核心注册和注销一个 USB 驱动的函数.

structusb_device *interface_to_usbdev(struct usb_interface *intf);

从 structusb_interface 获取控制 structusb_device *.

structusb_device;

控制完整 USB 设备的结构.

structusb_interface;

主 USB 设备结构, 所有的 USB 驱动用来和 USB 核心通讯的.

voidusb_set_intfdata(struct usb_interface *intf, void *data);

void*usb_get_intfdata(struct usb_interface *intf);

设置和获取在 structusb_interface 中的私有数据指针部分的函数.

structusb_class_driver;

描述 USB 驱动的一个结构, 这个驱动要使用 USB 主编号来和用户空间程序通讯.

intusb_register_dev(struct usb_interface *intf, struct usb_class_driver*class_driver);

voidusb_deregister_dev(struct usb_interface *intf, struct usb_class_driver*class_driver);

用来注册和注销一个特定 structusb_interface * 结构到 structusb_class_driver 结构的函数.

struct urb;

描述一个 USB 数据传输的结构.

struct urb*usb_alloc_urb(int iso_packets, int mem_flags);

voidusb_free_urb(struct urb *urb);

用来创建和销毁一个 struct usburb*的函数.

intusb_submit_urb(struct urb *urb, int mem_flags);

int usb_kill_urb(structurb *urb);

intusb_unlink_urb(struct urb *urb);

用来启动和停止一个 USB 数据传输的函数.

voidusb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,void *transfer_buffer, int buffer_length, usb_complete_t complete, void*context, int interval);

voidusb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,void *transfer_buffer, int buffer_length, usb_complete_t complete, void*context);

voidusb_fill_control_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,unsigned char *setup_packet, void *transfer_buffer, int buffer_ length,usb_complete_t complete, void *context);

用来在被提交给 USB 核心之前初始化一个 struct urb 的函数.

intusb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, intlen, int *actual_length, int timeout);

intusb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);

用来发送和接受 USB 数据的函数, 不必使用一个 struct urb.

 

#include<linux/fs.h>

intregister_blkdev(unsigned int major, const char *name);

intunregister_blkdev(unsigned int major, const char *name);

register_blkdev 注册一个块驱动到内核, 并且, 可选地, 获得一个主编号. 一个驱动可被注销, 使用 unregister_blkdev.

structblock_device_operations

持有大部分块驱动的方法的结构.

#include<linux/genhd.h>

structgendisk;

描述内核中单个块设备的结构.

structgendisk *alloc_disk(int minors);

voidadd_disk(struct gendisk *gd);

分配 gendisk 结构的函数, 并且返回它们到系统.

voidset_capacity(struct gendisk *gd, sector_t sectors);

存储设备能力(以 512-字节)在 gendisk 结构中.

voidadd_disk(struct gendisk *gd);

添加一个磁盘到内核. 一旦调用这个函数, 你的磁盘的方法可被内核调用.

intcheck_disk_change(struct block_device *bdev);

一个内核函数, 检查在给定磁盘驱动器中的介质改变, 并且采取要求的清理动作当检测到这样一个改变.

#include<linux/blkdev.h>

request_queue_tblk_init_queue(request_fn_proc *request, spinlock_t *lock);

voidblk_cleanup_queue(request_queue_t *);

处理块请求队列的创建和删除的函数.

structrequest *elv_next_request(request_queue_t *queue);

voidend_request(struct request *req, int success);

elv_next_request 从一个请求队列中获得下一个请求; end_request 可用在每个简单驱动器中来标识一个(或部分)请求完成.

voidblkdev_dequeue_request(struct request *req);

voidelv_requeue_request(request_queue_t *queue, struct request *req);

从队列中除去一个请求, 并且放回它的函数如果需要.

voidblk_stop_queue(request_queue_t *queue);

voidblk_start_queue(request_queue_t *queue);

如果你需要阻止对你的请求函数的进一步调用, 调用 blk_stop_queue 来完成. 调用 blk_start_queue 来使你的请求方法被再次调用.

voidblk_queue_bounce_limit(request_queue_t *queue, u64 dma_addr);

voidblk_queue_max_sectors(request_queue_t *queue, unsigned short max);

voidblk_queue_max_phys_segments(request_queue_t *queue, unsigned short max);

voidblk_queue_max_hw_segments(request_queue_t *queue, unsigned short max);

voidblk_queue_max_segment_size(request_queue_t *queue, unsigned int max);

blk_queue_segment_boundary(request_queue_t*queue, unsigned long mask);

voidblk_queue_dma_alignment(request_queue_t *queue, int mask);

voidblk_queue_hardsect_size(request_queue_t *queue, unsigned short max);

设置各种队列参数的函数, 来控制请求如何被创建给一个特殊设备; 这些参数在"队列控制函数"一节中描述.

#include<linux/bio.h>

structbio;

低级函数, 表示一个块 I/O 请求的一部分.

bio_sectors(structbio *bio);

bio_data_dir(structbio *bio);

2 个宏定义, 表示一个由 bio 结构描述的传送的大小和方向.

bio_for_each_segment(bvec,bio, segno);

一个伪控制结构, 用来循环组成一个 bio 结构的各个段.

char*__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);

void__bio_kunmap_atomic(char *buffer, enum km_type type);

__bio_kmap_atomic 可用来创建一个内核虚拟地址给一个在 bio 结构中的给定的段. 映射必须使用 __bio_kunmap_atomic 来恢复.

structpage *bio_page(struct bio *bio);

intbio_offset(struct bio *bio);

intbio_cur_sectors(struct bio *bio);

char*bio_data(struct bio *bio);

char*bio_kmap_irq(struct bio *bio, unsigned long *flags);

voidbio_kunmap_irq(char *buffer, unsigned long *flags);

一组存取者宏定义, 提供对一个 bio 结构中的"当前"段的存取.

voidblk_queue_ordered(request_queue_t *queue, int flag);

intblk_barrier_rq(struct request *req);

如果你的驱动实现屏障请求, 调用 blk_queue_ordered -- 如同它应当做的. 宏 blk_barrier_rq 返回一个非零值如果当前请求是一个屏障请求.

intblk_noretry_request(struct request *req);

这个宏返回一个非零值, 如果给定的请求不应当在出错时重新尝试.

intend_that_request_first(struct request *req, int success, int count);

voidend_that_request_last(struct request *req);

使用 end_that_request_firest 来指示一个块 I/O 请求的一部分完成. 当那个函数返回 0, 请求完成并且应当被传递给 end_that_request_last.

rq_for_each_bio(bio,request)

另一个用宏定义来实现的控制结构; 它步入构成一个请求的每个 bio.

intblk_rq_map_sg(request_queue_t *queue, struct request *req, struct scatterlist*list);

为一次 DMA 传送填充给定的散布表, 用需要来映射给定请求中的缓冲的信息

typedefint (make_request_fn) (request_queue_t *q, struct bio *bio);

make_request 函数的原型.

voidbio_endio(struct bio *bio, unsigned int bytes, int error);

指示一个给定 bio 的完成. 这个函数应当只用在你的驱动直接获取 bio , 通过 make_request 函数从块层.

request_queue_t*blk_alloc_queue(int flags);

voidblk_queue_make_request(request_queue_t *queue, make_request_fn *func);

使用 blk_alloc_queue 来分配由定制的 make_request 函数使用的请求队列, . 那个函数应当使用 blk_queue_make_request 来设置.

typedefint (prep_rq_fn) (request_queue_t *queue, struct request *req);

voidblk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);

一个命令准备函数的原型和设置函数, 它可用来准备必要的硬件命令, 在请求被传递给你的请求函数之前.

intblk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag*tags);

intblk_queue_resize_tags(request_queue_t *queue, int new_depth);

intblk_queue_start_tag(request_queue_t *queue, struct request *req);

voidblk_queue_end_tag(request_queue_t *queue, struct request *req);

structrequest *blk_queue_find_tag(request_queue_t *qeue, int tag);

voidblk_queue_invalidate_tags(request_queue_t *queue);

驱动使用被标记的命令队列的支持函数.

 

网络驱动

本节提供了本章中介绍的概念的参考. 也解释了每个驱动需要包含的头文件的角色. 在 net_device 和 sk_buff 结构中成员的列表, 但是, 这里没有重复.

#include<linux/netdevice.h>

定义 struct net_device 和 struct net_device_stats 的头文件, 包含了几个其他网络驱动需要的头文件.

structnet_device *alloc_netdev(int sizeof_priv, char *name, void (*setup)(structnet_device *);

structnet_device *alloc_etherdev(int sizeof_priv);

voidfree_netdev(struct net_device *dev);

分配和释放 net_device 结构的函数

intregister_netdev(struct net_device *dev);

voidunregister_netdev(struct net_device *dev);

注册和注销一个网络设备.

void*netdev_priv(struct net_device *dev);

获取网络设备结构的驱动私有区域的指针的函数.

structnet_device_stats;

持有设备统计的结构.

netif_start_queue(structnet_device *dev);

netif_stop_queue(structnet_device *dev);

netif_wake_queue(structnet_device *dev);

控制传送给驱动来发送的报文的函数. 没有报文被传送, 直到 netif_start_queue 被调用. netif_stop_queue 挂起发送, netif_wake_queue 重启队列并刺探网络层重启发送报文.

skb_shinfo(structsk_buff *skb);

宏定义, 提供对报文缓存的"shared info"部分的存取.

voidnetif_rx(struct sk_buff *skb);

调用来通知内核一个报文已经收到并且封装到一个 socket 缓存中的函数.

voidnetif_rx_schedule(dev);

来告诉内核报文可用并且应当启动查询接口; 它只是被 NAPI 兼容的驱动使用.

intnetif_receive_skb(struct sk_buff *skb);

voidnetif_rx_complete(struct net_device *dev);

应当只被 NAPI 兼容的驱动使用. netif_receive_skb 是对于 netif_rx 的 NAPI 对等函数; 它递交一个报文给内核. 当一个 NAPI 兼容的驱动已耗尽接收报文的供应, 它应当重开中断, 并且调用 netif_rx_complete 来停止查询.

#include<linux/if.h>

由 netdevice.h 包含, 这个文件声明接口标志( IFF_ 宏定义 )和 struct ifmap, 它在网络驱动的 ioctl 实现中有重要地位.

voidnetif_carrier_off(struct net_device *dev);

voidnetif_carrier_on(struct net_device *dev);

intnetif_carrier_ok(struct net_device *dev);

前 2 个函数可用来告知内核是否接口上有载波信号. netif_carrier_ok 测试载波状态, 如同在设备结构中反映的.

#include<linux/if_ether.h>

ETH_ALEN

ETH_P_IP

structethhdr;

由 netdevice.h 包含, if_ether.h 定义所有的 ETH_ 宏定义, 用来代表字节长度( 例如地址长度 )以及网络协议(例如 IP). 它也定义 ethhdr 结构.

#include<linux/skbuff.h>

struct sk_buff 和相关结构的定义, 以及几个操作缓存的内联函数. 这个头文件由 netdevice.h 包含.

structsk_buff *alloc_skb(unsigned int len, int priority);

structsk_buff *dev_alloc_skb(unsigned int len);

voidkfree_skb(struct sk_buff *skb);

voiddev_kfree_skb(struct sk_buff *skb);

voiddev_kfree_skb_irq(struct sk_buff *skb);

voiddev_kfree_skb_any(struct sk_buff *skb);

处理 socket 缓存的分配和释放的函数. 通常驱动应当使用 dev_ 变体, 其意图就是此目的.

unsignedchar *skb_put(struct sk_buff *skb, int len);

unsignedchar *__skb_put(struct sk_buff *skb, int len);

unsignedchar *skb_push(struct sk_buff *skb, int len);

unsignedchar *__skb_push(struct sk_buff *skb, int len);

添加数据到一个 skb 的函数; skb_put 在 skb 的尾部放置数据, 而 skb_push 放在开始. 正常版本进行检查以确保有足够的空间; 双下划线版本不进行检查.

intskb_headroom(struct sk_buff *skb);

intskb_tailroom(struct sk_buff *skb);

voidskb_reserve(struct sk_buff *skb, int len);

进行 skb 中的空间管理的函数. skb_headroom 和 skb_tailroom 说明在开始和结尾分别有多少空间可用. skb_reserve 可用来保留空间, 在一个必须为空的 skb 开始.

unsignedchar *skb_pull(struct sk_buff *skb, int len);

skb_pull "去除" 数据从一个 skb, 通过调整内部指针.

intskb_is_nonlinear(struct sk_buff *skb);

如果这个 skb 是为发散/汇聚 I/O 分隔为几个片, 函数返回一个真值.

intskb_headlen(struct sk_buff *skb);

返回 skb 的第一个片的长度, 由 skb->data 指向.

void*kmap_skb_frag(skb_frag_t *frag);

voidkunmap_skb_frag(void *vaddr);

提供对非线性 skb 中的片直接存取的函数.

#include<linux/etherdevice.h>

voidether_setup(struct net_device *dev);

为以太网驱动设置大部分方法为通用实现的函数. 它还设置 dev->flags 和安排下一个可用的 ethx 给 dev->name, 如果名子的第一个字符是一个空格或者 NULL 字符.

unsignedshort eth_type_trans(struct sk_buff *skb, struct net_device *dev);

当一个以太网接口收到一个报文, 这个函数被调用来设置 skb->pkt_type. 返回值是一个协议号, 通常存储于 skb->protocol.

#include<linux/sockios.h>

SIOCDEVPRIVATE

前 16 个 ioctl 命令, 每个驱动可为它们自己的私有用途而实现. 所有的网络 ioctl 命令都在 sockios.h 中定义.

#include<linux/mii.h>

structmii_if_info;

声明和一个结构, 支持实现 MII 标准的设备的驱动.

#include<linux/ethtool.h>

structethtool_ops;

声明和结构, 使得设备与 ethtool 工具一起工作.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值