patch-2.0.35 linux/drivers/char/baycom.c
Next file: linux/drivers/char/ftape/ftape-bsm.h
Previous file: linux/drivers/char/apm_bios.c
Back to the patch index
Back to the overall index
- Lines: 2328
- Date:
Mon Jul 13 13:47:29 1998
- Orig file:
v2.0.34/linux/drivers/char/baycom.c
- Orig date:
Mon Jul 8 00:21:45 1996
diff -u --recursive --new-file v2.0.34/linux/drivers/char/baycom.c linux/drivers/char/baycom.c
@@ -1,2327 +0,0 @@
-/*****************************************************************************/
-
-/*
- * baycom.c -- baycom ser12 and par96 radio modem driver.
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- *
- * Supported modems
- *
- * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only
- * of a modulator/demodulator chip, usually a TI TCM3105. The computer
- * is responsible for regenerating the receiver bit clock, as well as
- * for handling the HDLC protocol. The modem connects to a serial port,
- * hence the name. Since the serial port is not used as an async serial
- * port, the kernel driver for serial ports cannot be used, and this
- * driver only supports standard serial hardware (8250, 16450, 16550)
- *
- * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard.
- * The modem does all the filtering and regenerates the receiver clock.
- * Data is transferred from and to the PC via a shift register.
- * The shift register is filled with 16 bits and an interrupt is
- * signalled. The PC then empties the shift register in a burst. This
- * modem connects to the parallel port, hence the name. The modem
- * leaves the implementation of the HDLC protocol and the scrambler
- * polynomial to the PC.
- *
- * par97: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
- * modem is protocol compatible to par96, but uses only three low
- * power ICs and can therefore be fed from the parallel port and
- * does not require an additional power supply.
- *
- *
- * Command line options (insmod command line)
- *
- * major major number the driver should use; default 60
- * modem modem type of the first channel (minor 0); 1=ser12,
- * 2=par96/par97, any other value invalid
- * iobase base address of the port; common values are for ser12 0x3f8,
- * 0x2f8, 0x3e8, 0x2e8 and for par96/par97 0x378, 0x278, 0x3bc
- * irq interrupt line of the port; common values are for ser12 3,4
- * and for par96/par97 7
- * options 0=use hardware DCD, 1=use software DCD
- *
- *
- * History:
- * 0.1 03.05.96 Renamed from ser12 0.5 and added support for par96
- * 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
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/version.h>
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <asm/segment.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#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>
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_TYPE_NORMAL 0 /* not used */
-#define TTY_DRIVER_TYPE_BAYCOM 6
-
-/*
- * ser12 options:
- * BAYCOM_OPTIONS_SOFTDCD: if undefined, you must use the transmitters
- * hardware carrier detect circuitry, the driver will report DCD as soon as
- * there are transitions on the input line. Advantage: lower interrupt load
- * on the system. Disadvantage: slower, since hardware carrier detect
- * circuitry is usually slow.
- */
-
-#define BUFLEN_RX 8192
-#define BUFLEN_TX 8192
-
-#define NR_PORTS 4
-
-#define KISS_VERBOSE
-
-#define BAYCOM_MAGIC 0x3105bac0
-
-/* --------------------------------------------------------------------- */
-
-/*
- * user settable parameters (from the command line)
- */
-#ifndef MODULE
-static
-#endif /* MODULE */
-int major = BAYCOM_MAJOR;
-
-/* --------------------------------------------------------------------- */
-
-static struct tty_struct *baycom_table[NR_PORTS];
-static struct termios *baycom_termios[NR_PORTS];
-static struct termios *baycom_termios_locked[NR_PORTS];
-
-static int baycom_refcount;
-
-static struct tty_driver baycom_driver;
-
-static struct {
- int modem, iobase, irq, options;
-} baycom_ports[NR_PORTS] = { { BAYCOM_MODEM_INVALID, 0, 0, 0, }, };
-
-/* --------------------------------------------------------------------- */
-
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define SER12_EXTENT 8
-
-#define LPT_DATA(iobase) (iobase+0)
-#define LPT_STATUS(iobase) (iobase+1)
-#define LPT_CONTROL(iobase) (iobase+2)
-#define LPT_IRQ_ENABLE 0x10
-#define PAR96_BURSTBITS 16
-#define PAR96_BURST 4
-#define PAR96_PTT 2
-#define PAR96_TXBIT 1
-#define PAR96_ACK 0x40
-#define PAR96_RXBIT 0x20
-#define PAR96_DCD 0x10
-#define PAR97_POWER 0xf8
-
-#define PAR96_EXTENT 3
-
-/* ---------------------------------------------------------------------- */
-
-struct access_params {
- int tx_delay;
- int tx_tail;
- int slottime;
- int ppersist;
- int fulldup;
-};
-
-struct hdlc_state_rx {
- int rx_state; /* 0 = sync hunt, != 0 receiving */
- unsigned int bitstream;
- unsigned int bitbuf;
- int numbits;
- unsigned int shreg1, shreg2;
-
- int len;
- unsigned char *bp;
- unsigned char buffer[BAYCOM_MAXFLEN+2]; /* make room for CRC */
-};
-
-struct hdlc_state_tx {
- /*
- * 0 = send flags
- * 1 = send txtail (flags)
- * 2 = send packet
- */
- int tx_state;
- int numflags;
- unsigned int bitstream;
- 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 */
-};
-
-struct modem_state_ser12 {
- unsigned char last_sample;
- unsigned char interm_sample;
- unsigned int bit_pll;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
- unsigned char last_rxbit;
- unsigned char tx_bit;
-};
-
-struct modem_state_par96 {
- int dcd_count;
- unsigned int dcd_shreg;
- unsigned long descram;
- unsigned long scram;
-};
-
-struct modem_state {
- unsigned char dcd;
- short arb_divider;
- unsigned char flags;
- struct modem_state_ser12 ser12;
- struct modem_state_par96 par96;
-};
-
-struct packet_buffer {
- unsigned int rd;
- unsigned int wr;
-
- unsigned int buflen;
- unsigned char *buffer;
-};
-
-struct packet_hdr {
- unsigned int next;
- unsigned int len;
- /* packet following */
-};
-
-#ifdef BAYCOM_DEBUG
-struct bit_buffer {
- unsigned int rd;
- unsigned int wr;
- unsigned int shreg;
- unsigned char buffer[64];
-};
-
-struct debug_vals {
- unsigned long last_jiffies;
- unsigned cur_intcnt;
- unsigned last_intcnt;
- int cur_pllcorr;
- int last_pllcorr;
-};
-#endif /* BAYCOM_DEBUG */
-
-struct kiss_decode {
- unsigned char dec_state; /* 0 = hunt FEND */
- unsigned char escaped;
- unsigned char pkt_buf[BAYCOM_MAXFLEN+1];
- unsigned int wr;
-};
-
-/* ---------------------------------------------------------------------- */
-
-struct baycom_state {
- int magic;
-
- unsigned char modem_type;
-
- unsigned int iobase;
- unsigned int irq;
- unsigned int options;
-
- 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;
-
- struct access_params ch_params;
-
- struct hdlc_state_rx hdlc_rx;
- struct hdlc_state_tx hdlc_tx;
-
- int calibrate;
-
- struct modem_state modem;
-
-#ifdef BAYCOM_DEBUG
- struct bit_buffer bitbuf_channel;
- struct bit_buffer bitbuf_hdlc;
-
- struct debug_vals debug_vals;
-#endif /* BAYCOM_DEBUG */
-
- struct kiss_decode kiss_decode;
-
- struct baycom_statistics stat;
-};
-
-/* --------------------------------------------------------------------- */
-
-struct baycom_state baycom_state[NR_PORTS];
-
-#ifdef BAYCOM_USE_BH
-DECLARE_TASK_QUEUE(tq_baycom);
-#endif /* BAYCOM_USE_BH */
-
-/* --------------------------------------------------------------------- */
-
-/*
- * the CRC routines are stolen from WAMPES
- * by Dieter Deyke
- */
-
-static const unsigned short crc_ccitt_table[] = {
- 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-
-/*---------------------------------------------------------------------------*/
-
-static inline void append_crc_ccitt(unsigned char *buffer, int len)
-{
- unsigned int crc = 0xffff;
-
- for (;len>0;len--)
- crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
- crc ^= 0xffff;
- *buffer++ = crc;
- *buffer++ = crc >> 8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static inline int check_crc_ccitt(const unsigned char *buf,int cnt)
-{
- unsigned int crc = 0xffff;
-
- for (; cnt > 0; cnt--)
- crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
- return (crc & 0xffff) == 0xf0b8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-#if 0
-static int calc_crc_ccitt(const unsigned char *buf,int cnt)
-{
- unsigned int crc = 0xffff;
-
- for (; cnt > 0; cnt--)
- crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
- crc ^= 0xffff;
- return (crc & 0xffff);
-}
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-static int store_packet(struct packet_buffer *buf, unsigned char *data,
- char from_user, unsigned int len)
-{
- unsigned int free;
- struct packet_hdr *hdr;
- unsigned int needed = sizeof(struct packet_hdr)+len;
-
- free = buf->rd-buf->wr;
- if(buf->rd <= buf->wr) {
- free = buf->buflen - buf->wr;
- if((free < needed) && (buf->rd >= needed)) {
- hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
- hdr->next = 0;
- hdr->len = 0;
- buf->wr = 0;
- free = buf->rd;
- }
- }
- if(free < needed) return 0; /* buffer overrun */
- hdr = (struct packet_hdr *)(buf->buffer+buf->wr);
- if (from_user)
- memcpy_fromfs(hdr+1,data,len);
- else
- memcpy(hdr+1,data,len);
- hdr->len = len;
- hdr->next = buf->wr+needed;
- if (hdr->next + sizeof(struct packet_hdr) >= buf->buflen)
- hdr->next = 0;
- buf->wr = hdr->next;
- return 1;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void get_packet(struct packet_buffer *buf, unsigned char **data,
- unsigned int *len)
-{
- struct packet_hdr *hdr;
-
- *data = NULL;
- *len = 0;
- if (buf->rd == buf->wr)
- return;
- hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
- while (!(hdr->len)) {
- buf->rd = hdr->next;
- if (buf->rd == buf->wr)
- return;
- hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
- }
- *data = (unsigned char *)(hdr+1);
- *len = hdr->len;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void ack_packet(struct packet_buffer *buf)
-{
- struct packet_hdr *hdr;
-
- if (buf->rd == buf->wr)
- return;
- hdr = (struct packet_hdr *)(buf->buffer+buf->rd);
- buf->rd = hdr->next;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int store_kiss_packet(struct packet_buffer *buf, unsigned char *data,
- unsigned int len)
-{
- unsigned char *bp = data;
- int ln = len;
- /*
- * variables of buf
- */
- unsigned int rd;
- unsigned int wr;
- unsigned int buflen;
- unsigned char *buffer;
-
- if (!len || !data || !buf)
- return 0;
- buflen = buf->buflen;
- rd = buf->rd;
- wr = buf->wr;
- buffer = buf->buffer;
-
-#define ADD_CHAR(c) {\
- buffer[wr++] = c;\
- if (wr >= buflen) wr = 0;\
- if (wr == rd) return 0;\
- }
-#define ADD_KISSCHAR(c) {\
- if (((c) & 0xff) == KISS_FEND) {\
- ADD_CHAR(KISS_FESC);\
- ADD_CHAR(KISS_TFEND);\
- } else if (((c) & 0xff) == KISS_FESC) {\
- ADD_CHAR(KISS_FESC);\
- ADD_CHAR(KISS_TFESC);\
- } else {\
- ADD_CHAR(c);\
- }\
- }
-
- ADD_CHAR(KISS_FEND);
- ADD_KISSCHAR(KISS_CMD_DATA);
- for(; ln > 0; ln--,bp++) {
- ADD_KISSCHAR(*bp);
- }
- ADD_CHAR(KISS_FEND);
- buf->wr = wr;
-#undef ADD_CHAR
-#undef ADD_KISSCHAR
- return 1;
-}
-
-/* ---------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static inline void add_bitbuffer(struct bit_buffer * buf, unsigned int bit)
-{
- unsigned char new;
-
- if (!buf) return;
- new = buf->shreg & 1;
- buf->shreg >>= 1;
- if (bit)
- buf->shreg |= 0x80;
- if (new) {
- buf->buffer[buf->wr] = buf->shreg;
- buf->wr = (buf->wr+1) % sizeof(buf->buffer);
- 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 */
-
-/* ---------------------------------------------------------------------- */
-
-static inline unsigned int tenms_to_2flags(struct baycom_state *bc,
- unsigned int tenms)
-{
- switch (bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- return tenms * 3 / 4;
- case BAYCOM_MODEM_PAR96:
- return tenms * 6;
- default:
- return 0;
- }
-}
-
-/* ---------------------------------------------------------------------- */
-/*
- * The HDLC routines
- */
-
-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;
-
- word &= 0xffff;
-#ifdef BAYCOM_DEBUG
- add_bitbuffer_word(&bc->bitbuf_hdlc, word);
-#endif /* BAYCOM_DEBUG */
- 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.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 int hdlc_tx_word(struct baycom_state *bc)
-{
- unsigned int mask1, mask2, mask3;
- int i;
-
- if (!bc || !bc->hdlc_tx.ptt)
- return 0;
- 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;
- bc->hdlc_tx.tx_state = 0;
- return 0;
- case 0:
- case 1:
- if (bc->hdlc_tx.numflags) {
- bc->hdlc_tx.numflags--;
- bc->hdlc_tx.bitbuf |=
- 0x7e7e << bc->hdlc_tx.numbits;
- bc->hdlc_tx.numbits += 16;
- break;
- }
- 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.numflags = tenms_to_2flags
- (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.numflags = 1;
- 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;
- }
- }
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned short random_seed;
-
-static inline unsigned short random_num(void)
-{
- random_seed = 28629 * random_seed + 157;
- return random_seed;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static inline void tx_arbitrate(struct baycom_state *bc)
-{
- unsigned char *bp;
- unsigned int len;
-
- if (!bc || bc->hdlc_tx.ptt || bc->modem.dcd)
- return;
- get_packet(&bc->tx_buf, &bp, &len);
- if (!bp || !len)
- return;
-
- if (!bc->ch_params.fulldup) {
- if ((random_num() % 256) > bc->ch_params.ppersist)
- return;
- }
- bc->hdlc_tx.tx_state = 0;
- bc->hdlc_tx.numflags = tenms_to_2flags(bc, bc->ch_params.tx_delay);
- bc->hdlc_tx.numbits = bc->hdlc_tx.bitbuf = bc->hdlc_tx.bitstream = 0;
- bc->hdlc_tx.ptt = 1;
- bc->stat.ptt_keyed++;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef BAYCOM_DEBUG
-static void inline baycom_int_freq(struct baycom_state *bc)
-{
- unsigned long cur_jiffies = jiffies;
- /*
- * measure the interrupt frequency
- */
- bc->debug_vals.cur_intcnt++;
- if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
- bc->debug_vals.last_jiffies = cur_jiffies;
- bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
- bc->debug_vals.cur_intcnt = 0;
- bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
- bc->debug_vals.cur_pllcorr = 0;
- }
-}
-#endif /* BAYCOM_DEBUG */
-
-/* --------------------------------------------------------------------- */
-
-static inline void rx_chars_to_flip(struct baycom_state *bc)
-{
- int flip_free;
- unsigned int cnt;
- unsigned int new_rd;
- unsigned long flags;
-
- if ((!bc) || (!bc->tty) || (bc->tty->flip.count >= TTY_FLIPBUF_SIZE) ||
- (bc->rx_buf.rd == bc->rx_buf.wr) ||
- (!bc->tty->flip.char_buf_ptr) ||
- (!bc->tty->flip.flag_buf_ptr))
- return;
- for(;;) {
- flip_free = TTY_FLIPBUF_SIZE - bc->tty->flip.count;
- if (bc->rx_buf.rd <= bc->rx_buf.wr)
- cnt = bc->rx_buf.wr - bc->rx_buf.rd;
- else
- cnt = bc->rx_buf.buflen - bc->rx_buf.rd;
- if ((flip_free <= 0) || (!cnt)) {
- tty_schedule_flip(bc->tty);
- return;
- }
- if (cnt > flip_free)
- cnt = flip_free;
- save_flags(flags); cli();
- memcpy(bc->tty->flip.char_buf_ptr, bc->rx_buf.buffer+bc->rx_buf.rd, cnt);
- memset(bc->tty->flip.flag_buf_ptr, TTY_NORMAL, cnt);
- bc->tty->flip.count += cnt;
- bc->tty->flip.char_buf_ptr += cnt;
- bc->tty->flip.flag_buf_ptr += cnt;
- restore_flags(flags);
- new_rd = bc->rx_buf.rd+cnt;
- if (new_rd >= bc->rx_buf.buflen)
- new_rd -= bc->rx_buf.buflen;
- bc->rx_buf.rd = new_rd;
- }
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== SER12 specific routines =========================
- */
-
-static void inline ser12_set_divisor(struct baycom_state *bc,
- unsigned char divisor)
-{
- outb(0x81, LCR(bc->iobase)); /* DLAB = 1 */
- outb(divisor, DLL(bc->iobase));
- outb(0, DLM(bc->iobase));
- outb(0x01, LCR(bc->iobase)); /* word length = 6 */
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * must call the TX arbitrator every 10ms
- */
-#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
- 36 : 24)
-#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
- 240 : 12)
-
-static void baycom_ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct baycom_state *bc = (struct baycom_state *)dev_id;
- unsigned char cur_s;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
- /*
- * make sure the next interrupt is generated;
- * 0 must be used to power the modem; the modem draws its
- * power from the TxD line
- */
- outb(0x00, THR(bc->iobase));
- rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
- baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
- /*
- * check if transmitter active
- */
- if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
- ser12_set_divisor(bc, 12); /* one interrupt per channel bit */
- /*
- * first output the last bit (!) then call HDLC transmitter,
- * since this may take quite long
- */
- outb(0x0e | (bc->modem.ser12.tx_bit ? 1 : 0), MCR(bc->iobase));
- 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
- bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
- queue_task_irq_off(&bc->tq_receiver,
- &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#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;
- }
- /*
- * do demodulator
- */
- outb(0x0d, MCR(bc->iobase)); /* transmitter off */
- cur_s = inb(MSR(bc->iobase)) & 0x10; /* the CTS line */
-#ifdef BAYCOM_DEBUG
- add_bitbuffer(&bc->bitbuf_channel, cur_s);
-#endif /* BAYCOM_DEBUG */
- bc->modem.ser12.dcd_shreg <<= 1;
- if(cur_s != bc->modem.ser12.last_sample) {
- bc->modem.ser12.dcd_shreg |= 1;
-
- if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
- unsigned int dcdspos, dcdsneg;
-
- dcdspos = dcdsneg = 0;
- dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
- if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
- dcdspos += 2;
- dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
- dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
- dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
-
- bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
- } else
- bc->modem.ser12.dcd_sum0--;
- }
- bc->modem.ser12.last_sample = cur_s;
- if(!bc->modem.ser12.dcd_time) {
- bc->modem.dcd = (bc->modem.ser12.dcd_sum0 +
- bc->modem.ser12.dcd_sum1 +
- bc->modem.ser12.dcd_sum2) < 0;
- bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
- bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
- /* offset to ensure DCD off on silent input */
- bc->modem.ser12.dcd_sum0 = 2;
- bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
- }
- bc->modem.ser12.dcd_time--;
- if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
- /*
- * PLL code for the improved software DCD algorithm
- */
- if (bc->modem.ser12.interm_sample) {
- /*
- * intermediate sample; set timing correction to normal
- */
- ser12_set_divisor(bc, 4);
- } else {
- /*
- * do PLL correction and call HDLC receiver
- */
- switch (bc->modem.ser12.dcd_shreg & 7) {
- case 1: /* transition too late */
- ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
- break;
- case 4: /* transition too early */
- ser12_set_divisor(bc, 3);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
- break;
- default:
- ser12_set_divisor(bc, 4);
- break;
- }
- 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;
- }
- if (++bc->modem.ser12.interm_sample >= 3)
- bc->modem.ser12.interm_sample = 0;
- } else {
- /*
- * PLL algorithm for the hardware squelch DCD algorithm
- */
- if (bc->modem.ser12.interm_sample) {
- /*
- * intermediate sample; set timing correction to normal
- */
- ser12_set_divisor(bc, 6);
- } else {
- /*
- * do PLL correction and call HDLC receiver
- */
- switch (bc->modem.ser12.dcd_shreg & 3) {
- case 1: /* transition too late */
- ser12_set_divisor(bc, 7);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
- break;
- case 2: /* transition too early */
- ser12_set_divisor(bc, 5);
-#ifdef BAYCOM_DEBUG
- bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
- break;
- default:
- ser12_set_divisor(bc, 6);
- break;
- }
- 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);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
- c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
- { "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart ser12_check_uart(unsigned int iobase)
-{
- unsigned char b1,b2,b3;
- enum uart u;
- enum uart uart_tab[] =
- { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
- b1 = inb(MCR(iobase));
- outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
- b2 = inb(MSR(iobase));
- outb(0x1a, MCR(iobase));
- b3 = inb(MSR(iobase)) & 0xf0;
- outb(b1, MCR(iobase)); /* restore old values */
- outb(b2, MSR(iobase));
- if (b3 != 0x90)
- return c_uart_unknown;
- inb(RBR(iobase));
- inb(RBR(iobase));
- outb(0x01, FCR(iobase)); /* enable FIFOs */
- u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
- if (u == c_uart_16450) {
- outb(0x5a, SCR(iobase));
- b1 = inb(SCR(iobase));
- outb(0xa5, SCR(iobase));
- b2 = inb(SCR(iobase));
- if ((b1 != 0x5a) || (b2 != 0xa5))
- u = c_uart_8250;
- }
- return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_allocate_resources(unsigned int iobase, unsigned int irq,
- unsigned int options)
-{
- enum uart u;
-
- if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
- return -ENXIO;
- if (check_region(iobase, SER12_EXTENT))
- return -EACCES;
- if ((u = ser12_check_uart(iobase)) == c_uart_unknown)
- return -EIO;
- request_region(iobase, SER12_EXTENT, "baycom_ser12");
- outb(0, FCR(iobase)); /* disable FIFOs */
- outb(0x0d, MCR(iobase));
- printk(KERN_INFO "baycom: ser12 at iobase 0x%x irq %u options 0x%x "
- "uart %s\n", iobase, irq, options, uart_str[u]);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_deallocate_resources(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
- return;
- /*
- * disable interrupts
- */
- outb(0, IER(bc->iobase));
- outb(1, MCR(bc->iobase));
- /*
- * this should prevent kernel: Trying to free IRQx
- * messages
- */
- if (bc->opened > 0)
- free_irq(bc->irq, bc);
- release_region(bc->iobase, SER12_EXTENT);
- bc->modem_type = BAYCOM_MODEM_INVALID;
- printk(KERN_INFO "baycom: release ser12 at iobase 0x%x irq %u\n",
- bc->iobase, bc->irq);
- bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_on_open(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
- return -ENXIO;
- /*
- * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
- * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
- * depending on the usage of the software DCD routine
- */
- ser12_set_divisor(bc, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
- outb(0x0d, MCR(bc->iobase));
- outb(0, IER(bc->iobase));
- if (request_irq(bc->irq, baycom_ser12_interrupt, SA_INTERRUPT,
- "baycom_ser12", bc))
- return -EBUSY;
- /*
- * enable transmitter empty interrupt
- */
- outb(2, IER(bc->iobase));
- /*
- * the value here serves to power the modem
- */
- outb(0x00, THR(bc->iobase));
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_on_close(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_SER12)
- return;
- /*
- * disable interrupts
- */
- outb(0, IER(bc->iobase));
- outb(1, MCR(bc->iobase));
- free_irq(bc->irq, bc);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== PAR96 specific routines =========================
- */
-
-#define PAR96_DESCRAM_TAP1 0x20000
-#define PAR96_DESCRAM_TAP2 0x01000
-#define PAR96_DESCRAM_TAP3 0x00001
-
-#define PAR96_DESCRAM_TAPSH1 17
-#define PAR96_DESCRAM_TAPSH2 12
-#define PAR96_DESCRAM_TAPSH3 0
-
-#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
-#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- register struct baycom_state *bc = (struct baycom_state *)dev_id;
- int i;
- unsigned int data, descx, mask, mask2;
-
- if (!bc || bc->magic != BAYCOM_MAGIC)
- return;
-
- rx_chars_to_flip(bc);
-#ifdef BAYCOM_DEBUG
- baycom_int_freq(bc);
-#endif /* BAYCOM_DEBUG */
- /*
- * check if transmitter active
- */
- if (bc->hdlc_tx.ptt || bc->calibrate > 0) {
- /*
- * first output the last 16 bits (!) then call HDLC
- * transmitter, since this may take quite long
- * do the differential encoder and the scrambler on the fly
- */
- 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 & 1))
- bc->modem.par96.scram ^= 1;
- if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
- bc->modem.par96.scram ^=
- (PAR96_SCRAM_TAPN << 1);
- if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
- val |= PAR96_TXBIT;
- outb(val, LPT_DATA(bc->iobase));
- outb(val | PAR96_BURST, LPT_DATA(bc->iobase));
- }
- 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
- bc->hdlc_rx.shreg2 = bc->hdlc_tx.shreg1;
- queue_task_irq_off(&bc->tq_receiver, &tq_baycom);
-#endif /* HDLC_LOOPBACK */
-#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++) {
- bc->modem.par96.descram = (bc->modem.par96.descram << 1);
- if (inb(LPT_STATUS(bc->iobase)) & PAR96_RXBIT)
- bc->modem.par96.descram |= 1;
- descx = bc->modem.par96.descram ^
- (bc->modem.par96.descram >> 1);
- /* now the diff decoded data is inverted in descram */
- outb(PAR97_POWER | PAR96_PTT, LPT_DATA(bc->iobase));
- descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
- (descx >> PAR96_DESCRAM_TAPSH2));
- data >>= 1;
- if (!(descx & 1))
- data |= 0x8000;
- outb(PAR97_POWER | PAR96_PTT | PAR96_BURST,
- LPT_DATA(bc->iobase));
- }
-#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 << 16);
- /* search for flags and set the dcd counter appropriately */
- for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
- i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
- if ((bc->modem.par96.dcd_shreg & mask) == mask2)
- bc->modem.par96.dcd_count = BAYCOM_MAXFLEN+4;
- /* check for abort/noise sequences */
- for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
- i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
- if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
- (bc->modem.par96.dcd_count >= 0))
- bc->modem.par96.dcd_count -= BAYCOM_MAXFLEN-10;
- /* decrement and set the dcd variable */
- if (bc->modem.par96.dcd_count >= 0)
- bc->modem.par96.dcd_count -= 2;
- bc->modem.dcd = bc->modem.par96.dcd_count > 0;
- } else {
- 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;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_check_lpt(unsigned int iobase)
-{
- unsigned char b1,b2;
- int i;
-
- b1 = inb(LPT_DATA(iobase));
- b2 = inb(LPT_CONTROL(iobase));
- outb(0xaa, LPT_DATA(iobase));
- i = inb(LPT_DATA(iobase)) == 0xaa;
- outb(0x55, LPT_DATA(iobase));
- i &= inb(LPT_DATA(iobase)) == 0x55;
- outb(0x0a, LPT_CONTROL(iobase));
- i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
- outb(0x05, LPT_CONTROL(iobase));
- i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
- outb(b1, LPT_DATA(iobase));
- outb(b2, LPT_CONTROL(iobase));
- return !i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_allocate_resources(unsigned int iobase, unsigned int irq,
- unsigned int options)
-{
- if (!iobase || iobase > 0xfff || irq < 2 || irq > 15)
- return -ENXIO;
- if (check_region(iobase, PAR96_EXTENT))
- return -EACCES;
- if (par96_check_lpt(iobase))
- return -EIO;
- request_region(iobase, PAR96_EXTENT, "baycom_par96");
- outb(0, LPT_CONTROL(iobase)); /* disable interrupt */
- outb(PAR96_PTT | PAR97_POWER, LPT_DATA(iobase)); /* switch off PTT */
- printk(KERN_INFO "baycom: par96 at iobase 0x%x irq %u options 0x%x\n",
- iobase, irq, options);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_deallocate_resources(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
- return;
- outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */
- outb(PAR96_PTT, LPT_DATA(bc->iobase)); /* switch off PTT */
- /*
- * this should prevent kernel: Trying to free IRQx
- * messages
- */
- if (bc->opened > 0)
- free_irq(bc->irq, bc);
- release_region(bc->iobase, PAR96_EXTENT);
- bc->modem_type = BAYCOM_MODEM_INVALID;
- printk(KERN_INFO "baycom: release par96 at iobase 0x%x irq %u\n",
- bc->iobase, bc->irq);
- bc->iobase = bc->irq = bc->options = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_on_open(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
- return -ENXIO;
- outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */
- /* switch off PTT */
- outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
- if (request_irq(bc->irq, baycom_par96_interrupt, SA_INTERRUPT,
- "baycom_par96", bc))
- return -EBUSY;
- outb(LPT_IRQ_ENABLE, LPT_CONTROL(bc->iobase)); /* enable interrupt */
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_on_close(struct baycom_state *bc)
-{
- if (!bc || bc->modem_type != BAYCOM_MODEM_PAR96)
- return;
- outb(0, LPT_CONTROL(bc->iobase)); /* disable interrupt */
- /* switch off PTT */
- outb(PAR96_PTT | PAR97_POWER, LPT_DATA(bc->iobase));
- free_irq(bc->irq, bc);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== 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 ==========================
- */
-
-static inline int baycom_paranoia_check(struct baycom_state *bc,
- const char *routine)
-{
- if (!bc || bc->magic != BAYCOM_MAGIC) {
- printk(KERN_ERR "baycom: bad magic number for baycom struct "
- "in routine %s\n", routine);
- return 1;
- }
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * Here the tty driver code starts
- */
-
-static void baycom_put_fend(struct baycom_state *bc)
-{
- if (bc->kiss_decode.wr <= 0 ||
- (bc->kiss_decode.pkt_buf[0] & 0xf0) != 0)
- return;
-
- switch (bc->kiss_decode.pkt_buf[0] & 0xf) {
- case KISS_CMD_DATA:
- if (bc->kiss_decode.wr <= 8)
- break;
- if (!store_packet(&bc->tx_buf, bc->kiss_decode.pkt_buf+1, 0,
- bc->kiss_decode.wr-1))
- bc->stat.tx_bufferoverrun++;
- break;
-
- case KISS_CMD_TXDELAY:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.tx_delay = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: TX delay = %ums\n",
- bc->ch_params.tx_delay * 10);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_PPERSIST:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.ppersist = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: p-persistence = %u\n",
- bc->ch_params.ppersist);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_SLOTTIME:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.slottime = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: slottime = %ums\n",
- bc->ch_params.slottime * 10);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_TXTAIL:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.tx_tail = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: TX tail = %ums\n",
- bc->ch_params.tx_tail * 10);
-#endif /* KISS_VERBOSE */
- break;
-
- case KISS_CMD_FULLDUP:
- if (bc->kiss_decode.wr < 2)
- break;
- bc->ch_params.fulldup = bc->kiss_decode.pkt_buf[1];
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: %s duplex\n",
- bc->ch_params.fulldup ? "full" : "half");
-#endif /* KISS_VERBOSE */
- break;
-
- default:
-#ifdef KISS_VERBOSE
- printk(KERN_INFO "baycom: unhandled KISS packet code %u\n",
- bc->kiss_decode.pkt_buf[0] & 0xf);
-#endif /* KISS_VERBOSE */
- break;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_put_char(struct tty_struct *tty, unsigned char ch)
-{
- struct baycom_state *bc;
-
- if (!tty)
- return;
- if (baycom_paranoia_check(bc = tty->driver_data, "put_char"))
- return;
-
- if (ch == KISS_FEND) {
- baycom_put_fend(bc);
- bc->kiss_decode.wr = 0;
- bc->kiss_decode.escaped = 0;
- bc->kiss_decode.dec_state = 1;
- return;
- }
- if (!bc->kiss_decode.dec_state)
- return;
- if (ch == KISS_FESC) {
- bc->kiss_decode.escaped = 1;
- return;
- }
- if (bc->kiss_decode.wr >= sizeof(bc->kiss_decode.pkt_buf)) {
- bc->kiss_decode.wr = 0;
- bc->kiss_decode.dec_state = 0;
- return;
- }
- if (bc->kiss_decode.escaped) {
- if (ch == KISS_TFEND)
- bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] =
- KISS_FEND;
- else if (ch == KISS_TFESC)
- bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] =
- KISS_FESC;
- else {
- bc->kiss_decode.wr = 0;
- bc->kiss_decode.dec_state = 0;
- }
- bc->kiss_decode.escaped = 0;
- return;
- }
- bc->kiss_decode.pkt_buf[bc->kiss_decode.wr++] = ch;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- int c;
- const unsigned char *bp;
- struct baycom_state *bc;
-
- if (!tty || !buf || count <= 0)
- return count;
-
- if (baycom_paranoia_check(bc = tty->driver_data, "write"))
- return count;
-
- if (from_user) {
- for(c = count, bp = buf; c > 0; c--,bp++)
- baycom_put_char(tty, get_user(bp));
- } else {
- 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;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_write_room(struct tty_struct *tty)
-{
- int free;
- struct baycom_state *bc;
-
- if (!tty)
- return 0;
- if (baycom_paranoia_check(bc = tty->driver_data, "write_room"))
- return 0;
-
- free = bc->tx_buf.rd - bc->tx_buf.wr;
- if (free <= 0) {
- free = bc->tx_buf.buflen - bc->tx_buf.wr;
- if (free < bc->tx_buf.rd)
- free = bc->tx_buf.rd; /* we may fold */
- }
-
- return free / 2; /* a rather pessimistic estimate */
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_chars_in_buffer(struct tty_struct *tty)
-{
- int cnt;
- struct baycom_state *bc;
-
- if (!tty)
- return 0;
- if (baycom_paranoia_check(bc = tty->driver_data, "chars_in_buffer"))
- return 0;
-
- cnt = bc->tx_buf.wr - bc->tx_buf.rd;
- if (cnt < 0)
- cnt += bc->tx_buf.buflen;
-
- return cnt;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_flush_buffer(struct tty_struct *tty)
-{
- struct baycom_state *bc;
-
- if (!tty)
- return;
- if (baycom_paranoia_check(bc = tty->driver_data, "flush_buffer"))
- return;
-
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void baycom_dealloc_hw(struct baycom_state *bc)
-{
- if (!bc || bc->magic != BAYCOM_MAGIC ||
- bc->modem_type == BAYCOM_MODEM_INVALID)
- return;
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- ser12_deallocate_resources(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- par96_deallocate_resources(bc);
- break;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_set_hardware(struct baycom_state *bc,
- unsigned int modem_type, unsigned int iobase,
- unsigned int irq, unsigned int options)
-{
- int i;
-
- if (!bc)
- return -EINVAL;
-
- if (modem_type == BAYCOM_MODEM_SER12) {
- i = ser12_allocate_resources(iobase, irq, options);
- if (i < 0)
- return i;
- } else if (modem_type == BAYCOM_MODEM_PAR96) {
- i = par96_allocate_resources(iobase, irq, options);
- if (i < 0)
- return i;
- } else if (modem_type == BAYCOM_MODEM_INVALID) {
- iobase = irq = options = 0;
- } else {
- return -ENXIO;
- }
- baycom_dealloc_hw(bc);
- bc->modem_type = modem_type;
- bc->iobase = iobase;
- bc->irq = irq;
- bc->options = options;
- i = 0;
- if (bc->opened > 0) {
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- i = ser12_on_open(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- i = par96_on_open(bc);
- break;
- }
- }
- return i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- int i;
- struct baycom_state *bc;
- struct baycom_params par;
-
- if (!tty)
- return -EINVAL;
- if (baycom_paranoia_check(bc = tty->driver_data, "ioctl"))
- return -EINVAL;
-
- switch (cmd) {
- default:
- return -ENOIOCTLCMD;
-
- case TIOCMGET:
- i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
- if (i)
- return i;
- i = (bc->modem.dcd ? TIOCM_CAR : 0) |
- (bc->hdlc_tx.ptt ? TIOCM_RTS : 0);
- put_user(i, (int *) arg);
- return 0;
-
- case BAYCOMCTL_GETDCD:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i)
- put_user(bc->modem.dcd, (unsigned char *) arg);
- return i;
-
- case BAYCOMCTL_GETPTT:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i)
- put_user(bc->hdlc_tx.ptt, (unsigned char *) arg);
- return i;
-
- case BAYCOMCTL_PARAM_TXDELAY:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.tx_delay = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_PPERSIST:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.ppersist = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_SLOTTIME:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.slottime = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_TXTAIL:
- if (arg > 255)
- return -EINVAL;
- bc->ch_params.tx_tail = arg;
- return 0;
-
- case BAYCOMCTL_PARAM_FULLDUP:
- bc->ch_params.fulldup = arg ? 1 : 0;
- return 0;
-
- case BAYCOMCTL_CALIBRATE:
- bc->calibrate = arg * ((bc->modem_type == BAYCOM_MODEM_PAR96) ?
- 600 : 75);
- return 0;
-
- case BAYCOMCTL_GETPARAMS:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(par));
- if (i)
- return i;
- par.modem_type = bc->modem_type;
- par.iobase = bc->iobase;
- par.irq = bc->irq;
- par.options = bc->options;
- par.tx_delay = bc->ch_params.tx_delay;
- par.tx_tail = bc->ch_params.tx_tail;
- par.slottime = bc->ch_params.slottime;
- par.ppersist = bc->ch_params.ppersist;
- par.fulldup = bc->ch_params.fulldup;
- memcpy_tofs((void *)arg, &par, sizeof(par));
- return 0;
-
- case BAYCOMCTL_SETPARAMS:
- if (!suser())
- return -EPERM;
- i = verify_area(VERIFY_READ, (void *) arg,
- sizeof(par));
- if (i)
- return i;
- memcpy_fromfs(&par, (void *)arg, sizeof(par));
- printk(KERN_INFO "baycom: changing hardware type: modem %u "
- "iobase 0x%x irq %u options 0x%x\n", par.modem_type,
- par.iobase, par.irq, par.options);
- i = baycom_set_hardware(bc, par.modem_type, par.iobase,
- par.irq, par.options);
- if (i)
- return i;
- bc->ch_params.tx_delay = par.tx_delay;
- bc->ch_params.tx_tail = par.tx_tail;
- bc->ch_params.slottime = par.slottime;
- bc->ch_params.ppersist = par.ppersist;
- bc->ch_params.fulldup = par.fulldup;
- return 0;
-
- case BAYCOMCTL_GETSTAT:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct baycom_statistics));
- if (i)
- return i;
- memcpy_tofs((void *)arg, &bc->stat,
- sizeof(struct baycom_statistics));
- return 0;
-
-
-#ifdef BAYCOM_DEBUG
- case BAYCOMCTL_GETSAMPLES:
- if (bc->bitbuf_channel.rd == bc->bitbuf_channel.wr)
- return -EAGAIN;
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i) {
- put_user(bc->bitbuf_channel.buffer
- [bc->bitbuf_channel.rd],
- (unsigned char *) arg);
- bc->bitbuf_channel.rd = (bc->bitbuf_channel.rd+1) %
- sizeof(bc->bitbuf_channel.buffer);
- }
- return i;
-
- case BAYCOMCTL_GETBITS:
- if (bc->bitbuf_hdlc.rd == bc->bitbuf_hdlc.wr)
- return -EAGAIN;
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned char));
- if (!i) {
- put_user(bc->bitbuf_hdlc.buffer[bc->bitbuf_hdlc.rd],
- (unsigned char *) arg);
- bc->bitbuf_hdlc.rd = (bc->bitbuf_hdlc.rd+1) %
- sizeof(bc->bitbuf_hdlc.buffer);
- }
- return i;
-
- case BAYCOMCTL_DEBUG1:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned long));
- if (!i)
- put_user((bc->rx_buf.wr-bc->rx_buf.rd) %
- bc->rx_buf.buflen, (unsigned long *)arg);
- return i;
-
- case BAYCOMCTL_DEBUG2:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned long));
- if (!i)
- put_user(bc->debug_vals.last_intcnt,
- (unsigned long *)arg);
- return i;
-
- case BAYCOMCTL_DEBUG3:
- i = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned long));
- if (!i)
- put_user((long)bc->debug_vals.last_pllcorr,
- (long *)arg);
- return i;
-#endif /* BAYCOM_DEBUG */
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-int baycom_open(struct tty_struct *tty, struct file * filp)
-{
- int line;
- struct baycom_state *bc;
- int i;
-
- if(!tty)
- return -ENODEV;
-
- line = MINOR(tty->device) - tty->driver.minor_start;
- if (line < 0 || line >= NR_PORTS)
- return -ENODEV;
- bc = baycom_state+line;
-
- if (bc->opened > 0) {
- bc->opened++;
- MOD_INC_USE_COUNT;
- return 0;
- }
- /*
- * initialise some variables
- */
- bc->calibrate = 0;
-
- /*
- * allocate the buffer space
- */
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buflen = BUFLEN_RX;
- bc->tx_buf.buflen = BUFLEN_TX;
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- bc->rx_buf.buffer = kmalloc(bc->rx_buf.buflen, GFP_KERNEL);
- bc->tx_buf.buffer = kmalloc(bc->tx_buf.buflen, GFP_KERNEL);
- if (!bc->rx_buf.buffer || !bc->tx_buf.buffer) {
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
- bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
- return -ENOMEM;
- }
- /*
- * check if the modem type has been set
- */
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- i = ser12_on_open(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- i = par96_on_open(bc);
- break;
- case BAYCOM_MODEM_INVALID:
- /*
- * may open even if no hardware specified, in order to
- * subsequently allow the BAYCOMCTL_SETPARAMS ioctl
- */
- i = 0;
- break;
- default:
- return -ENODEV;
- }
- if (i)
- return i;
-
- bc->opened++;
- MOD_INC_USE_COUNT;
-
- tty->driver_data = bc;
- bc->tty = tty;
-
- return 0;
-}
-
-
-/* --------------------------------------------------------------------- */
-
-static void baycom_close(struct tty_struct *tty, struct file * filp)
-{
- struct baycom_state *bc;
-
- if(!tty) return;
- if (baycom_paranoia_check(bc = tty->driver_data, "close"))
- return;
-
- MOD_DEC_USE_COUNT;
- bc->opened--;
- if (bc->opened <= 0) {
- switch(bc->modem_type) {
- case BAYCOM_MODEM_SER12:
- ser12_on_close(bc);
- break;
- case BAYCOM_MODEM_PAR96:
- par96_on_close(bc);
- break;
- }
- tty->driver_data = NULL;
- bc->tty = NULL;
- bc->opened = 0;
- /*
- * free the buffers
- */
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
- bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
- }
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * And now the modules code and kernel interface.
- */
-
-static void init_channel(struct baycom_state *bc)
-{
- struct access_params dflt_ch_params = { 20, 2, 10, 40, 0 };
-
- if (!bc)
- return;
-
- bc->hdlc_rx.rx_state = 0;
-
- bc->hdlc_tx.tx_state = bc->hdlc_tx.numflags = 0;
- bc->hdlc_tx.bitstream = 0;
- bc->hdlc_tx.current_byte = bc->hdlc_tx.ptt = 0;
-
- memset(&bc->modem, 0, sizeof(bc->modem));
-
-#ifdef BAYCOM_DEBUG
- bc->bitbuf_channel.rd = bc->bitbuf_channel.wr = 0;
- bc->bitbuf_channel.shreg = 0x80;
-
- bc->bitbuf_hdlc.rd = bc->bitbuf_hdlc.wr = 0;
- 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)
-{
- int i;
-
- for(i = 0; i < NR_PORTS; i++) {
- struct baycom_state *bc = baycom_state+i;
-
- bc->magic = BAYCOM_MAGIC;
- bc->modem_type = BAYCOM_MODEM_INVALID;
- bc->iobase = bc->irq = bc->options = bc->opened = 0;
- bc->tty = NULL;
-
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->rx_buf.buflen = 0;
- bc->rx_buf.buffer = NULL;
-
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- bc->tx_buf.buflen = 0;
- bc->tx_buf.buffer = NULL;
-
- memset(&bc->stat, 0, sizeof(bc->stat));
-
- init_channel(bc);
- }
-}
-
-int baycom_init(void) {
- int i, j;
-
- /*
- * initialize the data structures
- */
- 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));
- baycom_driver.magic = TTY_DRIVER_MAGIC;
- baycom_driver.name = "baycom";
- baycom_driver.major = major;
- baycom_driver.minor_start = 0;
- baycom_driver.num = NR_PORTS;
- baycom_driver.type = TTY_DRIVER_TYPE_BAYCOM;
- baycom_driver.subtype = BAYCOM_TYPE_NORMAL;
- baycom_driver.init_termios.c_iflag = 0;
- baycom_driver.init_termios.c_oflag = 0;
- baycom_driver.init_termios.c_cflag = CS8 | B1200 | CREAD | CLOCAL;
- baycom_driver.init_termios.c_lflag = 0;
- baycom_driver.flags = TTY_DRIVER_REAL_RAW;
- baycom_driver.refcount = &baycom_refcount;
- baycom_driver.table = baycom_table;
- baycom_driver.termios = baycom_termios;
- baycom_driver.termios_locked = baycom_termios_locked;
- /*
- * the functions
- */
- baycom_driver.open = baycom_open;
- baycom_driver.close = baycom_close;
- baycom_driver.write = baycom_write;
- baycom_driver.put_char = baycom_put_char;
- baycom_driver.flush_chars = NULL;
- baycom_driver.write_room = baycom_write_room;
- baycom_driver.chars_in_buffer = baycom_chars_in_buffer;
- baycom_driver.flush_buffer = baycom_flush_buffer;
- baycom_driver.ioctl = baycom_ioctl;
- /*
- * cannot throttle the transmitter on this layer
- */
- baycom_driver.throttle = NULL;
- baycom_driver.unthrottle = NULL;
- /*
- * no special actions on termio changes
- */
- baycom_driver.set_termios = NULL;
- /*
- * no XON/XOFF and no hangup on the radio port
- */
- baycom_driver.stop = NULL;
- baycom_driver.start = NULL;
- baycom_driver.hangup = NULL;
- baycom_driver.set_ldisc = NULL;
-
- if (tty_register_driver(&baycom_driver)) {
- printk(KERN_WARNING "baycom: tty_register_driver failed\n");
- return -EIO;
- }
-
- for (i = 0; i < NR_PORTS &&
- baycom_ports[i].modem != BAYCOM_MODEM_INVALID; i++) {
- j = baycom_set_hardware(baycom_state+i,
- baycom_ports[i].modem,
- baycom_ports[i].iobase,
- baycom_ports[i].irq,
- baycom_ports[i].options);
- if (j < 0) {
- const char *s;
- switch (-j) {
- case ENXIO:
- s = "invalid iobase and/or irq";
- break;
- case EACCES:
- s = "io region already used";
- break;
- case EIO:
- s = "no uart/lpt port at iobase";
- break;
- case EBUSY:
- s = "interface already in use";
- break;
- case EINVAL:
- s = "internal error";
- break;
- default:
- s = "unknown error";
- break;
- }
- printk(KERN_WARNING "baycom: modem %u iobase 0x%x "
- "irq %u: (%i) %s\n", baycom_ports[i].modem,
- baycom_ports[i].iobase, baycom_ports[i].irq,
- j, s);
- }
- }
-
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-int modem = BAYCOM_MODEM_INVALID;
-int iobase = 0x3f8;
-int irq = 4;
-int options = BAYCOM_OPTIONS_SOFTDCD;
-
-int init_module(void)
-{
- int i;
-
- printk(KERN_INFO "baycom: init_module called\n");
-
- baycom_ports[0].modem = modem;
- baycom_ports[0].iobase = iobase;
- baycom_ports[0].irq = irq;
- baycom_ports[0].options = options;
- baycom_ports[1].modem = BAYCOM_MODEM_INVALID;
-
- i = baycom_init();
- if (i)
- return i;
-
- printk(KERN_INFO "baycom: version 0.3; "
- "(C) 1996 by Thomas Sailer HB9JNX, sailer@ife.ee.ethz.ch\n");
-
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
- int i;
-
- printk(KERN_INFO "baycom: cleanup_module called\n");
-
- disable_bh(BAYCOM_BH);
- if (tty_unregister_driver(&baycom_driver))
- printk(KERN_WARNING "baycom: failed to unregister tty "
- "driver\n");
- for(i = 0; i < NR_PORTS; i++) {
- struct baycom_state *bc = baycom_state+i;
-
- if (bc->magic != BAYCOM_MAGIC)
- printk(KERN_ERR "baycom: invalid magic in "
- "cleanup_module\n");
- else {
- baycom_dealloc_hw(bc);
- /*
- * free the buffers
- */
- bc->rx_buf.rd = bc->rx_buf.wr = 0;
- bc->tx_buf.rd = bc->tx_buf.wr = 0;
- if (bc->rx_buf.buffer)
- kfree_s(bc->rx_buf.buffer, bc->rx_buf.buflen);
- if (bc->tx_buf.buffer)
- kfree_s(bc->tx_buf.buffer, bc->tx_buf.buflen);
- bc->rx_buf.buffer = bc->tx_buf.buffer = NULL;
- bc->rx_buf.buflen = bc->tx_buf.buflen = 0;
- }
- }
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom=modem,io,irq,options[,modem,io,irq,options]
- * modem=1: ser12, modem=2: par96
- * options=0: hardware DCD, options=1: software DCD
- */
-
-void baycom_setup(char *str, int *ints)
-{
- int i;
-
- for (i = 0; i < NR_PORTS; i++)
- if (ints[0] >= 4*i+4) {
- baycom_ports[i].modem = ints[4*i+1];
- baycom_ports[i].iobase = ints[4*i+2];
- baycom_ports[i].irq = ints[4*i+3];
- baycom_ports[i].options = ints[4*i+4];
- } else
- baycom_ports[i].modem = BAYCOM_MODEM_INVALID;
-
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov