1394 lines
33 KiB
C
1394 lines
33 KiB
C
/* @(#)ipi.c 1.1 10/31/94 */
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)ipi.c 1.1 94/10/31 Copyr 1991 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Host Adapter Layer for IPI Channels.
|
|
*
|
|
* This module links IPI device and controller drivers to
|
|
* IPI Channel drivers. The IPI Channel drivers call ipi_channel
|
|
* to "register" with this module and declare their willingness
|
|
* to accept requests.
|
|
*
|
|
* The basic structure passed by this module is the ipiq, which
|
|
* contains pointers to the IPI command and response packets.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/errno.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <sundev/ipi3.h>
|
|
#include <sundev/ipi_driver.h>
|
|
#include <sundev/ipi_chan.h>
|
|
#include <sundev/ipi_trace.h>
|
|
#include <machine/psl.h>
|
|
#include <sys/debug.h>
|
|
|
|
#define RESP_BUF_LEN 512 /* size of response unpacking buf (on stack) */
|
|
|
|
extern caddr_t kmem_alloc();
|
|
|
|
/*
|
|
* Channel structure pointer anchor.
|
|
* The number of channels is dynamically configured.
|
|
*/
|
|
static struct ipi_chan **ipi_chan;
|
|
static int ipi_nchan;
|
|
|
|
#define CHANSET 4 /* number of channel pointers to extend by */
|
|
|
|
#define IPI_RETRY_TIMEOUT 21 /* timeout for command retried */
|
|
#define IPI_DEFAULT_TIMEOUT 19 /* default timeout for command */
|
|
static int ipi_retry_limit = IPI_RETRY_LIMIT;
|
|
static int ipi_retry_timeout = IPI_RETRY_TIMEOUT;
|
|
static int ipi_default_timeout = IPI_DEFAULT_TIMEOUT;
|
|
|
|
/*
|
|
* Request lookup table.
|
|
*
|
|
* This table provides lookup of requests by command reference number.
|
|
*
|
|
* The range of command reference numbers should be larger than the number
|
|
* of request packets in order to reduce the frequency re-use of
|
|
* command reference numbers. It cannot be increased beyond 64K-1.
|
|
*/
|
|
#define MAX_REFNUM 1024 /* max cmd ref number (16 bits) */
|
|
int ipi_lookup_len = MAX_REFNUM; /* size of refnum lookup table */
|
|
static struct ipiq **ipi_ipiq_lookup; /* lookup to IPIQ */
|
|
|
|
void assign_refnum();
|
|
void free_refnum();
|
|
|
|
static int ipi_refnum = 1; /* Next cmd reference number to use */
|
|
|
|
/*
|
|
* Header for list of ipiq allocations.
|
|
*/
|
|
struct ipiq_chunk ipiq_chunk_list = { &ipiq_chunk_list, 0};
|
|
|
|
static int ipi_pri;
|
|
|
|
#define IPI_WATCH_TIME (9*hz) /* interval between queue checks */
|
|
static void ipi_watch(); /* look for hung IPI requests */
|
|
static void ipi_setup_start(); /* setup q for call to channel */
|
|
|
|
|
|
/*
|
|
* Callback list.
|
|
*/
|
|
struct ipi_callback {
|
|
int (*ic_fcn)();
|
|
};
|
|
|
|
#define IPIQ_CBQ_LEN 10 /* one per driver should be enough */
|
|
|
|
static struct ipi_callback ipiq_callback_list[IPIQ_CBQ_LEN]; /* list */
|
|
static struct ipi_callback *ipiq_cb_in = ipiq_callback_list; /* in pointer */
|
|
static struct ipi_callback *ipiq_cb_out = &ipiq_callback_list[IPIQ_CBQ_LEN-1];
|
|
/* out pointer */
|
|
static int ipiq_free_wait; /* processes waiting for ipiqs */
|
|
static int ipiq_callback_len;
|
|
|
|
static struct ipiq_stats {
|
|
int i_runout; /* number of times out of ipiqs */
|
|
int i_runout_int; /* number of times out of internal */
|
|
int i_max_qlen; /* maximum callback length */
|
|
} ipiq_stats;
|
|
|
|
static void ipiq_callback(); /* schedule callback */
|
|
static int ipiq_call(); /* invoke callback */
|
|
|
|
/*
|
|
* Initialization:
|
|
* Called each time ipi_channel is called. Therefore this allocation
|
|
* is only performed if an on-line channel board is configured.
|
|
* Setup IPI command reference number lookup.
|
|
* Start command timeout watchdog routine.
|
|
*/
|
|
static char ipi_init_flag; /* non-zero after initialization */
|
|
|
|
static void
|
|
ipi_init()
|
|
{
|
|
if (ipi_init_flag)
|
|
return;
|
|
#ifdef IPI_TRACE_SOLO
|
|
ipi_inittrace();
|
|
#endif
|
|
|
|
#ifndef lint
|
|
/*
|
|
* IPIQ must be aligned so that the low order bits of the intparm
|
|
* can be used for flags. Lint doesn't understand why anyone would
|
|
* use constant expressions in conditionals.
|
|
*/
|
|
if (sizeof(struct ipiq) % sizeof(long)) {
|
|
panic("ipiq improperly sized");
|
|
}
|
|
#endif
|
|
/*
|
|
* Allocate and initialize command reference lookup table.
|
|
*/
|
|
ipi_ipiq_lookup = (struct ipiq **)kmem_zalloc((u_int)ipi_lookup_len
|
|
* sizeof(struct ipiq *));
|
|
if (ipi_ipiq_lookup == NULL)
|
|
panic("ipi_init: no memory for IPI lookup");
|
|
|
|
/*
|
|
* Schedule watchdog interrupt.
|
|
*/
|
|
timeout((int (*)())ipi_watch, (caddr_t)NULL, IPI_WATCH_TIME);
|
|
|
|
ipi_init_flag = 1; /* indicate initialization complete */
|
|
}
|
|
|
|
/*
|
|
* Perform initialization for a device.
|
|
*
|
|
* Returns non-zero if the address passed is invalid or the channel and
|
|
* slave are not attached.
|
|
*/
|
|
/* ARGSUSED */
|
|
ipi_device(addr, intpri, flags, async, recover, ctlr)
|
|
int addr; /* IPI device address */
|
|
int intpri;
|
|
int flags;
|
|
void (*async)(); /* async interrupt routine */
|
|
void (*recover)(); /* driver recovery routine */
|
|
caddr_t ctlr; /* driver info for ipiq structures */
|
|
{
|
|
int chan;
|
|
int slave;
|
|
int fac;
|
|
struct ipi_chan *ch;
|
|
struct ipi_slave *sl;
|
|
|
|
if ((chan = IPI_CHAN(addr)) >= ipi_nchan || chan < 0
|
|
|| (ch = ipi_chan[chan]) == NULL
|
|
|| ((slave = IPI_SLAVE(addr)) < 0 || slave >= IPI_NSLAVE)
|
|
|| ch->ic_slave[slave].ic_arg == 0
|
|
|| (fac = IPI_FAC(addr)) != IPI_NO_ADDR
|
|
&& (fac < 0 || fac >= IPI_NFAC))
|
|
return -1;
|
|
sl = &ch->ic_slave[slave];
|
|
sl->ic_intpri = intpri;
|
|
sl->ic_async = async;
|
|
sl->ic_recover = recover;
|
|
sl->ic_ctlr = ctlr; /* driver info for ipiq structures */
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate request (ipiq) structure for driver and setup DMA for buffer.
|
|
*
|
|
* The channel number is passed so that a compatible ipiq can be returned,
|
|
* i.e. one with the appropriate mappings.
|
|
* The command and response buffer lengths are passed in so that eventually
|
|
* we can handle drivers that desire long responses.
|
|
*
|
|
* Returns NULL if temporarily out of IPIQs.
|
|
* If buffer cannot be setup, returns NULL after setting error flags in buf.
|
|
*/
|
|
struct ipiq *
|
|
ipi_setup(addr, bp, cmdlen, resplen, callback, flags)
|
|
int addr; /* IPI address */
|
|
struct buf *bp; /* buffer for request (if any) */
|
|
int cmdlen;
|
|
int resplen;
|
|
int (*callback)(); /* callback function */
|
|
int flags; /* flags for request */
|
|
{
|
|
register struct ipiq *q;
|
|
register struct ipi_chan *ch;
|
|
register struct ipi_slave *sl;
|
|
register u_int chan;
|
|
register u_int slave;
|
|
|
|
chan = IPI_CHAN(addr);
|
|
slave = IPI_SLAVE(addr);
|
|
if (chan >= ipi_nchan || slave >= IPI_NSLAVE
|
|
|| (ch = ipi_chan[chan]) == NULL
|
|
|| ((sl = &ch->ic_slave[slave])->ic_arg) == 0)
|
|
{
|
|
printf("ipi_setup %x: invalid IPI address\n", addr);
|
|
return NULL;
|
|
}
|
|
|
|
q = (*ch->ic_driver->id_setup)(sl->ic_arg, bp, cmdlen, resplen,
|
|
callback, flags);
|
|
|
|
if (q != NULL) {
|
|
q->q_result = IP_SETUP;
|
|
q->q_chan = ch;
|
|
q->q_csf = addr;
|
|
q->q_intclass = sl->ic_intpri;
|
|
q->q_ctlr = sl->ic_ctlr;
|
|
q->q_time = ch->ic_slave[slave].ic_time;
|
|
}
|
|
return q;
|
|
}
|
|
|
|
/*
|
|
* Release resources for request.
|
|
*/
|
|
void
|
|
ipi_free(q)
|
|
register struct ipiq *q;
|
|
{
|
|
free_refnum(q);
|
|
(*q->q_chan->ic_driver->id_relse)(q);
|
|
}
|
|
|
|
/*
|
|
* Send command to channel driver.
|
|
*/
|
|
void
|
|
ipi_start(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ch = q->q_chan;
|
|
ipi_setup_start(q, flag, func);
|
|
ipi_trace6(TRC_IPI_CMD, q->q_csf, q->q_refnum,
|
|
((long *)q->q_cmd)[0], ((long *)q->q_cmd)[1],
|
|
((long *)q->q_cmd)[2], ((long *)q->q_cmd)[3]);
|
|
q->q_chan_func = ch->ic_driver->id_cmd;
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
|
|
static void
|
|
ipi_setup_start(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
register int addr;
|
|
|
|
q->q_flag |= flag & IP_DRIVER_FLAGS;
|
|
q->q_func = func;
|
|
|
|
/*
|
|
* Set deadline for completion.
|
|
*/
|
|
if (q->q_time > 0) {
|
|
q->q_time += time.tv_sec - boottime.tv_sec;
|
|
/*
|
|
* Increase the timeout by few more seconds to account for:
|
|
* 1. The command is not yet issued to the controller, it may
|
|
* remain in ic_wait queue for some time before being sent.
|
|
* 2. The default timeout seem to be not adequate under
|
|
* high load.
|
|
*/
|
|
q->q_time += 60;
|
|
}
|
|
q->q_result = IP_INPROGRESS;
|
|
|
|
/*
|
|
* Set IPI layer portion of packet.
|
|
*/
|
|
addr = q->q_csf;
|
|
q->q_cmd->hdr_slave = IPI_SLAVE(addr);;
|
|
q->q_cmd->hdr_facility = IPI_FAC(addr);
|
|
assign_refnum(q); /* assign command reference number */
|
|
}
|
|
|
|
/*
|
|
* Send reset slave command to channel driver.
|
|
*/
|
|
void
|
|
ipi_reset_slave(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ipi_setup_start(q, flag | IP_NO_RETRY, func);
|
|
ch = q->q_chan;
|
|
ipi_trace2(TRC_IPI_RST_SL, q->q_csf, q->q_refnum);
|
|
q->q_chan_func = ch->ic_driver->id_reset_slave;
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
|
|
/*
|
|
* Send reset channel command to channel driver.
|
|
*/
|
|
void
|
|
ipi_reset_chan(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ch = q->q_chan;
|
|
|
|
/*
|
|
* If this is just a virtual channel, for addressing bus-attached
|
|
* controllers (like the ISP-80), don't really reset it and the
|
|
* other controllers.
|
|
*/
|
|
if (!(ch->ic_flags & IC_VIRT_CHAN)) {
|
|
/*
|
|
* Tell all drivers that their controller will have to be reset.
|
|
* This should stop them from issuing new requests until the
|
|
* controller is reset/initialized. This routine should
|
|
* not issue any I/O of it's own, since it would only get in the
|
|
* way of the channel reset.
|
|
*/
|
|
ipi_recover_chan(IPI_CHAN(q->q_csf), IPR_REINIT);
|
|
}
|
|
|
|
ipi_setup_start(q, flag, func);
|
|
|
|
ipi_trace2(TRC_IPI_RST_CH, q->q_csf, q->q_refnum);
|
|
q->q_chan_func = ch->ic_driver->id_reset_chan;
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
|
|
/*
|
|
* Send NO-OP command to channel driver to test board function.
|
|
*/
|
|
void
|
|
ipi_test_board(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ch = q->q_chan;
|
|
ipi_setup_start(q, flag, func);
|
|
ipi_trace2(TRC_IPI_TST_BD, q->q_csf, q->q_refnum);
|
|
q->q_chan_func = ch->ic_driver->id_test_board;
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
|
|
/*
|
|
* Get transfer settings.
|
|
* Returns transfer setting octet in first byte of buffer.
|
|
*/
|
|
void
|
|
ipi_get_xfer_mode(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ch = q->q_chan;
|
|
ipi_setup_start(q, flag, func);
|
|
ipi_trace2(TRC_IPI_GET_XFR, q->q_csf, q->q_refnum);
|
|
q->q_chan_func = ch->ic_driver->id_get_xfer_mode;
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
|
|
/*
|
|
* Set transfer settings.
|
|
* Returns transfer setting octet in first byte of buffer.
|
|
*/
|
|
void
|
|
ipi_set_xfer_mode(q, flag, func, mode)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
int mode; /* transfer mode */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ch = q->q_chan;
|
|
ipi_setup_start(q, flag, func);
|
|
ipi_trace2(TRC_IPI_SET_XFR, q->q_csf, q->q_refnum);
|
|
q->q_chan_func = ch->ic_driver->id_set_xfer_mode;
|
|
(*q->q_chan_func)(q, mode);
|
|
}
|
|
|
|
/*
|
|
* Send reset board command to channel driver.
|
|
*/
|
|
void
|
|
ipi_reset_board(q, flag, func)
|
|
register struct ipiq *q; /* request pointer */
|
|
long flag; /* flags */
|
|
void (*func)(); /* interrupt routine */
|
|
{
|
|
struct ipi_chan *ch;
|
|
|
|
ch = q->q_chan;
|
|
ipi_setup_start(q, flag, func);
|
|
ipi_trace2(TRC_IPI_RST_BD, q->q_csf, q->q_refnum);
|
|
q->q_chan_func = ch->ic_driver->id_reset_board;
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
|
|
/*
|
|
* Indicate that all slaves should be reset.
|
|
* Called by a channel driver with multiple channels
|
|
* after a successful reset of the board.
|
|
* Purpose is to notify device driver that a slave reset is required.
|
|
*/
|
|
void
|
|
ipi_recover_chan(chan, code)
|
|
u_int chan;
|
|
int code;
|
|
{
|
|
register struct ipi_chan *ch;
|
|
register struct ipi_slave *sl;
|
|
|
|
if (chan >= ipi_nchan || (ch = ipi_chan[chan]) == NULL) {
|
|
printf("ipi %x: channel not configured\n", chan);
|
|
return;
|
|
}
|
|
for (sl = ch->ic_slave; sl < &ch->ic_slave[IPI_NSLAVE]; sl++) {
|
|
if (sl->ic_recover)
|
|
(*sl->ic_recover)(sl->ic_ctlr, code);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle interrupt from channel driver.
|
|
*/
|
|
void
|
|
ipi_intr(q)
|
|
register struct ipiq *q;
|
|
{
|
|
register struct ipi_chan *ch;
|
|
register struct ipiq *qq;
|
|
|
|
ch = q->q_chan;
|
|
|
|
/*
|
|
* See if there are any interrupts queued. These are
|
|
* interrupts that couldn't be handled because they occured while
|
|
* polling for other devices.
|
|
*
|
|
* Note that this loop starts at the head of the list each time,
|
|
* in case the list changes during the driver's interrupt routine.
|
|
*/
|
|
while ((qq = ch->ic_rupt.q_head) != NULL) {
|
|
if ((ch->ic_rupt.q_head = qq->q_next) == NULL)
|
|
ch->ic_rupt.q_tail = NULL;
|
|
if (qq->q_result == IP_ASYNC) {
|
|
(*qq->q_func)(qq); /* call device driver */
|
|
ipi_free(qq);
|
|
} else {
|
|
(*qq->q_func)(qq); /* call device driver */
|
|
}
|
|
}
|
|
|
|
#ifdef IPI_TRACE
|
|
if (q->q_resp) {
|
|
ipi_trace6(TRC_IPI_RUPT, q->q_csf, q->q_refnum,
|
|
((int *)q->q_resp)[0], ((int *)q->q_resp)[1],
|
|
((int *)q->q_resp)[2], ((int *)q->q_resp)[3]);
|
|
} else {
|
|
ipi_trace2(TRC_IPI_RUPT, q->q_csf, q->q_refnum);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* If the request is synchronous (using polling, not interrupts),
|
|
* this probably isn't really an interrupt, but an early presentation
|
|
* of an error or a simulated completion. Just queue the request
|
|
* so ipi_poll will find it.
|
|
*/
|
|
if (q->q_flag & IP_SYNC) {
|
|
q->q_next = NULL;
|
|
ch = q->q_chan;
|
|
if (ch->ic_rupt.q_head == NULL) {
|
|
ch->ic_rupt.q_head = q;
|
|
} else {
|
|
ch->ic_rupt.q_tail->q_next = q;
|
|
}
|
|
ch->ic_rupt.q_tail = q;
|
|
} else {
|
|
if (q->q_result == IP_ASYNC) {
|
|
(*q->q_func)(q); /* call device driver */
|
|
ipi_free(q);
|
|
} else {
|
|
(*q->q_func)(q); /* call device driver */
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reflect error detected by host adaptor layer to driver.
|
|
*/
|
|
void
|
|
ipi_complete(q, err)
|
|
register struct ipiq *q;
|
|
int err;
|
|
{
|
|
if (err)
|
|
q->q_result = err;
|
|
ipi_intr(q);
|
|
}
|
|
|
|
/*
|
|
* Setup request for asynchronous interrupt by channel. Channel will
|
|
* return the request through ipi_intr or the driver's poll routine.
|
|
* The routine returns non-zero on error.
|
|
* The request should be eventually freed by the channel driver.
|
|
*/
|
|
int
|
|
ipi_async(chan, q)
|
|
int chan; /* system channel number */
|
|
struct ipiq *q; /* request containing asynchronous response */
|
|
{
|
|
struct ipi_chan *ch;
|
|
struct ipi_slave *sl;
|
|
u_int slave;
|
|
|
|
if (chan >= ipi_nchan || (ch = ipi_chan[chan]) == NULL) {
|
|
printf("ipi_async: IPI channel %x not configured\n", chan);
|
|
return -1;
|
|
}
|
|
q->q_chan = ch; /* setup channel pointer so ipi_free works */
|
|
q->q_result = IP_ASYNC;
|
|
q->q_csf = IPI_MAKE_ADDR(chan, slave = q->q_resp->hdr_slave,
|
|
q->q_resp->hdr_facility);
|
|
if (slave < IPI_NSLAVE) {
|
|
sl = &ch->ic_slave[slave];
|
|
q->q_ctlr = sl->ic_ctlr;
|
|
q->q_func = sl->ic_async;
|
|
} else {
|
|
printf("ipi_async: ipi %x slave number out of range\n",
|
|
q->q_csf);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Wait synchronously for a completion interrupt from a command.
|
|
* Interrupts for the same unit or controller are passed back to the
|
|
* driver. All other interrupts received are held on a queue.
|
|
* Always called from the process level with IPI interrupts masked.
|
|
*
|
|
* Returns non-zero when the request completes.
|
|
* Returns zero if the timeout elapses first.
|
|
*/
|
|
ipi_poll(qq, time_out)
|
|
struct ipiq *qq; /* the request being polled */
|
|
long time_out; /* approx. time limit in microseconds */
|
|
{
|
|
register struct ipi_chan *ch;
|
|
register struct ipiq *q;
|
|
register struct ipiq *pq;
|
|
register int slave, fac, addr;
|
|
register struct ipi3header *ip;
|
|
|
|
ch = qq->q_chan;
|
|
ip = qq->q_cmd;
|
|
fac = IPI_MAKE_ADDR(0, ip->hdr_slave, ip->hdr_facility);
|
|
slave = IPI_MAKE_ADDR(0, ip->hdr_slave, IPI_NO_ADDR);
|
|
|
|
/*
|
|
* First see if the interrupt or any others for the controller or
|
|
* the same device was queued during an earlier poll or by an
|
|
* error during ipi_start.
|
|
*/
|
|
pq = NULL;
|
|
for (q = ch->ic_rupt.q_head; q != NULL; pq = q, q = q->q_next) {
|
|
ip = q->q_cmd;
|
|
addr = IPI_MAKE_ADDR(0, ip->hdr_slave, ip->hdr_facility);
|
|
if (addr == slave || addr == fac) {
|
|
if (pq != NULL)
|
|
pq->q_next = q->q_next;
|
|
else
|
|
ch->ic_rupt.q_head = q->q_next;
|
|
if (ch->ic_rupt.q_tail == q)
|
|
ch->ic_rupt.q_tail = q;
|
|
if (q->q_result == IP_ASYNC) {
|
|
(*q->q_func)(q); /* call device driver */
|
|
ipi_free(q);
|
|
} else {
|
|
(*q->q_func)(q); /* call device driver */
|
|
}
|
|
if (qq == q) {
|
|
return (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
time_out /= 0x1000; /* approx. time per loop */
|
|
time_out++;
|
|
while (time_out-- >= 0) {
|
|
/*
|
|
* Call the channel driver's poll routine.
|
|
*/
|
|
if ((q = (ch->ic_driver->id_poll)(qq)) != NULL) {
|
|
#ifdef IPI_TRACE
|
|
if (q->q_resp) {
|
|
ipi_trace6(TRC_IPI_RUPT, q->q_csf, q->q_refnum,
|
|
((int *)q->q_resp)[0],
|
|
((int *)q->q_resp)[1],
|
|
((int *)q->q_resp)[2],
|
|
((int *)q->q_resp)[3]);
|
|
} else {
|
|
ipi_trace2(TRC_IPI_RUPT, q->q_csf, q->q_refnum);
|
|
}
|
|
#endif
|
|
ip = q->q_cmd;
|
|
addr = IPI_MAKE_ADDR(0,ip->hdr_slave,ip->hdr_facility);
|
|
if (addr == slave || addr == fac) {
|
|
if (q->q_result == IP_ASYNC) {
|
|
(*q->q_func)(q); /* call device driver */
|
|
ipi_free(q);
|
|
} else {
|
|
(*q->q_func)(q); /* call device driver */
|
|
}
|
|
if (qq == q)
|
|
return (1);
|
|
} else {
|
|
/*
|
|
* The interrupt is not for the desired request,
|
|
* so just queue it on the channel.
|
|
*/
|
|
q->q_next = NULL;
|
|
if (ch->ic_rupt.q_head)
|
|
ch->ic_rupt.q_tail->q_next = q;
|
|
else
|
|
ch->ic_rupt.q_tail =
|
|
ch->ic_rupt.q_head = q;
|
|
}
|
|
}
|
|
DELAY(0x1000);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Register channel driver.
|
|
*
|
|
* Called during initialization to set up the IPI layer to call channel driver
|
|
* whenever requests come in for the channel.
|
|
*/
|
|
ipi_channel(chan, slave, dp, arg, intpri)
|
|
u_int chan; /* system channel number */
|
|
u_int slave; /* slave number (IPI_NO_ADDR for all) */
|
|
struct ipi_driver *dp; /* driver routines */
|
|
int arg; /* argument to pass driver routines */
|
|
int intpri; /* interrupt priority */
|
|
{
|
|
struct ipi_chan **new_chan;
|
|
struct ipi_chan *ch;
|
|
int i;
|
|
int j;
|
|
|
|
ipi_init();
|
|
/*
|
|
* If not enough channel pointers have been allocated, allocate them.
|
|
*/
|
|
if (ipi_nchan <= chan) {
|
|
i = chan + CHANSET - ipi_nchan % CHANSET;
|
|
new_chan = (struct ipi_chan **)
|
|
kmem_zalloc((u_int)i * sizeof(struct ipi_chan *));
|
|
if (new_chan == NULL) {
|
|
printf("ipi_channel: No memory to add channel %d\n",
|
|
chan);
|
|
return -1;
|
|
}
|
|
/*
|
|
* copy existing channel pointers.
|
|
*/
|
|
for (j = 0; j < ipi_nchan; j++)
|
|
new_chan[j] = ipi_chan[j];
|
|
/*
|
|
* free old pointers
|
|
*/
|
|
kmem_free((caddr_t)ipi_chan,
|
|
(u_int)ipi_nchan * sizeof(struct ipi_chan *));
|
|
ipi_chan = new_chan;
|
|
ipi_nchan = i;
|
|
}
|
|
|
|
/*
|
|
* Return error if slave is already defined.
|
|
*/
|
|
if ((ch = ipi_chan[chan]) != NULL && (slave == IPI_NO_ADDR
|
|
|| ch->ic_slave[slave].ic_arg != 0
|
|
|| ch->ic_driver != dp))
|
|
{
|
|
printf("ipi_channel: channel %d already defined\n", chan);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* setup new channel.
|
|
*/
|
|
if (ch == NULL) {
|
|
ipi_chan[chan] = ch =
|
|
(struct ipi_chan *)kmem_zalloc(sizeof(struct ipi_chan));
|
|
if (ch == NULL) {
|
|
printf("ipi_channel: no memory to alloc channel %d\n",
|
|
chan);
|
|
return -1;
|
|
}
|
|
ch->ic_chan = chan; /* system channel number */
|
|
ch->ic_driver = dp;
|
|
if (slave != IPI_NO_ADDR)
|
|
ch->ic_flags |= IC_VIRT_CHAN;
|
|
}
|
|
|
|
/*
|
|
* Setup specified slave or all slaves on the channel.
|
|
*/
|
|
for (i = 0; i < IPI_NSLAVE; i++) {
|
|
if (slave == IPI_NO_ADDR || slave == i) {
|
|
ch->ic_slave[i].ic_arg = arg;
|
|
ch->ic_slave[i].ic_intpri = intpri;
|
|
}
|
|
}
|
|
|
|
if (intpri > ipi_pri)
|
|
ipi_pri = intpri;
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* Free channel.
|
|
*
|
|
* This is used to undo the declaration of a channel after its configuration
|
|
* routines (probe/slave/attach) have failed.
|
|
*/
|
|
ipi_free_chan(chan)
|
|
int chan;
|
|
{
|
|
register struct ipi_chan *ch;
|
|
|
|
if (chan >= ipi_nchan || (ch = ipi_chan[chan]) == NULL) {
|
|
printf("ipi_free_chan: channel %d not allocated\n", chan);
|
|
return 0;
|
|
}
|
|
printf("ipi channel %x deleted\n", chan);
|
|
|
|
kmem_free((caddr_t)ch, sizeof(struct ipi_chan));
|
|
ipi_chan[chan] = NULL;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Parse response.
|
|
*
|
|
* Call functions in table for each parameter matched.
|
|
* Align parameters apropriately.
|
|
*/
|
|
void
|
|
ipi_parse_resp(q, table, arg)
|
|
struct ipiq *q;
|
|
struct ipi_resp_table *table;
|
|
caddr_t arg; /* argument for parsing functions */
|
|
{
|
|
register struct ipi_resp_table *rtp; /* response table pointer */
|
|
register struct ipi3resp *rp;
|
|
u_char buf[RESP_BUF_LEN];
|
|
register u_char *cp;
|
|
int id; /* parameter id */
|
|
int plen; /* parameter length */
|
|
int rlen; /* response length */
|
|
|
|
rp = q->q_resp;
|
|
rlen = rp->hdr_pktlen + sizeof(rp->hdr_pktlen)
|
|
- sizeof(struct ipi3resp);
|
|
cp = (u_char *)rp + sizeof(struct ipi3resp);
|
|
|
|
for (; rlen > 0; cp += plen) {
|
|
if ((plen = *cp + 1) > rlen) {
|
|
break;
|
|
}
|
|
rlen -= plen;
|
|
|
|
if (plen <= 1)
|
|
continue;
|
|
/*
|
|
* Find parameter ID in the table.
|
|
* Stop when reaching zero or matching parameter.
|
|
*/
|
|
id = cp[1];
|
|
for (rtp = table; rtp->rt_parm_id != 0
|
|
&& rtp->rt_parm_id != id; rtp++)
|
|
;
|
|
if (rtp->rt_func == NULL)
|
|
continue;
|
|
/*
|
|
* Check alignment if the parameter contains more than
|
|
* just the length and parameter ID.
|
|
*/
|
|
if (plen > 2 && (((int)cp + 2) % sizeof(long)) != 0) {
|
|
if (plen - 2 > sizeof(buf)) {
|
|
panic("ipi_parse_resp: buffer too short");
|
|
}
|
|
bcopy((caddr_t)cp+2, (caddr_t)buf, (u_int)plen-2);
|
|
(*rtp->rt_func)(q, id, buf, plen-2, arg);
|
|
} else {
|
|
(*rtp->rt_func)(q, id, cp+2, plen-2, arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print IPI parameters from response or command packet for debugging.
|
|
*/
|
|
static void
|
|
ipi_print_parms(bp, cp)
|
|
u_char *bp; /* start of packet (for length and offset of parms) */
|
|
u_char *cp; /* pointer to start of paramters */
|
|
{
|
|
int len; /* length remaining in packet */
|
|
int plen; /* length of parameter */
|
|
int col; /* column number */
|
|
|
|
/*
|
|
* Length is in first short, and doesn't include the 2 bytes for the
|
|
* length itself. Subtract out the header length.
|
|
*/
|
|
len = ((struct ipi3header *)bp)->hdr_pktlen + sizeof(u_short) - (cp-bp);
|
|
|
|
/*
|
|
* print header for parameters
|
|
*/
|
|
if (len > 0)
|
|
printf("\n addr len ID 1 2 3 4 5 6 7 8 9 a b c d e f 10\n");
|
|
|
|
while (len > 0) {
|
|
plen = *cp + 1; /* plen includes length byte */
|
|
if (plen > len) {
|
|
printf("Parameter length error:\n");
|
|
plen = len;
|
|
}
|
|
len -= plen;
|
|
printf(" %4x : ", cp - bp);
|
|
col = -2; /* column number for length */
|
|
while (plen-- > 0) {
|
|
if (col++ >= 0x10) {
|
|
printf("\n %4x : ", cp - bp);
|
|
col = 1;
|
|
}
|
|
printf("%2x ", *cp++);
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* Print command packet for debugging and errors.
|
|
*/
|
|
ipi_print_cmd(chan, ip, message)
|
|
u_int chan;
|
|
struct ipi3header *ip;
|
|
char *message;
|
|
{
|
|
if (ip == NULL) {
|
|
printf("%s: channel %x, null command\n", message, chan);
|
|
return;
|
|
}
|
|
printf("%s: channel %x slave %x facility %x\n", message, chan,
|
|
ip->hdr_slave, ip->hdr_facility);
|
|
printf("IPI command:\n len %x refnum %x opcode %2x mod %2x\n",
|
|
ip->hdr_pktlen, ip->hdr_refnum, ip->hdr_opcode, ip->hdr_mods);
|
|
ipi_print_parms((u_char *)ip, (u_char *)ip + sizeof(struct ipi3header));
|
|
}
|
|
|
|
|
|
/*
|
|
* Print response for debugging and errors.
|
|
*/
|
|
ipi_print_resp(chan, rp, message)
|
|
u_int chan;
|
|
struct ipi3resp *rp;
|
|
char *message;
|
|
{
|
|
if (rp == NULL) {
|
|
printf("%s: channel %x, null response\n", message, chan);
|
|
return;
|
|
}
|
|
printf("%s: channel %x slave %x facility %x\n", message, chan,
|
|
rp->hdr_slave, rp->hdr_facility);
|
|
printf("IPI Response:\n len %x refnum %x opcode %2x mod %2x stat %4x\n",
|
|
rp->hdr_pktlen, rp->hdr_refnum, rp->hdr_opcode, rp->hdr_mods,
|
|
rp->hdr_maj_stat);
|
|
|
|
ipi_print_parms((u_char *)rp, (u_char *)rp + sizeof(struct ipi3resp));
|
|
}
|
|
|
|
|
|
/*
|
|
* Find the next available command reference number and assign it to
|
|
* this command. Returns -1 if no free spot found.
|
|
*/
|
|
void
|
|
assign_refnum(q)
|
|
struct ipiq *q;
|
|
{
|
|
register int i;
|
|
struct ipiq **lp;
|
|
|
|
if (q->q_refnum != 0) {
|
|
printf("ipi: assign_refnum: q %x already has refnum %d\n",
|
|
q, q->q_refnum);
|
|
}
|
|
|
|
/*
|
|
* Find unused reference number.
|
|
*/
|
|
i = ipi_refnum;
|
|
for (lp = &ipi_ipiq_lookup[i]; *lp != NULL; ) {
|
|
++lp;
|
|
if (++i >= ipi_lookup_len) {
|
|
i = 1;
|
|
lp = &ipi_ipiq_lookup[1];
|
|
}
|
|
}
|
|
|
|
*lp = q;
|
|
q->q_refnum = i;
|
|
q->q_cmd->hdr_refnum = i;
|
|
|
|
/*
|
|
* setup next search start value.
|
|
*/
|
|
ipi_refnum = i + 1; /* where to start next */
|
|
if (ipi_refnum >= ipi_lookup_len)
|
|
ipi_refnum = 1;
|
|
}
|
|
|
|
/*
|
|
* Remove command reference number without freeing request.
|
|
*/
|
|
void
|
|
free_refnum(q)
|
|
register struct ipiq *q;
|
|
{
|
|
register int refnum;
|
|
register struct ipiq **lp;
|
|
|
|
if ((refnum = q->q_refnum) != 0) {
|
|
lp = &ipi_ipiq_lookup[refnum];
|
|
if (*lp != q) {
|
|
printf("free_refnum: q %x refnum %x lookup %x\n",
|
|
q, refnum, *lp);
|
|
panic("free_refnum: freeing wrong reference number");
|
|
} else {
|
|
*lp = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Lookup ipiq from command reference number.
|
|
*/
|
|
struct ipiq *
|
|
ipi_lookup(refnum)
|
|
u_short refnum;
|
|
{
|
|
register struct ipiq *q;
|
|
|
|
if (refnum >= ipi_lookup_len)
|
|
return NULL;
|
|
q = ipi_ipiq_lookup[refnum];
|
|
if (q != NULL && q->q_refnum != refnum) {
|
|
printf("ipi_lookup: found wrong command for refnum\n");
|
|
printf("ipi_lookup: looking for %x, found %x q %x\n",
|
|
refnum, q->q_refnum, q);
|
|
q = NULL;
|
|
}
|
|
return q;
|
|
}
|
|
|
|
/*
|
|
* Lookup ipiq from command I/O address.
|
|
*/
|
|
struct ipiq *
|
|
ipi_lookup_cmdaddr(io_addr)
|
|
caddr_t io_addr;
|
|
{
|
|
register struct ipiq *q;
|
|
register struct ipiq_chunk *qcp;
|
|
|
|
for (qcp = ipiq_chunk_list.qc_next; qcp != &ipiq_chunk_list;
|
|
qcp = qcp->qc_next)
|
|
{
|
|
for (q = qcp->qc_ipiqs; q < qcp->qc_end; q++)
|
|
if (q->q_cmd_ioaddr == io_addr)
|
|
return q;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Set timeout value for channel (in seconds).
|
|
* returns old timeout value, or -1 if error.
|
|
*
|
|
* The timeout is for all facilities on a slave.
|
|
*/
|
|
ipi_timeout(csf, new_time)
|
|
int csf; /* channel/slave/facility address */
|
|
int new_time;
|
|
{
|
|
int i;
|
|
int old_time;
|
|
struct ipi_chan *ch;
|
|
|
|
if ((i = IPI_CHAN(csf)) >= ipi_nchan || (ch = ipi_chan[i]) == NULL)
|
|
return -1;
|
|
if ((i = IPI_SLAVE(csf)) >= IPI_NSLAVE)
|
|
return -1;
|
|
if (new_time < 0)
|
|
return -1;
|
|
old_time = ch->ic_slave[i].ic_time;
|
|
ch->ic_slave[i].ic_time = new_time;
|
|
return old_time;
|
|
}
|
|
|
|
/*
|
|
* Check for hung requests.
|
|
*/
|
|
static void
|
|
ipi_watch()
|
|
{
|
|
register struct ipiq_chunk *qcp;
|
|
register struct ipiq *q;
|
|
int time_since_boot; /* time since boot in seconds */
|
|
int s;
|
|
|
|
time_since_boot = time.tv_sec - boottime.tv_sec;
|
|
|
|
for (qcp = ipiq_chunk_list.qc_next; qcp != &ipiq_chunk_list;
|
|
qcp = qcp->qc_next)
|
|
{
|
|
for (q = qcp->qc_ipiqs; q < qcp->qc_end; q++) {
|
|
if (q->q_time <= 0 || q->q_result != IP_INPROGRESS)
|
|
continue;
|
|
if (q->q_time < time_since_boot) {
|
|
/*
|
|
* It looks late. mask and check again.
|
|
*/
|
|
s = spl_ipi();
|
|
if (q->q_time >= 0 && q->q_time < time_since_boot
|
|
&& q->q_result == IP_INPROGRESS)
|
|
{
|
|
printf("ipi %x: missing interrupt. refnum %x\n",
|
|
q->q_csf, q->q_refnum);
|
|
q->q_result = IP_MISSING;
|
|
q->q_time = ipi_retry_timeout;
|
|
(*q->q_func)(q);
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
}
|
|
}
|
|
timeout((int (*)())ipi_watch, (caddr_t)NULL, IPI_WATCH_TIME);
|
|
}
|
|
|
|
/*
|
|
* Retry. Use original IPIQ to re-issue command.
|
|
* Change the command reference number to make it possible to detect
|
|
* problems associated with the original request coming back.
|
|
*/
|
|
void
|
|
ipi_retry(q)
|
|
register struct ipiq *q; /* request which had failed */
|
|
{
|
|
if (q->q_retry++ >= ipi_retry_limit) {
|
|
ipi_complete(q, IP_RETRY_FAILED);
|
|
} else {
|
|
/*
|
|
* Re-assign command reference number.
|
|
* XXX - channel driver should be notified of refnum change.
|
|
*/
|
|
free_refnum(q);
|
|
q->q_refnum = 0;
|
|
ipi_setup_start(q, q->q_flag, q->q_func);
|
|
ipi_trace6(TRC_IPI_RETRY, q->q_csf, q->q_refnum,
|
|
((long *)q->q_cmd)[0], ((long *)q->q_cmd)[1],
|
|
((long *)q->q_cmd)[2], ((long *)q->q_cmd)[3]);
|
|
(*q->q_chan_func)(q);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Ask device driver to retry all requests for a particular controller.
|
|
* The request passed as an argument is not affected.
|
|
* If slave is passed as IPI_NO_ADDR, all slaves on the channel are affected.
|
|
*
|
|
* This is necessary during controller or channel resets.
|
|
*/
|
|
void
|
|
ipi_retry_slave(qq, slave)
|
|
struct ipiq *qq;
|
|
int slave;
|
|
{
|
|
register struct ipi_chan *ch;
|
|
register struct ipiq_chunk *qcp;
|
|
register struct ipiq *q;
|
|
|
|
ch = qq->q_chan;
|
|
|
|
for (qcp = ipiq_chunk_list.qc_next; qcp != &ipiq_chunk_list;
|
|
qcp = qcp->qc_next)
|
|
{
|
|
for (q = qcp->qc_ipiqs; q < qcp->qc_end; q++) {
|
|
if (q->q_chan == ch && q != qq &&
|
|
(slave == IPI_NO_ADDR
|
|
|| IPI_SLAVE(q->q_csf) == slave)
|
|
&& (q->q_result == IP_INPROGRESS
|
|
|| q->q_result == IP_MISSING))
|
|
{
|
|
if (q->q_flag & IP_NO_RETRY)
|
|
ipi_complete(q, IP_RETRY_FAILED);
|
|
else
|
|
ipi_complete(q, IP_RETRY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allocate resources for future requests.
|
|
* Called during controller initialization by device driver.
|
|
*/
|
|
int
|
|
ipi_alloc(csf, count, size)
|
|
int csf; /* IPI address of controller */
|
|
int count; /* upper bound on number of structs needed */
|
|
int size; /* packet size required */
|
|
{
|
|
register struct ipi_chan *ch;
|
|
register struct ipi_slave *sl;
|
|
register u_int chan;
|
|
register u_int slave;
|
|
|
|
chan = IPI_CHAN(csf);
|
|
slave = IPI_SLAVE(csf);
|
|
if (chan >= ipi_nchan || slave >= IPI_NSLAVE
|
|
|| (ch = ipi_chan[chan]) == NULL
|
|
|| ((sl = &ch->ic_slave[slave])->ic_arg) == 0)
|
|
{
|
|
printf("ipi_alloc %x: invalid IPI address\n", csf);
|
|
return 0;
|
|
}
|
|
return (*ch->ic_driver->id_alloc)(sl->ic_arg, slave, count, size);
|
|
}
|
|
|
|
|
|
/*
|
|
* Common Queue free list management for IPI channels.
|
|
*/
|
|
struct ipiq *
|
|
ipiq_get(free_list, size, callback)
|
|
struct ipiq_free_list *free_list;
|
|
int size; /* length of command packet desired. */
|
|
int (*callback)(); /* routine to call when ipiq available */
|
|
{
|
|
struct ipiq_free_list *flp;
|
|
struct ipiq *q;
|
|
|
|
/*
|
|
* Find non-empty list that's big enough. Use callback or sleep
|
|
* if no requests are found.
|
|
*/
|
|
for (;;) {
|
|
for (flp = free_list; flp->fl_size > 0; flp++)
|
|
if (size <= flp->fl_size && flp->fl_list.q_head)
|
|
break;
|
|
if (flp->fl_size > 0)
|
|
break;
|
|
ipiq_stats.i_runout++;
|
|
if (callback == IPI_SLEEP) {
|
|
ipiq_wait();
|
|
} else if (callback == IPI_NULL) {
|
|
return NULL;
|
|
} else {
|
|
ipiq_callback(callback);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
q = flp->fl_list.q_head;
|
|
flp->fl_list.q_head = q->q_next;
|
|
|
|
/*
|
|
* Clean up the IPIQ for debugging -- delete some later for performance.
|
|
*/
|
|
q->q_func = NULL;
|
|
q->q_dev = NULL;
|
|
q->q_ctlr = NULL;
|
|
q->q_buf = NULL;
|
|
q->q_flag = 0;
|
|
q->q_next = NULL;
|
|
q->q_chan = NULL;
|
|
/*
|
|
* Changed the q_result from IP_INPROGRESS to IP_SETUP.
|
|
* The command is not yet issued/inprogress, and we do not want
|
|
* ipi_watch to check this command.
|
|
*/
|
|
q->q_result = IP_SETUP;
|
|
q->q_resp = NULL;
|
|
q->q_respq = NULL;
|
|
q->q_time = ipi_default_timeout;
|
|
q->q_retry = 0;
|
|
q->q_cmd = (struct ipi3header *)q->q_cmd_pkt;
|
|
q->q_cmd->hdr_pktlen = 0;
|
|
return q;
|
|
}
|
|
|
|
|
|
/*
|
|
* Free request
|
|
*/
|
|
void
|
|
ipiq_free(flp, q)
|
|
struct ipiq_free_list *flp;
|
|
register struct ipiq *q;
|
|
{
|
|
int size;
|
|
|
|
/*
|
|
* Find the right list. There should be an exact match.
|
|
*/
|
|
size = q->q_cmd_len;
|
|
for (; flp->fl_size < size; flp++)
|
|
;
|
|
ASSERT(flp->fl_size == size);
|
|
|
|
q->q_time = 0;
|
|
q->q_next = NULL;
|
|
q->q_result = IP_FREE;
|
|
|
|
/*
|
|
* If panicking, put free queue back on front of list so dump
|
|
* doesn't use all of them. Otherwise, put queue on end of list
|
|
* to preserve evidence in case of crash.
|
|
*/
|
|
if (panicstr) {
|
|
q->q_next = flp->fl_list.q_head;
|
|
if (flp->fl_list.q_head == NULL)
|
|
flp->fl_list.q_tail = q;
|
|
flp->fl_list.q_head = q;
|
|
} else {
|
|
if (flp->fl_list.q_head == NULL) {
|
|
flp->fl_list.q_head = q;
|
|
flp->fl_list.q_tail = q;
|
|
} else {
|
|
flp->fl_list.q_tail->q_next = q;
|
|
flp->fl_list.q_tail = q;
|
|
}
|
|
}
|
|
if (ipiq_free_wait > 0) {
|
|
ipiq_free_wait--;
|
|
wakeup((caddr_t)&ipiq_free_wait);
|
|
}
|
|
if (ipiq_callback_len)
|
|
(void) ipiq_call();
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a number of ipiqs to the given free list.
|
|
* The ipiqs in the chunk have been linked to each other front to back.
|
|
*/
|
|
void
|
|
ipiq_free_chunk(flp, qcp)
|
|
struct ipiq_free_list *flp; /* free list */
|
|
register struct ipiq_chunk *qcp; /* pointer to "chunk" */
|
|
{
|
|
struct ipiq *q;
|
|
|
|
q = qcp->qc_end - 1; /* point to last q in chunk */
|
|
q->q_next = NULL;
|
|
if (flp->fl_list.q_head == NULL) {
|
|
flp->fl_list.q_head = qcp->qc_ipiqs;
|
|
} else {
|
|
flp->fl_list.q_tail->q_next = qcp->qc_ipiqs;
|
|
}
|
|
flp->fl_list.q_tail = q;
|
|
|
|
/*
|
|
* link on to system-wide list of all IPIQs.
|
|
*/
|
|
qcp->qc_next = ipiq_chunk_list.qc_next;
|
|
ipiq_chunk_list.qc_next = qcp;
|
|
}
|
|
|
|
/*
|
|
* Wait for there to be more free queue elements.
|
|
*/
|
|
ipiq_wait()
|
|
{
|
|
ipiq_free_wait++;
|
|
(void) sleep((caddr_t)&ipiq_free_wait, PRIBIO);
|
|
}
|
|
|
|
/*
|
|
* Schedule callback when ipiq is available.
|
|
*/
|
|
static void
|
|
ipiq_callback(fp)
|
|
int (*fp)();
|
|
{
|
|
struct ipi_callback *p;
|
|
|
|
p = ipiq_cb_out;
|
|
while (p != ipiq_cb_in) {
|
|
if (p->ic_fcn == fp) {
|
|
return;
|
|
}
|
|
if (++p >= &ipiq_callback_list[IPIQ_CBQ_LEN])
|
|
p = ipiq_callback_list;
|
|
}
|
|
if (++ipiq_callback_len > ipiq_stats.i_max_qlen)
|
|
ipiq_stats.i_max_qlen++;
|
|
p = ipiq_cb_in;
|
|
p->ic_fcn = fp;
|
|
if (++p >= &ipiq_callback_list[IPIQ_CBQ_LEN])
|
|
p = ipiq_callback_list;
|
|
ipiq_cb_in = p;
|
|
ASSERT(ipiq_cb_in != ipiq_cb_out);
|
|
}
|
|
|
|
/*
|
|
* Call all callback routines currently in the list.
|
|
* New callbacks can be added by the ones called, but we won't call them.
|
|
*/
|
|
static int
|
|
ipiq_call()
|
|
{
|
|
struct ipi_callback *p;
|
|
struct ipi_callback *end;
|
|
int (*fp)();
|
|
|
|
p = ipiq_cb_out;
|
|
end = ipiq_cb_in;
|
|
while (ipiq_callback_len > 0) {
|
|
if (++p >= &ipiq_callback_list[IPIQ_CBQ_LEN])
|
|
p = ipiq_callback_list;
|
|
if (p == end)
|
|
break;
|
|
ipiq_cb_out = p;
|
|
ipiq_callback_len--;
|
|
fp = p->ic_fcn;
|
|
p->ic_fcn = NULL;
|
|
(void) (*fp)((caddr_t) 0);
|
|
}
|
|
}
|