塊設備:系統能夠隨機無序訪問固定大小的數據片的設備,這些數據片稱為塊。塊設備是以固定大小長度來傳送資料的,它使用緩沖區暫存數據,時機成熟后從緩存中一次性寫入到設備或者從設備中一次性放到緩存區。常見的塊設備有硬盤、CD-ROM驅動器、Flash閃存等等,它們也是通過文件形式存在於Linux中的。Linux以“b”表示塊設備。
字符設備:按照字符流方式被有序訪問,以不定長度的字元傳送資料,不存在緩沖區,所以對這種設備的讀寫都是實時的,比如鍵盤、串口、印表機等等。Linux以“c”表示字符設備。
區別:
1.字符設備只能以字節為最小單位訪問,而塊設備以塊為單位訪問,例如512字節,1024字節等
2.塊設備可以隨機訪問,但是字符設備不可以
3.字符和塊沒有訪問量大小的限制,塊也可以以字節為單位來訪問
可以看出,塊設備的復雜性要遠高於字符設備
數據結構
1.塊設備數據結構
struct gendisk (定義於 <linux/genhd.h>) 是單獨一個磁盤驅動器的內核表示. 事實上, 內核還使用 gendisk 來表示分區
2.字符設備數據結構
struct file;
struct inode;
file定義於 <linux/fs.h>, 是設備驅動中第二個最重要的數據結構. 文件結構代表一個打開的文件. 它由內核在 open 時創建, 並傳遞給在文件上操作的任何函數, 直到最后的關閉. 在文件的所有實例都關閉后, 內核釋放這個數據結構。
inode 結構由內核在內部用來表示文件.inode 結構包含大量關於文件的信息其中dev_t i_rdev成員包含實際的設備編號.struct cdev *i_cdev中struct cdev 是內核的內部結構, 代表字符設備。
設備訪問接口
1.塊設備訪問接口
字符設備通過 file_ 操作結構使它們的操作對系統可用. 一個類似的結構用在塊設備上; 它是 struct block_device_operations, 定義在 <linux/fs.h>,其主要操作方法如下:
int (*open)(struct inode *inode, struct file *filp);
int (*release)(struct inode *inode, struct file *filp);
就像它們的字符驅動對等體一樣工作的函數; 無論何時設備被打開和關閉都調用它們. 一個字符驅動可能通過啟動設備或者鎖住門(為可移出的介質)來響應一個 open 調用. 如果你將介質鎖入設備, 你當然應當在 release 方法中解鎖。
int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
實現 ioctl 系統調用的方法. 但是, 塊層首先解釋大量的標准請求; 因此大部分的塊驅動 ioctl 方法相當短。
2.字符設備訪問接口
struct file_operations 其中file_operation 結構中的每個成員必須指向驅動中的函數, 這些函數實現一個特別的操作, 或者對於不支持的操作留置為 NULL. 當指定為 NULL 指針時內核的確切的行為是每個函數不同的,該結構中主要函數如下:
ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);
filp 是文件指針, count 是請求的傳輸數據大小. buff 參數指向持有被寫入數據的緩存, 或者放入新數據的空緩存. 最后, offp 是一個指針指向一個"long offset type"對象, 它指出用戶正在存取的文件位置. 返回值是一個"signed size type"。
設備注冊
1.塊設備注冊
int register_blkdev(unsigned int major, const char *name);
int unregister_blkdev(unsigned int major, const char *name);
register_blkdev 注冊一個塊驅動到內核, 並且, 可選地, 獲得一個主編號. 一個驅動可被注銷, 使用 unregister_blkdev。
2.字符設備注冊
int register_chrdev_region(dev_t first, unsigned int count, char *name)
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name)
void unregister_chrdev_region(dev_t first, unsigned int count);
允許驅動分配和釋放設備編號的范圍的函數. register_chrdev_region 應當用在事先知道需要的主編號時; 對於動態分配, 使用 alloc_chrdev_region 代替.
現在我們來看看Linux系統中/dev/mtdN與/dev/mtdblockN的區別,即MTD字符設備和塊設備的區別。
/dev/mtdN是系統自身實現mtd分區鎖對應的字符設備。
MTD設備層是介於文件系統(比如jffs,因為flash不能覆寫,需要先擦除再寫,普通的FAT/NTFS不適用,因而設計jffs)和flash硬件驅動層之間的一個橋梁,有了mtd之后,可以為開發帶來很多便利。從文件系統編寫者角度看,他不需要關心使用什么類型的flash設置是其他類似的存儲介質,只要調用mtd提供的接口;從硬件驅動編寫者角度看,他不用關心使用了什么文件系統,只要少量mtd接口代碼就能操作flash,因為mtd本身就提供了很多的驅動代碼。
mtd_part:
struct mtd_part {
struct mtd_info mtd; //分區的信息
struct mtd_info *master; //主分區
uint64_t offset; //該分區的偏移地址
int index; //分區號
struct list_head list; //雙向鏈表,鏈接至mtd_partition
int registered;
};
其里面添加了一些ioctl,支持很多命令,如MEMGETINFO,MEMERASE等。
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
fprintf(stderr, "%s: %s: unable to get MTD device infon", exe_name, mtd_device);
return 1;
}
其中,MEMGETINFO,就是Linux MTD中的drivers/mtd/nand/mtdchar.c中的:
static int mtd_ioctl(struct inode *inode, struct file *file,
u_int cmd, u_long arg)
{
。。。。。
case MEMGETINFO:
info.type = mtd->type;
info.flags = mtd->flags;
info.size = mtd->size;
info.erasesize = mtd->erasesize;
info.writesize = mtd->writesize;
info.oobsize = mtd->oobsize;
/* The below fields are obsolete */
info.ecctype = -1;
info.eccsize = 0;
if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
return -EFAULT;
break;
。。。
}
而/dev/mtdblockN,是Nand Flash驅動中,驅動在用add_mtd_partitions()添加MTD設備分區,而生成的對應的塊設備。
根據以上內容,也就更加明白,為什么不能用nandwrite,flash_eraseall,flash_erase等工具去對/dev/mtdblockN去操作了。因為/dev/mtdblock中不包含對應的ioctl,不支持你這么操作。
2. mtd char 設備的主設備號是90,而mtd block設備的主設備號是31:
# ls /dev/mtd? -l
crw-r—– 1 root root 90, 0 May 30 2007 /dev/mtd0
crw-r—– 1 root root 90, 2 May 30 2007 /dev/mtd1
crw-r—– 1 root root 90, 4 Jul 17 2009 /dev/mtd2
crw-r—– 1 root root 90, 6 May 30 2007 /dev/mtd3
crwxrwxrwx 1 root root 90, 8 May 30 2007 /dev/mtd4
crwxrwxrwx 1 root root 90, 10 May 30 2007 /dev/mtd5
crwxrwxrwx 1 root root 90, 12 May 30 2007 /dev/mtd6
crwxrwxrwx 1 root root 90, 14 May 30 2007 /dev/mtd7
crwxrwxrwx 1 root root 90, 16 May 30 2007 /dev/mtd8
crwxrwxrwx 1 root root 90, 18 May 30 2007 /dev/mtd9
# ls /dev/mtdblock? -l
brw-r—– 1 root root 31, 0 May 30 2007 /dev/mtdblock0
brw-r—– 1 root root 31, 1 May 30 2007 /dev/mtdblock1
brw-r—– 1 root root 31, 2 May 30 2007 /dev/mtdblock2
brw-r—– 1 root root 31, 3 May 30 2007 /dev/mtdblock3
brwxrwxrwx 1 root root 31, 4 May 30 2007 /dev/mtdblock4
brwxrwxrwx 1 root root 31, 5 May 30 2007 /dev/mtdblock5
brwxrwxrwx 1 root root 31, 6 May 30 2007 /dev/mtdblock6
brwxrwxrwx 1 root root 31, 7 May 30 2007 /dev/mtdblock7
brwxrwxrwx 1 root root 31, 8 May 30 2007 /dev/mtdblock8
brwxrwxrwx 1 root root 31, 9 May 30 2007 /dev/mtdblock9
此設備號,定義在/include/linux/mtd/mtd.h中 :
#define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
3. 其中,mtd的塊設備的大小,可以通過查看分區信息獲得:
# cat /proc/partitions
major minor #blocks name
31 0 1024 mtdblock0
31 1 8192 mtdblock1
31 2 204800 mtdblock2
31 3 65536 mtdblock3
31 4 225280 mtdblock4
上面中顯示的塊設備大小,是block的數目,每個block是1KB。
而每個字符設備,其實就是對應着上面的每個塊設備。即/dev/mtd0對應/dev/mtdblock0,其他以此類推。換句話說,mtdblockN的一些屬性,也就是mtdN的屬性,比如大小。
4。對每個mtd字符設備的操作,比如利用nandwrite去對/dev/mtd0寫數據,實際就是操作/dev/mtdblock0。
而這些操作里面涉及到的偏移量offset,都指的是此mtd 分區內的偏移。比如向/dev/mtd1的offset為0的位置寫入數據,實際操作的是物理偏移offset=/dev/mtd0的大小=1MB=0x100000。
5.mtd的字符設備和塊設備的命名規則,可以參考下表:
參考自:
http://blog.csdn.net/bonnshore/article/details/7860997
另外可以參考:http://blog.csdn.net/xgbing/article/details/19476979(mtd塊設備緩存操作函數----mtdblock.c 講解)


