uClinux 驱动开发总结
弄了几天,终于成功实现了uClinux 下的驱动,喜悦中……
有收获了就应该总结一下,以便能给日后的工作提供一个参考,并且和想要写uClinux 驱动的朋友们分享一下。
使用uClinux-2.4 + S3C44B0X + ARM7 ,在redhat9 上编译,交叉编译工具使用arm-elf-tools 。
uClinux 版本:uClinux-dist-20060311.tar.gz
arm-elf-tools 版本:arm-elf-tools-20040427.sh
第一天,原来的资料消失了,只能上网搜,根据印象找到了一些有用的。以前有接触过这块板,但是对板上的资源基本没印象了,所以只能结合S3C44B0X 的资料重温一下。今天用汇编写裸机程序,验证了个别引脚的功能和对应的寄存器,下面是第一天做的笔记:
LCD 控制 :板子上标有LCD 的插槽, 一共20 根针, 下面是图解:
18 | 16 | 14 | 12 | 10 | 8 | 6 | 4 | 2 | 0 |
19 | 17 | 15 | 13 | 11 | 9 | 7 | 5 | 3 | 1 |
VD[7:0] 对应图中0 ~7 号引脚,9 号接地
当rPCOND[7:0] 为0x55(output) 时,可以通过rPDATD[3:0] 控制VD[3:0] 。
当rPCONC[7:4] 为0x55(output) 时,可以通过rPDATC[7:4] 控制VD[7:4] 。
LED 控制 :当rPCONC[7:2] 为0x54( 或0xFC) 时,rPDATC[3:1] 控制3 个LED 。
有了这些信息,接下来只要把驱动写出来就OK 了。
第二天,开始写驱动。遇到了一些问题,都记下来了(小菜才会遇到的东西,高手略过吧):
今天编译驱动,好久不接触,搞了一个下午才能开始。
出现@led_driver,c,231,0 说明led_driver 驱动没有注册成功
后来发现驱动名不能有字母以外的字符,把led_driver 改成leddriver 后驱动注册成功,相应的文件名也要改成leddriver.c 。
make menuconfig 和make dep 只是第一次编译的时候需要执行。
今天未能成功实现驱动。
搞了一天,驱动还是没能成功,但是确定了问题出在ioctl 。
第三天,几天终于搞定驱动了,写下了ioctl 失败的原因:
昨天编译的驱动没问题,问题在于用户程序调用ioctl 的时候没有使用_IO 宏,就像windows 驱动一样,同样需要一个IOCTL_CODE ,这个参数就不只是一个整数而已,下面是它的结构:
| dir | size | type | nr |
dir: 2bit,命令指示,表示命令类型
size: 14bit,数据大小,通常和第四个参数有关
type: 8bit,类型,又称之为幻数,表示设备的类型
nr: 8bit,命令序号。
各个字段的顺序和位宽可能在不同版本、不同平台的内核中是不同的,写内核模块时应使用内核提供的宏来作处理,而不直接设定。
当然这个cmd的规定对驱动程序来说并不是严格的,也可另起炉灶,但是这不好。
摘至:http://www.yuanma.org/data/2008/1022/article_3252.htm
以下是ioctl 的详解:
相对于驱动程序中的ioctl就是应用程序中的ioctl,其原型是:
ioctl (int __fd, unsigned long int __request, ...)
其中int __fd 是已经打开的文件描述符。
int __request 是请求命令字,这是与设备相关的,也就是对应驱动程序ioctl中的cmd
第三个参数依赖于第二个参数,通常是一个指针,或有或无。
同样对应用程序的request的设定也应使用相应的宏来处理,参看 /asm-generic/ioctl.h
下面以ldd3中提供的scull设备为对象,在应用程序中调用ioctl对其进行控制。
int main(void)
{
int fd = open("/dev/scull",O_RDONLY);
int qset_size = 2000;
if(fd<0){
printf("error/n");
return 0;
}
ioctl(fd, _IO('k',0) ); // 其中k是scull设备的类型
// 这条命令是复位scull设备中量子和量子集的大小。
printf("%d/n", ioctl(fd,_IO('k',8) ) ); // 获取默认量子集大小
ioctl(fd, _IOW('k',2,int), &qset_size ); // 更改量子集大小
printf("%d/n", ioctl(fd,_IO('k',8) ) );
close(fd);
return 0;
}
实际上,这里调用宏函数来设定request,只是为了更清晰的了解如何设定request( 也就是设定驱动程序中的 cmd),
而在应用程序中通常是直接给出其值。比如ioctl(sock, SIOCGIFNAME, &ifr)来获取接口名.
摘至: http://www.yuanma.org/data/2008/1022/article_3252.htm
OK ,用了宏之后成功点亮LED ,各个引脚都可以随心所欲地驱动了!
我的驱动控制11 个引脚,3 个LED ,VD[7:0] ,其中2 个LED 和uClinux 里面的某个功能冲突了,状态老是被覆盖,解决方法就是去掉该功能。
下面是完整的代码:
ioctl头文件:
驱动代码:
用户程序代码:
修改驱动编译配置:
1) 把led_ioctl.h和leddriver.c 丢到目录uClinux-dist/linux-2.4.x/drivers/char中
2) 修改文件
uClinux-dist/linux-2.4.x/drivers/char/Makefile
----------------------------------------------
obj-$(CONFIG_C5471_WDT) += wdt_c5471.o之后加
obj-$(CONFIG_LED_DRIVER) += leddriver.o
3) 修改文件
uClinux-dist/linux-2.4.x/drivers/char/Config.in
-----------------------------------------
if [ "$CONFIG_CPU_S3C44B0X" = "y" ]; then
bool 'Samsung S3C44B0X serial ports support' CONFIG_SERIAL_S3C44B0X之后加
bool 'LED Driver' CONFIG_LED_DRIVER
4) 修改文件
uClinux-dist/linux-2.4.x/drivers/char/mem.c
-----------------------------------------
开头的地方扎堆加
#ifdef CONFIG_LED_DRIVER
extern void led_init(void);
#endif
int __init chr_dev_init(void)之后加
#ifdef CONFIG_LED_DRIVER
led_init();
#endif
5) 继续修改文件
uClinux-dist/vendors/Samsung/44B0/Makefile
-----------------------------------------
ttypc,c,3,12 ttypd,c,3,13 ttype,c,3,14 ttypf,c,3,15/之后加
/
leddriver,c,231,0 /
修改用户程序编译配置:
1) 把use_led.c和led_ioctl.h丢入自己建立的目录uClinux-dist/user/use_led
2) 修改文件
uClinux-dist/user/Makefile
-----------------------------------------
扎堆加个下面
dir_$(CONFIG_USER_USE_LED) += use_led
3) 修改文件
uClinux-dist/config/Configure.help
-----------------------------------------
扎堆加个下面
CONFIG_USER_USE_LED
USE LED
4) 修改文件,就是在最后加上一段
uClinux-dist/config/Configure.in
-----------------------------------------
##############################
mainmenu_option next_comment
comment 'USE LED'
bool 'USE LED' CONFIG_USER_USE_LED
endmenu
###############################
编译
1) make menuconfig
要选中Kernel和User那两个选项,在kernel中的能找到“LED Driver”选项,勾上,在user中能找到“USE LED”选项,进去后把“USE LED”选上,保存退出。
2) make clean
3) make lib_only
4) make user_only
5) make romfs
6) make image
7) make
我的leddriver.c刚开始有不少问题,make会提示是多少行有问题,然后再回去改,还蛮不错的
# ls uClinux-dist/images/
romfs.img uclinux_ram.bin.gz uclinux_rom.bin
烧写
只是我的板子的地址,但基本上差不多,配置好U-BOOT的环境变量之后按照下面步骤:
1) tftp 0x0c008000 uclinux_rom.bin
2) 下载uclinux_rom.bin
3) erase 0x50000 0x1fffff
4) cp 0x0c008000 0x50000 48774
//48774根据自己载入文件的大小除以4再加2,我是121dcf/4+1的
5) save
6) reset
如果没问题,应该能看到uClinux的界面了,下面在板子中运行
1)cd /dev
2)ls
看见里面有个leddriver了吧?
3)cd /proc
4)cat devices
看见驱动列表吧?
leddriver 231也应该在里面
5)use_led,输入1所有灯亮,输入0所有灯灭
(注:以上过程部分参考网上其他教程)