Blame view
drivers/mtd/ftl.c
30.6 KB
1da177e4c Linux-2.6.12-rc2 |
1 |
/* This version ported to the Linux-MTD system by dwmw2@infradead.org |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
* * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups * * Based on: */ /*====================================================================== A Flash Translation Layer memory card driver This driver implements a disk-like block device driver with an apparent block size of 512 bytes for flash memory cards. ftl_cs.c 1.62 2000/02/01 00:59:04 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The initial developer of the original code is David A. Hinds <dahinds@users.sourceforge.net>. Portions created by David A. Hinds |
a1452a377 mtd: Update copyr... |
29 |
are Copyright © 1999 David A. Hinds. All Rights Reserved. |
1da177e4c Linux-2.6.12-rc2 |
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
Alternatively, the contents of this file may be used under the terms of the GNU General Public License version 2 (the "GPL"), in which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL. LEGAL NOTE: The FTL format is patented by M-Systems. They have granted a license for its use with PCMCIA devices: "M-Systems grants a royalty-free, non-exclusive license under any presently existing M-Systems intellectual property rights necessary for the design and development of FTL-compatible drivers, file systems and utilities using the data formats with PCMCIA PC Cards as described in the PCMCIA Flash Translation Layer (FTL) Specification." Use of the FTL format for non-PCMCIA applications may be an infringement of these patents. For additional information, |
631dd1a88 Update broken web... |
54 |
contact M-Systems directly. M-Systems since acquired by Sandisk. |
97894cda5 [MTD] core: Clean... |
55 |
|
1da177e4c Linux-2.6.12-rc2 |
56 57 58 59 60 61 62 |
======================================================================*/ #include <linux/mtd/blktrans.h> #include <linux/module.h> #include <linux/mtd/mtd.h> /*#define PSYCHO_DEBUG */ #include <linux/kernel.h> |
1da177e4c Linux-2.6.12-rc2 |
63 64 65 66 67 68 69 70 71 72 |
#include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/major.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/hdreg.h> #include <linux/vmalloc.h> #include <linux/blkpg.h> |
7c0f6ba68 Replace <asm/uacc... |
73 |
#include <linux/uaccess.h> |
1da177e4c Linux-2.6.12-rc2 |
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
#include <linux/mtd/ftl.h> /*====================================================================*/ /* Parameters that can be set with 'insmod' */ static int shuffle_freq = 50; module_param(shuffle_freq, int, 0); /*====================================================================*/ /* Major device # for FTL device */ #ifndef FTL_MAJOR #define FTL_MAJOR 44 #endif /*====================================================================*/ /* Maximum number of separate memory devices we'll allow */ #define MAX_DEV 4 /* Maximum number of regions per device */ #define MAX_REGION 4 /* Maximum number of partitions in an FTL region */ #define PART_BITS 4 /* Maximum number of outstanding erase requests per socket */ #define MAX_ERASE 8 /* Sector size -- shouldn't need to change */ #define SECTOR_SIZE 512 /* Each memory region corresponds to a minor device */ typedef struct partition_t { struct mtd_blktrans_dev mbd; |
3854be771 [MTD] Remove stra... |
112 113 |
uint32_t state; uint32_t *VirtualBlockMap; |
3854be771 [MTD] Remove stra... |
114 |
uint32_t FreeTotal; |
1da177e4c Linux-2.6.12-rc2 |
115 |
struct eun_info_t { |
3854be771 [MTD] Remove stra... |
116 117 118 119 |
uint32_t Offset; uint32_t EraseCount; uint32_t Free; uint32_t Deleted; |
1da177e4c Linux-2.6.12-rc2 |
120 121 |
} *EUNInfo; struct xfer_info_t { |
3854be771 [MTD] Remove stra... |
122 123 124 |
uint32_t Offset; uint32_t EraseCount; uint16_t state; |
1da177e4c Linux-2.6.12-rc2 |
125 |
} *XferInfo; |
3854be771 [MTD] Remove stra... |
126 127 128 129 |
uint16_t bam_index; uint32_t *bam_cache; uint16_t DataUnits; uint32_t BlocksPerUnit; |
1da177e4c Linux-2.6.12-rc2 |
130 |
erase_unit_header_t header; |
1da177e4c Linux-2.6.12-rc2 |
131 |
} partition_t; |
1da177e4c Linux-2.6.12-rc2 |
132 133 134 135 136 137 138 139 140 |
/* Partition state flags */ #define FTL_FORMATTED 0x01 /* Transfer unit states */ #define XFER_UNKNOWN 0x00 #define XFER_ERASING 0x01 #define XFER_ERASED 0x02 #define XFER_PREPARED 0x03 #define XFER_FAILED 0x04 |
1da177e4c Linux-2.6.12-rc2 |
141 142 143 144 145 |
/*====================================================================== Scan_header() checks to see if a memory region contains an FTL partition. build_maps() reads all the erase unit headers, builds the erase unit map, and then builds the virtual page map. |
97894cda5 [MTD] core: Clean... |
146 |
|
1da177e4c Linux-2.6.12-rc2 |
147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
======================================================================*/ static int scan_header(partition_t *part) { erase_unit_header_t header; loff_t offset, max_offset; size_t ret; int err; part->header.FormattedSize = 0; max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size; /* Search first megabyte for a valid FTL header */ for (offset = 0; (offset + sizeof(header)) < max_offset; offset += part->mbd.mtd->erasesize ? : 0x2000) { |
329ad399a mtd: introduce mt... |
161 162 |
err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret, (unsigned char *)&header); |
97894cda5 [MTD] core: Clean... |
163 164 |
if (err) |
1da177e4c Linux-2.6.12-rc2 |
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
return err; if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break; } if (offset == max_offset) { printk(KERN_NOTICE "ftl_cs: FTL header not found. "); return -ENOENT; } if (header.BlockSize != 9 || (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) || (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) { printk(KERN_NOTICE "ftl_cs: FTL header corrupt! "); return -1; } if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) { printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x ", 1 << header.EraseUnitSize,part->mbd.mtd->erasesize); return -1; } part->header = header; return 0; } static int build_maps(partition_t *part) { erase_unit_header_t header; |
3854be771 [MTD] Remove stra... |
195 196 |
uint16_t xvalid, xtrans, i; unsigned blocks, j; |
1da177e4c Linux-2.6.12-rc2 |
197 198 199 200 201 202 203 |
int hdr_ok, ret = -1; ssize_t retval; loff_t offset; /* Set up erase unit maps */ part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) - part->header.NumTransferUnits; |
6da2ec560 treewide: kmalloc... |
204 205 |
part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
206 207 208 209 210 |
if (!part->EUNInfo) goto out; for (i = 0; i < part->DataUnits; i++) part->EUNInfo[i].Offset = 0xffffffff; part->XferInfo = |
6da2ec560 treewide: kmalloc... |
211 212 213 |
kmalloc_array(part->header.NumTransferUnits, sizeof(struct xfer_info_t), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
214 215 216 217 218 219 220 |
if (!part->XferInfo) goto out_EUNInfo; xvalid = xtrans = 0; for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) << part->header.EraseUnitSize); |
329ad399a mtd: introduce mt... |
221 222 |
ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval, (unsigned char *)&header); |
97894cda5 [MTD] core: Clean... |
223 224 |
if (ret) |
1da177e4c Linux-2.6.12-rc2 |
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
goto out_XferInfo; ret = -1; /* Is this a transfer partition? */ hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0); if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) && (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) { part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset; part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount = le32_to_cpu(header.EraseCount); xvalid++; } else { if (xtrans == part->header.NumTransferUnits) { printk(KERN_NOTICE "ftl_cs: format error: too many " "transfer units! "); goto out_XferInfo; } if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) { part->XferInfo[xtrans].state = XFER_PREPARED; part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount); } else { part->XferInfo[xtrans].state = XFER_UNKNOWN; /* Pick anything reasonable for the erase count */ part->XferInfo[xtrans].EraseCount = le32_to_cpu(part->header.EraseCount); } part->XferInfo[xtrans].Offset = offset; xtrans++; } } /* Check for format trouble */ header = part->header; if ((xtrans != header.NumTransferUnits) || (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) { printk(KERN_NOTICE "ftl_cs: format error: erase units " "don't add up! "); goto out_XferInfo; } |
97894cda5 [MTD] core: Clean... |
265 |
|
1da177e4c Linux-2.6.12-rc2 |
266 267 |
/* Set up virtual page map */ blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize; |
42bc47b35 treewide: Use arr... |
268 |
part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t))); |
1da177e4c Linux-2.6.12-rc2 |
269 270 |
if (!part->VirtualBlockMap) goto out_XferInfo; |
3854be771 [MTD] Remove stra... |
271 |
memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t)); |
1da177e4c Linux-2.6.12-rc2 |
272 |
part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; |
6da2ec560 treewide: kmalloc... |
273 274 |
part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
275 276 277 278 279 280 281 282 283 284 |
if (!part->bam_cache) goto out_VirtualBlockMap; part->bam_index = 0xffff; part->FreeTotal = 0; for (i = 0; i < part->DataUnits; i++) { part->EUNInfo[i].Free = 0; part->EUNInfo[i].Deleted = 0; offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); |
97894cda5 [MTD] core: Clean... |
285 |
|
329ad399a mtd: introduce mt... |
286 287 288 |
ret = mtd_read(part->mbd.mtd, offset, part->BlocksPerUnit * sizeof(uint32_t), &retval, (unsigned char *)part->bam_cache); |
97894cda5 [MTD] core: Clean... |
289 290 |
if (ret) |
1da177e4c Linux-2.6.12-rc2 |
291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
goto out_bam_cache; for (j = 0; j < part->BlocksPerUnit; j++) { if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) { part->EUNInfo[i].Free++; part->FreeTotal++; } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) && (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks)) part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] = (i << header.EraseUnitSize) + (j << header.BlockSize); else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j]))) part->EUNInfo[i].Deleted++; } } |
97894cda5 [MTD] core: Clean... |
305 |
|
1da177e4c Linux-2.6.12-rc2 |
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
ret = 0; goto out; out_bam_cache: kfree(part->bam_cache); out_VirtualBlockMap: vfree(part->VirtualBlockMap); out_XferInfo: kfree(part->XferInfo); out_EUNInfo: kfree(part->EUNInfo); out: return ret; } /* build_maps */ /*====================================================================== Erase_xfer() schedules an asynchronous erase operation for a transfer unit. |
97894cda5 [MTD] core: Clean... |
325 |
|
1da177e4c Linux-2.6.12-rc2 |
326 327 328 |
======================================================================*/ static int erase_xfer(partition_t *part, |
3854be771 [MTD] Remove stra... |
329 |
uint16_t xfernum) |
1da177e4c Linux-2.6.12-rc2 |
330 331 332 333 334 335 |
{ int ret; struct xfer_info_t *xfer; struct erase_info *erase; xfer = &part->XferInfo[xfernum]; |
289c05222 mtd: replace DEBU... |
336 337 |
pr_debug("ftl_cs: erasing xfer unit at 0x%x ", xfer->Offset); |
1da177e4c Linux-2.6.12-rc2 |
338 339 340 |
xfer->state = XFER_ERASING; /* Is there a free erase slot? Always in MTD. */ |
97894cda5 [MTD] core: Clean... |
341 |
|
1da177e4c Linux-2.6.12-rc2 |
342 |
erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL); |
97894cda5 [MTD] core: Clean... |
343 |
if (!erase) |
1da177e4c Linux-2.6.12-rc2 |
344 |
return -ENOMEM; |
1da177e4c Linux-2.6.12-rc2 |
345 346 |
erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; |
97894cda5 [MTD] core: Clean... |
347 |
|
7e1f0dc05 mtd: introduce mt... |
348 |
ret = mtd_erase(part->mbd.mtd, erase); |
884cfd902 mtd: Stop assumin... |
349 350 351 352 353 354 355 356 |
if (!ret) { xfer->state = XFER_ERASED; xfer->EraseCount++; } else { xfer->state = XFER_FAILED; pr_notice("ftl_cs: erase failed: err = %d ", ret); } |
1da177e4c Linux-2.6.12-rc2 |
357 |
|
884cfd902 mtd: Stop assumin... |
358 |
kfree(erase); |
1da177e4c Linux-2.6.12-rc2 |
359 360 361 362 363 364 365 366 |
return ret; } /* erase_xfer */ /*====================================================================== Prepare_xfer() takes a freshly erased transfer unit and gives it an appropriate header. |
97894cda5 [MTD] core: Clean... |
367 |
|
1da177e4c Linux-2.6.12-rc2 |
368 |
======================================================================*/ |
1da177e4c Linux-2.6.12-rc2 |
369 370 371 372 373 |
static int prepare_xfer(partition_t *part, int i) { erase_unit_header_t header; struct xfer_info_t *xfer; int nbam, ret; |
3854be771 [MTD] Remove stra... |
374 |
uint32_t ctl; |
1da177e4c Linux-2.6.12-rc2 |
375 376 377 378 379 |
ssize_t retlen; loff_t offset; xfer = &part->XferInfo[i]; xfer->state = XFER_FAILED; |
97894cda5 [MTD] core: Clean... |
380 |
|
289c05222 mtd: replace DEBU... |
381 382 |
pr_debug("ftl_cs: preparing xfer unit at 0x%x ", xfer->Offset); |
1da177e4c Linux-2.6.12-rc2 |
383 384 385 386 387 |
/* Write the transfer unit header */ header = part->header; header.LogicalEUN = cpu_to_le16(0xffff); header.EraseCount = cpu_to_le32(xfer->EraseCount); |
eda95cbf7 mtd: introduce mt... |
388 389 |
ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen, (u_char *)&header); |
1da177e4c Linux-2.6.12-rc2 |
390 391 392 393 394 395 |
if (ret) { return ret; } /* Write the BAM stub */ |
b756816da mtd: ftl: Use DIV... |
396 397 |
nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) + le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE); |
1da177e4c Linux-2.6.12-rc2 |
398 399 400 |
offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset); ctl = cpu_to_le32(BLOCK_CONTROL); |
3854be771 [MTD] Remove stra... |
401 |
for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) { |
1da177e4c Linux-2.6.12-rc2 |
402 |
|
eda95cbf7 mtd: introduce mt... |
403 404 |
ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, (u_char *)&ctl); |
1da177e4c Linux-2.6.12-rc2 |
405 406 407 408 409 410 |
if (ret) return ret; } xfer->state = XFER_PREPARED; return 0; |
97894cda5 [MTD] core: Clean... |
411 |
|
1da177e4c Linux-2.6.12-rc2 |
412 413 414 415 416 417 418 419 420 421 422 |
} /* prepare_xfer */ /*====================================================================== Copy_erase_unit() takes a full erase block and a transfer unit, copies everything to the transfer unit, then swaps the block pointers. All data blocks are copied to the corresponding blocks in the target unit, so the virtual block map does not need to be updated. |
97894cda5 [MTD] core: Clean... |
423 |
|
1da177e4c Linux-2.6.12-rc2 |
424 |
======================================================================*/ |
3854be771 [MTD] Remove stra... |
425 426 |
static int copy_erase_unit(partition_t *part, uint16_t srcunit, uint16_t xferunit) |
1da177e4c Linux-2.6.12-rc2 |
427 428 429 430 |
{ u_char buf[SECTOR_SIZE]; struct eun_info_t *eun; struct xfer_info_t *xfer; |
3854be771 [MTD] Remove stra... |
431 432 |
uint32_t src, dest, free, i; uint16_t unit; |
1da177e4c Linux-2.6.12-rc2 |
433 434 435 |
int ret; ssize_t retlen; loff_t offset; |
3854be771 [MTD] Remove stra... |
436 |
uint16_t srcunitswap = cpu_to_le16(srcunit); |
1da177e4c Linux-2.6.12-rc2 |
437 438 439 |
eun = &part->EUNInfo[srcunit]; xfer = &part->XferInfo[xferunit]; |
289c05222 mtd: replace DEBU... |
440 441 |
pr_debug("ftl_cs: copying block 0x%x to 0x%x ", |
1da177e4c Linux-2.6.12-rc2 |
442 |
eun->Offset, xfer->Offset); |
97894cda5 [MTD] core: Clean... |
443 |
|
1da177e4c Linux-2.6.12-rc2 |
444 445 446 447 |
/* Read current BAM */ if (part->bam_index != srcunit) { offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); |
329ad399a mtd: introduce mt... |
448 449 450 |
ret = mtd_read(part->mbd.mtd, offset, part->BlocksPerUnit * sizeof(uint32_t), &retlen, (u_char *)(part->bam_cache)); |
1da177e4c Linux-2.6.12-rc2 |
451 452 453 454 455 |
/* mark the cache bad, in case we get an error later */ part->bam_index = 0xffff; if (ret) { |
97894cda5 [MTD] core: Clean... |
456 457 |
printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()! "); |
1da177e4c Linux-2.6.12-rc2 |
458 459 460 |
return ret; } } |
97894cda5 [MTD] core: Clean... |
461 |
|
1da177e4c Linux-2.6.12-rc2 |
462 463 464 465 |
/* Write the LogicalEUN for the transfer unit */ xfer->state = XFER_UNKNOWN; offset = xfer->Offset + 20; /* Bad! */ unit = cpu_to_le16(0x7fff); |
eda95cbf7 mtd: introduce mt... |
466 467 |
ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen, (u_char *)&unit); |
97894cda5 [MTD] core: Clean... |
468 |
|
1da177e4c Linux-2.6.12-rc2 |
469 470 471 472 473 |
if (ret) { printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()! "); return ret; } |
97894cda5 [MTD] core: Clean... |
474 |
|
1da177e4c Linux-2.6.12-rc2 |
475 476 477 478 479 480 481 482 483 484 485 486 |
/* Copy all data blocks from source unit to transfer unit */ src = eun->Offset; dest = xfer->Offset; free = 0; ret = 0; for (i = 0; i < part->BlocksPerUnit; i++) { switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) { case BLOCK_CONTROL: /* This gets updated later */ break; case BLOCK_DATA: case BLOCK_REPLACEMENT: |
329ad399a mtd: introduce mt... |
487 488 |
ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen, (u_char *)buf); |
1da177e4c Linux-2.6.12-rc2 |
489 490 491 492 493 |
if (ret) { printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit "); return ret; } |
eda95cbf7 mtd: introduce mt... |
494 495 |
ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen, (u_char *)buf); |
1da177e4c Linux-2.6.12-rc2 |
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 |
if (ret) { printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit "); return ret; } break; default: /* All other blocks must be free */ part->bam_cache[i] = cpu_to_le32(0xffffffff); free++; break; } src += SECTOR_SIZE; dest += SECTOR_SIZE; } /* Write the BAM to the transfer unit */ |
eda95cbf7 mtd: introduce mt... |
514 515 516 517 518 |
ret = mtd_write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), part->BlocksPerUnit * sizeof(int32_t), &retlen, (u_char *)part->bam_cache); |
1da177e4c Linux-2.6.12-rc2 |
519 520 521 522 523 |
if (ret) { printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit "); return ret; } |
97894cda5 [MTD] core: Clean... |
524 |
|
1da177e4c Linux-2.6.12-rc2 |
525 |
/* All clear? Then update the LogicalEUN again */ |
eda95cbf7 mtd: introduce mt... |
526 527 |
ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t), &retlen, (u_char *)&srcunitswap); |
1da177e4c Linux-2.6.12-rc2 |
528 529 530 531 532 |
if (ret) { printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit "); return ret; |
97894cda5 [MTD] core: Clean... |
533 |
} |
1da177e4c Linux-2.6.12-rc2 |
534 |
/* Update the maps and usage stats*/ |
6166a76f5 mtd: ftl: use swa... |
535 536 |
swap(xfer->EraseCount, eun->EraseCount); swap(xfer->Offset, eun->Offset); |
1da177e4c Linux-2.6.12-rc2 |
537 538 539 540 |
part->FreeTotal -= eun->Free; part->FreeTotal += free; eun->Free = free; eun->Deleted = 0; |
97894cda5 [MTD] core: Clean... |
541 |
|
1da177e4c Linux-2.6.12-rc2 |
542 543 |
/* Now, the cache should be valid for the new block */ part->bam_index = srcunit; |
97894cda5 [MTD] core: Clean... |
544 |
|
1da177e4c Linux-2.6.12-rc2 |
545 546 547 548 549 550 551 552 553 554 555 556 557 558 |
return 0; } /* copy_erase_unit */ /*====================================================================== reclaim_block() picks a full erase unit and a transfer unit and then calls copy_erase_unit() to copy one to the other. Then, it schedules an erase on the expired block. What's a good way to decide which transfer unit and which erase unit to use? Beats me. My way is to always pick the transfer unit with the fewest erases, and usually pick the data unit with the most deleted blocks. But with a small probability, pick the oldest data unit instead. This means that we generally postpone |
92394b5c2 mtd: spelling fixes |
559 |
the next reclamation as long as possible, but shuffle static |
1da177e4c Linux-2.6.12-rc2 |
560 |
stuff around a bit for wear leveling. |
97894cda5 [MTD] core: Clean... |
561 |
|
1da177e4c Linux-2.6.12-rc2 |
562 563 564 565 |
======================================================================*/ static int reclaim_block(partition_t *part) { |
3854be771 [MTD] Remove stra... |
566 567 |
uint16_t i, eun, xfer; uint32_t best; |
1da177e4c Linux-2.6.12-rc2 |
568 |
int queued, ret; |
289c05222 mtd: replace DEBU... |
569 570 571 572 |
pr_debug("ftl_cs: reclaiming space... "); pr_debug("NumTransferUnits == %x ", part->header.NumTransferUnits); |
1da177e4c Linux-2.6.12-rc2 |
573 574 575 576 577 578 579 |
/* Pick the least erased transfer unit */ best = 0xffffffff; xfer = 0xffff; do { queued = 0; for (i = 0; i < part->header.NumTransferUnits; i++) { int n=0; if (part->XferInfo[i].state == XFER_UNKNOWN) { |
289c05222 mtd: replace DEBU... |
580 581 |
pr_debug("XferInfo[%d].state == XFER_UNKNOWN ",i); |
1da177e4c Linux-2.6.12-rc2 |
582 583 584 585 |
n=1; erase_xfer(part, i); } if (part->XferInfo[i].state == XFER_ERASING) { |
289c05222 mtd: replace DEBU... |
586 587 |
pr_debug("XferInfo[%d].state == XFER_ERASING ",i); |
1da177e4c Linux-2.6.12-rc2 |
588 589 590 591 |
n=1; queued = 1; } else if (part->XferInfo[i].state == XFER_ERASED) { |
289c05222 mtd: replace DEBU... |
592 593 |
pr_debug("XferInfo[%d].state == XFER_ERASED ",i); |
1da177e4c Linux-2.6.12-rc2 |
594 595 596 597 |
n=1; prepare_xfer(part, i); } if (part->XferInfo[i].state == XFER_PREPARED) { |
289c05222 mtd: replace DEBU... |
598 599 |
pr_debug("XferInfo[%d].state == XFER_PREPARED ",i); |
1da177e4c Linux-2.6.12-rc2 |
600 601 602 603 604 605 606 |
n=1; if (part->XferInfo[i].EraseCount <= best) { best = part->XferInfo[i].EraseCount; xfer = i; } } if (!n) |
289c05222 mtd: replace DEBU... |
607 608 |
pr_debug("XferInfo[%d].state == %x ",i, part->XferInfo[i].state); |
1da177e4c Linux-2.6.12-rc2 |
609 610 611 612 |
} if (xfer == 0xffff) { if (queued) { |
289c05222 mtd: replace DEBU... |
613 |
pr_debug("ftl_cs: waiting for transfer " |
1da177e4c Linux-2.6.12-rc2 |
614 615 |
"unit to be prepared... "); |
327cf2922 mtd: do not use m... |
616 |
mtd_sync(part->mbd.mtd); |
1da177e4c Linux-2.6.12-rc2 |
617 618 619 620 621 622 623 |
} else { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: reclaim failed: no " "suitable transfer units! "); else |
289c05222 mtd: replace DEBU... |
624 |
pr_debug("ftl_cs: reclaim failed: no " |
1da177e4c Linux-2.6.12-rc2 |
625 626 |
"suitable transfer units! "); |
97894cda5 [MTD] core: Clean... |
627 |
|
1da177e4c Linux-2.6.12-rc2 |
628 629 630 631 632 633 634 |
return -EIO; } } } while (xfer == 0xffff); eun = 0; if ((jiffies % shuffle_freq) == 0) { |
289c05222 mtd: replace DEBU... |
635 636 |
pr_debug("ftl_cs: recycling freshest block... "); |
1da177e4c Linux-2.6.12-rc2 |
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
best = 0xffffffff; for (i = 0; i < part->DataUnits; i++) if (part->EUNInfo[i].EraseCount <= best) { best = part->EUNInfo[i].EraseCount; eun = i; } } else { best = 0; for (i = 0; i < part->DataUnits; i++) if (part->EUNInfo[i].Deleted >= best) { best = part->EUNInfo[i].Deleted; eun = i; } if (best == 0) { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: reclaim failed: " "no free blocks! "); else |
289c05222 mtd: replace DEBU... |
657 |
pr_debug("ftl_cs: reclaim failed: " |
1da177e4c Linux-2.6.12-rc2 |
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
"no free blocks! "); return -EIO; } } ret = copy_erase_unit(part, eun, xfer); if (!ret) erase_xfer(part, xfer); else printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed! "); return ret; } /* reclaim_block */ /*====================================================================== Find_free() searches for a free block. If necessary, it updates the BAM cache for the erase unit containing the free block. It returns the block index -- the erase unit is just the currently cached unit. If there are no free blocks, it returns 0 -- this is never a valid data block because it contains the header. |
97894cda5 [MTD] core: Clean... |
680 |
|
1da177e4c Linux-2.6.12-rc2 |
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 |
======================================================================*/ #ifdef PSYCHO_DEBUG static void dump_lists(partition_t *part) { int i; printk(KERN_DEBUG "ftl_cs: Free total = %d ", part->FreeTotal); for (i = 0; i < part->DataUnits; i++) printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, " "%d deleted ", i, part->EUNInfo[i].Offset >> part->header.EraseUnitSize, part->EUNInfo[i].Free, part->EUNInfo[i].Deleted); } #endif |
3854be771 [MTD] Remove stra... |
697 |
static uint32_t find_free(partition_t *part) |
1da177e4c Linux-2.6.12-rc2 |
698 |
{ |
3854be771 [MTD] Remove stra... |
699 700 |
uint16_t stop, eun; uint32_t blk; |
1da177e4c Linux-2.6.12-rc2 |
701 702 |
size_t retlen; int ret; |
97894cda5 [MTD] core: Clean... |
703 |
|
1da177e4c Linux-2.6.12-rc2 |
704 705 706 707 708 709 710 711 712 713 714 |
/* Find an erase unit with some free space */ stop = (part->bam_index == 0xffff) ? 0 : part->bam_index; eun = stop; do { if (part->EUNInfo[eun].Free != 0) break; /* Wrap around at end of table */ if (++eun == part->DataUnits) eun = 0; } while (eun != stop); if (part->EUNInfo[eun].Free == 0) return 0; |
97894cda5 [MTD] core: Clean... |
715 |
|
1da177e4c Linux-2.6.12-rc2 |
716 717 718 719 |
/* Is this unit's BAM cached? */ if (eun != part->bam_index) { /* Invalidate cache */ part->bam_index = 0xffff; |
329ad399a mtd: introduce mt... |
720 721 722 723 724 |
ret = mtd_read(part->mbd.mtd, part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), part->BlocksPerUnit * sizeof(uint32_t), &retlen, (u_char *)(part->bam_cache)); |
97894cda5 [MTD] core: Clean... |
725 |
|
1da177e4c Linux-2.6.12-rc2 |
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 |
if (ret) { printk(KERN_WARNING"ftl: Error reading BAM in find_free "); return 0; } part->bam_index = eun; } /* Find a free block */ for (blk = 0; blk < part->BlocksPerUnit; blk++) if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break; if (blk == part->BlocksPerUnit) { #ifdef PSYCHO_DEBUG static int ne = 0; if (++ne == 1) dump_lists(part); #endif printk(KERN_NOTICE "ftl_cs: bad free list! "); return 0; } |
289c05222 mtd: replace DEBU... |
747 748 |
pr_debug("ftl_cs: found free block at %d in %d ", blk, eun); |
1da177e4c Linux-2.6.12-rc2 |
749 |
return blk; |
97894cda5 [MTD] core: Clean... |
750 |
|
1da177e4c Linux-2.6.12-rc2 |
751 752 753 754 755 756 |
} /* find_free */ /*====================================================================== Read a series of sectors from an FTL partition. |
97894cda5 [MTD] core: Clean... |
757 |
|
1da177e4c Linux-2.6.12-rc2 |
758 759 760 761 762 |
======================================================================*/ static int ftl_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks) { |
3854be771 [MTD] Remove stra... |
763 |
uint32_t log_addr, bsize; |
1da177e4c Linux-2.6.12-rc2 |
764 765 766 |
u_long i; int ret; size_t offset, retlen; |
97894cda5 [MTD] core: Clean... |
767 |
|
289c05222 mtd: replace DEBU... |
768 769 |
pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld) ", |
1da177e4c Linux-2.6.12-rc2 |
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 |
part, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition "); return -EIO; } bsize = 1 << part->header.EraseUnitSize; for (i = 0; i < nblocks; i++) { if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) { printk(KERN_NOTICE "ftl_cs: bad read offset "); return -EIO; } log_addr = part->VirtualBlockMap[sector+i]; if (log_addr == 0xffffffff) memset(buffer, 0, SECTOR_SIZE); else { offset = (part->EUNInfo[log_addr / bsize].Offset + (log_addr % bsize)); |
329ad399a mtd: introduce mt... |
790 791 |
ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, (u_char *)buffer); |
1da177e4c Linux-2.6.12-rc2 |
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 |
if (ret) { printk(KERN_WARNING "Error reading MTD device in ftl_read() "); return ret; } } buffer += SECTOR_SIZE; } return 0; } /* ftl_read */ /*====================================================================== Write a series of sectors to an FTL partition |
97894cda5 [MTD] core: Clean... |
807 |
|
1da177e4c Linux-2.6.12-rc2 |
808 |
======================================================================*/ |
3854be771 [MTD] Remove stra... |
809 810 |
static int set_bam_entry(partition_t *part, uint32_t log_addr, uint32_t virt_addr) |
1da177e4c Linux-2.6.12-rc2 |
811 |
{ |
3854be771 [MTD] Remove stra... |
812 |
uint32_t bsize, blk, le_virt_addr; |
1da177e4c Linux-2.6.12-rc2 |
813 |
#ifdef PSYCHO_DEBUG |
3854be771 [MTD] Remove stra... |
814 |
uint32_t old_addr; |
1da177e4c Linux-2.6.12-rc2 |
815 |
#endif |
3854be771 [MTD] Remove stra... |
816 |
uint16_t eun; |
1da177e4c Linux-2.6.12-rc2 |
817 818 |
int ret; size_t retlen, offset; |
289c05222 mtd: replace DEBU... |
819 820 |
pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x) ", |
1da177e4c Linux-2.6.12-rc2 |
821 822 823 824 |
part, log_addr, virt_addr); bsize = 1 << part->header.EraseUnitSize; eun = log_addr / bsize; blk = (log_addr % bsize) / SECTOR_SIZE; |
3854be771 [MTD] Remove stra... |
825 |
offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) + |
1da177e4c Linux-2.6.12-rc2 |
826 |
le32_to_cpu(part->header.BAMOffset)); |
97894cda5 [MTD] core: Clean... |
827 |
|
1da177e4c Linux-2.6.12-rc2 |
828 |
#ifdef PSYCHO_DEBUG |
329ad399a mtd: introduce mt... |
829 830 |
ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, (u_char *)&old_addr); |
1da177e4c Linux-2.6.12-rc2 |
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 |
if (ret) { printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d ",ret); return ret; } old_addr = le32_to_cpu(old_addr); if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) || ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) || (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency! "); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x" ", new = 0x%x ", log_addr, old_addr, virt_addr); } return -EIO; } #endif le_virt_addr = cpu_to_le32(virt_addr); if (part->bam_index == eun) { #ifdef PSYCHO_DEBUG if (le32_to_cpu(part->bam_cache[blk]) != old_addr) { static int ne = 0; if (++ne < 5) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() " "inconsistency! "); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache" " = 0x%x ", le32_to_cpu(part->bam_cache[blk]), old_addr); } return -EIO; } #endif part->bam_cache[blk] = le_virt_addr; } |
eda95cbf7 mtd: introduce mt... |
871 872 |
ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, (u_char *)&le_virt_addr); |
1da177e4c Linux-2.6.12-rc2 |
873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
if (ret) { printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed! "); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x ", log_addr, virt_addr); } return ret; } /* set_bam_entry */ static int ftl_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks) { |
3854be771 [MTD] Remove stra... |
887 |
uint32_t bsize, log_addr, virt_addr, old_addr, blk; |
1da177e4c Linux-2.6.12-rc2 |
888 889 890 |
u_long i; int ret; size_t retlen, offset; |
289c05222 mtd: replace DEBU... |
891 892 |
pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld) ", |
1da177e4c Linux-2.6.12-rc2 |
893 894 895 896 897 898 899 900 901 902 903 904 |
part, sector, nblocks); if (!(part->state & FTL_FORMATTED)) { printk(KERN_NOTICE "ftl_cs: bad partition "); return -EIO; } /* See if we need to reclaim space, before we start */ while (part->FreeTotal < nblocks) { ret = reclaim_block(part); if (ret) return ret; } |
97894cda5 [MTD] core: Clean... |
905 |
|
1da177e4c Linux-2.6.12-rc2 |
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 |
bsize = 1 << part->header.EraseUnitSize; virt_addr = sector * SECTOR_SIZE | BLOCK_DATA; for (i = 0; i < nblocks; i++) { if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) { printk(KERN_NOTICE "ftl_cs: bad write offset "); return -EIO; } /* Grab a free block */ blk = find_free(part); if (blk == 0) { static int ne = 0; if (++ne < 5) printk(KERN_NOTICE "ftl_cs: internal error: " "no free blocks! "); return -ENOSPC; } /* Tag the BAM entry, and write the new block */ log_addr = part->bam_index * bsize + blk * SECTOR_SIZE; part->EUNInfo[part->bam_index].Free--; part->FreeTotal--; |
97894cda5 [MTD] core: Clean... |
931 |
if (set_bam_entry(part, log_addr, 0xfffffffe)) |
1da177e4c Linux-2.6.12-rc2 |
932 933 934 935 |
return -EIO; part->EUNInfo[part->bam_index].Deleted++; offset = (part->EUNInfo[part->bam_index].Offset + blk * SECTOR_SIZE); |
eda95cbf7 mtd: introduce mt... |
936 |
ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer); |
1da177e4c Linux-2.6.12-rc2 |
937 938 939 940 941 942 943 944 945 946 |
if (ret) { printk(KERN_NOTICE "ftl_cs: block write failed! "); printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" " = 0x%x, Offset = 0x%zx ", log_addr, virt_addr, offset); return -EIO; } |
97894cda5 [MTD] core: Clean... |
947 |
|
1da177e4c Linux-2.6.12-rc2 |
948 949 950 951 952 953 954 955 956 957 958 959 960 961 |
/* Only delete the old entry when the new entry is ready */ old_addr = part->VirtualBlockMap[sector+i]; if (old_addr != 0xffffffff) { part->VirtualBlockMap[sector+i] = 0xffffffff; part->EUNInfo[old_addr/bsize].Deleted++; if (set_bam_entry(part, old_addr, 0)) return -EIO; } /* Finally, set up the new pointers */ if (set_bam_entry(part, log_addr, virt_addr)) return -EIO; part->VirtualBlockMap[sector+i] = log_addr; part->EUNInfo[part->bam_index].Deleted--; |
97894cda5 [MTD] core: Clean... |
962 |
|
1da177e4c Linux-2.6.12-rc2 |
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 |
buffer += SECTOR_SIZE; virt_addr += SECTOR_SIZE; } return 0; } /* ftl_write */ static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) { partition_t *part = (void *)dev; u_long sect; /* Sort of arbitrary: round size down to 4KiB boundary */ sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; geo->heads = 1; geo->sectors = 8; geo->cylinders = sect >> 3; return 0; } static int ftl_readsect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { return ftl_read((void *)dev, buf, block, 1); } static int ftl_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { return ftl_write((void *)dev, buf, block, 1); } |
fdc53971b Support 'discard ... |
995 996 997 998 999 |
static int ftl_discardsect(struct mtd_blktrans_dev *dev, unsigned long sector, unsigned nr_sects) { partition_t *part = (void *)dev; uint32_t bsize = 1 << part->header.EraseUnitSize; |
289c05222 mtd: replace DEBU... |
1000 1001 |
pr_debug("FTL erase sector %ld for %d sectors ", |
fdc53971b Support 'discard ... |
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 |
sector, nr_sects); while (nr_sects) { uint32_t old_addr = part->VirtualBlockMap[sector]; if (old_addr != 0xffffffff) { part->VirtualBlockMap[sector] = 0xffffffff; part->EUNInfo[old_addr/bsize].Deleted++; if (set_bam_entry(part, old_addr, 0)) return -EIO; } nr_sects--; sector++; } return 0; } |
1da177e4c Linux-2.6.12-rc2 |
1018 |
/*====================================================================*/ |
5ce45d500 [MTD] ftl.c: make... |
1019 |
static void ftl_freepart(partition_t *part) |
1da177e4c Linux-2.6.12-rc2 |
1020 |
{ |
1da177e4c Linux-2.6.12-rc2 |
1021 1022 |
vfree(part->VirtualBlockMap); part->VirtualBlockMap = NULL; |
1da177e4c Linux-2.6.12-rc2 |
1023 1024 |
kfree(part->EUNInfo); part->EUNInfo = NULL; |
1da177e4c Linux-2.6.12-rc2 |
1025 1026 |
kfree(part->XferInfo); part->XferInfo = NULL; |
1da177e4c Linux-2.6.12-rc2 |
1027 1028 |
kfree(part->bam_cache); part->bam_cache = NULL; |
1da177e4c Linux-2.6.12-rc2 |
1029 1030 1031 1032 1033 |
} /* ftl_freepart */ static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { partition_t *partition; |
95b93a0cd [MTD] replace kma... |
1034 |
partition = kzalloc(sizeof(partition_t), GFP_KERNEL); |
97894cda5 [MTD] core: Clean... |
1035 |
|
1da177e4c Linux-2.6.12-rc2 |
1036 1037 1038 1039 1040 |
if (!partition) { printk(KERN_WARNING "No memory to scan for FTL on %s ", mtd->name); return; |
97894cda5 [MTD] core: Clean... |
1041 |
} |
1da177e4c Linux-2.6.12-rc2 |
1042 |
|
1da177e4c Linux-2.6.12-rc2 |
1043 |
partition->mbd.mtd = mtd; |
97894cda5 [MTD] core: Clean... |
1044 |
if ((scan_header(partition) == 0) && |
1da177e4c Linux-2.6.12-rc2 |
1045 |
(build_maps(partition) == 0)) { |
97894cda5 [MTD] core: Clean... |
1046 |
|
1da177e4c Linux-2.6.12-rc2 |
1047 1048 1049 1050 1051 1052 1053 |
partition->state = FTL_FORMATTED; #ifdef PCMCIA_DEBUG printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition ", le32_to_cpu(partition->header.FormattedSize) >> 10); #endif partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; |
191876729 [MTD] Allow varia... |
1054 |
|
1da177e4c Linux-2.6.12-rc2 |
1055 1056 1057 1058 1059 |
partition->mbd.tr = tr; partition->mbd.devnum = -1; if (!add_mtd_blktrans_dev((void *)partition)) return; } |
1da177e4c Linux-2.6.12-rc2 |
1060 1061 1062 1063 1064 1065 1066 |
kfree(partition); } static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); ftl_freepart((partition_t *)dev); |
1da177e4c Linux-2.6.12-rc2 |
1067 |
} |
5ce45d500 [MTD] ftl.c: make... |
1068 |
static struct mtd_blktrans_ops ftl_tr = { |
1da177e4c Linux-2.6.12-rc2 |
1069 1070 1071 |
.name = "ftl", .major = FTL_MAJOR, .part_bits = PART_BITS, |
191876729 [MTD] Allow varia... |
1072 |
.blksize = SECTOR_SIZE, |
1da177e4c Linux-2.6.12-rc2 |
1073 1074 |
.readsect = ftl_readsect, .writesect = ftl_writesect, |
fdc53971b Support 'discard ... |
1075 |
.discard = ftl_discardsect, |
1da177e4c Linux-2.6.12-rc2 |
1076 1077 1078 1079 1080 |
.getgeo = ftl_getgeo, .add_mtd = ftl_add_mtd, .remove_dev = ftl_remove_dev, .owner = THIS_MODULE, }; |
627df23c6 trivial: mtd: add... |
1081 |
static int __init init_ftl(void) |
1da177e4c Linux-2.6.12-rc2 |
1082 |
{ |
1da177e4c Linux-2.6.12-rc2 |
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 |
return register_mtd_blktrans(&ftl_tr); } static void __exit cleanup_ftl(void) { deregister_mtd_blktrans(&ftl_tr); } module_init(init_ftl); module_exit(cleanup_ftl); MODULE_LICENSE("Dual MPL/GPL"); MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices"); |