nanopi s2自带系统为3.4.39,处理器为s5p4418,自带gcc 版本是4.9.2。系统为Debin 4.9.2-10
(一)根据wiki上的说明,交叉编译器需要用Friendlyarm在git库中的arm-cortexa9-linux-gnuebihf-4.9.3.tar.xz,但下载下来后,发现centos系统不能识别xz格式的归档文件,就下载xz-4.999.9beta.tar.bz2文件解压后利用xz命令解压交叉编译器的压缩包,得到arm-cortexa9-linux-gnuebihf-4.9.3.tar文件,然后tar -xvf arm-cortexa9-linux-gnuebihf-4.9.3.tar -C
/opt/FriendlyARM/toolschain, 然后将编译器的路径加入到PATH中,vim ~/.bash_profile 最后加入export PATH=/opt/FriendlyARM/toolschain/4.9.3/bin:$PATH,然后#source ~/.bash_profile让环境变量起作用(当然也可以修改其他文件设置环境变量),利用echo $PATH 命令查看环境变量是否包含了…4.9.3/bin,但此时用file 命令发现其中的程序都是64位的,不能在我的32位的centos上运行。又重新在网上下载了gcc-linaro-arm-linux-gnueabihf-4.9-2014.07_linux.tar.xz,关键是要是gnueabihf格式的,中间的命名没有关系,然后解压设置环境变量。至此,我的centos系统上同时安装了4.5.3和4.9.1两个版本的交叉编译器,查看各自的bin文件夹4.5.3中arm-linux-gcc 实际上是arm-none-linux-gnueabi-gcc的链接文件;4.9.1中没有arm-linux-gcc,只有arm-linux-gnueabihf-gcc,它是arm-linux-gnueabihf-gcc-4.9.1的链接文件。这样就不存在两个版本的冲突,arm-linux-gcc对应4.5.3,arm-linux-gnueabihf-gcc对应4.9.1。这样交叉编译器搞定。
(二)在https://github.com/friendlyarm/linux-3.4.y.git的nanopi2-lollipop-mrl分支上下载内核源代码linux-3.4.y-nanopi2-lollipop-mr1.zip,解压后的文件夹linux-3.4.y-nanopi2-lollipop-mr1随便放在自己方便的文件夹中开始编译内核,Friendlyarm公司已经针对nanopi s2的相关硬件把系统进行了移植和配置,默认配置在./arch/arm/configs/中的nanopi2_linux_defconfig文件,cp ./arch/arm/configs/nanopi2_linux_defconfig ./.config,将默认配置设置为系统主目前下的配置,这时再打开make menuconfig时其中的选项就都选好了,如system type->ARM system type选项选定的是SLsiAP S5P4418D/Q,platform board类型已选定为NANOPI2…。然后需要设置主目录inux-3.4.y-nanopi2-lollipop-mr1下的Makefile文件,交叉编译改为CROSS_COMPILE ?= arm-linux-gnueabihf-,保存后,此时直接make zImage就可以编译成功了。
(三)编制驱动程序源文件drv_frame_misc.c(采用的是杂项设备注册方法),放在./drivers/char/目录中,修改该目录中的Kconfig文件,这个文件不像tiny6410中的,已经有了类似的CONFIG项,有明确的depends on CPU_S3C6410项可以借鉴。我也不知道在nanopi s2 中应该depends on什么,查看了主目录中的.config文件# CPU feature项中有CONFIG_CPU_S5P4418_SMP_ISR=y项,那就让我的驱动依赖于CPU_S5P4418_SMP_ISR的配置吧,为我的驱动增加如下内容(default状态为m,就不需要再去menuconfig中点选了):
然后配置./drivers/char/目录中的makefile文件,在文件尾部增加obj-$(CONFIG_CUMTZD_DRIVER) +=drv_frame_misc.o,保存后就可以make modules编译驱动了。
(四)驱动编译成功后,得到drv_frame_misc.ko,可以通过modinfo drv_frame_misc.ko命令查看其VERMAGIC 版本号,通过scureCRT rz加载到nanopi s2开发板上,insmod drv_frame_misc.ko命令得到错误结果如下
但是没有细节,通过命令dmesg |tail,显示上述错误的细节如下:
即版本号不对应。这时得返回虚拟机,修改linux-3.4.y-nanopi2-lollipop-mr1/include/linux/vermagic.h部分如下:
修改linux-3.4.y-nanopi2-lollipop-mr1/makefile开头如下:
重新make zImage、make modules出来的驱动就可以在nanopi开发板上insmod了。在虚拟机上arm-linux-gnueabihf-gcc交叉编译的测试程序也在开发板能正确执行。搞定!
驱动程序drv_frame_misc.c源码:
ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/poll.h> /* COPY_TO_USER */ #include <linux/ioctl.h> #include <linux/miscdevice.h> #define DEVICE_NAME "drv_f" static char drv_buf[8]; static ssize_t demo_write(struct file *filp,const char *buffer, size_t count, loff_t *ppos) { count = copy_from_user(drv_buf , buffer, 1); drv_buf[0]++; printk("user write data to driver\n"); return count; } static ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) { count = copy_to_user(buffer, (const void*)drv_buf,1); printk("user read data from driver\n"); return count; } static long demo_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { printk("ioctl runing\n"); switch(cmd){ case 1:printk("<0>""runing command 1 \n");break; case 2:printk("<0>""runing command 2 \n");break; default: printk("<0>""error cmd number\n");break; } return 0; } static int demo_open(struct inode *inode, struct file *file) { printk("device open sucess!\n"); return 0; } static int demo_release(struct inode *inode, struct file *filp) { printk("device release\n"); return 0; } static struct file_operations demo_fops = { .write= demo_write, .read= demo_read, .unlocked_ioctl=demo_ioctl, .open= demo_open, .release= demo_release, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = "drv_fam", .fops = &demo_fops, }; static int __init demo_init(void) { int result; result = misc_register(&misc); if (result < 0) { printk("<0>""demo init error\n"); return result; } printk(DEVICE_NAME " initialized\n"); return 0; } static void __exit demo_exit(void) { misc_deregister(&misc); printk(DEVICE_NAME " unloaded\n"); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL");测试程序源码: //================drv_f_test.c==================// #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> int main() { int fd; char buf=5; fd=open("/dev/drv_fam",O_RDWR); if(fd < 0){ printf("####DEMO device open fail####\n"); return (-1);
} printf("write to driver ,buf=%d\n",buf); write(fd,&buf,1); read(fd,&buf,1); printf("read from driver,buf=%d\n",buf); sleep(3); ioctl(fd,1,NULL); ioctl(fd,4,NULL); close(fd); return 0; }