/*
* ms_block.c - Sony MemoryStick (legacy) storage support
*/
#define DRIVER_NAME "ms_block"
#define pr_fmt(fmt) DRIVER_NAME ": " fmt
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/memstick.h>
#include <linux/idr.h>
#include <linux/hdreg.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/bitmap.h>
#include <linux/scatterlist.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include "ms_block.h"
static int debug;
static int cache_flush_timeout = 1000;
static bool verify_writes;
/*
* Copies section of 'sg_from' starting from offset 'offset' and with length
* 'len' To another scatterlist of to_nents enties
*/
static size_t msb_sg_copy(struct scatterlist *sg_from,
struct scatterlist *sg_to, int to_nents, size_t offset, size_t len)
{
size_t copied = 0;
while (offset > 0) {
if (offset >= sg_from->length) {
if (sg_is_last(sg_from))
return 0;
offset -= sg_from->length;
sg_from = sg_next(sg_from);
continue;
}
copied = min(len, sg_from->length - offset);
sg_set_page(sg_to, sg_page(sg_from),
copied, sg_from->offset + offset);
len -= copied;
offset = 0;
if (sg_is_last(sg_from) || !len)
goto out;
sg_to = sg_next(sg_to);
to_nents--;
sg_from = sg_next(sg_from);
}
while (len > sg_from->length && to_nents--) {
len -= sg_from->length;
copied += sg_from->length;
sg_set_page(sg_to, sg_page(sg_from),
sg_from->length, sg_from->offset);
if (sg_is_last(sg_from) || !len)
goto out;
sg_from = sg_next(sg_from);
sg_to = sg_next(sg_to);
}
if (len && to_nents) {
sg_set_page(sg_to, sg_page(sg_from), len, sg_from->offset);
copied += len;
}
out:
sg_mark_end(sg_to);
return copied;
}
/*
* Compares section of 'sg' starting from offset 'offset' and with length 'len'
* to linear buffer of length 'len' at address 'buffer'
* Returns 0 if equal and -1 otherwice
*/
static int msb_sg_compare_to_buffer(struct scatterlist *sg,
size_t offset, u8 *buffer, size_t len)
{
int retval = 0, cmplen;
struct sg_mapping_iter miter;
sg_miter_start(&miter, sg, sg_nents(sg),
SG_MITER_ATOMIC | SG_MITER_FROM_SG);
while (sg_miter_next(&miter) && len > 0) {
if (offset >= miter.length) {
offset -= miter.length;
continue;
}
cmplen = min(miter.length - offset, len);
retval = memcmp(miter.addr + offset, buffer, cmplen) ? -1 : 0;
if (retval)
break;
buffer += cmplen;
len -= cmplen;
offset = 0;
}
if (!retval && len)
retval = -1;
sg_miter_stop(&miter);
return retval;
}
/* Get zone at which block with logical address 'lba' lives
* Flash is broken into zones.
* Each zone consists of 512 eraseblocks, out of which in first
* zone 494 are used and 496 are for all following zones.
* Therefore zone #0 hosts blocks 0-493, zone #1 blocks 494-988, etc...
*/
static int msb_get_zone_from_lba(int lba)
{
if (lba < 494)
return 0;
return ((lba - 494) / 496) + 1;
}
/* Get zone of physical block. Trivial */
static int msb_get_zone_from_pba(int pba)
{
return pba / MS_BLOCKS_IN_ZONE;
}
/* Debug test to validate free block counts */
static int msb_validate_used_block_bitmap(struct msb_data *msb)
{
int total_free_blocks = 0;
int i;
if (!debug)
return 0;
for (i = 0; i < msb->zone_count; i++)
total_free_blocks += msb->free_block_count[i];
if (msb->block_count - bitmap_weight(msb->used_blocks_bitmap,
msb->block_count) == total_free_blocks)
return 0;
pr_err("BUG: free block counts don't match the bitmap");
msb->read_only = true;
return -EINVAL;
}
/* Mark physical block as used */
static void msb_mark_block_used(struct msb_data *msb, int pba)
{
int zone = msb_get_zone_from_pba(pba);
if (test_bit(pba, msb->used_blocks_bitmap)) {
pr_err(
"BUG: attempt to mark already used pba %d as used", pba);
msb->read_only = true;
return;
}
if (msb_validate_used_block_bitmap(msb))
return;
/* No races because all IO is single threaded */
__set_bit(pba, msb->used_blocks_bitmap);
msb->free_block_count[zone]--;
}
/* Mark physical block as free */
static void msb_mark_block_unused(struct msb_data *msb, int pba)
{
int zone = msb_get_zone_from_pba(pba);
if (!test_bit(pba, msb->used_blocks_bitmap)) {
pr_err("BUG: attempt to mark already unused pba %d as unused" , pba);
msb->read_only = true;
return;
}
if (msb_validate_used_block_bitmap(msb))
return;
/* No races because all IO is single threaded */
__clear_bit(pba, msb->used_blocks_bitmap);
msb->free_block_count[zone]++;
}
/* Invalidate current register window */
static void msb_invalidate_reg_window(struct msb_data *msb)
{
msb->reg_addr.w_offset = offsetof(struct ms_register, id);
msb->reg_addr.w_length = sizeof(struct ms_id_register);
msb->reg_addr.r_offset = offsetof(struct ms_register, id);
msb->reg_addr.r_length = sizeof(struct ms_id_register);
msb->addr_valid = false;
}
/* Start a state machine */
static int msb_run_state_machine(struct msb_data *msb, int (*state_func)
(struct memstick_dev *card, struct memstick_request **req))
{
struct memstick_dev *card = msb->card;
WARN_ON(msb->state != -1);
msb->int_polling = false;
msb->state = 0;
msb->exit_error = 0;
memset(&card->current_mrq, 0, sizeof(card->current_mrq));
card->next_request = state_func;
memstick_new_req(card->host);
wait_for_completion(&card->mrq_complete);
WARN_ON(msb->state != -1);
return msb->exit_error;
}
/* State machines call that to exit */
static int msb_exit_state_machine(struct msb_data *msb, int error)
{
WARN_ON(msb->state == -1);
msb->state = -1;
msb->exit_error = error;
msb->card->next_request = h_msb_default_bad;
/* Invalidate reg window on errors */
if (error)
msb_invalidate_reg_window(msb);
complete(&msb->card->mrq_complete);
return -ENXIO;
}
/* read INT register */
static int msb_read_int_reg(struct msb_data *msb, long timeout)
{
struct memstick_request *mrq = &msb->card->current_mrq;
WARN_ON(msb->state == -1);
if (!msb->int_polling) {
msb->int_timeout = jiffies +
msecs_to_jiffies(timeout == -1 ? 500 : timeout);
msb->int_polling = true;
} else if (time_after(jiffies, msb->int_timeout)) {
mrq->data[0] = MEMSTICK_INT_CMDNAK;
return 0;
}
if ((msb->caps & MEMSTICK_CAP_AUTO_GET_INT) &&
mrq->need_card_int && !mrq->error) {
mrq->data[0] = mrq->int_reg;
mrq->need_card_int = false;
return 0;
} else {
memstick_init_req(mrq, MS_TPC_GET_INT, NULL, 1);
return 1;
}
}
/* Read a register */
static int msb_read_regs(struct msb_data *msb, int offset, int len)
{
struct memstick_request *req = &msb->card->current_mrq;
if (msb->reg_addr.r_offset != offset ||
msb->reg_addr.r_length != len || !msb->addr_valid) {
msb->reg_addr.r_offset = offset;
msb->reg_addr.r_length = len;
msb->addr_valid = true;
memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS,
&msb->reg_addr, sizeof(msb->reg_addr));
return 0;
}
memstick_init_req(req, MS_TPC_READ_REG, NULL, len);
return 1;
}
/* Write a card register */
static int msb_write_regs(struct msb_data *msb, int offset, int len, void *buf)
{
struct memstick_request *req = &msb->card->current_mrq;
if (msb->reg_addr.w_offset != offset ||
msb->reg_addr.w_length != len || !msb->addr_valid) {
msb->reg_addr.w_offset = offset;
msb->reg_addr.w_length = len;
msb->addr_valid = true;
memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS,
&msb->reg_addr, sizeof(msb->reg_addr));
return 0;
}
memstick_init_req(req, MS_TPC_WRITE_REG, buf, len);
return 1;
}
/* Handler for absence of IO */
static int h_msb_default_bad(struct memstick_dev *card,
struct memstick_request **mrq)
{
return -ENXIO;
}
/*
* This function is a handler for reads of one page from device.
* Writes output to msb->current_sg, takes sector address from msb->reg.param
* Can also be used to read extra data only. Set params accordintly.
*/
static int h_msb_read_page(struct memstick_dev *card,
struct memstick_request **out_mrq)
{
ms_block.rar_legacy
版权申诉
143 浏览量
2022-09-19
21:26:54
上传
评论
收藏 15KB RAR 举报
alvarocfc
- 粉丝: 105
- 资源: 1万+
最新资源
- 基于python+opencv实现的暗通道先验的单幅图像去雾算法python仿真源代码+文档说明+截图演示(高分课程设计)
- 基于STM32G4与智能摄像头的疲劳驾驶检测系统源代码(96分课程设计)
- 单片机课后答案.doc
- 单自由度系统自由振动matlab程序.docx
- 高分课程设计-基于opencvdlib的疲劳驾驶检测系统python源码+文档说明+界面演示(带ui界面)
- 哈工大c语言-练习题.doc
- Springboot集成Netflix-ribbon、Consul实现负载均衡调用-源码
- 大一大二常用命令大全笔记demo
- Vision语言文件读写操作代码笔记
- 大数据、云计算和人工智能的深度剖析与相互关系-PPT.ppt
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈