Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48703
  • 博文数量: 26
  • 博客积分: 1175
  • 博客等级: 少尉
  • 技术积分: 300
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-14 19:16
文章分类
文章存档

2011年(1)

2010年(25)

我的朋友

分类: 嵌入式

2010-03-11 20:40:27

块设备
---------------------------------------------
主要特点是:任意寻址

1.块设备是基于磁盘设备出来的,所以它很大一部分实现是优化I/O请求。
2.分区,类型:主分区,扩展分区
3.文件,文件系统(要放文件就必须有文件系统)

               .-----------------.
               | register_blkdev |
               '-----------------'
                        |
                        v
               .-----------------.
               |   alloc_disk    |  (次设备号的数量)也就是分区数
               '-----------------'
                        |
                        v
               .-----------------.
               |     初始化      |
               '-----------------'
                        |
                        v
               .-----------------.
               |     add_disk    |
               '-----------------'
                        |
                        v
               .-----------------.
               |unregister_blkdev|
               '-----------------'
                        |
                        v
               .-----------------.
               |   del_gendisk   |
               '-----------------'
              
初始化部分:
-------------------------------------------------
test.gd->major = TEST_MAJOR; 主设备号
test.gd->first = 0;          次设备号的开始号
test.gd->fops  = &test_ops;  ops
test.gd->queue = blk_init_queue(request_proc, &test.lock);
                 我们申请的请求队列,request_proc处理请求,为什么我们后面还要有个自旋锁呢?因为请求队列的竞态有可能发生在操作系统底层,和驱动上层,为了防止重复锁。
strcpy(test.gd->disk_name,"test");
可以在/proc/devices中看到这个名字,我们在/dev下面不用自己建立设备文件,系统帮我们建立。
set_capacity(test.gd, TEST_NSECTORS);
设置这个磁盘有多大,这里的单位是扇区数,每个扇区内核规定是512B。如果设备的扇区不是512B,驱动要自己处理,做一个转换,欺骗内核。
test.gd->private_data = &test;
test.data = vmalloc(SECTOR_SIZE*TEST_NSECTORS);
memset(test.data, 0x00, SECTOR_SIZE*TEST_NSECTORS);
-------------------------------------------------

在旧内核中一般有一个ioctl的命令GETGEO来得到磁头数,磁道扇区数,柱面数,开始扇区号。
在新内核中在block_device_operations现在有一个成员.getgeo来代替ioctl这个命令。
int test_getgeo(struct block_device *dev, struct hd_geometry *geo)
{
        /* heads : 磁头数 , 盘面数 */
        /* sectors: 每个磁道的扇区数 */
        /* cylinders; 柱面数, 每个盘面的磁道数 */
        /* start: 开始扇区号 */

        /* 一个盘面的扇区数 = 每盘磁道数 x 每磁道的扇区数 */
        /*                整个硬盘柱面数 x 每磁道的扇区数*/
        /*                     cylinders x   sectors     */
        /*   cylinders  = 一个盘面的扇区数 / 每个磁道的扇区数 */
        /*                 整个硬盘的扇区数 / 每个柱面的扇区数 */

        geo->heads = 4;
        geo->sectors = 128;
        geo->cylinders = TEST_NSECTORS  / (geo->heads * geo->sectors);
        geo->start = 0;

        return 0;
}

分区这些动作不是我们驱动做的是,而是fdisk这种东西用内核的函数来做的,我们只管建立整个磁盘就行了。


主要的函数是这两个:
void test_trans(struct test_dev *dev, int offset, int sector_count, unsigned
char *buf, int write)
{
        if ((offset + sector_count) > TEST_NSECTORS) {
                return;
        }

        if (write) {
                memcpy(dev->data + offset * SECTOR_SIZE,  buf,  sector_count * SECTOR_SIZE);
        } else {
                memcpy(buf, dev->data + offset * SECTOR_SIZE, sector_count * SECTOR_SIZE);
        }
}

void request_proc(request_queue_t *rq) {
        struct request *re;
        struct test_dev *dev;

        while(( re = elv_next_request(rq)) != NULL) {
                if (blk_fs_request(re)) {
                        dev = re->rq_disk->private_data;
                        test_trans(dev ,re->sector, re->current_nr_sectors, re->buffer, rq_data_dir(re));

                        end_request(re, 1);
                } else {
                        end_request(re, 0);
                }
        }
}

没有列出所有源程序。只是单单列出了,重要的部分
阅读(457) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~