root/src/add-ons/kernel/bus_managers/scsi/dpc.cpp
/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the MIT License.
*/

/*
        Part of Open SCSI bus manager

        DPC handling (deferred procedure calls).

        DPC are executed by the service thread of the bus
        (see busses.c).
*/

#include "scsi_internal.h"

#include <string.h>
#include <stdlib.h>


status_t
scsi_alloc_dpc(scsi_dpc_info **dpc)
{
        SHOW_FLOW0(3, "");

        *dpc = (scsi_dpc_info *)malloc(sizeof(scsi_dpc_info));
        if (*dpc == NULL)
                return B_NO_MEMORY;

        memset(*dpc, 0, sizeof(scsi_dpc_info));
        return B_OK;
}


status_t
scsi_free_dpc(scsi_dpc_info *dpc)
{
        SHOW_FLOW0(3, "");

        free(dpc);
        return B_OK;
}


status_t
scsi_schedule_dpc(scsi_bus_info *bus, scsi_dpc_info *dpc, /*int flags,*/
        void (*func)(void *arg), void *arg)
{
        SHOW_FLOW(3, "bus=%p, dpc=%p", bus, dpc);
        acquire_spinlock_irq(&bus->dpc_lock);

        dpc->func = func;
        dpc->arg = arg;

        if (!dpc->registered) {
                dpc->registered = true;
                dpc->next = bus->dpc_list;
                bus->dpc_list = dpc;
        } else
                SHOW_FLOW0(3, "already registered - ignored");

        release_spinlock_irq(&bus->dpc_lock);

        // this is called in IRQ context, so scheduler is not allowed
        release_sem_etc(bus->start_service, 1, B_DO_NOT_RESCHEDULE);
        return B_OK;
}


/** execute pending DPCs */

bool
scsi_check_exec_dpc(scsi_bus_info *bus)
{
        SHOW_FLOW(3, "bus=%p, dpc_list=%p", bus, bus->dpc_list);
        acquire_spinlock_irq(&bus->dpc_lock);

        if (bus->dpc_list) {
                scsi_dpc_info *dpc;
                void (*dpc_func)(void *);
                void *dpc_arg;

                dpc = bus->dpc_list;
                bus->dpc_list = dpc->next;

                dpc_func = dpc->func;
                dpc_arg = dpc->arg;
                dpc->registered = false;

                release_spinlock_irq(&bus->dpc_lock);

                dpc_func(dpc_arg);
                return true;
        } 

        release_spinlock_irq(&bus->dpc_lock);
        return false;
}