⛏️ index : haiku.git

/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the MIT License.
*/

/*
	Part of Open SCSI bus manager

	Handling of bus/device blocking. Inline functions defined
	here don't wake service thread if required and don't lock bus, so 
	everyone using them must take care of that himself.
*/

#ifndef __BLOCKING_H__
#define __BLOCKING_H__

#include "dl_list.h"

void scsi_add_queued_request( scsi_ccb *request );
void scsi_add_queued_request_first( scsi_ccb *request );
void scsi_remove_queued_request( scsi_ccb *request );


static inline void scsi_add_req_queue_first( scsi_ccb *request )
{
	scsi_device_info *device = request->device;

	SHOW_FLOW( 3, "request=%p", request );

	ADD_CDL_LIST_HEAD( request, scsi_ccb, device->queued_reqs, );
}

static inline void scsi_add_req_queue_last( scsi_ccb *request )
{
	scsi_device_info *device = request->device;

	SHOW_FLOW( 3, "request=%p", request );

	ADD_CDL_LIST_TAIL( request, scsi_ccb, device->queued_reqs, );
}

static inline void scsi_remove_req_queue( scsi_ccb *request )
{
	scsi_device_info *device = request->device;
	
	SHOW_FLOW( 3, "request=%p", request );
	
	REMOVE_CDL_LIST( request, device->queued_reqs, );
}

// ignored, if device is already queued
static inline void scsi_add_device_queue_first( scsi_device_info *device )
{
	SHOW_FLOW0( 3, "" );

	if( DEVICE_IN_WAIT_QUEUE( device ))
		return;

	SHOW_FLOW0( 3, "was not in wait queue - adding" );
		
	ADD_CDL_LIST_HEAD( device, scsi_device_info, device->bus->waiting_devices, waiting_ );	
}

// ignored, if device is already queued
static inline void scsi_add_device_queue_last( scsi_device_info *device )
{
	SHOW_FLOW0( 3, "" );
	
	if( DEVICE_IN_WAIT_QUEUE( device ))
		return;
		
	SHOW_FLOW0( 3, "was not in wait queue - adding" );

	ADD_CDL_LIST_TAIL( device, scsi_device_info, device->bus->waiting_devices, waiting_ );
}

// ignored, if device is not in queue
static inline void scsi_remove_device_queue( scsi_device_info *device )
{
	SHOW_FLOW0( 3, "" );
	
	if( !DEVICE_IN_WAIT_QUEUE( device ))
		return;
		
	SHOW_FLOW0( 3, "was in wait queue - removing from it" );

	REMOVE_CDL_LIST( device, device->bus->waiting_devices, waiting_ );
	
	// reset next link so we can see that it's not in device queue
	device->waiting_next = NULL;
}

// set overflow bit of device; this will not remove device from bus queue!
// (multiple calls are ignored gracefully)
static inline void scsi_set_device_overflow( scsi_device_info *device )
{
	device->lock_count += device->sim_overflow ^ 1;
	device->sim_overflow = 1;
}

// set overflow bit of bus
// (multiple calls are ignored gracefully)
static inline void scsi_set_bus_overflow( scsi_bus_info *bus )
{
	bus->lock_count += bus->sim_overflow ^ 1;
	bus->sim_overflow = 1;
}

// clear overflow bit of device; this will not add device to bus queue!
// (multiple calls are ignored gracefully)
static inline void scsi_clear_device_overflow( scsi_device_info *device )
{
	device->lock_count -= device->sim_overflow;
	device->sim_overflow = 0;
}

// clear overflow bit of bus
// (multiple calls are ignored gracefully)
static inline void scsi_clear_bus_overflow( scsi_bus_info *bus )
{
	bus->lock_count -= bus->sim_overflow;
	bus->sim_overflow = 0;
}

// check whether bus has some pending requests it can process now
static inline bool scsi_can_service_bus( scsi_bus_info *bus )
{
	// bus must not be blocked and requests pending
	return (bus->lock_count == 0) & (bus->waiting_devices != NULL);
}

// unblock bus
// lock must be hold; service thread is not informed
static inline void scsi_unblock_bus_noresume( scsi_bus_info *bus, bool by_SIM )
{
	if( bus->blocked[by_SIM] > 0 ) {
		--bus->blocked[by_SIM];
		--bus->lock_count;
	} else {
		panic( "Tried to unblock bus %d which wasn't blocked", 
			bus->path_id );
	}
}


// unblock device
// lock must be hold; device is not added to queue and service thread is not informed
static inline void scsi_unblock_device_noresume( scsi_device_info *device, bool by_SIM )
{
	if( device->blocked[by_SIM] > 0 ) {
		--device->blocked[by_SIM];
		--device->lock_count;
	} else {
		panic( "Tried to unblock device %d/%d/%d which wasn't blocked", 
			device->bus->path_id, device->target_id, device->target_lun );
	}
}


// block bus
// lock must be hold
static inline void scsi_block_bus_nolock( scsi_bus_info *bus, bool by_SIM )
{
	++bus->blocked[by_SIM];
	++bus->lock_count;
}


// block device
// lock must be hold
static inline void scsi_block_device_nolock( scsi_device_info *device, bool by_SIM )
{
	++device->blocked[by_SIM];
	++device->lock_count;

	// remove device from bus queue as it cannot be processed anymore
	scsi_remove_device_queue( device );
}


void scsi_block_bus( scsi_bus_info *bus );
void scsi_unblock_bus( scsi_bus_info *bus );
void scsi_block_device( scsi_device_info *device );
void scsi_unblock_device( scsi_device_info *device );

void scsi_cont_send_bus( scsi_bus_info *bus );
void scsi_cont_send_device( scsi_device_info *device );

#endif