patch-2.2.15 linux/drivers/isdn/hisax/isar.c

Next file: linux/drivers/isdn/hisax/isar.h
Previous file: linux/drivers/isdn/hisax/hisax.h
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/isdn/hisax/isar.c linux/drivers/isdn/hisax/isar.c
@@ -1,4 +1,4 @@
-/* $Id: isar.c,v 1.8 1999/12/19 13:00:56 keil Exp $
+/* $Id: isar.c,v 1.9 2000/01/20 19:47:45 keil Exp $
 
  * isar.c   ISAR (Siemens PSB 7110) specific routines
  *
@@ -6,6 +6,9 @@
  *
  *
  * $Log: isar.c,v $
+ * Revision 1.9  2000/01/20 19:47:45  keil
+ * Add Fax Class 1 support
+ *
  * Revision 1.8  1999/12/19 13:00:56  keil
  * Fix races in setting a new mode
  *
@@ -45,7 +48,17 @@
 
 #define MIN(a,b) ((a<b)?a:b)
 
+#define DLE	0x10
+#define ETX	0x03
+
+
+const u_char faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146"; 
+const u_char faxmodulation[] = {3,24,48,72,73,74,96,97,98,121,122,145,146}; 
+#define FAXMODCNT 13
+
 void isar_setup(struct IsdnCardState *cs);
+static void isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para);
+static inline void ll_deliver_faxstat(struct BCState *bcs, u_char status);
 
 static inline int
 waitforHIA(struct IsdnCardState *cs, int timeout)
@@ -435,6 +448,12 @@
 isar_bh(struct BCState *bcs)
 {
 	BChannel_bh(bcs);
+	if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR);
+	if (test_and_clear_bit(B_LL_CONNECT, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+	if (test_and_clear_bit(B_LL_OK, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_OK);
 }
 
 static void
@@ -446,6 +465,42 @@
 }
 
 static inline void
+send_DLE_ETX(struct BCState *bcs)
+{
+	u_char dleetx[2] = {DLE,ETX};
+	struct sk_buff *skb;
+	
+	if ((skb = dev_alloc_skb(2))) {
+		memcpy(skb_put(skb, 2), dleetx, 2);
+		skb_queue_tail(&bcs->rqueue, skb);
+		isar_sched_event(bcs, B_RCVBUFREADY);
+	} else {
+		printk(KERN_WARNING "HiSax: skb out of memory\n");
+	}
+}
+
+static inline int
+dle_count(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
+}
+
+static inline void
+insert_dle(unsigned char *dest, unsigned char *src, int count) {
+	/* <DLE> in input stream have to be flagged as <DLE><DLE> */
+	while (count--) {
+		*dest++ = *src;
+		if (*src++ == DLE)
+			*dest++ = DLE;
+	}
+}
+ 
+static inline void
 isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
 {
 	u_char *ptr;
@@ -515,6 +570,88 @@
 			}
 		}
 		break;
+	case L1_MODE_FAX:
+		if (bcs->hw.isar.state != STFAX_ACTIV) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: not ACTIV");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+			rcv_mbox(cs, ireg, bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvidx = ireg->clsb +
+				dle_count(bcs->hw.isar.rcvbuf, ireg->clsb);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_rcv_frame: raw(%d) dle(%d)",
+					ireg->clsb, bcs->hw.isar.rcvidx);
+			if ((skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
+				insert_dle((u_char *)skb_put(skb, bcs->hw.isar.rcvidx),
+					bcs->hw.isar.rcvbuf, ireg->clsb);
+				skb_queue_tail(&bcs->rqueue, skb);
+				isar_sched_event(bcs, B_RCVBUFREADY);
+				if (ireg->cmsb & SART_NMD) { /* ABORT */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar_rcv_frame: no more data");
+					cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+					bcs->hw.isar.rcvidx = 0;
+					send_DLE_ETX(bcs);
+					sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+						0, NULL);
+					bcs->hw.isar.state = STFAX_ESCAPE;
+					isar_sched_event(bcs, B_LL_NOCARRIER);
+				}
+			} else {
+				printk(KERN_WARNING "HiSax: skb out of memory\n");
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		}
+		if (bcs->hw.isar.cmd != PCTRL_CMD_FRH) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: unknown fax mode %x",
+					bcs->hw.isar.cmd);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		/* PCTRL_CMD_FRH */
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD)
+				bcs->hw.isar.rcvidx = 0;
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
+						bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				} else {
+					memcpy(skb_put(skb, bcs->hw.isar.rcvidx),
+						bcs->hw.isar.rcvbuf,
+						bcs->hw.isar.rcvidx);
+					skb_queue_tail(&bcs->rqueue, skb);
+					isar_sched_event(bcs, B_RCVBUFREADY);
+					send_DLE_ETX(bcs);
+					isar_sched_event(bcs, B_LL_OK);
+				}
+			}
+		}
+		break;
 	default:
 		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
 		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
@@ -547,38 +684,57 @@
 		count = bcs->tx_skb->len;
 		msb = HDLC_FED;
 	}
-	if (!bcs->hw.isar.txcnt)
-		msb |= HDLC_FST;
 	save_flags(flags);
 	cli();
 	ptr = bcs->tx_skb->data;
+	if (!bcs->hw.isar.txcnt) {
+		msb |= HDLC_FST;
+		if ((bcs->mode == L1_MODE_FAX) &&
+			(bcs->hw.isar.cmd == PCTRL_CMD_FTH)) {
+			if (bcs->tx_skb->len > 1) {
+				if ((ptr[0]== 0xff) && (ptr[1] == 0x13))
+					/* last frame */
+					test_and_set_bit(BC_FLG_LASTDATA,
+						&bcs->Flag);
+			}  
+		}
+	}
 	skb_pull(bcs->tx_skb, count);
 	bcs->tx_cnt -= count;
 	bcs->hw.isar.txcnt += count;
 	switch (bcs->mode) {
-	case L1_MODE_NULL:
-		printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
-		break;
-	case L1_MODE_TRANS:
-	case L1_MODE_V32:
-		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
-			0, count, ptr)) {
-			if (cs->debug)
-				debugl1(cs, "isar bin data send dp%d failed",
-					bcs->hw.isar.dpath);
-		}
-		break;
-	case L1_MODE_HDLC:
-		if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
-			msb, count, ptr)) {
+		case L1_MODE_NULL:
+			printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_V32:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				0, count, ptr);
+			break;
+		case L1_MODE_HDLC:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				msb, count, ptr);
+			break;
+		case L1_MODE_FAX:
+			if (bcs->hw.isar.state != STFAX_ACTIV) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not ACTIV");
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { 
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					msb, count, ptr);
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					0, count, ptr);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not FTH/FTM");
+			}
+			break;
+		default:
 			if (cs->debug)
-				debugl1(cs, "isar hdlc data send dp%d failed",
-					bcs->hw.isar.dpath);
-		}
-		break;
-	default:
-		printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode);
-		break;
+				debugl1(cs, "isar_fill_fifo mode(%x) error", bcs->mode);
+			printk(KERN_ERR"isar_fill_fifo mode(%x) error\n", bcs->mode);
+			break;
 	}
 	restore_flags(flags);
 }
@@ -606,6 +762,18 @@
 			if (bcs->st->lli.l1writewakeup &&
 				(PACKET_NOACK != bcs->tx_skb->pkt_type))
 					bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt);
+			if (bcs->mode == L1_MODE_FAX) {
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					if (test_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+					if (test_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_LASTDATA, &bcs->Flag);
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				}
+			}
 			dev_kfree_skb(bcs->tx_skb);
 			bcs->hw.isar.txcnt = 0; 
 			bcs->tx_skb = NULL;
@@ -616,6 +784,18 @@
 		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
 		isar_fill_fifo(bcs);
 	} else {
+		if (test_and_clear_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+			if (test_and_clear_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+				if (test_and_clear_bit(BC_FLG_NMD_DATA, &bcs->Flag)) {
+					u_char dummy = 0;
+					sendmsg(bcs->cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_SDATA, 0x01, 1, &dummy);
+				}
+				test_and_set_bit(BC_FLG_LL_OK, &bcs->Flag);
+			} else {
+				isar_sched_event(bcs, B_LL_CONNECT);
+			}
+		}
 		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		isar_sched_event(bcs, B_XMTBUFREADY);
 	}
@@ -642,6 +822,7 @@
 	}
 	
 }
+
 const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
 			"300", "600", "1200", "2400", "4800", "7200",
 			"9600nt", "9600t", "12000", "14400", "WRONG"};
@@ -703,7 +884,7 @@
 }
 
 static void
-isar_pump_status_ev(struct BCState *bcs, u_char devt) {
+isar_pump_statev_modem(struct BCState *bcs, u_char devt) {
 	struct IsdnCardState *cs = bcs->cs;
 	u_char dps = SET_DPS(bcs->hw.isar.dpath);
 
@@ -772,6 +953,192 @@
 	}
 }
 
+static inline void
+ll_deliver_faxstat(struct BCState *bcs, u_char status)
+{
+        isdn_ctrl ic;
+	struct Channel *chanp = (struct Channel *) bcs->st->lli.userdata;
+ 
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "HL->LL FAXIND %x", status);
+	ic.driver = bcs->cs->myid;
+	ic.command = ISDN_STAT_FAXIND;
+	ic.arg = chanp->chan;
+	ic.parm.aux.cmd = status;
+	bcs->cs->iif.statcallb(&ic);
+}
+
+static void
+isar_pump_statev_fax(struct BCState *bcs, u_char devt) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char p1;
+
+	switch(devt) {
+		case PSEV_10MS_TIMER:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev TIMER");
+			break;
+		case PSEV_RSP_READY:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_READY");
+			bcs->hw.isar.state = STFAX_READY;
+			l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL);
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FRH, 3);
+			} else {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FTH, 3);
+			}
+			break;
+		case PSEV_LINE_TX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_TX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_RSP_CONN:
+			if (bcs->hw.isar.state == STFAX_CONT) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_CONN");
+				bcs->hw.isar.state = STFAX_ACTIV;
+				test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags);
+				sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					/* 1s Flags before data */
+					if (test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag))
+						del_timer(&bcs->hw.isar.ftimer);
+					/* 1000 ms */
+					bcs->hw.isar.ftimer.expires =
+						jiffies + ((1000 * HZ)/1000);
+					test_and_set_bit(BC_FLG_LL_CONN,
+						&bcs->Flag);
+					add_timer(&bcs->hw.isar.ftimer);
+				} else {
+					isar_sched_event(bcs, B_LL_CONNECT);
+				}
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev RSP_CONN wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_FLAGS_DET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev FLAGS_DET");
+			break;
+		case PSEV_RSP_DISC:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_DISC");
+			if (bcs->hw.isar.state == STFAX_ESCAPE) {
+				switch(bcs->hw.isar.newcmd) {
+					case PCTRL_CMD_FTH:
+					case PCTRL_CMD_FTM:
+						p1 = 10;
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							PCTRL_CMD_SILON, 1, &p1);
+						bcs->hw.isar.state = STFAX_SILDET;
+						break;
+					case PCTRL_CMD_FRH:
+					case PCTRL_CMD_FRM:
+						p1 = bcs->hw.isar.newmod;
+						bcs->hw.isar.newmod = 0;
+						bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+						bcs->hw.isar.newcmd = 0;
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							bcs->hw.isar.cmd, 1, &p1);
+						bcs->hw.isar.state = STFAX_LINE;
+						break;
+					default:
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "RSP_DISC unknown newcmd %x", bcs->hw.isar.newcmd);
+						break;
+				}
+			} else if (bcs->hw.isar.state == STFAX_ACTIV) {
+				if (test_and_clear_bit(BC_FLG_LL_OK, &bcs->Flag)) {
+					isar_sched_event(bcs, B_LL_OK);
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+					send_DLE_ETX(bcs);
+					isar_sched_event(bcs, B_LL_NOCARRIER);
+				} else {
+					ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+				}
+				bcs->hw.isar.state = STFAX_READY;
+			} else {
+				bcs->hw.isar.state = STFAX_READY;
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			}
+			break;
+		case PSEV_RSP_SILDET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILDET");
+			if (bcs->hw.isar.state == STFAX_SILDET) {
+				p1 = bcs->hw.isar.newmod;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+				bcs->hw.isar.newcmd = 0;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+					bcs->hw.isar.cmd, 1, &p1);
+				bcs->hw.isar.state = STFAX_LINE;
+			}
+			break;
+		case PSEV_RSP_SILOFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILOFF");
+			break;
+		case PSEV_RSP_FCERR:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_FCERR");
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			break;
+		default:
+			break;
+	}
+}
+
 static char debbuf[128];
 
 void
@@ -791,8 +1158,6 @@
 			} else {
 				debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
 					ireg->iis, ireg->cmsb, ireg->clsb);
-				printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n",
-					ireg->iis, ireg->cmsb, ireg->clsb);
 				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 			}
 			break;
@@ -818,12 +1183,18 @@
 		case ISAR_IIS_PSTEV:
 			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
 				rcv_mbox(cs, ireg, (u_char *)ireg->par);
-				isar_pump_status_ev(bcs, ireg->cmsb);
+				if (bcs->mode == L1_MODE_V32) {
+					isar_pump_statev_modem(bcs, ireg->cmsb);
+				} else if (bcs->mode == L1_MODE_FAX) {
+					isar_pump_statev_fax(bcs, ireg->cmsb);
+				} else {
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar IIS_PSTEV pmode %d stat %x",
+							bcs->mode, ireg->cmsb);
+				}
 			} else {
 				debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x",
 					ireg->iis, ireg->cmsb, ireg->clsb);
-				printk(KERN_WARNING"isar spurious IIS_PSTEV %x/%x/%x\n",
-					ireg->iis, ireg->cmsb, ireg->clsb);
 				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 			}
 			break;
@@ -834,8 +1205,6 @@
 			} else {
 				debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x",
 					ireg->iis, ireg->cmsb, ireg->clsb);
-				printk(KERN_WARNING"isar spurious IIS_PSTRSP %x/%x/%x\n",
-					ireg->iis, ireg->cmsb, ireg->clsb);
 				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
 			}
 			break;
@@ -870,6 +1239,17 @@
 }
 
 static void
+ftimer_handler(struct BCState *bcs) {
+	if (bcs->cs->debug)
+		debugl1(bcs->cs, "ftimer flags %04x",
+			bcs->Flag);
+	test_and_clear_bit(BC_FLG_FTI_RUN, &bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_LL_CONN, &bcs->Flag)) {
+		isar_sched_event(bcs, B_LL_CONNECT);
+	}
+}
+
+static void
 setup_pump(struct BCState *bcs) {
 	struct IsdnCardState *cs = bcs->cs;
 	u_char dps = SET_DPS(bcs->hw.isar.dpath);
@@ -879,11 +1259,7 @@
 		case L1_MODE_NULL:
 		case L1_MODE_TRANS:
 		case L1_MODE_HDLC:
-			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) {
-				if (cs->debug)
-					debugl1(cs, "isar pump bypass cfg dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
 			break;
 		case L1_MODE_V32:
 			ctrl = PMOD_DATAMODEM;
@@ -899,11 +1275,7 @@
 			param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
 			param[3] = PV32P4_UT144;
 			param[4] = PV32P5_UT144;
-			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar pump datamodem cfg dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
 			break;
 		case L1_MODE_FAX:
 			ctrl = PMOD_FAX;
@@ -914,11 +1286,11 @@
 				param[1] = PFAXP2_ATN;
 			}
 			param[0] = 6; /* 6 db */
-			if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar pump faxmodem cfg dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+			bcs->hw.isar.state = STFAX_NULL;
+			bcs->hw.isar.newcmd = 0;
+			bcs->hw.isar.newmod = 0;
+			test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag);
 			break;
 	}
 	udelay(1000);
@@ -934,37 +1306,25 @@
 	
 	switch (bcs->mode) {
 		case L1_MODE_NULL:
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) {
-				if (cs->debug)
-					debugl1(cs, "isar sart disable dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0,
+				NULL);
 			break;
 		case L1_MODE_TRANS:
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) {
-				if (cs->debug)
-					debugl1(cs, "isar sart binary dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2,
+				"\0\0");
 			break;
 		case L1_MODE_HDLC:
 		case L1_MODE_FAX:
 			param[0] = 0;
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar sart hdlc dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1,
+				param);
 			break;
 		case L1_MODE_V32:
 			ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
 			param[0] = S_P1_CHS_8;
 			param[1] = S_P2_BFT_DEF;
-			if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2, param)) {
-				if (cs->debug)
-					debugl1(cs, "isar sart v14 dp%d failed",
-						bcs->hw.isar.dpath);
-			}
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2,
+				param);
 			break;
 	}
 	udelay(1000);
@@ -1035,8 +1395,8 @@
 					&bcs->hw.isar.reg->Flags))
 					bcs->hw.isar.dpath = 1;
 				else {
-					printk(KERN_WARNING"isar modeisar analog works only with DP1\n");
-					debugl1(cs, "isar modeisar analog works only with DP1");
+					printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n");
+					debugl1(cs, "isar modeisar analog funktions only with DP1");
 					return(1);
 				}
 				break;
@@ -1060,6 +1420,107 @@
 	return(0);
 }
 
+static void
+isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) 
+{
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl = 0, nom = 0, p1 = 0;
+
+	switch(cmd) {
+		case ISDN_FAX_CLASS1_FTM:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FTH:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRM:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRH:
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+	}
+	if (ctrl)
+		sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
 void
 isar_setup(struct IsdnCardState *cs)
 {
@@ -1070,11 +1531,8 @@
 	msg = 61;
 	for (i=0; i<2; i++) {
 		/* Buffer Config */
-		if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
-			ISAR_HIS_P12CFG, 4, 1, &msg)) {
-			if (cs->debug)
-				debugl1(cs, "isar P%dCFG failed", i+1);
-		}
+		sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg);
 		cs->bcs[i].hw.isar.mml = msg;
 		cs->bcs[i].mode = 0;
 		cs->bcs[i].hw.isar.dpath = i + 1;
@@ -1141,6 +1599,7 @@
 						l1_msg_b(st, PH_ACTIVATE | REQUEST, arg);
 					break;
 				case L1_MODE_V32:
+				case L1_MODE_FAX:
 					if (modeisar(st->l1.bcs, st->l1.mode, st->l1.bc))
 						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
 					break;
@@ -1179,6 +1638,7 @@
 				debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
 		}
 	}
+	del_timer(&bcs->hw.isar.ftimer);
 }
 
 int
@@ -1200,6 +1660,9 @@
 	bcs->event = 0;
 	bcs->hw.isar.rcvidx = 0;
 	bcs->tx_cnt = 0;
+	bcs->hw.isar.ftimer.function = (void *) ftimer_handler;
+	bcs->hw.isar.ftimer.data = (long) bcs;
+	init_timer(&bcs->hw.isar.ftimer);
 	return (0);
 }
 
@@ -1220,15 +1683,66 @@
 int
 isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
 	u_long adr;
-	int features;
+	int features, i;
+	struct BCState *bcs;
 
 	if (cs->debug & L1_DEB_HSCX)
 		debugl1(cs, "isar_auxcmd cmd/ch %x/%d", ic->command, ic->arg);
 	switch (ic->command) {
+		case (ISDN_CMD_FAXCMD):
+			bcs = cs->channel[ic->arg].bcs;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_auxcmd cmd/subcmd %d/%d",
+					ic->parm.aux.cmd, ic->parm.aux.subcmd);
+			switch(ic->parm.aux.cmd) {
+				case ISDN_FAX_CLASS1_CTRL:
+					if (ic->parm.aux.subcmd == ETX)
+						test_and_set_bit(BC_FLG_DLEETX,
+							&bcs->Flag);
+					break;
+				case ISDN_FAX_CLASS1_FRM:
+				case ISDN_FAX_CLASS1_FRH:
+				case ISDN_FAX_CLASS1_FTM:
+				case ISDN_FAX_CLASS1_FTH:
+					if (ic->parm.aux.subcmd == AT_QUERY) {
+						sprintf(ic->parm.aux.para,
+							"%d", bcs->hw.isar.mod);
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_QUERY) {
+						strcpy(ic->parm.aux.para, faxmodulation_s);
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_VALUE) {
+						for(i=0;i<FAXMODCNT;i++)
+							if (faxmodulation[i]==ic->parm.aux.para[0])
+								break;
+						if ((FAXMODCNT > i) && 
+							test_bit(BC_FLG_INIT, &bcs->Flag)) {
+							isar_pump_cmd(bcs,
+								ic->parm.aux.cmd,
+								ic->parm.aux.para[0]);
+							return(0);
+						}
+					}
+					/* wrong modulation or not activ */
+					/* fall through */
+				default:
+					ic->command = ISDN_STAT_FAXIND;
+					ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR;
+					cs->iif.statcallb(ic);
+			}
+			break;
 		case (ISDN_CMD_IOCTL):
 			switch (ic->arg) {
 				case (9): /* load firmware */
-					features = ISDN_FEATURE_L2_MODEM;
+					features = ISDN_FEATURE_L2_MODEM |
+						ISDN_FEATURE_L2_FAX |
+						ISDN_FEATURE_L3_FCLASS1;
 					memcpy(&adr, ic->parm.num, sizeof(ulong));
 					if (isar_load_firmware(cs, (u_char *)adr))
 						return(1);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)