1/* 2 * Direct MTD block device access 3 * 4 * $Id: mtdblock.c,v 1.1.1.1 2008/10/15 03:26:35 james26_jang Exp $ 5 * 6 * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache 7 */ 8 9#include <linux/config.h> 10#include <linux/types.h> 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/slab.h> 14#include <linux/mtd/mtd.h> 15#include <linux/mtd/compatmac.h> 16 17#define MAJOR_NR MTD_BLOCK_MAJOR 18#define DEVICE_NAME "mtdblock" 19#define DEVICE_REQUEST mtdblock_request 20#define DEVICE_NR(device) (device) 21#define DEVICE_ON(device) 22#define DEVICE_OFF(device) 23#define DEVICE_NO_RANDOM 24#include <linux/blk.h> 25/* for old kernels... */ 26#ifndef QUEUE_EMPTY 27#define QUEUE_EMPTY (!CURRENT) 28#endif 29#if LINUX_VERSION_CODE < 0x20300 30#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) 31#else 32#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) 33#endif 34 35#ifdef CONFIG_DEVFS_FS 36#include <linux/devfs_fs_kernel.h> 37static void mtd_notify_add(struct mtd_info* mtd); 38static void mtd_notify_remove(struct mtd_info* mtd); 39static struct mtd_notifier notifier = { 40 mtd_notify_add, 41 mtd_notify_remove, 42 NULL 43}; 44static devfs_handle_t devfs_dir_handle = NULL; 45static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; 46#endif 47 48static struct mtdblk_dev { 49 struct mtd_info *mtd; /* Locked */ 50 int count; 51 struct semaphore cache_sem; 52 unsigned char *cache_data; 53 unsigned long cache_offset; 54 unsigned int cache_size; 55 enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; 56} *mtdblks[MAX_MTD_DEVICES]; 57 58static spinlock_t mtdblks_lock; 59 60static int mtd_sizes[MAX_MTD_DEVICES]; 61static int mtd_blksizes[MAX_MTD_DEVICES]; 62 63#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) 64#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT 65#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT 66#else 67#define BLK_INC_USE_COUNT do {} while(0) 68#define BLK_DEC_USE_COUNT do {} while(0) 69#endif 70 71/* 72 * Cache stuff... 73 * 74 * Since typical flash erasable sectors are much larger than what Linux's 75 * buffer cache can handle, we must implement read-modify-write on flash 76 * sectors for each block write requests. To avoid over-erasing flash sectors 77 * and to speed things up, we locally cache a whole flash sector while it is 78 * being written to until a different sector is required. 79 */ 80 81static void erase_callback(struct erase_info *done) 82{ 83 wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; 84 wake_up(wait_q); 85} 86 87static int erase_write (struct mtd_info *mtd, unsigned long pos, 88 int len, const char *buf) 89{ 90 struct erase_info erase; 91 DECLARE_WAITQUEUE(wait, current); 92 wait_queue_head_t wait_q; 93 size_t retlen; 94 int ret; 95 96 /* 97 * First, let's erase the flash block. 98 */ 99 100 init_waitqueue_head(&wait_q); 101 erase.mtd = mtd; 102 erase.callback = erase_callback; 103 erase.addr = pos; 104 erase.len = len; 105 erase.priv = (u_long)&wait_q; 106 107 set_current_state(TASK_INTERRUPTIBLE); 108 add_wait_queue(&wait_q, &wait); 109 110 ret = MTD_ERASE(mtd, &erase); 111 if (ret) { 112 set_current_state(TASK_RUNNING); 113 remove_wait_queue(&wait_q, &wait); 114 printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " 115 "on \"%s\" failed\n", 116 pos, len, mtd->name); 117 return ret; 118 } 119 120 schedule(); /* Wait for erase to finish. */ 121 remove_wait_queue(&wait_q, &wait); 122 123 /* 124 * Next, writhe data to flash. 125 */ 126 127 ret = MTD_WRITE (mtd, pos, len, &retlen, buf); 128 if (ret) 129 return ret; 130 if (retlen != len) 131 return -EIO; 132 return 0; 133} 134 135 136static int write_cached_data (struct mtdblk_dev *mtdblk) 137{ 138 struct mtd_info *mtd = mtdblk->mtd; 139 int ret; 140 141 if (mtdblk->cache_state != STATE_DIRTY) 142 return 0; 143 144 DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" " 145 "at 0x%lx, size 0x%x\n", mtd->name, 146 mtdblk->cache_offset, mtdblk->cache_size); 147 148 ret = erase_write (mtd, mtdblk->cache_offset, 149 mtdblk->cache_size, mtdblk->cache_data); 150 if (ret) 151 return ret; 152 153 /* 154 * Here we could argably set the cache state to STATE_CLEAN. 155 * However this could lead to inconsistency since we will not 156 * be notified if this content is altered on the flash by other 157 * means. Let's declare it empty and leave buffering tasks to 158 * the buffer cache instead. 159 */ 160 mtdblk->cache_state = STATE_EMPTY; 161 return 0; 162} 163 164 165static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, 166 int len, const char *buf) 167{ 168 struct mtd_info *mtd = mtdblk->mtd; 169 unsigned int sect_size = mtdblk->cache_size; 170 size_t retlen; 171 int ret; 172 173 DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n", 174 mtd->name, pos, len); 175 176 if (!sect_size) 177 return MTD_WRITE (mtd, pos, len, &retlen, buf); 178 179 while (len > 0) { 180 unsigned long sect_start = (pos/sect_size)*sect_size; 181 unsigned int offset = pos - sect_start; 182 unsigned int size = sect_size - offset; 183 if( size > len ) 184 size = len; 185 186 if (size == sect_size) { 187 /* 188 * We are covering a whole sector. Thus there is no 189 * need to bother with the cache while it may still be 190 * useful for other partial writes. 191 */ 192 ret = erase_write (mtd, pos, size, buf); 193 if (ret) 194 return ret; 195 } else { 196 /* Partial sector: need to use the cache */ 197 198 if (mtdblk->cache_state == STATE_DIRTY && 199 mtdblk->cache_offset != sect_start) { 200 ret = write_cached_data(mtdblk); 201 if (ret) 202 return ret; 203 } 204 205 if (mtdblk->cache_state == STATE_EMPTY || 206 mtdblk->cache_offset != sect_start) { 207 /* fill the cache with the current sector */ 208 mtdblk->cache_state = STATE_EMPTY; 209 ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data); 210 if (ret) 211 return ret; 212 if (retlen != sect_size) 213 return -EIO; 214 215 mtdblk->cache_offset = sect_start; 216 mtdblk->cache_size = sect_size; 217 mtdblk->cache_state = STATE_CLEAN; 218 } 219 220 /* write data to our local cache */ 221 memcpy (mtdblk->cache_data + offset, buf, size); 222 mtdblk->cache_state = STATE_DIRTY; 223 } 224 225 buf += size; 226 pos += size; 227 len -= size; 228 } 229 230 return 0; 231} 232 233 234static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, 235 int len, char *buf) 236{ 237 struct mtd_info *mtd = mtdblk->mtd; 238 unsigned int sect_size = mtdblk->cache_size; 239 size_t retlen; 240 int ret; 241 242 DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n", 243 mtd->name, pos, len); 244 245 if (!sect_size) 246 return MTD_READ (mtd, pos, len, &retlen, buf); 247 248 while (len > 0) { 249 unsigned long sect_start = (pos/sect_size)*sect_size; 250 unsigned int offset = pos - sect_start; 251 unsigned int size = sect_size - offset; 252 if (size > len) 253 size = len; 254 255 /* 256 * Check if the requested data is already cached 257 * Read the requested amount of data from our internal cache if it 258 * contains what we want, otherwise we read the data directly 259 * from flash. 260 */ 261 if (mtdblk->cache_state != STATE_EMPTY && 262 mtdblk->cache_offset == sect_start) { 263 memcpy (buf, mtdblk->cache_data + offset, size); 264 } else { 265 ret = MTD_READ (mtd, pos, size, &retlen, buf); 266 if (ret) 267 return ret; 268 if (retlen != size) 269 return -EIO; 270 } 271 272 buf += size; 273 pos += size; 274 len -= size; 275 } 276 277 return 0; 278} 279 280 281 282static int mtdblock_open(struct inode *inode, struct file *file) 283{ 284 struct mtdblk_dev *mtdblk; 285 struct mtd_info *mtd; 286 int dev; 287 288 DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); 289 290 if (!inode) 291 return -EINVAL; 292 293 dev = MINOR(inode->i_rdev); 294 if (dev >= MAX_MTD_DEVICES) 295 return -EINVAL; 296 297 BLK_INC_USE_COUNT; 298 299 mtd = get_mtd_device(NULL, dev); 300 if (!mtd) 301 return -ENODEV; 302 if (MTD_ABSENT == mtd->type) { 303 put_mtd_device(mtd); 304 BLK_DEC_USE_COUNT; 305 return -ENODEV; 306 } 307 308 spin_lock(&mtdblks_lock); 309 310 /* If it's already open, no need to piss about. */ 311 if (mtdblks[dev]) { 312 mtdblks[dev]->count++; 313 spin_unlock(&mtdblks_lock); 314 return 0; 315 } 316 317 /* OK, it's not open. Try to find it */ 318 319 /* First we have to drop the lock, because we have to 320 to things which might sleep. 321 */ 322 spin_unlock(&mtdblks_lock); 323 324 mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); 325 if (!mtdblk) { 326 put_mtd_device(mtd); 327 BLK_DEC_USE_COUNT; 328 return -ENOMEM; 329 } 330 memset(mtdblk, 0, sizeof(*mtdblk)); 331 mtdblk->count = 1; 332 mtdblk->mtd = mtd; 333 334 init_MUTEX (&mtdblk->cache_sem); 335 mtdblk->cache_state = STATE_EMPTY; 336 if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM && 337 mtdblk->mtd->erasesize) { 338 mtdblk->cache_size = mtdblk->mtd->erasesize; 339 mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); 340 if (!mtdblk->cache_data) { 341 put_mtd_device(mtdblk->mtd); 342 kfree(mtdblk); 343 BLK_DEC_USE_COUNT; 344 return -ENOMEM; 345 } 346 } 347 348 /* OK, we've created a new one. Add it to the list. */ 349 350 spin_lock(&mtdblks_lock); 351 352 if (mtdblks[dev]) { 353 /* Another CPU made one at the same time as us. */ 354 mtdblks[dev]->count++; 355 spin_unlock(&mtdblks_lock); 356 put_mtd_device(mtdblk->mtd); 357 vfree(mtdblk->cache_data); 358 kfree(mtdblk); 359 return 0; 360 } 361 362 mtdblks[dev] = mtdblk; 363 mtd_sizes[dev] = mtdblk->mtd->size/1024; 364 if (mtdblk->mtd->erasesize) 365 mtd_blksizes[dev] = mtdblk->mtd->erasesize; 366 if (mtd_blksizes[dev] > PAGE_SIZE) 367 mtd_blksizes[dev] = PAGE_SIZE; 368 set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); 369 370 spin_unlock(&mtdblks_lock); 371 372 DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); 373 374 return 0; 375} 376 377static release_t mtdblock_release(struct inode *inode, struct file *file) 378{ 379 int dev; 380 struct mtdblk_dev *mtdblk; 381 DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); 382 383 if (inode == NULL) 384 release_return(-ENODEV); 385 386 dev = MINOR(inode->i_rdev); 387 mtdblk = mtdblks[dev]; 388 389 down(&mtdblk->cache_sem); 390 write_cached_data(mtdblk); 391 up(&mtdblk->cache_sem); 392 393 spin_lock(&mtdblks_lock); 394 if (!--mtdblk->count) { 395 /* It was the last usage. Free the device */ 396 mtdblks[dev] = NULL; 397 spin_unlock(&mtdblks_lock); 398 if (mtdblk->mtd->sync) 399 mtdblk->mtd->sync(mtdblk->mtd); 400 put_mtd_device(mtdblk->mtd); 401 vfree(mtdblk->cache_data); 402 kfree(mtdblk); 403 } else { 404 spin_unlock(&mtdblks_lock); 405 } 406 407 DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); 408 409 BLK_DEC_USE_COUNT; 410 release_return(0); 411} 412 413 414/* 415 * This is a special request_fn because it is executed in a process context 416 * to be able to sleep independently of the caller. The io_request_lock 417 * is held upon entry and exit. 418 * The head of our request queue is considered active so there is no need 419 * to dequeue requests before we are done. 420 */ 421static void handle_mtdblock_request(void) 422{ 423 struct request *req; 424 struct mtdblk_dev *mtdblk; 425 unsigned int res; 426 427 for (;;) { 428 INIT_REQUEST; 429 req = CURRENT; 430 spin_unlock_irq(&io_request_lock); 431 mtdblk = mtdblks[MINOR(req->rq_dev)]; 432 res = 0; 433 434 if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) 435 panic(__FUNCTION__": minor out of bound"); 436 437 if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) 438 goto end_req; 439 440 // Handle the request 441 switch (req->cmd) 442 { 443 int err; 444 445 case READ: 446 down(&mtdblk->cache_sem); 447 err = do_cached_read (mtdblk, req->sector << 9, 448 req->current_nr_sectors << 9, 449 req->buffer); 450 up(&mtdblk->cache_sem); 451 if (!err) 452 res = 1; 453 break; 454 455 case WRITE: 456 // Read only device 457 if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) 458 break; 459 460 // Do the write 461 down(&mtdblk->cache_sem); 462 err = do_cached_write (mtdblk, req->sector << 9, 463 req->current_nr_sectors << 9, 464 req->buffer); 465 up(&mtdblk->cache_sem); 466 if (!err) 467 res = 1; 468 break; 469 } 470 471end_req: 472 spin_lock_irq(&io_request_lock); 473 end_request(res); 474 } 475} 476 477static volatile int leaving = 0; 478static DECLARE_MUTEX_LOCKED(thread_sem); 479static DECLARE_WAIT_QUEUE_HEAD(thr_wq); 480 481int mtdblock_thread(void *dummy) 482{ 483 struct task_struct *tsk = current; 484 DECLARE_WAITQUEUE(wait, tsk); 485 486 tsk->session = 1; 487 tsk->pgrp = 1; 488 /* we might get involved when memory gets low, so use PF_MEMALLOC */ 489 tsk->flags |= PF_MEMALLOC; 490 strcpy(tsk->comm, "mtdblockd"); 491 tsk->tty = NULL; 492 spin_lock_irq(&tsk->sigmask_lock); 493 sigfillset(&tsk->blocked); 494 recalc_sigpending(tsk); 495 spin_unlock_irq(&tsk->sigmask_lock); 496 exit_mm(tsk); 497 exit_files(tsk); 498 exit_sighand(tsk); 499 exit_fs(tsk); 500 501 while (!leaving) { 502 add_wait_queue(&thr_wq, &wait); 503 set_current_state(TASK_INTERRUPTIBLE); 504 spin_lock_irq(&io_request_lock); 505 if (QUEUE_EMPTY || QUEUE_PLUGGED) { 506 spin_unlock_irq(&io_request_lock); 507 schedule(); 508 remove_wait_queue(&thr_wq, &wait); 509 } else { 510 remove_wait_queue(&thr_wq, &wait); 511 set_current_state(TASK_RUNNING); 512 handle_mtdblock_request(); 513 spin_unlock_irq(&io_request_lock); 514 } 515 } 516 517 up(&thread_sem); 518 return 0; 519} 520 521#if LINUX_VERSION_CODE < 0x20300 522#define RQFUNC_ARG void 523#else 524#define RQFUNC_ARG request_queue_t *q 525#endif 526 527static void mtdblock_request(RQFUNC_ARG) 528{ 529 /* Don't do anything, except wake the thread if necessary */ 530 wake_up(&thr_wq); 531} 532 533 534static int mtdblock_ioctl(struct inode * inode, struct file * file, 535 unsigned int cmd, unsigned long arg) 536{ 537 struct mtdblk_dev *mtdblk; 538 539 mtdblk = mtdblks[MINOR(inode->i_rdev)]; 540 541#ifdef PARANOIA 542 if (!mtdblk) 543 BUG(); 544#endif 545 546 switch (cmd) { 547 case BLKGETSIZE: /* Return device size */ 548 return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg); 549 550#ifdef BLKGETSIZE64 551 case BLKGETSIZE64: 552 return put_user((u64)mtdblk->mtd->size, (u64 *)arg); 553#endif 554 555 case BLKFLSBUF: 556#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) 557 if(!capable(CAP_SYS_ADMIN)) 558 return -EACCES; 559#endif 560 fsync_dev(inode->i_rdev); 561 invalidate_buffers(inode->i_rdev); 562 down(&mtdblk->cache_sem); 563 write_cached_data(mtdblk); 564 up(&mtdblk->cache_sem); 565 if (mtdblk->mtd->sync) 566 mtdblk->mtd->sync(mtdblk->mtd); 567 return 0; 568 569 default: 570 return -EINVAL; 571 } 572} 573 574#if LINUX_VERSION_CODE < 0x20326 575static struct file_operations mtd_fops = 576{ 577 open: mtdblock_open, 578 ioctl: mtdblock_ioctl, 579 release: mtdblock_release, 580 read: block_read, 581 write: block_write 582}; 583#else 584static struct block_device_operations mtd_fops = 585{ 586#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) 587 owner: THIS_MODULE, 588#endif 589 open: mtdblock_open, 590 release: mtdblock_release, 591 ioctl: mtdblock_ioctl 592}; 593#endif 594 595#ifdef CONFIG_DEVFS_FS 596/* Notification that a new device has been added. Create the devfs entry for 597 * it. */ 598 599static void mtd_notify_add(struct mtd_info* mtd) 600{ 601 char name[8]; 602 603 if (!mtd || mtd->type == MTD_ABSENT) 604 return; 605 606 sprintf(name, "%d", mtd->index); 607 devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, 608 DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index, 609 S_IFBLK | S_IRUGO | S_IWUGO, 610 &mtd_fops, NULL); 611} 612 613static void mtd_notify_remove(struct mtd_info* mtd) 614{ 615 if (!mtd || mtd->type == MTD_ABSENT) 616 return; 617 618 devfs_unregister(devfs_rw_handle[mtd->index]); 619} 620#endif 621 622int __init init_mtdblock(void) 623{ 624 int i; 625 626 spin_lock_init(&mtdblks_lock); 627#ifdef CONFIG_DEVFS_FS 628 if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops)) 629 { 630 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", 631 MTD_BLOCK_MAJOR); 632 return -EAGAIN; 633 } 634 635 devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); 636 register_mtd_user(¬ifier); 637#else 638 if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { 639 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", 640 MTD_BLOCK_MAJOR); 641 return -EAGAIN; 642 } 643#endif 644 645 /* We fill it in at open() time. */ 646 for (i=0; i< MAX_MTD_DEVICES; i++) { 647 mtd_sizes[i] = 0; 648 mtd_blksizes[i] = BLOCK_SIZE; 649 } 650 init_waitqueue_head(&thr_wq); 651 /* Allow the block size to default to BLOCK_SIZE. */ 652 blksize_size[MAJOR_NR] = mtd_blksizes; 653 blk_size[MAJOR_NR] = mtd_sizes; 654 655 blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); 656 kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); 657 return 0; 658} 659 660static void __exit cleanup_mtdblock(void) 661{ 662 leaving = 1; 663 wake_up(&thr_wq); 664 down(&thread_sem); 665#ifdef CONFIG_DEVFS_FS 666 unregister_mtd_user(¬ifier); 667 devfs_unregister(devfs_dir_handle); 668 devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME); 669#else 670 unregister_blkdev(MAJOR_NR,DEVICE_NAME); 671#endif 672 blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); 673 blksize_size[MAJOR_NR] = NULL; 674 blk_size[MAJOR_NR] = NULL; 675} 676 677module_init(init_mtdblock); 678module_exit(cleanup_mtdblock); 679 680 681MODULE_LICENSE("GPL"); 682MODULE_AUTHOR("Nicolas Pitre <nico@cam.org> et al."); 683MODULE_DESCRIPTION("Caching read/erase/writeback block device emulation access to MTD devices"); 684