📄 ssfdc.c
字号:
/* * drivers/mtd/ssfdc.c * * Copyright (C) 2003 Simon Haynes (simon@baydel.con) * Baydel Ltd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This module provides a translation layer, via mtd, for smart * media card access. It essentially enables the possibility * of using cards on a hardware which does not have a hardware translation * layer and interchanging them with hardware that does ie: PC card readers * * I had to write this module for a specific task and in a short timeframe * for this reason I have imposed some restricions to make the job easier. * * To build an compile the driver I added the following lines * to mtd/Config.in * * dep_tristate ' SSFDC support' CONFIG_SSFDC $CONFIG_MTD * * to /mtd/Makefile * * obj-$(CONFIG_SSFDC) += ssfdc.o * * and compiled the kernel via the usual methods. * * I am sure that there are many problems I don't know about but here are * some that I know of * * Currently the driver uses MAJOR number 44 which I think is FTL or NFTL * I did this because I wanted a static number and I didn't know * how to go about getting a new one. This needs addressing * The dev nodes required are like standard. I only use minor 0 * (/dev/ssfdca), and minor 1 (/dev/ssfdca1). * You should be able to run fdisk on /dev/ssfdca and the first partition * is /dev/ssfdca1. There is no working code in the module for changing the * SMC and rebuilding the maps so the card should not be changed once the * module is loaded. At present I only look for 1 partition. But this is a * small commented hack. * * There is no support cards which do not have a 512 byte page size with 16 * bytes of oob and an erase size of 16K. * There are no checks for this at present. In addition the MTD reported size * must be 16M or a multiple. * * Code to handle multiple partitions or multiple cards is incomplete * Need to allocate data buffer and oob buffer on a per partition basis. * As I am only concerned with one partition I will do this if I ever need to. * The cached physical address variable also needs this attention. * * Recently I have started to work on media changes. Some of this is specific * to my hardware and you will see references to pt_ssfdc_smc and smc_status. * This code is incomplete and does not work. I have commented it for the moment * but it should give an indication of what I think is required. Maybe there is * something it mtd that can help * * 17th August 2004 MHB * * Following updating CVS I noticed some single bit data corruption. I believe * that this was down to the fact that I was using mtd->read instead of mtd->read_ecc * and that mtd->read was applying it's own error corretion from the wrong ecc bytes * I have now corrected this. * * During this time I noticed that while in allocate new I only seem to look for blocks * in 1 zone. So this limits the partition size to 16MB with all the other SMC size * restrictions*/#include <linux/config.h>#include <linux/types.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/mtd/mtd.h>#include <linux/mtd/blktrans.h>#include <linux/mtd/nand_ecc.h>#include <linux/sched.h>#include <linux/ptrace.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/major.h>#include <linux/ioctl.h>#include <linux/hdreg.h>#include <linux/list.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#if (LINUX_VERSION_CODE >= 0x20100)#include <linux/vmalloc.h>#endif#if (LINUX_VERSION_CODE >= 0x20303)#include <linux/blkpg.h>#endif#include <asm/semaphore.h>#define SSFDC_FORMAT 1#define PDEBUG(fmt, args...)#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT#if (LINUX_VERSION_CODE < 0x20320)#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn#define blk_init_queue(q, req) q = (req)#define blk_cleanup_queue(q) q = NULL#define request_arg_t void#else#define request_arg_t request_queue_t *q#endif#define TRUE 1#define FALSE 0#define SSFDC_MAJOR 44#define MAJOR_NR SSFDC_MAJOR#define DEVICE_NAME "ssfdc"#define DEVICE_REQUEST do_ssfdc_request#define DEVICE_ON(device)#define DEVICE_OFF(device)#include <linux/blk.h>#include "/home/simon/ebony/dbwhatu/dbwhatu/smccontrol.h"#define ZONE_SIZE (16 * 1024 * 1024)#define SMC_BLOCK_SIZE (16 * 1024)#define SECTOR_SIZE 512#define SECTORS_PER_ZONE (ZONE_SIZE / SECTOR_SIZE)#define BLOCKS_PER_ZONE (ZONE_SIZE / SMC_BLOCK_SIZE)#define SECTORS_PER_BLOCK (SMC_BLOCK_SIZE / SECTOR_SIZE)#define OOB_SIZE 16#define MAX_DEVICES 4#define MAX_PARTITIONS 8#define PARTITION_BITS 3#define MAX_ZONES 8int ssfdc_major = SSFDC_MAJOR;unsigned int ssfdc_cached = 0xFFFFFFFF;static unsigned char ssfdc_scratch[16384];static unsigned char ssfdc_buffer[16];static unsigned char ssfdc_ffoob_buf[OOB_SIZE * SECTORS_PER_BLOCK];static unsigned char ssfdc_oob_buf[OOB_SIZE * SECTORS_PER_BLOCK];static struct nand_oobinfo ssfdc_ffoob_info = { .useecc = 0,};typedef struct minor_t { atomic_t open; int cached; unsigned char * pt_data; unsigned char * pt_oob;} minor_t;typedef struct partition_t { int type; struct mtd_info *mtd; int count; unsigned int *zone; unsigned int zoneCount; minor_t minor[MAX_PARTITIONS]; unsigned int last_written[MAX_ZONES];} partition_t;partition_t SMCParts[MAX_DEVICES];static unsigned char ssfdc_ecc[] = {14, 13, 15, 9, 8, 10};static struct hd_struct ssfdc_hd[MAX_DEVICES * MAX_PARTITIONS];static int ssfdc_sizes[MAX_DEVICES * MAX_PARTITIONS];static int ssfdc_blocksizes[MAX_DEVICES * MAX_PARTITIONS];smc_control * pt_ssfdc_smc;static struct gendisk ssfdc_gendisk = { major: SSFDC_MAJOR, major_name: "ssfdc", minor_shift: PARTITION_BITS, max_p: MAX_PARTITIONS, part: ssfdc_hd, sizes: ssfdc_sizes,};static int ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);static int ssfdc_open(struct inode *inode, struct file *file);static int ssfdc_close(struct inode *inode, struct file *file);static int ssfdc_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks);static int ssfdc_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks);static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block);static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset);static int ssfdc_read_partitions(partition_t * pt_smcpart);static void ssfdc_notify_add(struct mtd_info *mtd);static void ssfdc_notify_remove(struct mtd_info *mtd);static void ssfdc_tables(partition_t * pt_smcpart);static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc);static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone);int ssfdc_parity(int number);static void ssfdc_erase_callback(struct erase_info *erase);static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wq);static struct mtd_notifier ssfdc_notifier = { add: ssfdc_notify_add, remove: ssfdc_notify_remove,};static struct block_device_operations ssfdc_fops = { open: ssfdc_open, release: ssfdc_close, ioctl: ssfdc_ioctl,}; static struct semaphore ssfdc_semaphore;static void ssfdc_notify_add(struct mtd_info *mtd) { if(mtd->index >= 1) return; // Hack to limit SSFDC to 1 partition if( ((mtd->size % ZONE_SIZE) != 0) && (mtd->size < (ZONE_SIZE * MAX_ZONES)) ){ PDEBUG("ssfdc_notify_add : mtd partition %d is not modulus 16M, not SSFDC\n", mtd->index); } else { memset((void *)&SMCParts[mtd->index].type, 0, sizeof(partition_t)); SMCParts[mtd->index].mtd = mtd; SMCParts[mtd->index].count = mtd->index; SMCParts[mtd->index].type = 1; SMCParts[mtd->index].zoneCount = mtd->size / ZONE_SIZE; SMCParts[mtd->index].zone = kmalloc(SMCParts[mtd->index].zoneCount * 8192, GFP_KERNEL); if(!SMCParts[mtd->index].zone) { printk(KERN_NOTICE "ssfdc_notify_add : mtd partition %d, failed to allocate mapping table\n", mtd->index); SMCParts[mtd->index].type = 0; } else { memset((void *)SMCParts[mtd->index].zone, 0xFF, SMCParts[mtd->index].zoneCount * 8192); } ssfdc_read_partitions((partition_t *)&SMCParts[mtd->index].type); } return;}static int ssfdc_read_partitions(partition_t * pt_smcpart) { int whole, i, j, size;//=printk("ssfdc_read_partitions : start\n"); for(i=0; i<MAX_PARTITIONS; i++) if ((atomic_read(&pt_smcpart->minor[i].open) > 1)) {//=printk("ssfdc_read_partitions : part %d busy\n", i); return -EBUSY; }//=printk("ssfdc_read_partitions : tables start\n"); ssfdc_tables(pt_smcpart);//=printk("ssfdc_read_partitions : tables end\n"); whole = pt_smcpart->count << PARTITION_BITS; j = MAX_PARTITIONS - 1; while (j-- > 0) { if (ssfdc_hd[whole+j].nr_sects > 0) { kdev_t rdev = MKDEV(SSFDC_MAJOR, whole+j); invalidate_device(rdev, 1); } ssfdc_hd[whole+j].start_sect = 0; ssfdc_hd[whole+j].nr_sects = 0; } size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; size /= (0x8 * 0x20); size = size * (0x8 * 0x20);//=printk("ssfdc_read_partitions : register start\n"); register_disk(&ssfdc_gendisk, whole >> PARTITION_BITS, MAX_PARTITIONS, &ssfdc_fops, size);//=printk("ssfdc_read_partitions : register end\n"); return 0;}static void ssfdc_notify_remove(struct mtd_info *mtd) {int i, j, whole; i=mtd->index; whole = i << PARTITION_BITS; if(SMCParts[i].mtd == mtd) { if(SMCParts[i].zone)kfree(SMCParts[i].zone); memset((void *)&SMCParts[i].type, 0, sizeof(partition_t)); for (j = 0; j < MAX_PARTITIONS; j++) { if (ssfdc_hd[whole+j].nr_sects > 0) { ssfdc_hd[whole+j].start_sect = 0; ssfdc_hd[whole+j].nr_sects=0; } } return; } return;}static int ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { int minor = MINOR(inode->i_rdev); int ret = -EINVAL; partition_t * pt_smcpart = (partition_t *)&SMCParts[(minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS].type; struct hd_geometry geo; int size;/* unsigned char smc_status; smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); if(!(smc_status & SMC_PRESENT)) { printk("ssfdc : media not present\n"); ret = 1; goto ssfdc_ioctl_error; } if(smc_status & SMC_CHANGED) { out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; ssfdc_read_partitions(pt_smcpart); printk("ssfdc : media change\n"); }*/ switch(cmd) { case HDIO_GETGEO: memset(&geo, 0, sizeof(geo)); size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; size /= (0x8 * 0x20); geo.heads = 0x8; geo.sectors = 0x20; geo.cylinders = size; geo.start = ssfdc_hd[minor].start_sect;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -