关于I2C
由于手头有几个传感器,都需求用到I2C接口,所以在之前就将I2C子体系温习并深入研究了一番。以下我所说到的或贴出的部分代码或许不适合真实的板级驱动,由于是以模块化方式做测验的。
在此模块化驱动中,不只需注册驱动(i2c_driver),一起也要对设备信息进行注册(i2c_client),我以为在这儿不分前后次序(就像“先有鸡仍是先有蛋”的问题相同没有意义)。在前边剖析i2c子体系的时分说到过,关于在i2c适配器注册后再增加的新的设备不能再用 i2c_register_board_info了,这会导致设备彻底不能被激活,而需求用的是i2c_new_device才干将设备动态的注册到体系中
在运用i2c_new_device时分不只需求设备的i2c_board_info结构体,还需求其所依靠的I2C适配器总线号。首要,关于总线号,可以经过i2cdetect指令进行检查:
然后,在代码中可以这样运用:
这样就获取了指定总线号的i2c_adapter指针,之后就可以运用这个指针给i2c_new_device用了。最终需求留意,在注册完设备信息后,要运用i2c_put_adapter(adap)将指针开释掉。
用于描绘硬件信息的结构体可以做为i2c_client的私有数据保存,而这个结构体中往往也要保存对应的client。这种相互的对应联系应该在probe接口函数中进行:
驱动未写,调试先行。假如在开端着手书写驱动前就能直接的经过东西的简略运用对器材进行调试检查的话,会对驱动的书写有很大的协助。所以这儿要说一下关于I2C在shell中的几个调试指令i2cdetect, i2cdump, i2cget, i2cset。首要是i2cdetect,一般用来勘探和罗列总线(上边就演示了一下),一般运用方法是:罗列总线-》勘探有用设备
检查效果如下:
这儿就可以看到,设备从地址为0x68和0x1e的设备有实践有用的硬件衔接,分别是HMC5883L和AD0接地(不连)的MPU6050。0x54 55 56 57为EEPROM,设备忙。
其次是i2cdump,用来检查器材内部寄存器值,用法为i2cdump -y 总线号 设备地址
然后是i2cget和i2cset,分别是对寄存器进行获取和写入。用法为i2cget -y 总线号 设备地址 寄存器地址 形式和i2cset -y 总线号 设备地址 寄存器地址 数值 形式。形式默以为b(byte)即读取8bit数据,i2cget可用形式有b/w/c,i2cset可用形式有b/w/c/i/s,其间w为 word(16bit),i和s分别为I2C和SMBUS的block数据。
Mutex互斥锁
千万不要忘掉初始化mutex互斥锁。静态初始化DEFINE_MUTEX(mutex_name),动态初始化mutex_init(struct mutex *lock)。忘掉初始化就运用的话是会直接形成内核报错的。
在中止上下文中不要运用mutex互斥锁,由于假如呈现了竞态,mutex有或许进入睡觉,而中止上下文中是肯定不允许睡觉的。所以千万不要运用,假如必定要在中止中运用锁机制来维护一些驱动资源,主张运用spinlock自旋锁(semaphore信号量也不允许运用,相同的原因)。
重视死锁。哪个操作需求进行锁必定要事前自行规划好,不要在某操作一进入的时分锁,而进入其子过程后又锁,这样就直接死锁了,体系freeze掉。
关于中止
中止的运用很简略,可是却有许多值得留意的细节点。
GPIO中止。如一些开发板上,外部扩展出来许多GPIO口,可是却找不到IRQ口,所以就需求将GPIO扩展为中止线。在代码中,运用gpio_to_irq(gpio_nr)函数(linux/gpio.h)就可以得到主动转化后的中止线号了,可以用来恳求中止。
若在request_irq的时分最终给的参数不为NULL,那么在free_irq的时分,第二个参数也就有必要与其共同,不然会使体系找不到要开释哪个中止的处理程序句柄(当然了,为NULL就都为NULL,这个没有问题,只需共同就可以)。
作业行列
作业行列分work_struct 和delayed_work。差异便是delayed_work会在指定的推迟后开端运转,而work_struct会立即被调度运转。
初始化。INIT_WORK(struct work_struct *work, void (*work_func)(struct work_struct *work))动态初始化work_struct。INIT_DELAYED_WORK(struct delayed_work *work, void (*work_func)(struct work_struct *work))动态初始化delayed_work,在delayed_work的work_func作业函数中,可以经过强制转化将*work转化为 struct delayed_work类型。
调度。work_struct的调度为schedule_work(struct work_struct *work),而delayed_work则需求别的一个推迟参数schedule_delayed_work(struct delayed_work *work, unsigned long delay),这儿的delay参数便是推迟多久后投入作业,单位是jiffies,常会用到类如msecs_to_jiffies(msecs)等转化函数。
假如未对作业函数指定行列,那么其会主动进入system_wq中,在驱动中也常会用到界说自己的作业行列,可是简略作业往往没有需求这样做。
关于频频上报信息的作业,最好界说自己的作业行列,将此作业放入自己的作业行列中运转,而不是放入体系默许的system_wq中,这样会防止在体系忙的时分自己的作业被很快调度走,有自己的作业行列在这方面可以起到很大的效果。
completion同步
由于在调试过程中尝试了自检,而又涉及到中止,所以采用了completion作为同步机制,这儿提出简略用法。
初始化。init_completion(struct completion *wait)
等候。wait_for_completion_timeout(struct completion *wait, unsigned long timeout),返回值为剩余时间,假如剩余时间为0,也便是说明超时了。
唤醒。complete(struct completion *wait)