Linux驱动程序开发快速参考

Linux设备驱动程序开发快速参
第1 章设备驱动简介
从这里开始, 我们进入内核编程的世界. 第 2 章介绍了模块化, 解释了内部的秘密以及展示了运行模块的代码. 第 2 章谈论字符驱动以及展示一个基于内存的设备驱动的代码, 出于乐趣对它读写. 使用内存作为设备的硬件基础使得任何人可以不用要求特殊的硬件来运行代码.

调试技术对程序员是必备的工具, 第 4 章介绍它. 对那些想分析当前内核的人同样重要的是并发的管理和竞争情况. 第 5 章关注的是由于并发存取资源而导致的问题, 并且介绍控制并发的 Linux 机制.

在具备了调试和并发管理的能力下, 我们转向字符驱动的高级特性, 例如阻塞操作, selet 的使用, 以及重要的 ioctl 调用; 这是第 6 章的主题.

在处理硬件管理之前, 我们研究多一点内核软件接口: 第 7 章展示了内核中是如何管理时间的, 第 8 章讲解了内存分配.

接下来我们集中到硬件. 第 9 章描述了 I/O 口的管理和设备上的内存缓存; 随后是中断处理, 在 第 10 章. 不幸的是, 不是每个人都能运行这些章节中的例子代码, 因为确实需要某些硬件来测试软件接口中断. 我们尽力保持需要的硬件支持到最小程度, 但是你仍然需要某些硬件, 例如标准并口, 来使用这些章节的例子代码.

第 11 章涉及内核数据类型的使用, 以及编写可移植代码.

本书的第 2 半专注于更高级的主题. 我们从深入硬件内部开始, 特别的, 是特殊外设总线功能. 第 12 章涉及编写 PCI 设备驱动, 第 13 章检验使用 USB 设备的 API.

具有了对外设总线的理解, 我们详细看一下 Linux 设备模型, 这是内核使用的抽象层来描述它管理的硬件和软件资源. 第 14 章是一个自底向上的设备模型框架的考察, 从 kobject 类型开始以及从那里进一步进行. 它涉及设备模型与真实设备的集成; 接下来是利用这些知识来接触如热插拔设备和电源管理等主题.

在第 15 章, 我们转移到 Linux 的内存管理. 这一章显示如何映射系统内存到用户空间( mmap 系统调用 ), 映射用户内存到内核空间( 使用 get_user_pages ), 以及如何映射任何一种内存到设备空间( 进行 直接内存存取 [DMA] 操作 ).

我们对内存的理解将对下面两章是有用的, 它们涉及到其他主要的驱动类型. 第 16 章介绍了块驱动, 并展示了与我们到现在为止已遇到过的字符驱动的区别. 第 17 章进入网络驱动的编写. 我们最后是讨论串行驱动(第 18 章)和一个参考书目.

第2 章建立和运行模块
本节总结了我们在本章接触到的内核函数, 变量, 宏定义, 和/proc 文件. 它的用意是作为一个参考. 每一项列都在相关头文件的后面, 如果有. 从这里开始, 在几乎每章的结尾会有类似一节, 总结一章中介绍的新符号. 本节中的项通常以在本章中出现的顺序排列:

insmod
modprobe
rmmod
用户空间工具, 加载模块到运行中的内核以及去除它们.

#include <linux/init.h>
module_init(init_function);
module_exit(cleanup_function);
指定模块的初始化和清理函数的宏定义.

__init
__initdata
__exit
__exitdata
函数( __init 和__exit )和数据(__initdata 和__exitdata)的标记, 只用在模块初始化或者清理时间. 为初始化所标识的项可能会在初始化完成后丢弃; 退出的项可能被丢弃如果内核没有配置模块卸载. 这些标记通过使相关的目标在可执行文件的特定的ELF 节里被替换来工作.

#include <linux/sched.h>
最重要的头文件中的一个. 这个文件包含很多驱动使用的内核API 的定义, 包括睡眠函数和许多变量声明.

struct task_struct *current;
当前进程.

current->pid
current->comm
进程ID 和当前进程的命令名.

obj-m
一个makefile 符号, 内核建立系统用来决定当前目录下的哪个模块应当被建立.

/sys/module
/proc/modules
/sys/module 是一个sysfs 目录层次, 包含当前加载模块的信息. /proc/moudles 是旧式的, 那种信息的单个文件版本. 其中的条目包含了模块名, 每个模块占用的内存数量, 以及使用计数. 另外的字串追加到每行的末尾来指定标志, 对这个模块当前是活动的.

vermagic.o
来自内核源码目录的目标文件, 描述一个模块为之建立的环境.

#include <linux/module.h>
必需的头文件. 它必须在一个模块源码中包含.

#include <linux/version.h>
头文件, 包含在建立的内核版本信息.

LINUX_VERSION_CODE
整型宏定义, 对#ifdef 版本依赖有用.

EXPORT_SYMBOL (symbol);
EXPORT_SYMBOL_GPL (symbol);
宏定义, 用来输出一个符号给内核. 第2 种形式输出没有版本信息, 第3 种限制输出给GPL 许可的模块.

MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);
放置文档在目标文件的模块中.

module_init(init_function);
module_exit(exit_function);
宏定义, 声明一个模块的初始化和清理函数.

#include <linux/moduleparam.h>
module_param(variable, type, perm);
宏定义, 创建模块参数, 可以被用户在模块加载时调整( 或者在启动时间, 对于内嵌代码). 类型可以是bool, charp, int, invbool, short, ushort, uint, ulong, 或者intarray.

#include <linux/kernel.h>
int printk(const char * fmt, ...);
内核代码的printf 类似物.

第3 章字符驱动
本章介绍了下面符号和头文件. struct file_operations 和struct file 中的成员的列表这里不重复了.

#include <linux/types.h>
dev_t
dev_t 是用来在内核里代表设备号的类型.

int MAJOR(dev_t dev);
int MINOR(dev_t dev);
从设备编号中抽取主次编号的宏.

dev_t MKDEV(unsigned int major, unsigned int minor);
从主次编号来建立dev_t 数据项的宏定义.

#include <linux/fs.h>
"文件系统"头文件是编写设备驱动需要的头文件. 许多重要的函数和数据结构在此定义.

int register_chrdev_region(dev_t first, unsigned int count, char *name)
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)
void unregister_chrdev_region(dev_t first, unsigned int count);
允许驱动分配和释放设备编号的范围的函数. register_chrdev_region 应当用在事先知道需要的主编号时; 对于动态分配, 使用alloc_chrdev_region 代替.

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
老的( 2.6 之前) 字符设备注册函数. 它在2.6 内核中被模拟, 但是不应当给新代码使用. 如果主编号不是0, 可以不变地用它; 否则一个动态编号被分配给这个设备.

int unregister_chrdev(unsigned int major, const char *name);
恢复一个由register_chrdev 所作的注册的函数. major 和name 字符串必须包含之前用来注册设备时同样的值.

struct file_operations;
struct file;
struct inode;
大部分设备驱动使用的3 个重要数据结构. file_operations 结构持有一个字符驱动的方法; struct file 代表一个打开的文件, struct inode 代表磁盘上的一个文件.

#include <linux/cdev.h>
struct cdev *cdev_alloc(void);
void cdev_init(struct cdev *dev, struct file_operations *fops);
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
void cdev_del(struct cdev *dev);
cdev 结构管理的函数, 它代表内核中的字符设备.

#include <linux/kernel.h>
container_of(pointer, type, field);
一个传统宏定义, 可用来获取一个结构指针, 从它里面包含的某个其他结构的指针.

#include <asm/uaccess.h>
这个包含文件声明内核代码使用的函数来移动数据到和从用户空间.

unsigned long copy_from_user (void *to, const void *from, unsigned long count);
unsigned long copy_to_user (void *to, const void *from, unsigned long count);
在用户空间和内核空间拷贝数据.
第4 章调试技术
第5 章并发和竞争情况
本章已介绍了很多符号给并发的管理. 最重要的这些在此总结:

#include <asm/semaphore.h>
定义旗标和其上操作的包含文件.

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

void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
这2 函数用来在运行时初始化一个旗标.

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

struct rw_semaphore;
init_rwsem(struct rw_semaphore *sem);
旗标的读者/写者版本和初始化它的函数.

void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
获得和释放对读者/写者旗标的读存取的函数.

void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);
管理对读者/写者旗标写存取的函数.

#include <linux/completion.h>
DECLARE_COMPLETION(name);
init_completion(struct completion *c);
INIT_COMPLETION(struct completion c);
描述Linux completion 机制的包含文件, 已经初始化completion 的正常方法. INIT_COMPLETION 应当只用来重新初始化一个之前已经使用过的completion.

void wait_for_completion(struct completion *c);
等待一个completion 事件发出.

void complete(struct completion *c);
void complete_all(struct completion *c);
发出一个completion 事件. completion 唤醒, 最多, 一个等待着的线程, 而complete_all 唤醒全部等待者.

void complete_and_exit(struct completion *c, long retval);
通过调用complete 来发出一个completion 事件, 并且为当前线程调用exit.

#include <linux/spinlock.h>
spinlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock_init(spinlock_t *lock);
定义自旋锁接口的包含文件, 以及初始化锁的2 个方法.

void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock);
void spin_lock_bh(spinlock_t *lock);
加锁一个自旋锁的各种方法, 并且, 可能地, 禁止中断.

int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
上面函数的非自旋版本; 在获取锁失败时返回0, 否则非零.

void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
释放一个自旋锁的相应方法.

rwlock_t lock = RW_LOCK_UNLOCKED
rwlock_init(rwlock_t *lock);
初始化读者/写者锁的2 个方法.

void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
获得一个读者/写者锁的读存取的函数.

void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
释放一个读者/写者自旋锁的读存取.

void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
获得一个读者/写者锁的写存取的函数.

void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
释放一个读者/写者自旋锁的写存取的函数.

#include <asm/atomic.h>
atomic_t v = ATOMIC_INIT(value);
void atomic_set(atomic_t *v, int i);
int atomic_read(atomic_t *v);
void atomic_add(int i, atomic_t *v);
void atomic_sub(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
int atomic_add_negative(int i, atomic_t *v);
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
原子地存取整数变量. atomic_t 变量必须只通过这些函数存取.

#include <asm/bitops.h>
void set_bit(nr, void *addr);
void clear_bit(nr, void *addr);
void change_bit(nr, void *addr);
test_bit(nr, void *addr);
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
原子地存取位值; 它们可用做标志或者锁变量. 使用这些函数阻止任何与并发存取这个位相关的竞争情况.

#include <linux/seqlock.h>
seqlock_t lock = SEQLOCK_UNLOCKED;
seqlock_init(seqlock_t *lock);
定义seqlock 的包含文件, 已经初始化它们的2 个方法.

unsigned int read_seqbegin(seqlock_t *lock);
unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
int read_seqretry(seqlock_t *lock, unsigned int seq);
int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags);
获得一个seqlock-保护的资源的读权限的函数.

void write_seqlock(seqlock_t *lock);
void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);
void write_seqlock_irq(seqlock_t *lock);
void write_seqlock_bh(seqlock_t *lock);
获取一个seqlock-保护的资源的写权限的函数.

void write_sequnlock(seqlock_t *lock);
void write_sequnlock_irqrestore(seqlock_t *lock, unsigned long flags);
void write_sequnlock_irq(seqlock_t *lock);
void write_sequnlock_bh(seqlock_t *lock);
释放一个seqlock-保护的资源的写权限的函数.

#include <linux/rcupdate.h>
需要使用读取-拷贝-更新(RCU)机制的包含文件.

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

void call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg);
安排一个回调在所有处理器已经被调度以及一个RCU-保护的资源可用被安全的释放之后运行.


第6 章高级字符驱动操作
本章介绍了下面的符号和头文件:

#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>
int access_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>
int put_user(datum,ptr);
int get_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(int capability);
返回非零值如果进程有给定的能力.

#include <linux/wait.h>
typedef struct { /* ... */ } wait_queue_head_t;
void init_waitqueue_head(wait_queue_head_t *queue);
DECLARE_WAIT_QUEUE_HEAD(queue);
Linux 等待队列的定义类型. 一个wait_queue_head_t 必须被明确在运行时使用init_waitqueue_head 或者编译时使用DEVLARE_WAIT_QUEUE_HEAD 进行初始化.

void wait_event(wait_queue_head_t q, int condition);
int wait_event_interruptible(wait_queue_head_t q, int condition);
int wait_event_timeout(wait_queue_head_t q, int condition, int time);
int wait_event_interruptible_timeout(wait_queue_head_t q, int condition,int time);
使进程在给定队列上睡眠, 直到给定条件值为真值.

void wake_up(struct wait_queue **q);
void wake_up_interruptible(struct wait_queue **q);
void wake_up_nr(struct wait_queue **q, int nr);
void wake_up_interruptible_nr(struct wait_queue **q, int nr);
void wake_up_all(struct wait_queue **q);
void wake_up_interruptible_all(struct wait_queue **q);
void wake_up_interruptible_sync(struct wait_queue **q);
唤醒在队列q 上睡眠的进程. _interruptible 的形式只唤醒可中断的进程. 正常地, 只有一个互斥等待者被唤醒, 但是这个行为可被_nr 或者_all 形式所改变. _sync 版本在返回之前不重新调度CPU.

#include <linux/sched.h>
set_current_state(int state);
设置当前进程的执行状态. TASK_RUNNING 意味着它已经运行, 而睡眠状态是TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE.

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

typedef struct { /* ... */ } wait_queue_t;
init_waitqueue_entry(wait_queue_t *entry, struct task_struct *task);
wait_queue_t 类型用来放置一个进程到一个等待队列.

void prepare_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);
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
帮忙函数, 可用来编码一个手工睡眠.

void sleep_on(wiat_queue_head_t *queue);
void interruptible_sleep_on(wiat_queue_head_t *queue);
老式的不推荐的函数, 它们无条件地使当前进程睡眠.

#include <linux/poll.h>
void poll_wait(struct file *filp, wait_queue_head_t *q, poll_table *p);
将当前进程放入一个等待队列, 不立刻调度. 它被设计来被设备驱动的poll 方法使用.

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

void kill_fasync(struct fasync_struct *fa, int sig, int band);
如果这个驱动支持异步通知, 这个函数可用来发送一个信号到登记在fa 中的进程.

int nonseekable_open(struct inode *inode, struct file *filp);
loff_t no_llseek(struct file *file, loff_t offset, int whence);
nonseekable_open 应当在任何不支持移位的设备的open 方法中被调用. 这样的设备应当使用no_llseek 作为它们的llseek 方法.

第7 章时间, 延时, 和延后工作
本章介绍了下面的符号.

7.7.1. 时间管理
#include <linux/param.h>
HZ
HZ 符号指定了每秒产生的时钟嘀哒的数目.

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

int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
int time_before_eq(unsigned long a, unsigned long b);
这些布尔表达式以一种安全的方式比较jiffies, 没有万一计数器溢出的问题和不需要存取jiffies_64.

u64 get_jiffies_64(void);
获取jiffies_64 而没有竞争条件.

#include <linux/time.h>
unsigned long timespec_to_jiffies(struct timespec *value);
void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
unsigned long timeval_to_jiffies(struct timeval *value);
void jiffies_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_t get_cycles(void);
以平***立的方式返回时戳计数器. 如果CPU 没提供时戳特性, 返回0.

#include <linux/time.h>
unsigned long mktime(year, mon, day, h, m, s);
返回自Epoch 以来的秒数, 基于6 个unsigned int 参数.

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

struct timespec current_kernel_time(void);
返回当前时间, 以一个jiffy 的精度.

7.7.2. 延迟
#include <linux/wait.h>
long wait_event_interruptible_timeout(wait_queue_head_t *q, condition, signed long timeout);
使当前进程在等待队列进入睡眠, 安装一个以jiffies 表达的超时值. 使用schedule_timeout( 下面) 给不可中断睡眠.

#include <linux/sched.h>
signed long schedule_timeout(signed long timeout);
调用调度器, 在确保当前进程在超时到的时候被唤醒后. 调用者首先必须调用set_curret_state 来使自己进入一个可中断的或者不可中断的睡眠状态.

#include <linux/delay.h>
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
引入一个整数纳秒, 微秒和毫秒的延迟. 获得的延迟至少是请求的值, 但是可能更多. 每个函数的参数必须不超过一个平台特定的限制(常常是几千).

void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
使进程进入睡眠给定的毫秒数(或者秒, 如果使ssleep).

7.7.3. 内核定时器
#include <asm/hardirq.h>
int in_interrupt(void);
int in_atomic(void);
返回一个布尔值告知是否调用代码在中断上下文或者原子上下文执行. 中断上下文是在一个进程上下文之外, 或者在硬件或者软件中断处理中. 原子上下文是当你不能调度一个中断上下文或者一个持有一个自旋锁的进程的上下文.

#include <linux/timer.h>
void init_timer(struct timer_list * timer);
struct timer_list TIMER_INITIALIZER(_function, _expires, _data);
这个函数和静态的定时器结构的声明是初始化一个timer_list 数据结构的2 个方法.

void add_timer(struct timer_list * timer);
注册定时器结构来在当前CPU 上运行.

int mod_timer(struct timer_list *timer, unsigned long expires);
改变一个已经被调度的定时器结构的超时时间. 它也能作为一个add_timer 的替代.

int timer_pending(struct timer_list * timer);
宏定义, 返回一个布尔值说明是否这个定时器结构已经被注册运行.

void del_timer(struct timer_list * timer);
void del_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);
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
前2 个宏定义声明一个tasklet 结构, 而tasklet_init 函数初始化一个已经通过分配或其他方式获得的tasklet 结构. 第2 个DECLARE 宏标识这个tasklet 为禁止的.

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

void tasklet_schedule(struct tasklet_struct *t);
void tasklet_hi_schedule(struct tasklet_struct *t);
调度一个tasklet 运行, 或者作为一个"正常" tasklet 或者一个高优先级的. 当软中断被执行, 高优先级tasklets 被首先处理, 而正常tasklet 最后执行.

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

7.7.5. 工作队列
#include <linux/workqueue.h>
struct workqueue_struct;
struct work_struct;
这些结构分别表示一个工作队列和一个工作入口.

struct workqueue_struct *create_workqueue(const char *name);
struct workqueue_struct *create_singlethread_workqueue(const char *name);
void destroy_workqueue(struct workqueue_struct *queue);
创建和销毁工作队列的函数. 一个对create_workqueue 的调用创建一个有一个工作者线程在系统中每个处理器上的队列; 相反, create_singlethread_workqueue 创建一个有一个单个工作者进程的工作队列.

DECLARE_WORK(name, void (*function)(void *), void *data);
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
声明和初始化工作队列入口的宏.

int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
从一个工作队列对工作进行排队执行的函数.

int cancel_delayed_work(struct work_struct *work);
void flush_workqueue(struct workqueue_struct *queue);
使用cancel_delayed_work 来从一个工作队列中去除入口; flush_workqueue 确保没有工作队列入口在系统中任何地方运行.

int schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
void flush_scheduled_work(void);
使用共享队列的函数.

第8 章分配内存
相关于内存分配的函数和符号是:

#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( ));
int kmem_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);
void kmem_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);
void mempool_destroy(mempool_t *pool);
创建内存池的函数, 它试图避免内存分配设备, 通过保持一个已分配项的"紧急列表".

void *mempool_alloc(mempool_t *pool, int gfp_mask);
void mempool_free(void *element, mempool_t *pool);
从(并且返回它们给)内存池分配项的函数.

unsigned long get_zeroed_page(int flags);
unsigned long __get_free_page(int flags);
unsigned long __get_free_pages(int flags, unsigned long order);
面向页的分配函数. get_zeroed_page 返回一个单个的, 零填充的页. 这个调用的所有的其他版本不初始化返回页的内容.

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

void free_page(unsigned long addr);
void free_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);
void free_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);
void iounmap(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);
void free_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);
void free_bootmem(unsigned long addr, unsigned long size);
在系统启动时进行分配和释放内存的函数(只能被直接连接到内核中去的驱动使用)

第9 章与硬件通讯
本章介绍下列与硬件管理相关的符号:

#include <linux/kernel.h>
void barrier(void)
这个"软件"内存屏蔽要求编译器对待所有内存是跨这个指令而非易失的.

#include <asm/system.h>
void rmb(void);
void read_barrier_depends(void);
void wmb(void);
void mb(void);
硬件内存屏障. 它们请求CPU(和编译器)来检查所有的跨这个指令的内存读, 写, 或都有.

#include <asm/io.h>
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
unsigned inl(unsigned port);
void outl(unsigned doubleword, unsigned port);
用来读和写I/O 端口的函数. 它们还可以被用户空间程序调用, 如果它们有正当的权限来存取端口.

unsigned inb_p(unsigned port);
如果在一次I/O 操作后需要一个小延时, 你可以使用在前一项中介绍的这些函数的6 个暂停对应部分; 这些暂停函数有以_p 结尾的名子.

void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
这些"字串函数"被优化为传送数据从一个输入端口到一个内存区, 或者其他的方式. 这些传送通过读或写到同一端口count 次来完成.

#include <linux/ioport.h>
struct resource *request_region(unsigned long start, unsigned long len, char *name);
void release_region(unsigned long start, unsigned long len);
int check_region(unsigned long start, unsigned long len);
I/O 端口的资源分配器. 这个检查函数成功返回0 并且在错误时小于0.

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
void release_mem_region(unsigned long start, unsigned long len);
int check_mem_region(unsigned long start, unsigned long len);
为内存区处理资源分配的函数

#include <asm/io.h>
void *ioremap(unsigned long phys_addr, unsigned long size);
void *ioremap_nocache(unsigned long phys_addr, unsigned long size);
void iounmap(void *virt_addr);
ioremap 重映射一个物理地址范围到处理器的虚拟地址空间, 使它对内核可用. iounmap 释放映射当不再需要它时.

#include <asm/io.h>
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
用来使用I/O 内存的存取者函数.

void ioread8_rep(void *addr, void *buf, unsigned long count);
void ioread16_rep(void *addr, void *buf, unsigned long count);
void ioread32_rep(void *addr, void *buf, unsigned long count);
void iowrite8_rep(void *addr, const void *buf, unsigned long count);
void iowrite16_rep(void *addr, const void *buf, unsigned long count);
void iowrite32_rep(void *addr, const void *buf, unsigned long count);
I/O 内存原语的"重复"版本.

unsigned readb(address);
unsigned readw(address);
unsigned readl(address);
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);
memset_io(address, value, count);
memcpy_fromio(dest, source, nbytes);
memcpy_toio(dest, source, nbytes);
旧的, 类型不安全的存取I/O 内存的函数.

void *ioport_map(unsigned long port, unsigned int count);
void ioport_unmap(void *addr);
一个想对待I/O 端口如同它们是I/O 内存的驱动作者, 可以传递它们的端口给ioport_map. 这个映射应当在不需要的时候恢复( 使用ioport_unmap )

第10章中断处理
本章中介绍了这些关于中断管理的符号:

#include <linux/interrupt.h>
int request_irq(unsigned int irq, irqreturn_t (*handler)( ), unsigned long flags, const char *dev_name, void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
调用这个注册和注销一个中断处理.

#include <linux/irq.h.h>
int can_request_irq(unsigned int irq, unsigned long flags);
这个函数, 在i386 和x86_64 体系上有, 返回一个非零值如果一个分配给定中断线的企图成功.

#include <asm/signal.h>
SA_INTERRUPT
SA_SHIRQ
SA_SAMPLE_RANDOM
给request_irq 的标志. SA_INTERRUPT 请求安装一个快速处理者( 相反是一个慢速的). SA_SHIRQ 安装一个共享的处理者, 并且第3 个flag 声称中断时戳可用来产生系统熵.

/proc/interrupts
/proc/stat
报告硬件中断和安装的处理者的文件系统节点.

unsigned long probe_irq_on(void);
int probe_irq_off(unsigned long);
驱动使用的函数, 当它不得不探测来决定哪个中断线被设备在使用. probe_irq_on 的结果必须传回给probe_irq_off 在中断产生之后. probe_irq_off 的返回值是被探测的中断号.

IRQ_NONE
IRQ_HANDLED
IRQ_RETVAL(int x)
从一个中断处理返回的可能值, 指示是否一个来自设备的真正的中断出现了.

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_restore(unsigned long flags);
使用local_irq_save 来禁止本地处理器的中断并且记住它们之前的状态. flags 可以被传递给local_irq_restore 来恢复之前的中断状态.

void local_irq_disable(void);
void local_irq_enable(void);
在当前处理器熵无条件禁止和使能中断的函数.

第11章内核中的数据类型
下列符号在本章中介绍了:

#include <linux/types.h>
typedef u8;
typedef u16;
typedef u32;
typedef u64;
保证是8-位, 16-位, 32-位和-位无符号整型值的类型. 对等的有符号类型也存在. 在用户空间, 你可用__u8, __u16, 等等来引用这些类型.

#include <asm/page.h>
PAGE_SIZE
PAGE_SHIFT
给当前体系定义每页的字节数, 以及页偏移的位数( 对于4 KB 页是12, 8 KB 是13 )的符号.

#include <asm/byteorder.h>
__LITTLE_ENDIAN
__BIG_ENDIAN
这2 个符号只有一个定义, 依赖体系.

#include <asm/byteorder.h>
u32 __cpu_to_le32 (u32);
u32 __le32_to_cpu (u32);
在已知字节序和处理器字节序之间转换的函数. 有超过60 个这样的函数: 在include/linux/byteorder/ 中的各种文件有完整的列表和它们以何种方式定义.

#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val, ptr);
一些体系需要使用这些宏保护不对齐的数据存取. 这些宏定义扩展成通常的指针解引用, 为那些允许你存取不对齐数据的体系.

#include <linux/err.h>
void *ERR_PTR(long error);
long PTR_ERR(const void *ptr);
long IS_ERR(const void *ptr);
允许错误码由返回指针值的函数返回.

#include <linux/list.h>
list_add(struct list_head *new, struct list_head *head);
list_add_tail(struct list_head *new, struct list_head *head);
list_del(struct list_head *entry);
list_del_init(struct list_head *entry);
list_empty(struct list_head *head);
list_entry(entry, type, member);
list_move(struct list_head *entry, struct list_head *head);
list_move_tail(struct list_head *entry, struct list_head *head);
list_splice(struct list_head *list, struct list_head *head);
操作环形, 双向链表的函数.

list_for_each(struct list_head *cursor, struct list_head *list)
list_for_each_prev(struct list_head *cursor, struct list_head *list)
list_for_each_safe(struct list_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)
方便的宏定义, 用在遍历链表上.

第12章PCI 驱动
本节总结在本章中介绍的符号:

#include <linux/pci.h>
包含PCI 寄存器的符号名和几个供应商和设备ID 值的头文件.

struct pci_dev;
表示内核中一个PCI 设备的结构.

struct pci_driver;
代表一个PCI 驱动的结构. 所有的PCI 驱动必须定义这个.

struct pci_device_id;
描述这个驱动支持的PCI 设备类型的结构.

int pci_register_driver(struct pci_driver *drv);
int pci_module_init(struct pci_driver *drv);
void pci_unregister_driver(struct pci_driver *drv);
从内核注册或注销一个PCI 驱动的函数.

struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from);
struct pci_dev *pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct pci_dev *from);
struct pci_dev *pci_find_subsys (unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, const struct pci_dev *from);
struct pci_dev *pci_find_class(unsigned int class, struct pci_dev *from);
在设备列表中搜寻带有一个特定签名的设备, 或者属于一个特定类的. 返回值是NULL 如果没找到. from 用来继续一个搜索; 在你第一次调用任一个函数时它必须是NULL, 并且它必须指向刚刚找到的设备如果你寻找更多的设备. 这些函数不推荐使用, 用pci_get_ 变体来代替.

struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from);
struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, struct pci_dev *from);
struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn);
在设备列表中搜索一个特定签名的设备, 或者属于一个特定类. 返回值是NULL 如果没找到. from 用来继续一个搜索; 在你第一次调用任一个函数时它必须是NULL, 并且它必须指向刚刚找到的设备如果你寻找更多的设备. 返回的结构使它的引用计数递增, 并且在调用者完成它, 函数pci_dev_put 必须被调用.

int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
int pci_write_config_byte (struct pci_dev *dev, int where, u8 *val);
int pci_write_config_word (struct pci_dev *dev, int where, u16 *val);
int pci_write_config_dword (struct pci_dev *dev, int where, u32 *val);
读或写PCI 配置寄存器的函数. 尽管Linux 内核负责字节序, 程序员必须小心字节序当从单个字节组合多字节值时. PCI 总线是小端.

int pci_enable_device(struct pci_dev *dev);
使能一个PCI 设备.

unsigned long pci_resource_start(struct pci_dev *dev, int bar);
unsigned long pci_resource_end(struct pci_dev *dev, int bar);
unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
处理PCI 设备资源的函数.

第13 章USB 驱动
本节总结本章介绍的符号:

#include <linux/usb.h>
所有和USB 相关的头文件. 它必须被所有的USB 设备驱动包含.

struct usb_driver;
描述USB 驱动的结构.

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

int usb_register(struct usb_driver *d);
用来从USB核心注册和注销一个USB 驱动的函数.

struct usb_device *interface_to_usbdev(struct usb_interface *intf);
从struct usb_interface 获取控制struct usb_device *.

struct usb_device;
控制完整USB 设备的结构.

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

void usb_set_intfdata(struct usb_interface *intf, void *data);
void *usb_get_intfdata(struct usb_interface *intf);
设置和获取在struct usb_interface 中的私有数据指针部分的函数.

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

int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
用来注册和注销一个特定struct usb_interface * 结构到struct usb_class_driver 结构的函数.

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

struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
void usb_free_urb(struct urb *urb);
用来创建和销毁一个struct usb urb*的函数.

int usb_submit_urb(struct urb *urb, int mem_flags);
int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);
用来启动和停止一个USB 数据传输的函数.

void usb_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);
void usb_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);
void usb_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 的函数.

int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
用来发送和接受USB 数据的函数, 不必使用一个struct urb.
第14 章Linux 设备模型
许多函数在本章中已经被介绍过; 这是它们全部的一个快速总结.

14.9.1. Kobjects结构
#include <linux/kobject.h>
包含文件, 包含kobject 的定义, 相关结构, 和函数.

void kobject_init(struct kobject *kobj);
int kobject_set_name(struct kobject *kobj, const char *format, ...);
用作kobject 初始化的函数

struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
为kobjects 管理引用计数的函数.

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

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

void kset_init(struct kset *kset);
int kset_add(struct kset *kset);
int kset_register(struct kset *kset);
void kset_unregister(struct kset *kset);
为ksets 初始化和注册的函数.

decl_subsys(name, type, hotplug_ops);
易于声明子系统的一个宏.

void subsystem_init(struct subsystem *subsys);
int subsystem_register(struct subsystem *subsys);
void subsystem_unregister(struct subsystem *subsys);
struct subsystem *subsys_get(struct subsystem *subsys);
void subsys_put(struct subsystem *subsys);
对子系统的操作.

14.9.2. sysfs 操作
#include <linux/sysfs.h>
包含sysfs 声明的包含文件.

int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
void sysfs_remove_link(struct kobject *kobj, char *name);
创建和去除和一个kobject 关联的属性文件的函数.

14.9.3. 总线, 设备, 和驱动
int bus_register(struct bus_type *bus);
void bus_unregister(struct bus_type *bus);
在设备模型中进行注册和注销总线的函数.

int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
int bus_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);
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
BUS_ATTR 宏可能用来声明一个bus_attribute 结构, 它可能接着被添加和去除, 使用上面2 个函数.

int device_register(struct device *dev);
void device_unregister(struct device *dev);
处理设备注册的函数.

DEVICE_ATTR(name, mode, show, store);
int device_create_file(struct device *device, struct device_attribute *entry);
void device_remove_file(struct device *dev, struct device_attribute *attr);
处理设备属性的宏和函数.

int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
注册和注销一个设备驱动的函数.

DRIVER_ATTR(name, mode, show, store);
int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);
void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);
关联驱动属性的宏和函数.

14.9.4. 类
struct class_simple *class_simple_create(struct module *owner, char *name);
void class_simple_destroy(struct class_simple *cs);
struct class_device *class_simple_device_add(struct class_simple *cs, dev_t devnum, struct device *device, const char *fmt, ...);
void class_simple_device_remove(dev_t dev);
int class_simple_set_hotplug(struct class_simple *cs, int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size));
实现class_simple 接口的函数; 它们管理包含一个dev 属性和很少其他属性的简单的类入口

int class_register(struct class *cls);
void class_unregister(struct class *cls);
注册和注销类.

CLASS_ATTR(name, mode, show, store);
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);
处理类属性的常用宏和函数.

int class_device_register(struct class_device *cd);
void class_device_unregister(struct class_device *cd);
int class_device_rename(struct class_device *cd, char *new_name);
CLASS_DEVICE_ATTR(name, mode, show, store);
int class_device_create_file(struct class_device *cls, const struct class_device_attribute *attr);
属性类设备接口的函数和宏.

int class_interface_register(struct class_interface *intf);
void class_interface_unregister(struct class_interface *intf);
添加一个接口到一个类(或去除它)的函数.

14.9.5. 固件
#include <linux/firmware.h>
int request_firmware(const struct firmware **fw, char *name, struct device *device);
int request_firmware_nowait(struct module *module, char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context));
void release_firmware(struct firmware *fw);
属性内核固件加载接口的函数.

第15章内存映射和DMA
本章介绍了下列关于内存处理的符号:

15.5.1. 介绍性材料
#include <linux/mm.h>
#include <asm/page.h>
和内存管理相关的大部分函数和结构, 原型和定义在这些头文件.

void *__va(unsigned long physaddr);
unsigned long __pa(void *kaddr);
在内核逻辑地址和物理地址之间转换的宏定义.

PAGE_SIZE
PAGE_SHIFT
常量, 给出底层硬件的页的大小(字节)和一个页面号必须被移位来转变为一个物理地址的位数.

struct page
在系统内存映射中表示一个硬件页的结构.

struct page *virt_to_page(void *kaddr);
void *page_address(struct page *page);
struct page *pfn_to_page(int pfn);
宏定义, 在内核逻辑地址和它们相关的内存映射入口之间转换的. page_address 只用在低地址页或者已被明确映射的高地址页. pfn_to_page 转换一个页面号到它的相关的struct page 指针.

unsigned long kmap(struct page *page);
void kunmap(struct page *page);
kmap 返回一个内核虚拟地址, 被映射到给定页, 如果需要并创建映射. kunmap 为给定页删除映射.

#include <linux/highmem.h>
#include <asm/kmap_types.h>
void *kmap_atomic(struct page *page, enum km_type type);
void kunmap_atomic(void *addr, enum km_type type);
kmap 的高性能版本; 结果的映射只能被原子代码持有. 对于驱动, type 应当是KM_USER1, KM_USER1, KM_IRQ0, 或者KM_IRQ1.

struct vm_area_struct;
描述一个VMA 的结构.

15.5.2. 实现mmap
int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_add, unsigned long pfn, unsigned long size, pgprot_t prot);
int io_remap_page_range(struct vm_area_struct *vma, unsigned long virt_add, unsigned long phys_add, unsigned long size, pgprot_t prot);
位于mmap 核心的函数. 它们映射size 字节的物理地址, 从pfn 指出的页号开始到虚拟地址virt_add. 和虚拟空间相关联的保护位在prot 里指定. io_remap_page_range 应当在目标地址在I/O 内存空间里时被使用.

struct page *vmalloc_to_page(void *vmaddr);
转换一个由vmalloc 获得的内核虚拟地址到它的对应的struct page 指针.

15.5.3. 实现直接I/O
int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas);
函数, 加锁一个用户空间缓冲到内存并且返回对应的struct page 指针. 调用者必须持有mm->mmap_sem.

SetPageDirty(struct page *page);
宏定义, 标识给定的页为"脏"(被修改)并且需要写到它的后备存储, 在它被释放前.

void page_cache_release(struct page *page);
释放给定的页从页缓存中.

int is_sync_kiocb(struct kiocb *iocb);
宏定义, 返回非零如果给定的IOCB 需要同步执行.

int aio_complete(struct kiocb *iocb, long res, long res2);
函数, 指示一个异步I/O 操作完成.

15.5.4. 直接内存存取
#include <asm/io.h>
unsigned long virt_to_bus(volatile void * address);
void * bus_to_virt(unsigned long address);
过时的不好的函数, 在内核, 虚拟, 和总线地址之间转换. 总线地址必须用来和外设通讯.

#include <linux/dma-mapping.h>
需要来定义通用DMA 函数的头文件.

int dma_set_mask(struct device *dev, u64 mask);
对于无法寻址整个32-位范围的外设, 这个函数通知内核可寻址的地址范围并且如果可进行DMA 返回非零.

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *bus_addr, int flag);
void dma_free_coherent(struct device *dev, size_t size, void *cpuaddr, dma_handle_t bus_addr);
分配和释放一致DMA 映射, 对一个将持续在驱动的生命周期中的缓冲.

#include <linux/dmapool.h>
struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation);
void dma_pool_destroy(struct dma_pool *pool);
void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);
void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t handle);
创建, 销毁, 和使用DMA 池来管理小DMA 区的函数.

enum dma_data_direction;
DMA_TO_DEVICE
DMA_FROM_DEVICE
DMA_BIDIRECTIONAL
DMA_NONE
符号, 用来告知流映射函数在什么方向数据移入或出缓冲.

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);
void dma_unmap_single(struct device *dev, dma_addr_t bus_addr, size_t size, enum dma_data_direction direction);
创建和销毁一个单使用, 流DMA 映射.

void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
同步一个由一个流映射的缓冲. 必须使用这些函数, 如果处理器必须存取一个缓冲当使用流映射时.(即, 当设备拥有缓冲时).

#include <asm/scatterlist.h>
struct scatterlist { /* ... */ };
dma_addr_t sg_dma_address(struct scatterlist *sg);
unsigned int sg_dma_len(struct scatterlist *sg);
这个散布表结构描述一个涉及不止一个缓冲的I/O 操作. 宏sg_dma_address he sg_dma_len 可用来抽取总线地址和缓冲长度来传递给设备, 当实现发散/汇聚操作时.

dma_map_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction);
dma_unmap_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction);
void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
void dma_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(unsigned int channel, const char *name);
void free_dma(unsigned int channel);
存取DMA 注册. 注册必须在使用ISA DMA 通道之前进行.

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

void set_dma_mode(unsigned int channel, char mode);
void set_dma_addr(unsigned int channel, unsigned int addr);
void set_dma_count(unsigned int channel, unsigned int count);
编程DMA 信息在DMA 控制器中. addr 是一个总线地址.

void disable_dma(unsigned int channel);
void enable_dma(unsigned int channel);
一个DMA 通道必须被关闭在配置期间. 这些函数改变DMA 通道的状态.

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

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

第16章块驱动
#include <linux/fs.h>
int register_blkdev(unsigned int major, const char *name);
int unregister_blkdev(unsigned int major, const char *name);
register_blkdev 注册一个块驱动到内核, 并且, 可选地, 获得一个主编号. 一个驱动可被注销, 使用unregister_blkdev.

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

#include <linux/genhd.h>
struct gendisk;
描述内核中单个块设备的结构.

struct gendisk *alloc_disk(int minors);
void add_disk(struct gendisk *gd);
分配gendisk 结构的函数, 并且返回它们到系统.

void set_capacity(struct gendisk *gd, sector_t sectors);
存储设备能力(以512-字节)在gendisk 结构中.

void add_disk(struct gendisk *gd);
添加一个磁盘到内核. 一旦调用这个函数, 你的磁盘的方法可被内核调用.

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

#include <linux/blkdev.h>
request_queue_t blk_init_queue(request_fn_proc *request, spinlock_t *lock);
void blk_cleanup_queue(request_queue_t *);
处理块请求队列的创建和删除的函数.

struct request *elv_next_request(request_queue_t *queue);
void end_request(struct request *req, int success);
elv_next_request 从一个请求队列中获得下一个请求; end_request 可用在每个简单驱动器中来标识一个(或部分)请求完成.

void blkdev_dequeue_request(struct request *req);
void elv_requeue_request(request_queue_t *queue, struct request *req);
从队列中除去一个请求, 并且放回它的函数如果需要.

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

void blk_queue_bounce_limit(request_queue_t *queue, u64 dma_addr);
void blk_queue_max_sectors(request_queue_t *queue, unsigned short max);
void blk_queue_max_phys_segments(request_queue_t *queue, unsigned short max);
void blk_queue_max_hw_segments(request_queue_t *queue, unsigned short max);
void blk_queue_max_segment_size(request_queue_t *queue, unsigned int max);
blk_queue_segment_boundary(request_queue_t *queue, unsigned long mask);
void blk_queue_dma_alignment(request_queue_t *queue, int mask);
void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max);
设置各种队列参数的函数, 来控制请求如何被创建给一个特殊设备; 这些参数在"队列控制函数"一节中描述.

#include <linux/bio.h>
struct bio;
低级函数, 表示一个块I/O 请求的一部分.

bio_sectors(struct bio *bio);
bio_data_dir(struct bio *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 来恢复.

struct page *bio_page(struct bio *bio);
int bio_offset(struct bio *bio);
int bio_cur_sectors(struct bio *bio);
char *bio_data(struct bio *bio);
char *bio_kmap_irq(struct bio *bio, unsigned long *flags);
void bio_kunmap_irq(char *buffer, unsigned long *flags);
一组存取者宏定义, 提供对一个bio 结构中的"当前"段的存取.

void blk_queue_ordered(request_queue_t *queue, int flag);
int blk_barrier_rq(struct request *req);
如果你的驱动实现屏障请求, 调用blk_queue_ordered -- 如同它应当做的. 宏blk_barrier_rq 返回一个非零值如果当前请求是一个屏障请求.

int blk_noretry_request(struct request *req);
这个宏返回一个非零值, 如果给定的请求不应当在出错时重新尝试.

int end_that_request_first(struct request *req, int success, int count);
void end_that_request_last(struct request *req);
使用end_that_request_firest 来指示一个块I/O 请求的一部分完成. 当那个函数返回0, 请求完成并且应当被传递给end_that_request_last.

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

int blk_rq_map_sg(request_queue_t *queue, struct request *req, struct scatterlist *list);
为一次DMA 传送填充给定的散布表, 用需要来映射给定请求中的缓冲的信息

typedef int (make_request_fn) (request_queue_t *q, struct bio *bio);
make_request 函数的原型.

void bio_endio(struct bio *bio, unsigned int bytes, int error);
指示一个给定bio 的完成. 这个函数应当只用在你的驱动直接获取bio , 通过make_request 函数从块层.

request_queue_t *blk_alloc_queue(int flags);
void blk_queue_make_request(request_queue_t *queue, make_request_fn *func);
使用blk_alloc_queue 来分配由定制的make_request 函数使用的请求队列, . 那个函数应当使用blk_queue_make_request 来设置.

typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req);
void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func);
一个命令准备函数的原型和设置函数, 它可用来准备必要的硬件命令, 在请求被传递给你的请求函数之前.

int blk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags);
int blk_queue_resize_tags(request_queue_t *queue, int new_depth);
int blk_queue_start_tag(request_queue_t *queue, struct request *req);
void blk_queue_end_tag(request_queue_t *queue, struct request *req);
struct request *blk_queue_find_tag(request_queue_t *qeue, int tag);
void blk_queue_invalidate_tags(request_queue_t *queue);
驱动使用被标记的命令队列的支持函数.

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

#include <linux/netdevice.h>
定义struct net_device 和struct net_device_stats 的头文件, 包含了几个其他网络驱动需要的头文件.

struct net_device *alloc_netdev(int sizeof_priv, char *name, void (*setup)(struct net_device *);
struct net_device *alloc_etherdev(int sizeof_priv);
void free_netdev(struct net_device *dev);
分配和释放net_device 结构的函数

int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev);
注册和注销一个网络设备.

void *netdev_priv(struct net_device *dev);
获取网络设备结构的驱动私有区域的指针的函数.

struct net_device_stats;
持有设备统计的结构.

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

skb_shinfo(struct sk_buff *skb);
宏定义, 提供对报文缓存的"shared info"部分的存取.

void netif_rx(struct sk_buff *skb);
调用来通知内核一个报文已经收到并且封装到一个socket 缓存中的函数.

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

int netif_receive_skb(struct sk_buff *skb);
void netif_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 实现中有重要地位.

void netif_carrier_off(struct net_device *dev);
void netif_carrier_on(struct net_device *dev);
int netif_carrier_ok(struct net_device *dev);
前2 个函数可用来告知内核是否接口上有载波信号. netif_carrier_ok 测试载波状态, 如同在设备结构中反映的.

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

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

struct sk_buff *alloc_skb(unsigned int len, int priority);
struct sk_buff *dev_alloc_skb(unsigned int len);
void kfree_skb(struct sk_buff *skb);
void dev_kfree_skb(struct sk_buff *skb);
void dev_kfree_skb_irq(struct sk_buff *skb);
void dev_kfree_skb_any(struct sk_buff *skb);
处理socket 缓存的分配和释放的函数. 通常驱动应当使用dev_ 变体, 其意图就是此目的.

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

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

unsigned char *skb_pull(struct sk_buff *skb, int len);
skb_pull "去除" 数据从一个skb, 通过调整内部指针.

int skb_is_nonlinear(struct sk_buff *skb);
如果这个skb 是为发散/汇聚I/O 分隔为几个片, 函数返回一个真值.

int skb_headlen(struct sk_buff *skb);
返回skb 的第一个片的长度, 由skb->data 指向.

void *kmap_skb_frag(skb_frag_t *frag);
void kunmap_skb_frag(void *vaddr);
提供对非线性skb 中的片直接存取的函数.

#include <linux/etherdevice.h>
void ether_setup(struct net_device *dev);
为以太网驱动设置大部分方法为通用实现的函数. 它还设置dev->flags 和安排下一个可用的ethx 给dev->name, 如果名子的第一个字符是一个空格或者NULL 字符.

unsigned short 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>
struct mii_if_info;
声明和一个结构, 支持实现MII 标准的设备的驱动.

#include <linux/ethtool.h>
struct ethtool_ops;
声明和结构, 使得设备与ethtool 工具一起工作.

第18章TTY 驱动
本节提供了对本章介绍的概念的参考. 它还解释了每个tty 驱动需要包含的头文件的角色. 在tty_driver 和tty_device 结构中的成员变量的列表, 但是, 在这里不重复.

#include <linux/tty_driver.h>
头文件, 包含struct tty_driver 的定义和声明一些在这个结构中的不同的标志.

#include <linux/tty.h>
头文件, 包含tty_struct 结构的定义和几个不同的宏定义来易于存取struct termios 的成员的单个值. 它还含有tty 驱动核心的函数声明.

#include <linux/tty_flip.h>
头文件, 包含几个tty flip 缓冲内联函数, 使得易于操作flip 缓冲结构.

#include <asm/termios.h>
头文件, 包含struct termio 的定义, 用于内核所建立的特定硬件平台.

struct tty_driver *alloc_tty_driver(int lines);
函数, 创建一个struct tty_driver, 可之后传递给tty_register_driver 和tty_unregister_driver 函数.

void put_tty_driver(struct tty_driver *driver);
函数, 清理尚未成功注册到tty 内核的struct tty_driver 结构.

void tty_set_operations(struct tty_driver *driver, struct tty_operations *op);
函数, 初始化struct tty_driver 的函数回调. 有必要在tty_register_driver 可被调用前调用.

int tty_register_driver(struct tty_driver *driver);
int tty_unregister_driver(struct tty_driver *driver);
函数, 从tty 核心注册和注销一个tty 驱动.

void tty_register_device(struct tty_driver *driver, unsigned minor, struct device *device);
void tty_unregister_device(struct tty_driver *driver, unsigned minor);
对tty 核心注册和注销一个单个tty 设备的函数.

void tty_insert_flip_char(struct tty_struct *tty, unsigned char ch, char flag);
插入字符到tty 设备的要被用户读的flip 缓冲的函数.

TTY_NORMAL
TTY_BREAK
TTY_FRAME
TTY_PARITY
TTY_OVERRUN
flag 参数的不同值, 用在tty_insert_flip_char 函数.

int tty_get_baud_rate(struct tty_struct *tty);
函数, 获取当前为特定tty 设备设置的波特率.

void tty_flip_buffer_push(struct tty_struct *tty);
函数, 将当前flip 缓冲中的数据推给用户.

tty_std_termios
变量, 使用一套通用的缺省线路设置来初始化一个termios 结构.









注意!此信息未认证,请谨慎判断信息的真实性!

全部评论
空

相关内容推荐

头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像 头像
点赞 评论 收藏
转发
点赞 评论 收藏
转发
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像 头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
2022-12-22 00:27
中南大学_2023
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
头像
点赞 评论 收藏
转发
点赞 收藏 评论
分享

全站热榜

正在热议