/* * * =================================== * 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. * * @(#) $FreeBSD: src/sys/dev/hfa/fore_command.c,v 1.6 1999/08/28 00:41:49 peter Exp $ * @(#) $DragonFly: src/sys/dev/atm/hfa/fore_command.c,v 1.3 2003/08/07 21:16:49 dillon Exp $ */ /* * FORE Systems 200-Series Adapter Support * --------------------------------------- * * Command queue management * */ #include "fore_include.h" /* * 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; }