---------------------------等待事件-------------------------------
wait_event(wq, condition)
等待以 wq 爲等待隊列頭的等待隊列被喚醒,前
提是 condition 條件必須滿足(爲真),否則一直阻
塞 。 此 函 數 會 將 進 程 設 置 爲
TASK_UNINTERRUPTIBLE 狀態
wait_event_timeout(wq, condition, timeout)
功能和 wait_event 類似,但是此函數可以添加超
時時間,以 jiffies 爲單位。此函數有返回值,如
果返回 0 的話表示超時時間到,而且 condition
爲假。爲 1 的話表示 condition 爲真,也就是條
件滿足了。
wait_event_interruptible(wq, condition)
與 wait_event 函數類似,但是此函數將進程設置
爲 TASK_INTERRUPTIBLE,就是可以被信號打
斷。
wait_event_interruptible_timeout(wq,
condition, timeout)
與 wait_event_timeout 函數類似,此函數也將進
程設置爲 TASK_INTERRUPTIBLE,可以被信號
打斷
---------------------------等待事件------------------------
select
poll
epoll
-------------------------------------
platfrom 總線驅動模型
device 設備
driver 驅動
重新學習設備樹:
dts 主要是描述板級信息
dtsi 主要描述soc信息;
make dtbs 命令編譯dtb設備樹文件
新板子添加設備dtb方法,
1添加一個dts文件
2、在dtb-$(CONFIG_SOC_IMX6ULL)下,添加一個板子的dtb信息
設備樹dts語法:
1、頭文件使用include 包含.h ,dts 和.dtsi文件;
2、節點格式:labe:node_name@unit_address ;{labe:標籤;node_name,節點名字;unit_address:色號被地址或是寄存器地址,沒有地址可以填0}
3、數據形式:
a,字符串
b, 32位無符號整數,數組
3,字符串列表;逗哈(,)分割
4、compatible 主要用於設備和驅動的綁定;屬性格式"manufacturer,mode":nanufacturer是廠商名稱,mode是驅動名字;屬性之可以是多個,多個之間逗號(,)間隔
5、model 用於描述設備模塊信息,屬性是一個字符串,
6、status 設備狀態;{"okey","disable","fail","fail-sss"};
7、#adress-cells 和 #size_cells :#address-cells決定了reg屬性中地址的佔用字節長度,#size-cells決定了reg屬性長度信息所佔的字長(32位)
8、reg :屬性形式:<start_addres,address_length>
9、range 屬性形式:<child_address,parent_address,child_address_length>
10、name
11、device_type
12、aliases 定義別名
13、chosen 主要是uboot向Linux傳遞參數;
中斷有關的設備樹屬性信息:
①、 #interrupt-cells,指定中斷源的信息 cells 個數。
②、 interrupt-controller,表示當前節點爲中斷控制器。
③、 interrupts,指定中斷號,觸發方式等。
④、 interrupt-parent,指定父中斷,也就是中斷控制器
---------------------------------------------
設備樹在文件系統中的體現: 在/proc/device-tree
設備節點:
struct device_node {
const char *name; /* 節點名字 */
const char *type; /* 設備類型 */
phandle phandle;
const char *full_name; /* 節點全名 */
struct fwnode_handle fwnode;
struct property *properties; /* 屬性 */
struct property *deadprops; /* removed 屬性 */
struct device_node *parent; /* 父節點 */
struct device_node *child; /* 子節點 */
struct device_node *sibling;
struct kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
OF函數:
1、通過節點名查找指定節點:struct device_node * of_find_node_by_name(struct device_node *from_node,const char *name);
2、通過device_type查找指定節點:struct device_node * of_fine_node_by_type(struct device_node *from_node,const char *type);
3、通過compatible和device_type查找節點 struct device_node * of_find_compatible_node(struct device_node *from_node,const char *type,const char*compatible_name);
4、通過of_device_id來查找節點:struct device_node * of_find_matching_node_and_match(struct device_node *from_node,const char matchs*,const char** match);
5、通過路徑來查找節點:struct device_node * of_find_by_name(const char *path);
6、查找指定節點的父節點:struct device_node * of_get_parent(const struct device_node *node)
7、迭代查找子節點:of_get_next_child(struct device_node *parent,struct device_node prev)
8、查找指定屬性:property * of_find_property(struct device_node *node,const char *name,int *len);len屬性字節數
9、獲取屬性元素的數量 :int of_property-count_elems_of_size(struct device_node *node,const char *prop_name,int len);//元素長度
10、從屬性中獲取指定索引位置的u32數據 int of_property_read_u32_index(struct device_node *node,const propname,u32 index,u32 *value);
11、讀取屬性中 u8、 u16、 u32 和 u64 類型的數組數據:
int of_property_read_u8_array(const struct device_node *np,
const char *propname,
u8 *out_values,
size_t sz)
int of_property_read_u16_array(const struct device_node *np,
const char *propname,
u16 *out_values,
size_t sz)
int of_property_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz)
int of_property_read_u64_array(const struct device_node *np,
const char *propname,
u32 *out_values,
size_t sz)
12、讀取這種只有一個整形值的屬性
int of_property_read_u8(const struct device_node *np,
const char *propname,
u8 *out_value)
int of_property_read_u16(const struct device_node *np,
const char *propname,
u16 *out_value)
int of_property_read_u32(const struct device_node *np,
const char *propname,
u32 *out_value)
int of_property_read_u64(const struct device_node *np,
const char *propname,
u64 *out_value)
13、讀取屬性中的字符串:int of_property_read_string(const struct device_node *np,const char *propname,char **value_str)
14、獲取#address-cells數據 int of_n_addr_cells(const struct device_node *np);
15、獲取#size-cells數據 int of_n_size_cells(const struct device_node *np);
------------------------
16、查看compatible屬性是否包含conpat指定的字符串:of_device_is_compatible(const struct device_node *np,const char *compat_str);//返回 0 則包含
17、獲取地址相關的屬性,主要是“reg”或者“assigned-addresses”屬性值:of_get_addr();
18、將從設備樹得到的地址轉換爲物理地址:u64 of_translate_address(struct device_node *dev, const _be32 * in_addr);
19、將reg屬性抓換位resource結構數據:int of_address_to_resource(struct device_node *dev,int index,struct *resource *re);
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;//常見參數IORESOURCE_MEM 、 IORESOURCE_REG 和IORESOURCE_IRQ
struct resource *parent, *sibling, *child;
};
20、獲取內存地址對應的虛擬地址:of_iomap(struct device_node *np,int index);//index:reg 屬性中要完成內存映射的段
--------------和gpio相關的of函數---------------------
21、獲取設備樹中某個屬性中包含幾個gpio信息 int of_gpio_named_count(struct device_node*np,const char *propname);
22、統計gpios這個屬性的gpio數量 int of_gpio_count(struct device_node *np);
23、獲取gpio編號 int of_get_named_gpio(struct *device_node *np,const char *propname,int index);//返回gpio編號
====================pintctrl 子系統==========start=======================
pinctrl 子系統主要工作內容如下:
①、獲取設備樹中 pin 信息。
②、根據獲取到的 pin 信息來設置 pin 的複用功能
③、根據獲取到的 pin 信息來設置 pin 的電氣特性,比如上/下拉、速度、驅動能力等
<mux_reg conf_reg input_reg mux_mode input_val> value
mux_reg 寄存器偏移地址
conf_reg 寄存器偏移地址
input_reg 寄存器偏移地址
mux_reg 寄存器值
input_reg 寄存器值
添加pinctrl模板:
1、在iomuxc節點下添加
2、節點前綴一定是pinctrl_
3、通過“fsl,pins”屬性配置
{
pinctrl_test:test
{
fsl,pins = <
//pin配置信息
>
}
}
====================pintctrl 子系統==========end=======================
====================gpio 子系統==========end=======================
相關api:
1、申請一個gpio管腳: int gpio_request(unsigned gpio,const char *label);
2、釋放gpio引腳: void gpio_free(unsigned gpio);
3、設置gppio輸入: int gpio_direction_input(unsigned gpio);
4、設置gpio輸出: int gpio_direction_output(unsigned gpio);
5、獲取gpio值: int gpio_get_value (unsigned gpio);
6、設置gpio值: void gpio_set_value(unsigned gpio,int value);
====================gpio 子系統==========end=======================
==========原子操作==================================================
ATOMIC_INIT(int i) 定義原子變量的時候對其初始化。
int atomic_read(atomic_t *v) 讀取 v 的值,並且返回。
void atomic_set(atomic_t *v, int i) 向 v 寫入 i 值。
void atomic_add(int i, atomic_t *v) 給 v 加上 i 值。
void atomic_sub(int i, atomic_t *v) 從 v 減去 i 值。
void atomic_inc(atomic_t *v) 給 v 加 1,也就是自增。
void atomic_dec(atomic_t *v) 從 v 減 1,也就是自減
int atomic_dec_return(atomic_t *v) 從 v 減 1,並且返回 v 的值。
int atomic_inc_return(atomic_t *v) 給 v 加 1,並且返回 v 的值。
int atomic_sub_and_test(int i, atomic_t *v) 從 v 減 i,如果結果爲 0 就返回真,否則返回假
int atomic_dec_and_test(atomic_t *v) 從 v 減 1,如果結果爲 0 就返回真,否則返回假
int atomic_inc_and_test(atomic_t *v) 給 v 加 1,如果結果爲 0 就返回真,否則返回假
int atomic_add_negative(int i, atomic_t *v) 給 v 加 i,如果結果爲負就返回真,否則返回假
==========原子操作==================================================
==========原子位操作 API 函數==================================================
void set_bit(int nr, void *p) 將 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p) 將 p 地址的第 nr 位清零。
void change_bit(int nr, void *p) 將 p 地址的第 nr 位進行翻轉。
int test_bit(int nr, void *p) 獲取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p) 將 p 地址的第 nr 位置 1,並且返回 nr 位原來的值。
int test_and_clear_bit(int nr, void *p) 將 p 地址的第 nr 位清零,並且返回 nr 位原來的值。
int test_and_change_bit(int nr, void *p) 將 p 地址的第 nr 位翻轉,並且返回 nr 位原來的值。
==========原子位操作 API 函數==================================================
==========自旋鎖 API 函數==================================================
1、沒有獲取到鎖就一直等着,cpu不處於工作狀態,不釋放cpu資源
2、中斷裏面使用自旋鎖,但是在中斷裏面使用自旋鎖的時候,在獲取鎖之前一定要先禁止本地中斷
標準自旋鎖:
一般代碼內使用自旋鎖:
DEFINE_SPINLOCK(spinlock_t lock) 定義並初始化一個自選變量。
int spin_lock_init(spinlock_t *lock) 初始化自旋鎖。
void spin_lock(spinlock_t *lock) 獲取指定的自旋鎖,也叫做加鎖。
void spin_unlock(spinlock_t *lock) 釋放指定的自旋鎖。
int spin_trylock(spinlock_t *lock) 嘗試獲取指定的自旋鎖,如果沒有獲取到就返回 0
int spin_is_locked(spinlock_t *lock) 檢查指定的自旋鎖是否被獲取,如果沒有被獲取就返回非 0,否則返回 0。
中斷代碼部分的自旋鎖:
void spin_lock_irq(spinlock_t *lock) 禁止本地中斷,並獲取自旋鎖。
void spin_unlock_irq(spinlock_t *lock) 激活本地中斷,並釋放自旋鎖。
建議使用以下兩個:
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) 保存中斷狀態,禁止本地中斷,並獲取自旋鎖。
void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags) 將中斷狀態恢復到以前的狀態,並且激活本地中斷,釋放自旋鎖。
在下半部裏面使用自旋鎖:
void spin_lock_bh(spinlock_t *lock) 關閉下半部,並獲取自旋鎖。
void spin_unlock_bh(spinlock_t *lock) 打開下半部,並釋放自旋鎖
------------------------------------------------------------------
讀寫自旋鎖:
讀寫自旋鎖爲讀和寫操作提供了不同的鎖,一次只能允許一個寫操作,也就是隻能一個線程持有寫鎖,而且不能進行讀操作。但是當沒有寫操作的時候允許一個或多個線程持有讀鎖,可以進行併發的讀操作
初始化讀寫自旋鎖
DEFINE_RWLOCK(rwlock_t lock) 定義並初始化讀寫鎖
void rwlock_init(rwlock_t *lock) 初始化讀寫鎖。
讀鎖
void read_lock(rwlock_t *lock) 獲取讀鎖。
void read_unlock(rwlock_t *lock) 釋放讀鎖。
void read_lock_irq(rwlock_t *lock) 禁止本地中斷,並且獲取讀鎖。
void read_unlock_irq(rwlock_t *lock) 打開本地中斷,並且釋放讀鎖。
void read_lock_irqsave(rwlock_t *lock,unsigned long flags) 保存中斷狀態,禁止本地中斷,並獲取讀鎖。
void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags) 將中斷狀態恢復到以前的狀態,並且激活本地中斷,釋放讀鎖。
void read_lock_bh(rwlock_t *lock) 關閉下半部,並獲取讀鎖。
void read_unlock_bh(rwlock_t *lock) 打開下半部,並釋放讀鎖。
寫鎖
void write_lock(rwlock_t *lock) 獲取讀鎖。
void write_unlock(rwlock_t *lock) 釋放讀鎖。
void write_lock_irq(rwlock_t *lock) 禁止本地中斷,並且獲取讀鎖。
void write_unlock_irq(rwlock_t *lock) 打開本地中斷,並且釋放讀鎖。
void write_lock_irqsave(rwlock_t *lock,unsigned long flags) 保存中斷狀態,禁止本地中斷,並獲取讀鎖。
void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags)將中斷狀態恢復到以前的狀態,並且激活本地中斷,釋放讀鎖。
void write_lock_bh(rwlock_t *lock) 關閉下半部,並獲取讀鎖。
void write_unlock_bh(rwlock_t *lock) 打開下半部,並釋放讀鎖
-----------------------------------------------------------------------
順序鎖
描述:順序鎖在讀寫鎖的基礎上衍生而來的,使用讀寫鎖的時候讀操作和寫操作不能同時進行。使用順序鎖的話可以允許在寫的時候進行讀操作,也就是實現同時讀寫,但是不允許同時進行併發的寫操作
初始化順序自旋鎖
DEFINE_SEQLOCK(seqlock_t sl) 定義並初始化順序鎖
void seqlock_ini seqlock_t *sl) 初始化順序鎖。
順序鎖寫操作
void write_seqlock(seqlock_t *sl) 獲取寫順序鎖。
void write_sequnlock(seqlock_t *sl) 釋放寫順序鎖。
void write_seqlock_irq(seqlock_t *sl) 禁止本地中斷,並且獲取寫順序鎖
void write_sequnlock_irq(seqlock_t *sl) 打開本地中斷,並且釋放寫順序鎖。
void write_seqlock_irqsave(seqlock_t *sl,unsigned long flags)保存中斷狀態,禁止本地中斷,並獲取寫順序鎖。
void write_sequnlock_irqrestore(seqlock_t *sl,unsigned long flags)將中斷狀態恢復到以前的狀態,並且激活本地中斷,釋放寫順序鎖。
void write_seqlock_bh(seqlock_t *sl) 關閉下半部,並獲取寫讀鎖。
void write_sequnlock_bh(seqlock_t *sl) 打開下半部,並釋放寫讀鎖。
順序鎖讀操作
unsigned read_seqbegin(const seqlock_t *sl)讀單元訪問共享資源的時候調用此函數,此函數會返回順序鎖的順序號。
unsigned read_seqretry(const seqlock_t *sl,unsigned start)讀結束以後調用此函數檢查在讀的過程中有沒有對資源進行寫操作,如果有的話就要重讀
==========自旋鎖 API 函數==================================================
==========信號量===========================================================
信號量的特點:
①、因爲信號量可以使等待資源線程進入休眠狀態,因此適用於那些佔用資源比較久的場合。
②、因此信號量不能用於中斷中,因爲信號量會引起休眠,中斷不能休眠。
③、如果共享資源的持有時間比較短,那就不適合使用信號量了,因爲頻繁的休眠、切換線程引起的開銷要遠大於信號量帶來的那點優勢
DEFINE_SEAMPHORE(name) 定義一個信號量,並且設置信號量的值爲 1。
void sema_init(struct semaphore *sem, int val) 初始化信號量 sem,設置信號量值爲 val。
void down(struct semaphore *sem) 獲取信號量,因爲會導致休眠,因此不能在中斷中使用。
int down_trylock(struct semaphore *sem); 嘗試獲取信號量,如果能獲取到信號量就獲取,並且返回 0。如果不能就返回非 0,並且不會進入休眠。
int down_interruptible(struct semaphore *sem); 獲取信號量,和 down 類似,只是使用 down 進入休眠狀態的線程不能被信號打斷。而使用此函數進入休眠以後是可以被信號打斷的。
void up(struct semaphore *sem); 釋放信號量
==========信號量===========================================================
==========互斥體===========================================================
注意事項:
①、 mutex 可以導致休眠,因此不能在中斷中使用 mutex,中斷中只能使用自旋鎖。
②、和信號量一樣, mutex 保護的臨界區可以調用引起阻塞的 API 函數。
③、因爲一次只有一個線程可以持有 mutex,因此,必須由 mutex 的持有者釋放 mutex。並且 mutex 不能遞歸上鎖和解鎖。
DEFINE_MUTEX(name) 定義並初始化一個 mutex 變量。
void mutex_init(mutex *lock) 初始化 mutex。
void mutex_lock(struct mutex *lock)獲取 mutex,也就是給 mutex 上鎖。如果獲取不到就進休眠。
void mutex_unlock(struct mutex *lock) 釋放 mutex,也就給 mutex 解鎖。
int mutex_trylock(struct mutex *lock)嘗試獲取 mutex,如果成功就返回 1,如果失敗就返回 0。
int mutex_is_locked(struct mutex *lock)判斷 mutex 是否被獲取,如果是的話就返回1,否則返回 0。
int mutex_lock_interruptible(struct mutex *lock)使用此函數獲取信號量失敗進入休眠以後可以被信號打斷
==========互斥體===========================================================
===========================內核定時器=======================================
Linux 內核使用全局變量 jiffies 來記錄系統從啓動以來的系統節拍數
time_after(unkown, known) unkown>known ?1:0;
time_before(unkown, known) unkown<=known ?1:0; unkown 通常爲 jiffies, known 通常是需要對比的值。
time_after_eq(unkown, known) unkown>=known ?1:0;
time_before_eq(unkown, known) unkown<=known ?1:0;
jiffies 和 ms、 us、 ns 之間的轉換函數
===========================內核定時器=======================================
將 jiffies 類型的參數 j 分別轉換爲對應的毫秒、微秒、納秒。
int jiffies_to_msecs(const unsigned long j)
int jiffies_to_usecs(const unsigned long j)
u64 jiffies_to_nsecs(const unsigned long j)
將毫秒、微秒、納秒轉換爲 jiffies 類型。
long msecs_to_jiffies(const unsigned int m)
long usecs_to_jiffies(const unsigned int u)
unsigned long nsecs_to_jiffies(u64 n)
定時器:
void init_timer(struct timer_list *timer);//初始化定時器
void add_timer(struct timer_list *timeer);//添加以後自動運行
void del_timer(struct timer_list *timeer);//多核處理器需要等待處理函數退出纔可以刪除
void del_timer_sync(struct timer_list *timer);//等待其他處理器處理完成以後再刪除
int mod_timer((struct timer_list *timeer,unsigned long expries);
內核延時函數:
納秒、微秒和毫秒延時函數。
void ndelay(unsigned long nsecs)
void udelay(unsigned long usecs)
void mdelay(unsigned long mseces)
===========================內核定時器=======================================
===========================Liunx中斷=======================================
不可以在中斷上下文和其他禁止睡眠的代碼段中使用 irq_requst;
irq_requst函數自動激活中斷不需要手動開啓
int requst_irq(unsigned int irq,//要申請的中斷號
irq_handler_t handler,//中斷處理函數
unsigned long flag,//中斷標誌
const char *name, //中斷名字
void *dev)//如果將flag設置位IRQ_SHARED的話,dev用來區分不同的中斷
flag:
IRQF_SHARED 設備共享一箇中斷線,共享的所有中斷都必須指定此標誌。如果使用共享中斷的話, request_irq 函數的 dev 參數就是唯一區分他們的標誌。
IRQF_ONESHOT 單次中斷,中斷執行一次就接觸。
IRQF_TRIGGER_NONE 無觸發。
IRQF_TRIGGER_RISING 上升沿觸發。
IRQF_TRIGGER_FALLING 下降沿觸發。
IRQF_TRIGGER_HIGH 高電平觸發。
IRQF_TRIGGER_LOW 低電平觸發
釋放中斷:
void free_irq(unsigned int irq,void *dev);
中斷處理函數:
irqreturn_t(*irq_handler_t (int ,void*));
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
中斷使能和禁止:
void enable_irq(unsigned int irq);
void disable_irq(unsigned int irq);//等到當前正在執行的中斷處理函數執行完才返回,因此使用者需要保證不會產生新的中斷,並且確保所有已經開始執行的中斷處理程序已經全部退出
void disable_irq_nosync(unsigned int irq);
全局中斷關閉:
local_irq_enable();
local_irq_disable();
保存恢復中斷狀態的禁止和恢復:
local_irq_save(flags);//保存在flags 禁止
local_irq_restore(flags)//恢復
上半部:上半部就是中斷處理函數,那些處理過程比較快,不會佔用很長時間的處理就可以放在上半部完成。
下半部:如果中斷處理過程比較耗時,那麼就將這些比較耗時的代碼提出來,交給下半部去執行,這樣中斷處理函數就會快進快出。
註冊軟中斷:
void open_softirq(int nr,void (*action)(struct softirq_action));
觸發軟中斷:
void raise_softirq(unsigned int nr);
nr:可選項
enum
{
HI_SOFTIRQ=0, /* 高優先級軟中斷 */
TIMER_SOFTIRQ, /* 定時器軟中斷 */
NET_TX_SOFTIRQ, /* 網絡數據發送軟中斷 */
NET_RX_SOFTIRQ, /* 網絡數據接收軟中斷 */
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, /* tasklet 軟中斷 */
SCHED_SOFTIRQ, /* 調度軟中斷 */
HRTIMER_SOFTIRQ, /* 高精度定時器軟中斷 */
RCU_SOFTIRQ, /* RCU 軟中斷 */
NR_SOFTIRQS
};
tasklet:
struct tasklet_struct
{
struct tasklet_struct *next; /* 下一個 tasklet */
unsigned long state; /* tasklet 狀態 */
atomic_t count; /* 計數器,記錄對 tasklet 的引用數 */
void (*func)(unsigned long); /* tasklet 執行的函數 */
unsigned long data; /* 函數 func 的參數 */
};
初始化tasklet:
DECLARE_TASKLET(name, func, data);
void tasklet_init(struct task_stuct *t,void (*fun)(unsigned long),unsigned long data );
中斷上半部分:
void tasklet_schedule(struct task_stuct *t);
工作隊列:
如果工作可以在睡眠,那麼可以選擇工作隊列;
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func; /* 工作隊列處理函數 */
// void (*work_func_t)(struct work_struct *work);
};
初始化:INIT_WORK(_work,_fun);
工作調度:bool schedule_work(struct work_struct * work);
中斷有關的設備樹屬性信息:
①、 #interrupt-cells,指定中斷源的信息 cells 個數。
②、 interrupt-controller,表示當前節點爲中斷控制器。
③、 interrupts,指定中斷號,觸發方式等。
④、 interrupt-parent,指定父中斷,也就是中斷控制器
設備樹操作:
從設備樹獲取中斷號:
unsigned int irq_of_parse_and_map(struct device_node *dev,int index);
從設備樹獲取gpio對應的中斷號:
int gpio_to_arq(unsigned int irq);
===========================Liunx中斷=======================================
=================================等待隊列===========================================
等待隊列;
等待隊列頭:
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
1、初始化隊列頭
void init_waitqueue_head(wait_queue_head_t *q)
DECLARE_WAIT_QUEUE_HEAD
等待隊列項:
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
DECLARE_WAITQUEUE(name, tsk)//初始化隊列項
2、添加到隊列頭
void add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)
3、移除到隊列頭:
void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait)
4、等待喚醒
void wake_up(wait_queue_head_t *q)
void wake_up_interruptible(wait_queue_head_t *q)
參數 q 就是要喚醒的等待隊列頭,這兩個函數會將這個等待隊列頭中的所有進程都喚醒。
wake_up 函數可以喚醒處於 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 狀態的進程,
wake_up_interruptible 函數只能喚醒處於 TASK_INTERRUPTIBLE 狀態的進程。
=================================等待隊列===========================================
應用程序下的阻塞和非阻塞相關函數
select
int select(int nfsd, //要監視的文件描述符個數
fd_set *readfds,//監視指定描述符的讀變化
fd_set *writefd,//監視指定描述符的寫變化
fd_set *exceptfds,//監視指定描述符的異常變化
struct timeval* timeout);//超時
返回值:0超時 -1失敗;>0表示有多少個變化;
struct timeval {
long tv_sec; /* 秒 */
long tv_usec; /* 微妙 */
void FD_ZERO(fd_set *set)
void FD_SET(int fd, fd_set *set)
void FD_CLR(int fd, fd_set *set)
int FD_ISSET(int fd, fd_set *set)
pool
struct foolfd{
int fd;
short events;
short reevents;
}
事件集合
POLLIN//有數據可以讀取
POLLPRI//有緊急數據讀取
POLLOUT//可以寫數據
POLLERR//發生錯誤
POLLHHUP//文件掛起
POLLNVAL //無效請求
POLLRDNORM <=>POLLIN
int poll (struct pollfd *fds, //監視的文件描述符監視事件集合
nfds_t nfs,//集合數量
time timeout);//超時ms
返回發生事件的個數,0超時,-1發生錯誤
epoll
1、創建epoll句柄:int epoll_create(int size);//size 無意義
struct epoll_event {
uint32_t events; /* epoll 事件 */
epoll_data_t data; /* 用戶數據 */
};
2、操作epoll句柄: int epoll_ctl(int epfd,//句柄
int op,//EPOOL_CTL-ADD,EPOOL_CTL-MOD,EPOOL_CTL-DEL
int fd, //文件描述符
struct epoll_event * event);
3、等待事件發生:int epoll_wait(int epfd,
struct epoll_event *events, //發生事件集合
int maxevents,//事件集合大小必須大於零
int timeout);//ms
驅動下的poll函數:
unsigned int (*poll) (struct file *filp,
struct poll_table_struct *wait);//一般傳給poll_wait();
返回值:嚮應用程序返回設備或者資源狀態,可以返回的資源狀態
poll_wait 函數原型如下:
void poll_wait(struct file * filp,
wait_queue_head_t * wait_address, 要添加到 poll_table 中的等待隊列頭
poll_table *p)是 poll_table; file_operations 中 poll 函數的 wait 參數。
應用程序下的信號處理:
sighandler_t signal(int signum,sighandler_t handler);
typedef void (*sighandler_t)(int signum);
---------------------------------------------------------------------
應用程序對異步通知的處理包括以下三部:
1、註冊信號處理函數
應用程序根據驅動程序所使用的信號來設置信號的處理函數,應用程序使用 signal 函數來
設置信號的處理函數。前面已經詳細的講過了,這裏就不細講了。
2、將本應用程序的進程號告訴給內核
使用 fcntl(fd, F_SETOWN, getpid())將本應用程序的進程號告訴給內核。
3、開啓異步通知
使用如下兩行程序開啓異步通知:
flags = fcntl(fd, F_GETFL); /* 獲取當前的進程狀態 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 開啓當前進程異步通知功能 */
重點就是通過 fcntl 函數設置進程狀態爲 FASYNC,經過這一步,驅動程序中的 fasync 函
數就會執行。
-----------------------------------------------------------------------
驅動內部異步處理:
實現函數 int (*fasync) (int fd, struct file *filp, int on);
發送信號:void kill_fasync(struct fasync_struct **fp, int sig, int band);
fp:要操作的 fasync_struct。
sig: 要發送的信號。
band: 可讀時設置爲 POLL_IN,可寫時設置爲 POLL_OUT。
==================================================================================