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、中断里面使用自旋锁,但是在中断里面使用自旋锁的时候,在获取锁之前一定要先禁止本地中断

扫描二维码关注公众号,回复: 9554480 查看本文章

标准自旋锁:    
    一般代码内使用自旋锁:
    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。
==================================================================================    
 

发布了136 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/u010261063/article/details/104636251
今日推荐