mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
1125 lines
36 KiB
C
1125 lines
36 KiB
C
/* $Id: scsi-bus.c,v 1.8 2007/02/15 01:30:43 fredette Exp $ */
|
|
|
|
/* scsi/scsi-bus.c - a generic SCSI bus element: */
|
|
|
|
/*
|
|
* Copyright (c) 2003 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: scsi-bus.c,v 1.8 2007/02/15 01:30:43 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include <tme/generic/scsi.h>
|
|
#include <tme/threads.h>
|
|
#ifdef HAVE_STDARG_H
|
|
#include <stdarg.h>
|
|
#else /* HAVE_STDARG_H */
|
|
#include <varargs.h>
|
|
#endif /* HAVE_STDARG_H */
|
|
|
|
/* macros: */
|
|
|
|
/* the callout flags: */
|
|
#define TME_SCSI_BUS_CALLOUT_CHECK (0)
|
|
#define TME_SCSI_BUS_CALLOUT_RUNNING TME_BIT(0)
|
|
#define TME_SCSI_BUS_CALLOUTS_MASK (-2)
|
|
#define TME_SCSI_BUS_CALLOUT_CYCLE TME_BIT(1)
|
|
|
|
/* structures: */
|
|
|
|
/* a scsi bus: */
|
|
struct tme_scsi_bus {
|
|
|
|
/* backpointer to our element: */
|
|
struct tme_element *tme_scsi_bus_element;
|
|
|
|
/* our mutex: */
|
|
tme_mutex_t tme_scsi_bus_mutex;
|
|
|
|
/* our connections: */
|
|
struct tme_connection *tme_scsi_bus_connections;
|
|
|
|
/* the callout flags: */
|
|
int tme_scsi_bus_callout_flags;
|
|
|
|
/* the current bus state: */
|
|
tme_scsi_control_t tme_scsi_bus_control;
|
|
tme_scsi_control_t tme_scsi_bus_data;
|
|
};
|
|
|
|
/* internal information about a SCSI connection: */
|
|
struct tme_scsi_connection_int {
|
|
|
|
/* the external SCSI connection: */
|
|
struct tme_scsi_connection tme_scsi_connection_int;
|
|
|
|
/* the cycle marker for this connection: */
|
|
tme_uint32_t tme_scsi_connection_int_cycle_marker;
|
|
|
|
/* the control and data lines currently asserted by this connection: */
|
|
tme_scsi_control_t tme_scsi_connection_int_control;
|
|
tme_scsi_data_t tme_scsi_connection_int_data;
|
|
|
|
/* the SCSI bus state last reported to the connection: */
|
|
tme_scsi_control_t tme_scsi_connection_int_last_control;
|
|
tme_scsi_control_t tme_scsi_connection_int_last_data;
|
|
|
|
/* any events that this connection is waiting on, and any that
|
|
triggered: */
|
|
tme_uint32_t tme_scsi_connection_int_events_waiting;
|
|
tme_uint32_t tme_scsi_connection_int_events_triggered;
|
|
|
|
/* any actions that this connection is waiting to take, and any that
|
|
were taken: */
|
|
tme_uint32_t tme_scsi_connection_int_actions_waiting;
|
|
tme_uint32_t tme_scsi_connection_int_actions_taken;
|
|
|
|
/* any step within the current action sequence: */
|
|
unsigned int tme_scsi_connection_int_sequence_step;
|
|
|
|
/* any DMA structure for this connection: */
|
|
struct tme_scsi_dma tme_scsi_connection_int_dma_buffer;
|
|
struct tme_scsi_dma *tme_scsi_connection_int_dma;
|
|
|
|
/* specific callout flags for this connection: */
|
|
int tme_scsi_connection_int_callout_flags;
|
|
};
|
|
|
|
/* the SCSI bus callout function. it must be called with the mutex locked: */
|
|
static void
|
|
_tme_scsi_bus_callout(struct tme_scsi_bus *scsi_bus, int new_callouts)
|
|
{
|
|
struct tme_scsi_connection_int *conn_int;
|
|
struct tme_scsi_connection *conn_scsi;
|
|
int callouts, later_callouts;
|
|
tme_scsi_control_t control;
|
|
tme_scsi_data_t data;
|
|
tme_uint32_t events_triggered;
|
|
tme_uint32_t actions_taken;
|
|
struct tme_scsi_dma dma_buffer;
|
|
const struct tme_scsi_dma *dma;
|
|
int rc;
|
|
|
|
/* add in any new callouts: */
|
|
scsi_bus->tme_scsi_bus_callout_flags |= new_callouts;
|
|
|
|
/* if this function is already running in another thread, simply
|
|
return now. the other thread will do our work: */
|
|
if (scsi_bus->tme_scsi_bus_callout_flags
|
|
& TME_SCSI_BUS_CALLOUT_RUNNING) {
|
|
return;
|
|
}
|
|
|
|
/* callouts are now running: */
|
|
scsi_bus->tme_scsi_bus_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_RUNNING;
|
|
|
|
/* assume that we won't need any later callouts: */
|
|
later_callouts = 0;
|
|
|
|
/* loop while callouts are needed: */
|
|
for (; ((callouts
|
|
= scsi_bus->tme_scsi_bus_callout_flags)
|
|
& TME_SCSI_BUS_CALLOUTS_MASK); ) {
|
|
|
|
/* clear the needed callouts: */
|
|
scsi_bus->tme_scsi_bus_callout_flags
|
|
= (callouts
|
|
& ~TME_SCSI_BUS_CALLOUTS_MASK);
|
|
callouts &= TME_SCSI_BUS_CALLOUTS_MASK;
|
|
|
|
/* if we need to call out SCSI bus cycles: */
|
|
if (callouts & TME_SCSI_BUS_CALLOUT_CYCLE) {
|
|
|
|
/* loop over all devices on the bus: */
|
|
for (conn_int
|
|
= ((struct tme_scsi_connection_int *)
|
|
scsi_bus->tme_scsi_bus_connections);
|
|
conn_int != NULL;
|
|
conn_int
|
|
= ((struct tme_scsi_connection_int *)
|
|
conn_int->tme_scsi_connection_int.tme_scsi_connection.tme_connection_next)) {
|
|
|
|
/* if this device doesn't need a callout, continue: */
|
|
if (!(conn_int->tme_scsi_connection_int_callout_flags
|
|
& TME_SCSI_BUS_CALLOUT_CYCLE)) {
|
|
continue;
|
|
}
|
|
|
|
/* clear the callout flag on this device: */
|
|
conn_int->tme_scsi_connection_int_callout_flags
|
|
&= ~TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
|
|
/* get the current state of the bus: */
|
|
control = scsi_bus->tme_scsi_bus_control;
|
|
data = scsi_bus->tme_scsi_bus_data;
|
|
|
|
/* remember this last bus state called out to this connection: */
|
|
conn_int->tme_scsi_connection_int_last_control
|
|
= control;
|
|
conn_int->tme_scsi_connection_int_last_data
|
|
= data;
|
|
|
|
/* get and clear the events triggered, actions taken, and any
|
|
DMA structure: */
|
|
events_triggered = conn_int->tme_scsi_connection_int_events_triggered;
|
|
actions_taken = conn_int->tme_scsi_connection_int_actions_taken;
|
|
dma = conn_int->tme_scsi_connection_int_dma;
|
|
if (dma != NULL) {
|
|
dma_buffer = *dma;
|
|
dma = &dma_buffer;
|
|
}
|
|
conn_int->tme_scsi_connection_int_events_triggered = 0;
|
|
conn_int->tme_scsi_connection_int_actions_taken = 0;
|
|
conn_int->tme_scsi_connection_int_dma = NULL;
|
|
|
|
/* add the cycle marker to the actions taken: */
|
|
actions_taken |= conn_int->tme_scsi_connection_int_cycle_marker;
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&scsi_bus->tme_scsi_bus_mutex);
|
|
|
|
/* do the callout: */
|
|
conn_scsi
|
|
= ((struct tme_scsi_connection *)
|
|
conn_int->tme_scsi_connection_int.tme_scsi_connection.tme_connection_other);
|
|
rc = ((*conn_scsi->tme_scsi_connection_cycle)
|
|
(conn_scsi,
|
|
control,
|
|
data,
|
|
events_triggered,
|
|
actions_taken,
|
|
dma));
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&scsi_bus->tme_scsi_bus_mutex);
|
|
|
|
/* if the callout was unsuccessful, remember that at some later
|
|
time this callout should be attempted again: */
|
|
if (rc != TME_OK) {
|
|
conn_int->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
later_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* put in any later callouts, and clear that callouts are running: */
|
|
scsi_bus->tme_scsi_bus_callout_flags = later_callouts;
|
|
}
|
|
|
|
/* this handles a SCSI bus cycle: */
|
|
static int
|
|
_tme_scsi_bus_cycle(struct tme_scsi_connection *conn_scsi,
|
|
tme_scsi_control_t control,
|
|
tme_scsi_data_t data,
|
|
tme_uint32_t events_asker,
|
|
tme_uint32_t actions_asker,
|
|
const struct tme_scsi_dma *dma_asker)
|
|
{
|
|
struct tme_scsi_bus *scsi_bus;
|
|
struct tme_scsi_connection_int *conn_int_asker, *conn_int;
|
|
tme_uint32_t events;
|
|
tme_uint32_t actions;
|
|
struct tme_scsi_dma *dma;
|
|
struct tme_scsi_connection_int *dma_initiator;
|
|
struct tme_scsi_connection_int *dma_target;
|
|
struct tme_scsi_dma *dma_in, *dma_out;
|
|
unsigned long count;
|
|
int bus_changed;
|
|
int new_callouts;
|
|
int again;
|
|
|
|
/* recover our bus and internal connection: */
|
|
scsi_bus = conn_scsi->tme_scsi_connection.tme_connection_element->tme_element_private;
|
|
conn_int_asker = (struct tme_scsi_connection_int *) conn_scsi;
|
|
|
|
/* assume we won't need any new callouts: */
|
|
new_callouts = 0;
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&scsi_bus->tme_scsi_bus_mutex);
|
|
|
|
/* update the cycle marker for this device: */
|
|
conn_int_asker->tme_scsi_connection_int_cycle_marker = (actions_asker & TME_SCSI_ACTION_CYCLE_MARKER);
|
|
actions_asker &= ~TME_SCSI_ACTION_CYCLE_MARKER;
|
|
|
|
/* update the signals that this device is asserting: */
|
|
conn_int_asker->tme_scsi_connection_int_control = control;
|
|
conn_int_asker->tme_scsi_connection_int_data = data;
|
|
|
|
/* if you're waiting on TME_SCSI_EVENT_BUS_CHANGE, you can't wait on
|
|
anything else, and you can't have any actions: */
|
|
/* you can't wait on TME_SCSI_EVENT_BUS_CHANGE and anything else: */
|
|
assert(!(events_asker & TME_SCSI_EVENT_BUS_CHANGE)
|
|
|| (events_asker == TME_SCSI_EVENT_BUS_CHANGE
|
|
&& actions_asker == TME_SCSI_ACTION_NONE));
|
|
|
|
/* you can do at most one of: select, reselect, DMA initiator, DMA target: */
|
|
assert (((events_asker
|
|
& (TME_SCSI_ACTION_SELECT
|
|
| TME_SCSI_ACTION_RESELECT
|
|
| TME_SCSI_ACTION_DMA_INITIATOR
|
|
| TME_SCSI_ACTION_DMA_TARGET))
|
|
& ((events_asker
|
|
& (TME_SCSI_ACTION_SELECT
|
|
| TME_SCSI_ACTION_RESELECT
|
|
| TME_SCSI_ACTION_DMA_INITIATOR
|
|
| TME_SCSI_ACTION_DMA_TARGET)) - 1)) == 0);
|
|
|
|
/* you can't provide a DMA structure without a DMA action, or with any events: */
|
|
assert ((dma_asker != NULL)
|
|
== ((actions_asker
|
|
& (TME_SCSI_ACTION_DMA_INITIATOR
|
|
| TME_SCSI_ACTION_DMA_TARGET)) != 0));
|
|
assert ((dma_asker == NULL) || (events_asker == TME_SCSI_EVENT_NONE));
|
|
|
|
/* update the events and actions for this device: */
|
|
conn_int_asker->tme_scsi_connection_int_events_waiting = events_asker;
|
|
conn_int_asker->tme_scsi_connection_int_events_triggered = 0;
|
|
conn_int_asker->tme_scsi_connection_int_actions_waiting = actions_asker;
|
|
conn_int_asker->tme_scsi_connection_int_actions_taken = 0;
|
|
conn_int_asker->tme_scsi_connection_int_sequence_step = 0;
|
|
|
|
/* if this is a DMA sequence: */
|
|
conn_int_asker->tme_scsi_connection_int_dma = NULL;
|
|
if (dma_asker != NULL) {
|
|
|
|
/* XXX is the 8-bit DMA restriction necessary? how does wide SCSI
|
|
handle transfers of odd numbers of bytes? */
|
|
if ((dma_asker->tme_scsi_dma_flags
|
|
& TME_SCSI_DMA_WIDTH)
|
|
!= TME_SCSI_DMA_8BIT) {
|
|
abort();
|
|
}
|
|
|
|
/* copy in the DMA structure: */
|
|
conn_int_asker->tme_scsi_connection_int_dma_buffer = *dma_asker;
|
|
conn_int_asker->tme_scsi_connection_int_dma = &conn_int_asker->tme_scsi_connection_int_dma_buffer;
|
|
|
|
/* if the caller passed us a DMA structure with zero bytes left to
|
|
transfer, call out a cycle now: */
|
|
if (dma_asker->tme_scsi_dma_resid == 0) {
|
|
conn_int_asker->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
conn_int_asker->tme_scsi_connection_int_events_waiting = TME_SCSI_EVENT_NONE;
|
|
conn_int_asker->tme_scsi_connection_int_actions_waiting = actions_asker;
|
|
new_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
}
|
|
}
|
|
|
|
/* if during any iteration of the below loop, we see or cause a
|
|
change on the bus, we want to call out cycles to all devices
|
|
waiting on a simple change: */
|
|
bus_changed = FALSE;
|
|
|
|
/* loop until things settle down: */
|
|
for (again = TRUE; again; ) {
|
|
again = FALSE;
|
|
|
|
/* get the current state of the bus: */
|
|
control = 0;
|
|
data = 0;
|
|
for (conn_int
|
|
= ((struct tme_scsi_connection_int *)
|
|
scsi_bus->tme_scsi_bus_connections);
|
|
conn_int != NULL;
|
|
conn_int
|
|
= ((struct tme_scsi_connection_int *)
|
|
conn_int->tme_scsi_connection_int.tme_scsi_connection.tme_connection_next)) {
|
|
control |= conn_int->tme_scsi_connection_int_control;
|
|
data |= conn_int->tme_scsi_connection_int_data;
|
|
}
|
|
if ((control != scsi_bus->tme_scsi_bus_control)
|
|
|| (data != scsi_bus->tme_scsi_bus_data)) {
|
|
bus_changed = TRUE;
|
|
}
|
|
scsi_bus->tme_scsi_bus_control = control;
|
|
scsi_bus->tme_scsi_bus_data = data;
|
|
|
|
/* loop over all devices on the bus: */
|
|
dma_initiator = NULL;
|
|
dma_target = NULL;
|
|
for (conn_int
|
|
= ((struct tme_scsi_connection_int *)
|
|
scsi_bus->tme_scsi_bus_connections);
|
|
conn_int != NULL;
|
|
conn_int
|
|
= ((struct tme_scsi_connection_int *)
|
|
conn_int->tme_scsi_connection_int.tme_scsi_connection.tme_connection_next)) {
|
|
|
|
/* get any waiting events and actions: */
|
|
events = conn_int->tme_scsi_connection_int_events_waiting;
|
|
actions = conn_int->tme_scsi_connection_int_actions_waiting;
|
|
|
|
/* if this device isn't waiting for any events and has no
|
|
actions to take, continue now: */
|
|
if (events == TME_SCSI_EVENT_NONE
|
|
&& actions == TME_SCSI_ACTION_NONE) {
|
|
continue;
|
|
}
|
|
|
|
/* if this device is waiting on any change to the bus state, and
|
|
the bus has changed: */
|
|
if ((events & TME_SCSI_EVENT_BUS_CHANGE)
|
|
&& (bus_changed
|
|
|| (control !=
|
|
conn_int->tme_scsi_connection_int_last_control)
|
|
|| (data
|
|
!= conn_int->tme_scsi_connection_int_last_data))) {
|
|
|
|
/* this event has triggered. we never take any action: */
|
|
conn_int->tme_scsi_connection_int_events_triggered = TME_SCSI_EVENT_BUS_CHANGE;
|
|
events = TME_SCSI_EVENT_NONE;
|
|
actions = TME_SCSI_ACTION_NONE;
|
|
}
|
|
|
|
/* if this device is waiting to be selected, and the device is
|
|
now being selected: */
|
|
if ((events & TME_SCSI_EVENT_SELECTED)
|
|
|
|
/* "In all systems, the target shall determine that it is
|
|
selected when SEL and its SCSI ID bit are true and BSY and
|
|
I/O are false for at least a bus settle delay." */
|
|
&& TME_SCSI_ID_SELECTED(TME_SCSI_EVENT_IDS_WHICH(events), control, data)) {
|
|
|
|
/* this event has triggered. the only action we can take is
|
|
to respond to the selection: */
|
|
conn_int->tme_scsi_connection_int_events_triggered = TME_SCSI_EVENT_SELECTED | TME_SCSI_EVENT_IDS_SELF(data);
|
|
events = TME_SCSI_EVENT_NONE;
|
|
actions &= TME_SCSI_ACTION_RESPOND_SELECTED;
|
|
}
|
|
|
|
/* if this device is waiting to be reselected, and the device is
|
|
now being reselected: */
|
|
if ((events & TME_SCSI_EVENT_RESELECTED)
|
|
|
|
/* "The initiator shall determine that it is reselected when
|
|
SEL, I/O, and its SCSI ID bit are true and BSY is false
|
|
for at least a bus settle delay." */
|
|
&& TME_SCSI_ID_RESELECTED(TME_SCSI_EVENT_IDS_WHICH(events), control, data)) {
|
|
|
|
/* this event has triggered. the only action we can take is
|
|
to respond to the reselection: */
|
|
conn_int->tme_scsi_connection_int_events_triggered = TME_SCSI_EVENT_RESELECTED | TME_SCSI_EVENT_IDS_SELF(data);
|
|
events = TME_SCSI_EVENT_NONE;
|
|
actions &= TME_SCSI_ACTION_RESPOND_RESELECTED;
|
|
}
|
|
|
|
/* if this device is waiting for the bus to be free, and the bus
|
|
is now free: */
|
|
if ((events & TME_SCSI_EVENT_BUS_FREE)
|
|
|
|
/* "SCSI devices shall detect the BUS FREE phase after SEL
|
|
and BSY are both false for at least a bus settle delay." */
|
|
&& (control
|
|
& (TME_SCSI_SIGNAL_SEL
|
|
| TME_SCSI_SIGNAL_BSY)) == 0) {
|
|
|
|
/* this event has triggered. we won't respond to selection or
|
|
reselection, since we won't be selected or reselected: */
|
|
conn_int->tme_scsi_connection_int_events_triggered = TME_SCSI_EVENT_BUS_FREE;
|
|
events = TME_SCSI_EVENT_NONE;
|
|
actions &= ~(TME_SCSI_ACTION_RESPOND_SELECTED | TME_SCSI_ACTION_RESPOND_RESELECTED);
|
|
}
|
|
|
|
/* put back this device's waiting events. if this device is
|
|
still waiting for one or more events, continue now: */
|
|
conn_int->tme_scsi_connection_int_events_waiting = events;
|
|
if (events != TME_SCSI_EVENT_NONE) {
|
|
continue;
|
|
}
|
|
|
|
/* if this device is arbitrating for the bus: */
|
|
if (TME_SCSI_ACTIONS_SELECTED(actions, TME_SCSI_ACTION_ARBITRATE_FULL)) {
|
|
|
|
/* assert BSY and the device ID on this device's behalf. if
|
|
we're doing full arbitration, also assert SEL on this
|
|
device's behalf: */
|
|
conn_int->tme_scsi_connection_int_control
|
|
= (TME_SCSI_SIGNAL_BSY
|
|
| (((actions & TME_SCSI_ACTION_ARBITRATE_FULL)
|
|
== TME_SCSI_ACTION_ARBITRATE_FULL)
|
|
? TME_SCSI_SIGNAL_SEL
|
|
: 0));
|
|
conn_int->tme_scsi_connection_int_data
|
|
= TME_BIT(TME_SCSI_ACTION_ID_SELF_WHICH(actions));
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* this action has been taken: */
|
|
conn_int->tme_scsi_connection_int_actions_taken
|
|
|= ((actions & TME_SCSI_ACTION_ARBITRATE_FULL)
|
|
| TME_SCSI_ACTION_ID_SELF(TME_SCSI_ACTION_ID_SELF_WHICH(actions)));
|
|
actions &= ~TME_SCSI_ACTION_ARBITRATE_FULL;
|
|
}
|
|
|
|
/* if this device is selecting or reselecting another device: */
|
|
if (TME_SCSI_ACTIONS_SELECTED(actions,
|
|
(TME_SCSI_ACTION_SELECT
|
|
| TME_SCSI_ACTION_SELECT_WITH_ATN
|
|
| TME_SCSI_ACTION_SELECT_SINGLE_INITIATOR
|
|
| TME_SCSI_ACTION_RESELECT))) {
|
|
|
|
/* "Except in certain single initiator environments with
|
|
initiators employing the single initiator option, the
|
|
initiator shall set the DATA BUS to a value which is the OR
|
|
of its SCSI ID bit and the target's SCSI ID bit. The
|
|
initiator shall then wait at least two deskew delays and
|
|
release BSY."
|
|
|
|
"Initiators that do not implement the RESELECTION phase and
|
|
do not operate in the multiple initiator environment are
|
|
allowed to set only the target's SCSI ID bit during the
|
|
SELECTION phase. This makes it impossible for the target
|
|
to determine the initiator's SCSI ID."
|
|
|
|
"The winning SCSI device becomes a target by asserting the
|
|
I/O signal. The winning SCSI device shall also set the
|
|
DATA BUS to a value that is the OR of its SCSI ID bit and
|
|
the initiator's SCSI ID bit. The target shall wait at
|
|
least two deskew delays and release BSY." */
|
|
|
|
/* set the sequence's selection/reselection SCSI IDs on the
|
|
bus, then set I/O if this is a reselection, and release
|
|
BSY: */
|
|
conn_int->tme_scsi_connection_int_data
|
|
= (TME_BIT(TME_SCSI_ACTION_ID_OTHER_WHICH(actions))
|
|
| (((actions & TME_SCSI_ACTION_SELECT_SINGLE_INITIATOR)
|
|
== TME_SCSI_ACTION_SELECT_SINGLE_INITIATOR)
|
|
? 0
|
|
: TME_BIT(TME_SCSI_ACTION_ID_SELF_WHICH(actions))));
|
|
conn_int->tme_scsi_connection_int_control
|
|
= (TME_SCSI_SIGNAL_SEL
|
|
| ((actions & TME_SCSI_ACTION_RESELECT)
|
|
? TME_SCSI_SIGNAL_I_O
|
|
: 0)
|
|
| (((actions & TME_SCSI_ACTION_SELECT_WITH_ATN)
|
|
== TME_SCSI_ACTION_SELECT_WITH_ATN)
|
|
? TME_SCSI_SIGNAL_ATN
|
|
: 0));
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* this action has been taken: */
|
|
conn_int->tme_scsi_connection_int_actions_taken
|
|
|= ((actions
|
|
& (TME_SCSI_ACTION_SELECT
|
|
| TME_SCSI_ACTION_SELECT_WITH_ATN
|
|
| TME_SCSI_ACTION_SELECT_SINGLE_INITIATOR
|
|
| TME_SCSI_ACTION_RESELECT))
|
|
| TME_SCSI_ACTION_ID_SELF(TME_SCSI_ACTION_ID_SELF_WHICH(actions))
|
|
| TME_SCSI_ACTION_ID_OTHER(TME_SCSI_ACTION_ID_OTHER_WHICH(actions)));
|
|
actions &= ~(TME_SCSI_ACTION_SELECT
|
|
| TME_SCSI_ACTION_SELECT_WITH_ATN
|
|
| TME_SCSI_ACTION_SELECT_SINGLE_INITIATOR
|
|
| TME_SCSI_ACTION_RESELECT);
|
|
}
|
|
|
|
/* if this device is responding to selection or reselection: */
|
|
if (TME_SCSI_ACTIONS_SELECTED(actions,
|
|
(TME_SCSI_ACTION_RESPOND_SELECTED
|
|
| TME_SCSI_ACTION_RESPOND_RESELECTED))) {
|
|
|
|
/* dispatch on the step: */
|
|
switch (conn_int->tme_scsi_connection_int_sequence_step) {
|
|
|
|
case 0:
|
|
|
|
/* assert BSY on the device's behalf: */
|
|
conn_int->tme_scsi_connection_int_control
|
|
= TME_SCSI_SIGNAL_BSY;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* advance to the next step: */
|
|
conn_int->tme_scsi_connection_int_sequence_step++;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 1:
|
|
|
|
/* "At least two deskew delays after the initiator detects
|
|
BSY is true, it shall release SEL and may change the DATA
|
|
BUS."
|
|
|
|
"After the reselected initiator detects SEL false, it
|
|
shall release BSY." */
|
|
if (!(control
|
|
& TME_SCSI_SIGNAL_SEL)) {
|
|
|
|
/* if this was a reselection: */
|
|
if (actions & TME_SCSI_ACTION_RESPOND_RESELECTED) {
|
|
|
|
/* negate BSY on the device's behalf: */
|
|
conn_int->tme_scsi_connection_int_control
|
|
= 0;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
}
|
|
|
|
/* this action has been taken: */
|
|
conn_int->tme_scsi_connection_int_actions_taken
|
|
|= (actions
|
|
& (TME_SCSI_ACTION_RESPOND_SELECTED
|
|
| TME_SCSI_ACTION_RESPOND_RESELECTED));
|
|
actions
|
|
&= ~(TME_SCSI_ACTION_RESPOND_SELECTED
|
|
| TME_SCSI_ACTION_RESPOND_RESELECTED);
|
|
conn_int->tme_scsi_connection_int_sequence_step = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* there can be at most one device in an initiator or target
|
|
information transfer phase DMA sequence: */
|
|
else if (TME_SCSI_ACTIONS_SELECTED(actions,
|
|
(TME_SCSI_ACTION_DMA_INITIATOR
|
|
| TME_SCSI_ACTION_DMA_INITIATOR_HOLD_ACK))) {
|
|
assert (dma_initiator == NULL);
|
|
dma_initiator = conn_int;
|
|
}
|
|
else if (TME_SCSI_ACTIONS_SELECTED(actions, TME_SCSI_ACTION_DMA_TARGET)) {
|
|
assert (dma_target == NULL);
|
|
dma_target = conn_int;
|
|
}
|
|
|
|
/* put back this device's waiting actions. if this device is
|
|
not waiting for any more actions: */
|
|
conn_int->tme_scsi_connection_int_actions_waiting = actions;
|
|
if ((actions
|
|
& (TME_SCSI_ACTION_DMA_INITIATOR
|
|
| TME_SCSI_ACTION_DMA_INITIATOR_HOLD_ACK
|
|
| TME_SCSI_ACTION_DMA_TARGET
|
|
| TME_SCSI_ACTION_RESPOND_SELECTED
|
|
| TME_SCSI_ACTION_RESPOND_RESELECTED
|
|
| TME_SCSI_ACTION_SELECT
|
|
| TME_SCSI_ACTION_SELECT_WITH_ATN
|
|
| TME_SCSI_ACTION_SELECT_SINGLE_INITIATOR
|
|
| TME_SCSI_ACTION_RESELECT
|
|
| TME_SCSI_ACTION_ARBITRATE_HALF
|
|
| TME_SCSI_ACTION_ARBITRATE_FULL)) == 0) {
|
|
|
|
/* call out a cycle on this device: */
|
|
conn_int->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
new_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
}
|
|
}
|
|
|
|
/* if we need to loop again, do so immediately: */
|
|
if (again) {
|
|
continue;
|
|
}
|
|
|
|
/* if a device is in the target information transfer phase DMA
|
|
sequence: */
|
|
if (dma_target != NULL) {
|
|
|
|
/* get this device's DMA structure: */
|
|
dma = dma_target->tme_scsi_connection_int_dma;
|
|
assert (dma != NULL);
|
|
|
|
/* dispatch on the sequence step: */
|
|
switch (dma_target->tme_scsi_connection_int_sequence_step) {
|
|
|
|
/* "If I/O is true (transfer to the initiator)... [after] ACK
|
|
is false the target may continue the transfer by driving
|
|
DB(7-0,P) and asserting REQ, as described above."
|
|
|
|
"If I/O is false (transfer to the target)... [after ACK is
|
|
false the target] may continue the transfer by asserting
|
|
REQ, as described above." */
|
|
case 2:
|
|
if (control & TME_SCSI_SIGNAL_ACK) {
|
|
break;
|
|
}
|
|
|
|
/* if the DMA has been exhausted, callout a cycle on this
|
|
device: */
|
|
if (control & TME_SCSI_SIGNAL_I_O) {
|
|
dma->tme_scsi_dma_out++;
|
|
}
|
|
else {
|
|
dma->tme_scsi_dma_in++;
|
|
}
|
|
if (--dma->tme_scsi_dma_resid == 0) {
|
|
dma_target->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
dma_target->tme_scsi_connection_int_actions_waiting
|
|
= TME_SCSI_ACTION_NONE;
|
|
dma_target->tme_scsi_connection_int_actions_taken
|
|
|= TME_SCSI_ACTION_DMA_TARGET;
|
|
new_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
break;
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
/* "If I/O is true (transfer to the initiator), the target shall
|
|
first drive DB(7-0,P) to their desired values, delay at least
|
|
one deskew delay plus a cable skew delay, then assert REQ."
|
|
|
|
"If I/O is false (transfer to the target) the target shall request
|
|
information by asserting REQ. " */
|
|
case 0:
|
|
|
|
/* assert REQ on the target's behalf: */
|
|
dma_target->tme_scsi_connection_int_control
|
|
|= TME_SCSI_SIGNAL_REQ;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* if I/O is asserted, assert the output data: */
|
|
if (control & TME_SCSI_SIGNAL_I_O) {
|
|
dma_target->tme_scsi_connection_int_data
|
|
= *(dma->tme_scsi_dma_out);
|
|
}
|
|
|
|
/* advance to step one: */
|
|
dma_target->tme_scsi_connection_int_sequence_step = 1;
|
|
break;
|
|
|
|
/* "If I/O is true (transfer to the initiator)... [when] ACK
|
|
becomes true at the target, the target may change or
|
|
release DB(7-0,P) and shall negate REQ."
|
|
|
|
"If I/O is false (transfer to the target)... [when] ACK
|
|
becomes true at the target, the target shall read
|
|
DB(7-0,P), then negate REQ." */
|
|
case 1:
|
|
if (control & TME_SCSI_SIGNAL_ACK) {
|
|
|
|
/* if I/O is negated, read the input data: */
|
|
if (!(control & TME_SCSI_SIGNAL_I_O)) {
|
|
*(dma->tme_scsi_dma_in) = data;
|
|
}
|
|
|
|
/* negate REQ on the target's behalf: */
|
|
dma_target->tme_scsi_connection_int_control
|
|
&= ~TME_SCSI_SIGNAL_REQ;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* advance to step two: */
|
|
dma_target->tme_scsi_connection_int_sequence_step = 2;
|
|
}
|
|
break;
|
|
|
|
default: assert (FALSE);
|
|
}
|
|
|
|
/* if the bus is being reset: */
|
|
if (control & TME_SCSI_SIGNAL_RST) {
|
|
|
|
/* negate all signals on the target's behalf: */
|
|
dma_target->tme_scsi_connection_int_control = 0;
|
|
dma_target->tme_scsi_connection_int_data = 0;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* callout a cycle on this device: */
|
|
dma_target->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
dma_target->tme_scsi_connection_int_actions_waiting
|
|
= TME_SCSI_ACTION_NONE;
|
|
dma_target->tme_scsi_connection_int_actions_taken
|
|
= TME_SCSI_ACTION_NONE;
|
|
dma_target->tme_scsi_connection_int_events_triggered
|
|
= TME_SCSI_EVENT_BUS_RESET;
|
|
new_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
}
|
|
}
|
|
|
|
/* if we need to loop again, do so immediately: */
|
|
if (again) {
|
|
continue;
|
|
}
|
|
|
|
/* if a device is in the initiator information transfer phase DMA
|
|
sequence: */
|
|
if (dma_initiator != NULL) {
|
|
|
|
/* get this device's DMA structure: */
|
|
dma = dma_initiator->tme_scsi_connection_int_dma;
|
|
assert (dma != NULL);
|
|
|
|
/* dispatch on the sequence step: */
|
|
switch (dma_initiator->tme_scsi_connection_int_sequence_step) {
|
|
|
|
/* "If I/O is true (transfer to the initiator)... [the]
|
|
initiator shall read DB(7-0,P) after REQ is true, then
|
|
signal its acceptance of the data by asserting ACK."
|
|
|
|
"If I/O is false (transfer to the target)... [the]
|
|
initiator shall drive DB(7-0,P) to their desired values
|
|
[after REQ is true], delay at least one deskew delay plus a
|
|
cable skew delay and assert ACK." */
|
|
case 0:
|
|
if (!(control & TME_SCSI_SIGNAL_REQ)) {
|
|
break;
|
|
}
|
|
|
|
/* if the information transfer phase has changed, callout a
|
|
cycle on this device: */
|
|
if (TME_SCSI_PHASE(control)
|
|
!= TME_SCSI_PHASE(dma_initiator->tme_scsi_connection_int_last_control)) {
|
|
dma_initiator->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
dma_initiator->tme_scsi_connection_int_actions_taken
|
|
|= dma_initiator->tme_scsi_connection_int_actions_waiting;
|
|
dma_initiator->tme_scsi_connection_int_actions_waiting
|
|
= TME_SCSI_ACTION_NONE;
|
|
new_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
break;
|
|
}
|
|
|
|
/* if another device is in the target information transfer
|
|
phase DMA sequence, we may do a bulk copy between them: */
|
|
if (dma_target != NULL) {
|
|
assert (dma_target->tme_scsi_connection_int_sequence_step == 1);
|
|
|
|
/* sort the devices' DMA structures into input and output: */
|
|
if (control & TME_SCSI_SIGNAL_I_O) {
|
|
dma_in = dma_initiator->tme_scsi_connection_int_dma;
|
|
dma_out = dma_target->tme_scsi_connection_int_dma;
|
|
}
|
|
else {
|
|
dma_out = dma_initiator->tme_scsi_connection_int_dma;
|
|
dma_in = dma_target->tme_scsi_connection_int_dma;
|
|
}
|
|
assert (dma_out != NULL && dma_in != NULL);
|
|
|
|
/* get the size of the bulk copy. we won't copy all
|
|
possible bytes, since this code doesn't know how to
|
|
assert control lines or make callouts. instead, we bulk
|
|
copy one less than all possible bytes, and let the
|
|
normal, non-bulk code handle the final byte: */
|
|
count = TME_MIN(dma_out->tme_scsi_dma_resid,
|
|
dma_in->tme_scsi_dma_resid);
|
|
assert (count > 0);
|
|
|
|
/* if we have bytes to bulk copy: */
|
|
count--;
|
|
if (count > 0) {
|
|
|
|
/* do the bulk copy: */
|
|
memcpy(dma_in->tme_scsi_dma_in,
|
|
dma_out->tme_scsi_dma_out,
|
|
count);
|
|
|
|
/* advance: */
|
|
dma_in->tme_scsi_dma_in += count;
|
|
dma_in->tme_scsi_dma_resid -= count;
|
|
dma_out->tme_scsi_dma_out += count;
|
|
dma_out->tme_scsi_dma_resid -= count;
|
|
|
|
/* if I/O is asserted, assert the new data on the target's
|
|
behalf: */
|
|
if (control & TME_SCSI_SIGNAL_I_O) {
|
|
data = *(dma_out->tme_scsi_dma_out);
|
|
dma_target->tme_scsi_connection_int_data = data;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if I/O is true, read the data, else
|
|
write the data: */
|
|
if (control & TME_SCSI_SIGNAL_I_O) {
|
|
*dma->tme_scsi_dma_in = data;
|
|
}
|
|
else {
|
|
dma_initiator->tme_scsi_connection_int_data
|
|
= *(dma->tme_scsi_dma_out);
|
|
}
|
|
|
|
/* assert ACK on the initiator's behalf: */
|
|
dma_initiator->tme_scsi_connection_int_control
|
|
|= TME_SCSI_SIGNAL_ACK;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* advance to step one: */
|
|
dma_initiator->tme_scsi_connection_int_sequence_step = 1;
|
|
break;
|
|
|
|
/* "If I/O is true (transfer to the initiator)... [after]
|
|
REQ is false the initiator shall then negate ACK."
|
|
|
|
"If I/O is false (transfer to the target)... [when]
|
|
REQ becomes false at the initiator, the initiator may
|
|
change or release DB(7-0,P) and shall negate ACK." */
|
|
case 1:
|
|
if (control & TME_SCSI_SIGNAL_REQ) {
|
|
break;
|
|
}
|
|
|
|
/* unless the initiator wants ACK held: */
|
|
if ((dma_initiator->tme_scsi_connection_int_actions_waiting
|
|
& TME_SCSI_ACTION_DMA_INITIATOR_HOLD_ACK)
|
|
!= TME_SCSI_ACTION_DMA_INITIATOR_HOLD_ACK) {
|
|
|
|
/* negate ACK on the initiator's behalf: */
|
|
dma_initiator->tme_scsi_connection_int_control
|
|
&= ~TME_SCSI_SIGNAL_ACK;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
}
|
|
|
|
/* if the DMA has been exhausted, callout a cycle on this
|
|
device: */
|
|
if (control & TME_SCSI_SIGNAL_I_O) {
|
|
dma->tme_scsi_dma_in++;
|
|
}
|
|
else {
|
|
dma->tme_scsi_dma_out++;
|
|
}
|
|
if (--dma->tme_scsi_dma_resid == 0) {
|
|
dma_initiator->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
dma_initiator->tme_scsi_connection_int_actions_taken
|
|
|= dma_initiator->tme_scsi_connection_int_actions_waiting;
|
|
dma_initiator->tme_scsi_connection_int_actions_waiting
|
|
= TME_SCSI_ACTION_NONE;
|
|
new_callouts |= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
}
|
|
|
|
/* otherwise, advance to step zero: */
|
|
else {
|
|
dma_initiator->tme_scsi_connection_int_sequence_step = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert (FALSE);
|
|
}
|
|
|
|
/* if the bus is being reset: */
|
|
if (control & TME_SCSI_SIGNAL_RST) {
|
|
|
|
/* negate all signals on the initiator's behalf: */
|
|
dma_initiator->tme_scsi_connection_int_control = 0;
|
|
dma_initiator->tme_scsi_connection_int_data = 0;
|
|
again = TRUE;
|
|
bus_changed = TRUE;
|
|
|
|
/* callout a cycle on this device: */
|
|
dma_initiator->tme_scsi_connection_int_callout_flags
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
dma_initiator->tme_scsi_connection_int_actions_waiting
|
|
= TME_SCSI_ACTION_NONE;
|
|
dma_initiator->tme_scsi_connection_int_actions_taken
|
|
= TME_SCSI_ACTION_NONE;
|
|
dma_initiator->tme_scsi_connection_int_events_triggered
|
|
= TME_SCSI_EVENT_BUS_RESET;
|
|
new_callouts
|
|
|= TME_SCSI_BUS_CALLOUT_CYCLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* make any needed callouts: */
|
|
_tme_scsi_bus_callout(scsi_bus, new_callouts);
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&scsi_bus->tme_scsi_bus_mutex);
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this scores a new connection: */
|
|
static int
|
|
_tme_scsi_bus_connection_score(struct tme_connection *conn,
|
|
unsigned int *_score)
|
|
{
|
|
struct tme_scsi_bus *scsi_bus;
|
|
struct tme_scsi_connection *conn_other;
|
|
|
|
/* both sides must be SCSI connections: */
|
|
assert (conn->tme_connection_type == TME_CONNECTION_SCSI);
|
|
assert (conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SCSI);
|
|
|
|
/* recover our bus and the other internal connection side: */
|
|
scsi_bus = conn->tme_connection_element->tme_element_private;
|
|
conn_other = (struct tme_scsi_connection *) conn->tme_connection_other;
|
|
|
|
/* you cannot connect a bus to a bus: */
|
|
/* XXX we need a way to distinguish a bus from a device: */
|
|
*_score
|
|
= 1;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this makes a new connection: */
|
|
static int
|
|
_tme_scsi_bus_connection_make(struct tme_connection *conn,
|
|
unsigned int state)
|
|
{
|
|
struct tme_scsi_bus *scsi_bus;
|
|
struct tme_scsi_connection_int *conn_int;
|
|
|
|
/* both sides must be SCSI connections: */
|
|
assert (conn->tme_connection_type == TME_CONNECTION_SCSI);
|
|
assert (conn->tme_connection_other->tme_connection_type == TME_CONNECTION_SCSI);
|
|
|
|
/* recover our bus and our internal connection side: */
|
|
scsi_bus = conn->tme_connection_element->tme_element_private;
|
|
conn_int = (struct tme_scsi_connection_int *) conn;
|
|
|
|
/* we're always set up to answer calls across the connection,
|
|
so we only have to do work when the connection has gone full,
|
|
namely taking the other side of the connection: */
|
|
if (state == TME_CONNECTION_FULL) {
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&scsi_bus->tme_scsi_bus_mutex);
|
|
|
|
/* add this connection to our list of connections: */
|
|
conn->tme_connection_next = scsi_bus->tme_scsi_bus_connections;
|
|
scsi_bus->tme_scsi_bus_connections = conn;
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&scsi_bus->tme_scsi_bus_mutex);
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this breaks a connection: */
|
|
static int
|
|
_tme_scsi_bus_connection_break(struct tme_connection *conn,
|
|
unsigned int state)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
/* this returns the new connections possible: */
|
|
static int
|
|
_tme_scsi_bus_connections_new(struct tme_element *element,
|
|
const char * const *args,
|
|
struct tme_connection **_conns,
|
|
char **_output)
|
|
{
|
|
struct tme_scsi_connection_int *conn_int;
|
|
struct tme_scsi_connection *conn_scsi;
|
|
struct tme_connection *conn;
|
|
|
|
/* we never take any arguments: */
|
|
if (args[1] != NULL) {
|
|
tme_output_append_error(_output,
|
|
"%s %s, ",
|
|
args[1],
|
|
_("unexpected"));
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* create our side of a SCSI connection: */
|
|
conn_int = tme_new0(struct tme_scsi_connection_int, 1);
|
|
conn_scsi = &conn_int->tme_scsi_connection_int;
|
|
conn = &conn_scsi->tme_scsi_connection;
|
|
|
|
/* fill in the generic connection: */
|
|
conn->tme_connection_next = *_conns;
|
|
conn->tme_connection_type = TME_CONNECTION_SCSI;
|
|
conn->tme_connection_score = _tme_scsi_bus_connection_score;
|
|
conn->tme_connection_make = _tme_scsi_bus_connection_make;
|
|
conn->tme_connection_break = _tme_scsi_bus_connection_break;
|
|
|
|
/* fill in the SCSI connection: */
|
|
conn_scsi->tme_scsi_connection_cycle = _tme_scsi_bus_cycle;
|
|
|
|
/* return the connection side possibility: */
|
|
*_conns = conn;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this creates a new SCSI bus element: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_scsi,bus) {
|
|
struct tme_scsi_bus *scsi_bus;
|
|
int usage;
|
|
int arg_i;
|
|
|
|
/* check our arguments: */
|
|
arg_i = 1;
|
|
usage = FALSE;
|
|
|
|
/* loop reading our arguments: */
|
|
for (;;) {
|
|
|
|
if (0) {
|
|
}
|
|
|
|
/* if we've run out of arguments: */
|
|
else if (args[arg_i + 0] == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
/* this is a bad argument: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
args[arg_i],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
_("usage:"),
|
|
args[0]);
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* allocate and initialize the new SCSI bus: */
|
|
scsi_bus = tme_new0(struct tme_scsi_bus, 1);
|
|
tme_mutex_init(&scsi_bus->tme_scsi_bus_mutex);
|
|
|
|
/* fill the element: */
|
|
element->tme_element_private = scsi_bus;
|
|
element->tme_element_connections_new = _tme_scsi_bus_connections_new;
|
|
|
|
return (TME_OK);
|
|
}
|