1 /* 2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. 3 * Copyright 2008, Marcus Overhagen. 4 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. 5 * Copyright 2002-2003, Thomas Kurschel. 6 * 7 * Distributed under the terms of the MIT License. 8 */ 9 10 #include "ATAPrivate.h" 11 12 ATAPIDevice::ATAPIDevice(ATAChannel *channel, uint8 index) 13 : ATADevice(channel, index) 14 { 15 } 16 17 18 ATAPIDevice::~ATAPIDevice() 19 { 20 } 21 22 23 status_t 24 ATAPIDevice::SendPacket(ATARequest *request) 25 { 26 TRACE_FUNCTION("%p\n", request); 27 28 // only READ/WRITE commands can use DMA 29 // (the device may support it always, but IDE controllers don't 30 // report how much data is transmitted, and this information is 31 // crucial for the SCSI protocol) 32 // special offer: let READ_CD commands use DMA too 33 uint8 command = fPacket[0]; 34 request->SetUseDMA(UseDMA() 35 && (command == SCSI_OP_READ_6 || command == SCSI_OP_WRITE_6 36 || command == SCSI_OP_READ_10 || command == SCSI_OP_WRITE_10 37 || command == SCSI_OP_READ_12 || command == SCSI_OP_WRITE_12 38 || command == SCSI_OP_READ_CD) 39 && fChannel->PrepareDMA(request) == B_OK); 40 TRACE("using dma: %s\n", request->UseDMA() ? "yes" : "no"); 41 42 if (!request->UseDMA()) 43 request->PrepareSGInfo(); 44 45 if (_FillTaskFilePacket(request) != B_OK) { 46 TRACE_ERROR("failed to setup transfer request\n"); 47 if (request->UseDMA()) 48 fChannel->FinishDMA(); 49 return B_ERROR; 50 } 51 52 status_t result = fChannel->SendRequest(request, 0); 53 if (result != B_OK) { 54 TRACE_ERROR("failed to send packet request\n"); 55 if (request->UseDMA()) 56 fChannel->FinishDMA(); 57 return result; 58 } 59 60 // wait for device to get ready for packet transmission 61 if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 62 ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, 100 * 1000) != B_OK) { 63 TRACE_ERROR("timeout waiting for data request\n"); 64 if (request->UseDMA()) 65 fChannel->FinishDMA(); 66 67 request->SetStatus(SCSI_SEQUENCE_FAIL); 68 return B_TIMED_OUT; 69 } 70 71 // make sure device really asks for command packet 72 fRegisterMask = ATA_MASK_INTERRUPT_REASON; 73 fChannel->ReadRegs(this); 74 75 if (!fTaskFile.packet_res.cmd_or_data 76 || fTaskFile.packet_res.input_or_output) { 77 TRACE_ERROR("device doesn't ask for packet\n"); 78 if (request->UseDMA()) 79 fChannel->FinishDMA(); 80 81 request->SetStatus(SCSI_SEQUENCE_FAIL); 82 return B_ERROR; 83 } 84 85 // some old drives need a delay before submitting the packet 86 spin(10); 87 88 // write packet 89 if (fChannel->WritePIO(fPacket, sizeof(fPacket)) != B_OK) { 90 TRACE_ERROR("failed to write packet\n"); 91 if (request->UseDMA()) 92 fChannel->FinishDMA(); 93 94 request->SetStatus(SCSI_HBA_ERR); 95 return B_ERROR; 96 } 97 98 if (!request->HasData()) 99 return _FinishRequest(request, ATA_WAIT_FINISH); 100 101 if (request->UseDMA()) { 102 fChannel->PrepareWaitingForInterrupt(); 103 fChannel->StartDMA(); 104 105 result = fChannel->WaitForInterrupt(request->Timeout()); 106 status_t dmaResult = fChannel->FinishDMA(); 107 if (result != B_OK) { 108 request->SetStatus(SCSI_CMD_TIMEOUT); 109 return B_TIMED_OUT; 110 } 111 112 result = _FinishRequest(request, ATA_WAIT_FINISH); 113 if (result != B_OK) { 114 TRACE_ERROR("device indicates transfer error after dma\n"); 115 return result; 116 } 117 118 // for ATAPI it's ok for the device to send too much 119 if (dmaResult == B_OK || dmaResult == B_DEV_DATA_OVERRUN) { 120 fDMAFailures = 0; 121 request->CCB()->data_resid = 0; 122 return B_OK; 123 } 124 125 TRACE_ERROR("dma transfer failed\n"); 126 request->SetSense(SCSIS_KEY_HARDWARE_ERROR, 127 SCSIS_ASC_LUN_COM_FAILURE); 128 fDMAFailures++; 129 if (fDMAFailures >= ATA_MAX_DMA_FAILURES) { 130 TRACE_ALWAYS("disabling DMA after %u failures\n", fDMAFailures); 131 fUseDMA = false; 132 } 133 134 return B_ERROR; 135 } 136 137 result = fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 138 ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, request->Timeout()); 139 if (result != B_OK) { 140 if (result == B_TIMED_OUT) { 141 TRACE_ERROR("timeout waiting for device to request data\n"); 142 request->SetStatus(SCSI_CMD_TIMEOUT); 143 return B_TIMED_OUT; 144 } else 145 return _FinishRequest(request, 0); 146 } 147 148 // PIO data transfer 149 while (true) { 150 fRegisterMask = ATA_MASK_INTERRUPT_REASON | ATA_MASK_BYTE_COUNT; 151 fChannel->ReadRegs(this); 152 153 if (fTaskFile.packet_res.cmd_or_data) { 154 TRACE_ERROR("device expecting command instead of data\n"); 155 request->SetStatus(SCSI_SEQUENCE_FAIL); 156 return B_ERROR; 157 } 158 159 size_t length = fTaskFile.packet_res.byte_count_0_7 160 | ((size_t)fTaskFile.packet_res.byte_count_8_15 << 8); 161 TRACE("about to transfer %lu bytes\n", length); 162 163 request->SetBytesLeft(length); 164 fChannel->ExecutePIOTransfer(request); 165 166 result = fChannel->Wait(0, ATA_STATUS_BUSY, 0, request->Timeout()); 167 if (result != B_OK) { 168 if (result == B_TIMED_OUT) { 169 TRACE_ERROR("timeout waiting for device to finish transfer\n"); 170 request->SetStatus(SCSI_CMD_TIMEOUT); 171 return B_TIMED_OUT; 172 } else 173 return _FinishRequest(request, 0); 174 } 175 176 if ((fChannel->AltStatus() & ATA_STATUS_DATA_REQUEST) == 0) { 177 // transfer complete 178 TRACE("pio transfer complete\n"); 179 break; 180 } 181 } 182 183 return _FinishRequest(request, ATA_WAIT_FINISH); 184 } 185 186 187 status_t 188 ATAPIDevice::ExecuteIO(ATARequest *request) 189 { 190 scsi_ccb *ccb = request->CCB(); 191 if (ccb->target_lun != 0) { 192 TRACE_ERROR("invalid target lun %d\n", ccb->target_lun); 193 request->SetStatus(SCSI_SEL_TIMEOUT); 194 return B_BAD_INDEX; 195 } 196 197 // ATAPI command packets are 12 bytes long; 198 // if the command is shorter, remaining bytes must be padded with zeros 199 memset(fPacket, 0, sizeof(fPacket)); 200 memcpy(fPacket, ccb->cdb, ccb->cdb_length); 201 202 request->SetDevice(this); 203 request->SetIsWrite((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT); 204 return SendPacket(request); 205 } 206 207 208 status_t 209 ATAPIDevice::Configure() 210 { 211 if (fInfoBlock.word_0.atapi.atapi_device != ATA_WORD_0_ATAPI_DEVICE) { 212 TRACE_ERROR("infoblock indicates non-atapi device\n"); 213 return B_ERROR; 214 } 215 216 fTaskFile.packet.lun = 0; 217 218 status_t result = ConfigureDMA(); 219 if (result != B_OK) 220 return result; 221 222 result = DisableCommandQueueing(); 223 if (result != B_OK) 224 return result; 225 226 return B_OK; 227 } 228 229 230 status_t 231 ATAPIDevice::_FillTaskFilePacket(ATARequest *request) 232 { 233 scsi_ccb *ccb = request->CCB(); 234 fRegisterMask = ATA_MASK_FEATURES | ATA_MASK_BYTE_COUNT; 235 fTaskFile.packet.dma = request->UseDMA() ? 1 : 0; 236 fTaskFile.packet.ovl = 0; 237 fTaskFile.packet.byte_count_0_7 = ccb->data_length & 0xff; 238 fTaskFile.packet.byte_count_8_15 = ccb->data_length >> 8; 239 fTaskFile.packet.command = ATA_COMMAND_PACKET; 240 return B_OK; 241 } 242 243 244 status_t 245 ATAPIDevice::_FinishRequest(ATARequest *request, uint32 flags) 246 { 247 if (fChannel->FinishRequest(request, flags 248 | ATA_CHECK_DEVICE_FAULT, 0) != B_OK) { 249 // when we get an error from a packet device, we instruct the 250 // scsi layer to do a request sense. but since we don't want to 251 // return an emulated sense coming from ata, we clear our sense 252 // key first so that the next request sense will go to the packet 253 // device directly (as a packet command). 254 request->ClearSense(); 255 request->SetStatus(SCSI_REQ_CMP_ERR); 256 request->CCB()->device_status = SCSI_STATUS_CHECK_CONDITION; 257 return B_ERROR; 258 } 259 260 return B_OK; 261 } 262