1/* 2 * Copyright 2004-2011, Haiku, Inc. All rights reserved. 3 * Copyright 2002-2004, Thomas Kurschel. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8/* 9 PIO data transmission 10 11 This file is more difficult then you might expect as the SCSI system 12 uses physical addresses everywhere which have to be mapped into 13 virtual address space during transmission. Additionally, during ATAPI 14 commands we may have to transmit more data then exist because the 15 data len specified by the command doesn't need to be the same as 16 of the data buffer provided. 17 18 The handling of S/G entries of odd size may look superfluous as the 19 SCSI bus manager can take care of that. In general, this would be possible 20 as most controllers need even alignment for DMA as well, but some can 21 handle _any_ S/G list and it wouldn't be sensitive to enforce stricter 22 alignement just for some rare PIO transmissions. 23 24 Little hint for the meaning of "transferred": this is the number of bytes 25 sent over the bus. For read-transmissions, this may be one more then copied 26 into the buffer (the extra byte read is stored in device->odd_byte), for 27 write-transmissions, this may be one less (the waiting byte is pending in 28 device->odd_byte). 29 30 In terms of error handling: we don't bother checking transmission of every 31 single byte via read/write_pio(). At least at the end of the request, when 32 the status bits are verified, we will see that something has gone wrong. 33 34 TBD: S/G entries may have odd start address. For non-Intel architecture 35 we either have to copy data to an aligned buffer or have to modify 36 PIO-handling in controller drivers. 37*/ 38 39#include "ide_internal.h" 40#include "ide_sim.h" 41 42#include <thread.h> 43#include <vm/vm.h> 44 45#include <string.h> 46 47 48// internal error code if scatter gather table is too short 49#define ERR_TOO_BIG (B_ERRORS_END + 1) 50 51 52/*! Prepare PIO transfer */ 53void 54prep_PIO_transfer(ide_device_info *device, ide_qrequest *qrequest) 55{ 56 SHOW_FLOW0(4, ""); 57 58 device->left_sg_elem = qrequest->request->sg_count; 59 device->cur_sg_elem = qrequest->request->sg_list; 60 device->cur_sg_ofs = 0; 61 device->has_odd_byte = false; 62 qrequest->request->data_resid = qrequest->request->data_length; 63} 64 65 66/*! Transfer virtually continuous data */ 67static inline status_t 68transfer_PIO_virtcont(ide_device_info *device, uint8 *virtualAddress, 69 int length, bool write, int *transferred) 70{ 71 ide_bus_info *bus = device->bus; 72 ide_controller_interface *controller = bus->controller; 73 void * channel_cookie = bus->channel_cookie; 74 75 if (write) { 76 // if there is a byte left from last chunk, transmit it together 77 // with the first byte of the current chunk (IDE requires 16 bits 78 // to be transmitted at once) 79 if (device->has_odd_byte) { 80 uint8 buffer[2]; 81 82 buffer[0] = device->odd_byte; 83 buffer[1] = *virtualAddress++; 84 85 controller->write_pio(channel_cookie, (uint16 *)buffer, 1, false); 86 87 --length; 88 *transferred += 2; 89 } 90 91 controller->write_pio(channel_cookie, (uint16 *)virtualAddress, 92 length / 2, false); 93 94 // take care if chunk size was odd, which means that 1 byte remains 95 virtualAddress += length & ~1; 96 *transferred += length & ~1; 97 98 device->has_odd_byte = (length & 1) != 0; 99 100 if (device->has_odd_byte) 101 device->odd_byte = *virtualAddress; 102 } else { 103 // if we read one byte too much last time, push it into current chunk 104 if (device->has_odd_byte) { 105 *virtualAddress++ = device->odd_byte; 106 --length; 107 } 108 109 SHOW_FLOW(4, "Reading PIO to %p, %d bytes", virtualAddress, length); 110 111 controller->read_pio(channel_cookie, (uint16 *)virtualAddress, 112 length / 2, false); 113 114 // take care of odd chunk size; 115 // in this case we read 1 byte to few! 116 virtualAddress += length & ~1; 117 *transferred += length & ~1; 118 119 device->has_odd_byte = (length & 1) != 0; 120 121 if (device->has_odd_byte) { 122 uint8 buffer[2]; 123 124 // now read the missing byte; as we have to read 2 bytes at once, 125 // we'll read one byte too much 126 controller->read_pio(channel_cookie, (uint16 *)buffer, 1, false); 127 128 *virtualAddress = buffer[0]; 129 device->odd_byte = buffer[1]; 130 131 *transferred += 2; 132 } 133 } 134 135 return B_OK; 136} 137 138 139/*! Transmit physically continuous data */ 140static inline status_t 141transfer_PIO_physcont(ide_device_info *device, addr_t physicalAddress, 142 int length, bool write, int *transferred) 143{ 144 // we must split up chunk into B_PAGE_SIZE blocks as we can map only 145 // one page into address space at once 146 while (length > 0) { 147 addr_t virtualAddress; 148 void* handle; 149 int page_left, cur_len; 150 status_t err; 151 Thread* thread = thread_get_current_thread(); 152 153 SHOW_FLOW(4, "Transmitting to/from physical address %lx, %d bytes left", 154 physicalAddress, length); 155 156 thread_pin_to_current_cpu(thread); 157 if (vm_get_physical_page_current_cpu(physicalAddress, &virtualAddress, 158 &handle) != B_OK) { 159 thread_unpin_from_current_cpu(thread); 160 // ouch: this should never ever happen 161 set_sense(device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE); 162 return B_ERROR; 163 } 164 165 // if chunks starts in the middle of a page, we have even less then 166 // a page left 167 page_left = B_PAGE_SIZE - physicalAddress % B_PAGE_SIZE; 168 169 SHOW_FLOW(4, "page_left=%d", page_left); 170 171 cur_len = min_c(page_left, length); 172 173 SHOW_FLOW(4, "cur_len=%d", cur_len); 174 175 err = transfer_PIO_virtcont(device, (uint8 *)virtualAddress, 176 cur_len, write, transferred); 177 178 vm_put_physical_page_current_cpu(virtualAddress, handle); 179 thread_unpin_from_current_cpu(thread); 180 181 if (err != B_OK) 182 return err; 183 184 length -= cur_len; 185 physicalAddress += cur_len; 186 } 187 188 return B_OK; 189} 190 191 192/*! Transfer PIO block from/to buffer */ 193static inline int 194transfer_PIO_block(ide_device_info *device, int length, bool write, int *transferred) 195{ 196 // data is usually split up into multiple scatter/gather blocks 197 while (length > 0) { 198 int left_bytes, cur_len; 199 status_t err; 200 201 if (device->left_sg_elem == 0) 202 // ups - buffer too small (for ATAPI data, this is OK) 203 return ERR_TOO_BIG; 204 205 // we might have transmitted part of a scatter/entry already! 206 left_bytes = device->cur_sg_elem->size - device->cur_sg_ofs; 207 208 cur_len = min_c(left_bytes, length); 209 210 err = transfer_PIO_physcont(device, 211 (addr_t)device->cur_sg_elem->address + device->cur_sg_ofs, 212 cur_len, write, transferred); 213 214 if (err != B_OK) 215 return err; 216 217 if (left_bytes <= length) { 218 // end of one scatter/gather block reached 219 device->cur_sg_ofs = 0; 220 ++device->cur_sg_elem; 221 --device->left_sg_elem; 222 } else { 223 // still in the same block 224 device->cur_sg_ofs += cur_len; 225 } 226 227 length -= cur_len; 228 } 229 230 return B_OK; 231} 232 233 234/*! Write zero data (required for ATAPI if we ran out of data) */ 235 236static void 237write_discard_PIO(ide_device_info *device, int length) 238{ 239 ide_bus_info *bus = device->bus; 240 uint8 buffer[32]; 241 242 memset(buffer, 0, sizeof(buffer)); 243 244 // we transmit 32 zero-bytes at once 245 // (not very efficient but easy to implement - you get what you deserve 246 // when you don't provide enough buffer) 247 while (length > 0) { 248 int cur_len; 249 250 // if device asks for odd number of bytes, append an extra byte to 251 // make length even (this is the "length + 1" term) 252 cur_len = min_c(length + 1, (int)(sizeof(buffer))) / 2; 253 254 bus->controller->write_pio(bus->channel_cookie, (uint16 *)buffer, cur_len, false); 255 256 length -= cur_len * 2; 257 } 258} 259 260 261/*! Read PIO data and discard it (required for ATAPI if buffer was too small) */ 262static void 263read_discard_PIO(ide_device_info *device, int length) 264{ 265 ide_bus_info *bus = device->bus; 266 uint8 buffer[32]; 267 268 // discard 32 bytes at once (see write_discard_PIO) 269 while (length > 0) { 270 int cur_len; 271 272 // read extra byte if length is odd (that's the "length + 1") 273 cur_len = min_c(length + 1, (int)sizeof(buffer)) / 2; 274 275 bus->controller->read_pio(bus->channel_cookie, (uint16 *)buffer, cur_len, false); 276 277 length -= cur_len * 2; 278 } 279} 280 281 282/*! write PIO data 283 return: there are 3 possible results 284 NO_ERROR - everything's nice and groovy 285 ERR_TOO_BIG - data buffer was too short, remaining data got discarded 286 B_ERROR - something serious went wrong, sense data was set 287*/ 288status_t 289write_PIO_block(ide_qrequest *qrequest, int length) 290{ 291 ide_device_info *device = qrequest->device; 292 int transferred; 293 status_t err; 294 295 transferred = 0; 296 err = transfer_PIO_block(device, length, true, &transferred); 297 298 qrequest->request->data_resid -= transferred; 299 300 if (err != ERR_TOO_BIG) 301 return err; 302 303 // there may be a pending odd byte - transmit that now 304 if (qrequest->device->has_odd_byte) { 305 uint8 buffer[2]; 306 307 buffer[0] = device->odd_byte; 308 buffer[1] = 0; 309 310 device->has_odd_byte = false; 311 312 qrequest->request->data_resid -= 1; 313 transferred += 2; 314 315 device->bus->controller->write_pio(device->bus->channel_cookie, (uint16 *)buffer, 1, false); 316 } 317 318 // "transferred" may actually be larger then length because the last odd-byte 319 // is sent together with an extra zero-byte 320 if (transferred >= length) 321 return err; 322 323 // Ouch! the device asks for data but we haven't got any left. 324 // Sadly, this behaviour is OK for ATAPI packets, but there is no 325 // way to tell the device that we don't have any data left; 326 // only solution is to send zero bytes, though it's BAD BAD BAD 327 write_discard_PIO(qrequest->device, length - transferred); 328 return ERR_TOO_BIG; 329} 330 331 332/*! read PIO data 333 return: see write_PIO_block 334*/ 335status_t 336read_PIO_block(ide_qrequest *qrequest, int length) 337{ 338 ide_device_info *device = qrequest->device; 339 int transferred; 340 status_t err; 341 342 transferred = 0; 343 err = transfer_PIO_block(qrequest->device, length, false, &transferred); 344 345 qrequest->request->data_resid -= transferred; 346 347 // if length was odd, there's an extra byte waiting in device->odd_byte 348 if (device->has_odd_byte) { 349 // discard byte 350 device->has_odd_byte = false; 351 // adjust res_id as the extra byte didn't reach the buffer 352 ++qrequest->request->data_resid; 353 } 354 355 if (err != ERR_TOO_BIG) 356 return err; 357 358 // the device returns more data then the buffer can store; 359 // for ATAPI this is OK - we just discard remaining bytes (there 360 // is no way to tell ATAPI about that, but we "only" waste time) 361 362 // perhaps discarding the extra odd-byte was sufficient 363 if (transferred >= length) 364 return err; 365 366 SHOW_FLOW(3, "discarding after %d bytes", transferred); 367 read_discard_PIO(qrequest->device, length - transferred); 368 return ERR_TOO_BIG; 369} 370