📄 iflash2_mtd.c
字号:
printk(", "); printk_size(region.BlockSize); printk(" blocks, %u ns\n", region.AccessSpeed); memset(dev->flash[i], 0, sizeof(struct flash_region_t)); /* Assume 128K blocks, if no geometry info present */ if (region.BlockSize == 1) region.BlockSize = 0x20000; dev->flash[i]->region = region; /* If not Series 100, then we'll use Vpp=12V */ if (region.JedecInfo != 0xaa) dev->vpp = 120; /* All Series 2 cards have 2MB component pairs */ dev->flash[i]->cell_size = 0x200000; i++; ret = CardServices(GetNextRegion, handle, ®ion); } } dev->flash[i] = NULL; } /* flash_config *//*====================================================================== After a card is removed, flash_release() will release the memory window allocated for this socket. ======================================================================*/static void flash_release(u_long arg){ dev_link_t *link = (dev_link_t *)arg; flash_dev_t *dev = link->priv; int i; DEBUG(0, "iflash2_mtd: flash_release(0x%p)\n", link); link->state &= ~DEV_CONFIG; if (link->win) { iounmap(dev->Base); i = MTDHelperEntry(MTDReleaseWindow, link->win); if (i != CS_SUCCESS) cs_error(link->handle, ReleaseWindow, i); } if (dev->vpp_usage == 0) del_timer(&dev->vpp_timeout); vpp_off((u_long)link); for (i = 0; (i < 2*CISTPL_MAX_DEVICES) && dev->flash[i]; i++) kfree(dev->flash[i]); if (link->state & DEV_STALE_LINK) flash_detach(link); } /* flash_release *//*====================================================================== The read request handler. This handler supports suspending current erase requests. Reading from a block that is currently erasing is undefined. ======================================================================*/static int flash_read(dev_link_t *link, char *buf, mtd_request_t *req){ flash_dev_t *dev = (flash_dev_t *)link->priv; flash_region_t *flash; region_info_t *region; mtd_mod_win_t mod; u_int from, length, nb, cell; u_long time; int ret; DEBUG(2, "iflash2_mtd: flash_read(0x%p, 0x%lx, 0x%p, 0x%x, " "0x%x)\n", link, req->MediaID, buf, req->SrcCardOffset, req->TransferLength); flash = (flash_region_t *)(req->MediaID); region = &flash->region; if ((req->SrcCardOffset / region->BlockSize) != ((req->SrcCardOffset+req->TransferLength-1) / region->BlockSize)) return CS_BAD_SIZE; if (region->Attributes & REGION_TYPE_AM) mod.Attributes = WIN_MEMORY_TYPE_AM; else mod.Attributes = WIN_MEMORY_TYPE_CM; mod.AccessSpeed = region->AccessSpeed; /* Suspend an in-progress block erase */ cell = (req->SrcCardOffset - region->CardOffset) / flash->cell_size; if (flash->cell[cell].state & FLASH_ERASING) { if ((flash->cell[cell].erase_addr / region->BlockSize) == (req->SrcCardOffset / region->BlockSize)) { req->Status = MTD_WAITREQ; return CS_BUSY; } link->state |= DEV_BUSY; mod.CardOffset = flash->cell[cell].erase_addr; ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod); if (ret != CS_SUCCESS) goto done; ret = suspend_erase((u_short *)dev->Base); if (ret != CS_SUCCESS) goto done; flash->cell[cell].state |= FLASH_ERASE_SUSPEND; } else link->state |= DEV_BUSY; mod.CardOffset = req->SrcCardOffset & ~(dev->Size-1); from = req->SrcCardOffset & (dev->Size-1); ret = CS_SUCCESS; time = jiffies; for (length = req->TransferLength; length > 0; length -= nb) { ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod); if (ret != CS_SUCCESS) goto done; nb = (from+length > dev->Size) ? dev->Size-from : length; if (req->Function & MTD_REQ_KERNEL) copy_from_pc(buf, &dev->Base[from], nb); else copy_pc_to_user(buf, &dev->Base[from], nb); buf += nb; from = 0; mod.CardOffset += dev->Size; } #ifdef PCMCIA_DEBUG time = jiffies - time; if (time > 1) DEBUG(3, "iflash2_mtd: read complete, time = %ld, " "avg = %ld ns/word, rate = %ld kb/sec\n", time, time*20000000/req->TransferLength, req->TransferLength*100/(time*1024));#endif done: if (flash->cell[cell].state & FLASH_ERASE_SUSPEND) { mod.CardOffset = flash->cell[cell].erase_addr; ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod); if (ret == CS_SUCCESS) resume_erase((u_short *)dev->Base); flash->cell[cell].state &= ~FLASH_ERASE_SUSPEND; } link->state &= ~DEV_BUSY; return ret;} /* flash_read *//*====================================================================== basic_write() handles a write that fits completely within a memory window that has already been set up. ======================================================================*/static int basic_write(char *dest, char *buf, u_int nb, u_int is_krnl){ char *start = dest; int ret; *(u_short *)dest = IF_READ_CSR; if (is_krnl) { if (nb & 1) { ret = byte_write(dest, *buf); if (ret != CS_SUCCESS) return ret; dest++; buf++; nb--; } for (; nb != 0; dest += 2, buf += 2, nb -= 2) { ret = word_write((u_short *)dest, *(u_short *)buf); if (ret != CS_SUCCESS) return ret; } } else { if (nb & 1) { char c; get_user(c, buf); ret = byte_write(dest, c); if (ret != CS_SUCCESS) return ret; dest++; buf++; nb--; } for (; nb != 0; dest += 2, buf += 2, nb -= 2) { u_short s; get_user(s, (u_short *)buf); ret = word_write((u_short *)dest, s); if (ret != CS_SUCCESS) return ret; } } return check_write((u_short *)start); } /* basic_write *//*====================================================================== The write request handler. The Series 2+ cards support automatic erase suspend for writes. ======================================================================*/static int flash_write(dev_link_t *link, char *buf, mtd_request_t *req){ flash_dev_t *dev = (flash_dev_t *)link->priv; mtd_mod_win_t mod; flash_region_t *flash; region_info_t *region; u_int from, length, nb, retry, cell; u_long time; cs_status_t status; int ret; DEBUG(2, "iflash2_mtd: flash_write(0x%p, 0x%lx, 0x%p, 0x%x, " "0x%x)\n", link, req->MediaID, buf, req->DestCardOffset, req->TransferLength); /* Check card write protect status */ ret = CardServices(GetStatus, link->handle, &status); if (ret != CS_SUCCESS) { cs_error(link->handle, GetStatus, ret); return CS_GENERAL_FAILURE; } if (status.CardState & CS_EVENT_WRITE_PROTECT) return CS_WRITE_PROTECTED; flash = (flash_region_t *)(req->MediaID); region = &flash->region; if ((req->DestCardOffset / region->BlockSize) != ((req->DestCardOffset+req->TransferLength-1) / region->BlockSize)) return CS_BAD_SIZE; if (vpp_setup(link, req) != 0) return CS_BUSY; /* Is this cell being erased? */ cell = (req->DestCardOffset - region->CardOffset) / flash->cell_size; if (flash->cell[cell].state & FLASH_ERASING) { req->Status = MTD_WAITREQ; return CS_BUSY; } link->state |= DEV_BUSY; if (region->Attributes & REGION_TYPE_AM) mod.Attributes = WIN_MEMORY_TYPE_AM; else mod.Attributes = WIN_MEMORY_TYPE_CM; mod.AccessSpeed = region->AccessSpeed; time = jiffies; mod.CardOffset = req->DestCardOffset & ~(dev->Size-1); from = req->DestCardOffset & (dev->Size-1); for (length = req->TransferLength ; length > 0; length -= nb) { nb = (from+length > dev->Size) ? dev->Size-from : length; ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod); if (ret != CS_SUCCESS) goto done; for (retry = 0; retry < retry_limit; retry++) { ret = basic_write(dev->Base+from, buf, nb, (req->Function & MTD_REQ_KERNEL)); if (ret == CS_SUCCESS) break; abort_cmd(link, dev->Base, cell, &mod); } if (retry == retry_limit) { printk(KERN_NOTICE "iflash2_mtd: write failed: " "too many retries!\n"); goto done; } buf += nb; from = 0; mod.CardOffset += dev->Size; }#ifdef PCMCIA_DEBUG time = jiffies - time; if (time > 1) DEBUG(3, "iflash2_mtd: write complete, time = %ld, " "avg = %ld us/word, rate = %ld kb/sec\n", time, time*20000/req->TransferLength, req->TransferLength*100/(time*1024));#endif done: reset_block((u_short *)dev->Base); link->state &= ~DEV_BUSY; /* Fire up the Vpp timer */ vpp_shutdown(link); return ret;} /* flash_write *//*====================================================================== The erase request handler. This handler supports simultaneous erases in different device components. ======================================================================*/static int flash_erase(dev_link_t *link, mtd_request_t *req){ flash_dev_t *dev = (flash_dev_t *)link->priv; cs_status_t status; flash_region_t *flash; region_info_t *region; mtd_mod_win_t mod; int i, ret; DEBUG(2, "iflash2_mtd: flash_erase(0x%p, 0x%lx, 0x%x, 0x%x)\n", link, req->MediaID, req->DestCardOffset, req->TransferLength); flash = (flash_region_t *)(req->MediaID); region = &flash->region; if (region->BlockSize != req->TransferLength) return CS_BAD_SIZE; i = (req->DestCardOffset-region->CardOffset)/flash->cell_size; if (!(req->Function & MTD_REQ_TIMEOUT)) { if (flash->cell[i].state & (FLASH_ERASING|FLASH_PENDING)) { req->Status = MTD_WAITREQ; return CS_BUSY; } /* Check card write protect status */ ret = CardServices(GetStatus, link->handle, &status); if (ret != CS_SUCCESS) { cs_error(link->handle, GetStatus, ret); return CS_GENERAL_FAILURE; } if (status.CardState & CS_EVENT_WRITE_PROTECT) return CS_WRITE_PROTECTED; flash->cell[i].state |= FLASH_PENDING; /* Activate Vpp if necessary */ if (vpp_setup(link, req) != 0) return CS_BUSY; } if (region->Attributes & REGION_TYPE_AM) mod.Attributes = WIN_MEMORY_TYPE_AM; else mod.Attributes = WIN_MEMORY_TYPE_CM; mod.AccessSpeed = region->AccessSpeed; mod.CardOffset = req->DestCardOffset; ret = MTDHelperEntry(MTDModifyWindow, link->win, &mod); if (ret != CS_SUCCESS) goto done; if (flash->cell[i].state & FLASH_PENDING) { /* Start a new block erase */ flash->cell[i].state &= ~FLASH_PENDING; flash->cell[i].state |= FLASH_ERASING; flash->cell[i].erase_addr = mod.CardOffset; flash->cell[i].erase_time = jiffies; flash->cell[i].erase_retries = 0; block_erase((u_short *)dev->Base); } else { /* Check on an already started erase */ ret = check_erase((u_short *)dev->Base); if (ret == CS_SUCCESS) goto done; else if (ret != CS_BUSY) { if (++flash->cell[i].erase_retries > retry_limit) { printk(KERN_NOTICE "iflash2_mtd: erase failed: " "too many retries!\n"); goto done; } else { flash->cell[i].erase_time = jiffies; abort_cmd(link, dev->Base, i, &mod); reset_block((u_short *)dev->Base); block_erase((u_short *)dev->Base); } } } /* If the request is not complete, has it taken too long? */ if (jiffies > flash->cell[i].erase_time + erase_limit) { printk(KERN_NOTICE "iflash2_mtd: erase timed out!\n"); reset_block((u_short *)dev->Base); ret = CS_GENERAL_FAILURE; goto done; } req->Status = MTD_WAITTIMER; req->Timeout = erase_timeout; return CS_BUSY; done: DEBUG(2, "iflash2_mtd: erase complete, time = %ld\n", jiffies - flash->cell[i].erase_time); flash->cell[i].state &= ~(FLASH_ERASING|FLASH_PENDING); reset_block((u_short *)dev->Base); vpp_shutdown(link); return ret;} /* flash_erase */ /*====================================================================*/static int flash_request(dev_link_t *link, void *buf, mtd_request_t *req){ int ret = 0; if (!(link->state & DEV_PRESENT)) return CS_NO_CARD; if (link->state & DEV_BUSY) { /* We do this because the erase routine uses the TIMEOUT flag to decide if this is a new request or a status check, so we need to propagate it */ if (req->Function & MTD_REQ_TIMEOUT) { req->Timeout = erase_timeout; req->Status = MTD_WAITTIMER; } else req->Status = MTD_WAITREQ; return CS_BUSY; } switch (req->Function & MTD_REQ_ACTION) { case MTD_REQ_READ: ret = flash_read(link, buf, req); break; case MTD_REQ_WRITE: ret = flash_write(link, buf, req); break; case MTD_REQ_ERASE: ret = flash_erase(link, req); break; case MTD_REQ_COPY: ret = CS_UNSUPPORTED_FUNCTION; break; } if (!(link->state & DEV_PRESENT)) return CS_GENERAL_FAILURE; return ret;} /* flash_request *//*====================================================================== The card status event handler. Mostly, this schedules other stuff to run after an event is received. A CARD_REMOVAL event also sets some flags to discourage the driver from trying to talk to the card any more. ======================================================================*/static int flash_event(event_t event, int priority, event_callback_args_t *args){ dev_link_t *link = args->client_data; DEBUG(1, "iflash2_mtd: flash_event(0x%06x)\n", event); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) mod_timer(&link->release, jiffies + HZ/20); break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; flash_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: break; case CS_EVENT_MTD_REQUEST: return flash_request(link, args->buffer, args->mtdrequest); break; } return CS_SUCCESS;} /* flash_event *//*====================================================================*/static int __init init_iflash2_mtd(void){ servinfo_t serv; DEBUG(0, "%s\n", version); /* Rescale parameters */ vpp_timeout_period = (vpp_timeout_period * HZ) / 1000; vpp_settle = (vpp_settle * HZ) / 1000; erase_limit = (erase_limit * HZ) / 1000; CardServices(GetCardServicesInfo, &serv); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "iflash2_mtd: Card Services release " "does not match!\n"); return -1; } register_pccard_driver(&dev_info, &flash_attach, &flash_detach); return 0;}static void __exit exit_iflash2_mtd(void){ DEBUG(0, "iflash2_mtd: unloading\n"); unregister_pccard_driver(&dev_info); while (dev_list != NULL) flash_detach(dev_list);}module_init(init_iflash2_mtd);module_exit(exit_iflash2_mtd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -