VMware-workstation-7-english
Linux内核版本:
Linux ubuntu 2.6.35-22-generic #33-Ubuntu SMP Sun Sep 19 20:34:50 UTC 2010 i686 GNU/Linux
附:
1、查看内核版本命令:
1) cat /proc/version
2) uname -a
3) uname -r
2、查看发行版本命令
1) lsb_release -a
2) 用命令找到/etc目录下的issue文件release文
驱动的大致理解:介于硬件和操作系统之间的一层软件接口,按照操作系统规范要求,写成,Linux下主要就是把对硬件的操作封装成对文件的操作,对硬件的操作也就是高低电平的设置读取,也就是读写01。
源码,主要是照例子编写:
- #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 <linux/slab.h>
- #define GLOBALMEM_SIZE 0x1000 /*Global memory size:4kb*/
- #define MEM_CLEAR 0x1 /*Global memory clear 0*/
- #define GLOBALMEM_MAJOR 250 /*Suggested globalmem major Number*/
- #define _user
- static globalmem_major = GLOBALMEM_MAJOR;
- /*cdev_init(),the globalmem's cdev linked file_operations struct*/
- /*struct of device globalmem_dev*/
- struct globalmem_dev
- {
- struct cdev cdev; /*cdev struct */
- unsigned char mem[GLOBALMEM_SIZE]; /*global memory */
- };
- struct globalmem_dev *globalmem_devp; /*the pointer of device struct*/
- /* the file open function */
- int globalmem_open(struct inode *inode,struct file *filp)
- {
- /*put the struct of device date to file private data pointer */
- filp->private_data= globalmem_devp;
- return 0;
- }
- /* the function of release file */
- int globalmem_release(struct inode *inode,struct file *filp)
- {
- return 0;
- }
- static ssize_t globalmem_read(struct file *filp,char _user *buf,size_t size,loff_t *ppos)
- {
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret =0;
- struct globalmem_dev *dev = filp->private_data;/*get the device pointer*/
- /*analyse and get usefull read length*/
- if(p >= GLOBALMEM_SIZE) //offset position over boundary*/
- {
- return count ? - ENXIO:0;
- }
- if(count >GLOBALMEM_SIZE -p) //to read word number too long
- {
- count =GLOBALMEM_SIZE -p;
- }
- /*kernel space -> user space*/
- if(copy_to_user(buf,(void*)(dev->mem +p),count))
- {
- ret = -EFAULT;
- }
- else
- {
- *ppos += count;
- ret = count;
- printk(KERN_INFO"read %d bytes(s) from %d \n",count,p);
- }
- return ret;
- }
- static ssize_t globalmem_write(struct file *filp,const char _user *buf,size_t size, loff_t *ppos)
- {
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret =0;
- struct globalmem_dev *dev = filp->private_data;/*get device pointer */
- /*analyse and get valid writing length */
- if(p >= GLOBALMEM_SIZE) // to write offset position over boundry*/
- {
- return count? -ENXIO:0;
- }
- /*user space -> kernel space */
- if(copy_from_user(dev->mem +p,buf,count))
- {
- ret = -EFAULT;
- }
- else
- {
- *ppos += count;
- ret = count;
-
- printk(KERN_INFO "written %d bytes(s) from %d \n",count,p);
- }
- return ret;
- }
- static loff_t globalmem_llseek(struct file *filp,loff_t offset, int orig)
- {
- loff_t ret;
- switch (orig)
- {
- case 0: /*offset from the file head*/
- if(offset < 0)
- {
- ret = - EINVAL;
- break;
- }
-
- if((unsigned int)offset>GLOBALMEM_SIZE) // offset overstep the boundary
- {
- ret = -EINVAL;
- break;
- }
- filp->f_pos = (unsigned int)offset;
- ret =filp->f_pos;
- break;
-
- case 1: /* offset from the current position */
- if((filp->f_pos +offset) >GLOBALMEM_SIZE) //offset overstep the boundary
- {
- ret = -EINVAL;
- break;
- }
- if((filp->f_pos +offset)<0)
- {
- ret = -EINVAL;
- break;
- }
- filp->f_pos += offset;
- ret = filp->f_pos;
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
- }
- static int globalmem_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
- {
- struct globalmem_dev *dev = filp->private_data;/* get the struct of device pointer */
- switch(cmd)
- {
- case MEM_CLEAR: //clear global meme
- memset(dev->mem,0,GLOBALMEM_SIZE);
- printk(KERN_INFO "globalmem is set to zero \n");
- break;
- default:
- return - EINVAL; //other order not support
- }
- return 0;
- }
- static const struct file_operations globalmem_fops=
- {
- .owner = THIS_MODULE,
- .llseek = globalmem_llseek,
- .read = globalmem_read,
- .write = globalmem_write,
- .ioctl = globalmem_ioctl,
- .open = globalmem_open,
- .release= globalmem_release,
- };
- /*init and add cdev struct*/
- static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
- {
- int err,devno = MKDEV(globalmem_major,0);
- cdev_init(&dev->cdev,&globalmem_fops);
- dev->cdev.owner = THIS_MODULE;
- dev->cdev.ops = &globalmem_fops;
- err = cdev_add(&dev->cdev,devno,1);
- if(err)
- {
- printk(KERN_NOTICE"Error %d adding globalmem",err);
- }
- }
- /* the function of globalmem driver module insmod */
- int globalmem_init(void)
- {
- int result;
- dev_t devno = MKDEV(globalmem_major,0);
-
- /*apply for char device driver region */
- if(globalmem_major)
- {
- result = register_chrdev_region(devno,1,"globalmem");
- }else{ /*dynamic get major device number*/
- result = alloc_chrdev_region(&devno,0,1,"globalmem");
- globalmem_major = MAJOR(devno);
- }
- if (result < 0)
- {
- return result;
- }
- /*dynamic apply for memery of the device struct */
- globalmem_devp = kmalloc(sizeof (struct globalmem_dev),GFP_KERNEL);
- if(!globalmem_devp)/* apply failed*/
- {
- result = -ENOMEM;
- goto fail_malloc;
- }
- memset(globalmem_devp,0,sizeof(struct globalmem_dev));
- globalmem_setup_cdev(globalmem_devp,0);
- return 0;
- fail_malloc:unregister_chrdev_region(devno,1);
- return result;
- }
- /*rsmod module function*/
- void globalmem_exit(void)
- {
- cdev_del(&globalmem_devp->cdev); /* unregister cdev*/
- kfree(globalmem_devp); /* release device struct memery*/
- unregister_chrdev_region(MKDEV(globalmem_major,0),1);/* release device number*/
- }
- MODULE_AUTHOR("Learner gsw");
- MODULE_LICENSE("Dual BSD/GPL");
- module_param(globalmem_major,int,S_IRUGO);
- module_init(globalmem_init);
- module_exit(globalmem_exit);
Makefile:
- obj-m = globalmem_char.o
- KVERSION = $(shell uname -r)
- all:
- make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
- clean:
- make -C /lib/modules/$(KVERSION)build M=$(PWD) clean
注:对应的.c 文件应为globalmem_char.c
若报错:implicit declaration of function ‘kmalloc’
在网上找了一下,是缺少了一个头文件
#include
驱动在用户空间的验证方法,
1 简单的手动验证:
在生成的.ko 目录下,
1)加载驱动:
insmod globalmem_char.ko
若报错:insmod: error inserting './globalmem.ko': -1 Device or resource busy的问题是因为设备号冲突了,用cat /proc/devices查看设备号。 .c文件里面的宏定义那里改为其他未占用的值。
2)lsmod 查看驱动是否加载:
3)cat /proc/devices 命令查看,主设备号为250的“globalmem_char”字符设备驱动,
3)创建 /dev/globalmem 设备节点: mknod /dev/globalmem c 250 0 命令,
4)并通过 echo 'hello world' > /dev/globalmem 命令和 cat /dev/globalmem 命
令分别验证设备的写和读.
cat 报错:
ANSWER:
<1>你把globalmem_read函数中的if (p >= GLOBALMEM_SIZE)改为if (p > GLOBALMEM_SIZE)就好了。具体原因是一次调用cat会读两次,每次读取4096个字节,此时文件读指针ppos就是4096了,在第二次读的时候,到if (p >= GLOBALMEM_SIZE)这里条件为真,globalmem_read就返回ENXIO错误了,所以才会出现No such device or address"错误。不知道在宋老师的机子上为什么就是正确的?
<2> 宋宝华 :返回错误值才是正确的,因为读的位置已经越界,所以要返回错误。该返回错误的时候返回正确,那就是错误。改成if (p > GLOBALMEM_SIZE)是错误的。
<3> 根据上面两点可以小结书上是没有错误的。输出的错误信息是正常的debug信息,千不该万不该就是cat调用了read两次。
2.c编程用户空间验证,待续:
工作较忙,抽点时间续上,注释的英文,由于水平有限,会有语法错误,尽量使用专业词汇。
c文件:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <fcntl.h>
- int main()
- {
- char string[40],reads[40];
- int length,ret;
- system("rmmod /dev/globalmem_char");
- system("rm /dev/globalmem");
- system("insmod /home/gsw/DriverLearn/globalMemory/globalmem_char.ko");
- system("mknod /dev/globalmem c 250 0");
- int fd = open("/dev/globalmem",O_RDWR);
- if(fd<0){
- printf("Open error!\n");
- return -1;
- }
- strcpy(string,"globalTest_driver Learner");
- length = strlen(string);
- if((ret=write(fd,string,length))!=length){
- printf("Write failed!\n");
- return -1;
- }
- lseek(fd,0,SEEK_SET);
- memset(reads,0,sizeof(reads));
- ret = read(fd,reads,length);
- printf("The read is:%s\n",reads);
- close(fd);
- }
system("rmmod /dev/globalmem_char");system("rm /dev/globalmem");
这两句是为了使下面的两句能够执行成功。
system("insmod /home/gsw/DriverLearn/globalMemory/globalmem_char.ko");
system("mknod /dev/globalmem c 250 0");
系统运行后一般只用这两句加载ko,建立节点就好了,通常都是在初始化相关的shell脚本里面写的。
对应的Makefile 文件:
- #define command variant
- CC = gcc
- AR = ar
- RM = rm
- CP = cp
- #set parametric variation
- INCLUDES =
- CFLAGS += -c $(INCLUDES)
- LKFLAGS +=
- #define local variable
- LIBS =
- OBJS = gloBalmemeTest.o
- TARGET = gloBalmemTest
- INSTALLDIR = ./
- all:$(TARGET)
- $(TARGET):$(OBJS)$(LIBS)
- $(CC) $(LKFLAGS) $(OBJS) $(LIBS) -o $@
- %.o:%.c
- $(CC) $(CFLAGS) $< -o $@
- install:
- # $(CP)$(TARGET)$(INSTALLDIR)
- clean:$(SUBDIR_CLEAN)
- -$(RM) *.o $(TARGET)
- $(SUBDIR_CLEAN):
- # $(RM) -C mylib clean
程序运行截图:
至此,简单的字符驱动模块已经基本成形,但需要学习的东西依然很多。
阅读(2531) | 评论(0) | 转发(0) |