说明:文章根据朱老师课堂讲解进行整理
一、通过tftp方式启动内核
在进入到驱动章节时,我们采用tftp的方式下载zimage,zimage是内核的镜像文件,有了它便能将内核启动起来。前提是开发板中已经有了uboot.bin的bootloader启动文件,且在uboot的命令行下开发板能够和虚拟机Ping通。
在启动开发板后,在倒数结束之前按下回车键进入uboot,对uboot的环境变量进行更改,此处修改的是bootcmd命令,
以前通过fastboot烧录进行启动的命令是:
set bootcmd 'movi read kernel 0x30008000;bootm 0x30008000',
修改为tftp启动的命令是
set bootcmd 'tftp 0x30008000 zImage;bootm 0x30008000',修改之后要记得save。这句话的意思是通过tftp获取zimage文件,并将zimage放置在0x30008000地址处,然后内核从0x30008000处开始启动。注意在做这步之前要先在虚拟机中下载并配置好tftp,然后编译内核得到zimage文件,并将zimage放入tftp服务器目录下(此处是自己的虚拟机做服务器),看到如下页面就说明正在通过tftp的方式启动内核
二、通过nfs挂载根文件系统
挂载之前要先配置好nfs,并通过busybox制作自己的一个根文件系统,这两步完成之后通过uboot模式下的命令行修改环境变量中的bootargs,
修改前的bootargs是
setenv bootargs 'root=/dev/nfs nfsroot=192.168.1.141:/root/rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200',
修改后的命令是
set bootargs root=/dev/nfs nfsroot=192.168.1.141:/root/porting_210/rootfs/rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200
需要说明的是nfsroot=192.168.1.141:/root/porting_210/rootfs/rootfs这句话中192.168.1.141是我自己虚拟机的ip地址,此处作为nfs的服务器地址,/root/porting_210/rootfs/rootfs这个是所需要挂载的根文件的文件夹目录,开发者根据自己的目录进行修改
三、在开发板下安装驱动
为了方便,直接对驱动的源码目录下的makefile进行修改,添加cp命令
cp:
cp *.ko /root/porting_210/rootfs/rootfs/driver_test
在根文件目录下mkdir一个driver_test目录,这样每次在虚拟机下编译完之后,再执行make cp命令便可将生成的.ko的驱动文件复制到根文件目录下,由于根文件系统又通过nfs进行了挂载,所以可以在开发板下对驱动执行操作。
查看设备号的使用情况:cat /proc/devices
先lsmod查看一下驱动,再insmod module_test.ko安装驱动(此处生成的驱动名为module_test.ko),然后再lsmod看一下开发板中的驱动信息,modinfo module_test.ko查看该驱动的详细信息包括内核版本等,卸载的指令是rmmod module_test.ko,最后可使用dmesg命令查看内核日志
创建设备文件的命令是 mknod /dev/test c 250 0
表示在根目录下的dev里创建了test的字符型设备,该设备的主设备号是250,次设备号是0
四、驱动led的例程
创建一个module_test.c的驱动源文件
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#define GPJ0DAT S5PV210_GPJ0DAT
#define GPJ0CON S5PV210_GPJ0CON
#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
char kbuf[100];
static int test_open(struct inode *inode, struct file *file){
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
printk(KERN_INFO "open led\n");
return 0;
}
static int test_close(struct inode *inode, struct file *file){
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
printk(KERN_INFO "close led\n");
return 0;
}
//驱动模块的读函数
int test_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = -1;
ret = copy_to_user(buf,kbuf,size);
if(ret == 0){
printk(KERN_INFO "chrdev read success\n");
return 0;
}
printk(KERN_INFO "chrdev read failed\n");
return -1;
}
//驱动模块的写函数
static int test_write(struct file *file,const char __user *user_buf,size_t count, loff_t *ppos)
{
int ret = -1;
ret = copy_from_user(kbuf,user_buf,count);
if(ret == 0){
printk(KERN_INFO "chrdev write success\n");
return 0;
}
printk(KERN_INFO "chrdev read failed\n");
return -1;
}
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.write = test_write,
.read = test_read,
.open = test_open,
.release = test_close,
};
int ret = -1;
// 模块安装函数
static int __init chrdev_init(void)
{
//注册函数
ret = register_chrdev(0, "module_test", &test_fops);
if (ret < 0) {
printk(KERN_ERR "module_test:"
"Registering the character device failed with %d\n",
ret);
return 0;
}
printk(KERN_INFO "chrdev_init register success,major is %d\n",ret);
//printk("<7>" "chrdev_init helloworld init\n");
//printk("<7> chrdev_init helloworld init\n");
return 0;
}
// 模块下载函数
static void __exit chrdev_exit(void)
{
//注销函数
unregister_chrdev(ret, "module_test");
printk(KERN_INFO "chrdev_exit success\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("wuqi_wf"); // 描述模块的作者
MODULE_DESCRIPTION("wuqi_module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias wuqi"); // 描述模块的别名信息
创建一个应用文件对驱动进行调用
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE "/dev/test"
char ubuf[100];
int main(){
int fd = -1;
//打开文件
fd = open(FILE,O_RDWR);
sleep(4);
if(fd < 0){
printf("open %s file fail\n",FILE);
return -1;
}
printf("file alread success open\n");
//后续添加读写接口
write(fd,"hello world",sizeof(ubuf));
read(fd,ubuf,sizeof(ubuf));
printf("读取到的驱动数据是%s\n",ubuf);
//关闭文件
close(fd);
printf("file success close");
return 0;
}
在开发板上执行insmod并mknod后,./app灯会亮四秒,并关闭
若有错误,请评论区指出