A linux compact flash block driver implements the compact flash hardware operation and block layer, which works on PC Card memory mode and bases on the ata driver and cfspec 4.1. The linux kernel version is linux 2.6.19.
/***********************************************************************
* cfcard.c : Compact flash block driver, complying with CF 4.1 specification.
* This driver implements the compact flash hardware operation and block layer.
* It works on PC Card memory mode
*
* Changelog:
* Jerry.du -- 12/24/2008, writes the driver based on the ata driver
*
************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "partition_table.h" /* which include partition table definition, project related */
#include "cfcard.h"
#define MODULE_NAME "cfcard"
//#define DEBUG
#undef TRACE
#ifdef DEBUG
#ifdef __KERNEL__
#define TRACE printk(KERN_EMRG "%s -- %s(): %d\n", MODULE_NAME, __FUNCTION__, __LINE__)
#else
#define TRACE printf("%s -- %s(): %d\n", MODULE_NAME, __FUNCTION__, __LINE__)
#endif /* __KERNEL__ */
#else
#define TRACE
#endif /* DEBUG */
static CF_INFO_T *cfcard_info;
static int cfcard_device_nums = 0;
static void __iomem *cfcard_gpio_base = NULL;
static CFCARD_DEVICE_INFO_T cf_device_info[CFCARD_DEVICE_MAXNUM] = {
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}},
{0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}, {0, 0, {'\0'}}
};
#define CF_REGISTER(x) ((volatile u8 *)(cfcard_info->reg_base + (x)))
#define CF_ATTRIBUTE(x) ((volatile u8 *)(cfcard_info->attr_base + (x)))
#define CF_BUFFER ((volatile u32 *)((unsigned long)cfcard_info->ram_base))
#define cfcard_outb(a, b) (*(volatile u8 *)(cfcard_info->reg_base + (b)) = (a))
#define cfcard_inb(a) ((u8)*(volatile u8 *)(cfcard_info->reg_base + (a)))
static int cfcard_hw_init(CF_INFO_T *cf_info);
static int cfcard_identify(CF_INFO_T *cf_info);
static inline int cfcard_isready(CF_INFO_T *cf_info);
static inline int cfcard_wait4ready(CF_INFO_T *cf_info, int to_us, u8 status_mask);
static int cfcard_set_cmd(CF_INFO_T *cf_info);
static int cfcard_do_trans(CF_INFO_T *cf_info);
static int cfcard_do_state(CF_INFO_T *cf_info);
static u32 cfcard_do_transfer(CF_INFO_T *cf_info, sector_t sector,
unsigned long nsect, u8 *buffer, int is_write);
static int cfcard_do_reset(CF_INFO_T *cf_info);
static int cfcard_detect(void);
static void cfcard_get_status(unsigned long dev_l);
static int cfcard_bdev_release(struct inode *inode, struct file *filp);
static int cfcard_bdev_transfer(CF_DEVICE *dev, sector_t sector,
unsigned long nsect, char *buffer, int is_write);
static void cfcard_bdev_request(request_queue_t *q);
static int cfcard_bdev_open(struct inode *inode, struct file *filp);
static int cfcard_bdev_setup_device(CF_DEVICE *dev, int which);
static int __init cfcard_bdev_init(void);
static void cfcard_bdev_cleanup(void);
static int cfcard_hw_init(CF_INFO_T *cf_info)
{
int ret = RC_ERROR;
u8 regv;
KDEBUG("cfcard_hw_init\n");
TRACE;
cfcard_gpio_base = ioremap(CFCARD_GPIO_BASE, CFCARD_GPIO_SIZE);
if (cfcard_gpio_base == NULL)
goto MEM_ERR;
TRACE;
cf_info->base = ioremap(CFCARD_BASE_ADDR, CFCARD_BASE_SIZE);
if (cf_info->base == NULL)
goto MEM_ERR;
KDEBUG("Ioremap:cfcard_cf_base = 0x%x\n", (u32)cf_info->base);
cf_info->attr_base = cf_info->base + CF_ATTRIBUTE_OFFSET;
cf_info->reg_base = cf_info->base + CF_REGISTER_OFFSET;
cf_info->ram_base = cf_info->base + CF_RAM_OFFSET;
TRACE;
if (cfcard_detect() == RC_ERROR) {
printk(KERN_ERR "cf card is not inserted!\n");
goto END;
}
TRACE;
if (cfcard_do_reset(cf_info) != CF_TRANS_OK) {
printk(KERN_ERR "cf reset failed\n");
goto END;
}
#if 0 //for test
printk("*CF_ATTRIBUTE(0) 0x%02x\n", *CF_ATTRIBUTE(0));
printk("*CF_ATTRIBUTE(2) 0x%02x\n", *CF_ATTRIBUTE(2));
printk("*CF_ATTRIBUTE(4) 0x%02x\n", *CF_ATTRIBUTE(4));
printk("*CF_ATTRIBUTE(6) 0x%02x\n", *CF_ATTRIBUTE(6));
printk("CF_REG_FR 0x%02x\n", cfcard_inb(CF_REG_FR));
printk("CF_REG_SC 0x%02x\n", cfcard_inb(CF_REG_SC));
printk("CF_REG_SN 0x%02x\n", cfcard_inb(CF_REG_SN));
printk("CF_REG_CL 0x%02x\n", cfcard_inb(CF_REG_CL));
printk("CF_REG_CH 0x%02x\n", cfcard_inb(CF_REG_CH));
printk("CF_REG_DH 0x%02x\n", cfcard_inb(CF_REG_DH));
printk("CF_REG_ST 0x%02x\n", cfcard_inb(CF_REG_ST));
printk("CF_REG_ER 0x%02x\n", cfcard_inb(CF_REG_ER));
printk("CF_REG_DC 0x%02x\n", cfcard_inb(CF_REG_DC));
printk("CF_REG_CAR 0x%02x\n", cfcard_inb(0x0f));
printk("\n");
#endif
TRACE;
init_timer(&cf_info->status_timer);
cf_info->status_timer.function = cfcard_get_status;
cf_info->status_timer.data = (unsigned long)cf_info;
cf_info->status_timer.expires = jiffies + CFCARD_TIMEOUT;
add_timer(&cf_info->status_timer);
TRACE;
cf_info->block_size = 1;
cfcard_outb(0x00, CF_REG_DH);
/* cfspec 4.1 page 116, diagostics code 0x01 means that no error detected */
TRACE;
cfcard_outb(CF_CMD_EXEC_DRIVE_DIAG, CF_REG_ST);
mdelay(500);
regv = cfcard_inb(CF_REG_FR);
if (regv != 0x01) {
printk(KERN_ERR "cf card diag error %x\n", regv);
goto END;
}
TRACE;
ret = cfcard_identify(cf_info);
if (!ret) {
printk(KERN_ERR "cfcard_id_device failed!\n");
goto END;
}
return RC_OK;
MEM_ERR:
printk(KERN_ERR "memory error!\n");
END:
if (cfcard_gpio_base)
iounmap(cfcard_gpio_base);
if (cf_info->base)
iounmap(cf_info->base);
del_timer(&cf_info->status_timer);
return ret;
}
static int cfcard_identify(CF_INFO_T *cf_info)
{
u8 sbuf[CF_SECT_SIZE];
int ret, vendor;
char tstr[21]; //serial number
BUG_ON(cf_info->tstate != TS_IDLE);
TRACE;
memset(sbuf, 0, sizeof(sbuf));
spin_lock(&cf_info->lock);
cf_info->tbuf = (char *)sbuf;
cf_info->tbuf_size = CF_SECT_SIZE;
cf_info->tsect_start = 0;
cf_info->tsector_count = 0;
cf_info->tsectors_left = 1;
cf_info->tread = 1;
cf_info->tcmd = CF_CMD_IDENTIFY_DRIVE;
spin_unlock(&cf_info->lock);
KDEBUG("cfcard_identify tsect_start %llx tsector_count %ld tsectors_left %ld tstate %d tread %ld tcmd %ld\n",
cf_info->tsect_start, cf_info->tsector_count, cf_info->tsectors_left,
cf_info->tstate, cf_info->tread, cf_info->tcmd);
printk(KERN_INFO "%s: identify drive..\n", MODULE_NAME);
ret = cfcard_do_state(cf_info);
if (ret != CF_TRANS_OK) {
printk(KERN_ERR "%s: BUG: identify failed\n", MODULE_NAME);
return 0;
}
TRACE;
vendor = (sbuf[1] << 8) | sbuf[0];
cf_info->head = (sbuf[109] << 8) | sbuf[108];
cf_info->cyl = (sbuf[111] << 8) | sbuf[110];
cf_info->spt = (sbuf[113] << 8) | sbuf[112];
cf_info->sectors = ((sbuf[117] << 24) | (sbuf[116] << 16) | (sbuf[115] << 8) | sbuf[114]);
memcpy(tstr, &sbuf[20], 20);
tstr[21] = 0;
printk(KERN_INFO "%s: %s detected, C/H/S=%ld/%ld/%ld sectors=%lu (%luMB) Serial=%s\n",
MODULE_NAME, (vendor == 0x848A ? "CF card" : "ATA drive"), cf_info->cyl, cf_info->head,
cf_info->spt, cf_info->sectors, cf_info->sectors >> 11, tstr);
return 1;
}
/* CF ready line is set high when the compact flash is ready to accept a new transfer operation */
static inline int cfcard_isready(CF_INFO_T *cf_info)
{
return (*CFCARD_GPIO_GPINDR(cfcard_gpio_base) & CF_READY);
}
static inline int cfcard_wait4ready(CF_INFO_T *cf_info, int to_us, u8 status_mask)
{
int us_passed = 0;
u8 regv;
if (!cfcard_isready(cf_info)) {
/* busy must appear within 400ns,
* but it may dissapear before we see it
* => must not wait for busy in a loop
*/
ndelay(400);
}
/*
* cfspec 4.1 page 128, write sector is class 2 of command,
* cf card set up the sector buffer for a write operation and set DRQ within 700 usec
*/
if (!cf_info->tread)
udelay(700);
while (us_passed++ < to_us) {
regv = cfcard_inb(CF_REG_ST);
if ((regv & 0xff) == status_mask)
return CF_TRANS_OK;
if (us_passed == 1 * CFCARD_SECS)
printk(KERN_WARNING "%s: wait for ready %dus..\n", MODULE_NAME, to_us);
udelay(1);
}
printk(KERN_ERR "%s: wait for ready timeout (%dus)"
", status 0x%02x\n", MODULE_NAME, to_us, cfcard_inb(CF_REG_ST));
cfcard_do_reset(cf_info);
return CF_TRANS_FAILED;
}
static int cfcard_set_cmd(CF_INFO_T *cf_info)
{
/* sector_count should be <=24 bits.. */
BUG_ON(cf_info->tsect_start >= 0x10000000);
KDEBUG("cfcard_set_cmd tsect_start %llx tsector_count %ld tsectors_left %ld tstate %d tread %ld tcmd %x\n",
cf_info->tsect_start, cf_info->tsector_count, cf_info->tsectors_left,
cf_info->tstate, cf_info->tread, cf_info->tcmd);
cfcard_outb(0x00, CF_REG_FR);
if (cf_info->tsector_count) {
cfcard_outb(cf_info->tsector_count & 0xff, CF_REG_SC);
cfcard_outb(cf_info->tsect_start & 0xff, CF_REG_SN);
cfcard_outb((cf_info->tsect_start >> 8) & 0xff, CF_REG_CL);
cfcard_outb((cf_info->tsect_start >> 16) & 0xff, CF_REG_CH);
}
cfcard_outb(((cf_info->tsect_start >> 24) & 0x0f) | CF_REG_DH_BASE | CF_REG_DH_LBA, CF_REG_DH);
cfcard_outb(cf_info->tcmd, CF_REG_CMD);
return cfcard_wait4ready(cf_info, 1 * CFCARD_SECS,
CF_REG_ST_RDY | CF_REG_ST_DSC | CF_REG_ST_DRQ);
}
static int cfcard_do_trans(CF_INFO_T *cf_info)
{
int ret;
u8 st;
int errId;
u32 *qbuf, *qend;
volatile u32 *ram_start;
//printk("cfcard_do_trans: %ld sectors left\n", cf_info->tsectors_left);
TRACE;
while (cf_info->tsectors_left) {
st = cfcard_inb(CF_REG_ST);
if (!(st & CF_REG_ST_DRQ)) {
printk(KERN_ERR "%s: do_trans without DRQ (status 0x%x)!\n", MODULE_NAME, st);
if (st & CF_REG_ST_ERR) {
errId = cfcard_inb(CF_REG_ER);
printk(KERN_ERR "%s: %s error, status 0x%x, errid 0x%x\n",
MODULE_NAME, (cf_info->tread ? "read" : "write"), st, errId);
}
return CF_TRANS_FAILED;
}
do { /* fill/read the buffer of one block */
ram_start = CF_BUFFER;
qbuf = (u32 *)cf_info->tbuf;
qend = qbuf + CF_SECT_SIZE / sizeof(u32);
if (cf_info->tread) {
while (qbuf != qend)
*qbuf++ = *ram_start++;
}
else {
while (qbuf != qend)
*ram_start++ = *qbuf++;
}
spin_lock(&cf_info->lock);
cf_info->tsectors_left--;
cf_info->tbuf += CF_SECT_SIZE;
cf_info->tbuf_size -= CF_SECT_SIZE;
spin_unlock(&cf_info->lock);
if (cf_info->tsectors_left) {
ret = cfcard_wait4ready(cf_info, 3 * CFCARD_SECS,
CF_REG_ST_RDY | CF_REG_ST_DSC | CF_REG_ST_DRQ);
if (ret != CF_TRANS_OK)
return ret;
}
else {
ret = cfcard_wait4ready(cf_info, 3 * CFCARD_SECS,
CF_REG_ST_RDY | CF_REG_ST_DSC);
if (ret != CF_TRANS_OK)
return ret;
}
} while (cf_info->tsectors_left > 0 && cf_info->tbuf_size > 0);
};
TRACE;
st = cfcard_inb(CF_REG_ST);
if (st & (CF_REG_ST_DRQ | CF_REG_ST_DWF | CF_REG_ST_ERR)) {
if (st & CF_REG_ST_DRQ) {
printk(KERN_ERR "%s: DRQ after all %ld sectors are %s"
", status 0x%x\n", MODULE_NAME, cf_info->tsector_count,
(cf_info->tread ? "read" : "written"), st);
} else if (st & CF_REG_ST_DWF) {
printk(KERN_ERR "%s: write fault, status 0x%x\n", MODULE_NAME, st);
} else {
errId = cfcard_inb(CF_REG_ER);
printk(KERN_ERR "%s: %s error, status 0x%x, errid 0x%x\n",
MODULE_NAME, (cf_info->tread ? "read" : "write"), st, errId);
}
return CF_TRANS_FAILED;
}
//printk("cfcard_do_trans: end\n");
return CF_TRANS_OK;
}
static int cfcard_do_state(CF_INFO_T *cf_info)
{
int ret;
//printk("cfcard_do_state begin %d\n", cf_info->tstate);
TRACE;
switch (cf_info->tstate) {
case TS_IDLE:
//printk("TS_IDLE\n");
cf_info->tstate = TS_READY;
case TS_READY:
//printk("TS_READY\n");
cf_info->tstate = TS_TRANS;
ret = cfcard_set_cmd(cf_info);
if (ret != CF_TRANS_OK)
break;
case TS_TRANS:
//printk("TS_TRANS\n");
ret = cfcard_do_trans(cf_info);
//printk("cfcard_do_trans end %d\n", ret);
break;
default:
printk(KERN_ERR "%s: BUG: unknown tstate %d\n", MODULE_NAME, cf_info->tstate);
return CF_TRANS_FAILED;
}
if (cf_info->tstate != TS_TRANS)
printk(KERN_ERR "cfcard_do_state error %d\n", cf_info->tstate);
cf_info->tstate = TS_IDLE;
//printk("cfcard_do_state end %d\n", cf_info->tstate);
return ret;
}
static u32 cfcard_do_transfer(CF_INFO_T *cf_info, sector_t sector,
unsigned long nsect, u8 *buffer, int is_write)
{
BUG_ON(cf_info->tstate != TS_IDLE);
TRACE;
//printk(KERN_INFO "cfcard_do_transfer sector %llx nsect %ld is_write %d\n\n",
//sector, nsect, is_write);
if (nsect > CF_MAX_SECT_PER_CMD) {
printk(KERN_WARNING "%s: sector count %lu out of range\n", MODULE_NAME, nsect);
return CF_TRANS_FAILED;
}
if (sector + nsect > cf_info->sectors) {
printk(KERN_WARNING "%s: sector %llx out of range\n", MODULE_NAME, sector);
return CF_TRANS_FAILED;
}
TRACE;
spin_lock(&cf_info->lock);
cf_info->tbuf = buffer;
cf_info->tbuf_size = nsect * CF_SECT_SIZE;
cf_info->tsect_start = sector;
cf_info->tsector_count = nsect;
cf_info->tsectors_left = cf_info->tsector_count;
cf_info->tread = (is_write) ? 0 : 1;
cf_info->tcmd = (cf_info->block_size == 1 ?
(is_write ? CF_CMD_WRITE_SECTORS : CF_CMD_READ_SECTORS) :
(is_write ? CF_CMD_WRITE_MULTIPLE : CF_CMD_READ_MULTIPLE));
spin_unlock(&cf_info->lock);
TRACE;
return cfcard_do_state(cf_info);
}
static int cfcard_do_reset(CF_INFO_T *cf_info)
{
int ret = CF_TRANS_OK;
printk(KERN_INFO "%s: resetting..\n", MODULE_NAME);
/* conf5~conf0 is set to 0, using memory mapped mode */
TRACE;
*CF_ATTRIBUTE(0) = 0x80;
mdelay(20);
*CF_ATTRIBUTE(0) = 0x00;
/* disable the interrupt */
TRACE;
cfcard_outb(CF_REG_DC_SRST | CF_REG_DC_IEN, CF_REG_DC);
udelay(100);
cfcard_outb(CF_REG_DC_IEN, CF_REG_DC);
TRACE;
ret = cfcard_wait4ready(cf_info, 2 * CFCARD_SECS, CF_REG_ST_RDY | CF_REG_ST_DSC);
printk(KERN_INFO "done\n");
return ret;
}
static int cfcard_detect(void)
{
volatile u32 cf_detect = *CFCARD_GPIO_GPINDR(cfcard_gpio_base);
KDEBUG("cfcard_detect\n");
TRACE;
//printk("CFCARD_GPIO_GPINDR 0x%08x\n", *CFCARD_GPIO_GPINDR(cfcard_gpio_base));
if ((cf_detect & (CF_CD1 | CF_CD2)) == 0) {
KDEBUG("cfcard_detect success\n");
return RC_OK;
}
else {
printk(KERN_ERR "cfcard_detect error\n");
return RC_ERROR;
}
}
/* print out the register temporarily */
static void cfcard_get_status(unsigned long dev_l)
{
#if 0
CF_INFO_T *cfinfo = (CF_INFO_T *)dev_l;
//printk("*CF_ATTRIBUTE(0) 0x%02x\n", *CF_ATTRIBUTE(0));
//printk("*CF_ATTRIBUTE(2) 0x%02x\n", *CF_ATTRIBUTE(2));
//printk("*CF_ATTRIBUTE(4) 0x%02x\n", *CF_ATTRIBUTE(4));
//printk("*CF_ATTRIBUTE(6) 0x%02x\n", *CF_ATTRIBUTE(6));
printk("CF_REG_FR 0x%02x\n", cfcard_inb(CF_REG_FR));
printk("CF_REG_SC 0x%02x\n", cfcard_inb(CF_REG_SC));
printk("CF_REG_SN 0x%02x\n", cfcard_inb(CF_REG_SN));
printk("CF_REG_CL 0x%02x\n", cfcard_inb(CF_REG_CL));
printk("CF_REG_CH 0x%02x\n", cfcard_inb(CF_REG_CH));
printk("CF_REG_DH 0x%02x\n", cfcard_inb(CF_REG_DH));
printk("CF_REG_ST 0x%02x\n", cfcard_inb(CF_REG_ST));
printk("CF_REG_ER 0x%02x\n", cfcard_inb(CF_REG_ER));
printk("CF_REG_DC 0x%02x\n", cfcard_inb(CF_REG_DC));
//printk("CF_REG_CAR 0x%02x\n", cfcard_inb(0x0f));
printk("\n");
cfinfo->status_timer.expires = jiffies + CFCARD_TIMEOUT;
add_timer(&cfinfo->status_timer);
#endif
}
static int cfcard_bdev_release(struct inode *inode, struct file *filp)
{
CF_DEVICE *dev = inode->i_bdev->bd_disk->private_data;
KDEBUG("cfcard_bdev_release\n");
TRACE;
spin_lock(&dev->lock);
dev->users--;
spin_unlock(&dev->lock);
return RC_OK;
}
static int cfcard_bdev_transfer(CF_DEVICE *dev, sector_t sector,
unsigned long nsect, char *buffer, int is_write)
{
sector_t start_sector;
int ret = RC_ERROR;
TRACE;
if (nsect <= 0)
goto END;
start_sector = dev->start_sector + sector;
//printk(KERN_INFO "cfcard_bdev_transfer sector %llx(%lx) nsect %ld is_write %d\n",
//start_sector, dev->start_sector, nsect, is_write);
TRACE;
ret = cfcard_do_transfer(cfcard_info, start_sector, nsect, buffer, is_write);
END:
return ret;
}
static void cfcard_bdev_request(request_queue_t *q)
{
struct request *req = NULL;
CF_DEVICE *dev = NULL;
unsigned retval = 0;
int rw;
TRACE;
while ((req = elv_next_request(q)) != NULL) {
dev = req->rq_disk->private_data;
//printk("dev->start_sector 0x%x\n", dev->start_sector);
if (!blk_fs_request(req)) {
printk(KERN_NOTICE "cfcard_bdev_request: skip non-fs request\n");
end_request(req, QUEUE_REQUEST_FAILED);
continue;
}
if ((req->sector + req->nr_sectors) > get_capacity(req->rq_disk)) {
printk(KERN_NOTICE "Out of range\n");
end_request(req, QUEUE_REQUEST_FAILED);
continue;
}
rw = rq_data_dir(req);
if (rw != READ && rw != WRITE) {
printk(KERN_NOTICE "cfcard_bdev_request: unknown request\n");
end_request(req, QUEUE_REQUEST_FAILED);
continue;
}
//printk("rw-%d,s-%llx,n-%lx,c-%x,hs-%llx,hn-%lx,hc-%x\n", rw,
//req->sector, req->nr_sectors, req->current_nr_sectors,
//req->hard_sector, req->hard_nr_sectors, req->hard_cur_sectors);
TRACE;
if (req->current_nr_sectors > 8)
printk("***%d sectors\n", req->current_nr_sectors);
retval = cfcard_bdev_transfer(dev, req->sector,
req->current_nr_sectors, req->buffer, rw);
if (retval < 0) {
printk(KERN_NOTICE "cfcard_bdev_request: skip failed request\n");
end_request(req, QUEUE_REQUEST_FAILED);
continue;
}
end_request(req, QUEUE_REQUEST_SUCCESS);
}
}
static int cfcard_bdev_open(struct inode *inode, struct file *filp)
{
CF_DEVICE *dev = inode->i_bdev->bd_disk->private_data;
KDEBUG("cfcard_bdev_open\n");
TRACE;
spin_lock(&dev->lock);
dev->users++;
filp->private_data = dev;
spin_unlock(&dev->lock);
return RC_OK;
}
static struct block_device_operations cfcard_fops = {
.owner = THIS_MODULE,
.open = cfcard_bdev_open,
.release = cfcard_bdev_release,
.media_changed = NULL,
.ioctl = NULL,
};
/*
* Set up our internal device.
*/
static int cfcard_bdev_setup_device(CF_DEVICE *dev, int which)
{
CFCARD_DEVICE_INFO_T *ptr_device;
int ret = RC_ERROR;
TRACE;
if (which < 0 || which > cfcard_device_nums) {
printk(KERN_ERR "device num should be 0 to %d\n", cfcard_device_nums);
goto out;
}
ptr_device = (CFCARD_DEVICE_INFO_T *)&cf_device_info[which];
memset(dev, 0, sizeof(CF_DEVICE));
dev->size = HARDSECT_SIZE * ptr_device->nsectors;
spin_lock_init(&dev->lock);
KDEBUG("cfcard_bdev_device device %d size %d\n", which, dev->size);
/*
* The I/O queue, depending on whether we are using our own
* make_request function or not.
*/
TRACE;
dev->queue = blk_init_queue(cfcard_bdev_request, &dev->lock);
if (dev->queue == NULL) {
printk(KERN_ERR "%s: no mem for queue\n", MODULE_NAME);
goto out;
}
blk_queue_hardsect_size(dev->queue, CFCARD_QUEUE_HARD_SECTOR_SIZE);
dev->queue->queuedata = dev;
/*
* And the gendisk structure.
*/
KDEBUG("cfcard_bdev_device alloc_disk\n");
TRACE;
dev->gd = alloc_disk(CFCARD_MINORS);
if (dev->gd == NULL) {
printk(KERN_ERR "alloc_disk failure\n");
goto out;
}
dev->gd->major = CFCARD_MAJOR;
dev->gd->first_minor = which * CFCARD_MINORS;
dev->gd->fops = &cfcard_fops;
dev->gd->queue = dev->queue;
dev->gd->private_data = dev;
dev->start_sector = ptr_device->start_sector;
TRACE;
snprintf(dev->gd->disk_name, 32, ptr_device->device_name);
set_capacity(dev->gd, ptr_device->nsectors * (HARDSECT_SIZE / KERNEL_SECTOR_SIZE));
KDEBUG("cfcard_bdev_device add_disk\n");
TRACE;
add_disk(dev->gd);
ret = RC_OK;
out:
return ret;
}
static int __init cfcard_bdev_init(void)
{
PARTITION_TABLE_T pt_table;
FlashRegion_T *ptr_region;
int ret = RC_ERROR;
int i, j;
KDEBUG("Initialize compact flash block device ...\n");
TRACE;
cfcard_info = (CF_INFO_T *)kmalloc(sizeof(CF_INFO_T), GFP_KERNEL);
if (!cfcard_info)
goto mem_err;
memset(cfcard_info, 0, sizeof(CF_INFO_T));
spin_lock_init(&cfcard_info->lock);
//printk("cfcard_info 0x%08x\n", cfcard_info);
/* initial compact flash hardware */
TRACE;
if (cfcard_hw_init(cfcard_info) != RC_OK) {
printk(KERN_ERR DRIVER_CF_NAME": Unable to init cf hardware\n");
goto out;
}
KDEBUG("cfcard_major is %d, driver_name is %s\n", CFCARD_MAJOR, DRIVER_CF_NAME);
TRACE;
ret = register_blkdev(CFCARD_MAJOR, DRIVER_CF_NAME);
if (ret < 0) {
printk(KERN_ERR DRIVER_CF_NAME": Unable to register block device\n");
goto out;
}
/*
* read partition table information from physical address
*/
TRACE;
memset(&pt_table, 0, sizeof(PARTITION_TABLE_T));
if (flash_get_partitiontable(&pt_table) != RC_OK) {
printk(KERN_ERR "Flash get partition table failed!\n");
goto out;
}
/*
* parse the partition table and setup the mtd partitions structure
*/
TRACE;
j = 0;
for (i = 0; i < MAX_PARTITION_NUMBER; i++) {
ptr_region = &pt_table.flash_region[i];
if (ptr_region->region_type == TYPE_NOT_USED)
continue;
/*
* we only parse "cfcard" typed device, the cfcard device is sized in bytes
* in u-boot-1.3.2, and the linux block driver sizes the disk as sector
*/
if (strcmp(ptr_region->device_name, DEVICE_CFCARD_NAME) == 0) {
snprintf(cf_device_info[j].device_name, 32, ptr_region->region_name);
cf_device_info[j].start_sector = ptr_region->region_base / HARDSECT_SIZE;
cf_device_info[j].nsectors= ptr_region->region_size / HARDSECT_SIZE;
j++;
cfcard_device_nums++;
}
}
if (cfcard_device_nums == 0) {
printk(KERN_ERR "System have no cfcard device!\n");
goto out;
}
TRACE;
cfcard_info->device = (CF_DEVICE *)kmalloc(cfcard_device_nums * sizeof(CF_DEVICE), GFP_KERNEL);
if (cfcard_info->device == NULL) {
printk(KERN_ERR "vmalloc failure.\n");
goto out;
}
TRACE;
for (i = 0; i < cfcard_device_nums; i++)
cfcard_bdev_setup_device(cfcard_info->device + i, i);
KDEBUG("Done!\n");
return RC_OK;
out:
unregister_blkdev(CFCARD_MAJOR, DRIVER_CF_NAME);
mem_err:
if (cfcard_info)
kfree(cfcard_info);
return ret;
}
static void cfcard_bdev_cleanup(void)
{
int i;
KDEBUG("Remove compact flash block device ...\n");
TRACE;
for (i = 0; i < cfcard_device_nums; i++) {
CF_DEVICE *dev = cfcard_info->base + i;
if (dev->gd) {
del_gendisk(dev->gd);
put_disk(dev->gd);
}
if (dev->queue)
blk_cleanup_queue(dev->queue);
}
TRACE;
unregister_blkdev(CFCARD_MAJOR, DRIVER_CF_NAME);
if (cfcard_info->base)
iounmap(cfcard_info->base);
kfree(cfcard_info->device);
del_timer(&cfcard_info->status_timer);
if (cfcard_info)
kfree(cfcard_info);
if (cfcard_gpio_base)
iounmap(cfcard_gpio_base);
KDEBUG("Done!\n");
}
module_init(cfcard_bdev_init);
module_exit(cfcard_bdev_cleanup);
MODULE_DESCRIPTION("Compact flash block driver");
MODULE_AUTHOR("Jerry.du");
MODULE_LICENSE("Dual BSD/GPL");
the attached files are source code.