2-6案列分析: memdev虚拟内存设备驱动

                                                           2-6案列分析: memdev虚拟内存设备驱动

1memdev虚拟内存字符设备:在驱动中分配一片指定大小的内存空间,作为虚拟字符设备。并在驱动中提供只对该片内存的读写、控制和定位函数seek,以供用户空间的进程能通过Linux系统调用访问这片内存

实例一:

2、测试源代码:

  2.1globalmem_test.c代码

#include <stdio.h>
#include <string.h>

int main()
{
	FILE *fp0 = NULL;
	char Buf[4096];
	int result;
	
	/*初始化Buf*/
	strcpy(Buf,"Mem is char dev!");
	printf("BUF: %s\n",Buf);
	
	/*打开设备文件*/
	fp0 = fopen("/dev/globalmem","r+");/*驱动对应的文件名为globalmen,以已读的方式打开*/
	if (fp0 == NULL)
	{
		perror("Open globalmem Error!\n");
		return -1;
	}
	
	/*写入设备*/
	result = fwrite(Buf, sizeof(Buf), 1, fp0);
	if (result  == -1)
	{
		perror("fwrite Error!\n");
		return -1;
	}
	
	/*重新定位文件位置(思考没有该指令,会有何后果)*/
	fseek(fp0,0,SEEK_SET);/*定位到从文件头开始读,写完后读写指针在文件的尾部*/
	
	/*清除Buf*/
	strcpy(Buf,"Buf is NULL!");
	printf("BUF: %s\n",Buf);
	sleep(1);
	
	/*读出设备*/
	result = fread(Buf, sizeof(Buf), 1, fp0);
	if (result  == -1)
	{
		perror("fread Error!\n");
		return -1;
	}
	
	/*检测结果*/
	printf("BUF: %s\n",Buf);	
	return 0;	
}

  2.2 Makefile文件

KERNELDIR ?=/home/student/linux-2.6.32.2

all: globalmem_test 

globalmem_test : globalmem_test.c
	#arm-linux-gcc -I$(KERNELDIR) -s -Wl,-warn-common --static -o $@ $^
	arm-linux-gcc -I$(KERNELDIR) -o $@ $^

clean :
	rm globalmem_test

3、驱动源代码:

  3.1 globalmem.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 <linux/slab.h>

#define GLOBALMEM_SIZE	0x1000	/*全局内存最大4K字节*/
#define MEM_CLEAR 0x1  /*清0全局内存*/
#define GLOBALMEM_MAJOR 0    /*预设的globalmem的主设备号*/

static int globalmem_major = GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct globalmem_dev                                     
{                                                        
  struct cdev cdev; /*cdev结构体*/                       
  unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/        
};

struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalmem_open(struct inode *inode, struct file *filp)
{
  /*将设备结构体指针赋值给文件私有数据指针*/
  filp->private_data = globalmem_devp;
  return 0;
}
/*文件释放函数*/
int globalmem_release(struct inode *inode, struct file *filp)/*file结构描述一个正在打开的设备文件*/
{
  return 0;
}

/* ioctl设备控制函数 */
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)
{
  struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/

  switch (cmd)
  {
    case MEM_CLEAR:
      memset(dev->mem, 0, GLOBALMEM_SIZE);      
      printk(KERN_INFO "globalmem is set to zero\n");
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

/*读函数*/
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,
  loff_t *ppos)
{
  unsigned long p =  *ppos;  /*file结构中保存的loff_t f_pos:当前读写位置*/
  unsigned int count = size;
  int ret = 0;
  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

  /*分析和获取有效的写长度*/
  if (p >= GLOBALMEM_SIZE)
    return count ?  - ENXIO: 0;   /* return ENXIO: No such device or address */
  if (count > GLOBALMEM_SIZE - p)
    count = GLOBALMEM_SIZE - p;

  /*内核空间->用户空间*/
  if (copy_to_user(buf, (void*)(dev->mem + p), count))
  {
    ret =  - EFAULT;   /* Bad address 拷贝不成功*/ 
  }
  else/*拷贝成功*/
  {
    *ppos += count;
    ret = count;
    
    printk(KERN_INFO "read %d bytes(s) from position %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; /*获得设备结构体指针*/
  
  /*分析和获取有效的写长度*/
  if (p >= GLOBALMEM_SIZE)
    return count ?  - ENXIO: 0; //ENXIO: No such device or addres
  if (count > GLOBALMEM_SIZE - p)
    count = GLOBALMEM_SIZE - p;
    
  /*用户空间->内核空间*/
  if (copy_from_user(dev->mem + p, buf, count))
    ret =  - EFAULT;  /*写失败*/
  else/*写成功*/
  {
    *ppos += count;
    ret = count;
    
    printk(KERN_INFO "written %d bytes(s) to position %d\n", count, p);
  }

  return ret;
}

/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
  loff_t ret = 0;
  switch (orig)
  {
    case 0:   /*相对文件头开始 SEEK_SET  位置偏移 */
      if (offset < 0)
      {
        ret =  - EINVAL;     /* Invalid argument */
        break;
      }
      if ((unsigned int)offset > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos = (unsigned int)offset;   /*file结构的成员loff_t f_pos:当前读写位置*/
      ret = filp->f_pos;
      break;
    case 1:   /*相对文件当前位置偏移*/
      if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      if ((filp->f_pos + offset) < 0)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos += offset;
      ret = filp->f_pos;
      break;
    default:
      ret =  - EINVAL;
      break;
  }
  return ret;
}

/*文件操作结构体*/
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,
};

/*初始化并注册cdev*/
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
  int err, devno = MKDEV(globalmem_major, index);

  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 LED%d", err, index);
}

/*设备驱动模块加载函数*/
int globalmem_init(void)
{
  int result;
  dev_t devno = MKDEV(globalmem_major, 0);

  /* 申请设备号*/
  if (globalmem_major)
    result = register_chrdev_region(devno, 1, "globalmem");
  else  /* 动态申请设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
    globalmem_major = MAJOR(devno);
  }  
  if (result < 0)
    return result;
    
  /* 动态申请设备结构体的内存*/
  globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
  if (!globalmem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;KERNELDIR ?=/home/student/linux-2.6.32.2

all: globalmem_test 

globalmem_test : globalmem_test.c
	#arm-linux-gcc -I$(KERNELDIR) -s -Wl,-warn-common --static -o $@ $^
	arm-linux-gcc -I$(KERNELDIR) -o $@ $^

clean :
	rm globalmem_test

    goto fail_malloc;
  }
  memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
  
  globalmem_setup_cdev(globalmem_devp, 0);
  printk("globalmem driver installed!\n");
  printk("globalmem_major is:%d\n",globalmem_major);
  printk("the device name is %s\n", "globalmem");

  return 0;

  fail_malloc: unregister_chrdev_region(devno, 1);
  return result;
}

/*模块卸载函数*/
void globalmem_exit(void)
{
  cdev_del(&globalmem_devp->cdev);   /*注销cdev*/
  kfree(globalmem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
  printk("globalmem driver uninstalled!\n");
}

MODULE_AUTHOR("Song Baohua");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);

  3.2Makefile文件:

ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /your/target/source/directory/
KERNELDIR ?=/home/student/linux-2.6.32.2

PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    obj-m := globalmem.o
endif

实例二:(两个设备组成,主设备号是相同的,但他们的此设备号不同)

4、测试代码

4.1memdev_test.c代码:

#include <stdio.h>
#include <string.h>

int main()
{
	FILE *fp0 = NULL;
	char Buf[4096];
	int result;
	
	/*初始化Buf*/
	strcpy(Buf,"Mem is char dev!");
	printf("BUF: %s\n",Buf);
	
	/*打开设备文件*/
	fp0 = fopen("/dev/memdev0","r+");/*文件名为memdev0 其中0为此设备号*/
	if (fp0 == NULL)
	{
		perror("Open Memdev0 Error!\n");ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /your/target/source/directory/
KERNELDIR ?=/home/student/linux-2.6.32.2

PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    obj-m := globalmem.o
endif

		return -1;
	}
	
	/*写入设备*/
	result = fwrite(Buf, sizeof(Buf), 1, fp0);
	if (result  == -1)
	{
		perror("fwrite Error!\n");
		return -1;
	}
	
	/*重新定位文件位置(思考没有该指令,会有何后果)*/
	fseek(fp0,0,SEEK_SET);
	
	/*清除Buf*/
	strcpy(Buf,"Buf is NULL!");
	printf("BUF: %s\n",Buf);
	
	sleep(1);
	/*读出设备*/
	result = fread(Buf, sizeof(Buf), 1, fp0);
	if (result  == -1)
	{
		perror("fread Error!\n");
		return -1;
	}
	
	/*检测结果*/
	printf("BUF: %s\n",Buf);
	return 0;	
}

4.2Makefile文件:

KERNELDIR ?=/home/student/linux-2.6.32.2
all: memdev_test 

memdev_test : memdev_test.c
	#arm-linux-gcc -I$(KERNELDIR) -s -Wl,-warn-common --static -o $@ $^
	arm-linux-gcc -I$(KERNELDIR) -o $@ $^

clean :
	rm memdev_test

5、驱动代码:

5.1头文件memdev.h代码

#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

/*mem设备描述结构体*/
struct mem_dev            /*这里的mem_devp结构设计的挺好留意内存分配问题*/                          
{                                                        
  char *data;                      
  unsigned long size;       
};

#endif /* _MEMDEV_H_ */

5.2驱动代码: 

#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>

#include "memdev.h"

static mem_major = MEMDEV_MAJOR;

module_param(mem_major, int, S_IRUGO);

struct mem_dev *mem_devp; /*设备?ifndef _MEMDEV_H_
#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

/*mem设备描述结构体*/
struct mem_dev            /*这里的mem_devp结构设计的挺好留意内存分配问题*/                          
{                                                        
  char *data;                      
  unsigned long size;       
};

#endif /* _MEMDEV_H_ */
峁固逯刚?/

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;  /*传递的是mem_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 *ppos)
{
  unsigned long p =  *ppos;
  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
  {
    *ppos += count;
    ret = count;
    
    printk(KERN_INFO "read %d bytes(s) from postion  %d\n", count, p);
  }

  return ret;
}

/*写函数*/
static ssize_t mem_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 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
  {
    *ppos += count;
    ret = count;
    
    printk(KERN_INFO "written %d bytes(s) to postion %d\n", count, p);
  }

  return ret;
}

/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{ 
    loff_t newpos;

    switch(whence) {
      case 0: /* SEEK_SET */
        newpos = offset;
        break;

      case 1: /* SEEK_CUR */
        newpos = filp->f_pos + offset;
        break;

      case 2: /* SEEK_END */
        newpos = MEMDEV_SIZE -1 + offset;
        break;

      default: /* can't happen */
        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, MEMDEV_NR_DEVS, "memdev");
  else  /* 动态分配设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, MEMDEV_NR_DEVS, "memdev");
    mem_major = MAJOR(devno);
  }  
  
  if (result < 0)
    return result;

  /*初始化cdev结构*/
  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); /*为mem_devp结构对象分配内存*/
  if (!mem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(mem_devp, 0, sizeof(struct mem_dev));
  
  /*为设备分配内存*/
  for (i=0; i < MEMDEV_NR_DEVS; i++) /*这里的mem_devp结构设计的挺好,注意指针*data的内存分配*/
  {
        mem_devp[i].size = MEMDEV_SIZE;
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devp[i].data, 0, MEMDEV_SIZE);
  }
  printk("memdev driver installed, with major %d\n", mem_major);
  printk("the device name is %s\n","memdev");
  return 0;

  fail_malloc: 
  unregister_chrdev_region(devno, 1);
  
  return result;
}

/*模块卸载函数*/
static void memdev_exit(void)
{
  int i;
  cdev_del(&cdev);   /*注销设备*/
  for (i=0; i < MEMDEV_NR_DEVS; i++) 
  	kfree(mem_devp[i].data);
  	
  kfree(mem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
  printk("memdev driver uninstalled\n");
}

MODULE_AUTHOR("Hanson He");
MODULE_LICENSE("GPL");

module_init(memdev_init);
module_exit(memdev_exit);

       注意:这里只有int mem_open(struct inode *inode, struct file *filp)和int mem_release(struct inode *inode, struct file *filp)两个函数中是带 struct inode ,而mem_open()中的才对其他有作用的。下面是取得对那个设备进行操作,然后是通过struct file结构中的成员private_data进行传递而其他ioctl成员中的函数都有带struct file参数,这样就很好传递了men_dev结构,便于以后进行各种操作。

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;  /*传递的是mem_dev结构数组中其中一个的地址*/

5.3Makefile文件:

ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /your/target/source/directory/
KERNELDIR ?=/home/student/linux-2.6.32.2
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    obj-m := memdev.o#include <linux/module.h>

演示过程:

 

 

 

声明:本文非原创,整理自申嵌

 

 

 

 

 


 

  

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值