mtdconcat.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:16k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * MTD device concatenation layer
  3.  *
  4.  * (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
  5.  *
  6.  * This code is GPL
  7.  *
  8.  * $Id: mtdconcat.c,v 1.1 2002/03/08 16:34:35 rkaiser Exp $
  9.  */
  10. #include <linux/module.h>
  11. #include <linux/types.h>
  12. #include <linux/kernel.h>
  13. #include <linux/slab.h>
  14. #include <linux/mtd/mtd.h>
  15. #include <linux/mtd/concat.h>
  16. /*
  17.  * Our storage structure:
  18.  * Subdev points to an array of pointers to struct mtd_info objects
  19.  * which is allocated along with this structure
  20.  *
  21.  */
  22. struct mtd_concat {
  23. struct mtd_info mtd;
  24. int             num_subdev;
  25. struct mtd_info **subdev;
  26. };
  27. /*
  28.  * how to calculate the size required for the above structure,
  29.  * including the pointer array subdev points to:
  30.  */
  31. #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev)
  32. ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
  33. /*
  34.  * Given a pointer to the MTD object in the mtd_concat structure,
  35.  * we can retrieve the pointer to that structure with this macro.
  36.  */
  37. #define CONCAT(x)  ((struct mtd_concat *)(x))
  38. /* 
  39.  * MTD methods which look up the relevant subdevice, translate the
  40.  * effective address and pass through to the subdevice.
  41.  */
  42. static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, 
  43. size_t *retlen, u_char *buf)
  44. {
  45. struct mtd_concat *concat = CONCAT(mtd);
  46. int err = -EINVAL;
  47. int i;
  48. *retlen = 0;
  49. for(i = 0; i < concat->num_subdev; i++)
  50. {
  51. struct mtd_info *subdev = concat->subdev[i];
  52. size_t size, retsize;
  53. if (from >= subdev->size)
  54. {
  55. size  = 0;
  56. from -= subdev->size;
  57. }
  58. else
  59. {
  60. if (from + len > subdev->size)
  61. size = subdev->size - from;
  62. else
  63. size = len;
  64. err = subdev->read(subdev, from, size, &retsize, buf);
  65. if(err)
  66. break;
  67. *retlen += retsize;
  68. len -= size;
  69. if(len == 0)
  70. break;
  71. err = -EINVAL;
  72. buf += size;
  73. from = 0;
  74. }
  75. }
  76. return err;
  77. }
  78. static int concat_write (struct mtd_info *mtd, loff_t to, size_t len,
  79. size_t *retlen, const u_char *buf)
  80. {
  81. struct mtd_concat *concat = CONCAT(mtd);
  82. int err = -EINVAL;
  83. int i;
  84. if (!(mtd->flags & MTD_WRITEABLE))
  85. return -EROFS;
  86. *retlen = 0;
  87. for(i = 0; i < concat->num_subdev; i++)
  88. {
  89. struct mtd_info *subdev = concat->subdev[i];
  90. size_t size, retsize;
  91. if (to >= subdev->size)
  92. {
  93. size  = 0;
  94. to -= subdev->size;
  95. }
  96. else
  97. {
  98. if (to + len > subdev->size)
  99. size = subdev->size - to;
  100. else
  101. size = len;
  102. if (!(subdev->flags & MTD_WRITEABLE))
  103. err = -EROFS;
  104. else
  105. err = subdev->write(subdev, to, size, &retsize, buf);
  106. if(err)
  107. break;
  108. *retlen += retsize;
  109. len -= size;
  110. if(len == 0)
  111. break;
  112. err = -EINVAL;
  113. buf += size;
  114. to = 0;
  115. }
  116. }
  117. return err;
  118. }
  119. static void concat_erase_callback (struct erase_info *instr)
  120. {
  121. wake_up((wait_queue_head_t *)instr->priv);
  122. }
  123. static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
  124. {
  125. int err;
  126. wait_queue_head_t waitq;
  127. DECLARE_WAITQUEUE(wait, current);
  128. /*
  129.  * This code was stol^H^H^H^Hinspired by mtdchar.c
  130.  */
  131. init_waitqueue_head(&waitq);
  132. erase->mtd = mtd;
  133. erase->callback = concat_erase_callback;
  134. erase->priv = (unsigned long)&waitq;
  135. /*
  136.  * FIXME: Allow INTERRUPTIBLE. Which means
  137.  * not having the wait_queue head on the stack.
  138.  */
  139. err = mtd->erase(mtd, erase);
  140. if (!err)
  141. {
  142. set_current_state(TASK_UNINTERRUPTIBLE);
  143. add_wait_queue(&waitq, &wait);
  144. if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED)
  145. schedule();
  146. remove_wait_queue(&waitq, &wait);
  147. set_current_state(TASK_RUNNING);
  148. err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0;
  149. }
  150. return err;
  151. }
  152. static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
  153. {
  154. struct mtd_concat *concat = CONCAT(mtd);
  155. struct mtd_info *subdev;
  156. int i, err;
  157. u_int32_t length;
  158. struct erase_info *erase;
  159. if (!(mtd->flags & MTD_WRITEABLE))
  160. return -EROFS;
  161. if(instr->addr > concat->mtd.size)
  162. return -EINVAL;
  163. if(instr->len + instr->addr > concat->mtd.size)
  164. return -EINVAL;
  165. /*
  166.  * Check for proper erase block alignment of the to-be-erased area.
  167.  * It is easier to do this based on the super device's erase
  168.  * region info rather than looking at each particular sub-device
  169.  * in turn.
  170.  */
  171. if (!concat->mtd.numeraseregions)
  172. { /* the easy case: device has uniform erase block size */
  173. if(instr->addr & (concat->mtd.erasesize - 1))
  174. return -EINVAL;
  175. if(instr->len & (concat->mtd.erasesize - 1))
  176. return -EINVAL;
  177. }
  178. else
  179. { /* device has variable erase size */
  180. struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions;
  181. /*
  182.  * Find the erase region where the to-be-erased area begins:
  183.  */
  184. for(i = 0; i < concat->mtd.numeraseregions && 
  185.            instr->addr >= erase_regions[i].offset; i++)
  186. ;
  187. --i;
  188. /*
  189.  * Now erase_regions[i] is the region in which the
  190.  * to-be-erased area begins. Verify that the starting
  191.  * offset is aligned to this region's erase size:
  192.  */
  193. if (instr->addr & (erase_regions[i].erasesize-1))
  194. return -EINVAL;
  195. /*
  196.  * now find the erase region where the to-be-erased area ends:
  197.  */
  198. for(; i < concat->mtd.numeraseregions && 
  199.       (instr->addr + instr->len) >=  erase_regions[i].offset ; ++i)
  200. ;
  201. --i;
  202. /*
  203.  * check if the ending offset is aligned to this region's erase size
  204.  */
  205. if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1))
  206. return -EINVAL;
  207. }
  208. /* make a local copy of instr to avoid modifying the caller's struct */
  209. erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL);
  210. if (!erase)
  211. return -ENOMEM;
  212. *erase = *instr;
  213. length = instr->len;
  214. /*
  215.  * find the subdevice where the to-be-erased area begins, adjust
  216.  * starting offset to be relative to the subdevice start
  217.  */
  218. for(i = 0; i < concat->num_subdev; i++)
  219. {
  220. subdev = concat->subdev[i];
  221. if(subdev->size <= erase->addr)
  222. erase->addr -= subdev->size;
  223. else
  224. break;
  225.     }
  226. if(i >= concat->num_subdev) /* must never happen since size */
  227. BUG(); /* limit has been verified above */
  228. /* now do the erase: */
  229. err = 0;
  230. for(;length > 0; i++) /* loop for all subevices affected by this request */
  231. {
  232. subdev = concat->subdev[i]; /* get current subdevice */
  233. /* limit length to subdevice's size: */
  234. if(erase->addr + length > subdev->size)
  235. erase->len = subdev->size - erase->addr;
  236. else
  237. erase->len = length;
  238. if (!(subdev->flags & MTD_WRITEABLE))
  239. {
  240. err = -EROFS;
  241. break;
  242. }
  243. length -= erase->len;
  244. if ((err = concat_dev_erase(subdev, erase)))
  245. {
  246. if(err == -EINVAL) /* sanity check: must never happen since */
  247. BUG(); /* block alignment has been checked above */
  248. break;
  249. }
  250. /*
  251.  * erase->addr specifies the offset of the area to be
  252.  * erased *within the current subdevice*. It can be
  253.  * non-zero only the first time through this loop, i.e.
  254.  * for the first subdevice where blocks need to be erased.
  255.  * All the following erases must begin at the start of the
  256.  * current subdevice, i.e. at offset zero.
  257.  */
  258. erase->addr = 0;
  259. }
  260. kfree(erase);
  261. if (err)
  262. return err;
  263. instr->state = MTD_ERASE_DONE;
  264. if (instr->callback)
  265. instr->callback(instr);
  266. return 0;
  267. }
  268. static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
  269. {
  270. struct mtd_concat *concat = CONCAT(mtd);
  271. int i, err = -EINVAL;
  272. if ((len + ofs) > mtd->size) 
  273. return -EINVAL;
  274. for(i = 0; i < concat->num_subdev; i++)
  275. {
  276. struct mtd_info *subdev = concat->subdev[i];
  277. size_t size;
  278. if (ofs >= subdev->size)
  279. {
  280. size  = 0;
  281. ofs -= subdev->size;
  282. }
  283. else
  284. {
  285. if (ofs + len > subdev->size)
  286. size = subdev->size - ofs;
  287. else
  288. size = len;
  289. err = subdev->lock(subdev, ofs, size);
  290. if(err)
  291. break;
  292. len -= size;
  293. if(len == 0)
  294. break;
  295. err = -EINVAL;
  296. ofs = 0;
  297. }
  298. }
  299. return err;
  300. }
  301. static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
  302. {
  303. struct mtd_concat *concat = CONCAT(mtd);
  304. int i, err = 0;
  305. if ((len + ofs) > mtd->size) 
  306. return -EINVAL;
  307. for(i = 0; i < concat->num_subdev; i++)
  308. {
  309. struct mtd_info *subdev = concat->subdev[i];
  310. size_t size;
  311. if (ofs >= subdev->size)
  312. {
  313. size  = 0;
  314. ofs -= subdev->size;
  315. }
  316. else
  317. {
  318. if (ofs + len > subdev->size)
  319. size = subdev->size - ofs;
  320. else
  321. size = len;
  322. err = subdev->unlock(subdev, ofs, size);
  323. if(err)
  324. break;
  325. len -= size;
  326. if(len == 0)
  327. break;
  328. err = -EINVAL;
  329. ofs = 0;
  330. }
  331. }
  332. return err;
  333. }
  334. static void concat_sync(struct mtd_info *mtd)
  335. {
  336. struct mtd_concat *concat = CONCAT(mtd);
  337. int i;
  338. for(i = 0; i < concat->num_subdev; i++)
  339. {
  340. struct mtd_info *subdev = concat->subdev[i];
  341. subdev->sync(subdev);
  342. }
  343. }
  344. static int concat_suspend(struct mtd_info *mtd)
  345. {
  346. struct mtd_concat *concat = CONCAT(mtd);
  347. int i, rc = 0;
  348. for(i = 0; i < concat->num_subdev; i++)
  349. {
  350. struct mtd_info *subdev = concat->subdev[i];
  351. if((rc = subdev->suspend(subdev)) < 0)
  352. return rc;
  353. }
  354. return rc;
  355. }
  356. static void concat_resume(struct mtd_info *mtd)
  357. {
  358. struct mtd_concat *concat = CONCAT(mtd);
  359. int i;
  360. for(i = 0; i < concat->num_subdev; i++)
  361. {
  362. struct mtd_info *subdev = concat->subdev[i];
  363. subdev->resume(subdev);
  364. }
  365. }
  366. /*
  367.  * This function constructs a virtual MTD device by concatenating
  368.  * num_devs MTD devices. A pointer to the new device object is
  369.  * stored to *new_dev upon success. This function does _not_
  370.  * register any devices: this is the caller's responsibility.
  371.  */
  372. struct mtd_info *mtd_concat_create(
  373. struct mtd_info *subdev[], /* subdevices to concatenate */
  374. int num_devs, /* number of subdevices      */
  375. char *name) /* name for the new device   */
  376. {
  377. int i;
  378. size_t size;
  379. struct mtd_concat *concat;
  380. u_int32_t max_erasesize, curr_erasesize;
  381. int num_erase_region;
  382. printk(KERN_NOTICE "Concatenating MTD devices:n");
  383. for(i = 0; i < num_devs; i++)
  384. printk(KERN_NOTICE "(%d): "%s"n", i, subdev[i]->name);
  385. printk(KERN_NOTICE "into device "%s"n", name);
  386. /* allocate the device structure */
  387. size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
  388. concat = kmalloc (size, GFP_KERNEL);
  389. if(!concat)
  390. {
  391. printk ("memory allocation error while creating concatenated device "%s"n",
  392. name);
  393. return NULL;
  394. }
  395. memset(concat, 0, size);
  396. concat->subdev = (struct mtd_info **)(concat + 1);
  397. /*
  398.  * Set up the new "super" device's MTD object structure, check for
  399.  * incompatibilites between the subdevices.
  400.  */
  401. concat->mtd.type      = subdev[0]->type;
  402. concat->mtd.flags     = subdev[0]->flags;
  403. concat->mtd.size      = subdev[0]->size;
  404. concat->mtd.erasesize = subdev[0]->erasesize;
  405. concat->mtd.oobblock  = subdev[0]->oobblock;
  406. concat->mtd.oobsize   = subdev[0]->oobsize;
  407. concat->mtd.ecctype   = subdev[0]->ecctype;
  408. concat->mtd.eccsize   = subdev[0]->eccsize;
  409. concat->subdev[0]   = subdev[0];
  410. for(i = 1; i < num_devs; i++)
  411. {
  412. if(concat->mtd.type != subdev[i]->type)
  413. {
  414. kfree(concat);
  415. printk ("Incompatible device type on "%s"n", subdev[i]->name);
  416. return NULL;
  417. }
  418. if(concat->mtd.flags != subdev[i]->flags)
  419. { /*
  420.  * Expect all flags except MTD_WRITEABLE to be equal on
  421.  * all subdevices.
  422.  */
  423. if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE)
  424. {
  425. kfree(concat);
  426. printk ("Incompatible device flags on "%s"n", subdev[i]->name);
  427. return NULL;
  428. }
  429. else /* if writeable attribute differs, make super device writeable */
  430. concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE;
  431. }
  432. concat->mtd.size += subdev[i]->size;
  433. if(concat->mtd.oobblock != subdev[i]->oobblock ||
  434.    concat->mtd.oobsize  != subdev[i]->oobsize  ||
  435.    concat->mtd.ecctype  != subdev[i]->ecctype  ||
  436.    concat->mtd.eccsize  != subdev[i]->eccsize)
  437. {
  438. kfree(concat);
  439. printk ("Incompatible OOB or ECC data on "%s"n", subdev[i]->name);
  440. return NULL;
  441. }
  442. concat->subdev[i] = subdev[i];
  443. }
  444. concat->num_subdev  = num_devs;
  445. concat->mtd.name    = name;
  446. /*
  447.  * NOTE: for now, we do not provide any readv()/writev() methods
  448.  *       because they are messy to implement and they are not
  449.  *       used to a great extent anyway.
  450.  */
  451. concat->mtd.erase   = concat_erase;
  452. concat->mtd.read    = concat_read;
  453. concat->mtd.write   = concat_write;
  454. concat->mtd.sync    = concat_sync;
  455. concat->mtd.lock    = concat_lock;
  456. concat->mtd.unlock  = concat_unlock;
  457. concat->mtd.suspend = concat_suspend;
  458. concat->mtd.resume  = concat_resume;
  459. /*
  460.  * Combine the erase block size info of the subdevices:
  461.  *
  462.  * first, walk the map of the new device and see how
  463.  * many changes in erase size we have
  464.  */
  465. max_erasesize = curr_erasesize = subdev[0]->erasesize;
  466. num_erase_region = 1;
  467. for(i = 0; i < num_devs; i++)
  468. {
  469. if(subdev[i]->numeraseregions == 0)
  470. { /* current subdevice has uniform erase size */
  471. if(subdev[i]->erasesize != curr_erasesize)
  472. { /* if it differs from the last subdevice's erase size, count it */
  473. ++num_erase_region;
  474. curr_erasesize = subdev[i]->erasesize;
  475. if(curr_erasesize > max_erasesize)
  476. max_erasesize = curr_erasesize;
  477. }
  478. }
  479. else
  480. { /* current subdevice has variable erase size */
  481. int j;
  482. for(j = 0; j < subdev[i]->numeraseregions; j++)
  483. { /* walk the list of erase regions, count any changes */
  484. if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
  485. {
  486. ++num_erase_region;
  487. curr_erasesize = subdev[i]->eraseregions[j].erasesize;
  488. if(curr_erasesize > max_erasesize)
  489. max_erasesize = curr_erasesize;
  490. }
  491. }
  492. }
  493. }
  494. if(num_erase_region == 1)
  495. { /*
  496.  * All subdevices have the same uniform erase size.
  497.  * This is easy:
  498.  */
  499. concat->mtd.erasesize = curr_erasesize;
  500. concat->mtd.numeraseregions = 0;
  501. }
  502. else
  503. { /*
  504.  * erase block size varies across the subdevices: allocate
  505.  * space to store the data describing the variable erase regions
  506.  */
  507. struct mtd_erase_region_info *erase_region_p;
  508. u_int32_t begin, position;
  509. concat->mtd.erasesize = max_erasesize;
  510. concat->mtd.numeraseregions = num_erase_region;
  511. concat->mtd.eraseregions = erase_region_p = kmalloc (
  512.      num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
  513. if(!erase_region_p)
  514. {
  515. kfree(concat);
  516. printk ("memory allocation error while creating erase region list"
  517.         " for device "%s"n", name);
  518. return NULL;
  519. }
  520. /*
  521.  * walk the map of the new device once more and fill in
  522.  * in erase region info:
  523.  */
  524. curr_erasesize = subdev[0]->erasesize;
  525. begin = position = 0;
  526. for(i = 0; i < num_devs; i++)
  527. {
  528. if(subdev[i]->numeraseregions == 0)
  529. { /* current subdevice has uniform erase size */
  530. if(subdev[i]->erasesize != curr_erasesize)
  531. { /*
  532.  *  fill in an mtd_erase_region_info structure for the area
  533.  *  we have walked so far:
  534.  */
  535. erase_region_p->offset    = begin;
  536. erase_region_p->erasesize = curr_erasesize;
  537. erase_region_p->numblocks = (position - begin) / curr_erasesize;
  538. begin = position;
  539. curr_erasesize = subdev[i]->erasesize;
  540. ++erase_region_p;
  541. }
  542. position += subdev[i]->size;
  543. }
  544. else
  545. { /* current subdevice has variable erase size */
  546. int j;
  547. for(j = 0; j < subdev[i]->numeraseregions; j++)
  548. { /* walk the list of erase regions, count any changes */
  549. if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
  550. {
  551. erase_region_p->offset    = begin;
  552. erase_region_p->erasesize = curr_erasesize;
  553. erase_region_p->numblocks = (position - begin) / curr_erasesize;
  554. begin = position;
  555. curr_erasesize = subdev[i]->eraseregions[j].erasesize;
  556. ++erase_region_p;
  557. }
  558. position += subdev[i]->eraseregions[j].numblocks * curr_erasesize;
  559. }
  560. }
  561. }
  562. /* Now write the final entry */
  563. erase_region_p->offset    = begin;
  564. erase_region_p->erasesize = curr_erasesize;
  565. erase_region_p->numblocks = (position - begin) / curr_erasesize;
  566. }
  567. return &concat->mtd;
  568. }
  569. /* 
  570.  * This function destroys an MTD object obtained from concat_mtd_devs()
  571.  */
  572. void mtd_concat_destroy(struct mtd_info *mtd)
  573. {
  574. struct mtd_concat *concat = CONCAT(mtd);
  575. if(concat->mtd.numeraseregions)
  576. kfree(concat->mtd.eraseregions);
  577. kfree(concat);
  578. }
  579. EXPORT_SYMBOL(mtd_concat_create);
  580. EXPORT_SYMBOL(mtd_concat_destroy);
  581. MODULE_LICENSE("GPL");
  582. MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
  583. MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");