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

Linux/Unix编程

开发平台:

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.2 2002/03/22 08:45:22 dwmw2 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. instr->state = MTD_ERASE_DONE;
  261. if (instr->callback)
  262. instr->callback(instr);
  263. kfree(erase);
  264. return err;
  265. }
  266. static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
  267. {
  268. struct mtd_concat *concat = CONCAT(mtd);
  269. int i, err = -EINVAL;
  270. if ((len + ofs) > mtd->size) 
  271. return -EINVAL;
  272. for(i = 0; i < concat->num_subdev; i++)
  273. {
  274. struct mtd_info *subdev = concat->subdev[i];
  275. size_t size;
  276. if (ofs >= subdev->size)
  277. {
  278. size  = 0;
  279. ofs -= subdev->size;
  280. }
  281. else
  282. {
  283. if (ofs + len > subdev->size)
  284. size = subdev->size - ofs;
  285. else
  286. size = len;
  287. err = subdev->lock(subdev, ofs, size);
  288. if(err)
  289. break;
  290. len -= size;
  291. if(len == 0)
  292. break;
  293. err = -EINVAL;
  294. ofs = 0;
  295. }
  296. }
  297. return err;
  298. }
  299. static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
  300. {
  301. struct mtd_concat *concat = CONCAT(mtd);
  302. int i, err = 0;
  303. if ((len + ofs) > mtd->size) 
  304. return -EINVAL;
  305. for(i = 0; i < concat->num_subdev; i++)
  306. {
  307. struct mtd_info *subdev = concat->subdev[i];
  308. size_t size;
  309. if (ofs >= subdev->size)
  310. {
  311. size  = 0;
  312. ofs -= subdev->size;
  313. }
  314. else
  315. {
  316. if (ofs + len > subdev->size)
  317. size = subdev->size - ofs;
  318. else
  319. size = len;
  320. err = subdev->unlock(subdev, ofs, size);
  321. if(err)
  322. break;
  323. len -= size;
  324. if(len == 0)
  325. break;
  326. err = -EINVAL;
  327. ofs = 0;
  328. }
  329. }
  330. return err;
  331. }
  332. static void concat_sync(struct mtd_info *mtd)
  333. {
  334. struct mtd_concat *concat = CONCAT(mtd);
  335. int i;
  336. for(i = 0; i < concat->num_subdev; i++)
  337. {
  338. struct mtd_info *subdev = concat->subdev[i];
  339. subdev->sync(subdev);
  340. }
  341. }
  342. static int concat_suspend(struct mtd_info *mtd)
  343. {
  344. struct mtd_concat *concat = CONCAT(mtd);
  345. int i, rc = 0;
  346. for(i = 0; i < concat->num_subdev; i++)
  347. {
  348. struct mtd_info *subdev = concat->subdev[i];
  349. if((rc = subdev->suspend(subdev)) < 0)
  350. return rc;
  351. }
  352. return rc;
  353. }
  354. static void concat_resume(struct mtd_info *mtd)
  355. {
  356. struct mtd_concat *concat = CONCAT(mtd);
  357. int i;
  358. for(i = 0; i < concat->num_subdev; i++)
  359. {
  360. struct mtd_info *subdev = concat->subdev[i];
  361. subdev->resume(subdev);
  362. }
  363. }
  364. /*
  365.  * This function constructs a virtual MTD device by concatenating
  366.  * num_devs MTD devices. A pointer to the new device object is
  367.  * stored to *new_dev upon success. This function does _not_
  368.  * register any devices: this is the caller's responsibility.
  369.  */
  370. struct mtd_info *mtd_concat_create(
  371. struct mtd_info *subdev[], /* subdevices to concatenate */
  372. int num_devs, /* number of subdevices      */
  373. char *name) /* name for the new device   */
  374. {
  375. int i;
  376. size_t size;
  377. struct mtd_concat *concat;
  378. u_int32_t max_erasesize, curr_erasesize;
  379. int num_erase_region;
  380. printk(KERN_NOTICE "Concatenating MTD devices:n");
  381. for(i = 0; i < num_devs; i++)
  382. printk(KERN_NOTICE "(%d): "%s"n", i, subdev[i]->name);
  383. printk(KERN_NOTICE "into device "%s"n", name);
  384. /* allocate the device structure */
  385. size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
  386. concat = kmalloc (size, GFP_KERNEL);
  387. if(!concat)
  388. {
  389. printk ("memory allocation error while creating concatenated device "%s"n",
  390. name);
  391. return NULL;
  392. }
  393. memset(concat, 0, size);
  394. concat->subdev = (struct mtd_info **)(concat + 1);
  395. /*
  396.  * Set up the new "super" device's MTD object structure, check for
  397.  * incompatibilites between the subdevices.
  398.  */
  399. concat->mtd.type      = subdev[0]->type;
  400. concat->mtd.flags     = subdev[0]->flags;
  401. concat->mtd.size      = subdev[0]->size;
  402. concat->mtd.erasesize = subdev[0]->erasesize;
  403. concat->mtd.oobblock  = subdev[0]->oobblock;
  404. concat->mtd.oobsize   = subdev[0]->oobsize;
  405. concat->mtd.ecctype   = subdev[0]->ecctype;
  406. concat->mtd.eccsize   = subdev[0]->eccsize;
  407. concat->subdev[0]   = subdev[0];
  408. for(i = 1; i < num_devs; i++)
  409. {
  410. if(concat->mtd.type != subdev[i]->type)
  411. {
  412. kfree(concat);
  413. printk ("Incompatible device type on "%s"n", subdev[i]->name);
  414. return NULL;
  415. }
  416. if(concat->mtd.flags != subdev[i]->flags)
  417. { /*
  418.  * Expect all flags except MTD_WRITEABLE to be equal on
  419.  * all subdevices.
  420.  */
  421. if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE)
  422. {
  423. kfree(concat);
  424. printk ("Incompatible device flags on "%s"n", subdev[i]->name);
  425. return NULL;
  426. }
  427. else /* if writeable attribute differs, make super device writeable */
  428. concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE;
  429. }
  430. concat->mtd.size += subdev[i]->size;
  431. if(concat->mtd.oobblock != subdev[i]->oobblock ||
  432.    concat->mtd.oobsize  != subdev[i]->oobsize  ||
  433.    concat->mtd.ecctype  != subdev[i]->ecctype  ||
  434.    concat->mtd.eccsize  != subdev[i]->eccsize)
  435. {
  436. kfree(concat);
  437. printk ("Incompatible OOB or ECC data on "%s"n", subdev[i]->name);
  438. return NULL;
  439. }
  440. concat->subdev[i] = subdev[i];
  441. }
  442. concat->num_subdev  = num_devs;
  443. concat->mtd.name    = name;
  444. /*
  445.  * NOTE: for now, we do not provide any readv()/writev() methods
  446.  *       because they are messy to implement and they are not
  447.  *       used to a great extent anyway.
  448.  */
  449. concat->mtd.erase   = concat_erase;
  450. concat->mtd.read    = concat_read;
  451. concat->mtd.write   = concat_write;
  452. concat->mtd.sync    = concat_sync;
  453. concat->mtd.lock    = concat_lock;
  454. concat->mtd.unlock  = concat_unlock;
  455. concat->mtd.suspend = concat_suspend;
  456. concat->mtd.resume  = concat_resume;
  457. /*
  458.  * Combine the erase block size info of the subdevices:
  459.  *
  460.  * first, walk the map of the new device and see how
  461.  * many changes in erase size we have
  462.  */
  463. max_erasesize = curr_erasesize = subdev[0]->erasesize;
  464. num_erase_region = 1;
  465. for(i = 0; i < num_devs; i++)
  466. {
  467. if(subdev[i]->numeraseregions == 0)
  468. { /* current subdevice has uniform erase size */
  469. if(subdev[i]->erasesize != curr_erasesize)
  470. { /* if it differs from the last subdevice's erase size, count it */
  471. ++num_erase_region;
  472. curr_erasesize = subdev[i]->erasesize;
  473. if(curr_erasesize > max_erasesize)
  474. max_erasesize = curr_erasesize;
  475. }
  476. }
  477. else
  478. { /* current subdevice has variable erase size */
  479. int j;
  480. for(j = 0; j < subdev[i]->numeraseregions; j++)
  481. { /* walk the list of erase regions, count any changes */
  482. if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
  483. {
  484. ++num_erase_region;
  485. curr_erasesize = subdev[i]->eraseregions[j].erasesize;
  486. if(curr_erasesize > max_erasesize)
  487. max_erasesize = curr_erasesize;
  488. }
  489. }
  490. }
  491. }
  492. if(num_erase_region == 1)
  493. { /*
  494.  * All subdevices have the same uniform erase size.
  495.  * This is easy:
  496.  */
  497. concat->mtd.erasesize = curr_erasesize;
  498. concat->mtd.numeraseregions = 0;
  499. }
  500. else
  501. { /*
  502.  * erase block size varies across the subdevices: allocate
  503.  * space to store the data describing the variable erase regions
  504.  */
  505. struct mtd_erase_region_info *erase_region_p;
  506. u_int32_t begin, position;
  507. concat->mtd.erasesize = max_erasesize;
  508. concat->mtd.numeraseregions = num_erase_region;
  509. concat->mtd.eraseregions = erase_region_p = kmalloc (
  510.      num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
  511. if(!erase_region_p)
  512. {
  513. kfree(concat);
  514. printk ("memory allocation error while creating erase region list"
  515.         " for device "%s"n", name);
  516. return NULL;
  517. }
  518. /*
  519.  * walk the map of the new device once more and fill in
  520.  * in erase region info:
  521.  */
  522. curr_erasesize = subdev[0]->erasesize;
  523. begin = position = 0;
  524. for(i = 0; i < num_devs; i++)
  525. {
  526. if(subdev[i]->numeraseregions == 0)
  527. { /* current subdevice has uniform erase size */
  528. if(subdev[i]->erasesize != curr_erasesize)
  529. { /*
  530.  *  fill in an mtd_erase_region_info structure for the area
  531.  *  we have walked so far:
  532.  */
  533. erase_region_p->offset    = begin;
  534. erase_region_p->erasesize = curr_erasesize;
  535. erase_region_p->numblocks = (position - begin) / curr_erasesize;
  536. begin = position;
  537. curr_erasesize = subdev[i]->erasesize;
  538. ++erase_region_p;
  539. }
  540. position += subdev[i]->size;
  541. }
  542. else
  543. { /* current subdevice has variable erase size */
  544. int j;
  545. for(j = 0; j < subdev[i]->numeraseregions; j++)
  546. { /* walk the list of erase regions, count any changes */
  547. if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
  548. {
  549. erase_region_p->offset    = begin;
  550. erase_region_p->erasesize = curr_erasesize;
  551. erase_region_p->numblocks = (position - begin) / curr_erasesize;
  552. begin = position;
  553. curr_erasesize = subdev[i]->eraseregions[j].erasesize;
  554. ++erase_region_p;
  555. }
  556. position += subdev[i]->eraseregions[j].numblocks * curr_erasesize;
  557. }
  558. }
  559. }
  560. /* Now write the final entry */
  561. erase_region_p->offset    = begin;
  562. erase_region_p->erasesize = curr_erasesize;
  563. erase_region_p->numblocks = (position - begin) / curr_erasesize;
  564. }
  565. return &concat->mtd;
  566. }
  567. /* 
  568.  * This function destroys an MTD object obtained from concat_mtd_devs()
  569.  */
  570. void mtd_concat_destroy(struct mtd_info *mtd)
  571. {
  572. struct mtd_concat *concat = CONCAT(mtd);
  573. if(concat->mtd.numeraseregions)
  574. kfree(concat->mtd.eraseregions);
  575. kfree(concat);
  576. }
  577. EXPORT_SYMBOL(mtd_concat_create);
  578. EXPORT_SYMBOL(mtd_concat_destroy);
  579. MODULE_LICENSE("GPL");
  580. MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
  581. MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");