/*! Emulate REQUEST SENSE */ void ide_request_sense(ide_device_info *device, ide_qrequest *qrequest) { scsi_ccb *request = qrequest->request; scsi_cmd_request_sense *cmd = (scsi_cmd_request_sense *)request->cdb; scsi_sense sense; uint32 transferSize; // cannot use finish_checksense here, as data is not copied into autosense buffer // but into normal data buffer, SCSI result is GOOD and CAM status is REQ_CMP if (device->combined_sense) create_sense(device, &sense); else memset(&sense, 0, sizeof(sense)); copy_sg_data(request, 0, cmd->allocation_length, &sense, sizeof(sense), false); // reset sense information on read device->combined_sense = 0; transferSize = min_c(sizeof(sense), cmd->allocation_length); transferSize = min_c(transferSize, request->data_length); request->data_resid = request->data_length - transferSize; // normally, all flags are set to "success", but for Request Sense // this would have overwritten the sense we want to read device->subsys_status = SCSI_REQ_CMP; request->device_status = SCSI_STATUS_GOOD; }
/*! Emulate INQUIRY command */ static void ata_inquiry(ide_device_info *device, ide_qrequest *qrequest) { scsi_ccb *request = qrequest->request; scsi_res_inquiry data; scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)request->cdb; uint32 allocation_length = cmd->allocation_length; uint32 transfer_size; if (cmd->evpd || cmd->page_code) { set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD); return; } memset(&data, 0, sizeof(data)); data.device_type = scsi_dev_direct_access; data.device_qualifier = scsi_periph_qual_connected; data.device_type_modifier = 0; data.removable_medium = false; data.ansi_version = 2; data.ecma_version = 0; data.iso_version = 0; data.response_data_format = 2; data.term_iop = false; // to be changed if we support TERM I/O data.additional_length = sizeof(scsi_res_inquiry) - 4; data.soft_reset = false; data.cmd_queue = device->queue_depth > 1; data.linked = false; // these values are free-style data.sync = false; data.write_bus16 = true; data.write_bus32 = false; data.relative_address = false; // the following fields are *much* to small, sigh... memcpy(data.vendor_ident, device->infoblock.model_number, sizeof(data.vendor_ident)); memcpy(data.product_ident, device->infoblock.model_number + 8, sizeof(data.product_ident)); memcpy(data.product_rev, " ", sizeof(data.product_rev)); copy_sg_data(request, 0, allocation_length, &data, sizeof(data), false); transfer_size = min(sizeof(data), allocation_length); transfer_size = min(transfer_size, request->data_length); request->data_resid = request->data_length - transfer_size; }
void VirtioSCSIRequest::RequestSense() { CALLED(); // Copy sense data from last request into data buffer of current request. // The sense data of last request is still present in the current request, // as it isn't cleared on SCSI_OP_REQUEST_SENSE. scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb; copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense, fResponse->sense_len, false); fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len, command->allocation_length), fCCB->data_length); fResponse->sense_len = 0; }
/*! Emulate READ CAPACITY command */ static void read_capacity(ide_device_info *device, ide_qrequest *qrequest) { scsi_ccb *request = qrequest->request; scsi_res_read_capacity data; scsi_cmd_read_capacity *cmd = (scsi_cmd_read_capacity *)request->cdb; uint32 lastBlock; if (cmd->pmi || cmd->lba) { set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD); return; } // TODO: 512 bytes fixed block size? data.block_size = B_HOST_TO_BENDIAN_INT32(512); lastBlock = device->total_sectors - 1; data.lba = B_HOST_TO_BENDIAN_INT32(lastBlock); copy_sg_data(request, 0, request->data_length, &data, sizeof(data), false); request->data_resid = max(request->data_length - sizeof(data), 0); }
static void ata_mode_sense_10(ide_device_info *device, ide_qrequest *qrequest) { scsi_ccb *request = qrequest->request; scsi_cmd_mode_sense_10 *cmd = (scsi_cmd_mode_sense_10 *)request->cdb; scsi_mode_param_header_10 param_header; scsi_modepage_control control; scsi_mode_param_block_desc block_desc; size_t totalLength = sizeof(scsi_mode_param_header_10) + sizeof(scsi_mode_param_block_desc) + sizeof(scsi_modepage_control); scsi_mode_param_dev_spec_da devspec = { _res0_0 : 0, dpo_fua : 0, _res0_6 : 0, write_protected : 0 }; uint32 allocationLength; SHOW_FLOW0(1, "Hi!"); allocationLength = B_BENDIAN_TO_HOST_INT16(cmd->allocation_length); // we answer control page requests and "all pages" requests // (as the latter are the same as the first) if ((cmd->page_code != SCSI_MODEPAGE_CONTROL && cmd->page_code != SCSI_MODEPAGE_ALL) || (cmd->page_control != SCSI_MODE_SENSE_PC_CURRENT && cmd->page_control != SCSI_MODE_SENSE_PC_SAVED)) { set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD); return; } //param_header = (scsi_mode_param_header_10 *)request->data; param_header.mode_data_length = B_HOST_TO_BENDIAN_INT16(totalLength - 1); param_header.medium_type = 0; // XXX standard is a bit vague here param_header.dev_spec_parameter = *(uint8 *)&devspec; param_header.block_desc_length = B_HOST_TO_BENDIAN_INT16(sizeof(scsi_mode_param_block_desc)); copy_sg_data(request, 0, allocationLength, ¶m_header, sizeof(param_header), false); /*block_desc = (scsi_mode_param_block_desc *)(request->data + sizeof(*param_header));*/ memset(&block_desc, 0, sizeof(block_desc)); // density is reserved (0), descriptor apply to entire medium (num_blocks=0) // remains the blocklen to be set block_desc.high_blocklen = 0; block_desc.med_blocklen = 512 >> 8; block_desc.low_blocklen = 512 & 0xff; copy_sg_data(request, sizeof(param_header), allocationLength, &block_desc, sizeof(block_desc), false); /*contr = (scsi_modepage_contr *)(request->data + sizeof(*param_header) + ((uint16)param_header->high_block_desc_len << 8) + param_header->low_block_desc_len);*/ memset(&control, 0, sizeof(control)); control.RLEC = false; control.DQue = !device->CQ_enabled; control.QErr = false; // when a command fails we requeue all // lost commands automagically control.QAM = SCSI_QAM_UNRESTRICTED; copy_sg_data(request, sizeof(param_header) + B_BENDIAN_TO_HOST_INT16(param_header.block_desc_length), allocationLength, &control, sizeof(control), false); // the number of bytes that were transferred to buffer is // restricted by allocation length and by request data buffer size totalLength = min(totalLength, allocationLength); totalLength = min(totalLength, request->data_length); request->data_resid = request->data_length - totalLength; } /*! Emulate modifying control page */ static bool ata_mode_select_control_page(ide_device_info *device, ide_qrequest *qrequest, scsi_modepage_control *page) { if (page->header.page_length != sizeof(*page) - sizeof(page->header)) { set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR); return false; } // we only support enabling/disabling command queuing enable_CQ(device, !page->DQue); return true; } /*! Emulate MODE SELECT 10 command */ static void ata_mode_select_10(ide_device_info *device, ide_qrequest *qrequest) { scsi_ccb *request = qrequest->request; scsi_cmd_mode_select_10 *cmd = (scsi_cmd_mode_select_10 *)request->cdb; scsi_mode_param_header_10 param_header; scsi_modepage_header page_header; uint32 totalLength; uint32 modepageOffset; char modepage_buffer[64]; // !!! enlarge this to support longer mode pages if (cmd->save_pages || cmd->pf != 1) { set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_CDB_FIELD); return; } totalLength = min(request->data_length, B_BENDIAN_TO_HOST_INT16(cmd->param_list_length)); // first, retrieve page header to get size of different chunks //param_header = (scsi_mode_param_header_10 *)request->data; if (!copy_sg_data(request, 0, totalLength, ¶m_header, sizeof(param_header), true)) goto err; totalLength = min(totalLength, B_BENDIAN_TO_HOST_INT16(param_header.mode_data_length) + 1UL); // this is the start of the first mode page; // we ignore the block descriptor silently modepageOffset = sizeof(param_header) + B_BENDIAN_TO_HOST_INT16(param_header.block_desc_length); // go through list of pages while (modepageOffset < totalLength) { uint32 pageLength; // get header to know how long page is if (!copy_sg_data(request, modepageOffset, totalLength, &page_header, sizeof(page_header), true)) goto err; // get size of one page and copy it to buffer pageLength = page_header.page_length + sizeof(scsi_modepage_header); // the buffer has a maximum size - this is really standard compliant but // sufficient for our needs if (pageLength > sizeof(modepage_buffer)) goto err; if (!copy_sg_data(request, modepageOffset, totalLength, &modepage_buffer, min(pageLength, sizeof(modepage_buffer)), true)) goto err; // modify page; // currently, we only support the control mode page switch (page_header.page_code) { case SCSI_MODEPAGE_CONTROL: if (!ata_mode_select_control_page(device, qrequest, (scsi_modepage_control *)modepage_buffer)) return; break; default: set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_INV_PARAM_LIST_FIELD); return; } modepageOffset += pageLength; } if (modepageOffset != totalLength) goto err; request->data_resid = request->data_length - totalLength; return; // if we arrive here, data length was incorrect err: set_sense(device, SCSIS_KEY_ILLEGAL_REQUEST, SCSIS_ASC_PARAM_LIST_LENGTH_ERR); }