root/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp
/*
 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
 * Copyright 2008, Marcus Overhagen.
 * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
 * Copyright 2002-2003, Thomas Kurschel.
 *
 * Distributed under the terms of the MIT License.
 */

#include "ATAPrivate.h"


scsi_for_sim_interface *gSCSIModule = NULL;
device_manager_info *gDeviceManager = NULL;


static status_t
ata_sim_init_bus(device_node *node, void **cookie)
{
        ATAChannel *channel = new(std::nothrow) ATAChannel(node);
        if (channel == NULL)
                return B_NO_MEMORY;

        status_t result = channel->InitCheck();
        if (result != B_OK) {
                TRACE_ERROR("failed to set up ata channel object\n");
                delete channel;
                return result;
        }

        *cookie = channel;
        return B_OK;
}


static void
ata_sim_uninit_bus(void *cookie)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        delete channel;
}


static void
ata_sim_bus_removed(void *cookie)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() != NULL) {
                gSCSIModule->block_bus(channel->Bus());
                channel->SetBus(NULL);
        }
}


static void
ata_sim_set_scsi_bus(scsi_sim_cookie cookie, scsi_bus bus)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        channel->SetBus(bus);
        channel->ScanBus();
}


static void
ata_sim_scsi_io(scsi_sim_cookie cookie, scsi_ccb *ccb)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() == NULL) {
                ccb->subsys_status = SCSI_NO_HBA;
                gSCSIModule->finished(ccb, 1);
                return;
        }

        if (channel->ExecuteIO(ccb) == B_BUSY)
                gSCSIModule->requeue(ccb, true);
}


static uchar
ata_sim_abort(scsi_sim_cookie cookie, scsi_ccb *ccb)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() == NULL)
                return SCSI_NO_HBA;

        // aborting individual commands is not possible
        return SCSI_REQ_CMP;
}


static uchar
ata_sim_reset_device(scsi_sim_cookie cookie, uchar targetId, uchar targetLun)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() == NULL)
                return SCSI_NO_HBA;

        // TODO: implement
        return SCSI_REQ_INVALID;
}


static uchar
ata_sim_term_io(scsi_sim_cookie cookie, scsi_ccb *ccb)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() == NULL)
                return SCSI_NO_HBA;

        // we don't terminate commands, ignore
        return SCSI_REQ_CMP;
}


static uchar
ata_sim_path_inquiry(scsi_sim_cookie cookie, scsi_path_inquiry *info)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() == NULL)
                return SCSI_NO_HBA;

        channel->PathInquiry(info);
        return SCSI_REQ_CMP;
}


static uchar
ata_sim_rescan_bus(scsi_sim_cookie cookie)
{
        // TODO: implement
        return SCSI_REQ_CMP;
}


static uchar
ata_sim_reset_bus(scsi_sim_cookie cookie)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        if (channel->Bus() == NULL)
                return SCSI_NO_HBA;

        //channel->Reset();
        panic("asking for trouble");
        return SCSI_REQ_CMP;
}


static void
ata_sim_get_restrictions(scsi_sim_cookie cookie, uchar targetID,
        bool *isATAPI, bool *noAutoSense, uint32 *maxBlocks)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        channel->GetRestrictions(targetID, isATAPI, noAutoSense, maxBlocks);
}


static status_t
ata_sim_control(scsi_sim_cookie cookie, uchar targetID, uint32 op, void *buffer,
        size_t length)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        return channel->Control(targetID, op, buffer, length);
}


status_t
ata_channel_added(device_node *parent)
{
        const char *controllerName;
        if (gDeviceManager->get_attr_string(parent,
                ATA_CONTROLLER_CONTROLLER_NAME_ITEM, &controllerName, true) != B_OK) {
                TRACE_ERROR("controller name missing\n");
                return B_ERROR;
        }

        int32 channelID = gDeviceManager->create_id(ATA_CHANNEL_ID_GENERATOR);
        if (channelID < 0) {
                TRACE_ERROR("out of channel ids\n");
                return B_ERROR;
        }

        device_attr attributes[] = {
                {
                        B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
                                { .string = SCSI_FOR_SIM_MODULE_NAME }
                },

                {
                        SCSI_DESCRIPTION_CONTROLLER_NAME, B_STRING_TYPE,
                                { .string = controllerName }
                },

                // maximum number of blocks per transmission:
                // - ATAPI uses packets, i.e. normal SCSI limits apply
                //   but I'm not sure about controller restrictions
                // - ATA allows up to 256 blocks for LBA28 and 65535 for LBA48
                // to fix specific drive bugs use ATAChannel::GetRestrictions()
                { B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { .ui32 = 0xffff } },
                { ATA_CHANNEL_ID_ITEM, B_UINT32_TYPE, { .ui32 = (uint32)channelID } },
                { NULL }
        };

        return gDeviceManager->register_node(parent, ATA_SIM_MODULE_NAME,
                attributes, NULL, NULL);
}


status_t
ata_interrupt_handler(void *cookie, uint8 status)
{
        ATAChannel *channel = (ATAChannel *)cookie;
        return channel->Interrupt(status);
}


static status_t
std_ops(int32 op, ...)
{
        switch (op) {
                case B_MODULE_INIT:
                case B_MODULE_UNINIT:
                        return B_OK;

                default:
                        break;
        }

        return B_ERROR;
}


scsi_sim_interface ata_sim_module = {
        {
                {
                        ATA_SIM_MODULE_NAME,
                        0,
                        std_ops
                },

                NULL, // supported devices
                NULL, // register node
                ata_sim_init_bus,
                ata_sim_uninit_bus,
                NULL, // register child devices
                NULL, // rescan
                ata_sim_bus_removed,
                NULL, // suspend
                NULL, // resume
        },

        ata_sim_set_scsi_bus,
        ata_sim_scsi_io,
        ata_sim_abort,
        ata_sim_reset_device,
        ata_sim_term_io,
        ata_sim_path_inquiry,
        ata_sim_rescan_bus,
        ata_sim_reset_bus,
        ata_sim_get_restrictions,
        ata_sim_control
};

ata_for_controller_interface ata_for_controller_module = {
        {
                {
                        ATA_FOR_CONTROLLER_MODULE_NAME,
                        0,
                        &std_ops
                },

                NULL, // supported devices
                ata_channel_added,
                NULL,
                NULL,
                NULL
        },

        ata_interrupt_handler
};


module_dependency module_dependencies[] = {
        { SCSI_FOR_SIM_MODULE_NAME, (module_info **)&gSCSIModule },
        { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
        {}
};

module_info *modules[] = {
        (module_info *)&ata_for_controller_module,
        (module_info *)&ata_sim_module,
        NULL
};