主要步骤
烧录内核及根文件系统
- 重新编译内核
(1)在模块运行之前,为了消除内核中自带的LED模块的影响,需要将自带的LED模块关掉:
make menuconfig
(2)在编写驱动之前,因为友善之臂将LED灯的驱动默认加载到内核中,所以需要先把内核自带的watch dog模块裁剪掉,要不然会出现错误:
在Device Drivers/ Watchdog Timer Support/ S3C2410 Watchdog中;
(3)保存退出之后,重新make成zImage文件。
- 烧录内核及根文件系统
在终端输入:
fastboot
sudo fastboot flash kernel zImage
sudo fastboot flash ramdisk ramdisk-u.img
sudo fastboot flash fat rootfs_qtopia_qt4.img
下载内核和根文件系统镜像后:
sudo fastboot reboot
用《从 0 开始学 Linux 驱动开发(一)》中的步骤做初步尝试
原文地址:https://paper.seebug.org/779/
- 创建新文件夹:在*/home/dy/tiny4412/kernel/linux-3.5/drivers*目录下;
mkdir hello
- 使用教程中的代码,但要Makefile中的修改路径:
KERN_DIR ?= /home/dy/tiny4412/kernel/linux-3.5
如果不修改,insmod .ko文件时会报错,这是因为编译内核的交叉编译器和内核不匹配,导致有些参数不兼容。错误如下:
- 在./hello下编译,生成hello.ko文件;
make
- 用minicom传输文件
(1)设置Filenames and paths;
minicom -s
(2)在*/tmp*下输入命令:
rx hello.ko
(3)在minicom终端中CTRL+A Z,选S,选择传输协议 xmodem ,就可以选择PC机上的hello.ko(用空格选中传输文件);
(4)传输完毕后可在*/tmp*目录下看到hello.ko。
- 加载内核模块:
insmod hello.ko
- 查看当前已经被加载的内核模块:
lsmod
- 移除模块
rmmod
驱动程序分析
- 设备号
(1)主次设备号
一个字符设备或块设备都有一个主设备号和一个次设备号。主设备号用来标识与设备文件相连的驱动程序,用来反映设备类型;次设备号被驱动程序用来辨别操作的是哪个设备,用来区分同类型的设备。
(2)分配设备号
(3)初始化并添加cdev
(4)设备注销
- 数据结构
(1)struct file
代表一个打开的文件描述符,系统中每一个打开的文件在内核中都有一个关联的struct file。它由内核在open时创建,并传递给在文件上操作的任何函数,直到最后关闭。当文件的所有实例都关闭之后,内核释放这个数据结构。
(2) struct inode
用来记录文件的物理信息。它和代表打开的file结构是不同的。一个文件可以对应多个file结构,但只有一个inode结构。inode一般作为file_operations结构中函数的参数传递过来。
(3) struct file_operations
结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。
配置WDT定时器(ledwdt.c)
-
看门狗功能
(1)当作常规时钟,可以产生中断;
(2)当看门狗定时器使用,当计数器WTCNT为0时,产生复位。 -
看门狗定时器
(1)看门狗定时器功能框图如下:
看门狗模块包括一个预比例因子放大器,一个四分频的分频器,一个16位计数器。看门狗的时钟信号源来自PCLK,为了得到宽范围的看门狗信号,PCLK先被预分频,然后再进过分频器分频。预分频比例因子和分频器的分频值,都可以由看门狗控制寄存器(WTCON)决定,预分频比例因子的范围是0~255,分频器的分频比可以是16、32、64或128。
(2)看门狗定时器时钟周期
看门狗定时器时钟周期的计算如下:
t_watchdog表示看门狗计数计时器WTCNT每减少1所用的时间,WTCNT相当于一个节拍的作用,当WTCNT=0时,如果看门狗控制寄存器WTCON[0]开启复位功能,则复位;如果看门狗控制寄存器WTCON[0]禁止复位功能,开启中断,则中断操作,并将数据寄存器WTDAT中的值重新赋值到计数寄存器WTCNT内,循环中断操作。
- 看门狗定时器相关寄存器
(1)看门狗定时器控制寄存器(WTCON)
WTCON寄存器的内容包括:用户是否启动看门狗定时器、4个分频比的选择、是否允许中断产生、是否允许复位操作等。如果用户想把看门狗定时当作一般定时器使用,应该使能中断功能,禁止看门狗定时器复位。 WTCON描述如下:
(2)看门狗定时器数据寄存器(WTDAT)
WTDAT用于指定超时时间。在看门狗把复位功能禁止并打开中断使能后,此时看门狗定时器就是一个普通的定时器,使用方法和普通定时器一样。当使用复位功能后,由于WTCNT的值减到0时,系统就会复位,所以WTCNT的值装不进看门狗计数寄存器WTCNT中。WTDAT描述如下:
(3)看门狗计数寄存器(WTCNT)
WTCNT包含看门狗定时器工作的时候,计数器的当前计数值。WTCNT描述如下:
- 在驱动程序中添加看门狗定时器的配置
(1)request_irq( )用来注册中断服务,函数原型为:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
irq:要申请的硬件中断号,对应IRQ_WDT;
handler:向系统注册的中断处理函数,对应do_irq;
irqflags:中断处理的属性,这里的IRQF_SHARED表示多个设备共享中断;
devname:设置中断名称,对应led1dog;
dev_id:在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL,这里设置为0x1111。
需要在入口函数中注册中断,回调中断处理函数。
(2)ioremap( )用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中(这里是内核空间),函数原型为:
void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)
phys_addr:要映射的起始的IO地址;
size:要映射的空间的大小;
flags:要映射的IO空间和权限有关的标志。
-
中断处理函数:实现了让LED闪烁的功能
-
释放中断调用
编写各个驱动操作函数
- read( )函数
(1)函数原型
ssize_t (*read) (struct file * filp, char __user * buffer, size_t size , loff_t * p)
filp:为进行读取信息的目标文件,对应fops
buffer:为对应放置信息的缓冲区(即用户空间内存地址),对应usr;
size:为要读取的信息长度;
p:为读的位置相对于文件开头的偏移,在读取信息后,这个指针一般都会移动,移动的值为要读取信息的长度值,对应pos。
(2)Copy_to_user( to, &from, sizeof(from))函数
To:用户空间函数(可以是数组);
From:内核空间函数(可以是数组);
sizeof(from):内核空间要传递的数组的长度。
- write( )函数
(1)函数原型
ssize_t (*write) (struct file * filp, const char __user * buffer, size_t count, loff_t * ppos)
filp:为目标文件结构体指针,对应fops;
buffer:为要写入文件的信息缓冲区,对应buf;
count:为要写入信息的长度;
ppos:为当前的偏移位置,这个值通常是用来判断写文件是否越界,对应pos。
(2)Copy_from_user(&from , to , sizeof(to) )函数
To:用户空间函数(可以是数组);
From:内核空间函数(可以是数组);
sizeof(from):内核空间要传递的数组的长度。
- 在设备操作合集中加入read和write
编写应用程序(ledwdt_demo.c)
-
建立设备文件名;
-
调用read( )函数:利用read函数读出指定LED的状态,输出状态信息;
网上的参考代码在调用read函数时,把传递的信息长度设为2,导致led3和led4无法输出状态信息。改为4后就可以成功读取信息。参考代码如下图所示:
- 调用write( )函数:利用write函数控制控制制定LED的状态。
编译驱动并传到开发板中
-
修改Makefile路径,并在最后加上ledwdt_demo的编译信息;
-
编译生成ledwdt.ko和ledwdt_demo可执行文件;
make
make ledwdt_demo
- 将文件传到开发板上
(1)打开minicom,设置文件路径:
minicom -s
选择Filenames and paths,设置Upload directory:
/home/dy/tiny4412/kernel/linux-3.5/drivers/ledwdt
(2)进入文件系统:
cd tmp/
(3)输入命令:
rx ledwdt.ko
CTRL+A Z,出现如下界面:
(4)输入S(Send files),选择xmodem,传输成功;
(5)按照相同步骤传输ledwdt_demo文件,/tmp目录下出现这两个文件:
- 运行.ko文件
(1)
insmod ledwdt.ko
LED1开始闪烁。之前在驱动代码里write( )函数中选择在内核中打印LED1闪烁的信息,但在minicom中无法停止打印,所以删去了那行代码。
(2)
lsmod
出现ledwdt
- 运行可执行文件
(1)给ledwdt_demo增加可执行权限:
chmod u+x ledwdt_demo
(2)运行ledwdt_demo:
./ledwdt_demo
- 删除.ko文件
rmmod ledwdt
不要加.ko!
参考资料
[1] https://blog.csdn.net/weixin_37771089/article/details/84988642
[2] http://blog.knownsec.com/2019/01/从-0-开始学-linux-驱动开发一/
[3] https://blog.csdn.net/liu454638324/article/details/40433529
[4] https://blog.csdn.net/muyang_ren/article/details/38090417
[5] https://blog.csdn.net/qq_21593899/article/details/51713740
[6] https://www.cnblogs.com/wenqiang/p/4781499.html
[7] https://blog.csdn.net/z961968549/article/details/78512230
[8] https://blog.csdn.net/morixinguan/article/details/50619675
[9] http://www.cnblogs.com/geneil/archive/2011/12/03/2272869.html