1 /* 2 * MTD device concatenation layer 3 * 4 * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> 5 * 6 * NAND support by Christian Gan <cgan@iders.ca> 7 * 8 * This code is GPL 9 * 10 * $Id: mtdconcat.c,v 1.11 2005/11/07 11:14:20 gleixner Exp $ 11 */ 12 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 #include <linux/sched.h> 17 #include <linux/types.h> 18 19 #include <linux/mtd/mtd.h> 20 #include <linux/mtd/concat.h> 21 22 /* 23 * Our storage structure: 24 * Subdev points to an array of pointers to struct mtd_info objects 25 * which is allocated along with this structure 26 * 27 */ 28 struct mtd_concat { 29 struct mtd_info mtd; 30 int num_subdev; 31 struct mtd_info **subdev; 32 }; 33 34 /* 35 * how to calculate the size required for the above structure, 36 * including the pointer array subdev points to: 37 */ 38 #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ 39 ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) 40 41 /* 42 * Given a pointer to the MTD object in the mtd_concat structure, 43 * we can retrieve the pointer to that structure with this macro. 44 */ 45 #define CONCAT(x) ((struct mtd_concat *)(x)) 46 47 /* 48 * MTD methods which look up the relevant subdevice, translate the 49 * effective address and pass through to the subdevice. 50 */ 51 52 static int 53 concat_read(struct mtd_info *mtd, loff_t from, size_t len, 54 size_t * retlen, u_char * buf) 55 { 56 struct mtd_concat *concat = CONCAT(mtd); 57 int err = -EINVAL; 58 int i; 59 60 *retlen = 0; 61 62 for (i = 0; i < concat->num_subdev; i++) { 63 struct mtd_info *subdev = concat->subdev[i]; 64 size_t size, retsize; 65 66 if (from >= subdev->size) { 67 /* Not destined for this subdev */ 68 size = 0; 69 from -= subdev->size; 70 continue; 71 } 72 if (from + len > subdev->size) 73 /* First part goes into this subdev */ 74 size = subdev->size - from; 75 else 76 /* Entire transaction goes into this subdev */ 77 size = len; 78 79 err = subdev->read(subdev, from, size, &retsize, buf); 80 81 if (err) 82 break; 83 84 *retlen += retsize; 85 len -= size; 86 if (len == 0) 87 break; 88 89 err = -EINVAL; 90 buf += size; 91 from = 0; 92 } 93 return err; 94 } 95 96 static int 97 concat_write(struct mtd_info *mtd, loff_t to, size_t len, 98 size_t * retlen, const u_char * buf) 99 { 100 struct mtd_concat *concat = CONCAT(mtd); 101 int err = -EINVAL; 102 int i; 103 104 if (!(mtd->flags & MTD_WRITEABLE)) 105 return -EROFS; 106 107 *retlen = 0; 108 109 for (i = 0; i < concat->num_subdev; i++) { 110 struct mtd_info *subdev = concat->subdev[i]; 111 size_t size, retsize; 112 113 if (to >= subdev->size) { 114 size = 0; 115 to -= subdev->size; 116 continue; 117 } 118 if (to + len > subdev->size) 119 size = subdev->size - to; 120 else 121 size = len; 122 123 if (!(subdev->flags & MTD_WRITEABLE)) 124 err = -EROFS; 125 else 126 err = subdev->write(subdev, to, size, &retsize, buf); 127 128 if (err) 129 break; 130 131 *retlen += retsize; 132 len -= size; 133 if (len == 0) 134 break; 135 136 err = -EINVAL; 137 buf += size; 138 to = 0; 139 } 140 return err; 141 } 142 143 static int 144 concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, 145 size_t * retlen, u_char * buf, u_char * eccbuf, 146 struct nand_oobinfo *oobsel) 147 { 148 struct mtd_concat *concat = CONCAT(mtd); 149 int err = -EINVAL; 150 int i; 151 152 *retlen = 0; 153 154 for (i = 0; i < concat->num_subdev; i++) { 155 struct mtd_info *subdev = concat->subdev[i]; 156 size_t size, retsize; 157 158 if (from >= subdev->size) { 159 /* Not destined for this subdev */ 160 size = 0; 161 from -= subdev->size; 162 continue; 163 } 164 165 if (from + len > subdev->size) 166 /* First part goes into this subdev */ 167 size = subdev->size - from; 168 else 169 /* Entire transaction goes into this subdev */ 170 size = len; 171 172 if (subdev->read_ecc) 173 err = subdev->read_ecc(subdev, from, size, 174 &retsize, buf, eccbuf, oobsel); 175 else 176 err = -EINVAL; 177 178 if (err) 179 break; 180 181 *retlen += retsize; 182 len -= size; 183 if (len == 0) 184 break; 185 186 err = -EINVAL; 187 buf += size; 188 if (eccbuf) { 189 eccbuf += subdev->oobsize; 190 /* in nand.c at least, eccbufs are 191 tagged with 2 (int)eccstatus'; we 192 must account for these */ 193 eccbuf += 2 * (sizeof (int)); 194 } 195 from = 0; 196 } 197 return err; 198 } 199 200 static int 201 concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, 202 size_t * retlen, const u_char * buf, u_char * eccbuf, 203 struct nand_oobinfo *oobsel) 204 { 205 struct mtd_concat *concat = CONCAT(mtd); 206 int err = -EINVAL; 207 int i; 208 209 if (!(mtd->flags & MTD_WRITEABLE)) 210 return -EROFS; 211 212 *retlen = 0; 213 214 for (i = 0; i < concat->num_subdev; i++) { 215 struct mtd_info *subdev = concat->subdev[i]; 216 size_t size, retsize; 217 218 if (to >= subdev->size) { 219 size = 0; 220 to -= subdev->size; 221 continue; 222 } 223 if (to + len > subdev->size) 224 size = subdev->size - to; 225 else 226 size = len; 227 228 if (!(subdev->flags & MTD_WRITEABLE)) 229 err = -EROFS; 230 else if (subdev->write_ecc) 231 err = subdev->write_ecc(subdev, to, size, 232 &retsize, buf, eccbuf, oobsel); 233 else 234 err = -EINVAL; 235 236 if (err) 237 break; 238 239 *retlen += retsize; 240 len -= size; 241 if (len == 0) 242 break; 243 244 err = -EINVAL; 245 buf += size; 246 if (eccbuf) 247 eccbuf += subdev->oobsize; 248 to = 0; 249 } 250 return err; 251 } 252 253 static int 254 concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, 255 size_t * retlen, u_char * buf) 256 { 257 struct mtd_concat *concat = CONCAT(mtd); 258 int err = -EINVAL; 259 int i; 260 261 *retlen = 0; 262 263 for (i = 0; i < concat->num_subdev; i++) { 264 struct mtd_info *subdev = concat->subdev[i]; 265 size_t size, retsize; 266 267 if (from >= subdev->size) { 268 /* Not destined for this subdev */ 269 size = 0; 270 from -= subdev->size; 271 continue; 272 } 273 if (from + len > subdev->size) 274 /* First part goes into this subdev */ 275 size = subdev->size - from; 276 else 277 /* Entire transaction goes into this subdev */ 278 size = len; 279 280 if (subdev->read_oob) 281 err = subdev->read_oob(subdev, from, size, 282 &retsize, buf); 283 else 284 err = -EINVAL; 285 286 if (err) 287 break; 288 289 *retlen += retsize; 290 len -= size; 291 if (len == 0) 292 break; 293 294 err = -EINVAL; 295 buf += size; 296 from = 0; 297 } 298 return err; 299 } 300 301 static int 302 concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, 303 size_t * retlen, const u_char * buf) 304 { 305 struct mtd_concat *concat = CONCAT(mtd); 306 int err = -EINVAL; 307 int i; 308 309 if (!(mtd->flags & MTD_WRITEABLE)) 310 return -EROFS; 311 312 *retlen = 0; 313 314 for (i = 0; i < concat->num_subdev; i++) { 315 struct mtd_info *subdev = concat->subdev[i]; 316 size_t size, retsize; 317 318 if (to >= subdev->size) { 319 size = 0; 320 to -= subdev->size; 321 continue; 322 } 323 if (to + len > subdev->size) 324 size = subdev->size - to; 325 else 326 size = len; 327 328 if (!(subdev->flags & MTD_WRITEABLE)) 329 err = -EROFS; 330 else if (subdev->write_oob) 331 err = subdev->write_oob(subdev, to, size, &retsize, 332 buf); 333 else 334 err = -EINVAL; 335 336 if (err) 337 break; 338 339 *retlen += retsize; 340 len -= size; 341 if (len == 0) 342 break; 343 344 err = -EINVAL; 345 buf += size; 346 to = 0; 347 } 348 return err; 349 } 350 351 static void concat_erase_callback(struct erase_info *instr) 352 { 353 wake_up((wait_queue_head_t *) instr->priv); 354 } 355 356 static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) 357 { 358 int err; 359 wait_queue_head_t waitq; 360 DECLARE_WAITQUEUE(wait, current); 361 362 /* 363 * This code was stol^H^H^H^Hinspired by mtdchar.c 364 */ 365 init_waitqueue_head(&waitq); 366 367 erase->mtd = mtd; 368 erase->callback = concat_erase_callback; 369 erase->priv = (unsigned long) &waitq; 370 371 /* 372 * FIXME: Allow INTERRUPTIBLE. Which means 373 * not having the wait_queue head on the stack. 374 */ 375 err = mtd->erase(mtd, erase); 376 if (!err) { 377 set_current_state(TASK_UNINTERRUPTIBLE); 378 add_wait_queue(&waitq, &wait); 379 if (erase->state != MTD_ERASE_DONE 380 && erase->state != MTD_ERASE_FAILED) 381 schedule(); 382 remove_wait_queue(&waitq, &wait); 383 set_current_state(TASK_RUNNING); 384 385 err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; 386 } 387 return err; 388 } 389 390 static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) 391 { 392 struct mtd_concat *concat = CONCAT(mtd); 393 struct mtd_info *subdev; 394 int i, err; 395 u_int32_t length, offset = 0; 396 struct erase_info *erase; 397 398 if (!(mtd->flags & MTD_WRITEABLE)) 399 return -EROFS; 400 401 if (instr->addr > concat->mtd.size) 402 return -EINVAL; 403 404 if (instr->len + instr->addr > concat->mtd.size) 405 return -EINVAL; 406 407 /* 408 * Check for proper erase block alignment of the to-be-erased area. 409 * It is easier to do this based on the super device's erase 410 * region info rather than looking at each particular sub-device 411 * in turn. 412 */ 413 if (!concat->mtd.numeraseregions) { 414 /* the easy case: device has uniform erase block size */ 415 if (instr->addr & (concat->mtd.erasesize - 1)) 416 return -EINVAL; 417 if (instr->len & (concat->mtd.erasesize - 1)) 418 return -EINVAL; 419 } else { 420 /* device has variable erase size */ 421 struct mtd_erase_region_info *erase_regions = 422 concat->mtd.eraseregions; 423 424 /* 425 * Find the erase region where the to-be-erased area begins: 426 */ 427 for (i = 0; i < concat->mtd.numeraseregions && 428 instr->addr >= erase_regions[i].offset; i++) ; 429 --i; 430 431 /* 432 * Now erase_regions[i] is the region in which the 433 * to-be-erased area begins. Verify that the starting 434 * offset is aligned to this region's erase size: 435 */ 436 if (instr->addr & (erase_regions[i].erasesize - 1)) 437 return -EINVAL; 438 439 /* 440 * now find the erase region where the to-be-erased area ends: 441 */ 442 for (; i < concat->mtd.numeraseregions && 443 (instr->addr + instr->len) >= erase_regions[i].offset; 444 ++i) ; 445 --i; 446 /* 447 * check if the ending offset is aligned to this region's erase size 448 */ 449 if ((instr->addr + instr->len) & (erase_regions[i].erasesize - 450 1)) 451 return -EINVAL; 452 } 453 454 instr->fail_addr = 0xffffffff; 455 456 /* make a local copy of instr to avoid modifying the caller's struct */ 457 erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); 458 459 if (!erase) 460 return -ENOMEM; 461 462 *erase = *instr; 463 length = instr->len; 464 465 /* 466 * find the subdevice where the to-be-erased area begins, adjust 467 * starting offset to be relative to the subdevice start 468 */ 469 for (i = 0; i < concat->num_subdev; i++) { 470 subdev = concat->subdev[i]; 471 if (subdev->size <= erase->addr) { 472 erase->addr -= subdev->size; 473 offset += subdev->size; 474 } else { 475 break; 476 } 477 } 478 479 /* must never happen since size limit has been verified above */ 480 if (i >= concat->num_subdev) 481 BUG(); 482 483 /* now do the erase: */ 484 err = 0; 485 for (; length > 0; i++) { 486 /* loop for all subdevices affected by this request */ 487 subdev = concat->subdev[i]; /* get current subdevice */ 488 489 /* limit length to subdevice's size: */ 490 if (erase->addr + length > subdev->size) 491 erase->len = subdev->size - erase->addr; 492 else 493 erase->len = length; 494 495 if (!(subdev->flags & MTD_WRITEABLE)) { 496 err = -EROFS; 497 break; 498 } 499 length -= erase->len; 500 if ((err = concat_dev_erase(subdev, erase))) { 501 /* sanity check: should never happen since 502 * block alignment has been checked above */ 503 if (err == -EINVAL) 504 BUG(); 505 if (erase->fail_addr != 0xffffffff) 506 instr->fail_addr = erase->fail_addr + offset; 507 break; 508 } 509 /* 510 * erase->addr specifies the offset of the area to be 511 * erased *within the current subdevice*. It can be 512 * non-zero only the first time through this loop, i.e. 513 * for the first subdevice where blocks need to be erased. 514 * All the following erases must begin at the start of the 515 * current subdevice, i.e. at offset zero. 516 */ 517 erase->addr = 0; 518 offset += subdev->size; 519 } 520 instr->state = erase->state; 521 kfree(erase); 522 if (err) 523 return err; 524 525 if (instr->callback) 526 instr->callback(instr); 527 return 0; 528 } 529 530 static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) 531 { 532 struct mtd_concat *concat = CONCAT(mtd); 533 int i, err = -EINVAL; 534 535 if ((len + ofs) > mtd->size) 536 return -EINVAL; 537 538 for (i = 0; i < concat->num_subdev; i++) { 539 struct mtd_info *subdev = concat->subdev[i]; 540 size_t size; 541 542 if (ofs >= subdev->size) { 543 size = 0; 544 ofs -= subdev->size; 545 continue; 546 } 547 if (ofs + len > subdev->size) 548 size = subdev->size - ofs; 549 else 550 size = len; 551 552 err = subdev->lock(subdev, ofs, size); 553 554 if (err) 555 break; 556 557 len -= size; 558 if (len == 0) 559 break; 560 561 err = -EINVAL; 562 ofs = 0; 563 } 564 565 return err; 566 } 567 568 static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 569 { 570 struct mtd_concat *concat = CONCAT(mtd); 571 int i, err = 0; 572 573 if ((len + ofs) > mtd->size) 574 return -EINVAL; 575 576 for (i = 0; i < concat->num_subdev; i++) { 577 struct mtd_info *subdev = concat->subdev[i]; 578 size_t size; 579 580 if (ofs >= subdev->size) { 581 size = 0; 582 ofs -= subdev->size; 583 continue; 584 } 585 if (ofs + len > subdev->size) 586 size = subdev->size - ofs; 587 else 588 size = len; 589 590 err = subdev->unlock(subdev, ofs, size); 591 592 if (err) 593 break; 594 595 len -= size; 596 if (len == 0) 597 break; 598 599 err = -EINVAL; 600 ofs = 0; 601 } 602 603 return err; 604 } 605 606 static void concat_sync(struct mtd_info *mtd) 607 { 608 struct mtd_concat *concat = CONCAT(mtd); 609 int i; 610 611 for (i = 0; i < concat->num_subdev; i++) { 612 struct mtd_info *subdev = concat->subdev[i]; 613 subdev->sync(subdev); 614 } 615 } 616 617 static int concat_suspend(struct mtd_info *mtd) 618 { 619 struct mtd_concat *concat = CONCAT(mtd); 620 int i, rc = 0; 621 622 for (i = 0; i < concat->num_subdev; i++) { 623 struct mtd_info *subdev = concat->subdev[i]; 624 if ((rc = subdev->suspend(subdev)) < 0) 625 return rc; 626 } 627 return rc; 628 } 629 630 static void concat_resume(struct mtd_info *mtd) 631 { 632 struct mtd_concat *concat = CONCAT(mtd); 633 int i; 634 635 for (i = 0; i < concat->num_subdev; i++) { 636 struct mtd_info *subdev = concat->subdev[i]; 637 subdev->resume(subdev); 638 } 639 } 640 641 /* 642 * This function constructs a virtual MTD device by concatenating 643 * num_devs MTD devices. A pointer to the new device object is 644 * stored to *new_dev upon success. This function does _not_ 645 * register any devices: this is the caller's responsibility. 646 */ 647 struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ 648 int num_devs, /* number of subdevices */ 649 char *name) 650 { /* name for the new device */ 651 int i; 652 size_t size; 653 struct mtd_concat *concat; 654 u_int32_t max_erasesize, curr_erasesize; 655 int num_erase_region; 656 657 printk(KERN_NOTICE "Concatenating MTD devices:\n"); 658 for (i = 0; i < num_devs; i++) 659 printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); 660 printk(KERN_NOTICE "into device \"%s\"\n", name); 661 662 /* allocate the device structure */ 663 size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); 664 concat = kmalloc(size, GFP_KERNEL); 665 if (!concat) { 666 printk 667 ("memory allocation error while creating concatenated device \"%s\"\n", 668 name); 669 return NULL; 670 } 671 memset(concat, 0, size); 672 concat->subdev = (struct mtd_info **) (concat + 1); 673 674 /* 675 * Set up the new "super" device's MTD object structure, check for 676 * incompatibilites between the subdevices. 677 */ 678 concat->mtd.type = subdev[0]->type; 679 concat->mtd.flags = subdev[0]->flags; 680 concat->mtd.size = subdev[0]->size; 681 concat->mtd.erasesize = subdev[0]->erasesize; 682 concat->mtd.oobblock = subdev[0]->oobblock; 683 concat->mtd.oobsize = subdev[0]->oobsize; 684 concat->mtd.ecctype = subdev[0]->ecctype; 685 concat->mtd.eccsize = subdev[0]->eccsize; 686 if (subdev[0]->read_ecc) 687 concat->mtd.read_ecc = concat_read_ecc; 688 if (subdev[0]->write_ecc) 689 concat->mtd.write_ecc = concat_write_ecc; 690 if (subdev[0]->read_oob) 691 concat->mtd.read_oob = concat_read_oob; 692 if (subdev[0]->write_oob) 693 concat->mtd.write_oob = concat_write_oob; 694 695 concat->subdev[0] = subdev[0]; 696 697 for (i = 1; i < num_devs; i++) { 698 if (concat->mtd.type != subdev[i]->type) { 699 kfree(concat); 700 printk("Incompatible device type on \"%s\"\n", 701 subdev[i]->name); 702 return NULL; 703 } 704 if (concat->mtd.flags != subdev[i]->flags) { 705 /* 706 * Expect all flags except MTD_WRITEABLE to be 707 * equal on all subdevices. 708 */ 709 if ((concat->mtd.flags ^ subdev[i]-> 710 flags) & ~MTD_WRITEABLE) { 711 kfree(concat); 712 printk("Incompatible device flags on \"%s\"\n", 713 subdev[i]->name); 714 return NULL; 715 } else 716 /* if writeable attribute differs, 717 make super device writeable */ 718 concat->mtd.flags |= 719 subdev[i]->flags & MTD_WRITEABLE; 720 } 721 concat->mtd.size += subdev[i]->size; 722 if (concat->mtd.oobblock != subdev[i]->oobblock || 723 concat->mtd.oobsize != subdev[i]->oobsize || 724 concat->mtd.ecctype != subdev[i]->ecctype || 725 concat->mtd.eccsize != subdev[i]->eccsize || 726 !concat->mtd.read_ecc != !subdev[i]->read_ecc || 727 !concat->mtd.write_ecc != !subdev[i]->write_ecc || 728 !concat->mtd.read_oob != !subdev[i]->read_oob || 729 !concat->mtd.write_oob != !subdev[i]->write_oob) { 730 kfree(concat); 731 printk("Incompatible OOB or ECC data on \"%s\"\n", 732 subdev[i]->name); 733 return NULL; 734 } 735 concat->subdev[i] = subdev[i]; 736 737 } 738 739 concat->num_subdev = num_devs; 740 concat->mtd.name = name; 741 742 /* 743 * NOTE: for now, we do not provide any readv()/writev() methods 744 * because they are messy to implement and they are not 745 * used to a great extent anyway. 746 */ 747 concat->mtd.erase = concat_erase; 748 concat->mtd.read = concat_read; 749 concat->mtd.write = concat_write; 750 concat->mtd.sync = concat_sync; 751 concat->mtd.lock = concat_lock; 752 concat->mtd.unlock = concat_unlock; 753 concat->mtd.suspend = concat_suspend; 754 concat->mtd.resume = concat_resume; 755 756 /* 757 * Combine the erase block size info of the subdevices: 758 * 759 * first, walk the map of the new device and see how 760 * many changes in erase size we have 761 */ 762 max_erasesize = curr_erasesize = subdev[0]->erasesize; 763 num_erase_region = 1; 764 for (i = 0; i < num_devs; i++) { 765 if (subdev[i]->numeraseregions == 0) { 766 /* current subdevice has uniform erase size */ 767 if (subdev[i]->erasesize != curr_erasesize) { 768 /* if it differs from the last subdevice's erase size, count it */ 769 ++num_erase_region; 770 curr_erasesize = subdev[i]->erasesize; 771 if (curr_erasesize > max_erasesize) 772 max_erasesize = curr_erasesize; 773 } 774 } else { 775 /* current subdevice has variable erase size */ 776 int j; 777 for (j = 0; j < subdev[i]->numeraseregions; j++) { 778 779 /* walk the list of erase regions, count any changes */ 780 if (subdev[i]->eraseregions[j].erasesize != 781 curr_erasesize) { 782 ++num_erase_region; 783 curr_erasesize = 784 subdev[i]->eraseregions[j]. 785 erasesize; 786 if (curr_erasesize > max_erasesize) 787 max_erasesize = curr_erasesize; 788 } 789 } 790 } 791 } 792 793 if (num_erase_region == 1) { 794 /* 795 * All subdevices have the same uniform erase size. 796 * This is easy: 797 */ 798 concat->mtd.erasesize = curr_erasesize; 799 concat->mtd.numeraseregions = 0; 800 } else { 801 /* 802 * erase block size varies across the subdevices: allocate 803 * space to store the data describing the variable erase regions 804 */ 805 struct mtd_erase_region_info *erase_region_p; 806 u_int32_t begin, position; 807 808 concat->mtd.erasesize = max_erasesize; 809 concat->mtd.numeraseregions = num_erase_region; 810 concat->mtd.eraseregions = erase_region_p = 811 kmalloc(num_erase_region * 812 sizeof (struct mtd_erase_region_info), GFP_KERNEL); 813 if (!erase_region_p) { 814 kfree(concat); 815 printk 816 ("memory allocation error while creating erase region list" 817 " for device \"%s\"\n", name); 818 return NULL; 819 } 820 821 /* 822 * walk the map of the new device once more and fill in 823 * in erase region info: 824 */ 825 curr_erasesize = subdev[0]->erasesize; 826 begin = position = 0; 827 for (i = 0; i < num_devs; i++) { 828 if (subdev[i]->numeraseregions == 0) { 829 /* current subdevice has uniform erase size */ 830 if (subdev[i]->erasesize != curr_erasesize) { 831 /* 832 * fill in an mtd_erase_region_info structure for the area 833 * we have walked so far: 834 */ 835 erase_region_p->offset = begin; 836 erase_region_p->erasesize = 837 curr_erasesize; 838 erase_region_p->numblocks = 839 (position - begin) / curr_erasesize; 840 begin = position; 841 842 curr_erasesize = subdev[i]->erasesize; 843 ++erase_region_p; 844 } 845 position += subdev[i]->size; 846 } else { 847 /* current subdevice has variable erase size */ 848 int j; 849 for (j = 0; j < subdev[i]->numeraseregions; j++) { 850 /* walk the list of erase regions, count any changes */ 851 if (subdev[i]->eraseregions[j]. 852 erasesize != curr_erasesize) { 853 erase_region_p->offset = begin; 854 erase_region_p->erasesize = 855 curr_erasesize; 856 erase_region_p->numblocks = 857 (position - 858 begin) / curr_erasesize; 859 begin = position; 860 861 curr_erasesize = 862 subdev[i]->eraseregions[j]. 863 erasesize; 864 ++erase_region_p; 865 } 866 position += 867 subdev[i]->eraseregions[j]. 868 numblocks * curr_erasesize; 869 } 870 } 871 } 872 /* Now write the final entry */ 873 erase_region_p->offset = begin; 874 erase_region_p->erasesize = curr_erasesize; 875 erase_region_p->numblocks = (position - begin) / curr_erasesize; 876 } 877 878 return &concat->mtd; 879 } 880 881 /* 882 * This function destroys an MTD object obtained from concat_mtd_devs() 883 */ 884 885 void mtd_concat_destroy(struct mtd_info *mtd) 886 { 887 struct mtd_concat *concat = CONCAT(mtd); 888 if (concat->mtd.numeraseregions) 889 kfree(concat->mtd.eraseregions); 890 kfree(concat); 891 } 892 893 EXPORT_SYMBOL(mtd_concat_create); 894 EXPORT_SYMBOL(mtd_concat_destroy); 895 896 MODULE_LICENSE("GPL"); 897 MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>"); 898 MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); 899