Commit 51c9d654 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Staging: delete tty drivers

Delete the drivers/staging/tty drivers as no one has wanted to step up
and maintain and fix them.  This was discussed in commit
4a6514e6 (tty: move obsolete and broken
tty drivers to drivers/staging/tty/)

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent bb2a97e9
...@@ -24,8 +24,6 @@ menuconfig STAGING ...@@ -24,8 +24,6 @@ menuconfig STAGING
if STAGING if STAGING
source "drivers/staging/tty/Kconfig"
source "drivers/staging/et131x/Kconfig" source "drivers/staging/et131x/Kconfig"
source "drivers/staging/slicoss/Kconfig" source "drivers/staging/slicoss/Kconfig"
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
# fix for build system bug... # fix for build system bug...
obj-$(CONFIG_STAGING) += staging.o obj-$(CONFIG_STAGING) += staging.o
obj-y += tty/
obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_SLICOSS) += slicoss/
obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_GO7007) += go7007/
......
config STALLION
tristate "Stallion EasyIO or EC8/32 support"
depends on STALDRV && (ISA || EISA || PCI)
help
If you have an EasyIO or EasyConnection 8/32 multiport Stallion
card, then this is for you; say Y. Make sure to read
<file:Documentation/serial/stallion.txt>.
To compile this driver as a module, choose M here: the
module will be called stallion.
config ISTALLION
tristate "Stallion EC8/64, ONboard, Brumby support"
depends on STALDRV && (ISA || EISA || PCI)
help
If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
serial multiport card, say Y here. Make sure to read
<file:Documentation/serial/stallion.txt>.
To compile this driver as a module, choose M here: the
module will be called istallion.
config DIGIEPCA
tristate "Digiboard Intelligent Async Support"
depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
---help---
This is a driver for Digi International's Xx, Xeve, and Xem series
of cards which provide multiple serial ports. You would need
something like this to connect more than two modems to your Linux
box, for instance in order to become a dial-in server. This driver
supports the original PC (ISA) boards as well as PCI, and EISA. If
you have a card like this, say Y here and read the file
<file:Documentation/serial/digiepca.txt>.
To compile this driver as a module, choose M here: the
module will be called epca.
config RISCOM8
tristate "SDL RISCom/8 card support"
depends on SERIAL_NONSTANDARD
help
This is a driver for the SDL Communications RISCom/8 multiport card,
which gives you many serial ports. You would need something like
this to connect more than two modems to your Linux box, for instance
in order to become a dial-in server. If you have a card like that,
say Y here and read the file <file:Documentation/serial/riscom8.txt>.
Also it's possible to say M here and compile this driver as kernel
loadable module; the module will be called riscom8.
config SPECIALIX
tristate "Specialix IO8+ card support"
depends on SERIAL_NONSTANDARD
help
This is a driver for the Specialix IO8+ multiport card (both the
ISA and the PCI version) which gives you many serial ports. You
would need something like this to connect more than two modems to
your Linux box, for instance in order to become a dial-in server.
If you have a card like that, say Y here and read the file
<file:Documentation/serial/specialix.txt>. Also it's possible to say
M here and compile this driver as kernel loadable module which will be
called specialix.
config COMPUTONE
tristate "Computone IntelliPort Plus serial support"
depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
---help---
This driver supports the entire family of Intelliport II/Plus
controllers with the exception of the MicroChannel controllers and
products previous to the Intelliport II. These are multiport cards,
which give you many serial ports. You would need something like this
to connect more than two modems to your Linux box, for instance in
order to become a dial-in server. If you have a card like that, say
Y here and read <file:Documentation/serial/computone.txt>.
To compile this driver as module, choose M here: the
module will be called ip2.
config SERIAL167
bool "CD2401 support for MVME166/7 serial ports"
depends on MVME16x
help
This is the driver for the serial ports on the Motorola MVME166,
167, and 172 boards. Everyone using one of these boards should say
Y here.
obj-$(CONFIG_STALLION) += stallion.o
obj-$(CONFIG_ISTALLION) += istallion.o
obj-$(CONFIG_DIGIEPCA) += epca.o
obj-$(CONFIG_SERIAL167) += serial167.o
obj-$(CONFIG_SPECIALIX) += specialix.o
obj-$(CONFIG_RISCOM8) += riscom8.o
obj-$(CONFIG_COMPUTONE) += ip2/
These are a few tty/serial drivers that either do not build,
or work if they do build, or if they seem to work, are for obsolete
hardware, or are full of unfixable races and no one uses them anymore.
If no one steps up to adopt any of these drivers, they will be removed
in the 2.6.41 release.
/*
* linux/drivers/char/cd1865.h -- Definitions relating to the CD1865
* for the Specialix IO8+ multiport serial driver.
*
* Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
* Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
*
* Specialix pays for the development and support of this driver.
* Please DO contact io8-linux@specialix.co.uk if you require
* support.
*
* This driver was developed in the BitWizard linux device
* driver service. If you require a linux device driver for your
* product, please contact devices@BitWizard.nl for a quote.
*
*/
/*
* Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards.
*/
/* Values of choice for Interrupt ACKs */
/* These values are "obligatory" if you use the register based
* interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865
* databook */
#define SX_ACK_MINT 0x75 /* goes to PILR1 */
#define SX_ACK_TINT 0x76 /* goes to PILR2 */
#define SX_ACK_RINT 0x77 /* goes to PILR3 */
/* Chip ID (is used when chips ar daisy chained.) */
#define SX_ID 0x10
/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */
#define CD186x_NCH 8 /* Total number of channels */
#define CD186x_TPC 16 /* Ticks per character */
#define CD186x_NFIFO 8 /* TX FIFO size */
/* Global registers */
#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */
#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */
#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */
#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */
#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */
#define CD186x_CAR 0x64 /* Channel Access Register */
#define CD186x_SRSR 0x65 /* Channel Access Register */
#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */
#define CD186x_PPRH 0x70 /* Prescaler Period Register High */
#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */
#define CD186x_RDR 0x78 /* Receiver Data Register */
#define CD186x_RCSR 0x7a /* Receiver Character Status Register */
#define CD186x_TDR 0x7b /* Transmit Data Register */
#define CD186x_EOIR 0x7f /* End of Interrupt Register */
#define CD186x_MRAR 0x75 /* Modem Request Acknowledge register */
#define CD186x_TRAR 0x76 /* Transmit Request Acknowledge register */
#define CD186x_RRAR 0x77 /* Receive Request Acknowledge register */
#define CD186x_SRCR 0x66 /* Service Request Configuration register */
/* Channel Registers */
#define CD186x_CCR 0x01 /* Channel Command Register */
#define CD186x_IER 0x02 /* Interrupt Enable Register */
#define CD186x_COR1 0x03 /* Channel Option Register 1 */
#define CD186x_COR2 0x04 /* Channel Option Register 2 */
#define CD186x_COR3 0x05 /* Channel Option Register 3 */
#define CD186x_CCSR 0x06 /* Channel Control Status Register */
#define CD186x_RDCR 0x07 /* Receive Data Count Register */
#define CD186x_SCHR1 0x09 /* Special Character Register 1 */
#define CD186x_SCHR2 0x0a /* Special Character Register 2 */
#define CD186x_SCHR3 0x0b /* Special Character Register 3 */
#define CD186x_SCHR4 0x0c /* Special Character Register 4 */
#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */
#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */
#define CD186x_MCR 0x12 /* Modem Change Register */
#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */
#define CD186x_MSVR 0x28 /* Modem Signal Value Register */
#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */
#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */
#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */
#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */
#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */
#define CD186x_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */
/* Global Interrupt Vector Register (R/W) */
#define GIVR_ITMASK 0x07 /* Interrupt type mask */
#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */
#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */
#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */
#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */
/* Global Interrupt Channel Register (R/W) */
#define GICR_CHAN 0x1c /* Channel Number Mask */
#define GICR_CHAN_OFF 2 /* Channel Number shift */
/* Channel Address Register (R/W) */
#define CAR_CHAN 0x07 /* Channel Number Mask */
#define CAR_A7 0x08 /* A7 Address Extension (unused) */
/* Receive Character Status Register (R/O) */
#define RCSR_TOUT 0x80 /* Rx Timeout */
#define RCSR_SCDET 0x70 /* Special Character Detected Mask */
#define RCSR_NO_SC 0x00 /* No Special Characters Detected */
#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */
#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */
#define RCSR_SC_3 0x30 /* Special Char 3 Detected */
#define RCSR_SC_4 0x40 /* Special Char 4 Detected */
#define RCSR_BREAK 0x08 /* Break has been detected */
#define RCSR_PE 0x04 /* Parity Error */
#define RCSR_FE 0x02 /* Frame Error */
#define RCSR_OE 0x01 /* Overrun Error */
/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
#define CCR_HARDRESET 0x81 /* Reset the chip */
#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */
#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */
#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */
#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */
#define CCR_SSCH1 0x21 /* Send Special Character 1 */
#define CCR_SSCH2 0x22 /* Send Special Character 2 */
#define CCR_SSCH3 0x23 /* Send Special Character 3 */
#define CCR_SSCH4 0x24 /* Send Special Character 4 */
#define CCR_TXEN 0x18 /* Enable Transmitter */
#define CCR_RXEN 0x12 /* Enable Receiver */
#define CCR_TXDIS 0x14 /* Disable Transmitter */
#define CCR_RXDIS 0x11 /* Disable Receiver */
/* Interrupt Enable Register (R/W) */
#define IER_DSR 0x80 /* Enable interrupt on DSR change */
#define IER_CD 0x40 /* Enable interrupt on CD change */
#define IER_CTS 0x20 /* Enable interrupt on CTS change */
#define IER_RXD 0x10 /* Enable interrupt on Receive Data */
#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */
#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */
#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */
#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */
/* Channel Option Register 1 (R/W) */
#define COR1_ODDP 0x80 /* Odd Parity */
#define COR1_PARMODE 0x60 /* Parity Mode mask */
#define COR1_NOPAR 0x00 /* No Parity */
#define COR1_FORCEPAR 0x20 /* Force Parity */
#define COR1_NORMPAR 0x40 /* Normal Parity */
#define COR1_IGNORE 0x10 /* Ignore Parity on RX */
#define COR1_STOPBITS 0x0c /* Number of Stop Bits */
#define COR1_1SB 0x00 /* 1 Stop Bit */
#define COR1_15SB 0x04 /* 1.5 Stop Bits */
#define COR1_2SB 0x08 /* 2 Stop Bits */
#define COR1_CHARLEN 0x03 /* Character Length */
#define COR1_5BITS 0x00 /* 5 bits */
#define COR1_6BITS 0x01 /* 6 bits */
#define COR1_7BITS 0x02 /* 7 bits */
#define COR1_8BITS 0x03 /* 8 bits */
/* Channel Option Register 2 (R/W) */
#define COR2_IXM 0x80 /* Implied XON mode */
#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */
#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */
#define COR2_LLM 0x10 /* Local Loopback Mode */
#define COR2_RLM 0x08 /* Remote Loopback Mode */
#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */
#define COR2_CTSAE 0x02 /* CTS Automatic Enable */
#define COR2_DSRAE 0x01 /* DSR Automatic Enable */
/* Channel Option Register 3 (R/W) */
#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */
#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */
#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */
#define COR3_SCDE 0x10 /* Special Character Detection Enable */
#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */
/* Channel Control Status Register (R/O) */
#define CCSR_RXEN 0x80 /* Receiver Enabled */
#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */
#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */
#define CCSR_TXEN 0x08 /* Transmitter Enabled */
#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */
#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */
/* Modem Change Option Register 1 (R/W) */
#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */
#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */
#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */
#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */
#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */
/* Modem Change Option Register 2 (R/W) */
#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */
#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */
#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */
/* Modem Change Register (R/W) */
#define MCR_DSRCHG 0x80 /* DSR Changed */
#define MCR_CDCHG 0x40 /* CD Changed */
#define MCR_CTSCHG 0x20 /* CTS Changed */
/* Modem Signal Value Register (R/W) */
#define MSVR_DSR 0x80 /* Current state of DSR input */
#define MSVR_CD 0x40 /* Current state of CD input */
#define MSVR_CTS 0x20 /* Current state of CTS input */
#define MSVR_DTR 0x02 /* Current state of DTR output */
#define MSVR_RTS 0x01 /* Current state of RTS output */
/* Escape characters */
#define CD186x_C_ESC 0x00 /* Escape character */
#define CD186x_C_SBRK 0x81 /* Start sending BREAK */
#define CD186x_C_DELAY 0x82 /* Delay output */
#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */
#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */
#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */
#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */
#define SRCR_PKGTYPE 0x80
#define SRCR_REGACKEN 0x40
#define SRCR_DAISYEN 0x20
#define SRCR_GLOBPRI 0x10
#define SRCR_UNFAIR 0x08
#define SRCR_AUTOPRI 0x02
#define SRCR_PRISEL 0x01
/* Definitions for DigiBoard ditty(1) command. */
#if !defined(TIOCMODG)
#define TIOCMODG (('d'<<8) | 250) /* get modem ctrl state */
#define TIOCMODS (('d'<<8) | 251) /* set modem ctrl state */
#endif
#if !defined(TIOCMSET)
#define TIOCMSET (('d'<<8) | 252) /* set modem ctrl state */
#define TIOCMGET (('d'<<8) | 253) /* set modem ctrl state */
#endif
#if !defined(TIOCMBIC)
#define TIOCMBIC (('d'<<8) | 254) /* set modem ctrl state */
#define TIOCMBIS (('d'<<8) | 255) /* set modem ctrl state */
#endif
#if !defined(TIOCSDTR)
#define TIOCSDTR (('e'<<8) | 0) /* set DTR */
#define TIOCCDTR (('e'<<8) | 1) /* clear DTR */
#endif
/************************************************************************
* Ioctl command arguments for DIGI parameters.
************************************************************************/
#define DIGI_GETA (('e'<<8) | 94) /* Read params */
#define DIGI_SETA (('e'<<8) | 95) /* Set params */
#define DIGI_SETAW (('e'<<8) | 96) /* Drain & set params */
#define DIGI_SETAF (('e'<<8) | 97) /* Drain, flush & set params */
#define DIGI_GETFLOW (('e'<<8) | 99) /* Get startc/stopc flow */
/* control characters */
#define DIGI_SETFLOW (('e'<<8) | 100) /* Set startc/stopc flow */
/* control characters */
#define DIGI_GETAFLOW (('e'<<8) | 101) /* Get Aux. startc/stopc */
/* flow control chars */
#define DIGI_SETAFLOW (('e'<<8) | 102) /* Set Aux. startc/stopc */
/* flow control chars */
#define DIGI_GETINFO (('e'<<8) | 103) /* Fill in digi_info */
#define DIGI_POLLER (('e'<<8) | 104) /* Turn on/off poller */
#define DIGI_INIT (('e'<<8) | 105) /* Allow things to run. */
struct digiflow_struct
{
unsigned char startc; /* flow cntl start char */
unsigned char stopc; /* flow cntl stop char */
};
typedef struct digiflow_struct digiflow_t;
/************************************************************************
* Values for digi_flags
************************************************************************/
#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */
#define DIGI_FAST 0x0002 /* Fast baud rates */
#define RTSPACE 0x0004 /* RTS input flow control */
#define CTSPACE 0x0008 /* CTS output flow control */
#define DSRPACE 0x0010 /* DSR output flow control */
#define DCDPACE 0x0020 /* DCD output flow control */
#define DTRPACE 0x0040 /* DTR input flow control */
#define DIGI_FORCEDCD 0x0100 /* Force carrier */
#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
#define DIGI_AIXON 0x0400 /* Aux flow control in fep */
/************************************************************************
* Values for digiDload
************************************************************************/
#define NORMAL 0
#define PCI_CTL 1
#define SIZE8 0
#define SIZE16 1
#define SIZE32 2
/************************************************************************
* Structure used with ioctl commands for DIGI parameters.
************************************************************************/
struct digi_struct
{
unsigned short digi_flags; /* Flags (see above) */
};
typedef struct digi_struct digi_t;
struct digi_info
{
unsigned long board; /* Which board is this ? */
unsigned char status; /* Alive or dead */
unsigned char type; /* see epca.h */
unsigned char subtype; /* For future XEM, XR, etc ... */
unsigned short numports; /* Number of ports configured */
unsigned char *port; /* I/O Address */
unsigned char *membase; /* DPR Address */
unsigned char *version; /* For future ... */
unsigned short windowData; /* For future ... */
} ;
#define CSTART 0x400L
#define CMAX 0x800L
#define ISTART 0x800L
#define IMAX 0xC00L
#define CIN 0xD10L
#define GLOBAL 0xD10L
#define EIN 0xD18L
#define FEPSTAT 0xD20L
#define CHANSTRUCT 0x1000L
#define RXTXBUF 0x4000L
struct global_data
{
u16 cin;
u16 cout;
u16 cstart;
u16 cmax;
u16 ein;
u16 eout;
u16 istart;
u16 imax;
};
struct board_chan
{
u32 filler1;
u32 filler2;
u16 tseg;
u16 tin;
u16 tout;
u16 tmax;
u16 rseg;
u16 rin;
u16 rout;
u16 rmax;
u16 tlow;
u16 rlow;
u16 rhigh;
u16 incr;
u16 etime;
u16 edelay;
unchar *dev;
u16 iflag;
u16 oflag;
u16 cflag;
u16 gmask;
u16 col;
u16 delay;
u16 imask;
u16 tflush;
u32 filler3;
u32 filler4;
u32 filler5;
u32 filler6;
u8 num;
u8 ract;
u8 bstat;
u8 tbusy;
u8 iempty;
u8 ilow;
u8 idata;
u8 eflag;
u8 tflag;
u8 rflag;
u8 xmask;
u8 xval;
u8 mstat;
u8 mchange;
u8 mint;
u8 lstat;
u8 mtran;
u8 orun;
u8 startca;
u8 stopca;
u8 startc;
u8 stopc;
u8 vnext;
u8 hflow;
u8 fillc;
u8 ochar;
u8 omask;
u8 filler7;
u8 filler8[28];
};
#define SRXLWATER 0xE0
#define SRXHWATER 0xE1
#define STOUT 0xE2
#define PAUSETX 0xE3
#define RESUMETX 0xE4
#define SAUXONOFFC 0xE6
#define SENDBREAK 0xE8
#define SETMODEM 0xE9
#define SETIFLAGS 0xEA
#define SONOFFC 0xEB
#define STXLWATER 0xEC
#define PAUSERX 0xEE
#define RESUMERX 0xEF
#define SETBUFFER 0xF2
#define SETCOOKED 0xF3
#define SETHFLOW 0xF4
#define SETCTRLFLAGS 0xF5
#define SETVNEXT 0xF6
#define BREAK_IND 0x01
#define LOWTX_IND 0x02
#define EMPTYTX_IND 0x04
#define DATA_IND 0x08
#define MODEMCHG_IND 0x20
#define FEP_HUPCL 0002000
#if 0
#define RTS 0x02
#define CD 0x08
#define DSR 0x10
#define CTS 0x20
#define RI 0x40
#define DTR 0x80
#endif
/*************************************************************************
* Defines and structure definitions for PCI BIOS Interface
*************************************************************************/
#define PCIMAX 32 /* maximum number of PCI boards */
#define PCI_VENDOR_DIGI 0x114F
#define PCI_DEVICE_EPC 0x0002
#define PCI_DEVICE_RIGHTSWITCH 0x0003 /* For testing */
#define PCI_DEVICE_XEM 0x0004
#define PCI_DEVICE_XR 0x0005
#define PCI_DEVICE_CX 0x0006
#define PCI_DEVICE_XRJ 0x0009 /* Jupiter boards with */
#define PCI_DEVICE_EPCJ 0x000a /* PLX 9060 chip for PCI */
/*
* On the PCI boards, there is no IO space allocated
* The I/O registers will be in the first 3 bytes of the
* upper 2MB of the 4MB memory space. The board memory
* will be mapped into the low 2MB of the 4MB memory space
*/
/* Potential location of PCI Bios from E0000 to FFFFF*/
#define PCI_BIOS_SIZE 0x00020000
/* Size of Memory and I/O for PCI (4MB) */
#define PCI_RAM_SIZE 0x00400000
/* Size of Memory (2MB) */
#define PCI_MEM_SIZE 0x00200000
/* Offset of I/0 in Memory (2MB) */
#define PCI_IO_OFFSET 0x00200000
#define MEMOUTB(basemem, pnum, setmemval) *(caddr_t)((basemem) + ( PCI_IO_OFFSET | pnum << 4 | pnum )) = (setmemval)
#define MEMINB(basemem, pnum) *(caddr_t)((basemem) + (PCI_IO_OFFSET | pnum << 4 | pnum )) /* for PCI I/O */
/*
Copyright (C) 1996 Digi International.
For technical support please email digiLinux@dgii.com or
call Digi tech support at (612) 912-3456
** This driver is no longer supported by Digi **
Much of this design and code came from epca.c which was
copyright (C) 1994, 1995 Troy De Jongh, and subsequently
modified by David Nugent, Christoph Lameter, Mike McLagan.
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.
*/
/* See README.epca for change history --DAT*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include "digiPCI.h"
#include "digi1.h"
#include "digiFep1.h"
#include "epca.h"
#include "epcaconfig.h"
#define VERSION "1.3.0.1-LK2.6"
/* This major needs to be submitted to Linux to join the majors list */
#define DIGIINFOMAJOR 35 /* For Digi specific ioctl */
#define MAXCARDS 7
#define epcaassert(x, msg) if (!(x)) epca_error(__LINE__, msg)
#define PFX "epca: "
static int nbdevs, num_cards, liloconfig;
static int digi_poller_inhibited = 1 ;
static int setup_error_code;
static int invalid_lilo_config;
/*
* The ISA boards do window flipping into the same spaces so its only sane with
* a single lock. It's still pretty efficient. This lock guards the hardware
* and the tty_port lock guards the kernel side stuff like use counts. Take
* this lock inside the port lock if you must take both.
*/
static DEFINE_SPINLOCK(epca_lock);
/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted
to 7 below. */
static struct board_info boards[MAXBOARDS];
static struct tty_driver *pc_driver;
static struct tty_driver *pc_info;
/* ------------------ Begin Digi specific structures -------------------- */
/*
* digi_channels represents an array of structures that keep track of each
* channel of the Digi product. Information such as transmit and receive
* pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored
* here. This structure is NOT used to overlay the cards physical channel
* structure.
*/
static struct channel digi_channels[MAX_ALLOC];
/*
* card_ptr is an array used to hold the address of the first channel structure
* of each card. This array will hold the addresses of various channels located
* in digi_channels.
*/
static struct channel *card_ptr[MAXCARDS];
static struct timer_list epca_timer;
/*
* Begin generic memory functions. These functions will be alias (point at)
* more specific functions dependent on the board being configured.
*/
static void memwinon(struct board_info *b, unsigned int win);
static void memwinoff(struct board_info *b, unsigned int win);
static void globalwinon(struct channel *ch);
static void rxwinon(struct channel *ch);
static void txwinon(struct channel *ch);
static void memoff(struct channel *ch);
static void assertgwinon(struct channel *ch);
static void assertmemoff(struct channel *ch);
/* ---- Begin more 'specific' memory functions for cx_like products --- */
static void pcxem_memwinon(struct board_info *b, unsigned int win);
static void pcxem_memwinoff(struct board_info *b, unsigned int win);
static void pcxem_globalwinon(struct channel *ch);
static void pcxem_rxwinon(struct channel *ch);
static void pcxem_txwinon(struct channel *ch);
static void pcxem_memoff(struct channel *ch);
/* ------ Begin more 'specific' memory functions for the pcxe ------- */
static void pcxe_memwinon(struct board_info *b, unsigned int win);
static void pcxe_memwinoff(struct board_info *b, unsigned int win);
static void pcxe_globalwinon(struct channel *ch);
static void pcxe_rxwinon(struct channel *ch);
static void pcxe_txwinon(struct channel *ch);
static void pcxe_memoff(struct channel *ch);
/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */
/* Note : pc64xe and pcxi share the same windowing routines */
static void pcxi_memwinon(struct board_info *b, unsigned int win);
static void pcxi_memwinoff(struct board_info *b, unsigned int win);
static void pcxi_globalwinon(struct channel *ch);
static void pcxi_rxwinon(struct channel *ch);
static void pcxi_txwinon(struct channel *ch);
static void pcxi_memoff(struct channel *ch);
/* - Begin 'specific' do nothing memory functions needed for some cards - */
static void dummy_memwinon(struct board_info *b, unsigned int win);
static void dummy_memwinoff(struct board_info *b, unsigned int win);
static void dummy_globalwinon(struct channel *ch);
static void dummy_rxwinon(struct channel *ch);
static void dummy_txwinon(struct channel *ch);
static void dummy_memoff(struct channel *ch);
static void dummy_assertgwinon(struct channel *ch);
static void dummy_assertmemoff(struct channel *ch);
static struct channel *verifyChannel(struct tty_struct *);
static void pc_sched_event(struct channel *, int);
static void epca_error(int, char *);
static void pc_close(struct tty_struct *, struct file *);
static void shutdown(struct channel *, struct tty_struct *tty);
static void pc_hangup(struct tty_struct *);
static int pc_write_room(struct tty_struct *);
static int pc_chars_in_buffer(struct tty_struct *);
static void pc_flush_buffer(struct tty_struct *);
static void pc_flush_chars(struct tty_struct *);
static int pc_open(struct tty_struct *, struct file *);
static void post_fep_init(unsigned int crd);
static void epcapoll(unsigned long);
static void doevent(int);
static void fepcmd(struct channel *, int, int, int, int, int);
static unsigned termios2digi_h(struct channel *ch, unsigned);
static unsigned termios2digi_i(struct channel *ch, unsigned);
static unsigned termios2digi_c(struct channel *ch, unsigned);
static void epcaparam(struct tty_struct *, struct channel *);
static void receive_data(struct channel *, struct tty_struct *tty);
static int pc_ioctl(struct tty_struct *,
unsigned int, unsigned long);
static int info_ioctl(struct tty_struct *,
unsigned int, unsigned long);
static void pc_set_termios(struct tty_struct *, struct ktermios *);
static void do_softint(struct work_struct *work);
static void pc_stop(struct tty_struct *);
static void pc_start(struct tty_struct *);
static void pc_throttle(struct tty_struct *tty);
static void pc_unthrottle(struct tty_struct *tty);
static int pc_send_break(struct tty_struct *tty, int msec);
static void setup_empty_event(struct tty_struct *tty, struct channel *ch);
static int pc_write(struct tty_struct *, const unsigned char *, int);
static int pc_init(void);
static int init_PCI(void);
/*
* Table of functions for each board to handle memory. Mantaining parallelism
* is a *very* good idea here. The idea is for the runtime code to blindly call
* these functions, not knowing/caring about the underlying hardware. This
* stuff should contain no conditionals; if more functionality is needed a
* different entry should be established. These calls are the interface calls
* and are the only functions that should be accessed. Anyone caught making
* direct calls deserves what they get.
*/
static void memwinon(struct board_info *b, unsigned int win)
{
b->memwinon(b, win);
}
static void memwinoff(struct board_info *b, unsigned int win)
{
b->memwinoff(b, win);
}
static void globalwinon(struct channel *ch)
{
ch->board->globalwinon(ch);
}
static void rxwinon(struct channel *ch)
{
ch->board->rxwinon(ch);
}
static void txwinon(struct channel *ch)
{
ch->board->txwinon(ch);
}
static void memoff(struct channel *ch)
{
ch->board->memoff(ch);
}
static void assertgwinon(struct channel *ch)
{
ch->board->assertgwinon(ch);
}
static void assertmemoff(struct channel *ch)
{
ch->board->assertmemoff(ch);
}
/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */
static void pcxem_memwinon(struct board_info *b, unsigned int win)
{
outb_p(FEPWIN | win, b->port + 1);
}
static void pcxem_memwinoff(struct board_info *b, unsigned int win)
{
outb_p(0, b->port + 1);
}
static void pcxem_globalwinon(struct channel *ch)
{
outb_p(FEPWIN, (int)ch->board->port + 1);
}
static void pcxem_rxwinon(struct channel *ch)
{
outb_p(ch->rxwin, (int)ch->board->port + 1);
}
static void pcxem_txwinon(struct channel *ch)
{
outb_p(ch->txwin, (int)ch->board->port + 1);
}
static void pcxem_memoff(struct channel *ch)
{
outb_p(0, (int)ch->board->port + 1);
}
/* ----------------- Begin pcxe memory window stuff ------------------ */
static void pcxe_memwinon(struct board_info *b, unsigned int win)
{
outb_p(FEPWIN | win, b->port + 1);
}
static void pcxe_memwinoff(struct board_info *b, unsigned int win)
{
outb_p(inb(b->port) & ~FEPMEM, b->port + 1);
outb_p(0, b->port + 1);
}
static void pcxe_globalwinon(struct channel *ch)
{
outb_p(FEPWIN, (int)ch->board->port + 1);
}
static void pcxe_rxwinon(struct channel *ch)
{
outb_p(ch->rxwin, (int)ch->board->port + 1);
}
static void pcxe_txwinon(struct channel *ch)
{
outb_p(ch->txwin, (int)ch->board->port + 1);
}
static void pcxe_memoff(struct channel *ch)
{
outb_p(0, (int)ch->board->port);
outb_p(0, (int)ch->board->port + 1);
}
/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */
static void pcxi_memwinon(struct board_info *b, unsigned int win)
{
outb_p(inb(b->port) | FEPMEM, b->port);
}
static void pcxi_memwinoff(struct board_info *b, unsigned int win)
{
outb_p(inb(b->port) & ~FEPMEM, b->port);
}
static void pcxi_globalwinon(struct channel *ch)
{
outb_p(FEPMEM, ch->board->port);
}
static void pcxi_rxwinon(struct channel *ch)
{
outb_p(FEPMEM, ch->board->port);
}
static void pcxi_txwinon(struct channel *ch)
{
outb_p(FEPMEM, ch->board->port);
}
static void pcxi_memoff(struct channel *ch)
{
outb_p(0, ch->board->port);
}
static void pcxi_assertgwinon(struct channel *ch)
{
epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off");
}
static void pcxi_assertmemoff(struct channel *ch)
{
epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on");
}
/*
* Not all of the cards need specific memory windowing routines. Some cards
* (Such as PCI) needs no windowing routines at all. We provide these do
* nothing routines so that the same code base can be used. The driver will
* ALWAYS call a windowing routine if it thinks it needs to; regardless of the
* card. However, dependent on the card the routine may or may not do anything.
*/
static void dummy_memwinon(struct board_info *b, unsigned int win)
{
}
static void dummy_memwinoff(struct board_info *b, unsigned int win)
{
}
static void dummy_globalwinon(struct channel *ch)
{
}
static void dummy_rxwinon(struct channel *ch)
{
}
static void dummy_txwinon(struct channel *ch)
{
}
static void dummy_memoff(struct channel *ch)
{
}
static void dummy_assertgwinon(struct channel *ch)
{
}
static void dummy_assertmemoff(struct channel *ch)
{
}
static struct channel *verifyChannel(struct tty_struct *tty)
{
/*
* This routine basically provides a sanity check. It insures that the
* channel returned is within the proper range of addresses as well as
* properly initialized. If some bogus info gets passed in
* through tty->driver_data this should catch it.
*/
if (tty) {
struct channel *ch = tty->driver_data;
if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) {
if (ch->magic == EPCA_MAGIC)
return ch;
}
}
return NULL;
}
static void pc_sched_event(struct channel *ch, int event)
{
/*
* We call this to schedule interrupt processing on some event. The
* kernel sees our request and calls the related routine in OUR driver.
*/
ch->event |= 1 << event;
schedule_work(&ch->tqueue);
}
static void epca_error(int line, char *msg)
{
printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg);
}
static void pc_close(struct tty_struct *tty, struct file *filp)
{
struct channel *ch;
struct tty_port *port;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch == NULL)
return;
port = &ch->port;
if (tty_port_close_start(port, tty, filp) == 0)
return;
pc_flush_buffer(tty);
shutdown(ch, tty);
tty_port_close_end(port, tty);
ch->event = 0; /* FIXME: review ch->event locking */
tty_port_tty_set(port, NULL);
}
static void shutdown(struct channel *ch, struct tty_struct *tty)
{
unsigned long flags;
struct board_chan __iomem *bc;
struct tty_port *port = &ch->port;
if (!(port->flags & ASYNC_INITIALIZED))
return;
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
bc = ch->brdchan;
/*
* In order for an event to be generated on the receipt of data the
* idata flag must be set. Since we are shutting down, this is not
* necessary clear this flag.
*/
if (bc)
writeb(0, &bc->idata);
/* If we're a modem control device and HUPCL is on, drop RTS & DTR. */
if (tty->termios->c_cflag & HUPCL) {
ch->omodem &= ~(ch->m_rts | ch->m_dtr);
fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1);
}
memoff(ch);
/*
* The channel has officially been closed. The next time it is opened it
* will have to reinitialized. Set a flag to indicate this.
*/
/* Prevent future Digi programmed interrupts from coming active */
port->flags &= ~ASYNC_INITIALIZED;
spin_unlock_irqrestore(&epca_lock, flags);
}
static void pc_hangup(struct tty_struct *tty)
{
struct channel *ch;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
pc_flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(ch, tty);
ch->event = 0; /* FIXME: review locking of ch->event */
tty_port_hangup(&ch->port);
}
}
static int pc_write(struct tty_struct *tty,
const unsigned char *buf, int bytesAvailable)
{
unsigned int head, tail;
int dataLen;
int size;
int amountCopied;
struct channel *ch;
unsigned long flags;
int remain;
struct board_chan __iomem *bc;
/*
* pc_write is primarily called directly by the kernel routine
* tty_write (Though it can also be called by put_char) found in
* tty_io.c. pc_write is passed a line discipline buffer where the data
* to be written out is stored. The line discipline implementation
* itself is done at the kernel level and is not brought into the
* driver.
*/
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch == NULL)
return 0;
/* Make a pointer to the channel data structure found on the board. */
bc = ch->brdchan;
size = ch->txbufsize;
amountCopied = 0;
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
head = readw(&bc->tin) & (size - 1);
tail = readw(&bc->tout);
if (tail != readw(&bc->tout))
tail = readw(&bc->tout);
tail &= (size - 1);
if (head >= tail) {
/* head has not wrapped */
/*
* remain (much like dataLen above) represents the total amount
* of space available on the card for data. Here dataLen
* represents the space existing between the head pointer and
* the end of buffer. This is important because a memcpy cannot
* be told to automatically wrap around when it hits the buffer
* end.
*/
dataLen = size - head;
remain = size - (head - tail) - 1;
} else {
/* head has wrapped around */
remain = tail - head - 1;
dataLen = remain;
}
/*
* Check the space on the card. If we have more data than space; reduce
* the amount of data to fit the space.
*/
bytesAvailable = min(remain, bytesAvailable);
txwinon(ch);
while (bytesAvailable > 0) {
/* there is data to copy onto card */
/*
* If head is not wrapped, the below will make sure the first
* data copy fills to the end of card buffer.
*/
dataLen = min(bytesAvailable, dataLen);
memcpy_toio(ch->txptr + head, buf, dataLen);
buf += dataLen;
head += dataLen;
amountCopied += dataLen;
bytesAvailable -= dataLen;
if (head >= size) {
head = 0;
dataLen = tail;
}
}
ch->statusflags |= TXBUSY;
globalwinon(ch);
writew(head, &bc->tin);
if ((ch->statusflags & LOWWAIT) == 0) {
ch->statusflags |= LOWWAIT;
writeb(1, &bc->ilow);
}
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
return amountCopied;
}
static int pc_write_room(struct tty_struct *tty)
{
int remain = 0;
struct channel *ch;
unsigned long flags;
unsigned int head, tail;
struct board_chan __iomem *bc;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
bc = ch->brdchan;
head = readw(&bc->tin) & (ch->txbufsize - 1);
tail = readw(&bc->tout);
if (tail != readw(&bc->tout))
tail = readw(&bc->tout);
/* Wrap tail if necessary */
tail &= (ch->txbufsize - 1);
remain = tail - head - 1;
if (remain < 0)
remain += ch->txbufsize;
if (remain && (ch->statusflags & LOWWAIT) == 0) {
ch->statusflags |= LOWWAIT;
writeb(1, &bc->ilow);
}
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
}
/* Return how much room is left on card */
return remain;
}
static int pc_chars_in_buffer(struct tty_struct *tty)
{
int chars;
unsigned int ctail, head, tail;
int remain;
unsigned long flags;
struct channel *ch;
struct board_chan __iomem *bc;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch == NULL)
return 0;
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
bc = ch->brdchan;
tail = readw(&bc->tout);
head = readw(&bc->tin);
ctail = readw(&ch->mailbox->cout);
if (tail == head && readw(&ch->mailbox->cin) == ctail &&
readb(&bc->tbusy) == 0)
chars = 0;
else { /* Begin if some space on the card has been used */
head = readw(&bc->tin) & (ch->txbufsize - 1);
tail &= (ch->txbufsize - 1);
/*
* The logic here is basically opposite of the above
* pc_write_room here we are finding the amount of bytes in the
* buffer filled. Not the amount of bytes empty.
*/
remain = tail - head - 1;
if (remain < 0)
remain += ch->txbufsize;
chars = (int)(ch->txbufsize - remain);
/*
* Make it possible to wakeup anything waiting for output in
* tty_ioctl.c, etc.
*
* If not already set. Setup an event to indicate when the
* transmit buffer empties.
*/
if (!(ch->statusflags & EMPTYWAIT))
setup_empty_event(tty, ch);
} /* End if some space on the card has been used */
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
/* Return number of characters residing on card. */
return chars;
}
static void pc_flush_buffer(struct tty_struct *tty)
{
unsigned int tail;
unsigned long flags;
struct channel *ch;
struct board_chan __iomem *bc;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch == NULL)
return;
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
bc = ch->brdchan;
tail = readw(&bc->tout);
/* Have FEP move tout pointer; effectively flushing transmit buffer */
fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
tty_wakeup(tty);
}
static void pc_flush_chars(struct tty_struct *tty)
{
struct channel *ch;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
unsigned long flags;
spin_lock_irqsave(&epca_lock, flags);
/*
* If not already set and the transmitter is busy setup an
* event to indicate when the transmit empties.
*/
if ((ch->statusflags & TXBUSY) &&
!(ch->statusflags & EMPTYWAIT))
setup_empty_event(tty, ch);
spin_unlock_irqrestore(&epca_lock, flags);
}
}
static int epca_carrier_raised(struct tty_port *port)
{
struct channel *ch = container_of(port, struct channel, port);
if (ch->imodem & ch->dcd)
return 1;
return 0;
}
static void epca_dtr_rts(struct tty_port *port, int onoff)
{
}
static int pc_open(struct tty_struct *tty, struct file *filp)
{
struct channel *ch;
struct tty_port *port;
unsigned long flags;
int line, retval, boardnum;
struct board_chan __iomem *bc;
unsigned int head;
line = tty->index;
if (line < 0 || line >= nbdevs)
return -ENODEV;
ch = &digi_channels[line];
port = &ch->port;
boardnum = ch->boardnum;
/* Check status of board configured in system. */
/*
* I check to see if the epca_setup routine detected a user error. It
* might be better to put this in pc_init, but for the moment it goes
* here.
*/
if (invalid_lilo_config) {
if (setup_error_code & INVALID_BOARD_TYPE)
printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n");
if (setup_error_code & INVALID_NUM_PORTS)
printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n");
if (setup_error_code & INVALID_MEM_BASE)
printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n");
if (setup_error_code & INVALID_PORT_BASE)
printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n");
if (setup_error_code & INVALID_BOARD_STATUS)
printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n");
if (setup_error_code & INVALID_ALTPIN)
printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n");
tty->driver_data = NULL; /* Mark this device as 'down' */
return -ENODEV;
}
if (boardnum >= num_cards || boards[boardnum].status == DISABLED) {
tty->driver_data = NULL; /* Mark this device as 'down' */
return -ENODEV;
}
bc = ch->brdchan;
if (bc == NULL) {
tty->driver_data = NULL;
return -ENODEV;
}
spin_lock_irqsave(&port->lock, flags);
/*
* Every time a channel is opened, increment a counter. This is
* necessary because we do not wish to flush and shutdown the channel
* until the last app holding the channel open, closes it.
*/
port->count++;
/*
* Set a kernel structures pointer to our local channel structure. This
* way we can get to it when passed only a tty struct.
*/
tty->driver_data = ch;
port->tty = tty;
/*
* If this is the first time the channel has been opened, initialize
* the tty->termios struct otherwise let pc_close handle it.
*/
spin_lock(&epca_lock);
globalwinon(ch);
ch->statusflags = 0;
/* Save boards current modem status */
ch->imodem = readb(&bc->mstat);
/*
* Set receive head and tail ptrs to each other. This indicates no data
* available to read.
*/
head = readw(&bc->rin);
writew(head, &bc->rout);
/* Set the channels associated tty structure */
/*
* The below routine generally sets up parity, baud, flow control
* issues, etc.... It effect both control flags and input flags.
*/
epcaparam(tty, ch);
memoff(ch);
spin_unlock(&epca_lock);
port->flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&port->lock, flags);
retval = tty_port_block_til_ready(port, tty, filp);
if (retval)
return retval;
/*
* Set this again in case a hangup set it to zero while this open() was
* waiting for the line...
*/
spin_lock_irqsave(&port->lock, flags);
port->tty = tty;
spin_lock(&epca_lock);
globalwinon(ch);
/* Enable Digi Data events */
writeb(1, &bc->idata);
memoff(ch);
spin_unlock(&epca_lock);
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
static int __init epca_module_init(void)
{
return pc_init();
}
module_init(epca_module_init);
static struct pci_driver epca_driver;
static void __exit epca_module_exit(void)
{
int count, crd;
struct board_info *bd;
struct channel *ch;
del_timer_sync(&epca_timer);
if (tty_unregister_driver(pc_driver) ||
tty_unregister_driver(pc_info)) {
printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n");
return;
}
put_tty_driver(pc_driver);
put_tty_driver(pc_info);
for (crd = 0; crd < num_cards; crd++) {
bd = &boards[crd];
if (!bd) { /* sanity check */
printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n");
return;
}
ch = card_ptr[crd];
for (count = 0; count < bd->numports; count++, ch++) {
struct tty_struct *tty = tty_port_tty_get(&ch->port);
if (tty) {
tty_hangup(tty);
tty_kref_put(tty);
}
}
}
pci_unregister_driver(&epca_driver);
}
module_exit(epca_module_exit);
static const struct tty_operations pc_ops = {
.open = pc_open,
.close = pc_close,
.write = pc_write,
.write_room = pc_write_room,
.flush_buffer = pc_flush_buffer,
.chars_in_buffer = pc_chars_in_buffer,
.flush_chars = pc_flush_chars,
.ioctl = pc_ioctl,
.set_termios = pc_set_termios,
.stop = pc_stop,
.start = pc_start,
.throttle = pc_throttle,
.unthrottle = pc_unthrottle,
.hangup = pc_hangup,
.break_ctl = pc_send_break
};
static const struct tty_port_operations epca_port_ops = {
.carrier_raised = epca_carrier_raised,
.dtr_rts = epca_dtr_rts,
};
static int info_open(struct tty_struct *tty, struct file *filp)
{
return 0;
}
static const struct tty_operations info_ops = {
.open = info_open,
.ioctl = info_ioctl,
};
static int __init pc_init(void)
{
int crd;
struct board_info *bd;
unsigned char board_id = 0;
int err = -ENOMEM;
int pci_boards_found, pci_count;
pci_count = 0;
pc_driver = alloc_tty_driver(MAX_ALLOC);
if (!pc_driver)
goto out1;
pc_info = alloc_tty_driver(MAX_ALLOC);
if (!pc_info)
goto out2;
/*
* If epca_setup has not been ran by LILO set num_cards to defaults;
* copy board structure defined by digiConfig into drivers board
* structure. Note : If LILO has ran epca_setup then epca_setup will
* handle defining num_cards as well as copying the data into the board
* structure.
*/
if (!liloconfig) {
/* driver has been configured via. epcaconfig */
nbdevs = NBDEVS;
num_cards = NUMCARDS;
memcpy(&boards, &static_boards,
sizeof(struct board_info) * NUMCARDS);
}
/*
* Note : If lilo was used to configure the driver and the ignore
* epcaconfig option was chosen (digiepca=2) then nbdevs and num_cards
* will equal 0 at this point. This is okay; PCI cards will still be
* picked up if detected.
*/
/*
* Set up interrupt, we will worry about memory allocation in
* post_fep_init.
*/
printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION);
/*
* NOTE : This code assumes that the number of ports found in the
* boards array is correct. This could be wrong if the card in question
* is PCI (And therefore has no ports entry in the boards structure.)
* The rest of the information will be valid for PCI because the
* beginning of pc_init scans for PCI and determines i/o and base
* memory addresses. I am not sure if it is possible to read the number
* of ports supported by the card prior to it being booted (Since that
* is the state it is in when pc_init is run). Because it is not
* possible to query the number of supported ports until after the card
* has booted; we are required to calculate the card_ptrs as the card
* is initialized (Inside post_fep_init). The negative thing about this
* approach is that digiDload's call to GET_INFO will have a bad port
* value. (Since this is called prior to post_fep_init.)
*/
pci_boards_found = 0;
if (num_cards < MAXBOARDS)
pci_boards_found += init_PCI();
num_cards += pci_boards_found;
pc_driver->owner = THIS_MODULE;
pc_driver->name = "ttyD";
pc_driver->major = DIGI_MAJOR;
pc_driver->minor_start = 0;
pc_driver->type = TTY_DRIVER_TYPE_SERIAL;
pc_driver->subtype = SERIAL_TYPE_NORMAL;
pc_driver->init_termios = tty_std_termios;
pc_driver->init_termios.c_iflag = 0;
pc_driver->init_termios.c_oflag = 0;
pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
pc_driver->init_termios.c_lflag = 0;
pc_driver->init_termios.c_ispeed = 9600;
pc_driver->init_termios.c_ospeed = 9600;
pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
tty_set_operations(pc_driver, &pc_ops);
pc_info->owner = THIS_MODULE;
pc_info->name = "digi_ctl";
pc_info->major = DIGIINFOMAJOR;
pc_info->minor_start = 0;
pc_info->type = TTY_DRIVER_TYPE_SERIAL;
pc_info->subtype = SERIAL_TYPE_INFO;
pc_info->init_termios = tty_std_termios;
pc_info->init_termios.c_iflag = 0;
pc_info->init_termios.c_oflag = 0;
pc_info->init_termios.c_lflag = 0;
pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
pc_info->init_termios.c_ispeed = 9600;
pc_info->init_termios.c_ospeed = 9600;
pc_info->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(pc_info, &info_ops);
for (crd = 0; crd < num_cards; crd++) {
/*
* This is where the appropriate memory handlers for the
* hardware is set. Everything at runtime blindly jumps through
* these vectors.
*/
/* defined in epcaconfig.h */
bd = &boards[crd];
switch (bd->type) {
case PCXEM:
case EISAXEM:
bd->memwinon = pcxem_memwinon;
bd->memwinoff = pcxem_memwinoff;
bd->globalwinon = pcxem_globalwinon;
bd->txwinon = pcxem_txwinon;
bd->rxwinon = pcxem_rxwinon;
bd->memoff = pcxem_memoff;
bd->assertgwinon = dummy_assertgwinon;
bd->assertmemoff = dummy_assertmemoff;
break;
case PCIXEM:
case PCIXRJ:
case PCIXR:
bd->memwinon = dummy_memwinon;
bd->memwinoff = dummy_memwinoff;
bd->globalwinon = dummy_globalwinon;
bd->txwinon = dummy_txwinon;
bd->rxwinon = dummy_rxwinon;
bd->memoff = dummy_memoff;
bd->assertgwinon = dummy_assertgwinon;
bd->assertmemoff = dummy_assertmemoff;
break;
case PCXE:
case PCXEVE:
bd->memwinon = pcxe_memwinon;
bd->memwinoff = pcxe_memwinoff;
bd->globalwinon = pcxe_globalwinon;
bd->txwinon = pcxe_txwinon;
bd->rxwinon = pcxe_rxwinon;
bd->memoff = pcxe_memoff;
bd->assertgwinon = dummy_assertgwinon;
bd->assertmemoff = dummy_assertmemoff;
break;
case PCXI:
case PC64XE:
bd->memwinon = pcxi_memwinon;
bd->memwinoff = pcxi_memwinoff;
bd->globalwinon = pcxi_globalwinon;
bd->txwinon = pcxi_txwinon;
bd->rxwinon = pcxi_rxwinon;
bd->memoff = pcxi_memoff;
bd->assertgwinon = pcxi_assertgwinon;
bd->assertmemoff = pcxi_assertmemoff;
break;
default:
break;
}
/*
* Some cards need a memory segment to be defined for use in
* transmit and receive windowing operations. These boards are
* listed in the below switch. In the case of the XI the amount
* of memory on the board is variable so the memory_seg is also
* variable. This code determines what they segment should be.
*/
switch (bd->type) {
case PCXE:
case PCXEVE:
case PC64XE:
bd->memory_seg = 0xf000;
break;
case PCXI:
board_id = inb((int)bd->port);
if ((board_id & 0x1) == 0x1) {
/* it's an XI card */
/* Is it a 64K board */
if ((board_id & 0x30) == 0)
bd->memory_seg = 0xf000;
/* Is it a 128K board */
if ((board_id & 0x30) == 0x10)
bd->memory_seg = 0xe000;
/* Is is a 256K board */
if ((board_id & 0x30) == 0x20)
bd->memory_seg = 0xc000;
/* Is it a 512K board */
if ((board_id & 0x30) == 0x30)
bd->memory_seg = 0x8000;
} else
printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n", (int)bd->port);
break;
}
}
err = tty_register_driver(pc_driver);
if (err) {
printk(KERN_ERR "Couldn't register Digi PC/ driver");
goto out3;
}
err = tty_register_driver(pc_info);
if (err) {
printk(KERN_ERR "Couldn't register Digi PC/ info ");
goto out4;
}
/* Start up the poller to check for events on all enabled boards */
init_timer(&epca_timer);
epca_timer.function = epcapoll;
mod_timer(&epca_timer, jiffies + HZ/25);
return 0;
out4:
tty_unregister_driver(pc_driver);
out3:
put_tty_driver(pc_info);
out2:
put_tty_driver(pc_driver);
out1:
return err;
}
static void post_fep_init(unsigned int crd)
{
int i;
void __iomem *memaddr;
struct global_data __iomem *gd;
struct board_info *bd;
struct board_chan __iomem *bc;
struct channel *ch;
int shrinkmem = 0, lowwater;
/*
* This call is made by the user via. the ioctl call DIGI_INIT. It is
* responsible for setting up all the card specific stuff.
*/
bd = &boards[crd];
/*
* If this is a PCI board, get the port info. Remember PCI cards do not
* have entries into the epcaconfig.h file, so we can't get the number
* of ports from it. Unfortunetly, this means that anyone doing a
* DIGI_GETINFO before the board has booted will get an invalid number
* of ports returned (It should return 0). Calls to DIGI_GETINFO after
* DIGI_INIT has been called will return the proper values.
*/
if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */
/*
* Below we use XEMPORTS as a memory offset regardless of which
* PCI card it is. This is because all of the supported PCI
* cards have the same memory offset for the channel data. This
* will have to be changed if we ever develop a PCI/XE card.
* NOTE : The FEP manual states that the port offset is 0xC22
* as opposed to 0xC02. This is only true for PC/XE, and PC/XI
* cards; not for the XEM, or CX series. On the PCI cards the
* number of ports is determined by reading a ID PROM located
* in the box attached to the card. The card can then determine
* the index the id to determine the number of ports available.
* (FYI - The id should be located at 0x1ac (And may use up to
* 4 bytes if the box in question is a XEM or CX)).
*/
/* PCI cards are already remapped at this point ISA are not */
bd->numports = readw(bd->re_map_membase + XEMPORTS);
epcaassert(bd->numports <= 64, "PCI returned a invalid number of ports");
nbdevs += (bd->numports);
} else {
/* Fix up the mappings for ISA/EISA etc */
/* FIXME: 64K - can we be smarter ? */
bd->re_map_membase = ioremap_nocache(bd->membase, 0x10000);
}
if (crd != 0)
card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports;
else
card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */
ch = card_ptr[crd];
epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range");
memaddr = bd->re_map_membase;
/*
* The below assignment will set bc to point at the BEGINNING of the
* cards channel structures. For 1 card there will be between 8 and 64
* of these structures.
*/
bc = memaddr + CHANSTRUCT;
/*
* The below assignment will set gd to point at the BEGINNING of global
* memory address 0xc00. The first data in that global memory actually
* starts at address 0xc1a. The command in pointer begins at 0xd10.
*/
gd = memaddr + GLOBAL;
/*
* XEPORTS (address 0xc22) points at the number of channels the card
* supports. (For 64XE, XI, XEM, and XR use 0xc02)
*/
if ((bd->type == PCXEVE || bd->type == PCXE) &&
(readw(memaddr + XEPORTS) < 3))
shrinkmem = 1;
if (bd->type < PCIXEM)
if (!request_region((int)bd->port, 4, board_desc[bd->type]))
return;
memwinon(bd, 0);
/*
* Remember ch is the main drivers channels structure, while bc is the
* cards channel structure.
*/
for (i = 0; i < bd->numports; i++, ch++, bc++) {
unsigned long flags;
u16 tseg, rseg;
tty_port_init(&ch->port);
ch->port.ops = &epca_port_ops;
ch->brdchan = bc;
ch->mailbox = gd;
INIT_WORK(&ch->tqueue, do_softint);
ch->board = &boards[crd];
spin_lock_irqsave(&epca_lock, flags);
switch (bd->type) {
/*
* Since some of the boards use different bitmaps for
* their control signals we cannot hard code these
* values and retain portability. We virtualize this
* data here.
*/
case EISAXEM:
case PCXEM:
case PCIXEM:
case PCIXRJ:
case PCIXR:
ch->m_rts = 0x02;
ch->m_dcd = 0x80;
ch->m_dsr = 0x20;
ch->m_cts = 0x10;
ch->m_ri = 0x40;
ch->m_dtr = 0x01;
break;
case PCXE:
case PCXEVE:
case PCXI:
case PC64XE:
ch->m_rts = 0x02;
ch->m_dcd = 0x08;
ch->m_dsr = 0x10;
ch->m_cts = 0x20;
ch->m_ri = 0x40;
ch->m_dtr = 0x80;
break;
}
if (boards[crd].altpin) {
ch->dsr = ch->m_dcd;
ch->dcd = ch->m_dsr;
ch->digiext.digi_flags |= DIGI_ALTPIN;
} else {
ch->dcd = ch->m_dcd;
ch->dsr = ch->m_dsr;
}
ch->boardnum = crd;
ch->channelnum = i;
ch->magic = EPCA_MAGIC;
tty_port_tty_set(&ch->port, NULL);
if (shrinkmem) {
fepcmd(ch, SETBUFFER, 32, 0, 0, 0);
shrinkmem = 0;
}
tseg = readw(&bc->tseg);
rseg = readw(&bc->rseg);
switch (bd->type) {
case PCIXEM:
case PCIXRJ:
case PCIXR:
/* Cover all the 2MEG cards */
ch->txptr = memaddr + ((tseg << 4) & 0x1fffff);
ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff);
ch->txwin = FEPWIN | (tseg >> 11);
ch->rxwin = FEPWIN | (rseg >> 11);
break;
case PCXEM:
case EISAXEM:
/* Cover all the 32K windowed cards */
/* Mask equal to window size - 1 */
ch->txptr = memaddr + ((tseg << 4) & 0x7fff);
ch->rxptr = memaddr + ((rseg << 4) & 0x7fff);
ch->txwin = FEPWIN | (tseg >> 11);
ch->rxwin = FEPWIN | (rseg >> 11);
break;
case PCXEVE:
case PCXE:
ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4)
& 0x1fff);
ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9);
ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4)
& 0x1fff);
ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >> 9);
break;
case PCXI:
case PC64XE:
ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4);
ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4);
ch->txwin = ch->rxwin = 0;
break;
}
ch->txbufhead = 0;
ch->txbufsize = readw(&bc->tmax) + 1;
ch->rxbufhead = 0;
ch->rxbufsize = readw(&bc->rmax) + 1;
lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2);
/* Set transmitter low water mark */
fepcmd(ch, STXLWATER, lowwater, 0, 10, 0);
/* Set receiver low water mark */
fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0);
/* Set receiver high water mark */
fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0);
writew(100, &bc->edelay);
writeb(1, &bc->idata);
ch->startc = readb(&bc->startc);
ch->stopc = readb(&bc->stopc);
ch->startca = readb(&bc->startca);
ch->stopca = readb(&bc->stopca);
ch->fepcflag = 0;
ch->fepiflag = 0;
ch->fepoflag = 0;
ch->fepstartc = 0;
ch->fepstopc = 0;
ch->fepstartca = 0;
ch->fepstopca = 0;
ch->port.close_delay = 50;
spin_unlock_irqrestore(&epca_lock, flags);
}
printk(KERN_INFO
"Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n",
VERSION, board_desc[bd->type], (long)bd->port,
(long)bd->membase, bd->numports);
memwinoff(bd, 0);
}
static void epcapoll(unsigned long ignored)
{
unsigned long flags;
int crd;
unsigned int head, tail;
struct channel *ch;
struct board_info *bd;
/*
* This routine is called upon every timer interrupt. Even though the
* Digi series cards are capable of generating interrupts this method
* of non-looping polling is more efficient. This routine checks for
* card generated events (Such as receive data, are transmit buffer
* empty) and acts on those events.
*/
for (crd = 0; crd < num_cards; crd++) {
bd = &boards[crd];
ch = card_ptr[crd];
if ((bd->status == DISABLED) || digi_poller_inhibited)
continue;
/*
* assertmemoff is not needed here; indeed it is an empty
* subroutine. It is being kept because future boards may need
* this as well as some legacy boards.
*/
spin_lock_irqsave(&epca_lock, flags);
assertmemoff(ch);
globalwinon(ch);
/*
* In this case head and tail actually refer to the event queue
* not the transmit or receive queue.
*/
head = readw(&ch->mailbox->ein);
tail = readw(&ch->mailbox->eout);
/* If head isn't equal to tail we have an event */
if (head != tail)
doevent(crd);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
} /* End for each card */
mod_timer(&epca_timer, jiffies + (HZ / 25));
}
static void doevent(int crd)
{
void __iomem *eventbuf;
struct channel *ch, *chan0;
static struct tty_struct *tty;
struct board_info *bd;
struct board_chan __iomem *bc;
unsigned int tail, head;
int event, channel;
int mstat, lstat;
/*
* This subroutine is called by epcapoll when an event is detected
* in the event queue. This routine responds to those events.
*/
bd = &boards[crd];
chan0 = card_ptr[crd];
epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range");
assertgwinon(chan0);
while ((tail = readw(&chan0->mailbox->eout)) !=
(head = readw(&chan0->mailbox->ein))) {
/* Begin while something in event queue */
assertgwinon(chan0);
eventbuf = bd->re_map_membase + tail + ISTART;
/* Get the channel the event occurred on */
channel = readb(eventbuf);
/* Get the actual event code that occurred */
event = readb(eventbuf + 1);
/*
* The two assignments below get the current modem status
* (mstat) and the previous modem status (lstat). These are
* useful because an event could signal a change in modem
* signals itself.
*/
mstat = readb(eventbuf + 2);
lstat = readb(eventbuf + 3);
ch = chan0 + channel;
if ((unsigned)channel >= bd->numports || !ch) {
if (channel >= bd->numports)
ch = chan0;
bc = ch->brdchan;
goto next;
}
bc = ch->brdchan;
if (bc == NULL)
goto next;
tty = tty_port_tty_get(&ch->port);
if (event & DATA_IND) { /* Begin DATA_IND */
receive_data(ch, tty);
assertgwinon(ch);
} /* End DATA_IND */
/* else *//* Fix for DCD transition missed bug */
if (event & MODEMCHG_IND) {
/* A modem signal change has been indicated */
ch->imodem = mstat;
if (test_bit(ASYNCB_CHECK_CD, &ch->port.flags)) {
/* We are now receiving dcd */
if (mstat & ch->dcd)
wake_up_interruptible(&ch->port.open_wait);
else /* No dcd; hangup */
pc_sched_event(ch, EPCA_EVENT_HANGUP);
}
}
if (tty) {
if (event & BREAK_IND) {
/* A break has been indicated */
tty_insert_flip_char(tty, 0, TTY_BREAK);
tty_schedule_flip(tty);
} else if (event & LOWTX_IND) {
if (ch->statusflags & LOWWAIT) {
ch->statusflags &= ~LOWWAIT;
tty_wakeup(tty);
}
} else if (event & EMPTYTX_IND) {
/* This event is generated by
setup_empty_event */
ch->statusflags &= ~TXBUSY;
if (ch->statusflags & EMPTYWAIT) {
ch->statusflags &= ~EMPTYWAIT;
tty_wakeup(tty);
}
}
tty_kref_put(tty);
}
next:
globalwinon(ch);
BUG_ON(!bc);
writew(1, &bc->idata);
writew((tail + 4) & (IMAX - ISTART - 4), &chan0->mailbox->eout);
globalwinon(chan0);
} /* End while something in event queue */
}
static void fepcmd(struct channel *ch, int cmd, int word_or_byte,
int byte2, int ncmds, int bytecmd)
{
unchar __iomem *memaddr;
unsigned int head, cmdTail, cmdStart, cmdMax;
long count;
int n;
/* This is the routine in which commands may be passed to the card. */
if (ch->board->status == DISABLED)
return;
assertgwinon(ch);
/* Remember head (As well as max) is just an offset not a base addr */
head = readw(&ch->mailbox->cin);
/* cmdStart is a base address */
cmdStart = readw(&ch->mailbox->cstart);
/*
* We do the addition below because we do not want a max pointer
* relative to cmdStart. We want a max pointer that points at the
* physical end of the command queue.
*/
cmdMax = (cmdStart + 4 + readw(&ch->mailbox->cmax));
memaddr = ch->board->re_map_membase;
if (head >= (cmdMax - cmdStart) || (head & 03)) {
printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n",
__LINE__, cmd, head);
printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n",
__LINE__, cmdMax, cmdStart);
return;
}
if (bytecmd) {
writeb(cmd, memaddr + head + cmdStart + 0);
writeb(ch->channelnum, memaddr + head + cmdStart + 1);
/* Below word_or_byte is bits to set */
writeb(word_or_byte, memaddr + head + cmdStart + 2);
/* Below byte2 is bits to reset */
writeb(byte2, memaddr + head + cmdStart + 3);
} else {
writeb(cmd, memaddr + head + cmdStart + 0);
writeb(ch->channelnum, memaddr + head + cmdStart + 1);
writeb(word_or_byte, memaddr + head + cmdStart + 2);
}
head = (head + 4) & (cmdMax - cmdStart - 4);
writew(head, &ch->mailbox->cin);
count = FEPTIMEOUT;
for (;;) {
count--;
if (count == 0) {
printk(KERN_ERR "<Error> - Fep not responding in fepcmd()\n");
return;
}
head = readw(&ch->mailbox->cin);
cmdTail = readw(&ch->mailbox->cout);
n = (head - cmdTail) & (cmdMax - cmdStart - 4);
/*
* Basically this will break when the FEP acknowledges the
* command by incrementing cmdTail (Making it equal to head).
*/
if (n <= ncmds * (sizeof(short) * 4))
break;
}
}
/*
* Digi products use fields in their channels structures that are very similar
* to the c_cflag and c_iflag fields typically found in UNIX termios
* structures. The below three routines allow mappings between these hardware
* "flags" and their respective Linux flags.
*/
static unsigned termios2digi_h(struct channel *ch, unsigned cflag)
{
unsigned res = 0;
if (cflag & CRTSCTS) {
ch->digiext.digi_flags |= (RTSPACE | CTSPACE);
res |= ((ch->m_cts) | (ch->m_rts));
}
if (ch->digiext.digi_flags & RTSPACE)
res |= ch->m_rts;
if (ch->digiext.digi_flags & DTRPACE)
res |= ch->m_dtr;
if (ch->digiext.digi_flags & CTSPACE)
res |= ch->m_cts;
if (ch->digiext.digi_flags & DSRPACE)
res |= ch->dsr;
if (ch->digiext.digi_flags & DCDPACE)
res |= ch->dcd;
if (res & (ch->m_rts))
ch->digiext.digi_flags |= RTSPACE;
if (res & (ch->m_cts))
ch->digiext.digi_flags |= CTSPACE;
return res;
}
static unsigned termios2digi_i(struct channel *ch, unsigned iflag)
{
unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK |
INPCK | ISTRIP | IXON | IXANY | IXOFF);
if (ch->digiext.digi_flags & DIGI_AIXON)
res |= IAIXON;
return res;
}
static unsigned termios2digi_c(struct channel *ch, unsigned cflag)
{
unsigned res = 0;
if (cflag & CBAUDEX) {
ch->digiext.digi_flags |= DIGI_FAST;
/*
* HUPCL bit is used by FEP to indicate fast baud table is to
* be used.
*/
res |= FEP_HUPCL;
} else
ch->digiext.digi_flags &= ~DIGI_FAST;
/*
* CBAUD has bit position 0x1000 set these days to indicate Linux
* baud rate remap. Digi hardware can't handle the bit assignment.
* (We use a different bit assignment for high speed.). Clear this
* bit out.
*/
res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE);
/*
* This gets a little confusing. The Digi cards have their own
* representation of c_cflags controlling baud rate. For the most part
* this is identical to the Linux implementation. However; Digi
* supports one rate (76800) that Linux doesn't. This means that the
* c_cflag entry that would normally mean 76800 for Digi actually means
* 115200 under Linux. Without the below mapping, a stty 115200 would
* only drive the board at 76800. Since the rate 230400 is also found
* after 76800, the same problem afflicts us when we choose a rate of
* 230400. Without the below modificiation stty 230400 would actually
* give us 115200.
*
* There are two additional differences. The Linux value for CLOCAL
* (0x800; 0004000) has no meaning to the Digi hardware. Also in later
* releases of Linux; the CBAUD define has CBAUDEX (0x1000; 0010000)
* ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX should be
* checked for a screened out prior to termios2digi_c returning. Since
* CLOCAL isn't used by the board this can be ignored as long as the
* returned value is used only by Digi hardware.
*/
if (cflag & CBAUDEX) {
/*
* The below code is trying to guarantee that only baud rates
* 115200 and 230400 are remapped. We use exclusive or because
* the various baud rates share common bit positions and
* therefore can't be tested for easily.
*/
if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) ||
(!((cflag & 0x7) ^ (B230400 & ~CBAUDEX))))
res += 1;
}
return res;
}
/* Caller must hold the locks */
static void epcaparam(struct tty_struct *tty, struct channel *ch)
{
unsigned int cmdHead;
struct ktermios *ts;
struct board_chan __iomem *bc;
unsigned mval, hflow, cflag, iflag;
bc = ch->brdchan;
epcaassert(bc != NULL, "bc out of range");
assertgwinon(ch);
ts = tty->termios;
if ((ts->c_cflag & CBAUD) == 0) { /* Begin CBAUD detected */
cmdHead = readw(&bc->rin);
writew(cmdHead, &bc->rout);
cmdHead = readw(&bc->tin);
/* Changing baud in mid-stream transmission can be wonderful */
/*
* Flush current transmit buffer by setting cmdTail pointer
* (tout) to cmdHead pointer (tin). Hopefully the transmit
* buffer is empty.
*/
fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0);
mval = 0;
} else { /* Begin CBAUD not detected */
/*
* c_cflags have changed but that change had nothing to do with
* BAUD. Propagate the change to the card.
*/
cflag = termios2digi_c(ch, ts->c_cflag);
if (cflag != ch->fepcflag) {
ch->fepcflag = cflag;
/* Set baud rate, char size, stop bits, parity */
fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0);
}
/*
* If the user has not forced CLOCAL and if the device is not a
* CALLOUT device (Which is always CLOCAL) we set flags such
* that the driver will wait on carrier detect.
*/
if (ts->c_cflag & CLOCAL)
clear_bit(ASYNCB_CHECK_CD, &ch->port.flags);
else
set_bit(ASYNCB_CHECK_CD, &ch->port.flags);
mval = ch->m_dtr | ch->m_rts;
} /* End CBAUD not detected */
iflag = termios2digi_i(ch, ts->c_iflag);
/* Check input mode flags */
if (iflag != ch->fepiflag) {
ch->fepiflag = iflag;
/*
* Command sets channels iflag structure on the board. Such
* things as input soft flow control, handling of parity
* errors, and break handling are all set here.
*
* break handling, parity handling, input stripping,
* flow control chars
*/
fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0);
}
/*
* Set the board mint value for this channel. This will cause hardware
* events to be generated each time the DCD signal (Described in mint)
* changes.
*/
writeb(ch->dcd, &bc->mint);
if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD))
if (ch->digiext.digi_flags & DIGI_FORCEDCD)
writeb(0, &bc->mint);
ch->imodem = readb(&bc->mstat);
hflow = termios2digi_h(ch, ts->c_cflag);
if (hflow != ch->hflow) {
ch->hflow = hflow;
/*
* Hard flow control has been selected but the board is not
* using it. Activate hard flow control now.
*/
fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1);
}
mval ^= ch->modemfake & (mval ^ ch->modem);
if (ch->omodem ^ mval) {
ch->omodem = mval;
/*
* The below command sets the DTR and RTS mstat structure. If
* hard flow control is NOT active these changes will drive the
* output of the actual DTR and RTS lines. If hard flow control
* is active, the changes will be saved in the mstat structure
* and only asserted when hard flow control is turned off.
*/
/* First reset DTR & RTS; then set them */
fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1);
fepcmd(ch, SETMODEM, mval, 0, 0, 1);
}
if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) {
ch->fepstartc = ch->startc;
ch->fepstopc = ch->stopc;
/*
* The XON / XOFF characters have changed; propagate these
* changes to the card.
*/
fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1);
}
if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) {
ch->fepstartca = ch->startca;
ch->fepstopca = ch->stopca;
/*
* Similar to the above, this time the auxilarly XON / XOFF
* characters have changed; propagate these changes to the card.
*/
fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1);
}
}
/* Caller holds lock */
static void receive_data(struct channel *ch, struct tty_struct *tty)
{
unchar *rptr;
struct ktermios *ts = NULL;
struct board_chan __iomem *bc;
int dataToRead, wrapgap, bytesAvailable;
unsigned int tail, head;
unsigned int wrapmask;
/*
* This routine is called by doint when a receive data event has taken
* place.
*/
globalwinon(ch);
if (ch->statusflags & RXSTOPPED)
return;
if (tty)
ts = tty->termios;
bc = ch->brdchan;
BUG_ON(!bc);
wrapmask = ch->rxbufsize - 1;
/*
* Get the head and tail pointers to the receiver queue. Wrap the head
* pointer if it has reached the end of the buffer.
*/
head = readw(&bc->rin);
head &= wrapmask;
tail = readw(&bc->rout) & wrapmask;
bytesAvailable = (head - tail) & wrapmask;
if (bytesAvailable == 0)
return;
/* If CREAD bit is off or device not open, set TX tail to head */
if (!tty || !ts || !(ts->c_cflag & CREAD)) {
writew(head, &bc->rout);
return;
}
if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0)
return;
if (readb(&bc->orun)) {
writeb(0, &bc->orun);
printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",
tty->name);
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
rxwinon(ch);
while (bytesAvailable > 0) {
/* Begin while there is data on the card */
wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail;
/*
* Even if head has wrapped around only report the amount of
* data to be equal to the size - tail. Remember memcpy can't
* automatically wrap around the receive buffer.
*/
dataToRead = (wrapgap < bytesAvailable) ? wrapgap
: bytesAvailable;
/* Make sure we don't overflow the buffer */
dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead);
if (dataToRead == 0)
break;
/*
* Move data read from our card into the line disciplines
* buffer for translation if necessary.
*/
memcpy_fromio(rptr, ch->rxptr + tail, dataToRead);
tail = (tail + dataToRead) & wrapmask;
bytesAvailable -= dataToRead;
} /* End while there is data on the card */
globalwinon(ch);
writew(tail, &bc->rout);
/* Must be called with global data */
tty_schedule_flip(tty);
}
static int info_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case DIGI_GETINFO:
{
struct digi_info di;
int brd;
if (get_user(brd, (unsigned int __user *)arg))
return -EFAULT;
if (brd < 0 || brd >= num_cards || num_cards == 0)
return -ENODEV;
memset(&di, 0, sizeof(di));
di.board = brd;
di.status = boards[brd].status;
di.type = boards[brd].type ;
di.numports = boards[brd].numports ;
/* Legacy fixups - just move along nothing to see */
di.port = (unsigned char *)boards[brd].port ;
di.membase = (unsigned char *)boards[brd].membase ;
if (copy_to_user((void __user *)arg, &di, sizeof(di)))
return -EFAULT;
break;
}
case DIGI_POLLER:
{
int brd = arg & 0xff000000 >> 16;
unsigned char state = arg & 0xff;
if (brd < 0 || brd >= num_cards) {
printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n");
return -ENODEV;
}
digi_poller_inhibited = state;
break;
}
case DIGI_INIT:
{
/*
* This call is made by the apps to complete the
* initialization of the board(s). This routine is
* responsible for setting the card to its initial
* state and setting the drivers control fields to the
* sutianle settings for the card in question.
*/
int crd;
for (crd = 0; crd < num_cards; crd++)
post_fep_init(crd);
break;
}
default:
return -ENOTTY;
}
return 0;
}
static int pc_tiocmget(struct tty_struct *tty)
{
struct channel *ch = tty->driver_data;
struct board_chan __iomem *bc;
unsigned int mstat, mflag = 0;
unsigned long flags;
if (ch)
bc = ch->brdchan;
else
return -EINVAL;
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
mstat = readb(&bc->mstat);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
if (mstat & ch->m_dtr)
mflag |= TIOCM_DTR;
if (mstat & ch->m_rts)
mflag |= TIOCM_RTS;
if (mstat & ch->m_cts)
mflag |= TIOCM_CTS;
if (mstat & ch->dsr)
mflag |= TIOCM_DSR;
if (mstat & ch->m_ri)
mflag |= TIOCM_RI;
if (mstat & ch->dcd)
mflag |= TIOCM_CD;
return mflag;
}
static int pc_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct channel *ch = tty->driver_data;
unsigned long flags;
if (!ch)
return -EINVAL;
spin_lock_irqsave(&epca_lock, flags);
/*
* I think this modemfake stuff is broken. It doesn't correctly reflect
* the behaviour desired by the TIOCM* ioctls. Therefore this is
* probably broken.
*/
if (set & TIOCM_RTS) {
ch->modemfake |= ch->m_rts;
ch->modem |= ch->m_rts;
}
if (set & TIOCM_DTR) {
ch->modemfake |= ch->m_dtr;
ch->modem |= ch->m_dtr;
}
if (clear & TIOCM_RTS) {
ch->modemfake |= ch->m_rts;
ch->modem &= ~ch->m_rts;
}
if (clear & TIOCM_DTR) {
ch->modemfake |= ch->m_dtr;
ch->modem &= ~ch->m_dtr;
}
globalwinon(ch);
/*
* The below routine generally sets up parity, baud, flow control
* issues, etc.... It effect both control flags and input flags.
*/
epcaparam(tty, ch);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
return 0;
}
static int pc_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
digiflow_t dflow;
unsigned long flags;
unsigned int mflag, mstat;
unsigned char startc, stopc;
struct board_chan __iomem *bc;
struct channel *ch = tty->driver_data;
void __user *argp = (void __user *)arg;
if (ch)
bc = ch->brdchan;
else
return -EINVAL;
switch (cmd) {
case TIOCMODG:
mflag = pc_tiocmget(tty);
if (put_user(mflag, (unsigned long __user *)argp))
return -EFAULT;
break;
case TIOCMODS:
if (get_user(mstat, (unsigned __user *)argp))
return -EFAULT;
return pc_tiocmset(tty, mstat, ~mstat);
case TIOCSDTR:
spin_lock_irqsave(&epca_lock, flags);
ch->omodem |= ch->m_dtr;
globalwinon(ch);
fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
break;
case TIOCCDTR:
spin_lock_irqsave(&epca_lock, flags);
ch->omodem &= ~ch->m_dtr;
globalwinon(ch);
fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
break;
case DIGI_GETA:
if (copy_to_user(argp, &ch->digiext, sizeof(digi_t)))
return -EFAULT;
break;
case DIGI_SETAW:
case DIGI_SETAF:
if (cmd == DIGI_SETAW) {
/* Setup an event to indicate when the transmit
buffer empties */
spin_lock_irqsave(&epca_lock, flags);
setup_empty_event(tty, ch);
spin_unlock_irqrestore(&epca_lock, flags);
tty_wait_until_sent(tty, 0);
} else {
/* ldisc lock already held in ioctl */
if (tty->ldisc->ops->flush_buffer)
tty->ldisc->ops->flush_buffer(tty);
}
/* Fall Thru */
case DIGI_SETA:
if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
return -EFAULT;
if (ch->digiext.digi_flags & DIGI_ALTPIN) {
ch->dcd = ch->m_dsr;
ch->dsr = ch->m_dcd;
} else {
ch->dcd = ch->m_dcd;
ch->dsr = ch->m_dsr;
}
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
/*
* The below routine generally sets up parity, baud, flow
* control issues, etc.... It effect both control flags and
* input flags.
*/
epcaparam(tty, ch);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
break;
case DIGI_GETFLOW:
case DIGI_GETAFLOW:
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
if (cmd == DIGI_GETFLOW) {
dflow.startc = readb(&bc->startc);
dflow.stopc = readb(&bc->stopc);
} else {
dflow.startc = readb(&bc->startca);
dflow.stopc = readb(&bc->stopca);
}
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
if (copy_to_user(argp, &dflow, sizeof(dflow)))
return -EFAULT;
break;
case DIGI_SETAFLOW:
case DIGI_SETFLOW:
if (cmd == DIGI_SETFLOW) {
startc = ch->startc;
stopc = ch->stopc;
} else {
startc = ch->startca;
stopc = ch->stopca;
}
if (copy_from_user(&dflow, argp, sizeof(dflow)))
return -EFAULT;
if (dflow.startc != startc || dflow.stopc != stopc) {
/* Begin if setflow toggled */
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
if (cmd == DIGI_SETFLOW) {
ch->fepstartc = ch->startc = dflow.startc;
ch->fepstopc = ch->stopc = dflow.stopc;
fepcmd(ch, SONOFFC, ch->fepstartc,
ch->fepstopc, 0, 1);
} else {
ch->fepstartca = ch->startca = dflow.startc;
ch->fepstopca = ch->stopca = dflow.stopc;
fepcmd(ch, SAUXONOFFC, ch->fepstartca,
ch->fepstopca, 0, 1);
}
if (ch->statusflags & TXSTOPPED)
pc_start(tty);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
} /* End if setflow toggled */
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static void pc_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
struct channel *ch;
unsigned long flags;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) { /* Begin if channel valid */
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
epcaparam(tty, ch);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
if ((old_termios->c_cflag & CRTSCTS) &&
((tty->termios->c_cflag & CRTSCTS) == 0))
tty->hw_stopped = 0;
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&ch->port.open_wait);
} /* End if channel valid */
}
static void do_softint(struct work_struct *work)
{
struct channel *ch = container_of(work, struct channel, tqueue);
/* Called in response to a modem change event */
if (ch && ch->magic == EPCA_MAGIC) {
struct tty_struct *tty = tty_port_tty_get(&ch->port);
if (tty && tty->driver_data) {
if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) {
tty_hangup(tty);
wake_up_interruptible(&ch->port.open_wait);
clear_bit(ASYNCB_NORMAL_ACTIVE,
&ch->port.flags);
}
}
tty_kref_put(tty);
}
}
/*
* pc_stop and pc_start provide software flow control to the routine and the
* pc_ioctl routine.
*/
static void pc_stop(struct tty_struct *tty)
{
struct channel *ch;
unsigned long flags;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
spin_lock_irqsave(&epca_lock, flags);
if ((ch->statusflags & TXSTOPPED) == 0) {
/* Begin if transmit stop requested */
globalwinon(ch);
/* STOP transmitting now !! */
fepcmd(ch, PAUSETX, 0, 0, 0, 0);
ch->statusflags |= TXSTOPPED;
memoff(ch);
} /* End if transmit stop requested */
spin_unlock_irqrestore(&epca_lock, flags);
}
}
static void pc_start(struct tty_struct *tty)
{
struct channel *ch;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
unsigned long flags;
spin_lock_irqsave(&epca_lock, flags);
/* Just in case output was resumed because of a change
in Digi-flow */
if (ch->statusflags & TXSTOPPED) {
/* Begin transmit resume requested */
struct board_chan __iomem *bc;
globalwinon(ch);
bc = ch->brdchan;
if (ch->statusflags & LOWWAIT)
writeb(1, &bc->ilow);
/* Okay, you can start transmitting again... */
fepcmd(ch, RESUMETX, 0, 0, 0, 0);
ch->statusflags &= ~TXSTOPPED;
memoff(ch);
} /* End transmit resume requested */
spin_unlock_irqrestore(&epca_lock, flags);
}
}
/*
* The below routines pc_throttle and pc_unthrottle are used to slow (And
* resume) the receipt of data into the kernels receive buffers. The exact
* occurrence of this depends on the size of the kernels receive buffer and
* what the 'watermarks' are set to for that buffer. See the n_ttys.c file for
* more details.
*/
static void pc_throttle(struct tty_struct *tty)
{
struct channel *ch;
unsigned long flags;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
spin_lock_irqsave(&epca_lock, flags);
if ((ch->statusflags & RXSTOPPED) == 0) {
globalwinon(ch);
fepcmd(ch, PAUSERX, 0, 0, 0, 0);
ch->statusflags |= RXSTOPPED;
memoff(ch);
}
spin_unlock_irqrestore(&epca_lock, flags);
}
}
static void pc_unthrottle(struct tty_struct *tty)
{
struct channel *ch;
unsigned long flags;
/*
* verifyChannel returns the channel from the tty struct if it is
* valid. This serves as a sanity check.
*/
ch = verifyChannel(tty);
if (ch != NULL) {
/* Just in case output was resumed because of a change
in Digi-flow */
spin_lock_irqsave(&epca_lock, flags);
if (ch->statusflags & RXSTOPPED) {
globalwinon(ch);
fepcmd(ch, RESUMERX, 0, 0, 0, 0);
ch->statusflags &= ~RXSTOPPED;
memoff(ch);
}
spin_unlock_irqrestore(&epca_lock, flags);
}
}
static int pc_send_break(struct tty_struct *tty, int msec)
{
struct channel *ch = tty->driver_data;
unsigned long flags;
if (msec == -1)
msec = 0xFFFF;
else if (msec > 0xFFFE)
msec = 0xFFFE;
else if (msec < 1)
msec = 1;
spin_lock_irqsave(&epca_lock, flags);
globalwinon(ch);
/*
* Maybe I should send an infinite break here, schedule() for msec
* amount of time, and then stop the break. This way, the user can't
* screw up the FEP by causing digi_send_break() to be called (i.e. via
* an ioctl()) more than once in msec amount of time.
* Try this for now...
*/
fepcmd(ch, SENDBREAK, msec, 0, 10, 0);
memoff(ch);
spin_unlock_irqrestore(&epca_lock, flags);
return 0;
}
/* Caller MUST hold the lock */
static void setup_empty_event(struct tty_struct *tty, struct channel *ch)
{
struct board_chan __iomem *bc = ch->brdchan;
globalwinon(ch);
ch->statusflags |= EMPTYWAIT;
/*
* When set the iempty flag request a event to be generated when the
* transmit buffer is empty (If there is no BREAK in progress).
*/
writeb(1, &bc->iempty);
memoff(ch);
}
#ifndef MODULE
static void __init epca_setup(char *str, int *ints)
{
struct board_info board;
int index, loop, last;
char *temp, *t2;
unsigned len;
/*
* If this routine looks a little strange it is because it is only
* called if a LILO append command is given to boot the kernel with
* parameters. In this way, we can provide the user a method of
* changing his board configuration without rebuilding the kernel.
*/
if (!liloconfig)
liloconfig = 1;
memset(&board, 0, sizeof(board));
/* Assume the data is int first, later we can change it */
/* I think that array position 0 of ints holds the number of args */
for (last = 0, index = 1; index <= ints[0]; index++)
switch (index) { /* Begin parse switch */
case 1:
board.status = ints[index];
/*
* We check for 2 (As opposed to 1; because 2 is a flag
* instructing the driver to ignore epcaconfig.) For
* this reason we check for 2.
*/
if (board.status == 2) {
/* Begin ignore epcaconfig as well as lilo cmd line */
nbdevs = 0;
num_cards = 0;
return;
} /* End ignore epcaconfig as well as lilo cmd line */
if (board.status > 2) {
printk(KERN_ERR "epca_setup: Invalid board status 0x%x\n",
board.status);
invalid_lilo_config = 1;
setup_error_code |= INVALID_BOARD_STATUS;
return;
}
last = index;
break;
case 2:
board.type = ints[index];
if (board.type >= PCIXEM) {
printk(KERN_ERR "epca_setup: Invalid board type 0x%x\n", board.type);
invalid_lilo_config = 1;
setup_error_code |= INVALID_BOARD_TYPE;
return;
}
last = index;
break;
case 3:
board.altpin = ints[index];
if (board.altpin > 1) {
printk(KERN_ERR "epca_setup: Invalid board altpin 0x%x\n", board.altpin);
invalid_lilo_config = 1;
setup_error_code |= INVALID_ALTPIN;
return;
}
last = index;
break;
case 4:
board.numports = ints[index];
if (board.numports < 2 || board.numports > 256) {
printk(KERN_ERR "epca_setup: Invalid board numports 0x%x\n", board.numports);
invalid_lilo_config = 1;
setup_error_code |= INVALID_NUM_PORTS;
return;
}
nbdevs += board.numports;
last = index;
break;
case 5:
board.port = ints[index];
if (ints[index] <= 0) {
printk(KERN_ERR "epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port);
invalid_lilo_config = 1;
setup_error_code |= INVALID_PORT_BASE;
return;
}
last = index;
break;
case 6:
board.membase = ints[index];
if (ints[index] <= 0) {
printk(KERN_ERR "epca_setup: Invalid memory base 0x%x\n",
(unsigned int)board.membase);
invalid_lilo_config = 1;
setup_error_code |= INVALID_MEM_BASE;
return;
}
last = index;
break;
default:
printk(KERN_ERR "<Error> - epca_setup: Too many integer parms\n");
return;
} /* End parse switch */
while (str && *str) { /* Begin while there is a string arg */
/* find the next comma or terminator */
temp = str;
/* While string is not null, and a comma hasn't been found */
while (*temp && (*temp != ','))
temp++;
if (!*temp)
temp = NULL;
else
*temp++ = 0;
/* Set index to the number of args + 1 */
index = last + 1;
switch (index) {
case 1:
len = strlen(str);
if (strncmp("Disable", str, len) == 0)
board.status = 0;
else if (strncmp("Enable", str, len) == 0)
board.status = 1;
else {
printk(KERN_ERR "epca_setup: Invalid status %s\n", str);
invalid_lilo_config = 1;
setup_error_code |= INVALID_BOARD_STATUS;
return;
}
last = index;
break;
case 2:
for (loop = 0; loop < EPCA_NUM_TYPES; loop++)
if (strcmp(board_desc[loop], str) == 0)
break;
/*
* If the index incremented above refers to a
* legitimate board type set it here.
*/
if (index < EPCA_NUM_TYPES)
board.type = loop;
else {
printk(KERN_ERR "epca_setup: Invalid board type: %s\n", str);
invalid_lilo_config = 1;
setup_error_code |= INVALID_BOARD_TYPE;
return;
}
last = index;
break;
case 3:
len = strlen(str);
if (strncmp("Disable", str, len) == 0)
board.altpin = 0;
else if (strncmp("Enable", str, len) == 0)
board.altpin = 1;
else {
printk(KERN_ERR "epca_setup: Invalid altpin %s\n", str);
invalid_lilo_config = 1;
setup_error_code |= INVALID_ALTPIN;
return;
}
last = index;
break;
case 4:
t2 = str;
while (isdigit(*t2))
t2++;
if (*t2) {
printk(KERN_ERR "epca_setup: Invalid port count %s\n", str);
invalid_lilo_config = 1;
setup_error_code |= INVALID_NUM_PORTS;
return;
}
/*
* There is not a man page for simple_strtoul but the
* code can be found in vsprintf.c. The first argument
* is the string to translate (To an unsigned long
* obviously), the second argument can be the address
* of any character variable or a NULL. If a variable
* is given, the end pointer of the string will be
* stored in that variable; if a NULL is given the end
* pointer will not be returned. The last argument is
* the base to use. If a 0 is indicated, the routine
* will attempt to determine the proper base by looking
* at the values prefix (A '0' for octal, a 'x' for
* hex, etc ... If a value is given it will use that
* value as the base.
*/
board.numports = simple_strtoul(str, NULL, 0);
nbdevs += board.numports;
last = index;
break;
case 5:
t2 = str;
while (isxdigit(*t2))
t2++;
if (*t2) {
printk(KERN_ERR "epca_setup: Invalid i/o address %s\n", str);
invalid_lilo_config = 1;
setup_error_code |= INVALID_PORT_BASE;
return;
}
board.port = simple_strtoul(str, NULL, 16);
last = index;
break;
case 6:
t2 = str;
while (isxdigit(*t2))
t2++;
if (*t2) {
printk(KERN_ERR "epca_setup: Invalid memory base %s\n", str);
invalid_lilo_config = 1;
setup_error_code |= INVALID_MEM_BASE;
return;
}
board.membase = simple_strtoul(str, NULL, 16);
last = index;
break;
default:
printk(KERN_ERR "epca: Too many string parms\n");
return;
}
str = temp;
} /* End while there is a string arg */
if (last < 6) {
printk(KERN_ERR "epca: Insufficient parms specified\n");
return;
}
/* I should REALLY validate the stuff here */
/* Copies our local copy of board into boards */
memcpy((void *)&boards[num_cards], (void *)&board, sizeof(board));
/* Does this get called once per lilo arg are what ? */
printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n",
num_cards, board_desc[board.type],
board.numports, (int)board.port, (unsigned int) board.membase);
num_cards++;
}
static int __init epca_real_setup(char *str)
{
int ints[11];
epca_setup(get_options(str, 11, ints), ints);
return 1;
}
__setup("digiepca", epca_real_setup);
#endif
enum epic_board_types {
brd_xr = 0,
brd_xem,
brd_cx,
brd_xrj,
};
/* indexed directly by epic_board_types enum */
static struct {
unsigned char board_type;
unsigned bar_idx; /* PCI base address region */
} epca_info_tbl[] = {
{ PCIXR, 0, },
{ PCIXEM, 0, },
{ PCICX, 0, },
{ PCIXRJ, 2, },
};
static int __devinit epca_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static int board_num = -1;
int board_idx, info_idx = ent->driver_data;
unsigned long addr;
if (pci_enable_device(pdev))
return -EIO;
board_num++;
board_idx = board_num + num_cards;
if (board_idx >= MAXBOARDS)
goto err_out;
addr = pci_resource_start(pdev, epca_info_tbl[info_idx].bar_idx);
if (!addr) {
printk(KERN_ERR PFX "PCI region #%d not available (size 0)\n",
epca_info_tbl[info_idx].bar_idx);
goto err_out;
}
boards[board_idx].status = ENABLED;
boards[board_idx].type = epca_info_tbl[info_idx].board_type;
boards[board_idx].numports = 0x0;
boards[board_idx].port = addr + PCI_IO_OFFSET;
boards[board_idx].membase = addr;
if (!request_mem_region(addr + PCI_IO_OFFSET, 0x200000, "epca")) {
printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n",
0x200000, addr + PCI_IO_OFFSET);
goto err_out;
}
boards[board_idx].re_map_port = ioremap_nocache(addr + PCI_IO_OFFSET,
0x200000);
if (!boards[board_idx].re_map_port) {
printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n",
0x200000, addr + PCI_IO_OFFSET);
goto err_out_free_pciio;
}
if (!request_mem_region(addr, 0x200000, "epca")) {
printk(KERN_ERR PFX "resource 0x%x @ 0x%lx unavailable\n",
0x200000, addr);
goto err_out_free_iounmap;
}
boards[board_idx].re_map_membase = ioremap_nocache(addr, 0x200000);
if (!boards[board_idx].re_map_membase) {
printk(KERN_ERR PFX "cannot map 0x%x @ 0x%lx\n",
0x200000, addr + PCI_IO_OFFSET);
goto err_out_free_memregion;
}
/*
* I don't know what the below does, but the hardware guys say its
* required on everything except PLX (In this case XRJ).
*/
if (info_idx != brd_xrj) {
pci_write_config_byte(pdev, 0x40, 0);
pci_write_config_byte(pdev, 0x46, 0);
}
return 0;
err_out_free_memregion:
release_mem_region(addr, 0x200000);
err_out_free_iounmap:
iounmap(boards[board_idx].re_map_port);
err_out_free_pciio:
release_mem_region(addr + PCI_IO_OFFSET, 0x200000);
err_out:
return -ENODEV;
}
static struct pci_device_id epca_pci_tbl[] = {
{ PCI_VENDOR_DIGI, PCI_DEVICE_XR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xr },
{ PCI_VENDOR_DIGI, PCI_DEVICE_XEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xem },
{ PCI_VENDOR_DIGI, PCI_DEVICE_CX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_cx },
{ PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, brd_xrj },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, epca_pci_tbl);
static int __init init_PCI(void)
{
memset(&epca_driver, 0, sizeof(epca_driver));
epca_driver.name = "epca";
epca_driver.id_table = epca_pci_tbl;
epca_driver.probe = epca_init_one;
return pci_register_driver(&epca_driver);
}
MODULE_LICENSE("GPL");
#define XEMPORTS 0xC02
#define XEPORTS 0xC22
#define MAX_ALLOC 0x100
#define MAXBOARDS 12
#define FEPCODESEG 0x0200L
#define FEPCODE 0x2000L
#define BIOSCODE 0xf800L
#define MISCGLOBAL 0x0C00L
#define NPORT 0x0C22L
#define MBOX 0x0C40L
#define PORTBASE 0x0C90L
/* Begin code defines used for epca_setup */
#define INVALID_BOARD_TYPE 0x1
#define INVALID_NUM_PORTS 0x2
#define INVALID_MEM_BASE 0x4
#define INVALID_PORT_BASE 0x8
#define INVALID_BOARD_STATUS 0x10
#define INVALID_ALTPIN 0x20
/* End code defines used for epca_setup */
#define FEPCLR 0x00
#define FEPMEM 0x02
#define FEPRST 0x04
#define FEPINT 0x08
#define FEPMASK 0x0e
#define FEPWIN 0x80
#define PCXE 0
#define PCXEVE 1
#define PCXEM 2
#define EISAXEM 3
#define PC64XE 4
#define PCXI 5
#define PCIXEM 7
#define PCICX 8
#define PCIXR 9
#define PCIXRJ 10
#define EPCA_NUM_TYPES 6
static char *board_desc[] =
{
"PC/Xe",
"PC/Xeve",
"PC/Xem",
"EISA/Xem",
"PC/64Xe",
"PC/Xi",
"unknown",
"PCI/Xem",
"PCI/CX",
"PCI/Xr",
"PCI/Xrj",
};
#define STARTC 021
#define STOPC 023
#define IAIXON 0x2000
#define TXSTOPPED 0x1
#define LOWWAIT 0x2
#define EMPTYWAIT 0x4
#define RXSTOPPED 0x8
#define TXBUSY 0x10
#define DISABLED 0
#define ENABLED 1
#define OFF 0
#define ON 1
#define FEPTIMEOUT 200000
#define SERIAL_TYPE_INFO 3
#define EPCA_EVENT_HANGUP 1
#define EPCA_MAGIC 0x5c6df104L
struct channel
{
long magic;
struct tty_port port;
unsigned char boardnum;
unsigned char channelnum;
unsigned char omodem; /* FEP output modem status */
unsigned char imodem; /* FEP input modem status */
unsigned char modemfake; /* Modem values to be forced */
unsigned char modem; /* Force values */
unsigned char hflow;
unsigned char dsr;
unsigned char dcd;
unsigned char m_rts ; /* The bits used in whatever FEP */
unsigned char m_dcd ; /* is indiginous to this board to */
unsigned char m_dsr ; /* represent each of the physical */
unsigned char m_cts ; /* handshake lines */
unsigned char m_ri ;
unsigned char m_dtr ;
unsigned char stopc;
unsigned char startc;
unsigned char stopca;
unsigned char startca;
unsigned char fepstopc;
unsigned char fepstartc;
unsigned char fepstopca;
unsigned char fepstartca;
unsigned char txwin;
unsigned char rxwin;
unsigned short fepiflag;
unsigned short fepcflag;
unsigned short fepoflag;
unsigned short txbufhead;
unsigned short txbufsize;
unsigned short rxbufhead;
unsigned short rxbufsize;
int close_delay;
unsigned long event;
uint dev;
unsigned long statusflags;
unsigned long c_iflag;
unsigned long c_cflag;
unsigned long c_lflag;
unsigned long c_oflag;
unsigned char __iomem *txptr;
unsigned char __iomem *rxptr;
struct board_info *board;
struct board_chan __iomem *brdchan;
struct digi_struct digiext;
struct work_struct tqueue;
struct global_data __iomem *mailbox;
};
struct board_info
{
unsigned char status;
unsigned char type;
unsigned char altpin;
unsigned short numports;
unsigned long port;
unsigned long membase;
void __iomem *re_map_port;
void __iomem *re_map_membase;
unsigned long memory_seg;
void ( * memwinon ) (struct board_info *, unsigned int) ;
void ( * memwinoff ) (struct board_info *, unsigned int) ;
void ( * globalwinon ) (struct channel *) ;
void ( * txwinon ) (struct channel *) ;
void ( * rxwinon ) (struct channel *) ;
void ( * memoff ) (struct channel *) ;
void ( * assertgwinon ) (struct channel *) ;
void ( * assertmemoff ) (struct channel *) ;
unsigned char poller_inhibited ;
};
#define NUMCARDS 0
#define NBDEVS 0
struct board_info static_boards[NUMCARDS]={
};
/* DO NOT HAND EDIT THIS FILE! */
#
# Makefile for the Computone IntelliPort Plus Driver
#
obj-$(CONFIG_COMPUTONE) += ip2.o
ip2-y := ip2main.o
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Definition table for In-line and Bypass commands. Applicable
* only when the standard loadware is active. (This is included
* source code, not a separate compilation module.)
*
*******************************************************************************/
//------------------------------------------------------------------------------
//
// Revision History:
//
// 10 October 1991 MAG First Draft
// 7 November 1991 MAG Reflects additional commands.
// 24 February 1992 MAG Additional commands for 1.4.x loadware
// 11 March 1992 MAG Additional commands
// 30 March 1992 MAG Additional command: CMD_DSS_NOW
// 18 May 1992 MAG Discovered commands 39 & 40 must be at the end of a
// packet: affects implementation.
//------------------------------------------------------------------------------
//************
//* Includes *
//************
#include "i2cmd.h" /* To get some bit-defines */
//------------------------------------------------------------------------------
// Here is the table of global arrays which represent each type of command
// supported in the IntelliPort standard loadware. See also i2cmd.h
// for a more complete explanation of what is going on.
//------------------------------------------------------------------------------
// Here are the various globals: note that the names are not used except through
// the macros defined in i2cmd.h. Also note that although they are character
// arrays here (for extendability) they are cast to structure pointers in the
// i2cmd.h macros. See i2cmd.h for flags definitions.
// Length Flags Command
static UCHAR ct02[] = { 1, BTH, 0x02 }; // DTR UP
static UCHAR ct03[] = { 1, BTH, 0x03 }; // DTR DN
static UCHAR ct04[] = { 1, BTH, 0x04 }; // RTS UP
static UCHAR ct05[] = { 1, BTH, 0x05 }; // RTS DN
static UCHAR ct06[] = { 1, BYP, 0x06 }; // START FL
static UCHAR ct07[] = { 2, BTH, 0x07,0 }; // BAUD
static UCHAR ct08[] = { 2, BTH, 0x08,0 }; // BITS
static UCHAR ct09[] = { 2, BTH, 0x09,0 }; // STOP
static UCHAR ct10[] = { 2, BTH, 0x0A,0 }; // PARITY
static UCHAR ct11[] = { 2, BTH, 0x0B,0 }; // XON
static UCHAR ct12[] = { 2, BTH, 0x0C,0 }; // XOFF
static UCHAR ct13[] = { 1, BTH, 0x0D }; // STOP FL
static UCHAR ct14[] = { 1, BYP|VIP, 0x0E }; // ACK HOTK
//static UCHAR ct15[]={ 2, BTH|VIP, 0x0F,0 }; // IRQ SET
static UCHAR ct16[] = { 2, INL, 0x10,0 }; // IXONOPTS
static UCHAR ct17[] = { 2, INL, 0x11,0 }; // OXONOPTS
static UCHAR ct18[] = { 1, INL, 0x12 }; // CTSENAB
static UCHAR ct19[] = { 1, BTH, 0x13 }; // CTSDSAB
static UCHAR ct20[] = { 1, INL, 0x14 }; // DCDENAB
static UCHAR ct21[] = { 1, BTH, 0x15 }; // DCDDSAB
static UCHAR ct22[] = { 1, BTH, 0x16 }; // DSRENAB
static UCHAR ct23[] = { 1, BTH, 0x17 }; // DSRDSAB
static UCHAR ct24[] = { 1, BTH, 0x18 }; // RIENAB
static UCHAR ct25[] = { 1, BTH, 0x19 }; // RIDSAB
static UCHAR ct26[] = { 2, BTH, 0x1A,0 }; // BRKENAB
static UCHAR ct27[] = { 1, BTH, 0x1B }; // BRKDSAB
//static UCHAR ct28[]={ 2, BTH, 0x1C,0 }; // MAXBLOKSIZE
//static UCHAR ct29[]={ 2, 0, 0x1D,0 }; // reserved
static UCHAR ct30[] = { 1, INL, 0x1E }; // CTSFLOWENAB
static UCHAR ct31[] = { 1, INL, 0x1F }; // CTSFLOWDSAB
static UCHAR ct32[] = { 1, INL, 0x20 }; // RTSFLOWENAB
static UCHAR ct33[] = { 1, INL, 0x21 }; // RTSFLOWDSAB
static UCHAR ct34[] = { 2, BTH, 0x22,0 }; // ISTRIPMODE
static UCHAR ct35[] = { 2, BTH|END, 0x23,0 }; // SENDBREAK
static UCHAR ct36[] = { 2, BTH, 0x24,0 }; // SETERRMODE
//static UCHAR ct36a[]={ 3, INL, 0x24,0,0 }; // SET_REPLACE
// The following is listed for completeness, but should never be sent directly
// by user-level code. It is sent only by library routines in response to data
// movement.
//static UCHAR ct37[]={ 5, BYP|VIP, 0x25,0,0,0,0 }; // FLOW PACKET
// Back to normal
//static UCHAR ct38[] = {11, BTH|VAR, 0x26,0,0,0,0,0,0,0,0,0,0 }; // DEF KEY SEQ
//static UCHAR ct39[]={ 3, BTH|END, 0x27,0,0 }; // OPOSTON
//static UCHAR ct40[]={ 1, BTH|END, 0x28 }; // OPOSTOFF
static UCHAR ct41[] = { 1, BYP, 0x29 }; // RESUME
//static UCHAR ct42[]={ 2, BTH, 0x2A,0 }; // TXBAUD
//static UCHAR ct43[]={ 2, BTH, 0x2B,0 }; // RXBAUD
//static UCHAR ct44[]={ 2, BTH, 0x2C,0 }; // MS PING
//static UCHAR ct45[]={ 1, BTH, 0x2D }; // HOTENAB
//static UCHAR ct46[]={ 1, BTH, 0x2E }; // HOTDSAB
//static UCHAR ct47[]={ 7, BTH, 0x2F,0,0,0,0,0,0 }; // UNIX FLAGS
//static UCHAR ct48[]={ 1, BTH, 0x30 }; // DSRFLOWENAB
//static UCHAR ct49[]={ 1, BTH, 0x31 }; // DSRFLOWDSAB
//static UCHAR ct50[]={ 1, BTH, 0x32 }; // DTRFLOWENAB
//static UCHAR ct51[]={ 1, BTH, 0x33 }; // DTRFLOWDSAB
//static UCHAR ct52[]={ 1, BTH, 0x34 }; // BAUDTABRESET
//static UCHAR ct53[] = { 3, BTH, 0x35,0,0 }; // BAUDREMAP
static UCHAR ct54[] = { 3, BTH, 0x36,0,0 }; // CUSTOMBAUD1
static UCHAR ct55[] = { 3, BTH, 0x37,0,0 }; // CUSTOMBAUD2
static UCHAR ct56[] = { 2, BTH|END, 0x38,0 }; // PAUSE
static UCHAR ct57[] = { 1, BYP, 0x39 }; // SUSPEND
static UCHAR ct58[] = { 1, BYP, 0x3A }; // UNSUSPEND
static UCHAR ct59[] = { 2, BTH, 0x3B,0 }; // PARITYCHK
static UCHAR ct60[] = { 1, INL|VIP, 0x3C }; // BOOKMARKREQ
//static UCHAR ct61[]={ 2, BTH, 0x3D,0 }; // INTERNALLOOP
//static UCHAR ct62[]={ 2, BTH, 0x3E,0 }; // HOTKTIMEOUT
static UCHAR ct63[] = { 2, INL, 0x3F,0 }; // SETTXON
static UCHAR ct64[] = { 2, INL, 0x40,0 }; // SETTXOFF
//static UCHAR ct65[]={ 2, BTH, 0x41,0 }; // SETAUTORTS
//static UCHAR ct66[]={ 2, BTH, 0x42,0 }; // SETHIGHWAT
//static UCHAR ct67[]={ 2, BYP, 0x43,0 }; // STARTSELFL
//static UCHAR ct68[]={ 2, INL, 0x44,0 }; // ENDSELFL
//static UCHAR ct69[]={ 1, BYP, 0x45 }; // HWFLOW_OFF
//static UCHAR ct70[]={ 1, BTH, 0x46 }; // ODSRFL_ENAB
//static UCHAR ct71[]={ 1, BTH, 0x47 }; // ODSRFL_DSAB
//static UCHAR ct72[]={ 1, BTH, 0x48 }; // ODCDFL_ENAB
//static UCHAR ct73[]={ 1, BTH, 0x49 }; // ODCDFL_DSAB
//static UCHAR ct74[]={ 2, BTH, 0x4A,0 }; // LOADLEVEL
//static UCHAR ct75[]={ 2, BTH, 0x4B,0 }; // STATDATA
//static UCHAR ct76[]={ 1, BYP, 0x4C }; // BREAK_ON
//static UCHAR ct77[]={ 1, BYP, 0x4D }; // BREAK_OFF
//static UCHAR ct78[]={ 1, BYP, 0x4E }; // GETFC
static UCHAR ct79[] = { 2, BYP, 0x4F,0 }; // XMIT_NOW
//static UCHAR ct80[]={ 4, BTH, 0x50,0,0,0 }; // DIVISOR_LATCH
//static UCHAR ct81[]={ 1, BYP, 0x51 }; // GET_STATUS
//static UCHAR ct82[]={ 1, BYP, 0x52 }; // GET_TXCNT
//static UCHAR ct83[]={ 1, BYP, 0x53 }; // GET_RXCNT
//static UCHAR ct84[]={ 1, BYP, 0x54 }; // GET_BOXIDS
//static UCHAR ct85[]={10, BYP, 0x55,0,0,0,0,0,0,0,0,0 }; // ENAB_MULT
//static UCHAR ct86[]={ 2, BTH, 0x56,0 }; // RCV_ENABLE
static UCHAR ct87[] = { 1, BYP, 0x57 }; // HW_TEST
//static UCHAR ct88[]={ 3, BTH, 0x58,0,0 }; // RCV_THRESHOLD
//static UCHAR ct90[]={ 3, BYP, 0x5A,0,0 }; // Set SILO
//static UCHAR ct91[]={ 2, BYP, 0x5B,0 }; // timed break
// Some composite commands as well
//static UCHAR cc01[]={ 2, BTH, 0x02,0x04 }; // DTR & RTS UP
//static UCHAR cc02[]={ 2, BTH, 0x03,0x05 }; // DTR & RTS DN
//********
//* Code *
//********
//******************************************************************************
// Function: i2cmdUnixFlags(iflag, cflag, lflag)
// Parameters: Unix tty flags
//
// Returns: Pointer to command structure
//
// Description:
//
// This routine sets the parameters of command 47 and returns a pointer to the
// appropriate structure.
//******************************************************************************
#if 0
cmdSyntaxPtr
i2cmdUnixFlags(unsigned short iflag,unsigned short cflag,unsigned short lflag)
{
cmdSyntaxPtr pCM = (cmdSyntaxPtr) ct47;
pCM->cmd[1] = (unsigned char) iflag;
pCM->cmd[2] = (unsigned char) (iflag >> 8);
pCM->cmd[3] = (unsigned char) cflag;
pCM->cmd[4] = (unsigned char) (cflag >> 8);
pCM->cmd[5] = (unsigned char) lflag;
pCM->cmd[6] = (unsigned char) (lflag >> 8);
return pCM;
}
#endif /* 0 */
//******************************************************************************
// Function: i2cmdBaudDef(which, rate)
// Parameters: ?
//
// Returns: Pointer to command structure
//
// Description:
//
// This routine sets the parameters of commands 54 or 55 (according to the
// argument which), and returns a pointer to the appropriate structure.
//******************************************************************************
static cmdSyntaxPtr
i2cmdBaudDef(int which, unsigned short rate)
{
cmdSyntaxPtr pCM;
switch(which)
{
case 1:
pCM = (cmdSyntaxPtr) ct54;
break;
default:
case 2:
pCM = (cmdSyntaxPtr) ct55;
break;
}
pCM->cmd[1] = (unsigned char) rate;
pCM->cmd[2] = (unsigned char) (rate >> 8);
return pCM;
}
/*******************************************************************************
*
* (c) 1999 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Definitions and support for In-line and Bypass commands.
* Applicable only when the standard loadware is active.
*
*******************************************************************************/
//------------------------------------------------------------------------------
// Revision History:
//
// 10 October 1991 MAG First Draft
// 7 November 1991 MAG Reflects some new commands
// 20 February 1992 MAG CMD_HOTACK corrected: no argument.
// 24 February 1992 MAG Support added for new commands for 1.4.x loadware.
// 11 March 1992 MAG Additional commands.
// 16 March 1992 MAG Additional commands.
// 30 March 1992 MAG Additional command: CMD_DSS_NOW
// 18 May 1992 MAG Changed CMD_OPOST
//
//------------------------------------------------------------------------------
#ifndef I2CMD_H // To prevent multiple includes
#define I2CMD_H 1
#include "ip2types.h"
// This module is designed to provide a uniform method of sending commands to
// the board through command packets. The difficulty is, some commands take
// parameters, others do not. Furthermore, it is often useful to send several
// commands to the same channel as part of the same packet. (See also i2pack.h.)
//
// This module is designed so that the caller should not be responsible for
// remembering the exact syntax of each command, or at least so that the
// compiler could check things somewhat. I'll explain as we go...
//
// First, a structure which can embody the syntax of each type of command.
//
typedef struct _cmdSyntax
{
UCHAR length; // Number of bytes in the command
UCHAR flags; // Information about the command (see below)
// The command and its parameters, which may be of arbitrary length. Don't
// worry yet how the parameters will be initialized; macros later take care
// of it. Also, don't worry about the arbitrary length issue; this structure
// is never used to allocate space (see i2cmd.c).
UCHAR cmd[2];
} cmdSyntax, *cmdSyntaxPtr;
// Bit assignments for flags
#define INL 1 // Set if suitable for inline commands
#define BYP 2 // Set if suitable for bypass commands
#define BTH (INL|BYP) // suitable for either!
#define END 4 // Set if this must be the last command in a block
#define VIP 8 // Set if this command is special in some way and really
// should only be sent from the library-level and not
// directly from user-level
#define VAR 0x10 // This command is of variable length!
// Declarations for the global arrays used to bear the commands and their
// arguments.
//
// Note: Since these are globals and the arguments might change, it is important
// that the library routine COPY these into buffers from whence they would be
// sent, rather than merely storing the pointers. In multi-threaded
// environments, important that the copy should obtain before any context switch
// is allowed. Also, for parameterized commands, DO NOT ISSUE THE SAME COMMAND
// MORE THAN ONCE WITH THE SAME PARAMETERS in the same call.
//
static UCHAR ct02[];
static UCHAR ct03[];
static UCHAR ct04[];
static UCHAR ct05[];
static UCHAR ct06[];
static UCHAR ct07[];
static UCHAR ct08[];
static UCHAR ct09[];
static UCHAR ct10[];
static UCHAR ct11[];
static UCHAR ct12[];
static UCHAR ct13[];
static UCHAR ct14[];
static UCHAR ct15[];
static UCHAR ct16[];
static UCHAR ct17[];
static UCHAR ct18[];
static UCHAR ct19[];
static UCHAR ct20[];
static UCHAR ct21[];
static UCHAR ct22[];
static UCHAR ct23[];
static UCHAR ct24[];
static UCHAR ct25[];
static UCHAR ct26[];
static UCHAR ct27[];
static UCHAR ct28[];
static UCHAR ct29[];
static UCHAR ct30[];
static UCHAR ct31[];
static UCHAR ct32[];
static UCHAR ct33[];
static UCHAR ct34[];
static UCHAR ct35[];
static UCHAR ct36[];
static UCHAR ct36a[];
static UCHAR ct41[];
static UCHAR ct42[];
static UCHAR ct43[];
static UCHAR ct44[];
static UCHAR ct45[];
static UCHAR ct46[];
static UCHAR ct48[];
static UCHAR ct49[];
static UCHAR ct50[];
static UCHAR ct51[];
static UCHAR ct52[];
static UCHAR ct56[];
static UCHAR ct57[];
static UCHAR ct58[];
static UCHAR ct59[];
static UCHAR ct60[];
static UCHAR ct61[];
static UCHAR ct62[];
static UCHAR ct63[];
static UCHAR ct64[];
static UCHAR ct65[];
static UCHAR ct66[];
static UCHAR ct67[];
static UCHAR ct68[];
static UCHAR ct69[];
static UCHAR ct70[];
static UCHAR ct71[];
static UCHAR ct72[];
static UCHAR ct73[];
static UCHAR ct74[];
static UCHAR ct75[];
static UCHAR ct76[];
static UCHAR ct77[];
static UCHAR ct78[];
static UCHAR ct79[];
static UCHAR ct80[];
static UCHAR ct81[];
static UCHAR ct82[];
static UCHAR ct83[];
static UCHAR ct84[];
static UCHAR ct85[];
static UCHAR ct86[];
static UCHAR ct87[];
static UCHAR ct88[];
static UCHAR ct89[];
static UCHAR ct90[];
static UCHAR ct91[];
static UCHAR cc01[];
static UCHAR cc02[];
// Now, refer to i2cmd.c, and see the character arrays defined there. They are
// cast here to cmdSyntaxPtr.
//
// There are library functions for issuing bypass or inline commands. These
// functions take one or more arguments of the type cmdSyntaxPtr. The routine
// then can figure out how long each command is supposed to be and easily add it
// to the list.
//
// For ease of use, we define manifests which return pointers to appropriate
// cmdSyntaxPtr things. But some commands also take arguments. If a single
// argument is used, we define a macro which performs the single assignment and
// (through the expedient of a comma expression) references the appropriate
// pointer. For commands requiring several arguments, we actually define a
// function to perform the assignments.
#define CMD_DTRUP (cmdSyntaxPtr)(ct02) // Raise DTR
#define CMD_DTRDN (cmdSyntaxPtr)(ct03) // Lower DTR
#define CMD_RTSUP (cmdSyntaxPtr)(ct04) // Raise RTS
#define CMD_RTSDN (cmdSyntaxPtr)(ct05) // Lower RTS
#define CMD_STARTFL (cmdSyntaxPtr)(ct06) // Start Flushing Data
#define CMD_DTRRTS_UP (cmdSyntaxPtr)(cc01) // Raise DTR and RTS
#define CMD_DTRRTS_DN (cmdSyntaxPtr)(cc02) // Lower DTR and RTS
// Set Baud Rate for transmit and receive
#define CMD_SETBAUD(arg) \
(((cmdSyntaxPtr)(ct07))->cmd[1] = (arg),(cmdSyntaxPtr)(ct07))
#define CBR_50 1
#define CBR_75 2
#define CBR_110 3
#define CBR_134 4
#define CBR_150 5
#define CBR_200 6
#define CBR_300 7
#define CBR_600 8
#define CBR_1200 9
#define CBR_1800 10
#define CBR_2400 11
#define CBR_4800 12
#define CBR_9600 13
#define CBR_19200 14
#define CBR_38400 15
#define CBR_2000 16
#define CBR_3600 17
#define CBR_7200 18
#define CBR_56000 19
#define CBR_57600 20
#define CBR_64000 21
#define CBR_76800 22
#define CBR_115200 23
#define CBR_C1 24 // Custom baud rate 1
#define CBR_C2 25 // Custom baud rate 2
#define CBR_153600 26
#define CBR_230400 27
#define CBR_307200 28
#define CBR_460800 29
#define CBR_921600 30
// Set Character size
//
#define CMD_SETBITS(arg) \
(((cmdSyntaxPtr)(ct08))->cmd[1] = (arg),(cmdSyntaxPtr)(ct08))
#define CSZ_5 0
#define CSZ_6 1
#define CSZ_7 2
#define CSZ_8 3
// Set number of stop bits
//
#define CMD_SETSTOP(arg) \
(((cmdSyntaxPtr)(ct09))->cmd[1] = (arg),(cmdSyntaxPtr)(ct09))
#define CST_1 0
#define CST_15 1 // 1.5 stop bits
#define CST_2 2
// Set parity option
//
#define CMD_SETPAR(arg) \
(((cmdSyntaxPtr)(ct10))->cmd[1] = (arg),(cmdSyntaxPtr)(ct10))
#define CSP_NP 0 // no parity
#define CSP_OD 1 // odd parity
#define CSP_EV 2 // Even parity
#define CSP_SP 3 // Space parity
#define CSP_MK 4 // Mark parity
// Define xon char for transmitter flow control
//
#define CMD_DEF_IXON(arg) \
(((cmdSyntaxPtr)(ct11))->cmd[1] = (arg),(cmdSyntaxPtr)(ct11))
// Define xoff char for transmitter flow control
//
#define CMD_DEF_IXOFF(arg) \
(((cmdSyntaxPtr)(ct12))->cmd[1] = (arg),(cmdSyntaxPtr)(ct12))
#define CMD_STOPFL (cmdSyntaxPtr)(ct13) // Stop Flushing data
// Acknowledge receipt of hotkey signal
//
#define CMD_HOTACK (cmdSyntaxPtr)(ct14)
// Define irq level to use. Should actually be sent by library-level code, not
// directly from user...
//
#define CMDVALUE_IRQ 15 // For library use at initialization. Until this command
// is sent, board processing doesn't really start.
#define CMD_SET_IRQ(arg) \
(((cmdSyntaxPtr)(ct15))->cmd[1] = (arg),(cmdSyntaxPtr)(ct15))
#define CIR_POLL 0 // No IRQ - Poll
#define CIR_3 3 // IRQ 3
#define CIR_4 4 // IRQ 4
#define CIR_5 5 // IRQ 5
#define CIR_7 7 // IRQ 7
#define CIR_10 10 // IRQ 10
#define CIR_11 11 // IRQ 11
#define CIR_12 12 // IRQ 12
#define CIR_15 15 // IRQ 15
// Select transmit flow xon/xoff options
//
#define CMD_IXON_OPT(arg) \
(((cmdSyntaxPtr)(ct16))->cmd[1] = (arg),(cmdSyntaxPtr)(ct16))
#define CIX_NONE 0 // Incoming Xon/Xoff characters not special
#define CIX_XON 1 // Xoff disable, Xon enable
#define CIX_XANY 2 // Xoff disable, any key enable
// Select receive flow xon/xoff options
//
#define CMD_OXON_OPT(arg) \
(((cmdSyntaxPtr)(ct17))->cmd[1] = (arg),(cmdSyntaxPtr)(ct17))
#define COX_NONE 0 // Don't send Xon/Xoff
#define COX_XON 1 // Send xon/xoff to start/stop incoming data
#define CMD_CTS_REP (cmdSyntaxPtr)(ct18) // Enable CTS reporting
#define CMD_CTS_NREP (cmdSyntaxPtr)(ct19) // Disable CTS reporting
#define CMD_DCD_REP (cmdSyntaxPtr)(ct20) // Enable DCD reporting
#define CMD_DCD_NREP (cmdSyntaxPtr)(ct21) // Disable DCD reporting
#define CMD_DSR_REP (cmdSyntaxPtr)(ct22) // Enable DSR reporting
#define CMD_DSR_NREP (cmdSyntaxPtr)(ct23) // Disable DSR reporting
#define CMD_RI_REP (cmdSyntaxPtr)(ct24) // Enable RI reporting
#define CMD_RI_NREP (cmdSyntaxPtr)(ct25) // Disable RI reporting
// Enable break reporting and select style
//
#define CMD_BRK_REP(arg) \
(((cmdSyntaxPtr)(ct26))->cmd[1] = (arg),(cmdSyntaxPtr)(ct26))
#define CBK_STAT 0x00 // Report breaks as a status (exception,irq)
#define CBK_NULL 0x01 // Report breaks as a good null
#define CBK_STAT_SEQ 0x02 // Report breaks as a status AND as in-band character
// sequence FFh, 01h, 10h
#define CBK_SEQ 0x03 // Report breaks as the in-band
//sequence FFh, 01h, 10h ONLY.
#define CBK_FLSH 0x04 // if this bit set also flush input data
#define CBK_POSIX 0x08 // if this bit set report as FF,0,0 sequence
#define CBK_SINGLE 0x10 // if this bit set with CBK_SEQ or CBK_STAT_SEQ
//then reports single null instead of triple
#define CMD_BRK_NREP (cmdSyntaxPtr)(ct27) // Disable break reporting
// Specify maximum block size for received data
//
#define CMD_MAX_BLOCK(arg) \
(((cmdSyntaxPtr)(ct28))->cmd[1] = (arg),(cmdSyntaxPtr)(ct28))
// -- COMMAND 29 is reserved --
#define CMD_CTSFL_ENAB (cmdSyntaxPtr)(ct30) // Enable CTS flow control
#define CMD_CTSFL_DSAB (cmdSyntaxPtr)(ct31) // Disable CTS flow control
#define CMD_RTSFL_ENAB (cmdSyntaxPtr)(ct32) // Enable RTS flow control
#define CMD_RTSFL_DSAB (cmdSyntaxPtr)(ct33) // Disable RTS flow control
// Specify istrip option
//
#define CMD_ISTRIP_OPT(arg) \
(((cmdSyntaxPtr)(ct34))->cmd[1] = (arg),(cmdSyntaxPtr)(ct34))
#define CIS_NOSTRIP 0 // Strip characters to character size
#define CIS_STRIP 1 // Strip any 8-bit characters to 7 bits
// Send a break of arg milliseconds
//
#define CMD_SEND_BRK(arg) \
(((cmdSyntaxPtr)(ct35))->cmd[1] = (arg),(cmdSyntaxPtr)(ct35))
// Set error reporting mode
//
#define CMD_SET_ERROR(arg) \
(((cmdSyntaxPtr)(ct36))->cmd[1] = (arg),(cmdSyntaxPtr)(ct36))
#define CSE_ESTAT 0 // Report error in a status packet
#define CSE_NOREP 1 // Treat character as though it were good
#define CSE_DROP 2 // Discard the character
#define CSE_NULL 3 // Replace with a null
#define CSE_MARK 4 // Replace with a 3-character sequence (as Unix)
#define CSE_REPLACE 0x8 // Replace the errored character with the
// replacement character defined here
#define CSE_STAT_REPLACE 0x18 // Replace the errored character with the
// replacement character defined here AND
// report the error as a status packet (as in
// CSE_ESTAT).
// COMMAND 37, to send flow control packets, is handled only by low-level
// library code in response to data movement and shouldn't ever be sent by the
// user code. See i2pack.h and the body of i2lib.c for details.
// Enable on-board post-processing, using options given in oflag argument.
// Formerly, this command was automatically preceded by a CMD_OPOST_OFF command
// because the loadware does not permit sending back-to-back CMD_OPOST_ON
// commands without an intervening CMD_OPOST_OFF. BUT, WE LEARN 18 MAY 92, that
// CMD_OPOST_ON and CMD_OPOST_OFF must each be at the end of a packet (or in a
// solo packet). This means the caller must specify separately CMD_OPOST_OFF,
// CMD_OPOST_ON(parm) when he calls i2QueueCommands(). That function will ensure
// each gets a separate packet. Extra CMD_OPOST_OFF's are always ok.
//
#define CMD_OPOST_ON(oflag) \
(*(USHORT *)(((cmdSyntaxPtr)(ct39))->cmd[1]) = (oflag), \
(cmdSyntaxPtr)(ct39))
#define CMD_OPOST_OFF (cmdSyntaxPtr)(ct40) // Disable on-board post-proc
#define CMD_RESUME (cmdSyntaxPtr)(ct41) // Resume: behave as though an XON
// were received;
// Set Transmit baud rate (see command 7 for arguments)
//
#define CMD_SETBAUD_TX(arg) \
(((cmdSyntaxPtr)(ct42))->cmd[1] = (arg),(cmdSyntaxPtr)(ct42))
// Set Receive baud rate (see command 7 for arguments)
//
#define CMD_SETBAUD_RX(arg) \
(((cmdSyntaxPtr)(ct43))->cmd[1] = (arg),(cmdSyntaxPtr)(ct43))
// Request interrupt from board each arg milliseconds. Interrupt will specify
// "received data", even though there may be no data present. If arg == 0,
// disables any such interrupts.
//
#define CMD_PING_REQ(arg) \
(((cmdSyntaxPtr)(ct44))->cmd[1] = (arg),(cmdSyntaxPtr)(ct44))
#define CMD_HOT_ENAB (cmdSyntaxPtr)(ct45) // Enable Hot-key checking
#define CMD_HOT_DSAB (cmdSyntaxPtr)(ct46) // Disable Hot-key checking
#if 0
// COMMAND 47: Send Protocol info via Unix flags:
// iflag = Unix tty t_iflag
// cflag = Unix tty t_cflag
// lflag = Unix tty t_lflag
// See System V Unix/Xenix documentation for the meanings of the bit fields
// within these flags
//
#define CMD_UNIX_FLAGS(iflag,cflag,lflag) i2cmdUnixFlags(iflag,cflag,lflag)
#endif /* 0 */
#define CMD_DSRFL_ENAB (cmdSyntaxPtr)(ct48) // Enable DSR receiver ctrl
#define CMD_DSRFL_DSAB (cmdSyntaxPtr)(ct49) // Disable DSR receiver ctrl
#define CMD_DTRFL_ENAB (cmdSyntaxPtr)(ct50) // Enable DTR flow control
#define CMD_DTRFL_DSAB (cmdSyntaxPtr)(ct51) // Disable DTR flow control
#define CMD_BAUD_RESET (cmdSyntaxPtr)(ct52) // Reset baudrate table
// COMMAND 54: Define custom rate #1
// rate = (short) 1/10 of the desired baud rate
//
#define CMD_BAUD_DEF1(rate) i2cmdBaudDef(1,rate)
// COMMAND 55: Define custom rate #2
// rate = (short) 1/10 of the desired baud rate
//
#define CMD_BAUD_DEF2(rate) i2cmdBaudDef(2,rate)
// Pause arg hundredths of seconds. (Note, this is NOT milliseconds.)
//
#define CMD_PAUSE(arg) \
(((cmdSyntaxPtr)(ct56))->cmd[1] = (arg),(cmdSyntaxPtr)(ct56))
#define CMD_SUSPEND (cmdSyntaxPtr)(ct57) // Suspend output
#define CMD_UNSUSPEND (cmdSyntaxPtr)(ct58) // Un-Suspend output
// Set parity-checking options
//
#define CMD_PARCHK(arg) \
(((cmdSyntaxPtr)(ct59))->cmd[1] = (arg),(cmdSyntaxPtr)(ct59))
#define CPK_ENAB 0 // Enable parity checking on input
#define CPK_DSAB 1 // Disable parity checking on input
#define CMD_BMARK_REQ (cmdSyntaxPtr)(ct60) // Bookmark request
// Enable/Disable internal loopback mode
//
#define CMD_INLOOP(arg) \
(((cmdSyntaxPtr)(ct61))->cmd[1] = (arg),(cmdSyntaxPtr)(ct61))
#define CIN_DISABLE 0 // Normal operation (default)
#define CIN_ENABLE 1 // Internal (local) loopback
#define CIN_REMOTE 2 // Remote loopback
// Specify timeout for hotkeys: Delay will be (arg x 10) milliseconds, arg == 0
// --> no timeout: wait forever.
//
#define CMD_HOT_TIME(arg) \
(((cmdSyntaxPtr)(ct62))->cmd[1] = (arg),(cmdSyntaxPtr)(ct62))
// Define (outgoing) xon for receive flow control
//
#define CMD_DEF_OXON(arg) \
(((cmdSyntaxPtr)(ct63))->cmd[1] = (arg),(cmdSyntaxPtr)(ct63))
// Define (outgoing) xoff for receiver flow control
//
#define CMD_DEF_OXOFF(arg) \
(((cmdSyntaxPtr)(ct64))->cmd[1] = (arg),(cmdSyntaxPtr)(ct64))
// Enable/Disable RTS on transmit (1/2 duplex-style)
//
#define CMD_RTS_XMIT(arg) \
(((cmdSyntaxPtr)(ct65))->cmd[1] = (arg),(cmdSyntaxPtr)(ct65))
#define CHD_DISABLE 0
#define CHD_ENABLE 1
// Set high-water-mark level (debugging use only)
//
#define CMD_SETHIGHWAT(arg) \
(((cmdSyntaxPtr)(ct66))->cmd[1] = (arg),(cmdSyntaxPtr)(ct66))
// Start flushing tagged data (tag = 0-14)
//
#define CMD_START_SELFL(tag) \
(((cmdSyntaxPtr)(ct67))->cmd[1] = (tag),(cmdSyntaxPtr)(ct67))
// End flushing tagged data (tag = 0-14)
//
#define CMD_END_SELFL(tag) \
(((cmdSyntaxPtr)(ct68))->cmd[1] = (tag),(cmdSyntaxPtr)(ct68))
#define CMD_HWFLOW_OFF (cmdSyntaxPtr)(ct69) // Disable HW TX flow control
#define CMD_ODSRFL_ENAB (cmdSyntaxPtr)(ct70) // Enable DSR output f/c
#define CMD_ODSRFL_DSAB (cmdSyntaxPtr)(ct71) // Disable DSR output f/c
#define CMD_ODCDFL_ENAB (cmdSyntaxPtr)(ct72) // Enable DCD output f/c
#define CMD_ODCDFL_DSAB (cmdSyntaxPtr)(ct73) // Disable DCD output f/c
// Set transmit interrupt load level. Count should be an even value 2-12
//
#define CMD_LOADLEVEL(count) \
(((cmdSyntaxPtr)(ct74))->cmd[1] = (count),(cmdSyntaxPtr)(ct74))
// If reporting DSS changes, map to character sequence FFh, 2, MSR
//
#define CMD_STATDATA(arg) \
(((cmdSyntaxPtr)(ct75))->cmd[1] = (arg),(cmdSyntaxPtr)(ct75))
#define CSTD_DISABLE// Report DSS changes as status packets only (default)
#define CSTD_ENABLE // Report DSS changes as in-band data sequence as well as
// by status packet.
#define CMD_BREAK_ON (cmdSyntaxPtr)(ct76)// Set break and stop xmit
#define CMD_BREAK_OFF (cmdSyntaxPtr)(ct77)// End break and restart xmit
#define CMD_GETFC (cmdSyntaxPtr)(ct78)// Request for flow control packet
// from board.
// Transmit this character immediately
//
#define CMD_XMIT_NOW(ch) \
(((cmdSyntaxPtr)(ct79))->cmd[1] = (ch),(cmdSyntaxPtr)(ct79))
// Set baud rate via "divisor latch"
//
#define CMD_DIVISOR_LATCH(which,value) \
(((cmdSyntaxPtr)(ct80))->cmd[1] = (which), \
*(USHORT *)(((cmdSyntaxPtr)(ct80))->cmd[2]) = (value), \
(cmdSyntaxPtr)(ct80))
#define CDL_RX 1 // Set receiver rate
#define CDL_TX 2 // Set transmit rate
// (CDL_TX | CDL_RX) Set both rates
// Request for special diagnostic status pkt from the board.
//
#define CMD_GET_STATUS (cmdSyntaxPtr)(ct81)
// Request time-stamped transmit character count packet.
//
#define CMD_GET_TXCNT (cmdSyntaxPtr)(ct82)
// Request time-stamped receive character count packet.
//
#define CMD_GET_RXCNT (cmdSyntaxPtr)(ct83)
// Request for box/board I.D. packet.
#define CMD_GET_BOXIDS (cmdSyntaxPtr)(ct84)
// Enable or disable multiple channels according to bit-mapped ushorts box 1-4
//
#define CMD_ENAB_MULT(enable, box1, box2, box3, box4) \
(((cmdSytaxPtr)(ct85))->cmd[1] = (enable), \
*(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[2]) = (box1), \
*(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[4]) = (box2), \
*(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[6]) = (box3), \
*(USHORT *)(((cmdSyntaxPtr)(ct85))->cmd[8]) = (box4), \
(cmdSyntaxPtr)(ct85))
#define CEM_DISABLE 0
#define CEM_ENABLE 1
// Enable or disable receiver or receiver interrupts (default both enabled)
//
#define CMD_RCV_ENABLE(ch) \
(((cmdSyntaxPtr)(ct86))->cmd[1] = (ch),(cmdSyntaxPtr)(ct86))
#define CRE_OFF 0 // Disable the receiver
#define CRE_ON 1 // Enable the receiver
#define CRE_INTOFF 2 // Disable receiver interrupts (to loadware)
#define CRE_INTON 3 // Enable receiver interrupts (to loadware)
// Starts up a hardware test process, which runs transparently, and sends a
// STAT_HWFAIL packet in case a hardware failure is detected.
//
#define CMD_HW_TEST (cmdSyntaxPtr)(ct87)
// Change receiver threshold and timeout value:
// Defaults: timeout = 20mS
// threshold count = 8 when DTRflow not in use,
// threshold count = 5 when DTRflow in use.
//
#define CMD_RCV_THRESHOLD(count,ms) \
(((cmdSyntaxPtr)(ct88))->cmd[1] = (count), \
((cmdSyntaxPtr)(ct88))->cmd[2] = (ms), \
(cmdSyntaxPtr)(ct88))
// Makes the loadware report DSS signals for this channel immediately.
//
#define CMD_DSS_NOW (cmdSyntaxPtr)(ct89)
// Set the receive silo parameters
// timeout is ms idle wait until delivery (~VTIME)
// threshold is max characters cause interrupt (~VMIN)
//
#define CMD_SET_SILO(timeout,threshold) \
(((cmdSyntaxPtr)(ct90))->cmd[1] = (timeout), \
((cmdSyntaxPtr)(ct90))->cmd[2] = (threshold), \
(cmdSyntaxPtr)(ct90))
// Set timed break in decisecond (1/10s)
//
#define CMD_LBREAK(ds) \
(((cmdSyntaxPtr)(ct91))->cmd[1] = (ds),(cmdSyntaxPtr)(ct66))
#endif // I2CMD_H
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Low-level interface code for the device driver
* (This is included source code, not a separate compilation
* module.)
*
*******************************************************************************/
//---------------------------------------------
// Function declarations private to this module
//---------------------------------------------
// Functions called only indirectly through i2eBordStr entries.
static int iiWriteBuf16(i2eBordStrPtr, unsigned char *, int);
static int iiWriteBuf8(i2eBordStrPtr, unsigned char *, int);
static int iiReadBuf16(i2eBordStrPtr, unsigned char *, int);
static int iiReadBuf8(i2eBordStrPtr, unsigned char *, int);
static unsigned short iiReadWord16(i2eBordStrPtr);
static unsigned short iiReadWord8(i2eBordStrPtr);
static void iiWriteWord16(i2eBordStrPtr, unsigned short);
static void iiWriteWord8(i2eBordStrPtr, unsigned short);
static int iiWaitForTxEmptyII(i2eBordStrPtr, int);
static int iiWaitForTxEmptyIIEX(i2eBordStrPtr, int);
static int iiTxMailEmptyII(i2eBordStrPtr);
static int iiTxMailEmptyIIEX(i2eBordStrPtr);
static int iiTrySendMailII(i2eBordStrPtr, unsigned char);
static int iiTrySendMailIIEX(i2eBordStrPtr, unsigned char);
static unsigned short iiGetMailII(i2eBordStrPtr);
static unsigned short iiGetMailIIEX(i2eBordStrPtr);
static void iiEnableMailIrqII(i2eBordStrPtr);
static void iiEnableMailIrqIIEX(i2eBordStrPtr);
static void iiWriteMaskII(i2eBordStrPtr, unsigned char);
static void iiWriteMaskIIEX(i2eBordStrPtr, unsigned char);
static void ii2Nop(void);
//***************
//* Static Data *
//***************
static int ii2Safe; // Safe I/O address for delay routine
static int iiDelayed; // Set when the iiResetDelay function is
// called. Cleared when ANY board is reset.
static DEFINE_RWLOCK(Dl_spinlock);
//********
//* Code *
//********
//=======================================================
// Initialization Routines
//
// iiSetAddress
// iiReset
// iiResetDelay
// iiInitialize
//=======================================================
//******************************************************************************
// Function: iiSetAddress(pB, address, delay)
// Parameters: pB - pointer to the board structure
// address - the purported I/O address of the board
// delay - pointer to the 1-ms delay function to use
// in this and any future operations to this board
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// This routine (roughly) checks for address validity, sets the i2eValid OK and
// sets the state to II_STATE_COLD which means that we haven't even sent a reset
// yet.
//
//******************************************************************************
static int
iiSetAddress( i2eBordStrPtr pB, int address, delayFunc_t delay )
{
// Should any failure occur before init is finished...
pB->i2eValid = I2E_INCOMPLETE;
// Cannot check upper limit except extremely: Might be microchannel
// Address must be on an 8-byte boundary
if ((unsigned int)address <= 0x100
|| (unsigned int)address >= 0xfff8
|| (address & 0x7)
)
{
I2_COMPLETE(pB, I2EE_BADADDR);
}
// Initialize accelerators
pB->i2eBase = address;
pB->i2eData = address + FIFO_DATA;
pB->i2eStatus = address + FIFO_STATUS;
pB->i2ePointer = address + FIFO_PTR;
pB->i2eXMail = address + FIFO_MAIL;
pB->i2eXMask = address + FIFO_MASK;
// Initialize i/o address for ii2DelayIO
ii2Safe = address + FIFO_NOP;
// Initialize the delay routine
pB->i2eDelay = ((delay != (delayFunc_t)NULL) ? delay : (delayFunc_t)ii2Nop);
pB->i2eValid = I2E_MAGIC;
pB->i2eState = II_STATE_COLD;
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiReset(pB)
// Parameters: pB - pointer to the board structure
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Attempts to reset the board (see also i2hw.h). Normally, we would use this to
// reset a board immediately after iiSetAddress(), but it is valid to reset a
// board from any state, say, in order to change or re-load loadware. (Under
// such circumstances, no reason to re-run iiSetAddress(), which is why it is a
// separate routine and not included in this routine.
//
//******************************************************************************
static int
iiReset(i2eBordStrPtr pB)
{
// Magic number should be set, else even the address is suspect
if (pB->i2eValid != I2E_MAGIC)
{
I2_COMPLETE(pB, I2EE_BADMAGIC);
}
outb(0, pB->i2eBase + FIFO_RESET); /* Any data will do */
iiDelay(pB, 50); // Pause between resets
outb(0, pB->i2eBase + FIFO_RESET); /* Second reset */
// We must wait before even attempting to read anything from the FIFO: the
// board's P.O.S.T may actually attempt to read and write its end of the
// FIFO in order to check flags, loop back (where supported), etc. On
// completion of this testing it would reset the FIFO, and on completion
// of all // P.O.S.T., write the message. We must not mistake data which
// might have been sent for testing as part of the reset message. To
// better utilize time, say, when resetting several boards, we allow the
// delay to be performed externally; in this way the caller can reset
// several boards, delay a single time, then call the initialization
// routine for all.
pB->i2eState = II_STATE_RESET;
iiDelayed = 0; // i.e., the delay routine hasn't been called since the most
// recent reset.
// Ensure anything which would have been of use to standard loadware is
// blanked out, since board has now forgotten everything!.
pB->i2eUsingIrq = I2_IRQ_UNDEFINED; /* to not use an interrupt so far */
pB->i2eWaitingForEmptyFifo = 0;
pB->i2eOutMailWaiting = 0;
pB->i2eChannelPtr = NULL;
pB->i2eChannelCnt = 0;
pB->i2eLeadoffWord[0] = 0;
pB->i2eFifoInInts = 0;
pB->i2eFifoOutInts = 0;
pB->i2eFatalTrap = NULL;
pB->i2eFatal = 0;
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiResetDelay(pB)
// Parameters: pB - pointer to the board structure
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Using the delay defined in board structure, waits two seconds (for board to
// reset).
//
//******************************************************************************
static int
iiResetDelay(i2eBordStrPtr pB)
{
if (pB->i2eValid != I2E_MAGIC) {
I2_COMPLETE(pB, I2EE_BADMAGIC);
}
if (pB->i2eState != II_STATE_RESET) {
I2_COMPLETE(pB, I2EE_BADSTATE);
}
iiDelay(pB,2000); /* Now we wait for two seconds. */
iiDelayed = 1; /* Delay has been called: ok to initialize */
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiInitialize(pB)
// Parameters: pB - pointer to the board structure
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Attempts to read the Power-on reset message. Initializes any remaining fields
// in the pB structure.
//
// This should be called as the third step of a process beginning with
// iiReset(), then iiResetDelay(). This routine checks to see that the structure
// is "valid" and in the reset state, also confirms that the delay routine has
// been called since the latest reset (to any board! overly strong!).
//
//******************************************************************************
static int
iiInitialize(i2eBordStrPtr pB)
{
int itemp;
unsigned char c;
unsigned short utemp;
unsigned int ilimit;
if (pB->i2eValid != I2E_MAGIC)
{
I2_COMPLETE(pB, I2EE_BADMAGIC);
}
if (pB->i2eState != II_STATE_RESET || !iiDelayed)
{
I2_COMPLETE(pB, I2EE_BADSTATE);
}
// In case there is a failure short of our completely reading the power-up
// message.
pB->i2eValid = I2E_INCOMPLETE;
// Now attempt to read the message.
for (itemp = 0; itemp < sizeof(porStr); itemp++)
{
// We expect the entire message is ready.
if (!I2_HAS_INPUT(pB)) {
pB->i2ePomSize = itemp;
I2_COMPLETE(pB, I2EE_PORM_SHORT);
}
pB->i2ePom.c[itemp] = c = inb(pB->i2eData);
// We check the magic numbers as soon as they are supposed to be read
// (rather than after) to minimize effect of reading something we
// already suspect can't be "us".
if ( (itemp == POR_1_INDEX && c != POR_MAGIC_1) ||
(itemp == POR_2_INDEX && c != POR_MAGIC_2))
{
pB->i2ePomSize = itemp+1;
I2_COMPLETE(pB, I2EE_BADMAGIC);
}
}
pB->i2ePomSize = itemp;
// Ensure that this was all the data...
if (I2_HAS_INPUT(pB))
I2_COMPLETE(pB, I2EE_PORM_LONG);
// For now, we'll fail to initialize if P.O.S.T reports bad chip mapper:
// Implying we will not be able to download any code either: That's ok: the
// condition is pretty explicit.
if (pB->i2ePom.e.porDiag1 & POR_BAD_MAPPER)
{
I2_COMPLETE(pB, I2EE_POSTERR);
}
// Determine anything which must be done differently depending on the family
// of boards!
switch (pB->i2ePom.e.porID & POR_ID_FAMILY)
{
case POR_ID_FII: // IntelliPort-II
pB->i2eFifoStyle = FIFO_II;
pB->i2eFifoSize = 512; // 512 bytes, always
pB->i2eDataWidth16 = false;
pB->i2eMaxIrq = 15; // Because board cannot tell us it is in an 8-bit
// slot, we do allow it to be done (documentation!)
pB->i2eGoodMap[1] =
pB->i2eGoodMap[2] =
pB->i2eGoodMap[3] =
pB->i2eChannelMap[1] =
pB->i2eChannelMap[2] =
pB->i2eChannelMap[3] = 0;
switch (pB->i2ePom.e.porID & POR_ID_SIZE)
{
case POR_ID_II_4:
pB->i2eGoodMap[0] =
pB->i2eChannelMap[0] = 0x0f; // four-port
// Since porPorts1 is based on the Hardware ID register, the numbers
// should always be consistent for IntelliPort-II. Ditto below...
if (pB->i2ePom.e.porPorts1 != 4)
{
I2_COMPLETE(pB, I2EE_INCONSIST);
}
break;
case POR_ID_II_8:
case POR_ID_II_8R:
pB->i2eGoodMap[0] =
pB->i2eChannelMap[0] = 0xff; // Eight port
if (pB->i2ePom.e.porPorts1 != 8)
{
I2_COMPLETE(pB, I2EE_INCONSIST);
}
break;
case POR_ID_II_6:
pB->i2eGoodMap[0] =
pB->i2eChannelMap[0] = 0x3f; // Six Port
if (pB->i2ePom.e.porPorts1 != 6)
{
I2_COMPLETE(pB, I2EE_INCONSIST);
}
break;
}
// Fix up the "good channel list based on any errors reported.
if (pB->i2ePom.e.porDiag1 & POR_BAD_UART1)
{
pB->i2eGoodMap[0] &= ~0x0f;
}
if (pB->i2ePom.e.porDiag1 & POR_BAD_UART2)
{
pB->i2eGoodMap[0] &= ~0xf0;
}
break; // POR_ID_FII case
case POR_ID_FIIEX: // IntelliPort-IIEX
pB->i2eFifoStyle = FIFO_IIEX;
itemp = pB->i2ePom.e.porFifoSize;
// Implicit assumption that fifo would not grow beyond 32k,
// nor would ever be less than 256.
if (itemp < 8 || itemp > 15)
{
I2_COMPLETE(pB, I2EE_INCONSIST);
}
pB->i2eFifoSize = (1 << itemp);
// These are based on what P.O.S.T thinks should be there, based on
// box ID registers
ilimit = pB->i2ePom.e.porNumBoxes;
if (ilimit > ABS_MAX_BOXES)
{
ilimit = ABS_MAX_BOXES;
}
// For as many boxes as EXIST, gives the type of box.
// Added 8/6/93: check for the ISA-4 (asic) which looks like an
// expandable but for whom "8 or 16?" is not the right question.
utemp = pB->i2ePom.e.porFlags;
if (utemp & POR_CEX4)
{
pB->i2eChannelMap[0] = 0x000f;
} else {
utemp &= POR_BOXES;
for (itemp = 0; itemp < ilimit; itemp++)
{
pB->i2eChannelMap[itemp] =
((utemp & POR_BOX_16) ? 0xffff : 0x00ff);
utemp >>= 1;
}
}
// These are based on what P.O.S.T actually found.
utemp = (pB->i2ePom.e.porPorts2 << 8) + pB->i2ePom.e.porPorts1;
for (itemp = 0; itemp < ilimit; itemp++)
{
pB->i2eGoodMap[itemp] = 0;
if (utemp & 1) pB->i2eGoodMap[itemp] |= 0x000f;
if (utemp & 2) pB->i2eGoodMap[itemp] |= 0x00f0;
if (utemp & 4) pB->i2eGoodMap[itemp] |= 0x0f00;
if (utemp & 8) pB->i2eGoodMap[itemp] |= 0xf000;
utemp >>= 4;
}
// Now determine whether we should transfer in 8 or 16-bit mode.
switch (pB->i2ePom.e.porBus & (POR_BUS_SLOT16 | POR_BUS_DIP16) )
{
case POR_BUS_SLOT16 | POR_BUS_DIP16:
pB->i2eDataWidth16 = true;
pB->i2eMaxIrq = 15;
break;
case POR_BUS_SLOT16:
pB->i2eDataWidth16 = false;
pB->i2eMaxIrq = 15;
break;
case 0:
case POR_BUS_DIP16: // In an 8-bit slot, DIP switch don't care.
default:
pB->i2eDataWidth16 = false;
pB->i2eMaxIrq = 7;
break;
}
break; // POR_ID_FIIEX case
default: // Unknown type of board
I2_COMPLETE(pB, I2EE_BAD_FAMILY);
break;
} // End the switch based on family
// Temporarily, claim there is no room in the outbound fifo.
// We will maintain this whenever we check for an empty outbound FIFO.
pB->i2eFifoRemains = 0;
// Now, based on the bus type, should we expect to be able to re-configure
// interrupts (say, for testing purposes).
switch (pB->i2ePom.e.porBus & POR_BUS_TYPE)
{
case POR_BUS_T_ISA:
case POR_BUS_T_UNK: // If the type of bus is undeclared, assume ok.
case POR_BUS_T_MCA:
case POR_BUS_T_EISA:
break;
default:
I2_COMPLETE(pB, I2EE_BADBUS);
}
if (pB->i2eDataWidth16)
{
pB->i2eWriteBuf = iiWriteBuf16;
pB->i2eReadBuf = iiReadBuf16;
pB->i2eWriteWord = iiWriteWord16;
pB->i2eReadWord = iiReadWord16;
} else {
pB->i2eWriteBuf = iiWriteBuf8;
pB->i2eReadBuf = iiReadBuf8;
pB->i2eWriteWord = iiWriteWord8;
pB->i2eReadWord = iiReadWord8;
}
switch(pB->i2eFifoStyle)
{
case FIFO_II:
pB->i2eWaitForTxEmpty = iiWaitForTxEmptyII;
pB->i2eTxMailEmpty = iiTxMailEmptyII;
pB->i2eTrySendMail = iiTrySendMailII;
pB->i2eGetMail = iiGetMailII;
pB->i2eEnableMailIrq = iiEnableMailIrqII;
pB->i2eWriteMask = iiWriteMaskII;
break;
case FIFO_IIEX:
pB->i2eWaitForTxEmpty = iiWaitForTxEmptyIIEX;
pB->i2eTxMailEmpty = iiTxMailEmptyIIEX;
pB->i2eTrySendMail = iiTrySendMailIIEX;
pB->i2eGetMail = iiGetMailIIEX;
pB->i2eEnableMailIrq = iiEnableMailIrqIIEX;
pB->i2eWriteMask = iiWriteMaskIIEX;
break;
default:
I2_COMPLETE(pB, I2EE_INCONSIST);
}
// Initialize state information.
pB->i2eState = II_STATE_READY; // Ready to load loadware.
// Some Final cleanup:
// For some boards, the bootstrap firmware may perform some sort of test
// resulting in a stray character pending in the incoming mailbox. If one is
// there, it should be read and discarded, especially since for the standard
// firmware, it's the mailbox that interrupts the host.
pB->i2eStartMail = iiGetMail(pB);
// Throw it away and clear the mailbox structure element
pB->i2eStartMail = NO_MAIL_HERE;
// Everything is ok now, return with good status/
pB->i2eValid = I2E_MAGIC;
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: ii2DelayTimer(mseconds)
// Parameters: mseconds - number of milliseconds to delay
//
// Returns: Nothing
//
// Description:
//
// This routine delays for approximately mseconds milliseconds and is intended
// to be called indirectly through i2Delay field in i2eBordStr. It uses the
// Linux timer_list mechanism.
//
// The Linux timers use a unit called "jiffies" which are 10mS in the Intel
// architecture. This function rounds the delay period up to the next "jiffy".
// In the Alpha architecture the "jiffy" is 1mS, but this driver is not intended
// for Alpha platforms at this time.
//
//******************************************************************************
static void
ii2DelayTimer(unsigned int mseconds)
{
msleep_interruptible(mseconds);
}
#if 0
//static void ii2DelayIO(unsigned int);
//******************************************************************************
// !!! Not Used, this is DOS crap, some of you young folks may be interested in
// in how things were done in the stone age of caculating machines !!!
// Function: ii2DelayIO(mseconds)
// Parameters: mseconds - number of milliseconds to delay
//
// Returns: Nothing
//
// Description:
//
// This routine delays for approximately mseconds milliseconds and is intended
// to be called indirectly through i2Delay field in i2eBordStr. It is intended
// for use where a clock-based function is impossible: for example, DOS drivers.
//
// This function uses the IN instruction to place bounds on the timing and
// assumes that ii2Safe has been set. This is because I/O instructions are not
// subject to caching and will therefore take a certain minimum time. To ensure
// the delay is at least long enough on fast machines, it is based on some
// fastest-case calculations. On slower machines this may cause VERY long
// delays. (3 x fastest case). In the fastest case, everything is cached except
// the I/O instruction itself.
//
// Timing calculations:
// The fastest bus speed for I/O operations is likely to be 10 MHz. The I/O
// operation in question is a byte operation to an odd address. For 8-bit
// operations, the architecture generally enforces two wait states. At 10 MHz, a
// single cycle time is 100nS. A read operation at two wait states takes 6
// cycles for a total time of 600nS. Therefore approximately 1666 iterations
// would be required to generate a single millisecond delay. The worst
// (reasonable) case would be an 8MHz system with no cacheing. In this case, the
// I/O instruction would take 125nS x 6 cyles = 750 nS. More importantly, code
// fetch of other instructions in the loop would take time (zero wait states,
// however) and would be hard to estimate. This is minimized by using in-line
// assembler for the in inner loop of IN instructions. This consists of just a
// few bytes. So we'll guess about four code fetches per loop. Each code fetch
// should take four cycles, so we have 125nS * 8 = 1000nS. Worst case then is
// that what should have taken 1 mS takes instead 1666 * (1750) = 2.9 mS.
//
// So much for theoretical timings: results using 1666 value on some actual
// machines:
// IBM 286 6MHz 3.15 mS
// Zenith 386 33MHz 2.45 mS
// (brandX) 386 33MHz 1.90 mS (has cache)
// (brandY) 486 33MHz 2.35 mS
// NCR 486 ?? 1.65 mS (microchannel)
//
// For most machines, it is probably safe to scale this number back (remember,
// for robust operation use an actual timed delay if possible), so we are using
// a value of 1190. This yields 1.17 mS for the fastest machine in our sample,
// 1.75 mS for typical 386 machines, and 2.25 mS the absolute slowest machine.
//
// 1/29/93:
// The above timings are too slow. Actual cycle times might be faster. ISA cycle
// times could approach 500 nS, and ...
// The IBM model 77 being microchannel has no wait states for 8-bit reads and
// seems to be accessing the I/O at 440 nS per access (from start of one to
// start of next). This would imply we need 1000/.440 = 2272 iterations to
// guarantee we are fast enough. In actual testing, we see that 2 * 1190 are in
// fact enough. For diagnostics, we keep the level at 1190, but developers note
// this needs tuning.
//
// Safe assumption: 2270 i/o reads = 1 millisecond
//
//******************************************************************************
static int ii2DelValue = 1190; // See timing calculations below
// 1666 for fastest theoretical machine
// 1190 safe for most fast 386 machines
// 1000 for fastest machine tested here
// 540 (sic) for AT286/6Mhz
static void
ii2DelayIO(unsigned int mseconds)
{
if (!ii2Safe)
return; /* Do nothing if this variable uninitialized */
while(mseconds--) {
int i = ii2DelValue;
while ( i-- ) {
inb(ii2Safe);
}
}
}
#endif
//******************************************************************************
// Function: ii2Nop()
// Parameters: None
//
// Returns: Nothing
//
// Description:
//
// iiInitialize will set i2eDelay to this if the delay parameter is NULL. This
// saves checking for a NULL pointer at every call.
//******************************************************************************
static void
ii2Nop(void)
{
return; // no mystery here
}
//=======================================================
// Routines which are available in 8/16-bit versions, or
// in different fifo styles. These are ALL called
// indirectly through the board structure.
//=======================================================
//******************************************************************************
// Function: iiWriteBuf16(pB, address, count)
// Parameters: pB - pointer to board structure
// address - address of data to write
// count - number of data bytes to write
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes 'count' bytes from 'address' to the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// sent (identity unknown...). Uses 16-bit (word) operations. Is called
// indirectly through pB->i2eWriteBuf.
//
//******************************************************************************
static int
iiWriteBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
{
// Rudimentary sanity checking here.
if (pB->i2eValid != I2E_MAGIC)
I2_COMPLETE(pB, I2EE_INVALID);
I2_OUTSW(pB->i2eData, address, count);
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiWriteBuf8(pB, address, count)
// Parameters: pB - pointer to board structure
// address - address of data to write
// count - number of data bytes to write
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes 'count' bytes from 'address' to the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// sent (identity unknown...). This is to be consistent with the 16-bit version.
// Uses 8-bit (byte) operations. Is called indirectly through pB->i2eWriteBuf.
//
//******************************************************************************
static int
iiWriteBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
{
/* Rudimentary sanity checking here */
if (pB->i2eValid != I2E_MAGIC)
I2_COMPLETE(pB, I2EE_INVALID);
I2_OUTSB(pB->i2eData, address, count);
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiReadBuf16(pB, address, count)
// Parameters: pB - pointer to board structure
// address - address to put data read
// count - number of data bytes to read
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Reads 'count' bytes into 'address' from the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// received (identity unknown...). Uses 16-bit (word) operations. Is called
// indirectly through pB->i2eReadBuf.
//
//******************************************************************************
static int
iiReadBuf16(i2eBordStrPtr pB, unsigned char *address, int count)
{
// Rudimentary sanity checking here.
if (pB->i2eValid != I2E_MAGIC)
I2_COMPLETE(pB, I2EE_INVALID);
I2_INSW(pB->i2eData, address, count);
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiReadBuf8(pB, address, count)
// Parameters: pB - pointer to board structure
// address - address to put data read
// count - number of data bytes to read
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Reads 'count' bytes into 'address' from the data fifo specified by the board
// structure pointer pB. Should count happen to be odd, an extra pad byte is
// received (identity unknown...). This to match the 16-bit behaviour. Uses
// 8-bit (byte) operations. Is called indirectly through pB->i2eReadBuf.
//
//******************************************************************************
static int
iiReadBuf8(i2eBordStrPtr pB, unsigned char *address, int count)
{
// Rudimentary sanity checking here.
if (pB->i2eValid != I2E_MAGIC)
I2_COMPLETE(pB, I2EE_INVALID);
I2_INSB(pB->i2eData, address, count);
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: iiReadWord16(pB)
// Parameters: pB - pointer to board structure
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Returns the word read from the data fifo specified by the board-structure
// pointer pB. Uses a 16-bit operation. Is called indirectly through
// pB->i2eReadWord.
//
//******************************************************************************
static unsigned short
iiReadWord16(i2eBordStrPtr pB)
{
return inw(pB->i2eData);
}
//******************************************************************************
// Function: iiReadWord8(pB)
// Parameters: pB - pointer to board structure
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Returns the word read from the data fifo specified by the board-structure
// pointer pB. Uses two 8-bit operations. Bytes are assumed to be LSB first. Is
// called indirectly through pB->i2eReadWord.
//
//******************************************************************************
static unsigned short
iiReadWord8(i2eBordStrPtr pB)
{
unsigned short urs;
urs = inb(pB->i2eData);
return (inb(pB->i2eData) << 8) | urs;
}
//******************************************************************************
// Function: iiWriteWord16(pB, value)
// Parameters: pB - pointer to board structure
// value - data to write
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes the word 'value' to the data fifo specified by the board-structure
// pointer pB. Uses 16-bit operation. Is called indirectly through
// pB->i2eWriteWord.
//
//******************************************************************************
static void
iiWriteWord16(i2eBordStrPtr pB, unsigned short value)
{
outw((int)value, pB->i2eData);
}
//******************************************************************************
// Function: iiWriteWord8(pB, value)
// Parameters: pB - pointer to board structure
// value - data to write
//
// Returns: True if everything appears copacetic.
// False if there is any error: the pB->i2eError field has the error
//
// Description:
//
// Writes the word 'value' to the data fifo specified by the board-structure
// pointer pB. Uses two 8-bit operations (writes LSB first). Is called
// indirectly through pB->i2eWriteWord.
//
//******************************************************************************
static void
iiWriteWord8(i2eBordStrPtr pB, unsigned short value)
{
outb((char)value, pB->i2eData);
outb((char)(value >> 8), pB->i2eData);
}
//******************************************************************************
// Function: iiWaitForTxEmptyII(pB, mSdelay)
// Parameters: pB - pointer to board structure
// mSdelay - period to wait before returning
//
// Returns: True if the FIFO is empty.
// False if it not empty in the required time: the pB->i2eError
// field has the error.
//
// Description:
//
// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
// not empty by the required time, returns false and error in pB->i2eError,
// otherwise returns true.
//
// mSdelay == 0 is taken to mean must be empty on the first test.
//
// This version operates on IntelliPort-II - style FIFO's
//
// Note this routine is organized so that if status is ok there is no delay at
// all called either before or after the test. Is called indirectly through
// pB->i2eWaitForTxEmpty.
//
//******************************************************************************
static int
iiWaitForTxEmptyII(i2eBordStrPtr pB, int mSdelay)
{
unsigned long flags;
int itemp;
for (;;)
{
// This routine hinges on being able to see the "other" status register
// (as seen by the local processor). His incoming fifo is our outgoing
// FIFO.
//
// By the nature of this routine, you would be using this as part of a
// larger atomic context: i.e., you would use this routine to ensure the
// fifo empty, then act on this information. Between these two halves,
// you will generally not want to service interrupts or in any way
// disrupt the assumptions implicit in the larger context.
//
// Even worse, however, this routine "shifts" the status register to
// point to the local status register which is not the usual situation.
// Therefore for extra safety, we force the critical section to be
// completely atomic, and pick up after ourselves before allowing any
// interrupts of any kind.
write_lock_irqsave(&Dl_spinlock, flags);
outb(SEL_COMMAND, pB->i2ePointer);
outb(SEL_CMD_SH, pB->i2ePointer);
itemp = inb(pB->i2eStatus);
outb(SEL_COMMAND, pB->i2ePointer);
outb(SEL_CMD_UNSH, pB->i2ePointer);
if (itemp & ST_IN_EMPTY)
{
I2_UPDATE_FIFO_ROOM(pB);
write_unlock_irqrestore(&Dl_spinlock, flags);
I2_COMPLETE(pB, I2EE_GOOD);
}
write_unlock_irqrestore(&Dl_spinlock, flags);
if (mSdelay-- == 0)
break;
iiDelay(pB, 1); /* 1 mS granularity on checking condition */
}
I2_COMPLETE(pB, I2EE_TXE_TIME);
}
//******************************************************************************
// Function: iiWaitForTxEmptyIIEX(pB, mSdelay)
// Parameters: pB - pointer to board structure
// mSdelay - period to wait before returning
//
// Returns: True if the FIFO is empty.
// False if it not empty in the required time: the pB->i2eError
// field has the error.
//
// Description:
//
// Waits up to "mSdelay" milliseconds for the outgoing FIFO to become empty; if
// not empty by the required time, returns false and error in pB->i2eError,
// otherwise returns true.
//
// mSdelay == 0 is taken to mean must be empty on the first test.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
// Note this routine is organized so that if status is ok there is no delay at
// all called either before or after the test. Is called indirectly through
// pB->i2eWaitForTxEmpty.
//
//******************************************************************************
static int
iiWaitForTxEmptyIIEX(i2eBordStrPtr pB, int mSdelay)
{
unsigned long flags;
for (;;)
{
// By the nature of this routine, you would be using this as part of a
// larger atomic context: i.e., you would use this routine to ensure the
// fifo empty, then act on this information. Between these two halves,
// you will generally not want to service interrupts or in any way
// disrupt the assumptions implicit in the larger context.
write_lock_irqsave(&Dl_spinlock, flags);
if (inb(pB->i2eStatus) & STE_OUT_MT) {
I2_UPDATE_FIFO_ROOM(pB);
write_unlock_irqrestore(&Dl_spinlock, flags);
I2_COMPLETE(pB, I2EE_GOOD);
}
write_unlock_irqrestore(&Dl_spinlock, flags);
if (mSdelay-- == 0)
break;
iiDelay(pB, 1); // 1 mS granularity on checking condition
}
I2_COMPLETE(pB, I2EE_TXE_TIME);
}
//******************************************************************************
// Function: iiTxMailEmptyII(pB)
// Parameters: pB - pointer to board structure
//
// Returns: True if the transmit mailbox is empty.
// False if it not empty.
//
// Description:
//
// Returns true or false according to whether the transmit mailbox is empty (and
// therefore able to accept more mail)
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static int
iiTxMailEmptyII(i2eBordStrPtr pB)
{
int port = pB->i2ePointer;
outb(SEL_OUTMAIL, port);
return inb(port) == 0;
}
//******************************************************************************
// Function: iiTxMailEmptyIIEX(pB)
// Parameters: pB - pointer to board structure
//
// Returns: True if the transmit mailbox is empty.
// False if it not empty.
//
// Description:
//
// Returns true or false according to whether the transmit mailbox is empty (and
// therefore able to accept more mail)
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static int
iiTxMailEmptyIIEX(i2eBordStrPtr pB)
{
return !(inb(pB->i2eStatus) & STE_OUT_MAIL);
}
//******************************************************************************
// Function: iiTrySendMailII(pB,mail)
// Parameters: pB - pointer to board structure
// mail - value to write to mailbox
//
// Returns: True if the transmit mailbox is empty, and mail is sent.
// False if it not empty.
//
// Description:
//
// If outgoing mailbox is empty, sends mail and returns true. If outgoing
// mailbox is not empty, returns false.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static int
iiTrySendMailII(i2eBordStrPtr pB, unsigned char mail)
{
int port = pB->i2ePointer;
outb(SEL_OUTMAIL, port);
if (inb(port) == 0) {
outb(SEL_OUTMAIL, port);
outb(mail, port);
return 1;
}
return 0;
}
//******************************************************************************
// Function: iiTrySendMailIIEX(pB,mail)
// Parameters: pB - pointer to board structure
// mail - value to write to mailbox
//
// Returns: True if the transmit mailbox is empty, and mail is sent.
// False if it not empty.
//
// Description:
//
// If outgoing mailbox is empty, sends mail and returns true. If outgoing
// mailbox is not empty, returns false.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static int
iiTrySendMailIIEX(i2eBordStrPtr pB, unsigned char mail)
{
if (inb(pB->i2eStatus) & STE_OUT_MAIL)
return 0;
outb(mail, pB->i2eXMail);
return 1;
}
//******************************************************************************
// Function: iiGetMailII(pB,mail)
// Parameters: pB - pointer to board structure
//
// Returns: Mailbox data or NO_MAIL_HERE.
//
// Description:
//
// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
// the mailbox, which is guaranteed != NO_MAIL_HERE.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static unsigned short
iiGetMailII(i2eBordStrPtr pB)
{
if (I2_HAS_MAIL(pB)) {
outb(SEL_INMAIL, pB->i2ePointer);
return inb(pB->i2ePointer);
} else {
return NO_MAIL_HERE;
}
}
//******************************************************************************
// Function: iiGetMailIIEX(pB,mail)
// Parameters: pB - pointer to board structure
//
// Returns: Mailbox data or NO_MAIL_HERE.
//
// Description:
//
// If no mail available, returns NO_MAIL_HERE otherwise returns the data from
// the mailbox, which is guaranteed != NO_MAIL_HERE.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static unsigned short
iiGetMailIIEX(i2eBordStrPtr pB)
{
if (I2_HAS_MAIL(pB))
return inb(pB->i2eXMail);
else
return NO_MAIL_HERE;
}
//******************************************************************************
// Function: iiEnableMailIrqII(pB)
// Parameters: pB - pointer to board structure
//
// Returns: Nothing
//
// Description:
//
// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static void
iiEnableMailIrqII(i2eBordStrPtr pB)
{
outb(SEL_MASK, pB->i2ePointer);
outb(ST_IN_MAIL, pB->i2ePointer);
}
//******************************************************************************
// Function: iiEnableMailIrqIIEX(pB)
// Parameters: pB - pointer to board structure
//
// Returns: Nothing
//
// Description:
//
// Enables board to interrupt host (only) by writing to host's in-bound mailbox.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static void
iiEnableMailIrqIIEX(i2eBordStrPtr pB)
{
outb(MX_IN_MAIL, pB->i2eXMask);
}
//******************************************************************************
// Function: iiWriteMaskII(pB)
// Parameters: pB - pointer to board structure
//
// Returns: Nothing
//
// Description:
//
// Writes arbitrary value to the mask register.
//
// This version operates on IntelliPort-II - style FIFO's
//
//******************************************************************************
static void
iiWriteMaskII(i2eBordStrPtr pB, unsigned char value)
{
outb(SEL_MASK, pB->i2ePointer);
outb(value, pB->i2ePointer);
}
//******************************************************************************
// Function: iiWriteMaskIIEX(pB)
// Parameters: pB - pointer to board structure
//
// Returns: Nothing
//
// Description:
//
// Writes arbitrary value to the mask register.
//
// This version operates on IntelliPort-IIEX - style FIFO's
//
//******************************************************************************
static void
iiWriteMaskIIEX(i2eBordStrPtr pB, unsigned char value)
{
outb(value, pB->i2eXMask);
}
//******************************************************************************
// Function: iiDownloadBlock(pB, pSource, isStandard)
// Parameters: pB - pointer to board structure
// pSource - loadware block to download
// isStandard - True if "standard" loadware, else false.
//
// Returns: Success or Failure
//
// Description:
//
// Downloads a single block (at pSource)to the board referenced by pB. Caller
// sets isStandard to true/false according to whether the "standard" loadware is
// what's being loaded. The normal process, then, is to perform an iiInitialize
// to the board, then perform some number of iiDownloadBlocks using the returned
// state to determine when download is complete.
//
// Possible return values: (see I2ELLIS.H)
// II_DOWN_BADVALID
// II_DOWN_BADFILE
// II_DOWN_CONTINUING
// II_DOWN_GOOD
// II_DOWN_BAD
// II_DOWN_BADSTATE
// II_DOWN_TIMEOUT
//
// Uses the i2eState and i2eToLoad fields (initialized at iiInitialize) to
// determine whether this is the first block, whether to check for magic
// numbers, how many blocks there are to go...
//
//******************************************************************************
static int
iiDownloadBlock ( i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard)
{
int itemp;
int loadedFirst;
if (pB->i2eValid != I2E_MAGIC) return II_DOWN_BADVALID;
switch(pB->i2eState)
{
case II_STATE_READY:
// Loading the first block after reset. Must check the magic number of the
// loadfile, store the number of blocks we expect to load.
if (pSource->e.loadMagic != MAGIC_LOADFILE)
{
return II_DOWN_BADFILE;
}
// Next we store the total number of blocks to load, including this one.
pB->i2eToLoad = 1 + pSource->e.loadBlocksMore;
// Set the state, store the version numbers. ('Cause this may have come
// from a file - we might want to report these versions and revisions in
// case of an error!
pB->i2eState = II_STATE_LOADING;
pB->i2eLVersion = pSource->e.loadVersion;
pB->i2eLRevision = pSource->e.loadRevision;
pB->i2eLSub = pSource->e.loadSubRevision;
// The time and date of compilation is also available but don't bother
// storing it for normal purposes.
loadedFirst = 1;
break;
case II_STATE_LOADING:
loadedFirst = 0;
break;
default:
return II_DOWN_BADSTATE;
}
// Now we must be in the II_STATE_LOADING state, and we assume i2eToLoad
// must be positive still, because otherwise we would have cleaned up last
// time and set the state to II_STATE_LOADED.
if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
return II_DOWN_TIMEOUT;
}
if (!iiWriteBuf(pB, pSource->c, LOADWARE_BLOCK_SIZE)) {
return II_DOWN_BADVALID;
}
// If we just loaded the first block, wait for the fifo to empty an extra
// long time to allow for any special startup code in the firmware, like
// sending status messages to the LCD's.
if (loadedFirst) {
if (!iiWaitForTxEmpty(pB, MAX_DLOAD_START_TIME)) {
return II_DOWN_TIMEOUT;
}
}
// Determine whether this was our last block!
if (--(pB->i2eToLoad)) {
return II_DOWN_CONTINUING; // more to come...
}
// It WAS our last block: Clean up operations...
// ...Wait for last buffer to drain from the board...
if (!iiWaitForTxEmpty(pB, MAX_DLOAD_READ_TIME)) {
return II_DOWN_TIMEOUT;
}
// If there were only a single block written, this would come back
// immediately and be harmless, though not strictly necessary.
itemp = MAX_DLOAD_ACK_TIME/10;
while (--itemp) {
if (I2_HAS_INPUT(pB)) {
switch (inb(pB->i2eData)) {
case LOADWARE_OK:
pB->i2eState =
isStandard ? II_STATE_STDLOADED :II_STATE_LOADED;
// Some revisions of the bootstrap firmware (e.g. ISA-8 1.0.2)
// will, // if there is a debug port attached, require some
// time to send information to the debug port now. It will do
// this before // executing any of the code we just downloaded.
// It may take up to 700 milliseconds.
if (pB->i2ePom.e.porDiag2 & POR_DEBUG_PORT) {
iiDelay(pB, 700);
}
return II_DOWN_GOOD;
case LOADWARE_BAD:
default:
return II_DOWN_BAD;
}
}
iiDelay(pB, 10); // 10 mS granularity on checking condition
}
// Drop-through --> timed out waiting for firmware confirmation
pB->i2eState = II_STATE_BADLOAD;
return II_DOWN_TIMEOUT;
}
//******************************************************************************
// Function: iiDownloadAll(pB, pSource, isStandard, size)
// Parameters: pB - pointer to board structure
// pSource - loadware block to download
// isStandard - True if "standard" loadware, else false.
// size - size of data to download (in bytes)
//
// Returns: Success or Failure
//
// Description:
//
// Given a pointer to a board structure, a pointer to the beginning of some
// loadware, whether it is considered the "standard loadware", and the size of
// the array in bytes loads the entire array to the board as loadware.
//
// Assumes the board has been freshly reset and the power-up reset message read.
// (i.e., in II_STATE_READY). Complains if state is bad, or if there seems to be
// too much or too little data to load, or if iiDownloadBlock complains.
//******************************************************************************
static int
iiDownloadAll(i2eBordStrPtr pB, loadHdrStrPtr pSource, int isStandard, int size)
{
int status;
// We know (from context) board should be ready for the first block of
// download. Complain if not.
if (pB->i2eState != II_STATE_READY) return II_DOWN_BADSTATE;
while (size > 0) {
size -= LOADWARE_BLOCK_SIZE; // How much data should there be left to
// load after the following operation ?
// Note we just bump pSource by "one", because its size is actually that
// of an entire block, same as LOADWARE_BLOCK_SIZE.
status = iiDownloadBlock(pB, pSource++, isStandard);
switch(status)
{
case II_DOWN_GOOD:
return ( (size > 0) ? II_DOWN_OVER : II_DOWN_GOOD);
case II_DOWN_CONTINUING:
break;
default:
return status;
}
}
// We shouldn't drop out: it means "while" caught us with nothing left to
// download, yet the previous DownloadBlock did not return complete. Ergo,
// not enough data to match the size byte in the header.
return II_DOWN_UNDER;
}
/*******************************************************************************
*
* (c) 1999 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Mainline code for the device driver
*
*******************************************************************************/
//------------------------------------------------------------------------------
// i2ellis.h
//
// IntelliPort-II and IntelliPort-IIEX
//
// Extremely
// Low
// Level
// Interface
// Services
//
// Structure Definitions and declarations for "ELLIS" service routines found in
// i2ellis.c
//
// These routines are based on properties of the IntelliPort-II and -IIEX
// hardware and bootstrap firmware, and are not sensitive to particular
// conventions of any particular loadware.
//
// Unlike i2hw.h, which provides IRONCLAD hardware definitions, the material
// here and in i2ellis.c is intended to provice a useful, but not required,
// layer of insulation from the hardware specifics.
//------------------------------------------------------------------------------
#ifndef I2ELLIS_H /* To prevent multiple includes */
#define I2ELLIS_H 1
//------------------------------------------------
// Revision History:
//
// 30 September 1991 MAG First Draft Started
// 12 October 1991 ...continued...
//
// 20 December 1996 AKM Linux version
//-------------------------------------------------
//----------------------
// Mandatory Includes:
//----------------------
#include "ip2types.h"
#include "i2hw.h" // The hardware definitions
//------------------------------------------
// STAT_BOXIDS packets
//------------------------------------------
#define MAX_BOX 4
typedef struct _bidStat
{
unsigned char bid_value[MAX_BOX];
} bidStat, *bidStatPtr;
// This packet is sent in response to a CMD_GET_BOXIDS bypass command. For -IIEX
// boards, reports the hardware-specific "asynchronous resource register" on
// each expansion box. Boxes not present report 0xff. For -II boards, the first
// element contains 0x80 for 8-port, 0x40 for 4-port boards.
// Box IDs aka ARR or Async Resource Register (more than you want to know)
// 7 6 5 4 3 2 1 0
// F F N N L S S S
// =============================
// F F - Product Family Designator
// =====+++++++++++++++++++++++++++++++
// 0 0 - Intelliport II EX / ISA-8
// 1 0 - IntelliServer
// 0 1 - SAC - Port Device (Intelliport III ??? )
// =====+++++++++++++++++++++++++++++++++++++++
// N N - Number of Ports
// 0 0 - 8 (eight)
// 0 1 - 4 (four)
// 1 0 - 12 (twelve)
// 1 1 - 16 (sixteen)
// =++++++++++++++++++++++++++++++++++
// L - LCD Display Module Present
// 0 - No
// 1 - LCD module present
// =========+++++++++++++++++++++++++++++++++++++
// S S S - Async Signals Supported Designator
// 0 0 0 - 8dss, Mod DCE DB25 Female
// 0 0 1 - 6dss, RJ-45
// 0 1 0 - RS-232/422 dss, DB25 Female
// 0 1 1 - RS-232/422 dss, separate 232/422 DB25 Female
// 1 0 0 - 6dss, 921.6 I/F with ST654's
// 1 0 1 - RS-423/232 8dss, RJ-45 10Pin
// 1 1 0 - 6dss, Mod DCE DB25 Female
// 1 1 1 - NO BOX PRESENT
#define FF(c) ((c & 0xC0) >> 6)
#define NN(c) ((c & 0x30) >> 4)
#define L(c) ((c & 0x08) >> 3)
#define SSS(c) (c & 0x07)
#define BID_HAS_654(x) (SSS(x) == 0x04)
#define BID_NO_BOX 0xff /* no box */
#define BID_8PORT 0x80 /* IP2-8 port */
#define BID_4PORT 0x81 /* IP2-4 port */
#define BID_EXP_MASK 0x30 /* IP2-EX */
#define BID_EXP_8PORT 0x00 /* 8, */
#define BID_EXP_4PORT 0x10 /* 4, */
#define BID_EXP_UNDEF 0x20 /* UNDEF, */
#define BID_EXP_16PORT 0x30 /* 16, */
#define BID_LCD_CTRL 0x08 /* LCD Controller */
#define BID_LCD_NONE 0x00 /* - no controller present */
#define BID_LCD_PRES 0x08 /* - controller present */
#define BID_CON_MASK 0x07 /* - connector pinouts */
#define BID_CON_DB25 0x00 /* - DB-25 F */
#define BID_CON_RJ45 0x01 /* - rj45 */
//------------------------------------------------------------------------------
// i2eBordStr
//
// This structure contains all the information the ELLIS routines require in
// dealing with a particular board.
//------------------------------------------------------------------------------
// There are some queues here which are guaranteed to never contain the entry
// for a single channel twice. So they must be slightly larger to allow
// unambiguous full/empty management
//
#define CH_QUEUE_SIZE ABS_MOST_PORTS+2
typedef struct _i2eBordStr
{
porStr i2ePom; // Structure containing the power-on message.
unsigned short i2ePomSize;
// The number of bytes actually read if
// different from sizeof i2ePom, indicates
// there is an error!
unsigned short i2eStartMail;
// Contains whatever inbound mailbox data
// present at startup. NO_MAIL_HERE indicates
// nothing was present. No special
// significance as of this writing, but may be
// useful for diagnostic reasons.
unsigned short i2eValid;
// Indicates validity of the structure; if
// i2eValid == I2E_MAGIC, then we can trust
// the other fields. Some (especially
// initialization) functions are good about
// checking for validity. Many functions do
// not, it being assumed that the larger
// context assures we are using a valid
// i2eBordStrPtr.
unsigned short i2eError;
// Used for returning an error condition from
// several functions which use i2eBordStrPtr
// as an argument.
// Accelerators to characterize separate features of a board, derived from a
// number of sources.
unsigned short i2eFifoSize;
// Always, the size of the FIFO. For
// IntelliPort-II, always the same, for -IIEX
// taken from the Power-On reset message.
volatile
unsigned short i2eFifoRemains;
// Used during normal operation to indicate a
// lower bound on the amount of data which
// might be in the outbound fifo.
unsigned char i2eFifoStyle;
// Accelerator which tells which style (-II or
// -IIEX) FIFO we are using.
unsigned char i2eDataWidth16;
// Accelerator which tells whether we should
// do 8 or 16-bit data transfers.
unsigned char i2eMaxIrq;
// The highest allowable IRQ, based on the
// slot size.
// Accelerators for various addresses on the board
int i2eBase; // I/O Address of the Board
int i2eData; // From here data transfers happen
int i2eStatus; // From here status reads happen
int i2ePointer; // (IntelliPort-II: pointer/commands)
int i2eXMail; // (IntelliPOrt-IIEX: mailboxes
int i2eXMask; // (IntelliPort-IIEX: mask write
//-------------------------------------------------------
// Information presented in a common format across boards
// For each box, bit map of the channels present. Box closest to
// the host is box 0. LSB is channel 0. IntelliPort-II (non-expandable)
// is taken to be box 0. These are derived from product i.d. registers.
unsigned short i2eChannelMap[ABS_MAX_BOXES];
// Same as above, except each is derived from firmware attempting to detect
// the uart presence (by reading a valid GFRCR register). If bits are set in
// i2eChannelMap and not in i2eGoodMap, there is a potential problem.
unsigned short i2eGoodMap[ABS_MAX_BOXES];
// ---------------------------
// For indirect function calls
// Routine to cause an N-millisecond delay: Patched by the ii2Initialize
// function.
void (*i2eDelay)(unsigned int);
// Routine to write N bytes to the board through the FIFO. Returns true if
// all copacetic, otherwise returns false and error is in i2eError field.
// IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER.
int (*i2eWriteBuf)(struct _i2eBordStr *, unsigned char *, int);
// Routine to read N bytes from the board through the FIFO. Returns true if
// copacetic, otherwise returns false and error in i2eError.
// IF COUNT IS ODD, ROUNDS UP TO THE NEXT EVEN NUMBER.
int (*i2eReadBuf)(struct _i2eBordStr *, unsigned char *, int);
// Returns a word from FIFO. Will use 2 byte operations if needed.
unsigned short (*i2eReadWord)(struct _i2eBordStr *);
// Writes a word to FIFO. Will use 2 byte operations if needed.
void (*i2eWriteWord)(struct _i2eBordStr *, unsigned short);
// Waits specified time for the Transmit FIFO to go empty. Returns true if
// ok, otherwise returns false and error in i2eError.
int (*i2eWaitForTxEmpty)(struct _i2eBordStr *, int);
// Returns true or false according to whether the outgoing mailbox is empty.
int (*i2eTxMailEmpty)(struct _i2eBordStr *);
// Checks whether outgoing mailbox is empty. If so, sends mail and returns
// true. Otherwise returns false.
int (*i2eTrySendMail)(struct _i2eBordStr *, unsigned char);
// If no mail available, returns NO_MAIL_HERE, else returns the value in the
// mailbox (guaranteed can't be NO_MAIL_HERE).
unsigned short (*i2eGetMail)(struct _i2eBordStr *);
// Enables the board to interrupt the host when it writes to the mailbox.
// Irqs will not occur, however, until the loadware separately enables
// interrupt generation to the host. The standard loadware does this in
// response to a command packet sent by the host. (Also, disables
// any other potential interrupt sources from the board -- other than the
// inbound mailbox).
void (*i2eEnableMailIrq)(struct _i2eBordStr *);
// Writes an arbitrary value to the mask register.
void (*i2eWriteMask)(struct _i2eBordStr *, unsigned char);
// State information
// During downloading, indicates the number of blocks remaining to download
// to the board.
short i2eToLoad;
// State of board (see manifests below) (e.g., whether in reset condition,
// whether standard loadware is installed, etc.
unsigned char i2eState;
// These three fields are only valid when there is loadware running on the
// board. (i2eState == II_STATE_LOADED or i2eState == II_STATE_STDLOADED )
unsigned char i2eLVersion; // Loadware version
unsigned char i2eLRevision; // Loadware revision
unsigned char i2eLSub; // Loadware subrevision
// Flags which only have meaning in the context of the standard loadware.
// Somewhat violates the layering concept, but there is so little additional
// needed at the board level (while much additional at the channel level),
// that this beats maintaining two different per-board structures.
// Indicates which IRQ the board has been initialized (from software) to use
// For MicroChannel boards, any value different from IRQ_UNDEFINED means
// that the software command has been sent to enable interrupts (or specify
// they are disabled). Special value: IRQ_UNDEFINED indicates that the
// software command to select the interrupt has not yet been sent, therefore
// (since the standard loadware insists that it be sent before any other
// packets are sent) no other packets should be sent yet.
unsigned short i2eUsingIrq;
// This is set when we hit the MB_OUT_STUFFED mailbox, which prevents us
// putting more in the mailbox until an appropriate mailbox message is
// received.
unsigned char i2eWaitingForEmptyFifo;
// Any mailbox bits waiting to be sent to the board are OR'ed in here.
unsigned char i2eOutMailWaiting;
// The head of any incoming packet is read into here, is then examined and
// we dispatch accordingly.
unsigned short i2eLeadoffWord[1];
// Running counter of interrupts where the mailbox indicated incoming data.
unsigned short i2eFifoInInts;
// Running counter of interrupts where the mailbox indicated outgoing data
// had been stripped.
unsigned short i2eFifoOutInts;
// If not void, gives the address of a routine to call if fatal board error
// is found (only applies to standard l/w).
void (*i2eFatalTrap)(struct _i2eBordStr *);
// Will point to an array of some sort of channel structures (whose format
// is unknown at this level, being a function of what loadware is
// installed and the code configuration (max sizes of buffers, etc.)).
void *i2eChannelPtr;
// Set indicates that the board has gone fatal.
unsigned short i2eFatal;
// The number of elements pointed to by i2eChannelPtr.
unsigned short i2eChannelCnt;
// Ring-buffers of channel structures whose channels have particular needs.
rwlock_t Fbuf_spinlock;
volatile
unsigned short i2Fbuf_strip; // Strip index
volatile
unsigned short i2Fbuf_stuff; // Stuff index
void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers
// of channels who need to send
// flow control packets.
rwlock_t Dbuf_spinlock;
volatile
unsigned short i2Dbuf_strip; // Strip index
volatile
unsigned short i2Dbuf_stuff; // Stuff index
void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers
// of channels who need to send
// data or in-line command packets.
rwlock_t Bbuf_spinlock;
volatile
unsigned short i2Bbuf_strip; // Strip index
volatile
unsigned short i2Bbuf_stuff; // Stuff index
void *i2Bbuf[CH_QUEUE_SIZE]; // An array of channel pointers
// of channels who need to send
// bypass command packets.
/*
* A set of flags to indicate that certain events have occurred on at least
* one of the ports on this board. We use this to decide whether to spin
* through the channels looking for breaks, etc.
*/
int got_input;
int status_change;
bidStat channelBtypes;
/*
* Debugging counters, etc.
*/
unsigned long debugFlowQueued;
unsigned long debugInlineQueued;
unsigned long debugDataQueued;
unsigned long debugBypassQueued;
unsigned long debugFlowCount;
unsigned long debugInlineCount;
unsigned long debugBypassCount;
rwlock_t read_fifo_spinlock;
rwlock_t write_fifo_spinlock;
// For queuing interrupt bottom half handlers. /\/\|=mhw=|\/\/
struct work_struct tqueue_interrupt;
struct timer_list SendPendingTimer; // Used by iiSendPending
unsigned int SendPendingRetry;
} i2eBordStr, *i2eBordStrPtr;
//-------------------------------------------------------------------
// Macro Definitions for the indirect calls defined in the i2eBordStr
//-------------------------------------------------------------------
//
#define iiDelay(a,b) (*(a)->i2eDelay)(b)
#define iiWriteBuf(a,b,c) (*(a)->i2eWriteBuf)(a,b,c)
#define iiReadBuf(a,b,c) (*(a)->i2eReadBuf)(a,b,c)
#define iiWriteWord(a,b) (*(a)->i2eWriteWord)(a,b)
#define iiReadWord(a) (*(a)->i2eReadWord)(a)
#define iiWaitForTxEmpty(a,b) (*(a)->i2eWaitForTxEmpty)(a,b)
#define iiTxMailEmpty(a) (*(a)->i2eTxMailEmpty)(a)
#define iiTrySendMail(a,b) (*(a)->i2eTrySendMail)(a,b)
#define iiGetMail(a) (*(a)->i2eGetMail)(a)
#define iiEnableMailIrq(a) (*(a)->i2eEnableMailIrq)(a)
#define iiDisableMailIrq(a) (*(a)->i2eWriteMask)(a,0)
#define iiWriteMask(a,b) (*(a)->i2eWriteMask)(a,b)
//-------------------------------------------
// Manifests for i2eBordStr:
//-------------------------------------------
typedef void (*delayFunc_t)(unsigned int);
// i2eValid
//
#define I2E_MAGIC 0x4251 // Structure is valid.
#define I2E_INCOMPLETE 0x1122 // Structure failed during init.
// i2eError
//
#define I2EE_GOOD 0 // Operation successful
#define I2EE_BADADDR 1 // Address out of range
#define I2EE_BADSTATE 2 // Attempt to perform a function when the board
// structure was in the incorrect state
#define I2EE_BADMAGIC 3 // Bad magic number from Power On test (i2ePomSize
// reflects what was read
#define I2EE_PORM_SHORT 4 // Power On message too short
#define I2EE_PORM_LONG 5 // Power On message too long
#define I2EE_BAD_FAMILY 6 // Un-supported board family type
#define I2EE_INCONSIST 7 // Firmware reports something impossible,
// e.g. unexpected number of ports... Almost no
// excuse other than bad FIFO...
#define I2EE_POSTERR 8 // Power-On self test reported a bad error
#define I2EE_BADBUS 9 // Unknown Bus type declared in message
#define I2EE_TXE_TIME 10 // Timed out waiting for TX Fifo to empty
#define I2EE_INVALID 11 // i2eValid field does not indicate a valid and
// complete board structure (for functions which
// require this be so.)
#define I2EE_BAD_PORT 12 // Discrepancy between channels actually found and
// what the product is supposed to have. Check
// i2eGoodMap vs i2eChannelMap for details.
#define I2EE_BAD_IRQ 13 // Someone specified an unsupported IRQ
#define I2EE_NOCHANNELS 14 // No channel structures have been defined (for
// functions requiring this).
// i2eFifoStyle
//
#define FIFO_II 0 /* IntelliPort-II style: see also i2hw.h */
#define FIFO_IIEX 1 /* IntelliPort-IIEX style */
// i2eGetMail
//
#define NO_MAIL_HERE 0x1111 // Since mail is unsigned char, cannot possibly
// promote to 0x1111.
// i2eState
//
#define II_STATE_COLD 0 // Addresses have been defined, but board not even
// reset yet.
#define II_STATE_RESET 1 // Board,if it exists, has just been reset
#define II_STATE_READY 2 // Board ready for its first block
#define II_STATE_LOADING 3 // Board continuing load
#define II_STATE_LOADED 4 // Board has finished load: status ok
#define II_STATE_BADLOAD 5 // Board has finished load: failed!
#define II_STATE_STDLOADED 6 // Board has finished load: standard firmware
// i2eUsingIrq
//
#define I2_IRQ_UNDEFINED 0x1352 /* No valid irq (or polling = 0) can
* ever promote to this! */
//------------------------------------------
// Handy Macros for i2ellis.c and others
// Note these are common to -II and -IIEX
//------------------------------------------
// Given a pointer to the board structure, does the input FIFO have any data or
// not?
//
#define I2_HAS_INPUT(pB) !(inb(pB->i2eStatus) & ST_IN_EMPTY)
// Given a pointer to the board structure, is there anything in the incoming
// mailbox?
//
#define I2_HAS_MAIL(pB) (inb(pB->i2eStatus) & ST_IN_MAIL)
#define I2_UPDATE_FIFO_ROOM(pB) ((pB)->i2eFifoRemains = (pB)->i2eFifoSize)
//------------------------------------------
// Function Declarations for i2ellis.c
//------------------------------------------
//
// Functions called directly
//
// Initialization of a board & structure is in four (five!) parts:
//
// 1) iiSetAddress() - Define the board address & delay function for a board.
// 2) iiReset() - Reset the board (provided it exists)
// -- Note you may do this to several boards --
// 3) iiResetDelay() - Delay for 2 seconds (once for all boards)
// 4) iiInitialize() - Attempt to read Power-up message; further initialize
// accelerators
//
// Then you may use iiDownloadAll() or iiDownloadFile() (in i2file.c) to write
// loadware. To change loadware, you must begin again with step 2, resetting
// the board again (step 1 not needed).
static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t );
static int iiReset(i2eBordStrPtr);
static int iiResetDelay(i2eBordStrPtr);
static int iiInitialize(i2eBordStrPtr);
// Routine to validate that all channels expected are there.
//
extern int iiValidateChannels(i2eBordStrPtr);
// Routine used to download a block of loadware.
//
static int iiDownloadBlock(i2eBordStrPtr, loadHdrStrPtr, int);
// Return values given by iiDownloadBlock, iiDownloadAll, iiDownloadFile:
//
#define II_DOWN_BADVALID 0 // board structure is invalid
#define II_DOWN_CONTINUING 1 // So far, so good, firmware expects more
#define II_DOWN_GOOD 2 // Download complete, CRC good
#define II_DOWN_BAD 3 // Download complete, but CRC bad
#define II_DOWN_BADFILE 4 // Bad magic number in loadware file
#define II_DOWN_BADSTATE 5 // Board is in an inappropriate state for
// downloading loadware. (see i2eState)
#define II_DOWN_TIMEOUT 6 // Timeout waiting for firmware
#define II_DOWN_OVER 7 // Too much data
#define II_DOWN_UNDER 8 // Not enough data
#define II_DOWN_NOFILE 9 // Loadware file not found
// Routine to download an entire loadware module: Return values are a subset of
// iiDownloadBlock's, excluding, of course, II_DOWN_CONTINUING
//
static int iiDownloadAll(i2eBordStrPtr, loadHdrStrPtr, int, int);
// Many functions defined here return True if good, False otherwise, with an
// error code in i2eError field. Here is a handy macro for setting the error
// code and returning.
//
#define I2_COMPLETE(pB,code) do { \
pB->i2eError = code; \
return (code == I2EE_GOOD);\
} while (0)
#endif // I2ELLIS_H
/*******************************************************************************
*
* (c) 1999 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Definitions limited to properties of the hardware or the
* bootstrap firmware. As such, they are applicable regardless of
* operating system or loadware (standard or diagnostic).
*
*******************************************************************************/
#ifndef I2HW_H
#define I2HW_H 1
//------------------------------------------------------------------------------
// Revision History:
//
// 23 September 1991 MAG First Draft Started...through...
// 11 October 1991 ... Continuing development...
// 6 August 1993 Added support for ISA-4 (asic) which is architected
// as an ISA-CEX with a single 4-port box.
//
// 20 December 1996 AKM Version for Linux
//
//------------------------------------------------------------------------------
/*------------------------------------------------------------------------------
HARDWARE DESCRIPTION:
Introduction:
The IntelliPort-II and IntelliPort-IIEX products occupy a block of eight (8)
addresses in the host's I/O space.
Some addresses are used to transfer data to/from the board, some to transfer
so-called "mailbox" messages, and some to read bit-mapped status information.
While all the products in the line are functionally similar, some use a 16-bit
data path to transfer data while others use an 8-bit path. Also, the use of
command /status/mailbox registers differs slightly between the II and IIEX
branches of the family.
The host determines what type of board it is dealing with by reading a string of
sixteen characters from the board. These characters are always placed in the
fifo by the board's local processor whenever the board is reset (either from
power-on or under software control) and are known as the "Power-on Reset
Message." In order that this message can be read from either type of board, the
hardware registers used in reading this message are the same. Once this message
has been read by the host, then it has the information required to operate.
General Differences between boards:
The greatest structural difference is between the -II and -IIEX families of
product. The -II boards use the Am4701 dual 512x8 bidirectional fifo to support
the data path, mailbox registers, and status registers. This chip contains some
features which are not used in the IntelliPort-II products; a description of
these is omitted here. Because of these many features, it contains many
registers, too many to access directly within a small address space. They are
accessed by first writing a value to a "pointer" register. This value selects
the register to be accessed. The next read or write to that address accesses
the selected register rather than the pointer register.
The -IIEX boards use a proprietary design similar to the Am4701 in function. But
because of a simpler, more streamlined design it doesn't require so many
registers. This means they can be accessed directly in single operations rather
than through a pointer register.
Besides these differences, there are differences in whether 8-bit or 16-bit
transfers are used to move data to the board.
The -II boards are capable only of 8-bit data transfers, while the -IIEX boards
may be configured for either 8-bit or 16-bit data transfers. If the on-board DIP
switch #8 is ON, and the card has been installed in a 16-bit slot, 16-bit
transfers are supported (and will be expected by the standard loadware). The
on-board firmware can determine the position of the switch, and whether the
board is installed in a 16-bit slot; it supplies this information to the host as
part of the power-up reset message.
The configuration switch (#8) and slot selection do not directly configure the
hardware. It is up to the on-board loadware and host-based drivers to act
according to the selected options. That is, loadware and drivers could be
written to perform 8-bit transfers regardless of the state of the DIP switch or
slot (and in a diagnostic environment might well do so). Likewise, 16-bit
transfers could be performed as long as the card is in a 16-bit slot.
Note the slot selection and DIP switch selection are provided separately: a
board running in 8-bit mode in a 16-bit slot has a greater range of possible
interrupts to choose from; information of potential use to the host.
All 8-bit data transfers are done in the same way, regardless of whether on a
-II board or a -IIEX board.
The host must consider two things then: 1) whether a -II or -IIEX product is
being used, and 2) whether an 8-bit or 16-bit data path is used.
A further difference is that -II boards always have a 512-byte fifo operating in
each direction. -IIEX boards may use fifos of varying size; this size is
reported as part of the power-up message.
I/O Map Of IntelliPort-II and IntelliPort-IIEX boards:
(Relative to the chosen base address)
Addr R/W IntelliPort-II IntelliPort-IIEX
---- --- -------------- ----------------
0 R/W Data Port (byte) Data Port (byte or word)
1 R/W (Not used) (MSB of word-wide data written to Data Port)
2 R Status Register Status Register
2 W Pointer Register Interrupt Mask Register
3 R/W (Not used) Mailbox Registers (6 bits: 11111100)
4,5 -- Reserved for future products
6 -- Reserved for future products
7 R Guaranteed to have no effect
7 W Hardware reset of board.
Rules:
All data transfers are performed using the even i/o address. If byte-wide data
transfers are being used, do INB/OUTB operations on the data port. If word-wide
transfers are used, do INW/OUTW operations. In some circumstances (such as
reading the power-up message) you will do INB from the data port, but in this
case the MSB of each word read is lost. When accessing all other unreserved
registers, use byte operations only.
------------------------------------------------------------------------------*/
//------------------------------------------------
// Mandatory Includes:
//------------------------------------------------
//
#include "ip2types.h"
//-------------------------------------------------------------------------
// Manifests for the I/O map:
//-------------------------------------------------------------------------
// R/W: Data port (byte) for IntelliPort-II,
// R/W: Data port (byte or word) for IntelliPort-IIEX
// Incoming or outgoing data passes through a FIFO, the status of which is
// available in some of the bits in FIFO_STATUS. This (bidirectional) FIFO is
// the primary means of transferring data, commands, flow-control, and status
// information between the host and board.
//
#define FIFO_DATA 0
// Another way of passing information between the board and the host is
// through "mailboxes". Unlike a FIFO, a mailbox holds only a single byte of
// data. Writing data to the mailbox causes a status bit to be set, and
// potentially interrupting the intended receiver. The sender has some way to
// determine whether the data has been read yet; as soon as it has, it may send
// more. The mailboxes are handled differently on -II and -IIEX products, as
// suggested below.
//------------------------------------------------------------------------------
// Read: Status Register for IntelliPort-II or -IIEX
// The presence of any bit set here will cause an interrupt to the host,
// provided the corresponding bit has been unmasked in the interrupt mask
// register. Furthermore, interrupts to the host are disabled globally until the
// loadware selects the irq line to use. With the exception of STN_MR, the bits
// remain set so long as the associated condition is true.
//
#define FIFO_STATUS 2
// Bit map of status bits which are identical for -II and -IIEX
//
#define ST_OUT_FULL 0x40 // Outbound FIFO full
#define ST_IN_EMPTY 0x20 // Inbound FIFO empty
#define ST_IN_MAIL 0x04 // Inbound Mailbox full
// The following exists only on the Intelliport-IIEX, and indicates that the
// board has not read the last outgoing mailbox data yet. In the IntelliPort-II,
// the outgoing mailbox may be read back: a zero indicates the board has read
// the data.
//
#define STE_OUT_MAIL 0x80 // Outbound mailbox full (!)
// The following bits are defined differently for -II and -IIEX boards. Code
// which relies on these bits will need to be functionally different for the two
// types of boards and should be generally avoided because of the additional
// complexity this creates:
// Bit map of status bits only on -II
// Fifo has been RESET (cleared when the status register is read). Note that
// this condition cannot be masked and would always interrupt the host, except
// that the hardware reset also disables interrupts globally from the board
// until re-enabled by loadware. This could also arise from the
// Am4701-supported command to reset the chip, but this command is generally not
// used here.
//
#define STN_MR 0x80
// See the AMD Am4701 data sheet for details on the following four bits. They
// are not presently used by Computone drivers.
//
#define STN_OUT_AF 0x10 // Outbound FIFO almost full (programmable)
#define STN_IN_AE 0x08 // Inbound FIFO almost empty (programmable)
#define STN_BD 0x02 // Inbound byte detected
#define STN_PE 0x01 // Parity/Framing condition detected
// Bit-map of status bits only on -IIEX
//
#define STE_OUT_HF 0x10 // Outbound FIFO half full
#define STE_IN_HF 0x08 // Inbound FIFO half full
#define STE_IN_FULL 0x02 // Inbound FIFO full
#define STE_OUT_MT 0x01 // Outbound FIFO empty
//------------------------------------------------------------------------------
// Intelliport-II -- Write Only: the pointer register.
// Values are written to this register to select the Am4701 internal register to
// be accessed on the next operation.
//
#define FIFO_PTR 0x02
// Values for the pointer register
//
#define SEL_COMMAND 0x1 // Selects the Am4701 command register
// Some possible commands:
//
#define SEL_CMD_MR 0x80 // Am4701 command to reset the chip
#define SEL_CMD_SH 0x40 // Am4701 command to map the "other" port into the
// status register.
#define SEL_CMD_UNSH 0 // Am4701 command to "unshift": port maps into its
// own status register.
#define SEL_MASK 0x2 // Selects the Am4701 interrupt mask register. The
// interrupt mask register is bit-mapped to match
// the status register (FIFO_STATUS) except for
// STN_MR. (See above.)
#define SEL_BYTE_DET 0x3 // Selects the Am4701 byte-detect register. (Not
// normally used except in diagnostics.)
#define SEL_OUTMAIL 0x4 // Selects the outbound mailbox (R/W). Reading back
// a value of zero indicates that the mailbox has
// been read by the board and is available for more
// data./ Writing to the mailbox optionally
// interrupts the board, depending on the loadware's
// setting of its interrupt mask register.
#define SEL_AEAF 0x5 // Selects AE/AF threshold register.
#define SEL_INMAIL 0x6 // Selects the inbound mailbox (Read)
//------------------------------------------------------------------------------
// IntelliPort-IIEX -- Write Only: interrupt mask (and misc flags) register:
// Unlike IntelliPort-II, bit assignments do NOT match those of the status
// register.
//
#define FIFO_MASK 0x2
// Mailbox readback select:
// If set, reads to FIFO_MAIL will read the OUTBOUND mailbox (host to board). If
// clear (default on reset) reads to FIFO_MAIL will read the INBOUND mailbox.
// This is the normal situation. The clearing of a mailbox is determined on
// -IIEX boards by waiting for the STE_OUT_MAIL bit to clear. Readback
// capability is provided for diagnostic purposes only.
//
#define MX_OUTMAIL_RSEL 0x80
#define MX_IN_MAIL 0x40 // Enables interrupts when incoming mailbox goes
// full (ST_IN_MAIL set).
#define MX_IN_FULL 0x20 // Enables interrupts when incoming FIFO goes full
// (STE_IN_FULL).
#define MX_IN_MT 0x08 // Enables interrupts when incoming FIFO goes empty
// (ST_IN_MT).
#define MX_OUT_FULL 0x04 // Enables interrupts when outgoing FIFO goes full
// (ST_OUT_FULL).
#define MX_OUT_MT 0x01 // Enables interrupts when outgoing FIFO goes empty
// (STE_OUT_MT).
// Any remaining bits are reserved, and should be written to ZERO for
// compatibility with future Computone products.
//------------------------------------------------------------------------------
// IntelliPort-IIEX: -- These are only 6-bit mailboxes !!! -- 11111100 (low two
// bits always read back 0).
// Read: One of the mailboxes, usually Inbound.
// Inbound Mailbox (MX_OUTMAIL_RSEL = 0)
// Outbound Mailbox (MX_OUTMAIL_RSEL = 1)
// Write: Outbound Mailbox
// For the IntelliPort-II boards, the outbound mailbox is read back to determine
// whether the board has read the data (0 --> data has been read). For the
// IntelliPort-IIEX, this is done by reading a status register. To determine
// whether mailbox is available for more outbound data, use the STE_OUT_MAIL bit
// in FIFO_STATUS. Moreover, although the Outbound Mailbox can be read back by
// setting MX_OUTMAIL_RSEL, it is NOT cleared when the board reads it, as is the
// case with the -II boards. For this reason, FIFO_MAIL is normally used to read
// the inbound FIFO, and MX_OUTMAIL_RSEL kept clear. (See above for
// MX_OUTMAIL_RSEL description.)
//
#define FIFO_MAIL 0x3
//------------------------------------------------------------------------------
// WRITE ONLY: Resets the board. (Data doesn't matter).
//
#define FIFO_RESET 0x7
//------------------------------------------------------------------------------
// READ ONLY: Will have no effect. (Data is undefined.)
// Actually, there will be an effect, in that the operation is sure to generate
// a bus cycle: viz., an I/O byte Read. This fact can be used to enforce short
// delays when no comparable time constant is available.
//
#define FIFO_NOP 0x7
//------------------------------------------------------------------------------
// RESET & POWER-ON RESET MESSAGE
/*------------------------------------------------------------------------------
RESET:
The IntelliPort-II and -IIEX boards are reset in three ways: Power-up, channel
reset, and via a write to the reset register described above. For products using
the ISA bus, these three sources of reset are equvalent. For MCA and EISA buses,
the Power-up and channel reset sources cause additional hardware initialization
which should only occur at system startup time.
The third type of reset, called a "command reset", is done by writing any data
to the FIFO_RESET address described above. This resets the on-board processor,
FIFO, UARTS, and associated hardware.
This passes control of the board to the bootstrap firmware, which performs a
Power-On Self Test and which detects its current configuration. For example,
-IIEX products determine the size of FIFO which has been installed, and the
number and type of expansion boxes attached.
This and other information is then written to the FIFO in a 16-byte data block
to be read by the host. This block is guaranteed to be present within two (2)
seconds of having received the command reset. The firmware is now ready to
receive loadware from the host.
It is good practice to perform a command reset to the board explicitly as part
of your software initialization. This allows your code to properly restart from
a soft boot. (Many systems do not issue channel reset on soft boot).
Because of a hardware reset problem on some of the Cirrus Logic 1400's which are
used on the product, it is recommended that you reset the board twice, separated
by an approximately 50 milliseconds delay. (VERY approximately: probably ok to
be off by a factor of five. The important point is that the first command reset
in fact generates a reset pulse on the board. This pulse is guaranteed to last
less than 10 milliseconds. The additional delay ensures the 1400 has had the
chance to respond sufficiently to the first reset. Why not a longer delay? Much
more than 50 milliseconds gets to be noticeable, but the board would still work.
Once all 16 bytes of the Power-on Reset Message have been read, the bootstrap
firmware is ready to receive loadware.
Note on Power-on Reset Message format:
The various fields have been designed with future expansion in view.
Combinations of bitfields and values have been defined which define products
which may not currently exist. This has been done to allow drivers to anticipate
the possible introduction of products in a systematic fashion. This is not
intended to suggest that each potential product is actually under consideration.
------------------------------------------------------------------------------*/
//----------------------------------------
// Format of Power-on Reset Message
//----------------------------------------
typedef union _porStr // "por" stands for Power On Reset
{
unsigned char c[16]; // array used when considering the message as a
// string of undifferentiated characters
struct // Elements used when considering values
{
// The first two bytes out of the FIFO are two magic numbers. These are
// intended to establish that there is indeed a member of the
// IntelliPort-II(EX) family present. The remaining bytes may be
// expected // to be valid. When reading the Power-on Reset message,
// if the magic numbers do not match it is probably best to stop
// reading immediately. You are certainly not reading our board (unless
// hardware is faulty), and may in fact be reading some other piece of
// hardware.
unsigned char porMagic1; // magic number: first byte == POR_MAGIC_1
unsigned char porMagic2; // magic number: second byte == POR_MAGIC_2
// The Version, Revision, and Subrevision are stored as absolute numbers
// and would normally be displayed in the format V.R.S (e.g. 1.0.2)
unsigned char porVersion; // Bootstrap firmware version number
unsigned char porRevision; // Bootstrap firmware revision number
unsigned char porSubRev; // Bootstrap firmware sub-revision number
unsigned char porID; // Product ID: Bit-mapped according to
// conventions described below. Among other
// things, this allows us to distinguish
// IntelliPort-II boards from IntelliPort-IIEX
// boards.
unsigned char porBus; // IntelliPort-II: Unused
// IntelliPort-IIEX: Bus Information:
// Bit-mapped below
unsigned char porMemory; // On-board DRAM size: in 32k blocks
// porPorts1 (and porPorts2) are used to determine the ports which are
// available to the board. For non-expandable product, a single number
// is sufficient. For expandable product, the board may be connected
// to as many as four boxes. Each box may be (so far) either a 16-port
// or an 8-port size. Whenever an 8-port box is used, the remaining 8
// ports leave gaps between existing channels. For that reason,
// expandable products must report a MAP of available channels. Since
// each UART supports four ports, we represent each UART found by a
// single bit. Using two bytes to supply the mapping information we
// report the presence or absence of up to 16 UARTS, or 64 ports in
// steps of 4 ports. For -IIEX products, the ports are numbered
// starting at the box closest to the controller in the "chain".
// Interpreted Differently for IntelliPort-II and -IIEX.
// -II: Number of ports (Derived actually from product ID). See
// Diag1&2 to indicate if uart was actually detected.
// -IIEX: Bit-map of UARTS found, LSB (see below for MSB of this). This
// bitmap is based on detecting the uarts themselves;
// see porFlags for information from the box i.d's.
unsigned char porPorts1;
unsigned char porDiag1; // Results of on-board P.O.S.T, 1st byte
unsigned char porDiag2; // Results of on-board P.O.S.T, 2nd byte
unsigned char porSpeed; // Speed of local CPU: given as MHz x10
// e.g., 16.0 MHz CPU is reported as 160
unsigned char porFlags; // Misc information (see manifests below)
// Bit-mapped: CPU type, UART's present
unsigned char porPorts2; // -II: Undefined
// -IIEX: Bit-map of UARTS found, MSB (see
// above for LSB)
// IntelliPort-II: undefined
// IntelliPort-IIEX: 1 << porFifoSize gives the size, in bytes, of the
// host interface FIFO, in each direction. When running the -IIEX in
// 8-bit mode, fifo capacity is halved. The bootstrap firmware will
// have already accounted for this fact in generating this number.
unsigned char porFifoSize;
// IntelliPort-II: undefined
// IntelliPort-IIEX: The number of boxes connected. (Presently 1-4)
unsigned char porNumBoxes;
} e;
} porStr, *porStrPtr;
//--------------------------
// Values for porStr fields
//--------------------------
//---------------------
// porMagic1, porMagic2
//----------------------
//
#define POR_MAGIC_1 0x96 // The only valid value for porMagic1
#define POR_MAGIC_2 0x35 // The only valid value for porMagic2
#define POR_1_INDEX 0 // Byte position of POR_MAGIC_1
#define POR_2_INDEX 1 // Ditto for POR_MAGIC_2
//----------------------
// porID
//----------------------
//
#define POR_ID_FAMILY 0xc0 // These bits indicate the general family of
// product.
#define POR_ID_FII 0x00 // Family is "IntelliPort-II"
#define POR_ID_FIIEX 0x40 // Family is "IntelliPort-IIEX"
// These bits are reserved, presently zero. May be used at a later date to
// convey other product information.
//
#define POR_ID_RESERVED 0x3c
#define POR_ID_SIZE 0x03 // Remaining bits indicate number of ports &
// Connector information.
#define POR_ID_II_8 0x00 // For IntelliPort-II, indicates 8-port using
// standard brick.
#define POR_ID_II_8R 0x01 // For IntelliPort-II, indicates 8-port using
// RJ11's (no CTS)
#define POR_ID_II_6 0x02 // For IntelliPort-II, indicates 6-port using
// RJ45's
#define POR_ID_II_4 0x03 // For IntelliPort-II, indicates 4-port using
// 4xRJ45 connectors
#define POR_ID_EX 0x00 // For IntelliPort-IIEX, indicates standard
// expandable controller (other values reserved)
//----------------------
// porBus
//----------------------
// IntelliPort-IIEX only: Board is installed in a 16-bit slot
//
#define POR_BUS_SLOT16 0x20
// IntelliPort-IIEX only: DIP switch #8 is on, selecting 16-bit host interface
// operation.
//
#define POR_BUS_DIP16 0x10
// Bits 0-2 indicate type of bus: This information is stored in the bootstrap
// loadware, different loadware being used on different products for different
// buses. For most situations, the drivers do not need this information; but it
// is handy in a diagnostic environment. For example, on microchannel boards,
// you would not want to try to test several interrupts, only the one for which
// you were configured.
//
#define POR_BUS_TYPE 0x07
// Unknown: this product doesn't know what bus it is running in. (e.g. if same
// bootstrap firmware were wanted for two different buses.)
//
#define POR_BUS_T_UNK 0
// Note: existing firmware for ISA-8 and MC-8 currently report the POR_BUS_T_UNK
// state, since the same bootstrap firmware is used for each.
#define POR_BUS_T_MCA 1 // MCA BUS */
#define POR_BUS_T_EISA 2 // EISA BUS */
#define POR_BUS_T_ISA 3 // ISA BUS */
// Values 4-7 Reserved
// Remaining bits are reserved
//----------------------
// porDiag1
//----------------------
#define POR_BAD_MAPPER 0x80 // HW failure on P.O.S.T: Chip mapper failed
// These two bits valid only for the IntelliPort-II
//
#define POR_BAD_UART1 0x01 // First 1400 bad
#define POR_BAD_UART2 0x02 // Second 1400 bad
//----------------------
// porDiag2
//----------------------
#define POR_DEBUG_PORT 0x80 // debug port was detected by the P.O.S.T
#define POR_DIAG_OK 0x00 // Indicates passage: Failure codes not yet
// available.
// Other bits undefined.
//----------------------
// porFlags
//----------------------
#define POR_CPU 0x03 // These bits indicate supposed CPU type
#define POR_CPU_8 0x01 // Board uses an 80188 (no such thing yet)
#define POR_CPU_6 0x02 // Board uses an 80186 (all existing products)
#define POR_CEX4 0x04 // If set, this is an ISA-CEX/4: An ISA-4 (asic)
// which is architected like an ISA-CEX connected
// to a (hitherto impossible) 4-port box.
#define POR_BOXES 0xf0 // Valid for IntelliPort-IIEX only: Map of Box
// sizes based on box I.D.
#define POR_BOX_16 0x10 // Set indicates 16-port, clear 8-port
//-------------------------------------
// LOADWARE and DOWNLOADING CODE
//-------------------------------------
/*
Loadware may be sent to the board in two ways:
1) It may be read from a (binary image) data file block by block as each block
is sent to the board. This is only possible when the initialization is
performed by code which can access your file system. This is most suitable
for diagnostics and appications which use the interface library directly.
2) It may be hard-coded into your source by including a .h file (typically
supplied by Computone), which declares a data array and initializes every
element. This achieves the same result as if an entire loadware file had
been read into the array.
This requires more data space in your program, but access to the file system
is not required. This method is more suited to driver code, which typically
is running at a level too low to access the file system directly.
At present, loadware can only be generated at Computone.
All Loadware begins with a header area which has a particular format. This
includes a magic number which identifies the file as being (purportedly)
loadware, CRC (for the loader), and version information.
*/
//-----------------------------------------------------------------------------
// Format of loadware block
//
// This is defined as a union so we can pass a pointer to one of these items
// and (if it is the first block) pick out the version information, etc.
//
// Otherwise, to deal with this as a simple character array
//------------------------------------------------------------------------------
#define LOADWARE_BLOCK_SIZE 512 // Number of bytes in each block of loadware
typedef union _loadHdrStr
{
unsigned char c[LOADWARE_BLOCK_SIZE]; // Valid for every block
struct // These fields are valid for only the first block of loadware.
{
unsigned char loadMagic; // Magic number: see below
unsigned char loadBlocksMore; // How many more blocks?
unsigned char loadCRC[2]; // Two CRC bytes: used by loader
unsigned char loadVersion; // Version number
unsigned char loadRevision; // Revision number
unsigned char loadSubRevision; // Sub-revision number
unsigned char loadSpares[9]; // Presently unused
unsigned char loadDates[32]; // Null-terminated string which can give
// date and time of compilation
} e;
} loadHdrStr, *loadHdrStrPtr;
//------------------------------------
// Defines for downloading code:
//------------------------------------
// The loadMagic field in the first block of the loadfile must be this, else the
// file is not valid.
//
#define MAGIC_LOADFILE 0x3c
// How do we know the load was successful? On completion of the load, the
// bootstrap firmware returns a code to indicate whether it thought the download
// was valid and intends to execute it. These are the only possible valid codes:
//
#define LOADWARE_OK 0xc3 // Download was ok
#define LOADWARE_BAD 0x5a // Download was bad (CRC error)
// Constants applicable to writing blocks of loadware:
// The first block of loadware might take 600 mS to load, in extreme cases.
// (Expandable board: worst case for sending startup messages to the LCD's).
// The 600mS figure is not really a calculation, but a conservative
// guess/guarantee. Usually this will be within 100 mS, like subsequent blocks.
//
#define MAX_DLOAD_START_TIME 1000 // 1000 mS
#define MAX_DLOAD_READ_TIME 100 // 100 mS
// Firmware should respond with status (see above) within this long of host
// having sent the final block.
//
#define MAX_DLOAD_ACK_TIME 100 // 100 mS, again!
//------------------------------------------------------
// MAXIMUM NUMBER OF PORTS PER BOARD:
// This is fixed for now (with the expandable), but may
// be expanding according to even newer products.
//------------------------------------------------------
//
#define ABS_MAX_BOXES 4 // Absolute most boxes per board
#define ABS_BIGGEST_BOX 16 // Absolute the most ports per box
#define ABS_MOST_PORTS (ABS_MAX_BOXES * ABS_BIGGEST_BOX)
#define I2_OUTSW(port, addr, count) outsw((port), (addr), (((count)+1)/2))
#define I2_OUTSB(port, addr, count) outsb((port), (addr), (((count)+1))&-2)
#define I2_INSW(port, addr, count) insw((port), (addr), (((count)+1)/2))
#define I2_INSB(port, addr, count) insb((port), (addr), (((count)+1))&-2)
#endif // I2HW_H
/*******************************************************************************
*
* (c) 1999 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport
* serial I/O controllers.
*
* DESCRIPTION: High-level interface code for the device driver. Uses the
* Extremely Low Level Interface Support (i2ellis.c). Provides an
* interface to the standard loadware, to support drivers or
* application code. (This is included source code, not a separate
* compilation module.)
*
*******************************************************************************/
//------------------------------------------------------------------------------
// Note on Strategy:
// Once the board has been initialized, it will interrupt us when:
// 1) It has something in the fifo for us to read (incoming data, flow control
// packets, or whatever).
// 2) It has stripped whatever we have sent last time in the FIFO (and
// consequently is ready for more).
//
// Note also that the buffer sizes declared in i2lib.h are VERY SMALL. This
// worsens performance considerably, but is done so that a great many channels
// might use only a little memory.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Revision History:
//
// 0.00 - 4/16/91 --- First Draft
// 0.01 - 4/29/91 --- 1st beta release
// 0.02 - 6/14/91 --- Changes to allow small model compilation
// 0.03 - 6/17/91 MAG Break reporting protected from interrupts routines with
// in-line asm added for moving data to/from ring buffers,
// replacing a variety of methods used previously.
// 0.04 - 6/21/91 MAG Initial flow-control packets not queued until
// i2_enable_interrupts time. Former versions would enqueue
// them at i2_init_channel time, before we knew how many
// channels were supposed to exist!
// 0.05 - 10/12/91 MAG Major changes: works through the ellis.c routines now;
// supports new 16-bit protocol and expandable boards.
// - 10/24/91 MAG Most changes in place and stable.
// 0.06 - 2/20/92 MAG Format of CMD_HOTACK corrected: the command takes no
// argument.
// 0.07 -- 3/11/92 MAG Support added to store special packet types at interrupt
// level (mostly responses to specific commands.)
// 0.08 -- 3/30/92 MAG Support added for STAT_MODEM packet
// 0.09 -- 6/24/93 MAG i2Link... needed to update number of boards BEFORE
// turning on the interrupt.
// 0.10 -- 6/25/93 MAG To avoid gruesome death from a bad board, we sanity check
// some incoming.
//
// 1.1 - 12/25/96 AKM Linux version.
// - 10/09/98 DMC Revised Linux version.
//------------------------------------------------------------------------------
//************
//* Includes *
//************
#include <linux/sched.h>
#include "i2lib.h"
//***********************
//* Function Prototypes *
//***********************
static void i2QueueNeeds(i2eBordStrPtr, i2ChanStrPtr, int);
static i2ChanStrPtr i2DeQueueNeeds(i2eBordStrPtr, int );
static void i2StripFifo(i2eBordStrPtr);
static void i2StuffFifoBypass(i2eBordStrPtr);
static void i2StuffFifoFlow(i2eBordStrPtr);
static void i2StuffFifoInline(i2eBordStrPtr);
static int i2RetryFlushOutput(i2ChanStrPtr);
// Not a documented part of the library routines (careful...) but the Diagnostic
// i2diag.c finds them useful to help the throughput in certain limited
// single-threaded operations.
static void iiSendPendingMail(i2eBordStrPtr);
static void serviceOutgoingFifo(i2eBordStrPtr);
// Functions defined in ip2.c as part of interrupt handling
static void do_input(struct work_struct *);
static void do_status(struct work_struct *);
//***************
//* Debug Data *
//***************
#ifdef DEBUG_FIFO
unsigned char DBGBuf[0x4000];
unsigned short I = 0;
static void
WriteDBGBuf(char *s, unsigned char *src, unsigned short n )
{
char *p = src;
// XXX: We need a spin lock here if we ever use this again
while (*s) { // copy label
DBGBuf[I] = *s++;
I = I++ & 0x3fff;
}
while (n--) { // copy data
DBGBuf[I] = *p++;
I = I++ & 0x3fff;
}
}
static void
fatality(i2eBordStrPtr pB )
{
int i;
for (i=0;i<sizeof(DBGBuf);i++) {
if ((i%16) == 0)
printk("\n%4x:",i);
printk("%02x ",DBGBuf[i]);
}
printk("\n");
for (i=0;i<sizeof(DBGBuf);i++) {
if ((i%16) == 0)
printk("\n%4x:",i);
if (DBGBuf[i] >= ' ' && DBGBuf[i] <= '~') {
printk(" %c ",DBGBuf[i]);
} else {
printk(" . ");
}
}
printk("\n");
printk("Last index %x\n",I);
}
#endif /* DEBUG_FIFO */
//********
//* Code *
//********
static inline int
i2Validate ( i2ChanStrPtr pCh )
{
//ip2trace(pCh->port_index, ITRC_VERIFY,ITRC_ENTER,2,pCh->validity,
// (CHANNEL_MAGIC | CHANNEL_SUPPORT));
return ((pCh->validity & (CHANNEL_MAGIC_BITS | CHANNEL_SUPPORT))
== (CHANNEL_MAGIC | CHANNEL_SUPPORT));
}
static void iiSendPendingMail_t(unsigned long data)
{
i2eBordStrPtr pB = (i2eBordStrPtr)data;
iiSendPendingMail(pB);
}
//******************************************************************************
// Function: iiSendPendingMail(pB)
// Parameters: Pointer to a board structure
// Returns: Nothing
//
// Description:
// If any outgoing mail bits are set and there is outgoing mailbox is empty,
// send the mail and clear the bits.
//******************************************************************************
static void
iiSendPendingMail(i2eBordStrPtr pB)
{
if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) )
{
if (iiTrySendMail(pB, pB->i2eOutMailWaiting))
{
/* If we were already waiting for fifo to empty,
* or just sent MB_OUT_STUFFED, then we are
* still waiting for it to empty, until we should
* receive an MB_IN_STRIPPED from the board.
*/
pB->i2eWaitingForEmptyFifo |=
(pB->i2eOutMailWaiting & MB_OUT_STUFFED);
pB->i2eOutMailWaiting = 0;
pB->SendPendingRetry = 0;
} else {
/* The only time we hit this area is when "iiTrySendMail" has
failed. That only occurs when the outbound mailbox is
still busy with the last message. We take a short breather
to let the board catch up with itself and then try again.
16 Retries is the limit - then we got a borked board.
/\/\|=mhw=|\/\/ */
if( ++pB->SendPendingRetry < 16 ) {
setup_timer(&pB->SendPendingTimer,
iiSendPendingMail_t, (unsigned long)pB);
mod_timer(&pB->SendPendingTimer, jiffies + 1);
} else {
printk( KERN_ERR "IP2: iiSendPendingMail unable to queue outbound mail\n" );
}
}
}
}
//******************************************************************************
// Function: i2InitChannels(pB, nChannels, pCh)
// Parameters: Pointer to Ellis Board structure
// Number of channels to initialize
// Pointer to first element in an array of channel structures
// Returns: Success or failure
//
// Description:
//
// This function patches pointers, back-pointers, and initializes all the
// elements in the channel structure array.
//
// This should be run after the board structure is initialized, through having
// loaded the standard loadware (otherwise it complains).
//
// In any case, it must be done before any serious work begins initializing the
// irq's or sending commands...
//
//******************************************************************************
static int
i2InitChannels ( i2eBordStrPtr pB, int nChannels, i2ChanStrPtr pCh)
{
int index, stuffIndex;
i2ChanStrPtr *ppCh;
if (pB->i2eValid != I2E_MAGIC) {
I2_COMPLETE(pB, I2EE_BADMAGIC);
}
if (pB->i2eState != II_STATE_STDLOADED) {
I2_COMPLETE(pB, I2EE_BADSTATE);
}
rwlock_init(&pB->read_fifo_spinlock);
rwlock_init(&pB->write_fifo_spinlock);
rwlock_init(&pB->Dbuf_spinlock);
rwlock_init(&pB->Bbuf_spinlock);
rwlock_init(&pB->Fbuf_spinlock);
// NO LOCK needed yet - this is init
pB->i2eChannelPtr = pCh;
pB->i2eChannelCnt = nChannels;
pB->i2Fbuf_strip = pB->i2Fbuf_stuff = 0;
pB->i2Dbuf_strip = pB->i2Dbuf_stuff = 0;
pB->i2Bbuf_strip = pB->i2Bbuf_stuff = 0;
pB->SendPendingRetry = 0;
memset ( pCh, 0, sizeof (i2ChanStr) * nChannels );
for (index = stuffIndex = 0, ppCh = (i2ChanStrPtr *)(pB->i2Fbuf);
nChannels && index < ABS_MOST_PORTS;
index++)
{
if ( !(pB->i2eChannelMap[index >> 4] & (1 << (index & 0xf)) ) ) {
continue;
}
rwlock_init(&pCh->Ibuf_spinlock);
rwlock_init(&pCh->Obuf_spinlock);
rwlock_init(&pCh->Cbuf_spinlock);
rwlock_init(&pCh->Pbuf_spinlock);
// NO LOCK needed yet - this is init
// Set up validity flag according to support level
if (pB->i2eGoodMap[index >> 4] & (1 << (index & 0xf)) ) {
pCh->validity = CHANNEL_MAGIC | CHANNEL_SUPPORT;
} else {
pCh->validity = CHANNEL_MAGIC;
}
pCh->pMyBord = pB; /* Back-pointer */
// Prepare an outgoing flow-control packet to send as soon as the chance
// occurs.
if ( pCh->validity & CHANNEL_SUPPORT ) {
pCh->infl.hd.i2sChannel = index;
pCh->infl.hd.i2sCount = 5;
pCh->infl.hd.i2sType = PTYPE_BYPASS;
pCh->infl.fcmd = 37;
pCh->infl.asof = 0;
pCh->infl.room = IBUF_SIZE - 1;
pCh->whenSendFlow = (IBUF_SIZE/5)*4; // when 80% full
// The following is similar to calling i2QueueNeeds, except that this
// is done in longhand, since we are setting up initial conditions on
// many channels at once.
pCh->channelNeeds = NEED_FLOW; // Since starting from scratch
pCh->sinceLastFlow = 0; // No bytes received since last flow
// control packet was queued
stuffIndex++;
*ppCh++ = pCh; // List this channel as needing
// initial flow control packet sent
}
// Don't allow anything to be sent until the status packets come in from
// the board.
pCh->outfl.asof = 0;
pCh->outfl.room = 0;
// Initialize all the ring buffers
pCh->Ibuf_stuff = pCh->Ibuf_strip = 0;
pCh->Obuf_stuff = pCh->Obuf_strip = 0;
pCh->Cbuf_stuff = pCh->Cbuf_strip = 0;
memset( &pCh->icount, 0, sizeof (struct async_icount) );
pCh->hotKeyIn = HOT_CLEAR;
pCh->channelOptions = 0;
pCh->bookMarks = 0;
init_waitqueue_head(&pCh->pBookmarkWait);
init_waitqueue_head(&pCh->open_wait);
init_waitqueue_head(&pCh->close_wait);
init_waitqueue_head(&pCh->delta_msr_wait);
// Set base and divisor so default custom rate is 9600
pCh->BaudBase = 921600; // MAX for ST654, changed after we get
pCh->BaudDivisor = 96; // the boxids (UART types) later
pCh->dataSetIn = 0;
pCh->dataSetOut = 0;
pCh->wopen = 0;
pCh->throttled = 0;
pCh->speed = CBR_9600;
pCh->flags = 0;
pCh->ClosingDelay = 5*HZ/10;
pCh->ClosingWaitTime = 30*HZ;
// Initialize task queue objects
INIT_WORK(&pCh->tqueue_input, do_input);
INIT_WORK(&pCh->tqueue_status, do_status);
#ifdef IP2DEBUG_TRACE
pCh->trace = ip2trace;
#endif
++pCh;
--nChannels;
}
// No need to check for wrap here; this is initialization.
pB->i2Fbuf_stuff = stuffIndex;
I2_COMPLETE(pB, I2EE_GOOD);
}
//******************************************************************************
// Function: i2DeQueueNeeds(pB, type)
// Parameters: Pointer to a board structure
// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW
// Returns:
// Pointer to a channel structure
//
// Description: Returns pointer struct of next channel that needs service of
// the type specified. Otherwise returns a NULL reference.
//
//******************************************************************************
static i2ChanStrPtr
i2DeQueueNeeds(i2eBordStrPtr pB, int type)
{
unsigned short queueIndex;
unsigned long flags;
i2ChanStrPtr pCh = NULL;
switch(type) {
case NEED_INLINE:
write_lock_irqsave(&pB->Dbuf_spinlock, flags);
if ( pB->i2Dbuf_stuff != pB->i2Dbuf_strip)
{
queueIndex = pB->i2Dbuf_strip;
pCh = pB->i2Dbuf[queueIndex];
queueIndex++;
if (queueIndex >= CH_QUEUE_SIZE) {
queueIndex = 0;
}
pB->i2Dbuf_strip = queueIndex;
pCh->channelNeeds &= ~NEED_INLINE;
}
write_unlock_irqrestore(&pB->Dbuf_spinlock, flags);
break;
case NEED_BYPASS:
write_lock_irqsave(&pB->Bbuf_spinlock, flags);
if (pB->i2Bbuf_stuff != pB->i2Bbuf_strip)
{
queueIndex = pB->i2Bbuf_strip;
pCh = pB->i2Bbuf[queueIndex];
queueIndex++;
if (queueIndex >= CH_QUEUE_SIZE) {
queueIndex = 0;
}
pB->i2Bbuf_strip = queueIndex;
pCh->channelNeeds &= ~NEED_BYPASS;
}
write_unlock_irqrestore(&pB->Bbuf_spinlock, flags);
break;
case NEED_FLOW:
write_lock_irqsave(&pB->Fbuf_spinlock, flags);
if (pB->i2Fbuf_stuff != pB->i2Fbuf_strip)
{
queueIndex = pB->i2Fbuf_strip;
pCh = pB->i2Fbuf[queueIndex];
queueIndex++;
if (queueIndex >= CH_QUEUE_SIZE) {
queueIndex = 0;
}
pB->i2Fbuf_strip = queueIndex;
pCh->channelNeeds &= ~NEED_FLOW;
}
write_unlock_irqrestore(&pB->Fbuf_spinlock, flags);
break;
default:
printk(KERN_ERR "i2DeQueueNeeds called with bad type:%x\n",type);
break;
}
return pCh;
}
//******************************************************************************
// Function: i2QueueNeeds(pB, pCh, type)
// Parameters: Pointer to a board structure
// Pointer to a channel structure
// type bit map: may include NEED_INLINE, NEED_BYPASS, or NEED_FLOW
// Returns: Nothing
//
// Description:
// For each type of need selected, if the given channel is not already in the
// queue, adds it, and sets the flag indicating it is in the queue.
//******************************************************************************
static void
i2QueueNeeds(i2eBordStrPtr pB, i2ChanStrPtr pCh, int type)
{
unsigned short queueIndex;
unsigned long flags;
// We turn off all the interrupts during this brief process, since the
// interrupt-level code might want to put things on the queue as well.
switch (type) {
case NEED_INLINE:
write_lock_irqsave(&pB->Dbuf_spinlock, flags);
if ( !(pCh->channelNeeds & NEED_INLINE) )
{
pCh->channelNeeds |= NEED_INLINE;
queueIndex = pB->i2Dbuf_stuff;
pB->i2Dbuf[queueIndex++] = pCh;
if (queueIndex >= CH_QUEUE_SIZE)
queueIndex = 0;
pB->i2Dbuf_stuff = queueIndex;
}
write_unlock_irqrestore(&pB->Dbuf_spinlock, flags);
break;
case NEED_BYPASS:
write_lock_irqsave(&pB->Bbuf_spinlock, flags);
if ((type & NEED_BYPASS) && !(pCh->channelNeeds & NEED_BYPASS))
{
pCh->channelNeeds |= NEED_BYPASS;
queueIndex = pB->i2Bbuf_stuff;
pB->i2Bbuf[queueIndex++] = pCh;
if (queueIndex >= CH_QUEUE_SIZE)
queueIndex = 0;
pB->i2Bbuf_stuff = queueIndex;
}
write_unlock_irqrestore(&pB->Bbuf_spinlock, flags);
break;
case NEED_FLOW:
write_lock_irqsave(&pB->Fbuf_spinlock, flags);
if ((type & NEED_FLOW) && !(pCh->channelNeeds & NEED_FLOW))
{
pCh->channelNeeds |= NEED_FLOW;
queueIndex = pB->i2Fbuf_stuff;
pB->i2Fbuf[queueIndex++] = pCh;
if (queueIndex >= CH_QUEUE_SIZE)
queueIndex = 0;
pB->i2Fbuf_stuff = queueIndex;
}
write_unlock_irqrestore(&pB->Fbuf_spinlock, flags);
break;
case NEED_CREDIT:
pCh->channelNeeds |= NEED_CREDIT;
break;
default:
printk(KERN_ERR "i2QueueNeeds called with bad type:%x\n",type);
break;
}
return;
}
//******************************************************************************
// Function: i2QueueCommands(type, pCh, timeout, nCommands, pCs,...)
// Parameters: type - PTYPE_BYPASS or PTYPE_INLINE
// pointer to the channel structure
// maximum period to wait
// number of commands (n)
// n commands
// Returns: Number of commands sent, or -1 for error
//
// get board lock before calling
//
// Description:
// Queues up some commands to be sent to a channel. To send possibly several
// bypass or inline commands to the given channel. The timeout parameter
// indicates how many HUNDREDTHS OF SECONDS to wait until there is room:
// 0 = return immediately if no room, -ive = wait forever, +ive = number of
// 1/100 seconds to wait. Return values:
// -1 Some kind of nasty error: bad channel structure or invalid arguments.
// 0 No room to send all the commands
// (+) Number of commands sent
//******************************************************************************
static int
i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands,
cmdSyntaxPtr pCs0,...)
{
int totalsize = 0;
int blocksize;
int lastended;
cmdSyntaxPtr *ppCs;
cmdSyntaxPtr pCs;
int count;
int flag;
i2eBordStrPtr pB;
unsigned short maxBlock;
unsigned short maxBuff;
short bufroom;
unsigned short stuffIndex;
unsigned char *pBuf;
unsigned char *pInsert;
unsigned char *pDest, *pSource;
unsigned short channel;
int cnt;
unsigned long flags = 0;
rwlock_t *lock_var_p = NULL;
// Make sure the channel exists, otherwise do nothing
if ( !i2Validate ( pCh ) ) {
return -1;
}
ip2trace (CHANN, ITRC_QUEUE, ITRC_ENTER, 0 );
pB = pCh->pMyBord;
// Board must also exist, and THE INTERRUPT COMMAND ALREADY SENT
if (pB->i2eValid != I2E_MAGIC || pB->i2eUsingIrq == I2_IRQ_UNDEFINED)
return -2;
// If the board has gone fatal, return bad, and also hit the trap routine if
// it exists.
if (pB->i2eFatal) {
if ( pB->i2eFatalTrap ) {
(*(pB)->i2eFatalTrap)(pB);
}
return -3;
}
// Set up some variables, Which buffers are we using? How big are they?
switch(type)
{
case PTYPE_INLINE:
flag = INL;
maxBlock = MAX_OBUF_BLOCK;
maxBuff = OBUF_SIZE;
pBuf = pCh->Obuf;
break;
case PTYPE_BYPASS:
flag = BYP;
maxBlock = MAX_CBUF_BLOCK;
maxBuff = CBUF_SIZE;
pBuf = pCh->Cbuf;
break;
default:
return -4;
}
// Determine the total size required for all the commands
totalsize = blocksize = sizeof(i2CmdHeader);
lastended = 0;
ppCs = &pCs0;
for ( count = nCommands; count; count--, ppCs++)
{
pCs = *ppCs;
cnt = pCs->length;
// Will a new block be needed for this one?
// Two possible reasons: too
// big or previous command has to be at the end of a packet.
if ((blocksize + cnt > maxBlock) || lastended) {
blocksize = sizeof(i2CmdHeader);
totalsize += sizeof(i2CmdHeader);
}
totalsize += cnt;
blocksize += cnt;
// If this command had to end a block, then we will make sure to
// account for it should there be any more blocks.
lastended = pCs->flags & END;
}
for (;;) {
// Make sure any pending flush commands go out before we add more data.
if ( !( pCh->flush_flags && i2RetryFlushOutput( pCh ) ) ) {
// How much room (this time through) ?
switch(type) {
case PTYPE_INLINE:
lock_var_p = &pCh->Obuf_spinlock;
write_lock_irqsave(lock_var_p, flags);
stuffIndex = pCh->Obuf_stuff;
bufroom = pCh->Obuf_strip - stuffIndex;
break;
case PTYPE_BYPASS:
lock_var_p = &pCh->Cbuf_spinlock;
write_lock_irqsave(lock_var_p, flags);
stuffIndex = pCh->Cbuf_stuff;
bufroom = pCh->Cbuf_strip - stuffIndex;
break;
default:
return -5;
}
if (--bufroom < 0) {
bufroom += maxBuff;
}
ip2trace (CHANN, ITRC_QUEUE, 2, 1, bufroom );
// Check for overflow
if (totalsize <= bufroom) {
// Normal Expected path - We still hold LOCK
break; /* from for()- Enough room: goto proceed */
}
ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize);
write_unlock_irqrestore(lock_var_p, flags);
} else
ip2trace(CHANN, ITRC_QUEUE, 3, 1, totalsize);
/* Prepare to wait for buffers to empty */
serviceOutgoingFifo(pB); // Dump what we got
if (timeout == 0) {
return 0; // Tired of waiting
}
if (timeout > 0)
timeout--; // So negative values == forever
if (!in_interrupt()) {
schedule_timeout_interruptible(1); // short nap
} else {
// we cannot sched/sleep in interrupt silly
return 0;
}
if (signal_pending(current)) {
return 0; // Wake up! Time to die!!!
}
ip2trace (CHANN, ITRC_QUEUE, 4, 0 );
} // end of for(;;)
// At this point we have room and the lock - stick them in.
channel = pCh->infl.hd.i2sChannel;
pInsert = &pBuf[stuffIndex]; // Pointer to start of packet
pDest = CMD_OF(pInsert); // Pointer to start of command
// When we start counting, the block is the size of the header
for (blocksize = sizeof(i2CmdHeader), count = nCommands,
lastended = 0, ppCs = &pCs0;
count;
count--, ppCs++)
{
pCs = *ppCs; // Points to command protocol structure
// If this is a bookmark request command, post the fact that a bookmark
// request is pending. NOTE THIS TRICK ONLY WORKS BECAUSE CMD_BMARK_REQ
// has no parameters! The more general solution would be to reference
// pCs->cmd[0].
if (pCs == CMD_BMARK_REQ) {
pCh->bookMarks++;
ip2trace (CHANN, ITRC_DRAIN, 30, 1, pCh->bookMarks );
}
cnt = pCs->length;
// If this command would put us over the maximum block size or
// if the last command had to be at the end of a block, we end
// the existing block here and start a new one.
if ((blocksize + cnt > maxBlock) || lastended) {
ip2trace (CHANN, ITRC_QUEUE, 5, 0 );
PTYPE_OF(pInsert) = type;
CHANNEL_OF(pInsert) = channel;
// count here does not include the header
CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader);
stuffIndex += blocksize;
if(stuffIndex >= maxBuff) {
stuffIndex = 0;
pInsert = pBuf;
}
pInsert = &pBuf[stuffIndex]; // Pointer to start of next pkt
pDest = CMD_OF(pInsert);
blocksize = sizeof(i2CmdHeader);
}
// Now we know there is room for this one in the current block
blocksize += cnt; // Total bytes in this command
pSource = pCs->cmd; // Copy the command into the buffer
while (cnt--) {
*pDest++ = *pSource++;
}
// If this command had to end a block, then we will make sure to account
// for it should there be any more blocks.
lastended = pCs->flags & END;
} // end for
// Clean up the final block by writing header, etc
PTYPE_OF(pInsert) = type;
CHANNEL_OF(pInsert) = channel;
// count here does not include the header
CMD_COUNT_OF(pInsert) = blocksize - sizeof(i2CmdHeader);
stuffIndex += blocksize;
if(stuffIndex >= maxBuff) {
stuffIndex = 0;
pInsert = pBuf;
}
// Updates the index, and post the need for service. When adding these to
// the queue of channels, we turn off the interrupt while doing so,
// because at interrupt level we might want to push a channel back to the
// end of the queue.
switch(type)
{
case PTYPE_INLINE:
pCh->Obuf_stuff = stuffIndex; // Store buffer pointer
write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
pB->debugInlineQueued++;
// Add the channel pointer to list of channels needing service (first
// come...), if it's not already there.
i2QueueNeeds(pB, pCh, NEED_INLINE);
break;
case PTYPE_BYPASS:
pCh->Cbuf_stuff = stuffIndex; // Store buffer pointer
write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags);
pB->debugBypassQueued++;
// Add the channel pointer to list of channels needing service (first
// come...), if it's not already there.
i2QueueNeeds(pB, pCh, NEED_BYPASS);
break;
}
ip2trace (CHANN, ITRC_QUEUE, ITRC_RETURN, 1, nCommands );
return nCommands; // Good status: number of commands sent
}
//******************************************************************************
// Function: i2GetStatus(pCh,resetBits)
// Parameters: Pointer to a channel structure
// Bit map of status bits to clear
// Returns: Bit map of current status bits
//
// Description:
// Returns the state of data set signals, and whether a break has been received,
// (see i2lib.h for bit-mapped result). resetBits is a bit-map of any status
// bits to be cleared: I2_BRK, I2_PAR, I2_FRA, I2_OVR,... These are cleared
// AFTER the condition is passed. If pCh does not point to a valid channel,
// returns -1 (which would be impossible otherwise.
//******************************************************************************
static int
i2GetStatus(i2ChanStrPtr pCh, int resetBits)
{
unsigned short status;
i2eBordStrPtr pB;
ip2trace (CHANN, ITRC_STATUS, ITRC_ENTER, 2, pCh->dataSetIn, resetBits );
// Make sure the channel exists, otherwise do nothing */
if ( !i2Validate ( pCh ) )
return -1;
pB = pCh->pMyBord;
status = pCh->dataSetIn;
// Clear any specified error bits: but note that only actual error bits can
// be cleared, regardless of the value passed.
if (resetBits)
{
pCh->dataSetIn &= ~(resetBits & (I2_BRK | I2_PAR | I2_FRA | I2_OVR));
pCh->dataSetIn &= ~(I2_DDCD | I2_DCTS | I2_DDSR | I2_DRI);
}
ip2trace (CHANN, ITRC_STATUS, ITRC_RETURN, 1, pCh->dataSetIn );
return status;
}
//******************************************************************************
// Function: i2Input(pChpDest,count)
// Parameters: Pointer to a channel structure
// Pointer to data buffer
// Number of bytes to read
// Returns: Number of bytes read, or -1 for error
//
// Description:
// Strips data from the input buffer and writes it to pDest. If there is a
// colossal blunder, (invalid structure pointers or the like), returns -1.
// Otherwise, returns the number of bytes read.
//******************************************************************************
static int
i2Input(i2ChanStrPtr pCh)
{
int amountToMove;
unsigned short stripIndex;
int count;
unsigned long flags = 0;
ip2trace (CHANN, ITRC_INPUT, ITRC_ENTER, 0);
// Ensure channel structure seems real
if ( !i2Validate( pCh ) ) {
count = -1;
goto i2Input_exit;
}
write_lock_irqsave(&pCh->Ibuf_spinlock, flags);
// initialize some accelerators and private copies
stripIndex = pCh->Ibuf_strip;
count = pCh->Ibuf_stuff - stripIndex;
// If buffer is empty or requested data count was 0, (trivial case) return
// without any further thought.
if ( count == 0 ) {
write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
goto i2Input_exit;
}
// Adjust for buffer wrap
if ( count < 0 ) {
count += IBUF_SIZE;
}
// Don't give more than can be taken by the line discipline
amountToMove = pCh->pTTY->receive_room;
if (count > amountToMove) {
count = amountToMove;
}
// How much could we copy without a wrap?
amountToMove = IBUF_SIZE - stripIndex;
if (amountToMove > count) {
amountToMove = count;
}
// Move the first block
pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
&(pCh->Ibuf[stripIndex]), NULL, amountToMove );
// If we needed to wrap, do the second data move
if (count > amountToMove) {
pCh->pTTY->ldisc->ops->receive_buf( pCh->pTTY,
pCh->Ibuf, NULL, count - amountToMove );
}
// Bump and wrap the stripIndex all at once by the amount of data read. This
// method is good regardless of whether the data was in one or two pieces.
stripIndex += count;
if (stripIndex >= IBUF_SIZE) {
stripIndex -= IBUF_SIZE;
}
pCh->Ibuf_strip = stripIndex;
// Update our flow control information and possibly queue ourselves to send
// it, depending on how much data has been stripped since the last time a
// packet was sent.
pCh->infl.asof += count;
if ((pCh->sinceLastFlow += count) >= pCh->whenSendFlow) {
pCh->sinceLastFlow -= pCh->whenSendFlow;
write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW);
} else {
write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
}
i2Input_exit:
ip2trace (CHANN, ITRC_INPUT, ITRC_RETURN, 1, count);
return count;
}
//******************************************************************************
// Function: i2InputFlush(pCh)
// Parameters: Pointer to a channel structure
// Returns: Number of bytes stripped, or -1 for error
//
// Description:
// Strips any data from the input buffer. If there is a colossal blunder,
// (invalid structure pointers or the like), returns -1. Otherwise, returns the
// number of bytes stripped.
//******************************************************************************
static int
i2InputFlush(i2ChanStrPtr pCh)
{
int count;
unsigned long flags;
// Ensure channel structure seems real
if ( !i2Validate ( pCh ) )
return -1;
ip2trace (CHANN, ITRC_INPUT, 10, 0);
write_lock_irqsave(&pCh->Ibuf_spinlock, flags);
count = pCh->Ibuf_stuff - pCh->Ibuf_strip;
// Adjust for buffer wrap
if (count < 0) {
count += IBUF_SIZE;
}
// Expedient way to zero out the buffer
pCh->Ibuf_strip = pCh->Ibuf_stuff;
// Update our flow control information and possibly queue ourselves to send
// it, depending on how much data has been stripped since the last time a
// packet was sent.
pCh->infl.asof += count;
if ( (pCh->sinceLastFlow += count) >= pCh->whenSendFlow )
{
pCh->sinceLastFlow -= pCh->whenSendFlow;
write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
i2QueueNeeds(pCh->pMyBord, pCh, NEED_FLOW);
} else {
write_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
}
ip2trace (CHANN, ITRC_INPUT, 19, 1, count);
return count;
}
//******************************************************************************
// Function: i2InputAvailable(pCh)
// Parameters: Pointer to a channel structure
// Returns: Number of bytes available, or -1 for error
//
// Description:
// If there is a colossal blunder, (invalid structure pointers or the like),
// returns -1. Otherwise, returns the number of bytes stripped. Otherwise,
// returns the number of bytes available in the buffer.
//******************************************************************************
#if 0
static int
i2InputAvailable(i2ChanStrPtr pCh)
{
int count;
// Ensure channel structure seems real
if ( !i2Validate ( pCh ) ) return -1;
// initialize some accelerators and private copies
read_lock_irqsave(&pCh->Ibuf_spinlock, flags);
count = pCh->Ibuf_stuff - pCh->Ibuf_strip;
read_unlock_irqrestore(&pCh->Ibuf_spinlock, flags);
// Adjust for buffer wrap
if (count < 0)
{
count += IBUF_SIZE;
}
return count;
}
#endif
//******************************************************************************
// Function: i2Output(pCh, pSource, count)
// Parameters: Pointer to channel structure
// Pointer to source data
// Number of bytes to send
// Returns: Number of bytes sent, or -1 for error
//
// Description:
// Queues the data at pSource to be sent as data packets to the board. If there
// is a colossal blunder, (invalid structure pointers or the like), returns -1.
// Otherwise, returns the number of bytes written. What if there is not enough
// room for all the data? If pCh->channelOptions & CO_NBLOCK_WRITE is set, then
// we transfer as many characters as we can now, then return. If this bit is
// clear (default), routine will spin along until all the data is buffered.
// Should this occur, the 1-ms delay routine is called while waiting to avoid
// applications that one cannot break out of.
//******************************************************************************
static int
i2Output(i2ChanStrPtr pCh, const char *pSource, int count)
{
i2eBordStrPtr pB;
unsigned char *pInsert;
int amountToMove;
int countOriginal = count;
unsigned short channel;
unsigned short stuffIndex;
unsigned long flags;
int bailout = 10;
ip2trace (CHANN, ITRC_OUTPUT, ITRC_ENTER, 2, count, 0 );
// Ensure channel structure seems real
if ( !i2Validate ( pCh ) )
return -1;
// initialize some accelerators and private copies
pB = pCh->pMyBord;
channel = pCh->infl.hd.i2sChannel;
// If the board has gone fatal, return bad, and also hit the trap routine if
// it exists.
if (pB->i2eFatal) {
if (pB->i2eFatalTrap) {
(*(pB)->i2eFatalTrap)(pB);
}
return -1;
}
// Proceed as though we would do everything
while ( count > 0 ) {
// How much room in output buffer is there?
read_lock_irqsave(&pCh->Obuf_spinlock, flags);
amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1;
read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
if (amountToMove < 0) {
amountToMove += OBUF_SIZE;
}
// Subtract off the headers size and see how much room there is for real
// data. If this is negative, we will discover later.
amountToMove -= sizeof (i2DataHeader);
// Don't move more (now) than can go in a single packet
if ( amountToMove > (int)(MAX_OBUF_BLOCK - sizeof(i2DataHeader)) ) {
amountToMove = MAX_OBUF_BLOCK - sizeof(i2DataHeader);
}
// Don't move more than the count we were given
if (amountToMove > count) {
amountToMove = count;
}
// Now we know how much we must move: NB because the ring buffers have
// an overflow area at the end, we needn't worry about wrapping in the
// middle of a packet.
// Small WINDOW here with no LOCK but I can't call Flush with LOCK
// We would be flushing (or ending flush) anyway
ip2trace (CHANN, ITRC_OUTPUT, 10, 1, amountToMove );
if ( !(pCh->flush_flags && i2RetryFlushOutput(pCh) )
&& amountToMove > 0 )
{
write_lock_irqsave(&pCh->Obuf_spinlock, flags);
stuffIndex = pCh->Obuf_stuff;
// Had room to move some data: don't know whether the block size,
// buffer space, or what was the limiting factor...
pInsert = &(pCh->Obuf[stuffIndex]);
// Set up the header
CHANNEL_OF(pInsert) = channel;
PTYPE_OF(pInsert) = PTYPE_DATA;
TAG_OF(pInsert) = 0;
ID_OF(pInsert) = ID_ORDINARY_DATA;
DATA_COUNT_OF(pInsert) = amountToMove;
// Move the data
memcpy( (char*)(DATA_OF(pInsert)), pSource, amountToMove );
// Adjust pointers and indices
pSource += amountToMove;
pCh->Obuf_char_count += amountToMove;
stuffIndex += amountToMove + sizeof(i2DataHeader);
count -= amountToMove;
if (stuffIndex >= OBUF_SIZE) {
stuffIndex = 0;
}
pCh->Obuf_stuff = stuffIndex;
write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
ip2trace (CHANN, ITRC_OUTPUT, 13, 1, stuffIndex );
} else {
// Cannot move data
// becuz we need to stuff a flush
// or amount to move is <= 0
ip2trace(CHANN, ITRC_OUTPUT, 14, 3,
amountToMove, pB->i2eFifoRemains,
pB->i2eWaitingForEmptyFifo );
// Put this channel back on queue
// this ultimatly gets more data or wakes write output
i2QueueNeeds(pB, pCh, NEED_INLINE);
if ( pB->i2eWaitingForEmptyFifo ) {
ip2trace (CHANN, ITRC_OUTPUT, 16, 0 );
// or schedule
if (!in_interrupt()) {
ip2trace (CHANN, ITRC_OUTPUT, 61, 0 );
schedule_timeout_interruptible(2);
if (signal_pending(current)) {
break;
}
continue;
} else {
ip2trace (CHANN, ITRC_OUTPUT, 62, 0 );
// let interrupt in = WAS restore_flags()
// We hold no lock nor is irq off anymore???
break;
}
break; // from while(count)
}
else if ( pB->i2eFifoRemains < 32 && !pB->i2eTxMailEmpty ( pB ) )
{
ip2trace (CHANN, ITRC_OUTPUT, 19, 2,
pB->i2eFifoRemains,
pB->i2eTxMailEmpty );
break; // from while(count)
} else if ( pCh->channelNeeds & NEED_CREDIT ) {
ip2trace (CHANN, ITRC_OUTPUT, 22, 0 );
break; // from while(count)
} else if ( --bailout) {
// Try to throw more things (maybe not us) in the fifo if we're
// not already waiting for it.
ip2trace (CHANN, ITRC_OUTPUT, 20, 0 );
serviceOutgoingFifo(pB);
//break; CONTINUE;
} else {
ip2trace (CHANN, ITRC_OUTPUT, 21, 3,
pB->i2eFifoRemains,
pB->i2eOutMailWaiting,
pB->i2eWaitingForEmptyFifo );
break; // from while(count)
}
}
} // End of while(count)
i2QueueNeeds(pB, pCh, NEED_INLINE);
// We drop through either when the count expires, or when there is some
// count left, but there was a non-blocking write.
if (countOriginal > count) {
ip2trace (CHANN, ITRC_OUTPUT, 17, 2, countOriginal, count );
serviceOutgoingFifo( pB );
}
ip2trace (CHANN, ITRC_OUTPUT, ITRC_RETURN, 2, countOriginal, count );
return countOriginal - count;
}
//******************************************************************************
// Function: i2FlushOutput(pCh)
// Parameters: Pointer to a channel structure
// Returns: Nothing
//
// Description:
// Sends bypass command to start flushing (waiting possibly forever until there
// is room), then sends inline command to stop flushing output, (again waiting
// possibly forever).
//******************************************************************************
static inline void
i2FlushOutput(i2ChanStrPtr pCh)
{
ip2trace (CHANN, ITRC_FLUSH, 1, 1, pCh->flush_flags );
if (pCh->flush_flags)
return;
if ( 1 != i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) {
pCh->flush_flags = STARTFL_FLAG; // Failed - flag for later
ip2trace (CHANN, ITRC_FLUSH, 2, 0 );
} else if ( 1 != i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL) ) {
pCh->flush_flags = STOPFL_FLAG; // Failed - flag for later
ip2trace (CHANN, ITRC_FLUSH, 3, 0 );
}
}
static int
i2RetryFlushOutput(i2ChanStrPtr pCh)
{
int old_flags = pCh->flush_flags;
ip2trace (CHANN, ITRC_FLUSH, 14, 1, old_flags );
pCh->flush_flags = 0; // Clear flag so we can avoid recursion
// and queue the commands
if ( old_flags & STARTFL_FLAG ) {
if ( 1 == i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_STARTFL) ) {
old_flags = STOPFL_FLAG; //Success - send stop flush
} else {
old_flags = STARTFL_FLAG; //Failure - Flag for retry later
}
ip2trace (CHANN, ITRC_FLUSH, 15, 1, old_flags );
}
if ( old_flags & STOPFL_FLAG ) {
if (1 == i2QueueCommands(PTYPE_INLINE, pCh, 0, 1, CMD_STOPFL)) {
old_flags = 0; // Success - clear flags
}
ip2trace (CHANN, ITRC_FLUSH, 16, 1, old_flags );
}
pCh->flush_flags = old_flags;
ip2trace (CHANN, ITRC_FLUSH, 17, 1, old_flags );
return old_flags;
}
//******************************************************************************
// Function: i2DrainOutput(pCh,timeout)
// Parameters: Pointer to a channel structure
// Maximum period to wait
// Returns: ?
//
// Description:
// Uses the bookmark request command to ask the board to send a bookmark back as
// soon as all the data is completely sent.
//******************************************************************************
static void
i2DrainWakeup(unsigned long d)
{
i2ChanStrPtr pCh = (i2ChanStrPtr)d;
ip2trace (CHANN, ITRC_DRAIN, 10, 1, pCh->BookmarkTimer.expires );
pCh->BookmarkTimer.expires = 0;
wake_up_interruptible( &pCh->pBookmarkWait );
}
static void
i2DrainOutput(i2ChanStrPtr pCh, int timeout)
{
wait_queue_t wait;
i2eBordStrPtr pB;
ip2trace (CHANN, ITRC_DRAIN, ITRC_ENTER, 1, pCh->BookmarkTimer.expires);
pB = pCh->pMyBord;
// If the board has gone fatal, return bad,
// and also hit the trap routine if it exists.
if (pB->i2eFatal) {
if (pB->i2eFatalTrap) {
(*(pB)->i2eFatalTrap)(pB);
}
return;
}
if ((timeout > 0) && (pCh->BookmarkTimer.expires == 0 )) {
// One per customer (channel)
setup_timer(&pCh->BookmarkTimer, i2DrainWakeup,
(unsigned long)pCh);
ip2trace (CHANN, ITRC_DRAIN, 1, 1, pCh->BookmarkTimer.expires );
mod_timer(&pCh->BookmarkTimer, jiffies + timeout);
}
i2QueueCommands( PTYPE_INLINE, pCh, -1, 1, CMD_BMARK_REQ );
init_waitqueue_entry(&wait, current);
add_wait_queue(&(pCh->pBookmarkWait), &wait);
set_current_state( TASK_INTERRUPTIBLE );
serviceOutgoingFifo( pB );
schedule(); // Now we take our interruptible sleep on
// Clean up the queue
set_current_state( TASK_RUNNING );
remove_wait_queue(&(pCh->pBookmarkWait), &wait);
// if expires == 0 then timer poped, then do not need to del_timer
if ((timeout > 0) && pCh->BookmarkTimer.expires &&
time_before(jiffies, pCh->BookmarkTimer.expires)) {
del_timer( &(pCh->BookmarkTimer) );
pCh->BookmarkTimer.expires = 0;
ip2trace (CHANN, ITRC_DRAIN, 3, 1, pCh->BookmarkTimer.expires );
}
ip2trace (CHANN, ITRC_DRAIN, ITRC_RETURN, 1, pCh->BookmarkTimer.expires );
return;
}
//******************************************************************************
// Function: i2OutputFree(pCh)
// Parameters: Pointer to a channel structure
// Returns: Space in output buffer
//
// Description:
// Returns -1 if very gross error. Otherwise returns the amount of bytes still
// free in the output buffer.
//******************************************************************************
static int
i2OutputFree(i2ChanStrPtr pCh)
{
int amountToMove;
unsigned long flags;
// Ensure channel structure seems real
if ( !i2Validate ( pCh ) ) {
return -1;
}
read_lock_irqsave(&pCh->Obuf_spinlock, flags);
amountToMove = pCh->Obuf_strip - pCh->Obuf_stuff - 1;
read_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
if (amountToMove < 0) {
amountToMove += OBUF_SIZE;
}
// If this is negative, we will discover later
amountToMove -= sizeof(i2DataHeader);
return (amountToMove < 0) ? 0 : amountToMove;
}
static void
ip2_owake( PTTY tp)
{
i2ChanStrPtr pCh;
if (tp == NULL) return;
pCh = tp->driver_data;
ip2trace (CHANN, ITRC_SICMD, 10, 2, tp->flags,
(1 << TTY_DO_WRITE_WAKEUP) );
tty_wakeup(tp);
}
static inline void
set_baud_params(i2eBordStrPtr pB)
{
int i,j;
i2ChanStrPtr *pCh;
pCh = (i2ChanStrPtr *) pB->i2eChannelPtr;
for (i = 0; i < ABS_MAX_BOXES; i++) {
if (pB->channelBtypes.bid_value[i]) {
if (BID_HAS_654(pB->channelBtypes.bid_value[i])) {
for (j = 0; j < ABS_BIGGEST_BOX; j++) {
if (pCh[i*16+j] == NULL)
break;
(pCh[i*16+j])->BaudBase = 921600; // MAX for ST654
(pCh[i*16+j])->BaudDivisor = 96;
}
} else { // has cirrus cd1400
for (j = 0; j < ABS_BIGGEST_BOX; j++) {
if (pCh[i*16+j] == NULL)
break;
(pCh[i*16+j])->BaudBase = 115200; // MAX for CD1400
(pCh[i*16+j])->BaudDivisor = 12;
}
}
}
}
}
//******************************************************************************
// Function: i2StripFifo(pB)
// Parameters: Pointer to a board structure
// Returns: ?
//
// Description:
// Strips all the available data from the incoming FIFO, identifies the type of
// packet, and either buffers the data or does what needs to be done.
//
// Note there is no overflow checking here: if the board sends more data than it
// ought to, we will not detect it here, but blindly overflow...
//******************************************************************************
// A buffer for reading in blocks for unknown channels
static unsigned char junkBuffer[IBUF_SIZE];
// A buffer to read in a status packet. Because of the size of the count field
// for these things, the maximum packet size must be less than MAX_CMD_PACK_SIZE
static unsigned char cmdBuffer[MAX_CMD_PACK_SIZE + 4];
// This table changes the bit order from MSR order given by STAT_MODEM packet to
// status bits used in our library.
static char xlatDss[16] = {
0 | 0 | 0 | 0 ,
0 | 0 | 0 | I2_CTS ,
0 | 0 | I2_DSR | 0 ,
0 | 0 | I2_DSR | I2_CTS ,
0 | I2_RI | 0 | 0 ,
0 | I2_RI | 0 | I2_CTS ,
0 | I2_RI | I2_DSR | 0 ,
0 | I2_RI | I2_DSR | I2_CTS ,
I2_DCD | 0 | 0 | 0 ,
I2_DCD | 0 | 0 | I2_CTS ,
I2_DCD | 0 | I2_DSR | 0 ,
I2_DCD | 0 | I2_DSR | I2_CTS ,
I2_DCD | I2_RI | 0 | 0 ,
I2_DCD | I2_RI | 0 | I2_CTS ,
I2_DCD | I2_RI | I2_DSR | 0 ,
I2_DCD | I2_RI | I2_DSR | I2_CTS };
static inline void
i2StripFifo(i2eBordStrPtr pB)
{
i2ChanStrPtr pCh;
int channel;
int count;
unsigned short stuffIndex;
int amountToRead;
unsigned char *pc, *pcLimit;
unsigned char uc;
unsigned char dss_change;
unsigned long bflags,cflags;
// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_ENTER, 0 );
while (I2_HAS_INPUT(pB)) {
// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 2, 0 );
// Process packet from fifo a one atomic unit
write_lock_irqsave(&pB->read_fifo_spinlock, bflags);
// The first word (or two bytes) will have channel number and type of
// packet, possibly other information
pB->i2eLeadoffWord[0] = iiReadWord(pB);
switch(PTYPE_OF(pB->i2eLeadoffWord))
{
case PTYPE_DATA:
pB->got_input = 1;
// ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 3, 0 );
channel = CHANNEL_OF(pB->i2eLeadoffWord); /* Store channel */
count = iiReadWord(pB); /* Count is in the next word */
// NEW: Check the count for sanity! Should the hardware fail, our death
// is more pleasant. While an oversize channel is acceptable (just more
// than the driver supports), an over-length count clearly means we are
// sick!
if ( ((unsigned int)count) > IBUF_SIZE ) {
pB->i2eFatal = 2;
write_unlock_irqrestore(&pB->read_fifo_spinlock,
bflags);
return; /* Bail out ASAP */
}
// Channel is illegally big ?
if ((channel >= pB->i2eChannelCnt) ||
(NULL==(pCh = ((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])))
{
iiReadBuf(pB, junkBuffer, count);
write_unlock_irqrestore(&pB->read_fifo_spinlock,
bflags);
break; /* From switch: ready for next packet */
}
// Channel should be valid, then
// If this is a hot-key, merely post its receipt for now. These are
// always supposed to be 1-byte packets, so we won't even check the
// count. Also we will post an acknowledgement to the board so that
// more data can be forthcoming. Note that we are not trying to use
// these sequences in this driver, merely to robustly ignore them.
if(ID_OF(pB->i2eLeadoffWord) == ID_HOT_KEY)
{
pCh->hotKeyIn = iiReadWord(pB) & 0xff;
write_unlock_irqrestore(&pB->read_fifo_spinlock,
bflags);
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_HOTACK);
break; /* From the switch: ready for next packet */
}
// Normal data! We crudely assume there is room for the data in our
// buffer because the board wouldn't have exceeded his credit limit.
write_lock_irqsave(&pCh->Ibuf_spinlock, cflags);
// We have 2 locks now
stuffIndex = pCh->Ibuf_stuff;
amountToRead = IBUF_SIZE - stuffIndex;
if (amountToRead > count)
amountToRead = count;
// stuffIndex would have been already adjusted so there would
// always be room for at least one, and count is always at least
// one.
iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead);
pCh->icount.rx += amountToRead;
// Update the stuffIndex by the amount of data moved. Note we could
// never ask for more data than would just fit. However, we might
// have read in one more byte than we wanted because the read
// rounds up to even bytes. If this byte is on the end of the
// packet, and is padding, we ignore it. If the byte is part of
// the actual data, we need to move it.
stuffIndex += amountToRead;
if (stuffIndex >= IBUF_SIZE) {
if ((amountToRead & 1) && (count > amountToRead)) {
pCh->Ibuf[0] = pCh->Ibuf[IBUF_SIZE];
amountToRead++;
stuffIndex = 1;
} else {
stuffIndex = 0;
}
}
// If there is anything left over, read it as well
if (count > amountToRead) {
amountToRead = count - amountToRead;
iiReadBuf(pB, &(pCh->Ibuf[stuffIndex]), amountToRead);
pCh->icount.rx += amountToRead;
stuffIndex += amountToRead;
}
// Update stuff index
pCh->Ibuf_stuff = stuffIndex;
write_unlock_irqrestore(&pCh->Ibuf_spinlock, cflags);
write_unlock_irqrestore(&pB->read_fifo_spinlock,
bflags);
#ifdef USE_IQ
schedule_work(&pCh->tqueue_input);
#else
do_input(&pCh->tqueue_input);
#endif
// Note we do not need to maintain any flow-control credits at this
// time: if we were to increment .asof and decrement .room, there
// would be no net effect. Instead, when we strip data, we will
// increment .asof and leave .room unchanged.
break; // From switch: ready for next packet
case PTYPE_STATUS:
ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 4, 0 );
count = CMD_COUNT_OF(pB->i2eLeadoffWord);
iiReadBuf(pB, cmdBuffer, count);
// We can release early with buffer grab
write_unlock_irqrestore(&pB->read_fifo_spinlock,
bflags);
pc = cmdBuffer;
pcLimit = &(cmdBuffer[count]);
while (pc < pcLimit) {
channel = *pc++;
ip2trace (channel, ITRC_SFIFO, 7, 2, channel, *pc );
/* check for valid channel */
if (channel < pB->i2eChannelCnt
&&
(pCh = (((i2ChanStrPtr*)pB->i2eChannelPtr)[channel])) != NULL
)
{
dss_change = 0;
switch (uc = *pc++)
{
/* Breaks and modem signals are easy: just update status */
case STAT_CTS_UP:
if ( !(pCh->dataSetIn & I2_CTS) )
{
pCh->dataSetIn |= I2_DCTS;
pCh->icount.cts++;
dss_change = 1;
}
pCh->dataSetIn |= I2_CTS;
break;
case STAT_CTS_DN:
if ( pCh->dataSetIn & I2_CTS )
{
pCh->dataSetIn |= I2_DCTS;
pCh->icount.cts++;
dss_change = 1;
}
pCh->dataSetIn &= ~I2_CTS;
break;
case STAT_DCD_UP:
ip2trace (channel, ITRC_MODEM, 1, 1, pCh->dataSetIn );
if ( !(pCh->dataSetIn & I2_DCD) )
{
ip2trace (CHANN, ITRC_MODEM, 2, 0 );
pCh->dataSetIn |= I2_DDCD;
pCh->icount.dcd++;
dss_change = 1;
}
pCh->dataSetIn |= I2_DCD;
ip2trace (channel, ITRC_MODEM, 3, 1, pCh->dataSetIn );
break;
case STAT_DCD_DN:
ip2trace (channel, ITRC_MODEM, 4, 1, pCh->dataSetIn );
if ( pCh->dataSetIn & I2_DCD )
{
ip2trace (channel, ITRC_MODEM, 5, 0 );
pCh->dataSetIn |= I2_DDCD;
pCh->icount.dcd++;
dss_change = 1;
}
pCh->dataSetIn &= ~I2_DCD;
ip2trace (channel, ITRC_MODEM, 6, 1, pCh->dataSetIn );
break;
case STAT_DSR_UP:
if ( !(pCh->dataSetIn & I2_DSR) )
{
pCh->dataSetIn |= I2_DDSR;
pCh->icount.dsr++;
dss_change = 1;
}
pCh->dataSetIn |= I2_DSR;
break;
case STAT_DSR_DN:
if ( pCh->dataSetIn & I2_DSR )
{
pCh->dataSetIn |= I2_DDSR;
pCh->icount.dsr++;
dss_change = 1;
}
pCh->dataSetIn &= ~I2_DSR;
break;
case STAT_RI_UP:
if ( !(pCh->dataSetIn & I2_RI) )
{
pCh->dataSetIn |= I2_DRI;
pCh->icount.rng++;
dss_change = 1;
}
pCh->dataSetIn |= I2_RI ;
break;
case STAT_RI_DN:
// to be compat with serial.c
//if ( pCh->dataSetIn & I2_RI )
//{
// pCh->dataSetIn |= I2_DRI;
// pCh->icount.rng++;
// dss_change = 1;
//}
pCh->dataSetIn &= ~I2_RI ;
break;
case STAT_BRK_DET:
pCh->dataSetIn |= I2_BRK;
pCh->icount.brk++;
dss_change = 1;
break;
// Bookmarks? one less request we're waiting for
case STAT_BMARK:
pCh->bookMarks--;
if (pCh->bookMarks <= 0 ) {
pCh->bookMarks = 0;
wake_up_interruptible( &pCh->pBookmarkWait );
ip2trace (channel, ITRC_DRAIN, 20, 1, pCh->BookmarkTimer.expires );
}
break;
// Flow control packets? Update the new credits, and if
// someone was waiting for output, queue him up again.
case STAT_FLOW:
pCh->outfl.room =
((flowStatPtr)pc)->room -
(pCh->outfl.asof - ((flowStatPtr)pc)->asof);
ip2trace (channel, ITRC_STFLW, 1, 1, pCh->outfl.room );
if (pCh->channelNeeds & NEED_CREDIT)
{
ip2trace (channel, ITRC_STFLW, 2, 1, pCh->channelNeeds);
pCh->channelNeeds &= ~NEED_CREDIT;
i2QueueNeeds(pB, pCh, NEED_INLINE);
if ( pCh->pTTY )
ip2_owake(pCh->pTTY);
}
ip2trace (channel, ITRC_STFLW, 3, 1, pCh->channelNeeds);
pc += sizeof(flowStat);
break;
/* Special packets: */
/* Just copy the information into the channel structure */
case STAT_STATUS:
pCh->channelStatus = *((debugStatPtr)pc);
pc += sizeof(debugStat);
break;
case STAT_TXCNT:
pCh->channelTcount = *((cntStatPtr)pc);
pc += sizeof(cntStat);
break;
case STAT_RXCNT:
pCh->channelRcount = *((cntStatPtr)pc);
pc += sizeof(cntStat);
break;
case STAT_BOXIDS:
pB->channelBtypes = *((bidStatPtr)pc);
pc += sizeof(bidStat);
set_baud_params(pB);
break;
case STAT_HWFAIL:
i2QueueCommands (PTYPE_INLINE, pCh, 0, 1, CMD_HW_TEST);
pCh->channelFail = *((failStatPtr)pc);
pc += sizeof(failStat);
break;
/* No explicit match? then
* Might be an error packet...
*/
default:
switch (uc & STAT_MOD_ERROR)
{
case STAT_ERROR:
if (uc & STAT_E_PARITY) {
pCh->dataSetIn |= I2_PAR;
pCh->icount.parity++;
}
if (uc & STAT_E_FRAMING){
pCh->dataSetIn |= I2_FRA;
pCh->icount.frame++;
}
if (uc & STAT_E_OVERRUN){
pCh->dataSetIn |= I2_OVR;
pCh->icount.overrun++;
}
break;
case STAT_MODEM:
// the answer to DSS_NOW request (not change)
pCh->dataSetIn = (pCh->dataSetIn
& ~(I2_RI | I2_CTS | I2_DCD | I2_DSR) )
| xlatDss[uc & 0xf];
wake_up_interruptible ( &pCh->dss_now_wait );
default:
break;
}
} /* End of switch on status type */
if (dss_change) {
#ifdef USE_IQ
schedule_work(&pCh->tqueue_status);
#else
do_status(&pCh->tqueue_status);
#endif
}
}
else /* Or else, channel is invalid */
{
// Even though the channel is invalid, we must test the
// status to see how much additional data it has (to be
// skipped)
switch (*pc++)
{
case STAT_FLOW:
pc += 4; /* Skip the data */
break;
default:
break;
}
}
} // End of while (there is still some status packet left)
break;
default: // Neither packet? should be impossible
ip2trace (ITRC_NO_PORT, ITRC_SFIFO, 5, 1,
PTYPE_OF(pB->i2eLeadoffWord) );
write_unlock_irqrestore(&pB->read_fifo_spinlock,
bflags);
break;
} // End of switch on type of packets
} /*while(board I2_HAS_INPUT)*/
ip2trace (ITRC_NO_PORT, ITRC_SFIFO, ITRC_RETURN, 0 );
// Send acknowledgement to the board even if there was no data!
pB->i2eOutMailWaiting |= MB_IN_STRIPPED;
return;
}
//******************************************************************************
// Function: i2Write2Fifo(pB,address,count)
// Parameters: Pointer to a board structure, source address, byte count
// Returns: bytes written
//
// Description:
// Writes count bytes to board io address(implied) from source
// Adjusts count, leaves reserve for next time around bypass cmds
//******************************************************************************
static int
i2Write2Fifo(i2eBordStrPtr pB, unsigned char *source, int count,int reserve)
{
int rc = 0;
unsigned long flags;
write_lock_irqsave(&pB->write_fifo_spinlock, flags);
if (!pB->i2eWaitingForEmptyFifo) {
if (pB->i2eFifoRemains > (count+reserve)) {
pB->i2eFifoRemains -= count;
iiWriteBuf(pB, source, count);
pB->i2eOutMailWaiting |= MB_OUT_STUFFED;
rc = count;
}
}
write_unlock_irqrestore(&pB->write_fifo_spinlock, flags);
return rc;
}
//******************************************************************************
// Function: i2StuffFifoBypass(pB)
// Parameters: Pointer to a board structure
// Returns: Nothing
//
// Description:
// Stuffs as many bypass commands into the fifo as possible. This is simpler
// than stuffing data or inline commands to fifo, since we do not have
// flow-control to deal with.
//******************************************************************************
static inline void
i2StuffFifoBypass(i2eBordStrPtr pB)
{
i2ChanStrPtr pCh;
unsigned char *pRemove;
unsigned short stripIndex;
unsigned short packetSize;
unsigned short paddedSize;
unsigned short notClogged = 1;
unsigned long flags;
int bailout = 1000;
// Continue processing so long as there are entries, or there is room in the
// fifo. Each entry represents a channel with something to do.
while ( --bailout && notClogged &&
(NULL != (pCh = i2DeQueueNeeds(pB,NEED_BYPASS))))
{
write_lock_irqsave(&pCh->Cbuf_spinlock, flags);
stripIndex = pCh->Cbuf_strip;
// as long as there are packets for this channel...
while (stripIndex != pCh->Cbuf_stuff) {
pRemove = &(pCh->Cbuf[stripIndex]);
packetSize = CMD_COUNT_OF(pRemove) + sizeof(i2CmdHeader);
paddedSize = roundup(packetSize, 2);
if (paddedSize > 0) {
if ( 0 == i2Write2Fifo(pB, pRemove, paddedSize,0)) {
notClogged = 0; /* fifo full */
i2QueueNeeds(pB, pCh, NEED_BYPASS); // Put back on queue
break; // Break from the channel
}
}
#ifdef DEBUG_FIFO
WriteDBGBuf("BYPS", pRemove, paddedSize);
#endif /* DEBUG_FIFO */
pB->debugBypassCount++;
pRemove += packetSize;
stripIndex += packetSize;
if (stripIndex >= CBUF_SIZE) {
stripIndex = 0;
pRemove = pCh->Cbuf;
}
}
// Done with this channel. Move to next, removing this one from
// the queue of channels if we cleaned it out (i.e., didn't get clogged.
pCh->Cbuf_strip = stripIndex;
write_unlock_irqrestore(&pCh->Cbuf_spinlock, flags);
} // Either clogged or finished all the work
#ifdef IP2DEBUG_TRACE
if ( !bailout ) {
ip2trace (ITRC_NO_PORT, ITRC_ERROR, 1, 0 );
}
#endif
}
//******************************************************************************
// Function: i2StuffFifoFlow(pB)
// Parameters: Pointer to a board structure
// Returns: Nothing
//
// Description:
// Stuffs as many flow control packets into the fifo as possible. This is easier
// even than doing normal bypass commands, because there is always at most one
// packet, already assembled, for each channel.
//******************************************************************************
static inline void
i2StuffFifoFlow(i2eBordStrPtr pB)
{
i2ChanStrPtr pCh;
unsigned short paddedSize = roundup(sizeof(flowIn), 2);
ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_ENTER, 2,
pB->i2eFifoRemains, paddedSize );
// Continue processing so long as there are entries, or there is room in the
// fifo. Each entry represents a channel with something to do.
while ( (NULL != (pCh = i2DeQueueNeeds(pB,NEED_FLOW)))) {
pB->debugFlowCount++;
// NO Chan LOCK needed ???
if ( 0 == i2Write2Fifo(pB,(unsigned char *)&(pCh->infl),paddedSize,0)) {
break;
}
#ifdef DEBUG_FIFO
WriteDBGBuf("FLOW",(unsigned char *) &(pCh->infl), paddedSize);
#endif /* DEBUG_FIFO */
} // Either clogged or finished all the work
ip2trace (ITRC_NO_PORT, ITRC_SFLOW, ITRC_RETURN, 0 );
}
//******************************************************************************
// Function: i2StuffFifoInline(pB)
// Parameters: Pointer to a board structure
// Returns: Nothing
//
// Description:
// Stuffs as much data and inline commands into the fifo as possible. This is
// the most complex fifo-stuffing operation, since there if now the channel
// flow-control issue to deal with.
//******************************************************************************
static inline void
i2StuffFifoInline(i2eBordStrPtr pB)
{
i2ChanStrPtr pCh;
unsigned char *pRemove;
unsigned short stripIndex;
unsigned short packetSize;
unsigned short paddedSize;
unsigned short notClogged = 1;
unsigned short flowsize;
unsigned long flags;
int bailout = 1000;
int bailout2;
ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_ENTER, 3, pB->i2eFifoRemains,
pB->i2Dbuf_strip, pB->i2Dbuf_stuff );
// Continue processing so long as there are entries, or there is room in the
// fifo. Each entry represents a channel with something to do.
while ( --bailout && notClogged &&
(NULL != (pCh = i2DeQueueNeeds(pB,NEED_INLINE))) )
{
write_lock_irqsave(&pCh->Obuf_spinlock, flags);
stripIndex = pCh->Obuf_strip;
ip2trace (CHANN, ITRC_SICMD, 3, 2, stripIndex, pCh->Obuf_stuff );
// as long as there are packets for this channel...
bailout2 = 1000;
while ( --bailout2 && stripIndex != pCh->Obuf_stuff) {
pRemove = &(pCh->Obuf[stripIndex]);
// Must determine whether this be a data or command packet to
// calculate correctly the header size and the amount of
// flow-control credit this type of packet will use.
if (PTYPE_OF(pRemove) == PTYPE_DATA) {
flowsize = DATA_COUNT_OF(pRemove);
packetSize = flowsize + sizeof(i2DataHeader);
} else {
flowsize = CMD_COUNT_OF(pRemove);
packetSize = flowsize + sizeof(i2CmdHeader);
}
flowsize = CREDIT_USAGE(flowsize);
paddedSize = roundup(packetSize, 2);
ip2trace (CHANN, ITRC_SICMD, 4, 2, pB->i2eFifoRemains, paddedSize );
// If we don't have enough credits from the board to send the data,
// flag the channel that we are waiting for flow control credit, and
// break out. This will clean up this channel and remove us from the
// queue of hot things to do.
ip2trace (CHANN, ITRC_SICMD, 5, 2, pCh->outfl.room, flowsize );
if (pCh->outfl.room <= flowsize) {
// Do Not have the credits to send this packet.
i2QueueNeeds(pB, pCh, NEED_CREDIT);
notClogged = 0;
break; // So to do next channel
}
if ( (paddedSize > 0)
&& ( 0 == i2Write2Fifo(pB, pRemove, paddedSize, 128))) {
// Do Not have room in fifo to send this packet.
notClogged = 0;
i2QueueNeeds(pB, pCh, NEED_INLINE);
break; // Break from the channel
}
#ifdef DEBUG_FIFO
WriteDBGBuf("DATA", pRemove, paddedSize);
#endif /* DEBUG_FIFO */
pB->debugInlineCount++;
pCh->icount.tx += flowsize;
// Update current credits
pCh->outfl.room -= flowsize;
pCh->outfl.asof += flowsize;
if (PTYPE_OF(pRemove) == PTYPE_DATA) {
pCh->Obuf_char_count -= DATA_COUNT_OF(pRemove);
}
pRemove += packetSize;
stripIndex += packetSize;
ip2trace (CHANN, ITRC_SICMD, 6, 2, stripIndex, pCh->Obuf_strip);
if (stripIndex >= OBUF_SIZE) {
stripIndex = 0;
pRemove = pCh->Obuf;
ip2trace (CHANN, ITRC_SICMD, 7, 1, stripIndex );
}
} /* while */
if ( !bailout2 ) {
ip2trace (CHANN, ITRC_ERROR, 3, 0 );
}
// Done with this channel. Move to next, removing this one from the
// queue of channels if we cleaned it out (i.e., didn't get clogged.
pCh->Obuf_strip = stripIndex;
write_unlock_irqrestore(&pCh->Obuf_spinlock, flags);
if ( notClogged )
{
ip2trace (CHANN, ITRC_SICMD, 8, 0 );
if ( pCh->pTTY ) {
ip2_owake(pCh->pTTY);
}
}
} // Either clogged or finished all the work
if ( !bailout ) {
ip2trace (ITRC_NO_PORT, ITRC_ERROR, 4, 0 );
}
ip2trace (ITRC_NO_PORT, ITRC_SICMD, ITRC_RETURN, 1,pB->i2Dbuf_strip);
}
//******************************************************************************
// Function: serviceOutgoingFifo(pB)
// Parameters: Pointer to a board structure
// Returns: Nothing
//
// Description:
// Helper routine to put data in the outgoing fifo, if we aren't already waiting
// for something to be there. If the fifo has only room for a very little data,
// go head and hit the board with a mailbox hit immediately. Otherwise, it will
// have to happen later in the interrupt processing. Since this routine may be
// called both at interrupt and foreground time, we must turn off interrupts
// during the entire process.
//******************************************************************************
static void
serviceOutgoingFifo(i2eBordStrPtr pB)
{
// If we aren't currently waiting for the board to empty our fifo, service
// everything that is pending, in priority order (especially, Bypass before
// Inline).
if ( ! pB->i2eWaitingForEmptyFifo )
{
i2StuffFifoFlow(pB);
i2StuffFifoBypass(pB);
i2StuffFifoInline(pB);
iiSendPendingMail(pB);
}
}
//******************************************************************************
// Function: i2ServiceBoard(pB)
// Parameters: Pointer to a board structure
// Returns: Nothing
//
// Description:
// Normally this is called from interrupt level, but there is deliberately
// nothing in here specific to being called from interrupt level. All the
// hardware-specific, interrupt-specific things happen at the outer levels.
//
// For example, a timer interrupt could drive this routine for some sort of
// polled operation. The only requirement is that the programmer deal with any
// atomiticity/concurrency issues that result.
//
// This routine responds to the board's having sent mailbox information to the
// host (which would normally cause an interrupt). This routine reads the
// incoming mailbox. If there is no data in it, this board did not create the
// interrupt and/or has nothing to be done to it. (Except, if we have been
// waiting to write mailbox data to it, we may do so.
//
// Based on the value in the mailbox, we may take various actions.
//
// No checking here of pB validity: after all, it shouldn't have been called by
// the handler unless pB were on the list.
//******************************************************************************
static inline int
i2ServiceBoard ( i2eBordStrPtr pB )
{
unsigned inmail;
unsigned long flags;
/* This should be atomic because of the way we are called... */
if (NO_MAIL_HERE == ( inmail = pB->i2eStartMail ) ) {
inmail = iiGetMail(pB);
}
pB->i2eStartMail = NO_MAIL_HERE;
ip2trace (ITRC_NO_PORT, ITRC_INTR, 2, 1, inmail );
if (inmail != NO_MAIL_HERE) {
// If the board has gone fatal, nothing to do but hit a bit that will
// alert foreground tasks to protest!
if ( inmail & MB_FATAL_ERROR ) {
pB->i2eFatal = 1;
goto exit_i2ServiceBoard;
}
/* Assuming no fatal condition, we proceed to do work */
if ( inmail & MB_IN_STUFFED ) {
pB->i2eFifoInInts++;
i2StripFifo(pB); /* There might be incoming packets */
}
if (inmail & MB_OUT_STRIPPED) {
pB->i2eFifoOutInts++;
write_lock_irqsave(&pB->write_fifo_spinlock, flags);
pB->i2eFifoRemains = pB->i2eFifoSize;
pB->i2eWaitingForEmptyFifo = 0;
write_unlock_irqrestore(&pB->write_fifo_spinlock,
flags);
ip2trace (ITRC_NO_PORT, ITRC_INTR, 30, 1, pB->i2eFifoRemains );
}
serviceOutgoingFifo(pB);
}
ip2trace (ITRC_NO_PORT, ITRC_INTR, 8, 0 );
exit_i2ServiceBoard:
return 0;
}
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Header file for high level library functions
*
*******************************************************************************/
#ifndef I2LIB_H
#define I2LIB_H 1
//------------------------------------------------------------------------------
// I2LIB.H
//
// IntelliPort-II and IntelliPort-IIEX
//
// Defines, structure definitions, and external declarations for i2lib.c
//------------------------------------------------------------------------------
//--------------------------------------
// Mandatory Includes:
//--------------------------------------
#include "ip2types.h"
#include "i2ellis.h"
#include "i2pack.h"
#include "i2cmd.h"
#include <linux/workqueue.h>
//------------------------------------------------------------------------------
// i2ChanStr -- Channel Structure:
// Used to track per-channel information for the library routines using standard
// loadware. Note also, a pointer to an array of these structures is patched
// into the i2eBordStr (see i2ellis.h)
//------------------------------------------------------------------------------
//
// If we make some limits on the maximum block sizes, we can avoid dealing with
// buffer wrap. The wrapping of the buffer is based on where the start of the
// packet is. Then there is always room for the packet contiguously.
//
// Maximum total length of an outgoing data or in-line command block. The limit
// of 36 on data is quite arbitrary and based more on DOS memory limitations
// than the board interface. However, for commands, the maximum packet length is
// MAX_CMD_PACK_SIZE, because the field size for the count is only a few bits
// (see I2PACK.H) in such packets. For data packets, the count field size is not
// the limiting factor. As of this writing, MAX_OBUF_BLOCK < MAX_CMD_PACK_SIZE,
// but be careful if wanting to modify either.
//
#define MAX_OBUF_BLOCK 36
// Another note on maximum block sizes: we are buffering packets here. Data is
// put into the buffer (if there is room) regardless of the credits from the
// board. The board sends new credits whenever it has removed from his buffers a
// number of characters equal to 80% of total buffer size. (Of course, the total
// buffer size is what is reported when the very first set of flow control
// status packets are received from the board. Therefore, to be robust, you must
// always fill the board to at least 80% of the current credit limit, else you
// might not give it enough to trigger a new report. These conditions are
// obtained here so long as the maximum output block size is less than 20% the
// size of the board's output buffers. This is true at present by "coincidence"
// or "infernal knowledge": the board's output buffers are at least 700 bytes
// long (20% = 140 bytes, at least). The 80% figure is "official", so the safest
// strategy might be to trap the first flow control report and guarantee that
// the effective maxObufBlock is the minimum of MAX_OBUF_BLOCK and 20% of first
// reported buffer credit.
//
#define MAX_CBUF_BLOCK 6 // Maximum total length of a bypass command block
#define IBUF_SIZE 512 // character capacity of input buffer per channel
#define OBUF_SIZE 1024// character capacity of output buffer per channel
#define CBUF_SIZE 10 // character capacity of output bypass buffer
typedef struct _i2ChanStr
{
// First, back-pointers so that given a pointer to this structure, you can
// determine the correct board and channel number to reference, (say, when
// issuing commands, etc. (Note, channel number is in infl.hd.i2sChannel.)
int port_index; // Index of port in channel structure array attached
// to board structure.
PTTY pTTY; // Pointer to tty structure for port (OS specific)
USHORT validity; // Indicates whether the given channel has been
// initialized, really exists (or is a missing
// channel, e.g. channel 9 on an 8-port box.)
i2eBordStrPtr pMyBord; // Back-pointer to this channel's board structure
int wopen; // waiting fer carrier
int throttled; // Set if upper layer can take no data
int flags; // Defined in tty.h
PWAITQ open_wait; // Pointer for OS sleep function.
PWAITQ close_wait; // Pointer for OS sleep function.
PWAITQ delta_msr_wait;// Pointer for OS sleep function.
PWAITQ dss_now_wait; // Pointer for OS sleep function.
struct timer_list BookmarkTimer; // Used by i2DrainOutput
wait_queue_head_t pBookmarkWait; // Used by i2DrainOutput
int BaudBase;
int BaudDivisor;
USHORT ClosingDelay;
USHORT ClosingWaitTime;
volatile
flowIn infl; // This structure is initialized as a completely
// formed flow-control command packet, and as such
// has the channel number, also the capacity and
// "as-of" data needed continuously.
USHORT sinceLastFlow; // Counts the number of characters read from input
// buffers, since the last time flow control info
// was sent.
USHORT whenSendFlow; // Determines when new flow control is to be sent to
// the board. Note unlike earlier manifestations of
// the driver, these packets can be sent from
// in-place.
USHORT channelNeeds; // Bit map of important things which must be done
// for this channel. (See bits below )
volatile
flowStat outfl; // Same type of structure is used to hold current
// flow control information used to control our
// output. "asof" is kept updated as data is sent,
// and "room" never goes to zero.
// The incoming ring buffer
// Unlike the outgoing buffers, this holds raw data, not packets. The two
// extra bytes are used to hold the byte-padding when there is room for an
// odd number of bytes before we must wrap.
//
UCHAR Ibuf[IBUF_SIZE + 2];
volatile
USHORT Ibuf_stuff; // Stuffing index
volatile
USHORT Ibuf_strip; // Stripping index
// The outgoing ring-buffer: Holds Data and command packets. N.B., even
// though these are in the channel structure, the channel is also written
// here, the easier to send it to the fifo when ready. HOWEVER, individual
// packets here are NOT padded to even length: the routines for writing
// blocks to the fifo will pad to even byte counts.
//
UCHAR Obuf[OBUF_SIZE+MAX_OBUF_BLOCK+4];
volatile
USHORT Obuf_stuff; // Stuffing index
volatile
USHORT Obuf_strip; // Stripping index
int Obuf_char_count;
// The outgoing bypass-command buffer. Unlike earlier manifestations, the
// flow control packets are sent directly from the structures. As above, the
// channel number is included in the packet, but they are NOT padded to even
// size.
//
UCHAR Cbuf[CBUF_SIZE+MAX_CBUF_BLOCK+2];
volatile
USHORT Cbuf_stuff; // Stuffing index
volatile
USHORT Cbuf_strip; // Stripping index
// The temporary buffer for the Linux tty driver PutChar entry.
//
UCHAR Pbuf[MAX_OBUF_BLOCK - sizeof (i2DataHeader)];
volatile
USHORT Pbuf_stuff; // Stuffing index
// The state of incoming data-set signals
//
USHORT dataSetIn; // Bit-mapped according to below. Also indicates
// whether a break has been detected since last
// inquiry.
// The state of outcoming data-set signals (as far as we can tell!)
//
USHORT dataSetOut; // Bit-mapped according to below.
// Most recent hot-key identifier detected
//
USHORT hotKeyIn; // Hot key as sent by the board, HOT_CLEAR indicates
// no hot key detected since last examined.
// Counter of outstanding requests for bookmarks
//
short bookMarks; // Number of outstanding bookmark requests, (+ive
// whenever a bookmark request if queued up, -ive
// whenever a bookmark is received).
// Misc options
//
USHORT channelOptions; // See below
// To store various incoming special packets
//
debugStat channelStatus;
cntStat channelRcount;
cntStat channelTcount;
failStat channelFail;
// To store the last values for line characteristics we sent to the board.
//
int speed;
int flush_flags;
void (*trace)(unsigned short,unsigned char,unsigned char,unsigned long,...);
/*
* Kernel counters for the 4 input interrupts
*/
struct async_icount icount;
/*
* Task queues for processing input packets from the board.
*/
struct work_struct tqueue_input;
struct work_struct tqueue_status;
struct work_struct tqueue_hangup;
rwlock_t Ibuf_spinlock;
rwlock_t Obuf_spinlock;
rwlock_t Cbuf_spinlock;
rwlock_t Pbuf_spinlock;
} i2ChanStr, *i2ChanStrPtr;
//---------------------------------------------------
// Manifests and bit-maps for elements in i2ChanStr
//---------------------------------------------------
//
// flush flags
//
#define STARTFL_FLAG 1
#define STOPFL_FLAG 2
// validity
//
#define CHANNEL_MAGIC_BITS 0xff00
#define CHANNEL_MAGIC 0x5300 // (validity & CHANNEL_MAGIC_BITS) ==
// CHANNEL_MAGIC --> structure good
#define CHANNEL_SUPPORT 0x0001 // Indicates channel is supported, exists,
// and passed P.O.S.T.
// channelNeeds
//
#define NEED_FLOW 1 // Indicates flow control has been queued
#define NEED_INLINE 2 // Indicates inline commands or data queued
#define NEED_BYPASS 4 // Indicates bypass commands queued
#define NEED_CREDIT 8 // Indicates would be sending except has not sufficient
// credit. The data is still in the channel structure,
// but the channel is not enqueued in the board
// structure again until there is a credit received from
// the board.
// dataSetIn (Also the bits for i2GetStatus return value)
//
#define I2_DCD 1
#define I2_CTS 2
#define I2_DSR 4
#define I2_RI 8
// dataSetOut (Also the bits for i2GetStatus return value)
//
#define I2_DTR 1
#define I2_RTS 2
// i2GetStatus() can optionally clear these bits
//
#define I2_BRK 0x10 // A break was detected
#define I2_PAR 0x20 // A parity error was received
#define I2_FRA 0x40 // A framing error was received
#define I2_OVR 0x80 // An overrun error was received
// i2GetStatus() automatically clears these bits */
//
#define I2_DDCD 0x100 // DCD changed from its former value
#define I2_DCTS 0x200 // CTS changed from its former value
#define I2_DDSR 0x400 // DSR changed from its former value
#define I2_DRI 0x800 // RI changed from its former value
// hotKeyIn
//
#define HOT_CLEAR 0x1322 // Indicates that no hot-key has been detected
// channelOptions
//
#define CO_NBLOCK_WRITE 1 // Writes don't block waiting for buffer. (Default
// is, they do wait.)
// fcmodes
//
#define I2_OUTFLOW_CTS 0x0001
#define I2_INFLOW_RTS 0x0002
#define I2_INFLOW_DSR 0x0004
#define I2_INFLOW_DTR 0x0008
#define I2_OUTFLOW_DSR 0x0010
#define I2_OUTFLOW_DTR 0x0020
#define I2_OUTFLOW_XON 0x0040
#define I2_OUTFLOW_XANY 0x0080
#define I2_INFLOW_XON 0x0100
#define I2_CRTSCTS (I2_OUTFLOW_CTS|I2_INFLOW_RTS)
#define I2_IXANY_MODE (I2_OUTFLOW_XON|I2_OUTFLOW_XANY)
//-------------------------------------------
// Macros used from user level like functions
//-------------------------------------------
// Macros to set and clear channel options
//
#define i2SetOption(pCh, option) pCh->channelOptions |= option
#define i2ClrOption(pCh, option) pCh->channelOptions &= ~option
// Macro to set fatal-error trap
//
#define i2SetFatalTrap(pB, routine) pB->i2eFatalTrap = routine
//--------------------------------------------
// Declarations and prototypes for i2lib.c
//--------------------------------------------
//
static int i2InitChannels(i2eBordStrPtr, int, i2ChanStrPtr);
static int i2QueueCommands(int, i2ChanStrPtr, int, int, cmdSyntaxPtr,...);
static int i2GetStatus(i2ChanStrPtr, int);
static int i2Input(i2ChanStrPtr);
static int i2InputFlush(i2ChanStrPtr);
static int i2Output(i2ChanStrPtr, const char *, int);
static int i2OutputFree(i2ChanStrPtr);
static int i2ServiceBoard(i2eBordStrPtr);
static void i2DrainOutput(i2ChanStrPtr, int);
#ifdef IP2DEBUG_TRACE
void ip2trace(unsigned short,unsigned char,unsigned char,unsigned long,...);
#else
#define ip2trace(a,b,c,d...) do {} while (0)
#endif
// Argument to i2QueueCommands
//
#define C_IN_LINE 1
#define C_BYPASS 0
#endif // I2LIB_H
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Definitions of the packets used to transfer data and commands
* Host <--> Board. Information provided here is only applicable
* when the standard loadware is active.
*
*******************************************************************************/
#ifndef I2PACK_H
#define I2PACK_H 1
//-----------------------------------------------
// Revision History:
//
// 10 October 1991 MAG First draft
// 24 February 1992 MAG Additions for 1.4.x loadware
// 11 March 1992 MAG New status packets
//
//-----------------------------------------------
//------------------------------------------------------------------------------
// Packet Formats:
//
// Information passes between the host and board through the FIFO in packets.
// These have headers which indicate the type of packet. Because the fifo data
// path may be 16-bits wide, the protocol is constrained such that each packet
// is always padded to an even byte count. (The lower-level interface routines
// -- i2ellis.c -- are designed to do this).
//
// The sender (be it host or board) must place some number of complete packets
// in the fifo, then place a message in the mailbox that packets are available.
// Placing such a message interrupts the "receiver" (be it board or host), who
// reads the mailbox message and determines that there are incoming packets
// ready. Since there are no partial packets, and the length of a packet is
// given in the header, the remainder of the packet can be read without checking
// for FIFO empty condition. The process is repeated, packet by packet, until
// the incoming FIFO is empty. Then the receiver uses the outbound mailbox to
// signal the board that it has read the data. Only then can the sender place
// additional data in the fifo.
//------------------------------------------------------------------------------
//
//------------------------------------------------
// Definition of Packet Header Area
//------------------------------------------------
//
// Caution: these only define header areas. In actual use the data runs off
// beyond the end of these structures.
//
// Since these structures are based on sequences of bytes which go to the board,
// there cannot be ANY padding between the elements.
#pragma pack(1)
//----------------------------
// DATA PACKETS
//----------------------------
typedef struct _i2DataHeader
{
unsigned char i2sChannel; /* The channel number: 0-255 */
// -- Bitfields are allocated LSB first --
// For incoming data, indicates whether this is an ordinary packet or a
// special one (e.g., hot key hit).
unsigned i2sId : 2 __attribute__ ((__packed__));
// For tagging data packets. There are flush commands which flush only data
// packets bearing a particular tag. (used in implementing IntelliView and
// IntelliPrint). THE TAG VALUE 0xf is RESERVED and must not be used (it has
// meaning internally to the loadware).
unsigned i2sTag : 4;
// These two bits determine the type of packet sent/received.
unsigned i2sType : 2;
// The count of data to follow: does not include the possible additional
// padding byte. MAXIMUM COUNT: 4094. The top four bits must be 0.
unsigned short i2sCount;
} i2DataHeader, *i2DataHeaderPtr;
// Structure is immediately followed by the data, proper.
//----------------------------
// NON-DATA PACKETS
//----------------------------
typedef struct _i2CmdHeader
{
unsigned char i2sChannel; // The channel number: 0-255 (Except where noted
// - see below
// Number of bytes of commands, status or whatever to follow
unsigned i2sCount : 6;
// These two bits determine the type of packet sent/received.
unsigned i2sType : 2;
} i2CmdHeader, *i2CmdHeaderPtr;
// Structure is immediately followed by the applicable data.
//---------------------------------------
// Flow Control Packets (Outbound)
//---------------------------------------
// One type of outbound command packet is so important that the entire structure
// is explicitly defined here. That is the flow-control packet. This is never
// sent by user-level code (as would be the commands to raise/lower DTR, for
// example). These are only sent by the library routines in response to reading
// incoming data into the buffers.
//
// The parameters inside the command block are maintained in place, then the
// block is sent at the appropriate time.
typedef struct _flowIn
{
i2CmdHeader hd; // Channel #, count, type (see above)
unsigned char fcmd; // The flow control command (37)
unsigned short asof; // As of byte number "asof" (LSB first!) I have room
// for "room" bytes
unsigned short room;
} flowIn, *flowInPtr;
//----------------------------------------
// (Incoming) Status Packets
//----------------------------------------
// Incoming packets which are non-data packets are status packets. In this case,
// the channel number in the header is unimportant. What follows are one or more
// sub-packets, the first word of which consists of the channel (first or low
// byte) and the status indicator (second or high byte), followed by possibly
// more data.
#define STAT_CTS_UP 0 /* CTS raised (no other bytes) */
#define STAT_CTS_DN 1 /* CTS dropped (no other bytes) */
#define STAT_DCD_UP 2 /* DCD raised (no other bytes) */
#define STAT_DCD_DN 3 /* DCD dropped (no other bytes) */
#define STAT_DSR_UP 4 /* DSR raised (no other bytes) */
#define STAT_DSR_DN 5 /* DSR dropped (no other bytes) */
#define STAT_RI_UP 6 /* RI raised (no other bytes) */
#define STAT_RI_DN 7 /* RI dropped (no other bytes) */
#define STAT_BRK_DET 8 /* BRK detect (no other bytes) */
#define STAT_FLOW 9 /* Flow control(-- more: see below */
#define STAT_BMARK 10 /* Bookmark (no other bytes)
* Bookmark is sent as a response to
* a command 60: request for bookmark
*/
#define STAT_STATUS 11 /* Special packet: see below */
#define STAT_TXCNT 12 /* Special packet: see below */
#define STAT_RXCNT 13 /* Special packet: see below */
#define STAT_BOXIDS 14 /* Special packet: see below */
#define STAT_HWFAIL 15 /* Special packet: see below */
#define STAT_MOD_ERROR 0xc0
#define STAT_MODEM 0xc0/* If status & STAT_MOD_ERROR:
* == STAT_MODEM, then this is a modem
* status packet, given in response to a
* CMD_DSS_NOW command.
* The low nibble has each data signal:
*/
#define STAT_MOD_DCD 0x8
#define STAT_MOD_RI 0x4
#define STAT_MOD_DSR 0x2
#define STAT_MOD_CTS 0x1
#define STAT_ERROR 0x80/* If status & STAT_MOD_ERROR
* == STAT_ERROR, then
* sort of error on the channel.
* The remaining seven bits indicate
* what sort of error it is.
*/
/* The low three bits indicate parity, framing, or overrun errors */
#define STAT_E_PARITY 4 /* Parity error */
#define STAT_E_FRAMING 2 /* Framing error */
#define STAT_E_OVERRUN 1 /* (uxart) overrun error */
//---------------------------------------
// STAT_FLOW packets
//---------------------------------------
typedef struct _flowStat
{
unsigned short asof;
unsigned short room;
}flowStat, *flowStatPtr;
// flowStat packets are received from the board to regulate the flow of outgoing
// data. A local copy of this structure is also kept to track the amount of
// credits used and credits remaining. "room" is the amount of space in the
// board's buffers, "as of" having received a certain byte number. When sending
// data to the fifo, you must calculate how much buffer space your packet will
// use. Add this to the current "asof" and subtract it from the current "room".
//
// The calculation for the board's buffer is given by CREDIT_USAGE, where size
// is the un-rounded count of either data characters or command characters.
// (Which is to say, the count rounded up, plus two).
#define CREDIT_USAGE(size) (((size) + 3) & ~1)
//---------------------------------------
// STAT_STATUS packets
//---------------------------------------
typedef struct _debugStat
{
unsigned char d_ccsr;
unsigned char d_txinh;
unsigned char d_stat1;
unsigned char d_stat2;
} debugStat, *debugStatPtr;
// debugStat packets are sent to the host in response to a CMD_GET_STATUS
// command. Each byte is bit-mapped as described below:
#define D_CCSR_XON 2 /* Has received XON, ready to transmit */
#define D_CCSR_XOFF 4 /* Has received XOFF, not transmitting */
#define D_CCSR_TXENAB 8 /* Transmitter is enabled */
#define D_CCSR_RXENAB 0x80 /* Receiver is enabled */
#define D_TXINH_BREAK 1 /* We are sending a break */
#define D_TXINH_EMPTY 2 /* No data to send */
#define D_TXINH_SUSP 4 /* Output suspended via command 57 */
#define D_TXINH_CMD 8 /* We are processing an in-line command */
#define D_TXINH_LCD 0x10 /* LCD diagnostics are running */
#define D_TXINH_PAUSE 0x20 /* We are processing a PAUSE command */
#define D_TXINH_DCD 0x40 /* DCD is low, preventing transmission */
#define D_TXINH_DSR 0x80 /* DSR is low, preventing transmission */
#define D_STAT1_TXEN 1 /* Transmit INTERRUPTS enabled */
#define D_STAT1_RXEN 2 /* Receiver INTERRUPTS enabled */
#define D_STAT1_MDEN 4 /* Modem (data set sigs) interrupts enabled */
#define D_STAT1_RLM 8 /* Remote loopback mode selected */
#define D_STAT1_LLM 0x10 /* Local internal loopback mode selected */
#define D_STAT1_CTS 0x20 /* CTS is low, preventing transmission */
#define D_STAT1_DTR 0x40 /* DTR is low, to stop remote transmission */
#define D_STAT1_RTS 0x80 /* RTS is low, to stop remote transmission */
#define D_STAT2_TXMT 1 /* Transmit buffers are all empty */
#define D_STAT2_RXMT 2 /* Receive buffers are all empty */
#define D_STAT2_RXINH 4 /* Loadware has tried to inhibit remote
* transmission: dropped DTR, sent XOFF,
* whatever...
*/
#define D_STAT2_RXFLO 8 /* Loadware can send no more data to host
* until it receives a flow-control packet
*/
//-----------------------------------------
// STAT_TXCNT and STAT_RXCNT packets
//----------------------------------------
typedef struct _cntStat
{
unsigned short cs_time; // (Assumes host is little-endian!)
unsigned short cs_count;
} cntStat, *cntStatPtr;
// These packets are sent in response to a CMD_GET_RXCNT or a CMD_GET_TXCNT
// bypass command. cs_time is a running 1 Millisecond counter which acts as a
// time stamp. cs_count is a running counter of data sent or received from the
// uxarts. (Not including data added by the chip itself, as with CRLF
// processing).
//------------------------------------------
// STAT_HWFAIL packets
//------------------------------------------
typedef struct _failStat
{
unsigned char fs_written;
unsigned char fs_read;
unsigned short fs_address;
} failStat, *failStatPtr;
// This packet is sent whenever the on-board diagnostic process detects an
// error. At startup, this process is dormant. The host can wake it up by
// issuing the bypass command CMD_HW_TEST. The process runs at low priority and
// performs continuous hardware verification; writing data to certain on-board
// registers, reading it back, and comparing. If it detects an error, this
// packet is sent to the host, and the process goes dormant again until the host
// sends another CMD_HW_TEST. It then continues with the next register to be
// tested.
//------------------------------------------------------------------------------
// Macros to deal with the headers more easily! Note that these are defined so
// they may be used as "left" as well as "right" expressions.
//------------------------------------------------------------------------------
// Given a pointer to the packet, reference the channel number
//
#define CHANNEL_OF(pP) ((i2DataHeaderPtr)(pP))->i2sChannel
// Given a pointer to the packet, reference the Packet type
//
#define PTYPE_OF(pP) ((i2DataHeaderPtr)(pP))->i2sType
// The possible types of packets
//
#define PTYPE_DATA 0 /* Host <--> Board */
#define PTYPE_BYPASS 1 /* Host ---> Board */
#define PTYPE_INLINE 2 /* Host ---> Board */
#define PTYPE_STATUS 2 /* Host <--- Board */
// Given a pointer to a Data packet, reference the Tag
//
#define TAG_OF(pP) ((i2DataHeaderPtr)(pP))->i2sTag
// Given a pointer to a Data packet, reference the data i.d.
//
#define ID_OF(pP) ((i2DataHeaderPtr)(pP))->i2sId
// The possible types of ID's
//
#define ID_ORDINARY_DATA 0
#define ID_HOT_KEY 1
// Given a pointer to a Data packet, reference the count
//
#define DATA_COUNT_OF(pP) ((i2DataHeaderPtr)(pP))->i2sCount
// Given a pointer to a Data packet, reference the beginning of data
//
#define DATA_OF(pP) &((unsigned char *)(pP))[4] // 4 = size of header
// Given a pointer to a Non-Data packet, reference the count
//
#define CMD_COUNT_OF(pP) ((i2CmdHeaderPtr)(pP))->i2sCount
#define MAX_CMD_PACK_SIZE 62 // Maximum size of such a count
// Given a pointer to a Non-Data packet, reference the beginning of data
//
#define CMD_OF(pP) &((unsigned char *)(pP))[2] // 2 = size of header
//--------------------------------
// MailBox Bits:
//--------------------------------
//--------------------------
// Outgoing (host to board)
//--------------------------
//
#define MB_OUT_STUFFED 0x80 // Host has placed output in fifo
#define MB_IN_STRIPPED 0x40 // Host has read in all input from fifo
//--------------------------
// Incoming (board to host)
//--------------------------
//
#define MB_IN_STUFFED 0x80 // Board has placed input in fifo
#define MB_OUT_STRIPPED 0x40 // Board has read all output from fifo
#define MB_FATAL_ERROR 0x20 // Board has encountered a fatal error
#pragma pack() // Reset padding to command-line default
#endif // I2PACK_H
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Driver constants for configuration and tuning
*
* NOTES:
*
*******************************************************************************/
#ifndef IP2_H
#define IP2_H
#include "ip2types.h"
#include "i2cmd.h"
/*************/
/* Constants */
/*************/
/* Device major numbers - since version 2.0.26. */
#define IP2_TTY_MAJOR 71
#define IP2_CALLOUT_MAJOR 72
#define IP2_IPL_MAJOR 73
/* Board configuration array.
* This array defines the hardware irq and address for up to IP2_MAX_BOARDS
* (4 supported per ip2_types.h) ISA board addresses and irqs MUST be specified,
* PCI and EISA boards are probed for and automagicly configed
* iff the addresses are set to 1 and 2 respectivily.
* 0x0100 - 0x03f0 == ISA
* 1 == PCI
* 2 == EISA
* 0 == (skip this board)
* This array defines the hardware addresses for them. Special
* addresses are EISA and PCI which go sniffing for boards.
* In a multiboard system the position in the array determines which port
* devices are assigned to each board:
* board 0 is assigned ttyF0.. to ttyF63,
* board 1 is assigned ttyF64 to ttyF127,
* board 2 is assigned ttyF128 to ttyF191,
* board 3 is assigned ttyF192 to ttyF255.
*
* In PCI and EISA bus systems each range is mapped to card in
* monotonically increasing slot number order, ISA position is as specified
* here.
* If the irqs are ALL set to 0,0,0,0 all boards operate in
* polled mode. For interrupt operation ISA boards require that the IRQ be
* specified, while PCI and EISA boards any nonzero entry
* will enable interrupts using the BIOS configured irq for the board.
* An invalid irq entry will default to polled mode for that card and print
* console warning.
* When the driver is loaded as a module these setting can be overridden on the
* modprobe command line or on an option line in /etc/modprobe.conf.
* If the driver is built-in the configuration must be
* set here for ISA cards and address set to 1 and 2 for PCI and EISA.
*
* Here is an example that shows most if not all possibe combinations:
*static ip2config_t ip2config =
*{
* {11,1,0,0}, // irqs
* { // Addresses
* 0x0308, // Board 0, ttyF0 - ttyF63// ISA card at io=0x308, irq=11
* 0x0001, // Board 1, ttyF64 - ttyF127//PCI card configured by BIOS
* 0x0000, // Board 2, ttyF128 - ttyF191// Slot skipped
* 0x0002 // Board 3, ttyF192 - ttyF255//EISA card configured by BIOS
* // but polled not irq driven
* }
*};
*/
/* this structure is zeroed out because the suggested method is to configure
* the driver as a module, set up the parameters with an options line in
* /etc/modprobe.conf and load with modprobe or kmod, the kernel
* module loader
*/
/* This structure is NOW always initialized when the driver is initialized.
* Compiled in defaults MUST be added to the io and irq arrays in
* ip2.c. Those values are configurable from insmod parameters in the
* case of modules or from command line parameters (ip2=io,irq) when
* compiled in.
*/
static ip2config_t ip2config =
{
{0,0,0,0}, // irqs
{ // Addresses
/* Do NOT set compile time defaults HERE! Use the arrays in
ip2.c! These WILL be overwritten! =mhw= */
0x0000, // Board 0, ttyF0 - ttyF63
0x0000, // Board 1, ttyF64 - ttyF127
0x0000, // Board 2, ttyF128 - ttyF191
0x0000 // Board 3, ttyF192 - ttyF255
}
};
#endif
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Driver constants for configuration and tuning
*
* NOTES:
*
*******************************************************************************/
#ifndef IP2IOCTL_H
#define IP2IOCTL_H
//*************
//* Constants *
//*************
// High baud rates (if not defined elsewhere.
#ifndef B153600
# define B153600 0010005
#endif
#ifndef B307200
# define B307200 0010006
#endif
#ifndef B921600
# define B921600 0010007
#endif
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
//
union ip2breadcrumb
{
struct {
unsigned char port, cat, codes, label;
} __attribute__ ((packed)) hdr;
unsigned long value;
};
#define ITRC_NO_PORT 0xFF
#define CHANN (pCh->port_index)
#define ITRC_ERROR '!'
#define ITRC_INIT 'A'
#define ITRC_OPEN 'B'
#define ITRC_CLOSE 'C'
#define ITRC_DRAIN 'D'
#define ITRC_IOCTL 'E'
#define ITRC_FLUSH 'F'
#define ITRC_STATUS 'G'
#define ITRC_HANGUP 'H'
#define ITRC_INTR 'I'
#define ITRC_SFLOW 'J'
#define ITRC_SBCMD 'K'
#define ITRC_SICMD 'L'
#define ITRC_MODEM 'M'
#define ITRC_INPUT 'N'
#define ITRC_OUTPUT 'O'
#define ITRC_PUTC 'P'
#define ITRC_QUEUE 'Q'
#define ITRC_STFLW 'R'
#define ITRC_SFIFO 'S'
#define ITRC_VERIFY 'V'
#define ITRC_WRITE 'W'
#define ITRC_ENTER 0x00
#define ITRC_RETURN 0xFF
#define ITRC_QUEUE_ROOM 2
#define ITRC_QUEUE_CMD 6
/*******************************************************************************
*
* (c) 1998 by Computone Corporation
*
********************************************************************************
*
*
* PACKAGE: Linux tty Device Driver for IntelliPort II family of multiport
* serial I/O controllers.
*
* DESCRIPTION: Driver constants and type definitions.
*
* NOTES:
*
*******************************************************************************/
#ifndef IP2TYPES_H
#define IP2TYPES_H
//*************
//* Constants *
//*************
// Define some limits for this driver. Ports per board is a hardware limitation
// that will not change. Current hardware limits this to 64 ports per board.
// Boards per driver is a self-imposed limit.
//
#define IP2_MAX_BOARDS 4
#define IP2_PORTS_PER_BOARD ABS_MOST_PORTS
#define IP2_MAX_PORTS (IP2_MAX_BOARDS*IP2_PORTS_PER_BOARD)
#define ISA 0
#define PCI 1
#define EISA 2
//********************
//* Type Definitions *
//********************
typedef struct tty_struct * PTTY;
typedef wait_queue_head_t PWAITQ;
typedef unsigned char UCHAR;
typedef unsigned int UINT;
typedef unsigned short USHORT;
typedef unsigned long ULONG;
typedef struct
{
short irq[IP2_MAX_BOARDS];
unsigned short addr[IP2_MAX_BOARDS];
int type[IP2_MAX_BOARDS];
#ifdef CONFIG_PCI
struct pci_dev *pci_dev[IP2_MAX_BOARDS];
#endif
} ip2config_t;
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* linux/drivers/char/riscom.c -- RISCom/8 multiport serial driver.
*
* Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
*
* This code is loosely based on the Linux serial driver, written by
* Linus Torvalds, Theodore T'so and others. The RISCom/8 card
* programming info was obtained from various drivers for other OSes
* (FreeBSD, ISC, etc), but no source code from those drivers were
* directly included in this driver.
*
*
* 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.
*
* Revision 1.1
*
* ChangeLog:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 27-Jun-2001
* - get rid of check_region and several cleanups
*/
#include <linux/module.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/serial.h>
#include <linux/fcntl.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/tty_flip.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "riscom8.h"
#include "riscom8_reg.h"
/* Am I paranoid or not ? ;-) */
#define RISCOM_PARANOIA_CHECK
/*
* Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals.
* You can slightly speed up things by #undefing the following option,
* if you are REALLY sure that your board is correct one.
*/
#define RISCOM_BRAIN_DAMAGED_CTS
/*
* The following defines are mostly for testing purposes. But if you need
* some nice reporting in your syslog, you can define them also.
*/
#undef RC_REPORT_FIFO
#undef RC_REPORT_OVERRUN
#define RISCOM_LEGAL_FLAGS \
(ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
static struct tty_driver *riscom_driver;
static DEFINE_SPINLOCK(riscom_lock);
static struct riscom_board rc_board[RC_NBOARD] = {
{
.base = RC_IOBASE1,
},
{
.base = RC_IOBASE2,
},
{
.base = RC_IOBASE3,
},
{
.base = RC_IOBASE4,
},
};
static struct riscom_port rc_port[RC_NBOARD * RC_NPORT];
/* RISCom/8 I/O ports addresses (without address translation) */
static unsigned short rc_ioport[] = {
#if 1
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c,
#else
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x10,
0x11, 0x12, 0x18, 0x28, 0x31, 0x32, 0x39, 0x3a, 0x40, 0x41, 0x61, 0x62,
0x63, 0x64, 0x6b, 0x70, 0x71, 0x78, 0x7a, 0x7b, 0x7f, 0x100, 0x101
#endif
};
#define RC_NIOPORT ARRAY_SIZE(rc_ioport)
static int rc_paranoia_check(struct riscom_port const *port,
char *name, const char *routine)
{
#ifdef RISCOM_PARANOIA_CHECK
static const char badmagic[] = KERN_INFO
"rc: Warning: bad riscom port magic number for device %s in %s\n";
static const char badinfo[] = KERN_INFO
"rc: Warning: null riscom port for device %s in %s\n";
if (!port) {
printk(badinfo, name, routine);
return 1;
}
if (port->magic != RISCOM8_MAGIC) {
printk(badmagic, name, routine);
return 1;
}
#endif
return 0;
}
/*
*
* Service functions for RISCom/8 driver.
*
*/
/* Get board number from pointer */
static inline int board_No(struct riscom_board const *bp)
{
return bp - rc_board;
}
/* Get port number from pointer */
static inline int port_No(struct riscom_port const *port)
{
return RC_PORT(port - rc_port);
}
/* Get pointer to board from pointer to port */
static inline struct riscom_board *port_Board(struct riscom_port const *port)
{
return &rc_board[RC_BOARD(port - rc_port)];
}
/* Input Byte from CL CD180 register */
static inline unsigned char rc_in(struct riscom_board const *bp,
unsigned short reg)
{
return inb(bp->base + RC_TO_ISA(reg));
}
/* Output Byte to CL CD180 register */
static inline void rc_out(struct riscom_board const *bp, unsigned short reg,
unsigned char val)
{
outb(val, bp->base + RC_TO_ISA(reg));
}
/* Wait for Channel Command Register ready */
static void rc_wait_CCR(struct riscom_board const *bp)
{
unsigned long delay;
/* FIXME: need something more descriptive then 100000 :) */
for (delay = 100000; delay; delay--)
if (!rc_in(bp, CD180_CCR))
return;
printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp));
}
/*
* RISCom/8 probe functions.
*/
static int rc_request_io_range(struct riscom_board * const bp)
{
int i;
for (i = 0; i < RC_NIOPORT; i++)
if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1,
"RISCom/8")) {
goto out_release;
}
return 0;
out_release:
printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n",
board_No(bp), bp->base);
while (--i >= 0)
release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
return 1;
}
static void rc_release_io_range(struct riscom_board * const bp)
{
int i;
for (i = 0; i < RC_NIOPORT; i++)
release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
}
/* Reset and setup CD180 chip */
static void __init rc_init_CD180(struct riscom_board const *bp)
{
unsigned long flags;
spin_lock_irqsave(&riscom_lock, flags);
rc_out(bp, RC_CTOUT, 0); /* Clear timeout */
rc_wait_CCR(bp); /* Wait for CCR ready */
rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */
spin_unlock_irqrestore(&riscom_lock, flags);
msleep(50); /* Delay 0.05 sec */
spin_lock_irqsave(&riscom_lock, flags);
rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */
rc_out(bp, CD180_GICR, 0); /* Clear all bits */
rc_out(bp, CD180_PILR1, RC_ACK_MINT); /* Prio for modem intr */
rc_out(bp, CD180_PILR2, RC_ACK_TINT); /* Prio for tx intr */
rc_out(bp, CD180_PILR3, RC_ACK_RINT); /* Prio for rx intr */
/* Setting up prescaler. We need 4 ticks per 1 ms */
rc_out(bp, CD180_PPRH, (RC_OSCFREQ/(1000000/RISCOM_TPS)) >> 8);
rc_out(bp, CD180_PPRL, (RC_OSCFREQ/(1000000/RISCOM_TPS)) & 0xff);
spin_unlock_irqrestore(&riscom_lock, flags);
}
/* Main probing routine, also sets irq. */
static int __init rc_probe(struct riscom_board *bp)
{
unsigned char val1, val2;
int irqs = 0;
int retries;
bp->irq = 0;
if (rc_request_io_range(bp))
return 1;
/* Are the I/O ports here ? */
rc_out(bp, CD180_PPRL, 0x5a);
outb(0xff, 0x80);
val1 = rc_in(bp, CD180_PPRL);
rc_out(bp, CD180_PPRL, 0xa5);
outb(0x00, 0x80);
val2 = rc_in(bp, CD180_PPRL);
if ((val1 != 0x5a) || (val2 != 0xa5)) {
printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not found.\n",
board_No(bp), bp->base);
goto out_release;
}
/* It's time to find IRQ for this board */
for (retries = 0; retries < 5 && irqs <= 0; retries++) {
irqs = probe_irq_on();
rc_init_CD180(bp); /* Reset CD180 chip */
rc_out(bp, CD180_CAR, 2); /* Select port 2 */
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_TXEN); /* Enable transmitter */
rc_out(bp, CD180_IER, IER_TXRDY);/* Enable tx empty intr */
msleep(50);
irqs = probe_irq_off(irqs);
val1 = rc_in(bp, RC_BSR); /* Get Board Status reg */
val2 = rc_in(bp, RC_ACK_TINT); /* ACK interrupt */
rc_init_CD180(bp); /* Reset CD180 again */
if ((val1 & RC_BSR_TINT) || (val2 != (RC_ID | GIVR_IT_TX))) {
printk(KERN_ERR "rc%d: RISCom/8 Board at 0x%03x not "
"found.\n", board_No(bp), bp->base);
goto out_release;
}
}
if (irqs <= 0) {
printk(KERN_ERR "rc%d: Can't find IRQ for RISCom/8 board "
"at 0x%03x.\n", board_No(bp), bp->base);
goto out_release;
}
bp->irq = irqs;
bp->flags |= RC_BOARD_PRESENT;
printk(KERN_INFO "rc%d: RISCom/8 Rev. %c board detected at "
"0x%03x, IRQ %d.\n",
board_No(bp),
(rc_in(bp, CD180_GFRCR) & 0x0f) + 'A', /* Board revision */
bp->base, bp->irq);
return 0;
out_release:
rc_release_io_range(bp);
return 1;
}
/*
*
* Interrupt processing routines.
*
*/
static struct riscom_port *rc_get_port(struct riscom_board const *bp,
unsigned char const *what)
{
unsigned char channel;
struct riscom_port *port;
channel = rc_in(bp, CD180_GICR) >> GICR_CHAN_OFF;
if (channel < CD180_NCH) {
port = &rc_port[board_No(bp) * RC_NPORT + channel];
if (port->port.flags & ASYNC_INITIALIZED)
return port;
}
printk(KERN_ERR "rc%d: %s interrupt from invalid port %d\n",
board_No(bp), what, channel);
return NULL;
}
static void rc_receive_exc(struct riscom_board const *bp)
{
struct riscom_port *port;
struct tty_struct *tty;
unsigned char status;
unsigned char ch, flag;
port = rc_get_port(bp, "Receive");
if (port == NULL)
return;
tty = tty_port_tty_get(&port->port);
#ifdef RC_REPORT_OVERRUN
status = rc_in(bp, CD180_RCSR);
if (status & RCSR_OE)
port->overrun++;
status &= port->mark_mask;
#else
status = rc_in(bp, CD180_RCSR) & port->mark_mask;
#endif
ch = rc_in(bp, CD180_RDR);
if (!status)
goto out;
if (status & RCSR_TOUT) {
printk(KERN_WARNING "rc%d: port %d: Receiver timeout. "
"Hardware problems ?\n",
board_No(bp), port_No(port));
goto out;
} else if (status & RCSR_BREAK) {
printk(KERN_INFO "rc%d: port %d: Handling break...\n",
board_No(bp), port_No(port));
flag = TTY_BREAK;
if (tty && (port->port.flags & ASYNC_SAK))
do_SAK(tty);
} else if (status & RCSR_PE)
flag = TTY_PARITY;
else if (status & RCSR_FE)
flag = TTY_FRAME;
else if (status & RCSR_OE)
flag = TTY_OVERRUN;
else
flag = TTY_NORMAL;
if (tty) {
tty_insert_flip_char(tty, ch, flag);
tty_flip_buffer_push(tty);
}
out:
tty_kref_put(tty);
}
static void rc_receive(struct riscom_board const *bp)
{
struct riscom_port *port;
struct tty_struct *tty;
unsigned char count;
port = rc_get_port(bp, "Receive");
if (port == NULL)
return;
tty = tty_port_tty_get(&port->port);
count = rc_in(bp, CD180_RDCR);
#ifdef RC_REPORT_FIFO
port->hits[count > 8 ? 9 : count]++;
#endif
while (count--) {
u8 ch = rc_in(bp, CD180_RDR);
if (tty)
tty_insert_flip_char(tty, ch, TTY_NORMAL);
}
if (tty) {
tty_flip_buffer_push(tty);
tty_kref_put(tty);
}
}
static void rc_transmit(struct riscom_board const *bp)
{
struct riscom_port *port;
struct tty_struct *tty;
unsigned char count;
port = rc_get_port(bp, "Transmit");
if (port == NULL)
return;
tty = tty_port_tty_get(&port->port);
if (port->IER & IER_TXEMPTY) {
/* FIFO drained */
rc_out(bp, CD180_CAR, port_No(port));
port->IER &= ~IER_TXEMPTY;
rc_out(bp, CD180_IER, port->IER);
goto out;
}
if ((port->xmit_cnt <= 0 && !port->break_length)
|| (tty && (tty->stopped || tty->hw_stopped))) {
rc_out(bp, CD180_CAR, port_No(port));
port->IER &= ~IER_TXRDY;
rc_out(bp, CD180_IER, port->IER);
goto out;
}
if (port->break_length) {
if (port->break_length > 0) {
if (port->COR2 & COR2_ETC) {
rc_out(bp, CD180_TDR, CD180_C_ESC);
rc_out(bp, CD180_TDR, CD180_C_SBRK);
port->COR2 &= ~COR2_ETC;
}
count = min_t(int, port->break_length, 0xff);
rc_out(bp, CD180_TDR, CD180_C_ESC);
rc_out(bp, CD180_TDR, CD180_C_DELAY);
rc_out(bp, CD180_TDR, count);
port->break_length -= count;
if (port->break_length == 0)
port->break_length--;
} else {
rc_out(bp, CD180_TDR, CD180_C_ESC);
rc_out(bp, CD180_TDR, CD180_C_EBRK);
rc_out(bp, CD180_COR2, port->COR2);
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_CORCHG2);
port->break_length = 0;
}
goto out;
}
count = CD180_NFIFO;
do {
rc_out(bp, CD180_TDR, port->port.xmit_buf[port->xmit_tail++]);
port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
if (--port->xmit_cnt <= 0)
break;
} while (--count > 0);
if (port->xmit_cnt <= 0) {
rc_out(bp, CD180_CAR, port_No(port));
port->IER &= ~IER_TXRDY;
rc_out(bp, CD180_IER, port->IER);
}
if (tty && port->xmit_cnt <= port->wakeup_chars)
tty_wakeup(tty);
out:
tty_kref_put(tty);
}
static void rc_check_modem(struct riscom_board const *bp)
{
struct riscom_port *port;
struct tty_struct *tty;
unsigned char mcr;
port = rc_get_port(bp, "Modem");
if (port == NULL)
return;
tty = tty_port_tty_get(&port->port);
mcr = rc_in(bp, CD180_MCR);
if (mcr & MCR_CDCHG) {
if (rc_in(bp, CD180_MSVR) & MSVR_CD)
wake_up_interruptible(&port->port.open_wait);
else if (tty)
tty_hangup(tty);
}
#ifdef RISCOM_BRAIN_DAMAGED_CTS
if (mcr & MCR_CTSCHG) {
if (rc_in(bp, CD180_MSVR) & MSVR_CTS) {
port->IER |= IER_TXRDY;
if (tty) {
tty->hw_stopped = 0;
if (port->xmit_cnt <= port->wakeup_chars)
tty_wakeup(tty);
}
} else {
if (tty)
tty->hw_stopped = 1;
port->IER &= ~IER_TXRDY;
}
rc_out(bp, CD180_IER, port->IER);
}
if (mcr & MCR_DSRCHG) {
if (rc_in(bp, CD180_MSVR) & MSVR_DSR) {
port->IER |= IER_TXRDY;
if (tty) {
tty->hw_stopped = 0;
if (port->xmit_cnt <= port->wakeup_chars)
tty_wakeup(tty);
}
} else {
if (tty)
tty->hw_stopped = 1;
port->IER &= ~IER_TXRDY;
}
rc_out(bp, CD180_IER, port->IER);
}
#endif /* RISCOM_BRAIN_DAMAGED_CTS */
/* Clear change bits */
rc_out(bp, CD180_MCR, 0);
tty_kref_put(tty);
}
/* The main interrupt processing routine */
static irqreturn_t rc_interrupt(int dummy, void *dev_id)
{
unsigned char status;
unsigned char ack;
struct riscom_board *bp = dev_id;
unsigned long loop = 0;
int handled = 0;
if (!(bp->flags & RC_BOARD_ACTIVE))
return IRQ_NONE;
while ((++loop < 16) && ((status = ~(rc_in(bp, RC_BSR))) &
(RC_BSR_TOUT | RC_BSR_TINT |
RC_BSR_MINT | RC_BSR_RINT))) {
handled = 1;
if (status & RC_BSR_TOUT)
printk(KERN_WARNING "rc%d: Got timeout. Hardware "
"error?\n", board_No(bp));
else if (status & RC_BSR_RINT) {
ack = rc_in(bp, RC_ACK_RINT);
if (ack == (RC_ID | GIVR_IT_RCV))
rc_receive(bp);
else if (ack == (RC_ID | GIVR_IT_REXC))
rc_receive_exc(bp);
else
printk(KERN_WARNING "rc%d: Bad receive ack "
"0x%02x.\n",
board_No(bp), ack);
} else if (status & RC_BSR_TINT) {
ack = rc_in(bp, RC_ACK_TINT);
if (ack == (RC_ID | GIVR_IT_TX))
rc_transmit(bp);
else
printk(KERN_WARNING "rc%d: Bad transmit ack "
"0x%02x.\n",
board_No(bp), ack);
} else /* if (status & RC_BSR_MINT) */ {
ack = rc_in(bp, RC_ACK_MINT);
if (ack == (RC_ID | GIVR_IT_MODEM))
rc_check_modem(bp);
else
printk(KERN_WARNING "rc%d: Bad modem ack "
"0x%02x.\n",
board_No(bp), ack);
}
rc_out(bp, CD180_EOIR, 0); /* Mark end of interrupt */
rc_out(bp, RC_CTOUT, 0); /* Clear timeout flag */
}
return IRQ_RETVAL(handled);
}
/*
* Routines for open & close processing.
*/
/* Called with disabled interrupts */
static int rc_setup_board(struct riscom_board *bp)
{
int error;
if (bp->flags & RC_BOARD_ACTIVE)
return 0;
error = request_irq(bp->irq, rc_interrupt, IRQF_DISABLED,
"RISCom/8", bp);
if (error)
return error;
rc_out(bp, RC_CTOUT, 0); /* Just in case */
bp->DTR = ~0;
rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */
bp->flags |= RC_BOARD_ACTIVE;
return 0;
}
/* Called with disabled interrupts */
static void rc_shutdown_board(struct riscom_board *bp)
{
if (!(bp->flags & RC_BOARD_ACTIVE))
return;
bp->flags &= ~RC_BOARD_ACTIVE;
free_irq(bp->irq, NULL);
bp->DTR = ~0;
rc_out(bp, RC_DTR, bp->DTR); /* Drop DTR on all ports */
}
/*
* Setting up port characteristics.
* Must be called with disabled interrupts
*/
static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp,
struct riscom_port *port)
{
unsigned long baud;
long tmp;
unsigned char cor1 = 0, cor3 = 0;
unsigned char mcor1 = 0, mcor2 = 0;
port->IER = 0;
port->COR2 = 0;
port->MSVR = MSVR_RTS;
baud = tty_get_baud_rate(tty);
/* Select port on the board */
rc_out(bp, CD180_CAR, port_No(port));
if (!baud) {
/* Drop DTR & exit */
bp->DTR |= (1u << port_No(port));
rc_out(bp, RC_DTR, bp->DTR);
return;
} else {
/* Set DTR on */
bp->DTR &= ~(1u << port_No(port));
rc_out(bp, RC_DTR, bp->DTR);
}
/*
* Now we must calculate some speed depended things
*/
/* Set baud rate for port */
tmp = (((RC_OSCFREQ + baud/2) / baud +
CD180_TPC/2) / CD180_TPC);
rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff);
rc_out(bp, CD180_TBPRH, (tmp >> 8) & 0xff);
rc_out(bp, CD180_RBPRL, tmp & 0xff);
rc_out(bp, CD180_TBPRL, tmp & 0xff);
baud = (baud + 5) / 10; /* Estimated CPS */
/* Two timer ticks seems enough to wakeup something like SLIP driver */
tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO;
port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
SERIAL_XMIT_SIZE - 1 : tmp);
/* Receiver timeout will be transmission time for 1.5 chars */
tmp = (RISCOM_TPS + RISCOM_TPS/2 + baud/2) / baud;
tmp = (tmp > 0xff) ? 0xff : tmp;
rc_out(bp, CD180_RTPR, tmp);
switch (C_CSIZE(tty)) {
case CS5:
cor1 |= COR1_5BITS;
break;
case CS6:
cor1 |= COR1_6BITS;
break;
case CS7:
cor1 |= COR1_7BITS;
break;
case CS8:
cor1 |= COR1_8BITS;
break;
}
if (C_CSTOPB(tty))
cor1 |= COR1_2SB;
cor1 |= COR1_IGNORE;
if (C_PARENB(tty)) {
cor1 |= COR1_NORMPAR;
if (C_PARODD(tty))
cor1 |= COR1_ODDP;
if (I_INPCK(tty))
cor1 &= ~COR1_IGNORE;
}
/* Set marking of some errors */
port->mark_mask = RCSR_OE | RCSR_TOUT;
if (I_INPCK(tty))
port->mark_mask |= RCSR_FE | RCSR_PE;
if (I_BRKINT(tty) || I_PARMRK(tty))
port->mark_mask |= RCSR_BREAK;
if (I_IGNPAR(tty))
port->mark_mask &= ~(RCSR_FE | RCSR_PE);
if (I_IGNBRK(tty)) {
port->mark_mask &= ~RCSR_BREAK;
if (I_IGNPAR(tty))
/* Real raw mode. Ignore all */
port->mark_mask &= ~RCSR_OE;
}
/* Enable Hardware Flow Control */
if (C_CRTSCTS(tty)) {
#ifdef RISCOM_BRAIN_DAMAGED_CTS
port->IER |= IER_DSR | IER_CTS;
mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
tty->hw_stopped = !(rc_in(bp, CD180_MSVR) &
(MSVR_CTS|MSVR_DSR));
#else
port->COR2 |= COR2_CTSAE;
#endif
}
/* Enable Software Flow Control. FIXME: I'm not sure about this */
/* Some people reported that it works, but I still doubt */
if (I_IXON(tty)) {
port->COR2 |= COR2_TXIBE;
cor3 |= (COR3_FCT | COR3_SCDE);
if (I_IXANY(tty))
port->COR2 |= COR2_IXM;
rc_out(bp, CD180_SCHR1, START_CHAR(tty));
rc_out(bp, CD180_SCHR2, STOP_CHAR(tty));
rc_out(bp, CD180_SCHR3, START_CHAR(tty));
rc_out(bp, CD180_SCHR4, STOP_CHAR(tty));
}
if (!C_CLOCAL(tty)) {
/* Enable CD check */
port->IER |= IER_CD;
mcor1 |= MCOR1_CDZD;
mcor2 |= MCOR2_CDOD;
}
if (C_CREAD(tty))
/* Enable receiver */
port->IER |= IER_RXD;
/* Set input FIFO size (1-8 bytes) */
cor3 |= RISCOM_RXFIFO;
/* Setting up CD180 channel registers */
rc_out(bp, CD180_COR1, cor1);
rc_out(bp, CD180_COR2, port->COR2);
rc_out(bp, CD180_COR3, cor3);
/* Make CD180 know about registers change */
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
/* Setting up modem option registers */
rc_out(bp, CD180_MCOR1, mcor1);
rc_out(bp, CD180_MCOR2, mcor2);
/* Enable CD180 transmitter & receiver */
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_TXEN | CCR_RXEN);
/* Enable interrupts */
rc_out(bp, CD180_IER, port->IER);
/* And finally set RTS on */
rc_out(bp, CD180_MSVR, port->MSVR);
}
/* Must be called with interrupts enabled */
static int rc_activate_port(struct tty_port *port, struct tty_struct *tty)
{
struct riscom_port *rp = container_of(port, struct riscom_port, port);
struct riscom_board *bp = port_Board(rp);
unsigned long flags;
if (tty_port_alloc_xmit_buf(port) < 0)
return -ENOMEM;
spin_lock_irqsave(&riscom_lock, flags);
clear_bit(TTY_IO_ERROR, &tty->flags);
bp->count++;
rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0;
rc_change_speed(tty, bp, rp);
spin_unlock_irqrestore(&riscom_lock, flags);
return 0;
}
/* Must be called with interrupts disabled */
static void rc_shutdown_port(struct tty_struct *tty,
struct riscom_board *bp, struct riscom_port *port)
{
#ifdef RC_REPORT_OVERRUN
printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n",
board_No(bp), port_No(port), port->overrun);
#endif
#ifdef RC_REPORT_FIFO
{
int i;
printk(KERN_INFO "rc%d: port %d: FIFO hits [ ",
board_No(bp), port_No(port));
for (i = 0; i < 10; i++)
printk("%ld ", port->hits[i]);
printk("].\n");
}
#endif
tty_port_free_xmit_buf(&port->port);
/* Select port */
rc_out(bp, CD180_CAR, port_No(port));
/* Reset port */
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_SOFTRESET);
/* Disable all interrupts from this port */
port->IER = 0;
rc_out(bp, CD180_IER, port->IER);
set_bit(TTY_IO_ERROR, &tty->flags);
if (--bp->count < 0) {
printk(KERN_INFO "rc%d: rc_shutdown_port: "
"bad board count: %d\n",
board_No(bp), bp->count);
bp->count = 0;
}
/*
* If this is the last opened port on the board
* shutdown whole board
*/
if (!bp->count)
rc_shutdown_board(bp);
}
static int carrier_raised(struct tty_port *port)
{
struct riscom_port *p = container_of(port, struct riscom_port, port);
struct riscom_board *bp = port_Board(p);
unsigned long flags;
int CD;
spin_lock_irqsave(&riscom_lock, flags);
rc_out(bp, CD180_CAR, port_No(p));
CD = rc_in(bp, CD180_MSVR) & MSVR_CD;
rc_out(bp, CD180_MSVR, MSVR_RTS);
bp->DTR &= ~(1u << port_No(p));
rc_out(bp, RC_DTR, bp->DTR);
spin_unlock_irqrestore(&riscom_lock, flags);
return CD;
}
static void dtr_rts(struct tty_port *port, int onoff)
{
struct riscom_port *p = container_of(port, struct riscom_port, port);
struct riscom_board *bp = port_Board(p);
unsigned long flags;
spin_lock_irqsave(&riscom_lock, flags);
bp->DTR &= ~(1u << port_No(p));
if (onoff == 0)
bp->DTR |= (1u << port_No(p));
rc_out(bp, RC_DTR, bp->DTR);
spin_unlock_irqrestore(&riscom_lock, flags);
}
static int rc_open(struct tty_struct *tty, struct file *filp)
{
int board;
int error;
struct riscom_port *port;
struct riscom_board *bp;
board = RC_BOARD(tty->index);
if (board >= RC_NBOARD || !(rc_board[board].flags & RC_BOARD_PRESENT))
return -ENODEV;
bp = &rc_board[board];
port = rc_port + board * RC_NPORT + RC_PORT(tty->index);
if (rc_paranoia_check(port, tty->name, "rc_open"))
return -ENODEV;
error = rc_setup_board(bp);
if (error)
return error;
tty->driver_data = port;
return tty_port_open(&port->port, tty, filp);
}
static void rc_flush_buffer(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_flush_buffer"))
return;
spin_lock_irqsave(&riscom_lock, flags);
port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
spin_unlock_irqrestore(&riscom_lock, flags);
tty_wakeup(tty);
}
static void rc_close_port(struct tty_port *port)
{
unsigned long flags;
struct riscom_port *rp = container_of(port, struct riscom_port, port);
struct riscom_board *bp = port_Board(rp);
unsigned long timeout;
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
spin_lock_irqsave(&riscom_lock, flags);
rp->IER &= ~IER_RXD;
rp->IER &= ~IER_TXRDY;
rp->IER |= IER_TXEMPTY;
rc_out(bp, CD180_CAR, port_No(rp));
rc_out(bp, CD180_IER, rp->IER);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
timeout = jiffies + HZ;
while (rp->IER & IER_TXEMPTY) {
spin_unlock_irqrestore(&riscom_lock, flags);
msleep_interruptible(jiffies_to_msecs(rp->timeout));
spin_lock_irqsave(&riscom_lock, flags);
if (time_after(jiffies, timeout))
break;
}
rc_shutdown_port(port->tty, bp, rp);
spin_unlock_irqrestore(&riscom_lock, flags);
}
static void rc_close(struct tty_struct *tty, struct file *filp)
{
struct riscom_port *port = tty->driver_data;
if (!port || rc_paranoia_check(port, tty->name, "close"))
return;
tty_port_close(&port->port, tty, filp);
}
static int rc_write(struct tty_struct *tty,
const unsigned char *buf, int count)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp;
int c, total = 0;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_write"))
return 0;
bp = port_Board(port);
while (1) {
spin_lock_irqsave(&riscom_lock, flags);
c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
SERIAL_XMIT_SIZE - port->xmit_head));
if (c <= 0)
break; /* lock continues to be held */
memcpy(port->port.xmit_buf + port->xmit_head, buf, c);
port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
port->xmit_cnt += c;
spin_unlock_irqrestore(&riscom_lock, flags);
buf += c;
count -= c;
total += c;
}
if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
!(port->IER & IER_TXRDY)) {
port->IER |= IER_TXRDY;
rc_out(bp, CD180_CAR, port_No(port));
rc_out(bp, CD180_IER, port->IER);
}
spin_unlock_irqrestore(&riscom_lock, flags);
return total;
}
static int rc_put_char(struct tty_struct *tty, unsigned char ch)
{
struct riscom_port *port = tty->driver_data;
unsigned long flags;
int ret = 0;
if (rc_paranoia_check(port, tty->name, "rc_put_char"))
return 0;
spin_lock_irqsave(&riscom_lock, flags);
if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1)
goto out;
port->port.xmit_buf[port->xmit_head++] = ch;
port->xmit_head &= SERIAL_XMIT_SIZE - 1;
port->xmit_cnt++;
ret = 1;
out:
spin_unlock_irqrestore(&riscom_lock, flags);
return ret;
}
static void rc_flush_chars(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_flush_chars"))
return;
if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped)
return;
spin_lock_irqsave(&riscom_lock, flags);
port->IER |= IER_TXRDY;
rc_out(port_Board(port), CD180_CAR, port_No(port));
rc_out(port_Board(port), CD180_IER, port->IER);
spin_unlock_irqrestore(&riscom_lock, flags);
}
static int rc_write_room(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
int ret;
if (rc_paranoia_check(port, tty->name, "rc_write_room"))
return 0;
ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
}
static int rc_chars_in_buffer(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
if (rc_paranoia_check(port, tty->name, "rc_chars_in_buffer"))
return 0;
return port->xmit_cnt;
}
static int rc_tiocmget(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp;
unsigned char status;
unsigned int result;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, __func__))
return -ENODEV;
bp = port_Board(port);
spin_lock_irqsave(&riscom_lock, flags);
rc_out(bp, CD180_CAR, port_No(port));
status = rc_in(bp, CD180_MSVR);
result = rc_in(bp, RC_RI) & (1u << port_No(port)) ? 0 : TIOCM_RNG;
spin_unlock_irqrestore(&riscom_lock, flags);
result |= ((status & MSVR_RTS) ? TIOCM_RTS : 0)
| ((status & MSVR_DTR) ? TIOCM_DTR : 0)
| ((status & MSVR_CD) ? TIOCM_CAR : 0)
| ((status & MSVR_DSR) ? TIOCM_DSR : 0)
| ((status & MSVR_CTS) ? TIOCM_CTS : 0);
return result;
}
static int rc_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct riscom_port *port = tty->driver_data;
unsigned long flags;
struct riscom_board *bp;
if (rc_paranoia_check(port, tty->name, __func__))
return -ENODEV;
bp = port_Board(port);
spin_lock_irqsave(&riscom_lock, flags);
if (set & TIOCM_RTS)
port->MSVR |= MSVR_RTS;
if (set & TIOCM_DTR)
bp->DTR &= ~(1u << port_No(port));
if (clear & TIOCM_RTS)
port->MSVR &= ~MSVR_RTS;
if (clear & TIOCM_DTR)
bp->DTR |= (1u << port_No(port));
rc_out(bp, CD180_CAR, port_No(port));
rc_out(bp, CD180_MSVR, port->MSVR);
rc_out(bp, RC_DTR, bp->DTR);
spin_unlock_irqrestore(&riscom_lock, flags);
return 0;
}
static int rc_send_break(struct tty_struct *tty, int length)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp = port_Board(port);
unsigned long flags;
if (length == 0 || length == -1)
return -EOPNOTSUPP;
spin_lock_irqsave(&riscom_lock, flags);
port->break_length = RISCOM_TPS / HZ * length;
port->COR2 |= COR2_ETC;
port->IER |= IER_TXRDY;
rc_out(bp, CD180_CAR, port_No(port));
rc_out(bp, CD180_COR2, port->COR2);
rc_out(bp, CD180_IER, port->IER);
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_CORCHG2);
rc_wait_CCR(bp);
spin_unlock_irqrestore(&riscom_lock, flags);
return 0;
}
static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port,
struct serial_struct __user *newinfo)
{
struct serial_struct tmp;
struct riscom_board *bp = port_Board(port);
int change_speed;
if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
return -EFAULT;
mutex_lock(&port->port.mutex);
change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
(tmp.flags & ASYNC_SPD_MASK));
if (!capable(CAP_SYS_ADMIN)) {
if ((tmp.close_delay != port->port.close_delay) ||
(tmp.closing_wait != port->port.closing_wait) ||
((tmp.flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
(tmp.flags & ASYNC_USR_MASK));
} else {
port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
(tmp.flags & ASYNC_FLAGS));
port->port.close_delay = tmp.close_delay;
port->port.closing_wait = tmp.closing_wait;
}
if (change_speed) {
unsigned long flags;
spin_lock_irqsave(&riscom_lock, flags);
rc_change_speed(tty, bp, port);
spin_unlock_irqrestore(&riscom_lock, flags);
}
mutex_unlock(&port->port.mutex);
return 0;
}
static int rc_get_serial_info(struct riscom_port *port,
struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
struct riscom_board *bp = port_Board(port);
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_CIRRUS;
tmp.line = port - rc_port;
mutex_lock(&port->port.mutex);
tmp.port = bp->base;
tmp.irq = bp->irq;
tmp.flags = port->port.flags;
tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
tmp.close_delay = port->port.close_delay * HZ/100;
tmp.closing_wait = port->port.closing_wait * HZ/100;
mutex_unlock(&port->port.mutex);
tmp.xmit_fifo_size = CD180_NFIFO;
return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
static int rc_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct riscom_port *port = tty->driver_data;
void __user *argp = (void __user *)arg;
int retval;
if (rc_paranoia_check(port, tty->name, "rc_ioctl"))
return -ENODEV;
switch (cmd) {
case TIOCGSERIAL:
retval = rc_get_serial_info(port, argp);
break;
case TIOCSSERIAL:
retval = rc_set_serial_info(tty, port, argp);
break;
default:
retval = -ENOIOCTLCMD;
}
return retval;
}
static void rc_throttle(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_throttle"))
return;
bp = port_Board(port);
spin_lock_irqsave(&riscom_lock, flags);
port->MSVR &= ~MSVR_RTS;
rc_out(bp, CD180_CAR, port_No(port));
if (I_IXOFF(tty)) {
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_SSCH2);
rc_wait_CCR(bp);
}
rc_out(bp, CD180_MSVR, port->MSVR);
spin_unlock_irqrestore(&riscom_lock, flags);
}
static void rc_unthrottle(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_unthrottle"))
return;
bp = port_Board(port);
spin_lock_irqsave(&riscom_lock, flags);
port->MSVR |= MSVR_RTS;
rc_out(bp, CD180_CAR, port_No(port));
if (I_IXOFF(tty)) {
rc_wait_CCR(bp);
rc_out(bp, CD180_CCR, CCR_SSCH1);
rc_wait_CCR(bp);
}
rc_out(bp, CD180_MSVR, port->MSVR);
spin_unlock_irqrestore(&riscom_lock, flags);
}
static void rc_stop(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_stop"))
return;
bp = port_Board(port);
spin_lock_irqsave(&riscom_lock, flags);
port->IER &= ~IER_TXRDY;
rc_out(bp, CD180_CAR, port_No(port));
rc_out(bp, CD180_IER, port->IER);
spin_unlock_irqrestore(&riscom_lock, flags);
}
static void rc_start(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
struct riscom_board *bp;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_start"))
return;
bp = port_Board(port);
spin_lock_irqsave(&riscom_lock, flags);
if (port->xmit_cnt && port->port.xmit_buf && !(port->IER & IER_TXRDY)) {
port->IER |= IER_TXRDY;
rc_out(bp, CD180_CAR, port_No(port));
rc_out(bp, CD180_IER, port->IER);
}
spin_unlock_irqrestore(&riscom_lock, flags);
}
static void rc_hangup(struct tty_struct *tty)
{
struct riscom_port *port = tty->driver_data;
if (rc_paranoia_check(port, tty->name, "rc_hangup"))
return;
tty_port_hangup(&port->port);
}
static void rc_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct riscom_port *port = tty->driver_data;
unsigned long flags;
if (rc_paranoia_check(port, tty->name, "rc_set_termios"))
return;
spin_lock_irqsave(&riscom_lock, flags);
rc_change_speed(tty, port_Board(port), port);
spin_unlock_irqrestore(&riscom_lock, flags);
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
rc_start(tty);
}
}
static const struct tty_operations riscom_ops = {
.open = rc_open,
.close = rc_close,
.write = rc_write,
.put_char = rc_put_char,
.flush_chars = rc_flush_chars,
.write_room = rc_write_room,
.chars_in_buffer = rc_chars_in_buffer,
.flush_buffer = rc_flush_buffer,
.ioctl = rc_ioctl,
.throttle = rc_throttle,
.unthrottle = rc_unthrottle,
.set_termios = rc_set_termios,
.stop = rc_stop,
.start = rc_start,
.hangup = rc_hangup,
.tiocmget = rc_tiocmget,
.tiocmset = rc_tiocmset,
.break_ctl = rc_send_break,
};
static const struct tty_port_operations riscom_port_ops = {
.carrier_raised = carrier_raised,
.dtr_rts = dtr_rts,
.shutdown = rc_close_port,
.activate = rc_activate_port,
};
static int __init rc_init_drivers(void)
{
int error;
int i;
riscom_driver = alloc_tty_driver(RC_NBOARD * RC_NPORT);
if (!riscom_driver)
return -ENOMEM;
riscom_driver->owner = THIS_MODULE;
riscom_driver->name = "ttyL";
riscom_driver->major = RISCOM8_NORMAL_MAJOR;
riscom_driver->type = TTY_DRIVER_TYPE_SERIAL;
riscom_driver->subtype = SERIAL_TYPE_NORMAL;
riscom_driver->init_termios = tty_std_termios;
riscom_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
riscom_driver->init_termios.c_ispeed = 9600;
riscom_driver->init_termios.c_ospeed = 9600;
riscom_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
tty_set_operations(riscom_driver, &riscom_ops);
error = tty_register_driver(riscom_driver);
if (error != 0) {
put_tty_driver(riscom_driver);
printk(KERN_ERR "rc: Couldn't register RISCom/8 driver, "
"error = %d\n", error);
return 1;
}
memset(rc_port, 0, sizeof(rc_port));
for (i = 0; i < RC_NPORT * RC_NBOARD; i++) {
tty_port_init(&rc_port[i].port);
rc_port[i].port.ops = &riscom_port_ops;
rc_port[i].magic = RISCOM8_MAGIC;
}
return 0;
}
static void rc_release_drivers(void)
{
tty_unregister_driver(riscom_driver);
put_tty_driver(riscom_driver);
}
#ifndef MODULE
/*
* Called at boot time.
*
* You can specify IO base for up to RC_NBOARD cards,
* using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt.
* Note that there will be no probing at default
* addresses in this case.
*
*/
static int __init riscom8_setup(char *str)
{
int ints[RC_NBOARD];
int i;
str = get_options(str, ARRAY_SIZE(ints), ints);
for (i = 0; i < RC_NBOARD; i++) {
if (i < ints[0])
rc_board[i].base = ints[i+1];
else
rc_board[i].base = 0;
}
return 1;
}
__setup("riscom8=", riscom8_setup);
#endif
static char banner[] __initdata =
KERN_INFO "rc: SDL RISCom/8 card driver v1.1, (c) D.Gorodchanin "
"1994-1996.\n";
static char no_boards_msg[] __initdata =
KERN_INFO "rc: No RISCom/8 boards detected.\n";
/*
* This routine must be called by kernel at boot time
*/
static int __init riscom8_init(void)
{
int i;
int found = 0;
printk(banner);
if (rc_init_drivers())
return -EIO;
for (i = 0; i < RC_NBOARD; i++)
if (rc_board[i].base && !rc_probe(&rc_board[i]))
found++;
if (!found) {
rc_release_drivers();
printk(no_boards_msg);
return -EIO;
}
return 0;
}
#ifdef MODULE
static int iobase;
static int iobase1;
static int iobase2;
static int iobase3;
module_param(iobase, int, 0);
module_param(iobase1, int, 0);
module_param(iobase2, int, 0);
module_param(iobase3, int, 0);
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(RISCOM8_NORMAL_MAJOR);
#endif /* MODULE */
/*
* You can setup up to 4 boards (current value of RC_NBOARD)
* by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
*
*/
static int __init riscom8_init_module(void)
{
#ifdef MODULE
int i;
if (iobase || iobase1 || iobase2 || iobase3) {
for (i = 0; i < RC_NBOARD; i++)
rc_board[i].base = 0;
}
if (iobase)
rc_board[0].base = iobase;
if (iobase1)
rc_board[1].base = iobase1;
if (iobase2)
rc_board[2].base = iobase2;
if (iobase3)
rc_board[3].base = iobase3;
#endif /* MODULE */
return riscom8_init();
}
static void __exit riscom8_exit_module(void)
{
int i;
rc_release_drivers();
for (i = 0; i < RC_NBOARD; i++)
if (rc_board[i].flags & RC_BOARD_PRESENT)
rc_release_io_range(&rc_board[i]);
}
module_init(riscom8_init_module);
module_exit(riscom8_exit_module);
/*
* linux/drivers/char/riscom8.h -- RISCom/8 multiport serial driver.
*
* Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
*
* This code is loosely based on the Linux serial driver, written by
* Linus Torvalds, Theodore T'so and others. The RISCom/8 card
* programming info was obtained from various drivers for other OSes
* (FreeBSD, ISC, etc), but no source code from those drivers were
* directly included in this driver.
*
*
* 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.
*/
#ifndef __LINUX_RISCOM8_H
#define __LINUX_RISCOM8_H
#include <linux/serial.h>
#ifdef __KERNEL__
#define RC_NBOARD 4
/* NOTE: RISCom decoder recognizes 16 addresses... */
#define RC_NPORT 8
#define RC_BOARD(line) (((line) >> 3) & 0x07)
#define RC_PORT(line) ((line) & (RC_NPORT - 1))
/* Ticks per sec. Used for setting receiver timeout and break length */
#define RISCOM_TPS 4000
/* Yeah, after heavy testing I decided it must be 6.
* Sure, You can change it if needed.
*/
#define RISCOM_RXFIFO 6 /* Max. receiver FIFO size (1-8) */
#define RISCOM8_MAGIC 0x0907
#define RC_IOBASE1 0x220
#define RC_IOBASE2 0x240
#define RC_IOBASE3 0x250
#define RC_IOBASE4 0x260
struct riscom_board {
unsigned long flags;
unsigned short base;
unsigned char irq;
signed char count;
unsigned char DTR;
};
#define RC_BOARD_PRESENT 0x00000001
#define RC_BOARD_ACTIVE 0x00000002
struct riscom_port {
int magic;
struct tty_port port;
int baud_base;
int timeout;
int custom_divisor;
int xmit_head;
int xmit_tail;
int xmit_cnt;
short wakeup_chars;
short break_length;
unsigned char mark_mask;
unsigned char IER;
unsigned char MSVR;
unsigned char COR2;
#ifdef RC_REPORT_OVERRUN
unsigned long overrun;
#endif
#ifdef RC_REPORT_FIFO
unsigned long hits[10];
#endif
};
#endif /* __KERNEL__ */
#endif /* __LINUX_RISCOM8_H */
/*
* linux/drivers/char/riscom8_reg.h -- RISCom/8 multiport serial driver.
*/
/*
* Definitions for RISCom/8 Async Mux card by SDL Communications, Inc.
*/
/*
* Address mapping between Cirrus Logic CD180 chip internal registers
* and ISA port addresses:
*
* CL-CD180 A6 A5 A4 A3 A2 A1 A0
* ISA A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
*/
#define RC_TO_ISA(r) ((((r)&0x07)<<1) | (((r)&~0x07)<<7))
/* RISCom/8 On-Board Registers (assuming address translation) */
#define RC_RI 0x100 /* Ring Indicator Register (R/O) */
#define RC_DTR 0x100 /* DTR Register (W/O) */
#define RC_BSR 0x101 /* Board Status Register (R/O) */
#define RC_CTOUT 0x101 /* Clear Timeout (W/O) */
/* Board Status Register */
#define RC_BSR_TOUT 0x08 /* Hardware Timeout */
#define RC_BSR_RINT 0x04 /* Receiver Interrupt */
#define RC_BSR_TINT 0x02 /* Transmitter Interrupt */
#define RC_BSR_MINT 0x01 /* Modem Ctl Interrupt */
/* On-board oscillator frequency (in Hz) */
#define RC_OSCFREQ 9830400
/* Values of choice for Interrupt ACKs */
#define RC_ACK_MINT 0x81 /* goes to PILR1 */
#define RC_ACK_RINT 0x82 /* goes to PILR3 */
#define RC_ACK_TINT 0x84 /* goes to PILR2 */
/* Chip ID (sorry, only one chip now) */
#define RC_ID 0x10
/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */
#define CD180_NCH 8 /* Total number of channels */
#define CD180_TPC 16 /* Ticks per character */
#define CD180_NFIFO 8 /* TX FIFO size */
/* Global registers */
#define CD180_GIVR 0x40 /* Global Interrupt Vector Register */
#define CD180_GICR 0x41 /* Global Interrupting Channel Register */
#define CD180_PILR1 0x61 /* Priority Interrupt Level Register 1 */
#define CD180_PILR2 0x62 /* Priority Interrupt Level Register 2 */
#define CD180_PILR3 0x63 /* Priority Interrupt Level Register 3 */
#define CD180_CAR 0x64 /* Channel Access Register */
#define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */
#define CD180_PPRH 0x70 /* Prescaler Period Register High */
#define CD180_PPRL 0x71 /* Prescaler Period Register Low */
#define CD180_RDR 0x78 /* Receiver Data Register */
#define CD180_RCSR 0x7a /* Receiver Character Status Register */
#define CD180_TDR 0x7b /* Transmit Data Register */
#define CD180_EOIR 0x7f /* End of Interrupt Register */
/* Channel Registers */
#define CD180_CCR 0x01 /* Channel Command Register */
#define CD180_IER 0x02 /* Interrupt Enable Register */
#define CD180_COR1 0x03 /* Channel Option Register 1 */
#define CD180_COR2 0x04 /* Channel Option Register 2 */
#define CD180_COR3 0x05 /* Channel Option Register 3 */
#define CD180_CCSR 0x06 /* Channel Control Status Register */
#define CD180_RDCR 0x07 /* Receive Data Count Register */
#define CD180_SCHR1 0x09 /* Special Character Register 1 */
#define CD180_SCHR2 0x0a /* Special Character Register 2 */
#define CD180_SCHR3 0x0b /* Special Character Register 3 */
#define CD180_SCHR4 0x0c /* Special Character Register 4 */
#define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */
#define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */
#define CD180_MCR 0x12 /* Modem Change Register */
#define CD180_RTPR 0x18 /* Receive Timeout Period Register */
#define CD180_MSVR 0x28 /* Modem Signal Value Register */
#define CD180_RBPRH 0x31 /* Receive Baud Rate Period Register High */
#define CD180_RBPRL 0x32 /* Receive Baud Rate Period Register Low */
#define CD180_TBPRH 0x39 /* Transmit Baud Rate Period Register High */
#define CD180_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */
/* Global Interrupt Vector Register (R/W) */
#define GIVR_ITMASK 0x07 /* Interrupt type mask */
#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */
#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */
#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */
#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */
/* Global Interrupt Channel Register (R/W) */
#define GICR_CHAN 0x1c /* Channel Number Mask */
#define GICR_CHAN_OFF 2 /* Channel Number Offset */
/* Channel Address Register (R/W) */
#define CAR_CHAN 0x07 /* Channel Number Mask */
#define CAR_A7 0x08 /* A7 Address Extension (unused) */
/* Receive Character Status Register (R/O) */
#define RCSR_TOUT 0x80 /* Rx Timeout */
#define RCSR_SCDET 0x70 /* Special Character Detected Mask */
#define RCSR_NO_SC 0x00 /* No Special Characters Detected */
#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */
#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */
#define RCSR_SC_3 0x30 /* Special Char 3 Detected */
#define RCSR_SC_4 0x40 /* Special Char 4 Detected */
#define RCSR_BREAK 0x08 /* Break has been detected */
#define RCSR_PE 0x04 /* Parity Error */
#define RCSR_FE 0x02 /* Frame Error */
#define RCSR_OE 0x01 /* Overrun Error */
/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
#define CCR_HARDRESET 0x81 /* Reset the chip */
#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */
#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */
#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */
#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */
#define CCR_SSCH1 0x21 /* Send Special Character 1 */
#define CCR_SSCH2 0x22 /* Send Special Character 2 */
#define CCR_SSCH3 0x23 /* Send Special Character 3 */
#define CCR_SSCH4 0x24 /* Send Special Character 4 */
#define CCR_TXEN 0x18 /* Enable Transmitter */
#define CCR_RXEN 0x12 /* Enable Receiver */
#define CCR_TXDIS 0x14 /* Disable Transmitter */
#define CCR_RXDIS 0x11 /* Disable Receiver */
/* Interrupt Enable Register (R/W) */
#define IER_DSR 0x80 /* Enable interrupt on DSR change */
#define IER_CD 0x40 /* Enable interrupt on CD change */
#define IER_CTS 0x20 /* Enable interrupt on CTS change */
#define IER_RXD 0x10 /* Enable interrupt on Receive Data */
#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */
#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */
#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */
#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */
/* Channel Option Register 1 (R/W) */
#define COR1_ODDP 0x80 /* Odd Parity */
#define COR1_PARMODE 0x60 /* Parity Mode mask */
#define COR1_NOPAR 0x00 /* No Parity */
#define COR1_FORCEPAR 0x20 /* Force Parity */
#define COR1_NORMPAR 0x40 /* Normal Parity */
#define COR1_IGNORE 0x10 /* Ignore Parity on RX */
#define COR1_STOPBITS 0x0c /* Number of Stop Bits */
#define COR1_1SB 0x00 /* 1 Stop Bit */
#define COR1_15SB 0x04 /* 1.5 Stop Bits */
#define COR1_2SB 0x08 /* 2 Stop Bits */
#define COR1_CHARLEN 0x03 /* Character Length */
#define COR1_5BITS 0x00 /* 5 bits */
#define COR1_6BITS 0x01 /* 6 bits */
#define COR1_7BITS 0x02 /* 7 bits */
#define COR1_8BITS 0x03 /* 8 bits */
/* Channel Option Register 2 (R/W) */
#define COR2_IXM 0x80 /* Implied XON mode */
#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */
#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */
#define COR2_LLM 0x10 /* Local Loopback Mode */
#define COR2_RLM 0x08 /* Remote Loopback Mode */
#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */
#define COR2_CTSAE 0x02 /* CTS Automatic Enable */
#define COR2_DSRAE 0x01 /* DSR Automatic Enable */
/* Channel Option Register 3 (R/W) */
#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */
#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */
#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */
#define COR3_SCDE 0x10 /* Special Character Detection Enable */
#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */
/* Channel Control Status Register (R/O) */
#define CCSR_RXEN 0x80 /* Receiver Enabled */
#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */
#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */
#define CCSR_TXEN 0x08 /* Transmitter Enabled */
#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */
#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */
/* Modem Change Option Register 1 (R/W) */
#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */
#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */
#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */
#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */
#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */
/* Modem Change Option Register 2 (R/W) */
#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */
#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */
#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */
/* Modem Change Register (R/W) */
#define MCR_DSRCHG 0x80 /* DSR Changed */
#define MCR_CDCHG 0x40 /* CD Changed */
#define MCR_CTSCHG 0x20 /* CTS Changed */
/* Modem Signal Value Register (R/W) */
#define MSVR_DSR 0x80 /* Current state of DSR input */
#define MSVR_CD 0x40 /* Current state of CD input */
#define MSVR_CTS 0x20 /* Current state of CTS input */
#define MSVR_DTR 0x02 /* Current state of DTR output */
#define MSVR_RTS 0x01 /* Current state of RTS output */
/* Escape characters */
#define CD180_C_ESC 0x00 /* Escape character */
#define CD180_C_SBRK 0x81 /* Start sending BREAK */
#define CD180_C_DELAY 0x82 /* Delay output */
#define CD180_C_EBRK 0x83 /* Stop sending BREAK */
/*
* linux/drivers/char/serial167.c
*
* Driver for MVME166/7 board serial ports, which are via a CD2401.
* Based very much on cyclades.c.
*
* MVME166/7 work by Richard Hirst [richard@sleepie.demon.co.uk]
*
* ==============================================================
*
* static char rcsid[] =
* "$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $";
*
* linux/kernel/cyclades.c
*
* Maintained by Marcio Saito (cyclades@netcom.com) and
* Randolph Bentson (bentson@grieg.seaslug.org)
*
* Much of the design and some of the code came from serial.c
* which was copyright (C) 1991, 1992 Linus Torvalds. It was
* extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
* and then fixed as suggested by Michael K. Johnson 12/12/92.
*
* This version does not support shared irq's.
*
* $Log: cyclades.c,v $
* Revision 1.36.1.4 1995/03/29 06:14:14 bentson
* disambiguate between Cyclom-16Y and Cyclom-32Ye;
*
* Changes:
*
* 200 lines of changes record removed - RGH 11-10-95, starting work on
* converting this to drive serial ports on mvme166 (cd2401).
*
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/08/25
* - get rid of verify_area
* - use get_user to access memory from userspace in set_threshold,
* set_default_threshold and set_timeout
* - don't use the panic function in serial167_init
* - do resource release on failure on serial167_init
* - include missing restore_flags in mvme167_serial_console_setup
*
* Kars de Jong <jongk@linux-m68k.org> - 2004/09/06
* - replace bottom half handler with task queue handler
*/
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/interrupt.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/serial167.h>
#include <linux/delay.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/tty_flip.h>
#include <linux/gfp.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/mvme16xhw.h>
#include <asm/bootinfo.h>
#include <asm/setup.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/init.h>
#define SERIAL_PARANOIA_CHECK
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_THROTTLE
#undef SERIAL_DEBUG_OTHER
#undef SERIAL_DEBUG_IO
#undef SERIAL_DEBUG_COUNT
#undef SERIAL_DEBUG_DTR
#undef CYCLOM_16Y_HACK
#define CYCLOM_ENABLE_MONITORING
#define WAKEUP_CHARS 256
#define STD_COM_FLAGS (0)
static struct tty_driver *cy_serial_driver;
extern int serial_console;
static struct cyclades_port *serial_console_info = NULL;
static unsigned int serial_console_cflag = 0;
u_char initial_console_speed;
/* Base address of cd2401 chip on mvme166/7 */
#define BASE_ADDR (0xfff45000)
#define pcc2chip ((volatile u_char *)0xfff42000)
#define PccSCCMICR 0x1d
#define PccSCCTICR 0x1e
#define PccSCCRICR 0x1f
#define PccTPIACKR 0x25
#define PccRPIACKR 0x27
#define PccIMLR 0x3f
/* This is the per-port data structure */
struct cyclades_port cy_port[] = {
/* CARD# */
{-1}, /* ttyS0 */
{-1}, /* ttyS1 */
{-1}, /* ttyS2 */
{-1}, /* ttyS3 */
};
#define NR_PORTS ARRAY_SIZE(cy_port)
/*
* This is used to look up the divisor speeds and the timeouts
* We're normally limited to 15 distinct baud rates. The extra
* are accessed via settings in info->flags.
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
* 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
* HI VHI
*/
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000,
0
};
#if 0
static char baud_co[] = { /* 25 MHz clock option table */
/* value => 00 01 02 03 04 */
/* divide by 8 32 128 512 2048 */
0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static char baud_bpr[] = { /* 25 MHz baud rate period table */
0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15
};
#endif
/* I think 166 brd clocks 2401 at 20MHz.... */
/* These values are written directly to tcor, and >> 5 for writing to rcor */
static u_char baud_co[] = { /* 20 MHz clock option table */
0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x60, 0x60, 0x40,
0x40, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* These values written directly to tbpr/rbpr */
static u_char baud_bpr[] = { /* 20 MHz baud rate period table */
0x00, 0xc0, 0x80, 0x58, 0x6c, 0x40, 0xc0, 0x81, 0x40, 0x81,
0x57, 0x40, 0x81, 0x40, 0x81, 0x40, 0x2b, 0x20, 0x15, 0x10
};
static u_char baud_cor4[] = { /* receive threshold */
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07
};
static void shutdown(struct cyclades_port *);
static int startup(struct cyclades_port *);
static void cy_throttle(struct tty_struct *);
static void cy_unthrottle(struct tty_struct *);
static void config_setup(struct cyclades_port *);
#ifdef CYCLOM_SHOW_STATUS
static void show_status(int);
#endif
/*
* I have my own version of udelay(), as it is needed when initialising
* the chip, before the delay loop has been calibrated. Should probably
* reference one of the vmechip2 or pccchip2 counter for an accurate
* delay, but this wild guess will do for now.
*/
void my_udelay(long us)
{
u_char x;
volatile u_char *p = &x;
int i;
while (us--)
for (i = 100; i; i--)
x |= *p;
}
static inline int serial_paranoia_check(struct cyclades_port *info, char *name,
const char *routine)
{
#ifdef SERIAL_PARANOIA_CHECK
if (!info) {
printk("Warning: null cyclades_port for (%s) in %s\n", name,
routine);
return 1;
}
if (info < &cy_port[0] || info >= &cy_port[NR_PORTS]) {
printk("Warning: cyclades_port out of range for (%s) in %s\n",
name, routine);
return 1;
}
if (info->magic != CYCLADES_MAGIC) {
printk("Warning: bad magic number for serial struct (%s) in "
"%s\n", name, routine);
return 1;
}
#endif
return 0;
} /* serial_paranoia_check */
#if 0
/* The following diagnostic routines allow the driver to spew
information on the screen, even (especially!) during interrupts.
*/
void SP(char *data)
{
unsigned long flags;
local_irq_save(flags);
printk(KERN_EMERG "%s", data);
local_irq_restore(flags);
}
char scrn[2];
void CP(char data)
{
unsigned long flags;
local_irq_save(flags);
scrn[0] = data;
printk(KERN_EMERG "%c", scrn);
local_irq_restore(flags);
} /* CP */
void CP1(int data)
{
(data < 10) ? CP(data + '0') : CP(data + 'A' - 10);
} /* CP1 */
void CP2(int data)
{
CP1((data >> 4) & 0x0f);
CP1(data & 0x0f);
} /* CP2 */
void CP4(int data)
{
CP2((data >> 8) & 0xff);
CP2(data & 0xff);
} /* CP4 */
void CP8(long data)
{
CP4((data >> 16) & 0xffff);
CP4(data & 0xffff);
} /* CP8 */
#endif
/* This routine waits up to 1000 micro-seconds for the previous
command to the Cirrus chip to complete and then issues the
new command. An error is returned if the previous command
didn't finish within the time limit.
*/
u_short write_cy_cmd(volatile u_char * base_addr, u_char cmd)
{
unsigned long flags;
volatile int i;
local_irq_save(flags);
/* Check to see that the previous command has completed */
for (i = 0; i < 100; i++) {
if (base_addr[CyCCR] == 0) {
break;
}
my_udelay(10L);
}
/* if the CCR never cleared, the previous command
didn't finish within the "reasonable time" */
if (i == 10) {
local_irq_restore(flags);
return (-1);
}
/* Issue the new command */
base_addr[CyCCR] = cmd;
local_irq_restore(flags);
return (0);
} /* write_cy_cmd */
/* cy_start and cy_stop provide software output flow control as a
function of XON/XOFF, software CTS, and other such stuff. */
static void cy_stop(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
int channel;
unsigned long flags;
#ifdef SERIAL_DEBUG_OTHER
printk("cy_stop %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_stop"))
return;
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = (u_char) (channel); /* index channel */
base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
local_irq_restore(flags);
} /* cy_stop */
static void cy_start(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
int channel;
unsigned long flags;
#ifdef SERIAL_DEBUG_OTHER
printk("cy_start %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_start"))
return;
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = (u_char) (channel);
base_addr[CyIER] |= CyTxMpty;
local_irq_restore(flags);
} /* cy_start */
/* The real interrupt service routines are called
whenever the card wants its hand held--chars
received, out buffer empty, modem change, etc.
*/
static irqreturn_t cd2401_rxerr_interrupt(int irq, void *dev_id)
{
struct tty_struct *tty;
struct cyclades_port *info;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
unsigned char err, rfoc;
int channel;
char data;
/* determine the channel and change to that context */
channel = (u_short) (base_addr[CyLICR] >> 2);
info = &cy_port[channel];
info->last_active = jiffies;
if ((err = base_addr[CyRISR]) & CyTIMEOUT) {
/* This is a receive timeout interrupt, ignore it */
base_addr[CyREOIR] = CyNOTRANS;
return IRQ_HANDLED;
}
/* Read a byte of data if there is any - assume the error
* is associated with this character */
if ((rfoc = base_addr[CyRFOC]) != 0)
data = base_addr[CyRDR];
else
data = 0;
/* if there is nowhere to put the data, discard it */
if (info->tty == 0) {
base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
return IRQ_HANDLED;
} else { /* there is an open port for this data */
tty = info->tty;
if (err & info->ignore_status_mask) {
base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
return IRQ_HANDLED;
}
if (tty_buffer_request_room(tty, 1) != 0) {
if (err & info->read_status_mask) {
if (err & CyBREAK) {
tty_insert_flip_char(tty, data,
TTY_BREAK);
if (info->flags & ASYNC_SAK) {
do_SAK(tty);
}
} else if (err & CyFRAME) {
tty_insert_flip_char(tty, data,
TTY_FRAME);
} else if (err & CyPARITY) {
tty_insert_flip_char(tty, data,
TTY_PARITY);
} else if (err & CyOVERRUN) {
tty_insert_flip_char(tty, 0,
TTY_OVERRUN);
/*
If the flip buffer itself is
overflowing, we still lose
the next incoming character.
*/
if (tty_buffer_request_room(tty, 1) !=
0) {
tty_insert_flip_char(tty, data,
TTY_FRAME);
}
/* These two conditions may imply */
/* a normal read should be done. */
/* else if(data & CyTIMEOUT) */
/* else if(data & CySPECHAR) */
} else {
tty_insert_flip_char(tty, 0,
TTY_NORMAL);
}
} else {
tty_insert_flip_char(tty, data, TTY_NORMAL);
}
} else {
/* there was a software buffer overrun
and nothing could be done about it!!! */
}
}
tty_schedule_flip(tty);
/* end of service */
base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;
return IRQ_HANDLED;
} /* cy_rxerr_interrupt */
static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id)
{
struct cyclades_port *info;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
int channel;
int mdm_change;
int mdm_status;
/* determine the channel and change to that context */
channel = (u_short) (base_addr[CyLICR] >> 2);
info = &cy_port[channel];
info->last_active = jiffies;
mdm_change = base_addr[CyMISR];
mdm_status = base_addr[CyMSVR1];
if (info->tty == 0) { /* nowhere to put the data, ignore it */
;
} else {
if ((mdm_change & CyDCD)
&& (info->flags & ASYNC_CHECK_CD)) {
if (mdm_status & CyDCD) {
/* CP('!'); */
wake_up_interruptible(&info->open_wait);
} else {
/* CP('@'); */
tty_hangup(info->tty);
wake_up_interruptible(&info->open_wait);
info->flags &= ~ASYNC_NORMAL_ACTIVE;
}
}
if ((mdm_change & CyCTS)
&& (info->flags & ASYNC_CTS_FLOW)) {
if (info->tty->stopped) {
if (mdm_status & CyCTS) {
/* !!! cy_start isn't used because... */
info->tty->stopped = 0;
base_addr[CyIER] |= CyTxMpty;
tty_wakeup(info->tty);
}
} else {
if (!(mdm_status & CyCTS)) {
/* !!! cy_stop isn't used because... */
info->tty->stopped = 1;
base_addr[CyIER] &=
~(CyTxMpty | CyTxRdy);
}
}
}
if (mdm_status & CyDSR) {
}
}
base_addr[CyMEOIR] = 0;
return IRQ_HANDLED;
} /* cy_modem_interrupt */
static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id)
{
struct cyclades_port *info;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
int channel;
int char_count, saved_cnt;
int outch;
/* determine the channel and change to that context */
channel = (u_short) (base_addr[CyLICR] >> 2);
/* validate the port number (as configured and open) */
if ((channel < 0) || (NR_PORTS <= channel)) {
base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
base_addr[CyTEOIR] = CyNOTRANS;
return IRQ_HANDLED;
}
info = &cy_port[channel];
info->last_active = jiffies;
if (info->tty == 0) {
base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
base_addr[CyTEOIR] = CyNOTRANS;
return IRQ_HANDLED;
}
/* load the on-chip space available for outbound data */
saved_cnt = char_count = base_addr[CyTFTC];
if (info->x_char) { /* send special char */
outch = info->x_char;
base_addr[CyTDR] = outch;
char_count--;
info->x_char = 0;
}
if (info->x_break) {
/* The Cirrus chip requires the "Embedded Transmit
Commands" of start break, delay, and end break
sequences to be sent. The duration of the
break is given in TICs, which runs at HZ
(typically 100) and the PPR runs at 200 Hz,
so the delay is duration * 200/HZ, and thus a
break can run from 1/100 sec to about 5/4 sec.
Need to check these values - RGH 141095.
*/
base_addr[CyTDR] = 0; /* start break */
base_addr[CyTDR] = 0x81;
base_addr[CyTDR] = 0; /* delay a bit */
base_addr[CyTDR] = 0x82;
base_addr[CyTDR] = info->x_break * 200 / HZ;
base_addr[CyTDR] = 0; /* terminate break */
base_addr[CyTDR] = 0x83;
char_count -= 7;
info->x_break = 0;
}
while (char_count > 0) {
if (!info->xmit_cnt) {
base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
break;
}
if (info->xmit_buf == 0) {
base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
break;
}
if (info->tty->stopped || info->tty->hw_stopped) {
base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy);
break;
}
/* Because the Embedded Transmit Commands have been
enabled, we must check to see if the escape
character, NULL, is being sent. If it is, we
must ensure that there is room for it to be
doubled in the output stream. Therefore we
no longer advance the pointer when the character
is fetched, but rather wait until after the check
for a NULL output character. (This is necessary
because there may not be room for the two chars
needed to send a NULL.
*/
outch = info->xmit_buf[info->xmit_tail];
if (outch) {
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
& (PAGE_SIZE - 1);
base_addr[CyTDR] = outch;
char_count--;
} else {
if (char_count > 1) {
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
& (PAGE_SIZE - 1);
base_addr[CyTDR] = outch;
base_addr[CyTDR] = 0;
char_count--;
char_count--;
} else {
break;
}
}
}
if (info->xmit_cnt < WAKEUP_CHARS)
tty_wakeup(info->tty);
base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS;
return IRQ_HANDLED;
} /* cy_tx_interrupt */
static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id)
{
struct tty_struct *tty;
struct cyclades_port *info;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
int channel;
char data;
int char_count;
int save_cnt;
/* determine the channel and change to that context */
channel = (u_short) (base_addr[CyLICR] >> 2);
info = &cy_port[channel];
info->last_active = jiffies;
save_cnt = char_count = base_addr[CyRFOC];
/* if there is nowhere to put the data, discard it */
if (info->tty == 0) {
while (char_count--) {
data = base_addr[CyRDR];
}
} else { /* there is an open port for this data */
tty = info->tty;
/* load # characters available from the chip */
#ifdef CYCLOM_ENABLE_MONITORING
++info->mon.int_count;
info->mon.char_count += char_count;
if (char_count > info->mon.char_max)
info->mon.char_max = char_count;
info->mon.char_last = char_count;
#endif
while (char_count--) {
data = base_addr[CyRDR];
tty_insert_flip_char(tty, data, TTY_NORMAL);
#ifdef CYCLOM_16Y_HACK
udelay(10L);
#endif
}
tty_schedule_flip(tty);
}
/* end of service */
base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;
return IRQ_HANDLED;
} /* cy_rx_interrupt */
/* This is called whenever a port becomes active;
interrupts are enabled and DTR & RTS are turned on.
*/
static int startup(struct cyclades_port *info)
{
unsigned long flags;
volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;
int channel;
if (info->flags & ASYNC_INITIALIZED) {
return 0;
}
if (!info->type) {
if (info->tty) {
set_bit(TTY_IO_ERROR, &info->tty->flags);
}
return 0;
}
if (!info->xmit_buf) {
info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
if (!info->xmit_buf) {
return -ENOMEM;
}
}
config_setup(info);
channel = info->line;
#ifdef SERIAL_DEBUG_OPEN
printk("startup channel %d\n", channel);
#endif
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR);
base_addr[CyCAR] = (u_char) channel; /* !!! Is this needed? */
base_addr[CyMSVR1] = CyRTS;
/* CP('S');CP('1'); */
base_addr[CyMSVR2] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
base_addr[CyIER] |= CyRxData;
info->flags |= ASYNC_INITIALIZED;
if (info->tty) {
clear_bit(TTY_IO_ERROR, &info->tty->flags);
}
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
local_irq_restore(flags);
#ifdef SERIAL_DEBUG_OPEN
printk(" done\n");
#endif
return 0;
} /* startup */
void start_xmit(struct cyclades_port *info)
{
unsigned long flags;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = channel;
base_addr[CyIER] |= CyTxMpty;
local_irq_restore(flags);
} /* start_xmit */
/*
* This routine shuts down a serial port; interrupts are disabled,
* and DTR is dropped if the hangup on close termio flag is on.
*/
static void shutdown(struct cyclades_port *info)
{
unsigned long flags;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
if (!(info->flags & ASYNC_INITIALIZED)) {
/* CP('$'); */
return;
}
channel = info->line;
#ifdef SERIAL_DEBUG_OPEN
printk("shutdown channel %d\n", channel);
#endif
/* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
SENT BEFORE DROPPING THE LINE !!! (Perhaps
set some flag that is read when XMTY happens.)
Other choices are to delay some fixed interval
or schedule some later processing.
*/
local_irq_save(flags);
if (info->xmit_buf) {
free_page((unsigned long)info->xmit_buf);
info->xmit_buf = NULL;
}
base_addr[CyCAR] = (u_char) channel;
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
base_addr[CyMSVR1] = 0;
/* CP('C');CP('1'); */
base_addr[CyMSVR2] = 0;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
}
write_cy_cmd(base_addr, CyDIS_RCVR);
/* it may be appropriate to clear _XMIT at
some later date (after testing)!!! */
if (info->tty) {
set_bit(TTY_IO_ERROR, &info->tty->flags);
}
info->flags &= ~ASYNC_INITIALIZED;
local_irq_restore(flags);
#ifdef SERIAL_DEBUG_OPEN
printk(" done\n");
#endif
} /* shutdown */
/*
* This routine finds or computes the various line characteristics.
*/
static void config_setup(struct cyclades_port *info)
{
unsigned long flags;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
unsigned cflag;
int i;
unsigned char ti, need_init_chan = 0;
if (!info->tty || !info->tty->termios) {
return;
}
if (info->line == -1) {
return;
}
cflag = info->tty->termios->c_cflag;
/* baud rate */
i = cflag & CBAUD;
#ifdef CBAUDEX
/* Starting with kernel 1.1.65, there is direct support for
higher baud rates. The following code supports those
changes. The conditional aspect allows this driver to be
used for earlier as well as later kernel versions. (The
mapping is slightly different from serial.c because there
is still the possibility of supporting 75 kbit/sec with
the Cyclades board.)
*/
if (i & CBAUDEX) {
if (i == B57600)
i = 16;
else if (i == B115200)
i = 18;
#ifdef B78600
else if (i == B78600)
i = 17;
#endif
else
info->tty->termios->c_cflag &= ~CBAUDEX;
}
#endif
if (i == 15) {
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
i += 1;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
i += 3;
}
/* Don't ever change the speed of the console port. It will
* run at the speed specified in bootinfo, or at 19.2K */
/* Actually, it should run at whatever speed 166Bug was using */
/* Note info->timeout isn't used at present */
if (info != serial_console_info) {
info->tbpr = baud_bpr[i]; /* Tx BPR */
info->tco = baud_co[i]; /* Tx CO */
info->rbpr = baud_bpr[i]; /* Rx BPR */
info->rco = baud_co[i] >> 5; /* Rx CO */
if (baud_table[i] == 134) {
info->timeout =
(info->xmit_fifo_size * HZ * 30 / 269) + 2;
/* get it right for 134.5 baud */
} else if (baud_table[i]) {
info->timeout =
(info->xmit_fifo_size * HZ * 15 / baud_table[i]) +
2;
/* this needs to be propagated into the card info */
} else {
info->timeout = 0;
}
}
/* By tradition (is it a standard?) a baud rate of zero
implies the line should be/has been closed. A bit
later in this routine such a test is performed. */
/* byte size and parity */
info->cor7 = 0;
info->cor6 = 0;
info->cor5 = 0;
info->cor4 = (info->default_threshold ? info->default_threshold : baud_cor4[i]); /* receive threshold */
/* Following two lines added 101295, RGH. */
/* It is obviously wrong to access CyCORx, and not info->corx here,
* try and remember to fix it later! */
channel = info->line;
base_addr[CyCAR] = (u_char) channel;
if (C_CLOCAL(info->tty)) {
if (base_addr[CyIER] & CyMdmCh)
base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */
/* ignore 1->0 modem transitions */
if (base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD))
base_addr[CyCOR4] &= ~(CyDSR | CyCTS | CyDCD);
/* ignore 0->1 modem transitions */
if (base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD))
base_addr[CyCOR5] &= ~(CyDSR | CyCTS | CyDCD);
} else {
if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh)
base_addr[CyIER] |= CyMdmCh; /* with modem intr */
/* act on 1->0 modem transitions */
if ((base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) !=
(CyDSR | CyCTS | CyDCD))
base_addr[CyCOR4] |= CyDSR | CyCTS | CyDCD;
/* act on 0->1 modem transitions */
if ((base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) !=
(CyDSR | CyCTS | CyDCD))
base_addr[CyCOR5] |= CyDSR | CyCTS | CyDCD;
}
info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP;
info->cor2 = CyETC;
switch (cflag & CSIZE) {
case CS5:
info->cor1 = Cy_5_BITS;
break;
case CS6:
info->cor1 = Cy_6_BITS;
break;
case CS7:
info->cor1 = Cy_7_BITS;
break;
case CS8:
info->cor1 = Cy_8_BITS;
break;
}
if (cflag & PARENB) {
if (cflag & PARODD) {
info->cor1 |= CyPARITY_O;
} else {
info->cor1 |= CyPARITY_E;
}
} else {
info->cor1 |= CyPARITY_NONE;
}
/* CTS flow control flag */
#if 0
/* Don't complcate matters for now! RGH 141095 */
if (cflag & CRTSCTS) {
info->flags |= ASYNC_CTS_FLOW;
info->cor2 |= CyCtsAE;
} else {
info->flags &= ~ASYNC_CTS_FLOW;
info->cor2 &= ~CyCtsAE;
}
#endif
if (cflag & CLOCAL)
info->flags &= ~ASYNC_CHECK_CD;
else
info->flags |= ASYNC_CHECK_CD;
/***********************************************
The hardware option, CyRtsAO, presents RTS when
the chip has characters to send. Since most modems
use RTS as reverse (inbound) flow control, this
option is not used. If inbound flow control is
necessary, DTR can be programmed to provide the
appropriate signals for use with a non-standard
cable. Contact Marcio Saito for details.
***********************************************/
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
/* CyCMR set once only in mvme167_init_serial() */
if (base_addr[CyLICR] != channel << 2)
base_addr[CyLICR] = channel << 2;
if (base_addr[CyLIVR] != 0x5c)
base_addr[CyLIVR] = 0x5c;
/* tx and rx baud rate */
if (base_addr[CyCOR1] != info->cor1)
need_init_chan = 1;
if (base_addr[CyTCOR] != info->tco)
base_addr[CyTCOR] = info->tco;
if (base_addr[CyTBPR] != info->tbpr)
base_addr[CyTBPR] = info->tbpr;
if (base_addr[CyRCOR] != info->rco)
base_addr[CyRCOR] = info->rco;
if (base_addr[CyRBPR] != info->rbpr)
base_addr[CyRBPR] = info->rbpr;
/* set line characteristics according configuration */
if (base_addr[CySCHR1] != START_CHAR(info->tty))
base_addr[CySCHR1] = START_CHAR(info->tty);
if (base_addr[CySCHR2] != STOP_CHAR(info->tty))
base_addr[CySCHR2] = STOP_CHAR(info->tty);
if (base_addr[CySCRL] != START_CHAR(info->tty))
base_addr[CySCRL] = START_CHAR(info->tty);
if (base_addr[CySCRH] != START_CHAR(info->tty))
base_addr[CySCRH] = START_CHAR(info->tty);
if (base_addr[CyCOR1] != info->cor1)
base_addr[CyCOR1] = info->cor1;
if (base_addr[CyCOR2] != info->cor2)
base_addr[CyCOR2] = info->cor2;
if (base_addr[CyCOR3] != info->cor3)
base_addr[CyCOR3] = info->cor3;
if (base_addr[CyCOR4] != info->cor4)
base_addr[CyCOR4] = info->cor4;
if (base_addr[CyCOR5] != info->cor5)
base_addr[CyCOR5] = info->cor5;
if (base_addr[CyCOR6] != info->cor6)
base_addr[CyCOR6] = info->cor6;
if (base_addr[CyCOR7] != info->cor7)
base_addr[CyCOR7] = info->cor7;
if (need_init_chan)
write_cy_cmd(base_addr, CyINIT_CHAN);
base_addr[CyCAR] = (u_char) channel; /* !!! Is this needed? */
/* 2ms default rx timeout */
ti = info->default_timeout ? info->default_timeout : 0x02;
if (base_addr[CyRTPRL] != ti)
base_addr[CyRTPRL] = ti;
if (base_addr[CyRTPRH] != 0)
base_addr[CyRTPRH] = 0;
/* Set up RTS here also ????? RGH 141095 */
if (i == 0) { /* baud rate is zero, turn off line */
if ((base_addr[CyMSVR2] & CyDTR) == CyDTR)
base_addr[CyMSVR2] = 0;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
} else {
if ((base_addr[CyMSVR2] & CyDTR) != CyDTR)
base_addr[CyMSVR2] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
}
if (info->tty) {
clear_bit(TTY_IO_ERROR, &info->tty->flags);
}
local_irq_restore(flags);
} /* config_setup */
static int cy_put_char(struct tty_struct *tty, unsigned char ch)
{
struct cyclades_port *info = tty->driver_data;
unsigned long flags;
#ifdef SERIAL_DEBUG_IO
printk("cy_put_char %s(0x%02x)\n", tty->name, ch);
#endif
if (serial_paranoia_check(info, tty->name, "cy_put_char"))
return 0;
if (!info->xmit_buf)
return 0;
local_irq_save(flags);
if (info->xmit_cnt >= PAGE_SIZE - 1) {
local_irq_restore(flags);
return 0;
}
info->xmit_buf[info->xmit_head++] = ch;
info->xmit_head &= PAGE_SIZE - 1;
info->xmit_cnt++;
local_irq_restore(flags);
return 1;
} /* cy_put_char */
static void cy_flush_chars(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
unsigned long flags;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
#ifdef SERIAL_DEBUG_IO
printk("cy_flush_chars %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_flush_chars"))
return;
if (info->xmit_cnt <= 0 || tty->stopped
|| tty->hw_stopped || !info->xmit_buf)
return;
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = channel;
base_addr[CyIER] |= CyTxMpty;
local_irq_restore(flags);
} /* cy_flush_chars */
/* This routine gets called when tty_write has put something into
the write_queue. If the port is not already transmitting stuff,
start it off by enabling interrupts. The interrupt service
routine will then ensure that the characters are sent. If the
port is already active, there is no need to kick it.
*/
static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct cyclades_port *info = tty->driver_data;
unsigned long flags;
int c, total = 0;
#ifdef SERIAL_DEBUG_IO
printk("cy_write %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_write")) {
return 0;
}
if (!info->xmit_buf) {
return 0;
}
while (1) {
local_irq_save(flags);
c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0) {
local_irq_restore(flags);
break;
}
memcpy(info->xmit_buf + info->xmit_head, buf, c);
info->xmit_head =
(info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1);
info->xmit_cnt += c;
local_irq_restore(flags);
buf += c;
count -= c;
total += c;
}
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
start_xmit(info);
}
return total;
} /* cy_write */
static int cy_write_room(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
int ret;
#ifdef SERIAL_DEBUG_IO
printk("cy_write_room %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_write_room"))
return 0;
ret = PAGE_SIZE - info->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
} /* cy_write_room */
static int cy_chars_in_buffer(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
#ifdef SERIAL_DEBUG_IO
printk("cy_chars_in_buffer %s %d\n", tty->name, info->xmit_cnt); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer"))
return 0;
return info->xmit_cnt;
} /* cy_chars_in_buffer */
static void cy_flush_buffer(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
unsigned long flags;
#ifdef SERIAL_DEBUG_IO
printk("cy_flush_buffer %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_flush_buffer"))
return;
local_irq_save(flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
local_irq_restore(flags);
tty_wakeup(tty);
} /* cy_flush_buffer */
/* This routine is called by the upper-layer tty layer to signal
that incoming characters should be throttled or that the
throttle should be released.
*/
static void cy_throttle(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
unsigned long flags;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("throttle %s: %d....\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
printk("cy_throttle %s\n", tty->name);
#endif
if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) {
return;
}
if (I_IXOFF(tty)) {
info->x_char = STOP_CHAR(tty);
/* Should use the "Send Special Character" feature!!! */
}
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
base_addr[CyMSVR1] = 0;
local_irq_restore(flags);
} /* cy_throttle */
static void cy_unthrottle(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
unsigned long flags;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("throttle %s: %d....\n", tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
printk("cy_unthrottle %s\n", tty->name);
#endif
if (serial_paranoia_check(info, tty->name, "cy_nthrottle")) {
return;
}
if (I_IXOFF(tty)) {
info->x_char = START_CHAR(tty);
/* Should use the "Send Special Character" feature!!! */
}
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
base_addr[CyMSVR1] = CyRTS;
local_irq_restore(flags);
} /* cy_unthrottle */
static int
get_serial_info(struct cyclades_port *info,
struct serial_struct __user * retinfo)
{
struct serial_struct tmp;
/* CP('g'); */
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->line;
tmp.port = info->line;
tmp.irq = 0;
tmp.flags = info->flags;
tmp.baud_base = 0; /*!!! */
tmp.close_delay = info->close_delay;
tmp.custom_divisor = 0; /*!!! */
tmp.hub6 = 0; /*!!! */
return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
} /* get_serial_info */
static int
set_serial_info(struct cyclades_port *info,
struct serial_struct __user * new_info)
{
struct serial_struct new_serial;
struct cyclades_port old_info;
/* CP('s'); */
if (!new_info)
return -EFAULT;
if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
return -EFAULT;
old_info = *info;
if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.close_delay != info->close_delay) ||
((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
(info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
return -EPERM;
info->flags = ((info->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
goto check_and_exit;
}
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
info->flags = ((info->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
info->close_delay = new_serial.close_delay;
check_and_exit:
if (info->flags & ASYNC_INITIALIZED) {
config_setup(info);
return 0;
}
return startup(info);
} /* set_serial_info */
static int cy_tiocmget(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
int channel;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
unsigned long flags;
unsigned char status;
channel = info->line;
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
local_irq_restore(flags);
return ((status & CyRTS) ? TIOCM_RTS : 0)
| ((status & CyDTR) ? TIOCM_DTR : 0)
| ((status & CyDCD) ? TIOCM_CAR : 0)
| ((status & CyDSR) ? TIOCM_DSR : 0)
| ((status & CyCTS) ? TIOCM_CTS : 0);
} /* cy_tiocmget */
static int
cy_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
{
struct cyclades_port *info = tty->driver_data;
int channel;
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
unsigned long flags;
channel = info->line;
if (set & TIOCM_RTS) {
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
base_addr[CyMSVR1] = CyRTS;
local_irq_restore(flags);
}
if (set & TIOCM_DTR) {
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
/* CP('S');CP('2'); */
base_addr[CyMSVR2] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
local_irq_restore(flags);
}
if (clear & TIOCM_RTS) {
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
base_addr[CyMSVR1] = 0;
local_irq_restore(flags);
}
if (clear & TIOCM_DTR) {
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
/* CP('C');CP('2'); */
base_addr[CyMSVR2] = 0;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
local_irq_restore(flags);
}
return 0;
} /* set_modem_info */
static void send_break(struct cyclades_port *info, int duration)
{ /* Let the transmit ISR take care of this (since it
requires stuffing characters into the output stream).
*/
info->x_break = duration;
if (!info->xmit_cnt) {
start_xmit(info);
}
} /* send_break */
static int
get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user * mon)
{
if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)))
return -EFAULT;
info->mon.int_count = 0;
info->mon.char_count = 0;
info->mon.char_max = 0;
info->mon.char_last = 0;
return 0;
}
static int set_threshold(struct cyclades_port *info, unsigned long __user * arg)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
unsigned long value;
int channel;
if (get_user(value, arg))
return -EFAULT;
channel = info->line;
info->cor4 &= ~CyREC_FIFO;
info->cor4 |= value & CyREC_FIFO;
base_addr[CyCOR4] = info->cor4;
return 0;
}
static int
get_threshold(struct cyclades_port *info, unsigned long __user * value)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
unsigned long tmp;
channel = info->line;
tmp = base_addr[CyCOR4] & CyREC_FIFO;
return put_user(tmp, value);
}
static int
set_default_threshold(struct cyclades_port *info, unsigned long __user * arg)
{
unsigned long value;
if (get_user(value, arg))
return -EFAULT;
info->default_threshold = value & 0x0f;
return 0;
}
static int
get_default_threshold(struct cyclades_port *info, unsigned long __user * value)
{
return put_user(info->default_threshold, value);
}
static int set_timeout(struct cyclades_port *info, unsigned long __user * arg)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
unsigned long value;
if (get_user(value, arg))
return -EFAULT;
channel = info->line;
base_addr[CyRTPRL] = value & 0xff;
base_addr[CyRTPRH] = (value >> 8) & 0xff;
return 0;
}
static int get_timeout(struct cyclades_port *info, unsigned long __user * value)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
unsigned long tmp;
channel = info->line;
tmp = base_addr[CyRTPRL];
return put_user(tmp, value);
}
static int set_default_timeout(struct cyclades_port *info, unsigned long value)
{
info->default_timeout = value & 0xff;
return 0;
}
static int
get_default_timeout(struct cyclades_port *info, unsigned long __user * value)
{
return put_user(info->default_timeout, value);
}
static int
cy_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct cyclades_port *info = tty->driver_data;
int ret_val = 0;
void __user *argp = (void __user *)arg;
#ifdef SERIAL_DEBUG_OTHER
printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */
#endif
tty_lock();
switch (cmd) {
case CYGETMON:
ret_val = get_mon_info(info, argp);
break;
case CYGETTHRESH:
ret_val = get_threshold(info, argp);
break;
case CYSETTHRESH:
ret_val = set_threshold(info, argp);
break;
case CYGETDEFTHRESH:
ret_val = get_default_threshold(info, argp);
break;
case CYSETDEFTHRESH:
ret_val = set_default_threshold(info, argp);
break;
case CYGETTIMEOUT:
ret_val = get_timeout(info, argp);
break;
case CYSETTIMEOUT:
ret_val = set_timeout(info, argp);
break;
case CYGETDEFTIMEOUT:
ret_val = get_default_timeout(info, argp);
break;
case CYSETDEFTIMEOUT:
ret_val = set_default_timeout(info, (unsigned long)arg);
break;
case TCSBRK: /* SVID version: non-zero arg --> no break */
ret_val = tty_check_change(tty);
if (ret_val)
break;
tty_wait_until_sent(tty, 0);
if (!arg)
send_break(info, HZ / 4); /* 1/4 second */
break;
case TCSBRKP: /* support for POSIX tcsendbreak() */
ret_val = tty_check_change(tty);
if (ret_val)
break;
tty_wait_until_sent(tty, 0);
send_break(info, arg ? arg * (HZ / 10) : HZ / 4);
break;
/* The following commands are incompletely implemented!!! */
case TIOCGSERIAL:
ret_val = get_serial_info(info, argp);
break;
case TIOCSSERIAL:
ret_val = set_serial_info(info, argp);
break;
default:
ret_val = -ENOIOCTLCMD;
}
tty_unlock();
#ifdef SERIAL_DEBUG_OTHER
printk("cy_ioctl done\n");
#endif
return ret_val;
} /* cy_ioctl */
static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
struct cyclades_port *info = tty->driver_data;
#ifdef SERIAL_DEBUG_OTHER
printk("cy_set_termios %s\n", tty->name);
#endif
if (tty->termios->c_cflag == old_termios->c_cflag)
return;
config_setup(info);
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->stopped = 0;
cy_start(tty);
}
#ifdef tytso_patch_94Nov25_1726
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait);
#endif
} /* cy_set_termios */
static void cy_close(struct tty_struct *tty, struct file *filp)
{
struct cyclades_port *info = tty->driver_data;
/* CP('C'); */
#ifdef SERIAL_DEBUG_OTHER
printk("cy_close %s\n", tty->name);
#endif
if (!info || serial_paranoia_check(info, tty->name, "cy_close")) {
return;
}
#ifdef SERIAL_DEBUG_OPEN
printk("cy_close %s, count = %d\n", tty->name, info->count);
#endif
if ((tty->count == 1) && (info->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. Info->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk("cy_close: bad serial port count; tty->count is 1, "
"info->count is %d\n", info->count);
info->count = 1;
}
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: decrementing count to %d\n", __LINE__,
info->count - 1);
#endif
if (--info->count < 0) {
printk("cy_close: bad serial port count for ttys%d: %d\n",
info->line, info->count);
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: setting count to 0\n", __LINE__);
#endif
info->count = 0;
}
if (info->count)
return;
info->flags |= ASYNC_CLOSING;
if (info->flags & ASYNC_INITIALIZED)
tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
shutdown(info);
cy_flush_buffer(tty);
tty_ldisc_flush(tty);
info->tty = NULL;
if (info->blocked_open) {
if (info->close_delay) {
msleep_interruptible(jiffies_to_msecs
(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
wake_up_interruptible(&info->close_wait);
#ifdef SERIAL_DEBUG_OTHER
printk("cy_close done\n");
#endif
} /* cy_close */
/*
* cy_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
void cy_hangup(struct tty_struct *tty)
{
struct cyclades_port *info = tty->driver_data;
#ifdef SERIAL_DEBUG_OTHER
printk("cy_hangup %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_hangup"))
return;
shutdown(info);
#if 0
info->event = 0;
info->count = 0;
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: setting count to 0\n", __LINE__);
#endif
info->tty = 0;
#endif
info->flags &= ~ASYNC_NORMAL_ACTIVE;
wake_up_interruptible(&info->open_wait);
} /* cy_hangup */
/*
* ------------------------------------------------------------
* cy_open() and friends
* ------------------------------------------------------------
*/
static int
block_til_ready(struct tty_struct *tty, struct file *filp,
struct cyclades_port *info)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int channel;
int retval;
volatile u_char *base_addr = (u_char *) BASE_ADDR;
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (info->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&info->close_wait);
if (info->flags & ASYNC_HUP_NOTIFY) {
return -EAGAIN;
} else {
return -ERESTARTSYS;
}
}
/*
* If non-blocking mode is set, then make the check up front
* and then exit.
*/
if (filp->f_flags & O_NONBLOCK) {
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* cy_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&info->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: %s, count = %d\n",
tty->name, info->count);
/**/
#endif
info->count--;
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count);
#endif
info->blocked_open++;
channel = info->line;
while (1) {
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
base_addr[CyMSVR1] = CyRTS;
/* CP('S');CP('4'); */
base_addr[CyMSVR2] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1],
base_addr[CyMSVR2]);
#endif
local_irq_restore(flags);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp)
|| !(info->flags & ASYNC_INITIALIZED)) {
if (info->flags & ASYNC_HUP_NOTIFY) {
retval = -EAGAIN;
} else {
retval = -ERESTARTSYS;
}
break;
}
local_irq_save(flags);
base_addr[CyCAR] = (u_char) channel;
/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
if (!(info->flags & ASYNC_CLOSING)
&& (C_CLOCAL(tty)
|| (base_addr[CyMSVR1] & CyDCD))) {
local_irq_restore(flags);
break;
}
local_irq_restore(flags);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: %s, count = %d\n",
tty->name, info->count);
/**/
#endif
tty_unlock();
schedule();
tty_lock();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp)) {
info->count++;
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: incrementing count to %d\n", __LINE__,
info->count);
#endif
}
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: %s, count = %d\n",
tty->name, info->count);
/**/
#endif
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
} /* block_til_ready */
/*
* This routine is called whenever a serial port is opened. It
* performs the serial-specific initialization for the tty structure.
*/
int cy_open(struct tty_struct *tty, struct file *filp)
{
struct cyclades_port *info;
int retval, line;
/* CP('O'); */
line = tty->index;
if ((line < 0) || (NR_PORTS <= line)) {
return -ENODEV;
}
info = &cy_port[line];
if (info->line < 0) {
return -ENODEV;
}
#ifdef SERIAL_DEBUG_OTHER
printk("cy_open %s\n", tty->name); /* */
#endif
if (serial_paranoia_check(info, tty->name, "cy_open")) {
return -ENODEV;
}
#ifdef SERIAL_DEBUG_OPEN
printk("cy_open %s, count = %d\n", tty->name, info->count);
/**/
#endif
info->count++;
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
#endif
tty->driver_data = info;
info->tty = tty;
/*
* Start up serial port
*/
retval = startup(info);
if (retval) {
return retval;
}
retval = block_til_ready(tty, filp, info);
if (retval) {
#ifdef SERIAL_DEBUG_OPEN
printk("cy_open returning after block_til_ready with %d\n",
retval);
#endif
return retval;
}
#ifdef SERIAL_DEBUG_OPEN
printk("cy_open done\n");
/**/
#endif
return 0;
} /* cy_open */
/*
* ---------------------------------------------------------------------
* serial167_init() and friends
*
* serial167_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
/*
* This routine prints out the appropriate serial driver version
* number, and identifies which options were configured into this
* driver.
*/
static void show_version(void)
{
printk("MVME166/167 cd2401 driver\n");
} /* show_version */
/* initialize chips on card -- return number of valid
chips (which is number of ports/4) */
/*
* This initialises the hardware to a reasonable state. It should
* probe the chip first so as to copy 166-Bug setup as a default for
* port 0. It initialises CMR to CyASYNC; that is never done again, so
* as to limit the number of CyINIT_CHAN commands in normal running.
*
* ... I wonder what I should do if this fails ...
*/
void mvme167_serial_console_setup(int cflag)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int ch;
u_char spd;
u_char rcor, rbpr, badspeed = 0;
unsigned long flags;
local_irq_save(flags);
/*
* First probe channel zero of the chip, to see what speed has
* been selected.
*/
base_addr[CyCAR] = 0;
rcor = base_addr[CyRCOR] << 5;
rbpr = base_addr[CyRBPR];
for (spd = 0; spd < sizeof(baud_bpr); spd++)
if (rbpr == baud_bpr[spd] && rcor == baud_co[spd])
break;
if (spd >= sizeof(baud_bpr)) {
spd = 14; /* 19200 */
badspeed = 1; /* Failed to identify speed */
}
initial_console_speed = spd;
/* OK, we have chosen a speed, now reset and reinitialise */
my_udelay(20000L); /* Allow time for any active o/p to complete */
if (base_addr[CyCCR] != 0x00) {
local_irq_restore(flags);
/* printk(" chip is never idle (CCR != 0)\n"); */
return;
}
base_addr[CyCCR] = CyCHIP_RESET; /* Reset the chip */
my_udelay(1000L);
if (base_addr[CyGFRCR] == 0x00) {
local_irq_restore(flags);
/* printk(" chip is not responding (GFRCR stayed 0)\n"); */
return;
}
/*
* System clock is 20Mhz, divided by 2048, so divide by 10 for a 1.0ms
* tick
*/
base_addr[CyTPR] = 10;
base_addr[CyPILR1] = 0x01; /* Interrupt level for modem change */
base_addr[CyPILR2] = 0x02; /* Interrupt level for tx ints */
base_addr[CyPILR3] = 0x03; /* Interrupt level for rx ints */
/*
* Attempt to set up all channels to something reasonable, and
* bang out a INIT_CHAN command. We should then be able to limit
* the amount of fiddling we have to do in normal running.
*/
for (ch = 3; ch >= 0; ch--) {
base_addr[CyCAR] = (u_char) ch;
base_addr[CyIER] = 0;
base_addr[CyCMR] = CyASYNC;
base_addr[CyLICR] = (u_char) ch << 2;
base_addr[CyLIVR] = 0x5c;
base_addr[CyTCOR] = baud_co[spd];
base_addr[CyTBPR] = baud_bpr[spd];
base_addr[CyRCOR] = baud_co[spd] >> 5;
base_addr[CyRBPR] = baud_bpr[spd];
base_addr[CySCHR1] = 'Q' & 0x1f;
base_addr[CySCHR2] = 'X' & 0x1f;
base_addr[CySCRL] = 0;
base_addr[CySCRH] = 0;
base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE;
base_addr[CyCOR2] = 0;
base_addr[CyCOR3] = Cy_1_STOP;
base_addr[CyCOR4] = baud_cor4[spd];
base_addr[CyCOR5] = 0;
base_addr[CyCOR6] = 0;
base_addr[CyCOR7] = 0;
base_addr[CyRTPRL] = 2;
base_addr[CyRTPRH] = 0;
base_addr[CyMSVR1] = 0;
base_addr[CyMSVR2] = 0;
write_cy_cmd(base_addr, CyINIT_CHAN | CyDIS_RCVR | CyDIS_XMTR);
}
/*
* Now do specials for channel zero....
*/
base_addr[CyMSVR1] = CyRTS;
base_addr[CyMSVR2] = CyDTR;
base_addr[CyIER] = CyRxData;
write_cy_cmd(base_addr, CyENB_RCVR | CyENB_XMTR);
local_irq_restore(flags);
my_udelay(20000L); /* Let it all settle down */
printk("CD2401 initialised, chip is rev 0x%02x\n", base_addr[CyGFRCR]);
if (badspeed)
printk
(" WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x\n",
rcor >> 5, rbpr);
} /* serial_console_init */
static const struct tty_operations cy_ops = {
.open = cy_open,
.close = cy_close,
.write = cy_write,
.put_char = cy_put_char,
.flush_chars = cy_flush_chars,
.write_room = cy_write_room,
.chars_in_buffer = cy_chars_in_buffer,
.flush_buffer = cy_flush_buffer,
.ioctl = cy_ioctl,
.throttle = cy_throttle,
.unthrottle = cy_unthrottle,
.set_termios = cy_set_termios,
.stop = cy_stop,
.start = cy_start,
.hangup = cy_hangup,
.tiocmget = cy_tiocmget,
.tiocmset = cy_tiocmset,
};
/* The serial driver boot-time initialization code!
Hardware I/O ports are mapped to character special devices on a
first found, first allocated manner. That is, this code searches
for Cyclom cards in the system. As each is found, it is probed
to discover how many chips (and thus how many ports) are present.
These ports are mapped to the tty ports 64 and upward in monotonic
fashion. If an 8-port card is replaced with a 16-port card, the
port mapping on a following card will shift.
This approach is different from what is used in the other serial
device driver because the Cyclom is more properly a multiplexer,
not just an aggregation of serial ports on one card.
If there are more cards with more ports than have been statically
allocated above, a warning is printed and the extra ports are ignored.
*/
static int __init serial167_init(void)
{
struct cyclades_port *info;
int ret = 0;
int good_ports = 0;
int port_num = 0;
int index;
int DefSpeed;
#ifdef notyet
struct sigaction sa;
#endif
if (!(mvme16x_config & MVME16x_CONFIG_GOT_CD2401))
return 0;
cy_serial_driver = alloc_tty_driver(NR_PORTS);
if (!cy_serial_driver)
return -ENOMEM;
#if 0
scrn[1] = '\0';
#endif
show_version();
/* Has "console=0,9600n8" been used in bootinfo to change speed? */
if (serial_console_cflag)
DefSpeed = serial_console_cflag & 0017;
else {
DefSpeed = initial_console_speed;
serial_console_info = &cy_port[0];
serial_console_cflag = DefSpeed | CS8;
#if 0
serial_console = 64; /*callout_driver.minor_start */
#endif
}
/* Initialize the tty_driver structure */
cy_serial_driver->owner = THIS_MODULE;
cy_serial_driver->name = "ttyS";
cy_serial_driver->major = TTY_MAJOR;
cy_serial_driver->minor_start = 64;
cy_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
cy_serial_driver->subtype = SERIAL_TYPE_NORMAL;
cy_serial_driver->init_termios = tty_std_termios;
cy_serial_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
cy_serial_driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(cy_serial_driver, &cy_ops);
ret = tty_register_driver(cy_serial_driver);
if (ret) {
printk(KERN_ERR "Couldn't register MVME166/7 serial driver\n");
put_tty_driver(cy_serial_driver);
return ret;
}
port_num = 0;
info = cy_port;
for (index = 0; index < 1; index++) {
good_ports = 4;
if (port_num < NR_PORTS) {
while (good_ports-- && port_num < NR_PORTS) {
/*** initialize port ***/
info->magic = CYCLADES_MAGIC;
info->type = PORT_CIRRUS;
info->card = index;
info->line = port_num;
info->flags = STD_COM_FLAGS;
info->tty = NULL;
info->xmit_fifo_size = 12;
info->cor1 = CyPARITY_NONE | Cy_8_BITS;
info->cor2 = CyETC;
info->cor3 = Cy_1_STOP;
info->cor4 = 0x08; /* _very_ small receive threshold */
info->cor5 = 0;
info->cor6 = 0;
info->cor7 = 0;
info->tbpr = baud_bpr[DefSpeed]; /* Tx BPR */
info->tco = baud_co[DefSpeed]; /* Tx CO */
info->rbpr = baud_bpr[DefSpeed]; /* Rx BPR */
info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */
info->close_delay = 0;
info->x_char = 0;
info->count = 0;
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: setting count to 0\n",
__LINE__);
#endif
info->blocked_open = 0;
info->default_threshold = 0;
info->default_timeout = 0;
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
/* info->session */
/* info->pgrp */
/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
info->read_status_mask =
CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY |
CyFRAME | CyOVERRUN;
/* info->timeout */
printk("ttyS%d ", info->line);
port_num++;
info++;
if (!(port_num & 7)) {
printk("\n ");
}
}
}
printk("\n");
}
while (port_num < NR_PORTS) {
info->line = -1;
port_num++;
info++;
}
ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0,
"cd2401_errors", cd2401_rxerr_interrupt);
if (ret) {
printk(KERN_ERR "Could't get cd2401_errors IRQ");
goto cleanup_serial_driver;
}
ret = request_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt, 0,
"cd2401_modem", cd2401_modem_interrupt);
if (ret) {
printk(KERN_ERR "Could't get cd2401_modem IRQ");
goto cleanup_irq_cd2401_errors;
}
ret = request_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt, 0,
"cd2401_txints", cd2401_tx_interrupt);
if (ret) {
printk(KERN_ERR "Could't get cd2401_txints IRQ");
goto cleanup_irq_cd2401_modem;
}
ret = request_irq(MVME167_IRQ_SER_RX, cd2401_rx_interrupt, 0,
"cd2401_rxints", cd2401_rx_interrupt);
if (ret) {
printk(KERN_ERR "Could't get cd2401_rxints IRQ");
goto cleanup_irq_cd2401_txints;
}
/* Now we have registered the interrupt handlers, allow the interrupts */
pcc2chip[PccSCCMICR] = 0x15; /* Serial ints are level 5 */
pcc2chip[PccSCCTICR] = 0x15;
pcc2chip[PccSCCRICR] = 0x15;
pcc2chip[PccIMLR] = 3; /* Allow PCC2 ints above 3!? */
return 0;
cleanup_irq_cd2401_txints:
free_irq(MVME167_IRQ_SER_TX, cd2401_tx_interrupt);
cleanup_irq_cd2401_modem:
free_irq(MVME167_IRQ_SER_MODEM, cd2401_modem_interrupt);
cleanup_irq_cd2401_errors:
free_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt);
cleanup_serial_driver:
if (tty_unregister_driver(cy_serial_driver))
printk(KERN_ERR
"Couldn't unregister MVME166/7 serial driver\n");
put_tty_driver(cy_serial_driver);
return ret;
} /* serial167_init */
module_init(serial167_init);
#ifdef CYCLOM_SHOW_STATUS
static void show_status(int line_num)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
int channel;
struct cyclades_port *info;
unsigned long flags;
info = &cy_port[line_num];
channel = info->line;
printk(" channel %d\n", channel);
/**/ printk(" cy_port\n");
printk(" card line flags = %d %d %x\n",
info->card, info->line, info->flags);
printk
(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
(long)info->tty, info->read_status_mask, info->timeout,
info->xmit_fifo_size);
printk(" cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x\n",
info->cor1, info->cor2, info->cor3, info->cor4, info->cor5,
info->cor6, info->cor7);
printk(" tbpr,tco,rbpr,rco = %d %d %d %d\n", info->tbpr, info->tco,
info->rbpr, info->rco);
printk(" close_delay event count = %d %d %d\n", info->close_delay,
info->event, info->count);
printk(" x_char blocked_open = %x %x\n", info->x_char,
info->blocked_open);
printk(" open_wait = %lx %lx %lx\n", (long)info->open_wait);
local_irq_save(flags);
/* Global Registers */
printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
printk(" CyCAR %x\n", base_addr[CyCAR]);
printk(" CyRISR %x\n", base_addr[CyRISR]);
printk(" CyTISR %x\n", base_addr[CyTISR]);
printk(" CyMISR %x\n", base_addr[CyMISR]);
printk(" CyRIR %x\n", base_addr[CyRIR]);
printk(" CyTIR %x\n", base_addr[CyTIR]);
printk(" CyMIR %x\n", base_addr[CyMIR]);
printk(" CyTPR %x\n", base_addr[CyTPR]);
base_addr[CyCAR] = (u_char) channel;
/* Virtual Registers */
#if 0
printk(" CyRIVR %x\n", base_addr[CyRIVR]);
printk(" CyTIVR %x\n", base_addr[CyTIVR]);
printk(" CyMIVR %x\n", base_addr[CyMIVR]);
printk(" CyMISR %x\n", base_addr[CyMISR]);
#endif
/* Channel Registers */
printk(" CyCCR %x\n", base_addr[CyCCR]);
printk(" CyIER %x\n", base_addr[CyIER]);
printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
#if 0
printk(" CyCCSR %x\n", base_addr[CyCCSR]);
printk(" CyRDCR %x\n", base_addr[CyRDCR]);
#endif
printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
#if 0
printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
printk(" CySCRL %x\n", base_addr[CySCRL]);
printk(" CySCRH %x\n", base_addr[CySCRH]);
printk(" CyLNC %x\n", base_addr[CyLNC]);
printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
#endif
printk(" CyRTPRL %x\n", base_addr[CyRTPRL]);
printk(" CyRTPRH %x\n", base_addr[CyRTPRH]);
printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
printk(" CyRBPR %x\n", base_addr[CyRBPR]);
printk(" CyRCOR %x\n", base_addr[CyRCOR]);
printk(" CyTBPR %x\n", base_addr[CyTBPR]);
printk(" CyTCOR %x\n", base_addr[CyTCOR]);
local_irq_restore(flags);
} /* show_status */
#endif
#if 0
/* Dummy routine in mvme16x/config.c for now */
/* Serial console setup. Called from linux/init/main.c */
void console_setup(char *str, int *ints)
{
char *s;
int baud, bits, parity;
int cflag = 0;
/* Sanity check. */
if (ints[0] > 3 || ints[1] > 3)
return;
/* Get baud, bits and parity */
baud = 2400;
bits = 8;
parity = 'n';
if (ints[2])
baud = ints[2];
if ((s = strchr(str, ','))) {
do {
s++;
} while (*s >= '0' && *s <= '9');
if (*s)
parity = *s++;
if (*s)
bits = *s - '0';
}
/* Now construct a cflag setting. */
switch (baud) {
case 1200:
cflag |= B1200;
break;
case 9600:
cflag |= B9600;
break;
case 19200:
cflag |= B19200;
break;
case 38400:
cflag |= B38400;
break;
case 2400:
default:
cflag |= B2400;
break;
}
switch (bits) {
case 7:
cflag |= CS7;
break;
default:
case 8:
cflag |= CS8;
break;
}
switch (parity) {
case 'o':
case 'O':
cflag |= PARODD;
break;
case 'e':
case 'E':
cflag |= PARENB;
break;
}
serial_console_info = &cy_port[ints[1]];
serial_console_cflag = cflag;
serial_console = ints[1] + 64; /*callout_driver.minor_start */
}
#endif
/*
* The following is probably out of date for 2.1.x serial console stuff.
*
* The console is registered early on from arch/m68k/kernel/setup.c, and
* it therefore relies on the chip being setup correctly by 166-Bug. This
* seems reasonable, as the serial port has been used to invoke the system
* boot. It also means that this function must not rely on any data
* initialisation performed by serial167_init() etc.
*
* Of course, once the console has been registered, we had better ensure
* that serial167_init() doesn't leave the chip non-functional.
*
* The console must be locked when we get here.
*/
void serial167_console_write(struct console *co, const char *str,
unsigned count)
{
volatile unsigned char *base_addr = (u_char *) BASE_ADDR;
unsigned long flags;
volatile u_char sink;
u_char ier;
int port;
u_char do_lf = 0;
int i = 0;
local_irq_save(flags);
/* Ensure transmitter is enabled! */
port = 0;
base_addr[CyCAR] = (u_char) port;
while (base_addr[CyCCR])
;
base_addr[CyCCR] = CyENB_XMTR;
ier = base_addr[CyIER];
base_addr[CyIER] = CyTxMpty;
while (1) {
if (pcc2chip[PccSCCTICR] & 0x20) {
/* We have a Tx int. Acknowledge it */
sink = pcc2chip[PccTPIACKR];
if ((base_addr[CyLICR] >> 2) == port) {
if (i == count) {
/* Last char of string is now output */
base_addr[CyTEOIR] = CyNOTRANS;
break;
}
if (do_lf) {
base_addr[CyTDR] = '\n';
str++;
i++;
do_lf = 0;
} else if (*str == '\n') {
base_addr[CyTDR] = '\r';
do_lf = 1;
} else {
base_addr[CyTDR] = *str++;
i++;
}
base_addr[CyTEOIR] = 0;
} else
base_addr[CyTEOIR] = CyNOTRANS;
}
}
base_addr[CyIER] = ier;
local_irq_restore(flags);
}
static struct tty_driver *serial167_console_device(struct console *c,
int *index)
{
*index = c->index;
return cy_serial_driver;
}
static struct console sercons = {
.name = "ttyS",
.write = serial167_console_write,
.device = serial167_console_device,
.flags = CON_PRINTBUFFER,
.index = -1,
};
static int __init serial167_console_init(void)
{
if (vme_brdtype == VME_TYPE_MVME166 ||
vme_brdtype == VME_TYPE_MVME167 ||
vme_brdtype == VME_TYPE_MVME177) {
mvme167_serial_console_setup(0);
register_console(&sercons);
}
return 0;
}
console_initcall(serial167_console_init);
MODULE_LICENSE("GPL");
/*
* specialix.c -- specialix IO8+ multiport serial driver.
*
* Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
* Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
*
* Specialix pays for the development and support of this driver.
* Please DO contact io8-linux@specialix.co.uk if you require
* support. But please read the documentation (specialix.txt)
* first.
*
* This driver was developed in the BitWizard linux device
* driver service. If you require a linux device driver for your
* product, please contact devices@BitWizard.nl for a quote.
*
* This code is firmly based on the riscom/8 serial driver,
* written by Dmitry Gorodchanin. The specialix IO8+ card
* programming information was obtained from the CL-CD1865 Data
* Book, and Specialix document number 6200059: IO8+ Hardware
* Functional Specification.
*
* 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.
*
* Revision history:
*
* Revision 1.0: April 1st 1997.
* Initial release for alpha testing.
* Revision 1.1: April 14th 1997.
* Incorporated Richard Hudsons suggestions,
* removed some debugging printk's.
* Revision 1.2: April 15th 1997.
* Ported to 2.1.x kernels.
* Revision 1.3: April 17th 1997
* Backported to 2.0. (Compatibility macros).
* Revision 1.4: April 18th 1997
* Fixed DTR/RTS bug that caused the card to indicate
* "don't send data" to a modem after the password prompt.
* Fixed bug for premature (fake) interrupts.
* Revision 1.5: April 19th 1997
* fixed a minor typo in the header file, cleanup a little.
* performance warnings are now MAXed at once per minute.
* Revision 1.6: May 23 1997
* Changed the specialix=... format to include interrupt.
* Revision 1.7: May 27 1997
* Made many more debug printk's a compile time option.
* Revision 1.8: Jul 1 1997
* port to linux-2.1.43 kernel.
* Revision 1.9: Oct 9 1998
* Added stuff for the IO8+/PCI version.
* Revision 1.10: Oct 22 1999 / Jan 21 2000.
* Added stuff for setserial.
* Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
*
*/
#define VERSION "1.11"
/*
* There is a bunch of documentation about the card, jumpers, config
* settings, restrictions, cables, device names and numbers in
* Documentation/serial/specialix.txt
*/
#include <linux/module.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/serial.h>
#include <linux/fcntl.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/gfp.h>
#include "specialix_io8.h"
#include "cd1865.h"
/*
This driver can spew a whole lot of debugging output at you. If you
need maximum performance, you should disable the DEBUG define. To
aid in debugging in the field, I'm leaving the compile-time debug
features enabled, and disable them "runtime". That allows me to
instruct people with problems to enable debugging without requiring
them to recompile...
*/
#define DEBUG
static int sx_debug;
static int sx_rxfifo = SPECIALIX_RXFIFO;
static int sx_rtscts;
#ifdef DEBUG
#define dprintk(f, str...) if (sx_debug & f) printk(str)
#else
#define dprintk(f, str...) /* nothing */
#endif
#define SX_DEBUG_FLOW 0x0001
#define SX_DEBUG_DATA 0x0002
#define SX_DEBUG_PROBE 0x0004
#define SX_DEBUG_CHAN 0x0008
#define SX_DEBUG_INIT 0x0010
#define SX_DEBUG_RX 0x0020
#define SX_DEBUG_TX 0x0040
#define SX_DEBUG_IRQ 0x0080
#define SX_DEBUG_OPEN 0x0100
#define SX_DEBUG_TERMIOS 0x0200
#define SX_DEBUG_SIGNALS 0x0400
#define SX_DEBUG_FIFO 0x0800
#define func_enter() dprintk(SX_DEBUG_FLOW, "io8: enter %s\n", __func__)
#define func_exit() dprintk(SX_DEBUG_FLOW, "io8: exit %s\n", __func__)
/* Configurable options: */
/* Am I paranoid or not ? ;-) */
#define SPECIALIX_PARANOIA_CHECK
/*
* The following defines are mostly for testing purposes. But if you need
* some nice reporting in your syslog, you can define them also.
*/
#undef SX_REPORT_FIFO
#undef SX_REPORT_OVERRUN
#define SPECIALIX_LEGAL_FLAGS \
(ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
static struct tty_driver *specialix_driver;
static struct specialix_board sx_board[SX_NBOARD] = {
{ 0, SX_IOBASE1, 9, },
{ 0, SX_IOBASE2, 11, },
{ 0, SX_IOBASE3, 12, },
{ 0, SX_IOBASE4, 15, },
};
static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
static int sx_paranoia_check(struct specialix_port const *port,
char *name, const char *routine)
{
#ifdef SPECIALIX_PARANOIA_CHECK
static const char *badmagic = KERN_ERR
"sx: Warning: bad specialix port magic number for device %s in %s\n";
static const char *badinfo = KERN_ERR
"sx: Warning: null specialix port for device %s in %s\n";
if (!port) {
printk(badinfo, name, routine);
return 1;
}
if (port->magic != SPECIALIX_MAGIC) {
printk(badmagic, name, routine);
return 1;
}
#endif
return 0;
}
/*
*
* Service functions for specialix IO8+ driver.
*
*/
/* Get board number from pointer */
static inline int board_No(struct specialix_board *bp)
{
return bp - sx_board;
}
/* Get port number from pointer */
static inline int port_No(struct specialix_port const *port)
{
return SX_PORT(port - sx_port);
}
/* Get pointer to board from pointer to port */
static inline struct specialix_board *port_Board(
struct specialix_port const *port)
{
return &sx_board[SX_BOARD(port - sx_port)];
}
/* Input Byte from CL CD186x register */
static inline unsigned char sx_in(struct specialix_board *bp,
unsigned short reg)
{
bp->reg = reg | 0x80;
outb(reg | 0x80, bp->base + SX_ADDR_REG);
return inb(bp->base + SX_DATA_REG);
}
/* Output Byte to CL CD186x register */
static inline void sx_out(struct specialix_board *bp, unsigned short reg,
unsigned char val)
{
bp->reg = reg | 0x80;
outb(reg | 0x80, bp->base + SX_ADDR_REG);
outb(val, bp->base + SX_DATA_REG);
}
/* Input Byte from CL CD186x register */
static inline unsigned char sx_in_off(struct specialix_board *bp,
unsigned short reg)
{
bp->reg = reg;
outb(reg, bp->base + SX_ADDR_REG);
return inb(bp->base + SX_DATA_REG);
}
/* Output Byte to CL CD186x register */
static inline void sx_out_off(struct specialix_board *bp,
unsigned short reg, unsigned char val)
{
bp->reg = reg;
outb(reg, bp->base + SX_ADDR_REG);
outb(val, bp->base + SX_DATA_REG);
}
/* Wait for Channel Command Register ready */
static void sx_wait_CCR(struct specialix_board *bp)
{
unsigned long delay, flags;
unsigned char ccr;
for (delay = SX_CCR_TIMEOUT; delay; delay--) {
spin_lock_irqsave(&bp->lock, flags);
ccr = sx_in(bp, CD186x_CCR);
spin_unlock_irqrestore(&bp->lock, flags);
if (!ccr)
return;
udelay(1);
}
printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
}
/* Wait for Channel Command Register ready */
static void sx_wait_CCR_off(struct specialix_board *bp)
{
unsigned long delay;
unsigned char crr;
unsigned long flags;
for (delay = SX_CCR_TIMEOUT; delay; delay--) {
spin_lock_irqsave(&bp->lock, flags);
crr = sx_in_off(bp, CD186x_CCR);
spin_unlock_irqrestore(&bp->lock, flags);
if (!crr)
return;
udelay(1);
}
printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
}
/*
* specialix IO8+ IO range functions.
*/
static int sx_request_io_range(struct specialix_board *bp)
{
return request_region(bp->base,
bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
"specialix IO8+") == NULL;
}
static void sx_release_io_range(struct specialix_board *bp)
{
release_region(bp->base, bp->flags & SX_BOARD_IS_PCI ?
SX_PCI_IO_SPACE : SX_IO_SPACE);
}
/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
static int sx_set_irq(struct specialix_board *bp)
{
int virq;
int i;
unsigned long flags;
if (bp->flags & SX_BOARD_IS_PCI)
return 1;
switch (bp->irq) {
/* In the same order as in the docs... */
case 15:
virq = 0;
break;
case 12:
virq = 1;
break;
case 11:
virq = 2;
break;
case 9:
virq = 3;
break;
default:printk(KERN_ERR
"Speclialix: cannot set irq to %d.\n", bp->irq);
return 0;
}
spin_lock_irqsave(&bp->lock, flags);
for (i = 0; i < 2; i++) {
sx_out(bp, CD186x_CAR, i);
sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
}
spin_unlock_irqrestore(&bp->lock, flags);
return 1;
}
/* Reset and setup CD186x chip */
static int sx_init_CD186x(struct specialix_board *bp)
{
unsigned long flags;
int scaler;
int rv = 1;
func_enter();
sx_wait_CCR_off(bp); /* Wait for CCR ready */
spin_lock_irqsave(&bp->lock, flags);
sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
spin_unlock_irqrestore(&bp->lock, flags);
msleep(50); /* Delay 0.05 sec */
spin_lock_irqsave(&bp->lock, flags);
sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
/* Set RegAckEn */
sx_out_off(bp, CD186x_SRCR, sx_in(bp, CD186x_SRCR) | SRCR_REGACKEN);
/* Setting up prescaler. We need 4 ticks per 1 ms */
scaler = SX_OSCFREQ/SPECIALIX_TPS;
sx_out_off(bp, CD186x_PPRH, scaler >> 8);
sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
spin_unlock_irqrestore(&bp->lock, flags);
if (!sx_set_irq(bp)) {
/* Figure out how to pass this along... */
printk(KERN_ERR "Cannot set irq to %d.\n", bp->irq);
rv = 0;
}
func_exit();
return rv;
}
static int read_cross_byte(struct specialix_board *bp, int reg, int bit)
{
int i;
int t;
unsigned long flags;
spin_lock_irqsave(&bp->lock, flags);
for (i = 0, t = 0; i < 8; i++) {
sx_out_off(bp, CD186x_CAR, i);
if (sx_in_off(bp, reg) & bit)
t |= 1 << i;
}
spin_unlock_irqrestore(&bp->lock, flags);
return t;
}
/* Main probing routine, also sets irq. */
static int sx_probe(struct specialix_board *bp)
{
unsigned char val1, val2;
int rev;
int chip;
func_enter();
if (sx_request_io_range(bp)) {
func_exit();
return 1;
}
/* Are the I/O ports here ? */
sx_out_off(bp, CD186x_PPRL, 0x5a);
udelay(1);
val1 = sx_in_off(bp, CD186x_PPRL);
sx_out_off(bp, CD186x_PPRL, 0xa5);
udelay(1);
val2 = sx_in_off(bp, CD186x_PPRL);
if (val1 != 0x5a || val2 != 0xa5) {
printk(KERN_INFO
"sx%d: specialix IO8+ Board at 0x%03x not found.\n",
board_No(bp), bp->base);
sx_release_io_range(bp);
func_exit();
return 1;
}
/* Check the DSR lines that Specialix uses as board
identification */
val1 = read_cross_byte(bp, CD186x_MSVR, MSVR_DSR);
val2 = read_cross_byte(bp, CD186x_MSVR, MSVR_RTS);
dprintk(SX_DEBUG_INIT,
"sx%d: DSR lines are: %02x, rts lines are: %02x\n",
board_No(bp), val1, val2);
/* They managed to switch the bit order between the docs and
the IO8+ card. The new PCI card now conforms to old docs.
They changed the PCI docs to reflect the situation on the
old card. */
val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
if (val1 != val2) {
printk(KERN_INFO
"sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
board_No(bp), val2, bp->base, val1);
sx_release_io_range(bp);
func_exit();
return 1;
}
/* Reset CD186x again */
if (!sx_init_CD186x(bp)) {
sx_release_io_range(bp);
func_exit();
return 1;
}
sx_request_io_range(bp);
bp->flags |= SX_BOARD_PRESENT;
/* Chip revcode pkgtype
GFRCR SRCR bit 7
CD180 rev B 0x81 0
CD180 rev C 0x82 0
CD1864 rev A 0x82 1
CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
CD1865 rev B 0x84 1
-- Thanks to Gwen Wang, Cirrus Logic.
*/
switch (sx_in_off(bp, CD186x_GFRCR)) {
case 0x82:
chip = 1864;
rev = 'A';
break;
case 0x83:
chip = 1865;
rev = 'A';
break;
case 0x84:
chip = 1865;
rev = 'B';
break;
case 0x85:
chip = 1865;
rev = 'C';
break; /* Does not exist at this time */
default:
chip = -1;
rev = 'x';
}
dprintk(SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR));
printk(KERN_INFO
"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
board_No(bp), bp->base, bp->irq, chip, rev);
func_exit();
return 0;
}
/*
*
* Interrupt processing routines.
* */
static struct specialix_port *sx_get_port(struct specialix_board *bp,
unsigned char const *what)
{
unsigned char channel;
struct specialix_port *port = NULL;
channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
dprintk(SX_DEBUG_CHAN, "channel: %d\n", channel);
if (channel < CD186x_NCH) {
port = &sx_port[board_No(bp) * SX_NPORT + channel];
dprintk(SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",
board_No(bp) * SX_NPORT + channel, port,
port->port.flags & ASYNC_INITIALIZED);
if (port->port.flags & ASYNC_INITIALIZED) {
dprintk(SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
func_exit();
return port;
}
}
printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
board_No(bp), what, channel);
return NULL;
}
static void sx_receive_exc(struct specialix_board *bp)
{
struct specialix_port *port;
struct tty_struct *tty;
unsigned char status;
unsigned char ch, flag;
func_enter();
port = sx_get_port(bp, "Receive");
if (!port) {
dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
func_exit();
return;
}
tty = port->port.tty;
status = sx_in(bp, CD186x_RCSR);
dprintk(SX_DEBUG_RX, "status: 0x%x\n", status);
if (status & RCSR_OE) {
port->overrun++;
dprintk(SX_DEBUG_FIFO,
"sx%d: port %d: Overrun. Total %ld overruns.\n",
board_No(bp), port_No(port), port->overrun);
}
status &= port->mark_mask;
/* This flip buffer check needs to be below the reading of the
status register to reset the chip's IRQ.... */
if (tty_buffer_request_room(tty, 1) == 0) {
dprintk(SX_DEBUG_FIFO,
"sx%d: port %d: Working around flip buffer overflow.\n",
board_No(bp), port_No(port));
func_exit();
return;
}
ch = sx_in(bp, CD186x_RDR);
if (!status) {
func_exit();
return;
}
if (status & RCSR_TOUT) {
printk(KERN_INFO
"sx%d: port %d: Receiver timeout. Hardware problems ?\n",
board_No(bp), port_No(port));
func_exit();
return;
} else if (status & RCSR_BREAK) {
dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
board_No(bp), port_No(port));
flag = TTY_BREAK;
if (port->port.flags & ASYNC_SAK)
do_SAK(tty);
} else if (status & RCSR_PE)
flag = TTY_PARITY;
else if (status & RCSR_FE)
flag = TTY_FRAME;
else if (status & RCSR_OE)
flag = TTY_OVERRUN;
else
flag = TTY_NORMAL;
if (tty_insert_flip_char(tty, ch, flag))
tty_flip_buffer_push(tty);
func_exit();
}
static void sx_receive(struct specialix_board *bp)
{
struct specialix_port *port;
struct tty_struct *tty;
unsigned char count;
func_enter();
port = sx_get_port(bp, "Receive");
if (port == NULL) {
dprintk(SX_DEBUG_RX, "Hmm, couldn't find port.\n");
func_exit();
return;
}
tty = port->port.tty;
count = sx_in(bp, CD186x_RDCR);
dprintk(SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
port->hits[count > 8 ? 9 : count]++;
while (count--)
tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
tty_flip_buffer_push(tty);
func_exit();
}
static void sx_transmit(struct specialix_board *bp)
{
struct specialix_port *port;
struct tty_struct *tty;
unsigned char count;
func_enter();
port = sx_get_port(bp, "Transmit");
if (port == NULL) {
func_exit();
return;
}
dprintk(SX_DEBUG_TX, "port: %p\n", port);
tty = port->port.tty;
if (port->IER & IER_TXEMPTY) {
/* FIFO drained */
sx_out(bp, CD186x_CAR, port_No(port));
port->IER &= ~IER_TXEMPTY;
sx_out(bp, CD186x_IER, port->IER);
func_exit();
return;
}
if ((port->xmit_cnt <= 0 && !port->break_length)
|| tty->stopped || tty->hw_stopped) {
sx_out(bp, CD186x_CAR, port_No(port));
port->IER &= ~IER_TXRDY;
sx_out(bp, CD186x_IER, port->IER);
func_exit();
return;
}
if (port->break_length) {
if (port->break_length > 0) {
if (port->COR2 & COR2_ETC) {
sx_out(bp, CD186x_TDR, CD186x_C_ESC);
sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
port->COR2 &= ~COR2_ETC;
}
count = min_t(int, port->break_length, 0xff);
sx_out(bp, CD186x_TDR, CD186x_C_ESC);
sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
sx_out(bp, CD186x_TDR, count);
port->break_length -= count;
if (port->break_length == 0)
port->break_length--;
} else {
sx_out(bp, CD186x_TDR, CD186x_C_ESC);
sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
sx_out(bp, CD186x_COR2, port->COR2);
sx_wait_CCR(bp);
sx_out(bp, CD186x_CCR, CCR_CORCHG2);
port->break_length = 0;
}
func_exit();
return;
}
count = CD186x_NFIFO;
do {
sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
if (--port->xmit_cnt <= 0)
break;
} while (--count > 0);
if (port->xmit_cnt <= 0) {
sx_out(bp, CD186x_CAR, port_No(port));
port->IER &= ~IER_TXRDY;
sx_out(bp, CD186x_IER, port->IER);
}
if (port->xmit_cnt <= port->wakeup_chars)
tty_wakeup(tty);
func_exit();
}
static void sx_check_modem(struct specialix_board *bp)
{
struct specialix_port *port;
struct tty_struct *tty;
unsigned char mcr;
int msvr_cd;
dprintk(SX_DEBUG_SIGNALS, "Modem intr. ");
port = sx_get_port(bp, "Modem");
if (port == NULL)
return;
tty = port->port.tty;
mcr = sx_in(bp, CD186x_MCR);
if ((mcr & MCR_CDCHG)) {
dprintk(SX_DEBUG_SIGNALS, "CD just changed... ");
msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
if (msvr_cd) {
dprintk(SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
wake_up_interruptible(&port->port.open_wait);
} else {
dprintk(SX_DEBUG_SIGNALS, "Sending HUP.\n");
tty_hangup(tty);
}
}
#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
if (mcr & MCR_CTSCHG) {
if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
tty->hw_stopped = 0;
port->IER |= IER_TXRDY;
if (port->xmit_cnt <= port->wakeup_chars)
tty_wakeup(tty);
} else {
tty->hw_stopped = 1;
port->IER &= ~IER_TXRDY;
}
sx_out(bp, CD186x_IER, port->IER);
}
if (mcr & MCR_DSSXHG) {
if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
tty->hw_stopped = 0;
port->IER |= IER_TXRDY;
if (port->xmit_cnt <= port->wakeup_chars)
tty_wakeup(tty);
} else {
tty->hw_stopped = 1;
port->IER &= ~IER_TXRDY;
}
sx_out(bp, CD186x_IER, port->IER);
}
#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
/* Clear change bits */
sx_out(bp, CD186x_MCR, 0);
}
/* The main interrupt processing routine */
static irqreturn_t sx_interrupt(int dummy, void *dev_id)
{
unsigned char status;
unsigned char ack;
struct specialix_board *bp = dev_id;
unsigned long loop = 0;
int saved_reg;
unsigned long flags;
func_enter();
spin_lock_irqsave(&bp->lock, flags);
dprintk(SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __func__,
port_No(sx_get_port(bp, "INT")),
SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
if (!(bp->flags & SX_BOARD_ACTIVE)) {
dprintk(SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n",
bp->irq);
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
return IRQ_NONE;
}
saved_reg = bp->reg;
while (++loop < 16) {
status = sx_in(bp, CD186x_SRSR) &
(SRSR_RREQint | SRSR_TREQint | SRSR_MREQint);
if (status == 0)
break;
if (status & SRSR_RREQint) {
ack = sx_in(bp, CD186x_RRAR);
if (ack == (SX_ID | GIVR_IT_RCV))
sx_receive(bp);
else if (ack == (SX_ID | GIVR_IT_REXC))
sx_receive_exc(bp);
else
printk(KERN_ERR
"sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
board_No(bp), status, ack);
} else if (status & SRSR_TREQint) {
ack = sx_in(bp, CD186x_TRAR);
if (ack == (SX_ID | GIVR_IT_TX))
sx_transmit(bp);
else
printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
board_No(bp), status, ack,
port_No(sx_get_port(bp, "Int")));
} else if (status & SRSR_MREQint) {
ack = sx_in(bp, CD186x_MRAR);
if (ack == (SX_ID | GIVR_IT_MODEM))
sx_check_modem(bp);
else
printk(KERN_ERR
"sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
board_No(bp), status, ack);
}
sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
}
bp->reg = saved_reg;
outb(bp->reg, bp->base + SX_ADDR_REG);
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
return IRQ_HANDLED;
}
/*
* Routines for open & close processing.
*/
static void turn_ints_off(struct specialix_board *bp)
{
unsigned long flags;
func_enter();
spin_lock_irqsave(&bp->lock, flags);
(void) sx_in_off(bp, 0); /* Turn off interrupts. */
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
}
static void turn_ints_on(struct specialix_board *bp)
{
unsigned long flags;
func_enter();
spin_lock_irqsave(&bp->lock, flags);
(void) sx_in(bp, 0); /* Turn ON interrupts. */
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
}
/* Called with disabled interrupts */
static int sx_setup_board(struct specialix_board *bp)
{
int error;
if (bp->flags & SX_BOARD_ACTIVE)
return 0;
if (bp->flags & SX_BOARD_IS_PCI)
error = request_irq(bp->irq, sx_interrupt,
IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
else
error = request_irq(bp->irq, sx_interrupt,
IRQF_DISABLED, "specialix IO8+", bp);
if (error)
return error;
turn_ints_on(bp);
bp->flags |= SX_BOARD_ACTIVE;
return 0;
}
/* Called with disabled interrupts */
static void sx_shutdown_board(struct specialix_board *bp)
{
func_enter();
if (!(bp->flags & SX_BOARD_ACTIVE)) {
func_exit();
return;
}
bp->flags &= ~SX_BOARD_ACTIVE;
dprintk(SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
bp->irq, board_No(bp));
free_irq(bp->irq, bp);
turn_ints_off(bp);
func_exit();
}
static unsigned int sx_crtscts(struct tty_struct *tty)
{
if (sx_rtscts)
return C_CRTSCTS(tty);
return 1;
}
/*
* Setting up port characteristics.
* Must be called with disabled interrupts
*/
static void sx_change_speed(struct specialix_board *bp,
struct specialix_port *port)
{
struct tty_struct *tty;
unsigned long baud;
long tmp;
unsigned char cor1 = 0, cor3 = 0;
unsigned char mcor1 = 0, mcor2 = 0;
static unsigned long again;
unsigned long flags;
func_enter();
tty = port->port.tty;
if (!tty || !tty->termios) {
func_exit();
return;
}
port->IER = 0;
port->COR2 = 0;
/* Select port on the board */
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
/* The Specialix board doesn't implement the RTS lines.
They are used to set the IRQ level. Don't touch them. */
if (sx_crtscts(tty))
port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
else
port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
spin_unlock_irqrestore(&bp->lock, flags);
dprintk(SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
baud = tty_get_baud_rate(tty);
if (baud == 38400) {
if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
baud = 57600;
if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
baud = 115200;
}
if (!baud) {
/* Drop DTR & exit */
dprintk(SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
if (!sx_crtscts(tty)) {
port->MSVR &= ~MSVR_DTR;
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_MSVR, port->MSVR);
spin_unlock_irqrestore(&bp->lock, flags);
} else
dprintk(SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
return;
} else {
/* Set DTR on */
if (!sx_crtscts(tty))
port->MSVR |= MSVR_DTR;
}
/*
* Now we must calculate some speed depended things
*/
/* Set baud rate for port */
tmp = port->custom_divisor ;
if (tmp)
printk(KERN_INFO
"sx%d: Using custom baud rate divisor %ld. \n"
"This is an untested option, please be careful.\n",
port_No(port), tmp);
else
tmp = (((SX_OSCFREQ + baud/2) / baud + CD186x_TPC/2) /
CD186x_TPC);
if (tmp < 0x10 && time_before(again, jiffies)) {
again = jiffies + HZ * 60;
/* Page 48 of version 2.0 of the CL-CD1865 databook */
if (tmp >= 12) {
printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
"Performance degradation is possible.\n"
"Read specialix.txt for more info.\n",
port_No(port), tmp);
} else {
printk(KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
"Warning: overstressing Cirrus chip. This might not work.\n"
"Read specialix.txt for more info.\n", port_No(port), tmp);
}
}
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
sx_out(bp, CD186x_RBPRL, tmp & 0xff);
sx_out(bp, CD186x_TBPRL, tmp & 0xff);
spin_unlock_irqrestore(&bp->lock, flags);
if (port->custom_divisor)
baud = (SX_OSCFREQ + port->custom_divisor/2) /
port->custom_divisor;
baud = (baud + 5) / 10; /* Estimated CPS */
/* Two timer ticks seems enough to wakeup something like SLIP driver */
tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
SERIAL_XMIT_SIZE - 1 : tmp);
/* Receiver timeout will be transmission time for 1.5 chars */
tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
tmp = (tmp > 0xff) ? 0xff : tmp;
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_RTPR, tmp);
spin_unlock_irqrestore(&bp->lock, flags);
switch (C_CSIZE(tty)) {
case CS5:
cor1 |= COR1_5BITS;
break;
case CS6:
cor1 |= COR1_6BITS;
break;
case CS7:
cor1 |= COR1_7BITS;
break;
case CS8:
cor1 |= COR1_8BITS;
break;
}
if (C_CSTOPB(tty))
cor1 |= COR1_2SB;
cor1 |= COR1_IGNORE;
if (C_PARENB(tty)) {
cor1 |= COR1_NORMPAR;
if (C_PARODD(tty))
cor1 |= COR1_ODDP;
if (I_INPCK(tty))
cor1 &= ~COR1_IGNORE;
}
/* Set marking of some errors */
port->mark_mask = RCSR_OE | RCSR_TOUT;
if (I_INPCK(tty))
port->mark_mask |= RCSR_FE | RCSR_PE;
if (I_BRKINT(tty) || I_PARMRK(tty))
port->mark_mask |= RCSR_BREAK;
if (I_IGNPAR(tty))
port->mark_mask &= ~(RCSR_FE | RCSR_PE);
if (I_IGNBRK(tty)) {
port->mark_mask &= ~RCSR_BREAK;
if (I_IGNPAR(tty))
/* Real raw mode. Ignore all */
port->mark_mask &= ~RCSR_OE;
}
/* Enable Hardware Flow Control */
if (C_CRTSCTS(tty)) {
#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
port->IER |= IER_DSR | IER_CTS;
mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
spin_lock_irqsave(&bp->lock, flags);
tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) &
(MSVR_CTS|MSVR_DSR));
spin_unlock_irqrestore(&bp->lock, flags);
#else
port->COR2 |= COR2_CTSAE;
#endif
}
/* Enable Software Flow Control. FIXME: I'm not sure about this */
/* Some people reported that it works, but I still doubt it */
if (I_IXON(tty)) {
port->COR2 |= COR2_TXIBE;
cor3 |= (COR3_FCT | COR3_SCDE);
if (I_IXANY(tty))
port->COR2 |= COR2_IXM;
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
spin_unlock_irqrestore(&bp->lock, flags);
}
if (!C_CLOCAL(tty)) {
/* Enable CD check */
port->IER |= IER_CD;
mcor1 |= MCOR1_CDZD;
mcor2 |= MCOR2_CDOD;
}
if (C_CREAD(tty))
/* Enable receiver */
port->IER |= IER_RXD;
/* Set input FIFO size (1-8 bytes) */
cor3 |= sx_rxfifo;
/* Setting up CD186x channel registers */
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_COR1, cor1);
sx_out(bp, CD186x_COR2, port->COR2);
sx_out(bp, CD186x_COR3, cor3);
spin_unlock_irqrestore(&bp->lock, flags);
/* Make CD186x know about registers change */
sx_wait_CCR(bp);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
/* Setting up modem option registers */
dprintk(SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n",
mcor1, mcor2);
sx_out(bp, CD186x_MCOR1, mcor1);
sx_out(bp, CD186x_MCOR2, mcor2);
spin_unlock_irqrestore(&bp->lock, flags);
/* Enable CD186x transmitter & receiver */
sx_wait_CCR(bp);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
/* Enable interrupts */
sx_out(bp, CD186x_IER, port->IER);
/* And finally set the modem lines... */
sx_out(bp, CD186x_MSVR, port->MSVR);
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
}
/* Must be called with interrupts enabled */
static int sx_setup_port(struct specialix_board *bp,
struct specialix_port *port)
{
unsigned long flags;
func_enter();
if (port->port.flags & ASYNC_INITIALIZED) {
func_exit();
return 0;
}
if (!port->xmit_buf) {
/* We may sleep in get_zeroed_page() */
unsigned long tmp;
tmp = get_zeroed_page(GFP_KERNEL);
if (tmp == 0L) {
func_exit();
return -ENOMEM;
}
if (port->xmit_buf) {
free_page(tmp);
func_exit();
return -ERESTARTSYS;
}
port->xmit_buf = (unsigned char *) tmp;
}
spin_lock_irqsave(&port->lock, flags);
if (port->port.tty)
clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
sx_change_speed(bp, port);
port->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
return 0;
}
/* Must be called with interrupts disabled */
static void sx_shutdown_port(struct specialix_board *bp,
struct specialix_port *port)
{
struct tty_struct *tty;
int i;
unsigned long flags;
func_enter();
if (!(port->port.flags & ASYNC_INITIALIZED)) {
func_exit();
return;
}
if (sx_debug & SX_DEBUG_FIFO) {
dprintk(SX_DEBUG_FIFO,
"sx%d: port %d: %ld overruns, FIFO hits [ ",
board_No(bp), port_No(port), port->overrun);
for (i = 0; i < 10; i++)
dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
dprintk(SX_DEBUG_FIFO, "].\n");
}
if (port->xmit_buf) {
free_page((unsigned long) port->xmit_buf);
port->xmit_buf = NULL;
}
/* Select port */
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
tty = port->port.tty;
if (tty == NULL || C_HUPCL(tty)) {
/* Drop DTR */
sx_out(bp, CD186x_MSVDTR, 0);
}
spin_unlock_irqrestore(&bp->lock, flags);
/* Reset port */
sx_wait_CCR(bp);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
/* Disable all interrupts from this port */
port->IER = 0;
sx_out(bp, CD186x_IER, port->IER);
spin_unlock_irqrestore(&bp->lock, flags);
if (tty)
set_bit(TTY_IO_ERROR, &tty->flags);
port->port.flags &= ~ASYNC_INITIALIZED;
if (!bp->count)
sx_shutdown_board(bp);
func_exit();
}
static int block_til_ready(struct tty_struct *tty, struct file *filp,
struct specialix_port *port)
{
DECLARE_WAITQUEUE(wait, current);
struct specialix_board *bp = port_Board(port);
int retval;
int do_clocal = 0;
int CD;
unsigned long flags;
func_enter();
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
interruptible_sleep_on(&port->port.close_wait);
if (port->port.flags & ASYNC_HUP_NOTIFY) {
func_exit();
return -EAGAIN;
} else {
func_exit();
return -ERESTARTSYS;
}
}
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
port->port.flags |= ASYNC_NORMAL_ACTIVE;
func_exit();
return 0;
}
if (C_CLOCAL(tty))
do_clocal = 1;
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&port->port.open_wait, &wait);
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->port.count--;
spin_unlock_irqrestore(&port->lock, flags);
port->port.blocked_open++;
while (1) {
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
if (sx_crtscts(tty)) {
/* Activate RTS */
port->MSVR |= MSVR_DTR; /* WTF? */
sx_out(bp, CD186x_MSVR, port->MSVR);
} else {
/* Activate DTR */
port->MSVR |= MSVR_DTR;
sx_out(bp, CD186x_MSVR, port->MSVR);
}
spin_unlock_irqrestore(&bp->lock, flags);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(port->port.flags & ASYNC_INITIALIZED)) {
if (port->port.flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
break;
}
if (!(port->port.flags & ASYNC_CLOSING) &&
(do_clocal || CD))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
tty_unlock();
schedule();
tty_lock();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->port.open_wait, &wait);
spin_lock_irqsave(&port->lock, flags);
if (!tty_hung_up_p(filp))
port->port.count++;
port->port.blocked_open--;
spin_unlock_irqrestore(&port->lock, flags);
if (retval) {
func_exit();
return retval;
}
port->port.flags |= ASYNC_NORMAL_ACTIVE;
func_exit();
return 0;
}
static int sx_open(struct tty_struct *tty, struct file *filp)
{
int board;
int error;
struct specialix_port *port;
struct specialix_board *bp;
int i;
unsigned long flags;
func_enter();
board = SX_BOARD(tty->index);
if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
func_exit();
return -ENODEV;
}
bp = &sx_board[board];
port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
port->overrun = 0;
for (i = 0; i < 10; i++)
port->hits[i] = 0;
dprintk(SX_DEBUG_OPEN,
"Board = %d, bp = %p, port = %p, portno = %d.\n",
board, bp, port, SX_PORT(tty->index));
if (sx_paranoia_check(port, tty->name, "sx_open")) {
func_exit();
return -ENODEV;
}
error = sx_setup_board(bp);
if (error) {
func_exit();
return error;
}
spin_lock_irqsave(&bp->lock, flags);
port->port.count++;
bp->count++;
tty->driver_data = port;
port->port.tty = tty;
spin_unlock_irqrestore(&bp->lock, flags);
error = sx_setup_port(bp, port);
if (error) {
func_exit();
return error;
}
error = block_til_ready(tty, filp, port);
if (error) {
func_exit();
return error;
}
func_exit();
return 0;
}
static void sx_flush_buffer(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
unsigned long flags;
struct specialix_board *bp;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
func_exit();
return;
}
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
spin_unlock_irqrestore(&port->lock, flags);
tty_wakeup(tty);
func_exit();
}
static void sx_close(struct tty_struct *tty, struct file *filp)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned long flags;
unsigned long timeout;
func_enter();
if (!port || sx_paranoia_check(port, tty->name, "close")) {
func_exit();
return;
}
spin_lock_irqsave(&port->lock, flags);
if (tty_hung_up_p(filp)) {
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
return;
}
bp = port_Board(port);
if (tty->count == 1 && port->port.count != 1) {
printk(KERN_ERR "sx%d: sx_close: bad port count;"
" tty->count is 1, port count is %d\n",
board_No(bp), port->port.count);
port->port.count = 1;
}
if (port->port.count > 1) {
port->port.count--;
bp->count--;
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
return;
}
port->port.flags |= ASYNC_CLOSING;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
spin_unlock_irqrestore(&port->lock, flags);
dprintk(SX_DEBUG_OPEN, "Closing\n");
if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, port->port.closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
dprintk(SX_DEBUG_OPEN, "Closed\n");
port->IER &= ~IER_RXD;
if (port->port.flags & ASYNC_INITIALIZED) {
port->IER &= ~IER_TXRDY;
port->IER |= IER_TXEMPTY;
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
sx_out(bp, CD186x_IER, port->IER);
spin_unlock_irqrestore(&bp->lock, flags);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
timeout = jiffies+HZ;
while (port->IER & IER_TXEMPTY) {
set_current_state(TASK_INTERRUPTIBLE);
msleep_interruptible(jiffies_to_msecs(port->timeout));
if (time_after(jiffies, timeout)) {
printk(KERN_INFO "Timeout waiting for close\n");
break;
}
}
}
if (--bp->count < 0) {
printk(KERN_ERR
"sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
board_No(bp), bp->count, tty->index);
bp->count = 0;
}
if (--port->port.count < 0) {
printk(KERN_ERR
"sx%d: sx_close: bad port count for tty%d: %d\n",
board_No(bp), port_No(port), port->port.count);
port->port.count = 0;
}
sx_shutdown_port(bp, port);
sx_flush_buffer(tty);
tty_ldisc_flush(tty);
spin_lock_irqsave(&port->lock, flags);
tty->closing = 0;
port->port.tty = NULL;
spin_unlock_irqrestore(&port->lock, flags);
if (port->port.blocked_open) {
if (port->port.close_delay)
msleep_interruptible(
jiffies_to_msecs(port->port.close_delay));
wake_up_interruptible(&port->port.open_wait);
}
port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&port->port.close_wait);
func_exit();
}
static int sx_write(struct tty_struct *tty,
const unsigned char *buf, int count)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
int c, total = 0;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_write")) {
func_exit();
return 0;
}
bp = port_Board(port);
if (!port->xmit_buf) {
func_exit();
return 0;
}
while (1) {
spin_lock_irqsave(&port->lock, flags);
c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
SERIAL_XMIT_SIZE - port->xmit_head));
if (c <= 0) {
spin_unlock_irqrestore(&port->lock, flags);
break;
}
memcpy(port->xmit_buf + port->xmit_head, buf, c);
port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
port->xmit_cnt += c;
spin_unlock_irqrestore(&port->lock, flags);
buf += c;
count -= c;
total += c;
}
spin_lock_irqsave(&bp->lock, flags);
if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
!(port->IER & IER_TXRDY)) {
port->IER |= IER_TXRDY;
sx_out(bp, CD186x_CAR, port_No(port));
sx_out(bp, CD186x_IER, port->IER);
}
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
return total;
}
static int sx_put_char(struct tty_struct *tty, unsigned char ch)
{
struct specialix_port *port = tty->driver_data;
unsigned long flags;
struct specialix_board *bp;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
func_exit();
return 0;
}
dprintk(SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
if (!port->xmit_buf) {
func_exit();
return 0;
}
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
dprintk(SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n",
port->xmit_cnt, port->xmit_buf);
if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1 || !port->xmit_buf) {
spin_unlock_irqrestore(&port->lock, flags);
dprintk(SX_DEBUG_TX, "Exit size\n");
func_exit();
return 0;
}
dprintk(SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
port->xmit_buf[port->xmit_head++] = ch;
port->xmit_head &= SERIAL_XMIT_SIZE - 1;
port->xmit_cnt++;
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
return 1;
}
static void sx_flush_chars(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
unsigned long flags;
struct specialix_board *bp = port_Board(port);
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
func_exit();
return;
}
if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
!port->xmit_buf) {
func_exit();
return;
}
spin_lock_irqsave(&bp->lock, flags);
port->IER |= IER_TXRDY;
sx_out(port_Board(port), CD186x_CAR, port_No(port));
sx_out(port_Board(port), CD186x_IER, port->IER);
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
}
static int sx_write_room(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
int ret;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
func_exit();
return 0;
}
ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
if (ret < 0)
ret = 0;
func_exit();
return ret;
}
static int sx_chars_in_buffer(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
func_exit();
return 0;
}
func_exit();
return port->xmit_cnt;
}
static int sx_tiocmget(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned char status;
unsigned int result;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, __func__)) {
func_exit();
return -ENODEV;
}
bp = port_Board(port);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
status = sx_in(bp, CD186x_MSVR);
spin_unlock_irqrestore(&bp->lock, flags);
dprintk(SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
port_No(port), status, sx_in(bp, CD186x_CAR));
dprintk(SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
if (sx_crtscts(port->port.tty)) {
result = TIOCM_DTR | TIOCM_DSR
| ((status & MSVR_DTR) ? TIOCM_RTS : 0)
| ((status & MSVR_CD) ? TIOCM_CAR : 0)
| ((status & MSVR_CTS) ? TIOCM_CTS : 0);
} else {
result = TIOCM_RTS | TIOCM_DSR
| ((status & MSVR_DTR) ? TIOCM_DTR : 0)
| ((status & MSVR_CD) ? TIOCM_CAR : 0)
| ((status & MSVR_CTS) ? TIOCM_CTS : 0);
}
func_exit();
return result;
}
static int sx_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct specialix_port *port = tty->driver_data;
unsigned long flags;
struct specialix_board *bp;
func_enter();
if (sx_paranoia_check(port, tty->name, __func__)) {
func_exit();
return -ENODEV;
}
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
if (sx_crtscts(port->port.tty)) {
if (set & TIOCM_RTS)
port->MSVR |= MSVR_DTR;
} else {
if (set & TIOCM_DTR)
port->MSVR |= MSVR_DTR;
}
if (sx_crtscts(port->port.tty)) {
if (clear & TIOCM_RTS)
port->MSVR &= ~MSVR_DTR;
} else {
if (clear & TIOCM_DTR)
port->MSVR &= ~MSVR_DTR;
}
spin_lock(&bp->lock);
sx_out(bp, CD186x_CAR, port_No(port));
sx_out(bp, CD186x_MSVR, port->MSVR);
spin_unlock(&bp->lock);
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
return 0;
}
static int sx_send_break(struct tty_struct *tty, int length)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp = port_Board(port);
unsigned long flags;
func_enter();
if (length == 0 || length == -1)
return -EOPNOTSUPP;
spin_lock_irqsave(&port->lock, flags);
port->break_length = SPECIALIX_TPS / HZ * length;
port->COR2 |= COR2_ETC;
port->IER |= IER_TXRDY;
spin_lock(&bp->lock);
sx_out(bp, CD186x_CAR, port_No(port));
sx_out(bp, CD186x_COR2, port->COR2);
sx_out(bp, CD186x_IER, port->IER);
spin_unlock(&bp->lock);
spin_unlock_irqrestore(&port->lock, flags);
sx_wait_CCR(bp);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CCR, CCR_CORCHG2);
spin_unlock_irqrestore(&bp->lock, flags);
sx_wait_CCR(bp);
func_exit();
return 0;
}
static int sx_set_serial_info(struct specialix_port *port,
struct serial_struct __user *newinfo)
{
struct serial_struct tmp;
struct specialix_board *bp = port_Board(port);
int change_speed;
func_enter();
if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
func_exit();
return -EFAULT;
}
mutex_lock(&port->port.mutex);
change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
(tmp.flags & ASYNC_SPD_MASK));
change_speed |= (tmp.custom_divisor != port->custom_divisor);
if (!capable(CAP_SYS_ADMIN)) {
if ((tmp.close_delay != port->port.close_delay) ||
(tmp.closing_wait != port->port.closing_wait) ||
((tmp.flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
func_exit();
mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
(tmp.flags & ASYNC_USR_MASK));
port->custom_divisor = tmp.custom_divisor;
} else {
port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
(tmp.flags & ASYNC_FLAGS));
port->port.close_delay = tmp.close_delay;
port->port.closing_wait = tmp.closing_wait;
port->custom_divisor = tmp.custom_divisor;
}
if (change_speed)
sx_change_speed(bp, port);
func_exit();
mutex_unlock(&port->port.mutex);
return 0;
}
static int sx_get_serial_info(struct specialix_port *port,
struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
struct specialix_board *bp = port_Board(port);
func_enter();
memset(&tmp, 0, sizeof(tmp));
mutex_lock(&port->port.mutex);
tmp.type = PORT_CIRRUS;
tmp.line = port - sx_port;
tmp.port = bp->base;
tmp.irq = bp->irq;
tmp.flags = port->port.flags;
tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
tmp.close_delay = port->port.close_delay * HZ/100;
tmp.closing_wait = port->port.closing_wait * HZ/100;
tmp.custom_divisor = port->custom_divisor;
tmp.xmit_fifo_size = CD186x_NFIFO;
mutex_unlock(&port->port.mutex);
if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
func_exit();
return -EFAULT;
}
func_exit();
return 0;
}
static int sx_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct specialix_port *port = tty->driver_data;
void __user *argp = (void __user *)arg;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
func_exit();
return -ENODEV;
}
switch (cmd) {
case TIOCGSERIAL:
func_exit();
return sx_get_serial_info(port, argp);
case TIOCSSERIAL:
func_exit();
return sx_set_serial_info(port, argp);
default:
func_exit();
return -ENOIOCTLCMD;
}
func_exit();
return 0;
}
static void sx_throttle(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
func_exit();
return;
}
bp = port_Board(port);
/* Use DTR instead of RTS ! */
if (sx_crtscts(tty))
port->MSVR &= ~MSVR_DTR;
else {
/* Auch!!! I think the system shouldn't call this then. */
/* Or maybe we're supposed (allowed?) to do our side of hw
handshake anyway, even when hardware handshake is off.
When you see this in your logs, please report.... */
printk(KERN_ERR
"sx%d: Need to throttle, but can't (hardware hs is off)\n",
port_No(port));
}
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CAR, port_No(port));
spin_unlock_irqrestore(&bp->lock, flags);
if (I_IXOFF(tty)) {
sx_wait_CCR(bp);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CCR, CCR_SSCH2);
spin_unlock_irqrestore(&bp->lock, flags);
sx_wait_CCR(bp);
}
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_MSVR, port->MSVR);
spin_unlock_irqrestore(&bp->lock, flags);
func_exit();
}
static void sx_unthrottle(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
func_exit();
return;
}
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
/* XXXX Use DTR INSTEAD???? */
if (sx_crtscts(tty))
port->MSVR |= MSVR_DTR;
/* Else clause: see remark in "sx_throttle"... */
spin_lock(&bp->lock);
sx_out(bp, CD186x_CAR, port_No(port));
spin_unlock(&bp->lock);
if (I_IXOFF(tty)) {
spin_unlock_irqrestore(&port->lock, flags);
sx_wait_CCR(bp);
spin_lock_irqsave(&bp->lock, flags);
sx_out(bp, CD186x_CCR, CCR_SSCH1);
spin_unlock_irqrestore(&bp->lock, flags);
sx_wait_CCR(bp);
spin_lock_irqsave(&port->lock, flags);
}
spin_lock(&bp->lock);
sx_out(bp, CD186x_MSVR, port->MSVR);
spin_unlock(&bp->lock);
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
}
static void sx_stop(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_stop")) {
func_exit();
return;
}
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
port->IER &= ~IER_TXRDY;
spin_lock(&bp->lock);
sx_out(bp, CD186x_CAR, port_No(port));
sx_out(bp, CD186x_IER, port->IER);
spin_unlock(&bp->lock);
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
}
static void sx_start(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_start")) {
func_exit();
return;
}
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
port->IER |= IER_TXRDY;
spin_lock(&bp->lock);
sx_out(bp, CD186x_CAR, port_No(port));
sx_out(bp, CD186x_IER, port->IER);
spin_unlock(&bp->lock);
}
spin_unlock_irqrestore(&port->lock, flags);
func_exit();
}
static void sx_hangup(struct tty_struct *tty)
{
struct specialix_port *port = tty->driver_data;
struct specialix_board *bp;
unsigned long flags;
func_enter();
if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
func_exit();
return;
}
bp = port_Board(port);
sx_shutdown_port(bp, port);
spin_lock_irqsave(&port->lock, flags);
bp->count -= port->port.count;
if (bp->count < 0) {
printk(KERN_ERR
"sx%d: sx_hangup: bad board count: %d port: %d\n",
board_No(bp), bp->count, tty->index);
bp->count = 0;
}
port->port.count = 0;
port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
port->port.tty = NULL;
spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&port->port.open_wait);
func_exit();
}
static void sx_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct specialix_port *port = tty->driver_data;
unsigned long flags;
struct specialix_board *bp;
if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
return;
bp = port_Board(port);
spin_lock_irqsave(&port->lock, flags);
sx_change_speed(port_Board(port), port);
spin_unlock_irqrestore(&port->lock, flags);
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
sx_start(tty);
}
}
static const struct tty_operations sx_ops = {
.open = sx_open,
.close = sx_close,
.write = sx_write,
.put_char = sx_put_char,
.flush_chars = sx_flush_chars,
.write_room = sx_write_room,
.chars_in_buffer = sx_chars_in_buffer,
.flush_buffer = sx_flush_buffer,
.ioctl = sx_ioctl,
.throttle = sx_throttle,
.unthrottle = sx_unthrottle,
.set_termios = sx_set_termios,
.stop = sx_stop,
.start = sx_start,
.hangup = sx_hangup,
.tiocmget = sx_tiocmget,
.tiocmset = sx_tiocmset,
.break_ctl = sx_send_break,
};
static int sx_init_drivers(void)
{
int error;
int i;
func_enter();
specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
if (!specialix_driver) {
printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
func_exit();
return 1;
}
specialix_driver->owner = THIS_MODULE;
specialix_driver->name = "ttyW";
specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
specialix_driver->subtype = SERIAL_TYPE_NORMAL;
specialix_driver->init_termios = tty_std_termios;
specialix_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
specialix_driver->init_termios.c_ispeed = 9600;
specialix_driver->init_termios.c_ospeed = 9600;
specialix_driver->flags = TTY_DRIVER_REAL_RAW |
TTY_DRIVER_HARDWARE_BREAK;
tty_set_operations(specialix_driver, &sx_ops);
error = tty_register_driver(specialix_driver);
if (error) {
put_tty_driver(specialix_driver);
printk(KERN_ERR
"sx: Couldn't register specialix IO8+ driver, error = %d\n",
error);
func_exit();
return 1;
}
memset(sx_port, 0, sizeof(sx_port));
for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
sx_port[i].magic = SPECIALIX_MAGIC;
tty_port_init(&sx_port[i].port);
spin_lock_init(&sx_port[i].lock);
}
func_exit();
return 0;
}
static void sx_release_drivers(void)
{
func_enter();
tty_unregister_driver(specialix_driver);
put_tty_driver(specialix_driver);
func_exit();
}
/*
* This routine must be called by kernel at boot time
*/
static int __init specialix_init(void)
{
int i;
int found = 0;
func_enter();
printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
if (sx_rtscts)
printk(KERN_INFO
"sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
else
printk(KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
for (i = 0; i < SX_NBOARD; i++)
spin_lock_init(&sx_board[i].lock);
if (sx_init_drivers()) {
func_exit();
return -EIO;
}
for (i = 0; i < SX_NBOARD; i++)
if (sx_board[i].base && !sx_probe(&sx_board[i]))
found++;
#ifdef CONFIG_PCI
{
struct pci_dev *pdev = NULL;
i = 0;
while (i < SX_NBOARD) {
if (sx_board[i].flags & SX_BOARD_PRESENT) {
i++;
continue;
}
pdev = pci_get_device(PCI_VENDOR_ID_SPECIALIX,
PCI_DEVICE_ID_SPECIALIX_IO8, pdev);
if (!pdev)
break;
if (pci_enable_device(pdev))
continue;
sx_board[i].irq = pdev->irq;
sx_board[i].base = pci_resource_start(pdev, 2);
sx_board[i].flags |= SX_BOARD_IS_PCI;
if (!sx_probe(&sx_board[i]))
found++;
}
/* May exit pci_get sequence early with lots of boards */
if (pdev != NULL)
pci_dev_put(pdev);
}
#endif
if (!found) {
sx_release_drivers();
printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
func_exit();
return -EIO;
}
func_exit();
return 0;
}
static int iobase[SX_NBOARD] = {0,};
static int irq[SX_NBOARD] = {0,};
module_param_array(iobase, int, NULL, 0);
module_param_array(irq, int, NULL, 0);
module_param(sx_debug, int, 0);
module_param(sx_rtscts, int, 0);
module_param(sx_rxfifo, int, 0);
/*
* You can setup up to 4 boards.
* by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
* You should specify the IRQs too in that case "irq=....,...".
*
* More than 4 boards in one computer is not possible, as the card can
* only use 4 different interrupts.
*
*/
static int __init specialix_init_module(void)
{
int i;
func_enter();
if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
for (i = 0; i < SX_NBOARD; i++) {
sx_board[i].base = iobase[i];
sx_board[i].irq = irq[i];
sx_board[i].count = 0;
}
}
func_exit();
return specialix_init();
}
static void __exit specialix_exit_module(void)
{
int i;
func_enter();
sx_release_drivers();
for (i = 0; i < SX_NBOARD; i++)
if (sx_board[i].flags & SX_BOARD_PRESENT)
sx_release_io_range(&sx_board[i]);
func_exit();
}
static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = {
{ PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
{ }
};
MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
module_init(specialix_init_module);
module_exit(specialix_exit_module);
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SPECIALIX_NORMAL_MAJOR);
/*
* linux/drivers/char/specialix_io8.h --
* Specialix IO8+ multiport serial driver.
*
* Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
* Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
*
*
* Specialix pays for the development and support of this driver.
* Please DO contact io8-linux@specialix.co.uk if you require
* support.
*
* This driver was developed in the BitWizard linux device
* driver service. If you require a linux device driver for your
* product, please contact devices@BitWizard.nl for a quote.
*
* This code is firmly based on the riscom/8 serial driver,
* written by Dmitry Gorodchanin. The specialix IO8+ card
* programming information was obtained from the CL-CD1865 Data
* Book, and Specialix document number 6200059: IO8+ Hardware
* Functional Specification.
*
* 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.
* */
#ifndef __LINUX_SPECIALIX_H
#define __LINUX_SPECIALIX_H
#include <linux/serial.h>
#ifdef __KERNEL__
/* You can have max 4 ISA cards in one PC, and I recommend not much
more than a few PCI versions of the card. */
#define SX_NBOARD 8
/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
#define SX_IO_SPACE 4
/* The PCI version decodes 8 addresses, but still only 2 are used. */
#define SX_PCI_IO_SPACE 8
/* eight ports per board. */
#define SX_NPORT 8
#define SX_BOARD(line) ((line) / SX_NPORT)
#define SX_PORT(line) ((line) & (SX_NPORT - 1))
#define SX_DATA_REG 0 /* Base+0 : Data register */
#define SX_ADDR_REG 1 /* base+1 : Address register. */
#define MHz *1000000 /* I'm ashamed of myself. */
/* On-board oscillator frequency */
#define SX_OSCFREQ (25 MHz/2)
/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
/* Ticks per sec. Used for setting receiver timeout and break length */
#define SPECIALIX_TPS 4000
/* Yeah, after heavy testing I decided it must be 6.
* Sure, You can change it if needed.
*/
#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */
#define SPECIALIX_MAGIC 0x0907
#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait up to
10 milliseconds before the internal
processor is available again after
you give it a command */
#define SX_IOBASE1 0x100
#define SX_IOBASE2 0x180
#define SX_IOBASE3 0x250
#define SX_IOBASE4 0x260
struct specialix_board {
unsigned long flags;
unsigned short base;
unsigned char irq;
//signed char count;
int count;
unsigned char DTR;
int reg;
spinlock_t lock;
};
#define SX_BOARD_PRESENT 0x00000001
#define SX_BOARD_ACTIVE 0x00000002
#define SX_BOARD_IS_PCI 0x00000004
struct specialix_port {
int magic;
struct tty_port port;
int baud_base;
int flags;
int timeout;
unsigned char * xmit_buf;
int custom_divisor;
int xmit_head;
int xmit_tail;
int xmit_cnt;
short wakeup_chars;
short break_length;
unsigned char mark_mask;
unsigned char IER;
unsigned char MSVR;
unsigned char COR2;
unsigned long overrun;
unsigned long hits[10];
spinlock_t lock;
};
#endif /* __KERNEL__ */
#endif /* __LINUX_SPECIALIX_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment