Linux 块设备驱动代码编写

吾爱主题 阅读:165 2024-04-05 13:53:37 评论:0

按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。char设备是比较简单的,只要分配了major、minor号,就可以进行读写处理了。相对而言,block和net要稍微复杂些。net设备姑且按下不谈,我们在以后的博文中会有涉及。今天,我们可以看看一个简单的block是怎么设计的。

为了将block和fs分开,kernel的设计者定义了request queue这一种形式。换一句话说,所有fs对block设备的请求,最终都会转变为request的形式。所以,对于block设备驱动开发的朋友来说,处理好了request queue就掌握了block设备的一半。当然,block设备很多,hd、floppy、ram都可以这么来定义,有兴趣的朋友可以在drivers/block寻找相关的代码来阅读。兴趣没有那么强的同学,可以看看我们这篇博文,基本上也能学个大概。有个基本的概念,再加上一个简单浅显的范例,对于一般的朋友来说,已经足够了。

闲话不多说,我们看看一个ramdisk代码驱动是怎么写的,代码来自《深入linux 设备驱动程序内核机制》,

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h>   #include <linux/fs.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/vmalloc.h> #include <linux/blkdev.h> #include <linux/hdreg.h>   #define RAMHD_NAME "ramhd" #define RAMHD_MAX_DEVICE 2 #define RAMHD_MAX_PARTITIONS 4   #define RAMHD_SECTOR_SIZE 512 #define RAMHD_SECTORS 16 #define RAMHD_HEADS 4 #define RAMHD_CYLINDERS 256   #define RAMHD_SECTOR_TOTAL (RAMHD_SECTORS * RAMHD_HEADS *RAMHD_CYLINDERS) #define RAMHD_SIZE (RAMHD_SECTOR_SIZE * RAMHD_SECTOR_TOTAL) //8mb   typedef struct {    unsigned char * data;    struct request_queue* queue;    struct gendisk* gd; }RAMHD_DEV;   static char * sdisk[RAMHD_MAX_DEVICE] = {NULL}; static RAMHD_DEV* rdev[RAMHD_MAX_DEVICE] = {NULL};   static dev_t ramhd_major;   static int ramhd_space_init( void ) {    int i;    int err = 0;    for (i = 0; i < RAMHD_MAX_DEVICE; i++){      sdisk[i] = vmalloc(RAMHD_SIZE);      if (!sdisk[i]){        err = -ENOMEM;        return err;      }            memset (sdisk[i], 0, RAMHD_SIZE);    }        return err; }   static void ramhd_space_clean( void ) {    int i;    for (i = 0; i < RAMHD_MAX_DEVICE; i++){      vfree(sdisk[i]);    } }   static int ramhd_open( struct block_device* bdev, fmode_t mode) {    return 0; }   static int ramhd_release( struct gendisk*gd, fmode_t mode) {    return 0; }   static int ramhd_ioctl( struct block_device* bdev, fmode_t mode, unsigned int cmd, unsigned long arg) {    int err;    struct hd_geometry geo;        switch (cmd)    {      case HDIO_GETGEO:        err = !access_ok(VERIFY_WRITE, arg, sizeof (geo));        if (err)          return -EFAULT;                  geo.cylinders = RAMHD_CYLINDERS;        geo.heads = RAMHD_HEADS;        geo.sectors = RAMHD_SECTORS;        geo.start = get_start_sect(bdev);                if (copy_to_user(( void *)arg, &geo, sizeof (geo)))          return -EFAULT;                return 0;    }        return -ENOTTY; }   static struct block_device_operations ramhd_fops = {    .owner = THIS_MODULE,    .open = ramhd_open,    .release = ramhd_release,    .ioctl = ramhd_ioctl, };   static int ramhd_make_request( struct request_queue* q, struct bio* bio) {    char * pRHdata;    char * pBuffer;    struct bio_vec* bvec;    int i;    int err = 0;        struct block_device* bdev = bio->bi_bdev;    RAMHD_DEV* pdev = bdev->bd_disk->private_data;        if (((bio->bi_sector * RAMHD_SECTOR_SIZE) + bio->bi_size) > RAMHD_SIZE){      err = -EIO;      return err;    }        pRHdata = pdev->data + (bio->bi_sector * RAMHD_SECTOR_SIZE);    bio_for_each_segment(bvec, bio, i){      pBuffer = kmap(bvec->bv_page) + bvec->bv_offset;      switch (bio_data_dir(bio)){        case READ:          memcpy (pBuffer, pRHdata, bvec->bv_len);          flush_dcache_page(bvec->bv_page);          break ;                  case WRITE:          flush_dcache_page(bvec->bv_page);          memcpy (pRHdata, pBuffer, bvec->bv_len);          break ;                  default :          kunmap(bvec->bv_page);          goto out;      }            kunmap(bvec->bv_page);      pRHdata += bvec->bv_len;    }     out:    bio_endio(bio, err);    return 0; }   static int alloc_ramdev( void ) {    int i;    for (i = 0; i < RAMHD_MAX_DEVICE; i++){      rdev[i] = kzalloc( sizeof (RAMHD_DEV), GFP_KERNEL);      if (!rdev[i]){        return -ENOMEM;      }    }        return 0; }   static void clean_ramdev( void ) {    int i;        for (i = 0; i < RAMHD_MAX_DEVICE; i++){      if (rdev[i])        kfree(rdev[i]);    } }   static int __init ramhd_init( void ) {    int i;        ramhd_space_init();    alloc_ramdev();        ramhd_major = register_blkdev(0, RAMHD_NAME);        for (i = 0; i < RAMHD_MAX_DEVICE; i++){      rdev[i]->data = sdisk[i];      rdev[i]->queue = blk_alloc_queue(GFP_KERNEL);      blk_queue_make_request(rdev[i]->queue, ramhd_make_request);            rdev[i]->gd = alloc_disk(RAMHD_MAX_PARTITIONS);      rdev[i]->gd->major = ramhd_major;      rdev[i]->gd->first_minor = i * RAMHD_MAX_PARTITIONS;      rdev[i]->gd->fops = &ramhd_fops;      rdev[i]->gd->queue = rdev[i]->queue;      rdev[i]->gd->private_data = rdev[i];      sprintf (rdev[i]->gd->disk_name, "ramhd%c" , 'a' +i);      rdev[i]->gd->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;      set_capacity(rdev[i]->gd, RAMHD_SECTOR_TOTAL);      add_disk(rdev[i]->gd);    }        return 0; }   static void __exit ramhd_exit( void ) {    int i;    for (i = 0; i < RAMHD_MAX_DEVICE; i++){      del_gendisk(rdev[i]->gd);      put_disk(rdev[i]->gd);      blk_cleanup_queue(rdev[i]->queue);    }        clean_ramdev();    ramhd_space_clean();    unregister_blkdev(ramhd_major, RAMHD_NAME); }   module_init(ramhd_init); module_exit(ramhd_exit);   MODULE_AUTHOR( "dennis__chen@ AMDLinuxFGL" ); MODULE_DESCRIPTION( "The ramdisk implementation with request function" ); MODULE_LICENSE( "GPL" );

为了大家方便,顺便也把Makefile放出来,看过前面blog的朋友都知道,这其实很简单,

?
1 2 3 4 5 6 7 8 9 10 11 12 ifneq ($(KERNELRELEASE),) obj-m := ramdisk.o   else PWD := $(shell pwd) KVER := $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all:    $(MAKE) -C $(KDIR) M=$(PWD) modules clean:    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.* Module.* endif

这段代码究竟有没有用呢?可以按照下面的步骤来做,

    a)make 一下,生成ramdisk.ko;

    b)编译好了之后,就可以安装驱动了,在linux下是这么做的,sudo insmod ramdisk.ko;

    c)安装好了,利用ls /dev/ramhd*, 就会发现在/dev下新增两个结点,即/dev/ramhda和/dev/ramhdb;

    d)不妨选择其中一个节点进行分区处理, sudo fdisk /dev/ramhda,简单处理的话就建立一个分区, 生成/dev/ramhda1;

    e)创建文件系统,sudo mkfs.ext3 /dev/ramhda1;

    f)有了上面的文件系统,就可以进行mount处理,不妨sudo mount /dev/ramhda1 /mnt;

    g)上面都弄好了,大家就可以copy、delete文件试试了,是不是很简单。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/feixiaoxing/article/details/11701797

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

【腾讯云】云服务器产品特惠热卖中
搜索
标签列表
    关注我们

    了解等多精彩内容