URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [block/] [blkmem.c] - Rev 1782
Compare with Previous | Blame | View Log
/* blkmem.c: Block access to memory spaces * * Copyright (C) 1997, 1998 D. Jeff Dionne <jeff@pfnet.com>, * Kenneth Albanowski <kjahds@kjahds.com>, * The Silver Hammer Group, Ltd. * * Based z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space * Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. This software is provided "as is" without express or * implied warranty. */ #include <linux/config.h> #include <linux/major.h> #include <linux/malloc.h> #define MAJOR_NR BLKMEM_MAJOR #define VERBOSE #define DEBUG #include <linux/blk.h> #include <linux/blkmem.h> #if 0 #include <linux/init.h> #endif #if defined(MODULE) #include <linux/module.h> #endif #include <asm/bitops.h> #include <asm/delay.h> /* #include <asm/shglports.h> */ #define TRUE (1) #define FALSE (0) /* * Please, configure the ROMFS for your system here */ #if defined( CONFIG_PILOT ) && defined( CONFIG_M68328 ) #define CAT_ROMARRAY #endif #if defined( CONFIG_PILOT ) && defined( CONFIG_M68EZ328 ) extern char _flashstart[]; #define FIXED_ROMARRAY _flashstart #endif #ifdef CONFIG_UCSIMM #define CAT_ROMARRAY #endif #ifdef CONFIG_M68EZ328ADS #ifdef CONFIG_M68EZ328ADS_RAM extern char _flashstart[]; #define FIXED_ROMARRAY _flashstart #else #define CAT_ROMARRAY #endif #endif #ifdef CONFIG_ALMA_ANS #ifdef CONFIG_ALMA_ANS_RAM extern char _flashstart[]; #define FIXED_ROMARRAY _flashstart #else #define CAT_ROMARRAY #endif #endif /******* END OF BOARD-SPECIFIC CONFIGURATION ************/ /* Simple romfs, at internal, cat on the end of kernel, or seperate fixed adderess romfs. */ #ifdef INTERNAL_ROMARRAY #include "testromfs.c" #endif #ifdef CAT_ROMARRAY unsigned char *romarray; extern char __data_rom_start[]; extern char _edata[]; extern char __data_start[]; #endif #ifdef FIXED_ROMARRAY unsigned char *romarray = (char *)(FIXED_ROMARRAY); #endif /* If defined, ROOT_ARENA causes the root device to be the specified arena, useful with romfs */ /* Now defining ROOT_DEV in arch/setup.c */ /*#define ROOT_ARENA 0*/ struct arena_t; typedef void (*xfer_func_t)(struct arena_t *, unsigned long address, unsigned long length, char * buffer); typedef void (*erase_func_t)(struct arena_t *, unsigned long address); typedef void (*program_func_t)(struct arena_t *, struct blkmem_program_t * prog); void program_main(struct arena_t *, struct blkmem_program_t *); void read_spare(struct arena_t *, unsigned long, unsigned long, char *); void write_spare(struct arena_t *, unsigned long, unsigned long, char *); void erase_spare(struct arena_t *, unsigned long); /* This array of structures defines the actual set of memory arenas, including access functions (if the memory isn't part of the main address space) */ struct arena_t { int rw; unsigned long address; /* Address of memory arena */ unsigned long length; /* Length of memory arena. If -1, try to get size from romfs header */ program_func_t program_func; /* Function to program in one go */ xfer_func_t read_func; /* Function to transfer data to main memory, or zero if none needed */ xfer_func_t write_func; /* Function to transfer data from main memory, zero if none needed */ erase_func_t erase_func; /* Function to erase a block of memory to zeros, or 0 if N/A */ unsigned long blksize; /* Size of block that can be erased at one time, or 0 if N/A */ unsigned long unitsize; unsigned char erasevalue; /* Contents of sectors when erased */ /*unsigned int auto_erase_bits; unsigned int did_erase_bits;*/ } arena[] = { #ifdef INTERNAL_ROMARRAY {romarray, sizeof(romarray)}, #endif #ifdef CAT_ROMARRAY {0, 0, -1}, #define FIXUP_ARENAS \ arena[0].address = (unsigned long)__data_rom_start + (unsigned long)_edata - (unsigned long)__data_start; #endif #ifdef FIXED_ROMARRAY {0, FIXED_ROMARRAY, -1}, #endif #ifdef CONFIG_SHGLCORE #ifdef CONFIG_SHGLCORE_2MEG {0, 0x0A0000, 0x200000-0x0A0000}, /* ROM FS */ {1, SHGLCORE_FLASH_BANK_0_ADDR, 0x80000, 0, 0, write_spare, erase_spare, 0x10000, 0x80000, 0xff}, {1, 0x000000, 0x200000, program_main, 0,0,0, 0x20000, 0x100000}, /* All main FLASH */ #else {0, 0x0A0000, 0x100000-0x0A0000}, /* ROM FS */ {1, SHGLCORE_FLASH_BANK_0_ADDR, 0x80000, 0, 0, write_spare, erase_spare, 0x10000, 0x80000, 0xff}, {1, 0x000000, 0x100000, program_main, 0,0,0, 0x20000, 0x100000}, /* All main FLASH */ #endif #define FIXUP_ARENAS \ extern unsigned long rom_length; \ arena[0].length = (unsigned long)rom_length - 0xA0000; \ arena[2].length = (unsigned long)rom_length; #endif }; #define arenas (sizeof(arena) / sizeof(struct arena_t)) static int blkmem_blocksizes[arenas]; static int blkmem_sizes[arenas]; #ifdef CONFIG_SHGLCORE static struct semaphore spare_lock = MUTEX; void read_spare(struct arena_t * a, unsigned long pos, unsigned long length, char * buffer) { #ifdef DEBUG printk("rsl\n"); #endif /* Mutex all access to FLASH */ down(&spare_lock); #ifdef DEBUG printk("rsld\n"); #endif /* Just copy the data into target buffer */ memcpy( buffer, (void*)(a->address+pos), length); /* Release MUTEX */ up(&spare_lock); #ifdef DEBUG printk("rsud\n"); #endif } void write_spare(struct arena_t * a, unsigned long pos, unsigned long length, char * buffer) { unsigned long start; unsigned char c; volatile unsigned char * address; unsigned char result; unsigned long fbase = a->address; unsigned long flags; #if 0 for(i = pos / a->blksize; i <= ((pos+length-1) / a->blksize); i++) { if (test_bit(i, &a->auto_erase_bits)) { /* erase sector start */ printk("Autoerase of sector %d\n", i); erase_spare(a, i * a->blksize); clear_bit(i, &a->auto_erase_bits); } } #endif #ifdef DEBUG printk("wsl\n"); #endif down(&spare_lock); #ifdef DEBUG printk("wsld\n"); #endif start = jiffies; address = (unsigned volatile char*)(fbase+pos); while (length>0) { c = *buffer++; /*printk("Checking spare_flash program of byte %lx, at address %p, value %x (%c), current %x (%c)\n", pos, address, c, c, *address, *address);*/ if (*address != c) { /*printk("Starting spare_flash program of byte %lx, at address %p\n", pos, address);*/ if (c & ~*address) { printk("Unable to write byte at %p (impossible bit transition in %x, actual %x)\n", address, c, *address); /*continue;*/ } save_flags(flags); cli(); *(unsigned volatile char *)(fbase | 0x5555)=0x0aa; *(unsigned volatile char *)(fbase | 0x2aaa)=0x055; *(unsigned volatile char *)(fbase | 0x5555)=0x0a0; *address = c; for(;;) { result = *address; /*printk("Read value %x (%c)\n", result, result);*/ if ((result & 0x80) == (c & 0x80)) break; if (result & 0x20) { printk("timeout of FLASH write at address %p of value %x (actual %x)\n", address, c, *address); *(unsigned volatile char *)(fbase)=0x0f0; /* Reset */ break; } } restore_flags(flags); /*printk("Completed spare_flash program of byte %lx, at address %p\n", pos, address);*/ #if 0 if (jiffies != start) { /*printk("Spare_flash rescheduling in write\n");*/ current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies; schedule(); current->timeout = 0; /*schedule();*/ start = jiffies; } #endif } address++; length--; } up(&spare_lock); #ifdef DEBUG printk("wsud\n"); #endif } void erase_spare(struct arena_t * a, unsigned long pos) { unsigned long fbase = a->address; int delay; unsigned volatile char * address; unsigned long flags; if (pos >= a->length) return; /* Mutex all access to FLASH memory */ #ifdef DEBUG printk("esl\n"); #endif down(&spare_lock); #ifdef DEBUG printk("esld\n"); #endif address = (unsigned volatile char*)(fbase + pos); printk("Starting spare_flash erase of byte %lx, at address %p\n", pos, address); save_flags(flags); cli(); again: delay = HZ/4+1; /* Initiate erase of FLASH sector */ *(unsigned volatile char *)(fbase | 0x5555)=0x0aa; *(unsigned volatile char *)(fbase | 0x2aaa)=0x055; *(unsigned volatile char *)(fbase | 0x5555)=0x080; *(unsigned volatile char *)(fbase | 0x5555)=0x0aa; *(unsigned volatile char *)(fbase | 0x2aaa)=0x055; *address = 0x030; /* Delay until erase is complete */ for (;;) { unsigned char result; #ifdef original_spare_erase_delay struct wait_queue *wait = NULL; #ifdef DEBUG printk("Spare_flash erase delaying for %d ticks, status is %x\n", delay, (unsigned int)*address); #endif current->timeout = jiffies + delay; #if 0 current->state = TASK_INTERRUPTIBLE; schedule(); current->timeout = 0; #endif interruptible_sleep_on(&wait); #endif udelay(100000); result = *address; if (result & 0x80) break; if (result & 0x20) { printk("timeout of Spare_flash erase of address %p\n", address); *(unsigned volatile char *)(fbase)=0x0f0; /* Reset */ printk("Sleeping a second and retrying\n"); udelay(1000000); goto again; } } restore_flags(flags); #ifdef DEBUG printk("Completed spare_flash erase of byte %lx, at address %p\n", pos, address); #endif up(&spare_lock); #ifdef DEBUG printk("esud\n"); #endif } #define VSP(X) (*(volatile unsigned short *)(X)) #define VSC(X) (*(volatile unsigned char *)(X)) #define SCSR VSP(0xfffc0c) #define SCSR_TDRE (1<<8) #define SCDR VSP(0xfffC0e) #define print_char(x) ({ \ while (!(SCSR & SCSR_TDRE)) \ ; \ SCDR = (x); \ }) #define print_hexdigit(x) ({ \ int digit = (x) & 0xf; \ if (digit>9) \ print_char('a'+digit-10); \ else \ print_char('0'+digit); \ \ }) #define print_num(x) ({ \ unsigned long num = (x); \ print_hexdigit(num >> 28); \ print_hexdigit(num >> 24); \ print_hexdigit(num >> 20); \ print_hexdigit(num >> 16); \ print_hexdigit(num >> 12); \ print_hexdigit(num >> 8); \ print_hexdigit(num >> 4); \ print_hexdigit(num >> 0); \ }) /* Note: sub_program_main must not reference _any_ data or code outside of itself, or leave interrupts enabled, due to the fact that it is probably erasing & reloading the kernel. */ #define SET_SHORT(x,y) VSP((x)) = (y) /*#define SET_SHORT(x,y) ({})*/ /*print_char('>');print_num(x);*/ /*printk("%8.8lx <= %04x\n", (x), (y))*/ #define SET_CHAR(x,y) VSC((x)) = (y) /*#define SET_CHAR(x,y) ({})*/ /*print_char('>');print_num(x);*/ /*printk("%8.8lx <= %02x\n", (x), (y))*/ #define GET_SHORT(x) VSP((x)) /*#define GET_SHORT(x) ({0;})*/ /*({print_char('<');print_num(x);0;})*/ /*(printk("%8.8lx => ....\n", (x)),0)*/ #define GET_CHAR(x) VSC((x)) /*#define GET_CHAR(x) ({0;})*/ /*({print_char('<');print_num(x);0;})*/ /*(printk("%8.8lx => ..\n", (x)),0)*/ void sub_program_main(struct arena_t * a, struct blkmem_program_t * prog) { volatile int i,l; unsigned long base, offset, ptr, min, max; unsigned char * c; unsigned int erased = 0; int failures; int retry; cli(); retry = 0; again: SET_ALARM_LED(1); retry++; if (retry>5) { goto give_up; } print_char('\r'); print_char('\n'); print_char('R'); print_char('0' + retry); failures = 0; erased = 0; /* for(i=prog->blocks-1;i>=0;i--) {*/ for(i=0;i<prog->blocks;i++) { SET_COMM_STATUS_LED(!GET_COMM_STATUS_LED()); print_char('\r'); print_char('\n'); print_num(prog->block[i].pos+a->address); print_char('-'); print_num(prog->block[i].pos+prog->block[i].length-1+a->address); print_char('\r'); print_char('\n'); if(prog->block[i].length > 0xE0000) break; for(l=prog->block[i].pos / a->blksize; l <= ((prog->block[i].pos+prog->block[i].length-1) / a->blksize); l++) { if (!test_bit(l, &erased)) { print_char('E'); print_char('0' + l / 10); print_char('0' + l % 10); print_char('\r'); print_char('\n'); if (l < 1) break; /*if (l >= 8) break;*/ ptr = l * a->blksize; offset = ptr % a->unitsize; base = ptr - offset; base += a->address; ptr += a->address; print_char('b'); print_char('a'); print_char('s'); print_char('e'); print_char(' '); print_num(base); print_char('\r'); print_char('\n'); print_char('o'); print_char('f'); print_char('f'); print_char(' '); print_num(offset); print_char('\r'); print_char('\n'); print_char('p'); print_char('t'); print_char('r'); print_char(' '); print_num(ptr); print_char('\r'); print_char('\n'); set_bit(l, &erased); if (ptr < 0x020000) break; /*if (ptr >= 0x100000) break;*/ print_num(ptr); SET_COMM_ERROR_LED(1); /* Erase even half of sector */ SET_SHORT( (base | (0x5555 << 1)), 0xaa00); SET_SHORT( (base | (0x2aaa << 1)), 0x5500); SET_SHORT( (base | (0x5555 << 1)), 0x8000); SET_SHORT( (base | (0x5555 << 1)), 0xaa00); SET_SHORT( (base | (0x2aaa << 1)), 0x5500); SET_SHORT( ptr, 0x3000); #ifdef original_erase_logic while (!(GET_SHORT(ptr) & 0x8000)) ; #else for (;;) { unsigned int status = GET_SHORT(ptr); if (status & 0x8000) { /* Erase complete */ break; } if (status & 0x2000) { /* Check again */ status = GET_SHORT(ptr); if (status & 0x8000) { /* Erase complete */ break; } /* Erase failed */ print_char('F'); /* Reset FLASH unit */ SET_SHORT( base, 0xf000); failures++; /* Continue (with unerased sector) */ break; } } #endif print_char(':'); /* Erase odd half of sector */ SET_SHORT( (base | (0x5555 << 1)), 0x00aa); SET_SHORT( (base | (0x2aaa << 1)), 0x0055); SET_SHORT( (base | (0x5555 << 1)), 0x0080); SET_SHORT( (base | (0x5555 << 1)), 0x00aa); SET_SHORT( (base | (0x2aaa << 1)), 0x0055); SET_SHORT( ptr, 0x0030); #ifdef original_erase_logic while (!(GET_SHORT(ptr) & 0x0080)) ; #else for (;;) { unsigned int status = GET_SHORT(ptr); if (status & 0x0080) { /* Erase complete */ break; } if (status & 0x0020) { /* Check again */ status = GET_SHORT(ptr); if (status & 0x0080) { /* Erase complete */ break; } /* Erase failed */ print_char('F'); /* Reset FLASH unit */ SET_SHORT( base, 0x00f0); failures++; /* Continue (with unerased sector) */ break; } } #endif print_char(':'); #if 0 probe = (volatile unsigned short*)(fbase + a->blksize * l); *probe = 0x3000; while (!(*probe & 0x8000)) ; print_char('.'); /* Erase odd half of sector */ *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00aa; *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x0055; *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x0080; *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00aa; *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x0055; probe = (volatile unsigned short*)(fbase + a->blksize * l); *probe = 0x0030; while (!(*probe & 0x0080)) break; print_char('.'); #endif SET_COMM_ERROR_LED(0); } } min = prog->block[i].pos+a->address; max = prog->block[i].pos+prog->block[i].length+a->address; for(ptr=min, c=prog->block[i].data; ptr<max; ptr++, c++) { offset = (ptr-a->address) % a->unitsize; base = ptr - offset; if (ptr < 0x020000) break; /*if (ptr >= 0x100000) break;*/ /*print_char('.');*/ #if 0 if ((fbase & 1) == 0) { /* Even bank */ *(unsigned volatile short *)(fbase | (0x5555 << 1))=0xaa00; *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x5500; *(unsigned volatile short *)(fbase | (0x5555 << 1))=0xa000; *(unsigned volatile short *)(fbase + (b & ~1)) =*c << 8; } else { /* Odd bank */ *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00aa; *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x0055; *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00a0; *(unsigned volatile short *)(fbase + (b & ~1)) =*c; } probe = (volatile unsigned char*)(fbase + b); while (*probe != *c) break; #endif if ((ptr & 1) == 0) { /* Even bank */ SET_SHORT( (base | (0x5555 << 1)), 0xaa00); SET_SHORT( (base | (0x2aaa << 1)), 0x5500); SET_SHORT( (base | (0x5555 << 1)), 0xa000); SET_SHORT( (ptr & ~1), *c << 8); } else { /* Odd bank */ SET_SHORT( (base | (0x5555 << 1)), 0x00aa); SET_SHORT( (base | (0x2aaa << 1)), 0x0055); SET_SHORT( (base | (0x5555 << 1)), 0x00a0); SET_SHORT( (ptr & ~1), *c); } #ifdef original_write_logic while (GET_CHAR(ptr) != *c) ; #else for (;;) { unsigned char status = GET_CHAR(ptr); if ((status & 0x80) == (*c & 0x80)) { /* Program complete */ break; } if (status & 0x20) { /* Check again */ status = GET_CHAR(ptr); if ((status & 0x80) == (*c & 0x80)) { /* Program complete */ break; } /* Program failed */ print_char('F'); /* Reset FLASH unit */ if ((ptr & 1) == 0) { /* Even bank */ SET_SHORT( base, 0xf000); } else { /* Odd bank */ SET_SHORT( base, 0x00f0); } failures++; /* Continue */ break; } } #endif /*print_char(' ');*/ } } if (failures > 0) { /* There were failures erasing the FLASH, so go back to the beginning and try it all again -- for lack of anything better to do. */ print_char('!'); goto again; } give_up: SET_ALARM_LED(1); HARD_RESET_NOW(); i = 1; while(i) ; } void program_main(struct arena_t * a, struct blkmem_program_t * prog) { int len; void (*code)(struct arena_t*, struct blkmem_program_t *); printk("program_main entered, blocks = %d\n", prog->blocks); len = &program_main-&sub_program_main; code = kmalloc(len, GFP_KERNEL); memcpy(code, &sub_program_main, len); code(a, prog); kfree(code); /*sub_program_main(a, prog);*/ } #endif /* CONFIG_SHGLCORE */ int general_program_func(struct inode * inode, struct file * file, struct arena_t * a, struct blkmem_program_t * prog) { int i,block; int err; unsigned int erased = 0; unsigned long pos = file->f_pos; /* Mandatory flush of all dirty buffers */ fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); for(i=0;i<prog->blocks;i++) { int min= prog->block[i].pos / a->blksize; int max = (prog->block[i].pos + prog->block[i].length - 1) / a->blksize; for(block=min; block <= max; block++) { if (!test_bit(block, &erased)) { printk("Erase of sector at pos %lx of arena %d (address %p)\n", block * a->blksize, MINOR(inode->i_rdev), (void*)(a->address+block*a->blksize)); /* Invoke erase function */ a->erase_func(a, block * a->blksize); set_bit(block, &erased); } } printk("Write of %lu bytes at pos %lu (data at %p)\n", prog->block[i].length, prog->block[i].pos, prog->block[i].data); a->write_func(a, prog->block[i].pos, prog->block[i].length, prog->block[i].data); schedule(); } if (prog->reset) HARD_RESET_NOW(); return 0; } static void complete_request(void * data); static struct tq_struct complete_tq = {0, 0, complete_request, NULL}; static void delay_request( void ) { queue_task(&complete_tq, &tq_scheduler); } static void complete_request( void * data) { unsigned long start; unsigned long len; struct arena_t * a = arena + DEVICE_NR(CURRENT_DEV); for(;;) { /* sectors are 512 bytes */ start = CURRENT->sector << 9; len = CURRENT->current_nr_sectors << 9; /*printk("blkmem: re-request %d\n", CURRENT->cmd);*/ if ( CURRENT->cmd == READ ) { /*printk("BMre-Read: %lx:%lx > %p\n", a->address + start, len, CURRENT->buffer);*/ a->read_func(a, start, len, CURRENT->buffer); } else if (CURRENT->cmd == WRITE) { /*printk("BMre-Write: %p > %lx:%lx\n", CURRENT->buffer, a->address + start, len);*/ a->write_func(a, start, len, CURRENT->buffer); } /*printk("ending blkmem request\n");*/ end_request( TRUE ); INIT_REQUEST; } #if 0 if (CURRENT) do_blkmem_request(); /* Process subsequent requests */ #endif } static void do_blkmem_request( void ) { unsigned long start; unsigned long len; struct arena_t * a = arena + DEVICE_NR(CURRENT_DEV); #if 0 printk( KERN_ERR DEVICE_NAME ": request\n"); #endif while ( TRUE ) { INIT_REQUEST; /* sectors are 512 bytes */ start = CURRENT->sector << 9; len = CURRENT->current_nr_sectors << 9; if ((start + len) > a->length) { printk( KERN_ERR DEVICE_NAME ": bad access: block=%ld, count=%ld (pos=%lx, len=%lx)\n", CURRENT->sector, CURRENT->current_nr_sectors, start+len, a->length); end_request( FALSE ); continue; } /*printk("blkmem: request %d\n", CURRENT->cmd);*/ if ( ( CURRENT->cmd != READ ) && ( CURRENT->cmd != WRITE ) ) { printk( KERN_ERR DEVICE_NAME ": bad command: %d\n", CURRENT->cmd ); end_request( FALSE ); continue; } if ( CURRENT->cmd == READ ) { /*printk("BMRead %d, pid %d: %lx:%lx > %p\n", DEVICE_NR(CURRENT_DEV), current->pid, a->address + start, len, CURRENT->buffer);*/ if (a->read_func) { delay_request(); return; /*a->read_func(a, start, len, CURRENT->buffer);*/ } else memcpy( CURRENT->buffer, (void*)(a->address + start), len ); } else if (CURRENT->cmd == WRITE) { /*printk("BMWrite %d: %p > %lx:%lx\n", DEVICE_NR(CURRENT_DEV), CURRENT->buffer, a->address + start, len);*/ if (a->write_func) { delay_request(); return; /*a->write_func(a, start, len, CURRENT->buffer);*/ } else memcpy( (void*)(a->address + start), CURRENT->buffer, len ); } /*printk("ending blkmem request\n");*/ end_request( TRUE ); } } #ifdef MAGIC_ROM_PTR static int blkmem_romptr( struct inode *inode, struct file *filp, struct vm_area_struct * vma) { struct arena_t * a = arena + MINOR(inode->i_rdev); if (a->read_func) return -ENOSYS; /* Can't do it, as this arena isn't in the main address space */ vma->vm_start = a->address + vma->vm_offset; return 0; } #endif static int blkmem_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct arena_t * a = arena + MINOR(inode->i_rdev); int err; switch (cmd) { case BMGETSIZES: /* Return device size in sectors */ if (!arg) return -EINVAL; err = verify_area(VERIFY_WRITE, (unsigned long *) arg, sizeof(unsigned long)); if (err) return err; put_user(a->blksize ? (a->length / a->blksize) : 0, (unsigned long *) arg); break; case BMGETSIZEB: /* Return device size in bytes */ if (!arg) return -EINVAL; err = verify_area(VERIFY_WRITE, (unsigned long *) arg, sizeof(unsigned long)); if (err) return err; put_user(a->length, (unsigned long *) arg); break; case BMSERASE: if (a->erase_func) { printk("Erase of sector at pos %lx of arena %d (address %p)\n", arg, MINOR(inode->i_rdev), (void*)(a->address+arg)); if (arg >= a->length) return -EINVAL; /* Mandatory flush of all dirty buffers */ fsync_dev(inode->i_rdev); /* Invalidate any buffers pointing to the section that will be erased */ invalidate_buffers_by_byte(inode->i_rdev, arg * a->blksize, a->blksize); /* Invoke erase function */ a->erase_func(a, arg); } else return -EINVAL; break; case BMSGSIZE: if (!arg) return -EINVAL; err = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long)); if (err) return err; put_user(a->blksize, (unsigned long*)arg); break; case BMSGERASEVALUE: err = verify_area(VERIFY_WRITE, (unsigned char*)arg, sizeof(unsigned char)); if (err) return err; *(unsigned char*)arg = a->erasevalue; break; case BMPROGRAM: { struct blkmem_program_t * prog; int i; if (!arg) return -EINVAL; prog = (struct blkmem_program_t*)arg; /* Extensive checks to verify that the programming data makes sense */ err = verify_area(VERIFY_READ, prog, sizeof(struct blkmem_program_t)); if (err) return err; if ((prog->magic1 != BMPROGRAM_MAGIC_1) || (prog->magic2 != BMPROGRAM_MAGIC_2)) return -EINVAL; err = verify_area(VERIFY_READ, prog, sizeof(struct blkmem_program_t) + sizeof(prog->block[0]) * prog->blocks); if (err) return err; for(i=0;i<prog->blocks;i++) if(prog->block[i].magic3 != BMPROGRAM_MAGIC_3) return -EINVAL; for(i=0;i<prog->blocks;i++) if ((prog->block[i].pos > a->length) || ((prog->block[i].pos+prog->block[i].length-1) > a->length)) return -EINVAL; for(i=0;i<prog->blocks;i++) { err = verify_area(VERIFY_READ, prog->block[i].data, prog->block[i].length); if (err) return err; } if (a->program_func) { a->program_func(a, prog); } else { return general_program_func(inode, file, a, prog); } break; } default: return -EINVAL; } return 0; } static int blkmem_open( struct inode *inode, struct file *filp ) { int device; struct arena_t * a; device = DEVICE_NR( inode->i_rdev ); #if 0 printk( KERN_ERR DEVICE_NAME ": open: %d\n", device ); #endif if ((MINOR(device) < 0) || (MINOR(device) >= arenas)) { printk("arena open of %d failed!\n", MINOR(device)); return -ENODEV; } a = &arena[MINOR(device)]; #ifdef DEBUG printk("Open of blkmem arena %d at %lx, length %lx\n", MINOR(device), a->address, a->length); #endif #if defined(MODULE) MOD_INC_USE_COUNT; #endif return 0; } static void blkmem_release( struct inode *inode, struct file *filp ) { #if 0 printk( KERN_ERR DEVICE_NAME ": release: %d\n", current_device ); #endif fsync_dev( inode->i_rdev ); #if defined(MODULE) MOD_DEC_USE_COUNT; #endif return; } static struct file_operations blkmem_fops = { NULL, /* lseek - default */ block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ NULL, /* readdir - bad */ NULL, /* poll */ blkmem_ioctl, /* ioctl */ NULL, blkmem_open, /* open */ blkmem_release, /* release */ block_fsync, /* fsync */ NULL, /* fasync */ NULL, /* check media change */ NULL, /* revalidate */ blkmem_romptr, /* romptr */ }; int blkmem_init( void ) { int i; if ( register_blkdev( MAJOR_NR, DEVICE_NAME, &blkmem_fops )) { printk( KERN_ERR DEVICE_NAME ": Unable to get major %d\n", MAJOR_NR ); return -EBUSY; } #ifdef FIXUP_ARENAS { FIXUP_ARENAS } #endif for(i=0;i<arenas;i++) { if (arena[i].length == -1) arena[i].length = *(volatile unsigned long *)(arena[i].address + 8); blkmem_blocksizes[i] = 1024; blkmem_sizes[i] = (arena[i].length + (1 << 10) - 1) >> 10; /* Round up */ arena[i].length = blkmem_sizes[i] << 10; } printk("Blkmem copyright 1998,1999 D. Jeff Dionne\nBlkmem copyright 1998 Kenneth Albanowski\nBlkmem %ld disk images:\n", arenas); for(i=0;i<arenas;i++) { printk("%d: %lX-%lX (%s)\n", i, arena[i].address, arena[i].address+arena[i].length-1, arena[i].rw ? "RW" : "RO" ); } read_ahead[ MAJOR_NR ] = 0; blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST; blksize_size[ MAJOR_NR ] = blkmem_blocksizes; blk_size[ MAJOR_NR ] = blkmem_sizes; #ifdef ROOT_ARENA ROOT_DEV = MKDEV(MAJOR_NR,ROOT_ARENA); #endif return 0; } #if defined(MODULE) int init_module( void ) { int error; error = blkmem_init(); if ( error == 0 ) { printk( KERN_INFO DEVICE_NAME ": loaded as module\n" ); } return error; } void cleanup_module( void ) { if ( unregister_blkdev( MAJOR_NR, DEVICE_NAME ) != 0 ) printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n"); if ( current_device != -1 ) { /* free whatever we resources remain here */ } return; } #endif