kernel——LLD 3RD書摘

主要是一重要的設備接口與內部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 工具一起工作.

轉自:http://blog.csdn.net/ccwwff/article/details/6593422

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章