⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pciscc_drv.c

📁 高速同步串口芯片PEB20534的驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
	{
		if (pChan->dq_rx[i].l1_frame)
			pciscc_free(pChan->dq_rx[i].l1_frame);
	}
}

/* find best BRG N and M match for given bitrate */
VOID pciscc_set_baud(
	PLOCAL_DEVICE_INFO pLDI,
	ULONG chan,
	ULONG rate
	)
{
	LONG brg_best_n	= 0;
	LONG brg_best_m	= 0;
	LONG brg_tmp,m,n;
	ULONG brg_error	= XTAL_FREQ;
	ULONG brg_tmp_error;

	for (m=0; m<16; m++) {
		for (n=0; n<63; n++) {
			brg_tmp = (n+1)*(1<<m);
			brg_tmp_error = labs(XTAL_FREQ/brg_tmp - rate);
			if (brg_tmp_error < brg_error) {
				brg_best_m = m;
				brg_best_n = n;
				brg_error = brg_tmp_error;
			}
		}
	}
	DbgPrint("PCISCC: set BRR%d M=%d, N=%d\n",chan,brg_best_m,brg_best_n);
	WriteL(pLDI,SCCBASE[chan]+BRR,(brg_best_m*BRM) | (brg_best_n*BRN));
}

/* report channel state (PTT/DCD) */
ULONG pciscc_state(
	IN PLOCAL_DEVICE_INFO pLDI,
	IN ULONG chan
	)
{
	PCHANNEL_INFO pChan;
	ULONG l;
	ULONG state = 0;

	pChan = &pLDI->Channel[chan];
	if (!pChan->initialized)
		return CH_DEAD;
	ReadL(pLDI,SCCBASE[chan]+STAR);
	l = ReadL(pLDI,SCCBASE[chan]+STAR);
	if (!(l & CD))
		state |= CH_DCD;
	if (!IsListEmpty(&pLDI->RxQueue))
		state |= CH_RXB;
	switch(pChan->txstate) {
	case TX_DELAY:
	case TX_XMIT:
	case TX_TAIL:
		state |= CH_PTT;
		break;
	case TX_CAL:
		state |= CH_TBY | CH_PTT;
		break;
	case TX_RESET:
		state |= CH_TBY;
		break;
	}
	return state;
}

/* send calibration signal (IRQ synchronized) */
BOOLEAN pciscc_calib_sync(
	IN PCALIB_SYNC_INFO pInfo
	)
{
	PLOCAL_DEVICE_INFO pLDI = pInfo->pLDI;
	ULONG chan = pInfo->chan;
	PCHANNEL_INFO pChan = &pLDI->Channel[chan];
	ULONG bits = pInfo->time * pChan->bitrate * 60;

	if (pChan->initialized) {
		if ((pChan->txstate == TX_CAL) && !bits) {
			WriteL(pLDI,SCCBASE[chan]+TIMR,MIN_FLAGS*TVALUE);
			WriteL(pLDI,SCCBASE[chan]+CMDR,STI);
		}
		if ((pChan->txstate != TX_RESET) && (pChan->txstate != TX_STALL) && bits) {
			pChan->ccr1 |= RTS;
			WriteL(pLDI,SCCBASE[chan]+CCR1,pChan->ccr1);
			WriteL(pLDI,SCCBASE[chan]+TIMR,bits*TVALUE);
			WriteL(pLDI,SCCBASE[chan]+CMDR,STI);
			pChan->txstate = TX_CAL;
		}
	}
	return TRUE;
}

/* XMIT frame (IRQ synchronized) */
BOOLEAN pciscc_xmit_sync(
	IN PXMIT_SYNC_INFO pInfo
	)
{
	PLOCAL_DEVICE_INFO pLDI = pInfo->pLDI;
	L1FRAME *tx_frame = pInfo->tx_frame;
	BOOLEAN enqueued = FALSE;
	PCHANNEL_INFO pChan;
	struct tx_desc *tdp;
	ULONG chan;
	ULONG txd_bits;
	
	/* get channel number */
	chan = tx_frame->chan;
	if (chan < 0 || chan > 3)
		return FALSE;
	pChan = &pLDI->Channel[chan];
	if (!pChan->initialized)
		return FALSE;
	/* descriptor list already full ? */
	tdp = pChan->dq_tx_last->next;
	if (tdp->l1_frame == NULL && tdp->next->l1_frame == NULL) {
		/* insert TX frame buffer into descriptor queue */
		if (pChan->txstate == TX_IDLE ||
			pChan->txstate == TX_DELAY ||
			pChan->txstate == TX_XMIT ||
			pChan->txstate == TX_TAIL)
		{
			tdp->l1_frame = tx_frame;
			tdp->dataptr = VirtToPhys(tx_frame->frame);
			tdp->result = 0;
			tdp->flags = FE | (NO * (tx_frame->len & 0x0fff));
			pChan->dq_tx_last = tdp;
			pChan->tx_enqueued++;
			enqueued = TRUE;
		}
		/* calculate tx keyup delay */
		txd_bits = pChan->bitrate * tx_frame->txdelay / 100;
		if (txd_bits < MIN_FLAGS) txd_bits = MIN_FLAGS;
		/* TX was idle, key up and start txdelay */
		if (pChan->txstate == TX_IDLE) {
			pChan->txstate=TX_DELAY;
			pChan->ccr1 |= RTS;
			WriteL(pLDI,SCCBASE[chan]+CCR1,pChan->ccr1);
			WriteL(pLDI,SCCBASE[chan]+TIMR,0);
			WriteL(pLDI,SCCBASE[chan]+TIMR,txd_bits*TVALUE);
			WriteL(pLDI,SCCBASE[chan]+CMDR,STI);
		}
		/* Update TX last descpriptor address if we are in TX_XMIT state */
		if (pChan->txstate == TX_XMIT) {
			switch (chan) {
			case 0:	WriteL(pLDI,CH0LTDA,VirtToPhys(tdp));
					break;
			case 1:	WriteL(pLDI,CH1LTDA,VirtToPhys(tdp));
					break;
			case 2:	WriteL(pLDI,CH2LTDA,VirtToPhys(tdp));
					break;
			case 3:	WriteL(pLDI,CH3LTDA,VirtToPhys(tdp));
					break;
			}
		}
	}
	return enqueued;
}

/* ---------------------------------------------------------------- */

/* interrupt service routine */
BOOLEAN pciscc_isr(
	IN PKINTERRUPT Interrupt,
	IN PVOID ServiceContext
	)
{
	PLOCAL_DEVICE_INFO pLDI = (PLOCAL_DEVICE_INFO)ServiceContext;
	PCHANNEL_INFO pChan;
	ULONG status;
	ULONG chan;
	PULONG iqp;

	/* ack' irq */
	status = ReadL(pLDI,GSTAR);
	WriteL(pLDI,GSTAR,status);
	/* process peripheral queue */
	iqp = pLDI->iq_per_next;
	while (iqp && *iqp) {
		*(iqp++)=0;
		if (iqp == pLDI->iq_per+CFG_IQLEN)
			iqp = pLDI->iq_per;
	}
	pLDI->iq_per_next = iqp;
	/* process configuration queue */
	pLDI->mailbox = MAILBOX_NONE;
	iqp = pLDI->iq_cfg_next;
	while(iqp && *iqp) {
		if ((*iqp) & ARACK)
			pLDI->mailbox = MAILBOX_OK;
		if ((*iqp) & ARF)
			pLDI->mailbox = MAILBOX_FAILURE;
		*(iqp++)=0;
		if (iqp == pLDI->iq_cfg+CFG_IQLEN)
			iqp = pLDI->iq_cfg;
	}
	pLDI->iq_cfg_next = iqp;
	/* process channel interrupt queues */
	for (chan = 0; chan < 4; chan++) {
		pChan = &pLDI->Channel[chan];
		/* process RX queue */
		iqp = pChan->iq_rx_next;
		while(iqp && *iqp) {
			if (pChan->initialized) {
				/* statistics only */
				if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RDO))
					pChan->stats.rx_bufferoverflow++;
				if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RFO))
					pChan->stats.rx_bufferoverflow++;
				if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_FLEX))
					pChan->stats.rx_bufferoverflow++;
			}
			pChan->rx_mailbox = *iqp;
			*(iqp++)=0;
			if (iqp == pChan->iq_rx+CFG_IQLEN)
				iqp = pChan->iq_rx;
		}
		pChan->iq_rx_next = iqp;
		/* process TX queue */
		iqp = pChan->iq_tx_next;
		while (iqp && *iqp) {
			if (pChan->initialized) {
				if (*iqp & TIN) {
					/* timer interrupt */
					WriteL(pLDI,SCCBASE[chan]+TIMR,0);
					if (pChan->tx_enqueued) {
						pChan->txstate = TX_XMIT;
						switch (chan) {
						case 0:	WriteL(pLDI,CH0LTDA,VirtToPhys(pChan->dq_tx_last));
								break;
						case 1:	WriteL(pLDI,CH1LTDA,VirtToPhys(pChan->dq_tx_last));
								break;
						case 2:	WriteL(pLDI,CH2LTDA,VirtToPhys(pChan->dq_tx_last));
								break;
						case 3:	WriteL(pLDI,CH3LTDA,VirtToPhys(pChan->dq_tx_last));
								break;
						}
					} else {
						pChan->txstate=TX_IDLE;
						pChan->ccr1 &= ~RTS;
						WriteL(pLDI,SCCBASE[chan]+CCR1,pChan->ccr1);
					}
				}
				if (*iqp & ALLS) {
					/* complete TX-frame sent out */
					if (pChan->tx_enqueued)
						pChan->tx_enqueued--;
					if (!pChan->tx_enqueued) {
						pChan->txstate=TX_TAIL;
						WriteL(pLDI,SCCBASE[chan]+TIMR,MIN_FLAGS*TVALUE);
						WriteL(pLDI,SCCBASE[chan]+CMDR,STI);
					}
				}
				if (*iqp & XDU) {
					/* TX data underrun */
					KeInsertQueueDpc(&pLDI->TxResetDpc,(PVOID)chan,NULL);
				}
			}
			pChan->tx_mailbox = *iqp;
			*(iqp++)=0;
			if (iqp == pChan->iq_tx+CFG_IQLEN)
				iqp = pChan->iq_tx;
		}
		pChan->iq_tx_next = iqp;
	}
	/* start bottom half DPC functions */
	if (status) {
		KeInsertQueueDpc(&pLDI->IsrDpc,NULL,NULL);
		return TRUE;
	}
	return FALSE;
}

/* ---------------------------------------------------------------- */

/* TX underrun reset DPC */
VOID pciscc_dpc_txreset(
	IN PKDPC Dpc,
	IN PDEVICE_OBJECT pDO,
	IN PVOID SystemArgument1,
	IN PVOID SystemArgument2
	)
{
	PLOCAL_DEVICE_INFO pLDI = pDO->DeviceExtension;
	int chan = (int)SystemArgument1;
	PCHANNEL_INFO pChan = &pLDI->Channel[chan];
	LARGE_INTEGER	time;
	LARGE_INTEGER	time_expire;

	KeAcquireSpinLockAtDpcLevel(&pLDI->Lock);
	if (pChan->initialized) {
		pChan->txstate=TX_STALL;
		pChan->stats.tx_error++;
	
		pChan->ccr1 &= ~RTS;
		WriteL(pLDI,SCCBASE[chan]+CCR1,pChan->ccr1);
		WriteL(pLDI,SCCBASE[chan]+CMDR,XRES);

		KeQuerySystemTime(&time);
		time_expire = RtlLargeIntegerAdd(time,RtlConvertUlongToLargeInteger(CHIP_TIMEOUT));
		do {
			KeQuerySystemTime(&time);
		} while RtlLargeIntegerLessThan(time,time_expire);

		pChan->txstate = TX_IDLE;
	}
	KeReleaseSpinLockFromDpcLevel(&pLDI->Lock);
}

/* ---------------------------------------------------------------- */

/* TX/RX queue processing DPC routine */
VOID pciscc_dpc_isr(
	IN PKDPC Dpc,
	IN PDEVICE_OBJECT pDO,
	IN PVOID SystemArgument1,
	IN PVOID SystemArgument2
	)
{
	PLOCAL_DEVICE_INFO pLDI = pDO->DeviceExtension;
	PCHANNEL_INFO pChan;
	struct rx_desc *rdp;
	struct tx_desc *tdp;
	L1FRAME *rx_new;
	USHORT bno;
	UCHAR rdsb;		/* receive data status byte */
	ULONG chan,status,valid,rx_cnt;

	KeAcquireSpinLockAtDpcLevel(&pLDI->Lock);
	for (chan = 0; chan < 4; chan++) {
		pChan = &pLDI->Channel[chan];
		if (!pChan->initialized)
			continue;
		/* transmit DMA queue */
		tdp = pChan->dq_tx_cleanup;
		while (tdp->result & C) {
			/* clean up all (C)omplete descriptors */
			if (tdp->l1_frame)
				pciscc_free(tdp->l1_frame);
			tdp->l1_frame = NULL;
			tdp->dataptr = VirtToPhys(pLDI->DmaBuffers->dummy);
			tdp->flags = FE | (8*NO);
			tdp->result = 0;
			tdp = tdp->next;
			pChan->stats.tx_frames++;
		}
		pChan->dq_tx_cleanup = tdp;
		/* receiver DMA queue */
		rdp = pChan->dq_rx_next;
		rx_cnt = 0;
		while (rdp->result & C) {
			if ((rdp->nextptr != VirtToPhys(rdp->next)) || (rdp->dataptr != VirtToPhys(rdp->l1_frame->frame))) {
				pChan->stats.rx_bufferoverflow++;
			}
			status = rdp->result;
			bno = (USHORT)(status >> 16) & 0x1fff;
			valid = 1;					/* we assume frame valid */
			if ((status & RA) || (bno < 5) || (bno > CFG_MTU) || !(status & FE) || (rdp->feptr != VirtToPhys(rdp))) {
				valid = 0;				/* aborted or invalid length */
			} else {
				rdsb = rdp->l1_frame->frame[bno-1];
				if (!(rdsb & SB_VFR))	/* incorrect bit length */
					valid = 0;
				if (rdsb & SB_RDO)		/* data overflow */
					valid = 0;
				if (!(rdsb & SB_CRC))	/* CRC error */
					valid = 0;
				if (rdsb & SB_RAB)		/* receive message aborted */
					valid = 0;
			}
			/* OK, this is a little bit tricky. We have to make sure
			 * that every descriptor has a buffer assigned. Thus we
			 * can only release a buffer to the link layer if we get
			 * a new one in turn before. */
			if (valid && pLDI->OpenCnt) {
				if ((rx_new = pciscc_alloc(sizeof(L1FRAME))) != NULL) {
					rdp->l1_frame->len = bno-1;
					rdp->l1_frame->chan = (UCHAR)chan;
					rdp->l1_frame->txdelay = 0;
					InsertTailList(&pLDI->RxQueue, &rdp->l1_frame->list_entry);
					rdp->l1_frame = rx_new;
					rdp->dataptr = VirtToPhys(rx_new->frame);
					pChan->stats.rx_frames++;
				} else
					pChan->stats.rx_overrun++;
			}
			rdp->flags = CFG_MTU*NO;
			rdp->result = 0;
			rdp->feptr = 0;
			rdp = rdp->next;
			rx_cnt++;
		}
		pChan->dq_rx_next = rdp;
		/*
		 * tell DMAC last available descriptor - keep up one
		 * descriptor space for security (paranoia) (...->prev->prev)
		 */
		if (rx_cnt) {
			switch (chan) {
			case 0:	WriteL(pLDI,CH0LRDA,VirtToPhys(rdp->prev->prev));
					break;
			case 1:	WriteL(pLDI,CH1LRDA,VirtToPhys(rdp->prev->prev));
					break;
			case 2:	WriteL(pLDI,CH2LRDA,VirtToPhys(rdp->prev->prev));
					break;
			case 3:	WriteL(pLDI,CH3LRDA,VirtToPhys(rdp->prev->prev));
					break;
			}
		}
	}
	KeReleaseSpinLockFromDpcLevel(&pLDI->Lock);

	/* Process pending IRPs */
	KeAcquireSpinLockAtDpcLevel(&pLDI->Lock);
    while (!IsListEmpty(&pLDI->RxPending) && !IsListEmpty(&pLDI->RxQueue))
	{
		PLIST_ENTRY pEntry;
		PIO_STACK_LOCATION pIrpStack;
		PIRP pIrp;
		L1FRAME *rx_frame;
		ULONG Count = 0;

        pEntry = RemoveHeadList(&pLDI->RxPending);
		pIrp = CONTAINING_RECORD(pEntry,IRP,Tail.Overlay.ListEntry);

		/* Get read buffer length */
		pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
		Count = pIrpStack->Parameters.Read.Length;
		if (Count > sizeof(L1FRAME))
			Count = sizeof(L1FRAME);

		/* Get RX frame */
		pEntry = RemoveHeadList(&pLDI->RxQueue);
		rx_frame = CONTAINING_RECORD(pEntry, L1FRAME, list_entry);
		RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, rx_frame, Count);
		pciscc_free(rx_frame);

		pIrp->IoStatus.Status = STATUS_SUCCESS;
		pIrp->IoStatus.Information = Count;
		IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	}
	KeReleaseSpinLockFromDpcLevel(&pLDI->Lock);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -