系列文章目录
Linux 内核设计与实现
深入理解 Linux 内核
Linux 设备驱动程序(一)
Linux 设备驱动程序(二)
Linux 设备驱动程序(三)
Linux 设备驱动程序(四)
Linux设备驱动开发详解
深入理解Linux虚拟内存管理
文章目录
- 系列文章目录
- 第十六章、块设备驱动程序
- 1、快速参考
- 第十七章、块设备驱动程序
- 1、快速参考
- 第十八章、TTY驱动程序
- 1、快速参考
- 符号
第十六章、块设备驱动程序
1、快速参考
#include <linux/fs.h>
int register_blkdev(unsigned iat 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);
// 在 gendisk 结构中保存设备容量(用 512 字节扇区为单位)。
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 *queve);
// 如果不想让自己的请求函数被调用、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_segment.s (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, unsigmed 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);
// 这两个宏用来获得 bio 结构描述的大小和传输方向。
bio_for_each_segment (bvec, bio, segno);
// 用来遍历组成 bio 结构的段的伪控制结构。
char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type);
void __bio_kurmap_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_first 表示完成一个块设备 I/O 请求的过程。如果该函数返回
// 0,则表示请求已经完成,应该被传递给 end_that_request_last。
rq_for_each_bio(bio, request)
// 另外一个以宏的形式实现的控制结构,它将遍历请求中的每个 bio 结构。
int blk_rq_map_sq(request_queue_t *queue, struct request *req, struct
scatterlist *list);
// 为DMA传输,需要将缓冲区映射到指定的 request 中,使用这些信息填充分散表。
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 结构的信号完成函数。只有当驱动程序通过 make_request 函数直接从
// 块设备层获得 bio 结构时,才使用该函数。
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);
// 为了让驱动程序使用标记命令队列而提供的支持函数。
第十七章、块设备驱动程序
1、快速参考
#include <linux/netdevice.h>
// 这个头文件保存有net_device和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);
// 提供对数据包缓存区中"共享信息"访问的宏。
void netif_rx(struct sk_buff *skb);
// 调用(包括中断期间)这个函数可通知内核已经接收到一个数据包,并封装入一个
// 套接字缓冲区。
void netif_rx_schedule(dev);
// 调用该函数通知内核数据包已经存在,并且在接口上启动轮询机制;它只在NAPI
// 驱动程序中使用。
int netif_receive_skb(struct sk_buff *skb);
void netif_rx_complete(struct net_device *dev);
// 这两个函数只在NAPI驱动程序中使用。NAPI中的netif_receive_skb函数与netif_rx
// 等价;它将数据包发送给内核。当NAPI驱动程序耗尽了为接收数据包准备的内存,
// 则它将重新启动中断,然后调用 netif_rx_complete 终止轮询函数。
#include <linux/if.h>
// netdevice.h 中包含该头文件。在该文件中声明了接口标志(IFF_macros)和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);
// 前两个函数告诉内核在指定接口上是否存在载波信号。netif_carrier_ok检查载波状
// 态作为在 device 结构中的应答。
#include <linux/if_ether.h>
ETH_ALEN
ETH_P_IP
struct ethhdr;
// netdevice.h 中包含该头文件。if_ether.h 中定义了所有的 ETH_ 宏,用来表示 octet
// 的长度(比如地址长度)和网络协议(比如 IP)。它还定义了 ethhdr 结构。
#include <linux/skbuff.h>
// 定义了sk_buff 及其相关结构,同时定义了许多作用于缓冲区的内联函数。该头
// 文件包含在 netdevice.h 中。
struct sk_buff *alloc_skb(unsigned int ler, 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);
// 分配和释放套接字缓冲区的函数。因此驱动程序通常使用具有 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 1en);
_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 的
// 开头和结尾,还有多少空间可用。skb_reserve 用于在 skb 开头部分保留空间,保
// 留的空间必须为空。
unsigned char *skb_pull(struct sk_buff *skb, int len);
// skb_pull 通过调整内部指针而"删除"skb 内的数据。
int skb_is_nonlinear(struct sk_buff *skb);
// 如果使用分散 / 聚集 I/O, 并且skb 分离成多个数据片段,则该函数返回真实值。
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。
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 命令中的第一个,每个驱动程序都能够出于自身的考虑实现它。在
// sockios.h 中定义了所有的网络 ioctl 命令。
#include <linux/mii.h>
struct mii_if_info;
// 支持实现 MII 标准的设备驱动程序的声明和结构。
#include <linux/ethtool.h>
struct ethtool_ops;
// 让设备可使用 ethtool 工具的声明和结构。
第十八章、TTY驱动程序
1、快速参考
#include <linux/tty_driver.h>
// 包含 tty_driver 结构定义,以及在该结构中一些不同标志位的声明。
#include <linux/tty.h>
// 该头文件包含了tty_struct结构的定义以及许多不同的宏定义,使得对termios
// 结构各成员值的访问更简单。它还包含了 tty 驱动程序核心的函数声明。
#include <linux/tty_flip.h>
// 包含了一些 tty 交替缓冲区 inline 函数的 头文件,这些 inline 通数能简化对交替缓
// 冲区结构的操作。
#include <asm/termios.h>
// 特定硬件平台创建内核时,使用的是包含 termio 结构定义的头文件。
struct tty_driver *alloc_tty_driver(int 1ines);
// 创建 tty_driver 结构的函数,该结构以后将被传递给 tty_register_driver 和
// tty_unregister_driver 函数。
void put_tty_driver(struct tty_driver *driver);
// 如果使用tty_driver结构向tty核心的注册没有成功,该函数负责清空 tty_driver
// 结构。
void tty_set_operations(struct tty_driver *driver, struct tty_operations *op);
// 负责初始化结构 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);
// 将字符插入到 ty 设备的交替缓冲区使用户能够读到的函数。
TTY_NORMAL
TTY_BREAK
TTY_FRAME
TTY_PARITY
TTY_OVERRUN
// 在 tty_insert_flip_char 函数中使用的不同的标志位。
int tty_get_baud_rate(struct tty_struct *tty);
// 获得指定 tty 设备当前波特率的函数。
void tty_flip_buffer_push(struct tty_struct *tty);
// 把数据放入当前交替缓冲区并传递给用户的函数。
tty_std_termios
// 用常见的默认线路设置初始化 termios 结构的变量。
符号
⇐ ⇒ ⇔ ⇆ ⇒ ⟺
①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿
⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑿⒀⒁⒂⒃⒄⒅⒆⒇
➊➋➌➍➎➏➐➑➒➓⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴
⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵
ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ
ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ
🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩
123
y = x 2 + z 3 y = x^2 + z_3 y=x2+z3
y = x 2 + z 3 + a b + b a y = x^2 + z_3 + \frac {a}{b} + \sqrt[a]{b} y=x2+z3+ba+ab
y = x 2 + z 3 (1) y = x^2 + z^3 \tag{1} y=x2+z3(1)