bool scsi_send_cmd(size_t data_length, bool reading, int sg_size, uint8 **sg_ptr, uint32 *sg_len, uint16 *stat, uint32 timeout) { static int pack_id = 0; // Check if buffer is large enough, allocate new buffer if needed if (!try_buffer(data_length)) { char str[256]; sprintf(str, GetString(STR_SCSI_BUFFER_ERR), data_length); ErrorAlert(str); return false; } // Process S/G table when writing if (!reading) { D(bug(" writing to buffer\n")); uint8 *buffer_ptr = buffer + sizeof(sg_header) + the_cmd_len; for (int i=0; i<sg_size; i++) { uint32 len = sg_len[i]; D(bug(" %d bytes from %08lx\n", len, sg_ptr[i])); memcpy(buffer_ptr, sg_ptr[i], len); buffer_ptr += len; } } // Request Sense and autosense data valid? sg_header *h = (sg_header *)buffer; int res; if (reading && the_cmd[0] == 0x03 && (h->target_status & DRIVER_SENSE)) { // Yes, fake command D(bug(" autosense\n")); memcpy(buffer + sizeof(sg_header), h->sense_buffer, 16); h->target_status &= ~DRIVER_SENSE; res = 0; *stat = 0; } else { // No, send regular command if (timeout) { int to = timeout * HZ / 60; ioctl(fd, SG_SET_TIMEOUT, &to); } ioctl(fd, SG_NEXT_CMD_LEN, &the_cmd_len); D(bug(" sending command, length %d\n", data_length)); int request_size, reply_size; if (reading) { h->pack_len = request_size = sizeof(sg_header) + the_cmd_len; h->reply_len = reply_size = sizeof(sg_header) + data_length; } else { h->pack_len = request_size = sizeof(sg_header) + the_cmd_len + data_length; h->reply_len = reply_size = sizeof(sg_header); } h->pack_id = pack_id++; h->result = 0; h->twelve_byte = (the_cmd_len == 12); h->target_status = 0; h->host_status = 0; h->driver_status = 0; h->other_flags = 0; memcpy(buffer + sizeof(sg_header), the_cmd, the_cmd_len); res = write(fd, buffer, request_size); D(bug(" request sent, actual %d, result %d\n", res, h->result)); if (res >= 0) { res = read(fd, buffer, reply_size); D(bug(" reply read, actual %d, result %d, status %02x\n", res, h->result, h->target_status << 1)); } *stat = h->target_status << 1; } // Process S/G table when reading if (reading && h->result == 0) { D(bug(" reading from buffer\n")); uint8 *buffer_ptr = buffer + sizeof(sg_header); for (int i=0; i<sg_size; i++) { uint32 len = sg_len[i]; D(bug(" %d bytes to %08lx\n", len, sg_ptr[i])); memcpy(sg_ptr[i], buffer_ptr, len); buffer_ptr += len; } } return res >= 0; }
bool scsi_send_cmd(size_t data_length, bool reading, int sg_size, uint8 **sg_ptr, uint32 *sg_len, uint16 *stat, uint32 timeout) { // Bypass the buffer if there's only one S/G table entry bool do_direct_transfer = (sg_size == 1 && ((uint32)sg_ptr[0] & 1) == 0 && direct_transfers_supported); if (!do_direct_transfer) { // Check if buffer is large enough, allocate new buffer if needed if (!try_buffer(data_length)) { char str[256]; sprintf(str, GetString(STR_SCSI_BUFFER_ERR), data_length); ErrorAlert(str); return false; } // Process S/G table when writing if (!reading) { D(bug(" writing to buffer\n")); uint8 *buffer_ptr = buffer; for (int i=0; i<sg_size; i++) { uint32 len = sg_len[i]; D(bug(" %d bytes from %08lx\n", len, sg_ptr[i])); memcpy(buffer_ptr, sg_ptr[i], len); buffer_ptr += len; } } } // Request Sense and autosense data valid? BYTE res = 0; if (cmd_buffer[0] == 0x03 && scsi.scsi_SenseActual) { // Yes, fake command D(bug(" autosense\n")); memcpy(buffer, sense_buffer, scsi.scsi_SenseActual); scsi.scsi_Status = 0; do_direct_transfer = false; } else { // No, send regular command D(bug(" sending command, length %ld\n", data_length)); if (do_direct_transfer) { scsi.scsi_Data = (UWORD *)sg_ptr[0]; scsi.scsi_Length = sg_len[0]; } else { scsi.scsi_Data = (UWORD *)buffer; scsi.scsi_Length = data_length; } scsi.scsi_Actual = 0; scsi.scsi_Flags = (reading ? SCSIF_READ : SCSIF_WRITE) | SCSIF_AUTOSENSE; scsi.scsi_SenseActual = 0; scsi.scsi_Status = 0; res = DoIO((struct IORequest *)io); D(bug(" command sent, res %d, status %d\n", res, scsi.scsi_Status)); *stat = scsi.scsi_Status; } if (!do_direct_transfer) { // Process S/G table when reading if (reading && res == 0) { D(bug(" reading from buffer\n")); uint8 *buffer_ptr = buffer; for (int i=0; i<sg_size; i++) { uint32 len = sg_len[i]; D(bug(" %d bytes to %08lx\n", len, sg_ptr[i])); memcpy(sg_ptr[i], buffer_ptr, len); buffer_ptr += len; } } } return res == 0; }