memdev驱动试验
今天开始驱动的学习,根据国嵌驱动开发视频一边学习,一边试验。国嵌视频上首先讲述字符驱动开发过程,memdev就是最好的例子。首先在看懂源码的基础上学习怎么将驱动装载到开发板。下面是memdev源码:
#ifndef _MEMDEV_H_ #define _MEMDEV_H_ #ifndef MEMDEV_MAJOR #define MEMDEV_MAJOR 260 //memdev采用静态分配设备号,不要和其它设备重复 #endif #ifndef MEMDEV_NR_DEVS #define MEMDEV_NR_DEVS 2 #endif #ifndef MEMDEV_SIZE #define MEMDEV_SIZE 4096 #endif struct mem_dev { char *data; unsigned long size; }; #endif
/********************************************* *memdev.c *********************************************/ #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include "memdev.h" static int mem_major = MEMDEV_MAJOR; module_param(mem_major,int,S_IRUGO); struct mem_dev *mem_devp; struct cdev cdev; int mem_open(struct inode *inode,struct file *filp) { struct mem_dev *dev; int num = MINOR(inode->i_rdev); if(num >=MEMDEV_NR_DEVS) return -ENODEV; dev = &mem_devp[num]; filp->private_data = dev; return 0; } int mem_release(struct inode *inode,struct file *filp) { return 0; } static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *poss) { unsigned long p = *poss; unsigned int count = size; int ret = 0; struct mem_dev *dev = filp->private_data; if(p >= MEMDEV_SIZE) return 0; if(count > MEMDEV_SIZE-p) count = MEMDEV_SIZE-p; if(copy_to_user(buf,(void*)(dev->data+p),count)) { ret = -EFAULT; } else { *poss +=count; ret = count; printk(KERN_INFO "read %d bytes from %lu/n",count,p); } return ret; } static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *poss) { unsigned long p = *poss; unsigned int count = size; int ret=0; struct mem_dev *dev = filp->private_data; if(p>=MEMDEV_SIZE) return 0; if(count>MEMDEV_SIZE-p) count = MEMDEV_SIZE-p; if(copy_from_user(dev->data+p,buf,count)) { ret = -EFAULT; } else { *poss += count; ret = count; printk(KERN_INFO "write %d bytes from %lu/n",count,p); } return ret; } static loff_t mem_llseek(struct file *filp,loff_t offset,int whence) { loff_t newpos; switch(whence) { case 0: newpos = offset; break; case 1: newpos = filp->f_pos + offset; break; case 2: newpos = MEMDEV_SIZE - 1 + offset; break; default: return -EINVAL; } if((newpos<0) || (newpos>MEMDEV_SIZE)) return -EINVAL; filp->f_pos = newpos; return newpos; } static const struct file_operations mem_fops = { .owner = THIS_MODULE, .llseek = mem_llseek, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, }; static int memdev_init(void) { int result; int i; dev_t devno = MKDEV(mem_major,0); if(mem_major) result = register_chrdev_region(devno,2,"memdev"); else { result = alloc_chrdev_region(&devno,0,2,"memdev"); mem_major = MAJOR(devno); } if(result < 0) return result; cdev_init(&cdev,&mem_fops); cdev.owner = THIS_MODULE; cdev.ops = &mem_fops; cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS); mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL); if(!mem_devp) { result = -ENOMEM; goto fail_malloc; } memset(mem_devp,0,MEMDEV_NR_DEVS * sizeof(struct mem_dev)); for(i=0;i<MEMDEV_NR_DEVS;i++) { mem_devp[i].size = MEMDEV_SIZE; mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL); memset(mem_devp[i].data,0,MEMDEV_SIZE); } return 0; fail_malloc: unregister_chrdev_region(devno,2); return result; } static void memdev_exit(void) { cdev_del(&cdev); kfree(mem_devp); unregister_chrdev_region(MKDEV(mem_major,0),2); } MODULE_AUTHOR("Zechin Liao"); MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit);
ifneq ($(KERNELRELEASE),) obj-m:=memdev.o else KERNELDIR:=/home/liao/image/linux-2.6.24 PWD:=$(shell pwd) all: make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- clean: rm -rf *.ko *.o *.mod.c *.mod.o *.symvers endif
执行make就会生成memdev.ko驱动文件,将编译好的驱动memdev.ko拷贝至开发板/lib/modules目录。
注意:驱动程序的编译依赖内核源代码,并且内核源代码(KERNELDIR指明)要与目标板运行的内核一致。
编写应用程序,对驱动程序测试
/********************************************* *memdevapp.c *********************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main() { int fd; char buf[4096]; strcpy(buf,"This is a example of charactar devices driver"); printf("buf:%s/n",buf); fd=open("/dev/memdev0",O_RDWR); if(fd == -1) { printf("open memdev failed!/n"); return -1; } write(fd,buf,sizeof(buf)); lseek(fd,0,SEEK_SET); strcpy(buf,"nothing"); read(fd,buf,sizeof(buf)); printf("buf:%s/n",buf); return 0; }
arm-linux-gcc –o memdevapp memdevapp.c编译应用程序,将其下载至开板
添加模块:
# cd /lib/modules
# insmod memdev.ko
添加设备节点:
# cd /dev
# cat /proc/devices 查看设备号
# mknod memdev0 c 260 0
运行应用程序
# ./memdevapp
以上方法采用的动态编译驱动,也可以静态方式将驱动编译进内核
1.将memdev.h和memdev.c两个驱动源文件拷贝至内核linux-2.6.24/drivers/char目录
2.修改该目录下Kconfig文件,添加如下内容
config MEMDEV_DRIVER
tristate "memdev driver"
depends on MACH_SMDK2440
default y if MACH_SMDK2440
help
this option enables support for memdev experiment
3.修改该目录下Makefile,添加如下内容
obj-$(CONFIG_MEMDEV_DRIVER) +=memdev.o
4.在make menuconfig时在字符设备中找到菜单项“memdev drinver”,选择为Y或M,编译进内核还是模块。