嵌入式linux简单程序.docx
1、demodemo.c#ifndef _KERNEL_#define _KERNEL_#endif#ifndef MODULE#define MODULE#endif#include <linux/config.h>#include <linux/module.h>/模块相关#include <linux/kernel.h>/内核相关#include <linux/fs.h>/file_operations#include <linux/types.h>/ssize_t定义文件#include <linux/init.h>/_init和_exit相关#include <linux/errno.h>#include <linux/cdev.h>#include <asm/uaccess.h>/copy_to_user()和copy_from_user()在此定义#include <asm/system.h>/*相关宏定义*/#define DEVICE_NAME"demo"/设备名称#define demo_MAJOR 88/主设备号#define demo_MINOR 0/次设备号#define ERROR -1static int MAX_BUF_LEN=1024;/数值的最大值static int WRI_LENGTH=0;/*结构体的定义*/static int demo_major=demo_MAJOR;struct demo_devstruct cdev cdev;char drv_buf1024;struct demo_dev * demo_devp;/*/*demo设备文件打开*/int demo_open(struct inode * inode,struct file *filp)filp->private_data=demo_devp;sprintf(demo_devp->drv_buf,"device open sucess!n");printk("device open sucess!n");return 0;/*/*demo设备文件关闭*/int demo_release(struct inode * inode,struct file *filp)return 0;/*/*逆序排列缓冲区数据*/static void do_write(char * drv_buf)int i;int len = WRI_LENGTH;char tmp;for(i = 0; i < len; i+,len-)tmp = drv_buflen-1;drv_buflen-1 = drv_bufi;drv_bufi = tmp;/*/static ssize_t demo_write(struct file *filp,const char *buffer, size_t count,loff_t *ppos) struct demo_dev* dev=filp->private_data;if(count > MAX_BUF_LEN)count = MAX_BUF_LEN;copy_from_user(dev->drv_buf, buffer, count);WRI_LENGTH = count;printk("user write data to drivern");do_write(dev->drv_buf);return count;/*/static ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)struct demo_dev* dev=filp->private_data;if(count > MAX_BUF_LEN)count=MAX_BUF_LEN;copy_to_user(buffer, dev->drv_buf,count);printk("user read data from drivern");return count;/*/static int demo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)printk("ioctl runingn");switch(cmd)case 1:printk("runing command 1 n");break;case 2:printk("runing command 2 n");break;default:printk("error cmd numbern");break;return 0;/*/*demo的模块加载函数*/static const struct file_operations demo_fops=.owner=THIS_MODULE,.read=demo_read,.write=demo_write,.ioctl=demo_ioctl,.open=demo_open,.release=demo_release,;/*/*demo的模块加载函数*/int _init demo_init(void)int result;int err;dev_t devno=MKDEV(demo_MAJOR,demo_MINOR); if(demo_MAJOR) result=register_chrdev_region(devno,1,"demo"); else result=alloc_chrdev_region(&devno,0,1,"demo"); demo_major=MAJOR(devno); if(result<0) return result; demo_devp=kmalloc(sizeof(struct demo_dev),GFP_KERNEL);if(!demo_devp)printk("空间申请失败n");return ERROR;printk("start demo init!n");cdev_init(&demo_devp->cdev,&demo_fops);demo_devp->cdev.owner=THIS_MODULE;err=cdev_add(&demo_devp->cdev,devno,1);if(err)printk("注册设备失败");unregister_chrdev_region(MKDEV(demo_MAJOR,demo_MINOR),1);return err;printk("demo init seccess !n");return 0;/*/*demo的模块卸载函数*/void _exit demo_exit(void)cdev_del(&demo_devp->cdev);kfree(demo_devp);unregister_chrdev_region(MKDEV(demo_MAJOR,demo_MINOR),1);MODULE_AUTHOR("Liang Baoqiang");MODULE_LICENSE("Dual BSD/GPL");module_init(demo_init);module_exit(demo_exit);Makefileobj-m += demo.o#mod1-y := mod_a.oKVERSION = $(shell uname -r)all: make -C /lib/modules/$(KVERSION)/build M=$(PWD) modulesclean:make -C /lib/modules/$(KVERSION)/build M=$(PWD) cleanrm -f *.o *.ko *.cmdtest_demo.c#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>void showbuf(char *buf);int MAX_LEN=32;int main()int fd;int i;char buf255;for(i=0; i<MAX_LEN; i+)bufi=i;fd=open("/dev/demo",O_RDWR);if(fd < 0)printf("#DEMO device open fail#n");return (-1);printf("write %d bytes data to /dev/demo n",MAX_LEN);showbuf(buf);write(fd,buf,MAX_LEN);printf("Read %d bytes data from /dev/demo n",MAX_LEN);read(fd,buf,MAX_LEN);showbuf(buf);ioctl(fd,1,NULL);ioctl(fd,4,NULL);close(fd);return 0;void showbuf(char *buf)int i,j=0;for(i=0;i<MAX_LEN;i+)if(i%4 =0)printf("n%4d: ",j+);printf("%4d ",bufi);printf("n*n");2、hello.c #include<linux/init.h>#include<linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void) printk(KERN_ALERT "Hello,init the module!"); return 0;static void hello_exit(void) printk(KERN_ALERT "Goodbye,exit the module!"); module_init(hello_init);module_exit(hello_exit);LINUX驱动版本的hello world 终于下决心好好学习LINUX内核和驱动开发了,不怕千万人耻笑,勇敢将自己的学习过程写出来:1、关于目录 /lib/modules/2.6.9-42.ELsmp/build/ 这个是内核源码所在的目录 一般使用这样的命令进入这个目录:cd /lib/modules/$(uname -r)/build/ 这个目录实际上指向了:/usr/src/kernels/2.6.9-42.EL-smp-i6862、编译驱动所使用的makefile 实际上编译驱动的时候是使用预先提供的一个makefile的,位置在:/lib/modules/$(uname -r)/build/Makefile 注意:M是大写的3、网上抄录的Linux驱动Hello world的源码:/ hello.c#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");static int hello_init(void) printk(KERN_ALERT "hello world!n"); return 0;static void hello_exit(void) printk(KERN_ALERT "goodbye!n"); module_init(hello_init);module_exit(hello_exit);4、写个makefile来编译这个驱动:(版本一,最简单的)#下面这行是文件Makefile的内容,注意M是大写的obj-m := hello.o把hello.c和Makefile保存在同一目录,然后执行:make -C /lib/modules/uname -r/build SUBDIRS=$PWD modules这样驱动就编译好了,成果是hello.ko文件。注意:makefile一定要写成Makefile,如果写成makefile就编译不过。(折腾啊,就这一步耗了N多时间)5、再写另一种Makefile:(版本二:最省事的)#以下是Makefile文件的内容obj-m := hello.oKERNEL_DIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)all: make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modulesclean: rm *.o *.ko然后执行:make就编译成功了,命令行不再加参数,很省事。注意:all: 和clean:下面的行,前面是一个TAB键6、加载驱动:执行insmod ./hello.ko屏幕上没反应。(因为我是在WINDOWS上用远程终端连上去的嘛)OK,先让时光倒流,回到加载驱动以前,先另开一个窗口,执行:tail -f /var/log/message然后在原来的窗口里执行:insmod ./hello.ko哈哈,/var/log/message文件里面看见了盼望已久的hello world!7、查看驱动:lsmod 看见 hello这个驱动在其中8、卸载驱动:rmmod hello 看见/var/log/message里显示了goodbye经过两周的摸索,终于对Linux设备驱动开发有了个初步的认识,下面对Linux设备驱动开发环境的搭建做个小结,以方便自己以后查询,同时也能给同道的初学者一点帮助。刚接触Linux设备驱动时,初学者往往连如何编译驱动程序都不懂,更别说编译进内核或加载测试了。一般都是在网上找个最简单的 helloworld驱动程序,然后严格按照网上所说的步骤编译,结果却得到一大堆见都没见过的错误,更不要说根据错误信息来解决问题了,很多人到这里就 不知道如何往下进行了。十几天前我也卡在这里很长时间,现在知道所以然了就记下来,些许对一些同道者有帮助。一个基本的Linux设备驱动开发环境由宿主机和目标机组成,宿主机就是用来做驱动开发工作的主机,目标机就是用来运行和测试设备驱动的主机,在宿 主机上需要有开发工具(gcc,gdb,make等)和linux源码(版本要对应目标机上的linux内核),而目标机上只要运行linux即可。由于 步骤有所不同,下面分为普通Linux设备驱动开发和嵌入式Linux设别驱动开发两种情况来讲述环境的搭建和驱动程序的编译:(一)普通Linux设备驱动开发普通Linux主要是区分于嵌入式Linux(一般指uClinux),在这种开发中宿主机和目标机可以是一台主机,即在本机上开发编译然后在本机 上加载运行(Linux设备驱动也可以直接编译进内核,但为了开发工作方便,一般采用动态加载的方式),当然也可以是两台主机,如果是两台主机的话,要保 证宿主机上的linux源码的版本号与目标机中的linux内核版本一致。普通Linux设备驱动开发的步骤如下:在宿主机上安装开发工具和下载linux源码(要求版本号和目标机上的linux内核版本一致)。开发工具主要有gcc、gdb、make等,这些工具在redhat或fc中默认就安装了,在debian或Ubuntu中可以通过下面这个命令安装:apt-get install build-essentiallinux源码可以通过以下几种途径获得:直接去www.kernel.org下载通过包管理工具下载源码,在debian和Ubuntu中可以通过下面这个命令下载,apt-get install linux-source-(版本号) ,下载后的文件在/usr/src目录中,解压到该目录即可将源码解压到/usr/src/目录后,进入linux-source-(版本号)目录中执行下面几个命令:make oldconfigmake preparemake scripts编写Linux驱动程序,以一个最简单的hello.c为例,hello.c的内容如下:#include "linux/init.h"#include "linux/module.h"static int hello_init(void) printk(KERN_ALERT "Hello World linux_driver_modulen"); return 0;static void hello_exit(void) printk(KERN_ALERT "Goodbey linux_driver_modulen");module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("lpj");写Makefile文件,一个示例如下,里面各项参数根据实际情况更改:#sample driver moduleobj-m := hello.oKDIR = /usr/src/linux-source-2.6.24/all: $(MAKE) -C $(KDIR) M=$(PWD).PHONY:cleanclean: rm -f *.mod.c *.mod.o *.ko *.o *.tmp_versions编译,在hello.c和Makefile所在目录下执行 make 即可,编译后在当前目录生成hello.ko文件加载并测试:加载使用insmod或modprobe命令来实现,如在当前路径执行如下代码:insmod hello.ko 或 modprobe hello注意,如果在虚拟终端加载内核的话,将看不到内核打印信息,因为内核打印信息不会输出到虚拟终端,而是输出到/proc/kmsg文件中,所以可以通过以下方式查看内核信息:cat /proc/kmsg 会一直打印,需要CtrlC手动终止dmesg 或 dmesg | tail -N ,N为一数字,表示显示最后N行卸载:使用rmmod命令卸载驱动模块,如 rmmod hello(二)嵌入式inux设备驱动开发这种开发中一般目标机为带有嵌入式处理器的开发板,而宿主机为PC,开发环境需要在宿主机上搭建,嵌入式Linux设备驱动开发的步骤如下:在宿主机上下载嵌入式Linux的源码,并安装嵌入式Linux开发工具(针对于不同的嵌入式处理器,工具也有所不同,如对应于Arm的arm-gcc系列,针对nios2处理器的nios2-cc系列)编写Linux设备驱动驱动程序,还以上面给出的hello.c为例,将该文件复制到(linux 源码目录)/drivers/(目标文件夹)中在(目标文件夹)中创建Makefile和Kconfig(菜单配置文件),内容分别如下:#makefileobj-$(CONFIG_HELLODRV) += hello.o#Kconfigmenu USER_DEVICE_DRIVERSconfig HELLODRV tristate "Hello" -help- This is a sample driver programme.endmenu注意,如果Kconfig文件中的"tristate"写成"bool",则该模块只能选为Y(编译进内核)或N(不选择),不能选为M(编译为模块,可动态加载)修改上层目录( linux内核源码目录/drivers/)中的Makefile和Kconfig文件,Makefile中加入如下语句:#makefileobj-y += (目标文件夹)(此处有多种写法,这只是其中一种)Kconfig中加入如下语句:#Kconfigsource "drivers/(目标文件夹)/Kconfig"编译内核:几个基本的命令及选择界面如下:make menuconfig 执行到这一步后,会看到下面这个界面:其中Vendor/Product.是选择处理器厂家和型号的,Kernel/Library.是配置应用程序的,按空格键或回车键可以进入选项进行配置,用上下键移动到Kernel/Library.菜单上,按空格或回车进入下面的内核配置界面:在该界面有两个Customize.选项,第一个是选择自定义配置内核,第二个是选择自定义配置应用程序,按空格键可以选择这些选项,选择后按 exit键退出,选择是否保存的时候选择“yes“,如果选择了第一个Customize.,则退出后会自动进入内核配置界面,如下图:该界面有很多选项,这里不细讲,我们要配置驱动模块,就用上下键移动到Device Drivers上,然后按回车或空格键进入,设备驱动配置界面如下图:这里就是linux-2.X/drivers/Kconfig里的内容了,下面那个绿色的V(+)表示这一页没显示完,可以用下键继续往下浏览,找到我们自己的菜单名,然后按回车或空格进入,我的模块配置界面如下:用M键使选项前的尖括号里显示M表示该模块要动态加载,也可以按y键选择直接编辑进内核,选择完后exit退出,选择yes或no的对话框通一选yes。make romfs #第一次编译内核前一定要有该步骤make加载测试:将生成的zImage文件下载到开发板,开发板上的嵌入式Linux启动后可以用insmod或modprobe加载驱动模块,测试完毕后可以通过rmmod命令卸载驱动模块