Linux驅動學習2

    ---------------------------等待事件-------------------------------
    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。
==================================================================================    
 

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