FAT vs jFFS2 for NAND.
Thomas Gleixner
tglx at linutronix.de
Tue Jun 20 09:52:29 EDT 2006
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 at 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 at 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/
More information about the linux-mtd
mailing list