Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

mtd: cc_ftl: New FTL driver for media players using China Chip firmware.

Tested on Dingoo A320: the FAT partition on the NAND is mounted successfully.
Read-only for now.
Not robust against bad FTL admin data yet: the driver won't crash, but it might return unnecessary I/O errors.

Squashed version of development done in jz-2.6.35 branch.
  • Loading branch information...
commit f2a4e73d3618fc5ff8d9872ef804e01481e5f182 1 parent 0542d5f
Maarten ter Huurne authored August 29, 2010
9  drivers/mtd/Kconfig
@@ -304,7 +304,6 @@ config SSFDC
304 304
 	  This enables read only access to SmartMedia formatted NAND
305 305
 	  flash. You can mount it with FAT file system.
306 306
 
307  
-
308 307
 config SM_FTL
309 308
 	tristate "SmartMedia/xD new translation layer"
310 309
 	depends on EXPERIMENTAL && BLOCK
@@ -320,6 +319,14 @@ config SM_FTL
320 319
 	  If you only need R/O access, you can use older R/O driver
321 320
 	  (CONFIG_SSFDC)
322 321
 
  322
+config CC_FTL
  323
+        tristate "China Chip Flash Translation Layer support"
  324
+	depends on BLOCK
  325
+	select MTD_BLKDEVS
  326
+	---help---
  327
+	  This provides support for the flash translation layer used by
  328
+	  media players that run firmware from China Chip.
  329
+
323 330
 config MTD_OOPS
324 331
 	tristate "Log panic/oops to an MTD buffer"
325 332
 	help
1  drivers/mtd/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
19 19
 obj-$(CONFIG_MTD_BLKDEVS)	+= mtd_blkdevs.o
20 20
 obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
21 21
 obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o
  22
+obj-$(CONFIG_CC_FTL)		+= cc_ftl.o
22 23
 obj-$(CONFIG_FTL)		+= ftl.o
23 24
 obj-$(CONFIG_NFTL)		+= nftl.o
24 25
 obj-$(CONFIG_INFTL)		+= inftl.o
251  drivers/mtd/cc_ftl.c
... ...
@@ -0,0 +1,251 @@
  1
+/*
  2
+ *  Copyright (C) 2010, Maarten ter Huurne <maarten@treewalker.org>
  3
+ *  Flash Translation Layer for media players using firmware from China Chip.
  4
+ *
  5
+ *  This initial implementation provides read-only access.
  6
+ *
  7
+ *  This program is free software; you can redistribute it and/or modify it
  8
+ *  under  the terms of the GNU General  Public License as published by the
  9
+ *  Free Software Foundation;  either version 2 of the License, or (at your
  10
+ *  option) any later version.
  11
+ *
  12
+ *  You should have received a copy of the GNU General Public License along
  13
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
  14
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
  15
+ *
  16
+ */
  17
+
  18
+#include <linux/errno.h>
  19
+#include <linux/hdreg.h>
  20
+#include <linux/init.h>
  21
+#include <linux/module.h>
  22
+#include <linux/mtd/blktrans.h>
  23
+#include <linux/mtd/mtd.h>
  24
+#include <linux/mtd/nand.h>
  25
+#include <linux/slab.h>
  26
+#include <linux/types.h>
  27
+#include <mtd/mtd-abi.h>
  28
+
  29
+
  30
+#define SECTOR_SIZE		512
  31
+
  32
+struct cc_ftl_partition {
  33
+	struct mtd_blktrans_dev mbd;
  34
+
  35
+	uint32_t *map;
  36
+};
  37
+
  38
+static int cc_ftl_readsect(struct mtd_blktrans_dev *dev, unsigned long block,
  39
+			   char *buffer)
  40
+{
  41
+	struct cc_ftl_partition *partition = (struct cc_ftl_partition *)dev;
  42
+	struct mtd_info *mtd = dev->mtd;
  43
+	uint64_t log_offs, phy_offs;
  44
+	uint32_t log_blk, phy_blk;
  45
+	size_t retlen;
  46
+	int ret;
  47
+
  48
+	/* Find physical location. */
  49
+	if (block >= dev->size)
  50
+		return -EIO;
  51
+	log_offs = ((uint64_t)block) * SECTOR_SIZE;
  52
+	log_blk = mtd_div_by_eb(log_offs, mtd);
  53
+	phy_blk = partition->map[log_blk];
  54
+	if (phy_blk == (uint32_t)-1)
  55
+		return -EIO;
  56
+	phy_offs = (uint64_t)phy_blk * mtd->erasesize;
  57
+	phy_offs += mtd_mod_by_eb(log_offs, mtd);
  58
+
  59
+	/* Read data. */
  60
+	ret = mtd->read(mtd, phy_offs, SECTOR_SIZE, &retlen, buffer);
  61
+	if (ret == -EUCLEAN) /* sector contains correctable errors */
  62
+		ret = 0;
  63
+	if (ret)
  64
+		return ret;
  65
+	if (retlen != SECTOR_SIZE)
  66
+		return -EIO;
  67
+
  68
+	return 0;
  69
+}
  70
+
  71
+uint32_t *cc_ftl_build_block_map(struct mtd_info *mtd)
  72
+{
  73
+	uint32_t num_blk = mtd_div_by_eb(mtd->size, mtd);
  74
+	uint32_t blk_found;
  75
+	uint32_t pages_per_blk = mtd_div_by_ws(mtd->erasesize, mtd);
  76
+	uint32_t phy_blk;
  77
+	uint8_t	oob_buf[mtd->oobsize];
  78
+	unsigned int seq_offs = mtd->oobsize - 4;
  79
+	uint32_t *map;
  80
+
  81
+	// TODO: Is it worth reading multiple oobs at once?
  82
+	//       Reading two will at least help against bit errors.
  83
+	struct mtd_oob_ops oob_ops = {
  84
+		.mode		= MTD_OOB_RAW,
  85
+		.len		= 0,
  86
+		.ooblen		= mtd->oobsize,
  87
+		.datbuf		= NULL,
  88
+		.oobbuf		= oob_buf,
  89
+	};
  90
+
  91
+	map = kmalloc(sizeof(uint32_t) * num_blk, GFP_KERNEL);
  92
+	if (!map)
  93
+		return NULL;
  94
+	memset(map, 0xFF, sizeof(uint32_t) * num_blk);
  95
+
  96
+	blk_found = 0;
  97
+	for (phy_blk = 0; phy_blk < num_blk; phy_blk++) {
  98
+		loff_t ofs = (loff_t)phy_blk << mtd->erasesize_shift;
  99
+		uint16_t signature;
  100
+		uint16_t last_page;
  101
+		uint32_t log_blk;
  102
+		int err;
  103
+
  104
+		if (mtd->block_isbad(mtd, ofs))
  105
+			continue;
  106
+		err = mtd->read_oob(mtd, ofs, &oob_ops);
  107
+		if (err)
  108
+			continue;
  109
+		signature = oob_buf[0] | ((uint16_t)oob_buf[1] << 8);
  110
+		if (signature != 0x00FF)
  111
+			continue;
  112
+		last_page = oob_buf[2] | ((uint16_t)oob_buf[3] << 8);
  113
+		if (last_page >= pages_per_blk)
  114
+			continue;
  115
+		log_blk = oob_buf[seq_offs] |
  116
+			  ((uint32_t)oob_buf[seq_offs + 1] << 8) |
  117
+			  ((uint32_t)oob_buf[seq_offs + 2] << 16) |
  118
+			  ((uint32_t)oob_buf[seq_offs + 3] << 24);
  119
+		if (log_blk == 0xFFFFFFFF)
  120
+			continue;
  121
+		if (log_blk >= num_blk) {
  122
+			printk(KERN_WARNING "physical block %d claims "
  123
+					"logical block %d which is beyond "
  124
+					"partition end %d\n",
  125
+					phy_blk, log_blk, num_blk
  126
+					);
  127
+			continue;
  128
+		}
  129
+		if (map[log_blk] != 0xFFFFFFFF) {
  130
+			// TODO: Version number might sort this out.
  131
+			printk(KERN_WARNING "physical block %d and %d both "
  132
+					"claim logical block %d\n",
  133
+					map[log_blk], phy_blk, log_blk
  134
+					);
  135
+			continue;
  136
+		}
  137
+		map[log_blk] = phy_blk;
  138
+		blk_found++;
  139
+	}
  140
+
  141
+	if (blk_found == 0) {
  142
+		kfree(map);
  143
+		return NULL;
  144
+	}
  145
+
  146
+	if (0) {
  147
+		uint32_t log_blk;
  148
+		for (log_blk = 0; log_blk < num_blk; log_blk++) {
  149
+			if (map[log_blk] != 0xFFFFFFFF) {
  150
+				printk("%04X:%04X ", log_blk, map[log_blk]);
  151
+			}
  152
+		}
  153
+		printk("\n");
  154
+	}
  155
+
  156
+	return map;
  157
+}
  158
+
  159
+static void cc_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
  160
+{
  161
+	struct cc_ftl_partition *partition;
  162
+	uint32_t *map;
  163
+
  164
+	/* Check for NAND first, so we know we can use the "chip" pointer. */
  165
+	if (mtd->type != MTD_NANDFLASH)
  166
+		return;
  167
+
  168
+	/* A bad block table is expected. */
  169
+	if (!mtd->block_isbad)
  170
+		return;
  171
+
  172
+	/* Erase size must be a power of two. */
  173
+	if (mtd->erasesize_shift == 0)
  174
+		return;
  175
+
  176
+	/* Erase size must be a multiple of sector size. */
  177
+	if ((mtd->erasesize & (SECTOR_SIZE - 1)) != 0)
  178
+		return;
  179
+
  180
+	/* Probably this translation layer can work with different oob sizes,
  181
+	 * but for now we only accept the layout it was tested with.
  182
+	 */
  183
+	if (mtd->oobsize != 128)
  184
+		return;
  185
+
  186
+	map = cc_ftl_build_block_map(mtd);
  187
+	if (!map)
  188
+		return;
  189
+
  190
+	partition = kzalloc(sizeof(struct cc_ftl_partition), GFP_KERNEL);
  191
+	if (!partition)
  192
+		goto err_map;
  193
+
  194
+	partition->mbd.mtd = mtd;
  195
+	// TODO: More reasonable guess.
  196
+	partition->mbd.size = mtd->size / SECTOR_SIZE;
  197
+	partition->mbd.tr = tr;
  198
+	partition->mbd.devnum = -1;
  199
+	partition->map = map;
  200
+
  201
+	if (add_mtd_blktrans_dev((struct mtd_blktrans_dev *)partition))
  202
+		goto err_partition;
  203
+
  204
+	return;
  205
+
  206
+err_partition:
  207
+	kfree(partition);
  208
+err_map:
  209
+	kfree(map);
  210
+}
  211
+
  212
+static void cc_ftl_remove_dev(struct mtd_blktrans_dev *dev)
  213
+{
  214
+	struct cc_ftl_partition *partition = (struct cc_ftl_partition *)dev;
  215
+
  216
+	if (del_mtd_blktrans_dev(dev))
  217
+		return;
  218
+
  219
+	kfree(partition->map);
  220
+	kfree(partition);
  221
+}
  222
+
  223
+static struct mtd_blktrans_ops cc_ftl_tr = {
  224
+	.name		= "ccnand",
  225
+	.major		= 242,	/* TODO: Register an official major number. */
  226
+	.part_bits	= 4,
  227
+	.blksize 	= SECTOR_SIZE,
  228
+
  229
+	.readsect	= cc_ftl_readsect,
  230
+	.add_mtd	= cc_ftl_add_mtd,
  231
+	.remove_dev	= cc_ftl_remove_dev,
  232
+
  233
+	.owner		= THIS_MODULE,
  234
+};
  235
+
  236
+static int __init cc_ftl_init(void)
  237
+{
  238
+	return register_mtd_blktrans(&cc_ftl_tr);
  239
+}
  240
+module_init(cc_ftl_init);
  241
+
  242
+static void __exit cc_ftl_exit(void)
  243
+{
  244
+	deregister_mtd_blktrans(&cc_ftl_tr);
  245
+}
  246
+module_exit(cc_ftl_exit);
  247
+
  248
+MODULE_LICENSE("GPL");
  249
+MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
  250
+MODULE_DESCRIPTION("Flash Translation Layer for media players "
  251
+		   "using firmware from China Chip");

0 notes on commit f2a4e73

Please sign in to comment.
Something went wrong with that request. Please try again.