Linux驱动开发: 块设备驱动开发

代码 代码 1194 人阅读 | 0 人回复

<
Linux内乱核版本: 3.5
测试装备:  和睦之臂Tiny4412开拓板  (CPU型号:  三星Exynos4412)
2019年三星公布的Galaxy S IIMX接纳的便是那款CPU。

1、块装备介绍

         块是一种具有必然规划的随机存与装备,对这类装备的读写是按块停止的,他利用缓冲区去寄存临时的数据,待前提成生后,从缓存一次性写进装备大要从装备一次性读到缓冲区。
块装备是取字符装备并列的观点, 那两类装备正在 Linux 中驱动的规划有较年夜差别,整体而行, 块装备驱动比字符装备驱动要庞大很多,正在 I/O 操纵上表现出极年夜的差别,缓冲、 I/O 调理、恳求行列等皆是取块装备驱动相干的观点。

         正在Linux中,驱动对块装备的输进或输出(I/O)操纵,城市背块装备收回一个恳求,正在驱动顶用request规划体形貌。但关于一些磁盘装备而行恳求的速率很缓,这时候候内乱核便供给一种行列的机造把那些I/O恳求增加到行列中(即:恳求行列),正在驱动顶用request_queue规划体形貌。正在背块装备提交那些恳求前内乱核会先施行恳求的兼并战排序预操纵,以进步会见的服从,然后再由内乱核中的I/O调理法式子体系去卖力提交  I/O 恳求,  调理法式将磁盘资本分派给体系中一切挂起的块 I/O  恳求,其事情是办理块装备的恳求行列,决议行列中的恳求的布列挨次和甚么时分派收恳求到装备。
         由通用块层(Generic Block Layer)卖力保持一个I/O恳求正在上层文件体系取底层物理磁盘之间的干系。正在通用块层中,凡是用一个bio规划体去对应一个I/O恳求。
          Linux供给了一个gendisk数据规划体,用去暗示一个自力的磁盘装备或分区,用于对底层物理磁盘停止会见。正在gendisk中有一个相同字符装备中file_operations的硬件操纵规划指针,是block_device_operations规划体。
     编写块装备驱动时,利用的一些单位介绍:
 1. 扇区(Sectors):任何块装备硬件对数据处理的底子单位。凡是,1个扇区的大小为512字节。(对装备而行)
2. 块 (Blocks):由Linux订定对内乱核或文件体系等数据处理的底子单位。凡是,1个块由1个或多个扇区构成。(对Linux操纵体系而行)
3. 段(Segments):由多少个相邻的块构成。是Linux内乱存办理机造中一个内乱存页大要内乱存页的一部分。
155409p91qznnznd98816c.jpg

 
 IO调理器
          便是电梯算法。我们明白,磁盘是的读写是经由过程机械性的挪动磁头去完成读写的,实际上磁盘装备满意块装备的随机读写的请求,可是出于节流磁盘,进步服从的思索,我们期望当磁头处于某一个地位的时分,一同将近来需求写正在四周的数据写进,而没有是那写一下,那写一下然后再返来,IO调理便是将上层收下去的IO恳求的挨次停止从头排序和对多个恳求停止兼并,如许就能够完成上述的进步服从、节流磁盘的目标。这类处理成绩的思绪利用电梯算法,一个运转中的电梯,一小我私家20楼->1楼,此外一小我私家15->5楼,电梯没有会先将第一小我私家收到1楼再来15楼接第两小我私家将其收到5楼,而是从20楼下去,到15楼的时分停下接人,到5楼将第两个放下,最初抵达1楼,一句话,电梯算法终极效劳的劣先挨次其实不根据按按钮的前后挨次。
Linux内乱核中供给了上面的几种电梯算法去完成IO调理:
1.    No-op I/O scheduler只完成了俭朴的FIFO的,只停止最俭朴的兼并,比力合适基于Flash的存储
2.    Anticipatory I/O scheduler推延IO恳求(约莫几个微秒),以期能对他们停止排序,得到更下服从
3.    Deadline I/O scheduler试图把每次恳求的提早降到最低,同时也会对BIO从头排序,出格合用于读与较多的场合,好比数据库
4.    CFQ I/O scheduler为体系内乱一切的使命分派平均的IO带宽,供给一个公允的事情情况,正在多媒体情况中,能包管音视频及时从磁盘中读与数据,是当前内乱核默许的调理器
我们能够经由过程内乱核传参的方法指定利用的调理算法:  kernel elevator=deadline
大要,利用以下号令改动内乱核调理算法:
  1. echo SCHEDULER > /sys/block/DEVICE/queue/scheduler
复造代码
2、块装备规划介绍

2.1 内乱核自带可参考的块装备驱动源码

  1. drivers\block\z2ram.c
  2. drivers\block\xd.c
  3. \drivers\mmc\host\sdhci-s3c.c
复造代码
2.2 块装备注册取登记函数

1. 注册函数
int register_blkdev(unsigned int major, const char *name)
 函数功用介绍:  注册一个新的块装备
函数参数介绍:
@major:块装备的主装备号[1..255]。 假如major = 0,暗示测验考试分派已利用的主装备号,返回值便暗示分派胜利的主装备号。
@name:新块装备的称号。 留意: 称号必需包管正在体系中是独一的(不克不及取装备称号重名)。
注册示例:
int Tiny4412_block_major = register_blkdev(0, "Tiny4412_block");
2. 登记函数
void unregister_blkdev(unsigned int major, const char *name)
 函数工程介绍: 登记已注册的块装备。
函数参数介绍:
@major: 主装备号
@name: 装备称号
登记示例:
unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");
2.3 静态分派恳求行列

struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
函数功用介绍: 分派一个默许的恳求行列,用该函数天生的恳求行列出有设置默许的IO调理器,假如编写的块装备是内乱存模仿块装备大要是SD卡、Flash等装备,就能够用此函数分派恳求行列。
函数参数介绍:
@ gfp_mask : 内乱存分派的方法。 GFP_KERNEL战GFP_ATOMIC,
  GFP_ATOMIC: 用去从中止处理战历程高低文以外的其他代码平分配内乱存. 从没有就寝
GFP_KERNEL: 内乱核内乱存的一般分派. 能够就寝
分派恳求行列示例:
struct request_queue *queue= =blk_alloc_queue(GFP_KERNEL);
卸载驱动时,能够经由过程kfree开释空间。
假如需求会见内部硬件,好比: 光盘、磁盘等内部物理装备时,要设置默许的调理器,能够挪用blk_init_queue函数分派恳求行列。
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
blk_init_queue()必需取blk_cleanup_queue()挪用配对。
函数参数介绍:
 @ rfn 是一个函数指针,规范为 typedef void (request_fn_proc) (struct request_queue *q);
 @ lock 自旋锁


2.4 绑定恳求行列

void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
函数功用介绍:  绑定blk_alloc_queue函数到恳求行列。
上一步介绍的blk_alloc_queue函数分派的恳求行列,因为没有会利用默许的IO调理器,此中的make_request_fn是出有赋值的,由于上层代码背恳求行列发作恳求时皆是经由过程make_request_fn那个函数去完成的。关于上层代码收回的恳求,能够间接用make_request_fn函数去完成恳求并间接将成果返回给上层的代码。
函数参数介绍:
struct request_queue *q :恳求行列指针。
make_request_fn *mfn : make_request_fn函数指针。
函数指针的本型以下:
typedef void (make_request_fn) (struct request_queue *q, struct bio *bio);
该函数指针正在Blkdev.h界说。
155409h7zii7swm5ftz7f5.jpg

 绑定恳求行列示例:
blk_queue_make_request(queue, Tiny4412_block_make_request);

2.5 make_request_fn处理函数编写

//间接提交恳求,行列处理
static void Tiny4412_block_make_request(struct request_queue *q, struct bio *bio)
{
        int i;
        struct bio_vec *bvec;
        sector_t sector = bio->bi_sector;

        /*经由过程for轮回遍历一个bio中一切的segment恳求*/
        bio_for_each_segment(bvec, bio, i)
        {
                         char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); /*映照内乱存空间(申请空间)*/
                         Tiny4412_block_dev_sector_read_write(sector, bio_cur_bytes(bio)>>9 ,buffer, bio_data_dir(bio) == WRITE);
                         /*
                                 sector: 当前扇区地位
                                 bio_cur_bytes(bio)>>9: 扇区读写数目
                                 buffer :读写的缓冲区指针尾地点
                                 bio_data_dir(bio): 判定是读仍是写
                         */
                         sector += bio_cur_bytes(bio)>>9;     /*偏偏移扇区*/
                         __bio_kunmap_atomic(bio, KM_USER0);  /*打消映照(开释空间)*/
        }
        bio_endio(bio, 0); /*完毕处理*/
        return;
}
make_request_fn函数指针传进的参数介绍:
struct bio *bio: 形貌块数据传收时如何完成添补或读与块给driver
struct request_queue *q :传进的恳求行列

2.6 扇区读写函数完成

代码示例:
unsigned long sector:  当前扇区地位
unsigned long nsect :  扇区读写数目
char *buffer        :  读写的缓冲区指针
int write           :  是读仍是写
*/
static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)
{
                /*块装备最小单位是一个扇区,一个扇区的字节数是512字节*/
                unsigned long offset = sector*512;
                unsigned long nbytes = nsect*512;
                if((offset + nbytes)>TINY4412_BLKDEV_BYTES)
                {
                         printk(KERN_NOTICE "写超越范畴,强迫完毕(%ld %ld)\n", offset, nbytes);
                         return;
                }
                if(write)   /*为实,暗示是写*/
                memcpy(tiny4412_blkdev_data + offset, buffer, nbytes);
                else      /*读操纵*/
                memcpy(buffer,tiny4412_blkdev_data + offset, nbytes);
}

2.7 分派一个gendisk规划

struct gendisk *alloc_disk(int minors)   //静态分派gendisk
void del_gendisk(struct gendisk *disk)  //登记gendisk
函数功用介绍:每一个块装备皆对应一个gendisk规划,函数alloc_disk用于分派一个gendisk规划。
函数参数介绍:
@minors: 数目
给分派的规划添补参数:
/*静态分派次装备号规划*/
        gd=alloc_disk(1);/*分派一个gendisk1暗示不克不及停止分区,只能牢固一个分区。 >1暗示撑持分区的数目
分区能够经由过程fdsik号令停止操纵*/
        gd->major=Tiny4412_block_major;      /*主装备号*/
        gd->first_minor=0;                               /*次装备号*/
        gd->fops=&Tiny4412_block_ops;      /*文件操纵汇合*/
        gd->queue=queue;                                   /*将恳求行列联系关系到gendisk规划*/
        snprintf(gd->disk_name, 32, "Tiny4412_block_%c",&#39;a&#39;); //设置磁盘称号,正在/dev下能够查察该称号

//块装备底子皆是利用文件体系函数停止操纵,该文件操纵汇合能够不消本人完成
static struct block_device_operations Tiny4412_block_ops=
{
        .owner = THIS_MODULE,
};
驱动装置以后,查察的节面疑息: 
155410slb2brjtjkf0rejt.jpg

 设置磁盘的容量

        /*留意: 块装备的大小利用扇区做为单位设置,而扇区的大小默许是512字节。
          cat /sys/block/xxxx/size 能够查察到设置的大小
          把字节为单位的大小转换为以扇区为单位时,我们需求除以512,大要左移9位
        */
        set_capacity(gd,TINY4412_BLKDEV_BYTES>>9);
2.8 增加磁盘分区疑息到内乱核

void add_disk(struct gendisk *disk)
函数功用介绍: 将分区疑息增加到内乱核。
函数参数: 添补好gendisk规划。
示例:
add_disk(gd);
12.9 初初化一个恳求行列

struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
示例:
tiny4412_blockdev_queue = blk_init_queue(do_tiny4412_blockdev_request, &tiny4412_blockdev_lock);
该函数里挪用了默许的IO调理器。 代码能够参考内乱核文件: drivers\block\z2ram.c

3、块装备示例代码

155410o6lyao2avwi9ikg9.jpg

 
3.1 内乱存模仿块装备(没有利用IO调理器)

内乱存空间接纳vmalloc函数停止分派。
  1. #include <linux/module.h>
  2. #include <linux/blkdev.h>
  3. #include <linux/hdreg.h>
  4. #include <linux/version.h>
  5. /*
  6. *     insmod tiny4412_blkdev.ko
  7. *     # or insmod tiny4412_blkdev.ko size=numK/M/G/T
  8. *     fdisk /dev/tiny4412_blkdev # create 2 patitions
  9. *     mkfs.ext2 /dev/tiny4412_blkdev1
  10. *     mkfs.ext2 /dev/tiny4412_blkdev2
  11. *     mount /dev/tiny4412_blkdev1 /mnt/temp1/
  12. *     mount /dev/tiny4412_blkdev2 /mnt/temp2/
  13. *     # play in /mnt/temp1/ and /mnt/temp2/
  14. *     umount /mnt/temp1/
  15. *     umount /mnt/temp2/
  16. *     rmmod tiny4412_blkdev.ko
  17. *
  18. */
  19. static int Tiny4412_block_major=0;
  20. static struct request_queue *tiny4412_blkdev_queue;
  21. static struct gendisk *tiny4412_blkdev_disk;
  22. static unsigned long long tiny4412_blkdev_bytes=1024*1024*10;//10M--空间容量
  23. #define TINY4412_BLKDEV_BYTES_1        (1024*1024*10)  /*设置块装备的巨细*/
  24. static unsigned char tiny4412_blkdev_data_1[TINY4412_BLKDEV_BYTES_1]; /*用于测试块装备的数组巨细*/
  25. /*
  26. * Handle an I/O request.
  27. * 完成扇区的读写
  28. unsigned long sector:  当前扇区地位
  29. unsigned long nsect :  扇区读写数目
  30. char *buffer        :  读写的缓冲区指针
  31. int write           :  是读仍是写
  32. */
  33. static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)
  34. {
  35.                 /*块装备最小单元是一个扇区,一个扇区的字节数是512字节*/
  36.                 unsigned long offset = sector;  /*写进数据的地位*/
  37.                 unsigned long nbytes = nsect;   /*写进的少度*/
  38.                 if((offset + nbytes)>TINY4412_BLKDEV_BYTES_1)
  39.                 {
  40.                         printk("写超越范畴,强迫完毕(%ld %ld)\n", offset, nbytes);
  41.                         return;
  42.                 }
  43.                 if(write) /*为实,暗示是写*/
  44.                 memcpy(tiny4412_blkdev_data_1 + offset, buffer, nbytes);
  45.                 else      /*读操纵*/
  46.                 memcpy(buffer,tiny4412_blkdev_data_1 + offset, nbytes);
  47. }
  48. /*
  49. 处置恳求
  50. */
  51. static int tiny4412_blkdev_make_request(struct request_queue *q, struct bio *bio)
  52. {
  53.         int dir;
  54.         unsigned long long dsk_offset;
  55.         struct bio_vec *bvec;
  56.         int i;
  57.         void *iovec_mem;
  58.        
  59.         /*判定读写标的目的*/
  60.         if(bio_data_dir(bio) == WRITE) dir = 1;
  61.         else dir = 0;
  62.         dsk_offset = bio->bi_sector << 9;
  63.         bio_for_each_segment(bvec, bio, i)
  64.         {
  65.                 iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
  66.                
  67.                 //肇端地位,少度,源数据,标的目的
  68.                 Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);
  69.                
  70.                 kunmap(bvec->bv_page);
  71.                 dsk_offset += bvec->bv_len;
  72.         }
  73.         bio_endio(bio, 0);
  74.         return 0;
  75. }
  76. static int tiny4412_blockdev_getgeo(struct block_device *bdev, struct hd_geometry *geo)
  77. {
  78.         /* 容量=heads*cylinders*sectors*512
  79.          * 存储容量 = 磁头数 × 磁讲(柱里)数 × 每讲扇区数 × 每扇区字节数
  80.          */
  81.         geo->heads     = 2;  /*磁头(普通一个盘里有两个磁头,正里一个/背面一个)*/
  82.         geo->cylinders = 32; /*柱里(普通一个盘里上有32个柱里)每一个盘片32个磁讲)*/
  83.         geo->sectors   = TINY4412_BLKDEV_BYTES_1/2/32/512; /*扇区,普通每一个磁讲上有12个扇区,那里需求按照前里柱里战磁头停止计较,不克不及治挖*/
  84.         return 0;
  85. }
  86. struct block_device_operations tiny4412_blkdev_fops =
  87. {
  88.     .owner= THIS_MODULE,
  89.          /*fdisk号令分区时需求挪用该函数,用于读与磁头、柱里、扇区等疑息*/
  90.         .getgeo        = tiny4412_blockdev_getgeo,
  91. };
  92. static int __init tiny4412_blkdev_init(void)
  93. {
  94.         /*静态分派恳求行列*/
  95.         tiny4412_blkdev_queue = blk_alloc_queue(GFP_KERNEL);
  96.        
  97.         /*绑定恳求行列*/
  98.         blk_queue_make_request(tiny4412_blkdev_queue,tiny4412_blkdev_make_request);
  99.        
  100.         /*静态分派次装备号构造*/
  101.         /*每个磁盘(分区)皆是利用一个gendisk构造保留*/
  102.         tiny4412_blkdev_disk = alloc_disk(64);
  103.        
  104.         /*磁盘称号赋值*/
  105.         strcpy(tiny4412_blkdev_disk->disk_name, "tiny4412_blkdev");
  106.         /*注册一个块装备,主动分派主装备号*/
  107.         Tiny4412_block_major = register_blkdev(0,"Tiny4412_block");
  108.         printk("Tiny4412_block_major=%d\n",Tiny4412_block_major);
  109.        
  110.         tiny4412_blkdev_disk->major=Tiny4412_block_major;           /*主装备号*/
  111.         tiny4412_blkdev_disk->first_minor = 0;                                   /*次装备号*/
  112.         tiny4412_blkdev_disk->fops = &tiny4412_blkdev_fops;   /*文件操纵分离*/
  113.         tiny4412_blkdev_disk->queue = tiny4412_blkdev_queue;  /*处置数据恳求的行列*/
  114.        
  115.         /*设置磁盘构造 capacity 的容量*/
  116.         /*留意: 块装备的巨细利用扇区做为单元设置,而扇区的巨细默许是512字节。
  117.           cat /sys/block/xxxx/size 能够检察到设置的巨细
  118.           把字节为单元的巨细转换为以扇区为单元时,我们需求除以512,大概左移9位
  119.         */
  120.         set_capacity(tiny4412_blkdev_disk,tiny4412_blkdev_bytes>>9);
  121.        
  122.         //增加磁盘疑息到内乱核
  123.         add_disk(tiny4412_blkdev_disk);
  124.         return 0;
  125. }
  126. static void __exit tiny4412_blkdev_exit(void)
  127. {
  128.         //删除磁盘
  129.         del_gendisk(tiny4412_blkdev_disk);
  130.        
  131.         put_disk(tiny4412_blkdev_disk);
  132.        
  133.         //肃清行列
  134.         blk_cleanup_queue(tiny4412_blkdev_queue);
  135.        
  136.         /*登记块装备*/
  137.         unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");
  138.        
  139. }
  140. module_init(tiny4412_blkdev_init);
  141. module_exit(tiny4412_blkdev_exit);
  142. MODULE_LICENSE("GPL");
复造代码
块装备操纵历程:
[root@wbyq code]#ls
tiny4412_block_device.ko
[root@wbyq code]#insmod tiny4412_block_device.ko
[  154.950000] Tiny4412_block_major=253
[  154.955000]  tiny4412_blkdev: unknown partition table (由于利用的内乱存装备模仿块装备,数组里出有分区表,以是第一次装置装备时,读与没有到装备分区表,那个提醒是一般的)
[root@wbyq code]#fdisk /dev/tiny4412_blkdev
Device contains neither a valid DOS partition table, nor Sun, SGI, OSF or GPT disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that the previous content
won&#39;t be recoverable.


Command (m for help): n (n暗示新建分区表)
Command action
   e   extended(暗示扩大分区)
   p   primary partition (1-4)(暗示主分区)
p (挑选p创立主分区)
Partition number (1-4): 1 (创立的主分区编号为1)
First cylinder (1-160, default 1): 1 (柱里肇端地位设置为1, 1-160暗示当前磁盘盈余的已分区的柱里范畴为1-160)
Last cylinder or +size or +sizeM or +sizeK (1-160, default 160): 100 (设置柱里的完毕地位)

Command (m for help): n (暗示主分区)
Command action
   e   extended
   p   primary partition (1-4)
p (挑选p创立主分区)
Partition number (1-4): 2 (创立的主分区编号为2)
First cylinder (101-160, default 101): 101 (柱里肇端地位设置为101, 101-160暗示当前磁盘盈余的已分区的柱里范畴为101-160)
Last cylinder or +size or +sizeM or +sizeK (101-160, default 160): 160 (设置柱里的完毕地位)

Command (m for help): p (挨印当前的分区状况)

Disk /dev/tiny4412_blkdev: 10 MB, 10485760 bytes (磁盘的总容量: M单位,字节单位)
2 heads, 64 sectors/track, 160 cylinders (一个共有2个磁头,每一个柱里有64个扇区,160个柱里)
(提醒: 存储容量 磁头数 × 磁讲(柱里) × 每讲扇区数 × 每扇区字节数)
Units = cylinders of 128 * 512 = 65536 bytes
             
               Device Boot      Start         End      Blocks  Id  System
/dev/tiny4412_blkdev1               1         100        6368  83  Linux  (分区1的疑息)
Partition 1 has different physical/logical endings:
     phys=(355, 1, 0) logical=(99, 1, 64)
Partition 1 does not end on cylinder boundary
/dev/tiny4412_blkdev2             101         160        3840  83  Linux  (分区2的疑息)
Partition 2 has different physical/logical endings:
     phys=(415, 1, 0) logical=(159, 1, 64)
Partition 2 does not end on cylinder boundary

Command (m for help): w (w暗示保存分区表,写进磁盘,并退出fdisk号令止)
The partition table has been altered. (提醒,分区暗示曾经变动)
Calling ioctl() to re-read partition table (挪用ioctl()从头读与分区表)
[  218.905000]  tiny4412_blkdev: tiny4412_blkdev1 tiny4412_blkdev2 (提醒分区以后创立胜利的装备节面)

[root@wbyq code]#ls /dev/tiny4412_blkdev* -l (查察/dev下分区胜利的装备节面)
brw-rw----    1 root     root      253,   0 Nov 24  2018 /dev/tiny4412_blkdev
brw-rw----    1 root     root      253,   1 Nov 24  2018 /dev/tiny4412_blkdev1
brw-rw----    1 root     root      253,   2 Nov 24  2018 /dev/tiny4412_blkdev2
 [root@wbyq code]#mkfs.ext2 /dev/tiny4412_blkdev1  (给第一个装备分区停止格局化文件体系)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
1592 inodes, 6368 blocks
318 blocks (5%) reserved for the super user
First data block=1
Maximum filesystem blocks=262144
1 block groups
8192 blocks per group, 8192 fragments per group
1592 inodes per group
[root@wbyq code]#mkfs.ext2 /dev/tiny4412_blkdev2(给第两个装备分区停止格局化文件体系)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
960 inodes, 3840 blocks
192 blocks (5%) reserved for the super user
First data block=1
Maximum filesystem blocks=262144
1 block groups
8192 blocks per group, 8192 fragments per group
960 inodes per group

[root@wbyq code]#mount /dev/tiny4412_blkdev1 /mnt/ (将第一个分区挂载到/mnt目次下)
[root@wbyq code]#mount /dev/tiny4412_blkdev2 /tmp/ (将第两个分区挂载到/mnt目次下)
[root@wbyq code]#df -h (查察当前体系磁盘的疑息)
Filesystem                Size      Used Available Use% Mounted on
192.168.10.11:/work/rootfs/
                         48.1G     16.5G     29.1G  36% /
/dev/tiny4412_blkdev1
                          6.0M     13.0K      5.7M   0% /mnt
/dev/tiny4412_blkdev2
                          3.6M     13.0K      3.4M   0% /tmp

/*上面便是别离进进到挂载的目次下停止文件拷贝操纵,测试块装备能否一般,最初正在打消挂载退出*/
[root@wbyq code]#cd /mnt/
[root@wbyq mnt]#ls
lost+found
[root@wbyq mnt]#cp /123.mp3 ./
[root@wbyq mnt]#
[root@wbyq mnt]#cd /tmp/
[root@wbyq tmp]#cp /123.mp3 ./
[root@wbyq tmp]#
[root@wbyq tmp]#ls
123.mp3     lost+found
[root@wbyq tmp]#cd /
[root@wbyq ]#umount /tmp/
[root@wbyq ]#umount /mnt/
3.2 利用SD卡编写块装备(没有利用IO调理器)

SD卡接纳SPI和谈通讯,底层接纳模仿的SPI体系,出有利用SPI子体系。
155410grmrqyyfybt24tfm.jpg

  1. #include <linux/module.h>
  2. #include <linux/moduleparam.h>
  3. #include <linux/init.h>
  4. #include <linux/sched.h>
  5. #include <linux/kernel.h>       
  6. #include <linux/slab.h>
  7. #include <linux/fs.h>            /* everything... */
  8. #include <linux/errno.h>        /* error codes */
  9. #include <linux/timer.h>
  10. #include <linux/types.h>        /* size_t */
  11. #include <linux/fcntl.h>        /* O_ACCMODE */
  12. #include <linux/hdreg.h>        /* HDIO_GETGEO */
  13. #include <linux/kdev_t.h>
  14. #include <linux/vmalloc.h>
  15. #include <linux/genhd.h>
  16. #include <linux/blkdev.h>
  17. #include <linux/bio.h>
  18. #include <linux/init.h>
  19. #include <linux/module.h>
  20. #include <linux/ioctl.h>
  21. #include <linux/fs.h>
  22. #include <linux/device.h>
  23. #include <linux/err.h>
  24. #include <linux/list.h>
  25. #include <linux/errno.h>
  26. #include <linux/mutex.h>
  27. #include <linux/slab.h>
  28. #include <linux/compat.h>
  29. #include <linux/spi/spi.h>
  30. #include <linux/spi/spidev.h>
  31. #include <asm/uaccess.h>
  32. #include <linux/gpio.h>
  33. #include <mach/gpio.h>
  34. #include <plat/gpio-cfg.h>
  35. #include <linux/delay.h>
  36. #include <linux/io.h>
  37. #include <linux/mutex.h>
  38. #include <linux/miscdevice.h>   /*纯项字符装备头文件*/
  39. /*--------------------------------SD相干操纵代码---------------------------------------------*/
  40. /*界说指针,用于领受假造地点*/
  41. volatile unsigned int *SD_GPBCON;
  42. volatile unsigned int *SD_GPBDAT;
  43.        
  44. /*
  45. 函数功用:SD初初化
  46. Tiny4412硬件毗连:
  47.         DO--MISO :GPB_2
  48.         DI--MOSI :GPB_3
  49.         CLK-SCLK :GPB_0
  50.         CS--CS   :GPB_1
  51. */
  52. void SDCardSpiInit(void)
  53. {
  54.         /*1. 初初化GPIO*/
  55.         /*映照物理地点*/
  56.         SD_GPBCON=ioremap(0x11400040,4);
  57.         SD_GPBDAT=ioremap(0x11400044,4);
  58.        
  59.         *SD_GPBCON &= ~(0xf  << 0 * 4);*SD_GPBCON |=  (0x1   << 0 * 4);
  60.         *SD_GPBCON &= ~(0xf  << 1 * 4);*SD_GPBCON |=  (0x1   << 1 * 4);
  61.         *SD_GPBCON &= ~(0xf  << 2 * 4);
  62.         *SD_GPBCON &= ~(0xf  << 3 * 4);*SD_GPBCON |=  (0x1   << 3 * 4);
  63.        
  64.         /*2. 上推GPIO心*/
  65.         //*SD_GPBDAT &= ~(1 << 4);//输出0
  66.         *SD_GPBDAT |= (1 << 0);   //输出1
  67.         *SD_GPBDAT |= (1 << 1);   //输出1
  68.         *SD_GPBDAT |= (1 << 3);   //输出1
  69. }
  70. // SD卡范例界说  
  71. #define SDCard_TYPE_ERR     0X00  //卡范例毛病
  72. #define SDCard_TYPE_MMC     0X01  //MMC卡
  73. #define SDCard_TYPE_V1      0X02
  74. #define SDCard_TYPE_V2      0X04
  75. #define SDCard_TYPE_V2HC    0X06          
  76. // SD卡指令表            
  77. #define SDCard_CMD0    0       //卡复位
  78. #define SDCard_CMD1    1
  79. #define SDCard_CMD8    8       //号令8 ,SEND_IF_COND
  80. #define SDCard_CMD9    9       //号令9 ,读CSD数据
  81. #define SDCard_CMD10   10      //号令10,读CID数据
  82. #define SDCard_CMD12   12      //号令12,截至数据传输
  83. #define SDCard_CMD13   16      //号令16,设置扇区巨细 应返回0x00
  84. #define SDCard_CMD17   17      //号令17,读扇区
  85. #define SDCard_CMD18   18      //号令18,读Multi 扇区
  86. #define SDCard_CMD23   23      //号令23,设置多扇区写进前预先擦除N个block
  87. #define SDCard_CMD24   24      //号令24,写扇区
  88. #define SDCard_CMD25   25      //号令25,写多个扇区
  89. #define SDCard_CMD41   41      //号令41,应返回0x00
  90. #define SDCard_CMD55   55      //号令55,应返回0x01
  91. #define SDCard_CMD58   58      //号令58,读OCR疑息
  92. #define SDCard_CMD59   59      //号令59,使能/制止CRC,应返回0x00、
  93. /*SD卡回应标识表记标帜字*/
  94. #define SDCard_RESPONSE_NO_ERROR      0x00   //准确回应
  95. #define SDCard_SD_IN_IDLE_STATE       0x01   //忙置形态
  96. #define SDCard_SD_ERASE_RESET         0x02   //擦除复位
  97. #define SDCard_RESPONSE_FAILURE       0xFF   //呼应失利
  98.                
  99. //函数声明              
  100. unsigned char SDCardReadWriteOneByte(unsigned char data);                 //底层接心,SPI读写字节函数
  101. unsigned char SDCardWaitBusy(void);                                                                                   //等候SD卡筹办
  102. unsigned char SDCardGetAck(unsigned char Response);                                                                               //得到应对
  103. unsigned char SDCardDeviceInit(void);                                                                    //初初化
  104. unsigned char SDCardReadData(unsigned char*buf,unsigned int sector,unsigned int cnt);                    //读块(扇区)
  105. unsigned char SDCardWriteData(unsigned char*buf,unsigned int sector,unsigned int cnt);                  //写块(扇区)
  106. unsigned int GetSDCardSectorCount(void);                                                   //读扇区数
  107. unsigned char GetSDCardCISDCardOutnfo(unsigned char *cid_data);           //读SD卡CID
  108. unsigned char GetSDCardCSSDCardOutnfo(unsigned char *csd_data);           //读SD卡CSD
  109.           
  110. static unsigned char  SD_Type=0;  //寄存SD卡的范例
  111. /*
  112. 函数功用:SD卡底层接心,经由过程SPI时序背SD卡读写一个字节
  113. 函数参数:data是要写进的数据
  114. 返 回 值:读到的数据
  115. 阐明:时序是第两个上降沿收罗数据
  116. */
  117. unsigned char SDCardReadWriteOneByte(unsigned char data_tx)
  118. {                 
  119.    u8 data_rx=0;
  120.          u8 i;
  121.          for(i=0;i<8;i++)
  122.          {
  123.                 *SD_GPBDAT &= ~(1 << 0);//输出0
  124.                 if(data_tx&0x80)*SD_GPBDAT |= (1 << 3);   //输出1
  125.                 else *SD_GPBDAT &= ~(1 << 3);//输出0
  126.                 data_tx<<=1; //持续收收下一个数据
  127.                 *SD_GPBDAT |= (1 << 0);   //输出1
  128.                 data_rx<<=1;
  129.                 if((*SD_GPBDAT & (1 << 2)))data_rx|=0x01;
  130.          }
  131.          return data_rx;
  132. }
  133. /*
  134. 函数功用:打消挑选,开释SPI总线
  135. */
  136. void SDCardCancelCS(void)
  137. {
  138.         *SD_GPBDAT |= (1 << 1);
  139.         SDCardReadWriteOneByte(0xff);//供给分外的8个时钟
  140. }
  141. /*
  142. 函数 功 能:挑选sd卡,而且等候卡筹办OK
  143. 函数返回值:0,胜利;1,失利;
  144. */
  145. unsigned char SDCardSelectCS(void)
  146. {
  147.         *SD_GPBDAT &= ~(1 << 1);
  148.         if(SDCardWaitBusy()==0)return 0;//等候胜利
  149.         SDCardCancelCS();
  150.         return 1;//等候失利
  151. }
  152. /*
  153. 函数 功 能:等候卡筹办好
  154. 函数返回值:0,筹办好了;其他,毛病代码
  155. */
  156. unsigned char SDCardWaitBusy(void)
  157. {
  158.         unsigned int t=0;
  159.         do
  160.         {
  161.                 if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
  162.                 t++;                  
  163.         }while(t<0xFFFFFF);//等候
  164.         return 1;
  165. }
  166. /*
  167. 函数功用:等候SD卡回应
  168. 函数参数:
  169.                 Response:要获得的回应值
  170. 返 回 值:
  171.                 0,胜利获得了该回应值
  172.                 其他,获得回应值失利
  173. */
  174. unsigned char SDCardGetAck(unsigned char Response)
  175. {
  176.         u16 Count=0xFFFF;//等候次数                                                             
  177.         while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等候获得精确的回应            
  178.         if(Count==0)return SDCard_RESPONSE_FAILURE;//获得回应失利   
  179.         else return SDCard_RESPONSE_NO_ERROR;//准确回应
  180. }
  181. /*
  182. 函数功用:从sd卡读与一个数据包的内乱容
  183. 函数参数:
  184.         buf:数据缓存区
  185.         len:要读与的数据少度.
  186. 返回值:
  187. 0,胜利;其他,失利;       
  188. */
  189. unsigned char SDCardRecvData(unsigned char*buf,u16 len)
  190. {                                    
  191.         if(SDCardGetAck(0xFE))return 1;//等候SD卡发还数据肇端令牌0xFE
  192.     while(len--)//开端领受数据
  193.     {
  194.         *buf=SDCardReadWriteOneByte(0xFF);
  195.         buf++;
  196.     }
  197.     //上面是2个真CRC(dummy CRC)
  198.     SDCardReadWriteOneByte(0xFF);
  199.     SDCardReadWriteOneByte(0xFF);                                                                                                                      
  200.     return 0;//读与胜利
  201. }
  202. /*
  203. 函数功用:背sd卡写进一个数据包的内乱容 512字节
  204. 函数参数:
  205.         buf 数据缓存区
  206.         cmd 指令
  207. 返 回 值:0暗示胜利;其他值暗示失利;
  208. */
  209. unsigned char SDCardSendData(unsigned char*buf,unsigned char cmd)
  210. {       
  211.         u16 t;                            
  212.         if(SDCardWaitBusy())return 1;  //等候筹办生效
  213.         SDCardReadWriteOneByte(cmd);
  214.         if(cmd!=0XFD)//没有是完毕指令
  215.         {
  216.                 for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//进步速率,削减函数传参工夫
  217.             SDCardReadWriteOneByte(0xFF); //疏忽crc
  218.             SDCardReadWriteOneByte(0xFF);
  219.                   t=SDCardReadWriteOneByte(0xFF); //领受呼应
  220.                 if((t&0x1F)!=0x05)return 2;   //呼应毛病                                                                                                                      
  221.         }                                                                                                                                                                       
  222.     return 0;//写进胜利
  223. }
  224. /*
  225. 函数功用:背SD卡收收一个号令
  226. 函数参数:
  227.                 unsigned char cmd   号令
  228.                 unsigned int arg  号令参数
  229.                 unsigned char crc   crc校验值       
  230. 返回值:SD卡返回的呼应
  231. */                                                                                                  
  232. unsigned char SendSDCardCmd(unsigned char cmd, unsigned int arg, unsigned char crc)
  233. {
  234.         unsigned char r1;       
  235.         unsigned char Retry=0;
  236.                
  237.         SDCardCancelCS();               //打消前次片选
  238.         if(SDCardSelectCS())return 0XFF;//片选生效
  239.         //收收数据
  240.         SDCardReadWriteOneByte(cmd | 0x40);//别离写进号令
  241.         SDCardReadWriteOneByte(arg >> 24);
  242.         SDCardReadWriteOneByte(arg >> 16);
  243.         SDCardReadWriteOneByte(arg >> 8);
  244.         SDCardReadWriteOneByte(arg);          
  245.         SDCardReadWriteOneByte(crc);
  246.         if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
  247.         Retry=0X1F;
  248.         do
  249.         {
  250.                 r1=SDCardReadWriteOneByte(0xFF);
  251.         }while((r1&0X80) && Retry--);          //等候呼应,或超时退出
  252.    return r1;        //返回形态值
  253. }       
  254. /*
  255. 函数功用:获得SD卡的CID疑息,包罗制作商疑息
  256. 函数参数:unsigned char *cid_data(寄存CID的内乱存,最少16Byte)          
  257. 返 回 值:
  258.                 0:胜利,1:毛病                               
  259. */
  260. unsigned char GetSDCardCISDCardOutnfo(unsigned char *cid_data)
  261. {
  262.     unsigned char r1;          
  263.     //收SDCard_CMD10号令,读CID
  264.     r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
  265.     if(r1==0x00)
  266.           {
  267.                         r1=SDCardRecvData(cid_data,16);//领受16个字节的数据         
  268.     }
  269.         SDCardCancelCS();//打消片选
  270.         if(r1)return 1;
  271.         else return 0;
  272. }       
  273. /*
  274. 函数阐明:
  275.         获得SD卡的CSD疑息,包罗容量战速率疑息
  276. 函数参数:
  277.         unsigned char *cid_data(寄存CID的内乱存,最少16Byte)            
  278. 返 回 值:
  279.         0:胜利,1:毛病       
  280. */
  281. unsigned char GetSDCardCSSDCardOutnfo(unsigned char *csd_data)
  282. {
  283.         unsigned char r1;         
  284.         r1=SendSDCardCmd(SDCard_CMD9,0,0x01);    //收SDCard_CMD9号令,读CSD
  285.         if(r1==0)
  286.         {
  287.                 r1=SDCardRecvData(csd_data, 16);//领受16个字节的数据
  288.         }
  289.         SDCardCancelCS();//打消片选
  290.         if(r1)return 1;
  291.         else return 0;
  292. }  
  293. /*
  294. 函数功用:获得SD卡的总扇区数(扇区数)   
  295. 返 回 值:
  296.         0暗示容量检测堕落,其他值暗示SD卡的容量(扇区数/512字节)
  297. 道   明:
  298.         每扇区的字节数必为512字节,假如没有是512字节,则初初化不克不及经由过程.       
  299. */
  300. unsigned int GetSDCardSectorCount(void)
  301. {
  302.     unsigned char csd[16];
  303.     unsigned int Capacity;  
  304.     unsigned char n;
  305.           u16 csize;                                              
  306.     if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;        //与CSD疑息,假如时期堕落,返回0
  307.     if((csd[0]&0xC0)==0x40)                //V2.00的卡,假如为SDHC卡,根据上面方法计较
  308.     {       
  309.                         csize = csd[9] + ((u16)csd[8] << 8) + 1;
  310.                         Capacity = (unsigned int)csize << 10;//获得扇区数                           
  311.     }
  312.                 else//V1.XX的卡
  313.     {       
  314.                         n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
  315.                         csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
  316.                         Capacity= (unsigned int)csize << (n - 9);//获得扇区数   
  317.     }
  318.     return Capacity;
  319. }
  320. /*
  321. 函数功用: 初初化SD卡
  322. 返 回 值: 非0暗示初初化失利!
  323. */
  324. unsigned char SDCardDeviceInit(void)
  325. {
  326.   unsigned char r1;      // 寄存SD卡的返回值
  327.   u16 retry;  // 用去停止超时计数
  328.   unsigned char buf[4];  
  329.         u16 i;
  330.         SDCardSpiInit();                //初初化底层IO心
  331.        
  332.         for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //收收起码74个脉冲
  333.         retry=20;
  334.         do
  335.         {
  336.                 r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//进进IDLE形态 忙置
  337.         }while((r1!=0X01) && retry--);
  338.         SD_Type=0;   //默许无卡
  339.         if(r1==0X01)
  340.         {
  341.                 if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1)  //SD V2.0
  342.                 {
  343.                         for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);        //Get trailing return value of R7 resp
  344.                         if(buf[2]==0X01&&buf[3]==0XAA)    //卡能否撑持2.7~3.6V
  345.                         {
  346.                                 retry=0XFFFE;
  347.                                 do
  348.                                 {
  349.                                         SendSDCardCmd(SDCard_CMD55,0,0X01);            //收收SDCard_CMD55
  350.                                         r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//收收SDCard_CMD41
  351.                                 }while(r1&&retry--);
  352.                                 if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//辨别SD2.0卡版本开端
  353.                                 {
  354.                                         for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//获得OCR值
  355.                                         if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC;    //查抄CCS
  356.                                         else SD_Type=SDCard_TYPE_V2;   
  357.                                 }
  358.                         }
  359.                 }
  360.                 else//SD V1.x/ MMC        V3
  361.                 {
  362.                         SendSDCardCmd(SDCard_CMD55,0,0X01);                //收收SDCard_CMD55
  363.                         r1=SendSDCardCmd(SDCard_CMD41,0,0X01);        //收收SDCard_CMD41
  364.                         if(r1<=1)
  365.                         {               
  366.                                 SD_Type=SDCard_TYPE_V1;
  367.                                 retry=0XFFFE;
  368.                                 do //等候退出IDLE形式
  369.                                 {
  370.                                         SendSDCardCmd(SDCard_CMD55,0,0X01);        //收收SDCard_CMD55
  371.                                         r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//收收SDCard_CMD41
  372.                                 }while(r1&&retry--);
  373.                         }
  374.                         else//MMC卡没有撑持SDCard_CMD55+SDCard_CMD41辨认
  375.                         {
  376.                                 SD_Type=SDCard_TYPE_MMC;//MMC V3
  377.                                 retry=0XFFFE;
  378.                                 do //等候退出IDLE形式
  379.                                 {                                                                                            
  380.                                         r1=SendSDCardCmd(SDCard_CMD1,0,0X01);//收收SDCard_CMD1
  381.                                 }while(r1&&retry--);  
  382.                         }
  383.                         if(retry==0||SendSDCardCmd(SDCard_CMD13,512,0X01)!=0)SD_Type=SDCard_TYPE_ERR;//毛病的卡
  384.                 }
  385.         }
  386.         SDCardCancelCS();       //打消片选
  387.         if(SD_Type)return 0;  //初初化胜利返回0
  388.         else if(r1)return r1; //返回值毛病值          
  389.         return 0xaa;          //其他毛病
  390. }
  391. /*
  392. 函数功用:读SD卡
  393. 函数参数:
  394.         buf:数据缓存区
  395.         sector:扇区
  396.         cnt:扇区数
  397. 返回值:
  398.         0,ok;其他,失利.
  399. 道  明:
  400.         SD卡一个扇区巨细512字节
  401. */
  402. unsigned char SDCardReadData(unsigned char*buf,unsigned int sector,unsigned int cnt)
  403. {
  404.         unsigned char r1;
  405.         if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//转换为字节地点
  406.         if(cnt==1)
  407.         {
  408.                 r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//读号令
  409.                 if(r1==0)                                                                                                  //指令收收胜利
  410.                 {
  411.                         r1=SDCardRecvData(buf,512);                        //领受512个字节          
  412.                 }
  413.         }else
  414.         {
  415.                 r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//持续读号令
  416.                 do
  417.                 {
  418.                         r1=SDCardRecvData(buf,512);//领受512个字节         
  419.                         buf+=512;  
  420.                 }while(--cnt && r1==0);        
  421.                 SendSDCardCmd(SDCard_CMD12,0,0X01);        //收收截至号令
  422.         }   
  423.         SDCardCancelCS();//打消片选
  424.         return r1;//
  425. }
  426. /*
  427. 函数功用:背SD卡写数据
  428. 函数参数:
  429.                 buf:数据缓存区
  430.                 sector:肇端扇区
  431.                 cnt:扇区数
  432. 返回值:
  433.                 0,ok;其他,失利.
  434. 道  明:
  435.                 SD卡一个扇区巨细512字节
  436. */
  437. unsigned char SDCardWriteData(unsigned char*buf,unsigned int sector,unsigned int cnt)
  438. {
  439.         unsigned char r1;
  440.         if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//转换为字节地点
  441.         if(cnt==1)
  442.         {
  443.                 r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//读号令
  444.                 if(r1==0)//指令收收胜利
  445.                 {
  446.                         r1=SDCardSendData(buf,0xFE);//写512个字节          
  447.                 }
  448.         }
  449.         else
  450.         {
  451.                 if(SD_Type!=SDCard_TYPE_MMC)
  452.                 {
  453.                         SendSDCardCmd(SDCard_CMD55,0,0X01);       
  454.                         SendSDCardCmd(SDCard_CMD23,cnt,0X01);//收收指令       
  455.                 }
  456.                 r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//持续读号令
  457.                 if(r1==0)
  458.                 {
  459.                         do
  460.                         {
  461.                                 r1=SDCardSendData(buf,0xFC);//领受512个字节         
  462.                                 buf+=512;  
  463.                         }while(--cnt && r1==0);
  464.                         r1=SDCardSendData(0,0xFD);//领受512个字节
  465.                 }
  466.         }   
  467.         SDCardCancelCS();//打消片选
  468.         return r1;//
  469. }       
  470. /*
  471. 功用阐明:
  472. 1. 撑持文件体系格局化: #mkfs.ext2 /dev/tiny4412_block_a
  473. 2. 撑持mount挂载: #mount /dev/tiny4412_block_a /mnt/
  474. 3. 撑持磁盘巨细检察: #cat /sys/block/Tiny4412_block_a/size
  475.                      #df -h   
  476. */
  477. static struct request_queue *queue=NULL;  /* 装备恳求行列 */
  478. static struct gendisk *gd;                    /* gendisk构造 */
  479. static unsigned int sd_size=0;                  //寄存SD卡返回的容量扇区数目单元(512字节)
  480. static int Tiny4412_block_major = 0;      /*寄存主装备号*/
  481. static DEFINE_MUTEX(sd_mutex); /*静态界说互斥锁*/
  482. /*
  483. * Handle an I/O request.
  484. * 完成扇区的读写
  485. */
  486. static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)
  487. {
  488.                 /*互斥锁,上锁*/       
  489.                 mutex_lock(&sd_mutex);
  490.                 sector>>=9;
  491.                 nsect>>=9;
  492.                
  493.                 /*块装备最小单元是一个扇区,一个扇区的字节数是512字节*/
  494.                 if(write)
  495.                 {
  496.                         if(SDCardWriteData(buffer,sector,nsect))
  497.                         {
  498.                                 printk(KERN_ERR"write error!\r\n");
  499.                                 printk("write --->  nsect=%ld,sector=%ld\r\n",nsect,sector);
  500.                         }
  501.                 }
  502.                 else
  503.                 {
  504.                         if(SDCardReadData(buffer,sector,nsect))
  505.                         {
  506.                                 printk(KERN_ERR"read error!\r\n");
  507.                                 printk("read --->  nsect=%ld,sector=%ld\r\n",nsect,sector);
  508.                         }
  509.                 }
  510.                
  511.                 /*互斥锁解锁*/       
  512.                 mutex_unlock(&sd_mutex);
  513. }
  514. /*
  515. 处置恳求
  516. */
  517. static void Tiny4412_block_make_request(struct request_queue *q, struct bio *bio)
  518. {
  519.         int dir;
  520.         unsigned long long dsk_offset;
  521.         struct bio_vec *bvec;
  522.         int i;
  523.         void *iovec_mem;
  524.        
  525.         /*判定读写标的目的*/
  526.         if(bio_data_dir(bio) == WRITE) dir = 1;
  527.         else dir = 0;
  528.         dsk_offset = bio->bi_sector << 9;
  529.         bio_for_each_segment(bvec, bio, i)
  530.         {
  531.                 iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
  532.                
  533.                 //肇端地位,少度,源数据,标的目的
  534.                 Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);
  535.                
  536.                 kunmap(bvec->bv_page);
  537.                 dsk_offset += bvec->bv_len;
  538.         }
  539.         bio_endio(bio, 0);
  540. }
  541. static int tiny4412_blockdev_getgeo(struct block_device *bdev, struct hd_geometry *geo)
  542. {
  543.         /* 容量=heads*cylinders*sectors*512
  544.          * 存储容量 = 磁头数 × 磁讲(柱里)数 × 每讲扇区数 × 每扇区字节数
  545.          */
  546.         geo->heads     = 2;  /*磁头(普通一个盘里有两个磁头,正里一个/背面一个)*/
  547.         geo->cylinders = 32; /*柱里(普通一个盘里上有32个柱里)每一个盘片32个磁讲)*/
  548.         geo->sectors   = sd_size/2/32; /*扇区,普通每一个磁讲上有12个扇区,那里需求按照前里柱里战磁头停止计较,不克不及治挖*/
  549.         /*geo->sectors =存储容量/磁头数/柱里/每扇区字节数*/
  550.         return 0;
  551. }
  552. /*
  553. * 块装备文件操纵汇合接心
  554. */
  555. static struct block_device_operations Tiny4412_block_ops=
  556. {
  557.         .owner = THIS_MODULE,
  558.          /*fdisk号令分区时需求挪用该函数,用于读与磁头、柱里、扇区等疑息*/
  559.         .getgeo        = tiny4412_blockdev_getgeo,
  560. };
  561. /*
  562. 驱动进口
  563. */
  564. static int __init Tiny4412_block_init(void)
  565. {
  566.         /*1. 初初化SD心*/
  567.         if(SDCardDeviceInit())
  568.         {
  569.            printk("SD卡初初化失利!\r\n");
  570.            return -1;
  571.         }
  572.        
  573.         /*2. 检测SD卡巨细*/
  574.         sd_size=GetSDCardSectorCount();        //检测SD卡巨细,返回值左移11位获得以M为单元的容量
  575.         printk("SD卡Sizeof:%dM  secnt=%d\r\n",sd_size>>11,sd_size);
  576.        
  577.         /*注册一个块装备,主动分派主装备号*/
  578.         Tiny4412_block_major = register_blkdev(0, "Tiny4412_SDdrv");
  579.         /*静态分派恳求行列*/
  580.         queue=blk_alloc_queue(GFP_KERNEL);
  581.         /*绑定恳求行列*/
  582.         blk_queue_make_request(queue, Tiny4412_block_make_request);
  583.         /*静态分派次装备号构造*/
  584.         gd=alloc_disk(16);/*分派一个gendisk,分区是一个*/
  585.         gd->major=Tiny4412_block_major;          /*主装备号*/
  586.         gd->first_minor=0;                                     /*次装备号*/
  587.         gd->fops=&Tiny4412_block_ops;
  588.         gd->queue=queue;                                           /*将恳求行列联系关系到gendisk构造*/
  589.         snprintf(gd->disk_name, 32, "tiny4412_sd%c",&#39;a&#39;);
  590.        
  591.         /*设置磁盘构造 capacity 的容量*/
  592.         /*留意: 块装备的巨细利用扇区做为单元设置,而扇区的巨细默许是512字节。
  593.           cat /sys/block/xxxx/size 能够检察到设置的巨细
  594.           把字节为单元的巨细转换为以扇区为单元时,我们需求除以512,大概左移9位
  595.         */
  596.         set_capacity(gd,sd_size);
  597.         /*注册磁盘装备*/
  598.         add_disk(gd);
  599.         printk("块装备注册胜利!\r\n");
  600.         return 0;
  601. }
  602. /*驱动出心*/
  603. static void Tiny4412_block_exit(void)
  604. {
  605.         /*开释假造地点*/
  606.         iounmap(SD_GPBCON);
  607.         iounmap(SD_GPBDAT);
  608.        
  609.         /*登记磁盘装备*/
  610.         if(gd)del_gendisk(gd);       
  611.         /*登记块装备*/
  612.         if(Tiny4412_block_major!=0)unregister_blkdev(Tiny4412_block_major, "Tiny4412_SDdrv");
  613.        
  614.         printk("块装备注消胜利!\r\n");
  615. }
  616. module_init(Tiny4412_block_init);
  617. module_exit(Tiny4412_block_exit);
  618. MODULE_LICENSE("GPL");
复造代码
 3.4 利用SD卡编写块装备(利用默许的IO调理器)

[code]/* 参考: * drivers\block\xd.c * drivers\block\z2ram.c */#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include  #include #include #include #include #include #include #include #include #include /*--------------------------------SD相干操纵代码---------------------------------------------*//*界说指针,用于领受假造地点*/volatile unsigned int *SD_GPBCON;volatile unsigned int *SD_GPBDAT;        /*函数功用:SD初初化Tiny4412硬件毗邻:        DO--MISO :GPB_2        DI--MOSI :GPB_3        CLK-SCLK :GPB_0        CS--CS   :GPB_1*/void SDCardSpiInit(void){        /*1. 初初化GPIO*/        /*映照物理地点*/        SD_GPBCON=ioremap(0x11400040,4);        SD_GPBDAT=ioremap(0x11400044,4);                *SD_GPBCON &= ~(0xf
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请您发送邮箱:Cdnjson@163.com提供相关证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
回复 关闭延时

使用道具 举报

 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则