Author: bonefish Date: 2010-06-21 18:14:31 +0200 (Mon, 21 Jun 2010) New Revision: 37198 Changeset: http://dev.haiku-os.org/changeset/37198/haiku Added: haiku/trunk/src/add-ons/kernel/bus_managers/ide/emulation.cpp haiku/trunk/src/add-ons/kernel/bus_managers/ide/pio.cpp Removed: haiku/trunk/src/add-ons/kernel/bus_managers/ide/emulation.c haiku/trunk/src/add-ons/kernel/bus_managers/ide/pio.c Modified: haiku/trunk/src/add-ons/kernel/bus_managers/ide/Jamfile haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_internal.h haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_sim.h Log: Fixed build -- code including <vm/vm.h> must be C++. Modified: haiku/trunk/src/add-ons/kernel/bus_managers/ide/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ide/Jamfile 2010-06-21 16:13:47 UTC (rev 37197) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ide/Jamfile 2010-06-21 16:14:31 UTC (rev 37198) @@ -10,10 +10,10 @@ channels.c devices.c dma.c - emulation.c + emulation.cpp ide.c ide_sim.c - pio.c + pio.cpp queuing.c scsi2ata.c sync.c Copied: haiku/trunk/src/add-ons/kernel/bus_managers/ide/emulation.cpp (from rev 37196, haiku/trunk/src/add-ons/kernel/bus_managers/ide/emulation.c) =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ide/emulation.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ide/emulation.cpp 2010-06-21 16:14:31 UTC (rev 37198) @@ -0,0 +1,111 @@ +/* + * Copyright 2004-2007, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx All rights reserved. + * Copyright 2002/03, Thomas Kurschel. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + +//! General SCSI emulation routines + + +#include "ide_internal.h" +#include "ide_sim.h" + +#include <vm/vm.h> + +#include <string.h> + + +/*! 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; +} + + +/*! Copy data between request data and buffer + request - request to copy data from/to + offset - offset of data in request + allocation_length- limit of request's data buffer according to CDB + buffer - data to copy data from/to + size - number of bytes to copy + to_buffer - true: copy from request to buffer + false: copy from buffer to request + return: true, if data of request was large enough +*/ +bool +copy_sg_data(scsi_ccb *request, uint offset, uint allocationLength, + void *buffer, int size, bool toBuffer) +{ + const physical_entry *sgList = request->sg_list; + int sgCount = request->sg_count; + int requestSize; + + SHOW_FLOW(3, "offset=%u, req_size_limit=%d, size=%d, sg_list=%p, sg_cnt=%d, %s buffer", + offset, allocationLength, size, sgList, sgCount, toBuffer ? "to" : "from"); + + // skip unused S/G entries + while (sgCount > 0 && offset >= sgList->size) { + offset -= sgList->size; + ++sgList; + --sgCount; + } + + if (sgCount == 0) + return 0; + + // remaining bytes we are allowed to copy from/to request + requestSize = min_c(allocationLength, request->data_length) - offset; + + // copy one S/G entry at a time + for (; size > 0 && requestSize > 0 && sgCount > 0; ++sgList, --sgCount) { + size_t bytes; + + bytes = min_c(size, requestSize); + bytes = min_c(bytes, sgList->size); + + SHOW_FLOW(4, "buffer=%p, virt_addr=%p, bytes=%d, to_buffer=%d", + buffer, (void *)(sgList->address + offset), (int)bytes, toBuffer); + + if (toBuffer) { + vm_memcpy_from_physical(buffer, sgList->address + offset, bytes, + false); + } else { + vm_memcpy_to_physical(sgList->address + offset, buffer, bytes, + false); + } + + buffer = (char *)buffer + bytes; + size -= bytes; + offset = 0; + } + + return size == 0; +} Modified: haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_internal.h =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_internal.h 2010-06-21 16:13:47 UTC (rev 37197) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_internal.h 2010-06-21 16:14:31 UTC (rev 37198) @@ -6,6 +6,8 @@ #define __IDE_INTERNAL_H__ +#include <sys/cdefs.h> + #include <bus/IDE.h> #include <bus/SCSI.h> #include "ide_device_infoblock.h" @@ -289,6 +291,9 @@ } +__BEGIN_DECLS + + // ata.c bool check_rw_error(ide_device_info *device, ide_qrequest *qrequest); @@ -399,4 +404,7 @@ status_t ide_timeout(timer *arg); +__END_DECLS + + #endif /* __IDE_INTERNAL_H__ */ Modified: haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_sim.h =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_sim.h 2010-06-21 16:13:47 UTC (rev 37197) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ide/ide_sim.h 2010-06-21 16:14:31 UTC (rev 37198) @@ -14,8 +14,11 @@ #define __IDE_SIM_H__ +#include <sys/cdefs.h> + #include "scsi_cmds.h" + extern scsi_for_sim_interface *scsi; extern scsi_sim_interface ide_sim_module; @@ -72,7 +75,11 @@ } +__BEGIN_DECLS + void create_sense(ide_device_info *device, scsi_sense *sense); +__END_DECLS + #endif Copied: haiku/trunk/src/add-ons/kernel/bus_managers/ide/pio.cpp (from rev 37196, haiku/trunk/src/add-ons/kernel/bus_managers/ide/pio.c) =================================================================== --- haiku/trunk/src/add-ons/kernel/bus_managers/ide/pio.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/bus_managers/ide/pio.cpp 2010-06-21 16:14:31 UTC (rev 37198) @@ -0,0 +1,369 @@ +/* + * Copyright 2004-2007, Haiku, Inc. All RightsReserved. + * Copyright 2002-2004, Thomas Kurschel. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + +/* + PIO data transmission + + This file is more difficult then you might expect as the SCSI system + uses physical addresses everywhere which have to be mapped into + virtual address space during transmission. Additionally, during ATAPI + commands we may have to transmit more data then exist because the + data len specified by the command doesn't need to be the same as + of the data buffer provided. + + The handling of S/G entries of odd size may look superfluous as the + SCSI bus manager can take care of that. In general, this would be possible + as most controllers need even alignment for DMA as well, but some can + handle _any_ S/G list and it wouldn't be sensitive to enforce stricter + alignement just for some rare PIO transmissions. + + Little hint for the meaning of "transferred": this is the number of bytes + sent over the bus. For read-transmissions, this may be one more then copied + into the buffer (the extra byte read is stored in device->odd_byte), for + write-transmissions, this may be one less (the waiting byte is pending in + device->odd_byte). + + In terms of error handling: we don't bother checking transmission of every + single byte via read/write_pio(). At least at the end of the request, when + the status bits are verified, we will see that something has gone wrong. + + TBD: S/G entries may have odd start address. For non-Intel architecture + we either have to copy data to an aligned buffer or have to modify + PIO-handling in controller drivers. +*/ + +#include "ide_internal.h" +#include "ide_sim.h" + +#include <thread.h> +#include <vm/vm.h> + +#include <string.h> + + +// internal error code if scatter gather table is too short +#define ERR_TOO_BIG (B_ERRORS_END + 1) + + +/*! Prepare PIO transfer */ +void +prep_PIO_transfer(ide_device_info *device, ide_qrequest *qrequest) +{ + SHOW_FLOW0(4, ""); + + device->left_sg_elem = qrequest->request->sg_count; + device->cur_sg_elem = qrequest->request->sg_list; + device->cur_sg_ofs = 0; + device->has_odd_byte = false; + qrequest->request->data_resid = qrequest->request->data_length; +} + + +/*! Transfer virtually continuous data */ +static inline status_t +transfer_PIO_virtcont(ide_device_info *device, uint8 *virtualAddress, + int length, bool write, int *transferred) +{ + ide_bus_info *bus = device->bus; + ide_controller_interface *controller = bus->controller; + void * channel_cookie = bus->channel_cookie; + + if (write) { + // if there is a byte left from last chunk, transmit it together + // with the first byte of the current chunk (IDE requires 16 bits + // to be transmitted at once) + if (device->has_odd_byte) { + uint8 buffer[2]; + + buffer[0] = device->odd_byte; + buffer[1] = *virtualAddress++; + + controller->write_pio(channel_cookie, (uint16 *)buffer, 1, false); + + --length; + *transferred += 2; + } + + controller->write_pio(channel_cookie, (uint16 *)virtualAddress, + length / 2, false); + + // take care if chunk size was odd, which means that 1 byte remains + virtualAddress += length & ~1; + *transferred += length & ~1; + + device->has_odd_byte = (length & 1) != 0; + + if (device->has_odd_byte) + device->odd_byte = *virtualAddress; + } else { + // if we read one byte too much last time, push it into current chunk + if (device->has_odd_byte) { + *virtualAddress++ = device->odd_byte; + --length; + } + + SHOW_FLOW(4, "Reading PIO to %p, %d bytes", virtualAddress, length); + + controller->read_pio(channel_cookie, (uint16 *)virtualAddress, + length / 2, false); + + // take care of odd chunk size; + // in this case we read 1 byte to few! + virtualAddress += length & ~1; + *transferred += length & ~1; + + device->has_odd_byte = (length & 1) != 0; + + if (device->has_odd_byte) { + uint8 buffer[2]; + + // now read the missing byte; as we have to read 2 bytes at once, + // we'll read one byte too much + controller->read_pio(channel_cookie, (uint16 *)buffer, 1, false); + + *virtualAddress = buffer[0]; + device->odd_byte = buffer[1]; + + *transferred += 2; + } + } + + return B_OK; +} + + +/*! Transmit physically continuous data */ +static inline status_t +transfer_PIO_physcont(ide_device_info *device, addr_t physicalAddress, + int length, bool write, int *transferred) +{ + // we must split up chunk into B_PAGE_SIZE blocks as we can map only + // one page into address space at once + while (length > 0) { + addr_t virtualAddress; + void* handle; + int page_left, cur_len; + status_t err; + struct thread* thread = thread_get_current_thread(); + + SHOW_FLOW(4, "Transmitting to/from physical address %lx, %d bytes left", + physicalAddress, length); + + thread_pin_to_current_cpu(thread); + if (vm_get_physical_page_current_cpu(physicalAddress, &virtualAddress, + &handle) != B_OK) { + thread_unpin_from_current_cpu(thread); + // ouch: this should never ever happen + set_sense(device, SCSIS_KEY_HARDWARE_ERROR, SCSIS_ASC_INTERNAL_FAILURE); + return B_ERROR; + } + + // if chunks starts in the middle of a page, we have even less then + // a page left + page_left = B_PAGE_SIZE - physicalAddress % B_PAGE_SIZE; + + SHOW_FLOW(4, "page_left=%d", page_left); + + cur_len = min_c(page_left, length); + + SHOW_FLOW(4, "cur_len=%d", cur_len); + + err = transfer_PIO_virtcont(device, (uint8 *)virtualAddress, + cur_len, write, transferred); + + vm_put_physical_page_current_cpu(virtualAddress, handle); + thread_unpin_from_current_cpu(thread); + + if (err != B_OK) + return err; + + length -= cur_len; + physicalAddress += cur_len; + } + + return B_OK; +} + + +/*! Transfer PIO block from/to buffer */ +static inline int +transfer_PIO_block(ide_device_info *device, int length, bool write, int *transferred) +{ + // data is usually split up into multiple scatter/gather blocks + while (length > 0) { + int left_bytes, cur_len; + status_t err; + + if (device->left_sg_elem == 0) + // ups - buffer too small (for ATAPI data, this is OK) + return ERR_TOO_BIG; + + // we might have transmitted part of a scatter/entry already! + left_bytes = device->cur_sg_elem->size - device->cur_sg_ofs; + + cur_len = min_c(left_bytes, length); + + err = transfer_PIO_physcont(device, + (addr_t)device->cur_sg_elem->address + device->cur_sg_ofs, + cur_len, write, transferred); + + if (err != B_OK) + return err; + + if (left_bytes <= length) { + // end of one scatter/gather block reached + device->cur_sg_ofs = 0; + ++device->cur_sg_elem; + --device->left_sg_elem; + } else { + // still in the same block + device->cur_sg_ofs += cur_len; + } + + length -= cur_len; + } + + return B_OK; +} + + +/*! Write zero data (required for ATAPI if we ran out of data) */ + +static void +write_discard_PIO(ide_device_info *device, int length) +{ + ide_bus_info *bus = device->bus; + uint8 buffer[32]; + + memset(buffer, 0, sizeof(buffer)); + + // we transmit 32 zero-bytes at once + // (not very efficient but easy to implement - you get what you deserve + // when you don't provide enough buffer) + while (length > 0) { + int cur_len; + + // if device asks for odd number of bytes, append an extra byte to + // make length even (this is the "length + 1" term) + cur_len = min_c(length + 1, (int)(sizeof(buffer))) / 2; + + bus->controller->write_pio(bus->channel_cookie, (uint16 *)buffer, cur_len, false); + + length -= cur_len * 2; + } +} + + +/*! Read PIO data and discard it (required for ATAPI if buffer was too small) */ +static void +read_discard_PIO(ide_device_info *device, int length) +{ + ide_bus_info *bus = device->bus; + uint8 buffer[32]; + + // discard 32 bytes at once (see write_discard_PIO) + while (length > 0) { + int cur_len; + + // read extra byte if length is odd (that's the "length + 1") + cur_len = min_c(length + 1, (int)sizeof(buffer)) / 2; + + bus->controller->read_pio(bus->channel_cookie, (uint16 *)buffer, cur_len, false); + + length -= cur_len * 2; + } +} + + +/*! write PIO data + return: there are 3 possible results + NO_ERROR - everything's nice and groovy + ERR_TOO_BIG - data buffer was too short, remaining data got discarded + B_ERROR - something serious went wrong, sense data was set +*/ +status_t +write_PIO_block(ide_qrequest *qrequest, int length) +{ + ide_device_info *device = qrequest->device; + int transferred; + status_t err; + + transferred = 0; + err = transfer_PIO_block(device, length, true, &transferred); + + qrequest->request->data_resid -= transferred; + + if (err != ERR_TOO_BIG) + return err; + + // there may be a pending odd byte - transmit that now + if (qrequest->device->has_odd_byte) { + uint8 buffer[2]; + + buffer[0] = device->odd_byte; + buffer[1] = 0; + + device->has_odd_byte = false; + + qrequest->request->data_resid -= 1; + transferred += 2; + + device->bus->controller->write_pio(device->bus->channel_cookie, (uint16 *)buffer, 1, false); + } + + // "transferred" may actually be larger then length because the last odd-byte + // is sent together with an extra zero-byte + if (transferred >= length) + return err; + + // Ouch! the device asks for data but we haven't got any left. + // Sadly, this behaviour is OK for ATAPI packets, but there is no + // way to tell the device that we don't have any data left; + // only solution is to send zero bytes, though it's BAD BAD BAD + write_discard_PIO(qrequest->device, length - transferred); + return ERR_TOO_BIG; +} + + +/*! read PIO data + return: see write_PIO_block +*/ +status_t +read_PIO_block(ide_qrequest *qrequest, int length) +{ + ide_device_info *device = qrequest->device; + int transferred; + status_t err; + + transferred = 0; + err = transfer_PIO_block(qrequest->device, length, false, &transferred); + + qrequest->request->data_resid -= transferred; + + // if length was odd, there's an extra byte waiting in device->odd_byte + if (device->has_odd_byte) { + // discard byte + device->has_odd_byte = false; + // adjust res_id as the extra byte didn't reach the buffer + ++qrequest->request->data_resid; + } + + if (err != ERR_TOO_BIG) + return err; + + // the device returns more data then the buffer can store; + // for ATAPI this is OK - we just discard remaining bytes (there + // is no way to tell ATAPI about that, but we "only" waste time) + + // perhaps discarding the extra odd-byte was sufficient + if (transferred >= length) + return err; + + SHOW_FLOW(3, "discarding after %d bytes", transferred); + read_discard_PIO(qrequest->device, length - transferred); + return ERR_TOO_BIG; +}