/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $Id: fore_command.c,v 1.4 1998/12/04 22:54:45 archie Exp $
*
*/
/*
* FORE Systems 200-Series Adapter Support
* ---------------------------------------
*
* Command queue management
*
*/
#include <dev/hfa/fore_include.h>
#ifndef lint
__RCSID("@(#) $Id: fore_command.c,v 1.4 1998/12/04 22:54:45 archie Exp $");
#endif
/*
* Local variables
*/
static struct t_atm_cause fore_cause = {
T_ATM_ITU_CODING,
T_ATM_LOC_USER,
T_ATM_CAUSE_TEMPORARY_FAILURE,
{0, 0, 0, 0}
};
/*
* Allocate Command Queue Data Structures
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* 0 allocations successful
* else allocation failed
*/
int
fore_cmd_allocate(fup)
Fore_unit *fup;
{
caddr_t memp;
/*
* Allocate non-cacheable memory for command status words
*/
memp = atm_dev_alloc(sizeof(Q_status) * CMD_QUELEN,
QSTAT_ALIGN, ATM_DEV_NONCACHE);
if (memp == NULL) {
return (1);
}
fup->fu_cmd_stat = (Q_status *) memp;
memp = DMA_GET_ADDR(fup->fu_cmd_stat, sizeof(Q_status) * CMD_QUELEN,
QSTAT_ALIGN, ATM_DEV_NONCACHE);
if (memp == NULL) {
return (1);
}
fup->fu_cmd_statd = (Q_status *) memp;
/*
* Allocate memory for statistics buffer
*/
memp = atm_dev_alloc(sizeof(Fore_stats), FORE_STATS_ALIGN, 0);
if (memp == NULL) {
return (1);
}
fup->fu_stats = (Fore_stats *) memp;
#ifdef FORE_PCI
/*
* Allocate memory for PROM buffer
*/
memp = atm_dev_alloc(sizeof(Fore_prom), FORE_PROM_ALIGN, 0);
if (memp == NULL) {
return (1);
}
fup->fu_prom = (Fore_prom *) memp;
#endif
return (0);
}
/*
* Command Queue Initialization
*
* Allocate and initialize the host-resident command queue structures
* and then initialize the CP-resident queue structures.
*
* Called at interrupt level.
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* none
*/
void
fore_cmd_initialize(fup)
Fore_unit *fup;
{
Aali *aap = fup->fu_aali;
Cmd_queue *cqp;
H_cmd_queue *hcp;
Q_status *qsp;
Q_status *qsp_dma;
int i;
/*
* Point to CP-resident command queue
*/
cqp = (Cmd_queue *)(fup->fu_ram + CP_READ(aap->aali_cmd_q));
/*
* Point to host-resident command queue structures
*/
hcp = fup->fu_cmd_q;
qsp = fup->fu_cmd_stat;
qsp_dma = fup->fu_cmd_statd;
/*
* Loop thru all queue entries and do whatever needs doing
*/
for (i = 0; i < CMD_QUELEN; i++) {
/*
* Set queue status word to free
*/
*qsp = QSTAT_FREE;
/*
* Set up host queue entry and link into ring
*/
hcp->hcq_cpelem = cqp;
hcp->hcq_status = qsp;
if (i == (CMD_QUELEN - 1))
hcp->hcq_next = fup->fu_cmd_q;
else
hcp->hcq_next = hcp + 1;
/*
* Now let the CP into the game
*/
cqp->cmdq_status = (CP_dma) CP_WRITE(qsp_dma);
/*
* Bump all queue pointers
*/
hcp++;
qsp++;
qsp_dma++;
cqp++;
}
/*
* Initialize queue pointers
*/
fup->fu_cmd_head = fup->fu_cmd_tail = fup->fu_cmd_q;
return;
}
/*
* Drain Command Queue
*
* This function will process and free all completed entries at the head
* of the command queue.
*
* May be called in interrupt state.
* Must be called with interrupts locked out.
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* none
*/
void
fore_cmd_drain(fup)
Fore_unit *fup;
{
H_cmd_queue *hcp;
Fore_vcc *fvp;
/*
* Process each completed entry
*/
while (*fup->fu_cmd_head->hcq_status & QSTAT_COMPLETED) {
hcp = fup->fu_cmd_head;
/*
* Process command completion
*/
switch (hcp->hcq_code) {
case CMD_ACT_VCCIN:
case CMD_ACT_VCCOUT:
fvp = hcp->hcq_arg;
if (*hcp->hcq_status & QSTAT_ERROR) {
/*
* VCC activation failed - just abort vcc
*/
if (fvp)
atm_cm_abort(fvp->fv_connvc,
&fore_cause);
fup->fu_pif.pif_cmderrors++;
} else {
/*
* Successful VCC activation
*/
if (fvp) {
fvp->fv_state = CVS_ACTIVE;
fup->fu_open_vcc++;
}
}
break;
case CMD_DACT_VCCIN:
case CMD_DACT_VCCOUT:
fvp = hcp->hcq_arg;
if (*hcp->hcq_status & QSTAT_ERROR) {
/*
* VCC dactivation failed - whine
*/
log(LOG_ERR,
"fore_cmd_drain: DACT failed, vcc=(%d,%d)\n",
fvp->fv_connvc->cvc_vcc->vc_vpi,
fvp->fv_connvc->cvc_vcc->vc_vci);
fup->fu_pif.pif_cmderrors++;
} else {
/*
* Successful VCC dactivation - so what?
*/
}
break;
case CMD_GET_STATS:
if (*hcp->hcq_status & QSTAT_ERROR) {
/*
* Couldn't get stats
*/
fup->fu_pif.pif_cmderrors++;
fup->fu_stats_ret = EIO;
} else {
/*
* Stats are now in unit buffer
*/
fup->fu_stats_ret = 0;
}
DMA_FREE_ADDR(fup->fu_stats, fup->fu_statsd,
sizeof(Fore_cp_stats), 0);
fup->fu_flags &= ~FUF_STATCMD;
/*
* Flush received stats data
*/
#ifdef VAC
if (vac)
vac_pageflush((addr_t)fup->fu_stats);
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
/*
* Little endian machines receives the stats in
* wrong byte order. Instead of swapping in user
* land, swap here so that everything going out
* of the kernel is in correct host order.
*/
{
u_long *bp = (u_long *)fup->fu_stats;
int loop;
for ( loop = 0; loop < sizeof(Fore_cp_stats)/
sizeof(long); loop++, bp++ )
*bp = ntohl(*bp);
}
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
/*
* Poke whoever is waiting on the stats
*/
wakeup((caddr_t)&fup->fu_stats);
break;
#ifdef FORE_PCI
case CMD_GET_PROM:
if (*hcp->hcq_status & QSTAT_ERROR) {
/*
* Couldn't get PROM data
*/
fup->fu_pif.pif_cmderrors++;
log(LOG_ERR,
"fore_cmd_drain: %s%d: GET_PROM failed\n",
fup->fu_pif.pif_name,
fup->fu_pif.pif_unit);
} else {
Fore_prom *fp = fup->fu_prom;
/*
* Flush received PROM data
*/
#ifdef VAC
if (vac)
vac_pageflush((addr_t)fp);
#endif
/*
* Copy PROM info into config areas
*/
KM_COPY(&fp->pr_mac[2],
&fup->fu_pif.pif_macaddr,
sizeof(struct mac_addr));
fup->fu_config.ac_macaddr =
fup->fu_pif.pif_macaddr;
snprintf(fup->fu_config.ac_hard_vers,
sizeof(fup->fu_config.ac_hard_vers),
"%ld.%ld.%ld",
(fp->pr_hwver >> 16) & 0xff,
(fp->pr_hwver >> 8) & 0xff,
fp->pr_hwver & 0xff);
fup->fu_config.ac_serial = fp->pr_serno;
}
DMA_FREE_ADDR(fup->fu_prom, fup->fu_promd,
sizeof(Fore_prom), 0);
break;
#endif /* FORE_PCI */
default:
log(LOG_ERR, "fore_cmd_drain: unknown command %ld\n",
hcp->hcq_code);
}
/*
* Mark this entry free for use and bump head pointer
* to the next entry in the queue
*/
*hcp->hcq_status = QSTAT_FREE;
fup->fu_cmd_head = hcp->hcq_next;
}
return;
}
/*
* Free Command Queue Data Structures
*
* Arguments:
* fup pointer to device unit structure
*
* Returns:
* none
*/
void
fore_cmd_free(fup)
Fore_unit *fup;
{
H_cmd_queue *hcp;
/*
* Deal with any commands left on the queue
*/
if (fup->fu_flags & CUF_INITED) {
while (*fup->fu_cmd_head->hcq_status != QSTAT_FREE) {
hcp = fup->fu_cmd_head;
switch (hcp->hcq_code) {
case CMD_GET_STATS:
/*
* Just in case someone is sleeping on this
*/
fup->fu_stats_ret = EIO;
wakeup((caddr_t)&fup->fu_stats);
break;
}
*hcp->hcq_status = QSTAT_FREE;
fup->fu_cmd_head = hcp->hcq_next;
}
}
/*
* Free the statistics buffer
*/
if (fup->fu_stats) {
atm_dev_free(fup->fu_stats);
fup->fu_stats = NULL;
}
#ifdef FORE_PCI
/*
* Free the PROM buffer
*/
if (fup->fu_prom) {
atm_dev_free(fup->fu_prom);
fup->fu_prom = NULL;
}
#endif
/*
* Free the status words
*/
if (fup->fu_cmd_stat) {
if (fup->fu_cmd_statd) {
DMA_FREE_ADDR(fup->fu_cmd_stat, fup->fu_cmd_statd,
sizeof(Q_status) * CMD_QUELEN,
ATM_DEV_NONCACHE);
}
atm_dev_free((volatile void *)fup->fu_cmd_stat);
fup->fu_cmd_stat = NULL;
fup->fu_cmd_statd = NULL;
}
return;
}