0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/init.h>
0013 #include <linux/slab.h>
0014 #include <linux/hdreg.h>
0015 #include <linux/mtd/mtd.h>
0016 #include <linux/mtd/rawnand.h>
0017 #include <linux/mtd/blktrans.h>
0018
0019 struct ssfdcr_record {
0020 struct mtd_blktrans_dev mbd;
0021 int usecount;
0022 unsigned char heads;
0023 unsigned char sectors;
0024 unsigned short cylinders;
0025 int cis_block;
0026 int erase_size;
0027 unsigned short *logic_block_map;
0028
0029 int map_len;
0030 };
0031
0032 #define SSFDCR_MAJOR 257
0033 #define SSFDCR_PARTN_BITS 3
0034
0035 #define SECTOR_SIZE 512
0036 #define SECTOR_SHIFT 9
0037 #define OOB_SIZE 16
0038
0039 #define MAX_LOGIC_BLK_PER_ZONE 1000
0040 #define MAX_PHYS_BLK_PER_ZONE 1024
0041
0042 #define KiB(x) ( (x) * 1024L )
0043 #define MiB(x) ( KiB(x) * 1024L )
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054 typedef struct {
0055 unsigned long size;
0056 unsigned short cyl;
0057 unsigned char head;
0058 unsigned char sec;
0059 } chs_entry_t;
0060
0061
0062 static const chs_entry_t chs_table[] = {
0063 { MiB( 1), 125, 4, 4 },
0064 { MiB( 2), 125, 4, 8 },
0065 { MiB( 4), 250, 4, 8 },
0066 { MiB( 8), 250, 4, 16 },
0067 { MiB( 16), 500, 4, 16 },
0068 { MiB( 32), 500, 8, 16 },
0069 { MiB( 64), 500, 8, 32 },
0070 { MiB(128), 500, 16, 32 },
0071 { 0 },
0072 };
0073
0074 static int get_chs(unsigned long size, unsigned short *cyl, unsigned char *head,
0075 unsigned char *sec)
0076 {
0077 int k;
0078 int found = 0;
0079
0080 k = 0;
0081 while (chs_table[k].size > 0 && size > chs_table[k].size)
0082 k++;
0083
0084 if (chs_table[k].size > 0) {
0085 if (cyl)
0086 *cyl = chs_table[k].cyl;
0087 if (head)
0088 *head = chs_table[k].head;
0089 if (sec)
0090 *sec = chs_table[k].sec;
0091 found = 1;
0092 }
0093
0094 return found;
0095 }
0096
0097
0098 static const uint8_t cis_numbers[] = {
0099 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
0100 };
0101
0102
0103 static int get_valid_cis_sector(struct mtd_info *mtd)
0104 {
0105 int ret, k, cis_sector;
0106 size_t retlen;
0107 loff_t offset;
0108 uint8_t *sect_buf;
0109
0110 cis_sector = -1;
0111
0112 sect_buf = kmalloc(SECTOR_SIZE, GFP_KERNEL);
0113 if (!sect_buf)
0114 goto out;
0115
0116
0117
0118
0119
0120
0121 for (k = 0, offset = 0; k < 4; k++, offset += mtd->erasesize) {
0122 if (mtd_block_isbad(mtd, offset)) {
0123 ret = mtd_read(mtd, offset, SECTOR_SIZE, &retlen,
0124 sect_buf);
0125
0126
0127 if (ret < 0 || retlen != SECTOR_SIZE) {
0128 printk(KERN_WARNING
0129 "SSFDC_RO:can't read CIS/IDI sector\n");
0130 } else if (!memcmp(sect_buf, cis_numbers,
0131 sizeof(cis_numbers))) {
0132
0133 cis_sector = (int)(offset >> SECTOR_SHIFT);
0134 } else {
0135 pr_debug("SSFDC_RO: CIS/IDI sector not found"
0136 " on %s (mtd%d)\n", mtd->name,
0137 mtd->index);
0138 }
0139 break;
0140 }
0141 }
0142
0143 kfree(sect_buf);
0144 out:
0145 return cis_sector;
0146 }
0147
0148
0149 static int read_physical_sector(struct mtd_info *mtd, uint8_t *sect_buf,
0150 int sect_no)
0151 {
0152 int ret;
0153 size_t retlen;
0154 loff_t offset = (loff_t)sect_no << SECTOR_SHIFT;
0155
0156 ret = mtd_read(mtd, offset, SECTOR_SIZE, &retlen, sect_buf);
0157 if (ret < 0 || retlen != SECTOR_SIZE)
0158 return -1;
0159
0160 return 0;
0161 }
0162
0163
0164 static int read_raw_oob(struct mtd_info *mtd, loff_t offs, uint8_t *buf)
0165 {
0166 struct mtd_oob_ops ops;
0167 int ret;
0168
0169 ops.mode = MTD_OPS_RAW;
0170 ops.ooboffs = 0;
0171 ops.ooblen = OOB_SIZE;
0172 ops.oobbuf = buf;
0173 ops.datbuf = NULL;
0174
0175 ret = mtd_read_oob(mtd, offs, &ops);
0176 if (ret < 0 || ops.oobretlen != OOB_SIZE)
0177 return -1;
0178
0179 return 0;
0180 }
0181
0182
0183 static int get_parity(int number, int size)
0184 {
0185 int k;
0186 int parity;
0187
0188 parity = 1;
0189 for (k = 0; k < size; k++) {
0190 parity += (number >> k);
0191 parity &= 1;
0192 }
0193 return parity;
0194 }
0195
0196
0197 static int get_logical_address(uint8_t *oob_buf)
0198 {
0199 int block_address, parity;
0200 int offset[2] = {6, 11};
0201 int j;
0202 int ok = 0;
0203
0204
0205
0206
0207
0208
0209 for (j = 0; j < ARRAY_SIZE(offset); j++) {
0210 block_address = ((int)oob_buf[offset[j]] << 8) |
0211 oob_buf[offset[j]+1];
0212
0213
0214 if ((block_address & ~0x7FF) == 0x1000) {
0215 parity = block_address & 0x01;
0216 block_address &= 0x7FF;
0217 block_address >>= 1;
0218
0219 if (get_parity(block_address, 10) != parity) {
0220 pr_debug("SSFDC_RO: logical address field%d"
0221 "parity error(0x%04X)\n", j+1,
0222 block_address);
0223 } else {
0224 ok = 1;
0225 break;
0226 }
0227 }
0228 }
0229
0230 if (!ok)
0231 block_address = -2;
0232
0233 pr_debug("SSFDC_RO: get_logical_address() %d\n",
0234 block_address);
0235
0236 return block_address;
0237 }
0238
0239
0240 static int build_logical_block_map(struct ssfdcr_record *ssfdc)
0241 {
0242 unsigned long offset;
0243 uint8_t oob_buf[OOB_SIZE];
0244 int ret, block_address, phys_block;
0245 struct mtd_info *mtd = ssfdc->mbd.mtd;
0246
0247 pr_debug("SSFDC_RO: build_block_map() nblks=%d (%luK)\n",
0248 ssfdc->map_len,
0249 (unsigned long)ssfdc->map_len * ssfdc->erase_size / 1024);
0250
0251
0252 for (phys_block = ssfdc->cis_block + 1; phys_block < ssfdc->map_len;
0253 phys_block++) {
0254 offset = (unsigned long)phys_block * ssfdc->erase_size;
0255 if (mtd_block_isbad(mtd, offset))
0256 continue;
0257
0258 ret = read_raw_oob(mtd, offset, oob_buf);
0259 if (ret < 0) {
0260 pr_debug("SSFDC_RO: mtd read_oob() failed at %lu\n",
0261 offset);
0262 return -1;
0263 }
0264 block_address = get_logical_address(oob_buf);
0265
0266
0267 if (block_address >= 0 &&
0268 block_address < MAX_LOGIC_BLK_PER_ZONE) {
0269 int zone_index;
0270
0271 zone_index = phys_block / MAX_PHYS_BLK_PER_ZONE;
0272 block_address += zone_index * MAX_LOGIC_BLK_PER_ZONE;
0273 ssfdc->logic_block_map[block_address] =
0274 (unsigned short)phys_block;
0275
0276 pr_debug("SSFDC_RO: build_block_map() phys_block=%d,"
0277 "logic_block_addr=%d, zone=%d\n",
0278 phys_block, block_address, zone_index);
0279 }
0280 }
0281 return 0;
0282 }
0283
0284 static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
0285 {
0286 struct ssfdcr_record *ssfdc;
0287 int cis_sector;
0288
0289
0290 if (!mtd_type_is_nand(mtd) || mtd->oobsize != OOB_SIZE ||
0291 mtd->size > UINT_MAX)
0292 return;
0293
0294
0295 cis_sector = get_valid_cis_sector(mtd);
0296 if (cis_sector == -1)
0297 return;
0298
0299 ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
0300 if (!ssfdc)
0301 return;
0302
0303 ssfdc->mbd.mtd = mtd;
0304 ssfdc->mbd.devnum = -1;
0305 ssfdc->mbd.tr = tr;
0306 ssfdc->mbd.readonly = 1;
0307
0308 ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT);
0309 ssfdc->erase_size = mtd->erasesize;
0310 ssfdc->map_len = (u32)mtd->size / mtd->erasesize;
0311
0312 pr_debug("SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n",
0313 ssfdc->cis_block, ssfdc->erase_size, ssfdc->map_len,
0314 DIV_ROUND_UP(ssfdc->map_len, MAX_PHYS_BLK_PER_ZONE));
0315
0316
0317 ssfdc->heads = 16;
0318 ssfdc->sectors = 32;
0319 get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors);
0320 ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) /
0321 ((long)ssfdc->sectors * (long)ssfdc->heads));
0322
0323 pr_debug("SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n",
0324 ssfdc->cylinders, ssfdc->heads , ssfdc->sectors,
0325 (long)ssfdc->cylinders * (long)ssfdc->heads *
0326 (long)ssfdc->sectors);
0327
0328 ssfdc->mbd.size = (long)ssfdc->heads * (long)ssfdc->cylinders *
0329 (long)ssfdc->sectors;
0330
0331
0332 ssfdc->logic_block_map =
0333 kmalloc_array(ssfdc->map_len,
0334 sizeof(ssfdc->logic_block_map[0]), GFP_KERNEL);
0335 if (!ssfdc->logic_block_map)
0336 goto out_err;
0337 memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) *
0338 ssfdc->map_len);
0339
0340
0341 if (build_logical_block_map(ssfdc) < 0)
0342 goto out_err;
0343
0344
0345 if (add_mtd_blktrans_dev(&ssfdc->mbd))
0346 goto out_err;
0347
0348 printk(KERN_INFO "SSFDC_RO: Found ssfdc%c on mtd%d (%s)\n",
0349 ssfdc->mbd.devnum + 'a', mtd->index, mtd->name);
0350 return;
0351
0352 out_err:
0353 kfree(ssfdc->logic_block_map);
0354 kfree(ssfdc);
0355 }
0356
0357 static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
0358 {
0359 struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
0360
0361 pr_debug("SSFDC_RO: remove_dev (i=%d)\n", dev->devnum);
0362
0363 del_mtd_blktrans_dev(dev);
0364 kfree(ssfdc->logic_block_map);
0365 }
0366
0367 static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
0368 unsigned long logic_sect_no, char *buf)
0369 {
0370 struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
0371 int sectors_per_block, offset, block_address;
0372
0373 sectors_per_block = ssfdc->erase_size >> SECTOR_SHIFT;
0374 offset = (int)(logic_sect_no % sectors_per_block);
0375 block_address = (int)(logic_sect_no / sectors_per_block);
0376
0377 pr_debug("SSFDC_RO: ssfdcr_readsect(%lu) sec_per_blk=%d, ofst=%d,"
0378 " block_addr=%d\n", logic_sect_no, sectors_per_block, offset,
0379 block_address);
0380
0381 BUG_ON(block_address >= ssfdc->map_len);
0382
0383 block_address = ssfdc->logic_block_map[block_address];
0384
0385 pr_debug("SSFDC_RO: ssfdcr_readsect() phys_block_addr=%d\n",
0386 block_address);
0387
0388 if (block_address < 0xffff) {
0389 unsigned long sect_no;
0390
0391 sect_no = (unsigned long)block_address * sectors_per_block +
0392 offset;
0393
0394 pr_debug("SSFDC_RO: ssfdcr_readsect() phys_sect_no=%lu\n",
0395 sect_no);
0396
0397 if (read_physical_sector(ssfdc->mbd.mtd, buf, sect_no) < 0)
0398 return -EIO;
0399 } else {
0400 memset(buf, 0xff, SECTOR_SIZE);
0401 }
0402
0403 return 0;
0404 }
0405
0406 static int ssfdcr_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
0407 {
0408 struct ssfdcr_record *ssfdc = (struct ssfdcr_record *)dev;
0409
0410 pr_debug("SSFDC_RO: ssfdcr_getgeo() C=%d, H=%d, S=%d\n",
0411 ssfdc->cylinders, ssfdc->heads, ssfdc->sectors);
0412
0413 geo->heads = ssfdc->heads;
0414 geo->sectors = ssfdc->sectors;
0415 geo->cylinders = ssfdc->cylinders;
0416
0417 return 0;
0418 }
0419
0420
0421
0422
0423
0424
0425
0426 static struct mtd_blktrans_ops ssfdcr_tr = {
0427 .name = "ssfdc",
0428 .major = SSFDCR_MAJOR,
0429 .part_bits = SSFDCR_PARTN_BITS,
0430 .blksize = SECTOR_SIZE,
0431 .getgeo = ssfdcr_getgeo,
0432 .readsect = ssfdcr_readsect,
0433 .add_mtd = ssfdcr_add_mtd,
0434 .remove_dev = ssfdcr_remove_dev,
0435 .owner = THIS_MODULE,
0436 };
0437
0438 static int __init init_ssfdcr(void)
0439 {
0440 printk(KERN_INFO "SSFDC read-only Flash Translation layer\n");
0441
0442 return register_mtd_blktrans(&ssfdcr_tr);
0443 }
0444
0445 static void __exit cleanup_ssfdcr(void)
0446 {
0447 deregister_mtd_blktrans(&ssfdcr_tr);
0448 }
0449
0450 module_init(init_ssfdcr);
0451 module_exit(cleanup_ssfdcr);
0452
0453 MODULE_LICENSE("GPL");
0454 MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
0455 MODULE_DESCRIPTION("Flash Translation Layer for read-only SSFDC SmartMedia card");