天天看點

Linux下編寫标準SD卡驅動(塊裝置)​

塊裝置介紹​

塊是一種具有一定結構的随機存取裝置,對這種裝置的讀寫是按塊進行的,他使用緩沖區來存放暫時的資料,待條件成熟後,從緩存一次性寫入裝置或者從裝置一次性讀到緩沖區。​

塊裝置是與字元裝置并列的概念,這兩類裝置在 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結構體。​

  • 編寫塊裝置驅動時,使用的一些機關介紹:​n 1. 扇區(Sectors):任何塊裝置硬體對資料處理的基本機關。通常,1個扇區的大小為512位元組。(對裝置而言)​

    2. 塊 (Blocks):由Linux制定對核心或檔案系統等資料處理的基本機關。通常,1個塊由1個或多個扇區組成。(對Linux作業系統而言)​

    n 3. 段(Segments):由若幹個相鄰的塊組成。是Linux記憶體管理機制中一個記憶體頁或者記憶體頁的一部分。​

  • Linux下編寫标準SD卡驅動(塊裝置)​
  • 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​

或者,使用如下指令改變核心排程算法: echo SCHEDULER > /sys/block/DEVICE/queue/scheduler​

1.2 塊裝置結構介紹​

1.2.1 核心自帶可參考的塊裝置驅動源碼​

drivers\block\z2ram.c​

drivers\block\xd.c​

\drivers\mmc\host\sdhci-s3c.c​

1.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");​

1.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釋放空間。​

如果需要通路外部硬體,比如: CD光牒、磁盤等外部實體裝置時,要設定預設的排程器,可以調用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 自旋鎖​

1.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定義。​

綁定請求隊列示例:​

blk_queue_make_request(queue, Tiny4412_block_make_request);​

1.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 :傳入的請求隊列​

1.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);​

}​

1.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);/*配置設定一個gendisk,1表示不能進行分區,隻能固定一個分區。 >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",'a'); //設定磁盤名稱,在/dev下可以檢視該名稱​

//塊裝置基本都是使用檔案系統函數進行操作,該檔案操作集合可以不用自己實作​

static struct block_device_operations Tiny4412_block_ops=​

{​

.owner = THIS_MODULE,​

};​

驅動安裝之後,檢視的節點資訊:​

  • 設定磁盤的容量

/*注意: 塊裝置的大小使用扇區作為機關設定,而扇區的大小預設是512位元組。​

可以檢視到設定的大小​

512,或者右移9位​

*/​

set_capacity(gd,TINY4412_BLKDEV_BYTES>>9);​

1.2.8 添加磁盤分區資訊到核心​

void add_disk(struct gendisk *disk)​

函數功能介紹: 将分區資訊添加到核心。​

函數參數: 填充好gendisk結構。​

示例:​

add_disk(gd);​

1.2.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​

1.3 塊裝置示例代碼​

圖1.3.1 塊裝置架構簡圖(了解整體架構)​

1.3.1 記憶體模拟塊裝置(不使用IO排程器)​

記憶體空間采用vmalloc函數進行配置設定。​

#include <linux/module.h> ​

#include <linux/blkdev.h> ​

#include <linux/hdreg.h> ​

#include <linux/version.h>​

/*​

* insmod tiny4412_blkdev.ko ​

* # or insmod tiny4412_blkdev.ko size=numK/M/G/T ​

* fdisk /dev/tiny4412_blkdev # create 2 patitions ​

* mkfs.ext2 /dev/tiny4412_blkdev1 ​

* mkfs.ext2 /dev/tiny4412_blkdev2 ​

* mount /dev/tiny4412_blkdev1 /mnt/temp1/ ​

* mount /dev/tiny4412_blkdev2 /mnt/temp2/ ​

* # play in /mnt/temp1/ and /mnt/temp2/ ​

* umount /mnt/temp1/ ​

* umount /mnt/temp2/ ​

* rmmod tiny4412_blkdev.ko ​

* ​

*/​

static int Tiny4412_block_major=0;​

static struct request_queue *tiny4412_blkdev_queue; ​

static struct gendisk *tiny4412_blkdev_disk;​

static unsigned long long tiny4412_blkdev_bytes=1024*1024*10;//10M--空間容量​

#define TINY4412_BLKDEV_BYTES_1 (1024*1024*10) /*設定塊裝置的大小*/​

static unsigned char tiny4412_blkdev_data_1[TINY4412_BLKDEV_BYTES_1]; /*用于測試塊裝置的數組大小*/​

/*​

* Handle an I/O request.​

* 實作扇區的讀寫​

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; /*寫入資料的位置*/​

unsigned long nbytes = nsect; /*寫入的長度*/​

if((offset + nbytes)>TINY4412_BLKDEV_BYTES_1)​

{​

printk("寫超出範圍,強制結束(%ld %ld)\n", offset, nbytes);​

return;​

}​

if(write) /*為真,表示是寫*/​

memcpy(tiny4412_blkdev_data_1 + offset, buffer, nbytes);​

else /*讀操作*/​

memcpy(buffer,tiny4412_blkdev_data_1 + offset, nbytes);​

}​

/*​

處理請求​

*/​

static int tiny4412_blkdev_make_request(struct request_queue *q, struct bio *bio) ​

{ ​

int dir; ​

unsigned long long dsk_offset; ​

struct bio_vec *bvec; ​

int i; ​

void *iovec_mem;​

/*判斷讀寫方向*/​

if(bio_data_dir(bio) == WRITE) dir = 1;​

else dir = 0;​

dsk_offset = bio->bi_sector << 9;​

bio_for_each_segment(bvec, bio, i) ​

{ ​

iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; ​

//起始位置,長度,源資料,方向​

Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);​

kunmap(bvec->bv_page);​

dsk_offset += bvec->bv_len; ​

}​

bio_endio(bio, 0); ​

return 0;​

}​

static int tiny4412_blockdev_getgeo(struct block_device *bdev, struct hd_geometry *geo)​

{​

/* 容量=heads*cylinders*sectors*512 ​

存儲容量 = 磁頭數 × 磁道(柱面)數 × 每道扇區數 × 每扇區位元組數​

*/​

geo->heads = 2; /*磁頭(一般一個盤面有兩個磁頭,正面一個/反面一個)*/​

geo->cylinders = 32; /*柱面(一般一個盤面上有32個柱面)每個盤片32個磁道)*/​

geo->sectors = TINY4412_BLKDEV_BYTES_1/2/32/512; /*扇區,一般每個磁道上有12個扇區,這裡需要根據前面柱面和磁頭進行計算,不能亂填*/​

return 0;​

}​

struct block_device_operations tiny4412_blkdev_fops = ​

{ ​

.owner= THIS_MODULE, ​

指令分區時需要調用該函數,用于讀取磁頭、柱面、扇區等資訊*/​

.getgeo= tiny4412_blockdev_getgeo,​

};​

static int __init tiny4412_blkdev_init(void) ​

{ ​

/*動态配置設定請求隊列*/​

tiny4412_blkdev_queue = blk_alloc_queue(GFP_KERNEL);​

/*綁定請求隊列*/​

blk_queue_make_request(tiny4412_blkdev_queue,tiny4412_blkdev_make_request);​

/*動态配置設定次裝置号結構*/​

/*每一個磁盤(分區)都是使用一個gendisk結構儲存*/​

tiny4412_blkdev_disk = alloc_disk(64); ​

/*磁盤名稱指派*/​

strcpy(tiny4412_blkdev_disk->disk_name, "tiny4412_blkdev"); ​

/*注冊一個塊裝置,自動配置設定主裝置号*/​

Tiny4412_block_major = register_blkdev(0,"Tiny4412_block");​

printk("Tiny4412_block_major=%d\n",Tiny4412_block_major);​

tiny4412_blkdev_disk->major=Tiny4412_block_major; 主裝置号*/​

tiny4412_blkdev_disk->first_minor = 0; 次裝置号*/​

tiny4412_blkdev_disk->fops = &tiny4412_blkdev_fops; /*檔案操作結合*/​

tiny4412_blkdev_disk->queue = tiny4412_blkdev_queue; /*處理資料請求的隊列*/​

/*設定磁盤結構 capacity 的容量*/​

/*注意: 塊裝置的大小使用扇區作為機關設定,而扇區的大小預設是512位元組。​

可以檢視到設定的大小​

512,或者右移9位​

*/​

set_capacity(tiny4412_blkdev_disk,tiny4412_blkdev_bytes>>9); ​

//添加磁盤資訊到核心​

add_disk(tiny4412_blkdev_disk);​

return 0;​

}​

static void __exit tiny4412_blkdev_exit(void) ​

{ ​

//删除磁盤​

del_gendisk(tiny4412_blkdev_disk);​

put_disk(tiny4412_blkdev_disk); ​

//清除隊列​

blk_cleanup_queue(tiny4412_blkdev_queue);​

/*登出塊裝置*/​

unregister_blkdev(Tiny4412_block_major, "Tiny4412_block");​

}​

module_init(tiny4412_blkdev_init); ​

module_exit(tiny4412_blkdev_exit);​

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'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_blkdev11100 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_blkdev2101160 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/​

1.3.2 使用SD卡編寫塊裝置(不使用IO排程器)​

SD卡采用SPI協定通信,底層采用模拟的SPI系統,沒有使用SPI子系統。​

  • 示例代碼:

#include <linux/module.h>​

#include <linux/moduleparam.h>​

#include <linux/init.h>​

#include <linux/sched.h>​

#include <linux/kernel.h>​

#include <linux/slab.h>​

#include <linux/fs.h> /* everything... */​

#include <linux/errno.h>/* error codes */​

#include <linux/timer.h>​

#include <linux/types.h>/* size_t */​

#include <linux/fcntl.h>/* O_ACCMODE */​

#include <linux/hdreg.h>/* HDIO_GETGEO */​

#include <linux/kdev_t.h>​

#include <linux/vmalloc.h>​

#include <linux/genhd.h>​

#include <linux/blkdev.h>​

#include <linux/bio.h>​

#include <linux/init.h>​

#include <linux/module.h>​

#include <linux/ioctl.h>​

#include <linux/fs.h>​

#include <linux/device.h>​

#include <linux/err.h>​

#include <linux/list.h>​

#include <linux/errno.h>​

#include <linux/mutex.h>​

#include <linux/slab.h>​

#include <linux/compat.h>​

#include <linux/spi/spi.h>​

#include <linux/spi/spidev.h>​

#include <asm/uaccess.h>​

#include <linux/gpio.h>​

#include <mach/gpio.h>​

#include <plat/gpio-cfg.h>​

#include <linux/delay.h>​

#include <linux/io.h>​

#include <linux/mutex.h>​

#include <linux/miscdevice.h> /*雜項字元裝置頭檔案*/​

/*--------------------------------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 << 0 * 4);*SD_GPBCON |= (0x1 << 0 * 4);​

*SD_GPBCON &= ~(0xf << 1 * 4);*SD_GPBCON |= (0x1 << 1 * 4);​

*SD_GPBCON &= ~(0xf << 2 * 4);​

*SD_GPBCON &= ~(0xf << 3 * 4);*SD_GPBCON |= (0x1 << 3 * 4);​

/*2. 上拉GPIO口*/​

//*SD_GPBDAT &= ~(1 << 4);//輸出0​

*SD_GPBDAT |= (1 << 0); //輸出1​

*SD_GPBDAT |= (1 << 1); //輸出1​

*SD_GPBDAT |= (1 << 3); //輸出1​

}​

// SD卡類型定義 ​

#define SDCard_TYPE_ERR 0X00 //卡類型錯誤​

#define SDCard_TYPE_MMC 0X01 //MMC卡​

#define SDCard_TYPE_V1 0X02​

#define SDCard_TYPE_V2 0X04​

#define SDCard_TYPE_V2HC 0X06​

// SD卡指令表 ​

#define SDCard_CMD0 0 //卡複位​

#define SDCard_CMD1 1​

#define SDCard_CMD8 8 //指令8 ,SEND_IF_COND​

#define SDCard_CMD9 9 //指令9 ,讀CSD資料​

#define SDCard_CMD10 10 //指令10,讀CID資料​

#define SDCard_CMD12 12 //指令12,停止資料傳輸​

#define SDCard_CMD13 16 //指令16,設定扇區大小 應傳回0x00​

#define SDCard_CMD17 17 //指令17,讀扇區​

#define SDCard_CMD18 18 //指令18,讀Multi 扇區​

#define SDCard_CMD23 23 //指令23,設定多扇區寫入前預先擦除N個block​

#define SDCard_CMD24 24 //指令24,寫扇區​

#define SDCard_CMD25 25 //指令25,寫多個扇區​

#define SDCard_CMD41 41 //指令41,應傳回0x00​

#define SDCard_CMD55 55 //指令55,應傳回0x01​

#define SDCard_CMD58 58 //指令58,讀OCR資訊​

#define SDCard_CMD59 59 //指令59,使能/禁止CRC,應傳回0x00、​

/*SD卡回應标記字*/​

#define SDCard_RESPONSE_NO_ERROR 0x00 //正确回應​

#define SDCard_SD_IN_IDLE_STATE 0x01 //閑置狀态​

#define SDCard_SD_ERASE_RESET 0x02 //擦除複位​

#define SDCard_RESPONSE_FAILURE 0xFF //響應失敗​

//函數聲明 ​

unsigned char SDCardReadWriteOneByte(unsigned char data); //底層接口,SPI讀寫位元組函數​

unsigned char SDCardWaitBusy(void);//等待SD卡準備​

unsigned char SDCardGetAck(unsigned char Response);//獲得應答​

unsigned char SDCardDeviceInit(void);初始化​

unsigned char SDCardReadData(unsigned char*buf,unsigned int sector,unsigned int cnt);讀塊(扇區)​

unsigned char SDCardWriteData(unsigned char*buf,unsigned int sector,unsigned int cnt);寫塊(扇區)​

unsigned int GetSDCardSectorCount(void); 讀扇區數​

unsigned char GetSDCardCISDCardOutnfo(unsigned char *cid_data); //讀SD卡CID​

unsigned char GetSDCardCSSDCardOutnfo(unsigned char *csd_data); //讀SD卡CSD​

static unsigned char SD_Type=0; //存放SD卡的類型​

/*​

函數功能:SD卡底層接口,通過SPI時序向SD卡讀寫一個位元組​

函數參數:data是要寫入的資料​

返 回 值:讀到的資料​

說明:時序是第二個上升沿采集資料​

*/​

unsigned char SDCardReadWriteOneByte(unsigned char data_tx)​

{​

u8 data_rx=0;​

u8 i;​

for(i=0;i<8;i++)​

{​

*SD_GPBDAT &= ~(1 << 0);//輸出0​

if(data_tx&0x80)*SD_GPBDAT |= (1 << 3); //輸出1​

else *SD_GPBDAT &= ~(1 << 3);//輸出0​

data_tx<<=1; //繼續發送下一個資料​

*SD_GPBDAT |= (1 << 0); //輸出1​

data_rx<<=1;​

if((*SD_GPBDAT & (1 << 2)))data_rx|=0x01;​

}​

return data_rx;​

}​

/*​

函數功能:取消選擇,釋放SPI總線​

*/​

void SDCardCancelCS(void)​

{​

*SD_GPBDAT |= (1 << 1);​

SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘​

}​

/*​

函數 功 能:選擇sd卡,并且等待卡準備OK​

函數傳回值:0,成功;1,失敗;​

*/​

unsigned char SDCardSelectCS(void)​

{​

*SD_GPBDAT &= ~(1 << 1);​

if(SDCardWaitBusy()==0)return 0;//等待成功​

SDCardCancelCS();​

return 1;//等待失敗​

}​

/*​

函數 功 能:等待卡準備好​

函數傳回值:0,準備好了;其他,錯誤代碼​

*/​

unsigned char SDCardWaitBusy(void)​

{​

unsigned int t=0;​

do​

{​

if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK​

t++;​

}while(t<0xFFFFFF);//等待 ​

return 1;​

}​

/*​

函數功能:等待SD卡回應​

函數參數:​

Response:要得到的回應值​

返 回 值:​

0,成功得到了該回應值​

其他,得到回應值失敗​

*/​

unsigned char SDCardGetAck(unsigned char Response)​

{​

u16 Count=0xFFFF;//等待次數​

while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到準确的回應 ​

if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回應失敗 ​

else return SDCard_RESPONSE_NO_ERROR;//正确回應​

}​

/*​

函數功能:從sd卡讀取一個資料包的内容​

函數參數:​

buf:資料緩存區​

len:要讀取的資料長度.​

傳回值:​

0,成功;其他,失敗;​

*/​

unsigned char SDCardRecvData(unsigned char*buf,u16 len)​

{​

if(SDCardGetAck(0xFE))return 1;//等待SD卡發回資料起始令牌0xFE​

開始接收資料​

{​

*buf=SDCardReadWriteOneByte(0xFF);​

buf++;​

}​

下面是2個僞CRC(dummy CRC)​

SDCardReadWriteOneByte(0xFF);​

SDCardReadWriteOneByte(0xFF);​

讀取成功​

}​

/*​

函數功能:向sd卡寫入一個資料包的内容 512位元組​

函數參數:​

buf 資料緩存區​

cmd 指令​

返 回 值:0表示成功;其他值表示失敗;​

*/​

unsigned char SDCardSendData(unsigned char*buf,unsigned char cmd)​

{​

u16 t;​

if(SDCardWaitBusy())return 1; //等待準備失效​

SDCardReadWriteOneByte(cmd);​

if(cmd!=0XFD)//不是結束指令​

{​

for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,減少函數傳參時間​

忽略crc​

SDCardReadWriteOneByte(0xFF);​

接收響應​

if((t&0x1F)!=0x05)return 2; //響應錯誤​

}​

寫入成功​

}​

/*​

函數功能:向SD卡發送一個指令​

函數參數:​

unsigned char cmd 指令 ​

unsigned int arg 指令參數​

unsigned char crc crc校驗值​

傳回值:SD卡傳回的響應​

*/​

unsigned char SendSDCardCmd(unsigned char cmd, unsigned int arg, unsigned char crc)​

{​

unsigned char r1;​

unsigned char Retry=0; ​

SDCardCancelCS(); //取消上次片選​

if(SDCardSelectCS())return 0XFF;//片選失效 ​

//發送資料​

SDCardReadWriteOneByte(cmd | 0x40);//分别寫入指令​

SDCardReadWriteOneByte(arg >> 24);​

SDCardReadWriteOneByte(arg >> 16);​

SDCardReadWriteOneByte(arg >> 8);​

SDCardReadWriteOneByte(arg);​

SDCardReadWriteOneByte(crc); ​

if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading​

Retry=0X1F;​

do​

{​

r1=SDCardReadWriteOneByte(0xFF);​

}while((r1&0X80) && Retry--);等待響應,或逾時退出​

return r1;//傳回狀态值​

}​

/*​

函數功能:擷取SD卡的CID資訊,包括制造商資訊​

函數參數:unsigned char *cid_data(存放CID的記憶體,至少16Byte)​

返 回 值:​

0:成功,1:錯誤​

*/​

unsigned char GetSDCardCISDCardOutnfo(unsigned char *cid_data)​

{​

unsigned char r1;​

發SDCard_CMD10指令,讀CID​

r1=SendSDCardCmd(SDCard_CMD10,0,0x01);​

if(r1==0x00)​

{​

r1=SDCardRecvData(cid_data,16);//接收16個位元組的資料​

}​

SDCardCancelCS();//取消片選​

if(r1)return 1;​

else return 0;​

}​

/*​

函數說明:​

擷取SD卡的CSD資訊,包括容量和速度資訊​

函數參數:​

unsigned char *cid_data(存放CID的記憶體,至少16Byte)​

返 回 值:​

0:成功,1:錯誤​

*/​

unsigned char GetSDCardCSSDCardOutnfo(unsigned char *csd_data)​

{​

unsigned char r1;​

r1=SendSDCardCmd(SDCard_CMD9,0,0x01); //發SDCard_CMD9指令,讀CSD​

if(r1==0)​

{​

r1=SDCardRecvData(csd_data, 16);//接收16個位元組的資料 ​

}​

SDCardCancelCS();//取消片選​

if(r1)return 1;​

else return 0;​

} ​

/*​

函數功能:擷取SD卡的總扇區數(扇區數) ​

返 回 值:​

0表示容量檢測出錯,其他值表示SD卡的容量(扇區數/512位元組)​

說 明:​

每扇區的位元組數必為512位元組,如果不是512位元組,則初始化不能通過.​

*/​

unsigned int GetSDCardSectorCount(void)​

{​

unsigned char csd[16];​

unsigned int Capacity; ​

unsigned char n;​

u16 csize; ​

if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;//取CSD資訊,如果期間出錯,傳回0​

if((csd[0]&0xC0)==0x40)的卡,如果為SDHC卡,按照下面方式計算​

{​

csize = csd[9] + ((u16)csd[8] << 8) + 1;​

Capacity = (unsigned int)csize << 10;//得到扇區數​

}​

else//V1.XX的卡 ​

{​

n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;​

csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;​

Capacity= (unsigned int)csize << (n - 9);//得到扇區數 ​

}​

return Capacity;​

}​

/*​

函數功能: 初始化SD卡​

返 回 值: 非0表示初始化失敗!​

*/​

unsigned char SDCardDeviceInit(void)​

{​

存放SD卡的傳回值​

用來進行逾時計數​

unsigned char buf[4]; ​

u16 i;​

SDCardSpiInit();//初始化底層IO口​

for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //發送最少74個脈沖​

retry=20;​

do​

{​

r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//進入IDLE狀态 閑置​

}while((r1!=0X01) && retry--);​

SD_Type=0; //預設無卡​

if(r1==0X01)​

{​

if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1) //SD V2.0​

{​

for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//Get trailing return value of R7 resp​

if(buf[2]==0X01&&buf[3]==0XAA) //卡是否支援2.7~3.6V​

{​

retry=0XFFFE;​

do​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);發送SDCard_CMD55​

r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//發送SDCard_CMD41​

}while(r1&&retry--);​

if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鑒别SD2.0卡版本開始​

{​

for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值​

if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC; //檢查CCS​

else SD_Type=SDCard_TYPE_V2; ​

}​

}​

}​

else//SD V1.x/ MMCV3​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);//發送SDCard_CMD55​

r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//發送SDCard_CMD41​

if(r1<=1)​

{​

SD_Type=SDCard_TYPE_V1;​

retry=0XFFFE;​

do //等待退出IDLE模式​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);//發送SDCard_CMD55​

r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//發送SDCard_CMD41​

}while(r1&&retry--);​

}​

else//MMC卡不支援SDCard_CMD55+SDCard_CMD41識别​

{​

SD_Type=SDCard_TYPE_MMC;//MMC V3​

retry=0XFFFE;​

do //等待退出IDLE模式​

{​

r1=SendSDCardCmd(SDCard_CMD1,0,0X01);//發送SDCard_CMD1​

}while(r1&&retry--); ​

}​

if(retry==0||SendSDCardCmd(SDCard_CMD13,512,0X01)!=0)SD_Type=SDCard_TYPE_ERR;//錯誤的卡​

}​

}​

SDCardCancelCS(); //取消片選​

if(SD_Type)return 0; //初始化成功傳回0​

else if(r1)return r1; //傳回值錯誤值​

return 0xaa; //其他錯誤​

}​

/*​

函數功能:讀SD卡​

函數參數:​

buf:資料緩存區​

sector:扇區​

cnt:扇區數​

傳回值:​

0,ok;其他,失敗.​

說 明:​

SD卡一個扇區大小512位元組​

*/​

unsigned char SDCardReadData(unsigned char*buf,unsigned int sector,unsigned int cnt)​

{​

unsigned char r1;​

if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//轉換為位元組位址​

if(cnt==1)​

{​

r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//讀指令​

if(r1==0)指令發送成功​

{​

r1=SDCardRecvData(buf,512);//接收512個位元組​

}​

}else​

{​

r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//連續讀指令​

do​

{​

r1=SDCardRecvData(buf,512);//接收512個位元組​

buf+=512; ​

}while(--cnt && r1==0); ​

SendSDCardCmd(SDCard_CMD12,0,0X01);//發送停止指令​

} ​

SDCardCancelCS();//取消片選​

return r1;//​

}​

/*​

函數功能:向SD卡寫資料​

函數參數:​

buf:資料緩存區​

sector:起始扇區​

cnt:扇區數​

傳回值:​

0,ok;其他,失敗.​

說 明:​

SD卡一個扇區大小512位元組​

*/​

unsigned char SDCardWriteData(unsigned char*buf,unsigned int sector,unsigned int cnt)​

{​

unsigned char r1;​

if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//轉換為位元組位址​

if(cnt==1)​

{​

r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//讀指令​

if(r1==0)//指令發送成功​

{​

r1=SDCardSendData(buf,0xFE);//寫512個位元組​

}​

}​

else​

{​

if(SD_Type!=SDCard_TYPE_MMC)​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);​

SendSDCardCmd(SDCard_CMD23,cnt,0X01);//發送指令​

}​

r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//連續讀指令​

if(r1==0)​

{​

do​

{​

r1=SDCardSendData(buf,0xFC);//接收512個位元組​

buf+=512; ​

}while(--cnt && r1==0);​

r1=SDCardSendData(0,0xFD);//接收512個位元組 ​

}​

} ​

SDCardCancelCS();//取消片選​

return r1;//​

}​

/*​

功能說明:​

1. 支援檔案系統格式化: #mkfs.ext2 /dev/tiny4412_block_a​

2. 支援mount挂載: #mount /dev/tiny4412_block_a /mnt/​

3. 支援磁盤大小檢視: #cat /sys/block/Tiny4412_block_a/size​

#df -h ​

*/​

static struct request_queue *queue=NULL; /* 裝置請求隊列 */​

static struct gendisk *gd; 結構 */​

static unsigned int sd_size=0; 存放SD卡傳回的容量扇區數量機關(512位元組)​

static int Tiny4412_block_major = 0; /*存放主裝置号*/​

static DEFINE_MUTEX(sd_mutex); /*靜态定義互斥鎖*/​

/*​

* Handle an I/O request.​

* 實作扇區的讀寫​

*/​

static void Tiny4412_block_dev_sector_read_write(unsigned long sector,unsigned long nsect, char *buffer, int write)​

{​

/*互斥鎖,上鎖*/​

mutex_lock(&sd_mutex);​

sector>>=9;​

nsect>>=9;​

/*塊裝置最小機關是一個扇區,一個扇區的位元組數是512位元組*/​

if(write)​

{​

if(SDCardWriteData(buffer,sector,nsect))​

{​

printk(KERN_ERR"write error!\r\n");​

printk("write ---> nsect=%ld,sector=%ld\r\n",nsect,sector);​

}​

}​

else​

{​

if(SDCardReadData(buffer,sector,nsect))​

{​

printk(KERN_ERR"read error!\r\n");​

printk("read ---> nsect=%ld,sector=%ld\r\n",nsect,sector);​

}​

}​

/*互斥鎖解鎖*/​

mutex_unlock(&sd_mutex);​

}​

/*​

處理請求​

*/​

static void Tiny4412_block_make_request(struct request_queue *q, struct bio *bio) ​

{ ​

int dir; ​

unsigned long long dsk_offset; ​

struct bio_vec *bvec; ​

int i; ​

void *iovec_mem;​

/*判斷讀寫方向*/​

if(bio_data_dir(bio) == WRITE) dir = 1;​

else dir = 0;​

dsk_offset = bio->bi_sector << 9;​

bio_for_each_segment(bvec, bio, i) ​

{ ​

iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset; ​

//起始位置,長度,源資料,方向​

Tiny4412_block_dev_sector_read_write(dsk_offset,bvec->bv_len,iovec_mem,dir);​

kunmap(bvec->bv_page);​

dsk_offset += bvec->bv_len; ​

}​

bio_endio(bio, 0); ​

}​

static int tiny4412_blockdev_getgeo(struct block_device *bdev, struct hd_geometry *geo)​

{​

/* 容量=heads*cylinders*sectors*512 ​

存儲容量 = 磁頭數 × 磁道(柱面)數 × 每道扇區數 × 每扇區位元組數​

*/​

geo->heads = 2; /*磁頭(一般一個盤面有兩個磁頭,正面一個/反面一個)*/​

geo->cylinders = 32; /*柱面(一般一個盤面上有32個柱面)每個盤片32個磁道)*/​

geo->sectors = sd_size/2/32; /*扇區,一般每個磁道上有12個扇區,這裡需要根據前面柱面和磁頭進行計算,不能亂填*/​

/*geo->sectors =存儲容量/磁頭數/柱面/每扇區位元組數*/​

return 0;​

}​

/*​

* 塊裝置檔案操作集合接口​

*/​

static struct block_device_operations Tiny4412_block_ops=​

{​

.owner = THIS_MODULE,​

指令分區時需要調用該函數,用于讀取磁頭、柱面、扇區等資訊*/​

.getgeo= tiny4412_blockdev_getgeo,​

};​

/*​

驅動入口​

*/​

static int __init Tiny4412_block_init(void)​

{​

/*1. 初始化SD口*/​

if(SDCardDeviceInit()) ​

{​

卡初始化失敗!\r\n");​

return -1;​

}​

/*2. 檢測SD卡大小*/​

sd_size=GetSDCardSectorCount();//檢測SD卡大小,傳回值右移11位得到以M為機關的容量​

printk("SD卡Sizeof:%dM secnt=%d\r\n",sd_size>>11,sd_size);​

/*注冊一個塊裝置,自動配置設定主裝置号*/​

Tiny4412_block_major = register_blkdev(0, "Tiny4412_SDdrv");​

/*動态配置設定請求隊列*/​

queue=blk_alloc_queue(GFP_KERNEL);​

/*綁定請求隊列*/​

blk_queue_make_request(queue, Tiny4412_block_make_request);​

/*動态配置設定次裝置号結構*/​

gd=alloc_disk(16);/*配置設定一個gendisk,分區是一個*/​

gd->major=Tiny4412_block_major; 主裝置号*/​

gd->first_minor=0;次裝置号*/​

gd->fops=&Tiny4412_block_ops;​

gd->queue=queue; 将請求隊列關聯到gendisk結構*/​

snprintf(gd->disk_name, 32, "tiny4412_sd%c",'a');​

/*設定磁盤結構 capacity 的容量*/​

/*注意: 塊裝置的大小使用扇區作為機關設定,而扇區的大小預設是512位元組。​

可以檢視到設定的大小​

512,或者右移9位​

*/​

set_capacity(gd,sd_size);​

/*注冊磁盤裝置*/​

add_disk(gd);​

printk("塊裝置注冊成功!\r\n");​

return 0;​

}​

/*驅動出口*/​

static void Tiny4412_block_exit(void)​

{​

/*釋放虛拟位址*/​

iounmap(SD_GPBCON);​

iounmap(SD_GPBDAT);​

/*登出磁盤裝置*/​

if(gd)del_gendisk(gd);​

/*登出塊裝置*/​

if(Tiny4412_block_major!=0)unregister_blkdev(Tiny4412_block_major, "Tiny4412_SDdrv");​

printk("塊裝置注消成功!\r\n");​

}​

module_init(Tiny4412_block_init);​

module_exit(Tiny4412_block_exit);​

MODULE_LICENSE("GPL");​

1.3.3 記憶體模拟塊裝置(使用預設的IO排程器)​

  • 示例代碼:

/* 參考:搜尋注冊函數即可​

* drivers\block\xd.c​

* drivers\block\z2ram.c​

*/​

#include <linux/module.h>​

#include <linux/errno.h>​

#include <linux/interrupt.h>​

#include <linux/mm.h>​

#include <linux/fs.h>​

#include <linux/kernel.h>​

#include <linux/timer.h>​

#include <linux/genhd.h>​

#include <linux/hdreg.h>​

#include <linux/ioport.h>​

#include <linux/init.h>​

#include <linux/wait.h>​

#include <linux/blkdev.h>​

#include <linux/blkpg.h>​

#include <linux/delay.h>​

#include <linux/io.h>​

#include <linux/vmalloc.h>​

#include <linux/hdreg.h> ​

#include <linux/version.h>​

#include <asm/system.h>​

#include <asm/uaccess.h>​

#include <asm/dma.h>​

static struct gendisk *tiny4412_blockdev_disk;​

static struct request_queue *tiny4412_blockdev_queue;​

static int major;​

static DEFINE_SPINLOCK(tiny4412_blockdev_lock);​

#define RAMBLOCK_SIZE (1024*1024*10) /*10M空間*/​

static unsigned char *tiny4412_blockdev_buf=NULL;​

static int tiny4412_blockdev_getgeo(struct block_device *bdev, struct hd_geometry *geo)​

{​

/* 容量=heads*cylinders*sectors*512 ​

存儲容量 = 磁頭數 × 磁道(柱面)數 × 每道扇區數 × 每扇區位元組數​

*/​

geo->heads = 2; /*磁頭(一般一個盤面有兩個磁頭,正面一個/反面一個)*/​

geo->cylinders = 32; /*柱面(一般一個盤面上有32個柱面)每個盤片32個磁道)*/​

geo->sectors = RAMBLOCK_SIZE/2/32/512; /*扇區,一般每個磁道上有12個扇區,這裡需要根據前面柱面和磁頭進行計算,不能亂填*/​

return 0;​

}​

static struct block_device_operations tiny4412_blockdev_fops = {​

.owner= THIS_MODULE,​

.getgeo= tiny4412_blockdev_getgeo,​

};​

static void do_tiny4412_blockdev_request(struct request_queue * q)​

{​

struct request *req;​

req = blk_fetch_request(q);​

while (req) ​

{​

unsigned long start = blk_rq_pos(req) *512; /*轉為位元組機關(起始位置)*/​

unsigned long len = blk_rq_cur_bytes(req); /*目前操作的位元組數量*/​

int err = 0;​

if (rq_data_dir(req) == READ) /*如果是讀*/​

memcpy(req->buffer, tiny4412_blockdev_buf+start, len);​

else​

memcpy(tiny4412_blockdev_buf+start, req->buffer,len);​

if (!__blk_end_request_cur(req, err)) /*判斷是否處理完畢請求*/​

req = blk_fetch_request(q); /*繼續處理下一個請求*/​

}​

}​

static int tiny4412_blockdev_init(void)​

{​

/* 1. 配置設定一個gendisk結構體 */​

tiny4412_blockdev_disk = alloc_disk(16); /* 次裝置号個數: 分區個數+1 */​

/* 2. 設定 */​

/* 2.1 配置設定/設定隊列: 提供讀寫能力 */​

tiny4412_blockdev_queue = blk_init_queue(do_tiny4412_blockdev_request, &tiny4412_blockdev_lock);​

tiny4412_blockdev_disk->queue = tiny4412_blockdev_queue;​

/* 2.2 設定其他屬性: 比如容量 */​

major = register_blkdev(0, "tiny4412_blockdev"); /* cat /proc/devices */​

tiny4412_blockdev_disk->major = major;​

tiny4412_blockdev_disk->first_minor = 0;​

sprintf(tiny4412_blockdev_disk->disk_name, "tiny4412_blockdev");​

tiny4412_blockdev_disk->fops = &tiny4412_blockdev_fops;​

set_capacity(tiny4412_blockdev_disk, RAMBLOCK_SIZE / 512);​

/* 3. 硬體相關操作 */​

tiny4412_blockdev_buf = vmalloc(RAMBLOCK_SIZE);​

if(tiny4412_blockdev_buf==NULL)​

{​

printk("空間申請失敗!\n");​

return -1;​

}​

/* 4. 注冊 */​

add_disk(tiny4412_blockdev_disk);​

return 0;​

}​

static void tiny4412_blockdev_exit(void)​

{​

unregister_blkdev(major, "tiny4412_blockdev");​

del_gendisk(tiny4412_blockdev_disk);​

put_disk(tiny4412_blockdev_disk);​

blk_cleanup_queue(tiny4412_blockdev_queue);​

vfree(tiny4412_blockdev_buf);​

}​

module_init(tiny4412_blockdev_init);​

module_exit(tiny4412_blockdev_exit);​

MODULE_LICENSE("GPL");​

1.3.4 使用SD卡編寫塊裝置(使用預設的IO排程器)​

/* 參考:​

* drivers\block\xd.c​

* drivers\block\z2ram.c​

*/​

#include <linux/module.h>​

#include <linux/errno.h>​

#include <linux/interrupt.h>​

#include <linux/mm.h>​

#include <linux/fs.h>​

#include <linux/kernel.h>​

#include <linux/timer.h>​

#include <linux/genhd.h>​

#include <linux/hdreg.h>​

#include <linux/ioport.h>​

#include <linux/init.h>​

#include <linux/wait.h>​

#include <linux/blkdev.h>​

#include <linux/blkpg.h>​

#include <linux/delay.h>​

#include <linux/io.h>​

#include <linux/vmalloc.h>​

#include <linux/hdreg.h> ​

#include <linux/version.h>​

#include <asm/system.h>​

#include <asm/uaccess.h>​

#include <asm/dma.h>​

#include <linux/gpio.h>​

#include <mach/gpio.h>​

#include <plat/gpio-cfg.h>​

#include <linux/delay.h>​

#include <linux/io.h>​

/*--------------------------------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 << 0 * 4);*SD_GPBCON |= (0x1 << 0 * 4);​

*SD_GPBCON &= ~(0xf << 1 * 4);*SD_GPBCON |= (0x1 << 1 * 4);​

*SD_GPBCON &= ~(0xf << 2 * 4);​

*SD_GPBCON &= ~(0xf << 3 * 4);*SD_GPBCON |= (0x1 << 3 * 4);​

/*2. 上拉GPIO口*/​

//*SD_GPBDAT &= ~(1 << 4);//輸出0​

*SD_GPBDAT |= (1 << 0); //輸出1​

*SD_GPBDAT |= (1 << 1); //輸出1​

*SD_GPBDAT |= (1 << 3); //輸出1​

}​

// SD卡類型定義 ​

#define SDCard_TYPE_ERR 0X00 //卡類型錯誤​

#define SDCard_TYPE_MMC 0X01 //MMC卡​

#define SDCard_TYPE_V1 0X02​

#define SDCard_TYPE_V2 0X04​

#define SDCard_TYPE_V2HC 0X06​

// SD卡指令表 ​

#define SDCard_CMD0 0 //卡複位​

#define SDCard_CMD1 1​

#define SDCard_CMD8 8 //指令8 ,SEND_IF_COND​

#define SDCard_CMD9 9 //指令9 ,讀CSD資料​

#define SDCard_CMD10 10 //指令10,讀CID資料​

#define SDCard_CMD12 12 //指令12,停止資料傳輸​

#define SDCard_CMD13 16 //指令16,設定扇區大小 應傳回0x00​

#define SDCard_CMD17 17 //指令17,讀扇區​

#define SDCard_CMD18 18 //指令18,讀Multi 扇區​

#define SDCard_CMD23 23 //指令23,設定多扇區寫入前預先擦除N個block​

#define SDCard_CMD24 24 //指令24,寫扇區​

#define SDCard_CMD25 25 //指令25,寫多個扇區​

#define SDCard_CMD41 41 //指令41,應傳回0x00​

#define SDCard_CMD55 55 //指令55,應傳回0x01​

#define SDCard_CMD58 58 //指令58,讀OCR資訊​

#define SDCard_CMD59 59 //指令59,使能/禁止CRC,應傳回0x00、​

/*SD卡回應标記字*/​

#define SDCard_RESPONSE_NO_ERROR 0x00 //正确回應​

#define SDCard_SD_IN_IDLE_STATE 0x01 //閑置狀态​

#define SDCard_SD_ERASE_RESET 0x02 //擦除複位​

#define SDCard_RESPONSE_FAILURE 0xFF //響應失敗​

//函數聲明 ​

unsigned char SDCardReadWriteOneByte(unsigned char data); //底層接口,SPI讀寫位元組函數​

unsigned char SDCardWaitBusy(void);//等待SD卡準備​

unsigned char SDCardGetAck(unsigned char Response);//獲得應答​

unsigned char SDCardDeviceInit(void);初始化​

unsigned char SDCardReadData(unsigned char*buf,unsigned int sector,unsigned int cnt);讀塊(扇區)​

unsigned char SDCardWriteData(unsigned char*buf,unsigned int sector,unsigned int cnt);寫塊(扇區)​

unsigned int GetSDCardSectorCount(void); 讀扇區數​

unsigned char GetSDCardCISDCardOutnfo(unsigned char *cid_data); //讀SD卡CID​

unsigned char GetSDCardCSSDCardOutnfo(unsigned char *csd_data); //讀SD卡CSD​

static unsigned char SD_Type=0; //存放SD卡的類型​

/*​

函數功能:SD卡底層接口,通過SPI時序向SD卡讀寫一個位元組​

函數參數:data是要寫入的資料​

返 回 值:讀到的資料​

說明:時序是第二個上升沿采集資料​

*/​

unsigned char SDCardReadWriteOneByte(unsigned char data_tx)​

{​

u8 data_rx=0;​

u8 i;​

for(i=0;i<8;i++)​

{​

*SD_GPBDAT &= ~(1 << 0);//輸出0​

if(data_tx&0x80)*SD_GPBDAT |= (1 << 3); //輸出1​

else *SD_GPBDAT &= ~(1 << 3);//輸出0​

data_tx<<=1; //繼續發送下一個資料​

*SD_GPBDAT |= (1 << 0); //輸出1​

data_rx<<=1;​

if((*SD_GPBDAT & (1 << 2)))data_rx|=0x01;​

}​

return data_rx;​

}​

/*​

函數功能:取消選擇,釋放SPI總線​

*/​

void SDCardCancelCS(void)​

{​

*SD_GPBDAT |= (1 << 1);​

SDCardReadWriteOneByte(0xff);//提供額外的8個時鐘​

}​

/*​

函數 功 能:選擇sd卡,并且等待卡準備OK​

函數傳回值:0,成功;1,失敗;​

*/​

unsigned char SDCardSelectCS(void)​

{​

*SD_GPBDAT &= ~(1 << 1);​

if(SDCardWaitBusy()==0)return 0;//等待成功​

SDCardCancelCS();​

return 1;//等待失敗​

}​

/*​

函數 功 能:等待卡準備好​

函數傳回值:0,準備好了;其他,錯誤代碼​

*/​

unsigned char SDCardWaitBusy(void)​

{​

unsigned int t=0;​

do​

{​

if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK​

t++;​

}while(t<0xFFFFFF);//等待 ​

return 1;​

}​

/*​

函數功能:等待SD卡回應​

函數參數:​

Response:要得到的回應值​

返 回 值:​

0,成功得到了該回應值​

其他,得到回應值失敗​

*/​

unsigned char SDCardGetAck(unsigned char Response)​

{​

u16 Count=0xFFFF;//等待次數​

while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到準确的回應 ​

if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回應失敗 ​

else return SDCard_RESPONSE_NO_ERROR;//正确回應​

}​

/*​

函數功能:從sd卡讀取一個資料包的内容​

函數參數:​

buf:資料緩存區​

len:要讀取的資料長度.​

傳回值:​

0,成功;其他,失敗;​

*/​

unsigned char SDCardRecvData(unsigned char*buf,u16 len)​

{​

if(SDCardGetAck(0xFE))return 1;//等待SD卡發回資料起始令牌0xFE​

開始接收資料​

{​

*buf=SDCardReadWriteOneByte(0xFF);​

buf++;​

}​

下面是2個僞CRC(dummy CRC)​

SDCardReadWriteOneByte(0xFF);​

SDCardReadWriteOneByte(0xFF);​

讀取成功​

}​

/*​

函數功能:向sd卡寫入一個資料包的内容 512位元組​

函數參數:​

buf 資料緩存區​

cmd 指令​

返 回 值:0表示成功;其他值表示失敗;​

*/​

unsigned char SDCardSendData(unsigned char*buf,unsigned char cmd)​

{​

u16 t;​

if(SDCardWaitBusy())return 1; //等待準備失效​

SDCardReadWriteOneByte(cmd);​

if(cmd!=0XFD)//不是結束指令​

{​

for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,減少函數傳參時間​

忽略crc​

SDCardReadWriteOneByte(0xFF);​

接收響應​

if((t&0x1F)!=0x05)return 2; //響應錯誤​

}​

寫入成功​

}​

/*​

函數功能:向SD卡發送一個指令​

函數參數:​

unsigned char cmd 指令 ​

unsigned int arg 指令參數​

unsigned char crc crc校驗值​

傳回值:SD卡傳回的響應​

*/​

unsigned char SendSDCardCmd(unsigned char cmd, unsigned int arg, unsigned char crc)​

{​

unsigned char r1;​

unsigned char Retry=0; ​

SDCardCancelCS(); //取消上次片選​

if(SDCardSelectCS())return 0XFF;//片選失效 ​

//發送資料​

SDCardReadWriteOneByte(cmd | 0x40);//分别寫入指令​

SDCardReadWriteOneByte(arg >> 24);​

SDCardReadWriteOneByte(arg >> 16);​

SDCardReadWriteOneByte(arg >> 8);​

SDCardReadWriteOneByte(arg);​

SDCardReadWriteOneByte(crc); ​

if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading​

Retry=0X1F;​

do​

{​

r1=SDCardReadWriteOneByte(0xFF);​

}while((r1&0X80) && Retry--);等待響應,或逾時退出​

return r1;//傳回狀态值​

}​

/*​

函數功能:擷取SD卡的CID資訊,包括制造商資訊​

函數參數:unsigned char *cid_data(存放CID的記憶體,至少16Byte)​

返 回 值:​

0:成功,1:錯誤​

*/​

unsigned char GetSDCardCISDCardOutnfo(unsigned char *cid_data)​

{​

unsigned char r1;​

發SDCard_CMD10指令,讀CID​

r1=SendSDCardCmd(SDCard_CMD10,0,0x01);​

if(r1==0x00)​

{​

r1=SDCardRecvData(cid_data,16);//接收16個位元組的資料​

}​

SDCardCancelCS();//取消片選​

if(r1)return 1;​

else return 0;​

}​

/*​

函數說明:​

擷取SD卡的CSD資訊,包括容量和速度資訊​

函數參數:​

unsigned char *cid_data(存放CID的記憶體,至少16Byte)​

返 回 值:​

0:成功,1:錯誤​

*/​

unsigned char GetSDCardCSSDCardOutnfo(unsigned char *csd_data)​

{​

unsigned char r1;​

r1=SendSDCardCmd(SDCard_CMD9,0,0x01); //發SDCard_CMD9指令,讀CSD​

if(r1==0)​

{​

r1=SDCardRecvData(csd_data, 16);//接收16個位元組的資料 ​

}​

SDCardCancelCS();//取消片選​

if(r1)return 1;​

else return 0;​

} ​

/*​

函數功能:擷取SD卡的總扇區數(扇區數) ​

返 回 值:​

0表示容量檢測出錯,其他值表示SD卡的容量(扇區數/512位元組)​

說 明:​

每扇區的位元組數必為512位元組,如果不是512位元組,則初始化不能通過.​

*/​

unsigned int GetSDCardSectorCount(void)​

{​

unsigned char csd[16];​

unsigned int Capacity; ​

unsigned char n;​

u16 csize; ​

if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0;//取CSD資訊,如果期間出錯,傳回0​

if((csd[0]&0xC0)==0x40)的卡,如果為SDHC卡,按照下面方式計算​

{​

csize = csd[9] + ((u16)csd[8] << 8) + 1;​

Capacity = (unsigned int)csize << 10;//得到扇區數​

}​

else//V1.XX的卡 ​

{​

n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;​

csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;​

Capacity= (unsigned int)csize << (n - 9);//得到扇區數 ​

}​

return Capacity;​

}​

/*​

函數功能: 初始化SD卡​

返 回 值: 非0表示初始化失敗!​

*/​

unsigned char SDCardDeviceInit(void)​

{​

存放SD卡的傳回值​

用來進行逾時計數​

unsigned char buf[4]; ​

u16 i;​

SDCardSpiInit();//初始化底層IO口​

for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //發送最少74個脈沖​

retry=20;​

do​

{​

r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//進入IDLE狀态 閑置​

}while((r1!=0X01) && retry--);​

SD_Type=0; //預設無卡​

if(r1==0X01)​

{​

if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1) //SD V2.0​

{​

for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//Get trailing return value of R7 resp​

if(buf[2]==0X01&&buf[3]==0XAA) //卡是否支援2.7~3.6V​

{​

retry=0XFFFE;​

do​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);發送SDCard_CMD55​

r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//發送SDCard_CMD41​

}while(r1&&retry--);​

if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鑒别SD2.0卡版本開始​

{​

for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值​

if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC; //檢查CCS​

else SD_Type=SDCard_TYPE_V2; ​

}​

}​

}​

else//SD V1.x/ MMCV3​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);//發送SDCard_CMD55​

r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//發送SDCard_CMD41​

if(r1<=1)​

{​

SD_Type=SDCard_TYPE_V1;​

retry=0XFFFE;​

do //等待退出IDLE模式​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);//發送SDCard_CMD55​

r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//發送SDCard_CMD41​

}while(r1&&retry--);​

}​

else//MMC卡不支援SDCard_CMD55+SDCard_CMD41識别​

{​

SD_Type=SDCard_TYPE_MMC;//MMC V3​

retry=0XFFFE;​

do //等待退出IDLE模式​

{​

r1=SendSDCardCmd(SDCard_CMD1,0,0X01);//發送SDCard_CMD1​

}while(r1&&retry--); ​

}​

if(retry==0||SendSDCardCmd(SDCard_CMD13,512,0X01)!=0)SD_Type=SDCard_TYPE_ERR;//錯誤的卡​

}​

}​

SDCardCancelCS(); //取消片選​

if(SD_Type)return 0; //初始化成功傳回0​

else if(r1)return r1; //傳回值錯誤值​

return 0xaa; //其他錯誤​

}​

/*​

函數功能:讀SD卡​

函數參數:​

buf:資料緩存區​

sector:扇區​

cnt:扇區數​

傳回值:​

0,ok;其他,失敗.​

說 明:​

SD卡一個扇區大小512位元組​

*/​

unsigned char SDCardReadData(unsigned char*buf,unsigned int sector,unsigned int cnt)​

{​

unsigned char r1;​

if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//轉換為位元組位址​

if(cnt==1)​

{​

r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//讀指令​

if(r1==0)指令發送成功​

{​

r1=SDCardRecvData(buf,512);//接收512個位元組​

}​

}else​

{​

r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//連續讀指令​

do​

{​

r1=SDCardRecvData(buf,512);//接收512個位元組​

buf+=512; ​

}while(--cnt && r1==0); ​

SendSDCardCmd(SDCard_CMD12,0,0X01);//發送停止指令​

} ​

SDCardCancelCS();//取消片選​

return r1;//​

}​

/*​

函數功能:向SD卡寫資料​

函數參數:​

buf:資料緩存區​

sector:起始扇區​

cnt:扇區數​

傳回值:​

0,ok;其他,失敗.​

說 明:​

SD卡一個扇區大小512位元組​

*/​

unsigned char SDCardWriteData(unsigned char*buf,unsigned int sector,unsigned int cnt)​

{​

unsigned char r1;​

if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//轉換為位元組位址​

if(cnt==1)​

{​

r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//讀指令​

if(r1==0)//指令發送成功​

{​

r1=SDCardSendData(buf,0xFE);//寫512個位元組​

}​

}​

else​

{​

if(SD_Type!=SDCard_TYPE_MMC)​

{​

SendSDCardCmd(SDCard_CMD55,0,0X01);​

SendSDCardCmd(SDCard_CMD23,cnt,0X01);//發送指令​

}​

r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//連續讀指令​

if(r1==0)​

{​

do​

{​

r1=SDCardSendData(buf,0xFC);//接收512個位元組​

buf+=512; ​

}while(--cnt && r1==0);​

r1=SDCardSendData(0,0xFD);//接收512個位元組 ​

}​

} ​

SDCardCancelCS();//取消片選​

return r1;//​

}​

static struct gendisk *tiny4412_blockdev_disk;​

static struct request_queue *tiny4412_blockdev_queue;​

static int major; //主裝置号​

static DEFINE_SPINLOCK(tiny4412_blockdev_lock); //自旋鎖​

static unsigned int sd_size=0; 存放SD卡傳回的容量扇區數量機關(512位元組)​

static int tiny4412_blockdev_getgeo(struct block_device *bdev, struct hd_geometry *geo)​

{​

/* 容量=heads*cylinders*sectors*512 ​

存儲容量 = 磁頭數 × 磁道(柱面)數 × 每道扇區數 × 每扇區位元組數​

*/​

geo->heads = 200; /*磁頭(一般一個盤面有兩個磁頭,正面一個/反面一個)*/​

geo->cylinders = 32; /*柱面(一般一個盤面上有32個柱面)每個盤片32個磁道)*/​

geo->sectors = sd_size/200/32; /*扇區,一般每個磁道上有12個扇區,這裡需要根據前面柱面和磁頭進行計算,不能亂填*/​

return 0;​

}​

static struct block_device_operations tiny4412_blockdev_fops = {​

.owner= THIS_MODULE,​

.getgeo= tiny4412_blockdev_getgeo,​

};​

static void do_tiny4412_blockdev_request(struct request_queue * q)​

{​

struct request *req;​

req = blk_fetch_request(q);​

while (req) ​

{​

unsigned long start = blk_rq_pos(req); /*起始扇區位置*/​

unsigned long len = (blk_rq_cur_bytes(req)>>9); /*目前操作的扇區數量*/​

int err = 0;​

if(rq_data_dir(req) == READ) /*如果是讀*/​

{​

if(SDCardReadData(req->buffer,start,len))​

{​

printk(KERN_ERR"read error!\r\n");​

printk("read ---> nsect=%ld,sector=%ld\r\n",len,start);​

}​

}​

else​

{​

if(SDCardWriteData(req->buffer,start,len))​

{​

printk(KERN_ERR"write error!\r\n");​

printk("write ---> nsect=%ld,sector=%ld\r\n",len,start);​

}​

}​

if (!__blk_end_request_cur(req, err)) /*判斷是否處理完畢請求*/​

req = blk_fetch_request(q); /*繼續處理下一個請求*/​

}​

}​

static int tiny4412_blockdev_init(void)​

{​

/*初始化SD卡*/​

if(SDCardDeviceInit()) ​

{​

卡初始化失敗!\r\n");​

return -1;​

}​

/*檢測SD卡大小*/​

sd_size=GetSDCardSectorCount();//檢測SD卡大小,傳回值右移11位得到以M為機關的容量​

printk("SD卡Sizeof:%dM secnt=%d\r\n",sd_size>>11,sd_size);​

/* 1. 配置設定一個gendisk結構體 */​

tiny4412_blockdev_disk = alloc_disk(16); /* 次裝置号個數: 分區個數+1 */​

/* 2. 設定 */​

/* 2.1 配置設定/設定隊列: 提供讀寫能力 */​

tiny4412_blockdev_queue = blk_init_queue(do_tiny4412_blockdev_request, &tiny4412_blockdev_lock);​

tiny4412_blockdev_disk->queue = tiny4412_blockdev_queue;​

/* 2.2 設定其他屬性: 比如容量 */​

major = register_blkdev(0,"blockdev"); /* cat /proc/devices */​

printk("注冊major=%d\n",major);​

tiny4412_blockdev_disk->major = major;​

tiny4412_blockdev_disk->first_minor = 0;​

sprintf(tiny4412_blockdev_disk->disk_name, "tiny4412_blockdev");​

tiny4412_blockdev_disk->fops = &tiny4412_blockdev_fops;​

set_capacity(tiny4412_blockdev_disk, sd_size); //設定磁盤容量​

/* 3. 硬體相關操作 */​

/* 4. 注冊 */​

add_disk(tiny4412_blockdev_disk);​

return 0;​

}​

static void tiny4412_blockdev_exit(void)​

{​

printk("登出major=%d\n",major);​

unregister_blkdev(major,"blockdev");​

del_gendisk(tiny4412_blockdev_disk);​

put_disk(tiny4412_blockdev_disk);​

blk_cleanup_queue(tiny4412_blockdev_queue);​

/*釋放虛拟位址*/​

iounmap(SD_GPBCON);​

iounmap(SD_GPBDAT);​

}​

module_init(tiny4412_blockdev_init);​

module_exit(tiny4412_blockdev_exit);​

MODULE_LICENSE("GPL");​

1.4 磁盤的構造分析​

硬碟是電腦主要的存儲媒介之一,由一個或者多個鋁制或者玻璃制的碟片組成。碟片外覆寫有鐵磁性材料。​

硬碟有固态硬碟(SSD 盤,新式硬碟)、機械硬碟(HDD 傳統硬碟)、混合硬碟(HHD 一塊基于傳統機械硬碟誕生出來的新硬碟)。SSD采用閃存顆粒來存儲,HDD采用磁性碟片來存儲,混合硬碟(HHD: Hybrid Hard Disk)是把磁性硬碟和閃存內建到一起的一種硬碟。絕大多數硬碟都是固定硬碟,被永久性地密封固定在硬碟驅動器中。​

圖4.1 機械硬碟​

硬碟是集精密機械、微電子電路、電磁轉換為一體的電腦儲存設備,它存儲着電腦系統資源和重要的資訊及資料,這些因素使硬碟在PC機中成為最為重要的一個硬體裝置​

  • 最精密的部分--磁頭:​由于磁頭工作的性質,對磁感應的要求非常高。磁頭是在高速旋轉的盤片上懸浮的,懸浮力來自盤片旋轉帶動的氣流,磁頭必須懸浮而不是接觸盤面,避免盤面和磁頭發生互相接觸的磨損。​
  • 硬碟存儲的媒體--盤片:​盤片是以堅固耐用的材料為盤基,将磁粉附着在平滑的鋁合金或玻璃圓盤基上。這些磁粉被劃分成稱為磁道的若幹個同心圓,每個同心圓就好像有無數的小磁鐵,它們分别代表着0和1狀态。當小磁鐵受到來自磁頭的磁力影響時,其排列方向會随之改變。​

    下面主要講解機械硬碟的構造,機械硬碟是由一個個盤片組成的,我們先從個盤片結構講起。下圖圖中的一圈圈灰色同心圓為一條條磁道,從圓心向外畫直線,可以将磁道劃分為若幹個弧段,每個磁道上一個弧段被稱之為一個扇區(圖踐綠色部分)。扇區是磁盤的最小組成單元,通常是512位元組。​

  • Linux下編寫标準SD卡驅動(塊裝置)​
    Linux下編寫标準SD卡驅動(塊裝置)​
  • 圖4.2 盤面構造​
  • 磁盤的常見參數如下:​
  1. 磁頭(head)​
  2. 磁道(track)​
  3. 柱面(cylinder)​
  4. 扇區(sector)​
  5. 圓盤(platter)​

上圖2中磁盤是一個有 3個盤面6個磁頭(一個盤面有正反面兩個磁頭,兩面都可以獨立讀寫),7個柱面(每個盤片7個磁道) 的磁盤,每條磁道有12個扇區,是以此磁盤的容量為6*7*12*512位元組。​

計算方法:存儲容量=磁頭數 × 磁道(柱面)數 × 每道扇區數 × 每扇區位元組數​

繼續閱讀