patch-pre2.0.13 linux/drivers/char/baycom.c

Next file: linux/drivers/char/pty.c
Previous file: linux/drivers/char/Config.in
Back to the patch index
Back to the overall index

diff -u --recursive --new-file pre2.0.12/linux/drivers/char/baycom.c linux/drivers/char/baycom.c
@@ -66,6 +66,11 @@
  *                  Various resource allocation cleanups
  *   0.2  12.05.96  Changed major to allocated 51. Integrated into kernel
  *                  source tree
+ *   0.3  04.06.96  Major bug fixed (forgot to wake up after write) which
+ *                  interestingly manifested only with kernel ax25
+ *                  (the slip line discipline)
+ *                  introduced bottom half and tq_baycom
+ *                  HDLC processing now done with interrupts on
  */
 
 /*****************************************************************************/
@@ -89,7 +94,8 @@
 #include <linux/ioport.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
 #include <linux/baycom.h>
 
 /* --------------------------------------------------------------------- */
@@ -106,13 +112,12 @@
  * circuitry is usually slow.
  */
 
-#define BUFLEN_RX 16384
-#define BUFLEN_TX 16384
+#define BUFLEN_RX 8192
+#define BUFLEN_TX 8192
 
 #define NR_PORTS 4
 
 #define KISS_VERBOSE
-#undef HDLC_LOOPBACK
 
 #define BAYCOM_MAGIC 0x3105bac0
 
@@ -184,10 +189,11 @@
 
 struct hdlc_state_rx {
 	int rx_state;	/* 0 = sync hunt, != 0 receiving */
-	unsigned char lastbit;
 	unsigned int bitstream;
-	unsigned int assembly;
-	
+	unsigned int bitbuf;
+	int numbits;
+	unsigned int shreg1, shreg2;
+
 	int len;
 	unsigned char *bp;
 	unsigned char buffer[BAYCOM_MAXFLEN+2];	   /* make room for CRC */
@@ -205,6 +211,10 @@
 	unsigned int current_byte;
 	unsigned char ptt;
 
+	unsigned int bitbuf;
+	int numbits;
+	unsigned int shreg1, shreg2;
+
 	int len;
 	unsigned char *bp;
 	unsigned char buffer[BAYCOM_MAXFLEN+2];		/* make room for CRC */
@@ -226,12 +236,13 @@
 	unsigned int dcd_shreg;
 	unsigned long descram;
 	unsigned long scram;
-	unsigned int tx_bits;
+	unsigned char last_rxbit;
 };
 
 struct modem_state {
 	unsigned char dcd;
 	short arb_divider;
+	unsigned char flags;
 	struct modem_state_ser12 ser12;
 	struct modem_state_par96 par96;
 };
@@ -288,6 +299,10 @@
 	int opened;
 	struct tty_struct *tty;
 
+#ifdef BAYCOM_USE_BH
+	struct tq_struct tq_receiver, tq_transmitter, tq_arbitrate;
+#endif /* BAYCOM_USE_BH */
+
 	struct packet_buffer rx_buf;
 	struct packet_buffer tx_buf;
 
@@ -316,6 +331,10 @@
 
 struct baycom_state baycom_state[NR_PORTS];
 
+#ifdef BAYCOM_USE_BH
+DECLARE_TASK_QUEUE(tq_baycom);
+#endif /* BAYCOM_USE_BH */
+
 /* --------------------------------------------------------------------- */
 
 /*
@@ -520,16 +539,29 @@
 #ifdef BAYCOM_DEBUG
 static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit)
 {
+	unsigned char new;
+
 	if (!buf) return;
-	buf->shreg <<= 1;
+	new = buf->shreg & 1;
+	buf->shreg >>= 1;
 	if (bit)
-		buf->shreg |= 1;
-	if (buf->shreg & 0x100) {
+		buf->shreg |= 0x80;
+	if (new) {
 		buf->buffer[buf->wr] = buf->shreg;
 		buf->wr = (buf->wr+1) % sizeof(buf->buffer);
-		buf->shreg = 1;
+		buf->shreg = 0x80;
 	}
 }
+
+static inline void add_bitbuffer_word(struct bit_buffer * buf, 
+				      unsigned int bits)
+{
+	buf->buffer[buf->wr] = bits & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+	buf->buffer[buf->wr] = (bits >> 8) & 0xff;
+	buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+
+}
 #endif /* BAYCOM_DEBUG */
 
 /* ---------------------------------------------------------------------- */
@@ -553,67 +585,98 @@
  * one bit per call
  */
 
-static void hdlc_rx_bit(struct baycom_state *bc, unsigned int bit)
+static inline int hdlc_rx_add_bytes(struct baycom_state *bc, 
+				    unsigned int bits, int num)
 {
+	int added = 0;
+	while (bc->hdlc_rx.rx_state && num >= 8) {
+		if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
+			bc->hdlc_rx.rx_state = 0;
+			return 0;
+		}
+		*bc->hdlc_rx.bp++ = bits >> (32-num);
+		bc->hdlc_rx.len++;
+		num -= 8;
+		added += 8;
+	}
+	return added;
+}
+
+static inline void hdlc_rx_flag(struct baycom_state *bc)
+{
+	if (bc->hdlc_rx.len < 4) 
+		return;
+	if (!check_crc_ccitt(bc->hdlc_rx.buffer, bc->hdlc_rx.len)) 
+		return;
+       	bc->stat.rx_packets++;
+	if (!store_kiss_packet(&bc->rx_buf,
+			       bc->hdlc_rx.buffer,
+			       bc->hdlc_rx.len-2))
+		bc->stat.rx_bufferoverrun++;
+}
+
+static void hdlc_rx_word(struct baycom_state *bc, unsigned int word)
+{
+	int i;
+	unsigned int mask1, mask2, mask3, mask4, mask5, mask6;
+	
 	if (!bc) return;
 
-	bc->hdlc_rx.bitstream <<= 1;
-	if (bit)
-		bc->hdlc_rx.bitstream |= 1;
+	word &= 0xffff;
 #ifdef BAYCOM_DEBUG
-	add_bitbuffer(&bc->bitbuf_hdlc, bc->hdlc_rx.bitstream & 1);
+	add_bitbuffer_word(&bc->bitbuf_hdlc, word);
 #endif /* BAYCOM_DEBUG */
-	if(bc->hdlc_rx.rx_state) {
-		if ((bc->hdlc_rx.bitstream & 0x3f) != 0x3e) {
-			/* not a stuffed bit */
-			if (bc->hdlc_rx.bitstream & 1)
-				bc->hdlc_rx.assembly |= 0x100;
-			if (bc->hdlc_rx.assembly & 1) {
-				/* store byte */
-				if (bc->hdlc_rx.len >= sizeof(bc->hdlc_rx.buffer)) {
-					bc->hdlc_rx.rx_state = 0;
-				} else {
-					*bc->hdlc_rx.bp++ = bc->hdlc_rx.assembly>>1;
-					bc->hdlc_rx.len++;
-					bc->hdlc_rx.assembly = 0x80;
-				}
-			} else {
-				bc->hdlc_rx.assembly >>= 1;
-			}
-		}
-		if ((bc->hdlc_rx.bitstream & 0x7f) == 0x7e) {
-			if (bc->hdlc_rx.len >= 4) {
-				if (check_crc_ccitt(bc->hdlc_rx.buffer,bc->hdlc_rx.len)) {
-					bc->stat.rx_packets++;
-					if (!store_kiss_packet(&bc->rx_buf,bc->hdlc_rx.buffer,bc->hdlc_rx.len-2))
-						bc->stat.rx_bufferoverrun++;
-				}
+       	bc->hdlc_rx.bitstream >>= 16;
+	bc->hdlc_rx.bitstream |= word << 16;
+	bc->hdlc_rx.bitbuf >>= 16;
+	bc->hdlc_rx.bitbuf |= word << 16;
+	bc->hdlc_rx.numbits += 16;
+	for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+	    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
+	    i >= 0; 
+	    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
+	    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+		if ((bc->hdlc_rx.bitstream & mask1) == mask1)
+			bc->hdlc_rx.rx_state = 0; /* abort received */
+		else if ((bc->hdlc_rx.bitstream & mask2) == mask3) {
+			/* flag received */
+			if (bc->hdlc_rx.rx_state) {
+				hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf << 
+						  (8 + i), bc->hdlc_rx.numbits
+						  - 8 - i);
+				hdlc_rx_flag(bc);
 			}
 			bc->hdlc_rx.len = 0;
 			bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
-			bc->hdlc_rx.assembly = 0x80;
-		}
-		if ((bc->hdlc_rx.bitstream & 0x7f) == 0x7f)
-			bc->hdlc_rx.rx_state = 0;
-	} else {
-		if ((bc->hdlc_rx.bitstream & 0x7f) == 0x7e) {
-			bc->hdlc_rx.len = 0;
-			bc->hdlc_rx.bp = bc->hdlc_rx.buffer;
-			bc->hdlc_rx.assembly = 0x80;
 			bc->hdlc_rx.rx_state = 1;
+			bc->hdlc_rx.numbits = i;
+		} else if ((bc->hdlc_rx.bitstream & mask4) == mask5) {
+			/* stuffed bit */
+			bc->hdlc_rx.numbits--;
+			bc->hdlc_rx.bitbuf = (bc->hdlc_rx.bitbuf & (~mask6)) |
+				((bc->hdlc_rx.bitbuf & mask6) << 1);
 		}
 	}
+	bc->hdlc_rx.numbits -= hdlc_rx_add_bytes(bc, bc->hdlc_rx.bitbuf,
+						 bc->hdlc_rx.numbits);
 }
 
 /* ---------------------------------------------------------------------- */
 
-static unsigned char hdlc_tx_bit(struct baycom_state *bc)
+static unsigned int hdlc_tx_word(struct baycom_state *bc)
 {
-	unsigned char bit;
+	unsigned int mask1, mask2, mask3;
+	int i;
 
 	if (!bc || !bc->hdlc_tx.ptt)
 		return 0;
-	for(;;) {
+	for (;;) {
+		if (bc->hdlc_tx.numbits >= 16) {
+			unsigned int ret = bc->hdlc_tx.bitbuf & 0xffff;
+			bc->hdlc_tx.bitbuf >>= 16;
+			bc->hdlc_tx.numbits -= 16;
+			return ret;
+		}
 		switch (bc->hdlc_tx.tx_state) {
 		default:
 			bc->hdlc_tx.ptt = 0;
@@ -621,80 +684,68 @@
 			return 0;
 		case 0:
 		case 1:
-			if (bc->hdlc_tx.current_byte > 1) {
-				/*
-				 * return bit 
-				 */
-				bit = bc->hdlc_tx.current_byte & 1;
-				bc->hdlc_tx.current_byte >>= 1;
-				return bit;
-			}
-			/*
-			 * get new bit 
-			 */
 			if (bc->hdlc_tx.numflags) {
 				bc->hdlc_tx.numflags--;
-				bc->hdlc_tx.current_byte = 0x17e;
-			} else {
-				if (bc->hdlc_tx.tx_state == 1) {
-					bc->hdlc_tx.ptt = 0;
-					return 0;
-				}
-				get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
-					   &bc->hdlc_tx.len);
-				if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
-					bc->hdlc_tx.tx_state = 1;
-					bc->hdlc_tx.current_byte = 0;
-					bc->hdlc_tx.numflags = tenms_to_flags
-						(bc, bc->ch_params.tx_tail);
-				} else if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
-					bc->hdlc_tx.tx_state = 0;
-					bc->hdlc_tx.current_byte = 0;
-					bc->hdlc_tx.numflags = 1;
-					ack_packet(&bc->tx_buf);
-				} else {
-					memcpy(bc->hdlc_tx.buffer,
-					       bc->hdlc_tx.bp, 
-					       bc->hdlc_tx.len);
-					ack_packet(&bc->tx_buf);
-					bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
-					append_crc_ccitt(bc->hdlc_tx.buffer,
-							 bc->hdlc_tx.len);
-					/* the appended CRC */
-					bc->hdlc_tx.len += 2; 
-					bc->hdlc_tx.tx_state = 2;
-					bc->hdlc_tx.current_byte = 0;
-					bc->hdlc_tx.bitstream = 0;
-					bc->stat.tx_packets++;
-				}
+				bc->hdlc_tx.bitbuf |= 
+					0x7e7e << bc->hdlc_tx.numbits;
+				bc->hdlc_tx.numbits += 16;
+				break;
 			}
-			break;
-		case 2:
-			if ((bc->hdlc_tx.bitstream & 0x1f) == 0x1f) {
-				/*
-				 * bit stuffing
-				 */
-				bc->hdlc_tx.bitstream <<= 1;
+			if (bc->hdlc_tx.tx_state == 1) {
+				bc->hdlc_tx.ptt = 0;
 				return 0;
 			}
-			if (bc->hdlc_tx.current_byte > 1) {
-				/*
-				 * return bit 
-				 */
-				bc->hdlc_tx.bitstream <<= 1;
-				bit = bc->hdlc_tx.current_byte & 1;
-				bc->hdlc_tx.bitstream |= bit;
-				bc->hdlc_tx.current_byte >>= 1;
-				return bit;
+			get_packet(&bc->tx_buf, &bc->hdlc_tx.bp,
+				   &bc->hdlc_tx.len);
+			if (!bc->hdlc_tx.bp || !bc->hdlc_tx.len) {
+				bc->hdlc_tx.tx_state = 1;
+				bc->hdlc_tx.numflags = tenms_to_flags
+					(bc, bc->ch_params.tx_tail);
+				break;
+			}
+			if (bc->hdlc_tx.len >= BAYCOM_MAXFLEN) {
+				bc->hdlc_tx.tx_state = 0;
+				bc->hdlc_tx.numflags = 1;
+				ack_packet(&bc->tx_buf);
+				break;
 			}
+			memcpy(bc->hdlc_tx.buffer, bc->hdlc_tx.bp, 
+			       bc->hdlc_tx.len);
+			ack_packet(&bc->tx_buf);
+			bc->hdlc_tx.bp = bc->hdlc_tx.buffer;
+			append_crc_ccitt(bc->hdlc_tx.buffer, bc->hdlc_tx.len);
+			/* the appended CRC */
+			bc->hdlc_tx.len += 2; 
+			bc->hdlc_tx.tx_state = 2;
+			bc->hdlc_tx.bitstream = 0;
+			bc->stat.tx_packets++;
+			break;
+		case 2:
 			if (!bc->hdlc_tx.len) {
 				bc->hdlc_tx.tx_state = 0;
-				bc->hdlc_tx.current_byte = 0;
 				bc->hdlc_tx.numflags = 1;
-			} else {
-				bc->hdlc_tx.len--;
-				bc->hdlc_tx.current_byte = 0x100 | 
-					(*bc->hdlc_tx.bp++);
+				break;
+			}
+			bc->hdlc_tx.len--;
+			bc->hdlc_tx.bitbuf |= *bc->hdlc_tx.bp <<
+				bc->hdlc_tx.numbits;
+			bc->hdlc_tx.bitstream >>= 8;
+			bc->hdlc_tx.bitstream |= (*bc->hdlc_tx.bp++) << 16;
+			mask1 = 0x1f000;
+			mask2 = 0x10000;
+			mask3 = 0xffffffff >> (31-bc->hdlc_tx.numbits);
+			bc->hdlc_tx.numbits += 8;
+			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
+			    mask3 = (mask3 << 1) | 1) {
+				if ((bc->hdlc_tx.bitstream & mask1) != mask1) 
+					continue;
+				bc->hdlc_tx.bitstream &= ~mask2;
+				bc->hdlc_tx.bitbuf = 
+					(bc->hdlc_tx.bitbuf & mask3) |
+						((bc->hdlc_tx.bitbuf & 
+						 (~mask3)) << 1);
+				bc->hdlc_tx.numbits++;
+				mask3 = (mask3 << 1) | 1;
 			}
 			break;
 		}
@@ -720,7 +771,7 @@
 	
 	if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd)
 		return;
-	get_packet(&bc->tx_buf,&bp,&len);
+	get_packet(&bc->tx_buf, &bp, &len);
 	if (!bp || !len)
 		return;
 	
@@ -845,18 +896,34 @@
 		 * since this may take quite long
 		 */
 		outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase));
-		if (bc->calibrate > 0) {
-			bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
-			bc->calibrate--;
-			return;
-		}
+		if (bc->hdlc_tx.shreg1 <= 1) {
+			if (bc->calibrate > 0) {
+				bc->hdlc_tx.shreg1 = 0x10000;
+				bc->calibrate--;
+			} else {
+#ifdef BAYCOM_USE_BH
+				bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
+				bc->hdlc_tx.shreg2 = 0;
+				queue_task_irq_off(&bc->tq_transmitter, 
+						   &tq_baycom);
+				mark_bh(BAYCOM_BH);
 #ifdef HDLC_LOOPBACK
-		hdlc_rx_bit(bc, bc->modem.ser12.tx_bit == 
-			    bc->modem.ser12.last_rxbit);
-		bc->modem.ser12.last_rxbit = bc->modem.ser12.tx_bit;
+				bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
+				queue_task_irq_off(&bc->tq_receiver, 
+						   &tq_baycom);
 #endif /* HDLC_LOOPBACK */
-		if (!hdlc_tx_bit(bc))
+#else /* BAYCOM_USE_BH */
+				bc->hdlc_tx.shreg1 = hdlc_tx_word(bc) 
+					| 0x10000;
+#ifdef HDLC_LOOPBACK
+				hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
+#endif /* HDLC_LOOPBACK */
+#endif /* BAYCOM_USE_BH */
+			}	
+		}
+		if (!(bc->hdlc_tx.shreg1 & 1))
 			bc->modem.ser12.tx_bit = !bc->modem.ser12.tx_bit;
+		bc->hdlc_tx.shreg1 >>= 1;
 		return;
 	}
 	/*
@@ -928,8 +995,10 @@
 				ser12_set_divisor(bc, 4);
 				break;
 			}
-			hdlc_rx_bit(bc, bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit);
+			bc->hdlc_rx.shreg1 >>= 1;
+			if (bc->modem.ser12.last_sample == 
+			    bc->modem.ser12.last_rxbit)
+				bc->hdlc_rx.shreg1 |= 0x10000;
 			bc->modem.ser12.last_rxbit = 
 				bc->modem.ser12.last_sample;
 		}
@@ -965,15 +1034,32 @@
 				ser12_set_divisor(bc, 6);
 				break;
 			}
-			hdlc_rx_bit(bc, bc->modem.ser12.last_sample == 
-			    bc->modem.ser12.last_rxbit);
+			bc->hdlc_rx.shreg1 >>= 1;
+			if (bc->modem.ser12.last_sample == 
+			    bc->modem.ser12.last_rxbit)
+				bc->hdlc_rx.shreg1 |= 0x10000;
 			bc->modem.ser12.last_rxbit = 
 				bc->modem.ser12.last_sample;
 		}
 		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
 	}
+	if (bc->hdlc_rx.shreg1 & 1) {
+#ifdef BAYCOM_USE_BH
+		bc->hdlc_rx.shreg2 = (bc->hdlc_rx.shreg1 >> 1) | 0x10000;
+		queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
+		mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
+		hdlc_rx_word(bc, bc->hdlc_rx.shreg1 >> 1);
+#endif /* BAYCOM_USE_BH */
+		bc->hdlc_rx.shreg1 = 0x10000;
+	}
 	if (--bc->modem.arb_divider <= 0) {
+#ifdef BAYCOM_USE_BH
+		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
+		mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
 		tx_arbitrate(bc);
+#endif /* BAYCOM_USE_BH */
 		bc->modem.arb_divider = bc->ch_params.slottime * 
 			SER12_ARB_DIVIDER(bc);
 	}
@@ -1126,7 +1212,7 @@
 {
 	register struct baycom_state *bc = (struct baycom_state *)dev_id;
 	int i;
-	unsigned int data, mask, mask2;
+	unsigned int data, rawdata, mask, mask2;
 	
 	if (!bc || bc->magic != BAYCOM_MAGIC)
 		return;
@@ -1144,12 +1230,12 @@
 		 * transmitter, since this may take quite long
 		 * do the differential encoder and the scrambler on the fly
 		 */
-		data = bc->modem.par96.tx_bits;
-		for(i = 0; i < PAR96_BURSTBITS; i++, data <<= 1) {
+		data = bc->hdlc_tx.shreg1;
+		for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
 			unsigned char val = PAR97_POWER;
 			bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
 						 (bc->modem.par96.scram & 1));
-			if (!(data & 0x8000))
+			if (!(data & 1))
 				bc->modem.par96.scram ^= 1;
 			if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
 				bc->modem.par96.scram ^= 
@@ -1160,26 +1246,31 @@
 			outb(val | PAR96_BURST, LPT_DATA(bc->iobase));
 		}
 		if (bc->calibrate > 0) {
-			bc->modem.par96.tx_bits = 0;
+			bc->hdlc_tx.shreg1 = 0x10000;
 			bc->calibrate--;
-			return;
-		}
+		} else {
+#ifdef BAYCOM_USE_BH
+			bc->hdlc_tx.shreg1 = bc->hdlc_tx.shreg2;
+			bc->hdlc_tx.shreg2 = 0;
+			queue_task_irq_off(&bc->tq_transmitter, &tq_baycom);
+			mark_bh(BAYCOM_BH);
 #ifdef HDLC_LOOPBACK
-		for(mask = 0x8000, i = 0; i < PAR96_BURSTBITS; i++, mask >>= 1)
-			hdlc_rx_bit(bc, bc->modem.par96.tx_bits & mask);
+			bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
+			queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
 #endif /* HDLC_LOOPBACK */
-		bc->modem.par96.tx_bits = 0;
-		for(i = 0; i < PAR96_BURSTBITS; i++) {
-			bc->modem.par96.tx_bits <<= 1;
-			if (hdlc_tx_bit(bc))
-				bc->modem.par96.tx_bits |= 1;
+#else /* BAYCOM_USE_BH */
+			bc->hdlc_tx.shreg1 = hdlc_tx_word(bc);
+#ifdef HDLC_LOOPBACK
+			hdlc_rx_word(bc, bc->hdlc_tx.shreg1);
+#endif /* HDLC_LOOPBACK */
+#endif /* BAYCOM_USE_BH */
 		}
 		return;
 	}
 	/*
 	 * do receiver; differential decode and descramble on the fly
 	 */
-	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+	for(rawdata = data = i = 0; i < PAR96_BURSTBITS; i++) {
 		unsigned int descx;
 		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
 		if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT)
@@ -1190,19 +1281,31 @@
 		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase));
 		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
 			  (descx >> PAR96_DESCRAM_TAPSH2));
-		data <<= 1;
-		data |= !(descx & 1);
+		if (descx & 1)
+			bc->modem.par96.last_rxbit = 
+				!bc->modem.par96.last_rxbit;
+		data >>= 1;
+		if (bc->modem.par96.last_rxbit)
+			data |= 0x8000;
+		rawdata <<= 1;
+		rawdata |= !(descx & 1);
 		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, 
 		     LPT_DATA(bc->iobase));
 	}
-	for(mask = 0x8000, i = 0; i < PAR96_BURSTBITS; i++, mask >>= 1)
-		hdlc_rx_bit(bc, data & mask);
+#ifdef BAYCOM_USE_BH
+	bc->hdlc_rx.shreg2 = bc->hdlc_rx.shreg1;
+	bc->hdlc_rx.shreg1 = data | 0x10000;
+	queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
+	mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
+	hdlc_rx_word(bc, data);
+#endif /* BAYCOM_USE_BH */
 	/*
 	 * do DCD algorithm
 	 */
 	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
 		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg << 16)
-			| data;
+			| rawdata;
 		/* search for flags and set the dcd counter appropriately */
 		for(mask = 0x7f8000, mask2 = 0x3f0000, i = 0; 
 		    i < PAR96_BURSTBITS; i++, mask >>= 1, mask2 >>= 1)
@@ -1223,7 +1326,12 @@
 		bc->modem.dcd = !!(inb(LPT_STATUS(bc->iobase)) & PAR96_DCD);
 	}
 	if (--bc->modem.arb_divider <= 0) {
+#ifdef BAYCOM_USE_BH
+		queue_task_irq_off(&bc->tq_arbitrate, &tq_baycom);
+		mark_bh(BAYCOM_BH);
+#else /* BAYCOM_USE_BH */
 		tx_arbitrate(bc);
+#endif /* BAYCOM_USE_BH */
 		bc->modem.arb_divider = bc->ch_params.slottime * 6;
 	}
 }
@@ -1320,6 +1428,58 @@
 
 /* --------------------------------------------------------------------- */
 /*
+ * ===================== Bottom half (soft interrupt) ====================
+ */
+
+#ifdef BAYCOM_USE_BH
+static void bh_receiver(void *private)
+{
+	struct baycom_state *bc = (struct baycom_state *)private;
+	unsigned int temp;
+
+	if (!bc || bc->magic != BAYCOM_MAGIC)
+		return;
+	if (!bc->hdlc_rx.shreg2)
+		return;
+	temp = bc->hdlc_rx.shreg2;
+	bc->hdlc_rx.shreg2 = 0;
+	hdlc_rx_word(bc, temp);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void bh_transmitter(void *private)
+{
+	struct baycom_state *bc = (struct baycom_state *)private;
+
+	if (!bc || bc->magic != BAYCOM_MAGIC)
+		return;
+	if (bc->hdlc_tx.shreg2)
+		return;
+	bc->hdlc_tx.shreg2 = hdlc_tx_word(bc) | 0x10000;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void bh_arbitrate(void *private)
+{
+	struct baycom_state *bc = (struct baycom_state *)private;
+
+	if (!bc || bc->magic != BAYCOM_MAGIC)
+		return;
+	tx_arbitrate(bc);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void baycom_bottom_half(void)
+{
+	run_task_queue(&tq_baycom);
+}
+#endif /* BAYCOM_USE_BH */
+
+/* --------------------------------------------------------------------- */
+/*
  * ===================== TTY interface routines ==========================
  */
 
@@ -1369,7 +1529,7 @@
 			break;
 		bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1];
 #ifdef KISS_VERBOSE
-		printk("KERN_INFO baycom: p-persistence = %u\n", 
+		printk(KERN_INFO "baycom: p-persistence = %u\n", 
 		       bc->ch_params.ppersist);
 #endif /* KISS_VERBOSE */
 		break;
@@ -1379,7 +1539,7 @@
 			break;
 		bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1];
 #ifdef KISS_VERBOSE
-		printk("baycom: slottime = %ums\n", 
+		printk(KERN_INFO "baycom: slottime = %ums\n", 
 		       bc->ch_params.slottime * 10);
 #endif /* KISS_VERBOSE */
 		break;
@@ -1477,6 +1637,10 @@
 		for(c = count, bp = buf; c > 0; c--,bp++)
 			baycom_put_char(tty, *bp);
 	}
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+	wake_up_interruptible(&tty->write_wait);
 	return count;
 }
 
@@ -1513,10 +1677,10 @@
 		return 0;
 	if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer"))
 		return 0;
-		
-	cnt = bc->rx_buf.wr - bc->rx_buf.rd;
+
+	cnt = bc->tx_buf.wr - bc->tx_buf.rd;
 	if (cnt < 0)
-		cnt += bc->rx_buf.buflen;
+		cnt += bc->tx_buf.buflen;
 		
 	return cnt;
 }
@@ -1669,7 +1833,7 @@
 		
 	case BAYCOMCTL_CALIBRATE:
 		bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ?
-				       600 : 1200);
+				       600 : 75);
 		return 0;
 
 	case BAYCOMCTL_GETPARAMS:
@@ -1917,16 +2081,28 @@
 
 #ifdef BAYCOM_DEBUG
 	bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0;
-	bc->bitbuf_channel.shreg = 1;
+	bc->bitbuf_channel.shreg = 0x80;
 
 	bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0;
-	bc->bitbuf_hdlc.shreg = 1;
+	bc->bitbuf_hdlc.shreg = 0x80;
 #endif /* BAYCOM_DEBUG */
 
 	bc->kiss_decode.dec_state = bc->kiss_decode.escaped = 
 	bc->kiss_decode.wr = 0;
 
 	bc->ch_params = dflt_ch_params;
+
+#ifdef BAYCOM_USE_BH
+	bc->tq_receiver.next = bc->tq_transmitter.next =
+		bc->tq_arbitrate.next = NULL;
+	bc->tq_receiver.sync = bc->tq_transmitter.sync =
+		bc->tq_arbitrate.sync = 0;
+	bc->tq_receiver.data = bc->tq_transmitter.data =
+		bc->tq_arbitrate.data = bc;
+	bc->tq_receiver.routine = bh_receiver;
+	bc->tq_transmitter.routine = bh_transmitter;
+	bc->tq_arbitrate.routine = bh_arbitrate;
+#endif /* BAYCOM_USE_BH */
 }
 
 static void init_datastructs(void)
@@ -1963,6 +2139,12 @@
 	 */
 	init_datastructs();
 	/*
+	 * initialize bottom half handler
+ 	 */
+#ifdef BAYCOM_USE_BH
+	init_bh(BAYCOM_BH, baycom_bottom_half);
+#endif /* BAYCOM_USE_BH */
+	/*
 	 * register the driver as tty driver
 	 */
 	memset(&baycom_driver, 0, sizeof(struct tty_driver));
@@ -2080,7 +2262,7 @@
 	if (i)
 		return i;
 
-	printk(KERN_INFO "baycom: version 0.2; "
+	printk(KERN_INFO "baycom: version 0.3; "
 	       "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n");
 
 	return 0;
@@ -2092,10 +2274,6 @@
 {
 	int i;
 
-#if 0
-	if (MOD_IN_USE)
-		printk(KERN_INFO "baycom: device busy, remove delayed\n");
-#endif
 	printk(KERN_INFO "baycom: cleanup_module called\n");
 
 	if (tty_unregister_driver(&baycom_driver))

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this