/*
* linux/drivers/mmc/card/mmc_test.c
*
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/scatterlist.h>
#define RESULT_OK 0
#define RESULT_FAIL 1
#define RESULT_UNSUP_HOST 2
#define RESULT_UNSUP_CARD 3
#define BUFFER_SIZE (PAGE_SIZE * 4)
struct mmc_test_card {
struct mmc_card *card;
u8 *buffer;
};
/*******************************************************************/
/* Helper functions */
/*******************************************************************/
static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
{
struct mmc_command cmd;
int ret;
cmd.opcode = MMC_SET_BLOCKLEN;
cmd.arg = size;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
if (ret)
return ret;
return 0;
}
static int __mmc_test_transfer(struct mmc_test_card *test, int write,
unsigned broken_xfer, u8 *buffer, unsigned addr,
unsigned blocks, unsigned blksz)
{
int ret, busy;
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_command stop;
struct mmc_data data;
struct scatterlist sg;
memset(&mrq, 0, sizeof(struct mmc_request));
mrq.cmd = &cmd;
mrq.data = &data;
memset(&cmd, 0, sizeof(struct mmc_command));
if (broken_xfer) {
if (blocks > 1) {
cmd.opcode = write ?
MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
} else {
cmd.opcode = MMC_SEND_STATUS;
}
} else {
if (blocks > 1) {
cmd.opcode = write ?
MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
} else {
cmd.opcode = write ?
MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
}
}
if (broken_xfer && blocks == 1)
cmd.arg = test->card->rca << 16;
else
cmd.arg = addr;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&stop, 0, sizeof(struct mmc_command));
if (!broken_xfer && (blocks > 1)) {
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
mrq.stop = &stop;
}
memset(&data, 0, sizeof(struct mmc_data));
data.blksz = blksz;
data.blocks = blocks;
data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, buffer, blocks * blksz);
mmc_set_data_timeout(&data, test->card);
mmc_wait_for_req(test->card->host, &mrq);
ret = 0;
if (broken_xfer) {
if (!ret && cmd.error)
ret = cmd.error;
if (!ret && data.error == 0)
ret = RESULT_FAIL;
if (!ret && data.error != -ETIMEDOUT)
ret = data.error;
if (!ret && stop.error)
ret = stop.error;
if (blocks > 1) {
if (!ret && data.bytes_xfered > blksz)
ret = RESULT_FAIL;
} else {
if (!ret && data.bytes_xfered > 0)
ret = RESULT_FAIL;
}
} else {
if (!ret && cmd.error)
ret = cmd.error;
if (!ret && data.error)
ret = data.error;
if (!ret && stop.error)
ret = stop.error;
if (!ret && data.bytes_xfered != blocks * blksz)
ret = RESULT_FAIL;
}
if (ret == -EINVAL)
ret = RESULT_UNSUP_HOST;
busy = 0;
do {
int ret2;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
cmd.arg = test->card->