static int h_msb_read_page()

in core/ms_block.c [335:463]


static int h_msb_read_page(struct memstick_dev *card,
					struct memstick_request **out_mrq)
{
	struct msb_data *msb = memstick_get_drvdata(card);
	struct memstick_request *mrq = *out_mrq = &card->current_mrq;
	struct scatterlist sg[2];
	u8 command, intreg;

	if (mrq->error) {
		dbg("read_page, unknown error");
		return msb_exit_state_machine(msb, mrq->error);
	}
again:
	switch (msb->state) {
	case MSB_RP_SEND_BLOCK_ADDRESS:
		/* msb_write_regs sometimes "fails" because it needs to update
		 * the reg window, and thus it returns request for that.
		 * Then we stay in this state and retry
		 */
		if (!msb_write_regs(msb,
			offsetof(struct ms_register, param),
			sizeof(struct ms_param_register),
			(unsigned char *)&msb->regs.param))
			return 0;

		msb->state = MSB_RP_SEND_READ_COMMAND;
		return 0;

	case MSB_RP_SEND_READ_COMMAND:
		command = MS_CMD_BLOCK_READ;
		memstick_init_req(mrq, MS_TPC_SET_CMD, &command, 1);
		msb->state = MSB_RP_SEND_INT_REQ;
		return 0;

	case MSB_RP_SEND_INT_REQ:
		msb->state = MSB_RP_RECEIVE_INT_REQ_RESULT;
		/* If dont actually need to send the int read request (only in
		 * serial mode), then just fall through
		 */
		if (msb_read_int_reg(msb, -1))
			return 0;
		fallthrough;

	case MSB_RP_RECEIVE_INT_REQ_RESULT:
		intreg = mrq->data[0];
		msb->regs.status.interrupt = intreg;

		if (intreg & MEMSTICK_INT_CMDNAK)
			return msb_exit_state_machine(msb, -EIO);

		if (!(intreg & MEMSTICK_INT_CED)) {
			msb->state = MSB_RP_SEND_INT_REQ;
			goto again;
		}

		msb->int_polling = false;
		msb->state = (intreg & MEMSTICK_INT_ERR) ?
			MSB_RP_SEND_READ_STATUS_REG : MSB_RP_SEND_OOB_READ;
		goto again;

	case MSB_RP_SEND_READ_STATUS_REG:
		 /* read the status register to understand source of the INT_ERR */
		if (!msb_read_regs(msb,
			offsetof(struct ms_register, status),
			sizeof(struct ms_status_register)))
			return 0;

		msb->state = MSB_RP_RECEIVE_STATUS_REG;
		return 0;

	case MSB_RP_RECEIVE_STATUS_REG:
		msb->regs.status = *(struct ms_status_register *)mrq->data;
		msb->state = MSB_RP_SEND_OOB_READ;
		fallthrough;

	case MSB_RP_SEND_OOB_READ:
		if (!msb_read_regs(msb,
			offsetof(struct ms_register, extra_data),
			sizeof(struct ms_extra_data_register)))
			return 0;

		msb->state = MSB_RP_RECEIVE_OOB_READ;
		return 0;

	case MSB_RP_RECEIVE_OOB_READ:
		msb->regs.extra_data =
			*(struct ms_extra_data_register *) mrq->data;
		msb->state = MSB_RP_SEND_READ_DATA;
		fallthrough;

	case MSB_RP_SEND_READ_DATA:
		/* Skip that state if we only read the oob */
		if (msb->regs.param.cp == MEMSTICK_CP_EXTRA) {
			msb->state = MSB_RP_RECEIVE_READ_DATA;
			goto again;
		}

		sg_init_table(sg, ARRAY_SIZE(sg));
		msb_sg_copy(msb->current_sg, sg, ARRAY_SIZE(sg),
			msb->current_sg_offset,
			msb->page_size);

		memstick_init_req_sg(mrq, MS_TPC_READ_LONG_DATA, sg);
		msb->state = MSB_RP_RECEIVE_READ_DATA;
		return 0;

	case MSB_RP_RECEIVE_READ_DATA:
		if (!(msb->regs.status.interrupt & MEMSTICK_INT_ERR)) {
			msb->current_sg_offset += msb->page_size;
			return msb_exit_state_machine(msb, 0);
		}

		if (msb->regs.status.status1 & MEMSTICK_UNCORR_ERROR) {
			dbg("read_page: uncorrectable error");
			return msb_exit_state_machine(msb, -EBADMSG);
		}

		if (msb->regs.status.status1 & MEMSTICK_CORR_ERROR) {
			dbg("read_page: correctable error");
			msb->current_sg_offset += msb->page_size;
			return msb_exit_state_machine(msb, -EUCLEAN);
		} else {
			dbg("read_page: INT error, but no status error bits");
			return msb_exit_state_machine(msb, -EIO);
		}
	}

	BUG();
}