嵌入式开发实作(2.6版驱动开发)
KEY:Linux 嵌入式 驱动开发
主设备号与次设备号(Device major and minor numbers)
逗号前是主设备号(major number),主设备号唯一标识一个[设备驱动程序 ];逗号后是次设备号(minor number),次设备号唯一标识一个具体[设备 ]。意思是说,一种驱动程序可驱动多个相似的具体设备。想知道一个具体设备使用哪种驱动,又或者手上有奇怪的设备,可以查看内核文档(Documentation/devices.txt)。
创建设备文件(Device file creation)
驱动模载被加载时是不创建相应的设备文件的,必须另外创建:
$ mknod /dev/<device> [c|b] <major> <minor>
Examples:
$ mknod /dev/ttyS0 c 4 64
$ mknod /dev/hda1 b 3 1
动态创建设备文件实例 (例子来自互联上,链接丢失)
在撰写Linux设备驱动程程的时候,如果指定主设备号(MAJOR)为0,就可以利用alloc_chrdev_region()函式让系统自动分配一个可用MAJOR给设备,代码片段:
这样驱动写好后载入核心执行,就会分配到一个MAJOR,可利用cat /proc/devices查看,但是如果要存取这个设备,还需要手动用mknod在/dev建立设备文件,但问题是怎么知道分配到的设备号码是什么?这里利用一个shell script搭配awk来达成。
然后只要将这个script文件设成启动时执行就可以啰!
hello module
模块编码指导(Module coding guidelines )
- C includes: 你不能在内核模块内使用C标准库的函数,比如printf(), strcat()。因为C库是基于内核而实现 的,而不是反过来用C库来实现内核模块。Linux提供了一些类似的内核函数,比如printk()。所在内核模块的源码只能包含内核的头文件。
- 不要使用浮点数(floating point numbers),因为你的代码可以运行在没有浮点处理单元的处理器上(比如ARM)。内核可以通过软件方法模拟浮点数,但这样会很慢。
- Define all symbols as static, except exported ones (avoid namespace pollution)
- 更的指导请查看Documentation/CodingStyle或参考GNU coding standards: http://www.gnu.org/prep/standards.html
编译模块(Compiling a module)
以下是编译2.6 版的内核模块的一个模板,只需运行make就可以构建hello.ko(注意$(MAKE)一行必须是以tab字符开头):
使用模块(Using the module)
- 以root登陆,执行:
$ tail f /var/log/messages - 同样以root登陆另一个终端,加载模块:
$ insmod ./hello.ko
你将可以看到(/var/log/messages):
Sep 13 22:02:30 localhost kernel: Hello, world - 卸载模块:
$ rmmod hello
你将可以看到:
Sep 13 22:02:37 localhost kernel: Goodbye, cruel world
模块实用工具(Module utilities)
- 安装指定模块:
$ insmod <module_name>
$ insmod <module_path>.ko - 安装指定模块及其依赖模块:
$ modprobe <module_name> - 卸载模块:
$ rmmod <module_name>
定义并传递模块参数(Defining and passing module parameters)
hello_param
- 加载模块并传递参数值
insmod ./hello_param.ko howmany=2 whom=universe
你将可以看到(/var/log/messages):
Sep 13 23:04:30 localhost kernel: (0) Hello, universe
Sep 13 23:04:30 localhost kernel: (1) Hello, universe - 卸载模块
rmmod hello_param
你将可以看到:
Sep 13 23:04:38 localhost kernel: Goodbye, cruel universe
声明模块参数(Declaring module parameters)
- module_param(name, type, perm);
- name: regular name symbol
- type: either byte, short, ushort, int, uint, long, ulong, charp, bool or invbool (checked at compile time!)
- perm: permissions for the corresponding entry in /sys/module/<module_name>/<param>. Safe to use 0.
- module_param_named(name, value, type, perm);
To make the name variable available outside the module and the value variable inside. - module_param_string(name, string, len, perm);
To defined name as charp, string prefilled with string of length len, usually sizeof(string) - module_param_array(name, type, num, perm);
To declare an array of parameters
传递模块参数(Passing module parameters)
- Through insmod or modprobe:
insmod ./hello_param.ko howmany=2 whom=universe - Through modprobe
after changing the /etc/modprobe.conf file:
options hello_param howmany=2 whom=universe - Through the kernel command line, when the module is built statically into the kernel:
options hello_param.howmany=2 /
hello_param.whom=universe
模块依赖(Module dependencies)
模块开发者不必关心模块间的依赖,因为在构建内核的时候,构建系统会根据模块的输出符号自动计算模块依赖。module2会被发现依赖于module1 ,因为module2引用了module1 输出的符号。模块依赖保存在:
/lib/modules/<version>/modules.dep
你可以通过以下命令更新这个文件:
depmod a [<version>]
把源码添加入内核树
New directory in kernel sources
To add an acme_drivers/ directory to the kernel sources:
- Move the acme_drivers/ directory to the appropriate location in kernel sources
- Create an acme_driver/Kconfig directory
- Create an acme_driver/Makefile file based on the Kconfig variables In the parent directory Kconfig file, add source “acme_driver/Kconfig”
- Run make xconfig and see your new options!
- In the parent directory Makefile file, add “obj$(CONFIG_ACME) += acme_driver/” (just 1 condition) or “objy += acme_driver/” (several conditions)
- Run make xconfig and see your new options!
- Run make and your new files are compiled!
See Documentation/kbuild/*.txt for details