busybox文件系统与简单驱动学习(2)-虚拟硬盘驱动实战篇

一、Virtualdisk虚拟文件系统

1、Virtualdisk虚拟文件系统介绍

Virtualdisk虚拟文件系统为一虚拟磁盘设备,在这个设备中分配了8K的连续内存空间,并定义了两个端口数据(port1和port2)。驱动程序可以对设备进行读写、控制和定位操作,用户空间的程序可以通过Linux系统调用访问virtualdisk设备中的数据。
参考《Linux驱动开发入门与实战》第二版这本书的虚拟文件教程,并在自己硬件开发平台讯为iTOP4412上进行实际验证,期间对遇到的问题进行记录与修改,最终在该基础上实现了驱动加载与使用。

2、开发环境

电脑端:window10家庭中文版,虚拟机VMware12,Ubuntu14.04,交叉编译环境使用讯为自带 arm-none-linux-gnueabi-交叉编译环境。
板子端:u-boot、内核使用讯为自带,文件系统使用busybox自建文件系统,具体创建在上一篇《busybox文件系统与简单驱动学习(1)》(https://blog.csdn.net/yongwenn163/article/details/80024833)已经介绍。
硬件环境:使用讯为精英版POP开发板。

3、参考代码出处

清华大学出版社网站www.tup.com.cn,搜索“Linux驱动开发入门与实战”关键词进入进行下载。

二、编译中问题解决

1、makefile文件修改

将makefile文件自带路径修改为目前自己itop412内核所在路径,确保内核被成功编译过,我的路径为

    KERNELDIR ?= /home/ntu/iTOP4412/iTop4412_Kernel_3.0

2、编译指令

make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-

(1)问题:没有定义“devno”变量报错
解决:查看驱动代码中涉及“devno”的部分发现

static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minor)
{
  int err;
  devno = MKDEV(VirtualDisk_major, minor);

int VirtualDisk_init(void)
{
  int result;
  dev_t devno = MKDEV(VirtualDisk_major, 0);

中都使用了“devno”变量,可能作者使用的内核系统版本不同的原因,在“VirtualDisk_setup_cdev”函数中直接使用变量是正确的,但在我自己的版本编译中出现问题,该指令意思为获取驱动设备号,经过分析最终只需要在“VirtualDisk_setup_cdev”函数中加上变量devno定义即可,修改后如下所示:

static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minor)
{
  int err;
  dev_t devno = MKDEV(VirtualDisk_major, minor);

(2)问题:implicit declaration of function ‘kmalloc’
解决:缺少头文件,添加即可,参考:https://blog.csdn.net/tiany524/article/details/6358771

#include <linux/slab.h>

(3)问题:error: unknown field ‘ioctl’ specified in initializer
解决:该问题原因出在内核版本变化,ioctl定义有变化,细节不再深究。参考:https://blog.csdn.net/u012210613/article/details/50478962
ioctl函数第一个参数删除,删除后如下所示:

static int VirtualDisk_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

然后

.ioctl = VirtualDisk_ioctl,

修改为

.unlocked_ioctl = VirtualDisk_ioctl,

三、加载使用过程

1、手动加载

(1)将生成的ko文件通过nfs共享到板子之后,修改权限为可执行,然后insmod VirtualDisk.ko加载驱动进入内核,通过lsmod指令可以查阅到驱动已经进入内核,但在/dev文件下却无法看到。
(2)通过查阅资料,要想让驱动可以识别需要手动加载或者自动加载进去到/dev文件下,先讲手动模式。
(3)下面指令即可手动加载成功,其中 c 代表字符设备,200为主设备号,0为次设备号,参考https://blog.csdn.net/u011202336/article/details/9172109

mknod  /dev/VirtualDisk c 200 0

2、自动加载

(1)自动加载需要在驱动中利用udev(mdev)来实现设备节点的自动创建。参考http://blog.sina.com.cn/s/blog_6405313801019xv9.html
https://blog.csdn.net/u013937069/article/details/51318610
(2)驱动代码开头定义类结构体

static struct class* Virtualdisk_class = NULL;  
static struct device* Virtualdisk_device = NULL; 

(3)VirtualDisk_init函数中添加类与设备创建

  Virtualdisk_class = class_create(THIS_MODULE,"VirtualDisk");  
  if(IS_ERR(Virtualdisk_class))
  {
    printk("failed to create virtualdisk module class.\n");
    unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
    return -1;
  }
  Virtualdisk_device = device_create(Virtualdisk_class,NULL,MKDEV(VirtualDisk_major, 0),NULL,"VirtualDisk");
  if(IS_ERR(Virtualdisk_device))
  {
    printk("failed to create virtualdisk module device.\n");
    unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
    return -1;
  }

(4)VirtualDisk_exit设备卸载

  device_destroy(Virtualdisk_class,MKDEV(VirtualDisk_major, 0));
  class_destroy(Virtualdisk_class);

(5)编译问题:implicit declaration of function ‘class_create’ implicit declaration of function ‘class_device_
解决:添加头文件

#include <linux/device.h>

(6)然后继续insmod、ls /dev就可以成功自动加载设备到/dev文件下,可以进行使用。

(7)执行结果:./VirtualDiskTest

[  749.210073] written 27 bytes(s) from 0
[  749.212376] read 1024 bytes(s) from 0
[  749.216144] read 1024 bytes(s) from 4
one two three four five six
two three four five six

四、所有代码

上干货,包括驱动代码、makefile、测试代码

1、VirualDisk.c

/*======================================================================
    A VirtualDisk driver as an example of char device drivers  

======================================================================*/
#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 <linux/device.h> 

#define VIRTUALDISK_SIZE    0x2000  /*È«ŸÖÄÚŽæ×îŽó8K×ÖœÚ*/
#define MEM_CLEAR 0x1  /*È«ŸÖÄÚŽæÇåÁã*/
#define PORT1_SET 0x2  /*œ«port1¶Ë¿ÚÇåÁã*/
#define PORT2_SET 0x3  /*œ«port2¶Ë¿ÚÇåÁã*/
/*
#define VirtualDisk_MAGIC 100
#define MEM_CLEAR _IO(VirtualDisk_MAGIC,0)
#define PORT1_SET _IO(VirtualDisk_MAGIC,1)
#define PORT2_SET _IO(VirtualDisk_MAGIC,2)
*/
#define VIRTUALDISK_MAJOR 200    /*Ô€ÉèµÄVirtualDiskµÄÖ÷É豞ºÅΪ200*/

static int VirtualDisk_major = VIRTUALDISK_MAJOR;
/*VirtualDiskÉ豞œá¹¹Ìå*/
struct VirtualDisk                                     
{                                                        
  struct cdev cdev; /*cdevœá¹¹Ìå*/                       
  unsigned char mem[VIRTUALDISK_SIZE]; /*È«ŸÖÄÚŽæ8K*/
  int port1; /*Áœžö²»Í¬ÀàÐ͵Ķ˿Ú*/
  long port2;
  long count;  /*ŒÇÂŒÉ豞Ŀǰ±»¶àÉÙÉ豞Žò¿ª*/
};

static struct class* Virtualdisk_class = NULL;  

static struct device* Virtualdisk_device = NULL; 
struct VirtualDisk *Virtualdisk_devp; /*É豞œá¹¹ÌåÖžÕë*/
/*ÎÄŒþŽò¿ªº¯Êý*/
int VirtualDisk_open(struct inode *inode, struct file *filp)
{
  /*œ«É豞œá¹¹ÌåÖžÕëž³ÖµžøÎÄŒþËœÓÐÊýŸÝÖžÕë*/
  filp->private_data = Virtualdisk_devp;
  struct VirtualDisk *devp = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
  devp->count++;/*ÔöŒÓÉ豞Žò¿ªŽÎÊý*/
  return 0;
}
/*ÎÄŒþÊͷź¯Êý*/
int VirtualDisk_release(struct inode *inode, struct file *filp)
{
  struct VirtualDisk *devp = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
  devp->count--;/*ŒõÉÙÉ豞Žò¿ªŽÎÊý*/
  return 0;
}

/* ioctlÉ豞¿ØÖƺ¯Êý */
//static int VirtualDisk_ioctl(struct inode *inodep, struct file
static int VirtualDisk_ioctl(struct file
 *filp, unsigned
  int cmd, unsigned long arg)
{
  struct VirtualDisk *devp = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/

  switch (cmd)
  {
    case MEM_CLEAR:/*É豞ÄÚŽæÇåÁã*/
      memset(devp->mem, 0, VIRTUALDISK_SIZE);      
      printk(KERN_INFO "VirtualDisk is set to zero\n");
      break;
    case PORT1_SET:/*œ«¶Ë¿Ú1ÖÃ0*/
      devp->port1=0;
      break;
    case PORT2_SET:/*œ«¶Ë¿Ú2ÖÃ0*/
      devp->port2=0;
      break;
    default:
      return  - EINVAL;
  }
  return 0;
}

/*¶Áº¯Êý*/
static ssize_t VirtualDisk_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 VirtualDisk *devp = filp->private_data; /*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/

  /*·ÖÎöºÍ»ñÈ¡ÓÐЧµÄ¶Á³€¶È*/
  if (p >= VIRTUALDISK_SIZE)  /*Òª¶ÁÈ¡µÄÆ«ÒÆŽóÓÚÉ豞µÄÄÚŽæ¿ÕŒä*/
    return count ?  - ENXIO: 0;/*¶ÁÈ¡µØÖ·ŽíÎó*/
  if (count > VIRTUALDISK_SIZE - p)/*Òª¶ÁÈ¡µÄ×֜ڎóÓÚÉ豞µÄÄÚŽæ¿ÕŒä*/
    count = VIRTUALDISK_SIZE - p;/*œ«Òª¶ÁÈ¡µÄ×ÖœÚÊýÉèΪʣÓàµÄ×ÖœÚÊý*/
  /*Äں˿Ռä->Óû§¿ÕŒäœ»»»ÊýŸÝ*/
  if (copy_to_user(buf, (void*)(devp->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 VirtualDisk_write(struct file *filp, const char __user *buf,
  size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos; /*ŒÇÂŒÎÄŒþÖžÕëÆ«ÒÆλÖÃ*/
  int ret = 0;  /*·µ»ØÖµ*/
  unsigned int count = size;/*ŒÇÂŒÐèҪЎÈëµÄ×ÖœÚÊý*/
  struct VirtualDisk *devp = filp->private_data; /*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/

  /*·ÖÎöºÍ»ñÈ¡ÓÐЧµÄÐŽ³€¶È*/
  if (p >= VIRTUALDISK_SIZE)/*ҪЎÈëµÄÆ«ÒÆŽóÓÚÉ豞µÄÄÚŽæ¿ÕŒä*/
    return count ?  - ENXIO: 0;/*ÐŽÈëµØÖ·ŽíÎó*/
  if (count > VIRTUALDISK_SIZE - p)/*ҪЎÈëµÄ×֜ڎóÓÚÉ豞µÄÄÚŽæ¿ÕŒä*/
    count = VIRTUALDISK_SIZE - p;/*œ«ÒªÐŽÈëµÄ×ÖœÚÊýÉèΪʣÓàµÄ×ÖœÚÊý*/

  /*Óû§¿ÕŒä->Äں˿Ռä*/
  if (copy_from_user(devp->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;
}

/* seekÎÄŒþ¶šÎ»º¯Êý */
static loff_t VirtualDisk_llseek(struct file *filp, loff_t offset, int orig)
{
  loff_t ret = 0;/*·µ»ØµÄλÖÃÆ«ÒÆ*/
  switch (orig)
  {
    case SEEK_SET:   /*Ïà¶ÔÎÄŒþ¿ªÊŒÎ»ÖÃÆ«ÒÆ*/
      if (offset < 0)/*offset²»ºÏ·š*/
      {
        ret =  - EINVAL;        /*ÎÞЧµÄÖžÕë*/
        break;
      }
      if ((unsigned int)offset > VIRTUALDISK_SIZE)/*Æ«ÒÆŽóÓÚÉ豞ÄÚŽæ*/
      {
        ret =  - EINVAL;        /*ÎÞЧµÄÖžÕë*/
        break;
      }
      filp->f_pos = (unsigned int)offset;   /*žüÐÂÎÄŒþÖžÕëλÖÃ*/
      ret = filp->f_pos;/*·µ»ØµÄλÖÃÆ«ÒÆ*/
      break;
    case SEEK_CUR:   /*Ïà¶ÔÎÄŒþµ±Ç°Î»ÖÃÆ«ÒÆ*/
      if ((filp->f_pos + offset) > VIRTUALDISK_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 VirtualDisk_fops =
{
  .owner = THIS_MODULE,
  .llseek = VirtualDisk_llseek,/*¶šÎ»Æ«ÒÆÁ¿º¯Êý*/
  .read = VirtualDisk_read,/*¶ÁÉ豞º¯Êý*/
  .write = VirtualDisk_write,/*ÐŽÉ豞º¯Êý*/
  //.ioctl = VirtualDisk_ioctl,/*¿ØÖƺ¯Êý*/
  .unlocked_ioctl = VirtualDisk_ioctl,/*¿ØÖƺ¯Êý*/
  .open = VirtualDisk_open,/*Žò¿ªÉ豞º¯Êý*/
  .release = VirtualDisk_release,/*ÊÍ·ÅÉ豞º¯Êý*/
};

/*³õÊŒ»¯²¢×¢²ácdev*/
static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minor)
{
  int err;
  dev_t devno = MKDEV(VirtualDisk_major, minor);/*¹¹ÔìÉ豞ºÅ*/
  cdev_init(&dev->cdev, &VirtualDisk_fops);/*³õÊŒ»¯cdevÉ豞*/
  dev->cdev.owner = THIS_MODULE;/*ʹÇý¶¯³ÌÐòÊôÓÚžÃÄ£¿é*/
  dev->cdev.ops = &VirtualDisk_fops;/*cdevÁ¬œÓfile_operationsÖžÕë*/
  err = cdev_add(&dev->cdev, devno, 1);/*œ«cdev×¢²áµœÏµÍ³ÖÐ*/
  if (err)
    printk(KERN_NOTICE "Error in cdev_add()\n");
}
/*É豞Çý¶¯Ä£¿éŒÓÔغ¯Êý*/
int VirtualDisk_init(void)
{
  int result;
  dev_t devno = MKDEV(VirtualDisk_major, 0); /*¹¹œšÉ豞ºÅ*/

  /* ÉêÇëÉ豞ºÅ*/
  if (VirtualDisk_major)  /* Èç¹û²»Îª0£¬ÔòŸ²Ì¬ÉêÇë*/
    result = register_chrdev_region(devno, 1, "VirtualDisk");
  else  /* ¶¯Ì¬ÉêÇëÉ豞ºÅ */
  {
    result = alloc_chrdev_region(&devno, 0, 1, "VirtualDisk");
    VirtualDisk_major = MAJOR(devno);/* ŽÓÉêÇëÉ豞ºÅÖеõœÖ÷É豞ºÅ */
  }  
  if (result < 0)
    return result;
  Virtualdisk_class = class_create(THIS_MODULE,"VirtualDisk");  
  if(IS_ERR(Virtualdisk_class))
  {
    printk("failed to create virtualdisk module class.\n");
    unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
    return -1;
  }
  Virtualdisk_device = device_create(Virtualdisk_class,NULL,MKDEV(VirtualDisk_major, 0),NULL,"VirtualDisk");
  if(IS_ERR(Virtualdisk_device))
  {
    printk("failed to create virtualdisk module device.\n");
    unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
    return -1;
  }
  /* ¶¯Ì¬ÉêÇëÉ豞œá¹¹ÌåµÄÄÚŽæ*/
  Virtualdisk_devp = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
  if (!Virtualdisk_devp)    /*ÉêÇëʧ°Ü*/
  {
    result =  - ENOMEM;
    goto fail_kmalloc;
  }
  memset(Virtualdisk_devp, 0, sizeof(struct VirtualDisk));/*œ«ÄÚŽæÇåÁã*/
  /*³õÊŒ»¯²¢ÇÒÌíŒÓcdevœá¹¹Ìå*/
  VirtualDisk_setup_cdev(Virtualdisk_devp, 0);
  return 0;

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

/*Ä£¿éжÔغ¯Êý*/
void VirtualDisk_exit(void)
{
  cdev_del(&Virtualdisk_devp->cdev);   /*×¢Ïúcdev*/
  kfree(Virtualdisk_devp);     /*ÊÍ·ÅÉ豞œá¹¹ÌåÄÚŽæ*/
  device_destroy(Virtualdisk_class,MKDEV(VirtualDisk_major, 0));
  class_destroy(Virtualdisk_class);
  unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1); /*ÊÍ·ÅÉ豞ºÅ*/
}

MODULE_AUTHOR("Zheng Qiang");
MODULE_LICENSE("Dual BSD/GPL");

module_param(VirtualDisk_major, int, S_IRUGO);

module_init(VirtualDisk_init);
module_exit(VirtualDisk_exit);

2、makefile


# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.
ifeq ($(KERNELRELEASE),)

    # Assume the source tree is where the running kernel was built
    # You should set KERNELDIR in the environment if it's elsewhere
    KERNELDIR ?= /home/ntu/iTOP4412/iTop4412_Kernel_3.0
    # The current directory is passed to sub-makes as argument
    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
    # called from kernel build system: just declare what our modules are
    obj-m := VirtualDisk.o 
endif

3、VirtualDiskTest.c

/*

 * File Name: VirtualDiskTest.c

 */

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>



void main()

{

   int fileno;/*ÓÃÓÚÎÄŒþ±íÊö·û*/

   int number;

   char data[]="one two three four five six";/*ÐŽÈëVirtualDiskµÄÊýŸÝ*/

   char str[1024];/*Óû§¿ÕŒä»º³å*/

   int len;



   fileno = open("/dev/VirtualDisk",O_RDWR);/*ÒÔ¶ÁÐŽ·œÊœŽò¿ªÉ豞ÎÄŒþ*/



   if (fileno == -1)/*Žò¿ªÎÄŒþʧ°Ü*/

   {

    printf("open VirtualDisk device errr!\n");

    return 0;

   }



   write(fileno,data,strlen(data));/*œ«ÊýŸÝÐŽÈëÉ豞*/

   close(fileno);/*¹Ø±ÕÉ豞ÎÄŒþ*/



   fileno=open("/dev/VirtualDisk",O_RDWR);/*ÒÔ¶ÁÐŽ·œÊœŽò¿ªÉ豞ÎÄŒþ*/

   len=read(fileno,str,1024);/*¶Á³öÉ豞ÖеÄÊýŸÝ*/

   str[len]='\0';

   printf("%s\n",str);/*ÏÔÊŸÉ豞µÄÊýŸÝ*/

   close(fileno);/*¹Ø±ÕÉ豞ÎÄŒþ*/



   fileno=open("/dev/VirtualDisk",O_RDWR);/*ÒÔ¶ÁÐŽ·œÊœŽò¿ªÉ豞ÎÄŒþ*/

   lseek(fileno,4,SEEK_SET);/*œ«ÎÄŒþÖžÕëºóÒÆ4×֜ڣ¬µ±Ç°Î»ÖõÄ×Ö·ûΪt*/

   len=read(fileno,str,1024);/*¶Á³öÉ豞ÖеÄÊýŸÝ*/

   str[len]='\0';

   printf("%s\n",str);/*ÏÔÊŸÉ豞µÄÊýŸÝ*/

   close(fileno);/*¹Ø±ÕÉ豞ÎÄŒþ*/



   return 0;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值