mtdblock.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:16k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

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