static int esp_process_event()

in esp_scsi.c [1689:2026]


static int esp_process_event(struct esp *esp)
{
	int write, i;

again:
	write = 0;
	esp_log_event("process event %d phase %x\n",
		      esp->event, esp->sreg & ESP_STAT_PMASK);
	switch (esp->event) {
	case ESP_EVENT_CHECK_PHASE:
		switch (esp->sreg & ESP_STAT_PMASK) {
		case ESP_DOP:
			esp_event(esp, ESP_EVENT_DATA_OUT);
			break;
		case ESP_DIP:
			esp_event(esp, ESP_EVENT_DATA_IN);
			break;
		case ESP_STATP:
			esp_flush_fifo(esp);
			scsi_esp_cmd(esp, ESP_CMD_ICCSEQ);
			esp_event(esp, ESP_EVENT_STATUS);
			esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
			return 1;

		case ESP_MOP:
			esp_event(esp, ESP_EVENT_MSGOUT);
			break;

		case ESP_MIP:
			esp_event(esp, ESP_EVENT_MSGIN);
			break;

		case ESP_CMDP:
			esp_event(esp, ESP_EVENT_CMD_START);
			break;

		default:
			shost_printk(KERN_INFO, esp->host,
				     "Unexpected phase, sreg=%02x\n",
				     esp->sreg);
			esp_schedule_reset(esp);
			return 0;
		}
		goto again;

	case ESP_EVENT_DATA_IN:
		write = 1;
		fallthrough;

	case ESP_EVENT_DATA_OUT: {
		struct esp_cmd_entry *ent = esp->active_cmd;
		struct scsi_cmnd *cmd = ent->cmd;
		dma_addr_t dma_addr = esp_cur_dma_addr(ent, cmd);
		unsigned int dma_len = esp_cur_dma_len(ent, cmd);

		if (esp->rev == ESP100)
			scsi_esp_cmd(esp, ESP_CMD_NULL);

		if (write)
			ent->flags |= ESP_CMD_FLAG_WRITE;
		else
			ent->flags &= ~ESP_CMD_FLAG_WRITE;

		if (esp->ops->dma_length_limit)
			dma_len = esp->ops->dma_length_limit(esp, dma_addr,
							     dma_len);
		else
			dma_len = esp_dma_length_limit(esp, dma_addr, dma_len);

		esp->data_dma_len = dma_len;

		if (!dma_len) {
			shost_printk(KERN_ERR, esp->host,
				     "DMA length is zero!\n");
			shost_printk(KERN_ERR, esp->host,
				     "cur adr[%08llx] len[%08x]\n",
				     (unsigned long long)esp_cur_dma_addr(ent, cmd),
				     esp_cur_dma_len(ent, cmd));
			esp_schedule_reset(esp);
			return 0;
		}

		esp_log_datastart("start data addr[%08llx] len[%u] write(%d)\n",
				  (unsigned long long)dma_addr, dma_len, write);

		esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len,
				       write, ESP_CMD_DMA | ESP_CMD_TI);
		esp_event(esp, ESP_EVENT_DATA_DONE);
		break;
	}
	case ESP_EVENT_DATA_DONE: {
		struct esp_cmd_entry *ent = esp->active_cmd;
		struct scsi_cmnd *cmd = ent->cmd;
		int bytes_sent;

		if (esp->ops->dma_error(esp)) {
			shost_printk(KERN_INFO, esp->host,
				     "data done, DMA error, resetting\n");
			esp_schedule_reset(esp);
			return 0;
		}

		if (ent->flags & ESP_CMD_FLAG_WRITE) {
			/* XXX parity errors, etc. XXX */

			esp->ops->dma_drain(esp);
		}
		esp->ops->dma_invalidate(esp);

		if (esp->ireg != ESP_INTR_BSERV) {
			/* We should always see exactly a bus-service
			 * interrupt at the end of a successful transfer.
			 */
			shost_printk(KERN_INFO, esp->host,
				     "data done, not BSERV, resetting\n");
			esp_schedule_reset(esp);
			return 0;
		}

		bytes_sent = esp_data_bytes_sent(esp, ent, cmd);

		esp_log_datadone("data done flgs[%x] sent[%d]\n",
				 ent->flags, bytes_sent);

		if (bytes_sent < 0) {
			/* XXX force sync mode for this target XXX */
			esp_schedule_reset(esp);
			return 0;
		}

		esp_advance_dma(esp, ent, cmd, bytes_sent);
		esp_event(esp, ESP_EVENT_CHECK_PHASE);
		goto again;
	}

	case ESP_EVENT_STATUS: {
		struct esp_cmd_entry *ent = esp->active_cmd;

		if (esp->ireg & ESP_INTR_FDONE) {
			ent->status = esp_read8(ESP_FDATA);
			ent->message = esp_read8(ESP_FDATA);
			scsi_esp_cmd(esp, ESP_CMD_MOK);
		} else if (esp->ireg == ESP_INTR_BSERV) {
			ent->status = esp_read8(ESP_FDATA);
			ent->message = 0xff;
			esp_event(esp, ESP_EVENT_MSGIN);
			return 0;
		}

		if (ent->message != COMMAND_COMPLETE) {
			shost_printk(KERN_INFO, esp->host,
				     "Unexpected message %x in status\n",
				     ent->message);
			esp_schedule_reset(esp);
			return 0;
		}

		esp_event(esp, ESP_EVENT_FREE_BUS);
		esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
		break;
	}
	case ESP_EVENT_FREE_BUS: {
		struct esp_cmd_entry *ent = esp->active_cmd;
		struct scsi_cmnd *cmd = ent->cmd;

		if (ent->message == COMMAND_COMPLETE ||
		    ent->message == DISCONNECT)
			scsi_esp_cmd(esp, ESP_CMD_ESEL);

		if (ent->message == COMMAND_COMPLETE) {
			esp_log_cmddone("Command done status[%x] message[%x]\n",
					ent->status, ent->message);
			if (ent->status == SAM_STAT_TASK_SET_FULL)
				esp_event_queue_full(esp, ent);

			if (ent->status == SAM_STAT_CHECK_CONDITION &&
			    !(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) {
				ent->flags |= ESP_CMD_FLAG_AUTOSENSE;
				esp_autosense(esp, ent);
			} else {
				esp_cmd_is_done(esp, ent, cmd, DID_OK);
			}
		} else if (ent->message == DISCONNECT) {
			esp_log_disconnect("Disconnecting tgt[%d] tag[%x:%x]\n",
					   cmd->device->id,
					   ent->tag[0], ent->tag[1]);

			esp->active_cmd = NULL;
			esp_maybe_execute_command(esp);
		} else {
			shost_printk(KERN_INFO, esp->host,
				     "Unexpected message %x in freebus\n",
				     ent->message);
			esp_schedule_reset(esp);
			return 0;
		}
		if (esp->active_cmd)
			esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
		break;
	}
	case ESP_EVENT_MSGOUT: {
		scsi_esp_cmd(esp, ESP_CMD_FLUSH);

		if (esp_debug & ESP_DEBUG_MSGOUT) {
			int i;
			printk("ESP: Sending message [ ");
			for (i = 0; i < esp->msg_out_len; i++)
				printk("%02x ", esp->msg_out[i]);
			printk("]\n");
		}

		if (esp->rev == FASHME) {
			int i;

			/* Always use the fifo.  */
			for (i = 0; i < esp->msg_out_len; i++) {
				esp_write8(esp->msg_out[i], ESP_FDATA);
				esp_write8(0, ESP_FDATA);
			}
			scsi_esp_cmd(esp, ESP_CMD_TI);
		} else {
			if (esp->msg_out_len == 1) {
				esp_write8(esp->msg_out[0], ESP_FDATA);
				scsi_esp_cmd(esp, ESP_CMD_TI);
			} else if (esp->flags & ESP_FLAG_USE_FIFO) {
				for (i = 0; i < esp->msg_out_len; i++)
					esp_write8(esp->msg_out[i], ESP_FDATA);
				scsi_esp_cmd(esp, ESP_CMD_TI);
			} else {
				/* Use DMA. */
				memcpy(esp->command_block,
				       esp->msg_out,
				       esp->msg_out_len);

				esp->ops->send_dma_cmd(esp,
						       esp->command_block_dma,
						       esp->msg_out_len,
						       esp->msg_out_len,
						       0,
						       ESP_CMD_DMA|ESP_CMD_TI);
			}
		}
		esp_event(esp, ESP_EVENT_MSGOUT_DONE);
		break;
	}
	case ESP_EVENT_MSGOUT_DONE:
		if (esp->rev == FASHME) {
			scsi_esp_cmd(esp, ESP_CMD_FLUSH);
		} else {
			if (esp->msg_out_len > 1)
				esp->ops->dma_invalidate(esp);

			/* XXX if the chip went into disconnected mode,
			 * we can't run the phase state machine anyway.
			 */
			if (!(esp->ireg & ESP_INTR_DC))
				scsi_esp_cmd(esp, ESP_CMD_NULL);
		}

		esp->msg_out_len = 0;

		esp_event(esp, ESP_EVENT_CHECK_PHASE);
		goto again;
	case ESP_EVENT_MSGIN:
		if (esp->ireg & ESP_INTR_BSERV) {
			if (esp->rev == FASHME) {
				if (!(esp_read8(ESP_STATUS2) &
				      ESP_STAT2_FEMPTY))
					scsi_esp_cmd(esp, ESP_CMD_FLUSH);
			} else {
				scsi_esp_cmd(esp, ESP_CMD_FLUSH);
				if (esp->rev == ESP100)
					scsi_esp_cmd(esp, ESP_CMD_NULL);
			}
			scsi_esp_cmd(esp, ESP_CMD_TI);
			esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
			return 1;
		}
		if (esp->ireg & ESP_INTR_FDONE) {
			u8 val;

			if (esp->rev == FASHME)
				val = esp->fifo[0];
			else
				val = esp_read8(ESP_FDATA);
			esp->msg_in[esp->msg_in_len++] = val;

			esp_log_msgin("Got msgin byte %x\n", val);

			if (!esp_msgin_process(esp))
				esp->msg_in_len = 0;

			if (esp->rev == FASHME)
				scsi_esp_cmd(esp, ESP_CMD_FLUSH);

			scsi_esp_cmd(esp, ESP_CMD_MOK);

			/* Check whether a bus reset is to be done next */
			if (esp->event == ESP_EVENT_RESET)
				return 0;

			if (esp->event != ESP_EVENT_FREE_BUS)
				esp_event(esp, ESP_EVENT_CHECK_PHASE);
		} else {
			shost_printk(KERN_INFO, esp->host,
				     "MSGIN neither BSERV not FDON, resetting");
			esp_schedule_reset(esp);
			return 0;
		}
		break;
	case ESP_EVENT_CMD_START:
		memcpy(esp->command_block, esp->cmd_bytes_ptr,
		       esp->cmd_bytes_left);
		esp_send_dma_cmd(esp, esp->cmd_bytes_left, 16, ESP_CMD_TI);
		esp_event(esp, ESP_EVENT_CMD_DONE);
		esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
		break;
	case ESP_EVENT_CMD_DONE:
		esp->ops->dma_invalidate(esp);
		if (esp->ireg & ESP_INTR_BSERV) {
			esp_event(esp, ESP_EVENT_CHECK_PHASE);
			goto again;
		}
		esp_schedule_reset(esp);
		return 0;

	case ESP_EVENT_RESET:
		scsi_esp_cmd(esp, ESP_CMD_RS);
		break;

	default:
		shost_printk(KERN_INFO, esp->host,
			     "Unexpected event %x, resetting\n", esp->event);
		esp_schedule_reset(esp);
		return 0;
	}
	return 1;
}