diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/memdisk | |
Diffstat (limited to 'drivers/memdisk')
| -rw-r--r-- | drivers/memdisk/Makefile | 1 | ||||
| -rwxr-xr-x | drivers/memdisk/memdisk.c | 235 | ||||
| -rw-r--r-- | drivers/memdisk/memdisk.h | 71 |
3 files changed, 307 insertions, 0 deletions
diff --git a/drivers/memdisk/Makefile b/drivers/memdisk/Makefile new file mode 100644 index 00000000..0108eb30 --- /dev/null +++ b/drivers/memdisk/Makefile @@ -0,0 +1 @@ +obj-y += memdisk.o
\ No newline at end of file diff --git a/drivers/memdisk/memdisk.c b/drivers/memdisk/memdisk.c new file mode 100755 index 00000000..b30c89f1 --- /dev/null +++ b/drivers/memdisk/memdisk.c @@ -0,0 +1,235 @@ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> + +#include <linux/kernel.h> /* printk() */ +#include <linux/fs.h> /* everything... */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/vmalloc.h> +#include <linux/genhd.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> + +MODULE_LICENSE("Dual BSD/GPL"); +static char *Version = "1.4"; + +static int memdisk_major = 0; +module_param(memdisk_major, int, 0); +static int hardsect_size = 512; +module_param(hardsect_size, int, 0); + +/* + * We can tweak our hardware sector size, but the kernel talks to us + * in terms of small sectors, always. + */ +#define KERNEL_SECTOR_SIZE 512 +#define MEMDISK_MINORS 16 +#define MEMDISKS_COUNT 3 +/* + * The internal representation of our device. + */ +static struct memdisk_dev { + unsigned long size; /* Device size in sectors */ + spinlock_t lock; /* For mutual exclusion */ + u8 *data; /* The data array */ + struct request_queue *queue; /* The device request queue */ + struct gendisk *gd; /* The gendisk structure */ +} memdisk_device; + +#if 1 //1G +#define MEMDISK_SYSTEM_PHYS 0xA0000000 +#define MEMDISK_SYSTEM_SIZE 0x12c00000 // 300M + +#define MEMDISK_USERDATA_PHYS 0xB2E00000 +#define MEMDISK_USERDATA_SIZE 0x9600000 // 150M + +#define MEMDISK_CACHE_PHYS 0xBC600000 +#define MEMDISK_CACHE_SIZE 0x3200000 // 50M +#else //512M +#define MEMDISK_SYSTEM_PHYS 0xA0000000 +#define MEMDISK_SYSTEM_SIZE 0xD200000 // 210M + +#define MEMDISK_USERDATA_PHYS 0xB2E00000 +#define MEMDISK_USERDATA_SIZE 0x2800000 // 40M + +#define MEMDISK_CACHE_PHYS 0xBC600000 +#define MEMDISK_CACHE_SIZE 0x600000 // 6M +#endif + +static struct memdisk_dev sprd_memdisks[] = { + [0] = { + .data = MEMDISK_SYSTEM_PHYS, + .size = MEMDISK_SYSTEM_SIZE, + }, + [1] = { + .data = MEMDISK_USERDATA_PHYS, + .size = MEMDISK_USERDATA_SIZE, + }, + [2] = { + .data = MEMDISK_CACHE_PHYS, + .size = MEMDISK_CACHE_SIZE, + }, +}; + +/* + * Handle an I/O request. + */ +static void memdisk_transfer(struct memdisk_dev *dev, sector_t sector + , unsigned long nsect, char *buffer, int write) +{ + unsigned long offset = sector * hardsect_size; + unsigned long nbytes = nsect * hardsect_size; + + //printk("memdisk_transfer: %s offset:%x, %d bytes. \n", write ? "write" : "read", offset, nbytes); + if ((offset + nbytes) > (dev->size * hardsect_size)) { + printk (KERN_NOTICE "memdisk: Beyond-end write (%ld %ld)\n", offset, nbytes); + return; + } + + if (write) + memcpy(dev->data + offset, buffer, nbytes); + else + memcpy(buffer, dev->data + offset, nbytes); +} + +static void memdisk_request(struct request_queue *q) { + struct request *req; + struct memdisk_dev *dev = q->queuedata; + + req = blk_fetch_request(q); + while (req != NULL) { + if (req->cmd_type != REQ_TYPE_FS) { + printk (KERN_NOTICE "Skip non-CMD request/n"); + __blk_end_request_all(req, -EIO); + continue; + } + memdisk_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), + req->buffer, rq_data_dir(req)); + if ( ! __blk_end_request_cur(req, 0) ) { + req = blk_fetch_request(q); + } + } +} + +/* + * The HDIO_GETGEO ioctl is handled in blkdev_ioctl(), i + * calls this. We need to implement getgeo, since we can't + * use tools such as fdisk to partition the drive otherwise. + */ +int memdisk_getgeo(struct block_device * bd, struct hd_geometry * geo) +{ + long size; + + printk("memdisk_getgeo. \n"); + + struct memdisk_dev *dev = bd->bd_disk->private_data; + size = dev->size *(hardsect_size / KERNEL_SECTOR_SIZE); + + geo->cylinders = (size & ~0x3f) >> 6; + geo->heads = 4; + geo->sectors = 16; + geo->start = 4; + + return 0; +} + +/* + * The device operations structure. + */ +static struct block_device_operations memdisk_ops = { + .owner = THIS_MODULE, + .getgeo = memdisk_getgeo +}; + +/* + * Set up our internal device. + */ +static void memdisk_setup_device(struct memdisk_dev *dev, int i) +{ + unsigned char __iomem *base; + printk("memdisk_setup_device i:%d. \n", i); + + spin_lock_init(&dev->lock); + base = ioremap(sprd_memdisks[i].data, sprd_memdisks[i].size); + if(!base) { + printk("memdisk_setup_device ioremap error, dev[%d] data: %x, size: %x\n", i, sprd_memdisks[i].data, sprd_memdisks[i].size); + return; + } + + printk("memdisk_setup_device base: %x, start: %x, size: %x\n",base, sprd_memdisks[i].data, sprd_memdisks[i].size); + sprd_memdisks[i].data = base; + sprd_memdisks[i].size = (sprd_memdisks[i].size / hardsect_size); + + dev->queue = blk_init_queue(memdisk_request, &dev->lock); + if (dev->queue == NULL) { + printk ("memdisk_setup_device blk_init_queue failure. \n"); + return; + } + + blk_queue_logical_block_size(dev->queue, hardsect_size); + dev->queue->queuedata = dev; + /* + * And the gendisk structure. + */ + dev->gd = alloc_disk(MEMDISK_MINORS); + if (! dev->gd) { + printk ("memdisk_setup_device alloc_disk failure. \n"); + return; + } + dev->gd->major = memdisk_major; + dev->gd->first_minor = i*MEMDISK_MINORS; + dev->gd->fops = &memdisk_ops; + dev->gd->queue = dev->queue; + dev->gd->private_data = dev; + + snprintf (dev->gd->disk_name, 32, "memdisk.%d", i); + set_capacity(dev->gd, sprd_memdisks[i].size*(hardsect_size/KERNEL_SECTOR_SIZE)); + add_disk(dev->gd); + + printk("memdisk_setup_device i:%d success.\n", i); + return; +} + +static int __init memdisk_init(void) +{ + int i; + + /* + * Get registered. + */ + printk("memdisk_init. \n"); + memdisk_major = register_blkdev(memdisk_major, "memdisk"); + if (memdisk_major <= 0) { + printk("memdisk: unable to get major number\n"); + return -EBUSY; + } + + for (i = 0; i < MEMDISKS_COUNT; i++) + memdisk_setup_device(&sprd_memdisks[i], i); + + printk("memdisk_init finished. \n"); + return 0; +} + +static void __exit memdisk_exit(void) +{ + int i; + + printk("memdisk_exit. \n"); + for (i = 0; i < MEMDISKS_COUNT; i++) { + struct memdisk_dev *dev = &sprd_memdisks[i]; + + if (dev->gd) { + del_gendisk(dev->gd); + put_disk(dev->gd); + } + if (dev->queue) { + blk_cleanup_queue(dev->queue); + } + } + unregister_blkdev(memdisk_major, "memdisk"); +} + +module_init(memdisk_init); +module_exit(memdisk_exit); diff --git a/drivers/memdisk/memdisk.h b/drivers/memdisk/memdisk.h new file mode 100644 index 00000000..c443d70f --- /dev/null +++ b/drivers/memdisk/memdisk.h @@ -0,0 +1,71 @@ + +/* + * sbull.h -- definitions for the char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + */ + + +#include <linux/ioctl.h> + +/* Multiqueue only works on 2.4 */ +#ifdef SBULL_MULTIQUEUE +# warning "Multiqueue only works on 2.4 kernels" +#endif + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SBULL_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "sbull: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + + +#define SBULL_MAJOR 0 /* dynamic major by default */ +#define SBULL_DEVS 2 /* two disks */ +#define SBULL_RAHEAD 2 /* two sectors */ +#define SBULL_SIZE 2048 /* two megs each */ +#define SBULL_BLKSIZE 1024 /* 1k blocks */ +#define SBULL_HARDSECT 512 /* 2.2 and 2.4 can used different values */ + +#define SBULLR_MAJOR 0 /* Dynamic major for raw device */ +/* + * The sbull device is removable: if it is left closed for more than + * half a minute, it is removed. Thus use a usage count and a + * kernel timer + */ + +typedef struct Sbull_Dev { + int size; + int usage; + struct timer_list timer; + spinlock_t lock; + u8 *data; +#ifdef SBULL_MULTIQUEUE + request_queue_t *queue; + int busy; +#endif +} Sbull_Dev; |
