* FAT vs jFFS2 for NAND.
@ 2006-05-28 2:58 Han Chang
2006-05-28 5:54 ` Charles Manning
0 siblings, 1 reply; 16+ messages in thread
From: Han Chang @ 2006-05-28 2:58 UTC (permalink / raw)
To: linux-mtd
Hi there,
My first question is that if I can use FAT file system for NAND. If yes,
what are the pros and cons for using FAT vs. JFFS2.
Many thanks,
Han
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-05-28 2:58 FAT vs jFFS2 for NAND Han Chang
@ 2006-05-28 5:54 ` Charles Manning
2006-06-15 0:34 ` Han Chang
0 siblings, 1 reply; 16+ messages in thread
From: Charles Manning @ 2006-05-28 5:54 UTC (permalink / raw)
To: linux-mtd; +Cc: Han Chang
On Sunday 28 May 2006 14:58, Han Chang wrote:
> Hi there,
>
> My first question is that if I can use FAT file system for NAND. If yes,
> what are the pros and cons for using FAT vs. JFFS2.
FAT needs to work with a block driver, so you can use FAT if you use a block
driver on top of the NAND.
You can use JFFS2 or YAFFS as true flash file systems (ie that work directly
with the NAND).
Pros of FAT:
*) If you're using this on a device that must look like a USB mass storage
device, or similar, then FAT is easier for people to use.
*) Have a smaller RAM footprint than JFFS2 or YAFFS. JFFS2 and YAFFS both use
ram to build runtime look-up trees.
*) FAT file systems will typically mount faster than JFFS2 and YAFFS, though
both YAFFS and JFFS2 mount times have reduced significantly in recent weeks.
Pros of YAFFS or JFFS2:
*) Faster. YAFFS is faster than JFFS2 which should be faster than FAT.
*) YAFFS and JFFS2 are both log structured fs which make them far more robust
against corruption than FAT.
*) YAFFS abd JFFS2 support features like links which are missing from FAT.
-- Charles
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-05-28 5:54 ` Charles Manning
@ 2006-06-15 0:34 ` Han Chang
2006-06-15 7:53 ` Thomas Gleixner
0 siblings, 1 reply; 16+ messages in thread
From: Han Chang @ 2006-06-15 0:34 UTC (permalink / raw)
To: manningc2, linux-mtd
Thanks for the information. Now I started to make FAT work on NAND.
"mkdosfs" uses ioctl to check if the device is a floppy disk or hard disk,
but NAND is neither of these, so it fails. Is there any way to get around
this?
Should I do fdisk on the NAND device, if I can already create partition in
the driver initiation?
Thanks,
Han
>From: Charles Manning <manningc2@actrix.gen.nz>
>To: linux-mtd@lists.infradead.org
>CC: "Han Chang" <posaune@hotmail.com>
>Subject: Re: FAT vs jFFS2 for NAND.
>Date: Sun, 28 May 2006 17:54:26 +1200
>
>On Sunday 28 May 2006 14:58, Han Chang wrote:
> > Hi there,
> >
> > My first question is that if I can use FAT file system for NAND. If yes,
> > what are the pros and cons for using FAT vs. JFFS2.
>
>FAT needs to work with a block driver, so you can use FAT if you use a
>block
>driver on top of the NAND.
>
>You can use JFFS2 or YAFFS as true flash file systems (ie that work
>directly
>with the NAND).
>
>
>Pros of FAT:
>*) If you're using this on a device that must look like a USB mass storage
>device, or similar, then FAT is easier for people to use.
>*) Have a smaller RAM footprint than JFFS2 or YAFFS. JFFS2 and YAFFS both
>use
>ram to build runtime look-up trees.
>*) FAT file systems will typically mount faster than JFFS2 and YAFFS,
>though
>both YAFFS and JFFS2 mount times have reduced significantly in recent
>weeks.
>
>Pros of YAFFS or JFFS2:
>*) Faster. YAFFS is faster than JFFS2 which should be faster than FAT.
>*) YAFFS and JFFS2 are both log structured fs which make them far more
>robust
>against corruption than FAT.
>*) YAFFS abd JFFS2 support features like links which are missing from FAT.
>
>-- Charles
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-15 0:34 ` Han Chang
@ 2006-06-15 7:53 ` Thomas Gleixner
2006-06-19 18:31 ` Han Chang
0 siblings, 1 reply; 16+ messages in thread
From: Thomas Gleixner @ 2006-06-15 7:53 UTC (permalink / raw)
To: Han Chang; +Cc: manningc2, linux-mtd
On Wed, 2006-06-14 at 17:34 -0700, Han Chang wrote:
> Thanks for the information. Now I started to make FAT work on NAND.
> "mkdosfs" uses ioctl to check if the device is a floppy disk or hard disk,
> but NAND is neither of these, so it fails. Is there any way to get around
> this?
The only way which would make sense to a certain degree is to resurrect
the SmartMedia Format driver, which bitrots in the old MTD CVS. It is
designed to allow FAT on NAND FLASH, but I have no idea whats the
current status of that code is.
> Should I do fdisk on the NAND device, if I can already create partition in
> the driver initiation?
A parition does not transform NAND into a block device. You need to use
the block device driver of MTD. Be warned that you will wear out your
FLASH in foreseable time and data loss on powerfail is guaranteed by
design.
Is there any real good reason why you want to use FAT on a FLASH?
tglx
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-15 7:53 ` Thomas Gleixner
@ 2006-06-19 18:31 ` Han Chang
2006-06-19 18:38 ` Thomas Gleixner
` (2 more replies)
0 siblings, 3 replies; 16+ messages in thread
From: Han Chang @ 2006-06-19 18:31 UTC (permalink / raw)
To: tglx; +Cc: manningc2, linux-mtd
Thanks! The reason for using FAT on the NAND is when the device has the NAND
is connected to a PC via USB, it can appear to be storage device read by the
PC user directly.
Could you provide more details on how to get this SmartMedia Format driver?
Anyone know any other source base which I can leverage to do the job?
Thanks,
Han
>From: Thomas Gleixner <tglx@linutronix.de>
>Reply-To: tglx@linutronix.de
>To: Han Chang <posaune@hotmail.com>
>CC: manningc2@actrix.gen.nz, linux-mtd@lists.infradead.org
>Subject: Re: FAT vs jFFS2 for NAND.
>Date: Thu, 15 Jun 2006 09:53:13 +0200
>
>On Wed, 2006-06-14 at 17:34 -0700, Han Chang wrote:
> > Thanks for the information. Now I started to make FAT work on NAND.
> > "mkdosfs" uses ioctl to check if the device is a floppy disk or hard
>disk,
> > but NAND is neither of these, so it fails. Is there any way to get
>around
> > this?
>
>The only way which would make sense to a certain degree is to resurrect
>the SmartMedia Format driver, which bitrots in the old MTD CVS. It is
>designed to allow FAT on NAND FLASH, but I have no idea whats the
>current status of that code is.
>
> > Should I do fdisk on the NAND device, if I can already create partition
>in
> > the driver initiation?
>
>A parition does not transform NAND into a block device. You need to use
>the block device driver of MTD. Be warned that you will wear out your
>FLASH in foreseable time and data loss on powerfail is guaranteed by
>design.
>
>Is there any real good reason why you want to use FAT on a FLASH?
>
> tglx
>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-19 18:31 ` Han Chang
@ 2006-06-19 18:38 ` Thomas Gleixner
2006-06-19 20:23 ` David Woodhouse
2006-06-20 11:31 ` Claudio Lanconelli
2 siblings, 0 replies; 16+ messages in thread
From: Thomas Gleixner @ 2006-06-19 18:38 UTC (permalink / raw)
To: Han Chang; +Cc: manningc2, linux-mtd
On Mon, 2006-06-19 at 11:31 -0700, Han Chang wrote:
> Thanks! The reason for using FAT on the NAND is when the device has the NAND
> is connected to a PC via USB, it can appear to be storage device read by the
> PC user directly.
>
> Could you provide more details on how to get this SmartMedia Format driver?
Its in the old MTD CVS:
cvs -d :ext:anoncvs@cvs.infradead.org:/home/cvs co mtd
The driver is in drivers/mtd and probably needs some serious attention.
> Anyone know any other source base which I can leverage to do the job?
Not that I'm aware of.
tglx
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-19 18:31 ` Han Chang
2006-06-19 18:38 ` Thomas Gleixner
@ 2006-06-19 20:23 ` David Woodhouse
2006-06-19 21:10 ` Charles Manning
2006-06-20 11:31 ` Claudio Lanconelli
2 siblings, 1 reply; 16+ messages in thread
From: David Woodhouse @ 2006-06-19 20:23 UTC (permalink / raw)
To: Han Chang; +Cc: tglx, manningc2, linux-mtd
On Mon, 2006-06-19 at 11:31 -0700, Han Chang wrote:
> Thanks! The reason for using FAT on the NAND is when the device has the NAND
> is connected to a PC via USB, it can appear to be storage device read by the
> PC user directly.
Can't you use the 'PTP' USB protocol, which is designed for sharing
pictures? It can also share an arbitrary file system, I believe.
Failing that, SmartMedia makes a certain amount of sense. Use the code
from the CVS tree as a basis, or just do it yourself (using the
mtd_blkdevs helper stuff which we already use for nftl etc.)
--
dwmw2
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-19 20:23 ` David Woodhouse
@ 2006-06-19 21:10 ` Charles Manning
0 siblings, 0 replies; 16+ messages in thread
From: Charles Manning @ 2006-06-19 21:10 UTC (permalink / raw)
To: David Woodhouse; +Cc: Han Chang, tglx, linux-mtd
On Tuesday 20 June 2006 08:23, David Woodhouse wrote:
> On Mon, 2006-06-19 at 11:31 -0700, Han Chang wrote:
> > Thanks! The reason for using FAT on the NAND is when the device has the
> > NAND is connected to a PC via USB, it can appear to be storage device
> > read by the PC user directly.
>
> Can't you use the 'PTP' USB protocol, which is designed for sharing
> pictures? It can also share an arbitrary file system, I believe.
I looked at PTP a while back. This is very picture centric and is no use for
general file transfer.
There's also a newer (and as-yet unratified) protocol that allows transfers of
other files. These allow transfer of other file types, but still don't
support a full fs.
A USB-ftp would be a GoodThing.
>
> Failing that, SmartMedia makes a certain amount of sense. Use the code
> from the CVS tree as a basis, or just do it yourself (using the
> mtd_blkdevs helper stuff which we already use for nftl etc.)
Unfortiunately USB Mass Storage (with a Windows host) pretty much forces the
use of FAT on top of a block driver. The SmartMedia block driver model is
suffieciently robust (FAT corruptions are far more likely to cause failures).
-- CHarles
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-19 18:31 ` Han Chang
2006-06-19 18:38 ` Thomas Gleixner
2006-06-19 20:23 ` David Woodhouse
@ 2006-06-20 11:31 ` Claudio Lanconelli
2006-06-20 12:30 ` David Woodhouse
2 siblings, 1 reply; 16+ messages in thread
From: Claudio Lanconelli @ 2006-06-20 11:31 UTC (permalink / raw)
To: linux-mtd
[-- Attachment #1: Type: text/plain, Size: 537 bytes --]
Han Chang wrote:
> Thanks! The reason for using FAT on the NAND is when the device has the NAND
> is connected to a PC via USB, it can appear to be storage device read by the
> PC user directly.
>
> Could you provide more details on how to get this SmartMedia Format driver?
>
> Anyone know any other source base which I can leverage to do the job?
>
Hi,
I made some time ago a Smartmedia read only layer for MTD, since I can't
make the current ssfdc.c working.
I attach it if you want to give a look.
Cheers,
Claudio Lanconelli
[-- Attachment #2: ssfdc_ro.c --]
[-- Type: text/x-csrc, Size: 13071 bytes --]
/*
* Linux driver for SSFDC Flash Translation Layer (Read only)
* (c) 2005 Eptar srl
* Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
*
* Based on NTFL and MTDBLOCK_RO drivers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* $Id: ssfdc_ro.c,v 1.5 2005/11/28 13:54:08 claudio Exp $
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/hdreg.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/blktrans.h>
#undef ENABLE_GETGEO
#undef DEBUG
#define DEBUG(n, args...) \
do { \
if (n <= SSFDC_RO_DEBUG_VERBOSE) \
printk(KERN_INFO args); \
} while(0)
#define SSFDC_RO_DEBUG_VERBOSE 0
struct ssfdcr_record {
struct mtd_blktrans_dev mbd;
int usecount;
#ifdef ENABLE_GETGEO
unsigned char heads;
unsigned char sectors;
unsigned short cylinders;
#endif
int cis_block; //block n. containing CIS/IDI
int erase_size; //phys_block_size
unsigned short *logic_block_map; //all zones (max 8192 phys blocks on the 128MB)
int map_len; //n. phys_blocks on the card
};
static const struct nand_oobinfo ssfdc_oobinfo = {
.useecc = MTD_NANDECC_PLACEONLY,
.eccbytes = 6,
.eccpos = {14, 13, 15, 9, 8, 10}
};
#define SSFDCR_MAJOR 44
#define SSFDCR_PARTN_BITS 3
#define SECTOR_SIZE 512
#define SECTOR_SHIFT 9
#define OOB_SIZE 16
#define MAX_LOGIC_BLK_PER_ZONE 1000
#define MAX_PHYS_BLK_PER_ZONE 1024
#define ArraySize(x) ( sizeof(x) / sizeof((x)[0]) )
#define KB(x) ( (x) * 1024L )
#define MB(x) ( KB(x) * 1024L )
/** CHS Table
1MB 2MB 4MB 8MB 16MB 32MB 64MB 128MB
NCylinder 125 125 250 250 500 500 500 500
NHead 4 4 4 4 4 8 8 16
NSector 4 8 8 16 16 16 32 32
SumSector 2,000 4,000 8,000 16,000 32,000 64,000 128,000 256,000
SectorSize 512 512 512 512 512 512 512 512
**/
#ifdef ENABLE_GETGEO
typedef struct {
unsigned long size;
unsigned short cyl;
unsigned char head;
unsigned char sec;
} chs_entry_t;
//Must be ordered by size
static const chs_entry_t chs_table[] = {
{ MB( 1), 125, 4, 4 },
{ MB( 2), 125, 4, 8 },
{ MB( 4), 250, 4, 8 },
{ MB( 8), 250, 4, 16 },
{ MB( 16), 500, 4, 16 },
{ MB( 32), 500, 8, 16 },
{ MB( 64), 500, 8, 32 },
{ MB(128), 500, 16, 32 },
{ 0 },
};
static int get_chs(unsigned long size, unsigned short *cyl, unsigned char *head, unsigned char *sec)
{
int k;
int found = 0;
k = 0;
while ( chs_table[k].size > 0 && size > chs_table[k].size )
k++;
if ( chs_table[k].size > 0 )
{
if (cyl)
*cyl = chs_table[k].cyl;
if (head)
*head = chs_table[k].head;
if (sec)
*sec = chs_table[k].sec;
found = 1;
}
return found;
}
#endif
static const unsigned char nibble_count_bits[16] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4
};
//Counts bit 1 in a byte. Use look up table to speed up count
static int byte_count_bits(unsigned char val)
{
return nibble_count_bits[val >> 4] + nibble_count_bits[val & 0x0f];
}
static const unsigned char cis_numbers[] = {
0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
};
#define OOB_BLOCKSTATUS_OFFSET 5
#define block_is_bad(x) ( byte_count_bits(x) < 7 )
#define block_is_good(x) ( !block_is_bad(x) )
//Read and check for a valid CIS sector
static int get_valid_cis_sector(struct mtd_info *mtd)
{
int ret, k, cis_sector;
size_t retlen;
loff_t offset;
unsigned char sect_buf[SECTOR_SIZE];
unsigned char oob_buf[OOB_SIZE];
//Look for CIS/IDI sector on the first GOOD block (give up after 4 bad blocks)
//If the first good block doesn't contain CIS number the flash is not SSFDC formatted
cis_sector = -1;
for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize)
{
ret = MTD_READOOB(mtd, offset, OOB_SIZE, &retlen, oob_buf);
if ( ret < 0 || retlen != OOB_SIZE )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: can't read OOB data on sector %d\n",
(int)(offset >> SECTOR_SHIFT));
break;
}
if ( block_is_good( oob_buf[OOB_BLOCKSTATUS_OFFSET] ) )
{
ret = MTD_READ(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
if ( ret < 0 || retlen != SECTOR_SIZE )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: can't read CIS/IDI sector\n");
}
else
if ( !memcmp(sect_buf, cis_numbers, sizeof(cis_numbers)) ) //CIS pattern matching on the sector buffer
{
cis_sector = (int)(offset >> SECTOR_SHIFT); //Found
}
else
{
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: CIS/IDI sector not found on %s (mtd%d)\n",
mtd->name, mtd->index);
}
break;
}
}
return cis_sector;
}
//Read physical sector (just a wrapper to MTD_READ)
static int read_physical_sector(struct mtd_info *mtd, unsigned char *sect_buf, int sect_no)
{
int ret;
size_t retlen;
loff_t offset = (loff_t)sect_no << SECTOR_SHIFT;
ret = MTD_READ(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
if ( ret < 0 || retlen != SECTOR_SIZE )
return -1;
return 0;
}
//Parity calculator on a word of n bit size
static int get_parity(int number, int size)
{
int k;
int parity;
parity = 1;
for (k = 0; k < size; k++)
{
parity += (number >> k);
parity &= 1;
}
return parity;
}
//Read and validate the logical block address field stored in the OOB
static int get_logical_address(unsigned char oob_buf[OOB_SIZE])
{
int block_address, parity;
int offset[2] = {6, 11}; //offset of the two address fields within OOB
int j;
int ok = 0;
//First we check for good block
if ( block_is_bad(oob_buf[OOB_BLOCKSTATUS_OFFSET]) )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: get_logical_address() Bad block\n");
return -1; //Bad block
}
//Look for the first valid logical address
//Valid address has fixed pattern on most significant bits and parity check
for (j = 0; j < ArraySize(offset); j++)
{
block_address = ((int)oob_buf[offset[j]] << 8) | oob_buf[offset[j]+1];
//Check for the signature bits in the address field (most significant bits)
if( (block_address & ~0x7FF) == 0x1000 )
{
parity = block_address & 0x01;
block_address &= 0x7FF;
block_address >>= 1;
if( get_parity(block_address, 10) != parity )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: logical address field%d parity error (0x%04X)\n", j+1, block_address);
}
else
{
ok = 1;
break;
}
}
}
if ( !ok )
block_address = -2;
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: get_logical_address() %d\n", block_address);
return block_address;
}
//Build the logic block map
static int build_logical_block_map(struct ssfdcr_record *ssfdc)
{
unsigned long offset;
unsigned char oob_buf[OOB_SIZE];
int ret, block_address, phys_block;
size_t retlen;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: build_block_map() n.blocks = %d (%luK)\n",
ssfdc->map_len, (unsigned long)ssfdc->map_len * ssfdc->erase_size / 1024 );
//Scan every physical block, skip CIS block
for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len; phys_block++)
{
offset = (unsigned long)phys_block * ssfdc->erase_size;
ret = MTD_READOOB(ssfdc->mbd.mtd, offset, OOB_SIZE, &retlen, oob_buf);
if ( ret < 0 || retlen != OOB_SIZE )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: mtd read_oob() failed at %lu\n", offset);
return -1;
}
block_address = get_logical_address(oob_buf);
//Skip bad blocks and invalid addresses
if ( block_address >= 0 && block_address < MAX_LOGIC_BLK_PER_ZONE )
{
int zone_index;
zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE;
block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE;
ssfdc->logic_block_map[block_address] = (unsigned short)phys_block;
DEBUG(MTD_DEBUG_LEVEL2, "SSFDC_RO: build_block_map() phys_block=%d, logic_block_addr=%d, zone=%d\n",
phys_block, block_address, zone_index);
}
}
return 0;
}
static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
struct ssfdcr_record *ssfdc;
int cis_sector;
//Check for NAND flash
if (mtd->type != MTD_NANDFLASH)
return;
//Check for SSDFC format by reading CIS/IDI sector
cis_sector = get_valid_cis_sector(mtd);
if ( cis_sector == -1 )
return;
ssfdc = kmalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
if ( !ssfdc )
{
printk(KERN_WARNING "SSFDC_RO: out of memory for data structures\n");
return;
}
memset(ssfdc, 0, sizeof(*ssfdc));
ssfdc->mbd.mtd = mtd;
ssfdc->mbd.devnum = -1;
ssfdc->mbd.blksize = SECTOR_SIZE;
ssfdc->mbd.tr = tr;
ssfdc->mbd.readonly = 1;
ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
ssfdc->erase_size = mtd->erasesize;
ssfdc->map_len = mtd->size / mtd->erasesize;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: cis_block=%d, erase_size=%d, map_len=%d, n_zones=%d\n",
ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
(ssfdc->map_len + MAX_PHYS_BLK_PER_ZONE - 1) / MAX_PHYS_BLK_PER_ZONE);
#ifdef ENABLE_GETGEO
// Set geometry
get_chs( mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) /
((long)ssfdc->sectors * (long)ssfdc->heads));
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
(long)ssfdc->cylinders * (long)ssfdc->heads *
(long)ssfdc->sectors );
ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders * (long)ssfdc->sectors;
#else
ssfdc->mbd.size = ssfdc->map_len * (ssfdc->erase_size >> SECTOR_SHIFT);
#endif
//Allocate logical block map
ssfdc->logic_block_map = kmalloc( sizeof(ssfdc->logic_block_map[0]) * ssfdc->map_len, GFP_KERNEL);
if (!ssfdc->logic_block_map)
{
printk(KERN_WARNING "SSFDC_RO: out of memory for data structures\n");
kfree(ssfdc);
return;
}
memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) * ssfdc->map_len);
//Build logical block map
if( build_logical_block_map(ssfdc) < 0 )
{
if ( ssfdc->logic_block_map )
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
return;
}
//Register device + partitions
if (add_mtd_blktrans_dev(&ssfdc->mbd))
{
if ( ssfdc->logic_block_map )
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
return;
}
printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n",
ssfdc->mbd.devnum + 'a', mtd->index, mtd->name);
}
static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
del_mtd_blktrans_dev(dev);
if (ssfdc->logic_block_map)
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
}
static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, unsigned long logic_sect_no, char *buf)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
int sectors_per_block, offset, block_address;
sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT;
offset = (int)(logic_sect_no % sectors_per_block);
block_address = (int)(logic_sect_no / sectors_per_block);
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d, block_addr=%d\n",
logic_sect_no, sectors_per_block, offset, block_address);
if ( block_address >= ssfdc->map_len )
BUG();
block_address = ssfdc->logic_block_map[block_address];
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n", block_address);
if ( block_address < 0xffff )
{
unsigned long sect_no;
sect_no = (unsigned long)block_address * sectors_per_block + offset;
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n", sect_no);
if ( read_physical_sector( ssfdc->mbd.mtd, buf, sect_no ) < 0 )
return -EIO;
}
else
{
memset(buf, 0xff, SECTOR_SIZE);
}
return 0;
}
#ifdef ENABLE_GETGEO
static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
geo->heads = ssfdc->heads;
geo->sectors = ssfdc->sectors;
geo->cylinders = ssfdc->cylinders;
return 0;
}
#endif
/****************************************************************************
*
* Module stuff
*
****************************************************************************/
static struct mtd_blktrans_ops ssfdcr_tr = {
.name = "ssfdc",
.major = SSFDCR_MAJOR,
.part_bits = SSFDCR_PARTN_BITS,
#ifdef ENABLE_GETGEO
.getgeo = ssfdcr_getgeo,
#endif
.readsect = ssfdcr_readsect,
.add_mtd = ssfdcr_add_mtd,
.remove_dev = ssfdcr_remove_dev,
.owner = THIS_MODULE,
};
static int __init init_ssfdcr(void)
{
printk(KERN_INFO "SSFDC Read only Flash Translation layer $Revision: 1.5 $\n");
return register_mtd_blktrans(&ssfdcr_tr);
}
static void __exit cleanup_ssfdcr(void)
{
deregister_mtd_blktrans(&ssfdcr_tr);
}
module_init(init_ssfdcr);
module_exit(cleanup_ssfdcr);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_DESCRIPTION("Flash Translation Layer for read-only SSDFC SmartMedia card");
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-20 11:31 ` Claudio Lanconelli
@ 2006-06-20 12:30 ` David Woodhouse
2006-06-20 13:25 ` Claudio Lanconelli
0 siblings, 1 reply; 16+ messages in thread
From: David Woodhouse @ 2006-06-20 12:30 UTC (permalink / raw)
To: Claudio Lanconelli; +Cc: linux-mtd
On Tue, 2006-06-20 at 13:31 +0200, Claudio Lanconelli wrote:
> I made some time ago a Smartmedia read only layer for MTD, since I
> can't make the current ssfdc.c working.
> I attach it if you want to give a look.
Thanks. I think it makes a certain amount of sense to merge that --
people can add write support to it later. Please could you re-send with
a Signed-off-by: line so that it can be merged?
--
dwmw2
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-20 12:30 ` David Woodhouse
@ 2006-06-20 13:25 ` Claudio Lanconelli
2006-06-20 13:52 ` Thomas Gleixner
0 siblings, 1 reply; 16+ messages in thread
From: Claudio Lanconelli @ 2006-06-20 13:25 UTC (permalink / raw)
To: David Woodhouse; +Cc: linux-mtd
David Woodhouse wrote:
> Thanks. I think it makes a certain amount of sense to merge that --
> people can add write support to it later. Please could you re-send with
> a Signed-off-by: line so that it can be merged
Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
/*
* Linux driver for SSFDC Flash Translation Layer (Read only)
* (c) 2005 Eptar srl
* Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
*
* Based on NTFL and MTDBLOCK_RO drivers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* $Id: ssfdc_ro.c,v 1.5 2005/11/28 13:54:08 claudio Exp $
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/hdreg.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/blktrans.h>
#undef ENABLE_GETGEO
#undef DEBUG
#define DEBUG(n, args...) \
do { \
if (n <= SSFDC_RO_DEBUG_VERBOSE) \
printk(KERN_INFO args); \
} while(0)
#define SSFDC_RO_DEBUG_VERBOSE 0
struct ssfdcr_record {
struct mtd_blktrans_dev mbd;
int usecount;
#ifdef ENABLE_GETGEO
unsigned char heads;
unsigned char sectors;
unsigned short cylinders;
#endif
int cis_block; //block n. containing CIS/IDI
int erase_size; //phys_block_size
unsigned short *logic_block_map; //all zones (max 8192 phys
blocks on the 128MB)
int map_len; //n. phys_blocks on the card
};
static const struct nand_oobinfo ssfdc_oobinfo = {
.useecc = MTD_NANDECC_PLACEONLY,
.eccbytes = 6,
.eccpos = {14, 13, 15, 9, 8, 10}
};
#define SSFDCR_MAJOR 44
#define SSFDCR_PARTN_BITS 3
#define SECTOR_SIZE 512
#define SECTOR_SHIFT 9
#define OOB_SIZE 16
#define MAX_LOGIC_BLK_PER_ZONE 1000
#define MAX_PHYS_BLK_PER_ZONE 1024
#define ArraySize(x) ( sizeof(x) / sizeof((x)[0]) )
#define KB(x) ( (x) * 1024L )
#define MB(x) ( KB(x) * 1024L )
/** CHS Table
1MB 2MB 4MB 8MB 16MB 32MB
64MB 128MB
NCylinder 125 125 250 250 500
500 500 500
NHead 4 4 4 4 4 8 8
16
NSector 4 8 8 16 16 16
32 32
SumSector 2,000 4,000 8,000 16,000 32,000 64,000
128,000 256,000
SectorSize 512 512 512 512 512
512 512 512
**/
#ifdef ENABLE_GETGEO
typedef struct {
unsigned long size;
unsigned short cyl;
unsigned char head;
unsigned char sec;
} chs_entry_t;
//Must be ordered by size
static const chs_entry_t chs_table[] = {
{ MB( 1), 125, 4, 4 },
{ MB( 2), 125, 4, 8 },
{ MB( 4), 250, 4, 8 },
{ MB( 8), 250, 4, 16 },
{ MB( 16), 500, 4, 16 },
{ MB( 32), 500, 8, 16 },
{ MB( 64), 500, 8, 32 },
{ MB(128), 500, 16, 32 },
{ 0 },
};
static int get_chs(unsigned long size, unsigned short *cyl, unsigned
char *head, unsigned char *sec)
{
int k;
int found = 0;
k = 0;
while ( chs_table[k].size > 0 && size > chs_table[k].size )
k++;
if ( chs_table[k].size > 0 )
{
if (cyl)
*cyl = chs_table[k].cyl;
if (head)
*head = chs_table[k].head;
if (sec)
*sec = chs_table[k].sec;
found = 1;
}
return found;
}
#endif
static const unsigned char nibble_count_bits[16] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4
};
//Counts bit 1 in a byte. Use look up table to speed up count
static int byte_count_bits(unsigned char val)
{
return nibble_count_bits[val >> 4] + nibble_count_bits[val & 0x0f];
}
static const unsigned char cis_numbers[] = {
0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
};
#define OOB_BLOCKSTATUS_OFFSET 5
#define block_is_bad(x) ( byte_count_bits(x) < 7 )
#define block_is_good(x) ( !block_is_bad(x) )
//Read and check for a valid CIS sector
static int get_valid_cis_sector(struct mtd_info *mtd)
{
int ret, k, cis_sector;
size_t retlen;
loff_t offset;
unsigned char sect_buf[SECTOR_SIZE];
unsigned char oob_buf[OOB_SIZE];
//Look for CIS/IDI sector on the first GOOD block (give up after 4
bad blocks)
//If the first good block doesn't contain CIS number the flash is
not SSFDC formatted
cis_sector = -1;
for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize)
{
ret = MTD_READOOB(mtd, offset, OOB_SIZE, &retlen, oob_buf);
if ( ret < 0 || retlen != OOB_SIZE )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: can't read OOB data on
sector %d\n",
(int)(offset >> SECTOR_SHIFT));
break;
}
if ( block_is_good( oob_buf[OOB_BLOCKSTATUS_OFFSET] ) )
{
ret = MTD_READ(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
if ( ret < 0 || retlen != SECTOR_SIZE )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: can't read CIS/IDI
sector\n");
}
else
if ( !memcmp(sect_buf, cis_numbers, sizeof(cis_numbers))
) //CIS pattern matching on the sector buffer
{
cis_sector = (int)(offset >> SECTOR_SHIFT); //Found
}
else
{
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: CIS/IDI sector not
found on %s (mtd%d)\n",
mtd->name, mtd->index);
}
break;
}
}
return cis_sector;
}
//Read physical sector (just a wrapper to MTD_READ)
static int read_physical_sector(struct mtd_info *mtd, unsigned char
*sect_buf, int sect_no)
{
int ret;
size_t retlen;
loff_t offset = (loff_t)sect_no << SECTOR_SHIFT;
ret = MTD_READ(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
if ( ret < 0 || retlen != SECTOR_SIZE )
return -1;
return 0;
}
//Parity calculator on a word of n bit size
static int get_parity(int number, int size)
{
int k;
int parity;
parity = 1;
for (k = 0; k < size; k++)
{
parity += (number >> k);
parity &= 1;
}
return parity;
}
//Read and validate the logical block address field stored in the OOB
static int get_logical_address(unsigned char oob_buf[OOB_SIZE])
{
int block_address, parity;
int offset[2] = {6, 11}; //offset of the two address fields
within OOB
int j;
int ok = 0;
//First we check for good block
if ( block_is_bad(oob_buf[OOB_BLOCKSTATUS_OFFSET]) )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: get_logical_address() Bad
block\n");
return -1; //Bad block
}
//Look for the first valid logical address
//Valid address has fixed pattern on most significant bits and
parity check
for (j = 0; j < ArraySize(offset); j++)
{
block_address = ((int)oob_buf[offset[j]] << 8) |
oob_buf[offset[j]+1];
//Check for the signature bits in the address field (most
significant bits)
if( (block_address & ~0x7FF) == 0x1000 )
{
parity = block_address & 0x01;
block_address &= 0x7FF;
block_address >>= 1;
if( get_parity(block_address, 10) != parity )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: logical address
field%d parity error (0x%04X)\n", j+1, block_address);
}
else
{
ok = 1;
break;
}
}
}
if ( !ok )
block_address = -2;
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: get_logical_address() %d\n",
block_address);
return block_address;
}
//Build the logic block map
static int build_logical_block_map(struct ssfdcr_record *ssfdc)
{
unsigned long offset;
unsigned char oob_buf[OOB_SIZE];
int ret, block_address, phys_block;
size_t retlen;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: build_block_map() n.blocks = %d
(%luK)\n",
ssfdc->map_len, (unsigned long)ssfdc->map_len *
ssfdc->erase_size / 1024 );
//Scan every physical block, skip CIS block
for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len;
phys_block++)
{
offset = (unsigned long)phys_block * ssfdc->erase_size;
ret = MTD_READOOB(ssfdc->mbd.mtd, offset, OOB_SIZE, &retlen,
oob_buf);
if ( ret < 0 || retlen != OOB_SIZE )
{
DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: mtd read_oob() failed at
%lu\n", offset);
return -1;
}
block_address = get_logical_address(oob_buf);
//Skip bad blocks and invalid addresses
if ( block_address >= 0 && block_address < MAX_LOGIC_BLK_PER_ZONE )
{
int zone_index;
zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE;
block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE;
ssfdc->logic_block_map[block_address] = (unsigned
short)phys_block;
DEBUG(MTD_DEBUG_LEVEL2, "SSFDC_RO: build_block_map()
phys_block=%d, logic_block_addr=%d, zone=%d\n",
phys_block, block_address, zone_index);
}
}
return 0;
}
static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info
*mtd)
{
struct ssfdcr_record *ssfdc;
int cis_sector;
//Check for NAND flash
if (mtd->type != MTD_NANDFLASH)
return;
//Check for SSDFC format by reading CIS/IDI sector
cis_sector = get_valid_cis_sector(mtd);
if ( cis_sector == -1 )
return;
ssfdc = kmalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
if ( !ssfdc )
{
printk(KERN_WARNING "SSFDC_RO: out of memory for data
structures\n");
return;
}
memset(ssfdc, 0, sizeof(*ssfdc));
ssfdc->mbd.mtd = mtd;
ssfdc->mbd.devnum = -1;
ssfdc->mbd.blksize = SECTOR_SIZE;
ssfdc->mbd.tr = tr;
ssfdc->mbd.readonly = 1;
ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
ssfdc->erase_size = mtd->erasesize;
ssfdc->map_len = mtd->size / mtd->erasesize;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: cis_block=%d, erase_size=%d,
map_len=%d, n_zones=%d\n",
ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
(ssfdc->map_len + MAX_PHYS_BLK_PER_ZONE - 1) /
MAX_PHYS_BLK_PER_ZONE);
#ifdef ENABLE_GETGEO
// Set geometry
get_chs( mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) /
((long)ssfdc->sectors * (long)ssfdc->heads));
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
(long)ssfdc->cylinders * (long)ssfdc->heads *
(long)ssfdc->sectors );
ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders *
(long)ssfdc->sectors;
#else
ssfdc->mbd.size = ssfdc->map_len * (ssfdc->erase_size >> SECTOR_SHIFT);
#endif
//Allocate logical block map
ssfdc->logic_block_map = kmalloc( sizeof(ssfdc->logic_block_map[0])
* ssfdc->map_len, GFP_KERNEL);
if (!ssfdc->logic_block_map)
{
printk(KERN_WARNING "SSFDC_RO: out of memory for data
structures\n");
kfree(ssfdc);
return;
}
memset(ssfdc->logic_block_map, 0xff,
sizeof(ssfdc->logic_block_map[0]) * ssfdc->map_len);
//Build logical block map
if( build_logical_block_map(ssfdc) < 0 )
{
if ( ssfdc->logic_block_map )
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
return;
}
//Register device + partitions
if (add_mtd_blktrans_dev(&ssfdc->mbd))
{
if ( ssfdc->logic_block_map )
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
return;
}
printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n",
ssfdc->mbd.devnum + 'a', mtd->index, mtd->name);
}
static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
del_mtd_blktrans_dev(dev);
if (ssfdc->logic_block_map)
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
}
static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, unsigned long
logic_sect_no, char *buf)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
int sectors_per_block, offset, block_address;
sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT;
offset = (int)(logic_sect_no % sectors_per_block);
block_address = (int)(logic_sect_no / sectors_per_block);
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect(%lu)
sec_per_blk=%d, ofst=%d, block_addr=%d\n",
logic_sect_no, sectors_per_block, offset,
block_address);
if ( block_address >= ssfdc->map_len )
BUG();
block_address = ssfdc->logic_block_map[block_address];
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect()
phys_block_addr=%d\n", block_address);
if ( block_address < 0xffff )
{
unsigned long sect_no;
sect_no = (unsigned long)block_address * sectors_per_block + offset;
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect()
phys_sect_no=%lu\n", sect_no);
if ( read_physical_sector( ssfdc->mbd.mtd, buf, sect_no ) < 0 )
return -EIO;
}
else
{
memset(buf, 0xff, SECTOR_SIZE);
}
return 0;
}
#ifdef ENABLE_GETGEO
static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct
hd_geometry *geo)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
geo->heads = ssfdc->heads;
geo->sectors = ssfdc->sectors;
geo->cylinders = ssfdc->cylinders;
return 0;
}
#endif
/****************************************************************************
*
* Module stuff
*
****************************************************************************/
static struct mtd_blktrans_ops ssfdcr_tr = {
.name = "ssfdc",
.major = SSFDCR_MAJOR,
.part_bits = SSFDCR_PARTN_BITS,
#ifdef ENABLE_GETGEO
.getgeo = ssfdcr_getgeo,
#endif
.readsect = ssfdcr_readsect,
.add_mtd = ssfdcr_add_mtd,
.remove_dev = ssfdcr_remove_dev,
.owner = THIS_MODULE,
};
static int __init init_ssfdcr(void)
{
printk(KERN_INFO "SSFDC Read only Flash Translation layer $Revision:
1.5 $\n");
return register_mtd_blktrans(&ssfdcr_tr);
}
static void __exit cleanup_ssfdcr(void)
{
deregister_mtd_blktrans(&ssfdcr_tr);
}
module_init(init_ssfdcr);
module_exit(cleanup_ssfdcr);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_DESCRIPTION("Flash Translation Layer for read-only SSDFC
SmartMedia card");
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-20 13:25 ` Claudio Lanconelli
@ 2006-06-20 13:52 ` Thomas Gleixner
2006-06-20 17:26 ` Claudio Lanconelli
0 siblings, 1 reply; 16+ messages in thread
From: Thomas Gleixner @ 2006-06-20 13:52 UTC (permalink / raw)
To: Claudio Lanconelli; +Cc: linux-mtd, David Woodhouse
On Tue, 2006-06-20 at 15:25 +0200, Claudio Lanconelli wrote:
> David Woodhouse wrote:
> > Thanks. I think it makes a certain amount of sense to merge that --
> > people can add write support to it later. Please could you re-send with
> > a Signed-off-by: line so that it can be merged
> Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
Can you please resubmit. The code is line wrapped and whitespace
damaged.
Nevertheless some comments inlined
tglx
All over the place:
Please use tabs not spaces ! (tabsize = 8)
Please do not use C++ comments !
Fixup if, else, for brackets positions
> * $Id: ssfdc_ro.c,v 1.5 2005/11/28 13:54:08 claudio Exp $
Please remove cvs id
> */
>
> #include <linux/config.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
random newline
> #include <linux/init.h>
> #include <linux/slab.h>
> #include <linux/hdreg.h>
> #include <linux/mtd/mtd.h>
> #include <linux/mtd/nand.h>
> #include <linux/mtd/blktrans.h>
>
> #undef ENABLE_GETGEO
>
> #undef DEBUG
> #define DEBUG(n, args...) \
> do { \
> if (n <= SSFDC_RO_DEBUG_VERBOSE) \
> printk(KERN_INFO args); \
> } while(0)
>
> #define SSFDC_RO_DEBUG_VERBOSE 0
Please use the existing MTD_DEBUG stuff. No need to add another debug
hack to the existing 10000 in the kernel
> struct ssfdcr_record {
> struct mtd_blktrans_dev mbd;
> int usecount;
> #ifdef ENABLE_GETGEO
> unsigned char heads;
> unsigned char sectors;
> unsigned short cylinders;
> #endif
> int cis_block; //block n. containing CIS/IDI
> int erase_size; //phys_block_size
> unsigned short *logic_block_map; //all zones (max 8192 phys
> blocks on the 128MB)
> int map_len; //n. phys_blocks on the card
> };
> static const struct nand_oobinfo ssfdc_oobinfo = {
> .useecc = MTD_NANDECC_PLACEONLY,
> .eccbytes = 6,
> .eccpos = {14, 13, 15, 9, 8, 10}
> };
>
> #define SSFDCR_MAJOR 44
> #define SSFDCR_PARTN_BITS 3
>
> #define SECTOR_SIZE 512
> #define SECTOR_SHIFT 9
> #define OOB_SIZE 16
>
> #define MAX_LOGIC_BLK_PER_ZONE 1000
> #define MAX_PHYS_BLK_PER_ZONE 1024
>
> #define ArraySize(x) ( sizeof(x) / sizeof((x)[0]) )
>
> #define KB(x) ( (x) * 1024L )
> #define MB(x) ( KB(x) * 1024L )
>
> /** CHS Table
> 1MB 2MB 4MB 8MB 16MB 32MB
> 64MB 128MB
> NCylinder 125 125 250 250 500
> 500 500 500
> NHead 4 4 4 4 4 8 8
> 16
> NSector 4 8 8 16 16 16
> 32 32
> SumSector 2,000 4,000 8,000 16,000 32,000 64,000
> 128,000 256,000
> SectorSize 512 512 512 512 512
> 512 512 512
> **/
>
> #ifdef ENABLE_GETGEO
>
> typedef struct {
> unsigned long size;
> unsigned short cyl;
> unsigned char head;
> unsigned char sec;
> } chs_entry_t;
>
> //Must be ordered by size
> static const chs_entry_t chs_table[] = {
> { MB( 1), 125, 4, 4 },
> { MB( 2), 125, 4, 8 },
> { MB( 4), 250, 4, 8 },
> { MB( 8), 250, 4, 16 },
> { MB( 16), 500, 4, 16 },
> { MB( 32), 500, 8, 16 },
> { MB( 64), 500, 8, 32 },
> { MB(128), 500, 16, 32 },
> { 0 },
> };
>
> static int get_chs(unsigned long size, unsigned short *cyl, unsigned
> char *head, unsigned char *sec)
> {
> int k;
> int found = 0;
>
> k = 0;
> while ( chs_table[k].size > 0 && size > chs_table[k].size )
while (chs_ .... size)
> k++;
>
> if ( chs_table[k].size > 0 )
dito
> {
> if (cyl)
> *cyl = chs_table[k].cyl;
> if (head)
> *head = chs_table[k].head;
> if (sec)
> *sec = chs_table[k].sec;
> found = 1;
> }
>
> return found;
> }
> #endif
>
>
> static const unsigned char nibble_count_bits[16] = {
> 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4
> };
>
> //Counts bit 1 in a byte. Use look up table to speed up count
> static int byte_count_bits(unsigned char val)
> {
> return nibble_count_bits[val >> 4] + nibble_count_bits[val & 0x0f];
> }
>
> static const unsigned char cis_numbers[] = {
> 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
> };
>
> #define OOB_BLOCKSTATUS_OFFSET 5
>
> #define block_is_bad(x) ( byte_count_bits(x) < 7 )
> #define block_is_good(x) ( !block_is_bad(x) )
Please use the bad block functions mtd->block_is_bad()
> //Read and check for a valid CIS sector
> static int get_valid_cis_sector(struct mtd_info *mtd)
> {
> int ret, k, cis_sector;
> size_t retlen;
> loff_t offset;
> unsigned char sect_buf[SECTOR_SIZE];
> unsigned char oob_buf[OOB_SIZE];
>
> //Look for CIS/IDI sector on the first GOOD block (give up after 4
> bad blocks)
> //If the first good block doesn't contain CIS number the flash is
> not SSFDC formatted
> cis_sector = -1;
> for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize)
> {
for ( ) {
> ret = MTD_READOOB(mtd, offset, OOB_SIZE, &retlen, oob_buf);
> if ( ret < 0 || retlen != OOB_SIZE )
> {
if () {
> DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: can't read OOB data on
> sector %d\n",
> (int)(offset >> SECTOR_SHIFT));
> break;
> }
>
> if ( block_is_good( oob_buf[OOB_BLOCKSTATUS_OFFSET] ) )
> {
dito
> ret = MTD_READ(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
> if ( ret < 0 || retlen != SECTOR_SIZE )
> {
dito
> DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: can't read CIS/IDI
> sector\n");
> }
> else
} else if () {
> if ( !memcmp(sect_buf, cis_numbers, sizeof(cis_numbers))
> ) //CIS pattern matching on the sector buffer
> {
> cis_sector = (int)(offset >> SECTOR_SHIFT); //Found
> }
> else
> {
} else {
please fix this all over the code
> DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: CIS/IDI sector not
> found on %s (mtd%d)\n",
> mtd->name, mtd->index);
> }
> break;
> }
> }
>
> return cis_sector;
> }
>
> //Read physical sector (just a wrapper to MTD_READ)
> static int read_physical_sector(struct mtd_info *mtd, unsigned char
> *sect_buf, int sect_no)
> {
> int ret;
> size_t retlen;
> loff_t offset = (loff_t)sect_no << SECTOR_SHIFT;
>
> ret = MTD_READ(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
Please do not use MTD_READ, use mtd->read() instead
> if ( ret < 0 || retlen != SECTOR_SIZE )
> return -1;
>
> return 0;
> }
>
> //Parity calculator on a word of n bit size
> static int get_parity(int number, int size)
> {
> int k;
> int parity;
>
> parity = 1;
> for (k = 0; k < size; k++)
> {
> parity += (number >> k);
> parity &= 1;
> }
> return parity;
> }
>
> //Read and validate the logical block address field stored in the OOB
> static int get_logical_address(unsigned char oob_buf[OOB_SIZE])
(uint8_t *oob_buf)
> {
> int block_address, parity;
> int offset[2] = {6, 11}; //offset of the two address fields
> within OOB
> int j;
> int ok = 0;
>
> //First we check for good block
> if ( block_is_bad(oob_buf[OOB_BLOCKSTATUS_OFFSET]) )
> {
> DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: get_logical_address() Bad
> block\n");
> return -1; //Bad block
> }
>
> //Look for the first valid logical address
> //Valid address has fixed pattern on most significant bits and
> parity check
> for (j = 0; j < ArraySize(offset); j++)
> {
> block_address = ((int)oob_buf[offset[j]] << 8) |
> oob_buf[offset[j]+1];
>
> //Check for the signature bits in the address field (most
> significant bits)
> if( (block_address & ~0x7FF) == 0x1000 )
> {
> parity = block_address & 0x01;
> block_address &= 0x7FF;
> block_address >>= 1;
>
> if( get_parity(block_address, 10) != parity )
> {
> DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: logical address
> field%d parity error (0x%04X)\n", j+1, block_address);
> }
> else
> {
> ok = 1;
> break;
> }
> }
> }
>
> if ( !ok )
> block_address = -2;
>
> DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: get_logical_address() %d\n",
> block_address);
>
> return block_address;
> }
>
> //Build the logic block map
> static int build_logical_block_map(struct ssfdcr_record *ssfdc)
> {
> unsigned long offset;
> unsigned char oob_buf[OOB_SIZE];
> int ret, block_address, phys_block;
> size_t retlen;
>
> DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: build_block_map() n.blocks = %d
> (%luK)\n",
> ssfdc->map_len, (unsigned long)ssfdc->map_len *
> ssfdc->erase_size / 1024 );
>
> //Scan every physical block, skip CIS block
> for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len;
> phys_block++)
> {
> offset = (unsigned long)phys_block * ssfdc->erase_size;
> ret = MTD_READOOB(ssfdc->mbd.mtd, offset, OOB_SIZE, &retlen,
> oob_buf);
mtd->read_oob()
> if ( ret < 0 || retlen != OOB_SIZE )
> {
> DEBUG(MTD_DEBUG_LEVEL0, "SSFDC_RO: mtd read_oob() failed at
> %lu\n", offset);
> return -1;
> }
> block_address = get_logical_address(oob_buf);
>
> //Skip bad blocks and invalid addresses
> if ( block_address >= 0 && block_address < MAX_LOGIC_BLK_PER_ZONE )
> {
> int zone_index;
>
> zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE;
> block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE;
> ssfdc->logic_block_map[block_address] = (unsigned
> short)phys_block;
>
> DEBUG(MTD_DEBUG_LEVEL2, "SSFDC_RO: build_block_map()
> phys_block=%d, logic_block_addr=%d, zone=%d\n",
> phys_block, block_address, zone_index);
> }
> }
> return 0;
> }
>
> static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info
> *mtd)
> {
> struct ssfdcr_record *ssfdc;
> int cis_sector;
>
> //Check for NAND flash
> if (mtd->type != MTD_NANDFLASH)
> return;
>
> //Check for SSDFC format by reading CIS/IDI sector
> cis_sector = get_valid_cis_sector(mtd);
> if ( cis_sector == -1 )
if (cis_sector == -1)
> return;
>
> ssfdc = kmalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
> if ( !ssfdc )
> {
> printk(KERN_WARNING "SSFDC_RO: out of memory for data
> structures\n");
> return;
> }
> memset(ssfdc, 0, sizeof(*ssfdc));
Please use kzalloc() instead of kmalloc + memset
> ssfdc->mbd.mtd = mtd;
> ssfdc->mbd.devnum = -1;
> ssfdc->mbd.blksize = SECTOR_SIZE;
> ssfdc->mbd.tr = tr;
> ssfdc->mbd.readonly = 1;
>
> ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
> ssfdc->erase_size = mtd->erasesize;
> ssfdc->map_len = mtd->size / mtd->erasesize;
>
> DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: cis_block=%d, erase_size=%d,
> map_len=%d, n_zones=%d\n",
> ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
> (ssfdc->map_len + MAX_PHYS_BLK_PER_ZONE - 1) /
> MAX_PHYS_BLK_PER_ZONE);
>
> #ifdef ENABLE_GETGEO
> // Set geometry
> get_chs( mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
> ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) /
> ((long)ssfdc->sectors * (long)ssfdc->heads));
>
> DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
> ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
> (long)ssfdc->cylinders * (long)ssfdc->heads *
> (long)ssfdc->sectors );
>
> ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders *
> (long)ssfdc->sectors;
> #else
> ssfdc->mbd.size = ssfdc->map_len * (ssfdc->erase_size >> SECTOR_SHIFT);
> #endif
>
> //Allocate logical block map
> ssfdc->logic_block_map = kmalloc( sizeof(ssfdc->logic_block_map[0])
> * ssfdc->map_len, GFP_KERNEL);
> if (!ssfdc->logic_block_map)
> {
> printk(KERN_WARNING "SSFDC_RO: out of memory for data
> structures\n");
> kfree(ssfdc);
> return;
goto out_err;
> }
> memset(ssfdc->logic_block_map, 0xff,
> sizeof(ssfdc->logic_block_map[0]) * ssfdc->map_len);
>
> //Build logical block map
> if( build_logical_block_map(ssfdc) < 0 )
> {
> if ( ssfdc->logic_block_map )
> kfree(ssfdc->logic_block_map);
no need to check for null
> kfree(ssfdc);
> return;
goto out_err;
> }
>
> //Register device + partitions
> if (add_mtd_blktrans_dev(&ssfdc->mbd))
> {
> if ( ssfdc->logic_block_map )
> kfree(ssfdc->logic_block_map);
> kfree(ssfdc);
> return;
goto out_err;
> }
>
> printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n",
> ssfdc->mbd.devnum + 'a', mtd->index, mtd->name);
return;
out_err:
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
> }
>
> static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
> {
> struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
>
> DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
>
> del_mtd_blktrans_dev(dev);
> if (ssfdc->logic_block_map)
no check please
> kfree(ssfdc->logic_block_map);
> kfree(ssfdc);
> }
>
> static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, unsigned long
> logic_sect_no, char *buf)
> {
> struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
> int sectors_per_block, offset, block_address;
>
> sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT;
> offset = (int)(logic_sect_no % sectors_per_block);
> block_address = (int)(logic_sect_no / sectors_per_block);
>
> DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect(%lu)
> sec_per_blk=%d, ofst=%d, block_addr=%d\n",
> logic_sect_no, sectors_per_block, offset,
> block_address);
>
> if ( block_address >= ssfdc->map_len )
> BUG();
>
> block_address = ssfdc->logic_block_map[block_address];
>
> DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect()
> phys_block_addr=%d\n", block_address);
>
> if ( block_address < 0xffff )
> {
> unsigned long sect_no;
>
> sect_no = (unsigned long)block_address * sectors_per_block + offset;
>
> DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: ssfdcr_readsect()
> phys_sect_no=%lu\n", sect_no);
>
> if ( read_physical_sector( ssfdc->mbd.mtd, buf, sect_no ) < 0 )
> return -EIO;
> }
> else
> {
> memset(buf, 0xff, SECTOR_SIZE);
> }
>
> return 0;
> }
>
> #ifdef ENABLE_GETGEO
> static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct
> hd_geometry *geo)
> {
> struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
>
> DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
> ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
>
> geo->heads = ssfdc->heads;
> geo->sectors = ssfdc->sectors;
> geo->cylinders = ssfdc->cylinders;
>
> return 0;
> }
> #endif
>
> /****************************************************************************
> *
> * Module stuff
> *
> ****************************************************************************/
>
> static struct mtd_blktrans_ops ssfdcr_tr = {
> .name = "ssfdc",
> .major = SSFDCR_MAJOR,
> .part_bits = SSFDCR_PARTN_BITS,
> #ifdef ENABLE_GETGEO
> .getgeo = ssfdcr_getgeo,
> #endif
> .readsect = ssfdcr_readsect,
> .add_mtd = ssfdcr_add_mtd,
> .remove_dev = ssfdcr_remove_dev,
> .owner = THIS_MODULE,
> };
>
> static int __init init_ssfdcr(void)
> {
> printk(KERN_INFO "SSFDC Read only Flash Translation layer $Revision:
> 1.5 $\n");
>
> return register_mtd_blktrans(&ssfdcr_tr);
> }
>
> static void __exit cleanup_ssfdcr(void)
> {
> deregister_mtd_blktrans(&ssfdcr_tr);
> }
>
> module_init(init_ssfdcr);
> module_exit(cleanup_ssfdcr);
>
> MODULE_LICENSE("GPL");
> MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
> MODULE_DESCRIPTION("Flash Translation Layer for read-only SSDFC
> SmartMedia card");
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-20 13:52 ` Thomas Gleixner
@ 2006-06-20 17:26 ` Claudio Lanconelli
2006-06-20 17:41 ` Thomas Gleixner
0 siblings, 1 reply; 16+ messages in thread
From: Claudio Lanconelli @ 2006-06-20 17:26 UTC (permalink / raw)
To: tglx; +Cc: linux-mtd
[-- Attachment #1: Type: text/plain, Size: 359 bytes --]
Thanks Thomas for your tips.
Here a second try, does it look fine?
I attached the file instead of inline to avoid tab/space replacement by
email client.
however can I ask you where I find prototype/usage for mtd->block_is_bad()?
And against which tree I need to build it? Be patient, please.
Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
[-- Attachment #2: ssfdc_ro.c --]
[-- Type: text/plain, Size: 12267 bytes --]
/*
* Linux driver for SSFDC Flash Translation Layer (Read only)
* (c) 2005 Eptar srl
* Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
*
* Based on NTFL and MTDBLOCK_RO drivers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/hdreg.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/blktrans.h>
/* #undef CONFIG_SSFDCRO_ENABLE_GETGEO */
struct ssfdcr_record {
struct mtd_blktrans_dev mbd;
int usecount;
#ifdef CONFIG_SSFDCRO_ENABLE_GETGEO
unsigned char heads;
unsigned char sectors;
unsigned short cylinders;
#endif
int cis_block; /* block n. containing CIS/IDI */
int erase_size; /* phys_block_size */
unsigned short *logic_block_map; /* all zones (max 8192 phys blocks on
the 128MB) */
int map_len; /* n. phys_blocks on the card */
};
static const struct nand_oobinfo ssfdc_oobinfo = {
.useecc = MTD_NANDECC_PLACEONLY,
.eccbytes = 6,
.eccpos = {14, 13, 15, 9, 8, 10}
};
#define SSFDCR_MAJOR 44
#define SSFDCR_PARTN_BITS 3
#define SECTOR_SIZE 512
#define SECTOR_SHIFT 9
#define OOB_SIZE 16
#define MAX_LOGIC_BLK_PER_ZONE 1000
#define MAX_PHYS_BLK_PER_ZONE 1024
#define ArraySize(x) ( sizeof(x) / sizeof((x)[0]) )
#define KB(x) ( (x) * 1024L )
#define MB(x) ( KB(x) * 1024L )
/** CHS Table
1MB 2MB 4MB 8MB 16MB 32MB 64MB 128MB
NCylinder 125 125 250 250 500 500 500 500
NHead 4 4 4 4 4 8 8 16
NSector 4 8 8 16 16 16 32 32
SumSector 2,000 4,000 8,000 16,000 32,000 64,000 128,000 256,000
SectorSize 512 512 512 512 512 512 512 512
**/
#ifdef CONFIG_SSFDCRO_ENABLE_GETGEO
typedef struct {
unsigned long size;
unsigned short cyl;
unsigned char head;
unsigned char sec;
} chs_entry_t;
//Must be ordered by size
static const chs_entry_t chs_table[] = {
{ MB( 1), 125, 4, 4 },
{ MB( 2), 125, 4, 8 },
{ MB( 4), 250, 4, 8 },
{ MB( 8), 250, 4, 16 },
{ MB( 16), 500, 4, 16 },
{ MB( 32), 500, 8, 16 },
{ MB( 64), 500, 8, 32 },
{ MB(128), 500, 16, 32 },
{ 0 },
};
static int get_chs(unsigned long size, unsigned short *cyl, unsigned char *head,
unsigned char *sec)
{
int k;
int found = 0;
k = 0;
while (chs_table[k].size > 0 && size > chs_table[k].size)
k++;
if (chs_table[k].size > 0) {
if (cyl)
*cyl = chs_table[k].cyl;
if (head)
*head = chs_table[k].head;
if (sec)
*sec = chs_table[k].sec;
found = 1;
}
return found;
}
#endif
static const uint8_t nibble_count_bits[16] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4
};
//Counts bit 1 in a byte. Use look up table to speed up count
static int byte_count_bits(uint8_t val)
{
return nibble_count_bits[val >> 4] + nibble_count_bits[val & 0x0f];
}
static const uint8_t cis_numbers[] = {
0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
};
/* Read and check for a valid CIS sector */
static int get_valid_cis_sector(struct mtd_info *mtd)
{
int ret, k, cis_sector;
size_t retlen;
loff_t offset;
uint8_t sect_buf[SECTOR_SIZE];
/* Look for CIS/IDI sector on the first GOOD block (give up after 4 bad
blocks). If the first good block doesn't contain CIS number the flash
is not SSFDC formatted */
cis_sector = -1;
for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize) {
if (!mtd->block_is_bad(mtd, offset)) {
ret = mtd->read(mtd, offset, SECTOR_SIZE, &retlen,
sect_buf);
/* CIS pattern match on the sector buffer */
if ( ret < 0 || retlen != SECTOR_SIZE ) {
printk(KERN_WARNING
"SSFDC_RO:can't read CIS/IDI sector\n");
} else if ( !memcmp(sect_buf, cis_numbers,
sizeof(cis_numbers)) ) {
/* Found */
cis_sector = (int)(offset >> SECTOR_SHIFT);
} else {
DEBUG(MTD_DEBUG_LEVEL0,
"SSFDC_RO: CIS/IDI sector not found"
" on %s (mtd%d)\n", mtd->name,
mtd->index);
}
break;
}
}
return cis_sector;
}
/* Read physical sector (just a wrapper to MTD_READ) */
static int read_physical_sector(struct mtd_info *mtd, uint8_t *sect_buf,
int sect_no)
{
int ret;
size_t retlen;
loff_t offset = (loff_t)sect_no << SECTOR_SHIFT;
ret = mtd->read(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
if ( ret < 0 || retlen != SECTOR_SIZE )
return -1;
return 0;
}
/* Parity calculator on a word of n bit size */
static int get_parity(int number, int size)
{
int k;
int parity;
parity = 1;
for (k = 0; k < size; k++) {
parity += (number >> k);
parity &= 1;
}
return parity;
}
/* Read and validate the logical block address field stored in the OOB */
static int get_logical_address(uint8_t *oob_buf)
{
int block_address, parity;
int offset[2] = {6, 11}; /* offset of the 2 address fields within OOB */
int j;
int ok = 0;
/* Look for the first valid logical address
Valid address has fixed pattern on most significant bits and
parity check */
for (j = 0; j < ArraySize(offset); j++) {
block_address = ((int)oob_buf[offset[j]] << 8) |
oob_buf[offset[j]+1];
/* Check for the signature bits in the address field
(most significant bits) */
if ((block_address & ~0x7FF) == 0x1000) {
parity = block_address & 0x01;
block_address &= 0x7FF;
block_address >>= 1;
if (get_parity(block_address, 10) != parity) {
DEBUG(MTD_DEBUG_LEVEL0,
"SSFDC_RO: logical address field%d"
"parity error(0x%04X)\n", j+1,
block_address);
} else {
ok = 1;
break;
}
}
}
if ( !ok )
block_address = -2;
DEBUG(MTD_DEBUG_LEVEL3, "SSFDC_RO: get_logical_address() %d\n",
block_address);
return block_address;
}
/* Build the logic block map */
static int build_logical_block_map(struct ssfdcr_record *ssfdc)
{
unsigned long offset;
uint8_t oob_buf[OOB_SIZE];
int ret, block_address, phys_block;
size_t retlen;
struct mtd_info *mtd = ssfdc->mbd.mtd;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: build_block_map() nblks=%d (%luK)\n",
ssfdc->map_len, (unsigned long)ssfdc->map_len *
ssfdc->erase_size / 1024 );
/* Scan every physical block, skip CIS block */
for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len;
phys_block++) {
offset = (unsigned long)phys_block * ssfdc->erase_size;
ret = mtd->read_oob(mtd, offset, OOB_SIZE, &retlen, oob_buf);
if (ret < 0 || retlen != OOB_SIZE) {
DEBUG(MTD_DEBUG_LEVEL0,
"SSFDC_RO: mtd read_oob() failed at %lu\n",
offset);
return -1;
}
if (mtd->block_is_bad(mtd, offset))
block_address = -1;
else
block_address = get_logical_address(oob_buf);
/* Skip bad blocks and invalid addresses */
if (block_address >= 0 &&
block_address < MAX_LOGIC_BLK_PER_ZONE) {
int zone_index;
zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE;
block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE;
ssfdc->logic_block_map[block_address] =
(unsigned short)phys_block;
DEBUG(MTD_DEBUG_LEVEL2,
"SSFDC_RO: build_block_map() phys_block=%d,"
"logic_block_addr=%d, zone=%d\n",
phys_block, block_address, zone_index);
}
}
return 0;
}
static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
struct ssfdcr_record *ssfdc;
int cis_sector;
/* Check for NAND flash */
if (mtd->type != MTD_NANDFLASH)
return;
/* Check for SSDFC format by reading CIS/IDI sector */
cis_sector = get_valid_cis_sector(mtd);
if (cis_sector == -1)
return;
ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
if (!ssfdc) {
printk(KERN_WARNING
"SSFDC_RO: out of memory for data structures\n");
return;
}
ssfdc->mbd.mtd = mtd;
ssfdc->mbd.devnum = -1;
ssfdc->mbd.blksize = SECTOR_SIZE;
ssfdc->mbd.tr = tr;
ssfdc->mbd.readonly = 1;
ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
ssfdc->erase_size = mtd->erasesize;
ssfdc->map_len = mtd->size / mtd->erasesize;
DEBUG(MTD_DEBUG_LEVEL1,
"SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n",
ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
(ssfdc->map_len + MAX_PHYS_BLK_PER_ZONE - 1) /
MAX_PHYS_BLK_PER_ZONE);
#ifdef CONFIG_SSFDCRO_ENABLE_GETGEO
/* Set geometry */
get_chs( mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) /
((long)ssfdc->sectors * (long)ssfdc->heads));
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
(long)ssfdc->cylinders * (long)ssfdc->heads *
(long)ssfdc->sectors );
ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders *
(long)ssfdc->sectors;
#else
ssfdc->mbd.size = ssfdc->map_len * (ssfdc->erase_size >> SECTOR_SHIFT);
#endif
/* Allocate logical block map */
ssfdc->logic_block_map = kmalloc( sizeof(ssfdc->logic_block_map[0]) *
ssfdc->map_len, GFP_KERNEL);
if (!ssfdc->logic_block_map) {
printk(KERN_WARNING
"SSFDC_RO: out of memory for data structures\n");
goto out_err;
}
memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) *
ssfdc->map_len);
/* Build logical block map */
if (build_logical_block_map(ssfdc) < 0)
goto out_err;
/* Register device + partitions */
if (add_mtd_blktrans_dev(&ssfdc->mbd))
goto out_err;
printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n",
ssfdc->mbd.devnum + 'a', mtd->index, mtd->name);
return;
out_err:
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
}
static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
del_mtd_blktrans_dev(dev);
kfree(ssfdc->logic_block_map);
kfree(ssfdc);
}
static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
unsigned long logic_sect_no, char *buf)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
int sectors_per_block, offset, block_address;
sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT;
offset = (int)(logic_sect_no % sectors_per_block);
block_address = (int)(logic_sect_no / sectors_per_block);
DEBUG(MTD_DEBUG_LEVEL3,
"SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d,"
" block_addr=%d\n", logic_sect_no, sectors_per_block, offset,
block_address);
if (block_address >= ssfdc->map_len)
BUG();
block_address = ssfdc->logic_block_map[block_address];
DEBUG(MTD_DEBUG_LEVEL3,
"SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n",
block_address);
if (block_address < 0xffff) {
unsigned long sect_no;
sect_no = (unsigned long)block_address * sectors_per_block +
offset;
DEBUG(MTD_DEBUG_LEVEL3,
"SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n",
sect_no);
if (read_physical_sector( ssfdc->mbd.mtd, buf, sect_no ) < 0)
return -EIO;
} else {
memset(buf, 0xff, SECTOR_SIZE);
}
return 0;
}
#ifdef CONFIG_SSFDCRO_ENABLE_GETGEO
static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
{
struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
geo->heads = ssfdc->heads;
geo->sectors = ssfdc->sectors;
geo->cylinders = ssfdc->cylinders;
return 0;
}
#endif
/****************************************************************************
*
* Module stuff
*
****************************************************************************/
static struct mtd_blktrans_ops ssfdcr_tr = {
.name = "ssfdc",
.major = SSFDCR_MAJOR,
.part_bits = SSFDCR_PARTN_BITS,
#ifdef CONFIG_SSFDCRO_ENABLE_GETGEO
.getgeo = ssfdcr_getgeo,
#endif
.readsect = ssfdcr_readsect,
.add_mtd = ssfdcr_add_mtd,
.remove_dev = ssfdcr_remove_dev,
.owner = THIS_MODULE,
};
static int __init init_ssfdcr(void)
{
printk(KERN_INFO "SSFDC Read only Flash Translation layer\n");
return register_mtd_blktrans(&ssfdcr_tr);
}
static void __exit cleanup_ssfdcr(void)
{
deregister_mtd_blktrans(&ssfdcr_tr);
}
module_init(init_ssfdcr);
module_exit(cleanup_ssfdcr);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_DESCRIPTION("Flash Translation Layer for read-only SSDFC SmartMedia card");
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-20 17:26 ` Claudio Lanconelli
@ 2006-06-20 17:41 ` Thomas Gleixner
2006-06-21 14:15 ` Claudio Lanconelli
0 siblings, 1 reply; 16+ messages in thread
From: Thomas Gleixner @ 2006-06-20 17:41 UTC (permalink / raw)
To: Claudio Lanconelli; +Cc: linux-mtd
Claudio,
On Tue, 2006-06-20 at 19:26 +0200, Claudio Lanconelli wrote:
> Thanks Thomas for your tips.
> Here a second try, does it look fine?
Much better now.
> I attached the file instead of inline to avoid tab/space replacement by
> email client.
> however can I ask you where I find prototype/usage for mtd->block_is_bad()?
Oops, mtd->block_isbad()
if (mtd->block_isbad(mtd, offset)
skip_bad_block();
else
scan_block()
offs is the offset of the block:
0 for the first block
16384 for the second ...
you certainly have this offset handy in the scan any way
This looks up the bad block table which is created in ram after the NAND
chip has been detected in nand_scan() and helps you to avoid reading bad
blocks. Just skip the block, when the function returns 1.
The only exception I think is block 0, which contains the SSFDC header
and is marked bad for protection.
> And against which tree I need to build it? Be patient, please.
The best is to build against mtd-26.git from git.infradead.org. git
instructions are on the MTD homepage.
I do a thorough review, when you have it running with that tree.
Thanks,
tglx
> #define ArraySize(x) ( sizeof(x) / sizeof((x)[0]) )
use the existing ARRAY_SIZE() macro please
> //Must be ordered by size
C++
> static const chs_entry_t chs_table[] = {
> /* Read and check for a valid CIS sector */
> static int get_valid_cis_sector(struct mtd_info *mtd)
> {
> int ret, k, cis_sector;
> size_t retlen;
> loff_t offset;
> uint8_t sect_buf[SECTOR_SIZE];
>
> /* Look for CIS/IDI sector on the first GOOD block (give up after 4 bad
> blocks). If the first good block doesn't contain CIS number the flash
> is not SSFDC formatted */
/*
* Use this for
* multiline comments please
*/
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-20 17:41 ` Thomas Gleixner
@ 2006-06-21 14:15 ` Claudio Lanconelli
2006-06-21 18:19 ` Thomas Gleixner
0 siblings, 1 reply; 16+ messages in thread
From: Claudio Lanconelli @ 2006-06-21 14:15 UTC (permalink / raw)
To: tglx; +Cc: linux-mtd
Hi Thomas,
Thomas Gleixner wrote:
> This looks up the bad block table which is created in ram after the NAND
> chip has been detected in nand_scan() and helps you to avoid reading bad
> blocks. Just skip the block, when the function returns 1.
>
> The only exception I think is block 0, which contains the SSFDC header
> and is marked bad for protection.
>
>
Are you sure about block 0 marked bad for protection? I read in
smartmedia specification by Toshiba that SSFDC header called CIS/IDI is
located on the first good block. Here the sentence:
"The CIS/IDI Field is placed in physical block 0.
If physical block 0 is found to be a defective block, the area is placed
in the
first normal block that is found after physical block 0.
Irrespective of the page size, only one block is assigned."
[...]
"The indication of a defective block and logical block arrangement in
the data
area will be set in the redundancy area.
In the case of the 512+16 bytes/page models, the 6th byte (byte address
517th)
in all pages in the redundant section contains two or more “0” bits to
indicate
a defective block."
My code follow these instructions on the SSFDC header location, is it
correct?
Anyone who knows it for sure?
Another question about the ECC placement. I think the MTD default ECC
bytes placement
in the redundant area is not suitable for SSFDC. I found the
CONFIG_MTD_NAND_ECC_SMC
in mtd configuration, but it's not used anywhere in the code. How can
the ssfdc_ro layer tell
to use smartmedia ECC bytes placement?
Thanks,
Claudio
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: FAT vs jFFS2 for NAND.
2006-06-21 14:15 ` Claudio Lanconelli
@ 2006-06-21 18:19 ` Thomas Gleixner
0 siblings, 0 replies; 16+ messages in thread
From: Thomas Gleixner @ 2006-06-21 18:19 UTC (permalink / raw)
To: Claudio Lanconelli; +Cc: linux-mtd
Claudio,
On Wed, 2006-06-21 at 16:15 +0200, Claudio Lanconelli wrote:
> Are you sure about block 0 marked bad for protection? I read in
> smartmedia specification by Toshiba that SSFDC header called CIS/IDI is
> located on the first good block. Here the sentence:
>
> "The CIS/IDI Field is placed in physical block 0.
> If physical block 0 is found to be a defective block, the area is placed
> in the
> first normal block that is found after physical block 0.
> Irrespective of the page size, only one block is assigned."
> [...]
> "The indication of a defective block and logical block arrangement in
> the data
> area will be set in the redundancy area.
> In the case of the 512+16 bytes/page models, the 6th byte (byte address
> 517th)
> in all pages in the redundant section contains two or more “0” bits to
> indicate
> a defective block."
ok
> My code follow these instructions on the SSFDC header location, is it
> correct?
> Anyone who knows it for sure?
No, this was just from the top of my head.
> Another question about the ECC placement. I think the MTD default ECC
> bytes placement
> in the redundant area is not suitable for SSFDC. I found the
> CONFIG_MTD_NAND_ECC_SMC
> in mtd configuration, but it's not used anywhere in the code. How can
> the ssfdc_ro layer tell
> to use smartmedia ECC bytes placement?
Well, if you use the device with SmartMedia FAT you probably want to
enable this switch and use the default configuration. I removed that
client supplied ECC scheme setting, as it turned out to be a complete
nightmare.
tglx
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2006-06-21 18:17 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-05-28 2:58 FAT vs jFFS2 for NAND Han Chang
2006-05-28 5:54 ` Charles Manning
2006-06-15 0:34 ` Han Chang
2006-06-15 7:53 ` Thomas Gleixner
2006-06-19 18:31 ` Han Chang
2006-06-19 18:38 ` Thomas Gleixner
2006-06-19 20:23 ` David Woodhouse
2006-06-19 21:10 ` Charles Manning
2006-06-20 11:31 ` Claudio Lanconelli
2006-06-20 12:30 ` David Woodhouse
2006-06-20 13:25 ` Claudio Lanconelli
2006-06-20 13:52 ` Thomas Gleixner
2006-06-20 17:26 ` Claudio Lanconelli
2006-06-20 17:41 ` Thomas Gleixner
2006-06-21 14:15 ` Claudio Lanconelli
2006-06-21 18:19 ` Thomas Gleixner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox