diff options
Diffstat (limited to 'drivers/mtd/mtdpart.c')
| -rw-r--r-- | drivers/mtd/mtdpart.c | 154 |
1 files changed, 136 insertions, 18 deletions
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index dc6558568876..79e3689f1e16 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
| @@ -29,9 +29,11 @@ | |||
| 29 | #include <linux/kmod.h> | 29 | #include <linux/kmod.h> |
| 30 | #include <linux/mtd/mtd.h> | 30 | #include <linux/mtd/mtd.h> |
| 31 | #include <linux/mtd/partitions.h> | 31 | #include <linux/mtd/partitions.h> |
| 32 | #include <linux/err.h> | ||
| 32 | 33 | ||
| 33 | /* Our partition linked list */ | 34 | /* Our partition linked list */ |
| 34 | static LIST_HEAD(mtd_partitions); | 35 | static LIST_HEAD(mtd_partitions); |
| 36 | static DEFINE_MUTEX(mtd_partitions_mutex); | ||
| 35 | 37 | ||
| 36 | /* Our partition node structure */ | 38 | /* Our partition node structure */ |
| 37 | struct mtd_part { | 39 | struct mtd_part { |
| @@ -326,6 +328,12 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
| 326 | return res; | 328 | return res; |
| 327 | } | 329 | } |
| 328 | 330 | ||
| 331 | static inline void free_partition(struct mtd_part *p) | ||
| 332 | { | ||
| 333 | kfree(p->mtd.name); | ||
| 334 | kfree(p); | ||
| 335 | } | ||
| 336 | |||
| 329 | /* | 337 | /* |
| 330 | * This function unregisters and destroy all slave MTD objects which are | 338 | * This function unregisters and destroy all slave MTD objects which are |
| 331 | * attached to the given master MTD object. | 339 | * attached to the given master MTD object. |
| @@ -334,33 +342,42 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
| 334 | int del_mtd_partitions(struct mtd_info *master) | 342 | int del_mtd_partitions(struct mtd_info *master) |
| 335 | { | 343 | { |
| 336 | struct mtd_part *slave, *next; | 344 | struct mtd_part *slave, *next; |
| 345 | int ret, err = 0; | ||
| 337 | 346 | ||
| 347 | mutex_lock(&mtd_partitions_mutex); | ||
| 338 | list_for_each_entry_safe(slave, next, &mtd_partitions, list) | 348 | list_for_each_entry_safe(slave, next, &mtd_partitions, list) |
| 339 | if (slave->master == master) { | 349 | if (slave->master == master) { |
| 350 | ret = del_mtd_device(&slave->mtd); | ||
| 351 | if (ret < 0) { | ||
| 352 | err = ret; | ||
| 353 | continue; | ||
| 354 | } | ||
| 340 | list_del(&slave->list); | 355 | list_del(&slave->list); |
| 341 | del_mtd_device(&slave->mtd); | 356 | free_partition(slave); |
| 342 | kfree(slave); | ||
| 343 | } | 357 | } |
| 358 | mutex_unlock(&mtd_partitions_mutex); | ||
| 344 | 359 | ||
| 345 | return 0; | 360 | return err; |
| 346 | } | 361 | } |
| 347 | EXPORT_SYMBOL(del_mtd_partitions); | 362 | EXPORT_SYMBOL(del_mtd_partitions); |
| 348 | 363 | ||
| 349 | static struct mtd_part *add_one_partition(struct mtd_info *master, | 364 | static struct mtd_part *allocate_partition(struct mtd_info *master, |
| 350 | const struct mtd_partition *part, int partno, | 365 | const struct mtd_partition *part, int partno, |
| 351 | uint64_t cur_offset) | 366 | uint64_t cur_offset) |
| 352 | { | 367 | { |
| 353 | struct mtd_part *slave; | 368 | struct mtd_part *slave; |
| 369 | char *name; | ||
| 354 | 370 | ||
| 355 | /* allocate the partition structure */ | 371 | /* allocate the partition structure */ |
| 356 | slave = kzalloc(sizeof(*slave), GFP_KERNEL); | 372 | slave = kzalloc(sizeof(*slave), GFP_KERNEL); |
| 357 | if (!slave) { | 373 | name = kstrdup(part->name, GFP_KERNEL); |
| 374 | if (!name || !slave) { | ||
| 358 | printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", | 375 | printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", |
| 359 | master->name); | 376 | master->name); |
| 360 | del_mtd_partitions(master); | 377 | kfree(name); |
| 361 | return NULL; | 378 | kfree(slave); |
| 379 | return ERR_PTR(-ENOMEM); | ||
| 362 | } | 380 | } |
| 363 | list_add(&slave->list, &mtd_partitions); | ||
| 364 | 381 | ||
| 365 | /* set up the MTD object for this partition */ | 382 | /* set up the MTD object for this partition */ |
| 366 | slave->mtd.type = master->type; | 383 | slave->mtd.type = master->type; |
| @@ -371,7 +388,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 371 | slave->mtd.oobavail = master->oobavail; | 388 | slave->mtd.oobavail = master->oobavail; |
| 372 | slave->mtd.subpage_sft = master->subpage_sft; | 389 | slave->mtd.subpage_sft = master->subpage_sft; |
| 373 | 390 | ||
| 374 | slave->mtd.name = part->name; | 391 | slave->mtd.name = name; |
| 375 | slave->mtd.owner = master->owner; | 392 | slave->mtd.owner = master->owner; |
| 376 | slave->mtd.backing_dev_info = master->backing_dev_info; | 393 | slave->mtd.backing_dev_info = master->backing_dev_info; |
| 377 | 394 | ||
| @@ -518,12 +535,89 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 518 | } | 535 | } |
| 519 | 536 | ||
| 520 | out_register: | 537 | out_register: |
| 521 | /* register our partition */ | ||
| 522 | add_mtd_device(&slave->mtd); | ||
| 523 | |||
| 524 | return slave; | 538 | return slave; |
| 525 | } | 539 | } |
| 526 | 540 | ||
| 541 | int mtd_add_partition(struct mtd_info *master, char *name, | ||
| 542 | long long offset, long long length) | ||
| 543 | { | ||
| 544 | struct mtd_partition part; | ||
| 545 | struct mtd_part *p, *new; | ||
| 546 | uint64_t start, end; | ||
| 547 | int ret = 0; | ||
| 548 | |||
| 549 | /* the direct offset is expected */ | ||
| 550 | if (offset == MTDPART_OFS_APPEND || | ||
| 551 | offset == MTDPART_OFS_NXTBLK) | ||
| 552 | return -EINVAL; | ||
| 553 | |||
| 554 | if (length == MTDPART_SIZ_FULL) | ||
| 555 | length = master->size - offset; | ||
| 556 | |||
| 557 | if (length <= 0) | ||
| 558 | return -EINVAL; | ||
| 559 | |||
| 560 | part.name = name; | ||
| 561 | part.size = length; | ||
| 562 | part.offset = offset; | ||
| 563 | part.mask_flags = 0; | ||
| 564 | part.ecclayout = NULL; | ||
| 565 | |||
| 566 | new = allocate_partition(master, &part, -1, offset); | ||
| 567 | if (IS_ERR(new)) | ||
| 568 | return PTR_ERR(new); | ||
| 569 | |||
| 570 | start = offset; | ||
| 571 | end = offset + length; | ||
| 572 | |||
| 573 | mutex_lock(&mtd_partitions_mutex); | ||
| 574 | list_for_each_entry(p, &mtd_partitions, list) | ||
| 575 | if (p->master == master) { | ||
| 576 | if ((start >= p->offset) && | ||
| 577 | (start < (p->offset + p->mtd.size))) | ||
| 578 | goto err_inv; | ||
| 579 | |||
| 580 | if ((end >= p->offset) && | ||
| 581 | (end < (p->offset + p->mtd.size))) | ||
| 582 | goto err_inv; | ||
| 583 | } | ||
| 584 | |||
| 585 | list_add(&new->list, &mtd_partitions); | ||
| 586 | mutex_unlock(&mtd_partitions_mutex); | ||
| 587 | |||
| 588 | add_mtd_device(&new->mtd); | ||
| 589 | |||
| 590 | return ret; | ||
| 591 | err_inv: | ||
| 592 | mutex_unlock(&mtd_partitions_mutex); | ||
| 593 | free_partition(new); | ||
| 594 | return -EINVAL; | ||
| 595 | } | ||
| 596 | EXPORT_SYMBOL_GPL(mtd_add_partition); | ||
| 597 | |||
| 598 | int mtd_del_partition(struct mtd_info *master, int partno) | ||
| 599 | { | ||
| 600 | struct mtd_part *slave, *next; | ||
| 601 | int ret = -EINVAL; | ||
| 602 | |||
| 603 | mutex_lock(&mtd_partitions_mutex); | ||
| 604 | list_for_each_entry_safe(slave, next, &mtd_partitions, list) | ||
| 605 | if ((slave->master == master) && | ||
| 606 | (slave->mtd.index == partno)) { | ||
| 607 | ret = del_mtd_device(&slave->mtd); | ||
| 608 | if (ret < 0) | ||
| 609 | break; | ||
| 610 | |||
| 611 | list_del(&slave->list); | ||
| 612 | free_partition(slave); | ||
| 613 | break; | ||
| 614 | } | ||
| 615 | mutex_unlock(&mtd_partitions_mutex); | ||
| 616 | |||
| 617 | return ret; | ||
| 618 | } | ||
| 619 | EXPORT_SYMBOL_GPL(mtd_del_partition); | ||
| 620 | |||
| 527 | /* | 621 | /* |
| 528 | * This function, given a master MTD object and a partition table, creates | 622 | * This function, given a master MTD object and a partition table, creates |
| 529 | * and registers slave MTD objects which are bound to the master according to | 623 | * and registers slave MTD objects which are bound to the master according to |
| @@ -544,9 +638,16 @@ int add_mtd_partitions(struct mtd_info *master, | |||
| 544 | printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); | 638 | printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); |
| 545 | 639 | ||
| 546 | for (i = 0; i < nbparts; i++) { | 640 | for (i = 0; i < nbparts; i++) { |
| 547 | slave = add_one_partition(master, parts + i, i, cur_offset); | 641 | slave = allocate_partition(master, parts + i, i, cur_offset); |
| 548 | if (!slave) | 642 | if (IS_ERR(slave)) |
| 549 | return -ENOMEM; | 643 | return PTR_ERR(slave); |
| 644 | |||
| 645 | mutex_lock(&mtd_partitions_mutex); | ||
| 646 | list_add(&slave->list, &mtd_partitions); | ||
| 647 | mutex_unlock(&mtd_partitions_mutex); | ||
| 648 | |||
| 649 | add_mtd_device(&slave->mtd); | ||
| 650 | |||
| 550 | cur_offset = slave->offset + slave->mtd.size; | 651 | cur_offset = slave->offset + slave->mtd.size; |
| 551 | } | 652 | } |
| 552 | 653 | ||
| @@ -618,3 +719,20 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types, | |||
| 618 | return ret; | 719 | return ret; |
| 619 | } | 720 | } |
| 620 | EXPORT_SYMBOL_GPL(parse_mtd_partitions); | 721 | EXPORT_SYMBOL_GPL(parse_mtd_partitions); |
| 722 | |||
| 723 | int mtd_is_master(struct mtd_info *mtd) | ||
| 724 | { | ||
| 725 | struct mtd_part *part; | ||
| 726 | int nopart = 0; | ||
| 727 | |||
| 728 | mutex_lock(&mtd_partitions_mutex); | ||
| 729 | list_for_each_entry(part, &mtd_partitions, list) | ||
| 730 | if (&part->mtd == mtd) { | ||
| 731 | nopart = 1; | ||
| 732 | break; | ||
| 733 | } | ||
| 734 | mutex_unlock(&mtd_partitions_mutex); | ||
| 735 | |||
| 736 | return nopart; | ||
| 737 | } | ||
| 738 | EXPORT_SYMBOL_GPL(mtd_is_master); | ||
