Author: kallisti5 Date: 2011-09-08 17:47:25 +0200 (Thu, 08 Sep 2011) New Revision: 42725 Changeset: https://dev.haiku-os.org/changeset/42725 Added: haiku/trunk/src/add-ons/kernel/generic/ata_adapter/ata_adapter.cpp haiku/trunk/src/add-ons/kernel/generic/ide_adapter/ide_adapter.cpp Removed: haiku/trunk/src/add-ons/kernel/generic/ata_adapter/ata_adapter.c haiku/trunk/src/add-ons/kernel/generic/ide_adapter/ide_adapter.c Modified: haiku/trunk/src/add-ons/kernel/generic/ata_adapter/Jamfile haiku/trunk/src/add-ons/kernel/generic/ide_adapter/Jamfile Log: * change c to cpp to resolve C89/C99 issue. (thanks Deadyak!) * changing to cpp uncovered a few bugs in ide_adaptor * correct losing signed integer * correct a variable name that conflicted with a type * gcc2 build now fixed after r42724 Modified: haiku/trunk/src/add-ons/kernel/generic/ata_adapter/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/ata_adapter/Jamfile 2011-09-08 03:15:37 UTC (rev 42724) +++ haiku/trunk/src/add-ons/kernel/generic/ata_adapter/Jamfile 2011-09-08 15:47:25 UTC (rev 42725) @@ -3,6 +3,6 @@ UsePrivateHeaders drivers kernel ; KernelAddon ata_adapter : - ata_adapter.c + ata_adapter.cpp ; Copied: haiku/trunk/src/add-ons/kernel/generic/ata_adapter/ata_adapter.cpp (from rev 42724, haiku/trunk/src/add-ons/kernel/generic/ata_adapter/ata_adapter.c) =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/ata_adapter/ata_adapter.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/ata_adapter/ata_adapter.cpp 2011-09-08 15:47:25 UTC (rev 42725) @@ -0,0 +1,894 @@ +/* + * Copyright 2002-04, Thomas Kurschel. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +/* + Generic ATA adapter library. +*/ + +#include <KernelExport.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> + +#include <ata_adapter.h> +#include <tracing.h> + +#define debug_level_flow 0 +#define debug_level_error 3 +#define debug_level_info 3 + +#define DEBUG_MSG_PREFIX "ATA PCI -- " + +#include "wrapper.h" + +#define TRACE dprintf + +#define INTERRUPT_TRACING 0 +#if INTERRUPT_TRACING + #define TRACE_INT(a...) ktrace_printf(a) +#else + #define TRACE_INT(a...) +#endif + + +#if ATA_DMA_TRACING + #define TRACE_DMA(x...) ktrace_printf(x) +#else + #define TRACE_DMA(x...) +#endif + +static ata_for_controller_interface *sATA; +static device_manager_info *sDeviceManager; + + +static void +set_channel(ata_adapter_channel_info *channel, ata_channel ataChannel) +{ + channel->ataChannel = ataChannel; +} + + +static status_t +ata_adapter_write_command_block_regs(ata_adapter_channel_info *channel, + ata_task_file *tf, ata_reg_mask mask) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + int i; + + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + // LBA48 registers must be written twice + if (((1 << (i + 7)) & mask) != 0) { + SHOW_FLOW( 4, "%x->HI(%x)", tf->raw.r[i + 7], i ); + pci->write_io_8(device, ioaddr + 1 + i, tf->raw.r[i + 7]); + } + + if (((1 << i) & mask) != 0) { + SHOW_FLOW( 4, "%x->LO(%x)", tf->raw.r[i], i ); + pci->write_io_8(device, ioaddr + 1 + i, tf->raw.r[i]); + } + } + + return B_OK; +} + + +static status_t +ata_adapter_read_command_block_regs(ata_adapter_channel_info *channel, + ata_task_file *tf, ata_reg_mask mask) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + int i; + + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + if (((1 << i) & mask) != 0) { + tf->raw.r[i] = pci->read_io_8(device, ioaddr + 1 + i); + SHOW_FLOW( 4, "%x: %x", i, (int)tf->raw.r[i] ); + } + } + + return B_OK; +} + + +static uint8 +ata_adapter_get_altstatus(ata_adapter_channel_info *channel) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + uint16 altstatusaddr = channel->control_block_base; + + if (channel->lost) + return 0x01; // Error bit + + return pci->read_io_8(device, altstatusaddr); +} + + +static status_t +ata_adapter_write_device_control(ata_adapter_channel_info *channel, uint8 val) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + uint16 device_control_addr = channel->control_block_base; + + SHOW_FLOW(3, "%x", (int)val); + + if (channel->lost) + return B_ERROR; + + pci->write_io_8(device, device_control_addr, val); + + return B_OK; +} + + +static status_t +ata_adapter_write_pio(ata_adapter_channel_info *channel, uint16 *data, + int count, bool force_16bit) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + force_16bit = true; + + if ((count & 1) != 0 || force_16bit) { + for (; count > 0; --count) + pci->write_io_16(device, ioaddr, *(data++)); + } else { + uint32 *cur_data = (uint32 *)data; + + for (; count > 0; count -= 2) + pci->write_io_32(device, ioaddr, *(cur_data++)); + } + + return B_OK; +} + + +static status_t +ata_adapter_read_pio(ata_adapter_channel_info *channel, uint16 *data, + int count, bool force_16bit) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + force_16bit = true; + + if ((count & 1) != 0 || force_16bit) { + for (; count > 0; --count) + *(data++) = pci->read_io_16(device, ioaddr ); + } else { + uint32 *cur_data = (uint32 *)data; + + for (; count > 0; count -= 2) + *(cur_data++) = pci->read_io_32(device, ioaddr); + } + + return B_OK; +} + + +static int32 +ata_adapter_inthand(void *arg) +{ + ata_adapter_channel_info *channel = (ata_adapter_channel_info *)arg; + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + uint8 statusATA, statusBM; + + TRACE_INT("ata_adapter_inthand\n"); + + // need to read bus master status first, because some controllers + // will clear the interrupt status bit once ATA status is read + statusBM = pci->read_io_8(device, channel->bus_master_base + + ATA_BM_STATUS_REG); + TRACE_INT("ata_adapter_inthand: BM-status 0x%02x\n", statusBM); + + // test if the interrupt was really generated by our controller + if (statusBM & ATA_BM_STATUS_INTERRUPT) { + // read ATA status register to acknowledge interrupt + statusATA = pci->read_io_8(device, channel->command_block_base + 7); + TRACE_INT("ata_adapter_inthand: ATA-status 0x%02x\n", statusATA); + + // clear pending PCI bus master DMA interrupt, for those + // controllers who don't clear it themselves + pci->write_io_8(device, channel->bus_master_base + ATA_BM_STATUS_REG, + (statusBM & 0xf8) | ATA_BM_STATUS_INTERRUPT); + + if (!channel->dmaing) { + // we check this late so that potential spurious interrupts + // are acknoledged above + TRACE_INT("ata_adapter_inthand: no DMA transfer active\n"); + return B_UNHANDLED_INTERRUPT; + } + + // signal interrupt to ATA stack + return sATA->interrupt_handler(channel->ataChannel, statusATA); + } else { + TRACE_INT("ata_adapter_inthand: not BM\n"); + return B_UNHANDLED_INTERRUPT; + } +} + + +static status_t +ata_adapter_prepare_dma(ata_adapter_channel_info *channel, + const physical_entry *sgList, size_t sgListCount, bool writeToDevice) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + uint8 command; + uint8 status; + prd_entry *prd = channel->prdt; + int i; + + TRACE_DMA("ata_adapter: prepare_dma (%s) %lu entrys:\n", + writeToDevice ? "write" : "read", sgListCount); + + for (i = sgListCount - 1, prd = channel->prdt; i >= 0; --i, ++prd, ++sgList) { + prd->address = B_HOST_TO_LENDIAN_INT32((uint32)pci->ram_address(device, + (void*)(addr_t)sgList->address)); + // 0 means 64K - this is done automatically be discarding upper 16 bits + prd->count = B_HOST_TO_LENDIAN_INT16((uint16)sgList->size); + prd->EOT = i == 0; + + TRACE_DMA("ata_adapter: %#" B_PRIxPHYSADDR ", %" B_PRIuPHYSADDR " => " + "%#010" B_PRIx32 ", %" B_PRIu16 ", %d\n", + sgList->address, sgList->size, + prd->address, prd->count, prd->EOT); + SHOW_FLOW( 4, "%#010" B_PRIx32 ", %" B_PRIu16 ", %d", + prd->address, prd->count, prd->EOT); + } + + pci->write_io_32(device, channel->bus_master_base + ATA_BM_PRDT_ADDRESS, + (pci->read_io_32(device, channel->bus_master_base + ATA_BM_PRDT_ADDRESS) & 3) + | (B_HOST_TO_LENDIAN_INT32((uint32)pci->ram_address(device, + (void *)channel->prdt_phys)) & ~3)); + + // reset interrupt and error signal + status = pci->read_io_8(device, channel->bus_master_base + + ATA_BM_STATUS_REG) | ATA_BM_STATUS_INTERRUPT | ATA_BM_STATUS_ERROR; + pci->write_io_8(device, + channel->bus_master_base + ATA_BM_STATUS_REG, status); + + // set data direction + command = pci->read_io_8(device, channel->bus_master_base + + ATA_BM_COMMAND_REG); + if (writeToDevice) + command &= ~ATA_BM_COMMAND_READ_FROM_DEVICE; + else + command |= ATA_BM_COMMAND_READ_FROM_DEVICE; + + pci->write_io_8(device, channel->bus_master_base + ATA_BM_COMMAND_REG, + command); + + return B_OK; +} + + +static status_t +ata_adapter_start_dma(ata_adapter_channel_info *channel) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + uint8 command; + + command = pci->read_io_8(device, channel->bus_master_base + + ATA_BM_COMMAND_REG); + + command |= ATA_BM_COMMAND_START_STOP; + + channel->dmaing = true; + + pci->write_io_8(device, channel->bus_master_base + ATA_BM_COMMAND_REG, + command); + + return B_OK; +} + + +static status_t +ata_adapter_finish_dma(ata_adapter_channel_info *channel) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + uint8 command; + uint8 status; + + // read BM status first + status = pci->read_io_8(device, channel->bus_master_base + + ATA_BM_STATUS_REG); + + // stop DMA engine, this also clears ATA_BM_STATUS_ACTIVE + // in the BM status register + command = pci->read_io_8(device, channel->bus_master_base + + ATA_BM_COMMAND_REG); + pci->write_io_8(device, channel->bus_master_base + ATA_BM_COMMAND_REG, + command & ~ATA_BM_COMMAND_START_STOP); + channel->dmaing = false; + + // reset error flag + pci->write_io_8(device, channel->bus_master_base + ATA_BM_STATUS_REG, + status | ATA_BM_STATUS_ERROR); + + if ((status & ATA_BM_STATUS_ACTIVE) != 0) + return B_DEV_DATA_OVERRUN; + + if ((status & ATA_BM_STATUS_ERROR) != 0) + return B_ERROR; + + return B_OK; +} + + +static status_t +ata_adapter_init_channel(device_node *node, + ata_adapter_channel_info **cookie, size_t total_data_size, + int32 (*inthand)(void *arg)) +{ + ata_adapter_controller_info *controller; + ata_adapter_channel_info *channel; + uint16 command_block_base, control_block_base; + uint8 intnum; + int prdt_size; + physical_entry pe[1]; + uint8 channel_index; + status_t res; + + TRACE("PCI-ATA: init channel...\n"); + +#if 0 + if (1 /* debug */){ + uint8 bus, device, function; + uint16 vendorID, deviceID; + sDeviceManager->get_attr_uint8(node, PCI_DEVICE_BUS_ITEM, &bus, true); + sDeviceManager->get_attr_uint8(node, PCI_DEVICE_DEVICE_ITEM, &device, true); + sDeviceManager->get_attr_uint8(node, PCI_DEVICE_FUNCTION_ITEM, &function, true); + sDeviceManager->get_attr_uint16(node, PCI_DEVICE_VENDOR_ID_ITEM, &vendorID, true); + sDeviceManager->get_attr_uint16(node, PCI_DEVICE_DEVICE_ID_ITEM, &deviceID, true); + TRACE("PCI-ATA: bus %3d, device %2d, function %2d: vendor %04x, device %04x\n", + bus, device, function, vendorID, deviceID); + } +#endif + + // get device data + if (sDeviceManager->get_attr_uint16(node, ATA_ADAPTER_COMMAND_BLOCK_BASE, &command_block_base, false) != B_OK + || sDeviceManager->get_attr_uint16(node, ATA_ADAPTER_CONTROL_BLOCK_BASE, &control_block_base, false) != B_OK + || sDeviceManager->get_attr_uint8(node, ATA_ADAPTER_INTNUM, &intnum, true) != B_OK + || sDeviceManager->get_attr_uint8(node, ATA_ADAPTER_CHANNEL_INDEX, &channel_index, false) != B_OK) + return B_ERROR; + + { + device_node *parent = sDeviceManager->get_parent_node(node); + sDeviceManager->get_driver(parent, NULL, (void **)&controller); + sDeviceManager->put_node(parent); + } + + channel = (ata_adapter_channel_info *)malloc(total_data_size); + if (channel == NULL) { + res = B_NO_MEMORY; + goto err; + } + + TRACE("PCI-ATA: channel index %d\n", channel_index); + + channel->node = node; + channel->pci = controller->pci; + channel->device = controller->device; + channel->lost = false; + channel->command_block_base = command_block_base; + channel->control_block_base = control_block_base; + channel->bus_master_base = controller->bus_master_base + (channel_index * 8); + channel->intnum = intnum; + channel->dmaing = false; + channel->inthand = inthand; + + TRACE("PCI-ATA: bus master base %#x\n", channel->bus_master_base); + + // PRDT must be contiguous, dword-aligned and must not cross 64K boundary +// TODO: Where's the handling for the 64 K boundary? create_area_etc() can be +// used. + prdt_size = (ATA_ADAPTER_MAX_SG_COUNT * sizeof( prd_entry ) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1); + channel->prd_area = create_area("prd", (void **)&channel->prdt, B_ANY_KERNEL_ADDRESS, + prdt_size, B_32_BIT_CONTIGUOUS, 0); + if (channel->prd_area < B_OK) { + res = channel->prd_area; + goto err2; + } + + get_memory_map(channel->prdt, prdt_size, pe, 1); + channel->prdt_phys = pe[0].address; + + SHOW_FLOW(3, "virt=%p, phys=%x", channel->prdt, (int)channel->prdt_phys); + + res = install_io_interrupt_handler(channel->intnum, + inthand, channel, 0); + + if (res < 0) { + SHOW_ERROR(0, "couldn't install irq handler @%d", channel->intnum); + goto err3; + } + + TRACE("PCI-ATA: init channel done\n"); + + // disable interrupts + ata_adapter_write_device_control(channel, ATA_DEVICE_CONTROL_BIT3 | ATA_DEVICE_CONTROL_DISABLE_INTS); + + *cookie = channel; + + return B_OK; + +err3: + delete_area(channel->prd_area); +err2: +err: + free(channel); + + return res; +} + + +static void +ata_adapter_uninit_channel(ata_adapter_channel_info *channel) +{ + // disable IRQs + ata_adapter_write_device_control(channel, ATA_DEVICE_CONTROL_BIT3 | ATA_DEVICE_CONTROL_DISABLE_INTS); + + // catch spurious interrupt + // (some controllers generate an IRQ when you _disable_ interrupts, + // they are delayed by less then 40 µs, so 1 ms is safe) + snooze(1000); + + remove_io_interrupt_handler(channel->intnum, channel->inthand, channel); + + delete_area(channel->prd_area); + free(channel); +} + + +static void +ata_adapter_channel_removed(ata_adapter_channel_info *channel) +{ + SHOW_FLOW0( 3, "" ); + + if (channel != NULL) + // disable access instantly + atomic_or((int32*)&channel->lost, 1); +} + + +/** publish node of ata channel */ + +static status_t +ata_adapter_publish_channel(device_node *controller_node, + const char *channel_module_name, uint16 command_block_base, + uint16 control_block_base, uint8 intnum, bool can_dma, + uint8 channel_index, const char *name, const io_resource *resources, + device_node **node) +{ + char prettyName[25]; + sprintf(prettyName, "ATA Channel %" B_PRIu8, channel_index); + + device_attr attrs[] = { + // info about ourself and our consumer + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + { string: prettyName }}, + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, + { string: ATA_FOR_CONTROLLER_MODULE_NAME }}, + + // private data to identify channel + { ATA_ADAPTER_COMMAND_BLOCK_BASE, B_UINT16_TYPE, + { ui16: command_block_base }}, + { ATA_ADAPTER_CONTROL_BLOCK_BASE, B_UINT16_TYPE, + { ui16: control_block_base }}, + { ATA_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: can_dma }}, + { ATA_ADAPTER_INTNUM, B_UINT8_TYPE, { ui8: intnum }}, + { ATA_ADAPTER_CHANNEL_INDEX, B_UINT8_TYPE, { ui8: channel_index }}, + { NULL } + }; + + SHOW_FLOW0(2, ""); + + return sDeviceManager->register_node(controller_node, channel_module_name, attrs, + resources, node); +} + + +/** detect IDE channel */ + +static status_t +ata_adapter_detect_channel(pci_device_module_info *pci, pci_device *pci_device, + device_node *controller_node, const char *channel_module_name, + bool controller_can_dma, uint16 command_block_base, uint16 control_block_base, + uint16 bus_master_base, uint8 intnum, uint8 channel_index, const char *name, + device_node **node, bool supports_compatibility_mode) +{ + uint8 api; + uint16 pcicmdOld; + uint16 pcicmdNew; + uint16 pciVendor; + + SHOW_FLOW0( 3, "" ); + + // if channel works in compatibility mode, addresses and interrupt are fixed + api = pci->read_pci_config(pci_device, PCI_class_api, 1); + + if (supports_compatibility_mode + && channel_index == 0 && (api & ATA_API_PRIMARY_NATIVE) == 0) { + command_block_base = 0x1f0; + control_block_base = 0x3f6; + intnum = 14; + TRACE("PCI-ATA: Controller in legacy mode: cmd %#x, ctrl %#x, irq %d\n", + command_block_base, control_block_base, intnum); + } else if (supports_compatibility_mode + && channel_index == 1 && (api & ATA_API_PRIMARY_NATIVE) == 0) { + command_block_base = 0x170; + control_block_base = 0x376; + intnum = 15; + TRACE("PCI-ATA: Controller in legacy mode: cmd %#x, ctrl %#x, irq %d\n", + command_block_base, control_block_base, intnum); + } else { + if (command_block_base == 0 || control_block_base == 0) { + TRACE("PCI-ATA: Command/Control Block base is not configured\n"); + return B_ERROR; + } + if (intnum == 0 || intnum == 0xff) { + TRACE("PCI-ATA: Interrupt is not configured\n"); + return B_ERROR; + } + + // historically, they start at 3f6h/376h, but PCI spec requires registers + // to be aligned at 4 bytes, so only 3f4h/374h can be specified; thus + // PCI IDE defines that control block starts at offset 2 + control_block_base += 2; + TRACE("PCI-ATA: Controller in native mode: cmd %#x, ctrl %#x, irq %d\n", + command_block_base, control_block_base, intnum); + } + + + // this should be done in ata_adapter_init_controller but there is crashes + pcicmdOld = pcicmdNew = pci->read_pci_config(pci_device, PCI_command, 2); + if ((pcicmdNew & (1 << 10)) != 0) { + TRACE("PCI-ATA: enabling interrupts\n"); + pcicmdNew &= ~(1 << 10); + } + if ((pcicmdNew & PCI_command_io) == 0) { + TRACE("PCI-ATA: enabling io decoder\n"); + pcicmdNew |= PCI_command_io; + } + if ((pcicmdNew & PCI_command_master) == 0) { + TRACE("PCI-ATA: enabling bus mastering\n"); + pcicmdNew |= PCI_command_master; + } + if (pcicmdOld != pcicmdNew) { + pci->write_pci_config(pci_device, PCI_command, 2, pcicmdNew); + TRACE("PCI-ATA: pcicmd changed from 0x%04x to 0x%04x\n", + pcicmdOld, pcicmdNew); + } + + + if (supports_compatibility_mode) { + // read status of primary(!) channel to detect simplex + uint8 status = pci->read_io_8(pci_device, bus_master_base + + ATA_BM_STATUS_REG); + + if (status & ATA_BM_STATUS_SIMPLEX_DMA && channel_index != 0) { + // in simplex mode, channels cannot operate independantly of each other; + // we simply disable bus mastering of second channel to satisfy that; + // better were to use a controller lock, but this had to be done in the IDE + // bus manager, and I don't see any reason to add extra code for old + // simplex controllers + + // Intel controllers use this bit for something else and are not simplex. + pciVendor = pci->read_pci_config(pci_device, PCI_vendor_id, 2); + + if (pciVendor != 0x8086) { + TRACE("PCI-ATA: Simplex controller - disabling DMA of secondary channel\n"); + controller_can_dma = false; + } else { + TRACE("PCI-ATA: Simplex bit ignored - Intel controller\n"); + } + } + } + + { + io_resource resources[3] = { + { B_IO_PORT, command_block_base, 8 }, + { B_IO_PORT, control_block_base, 1 }, + {} + }; + + return ata_adapter_publish_channel(controller_node, channel_module_name, + command_block_base, control_block_base, intnum, controller_can_dma, + channel_index, name, resources, node); + } +} + + +static status_t +ata_adapter_init_controller(device_node *node, + ata_adapter_controller_info **cookie, size_t total_data_size) +{ + pci_device_module_info *pci; + pci_device *device; + ata_adapter_controller_info *controller; + uint16 bus_master_base; + + // get device data + if (sDeviceManager->get_attr_uint16(node, ATA_ADAPTER_BUS_MASTER_BASE, &bus_master_base, false) != B_OK) + return B_ERROR; + + { + device_node *parent = sDeviceManager->get_parent_node(node); + sDeviceManager->get_driver(parent, (driver_module_info **)&pci, (void **)&device); + sDeviceManager->put_node(parent); + } + + controller = (ata_adapter_controller_info *)malloc(total_data_size); + if (controller == NULL) + return B_NO_MEMORY; + +#if 0 + pcicmdOld = pcicmdNew = pci->read_pci_config(node, PCI_command, 2); + if ((pcicmdNew & PCI_command_io) == 0) { + TRACE("PCI-ATA: adapter init: enabling io decoder\n"); + pcicmdNew |= PCI_command_io; + } + if ((pcicmdNew & PCI_command_master) == 0) { + TRACE("PCI-ATA: adapter init: enabling bus mastering\n"); + pcicmdNew |= PCI_command_master; + } + if (pcicmdOld != pcicmdNew) { + pci->write_pci_config(node, PCI_command, 2, pcicmdNew); + TRACE("PCI-ATA: adapter init: pcicmd old 0x%04x, new 0x%04x\n", + pcicmdOld, pcicmdNew); + } +#endif + + controller->node = node; + controller->pci = pci; + controller->device = device; + controller->lost = false; + controller->bus_master_base = bus_master_base; + + *cookie = controller; + + return B_OK; +} + + +static void +ata_adapter_uninit_controller(ata_adapter_controller_info *controller) +{ + free(controller); +} + + +static void +ata_adapter_controller_removed(ata_adapter_controller_info *controller) +{ + SHOW_FLOW0(3, ""); + + if (controller != NULL) { + // disable access instantly; unit_device takes care of unregistering ioports + atomic_or((int32*)&controller->lost, 1); + } +} + + +/** publish node of ata controller */ + +static status_t +ata_adapter_publish_controller(device_node *parent, uint16 bus_master_base, + io_resource *resources, const char *controller_driver, + const char *controller_driver_type, const char *controller_name, bool can_dma, + bool can_cq, uint32 dma_alignment, uint32 dma_boundary, uint32 max_sg_block_size, + device_node **node) +{ + device_attr attrs[] = { + // properties of this controller for ata bus manager + // there are always max. 2 devices + // (unless this is a Compact Flash Card with a built-in IDE controller, + // which has exactly 1 device) + { ATA_CONTROLLER_MAX_DEVICES_ITEM, B_UINT8_TYPE, { ui8: 2 }}, + // of course we can DMA + { ATA_CONTROLLER_CAN_DMA_ITEM, B_UINT8_TYPE, { ui8: can_dma }}, + // choose any name here + { ATA_CONTROLLER_CONTROLLER_NAME_ITEM, B_STRING_TYPE, + { string: controller_name }}, + + // DMA properties + // data must be word-aligned; + // warning: some controllers are more picky! + { B_DMA_ALIGNMENT, B_UINT32_TYPE, { ui32: dma_alignment /*1*/}}, + // one S/G block must not cross 64K boundary + { B_DMA_BOUNDARY, B_UINT32_TYPE, { ui32: dma_boundary/*0xffff*/ }}, + // max size of S/G block is 16 bits with zero being 64K + { B_DMA_MAX_SEGMENT_BLOCKS, B_UINT32_TYPE, + { ui32: max_sg_block_size/*0x10000*/ }}, + { B_DMA_MAX_SEGMENT_COUNT, B_UINT32_TYPE, + { ui32: ATA_ADAPTER_MAX_SG_COUNT }}, + { B_DMA_HIGH_ADDRESS, B_UINT64_TYPE, { ui64: 0x100000000LL }}, + + // private data to find controller + { ATA_ADAPTER_BUS_MASTER_BASE, B_UINT16_TYPE, { ui16: bus_master_base }}, + { NULL } + }; + + SHOW_FLOW0( 2, "" ); + + return sDeviceManager->register_node(parent, controller_driver, attrs, resources, node); +} + + +/** detect pure IDE controller, i.e. without channels */ + +static status_t +ata_adapter_detect_controller(pci_device_module_info *pci, pci_device *pci_device, + device_node *parent, uint16 bus_master_base, const char *controller_driver, + const char *controller_driver_type, const char *controller_name, bool can_dma, + bool can_cq, uint32 dma_alignment, uint32 dma_boundary, uint32 max_sg_block_size, + device_node **node) +{ + io_resource resources[2] = { + { B_IO_PORT, bus_master_base, 16 }, + {} + }; + + SHOW_FLOW0( 3, "" ); + + if (bus_master_base == 0) { + TRACE("PCI-ATA: Controller detection failed! bus master base not configured\n"); + return B_ERROR; + } + + return ata_adapter_publish_controller(parent, bus_master_base, resources, + controller_driver, controller_driver_type, controller_name, can_dma, can_cq, + dma_alignment, dma_boundary, max_sg_block_size, node); +} + + +static status_t +ata_adapter_probe_controller(device_node *parent, const char *controller_driver, + const char *controller_driver_type, const char *controller_name, + const char *channel_module_name, bool can_dma, bool can_cq, uint32 dma_alignment, + uint32 dma_boundary, uint32 max_sg_block_size, bool supports_compatibility_mode) +{ + pci_device_module_info *pci; + pci_device *device; + uint16 command_block_base[2]; + uint16 control_block_base[2]; + uint16 bus_master_base; + device_node *controller_node; + device_node *channels[2]; + uint8 intnum; + status_t res; + + SHOW_FLOW0( 3, "" ); + + sDeviceManager->get_driver(parent, (driver_module_info **)&pci, (void **)&device); + + command_block_base[0] = pci->read_pci_config(device, PCI_base_registers, 4 ); + control_block_base[0] = pci->read_pci_config(device, PCI_base_registers + 4, 4); + command_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 8, 4); + control_block_base[1] = pci->read_pci_config(device, PCI_base_registers + 12, 4); + bus_master_base = pci->read_pci_config(device, PCI_base_registers + 16, 4); + intnum = pci->read_pci_config(device, PCI_interrupt_line, 1); + + command_block_base[0] &= PCI_address_io_mask; + control_block_base[0] &= PCI_address_io_mask; + command_block_base[1] &= PCI_address_io_mask; + control_block_base[1] &= PCI_address_io_mask; + bus_master_base &= PCI_address_io_mask; + + res = ata_adapter_detect_controller(pci, device, parent, bus_master_base, + controller_driver, controller_driver_type, controller_name, can_dma, + can_cq, dma_alignment, dma_boundary, max_sg_block_size, &controller_node); + // don't register if controller is already registered! + // (happens during rescan; registering new channels would kick out old channels) + if (res != B_OK || controller_node == NULL) + return res; + + // ignore errors during registration of channels - could be a simple rescan collision + ata_adapter_detect_channel(pci, device, controller_node, channel_module_name, + can_dma, command_block_base[0], control_block_base[0], bus_master_base, + intnum, 0, "Primary Channel", &channels[0], supports_compatibility_mode); + + ata_adapter_detect_channel(pci, device, controller_node, channel_module_name, + can_dma, command_block_base[1], control_block_base[1], bus_master_base, + intnum, 1, "Secondary Channel", &channels[1], supports_compatibility_mode); + + return B_OK; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + return B_ERROR; + } +} + + +module_dependency module_dependencies[] = { + { ATA_FOR_CONTROLLER_MODULE_NAME, (module_info **)&sATA }, + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, + {} +}; + + +static ata_adapter_interface adapter_interface = { + { + ATA_ADAPTER_MODULE_NAME, + 0, + std_ops + }, + + set_channel, + + ata_adapter_write_command_block_regs, + ata_adapter_read_command_block_regs, + + ata_adapter_get_altstatus, + ata_adapter_write_device_control, + + ata_adapter_write_pio, + ata_adapter_read_pio, + + ata_adapter_prepare_dma, + ata_adapter_start_dma, + ata_adapter_finish_dma, + + ata_adapter_inthand, + + ata_adapter_init_channel, + ata_adapter_uninit_channel, + ata_adapter_channel_removed, + + ata_adapter_publish_channel, + ata_adapter_detect_channel, + + ata_adapter_init_controller, + ata_adapter_uninit_controller, + ata_adapter_controller_removed, + + ata_adapter_publish_controller, + ata_adapter_detect_controller, + + ata_adapter_probe_controller +}; + +module_info *modules[] = { + &adapter_interface.info, + NULL +}; Modified: haiku/trunk/src/add-ons/kernel/generic/ide_adapter/Jamfile =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/ide_adapter/Jamfile 2011-09-08 03:15:37 UTC (rev 42724) +++ haiku/trunk/src/add-ons/kernel/generic/ide_adapter/Jamfile 2011-09-08 15:47:25 UTC (rev 42725) @@ -3,6 +3,6 @@ UsePrivateHeaders drivers kernel ; KernelAddon ide_adapter : - ide_adapter.c + ide_adapter.cpp ; Copied: haiku/trunk/src/add-ons/kernel/generic/ide_adapter/ide_adapter.cpp (from rev 42724, haiku/trunk/src/add-ons/kernel/generic/ide_adapter/ide_adapter.c) =================================================================== --- haiku/trunk/src/add-ons/kernel/generic/ide_adapter/ide_adapter.cpp (rev 0) +++ haiku/trunk/src/add-ons/kernel/generic/ide_adapter/ide_adapter.cpp 2011-09-08 15:47:25 UTC (rev 42725) @@ -0,0 +1,845 @@ +/* + * Copyright 2002-04, Thomas Kurschel. All rights reserved. + * Distributed under the terms of the MIT License. + */ + +/* + Generic IDE adapter library. + + The correct name would be ATA adapter, but I chose the old name as it's + more widely known. +*/ + +#include <KernelExport.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> + +#include <device_manager.h> +#include <bus/PCI.h> +#include <bus/IDE.h> +#include <ide_types.h> +#include <ide_adapter.h> +#include <lendian_bitfield.h> + +#define debug_level_flow 0 +#define debug_level_error 3 +#define debug_level_info 3 + +#define DEBUG_MSG_PREFIX "IDE PCI -- " + +#include "wrapper.h" + +#define TRACE dprintf + + +static ide_for_controller_interface *ide; +static device_manager_info *pnp; + + +static void +set_channel(ide_adapter_channel_info *channel, ide_channel ideChannel) +{ + channel->ideChannel = ideChannel; +} + + +static status_t +ide_adapter_write_command_block_regs(ide_adapter_channel_info *channel, + ide_task_file *tf, ide_reg_mask mask) +{ + pci_device_module_info *pci = channel->pci; + pci_device *device = channel->device; + int i; + + uint16 ioaddr = channel->command_block_base; + + if (channel->lost) + return B_ERROR; + + for (i = 0; i < 7; i++) { + // LBA48 registers must be written twice + if (((1 << (i + 7)) & mask) != 0) { + SHOW_FLOW( 4, "%x->HI(%x)", tf->raw.r[i + 7], i ); + pci->write_io_8(device, ioaddr + 1 + i, tf->raw.r[i + 7]); + } + + if (((1 << i) & mask) != 0) { + SHOW_FLOW( 4, "%x->LO(%x)", tf->raw.r[i], i ); + pci->write_io_8(device, ioaddr + 1 + i, tf->raw.r[i]); [... truncated: 777 lines follow ...]