【ACCESS精品源码栏目提醒】:以下是网学会员为您推荐的ACCESS精品源码-linux简单虚拟硬盘驱动分析 - 其它资料,希望本篇文章对您学习有所帮助。
linux 简单虚拟硬盘驱动分析 2012-06-06 16:15 122 人阅读 评论0 收藏 举报includeincludeincludeincludeincludeincludeincludeincludeincludeincludeincludeincludedefine VHDD_DEV_NAME vhdddefine VHDD_DEV_MAJOR 220define VHDD_MAX_DEVICE 2define VHDD_MAX_PARTITONS 16 /vhdd 可以进行分区,允许的分区总共为15 个,因此总分区数应为 16///define VHDD_DEVICE_SIZE 410241024/vhdd ram disk 模拟了硬盘,因此要定义相应的内容。
扇区大小为 512 字节,可以任意指定圆柱面cylinder书设定为 256每个 cylinder 的扇区数设定为 16磁头数VHDD_HEADS设为 2一台设备容量为扇区大小*圆柱面数*扇区数磁头数,这里=4M,应为 disk提供的大小相一致/define VHDD_SECTOR_SIZE 512define VHDD_SECTORS 16define VHDD_HEADS 2define VHDD_CYLINDERS 256define VHDD_SECTOR_TOTALVHDD_SECTORSVHDD_HEADSVHDD_CYLINDERSdefine VHDD_SIZE VHDD_SECTOR_SIZEVHDD_SECTOR_TOTALtypedef structunsigned char datastruct request_queue queuespinlock_t lock/分别管理自己的 lock/struct gendisk gdvhdd_device//char vdiskVHDD_MAX_DEVICE NULLextern char vdiskVHDD_MAX_DEVICEstatic vhdd_device deviceVHDD_MAX_DEVICE/vhdd 为模拟硬盘的运行,使用内核定时器启动中断interrupt。
为了使用内核定时器,定义 了 timer_list 结构体变量 vhdd_timer。
另外,为了处理请求函数的重入定义了 vhdd_busy 变量。
该值不为 0 时,块设备驱动程序的请求处理函数vhdd_request就会处于运行状态。
这不仅仅是中断的启动方式,也是必要的变量,因为整体设备会因一个中断而运行/static struct timer_list vhdd_timerstatic int vhdd_busy0static void vhdd_requeststruct request_queue qvhdd_device pdevicestruct request vhdd_reqchar pDataint sizeifvhdd_busy return/先检查当前块设备是否处于处理请求的状态,如果处于处理状态,就立即终止函数。
这是因为处理中断的过程中还会调用 vhdd_request函数/while1vhdd_req elv_next_requestq 知道处理完等待中的所有请求。
ifvhdd_req return/vhdd_request 函数反复执行,使用 elv_next_request 函数获得处理请求。
若处理请求的 request 结构体地址返回NULL 时,表明没有处理请求,因此结束函数/pdevice vhdd_device vhdd_req-rq_disk-private_datapData pdevice-data vhdd_req-sector VHDD_SECTOR_SIZE/vhdd_req-current_nr_sectors 包含处理扇区数,如果超出范围,想内核同志出错,调用 end_requestvhdd_req 0想内核通知出错,并处理下一个请求/ifvhdd_req-sector vhdd_req-current_nr_sectors get_capacityvhdd_req-rq_diskend_requestvhdd_req 0continuesize vhdd_req-current_nr_sectors VHDD_SECTOR_SIZEswitchrq_data_dirvhdd_reqcase READ:memcpyvhdd_req-buffer pData sizebreakcase WRITE:memcpypData vhdd_req-buffer sizebreakdefault:end_requestvhdd_req 0continue/普通的硬盘在读取数据的时候,先把将要读取的扇区信息传递到硬盘上,并等待中断。
确认发生了中断,并正常处理后,读取硬盘上的数据。
相反,在向硬盘上写入数据时,先要传递写入扇区的信息和数据,再等待中断。
结束处理后发生中断。
这里使用内核的定时器模拟结束处理的中断。
本例子中,vhdd 进以简单的 ram 模拟硬盘,并在 vhdd_request 处理数据后,使用内核的定时器模拟结束处理的中断。
在使用 vhdd_timer 内核定时器时,经一段时间后,调用作为中断函数的 vhdd_interrupt 函数,为了表示块设备驱动程序的运行状态,把 vhdd_busy 设为 1,然后结束函数。
如前所述,该值就是用于忽略数据处理过程中内核调用的 vhdd_request 函数/vhdd_timer.expires jiffies 2vhdd_timer.data unsigned longpdeviceadd_timervhdd_timervhdd_busy 1returnvoid vhdd_interruptunsigned long datavhdd_device pdevicestruct request vhdd_reqpdevice vhdd_device datavhdd_req elv_next_requestpdevice-queueend_requestvhdd_req 1vhdd_busy 0vhdd_requestpdevice-queueint vhdd_openstruct inode inode struct file filpreturn 0int vhdd_release struct inode inode struct file filpreturn 0/vhdd 模拟硬盘,因此只需要能处理 ioctl 磁盘结构的 HDIO_GETGEO其余军返回 ENOTTYHDIO_GETGEO 是 fdisk 等分区处理程序,适用于获取设备的磁盘结构信息的命令,为了执行该命令,在 hd_geometry 结构体变量上传递 cylinder 数,扇区数,head 数及实际扇区的起始值。
与 2.4 内核不同的是,2.6 内核中可以直接代如扇区的起始值,也可以使用 get_start_sect 函数处理该值。
内核源代码中支持的大多数设备驱动程序都使用 get_start_sect 处理扇区值。
/int vhdd_ioctlstruct inode inode struct file filp unsigned int cmdunsigned long argint errstruct hd_geometry geoswitchcmdcase HDIO_GETGEO:err
access_okVERIFY_WRITE arg sizeofgeoiferr return -EFAULTgeo.cylinders VHDD_CYLINDERSgeo.heads VHDD_HEADSgeo.sectors VHDD_SECTORSgeo.start get_start_sectinode-i_bdevifcopy_to_uservoidarg geo sizeofgeoreturn -EFAULTreturn 0return -ENOTTYstatic struct block_device_operations vhdd_fops .owner THIS_MODULE.open vhdd_open.release vhdd_release.ioctl vhdd_ioctlint vhdd_initvoidint lp//vdisk0 vmallocVHDD_DEVICE_SIZE//vdisk1 vmallocVHDD_DEVICE_SIZE//memsetvdisk0 0 VHDD_DEVICE_SIZE//memsetvdisk1 0 VHDD_DEVICE_SIZE/使用下面的函数初始化内核定时器结构题 vhdd_timer 变量后,把由定时器而启动的 vhdd_interrupt函数注册到 vhdd_timer.function 函数中。
vhdd_timer.function函数用于模拟硬盘的中断/init_timervhdd_timervhdd_timer.function vhdd_interruptregister_blkdevVHDD_DEV_MAJOR VHDD_DEV_NAMEforlp 0 lp VHDD_MAX_DEVICE lpdevicelp.data vdisklp/定义此 data,使 vdisk 所获得的内存地址能够用在request函数上//分配 gendisk 结构体,而 gendisk 结构体是注册块设备的信息结构体。
vhdd 共处理 15 个分区,因此,变量上代入 16。
在内核里该值大于 2 时,确定存在内存分区,并执行分区处理/devicelp.gd alloc_diskVHDD_MAX_PARTITONS/使用 blk_init_queue 函数注册内核和块设备驱动程序的实际输入输出路径vhdd_request 函数,在 devicelp.queue 上设置生成的请求队列。
对于 blk_init_queue函数,除了 request函数外,为了管理等待队列的 Lock需要使用 spinlock_t 结构体,因此,下面初始化 lock 变量,并代如到 blk_init_queue 函数中/spin_lock_initdevicelp.lockdevicelp.queue blk_init_queuevhdd_request devicelp.lockdevicelp.gd-major VHDD_DEV_MAJORdevicelp.gd-first_minor lpVHDD_MAX_PARTITONSdevicelp.gd-fops vhdd_fopsdevicelp.gd-queue devicelp.queuedevicelp.gd-private_data devicelp 在/为了在 proc 和 sysfs 文件系统中表示分区及块设备, devicelp.gd-disk_name上分别代入”vhdda””vhddb”,然后使用 set_capacity 函数设置各台块设备的扇区数/sprintfdevicelp.gd-disk_name vhddc alpset_capacitydevicelp.gdVHDD_SECTOR_TOTAL/为了检索分区并注册块设备,调用 add_disk函数。
调用该函数后,内核会在内核内部注册各台块设备,需要检索分区时,在 gendisk 结构体的内部设置分区处理变量/add_diskdevicelp.gdreturn 0void vhdd_exitvoidint lpforlp 0lp VHDD_MAX_DEVICE lpdel_gendiskdevicelp.gdput_diskdevicelp.gdblk_cleanup_queuedevicelp.queueunregister_blkdevVHDD_DEV_MAJOR VHDD_DEV_NAME//vfreevdisk0//vfreevdisk1module_initvhdd_initmodule_exitvhdd_exitMODULE_LICENSEDual BSD/GPL
上一篇:
Android之蓝牙驱动开发总结
下一篇:
加强企业管理力度 提升产品绿色竞争力