1 /* 2 * rfd_ftl.c -- resident flash disk (flash translation layer) 3 * 4 * Copyright © 2005 Sean Young <sean@mess.org> 5 * 6 * This type of flash translation layer (FTL) is used by the Embedded BIOS 7 * by General Software. It is known as the Resident Flash Disk (RFD), see: 8 * 9 * http://www.gensw.com/pages/prod/bios/rfd.htm 10 * 11 * based on ftl.c 12 */ 13 14 #include <linux/hdreg.h> 15 #include <linux/init.h> 16 #include <linux/mtd/blktrans.h> 17 #include <linux/mtd/mtd.h> 18 #include <linux/vmalloc.h> 19 #include <linux/slab.h> 20 #include <linux/jiffies.h> 21 #include <linux/module.h> 22 23 #include <asm/types.h> 24 25 static int block_size = 0; 26 module_param(block_size, int, 0); 27 MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size"); 28 29 #define PREFIX "rfd_ftl: " 30 31 /* This major has been assigned by device@lanana.org */ 32 #ifndef RFD_FTL_MAJOR 33 #define RFD_FTL_MAJOR 256 34 #endif 35 36 /* Maximum number of partitions in an FTL region */ 37 #define PART_BITS 4 38 39 /* An erase unit should start with this value */ 40 #define RFD_MAGIC 0x9193 41 42 /* the second value is 0xffff or 0xffc8; function unknown */ 43 44 /* the third value is always 0xffff, ignored */ 45 46 /* next is an array of mapping for each corresponding sector */ 47 #define HEADER_MAP_OFFSET 3 48 #define SECTOR_DELETED 0x0000 49 #define SECTOR_ZERO 0xfffe 50 #define SECTOR_FREE 0xffff 51 52 #define SECTOR_SIZE 512 53 54 #define SECTORS_PER_TRACK 63 55 56 struct block { 57 enum { 58 BLOCK_OK, 59 BLOCK_ERASING, 60 BLOCK_ERASED, 61 BLOCK_UNUSED, 62 BLOCK_FAILED 63 } state; 64 int free_sectors; 65 int used_sectors; 66 int erases; 67 u_long offset; 68 }; 69 70 struct partition { 71 struct mtd_blktrans_dev mbd; 72 73 u_int block_size; /* size of erase unit */ 74 u_int total_blocks; /* number of erase units */ 75 u_int header_sectors_per_block; /* header sectors in erase unit */ 76 u_int data_sectors_per_block; /* data sectors in erase unit */ 77 u_int sector_count; /* sectors in translated disk */ 78 u_int header_size; /* bytes in header sector */ 79 int reserved_block; /* block next up for reclaim */ 80 int current_block; /* block to write to */ 81 u16 *header_cache; /* cached header */ 82 83 int is_reclaiming; 84 int cylinders; 85 int errors; 86 u_long *sector_map; 87 struct block *blocks; 88 }; 89 90 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf); 91 92 static int build_block_map(struct partition *part, int block_no) 93 { 94 struct block *block = &part->blocks[block_no]; 95 int i; 96 97 block->offset = part->block_size * block_no; 98 99 if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) { 100 block->state = BLOCK_UNUSED; 101 return -ENOENT; 102 } 103 104 block->state = BLOCK_OK; 105 106 for (i=0; i<part->data_sectors_per_block; i++) { 107 u16 entry; 108 109 entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]); 110 111 if (entry == SECTOR_DELETED) 112 continue; 113 114 if (entry == SECTOR_FREE) { 115 block->free_sectors++; 116 continue; 117 } 118 119 if (entry == SECTOR_ZERO) 120 entry = 0; 121 122 if (entry >= part->sector_count) { 123 printk(KERN_WARNING PREFIX 124 "'%s': unit #%d: entry %d corrupt, " 125 "sector %d out of range\n", 126 part->mbd.mtd->name, block_no, i, entry); 127 continue; 128 } 129 130 if (part->sector_map[entry] != -1) { 131 printk(KERN_WARNING PREFIX 132 "'%s': more than one entry for sector %d\n", 133 part->mbd.mtd->name, entry); 134 part->errors = 1; 135 continue; 136 } 137 138 part->sector_map[entry] = block->offset + 139 (i + part->header_sectors_per_block) * SECTOR_SIZE; 140 141 block->used_sectors++; 142 } 143 144 if (block->free_sectors == part->data_sectors_per_block) 145 part->reserved_block = block_no; 146 147 return 0; 148 } 149 150 static int scan_header(struct partition *part) 151 { 152 int sectors_per_block; 153 int i, rc = -ENOMEM; 154 int blocks_found; 155 size_t retlen; 156 157 sectors_per_block = part->block_size / SECTOR_SIZE; 158 part->total_blocks = (u32)part->mbd.mtd->size / part->block_size; 159 160 if (part->total_blocks < 2) 161 return -ENOENT; 162 163 /* each erase block has three bytes header, followed by the map */ 164 part->header_sectors_per_block = 165 ((HEADER_MAP_OFFSET + sectors_per_block) * 166 sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE; 167 168 part->data_sectors_per_block = sectors_per_block - 169 part->header_sectors_per_block; 170 171 part->header_size = (HEADER_MAP_OFFSET + 172 part->data_sectors_per_block) * sizeof(u16); 173 174 part->cylinders = (part->data_sectors_per_block * 175 (part->total_blocks - 1) - 1) / SECTORS_PER_TRACK; 176 177 part->sector_count = part->cylinders * SECTORS_PER_TRACK; 178 179 part->current_block = -1; 180 part->reserved_block = -1; 181 part->is_reclaiming = 0; 182 183 part->header_cache = kmalloc(part->header_size, GFP_KERNEL); 184 if (!part->header_cache) 185 goto err; 186 187 part->blocks = kcalloc(part->total_blocks, sizeof(struct block), 188 GFP_KERNEL); 189 if (!part->blocks) 190 goto err; 191 192 part->sector_map = vmalloc(part->sector_count * sizeof(u_long)); 193 if (!part->sector_map) { 194 printk(KERN_ERR PREFIX "'%s': unable to allocate memory for " 195 "sector map", part->mbd.mtd->name); 196 goto err; 197 } 198 199 for (i=0; i<part->sector_count; i++) 200 part->sector_map[i] = -1; 201 202 for (i=0, blocks_found=0; i<part->total_blocks; i++) { 203 rc = part->mbd.mtd->read(part->mbd.mtd, 204 i * part->block_size, part->header_size, 205 &retlen, (u_char*)part->header_cache); 206 207 if (!rc && retlen != part->header_size) 208 rc = -EIO; 209 210 if (rc) 211 goto err; 212 213 if (!build_block_map(part, i)) 214 blocks_found++; 215 } 216 217 if (blocks_found == 0) { 218 printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n", 219 part->mbd.mtd->name); 220 rc = -ENOENT; 221 goto err; 222 } 223 224 if (part->reserved_block == -1) { 225 printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n", 226 part->mbd.mtd->name); 227 228 part->errors = 1; 229 } 230 231 return 0; 232 233 err: 234 vfree(part->sector_map); 235 kfree(part->header_cache); 236 kfree(part->blocks); 237 238 return rc; 239 } 240 241 static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf) 242 { 243 struct partition *part = (struct partition*)dev; 244 u_long addr; 245 size_t retlen; 246 int rc; 247 248 if (sector >= part->sector_count) 249 return -EIO; 250 251 addr = part->sector_map[sector]; 252 if (addr != -1) { 253 rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE, 254 &retlen, (u_char*)buf); 255 if (!rc && retlen != SECTOR_SIZE) 256 rc = -EIO; 257 258 if (rc) { 259 printk(KERN_WARNING PREFIX "error reading '%s' at " 260 "0x%lx\n", part->mbd.mtd->name, addr); 261 return rc; 262 } 263 } else 264 memset(buf, 0, SECTOR_SIZE); 265 266 return 0; 267 } 268 269 static void erase_callback(struct erase_info *erase) 270 { 271 struct partition *part; 272 u16 magic; 273 int i, rc; 274 size_t retlen; 275 276 part = (struct partition*)erase->priv; 277 278 i = (u32)erase->addr / part->block_size; 279 if (i >= part->total_blocks || part->blocks[i].offset != erase->addr || 280 erase->addr > UINT_MAX) { 281 printk(KERN_ERR PREFIX "erase callback for unknown offset %llx " 282 "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name); 283 return; 284 } 285 286 if (erase->state != MTD_ERASE_DONE) { 287 printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', " 288 "state %d\n", (unsigned long long)erase->addr, 289 part->mbd.mtd->name, erase->state); 290 291 part->blocks[i].state = BLOCK_FAILED; 292 part->blocks[i].free_sectors = 0; 293 part->blocks[i].used_sectors = 0; 294 295 kfree(erase); 296 297 return; 298 } 299 300 magic = cpu_to_le16(RFD_MAGIC); 301 302 part->blocks[i].state = BLOCK_ERASED; 303 part->blocks[i].free_sectors = part->data_sectors_per_block; 304 part->blocks[i].used_sectors = 0; 305 part->blocks[i].erases++; 306 307 rc = part->mbd.mtd->write(part->mbd.mtd, 308 part->blocks[i].offset, sizeof(magic), &retlen, 309 (u_char*)&magic); 310 311 if (!rc && retlen != sizeof(magic)) 312 rc = -EIO; 313 314 if (rc) { 315 printk(KERN_ERR PREFIX "'%s': unable to write RFD " 316 "header at 0x%lx\n", 317 part->mbd.mtd->name, 318 part->blocks[i].offset); 319 part->blocks[i].state = BLOCK_FAILED; 320 } 321 else 322 part->blocks[i].state = BLOCK_OK; 323 324 kfree(erase); 325 } 326 327 static int erase_block(struct partition *part, int block) 328 { 329 struct erase_info *erase; 330 int rc = -ENOMEM; 331 332 erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL); 333 if (!erase) 334 goto err; 335 336 erase->mtd = part->mbd.mtd; 337 erase->callback = erase_callback; 338 erase->addr = part->blocks[block].offset; 339 erase->len = part->block_size; 340 erase->priv = (u_long)part; 341 342 part->blocks[block].state = BLOCK_ERASING; 343 part->blocks[block].free_sectors = 0; 344 345 rc = part->mbd.mtd->erase(part->mbd.mtd, erase); 346 347 if (rc) { 348 printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' " 349 "failed\n", (unsigned long long)erase->addr, 350 (unsigned long long)erase->len, part->mbd.mtd->name); 351 kfree(erase); 352 } 353 354 err: 355 return rc; 356 } 357 358 static int move_block_contents(struct partition *part, int block_no, u_long *old_sector) 359 { 360 void *sector_data; 361 u16 *map; 362 size_t retlen; 363 int i, rc = -ENOMEM; 364 365 part->is_reclaiming = 1; 366 367 sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL); 368 if (!sector_data) 369 goto err3; 370 371 map = kmalloc(part->header_size, GFP_KERNEL); 372 if (!map) 373 goto err2; 374 375 rc = part->mbd.mtd->read(part->mbd.mtd, 376 part->blocks[block_no].offset, part->header_size, 377 &retlen, (u_char*)map); 378 379 if (!rc && retlen != part->header_size) 380 rc = -EIO; 381 382 if (rc) { 383 printk(KERN_ERR PREFIX "error reading '%s' at " 384 "0x%lx\n", part->mbd.mtd->name, 385 part->blocks[block_no].offset); 386 387 goto err; 388 } 389 390 for (i=0; i<part->data_sectors_per_block; i++) { 391 u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]); 392 u_long addr; 393 394 395 if (entry == SECTOR_FREE || entry == SECTOR_DELETED) 396 continue; 397 398 if (entry == SECTOR_ZERO) 399 entry = 0; 400 401 /* already warned about and ignored in build_block_map() */ 402 if (entry >= part->sector_count) 403 continue; 404 405 addr = part->blocks[block_no].offset + 406 (i + part->header_sectors_per_block) * SECTOR_SIZE; 407 408 if (*old_sector == addr) { 409 *old_sector = -1; 410 if (!part->blocks[block_no].used_sectors--) { 411 rc = erase_block(part, block_no); 412 break; 413 } 414 continue; 415 } 416 rc = part->mbd.mtd->read(part->mbd.mtd, addr, 417 SECTOR_SIZE, &retlen, sector_data); 418 419 if (!rc && retlen != SECTOR_SIZE) 420 rc = -EIO; 421 422 if (rc) { 423 printk(KERN_ERR PREFIX "'%s': Unable to " 424 "read sector for relocation\n", 425 part->mbd.mtd->name); 426 427 goto err; 428 } 429 430 rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part, 431 entry, sector_data); 432 433 if (rc) 434 goto err; 435 } 436 437 err: 438 kfree(map); 439 err2: 440 kfree(sector_data); 441 err3: 442 part->is_reclaiming = 0; 443 444 return rc; 445 } 446 447 static int reclaim_block(struct partition *part, u_long *old_sector) 448 { 449 int block, best_block, score, old_sector_block; 450 int rc; 451 452 /* we have a race if sync doesn't exist */ 453 if (part->mbd.mtd->sync) 454 part->mbd.mtd->sync(part->mbd.mtd); 455 456 score = 0x7fffffff; /* MAX_INT */ 457 best_block = -1; 458 if (*old_sector != -1) 459 old_sector_block = *old_sector / part->block_size; 460 else 461 old_sector_block = -1; 462 463 for (block=0; block<part->total_blocks; block++) { 464 int this_score; 465 466 if (block == part->reserved_block) 467 continue; 468 469 /* 470 * Postpone reclaiming if there is a free sector as 471 * more removed sectors is more efficient (have to move 472 * less). 473 */ 474 if (part->blocks[block].free_sectors) 475 return 0; 476 477 this_score = part->blocks[block].used_sectors; 478 479 if (block == old_sector_block) 480 this_score--; 481 else { 482 /* no point in moving a full block */ 483 if (part->blocks[block].used_sectors == 484 part->data_sectors_per_block) 485 continue; 486 } 487 488 this_score += part->blocks[block].erases; 489 490 if (this_score < score) { 491 best_block = block; 492 score = this_score; 493 } 494 } 495 496 if (best_block == -1) 497 return -ENOSPC; 498 499 part->current_block = -1; 500 part->reserved_block = best_block; 501 502 pr_debug("reclaim_block: reclaiming block #%d with %d used " 503 "%d free sectors\n", best_block, 504 part->blocks[best_block].used_sectors, 505 part->blocks[best_block].free_sectors); 506 507 if (part->blocks[best_block].used_sectors) 508 rc = move_block_contents(part, best_block, old_sector); 509 else 510 rc = erase_block(part, best_block); 511 512 return rc; 513 } 514 515 /* 516 * IMPROVE: It would be best to choose the block with the most deleted sectors, 517 * because if we fill that one up first it'll have the most chance of having 518 * the least live sectors at reclaim. 519 */ 520 static int find_free_block(struct partition *part) 521 { 522 int block, stop; 523 524 block = part->current_block == -1 ? 525 jiffies % part->total_blocks : part->current_block; 526 stop = block; 527 528 do { 529 if (part->blocks[block].free_sectors && 530 block != part->reserved_block) 531 return block; 532 533 if (part->blocks[block].state == BLOCK_UNUSED) 534 erase_block(part, block); 535 536 if (++block >= part->total_blocks) 537 block = 0; 538 539 } while (block != stop); 540 541 return -1; 542 } 543 544 static int find_writable_block(struct partition *part, u_long *old_sector) 545 { 546 int rc, block; 547 size_t retlen; 548 549 block = find_free_block(part); 550 551 if (block == -1) { 552 if (!part->is_reclaiming) { 553 rc = reclaim_block(part, old_sector); 554 if (rc) 555 goto err; 556 557 block = find_free_block(part); 558 } 559 560 if (block == -1) { 561 rc = -ENOSPC; 562 goto err; 563 } 564 } 565 566 rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block].offset, 567 part->header_size, &retlen, (u_char*)part->header_cache); 568 569 if (!rc && retlen != part->header_size) 570 rc = -EIO; 571 572 if (rc) { 573 printk(KERN_ERR PREFIX "'%s': unable to read header at " 574 "0x%lx\n", part->mbd.mtd->name, 575 part->blocks[block].offset); 576 goto err; 577 } 578 579 part->current_block = block; 580 581 err: 582 return rc; 583 } 584 585 static int mark_sector_deleted(struct partition *part, u_long old_addr) 586 { 587 int block, offset, rc; 588 u_long addr; 589 size_t retlen; 590 u16 del = cpu_to_le16(SECTOR_DELETED); 591 592 block = old_addr / part->block_size; 593 offset = (old_addr % part->block_size) / SECTOR_SIZE - 594 part->header_sectors_per_block; 595 596 addr = part->blocks[block].offset + 597 (HEADER_MAP_OFFSET + offset) * sizeof(u16); 598 rc = part->mbd.mtd->write(part->mbd.mtd, addr, 599 sizeof(del), &retlen, (u_char*)&del); 600 601 if (!rc && retlen != sizeof(del)) 602 rc = -EIO; 603 604 if (rc) { 605 printk(KERN_ERR PREFIX "error writing '%s' at " 606 "0x%lx\n", part->mbd.mtd->name, addr); 607 if (rc) 608 goto err; 609 } 610 if (block == part->current_block) 611 part->header_cache[offset + HEADER_MAP_OFFSET] = del; 612 613 part->blocks[block].used_sectors--; 614 615 if (!part->blocks[block].used_sectors && 616 !part->blocks[block].free_sectors) 617 rc = erase_block(part, block); 618 619 err: 620 return rc; 621 } 622 623 static int find_free_sector(const struct partition *part, const struct block *block) 624 { 625 int i, stop; 626 627 i = stop = part->data_sectors_per_block - block->free_sectors; 628 629 do { 630 if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]) 631 == SECTOR_FREE) 632 return i; 633 634 if (++i == part->data_sectors_per_block) 635 i = 0; 636 } 637 while(i != stop); 638 639 return -1; 640 } 641 642 static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr) 643 { 644 struct partition *part = (struct partition*)dev; 645 struct block *block; 646 u_long addr; 647 int i; 648 int rc; 649 size_t retlen; 650 u16 entry; 651 652 if (part->current_block == -1 || 653 !part->blocks[part->current_block].free_sectors) { 654 655 rc = find_writable_block(part, old_addr); 656 if (rc) 657 goto err; 658 } 659 660 block = &part->blocks[part->current_block]; 661 662 i = find_free_sector(part, block); 663 664 if (i < 0) { 665 rc = -ENOSPC; 666 goto err; 667 } 668 669 addr = (i + part->header_sectors_per_block) * SECTOR_SIZE + 670 block->offset; 671 rc = part->mbd.mtd->write(part->mbd.mtd, 672 addr, SECTOR_SIZE, &retlen, (u_char*)buf); 673 674 if (!rc && retlen != SECTOR_SIZE) 675 rc = -EIO; 676 677 if (rc) { 678 printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n", 679 part->mbd.mtd->name, addr); 680 if (rc) 681 goto err; 682 } 683 684 part->sector_map[sector] = addr; 685 686 entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector); 687 688 part->header_cache[i + HEADER_MAP_OFFSET] = entry; 689 690 addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16); 691 rc = part->mbd.mtd->write(part->mbd.mtd, addr, 692 sizeof(entry), &retlen, (u_char*)&entry); 693 694 if (!rc && retlen != sizeof(entry)) 695 rc = -EIO; 696 697 if (rc) { 698 printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n", 699 part->mbd.mtd->name, addr); 700 if (rc) 701 goto err; 702 } 703 block->used_sectors++; 704 block->free_sectors--; 705 706 err: 707 return rc; 708 } 709 710 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf) 711 { 712 struct partition *part = (struct partition*)dev; 713 u_long old_addr; 714 int i; 715 int rc = 0; 716 717 pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector); 718 719 if (part->reserved_block == -1) { 720 rc = -EACCES; 721 goto err; 722 } 723 724 if (sector >= part->sector_count) { 725 rc = -EIO; 726 goto err; 727 } 728 729 old_addr = part->sector_map[sector]; 730 731 for (i=0; i<SECTOR_SIZE; i++) { 732 if (!buf[i]) 733 continue; 734 735 rc = do_writesect(dev, sector, buf, &old_addr); 736 if (rc) 737 goto err; 738 break; 739 } 740 741 if (i == SECTOR_SIZE) 742 part->sector_map[sector] = -1; 743 744 if (old_addr != -1) 745 rc = mark_sector_deleted(part, old_addr); 746 747 err: 748 return rc; 749 } 750 751 static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) 752 { 753 struct partition *part = (struct partition*)dev; 754 755 geo->heads = 1; 756 geo->sectors = SECTORS_PER_TRACK; 757 geo->cylinders = part->cylinders; 758 759 return 0; 760 } 761 762 static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 763 { 764 struct partition *part; 765 766 if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX) 767 return; 768 769 part = kzalloc(sizeof(struct partition), GFP_KERNEL); 770 if (!part) 771 return; 772 773 part->mbd.mtd = mtd; 774 775 if (block_size) 776 part->block_size = block_size; 777 else { 778 if (!mtd->erasesize) { 779 printk(KERN_WARNING PREFIX "please provide block_size"); 780 goto out; 781 } else 782 part->block_size = mtd->erasesize; 783 } 784 785 if (scan_header(part) == 0) { 786 part->mbd.size = part->sector_count; 787 part->mbd.tr = tr; 788 part->mbd.devnum = -1; 789 if (!(mtd->flags & MTD_WRITEABLE)) 790 part->mbd.readonly = 1; 791 else if (part->errors) { 792 printk(KERN_WARNING PREFIX "'%s': errors found, " 793 "setting read-only\n", mtd->name); 794 part->mbd.readonly = 1; 795 } 796 797 printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n", 798 mtd->name, mtd->type, mtd->flags); 799 800 if (!add_mtd_blktrans_dev((void*)part)) 801 return; 802 } 803 out: 804 kfree(part); 805 } 806 807 static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) 808 { 809 struct partition *part = (struct partition*)dev; 810 int i; 811 812 for (i=0; i<part->total_blocks; i++) { 813 pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n", 814 part->mbd.mtd->name, i, part->blocks[i].erases); 815 } 816 817 del_mtd_blktrans_dev(dev); 818 vfree(part->sector_map); 819 kfree(part->header_cache); 820 kfree(part->blocks); 821 } 822 823 static struct mtd_blktrans_ops rfd_ftl_tr = { 824 .name = "rfd", 825 .major = RFD_FTL_MAJOR, 826 .part_bits = PART_BITS, 827 .blksize = SECTOR_SIZE, 828 829 .readsect = rfd_ftl_readsect, 830 .writesect = rfd_ftl_writesect, 831 .getgeo = rfd_ftl_getgeo, 832 .add_mtd = rfd_ftl_add_mtd, 833 .remove_dev = rfd_ftl_remove_dev, 834 .owner = THIS_MODULE, 835 }; 836 837 static int __init init_rfd_ftl(void) 838 { 839 return register_mtd_blktrans(&rfd_ftl_tr); 840 } 841 842 static void __exit cleanup_rfd_ftl(void) 843 { 844 deregister_mtd_blktrans(&rfd_ftl_tr); 845 } 846 847 module_init(init_rfd_ftl); 848 module_exit(cleanup_rfd_ftl); 849 850 MODULE_LICENSE("GPL"); 851 MODULE_AUTHOR("Sean Young <sean@mess.org>"); 852 MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, " 853 "used by General Software's Embedded BIOS"); 854 855