diff options
Diffstat (limited to 'sys/dev/hfa/fore_buffer.c')
-rw-r--r-- | sys/dev/hfa/fore_buffer.c | 772 |
1 files changed, 772 insertions, 0 deletions
diff --git a/sys/dev/hfa/fore_buffer.c b/sys/dev/hfa/fore_buffer.c new file mode 100644 index 0000000..d8bdce4 --- /dev/null +++ b/sys/dev/hfa/fore_buffer.c @@ -0,0 +1,772 @@ +/* + * + * =================================== + * 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_buffer.c,v 1.6 1997/05/06 22:09:21 mks Exp $ + * + */ + +/* + * FORE Systems 200-Series Adapter Support + * --------------------------------------- + * + * Buffer Supply queue management + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: fore_buffer.c,v 1.6 1997/05/06 22:09:21 mks Exp $"; +#endif + +#include <dev/hfa/fore_include.h> + + +/* + * Local functions + */ +static void fore_buf_drain __P((Fore_unit *)); +static void fore_buf_supply_1s __P((Fore_unit *)); +static void fore_buf_supply_1l __P((Fore_unit *)); + + +/* + * Allocate Buffer Supply Queues Data Structures + * + * Here we are allocating memory for both Strategy 1 Small and Large + * structures contiguously. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * 0 allocations successful + * else allocation failed + */ +int +fore_buf_allocate(fup) + Fore_unit *fup; +{ + caddr_t memp; + + /* + * Allocate non-cacheable memory for buffer supply status words + */ + memp = atm_dev_alloc( + sizeof(Q_status) * (BUF1_SM_QUELEN + BUF1_LG_QUELEN), + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_stat = (Q_status *) memp; + fup->fu_buf1l_stat = ((Q_status *) memp) + BUF1_SM_QUELEN; + + memp = DMA_GET_ADDR(fup->fu_buf1s_stat, + sizeof(Q_status) * (BUF1_SM_QUELEN + BUF1_LG_QUELEN), + QSTAT_ALIGN, ATM_DEV_NONCACHE); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_statd = (Q_status *) memp; + fup->fu_buf1l_statd = ((Q_status *) memp) + BUF1_SM_QUELEN; + + /* + * Allocate memory for buffer supply descriptors + */ + memp = atm_dev_alloc(sizeof(Buf_descr) * + ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) + + (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)), + BUF_DESCR_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_desc = (Buf_descr *) memp; + fup->fu_buf1l_desc = ((Buf_descr *) memp) + + (BUF1_SM_QUELEN * BUF1_SM_ENTSIZE); + + memp = DMA_GET_ADDR(fup->fu_buf1s_desc, sizeof(Buf_descr) * + ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) + + (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)), + BUF_DESCR_ALIGN, 0); + if (memp == NULL) { + return (1); + } + fup->fu_buf1s_descd = (Buf_descr *) memp; + fup->fu_buf1l_descd = ((Buf_descr *) memp) + + (BUF1_SM_QUELEN * BUF1_SM_ENTSIZE); + + return (0); +} + + +/* + * Buffer Supply Queues Initialization + * + * Allocate and initialize the host-resident buffer supply 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_buf_initialize(fup) + Fore_unit *fup; +{ + Aali *aap = fup->fu_aali; + Buf_queue *cqp; + H_buf_queue *hbp; + Buf_descr *bdp; + Buf_descr *bdp_dma; + Q_status *qsp; + Q_status *qsp_dma; + int i; + + /* + * Initialize Strategy 1 Small Queues + */ + + /* + * Point to CP-resident buffer supply queue + */ + cqp = (Buf_queue *)(fup->fu_ram + CP_READ(aap->aali_buf1s_q)); + + /* + * Point to host-resident buffer supply queue structures + */ + hbp = fup->fu_buf1s_q; + qsp = fup->fu_buf1s_stat; + qsp_dma = fup->fu_buf1s_statd; + bdp = fup->fu_buf1s_desc; + bdp_dma = fup->fu_buf1s_descd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < BUF1_SM_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hbp->hbq_cpelem = cqp; + hbp->hbq_status = qsp; + hbp->hbq_descr = bdp; + hbp->hbq_descr_dma = bdp_dma; + if (i == (BUF1_SM_QUELEN - 1)) + hbp->hbq_next = fup->fu_buf1s_q; + else + hbp->hbq_next = hbp + 1; + + /* + * Now let the CP into the game + */ + cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hbp++; + qsp++; + qsp_dma++; + bdp += BUF1_SM_ENTSIZE; + bdp_dma += BUF1_SM_ENTSIZE; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_buf1s_head = fup->fu_buf1s_tail = fup->fu_buf1s_q; + + + /* + * Initialize Strategy 1 Large Queues + */ + + /* + * Point to CP-resident buffer supply queue + */ + cqp = (Buf_queue *)(fup->fu_ram + CP_READ(aap->aali_buf1l_q)); + + /* + * Point to host-resident buffer supply queue structures + */ + hbp = fup->fu_buf1l_q; + qsp = fup->fu_buf1l_stat; + qsp_dma = fup->fu_buf1l_statd; + bdp = fup->fu_buf1l_desc; + bdp_dma = fup->fu_buf1l_descd; + + /* + * Loop thru all queue entries and do whatever needs doing + */ + for (i = 0; i < BUF1_LG_QUELEN; i++) { + + /* + * Set queue status word to free + */ + *qsp = QSTAT_FREE; + + /* + * Set up host queue entry and link into ring + */ + hbp->hbq_cpelem = cqp; + hbp->hbq_status = qsp; + hbp->hbq_descr = bdp; + hbp->hbq_descr_dma = bdp_dma; + if (i == (BUF1_LG_QUELEN - 1)) + hbp->hbq_next = fup->fu_buf1l_q; + else + hbp->hbq_next = hbp + 1; + + /* + * Now let the CP into the game + */ + cqp->cq_status = (CP_dma) CP_WRITE(qsp_dma); + + /* + * Bump all queue pointers + */ + hbp++; + qsp++; + qsp_dma++; + bdp += BUF1_LG_ENTSIZE; + bdp_dma += BUF1_LG_ENTSIZE; + cqp++; + } + + /* + * Initialize queue pointers + */ + fup->fu_buf1l_head = fup->fu_buf1l_tail = fup->fu_buf1l_q; + + return; +} + + +/* + * Supply Buffers to CP + * + * This function will resupply the CP with buffers to be used to + * store incoming data. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_buf_supply(fup) + Fore_unit *fup; +{ + + /* + * First, clean out the supply queues + */ + fore_buf_drain(fup); + + /* + * Then, supply the buffers for each queue + */ + fore_buf_supply_1s(fup); + fore_buf_supply_1l(fup); + + return; +} + + +/* + * Supply Strategy 1 Small Buffers to CP + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_buf_supply_1s(fup) + Fore_unit *fup; +{ + H_buf_queue *hbp; + Buf_queue *cqp; + Buf_descr *bdp; + Buf_handle *bhp; + KBuffer *m; + int nvcc, nbuf, i; + + /* + * Figure out how many buffers we should be giving to the CP. + * We're basing this calculation on the current number of open + * VCCs thru this device, with certain minimum and maximum values + * enforced. This will then allow us to figure out how many more + * buffers we need to supply to the CP. This will be rounded up + * to fill a supply queue entry. + */ + nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC); + nbuf = nvcc * 4; + nbuf = MIN(nbuf, BUF1_SM_CPPOOL); + nbuf -= fup->fu_buf1s_cnt; + nbuf = roundup(nbuf, BUF1_SM_ENTSIZE); + + /* + * OK, now supply the buffers to the CP + */ + while (nbuf > 0) { + + /* + * Acquire a supply queue entry + */ + hbp = fup->fu_buf1s_tail; + if (!((*hbp->hbq_status) & QSTAT_FREE)) + break; + bdp = hbp->hbq_descr; + + /* + * Get a buffer for each descriptor in the queue entry + */ + for (i = 0; i < BUF1_SM_ENTSIZE; i++, bdp++) { + caddr_t cp; + + /* + * Get a small buffer + */ + KB_ALLOCPKT(m, BUF1_SM_SIZE, KB_F_NOWAIT, KB_T_DATA); + if (m == 0) { + break; + } + KB_HEADSET(m, BUF1_SM_DOFF); + + /* + * Point to buffer handle structure + */ + bhp = (Buf_handle *)((caddr_t)m + BUF1_SM_HOFF); + bhp->bh_type = BHT_S1_SMALL; + + /* + * Setup buffer descriptor + */ + bdp->bsd_handle = bhp; + KB_DATASTART(m, cp, caddr_t); + bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR( + cp, BUF1_SM_SIZE, BUF_DATA_ALIGN, 0); + if (bdp->bsd_buffer == NULL) { + /* + * Unable to assign dma address - free up + * this descriptor's buffer + */ + fup->fu_stats->st_drv.drv_bf_segdma++; + KB_FREEALL(m); + break; + } + + /* + * All set, so queue buffer (handle) + */ + ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq); + } + + /* + * If we we're not able to fill all the descriptors for + * an entry, free up what's been partially built + */ + if (i != BUF1_SM_ENTSIZE) { + + /* + * Clean up each used descriptor + */ + for (bdp = hbp->hbq_descr; i; i--, bdp++) { + + bhp = bdp->bsd_handle; + + DEQUEUE(bhp, Buf_handle, bh_qelem, + fup->fu_buf1s_bq); + + m = (KBuffer *) + ((caddr_t)bhp - BUF1_SM_HOFF); + KB_FREEALL(m); + } + break; + } + + /* + * Finally, we've got an entry ready for the CP. + * So claim the host queue entry and setup the CP-resident + * queue entry. The CP will (potentially) grab the supplied + * buffers when the descriptor pointer is set. + */ + fup->fu_buf1s_tail = hbp->hbq_next; + (*hbp->hbq_status) = QSTAT_PENDING; + cqp = hbp->hbq_cpelem; + cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma); + + /* + * Update counters, etc for supplied buffers + */ + fup->fu_buf1s_cnt += BUF1_SM_ENTSIZE; + nbuf -= BUF1_SM_ENTSIZE; + } + + return; +} + + +/* + * Supply Strategy 1 Large Buffers to CP + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_buf_supply_1l(fup) + Fore_unit *fup; +{ + H_buf_queue *hbp; + Buf_queue *cqp; + Buf_descr *bdp; + Buf_handle *bhp; + KBuffer *m; + int nvcc, nbuf, i; + + /* + * Figure out how many buffers we should be giving to the CP. + * We're basing this calculation on the current number of open + * VCCs thru this device, with certain minimum and maximum values + * enforced. This will then allow us to figure out how many more + * buffers we need to supply to the CP. This will be rounded up + * to fill a supply queue entry. + */ + nvcc = MAX(fup->fu_open_vcc, BUF_MIN_VCC); + nbuf = nvcc * 4 * RECV_MAX_SEGS; + nbuf = MIN(nbuf, BUF1_LG_CPPOOL); + nbuf -= fup->fu_buf1l_cnt; + nbuf = roundup(nbuf, BUF1_LG_ENTSIZE); + + /* + * OK, now supply the buffers to the CP + */ + while (nbuf > 0) { + + /* + * Acquire a supply queue entry + */ + hbp = fup->fu_buf1l_tail; + if (!((*hbp->hbq_status) & QSTAT_FREE)) + break; + bdp = hbp->hbq_descr; + + /* + * Get a buffer for each descriptor in the queue entry + */ + for (i = 0; i < BUF1_LG_ENTSIZE; i++, bdp++) { + caddr_t cp; + + /* + * Get a cluster buffer + */ + KB_ALLOCEXT(m, BUF1_LG_SIZE, KB_F_NOWAIT, KB_T_DATA); + if (m == 0) { + break; + } + KB_HEADSET(m, BUF1_LG_DOFF); + + /* + * Point to buffer handle structure + */ + bhp = (Buf_handle *)((caddr_t)m + BUF1_LG_HOFF); + bhp->bh_type = BHT_S1_LARGE; + + /* + * Setup buffer descriptor + */ + bdp->bsd_handle = bhp; + KB_DATASTART(m, cp, caddr_t); + bhp->bh_dma = bdp->bsd_buffer = (H_dma) DMA_GET_ADDR( + cp, BUF1_LG_SIZE, BUF_DATA_ALIGN, 0); + if (bdp->bsd_buffer == NULL) { + /* + * Unable to assign dma address - free up + * this descriptor's buffer + */ + fup->fu_stats->st_drv.drv_bf_segdma++; + KB_FREEALL(m); + break; + } + + /* + * All set, so queue buffer (handle) + */ + ENQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq); + } + + /* + * If we we're not able to fill all the descriptors for + * an entry, free up what's been partially built + */ + if (i != BUF1_LG_ENTSIZE) { + + /* + * Clean up each used descriptor + */ + for (bdp = hbp->hbq_descr; i; i--, bdp++) { + bhp = bdp->bsd_handle; + + DEQUEUE(bhp, Buf_handle, bh_qelem, + fup->fu_buf1l_bq); + + m = (KBuffer *) + ((caddr_t)bhp - BUF1_LG_HOFF); + KB_FREEALL(m); + } + break; + } + + /* + * Finally, we've got an entry ready for the CP. + * So claim the host queue entry and setup the CP-resident + * queue entry. The CP will (potentially) grab the supplied + * buffers when the descriptor pointer is set. + */ + fup->fu_buf1l_tail = hbp->hbq_next; + (*hbp->hbq_status) = QSTAT_PENDING; + cqp = hbp->hbq_cpelem; + cqp->cq_descr = (CP_dma) CP_WRITE((u_long)hbp->hbq_descr_dma); + + /* + * Update counters, etc for supplied buffers + */ + fup->fu_buf1l_cnt += BUF1_LG_ENTSIZE; + nbuf -= BUF1_LG_ENTSIZE; + } + + return; +} + + +/* + * Drain Buffer Supply Queues + * + * This function will free all completed entries at the head of each + * buffer supply queue. Since we consider the CP to "own" the buffers + * once we put them on a supply queue and since a completed supply queue + * entry is only telling us that the CP has accepted the buffers that we + * gave to it, there's not much to do here. + * + * May be called in interrupt state. + * Must be called with interrupts locked out. + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +static void +fore_buf_drain(fup) + Fore_unit *fup; +{ + H_buf_queue *hbp; + + /* + * Drain Strategy 1 Small Queue + */ + + /* + * Process each completed entry + */ + while (*fup->fu_buf1s_head->hbq_status & QSTAT_COMPLETED) { + + hbp = fup->fu_buf1s_head; + + if (*hbp->hbq_status & QSTAT_ERROR) { + /* + * XXX - what does this mean??? + */ + log(LOG_ERR, "fore_buf_drain: buf1s queue error\n"); + } + + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hbp->hbq_status = QSTAT_FREE; + fup->fu_buf1s_head = hbp->hbq_next; + } + + + /* + * Drain Strategy 1 Large Queue + */ + + /* + * Process each completed entry + */ + while (*fup->fu_buf1l_head->hbq_status & QSTAT_COMPLETED) { + + hbp = fup->fu_buf1l_head; + + if (*hbp->hbq_status & QSTAT_ERROR) { + /* + * XXX - what does this mean??? + */ + log(LOG_ERR, "fore_buf_drain: buf1l queue error\n"); + } + + /* + * Mark this entry free for use and bump head pointer + * to the next entry in the queue + */ + *hbp->hbq_status = QSTAT_FREE; + fup->fu_buf1l_head = hbp->hbq_next; + } + + return; +} + + +/* + * Free Buffer Supply Queue Data Structures + * + * Arguments: + * fup pointer to device unit structure + * + * Returns: + * none + */ +void +fore_buf_free(fup) + Fore_unit *fup; +{ + Buf_handle *bhp; + KBuffer *m; + + /* + * Free any previously supplied and not returned buffers + */ + if (fup->fu_flags & CUF_INITED) { + + /* + * Run through Strategy 1 Small queue + */ + while (bhp = Q_HEAD(fup->fu_buf1s_bq, Buf_handle)) { + caddr_t cp; + + /* + * Back off to buffer + */ + m = (KBuffer *)((caddr_t)bhp - BUF1_SM_HOFF); + + /* + * Dequeue handle and free buffer + */ + DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1s_bq); + + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_SM_SIZE, 0); + + KB_FREEALL(m); + } + + /* + * Run through Strategy 1 Large queue + */ + while (bhp = Q_HEAD(fup->fu_buf1l_bq, Buf_handle)) { + caddr_t cp; + + /* + * Back off to buffer + */ + m = (KBuffer *)((caddr_t)bhp - BUF1_LG_HOFF); + + /* + * Dequeue handle and free buffer + */ + DEQUEUE(bhp, Buf_handle, bh_qelem, fup->fu_buf1l_bq); + + KB_DATASTART(m, cp, caddr_t); + DMA_FREE_ADDR(cp, bhp->bh_dma, BUF1_LG_SIZE, 0); + + KB_FREEALL(m); + } + } + + /* + * Free the status words + */ + if (fup->fu_buf1s_stat) { + if (fup->fu_buf1s_statd) { + DMA_FREE_ADDR(fup->fu_buf1s_stat, fup->fu_buf1s_statd, + sizeof(Q_status) * + (BUF1_SM_QUELEN + BUF1_LG_QUELEN), + ATM_DEV_NONCACHE); + } + atm_dev_free((void *)fup->fu_buf1s_stat); + fup->fu_buf1s_stat = NULL; + fup->fu_buf1s_statd = NULL; + fup->fu_buf1l_stat = NULL; + fup->fu_buf1l_statd = NULL; + } + + /* + * Free the transmit descriptors + */ + if (fup->fu_buf1s_desc) { + if (fup->fu_buf1s_descd) { + DMA_FREE_ADDR(fup->fu_buf1s_desc, fup->fu_buf1s_descd, + sizeof(Buf_descr) * + ((BUF1_SM_QUELEN * BUF1_SM_ENTSIZE) + + (BUF1_LG_QUELEN * BUF1_LG_ENTSIZE)), + 0); + } + atm_dev_free(fup->fu_buf1s_desc); + fup->fu_buf1s_desc = NULL; + fup->fu_buf1s_descd = NULL; + fup->fu_buf1l_desc = NULL; + fup->fu_buf1l_descd = NULL; + } + + return; +} + |