Date:2010-08-29 06:00:55 (12 years 1 month ago)
Author:Maarten ter Huurne
Commit:39a59e17abea230aeeeae9d279a30760af67db2e
Message: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.
Files: drivers/mtd/Kconfig (2 diffs)
drivers/mtd/Makefile (1 diff)
drivers/mtd/cc_ftl.c (1 diff)

Change Details

drivers/mtd/Kconfig
304304      This enables read only access to SmartMedia formatted NAND
305305      flash. You can mount it with FAT file system.
306306
307
308307config SM_FTL
309308    tristate "SmartMedia/xD new translation layer"
310309    depends on EXPERIMENTAL && BLOCK
...... 
320319      If you only need R/O access, you can use older R/O driver
321320      (CONFIG_SSFDC)
322321
322config 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
323330config MTD_OOPS
324331    tristate "Log panic/oops to an MTD buffer"
325332    help
drivers/mtd/Makefile
1919obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
2020obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
2121obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o
22obj-$(CONFIG_CC_FTL) += cc_ftl.o
2223obj-$(CONFIG_FTL) += ftl.o
2324obj-$(CONFIG_NFTL) += nftl.o
2425obj-$(CONFIG_INFTL) += inftl.o
drivers/mtd/cc_ftl.c
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
32struct cc_ftl_partition {
33    struct mtd_blktrans_dev mbd;
34
35    uint32_t *map;
36};
37
38static 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
71uint32_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
159static 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
206err_partition:
207    kfree(partition);
208err_map:
209    kfree(map);
210}
211
212static 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
223static 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
236static int __init cc_ftl_init(void)
237{
238    return register_mtd_blktrans(&cc_ftl_tr);
239}
240module_init(cc_ftl_init);
241
242static void __exit cc_ftl_exit(void)
243{
244    deregister_mtd_blktrans(&cc_ftl_tr);
245}
246module_exit(cc_ftl_exit);
247
248MODULE_LICENSE("GPL");
249MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
250MODULE_DESCRIPTION("Flash Translation Layer for media players "
251           "using firmware from China Chip");

Archive Download the corresponding diff file



interactive