Linux块设备方式的nvram驱动实例


一般可以通过字符设备驱动的方式访问nvram,在这里,通过块设备驱动方式去驱动nvram,以了解和熟悉块设备驱动编写。

测试系统:fedora7
nvram总可接大小:8k x 64页 x 8片 = 4 M 字节
测试硬件:intel板卡

#include <linux/types.h> 
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/errno.h> 
#include <linux/vmalloc.h>
#include <linux/hdreg.h>
#include <linux/kdev_t.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>

#define NVRAM_NAME "board_nvram"
#define NVRAM_SIZE 0x80000
#define NVRAM_BLOCKSIZE 512

struct nvram_dev_t{
    int size; /* Device size in sectors */
    u8 *data;  
    struct request_queue *queue;
    struct gendisk *disk;
};

struct nvram_dev_t nvram_dev;

static int hardsect=NVRAM_BLOCKSIZE;
static int  major=224;
static int  minor = 0; 
static int  iobaadr = 0x300; //板卡io空间基地址
static unsigned long  membaadr=0xdc000; //内存空间基地址
static unsigned long  mem_len=8192; //页映射长度bytes
char* preg;

void SelectPage(unsigned page)
{
    outb_p(0xc0|(page & 0x3f),iobaadr+8); //页选
}

static void nvram_transfer(struct nvram_dev_t *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
{
    unsigned long offset = sector*hardsect;
    unsigned long nbytes = nsect*hardsect;
     char *rdptr;
     unsigned long index;  
       int time;   

    unsigned long r_size;  /*size of one request*/
    unsigned long t_size;  /*size of one tansfer*/

    if ((offset + nbytes) > NVRAM_SIZE) {
        printk (KERN_NOTICE "invaild offset\n");
        return;
    }

    offset=(sector * hardsect) & (mem_len-1); 
    index= (sector* hardsect)>>13; 
    rdptr = preg+ offset;   
    r_size=nbytes;      
    if(r_size>(mem_len-offset)){ 
      time=((r_size-(mem_len-offset))>>13 )+ 2;  
    }else 
      time=1;

    if (write)  //write
        while(time){
             SelectPage(index);
             if(time==1) t_size=r_size;
             else t_size=mem_len-offset;
             memcpy_toio(rdptr,buffer,t_size);
             r_size-=t_size;
             time--;
             index++;
             offset=0;
        } 
    else      //read
       while(time){
            SelectPage(index);
             if(time==1) t_size=r_size;
             else t_size=mem_len-offset;
             memcpy_fromio(buffer,rdptr,t_size);
             r_size-=t_size;
             time--;
             index++;
             offset=0;
       }
}

static int nvram_xfer_bio(struct nvram_dev_t *dev, struct bio *bio)
{
    int i;
    struct bio_vec *bvec;
    sector_t sector = bio->bi_sector;

    /* Do each segment independently. */
    bio_for_each_segment(bvec, bio, i) {
        char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
        nvram_transfer(dev, sector, bio_cur_sectors(bio),
                buffer, bio_data_dir(bio) == WRITE);
        sector += bio_cur_sectors(bio);
        __bio_kunmap_atomic(bio, KM_USER0);
    }
    return 0; /* Always "succeed" */
}


static int nvram_make_request(request_queue_t *queue,struct bio *bio)
{
    struct nvram_dev_t*dev = &nvram_dev;
    int status;
    printk("nvram_make_request\n");
    status = nvram_xfer_bio(dev, bio);
    bio_endio(bio, bio->bi_size, status);
    return 0;
}

int nvram_open(struct inode *inode, struct file *filp) 
{
    try_module_get(THIS_MODULE);
    return 0; 
}

int nvram_release(struct inode *inode, struct file *filp) 
{ 
    module_put(THIS_MODULE);
    return 0;
}

int nvram_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
    int err;
    long size;  
    struct hd_geometry geo;

    switch(cmd) {

      case BLKGETSIZE:

        if (!arg) return -EINVAL; 
        err = ! access_ok (VERIFY_WRITE, arg, sizeof(long));
        if (err) return -EFAULT;
        size = NVRAM_SIZE/512;
        if (copy_to_user((long *) arg, &size, sizeof (long)))
            return -EFAULT;
        return 0;

      case BLKRRPART:
        return -ENOTTY;

      case HDIO_GETGEO: // 获取hd_geometry结构,物理信息
        err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo));
        if (err) return -EFAULT;
        size = NVRAM_SIZE/512;
        geo.cylinders = (size & ~0x3f) >> 6;
        geo.heads = 4;
        geo.sectors = 16;
        geo.start = 4;
        if (copy_to_user((void *) arg, &geo, sizeof(geo)))
             return -EFAULT;
        return 0;
    }

    return -ENOTTY; /* unknown command */
}

struct block_device_operations nvram_bdops ={ 
 .owner = THIS_MODULE,
 .open = nvram_open, 
 .release = nvram_release, 
 .ioctl = nvram_ioctl, 
};  

static void __exit nvram_exit(void)
{
    if (nvram_dev.disk) {
        del_gendisk(nvram_dev.disk);
        put_disk(nvram_dev.disk);
    }

    if (nvram_dev.queue) {
       blk_put_queue(nvram_dev.queue);
    }

    unregister_blkdev(major, "nvram");
    unregister_chrdev_region(MKDEV(major,minor), 1); 
    release_region(iobaadr, 13);
    release_mem_region(membaadr, mem_len);
    iounmap(preg);
    printk("Board Nvram Driver unloaded\n");
}

static int nvram_init(void){
    int  ret;
    int err = -ENOMEM;
    struct gendisk *disk;

    dev_t devno=MKDEV(major, minor);

    ret = register_chrdev_region(devno,1,NVRAM_NAME);        
    if(ret<0){
        printk("Unable to register character device!\n");
        return ret;
    }

    nvram_dev.disk= alloc_disk(1);
    if (!nvram_dev.disk){
        unregister_chrdev_region(devno, 1); 
        return err;
    }

    nvram_dev.queue= blk_alloc_queue(GFP_KERNEL);
    if (!nvram_dev.queue) {
        unregister_chrdev_region(devno, 1); 
        put_disk(nvram_dev.disk);
        return err;
    }

    if (register_blkdev(major, "nvram")) {
        err = -EIO;
        goto out;
    }

    if (NULL == request_region(iobaadr, 13, NVRAM_NAME)){
       printk("io error\n");
       nvram_exit();
       return -EINVAL;
    }

   if (NULL == request_mem_region(membaadr, mem_len, NVRAM_NAME)){
       printk("mem error\n");
       nvram_exit();
       return -EINVAL;
   }    

   preg= ioremap(membaadr,mem_len);

   disk= nvram_dev.disk;
      blk_queue_make_request(nvram_dev.queue,&nvram_make_request);
blk_queue_hardsect_size(nvram_dev.queue, hardsect);

    /* rd_size is given in kB */
    disk->major = major;
    disk->first_minor = 0;
    disk->fops = &nvram_bdops;
    disk->queue = nvram_dev.queue;

    sprintf(disk->disk_name, "nvram%d", 0);
    set_capacity(disk, 1024);
    add_disk(disk);
    outb_p(0x55,iobaadr+9);  //nvram 片选使能

    printk("Nvram is Initialized\n");
    return 0;

    out:
      unregister_chrdev_region(devno, 1); 
      put_disk(nvram_dev.disk);
      blk_cleanup_queue(nvram_dev.queue);

    return err;
}

module_init(nvram_init);
module_exit(nvram_exit);

MODULE_LICENSE("Dual BSD/GPL");

测试:

insmod board_nvram.ko
mkfs.ext2 /dev/nvram0
mount /dev/nvram0 /mnt

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告