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

📄 w6692.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: w6692.c,v 1.18.2.4 2004/02/11 13:21:34 keil Exp $ * * Winbond W6692 specific routines * * Author       Petr Novak * Copyright    by Petr Novak        <petr.novak@i.cz> *  * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include <linux/config.h>#include <linux/init.h>#include "hisax.h"#include "w6692.h"#include "isdnl1.h"#include <linux/interrupt.h>#include <linux/pci.h>/* table entry in the PCI devices list */typedef struct {	int vendor_id;	int device_id;	char *vendor_name;	char *card_name;} PCI_ENTRY;static const PCI_ENTRY id_list[] ={	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},	{PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},	{0, 0, "U.S.Robotics", "ISDN PCI Card TA"}};#define W6692_SV_USR   0x16ec#define W6692_SD_USR   0x3409#define W6692_WINBOND  0#define W6692_DYNALINK 1#define W6692_USR      2extern const char *CardType[];static const char *w6692_revision = "$Revision: 1.18.2.4 $";#define DBUSY_TIMER_VALUE 80static char *W6692Ver[] __initdata ={"W6692 V00", "W6692 V01", "W6692 V10", "W6692 V11"};static void __initW6692Version(struct IsdnCardState *cs, char *s){	int val;	val = cs->readW6692(cs, W_D_RBCH);	printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);}static voidph_command(struct IsdnCardState *cs, unsigned int command){	if (cs->debug & L1_DEB_ISAC)		debugl1(cs, "ph_command %x", command);	cs->writeisac(cs, W_CIX, command);}static voidW6692_new_ph(struct IsdnCardState *cs){	switch (cs->dc.w6692.ph_state) {		case (W_L1CMD_RST):			ph_command(cs, W_L1CMD_DRC);			l1_msg(cs, HW_RESET | INDICATION, NULL);			/* fallthru */		case (W_L1IND_CD):			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);			break;		case (W_L1IND_DRD):			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);			break;		case (W_L1IND_CE):			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);			break;		case (W_L1IND_LD):			l1_msg(cs, HW_RSYNC | INDICATION, NULL);			break;		case (W_L1IND_ARD):			l1_msg(cs, HW_INFO2 | INDICATION, NULL);			break;		case (W_L1IND_AI8):			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);			break;		case (W_L1IND_AI10):			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);			break;		default:			break;	}}static voidW6692_bh(struct IsdnCardState *cs){	struct PStack *stptr;	if (!cs)		return;	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {		if (cs->debug)			debugl1(cs, "D-Channel Busy cleared");		stptr = cs->stlist;		while (stptr != NULL) {			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);			stptr = stptr->next;		}	}	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))		W6692_new_ph(cs);	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))		DChannel_proc_rcv(cs);	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))		DChannel_proc_xmt(cs);/*   if (test_and_clear_bit(D_RX_MON1, &cs->event))   arcofi_fsm(cs, ARCOFI_RX_END, NULL);   if (test_and_clear_bit(D_TX_MON1, &cs->event))   arcofi_fsm(cs, ARCOFI_TX_END, NULL); */}static voidW6692_empty_fifo(struct IsdnCardState *cs, int count){	u_char *ptr;	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))		debugl1(cs, "W6692_empty_fifo");	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {		if (cs->debug & L1_DEB_WARN)			debugl1(cs, "W6692_empty_fifo overrun %d",				cs->rcvidx + count);		cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);		cs->rcvidx = 0;		return;	}	ptr = cs->rcvbuf + cs->rcvidx;	cs->rcvidx += count;	cs->readW6692fifo(cs, ptr, count);	cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);	if (cs->debug & L1_DEB_ISAC_FIFO) {		char *t = cs->dlog;		t += sprintf(t, "W6692_empty_fifo cnt %d", count);		QuickHex(t, ptr, count);		debugl1(cs, cs->dlog);	}}static voidW6692_fill_fifo(struct IsdnCardState *cs){	int count, more;	u_char *ptr;	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))		debugl1(cs, "W6692_fill_fifo");	if (!cs->tx_skb)		return;	count = cs->tx_skb->len;	if (count <= 0)		return;	more = 0;	if (count > W_D_FIFO_THRESH) {		more = !0;		count = W_D_FIFO_THRESH;	}	ptr = cs->tx_skb->data;	skb_pull(cs->tx_skb, count);	cs->tx_cnt += count;	cs->writeW6692fifo(cs, ptr, count);	cs->writeW6692(cs, W_D_CMDR, more ? W_D_CMDR_XMS : (W_D_CMDR_XMS | W_D_CMDR_XME));	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {		debugl1(cs, "W6692_fill_fifo dbusytimer running");		del_timer(&cs->dbusytimer);	}	init_timer(&cs->dbusytimer);	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);	add_timer(&cs->dbusytimer);	if (cs->debug & L1_DEB_ISAC_FIFO) {		char *t = cs->dlog;		t += sprintf(t, "W6692_fill_fifo cnt %d", count);		QuickHex(t, ptr, count);		debugl1(cs, cs->dlog);	}}static voidW6692B_empty_fifo(struct BCState *bcs, int count){	u_char *ptr;	struct IsdnCardState *cs = bcs->cs;	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))		debugl1(cs, "W6692B_empty_fifo");	if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) {		if (cs->debug & L1_DEB_WARN)			debugl1(cs, "W6692B_empty_fifo: incoming packet too large");		cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);		bcs->hw.w6692.rcvidx = 0;		return;	}	ptr = bcs->hw.w6692.rcvbuf + bcs->hw.w6692.rcvidx;	bcs->hw.w6692.rcvidx += count;	READW6692BFIFO(cs, bcs->channel, ptr, count);	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);	if (cs->debug & L1_DEB_HSCX_FIFO) {		char *t = bcs->blog;		t += sprintf(t, "W6692B_empty_fifo %c cnt %d",			     bcs->channel + '1', count);		QuickHex(t, ptr, count);		debugl1(cs, bcs->blog);	}}static voidW6692B_fill_fifo(struct BCState *bcs){	struct IsdnCardState *cs = bcs->cs;	int more, count;	u_char *ptr;	if (!bcs->tx_skb)		return;	if (bcs->tx_skb->len <= 0)		return;	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;	if (bcs->tx_skb->len > W_B_FIFO_THRESH) {		more = 1;		count = W_B_FIFO_THRESH;	} else		count = bcs->tx_skb->len;	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))		debugl1(cs, "W6692B_fill_fifo%s%d", (more ? " ": " last "), count);	ptr = bcs->tx_skb->data;	skb_pull(bcs->tx_skb, count);	bcs->tx_cnt -= count;	bcs->hw.w6692.count += count;	WRITEW6692BFIFO(cs, bcs->channel, ptr, count);	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME));	if (cs->debug & L1_DEB_HSCX_FIFO) {		char *t = bcs->blog;		t += sprintf(t, "W6692B_fill_fifo %c cnt %d",			     bcs->channel + '1', count);		QuickHex(t, ptr, count);		debugl1(cs, bcs->blog);	}}static voidW6692B_interrupt(struct IsdnCardState *cs, u_char bchan){	u_char val;	u_char r;	struct BCState *bcs;	struct sk_buff *skb;	int count;	bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1);	val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR);	debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val);	if (!test_bit(BC_FLG_INIT, &bcs->Flag)) {		debugl1(cs, "W6692B not INIT yet");		return;	}	if (val & W_B_EXI_RME) {	/* RME */		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);		if (r & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {			if (cs->debug & L1_DEB_WARN)				debugl1(cs, "W6692 B STAR %x", r);			if ((r & W_B_STAR_RDOV) && bcs->mode)				if (cs->debug & L1_DEB_WARN)					debugl1(cs, "W6692 B RDOV mode=%d",						bcs->mode);			if (r & W_B_STAR_CRCE)				if (cs->debug & L1_DEB_WARN)					debugl1(cs, "W6692 B CRC error");			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);		} else {			count = cs->BC_Read_Reg(cs, bchan, W_B_RBCL) & (W_B_FIFO_THRESH - 1);			if (count == 0)				count = W_B_FIFO_THRESH;			W6692B_empty_fifo(bcs, count);			if ((count = bcs->hw.w6692.rcvidx) > 0) {				if (cs->debug & L1_DEB_HSCX_FIFO)					debugl1(cs, "W6692 Bchan Frame %d", count);				if (!(skb = dev_alloc_skb(count)))					printk(KERN_WARNING "W6692: Bchan receive out of memory\n");				else {					memcpy(skb_put(skb, count), bcs->hw.w6692.rcvbuf, count);					skb_queue_tail(&bcs->rqueue, skb);				}			}		}		bcs->hw.w6692.rcvidx = 0;		schedule_event(bcs, B_RCVBUFREADY);	}	if (val & W_B_EXI_RMR) {	/* RMR */		W6692B_empty_fifo(bcs, W_B_FIFO_THRESH);		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);		if (r & W_B_STAR_RDOV) {			if (cs->debug & L1_DEB_WARN)				debugl1(cs, "W6692 B RDOV(RMR) mode=%d",bcs->mode);			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);			if (bcs->mode != L1_MODE_TRANS)				bcs->hw.w6692.rcvidx = 0;		}		if (bcs->mode == L1_MODE_TRANS) {			/* receive audio data */			if (!(skb = dev_alloc_skb(W_B_FIFO_THRESH)))				printk(KERN_WARNING "HiSax: receive out of memory\n");			else {				memcpy(skb_put(skb, W_B_FIFO_THRESH), bcs->hw.w6692.rcvbuf, W_B_FIFO_THRESH);				skb_queue_tail(&bcs->rqueue, skb);			}			bcs->hw.w6692.rcvidx = 0;			schedule_event(bcs, B_RCVBUFREADY);		}	}	if (val & W_B_EXI_XDUN) {	/* XDUN */		cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);		if (cs->debug & L1_DEB_WARN)			debugl1(cs, "W6692 B EXIR %x Lost TX", val);		if (bcs->mode == 1)			W6692B_fill_fifo(bcs);		else {			/* Here we lost an TX interrupt, so			   * restart transmitting the whole frame.			 */			if (bcs->tx_skb) {				skb_push(bcs->tx_skb, bcs->hw.w6692.count);				bcs->tx_cnt += bcs->hw.w6692.count;				bcs->hw.w6692.count = 0;			}		}		return;	}	if (val & W_B_EXI_XFR) {	/* XFR */		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);		if (r & W_B_STAR_XDOW) {			if (cs->debug & L1_DEB_WARN)				debugl1(cs, "W6692 B STAR %x XDOW", r);			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);			if (bcs->tx_skb && (bcs->mode != 1)) {				skb_push(bcs->tx_skb, bcs->hw.w6692.count);				bcs->tx_cnt += bcs->hw.w6692.count;				bcs->hw.w6692.count = 0;			}		}		if (bcs->tx_skb) {			if (bcs->tx_skb->len) {				W6692B_fill_fifo(bcs);				return;			} else {				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {					u_long	flags;					spin_lock_irqsave(&bcs->aclock, flags);					bcs->ackcnt += bcs->hw.w6692.count;					spin_unlock_irqrestore(&bcs->aclock, flags);					schedule_event(bcs, B_ACKPENDING);				}				dev_kfree_skb_irq(bcs->tx_skb);				bcs->hw.w6692.count = 0;				bcs->tx_skb = NULL;			}		}		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {			bcs->hw.w6692.count = 0;			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);			W6692B_fill_fifo(bcs);		} else {			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);			schedule_event(bcs, B_XMTBUFREADY);		}	}}static irqreturn_tW6692_interrupt(int intno, void *dev_id, struct pt_regs *regs){	struct IsdnCardState	*cs = dev_id;	u_char			val, exval, v1;	struct sk_buff		*skb;	u_int			count;	u_long			flags;	int			icnt = 5;	spin_lock_irqsave(&cs->lock, flags);	val = cs->readW6692(cs, W_ISTA);	if (!val) {		spin_unlock_irqrestore(&cs->lock, flags);		return IRQ_NONE;	}      StartW6692:	if (cs->debug & L1_DEB_ISAC)		debugl1(cs, "W6692 ISTA %x", val);	if (val & W_INT_D_RME) {	/* RME */		exval = cs->readW6692(cs, W_D_RSTA);		if (exval & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {			if (exval & W_D_RSTA_RDOV)				if (cs->debug & L1_DEB_WARN)					debugl1(cs, "W6692 RDOV");			if (exval & W_D_RSTA_CRCE)				if (cs->debug & L1_DEB_WARN)					debugl1(cs, "W6692 D-channel CRC error");			if (exval & W_D_RSTA_RMB)				if (cs->debug & L1_DEB_WARN)					debugl1(cs, "W6692 D-channel ABORT");			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);		} else {			count = cs->readW6692(cs, W_D_RBCL) & (W_D_FIFO_THRESH - 1);			if (count == 0)				count = W_D_FIFO_THRESH;			W6692_empty_fifo(cs, count);			if ((count = cs->rcvidx) > 0) {				cs->rcvidx = 0;				if (!(skb = alloc_skb(count, GFP_ATOMIC)))					printk(KERN_WARNING "HiSax: D receive out of memory\n");				else {					memcpy(skb_put(skb, count), cs->rcvbuf, count);					skb_queue_tail(&cs->rq, skb);				}			}		}		cs->rcvidx = 0;		schedule_event(cs, D_RCVBUFREADY);	}	if (val & W_INT_D_RMR) {	/* RMR */		W6692_empty_fifo(cs, W_D_FIFO_THRESH);	}	if (val & W_INT_D_XFR) {	/* XFR */		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))			del_timer(&cs->dbusytimer);		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))			schedule_event(cs, D_CLEARBUSY);		if (cs->tx_skb) {			if (cs->tx_skb->len) {				W6692_fill_fifo(cs);				goto afterXFR;			} else {				dev_kfree_skb_irq(cs->tx_skb);				cs->tx_cnt = 0;				cs->tx_skb = NULL;			}		}		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {			cs->tx_cnt = 0;			W6692_fill_fifo(cs);		} else			schedule_event(cs, D_XMTBUFREADY);	}      afterXFR:	if (val & (W_INT_XINT0 | W_INT_XINT1)) {	/* XINT0/1 - never */		if (cs->debug & L1_DEB_ISAC)			debugl1(cs, "W6692 spurious XINT!");	}	if (val & W_INT_D_EXI) {	/* EXI */		exval = cs->readW6692(cs, W_D_EXIR);		if (cs->debug & L1_DEB_WARN)			debugl1(cs, "W6692 D_EXIR %02x", exval);		if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {	/* Transmit underrun/collision */			debugl1(cs, "W6692 D-chan underrun/collision");			printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL\n");			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))				del_timer(&cs->dbusytimer);			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))				schedule_event(cs, D_CLEARBUSY);			if (cs->tx_skb) {	/* Restart frame */				skb_push(cs->tx_skb, cs->tx_cnt);				cs->tx_cnt = 0;				W6692_fill_fifo(cs);			} else {				printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL no skb\n");				debugl1(cs, "W6692 XDUN/XCOL no skb");				cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST);			}		}		if (exval & W_D_EXI_RDOV) {	/* RDOV */			debugl1(cs, "W6692 D-channel RDOV");			printk(KERN_WARNING "HiSax: W6692 D-RDOV\n");			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST);		}		if (exval & W_D_EXI_TIN2) {	/* TIN2 - never */			debugl1(cs, "W6692 spurious TIN2 interrupt");		}		if (exval & W_D_EXI_MOC) {	/* MOC - not supported */			debugl1(cs, "W6692 spurious MOC interrupt");			v1 = cs->readW6692(cs, W_MOSR);			debugl1(cs, "W6692 MOSR %02x", v1);		}		if (exval & W_D_EXI_ISC) {	/* ISC - Level1 change */			v1 = cs->readW6692(cs, W_CIR);			if (cs->debug & L1_DEB_ISAC)				debugl1(cs, "W6692 ISC CIR=0x%02X", v1);			if (v1 & W_CIR_ICC) {				cs->dc.w6692.ph_state = v1 & W_CIR_COD_MASK;				if (cs->debug & L1_DEB_ISAC)					debugl1(cs, "ph_state_change %x", cs->dc.w6692.ph_state);				schedule_event(cs, D_L1STATECHANGE);			}			if (v1 & W_CIR_SCC) {				v1 = cs->readW6692(cs, W_SQR);				debugl1(cs, "W6692 SCC SQR=0x%02X", v1);			}		}		if (exval & W_D_EXI_WEXP) {			debugl1(cs, "W6692 spurious WEXP interrupt!");		}		if (exval & W_D_EXI_TEXP) {			debugl1(cs, "W6692 spurious TEXP interrupt!");		}	}	if (val & W_INT_B1_EXI) {		debugl1(cs, "W6692 B channel 1 interrupt");		W6692B_interrupt(cs, 0);	}	if (val & W_INT_B2_EXI) {		debugl1(cs, "W6692 B channel 2 interrupt");		W6692B_interrupt(cs, 1);	}	val = cs->readW6692(cs, W_ISTA);

⌨️ 快捷键说明

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