Linux驱动开发盲点笔记1

Posted 天才2012

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发盲点笔记1相关的知识,希望对你有一定的参考价值。

1. vim中在找到搜索目标后,使用n与N进行定位查找

2. vim中使用gg到最好第一行,使用xxxG到某一行,否则G直接到最后一行;

3. ln -s 产生的链接文件最终指向的目标文件src 新产生的当前软链接文件dst。
ln -s project(磁盘上实际存在的文件或者目录) a.lnk
ln -s src dst(新产生的文件dst,dst链接到src)
symlink功能类似

4 tar -czvf 最终生产的tar打包好的文件 待打包的文件或者文件夹
tar czvf a.tar.bz  a


5. 移植dts的关键点是,待编译的dts文件均需要放置于/arch/arm/boot/dts下面。make时会默认在这里寻找dts编译文件入口。然后编译时以同名的dtb来执行make编译才能被makefile解析为目标,再去查找同名的dts由DTC来生产dtb文件。而dtb就是最终根据dts编译出来的二进制文件,位于arch/arm/boot下

6.在linux内核中断编程中需要注意:
中断上下文中不能出现调度休眠的情况。即不能使用互斥,信号量PV操作会引起休眠等方式。
中断一般需要处理的自旋锁保护临界资源时也需要较短时间的。
一般原子上下文中不能出现睡眠。
msleep/wait_event_interrupt等函数都是调度休眠延迟的,故不能出现在中断上下文中。

7. list_for_each与list_for_each_safe的区别
因此之遍历链表不删除结点时,可以使用list_for_each(),而当要删除结点操作时,则要使用list_for_each_safe()。

8. 模块入口函数编码注意点
module init/exit函数是无参函数,且应当是int类型的返回值

9. linux编译模块时的版本号
使用modinfo可查看编译出来的ko文件对应的内核版本号
使用uname或者 cat /proc/version 可在目标系统上查看内核版本号。
可查看kernel编译过程生成的文件: include/generated/utsrelease.h ,确定编译出来的内核的版本号。

10 df显示磁盘分区信息如df -h, du -sh来显示当前目录下的各文件与目录的大小信息

11 declare -f xxx用于输出当前shell支持的函数输出

12 find -name *xxx 查找文件

13 grep "xxx" -r ./*

14  cat /proc/version 查看Ubuntu版本号,  lsb_release -a
cat /etc/issue


15 makefile中的=与:=
前者在赋值时会查看赋值变量的全局性
后者在赋值时,只看被赋值变量再前面是否已经被定义过。

16 &是进程在后台运行不占据终端

17 linux设备驱动阻塞与非阻塞方式,阻塞与非阻塞是针对数据的读写,即资源的获取而言的,当无资源时,阻塞会调度进程休眠,而非阻塞则立刻返回
poll函数本质只是来查询当前设备是否已经可读或者可写POLLIN和POLLOUT。
利用select和poll可以去确保对设备的非阻塞操作(select和poll本身就一种阻塞操作,阻塞完成后可表明读写资源是ok的,从而确保资源是可读写操作的)
而所谓的阻塞操作,是对设备读写时直接进行进行进程的调度与休眠,直到数据可读或者可写,才回返回。

在自己定义的fops poll接口中需要实现poll_wait函数,该函数只是将当前需要进程poll_table加入到一个等待队列头中去,并不会产生休眠,但真正的休眠调度需要在do_sys_poll中由poll_schedule_timeout来休眠调度。
在等待队列头被wake_up唤醒后,会继续执行一次poll接口调用,再次将自己加入到wait queue中,此后一般是可以POLLIN或者POLLOUT,从而退出do_sys_poll的调用,返回查询结果给用户层。


static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
{
    if (p && p->_qproc && wait_address)
        p->_qproc(filp, wait_address, p);//__pollwait
}

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
                poll_table *p)
{
    struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
    struct poll_table_entry *entry = poll_get_entry(pwq);
    if (!entry)
        return;
    get_file(filp);
    entry->filp = filp;
    entry->wait_address = wait_address;
    entry->key = p->_key;
    init_waitqueue_func_entry(&entry->wait, pollwake);
    entry->wait.private = pwq;
    add_wait_queue(wait_address, &entry->wait);//将poll进程的wait加入到等待队列头
}

18 linux中断嵌套基本原理
1)linux响应中断后,将响应程序分成两部分:顶部和底部;
2)在执行完顶部后,就打开中断(清掉了相关的中断标志);底部由系统调度运行,底部运行时,允许中断;
3)如果在驱动中,申请中断request_iqr()时,使用了参数IRQF_DISABLED,则在调用中断处理函数时,屏蔽所有中断,从而使中断得到快速执行。
4)综合2)和3),可知LINUX允许中断嵌套。

19 devm_kzalloc可以更好的管理设备的相关资源

20 dts中的lable
了解了基本的device tree的结构后,我们总要把这些结构体现在device tree source code上来。在linux kernel中,扩展名是dts的文件就是描述硬件信息的device tree source file,在dts文件中,一个node被定义成:

[label:] node-name[@unit-address] {
  [properties definitions]
  [child nodes]
}

“[]”表示option,因此可以定义一个只有node name的空节点。label方便在dts文件中引用,具体后面会描述。child node的格式和node是完全一样的,因此,一个dts文件中就是若干嵌套组成的node,property以及child note、child note property描述。

21 dts中include dtsi的处理过程
尝试了下,貌似label的引用和include的位置无关,应该是提前于加载类似于makefile的include处理,而不是C编译的处理

22 push-pull open_drain
开漏加上拉,在高电平的时候能提供的电流很小,还有一个问题就是电平的翻转速度比较慢,总之就是驱动能力不行。但是开漏的方式可以借线与逻辑实现准双向IO,这一点是推挽做不到的。

浮空,顾名思义就是悬浮在空中,上面用绳子一拉就上去,下面用绳子一拉就沉下去了。
上拉,下拉的设置主要还是看你外接的驱动电路的具体情况配置的。
开漏,就等于输出口接了个NPN三极管,并且只接了e,b. c极是开路的,你可以直接接一个电阻到3.3V,也可以接一个电阻到5v,这样在输出1的时候,就有5V的电压,也可以输出3.3V的电压了,而不接电阻上拉,这个输出高是不能实现的。
推挽,就是有推有拉,任何时候IO口的点平都是确定的,不需要外接上拉或者下拉电阻


23 dts中的&用法
在root node中如果有一个A:A{}子节点,然后在这个dts中的根节点/{}外面又增加了
&A{
xxxxxx
其他node信息
}
这种方式是不是就是只是给A节点增加信息,本质就是只有一个A node,只是分开来描述而已

dts设备节点定义名字定义格式:
[label:] node-name[@unit-address]
如cif:[email protected],其对应的device node name还是cif,[]表示可选择的存在。


23 ffs函数取最低有效的位置,并置为1,其余位清零


24 vmalloc_user
每次vmalloc之后,内核会创建一个vm_struct,用以映射分配到的不连续的内存区域
vma是将物理内存映射到进程的虚拟地址空间而vm_struct是将物理内存映射到内核的线性地址空间
mm_struct是管理整个进程的虚拟地址空间,一般以current->mm来获取。
vmalloc_user的空间映射回用户remap_vmalloc_range

25 linux驱动自动加载顺序,不同级别按照init的level,同一等级按照makefile中定义的编译顺序来决定,先编译的模块则先会被链接到同等级中的区域块
Makefile中定义模块a和b:
obj-y=a;
obj-y=b;
则同一等级的a、b模块,a先会被加载,b后加载


26 dts例子
/{
  soc0{
  compatible = "my, soc0";
  node1{
  compatible = "my, node1";                    
  node1_1{
  compatible = "my, node1_1";
  node1_1_1{
  compatible = "my, node1_1_1";
}
}
}
  node2{ }
}
  soc1{ compatible = "my, soc1"; }
}

27 shell脚本函数变量
变量含义
$0当前脚本的文件名
$n传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$#传递给脚本或函数的参数个数。
$*传递给脚本或函数的所有参数。
[email protected]传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同,下面将会讲到。
$?上个命令的退出状态,或函数的返回值。
$$当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

以上是关于Linux驱动开发盲点笔记1的主要内容,如果未能解决你的问题,请参考以下文章

Android深度探索(卷1)HAL与驱动开发 第八章 让开发板发出声音:蜂鸣器驱动 读书笔记

Android深度探索(卷1)HAL与驱动开发 第九章 硬件抽象层:HAL 读书笔记

Android深度探索(卷1)HAL与驱动开发 第十章 嵌入式Linux的调试技术 读书笔记

第八章 读书笔记

Linux驱动开发--设备驱动基础笔记 1

Android深度探索--HAL与驱动开发----第八章读书笔记