aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/net/sunqe.h
blob: af34f36111edef7f64c31e402e26feb9a9550f5e (plain) (tree)





























































































































































































































































































































































                                                                                                 
/* $Id: sunqe.h,v 1.13 2000/02/09 11:15:42 davem Exp $
 * sunqe.h: Definitions for the Sun QuadEthernet driver.
 *
 * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
 */

#ifndef _SUNQE_H
#define _SUNQE_H

/* QEC global registers. */
#define GLOB_CTRL	0x00UL		/* Control			*/
#define GLOB_STAT	0x04UL		/* Status			*/
#define GLOB_PSIZE	0x08UL		/* Packet Size			*/
#define GLOB_MSIZE	0x0cUL		/* Local-memory Size		*/
#define GLOB_RSIZE	0x10UL		/* Receive partition size	*/
#define GLOB_TSIZE	0x14UL		/* Transmit partition size	*/
#define GLOB_REG_SIZE	0x18UL

#define GLOB_CTRL_MMODE       0x40000000 /* MACE qec mode            */
#define GLOB_CTRL_BMODE       0x10000000 /* BigMAC qec mode          */
#define GLOB_CTRL_EPAR        0x00000020 /* Enable parity            */
#define GLOB_CTRL_ACNTRL      0x00000018 /* SBUS arbitration control */
#define GLOB_CTRL_B64         0x00000004 /* 64 byte dvma bursts      */
#define GLOB_CTRL_B32         0x00000002 /* 32 byte dvma bursts      */
#define GLOB_CTRL_B16         0x00000000 /* 16 byte dvma bursts      */
#define GLOB_CTRL_RESET       0x00000001 /* Reset the QEC            */

#define GLOB_STAT_TX          0x00000008 /* BigMAC Transmit IRQ      */
#define GLOB_STAT_RX          0x00000004 /* BigMAC Receive IRQ       */
#define GLOB_STAT_BM          0x00000002 /* BigMAC Global IRQ        */
#define GLOB_STAT_ER          0x00000001 /* BigMAC Error IRQ         */

#define GLOB_PSIZE_2048       0x00       /* 2k packet size           */
#define GLOB_PSIZE_4096       0x01       /* 4k packet size           */
#define GLOB_PSIZE_6144       0x10       /* 6k packet size           */
#define GLOB_PSIZE_8192       0x11       /* 8k packet size           */

/* In MACE mode, there are four qe channels.  Each channel has it's own
 * status bits in the QEC status register.  This macro picks out the
 * ones you want.
 */
#define GLOB_STAT_PER_QE(status, channel) (((status) >> ((channel) * 4)) & 0xf)

/* The following registers are for per-qe channel information/status. */
#define CREG_CTRL	0x00UL	/* Control                   */
#define CREG_STAT	0x04UL	/* Status                    */
#define CREG_RXDS	0x08UL	/* RX descriptor ring ptr    */
#define CREG_TXDS	0x0cUL	/* TX descriptor ring ptr    */
#define CREG_RIMASK	0x10UL	/* RX Interrupt Mask         */
#define CREG_TIMASK	0x14UL	/* TX Interrupt Mask         */
#define CREG_QMASK	0x18UL	/* QEC Error Interrupt Mask  */
#define CREG_MMASK	0x1cUL	/* MACE Error Interrupt Mask */
#define CREG_RXWBUFPTR	0x20UL	/* Local memory rx write ptr */
#define CREG_RXRBUFPTR	0x24UL	/* Local memory rx read ptr  */
#define CREG_TXWBUFPTR	0x28UL	/* Local memory tx write ptr */
#define CREG_TXRBUFPTR	0x2cUL	/* Local memory tx read ptr  */
#define CREG_CCNT	0x30UL	/* Collision Counter         */
#define CREG_PIPG	0x34UL	/* Inter-Frame Gap           */
#define CREG_REG_SIZE	0x38UL

#define CREG_CTRL_RXOFF       0x00000004  /* Disable this qe's receiver*/
#define CREG_CTRL_RESET       0x00000002  /* Reset this qe channel     */
#define CREG_CTRL_TWAKEUP     0x00000001  /* Transmitter Wakeup, 'go'. */

#define CREG_STAT_EDEFER      0x10000000  /* Excessive Defers          */
#define CREG_STAT_CLOSS       0x08000000  /* Carrier Loss              */
#define CREG_STAT_ERETRIES    0x04000000  /* More than 16 retries      */
#define CREG_STAT_LCOLL       0x02000000  /* Late TX Collision         */
#define CREG_STAT_FUFLOW      0x01000000  /* FIFO Underflow            */
#define CREG_STAT_JERROR      0x00800000  /* Jabber Error              */
#define CREG_STAT_BERROR      0x00400000  /* Babble Error              */
#define CREG_STAT_TXIRQ       0x00200000  /* Transmit Interrupt        */
#define CREG_STAT_CCOFLOW     0x00100000  /* TX Coll-counter Overflow  */
#define CREG_STAT_TXDERROR    0x00080000  /* TX Descriptor is bogus    */
#define CREG_STAT_TXLERR      0x00040000  /* Late Transmit Error       */
#define CREG_STAT_TXPERR      0x00020000  /* Transmit Parity Error     */
#define CREG_STAT_TXSERR      0x00010000  /* Transmit SBUS error ack   */
#define CREG_STAT_RCCOFLOW    0x00001000  /* RX Coll-counter Overflow  */
#define CREG_STAT_RUOFLOW     0x00000800  /* Runt Counter Overflow     */
#define CREG_STAT_MCOFLOW     0x00000400  /* Missed Counter Overflow   */
#define CREG_STAT_RXFOFLOW    0x00000200  /* RX FIFO Overflow          */
#define CREG_STAT_RLCOLL      0x00000100  /* RX Late Collision         */
#define CREG_STAT_FCOFLOW     0x00000080  /* Frame Counter Overflow    */
#define CREG_STAT_CECOFLOW    0x00000040  /* CRC Error-counter Overflow*/
#define CREG_STAT_RXIRQ       0x00000020  /* Receive Interrupt         */
#define CREG_STAT_RXDROP      0x00000010  /* Dropped a RX'd packet     */
#define CREG_STAT_RXSMALL     0x00000008  /* Receive buffer too small  */
#define CREG_STAT_RXLERR      0x00000004  /* Receive Late Error        */
#define CREG_STAT_RXPERR      0x00000002  /* Receive Parity Error      */
#define CREG_STAT_RXSERR      0x00000001  /* Receive SBUS Error ACK    */

#define CREG_STAT_ERRORS      (CREG_STAT_EDEFER|CREG_STAT_CLOSS|CREG_STAT_ERETRIES|     \
			       CREG_STAT_LCOLL|CREG_STAT_FUFLOW|CREG_STAT_JERROR|       \
			       CREG_STAT_BERROR|CREG_STAT_CCOFLOW|CREG_STAT_TXDERROR|   \
			       CREG_STAT_TXLERR|CREG_STAT_TXPERR|CREG_STAT_TXSERR|      \
			       CREG_STAT_RCCOFLOW|CREG_STAT_RUOFLOW|CREG_STAT_MCOFLOW| \
			       CREG_STAT_RXFOFLOW|CREG_STAT_RLCOLL|CREG_STAT_FCOFLOW|   \
			       CREG_STAT_CECOFLOW|CREG_STAT_RXDROP|CREG_STAT_RXSMALL|   \
			       CREG_STAT_RXLERR|CREG_STAT_RXPERR|CREG_STAT_RXSERR)

#define CREG_QMASK_COFLOW     0x00100000  /* CollCntr overflow         */
#define CREG_QMASK_TXDERROR   0x00080000  /* TXD error                 */
#define CREG_QMASK_TXLERR     0x00040000  /* TX late error             */
#define CREG_QMASK_TXPERR     0x00020000  /* TX parity error           */
#define CREG_QMASK_TXSERR     0x00010000  /* TX sbus error ack         */
#define CREG_QMASK_RXDROP     0x00000010  /* RX drop                   */
#define CREG_QMASK_RXBERROR   0x00000008  /* RX buffer error           */
#define CREG_QMASK_RXLEERR    0x00000004  /* RX late error             */
#define CREG_QMASK_RXPERR     0x00000002  /* RX parity error           */
#define CREG_QMASK_RXSERR     0x00000001  /* RX sbus error ack         */

#define CREG_MMASK_EDEFER     0x10000000  /* Excess defer              */
#define CREG_MMASK_CLOSS      0x08000000  /* Carrier loss              */
#define CREG_MMASK_ERETRY     0x04000000  /* Excess retry              */
#define CREG_MMASK_LCOLL      0x02000000  /* Late collision error      */
#define CREG_MMASK_UFLOW      0x01000000  /* Underflow                 */
#define CREG_MMASK_JABBER     0x00800000  /* Jabber error              */
#define CREG_MMASK_BABBLE     0x00400000  /* Babble error              */
#define CREG_MMASK_OFLOW      0x00000800  /* Overflow                  */
#define CREG_MMASK_RXCOLL     0x00000400  /* RX Coll-Cntr overflow     */
#define CREG_MMASK_RPKT       0x00000200  /* Runt pkt overflow         */
#define CREG_MMASK_MPKT       0x00000100  /* Missed pkt overflow       */

#define CREG_PIPG_TENAB       0x00000020  /* Enable Throttle           */
#define CREG_PIPG_MMODE       0x00000010  /* Manual Mode               */
#define CREG_PIPG_WMASK       0x0000000f  /* SBUS Wait Mask            */

/* Per-channel AMD 79C940 MACE registers. */
#define MREGS_RXFIFO	0x00UL	/* Receive FIFO                   */
#define MREGS_TXFIFO	0x01UL	/* Transmit FIFO                  */
#define MREGS_TXFCNTL	0x02UL	/* Transmit Frame Control         */
#define MREGS_TXFSTAT	0x03UL	/* Transmit Frame Status          */
#define MREGS_TXRCNT	0x04UL	/* Transmit Retry Count           */
#define MREGS_RXFCNTL	0x05UL	/* Receive Frame Control          */
#define MREGS_RXFSTAT	0x06UL	/* Receive Frame Status           */
#define MREGS_FFCNT	0x07UL	/* FIFO Frame Count               */
#define MREGS_IREG	0x08UL	/* Interrupt Register             */
#define MREGS_IMASK	0x09UL	/* Interrupt Mask                 */
#define MREGS_POLL	0x0aUL	/* POLL Register                  */
#define MREGS_BCONFIG	0x0bUL	/* BIU Config                     */
#define MREGS_FCONFIG	0x0cUL	/* FIFO Config                    */
#define MREGS_MCONFIG	0x0dUL	/* MAC Config                     */
#define MREGS_PLSCONFIG	0x0eUL	/* PLS Config                     */
#define MREGS_PHYCONFIG	0x0fUL	/* PHY Config                     */
#define MREGS_CHIPID1	0x10UL	/* Chip-ID, low bits              */
#define MREGS_CHIPID2	0x11UL	/* Chip-ID, high bits             */
#define MREGS_IACONFIG	0x12UL	/* Internal Address Config        */
	/* 0x13UL, reserved */
#define MREGS_FILTER	0x14UL	/* Logical Address Filter         */
#define MREGS_ETHADDR	0x15UL	/* Our Ethernet Address           */
	/* 0x16UL, reserved */
	/* 0x17UL, reserved */
#define MREGS_MPCNT	0x18UL	/* Missed Packet Count            */
	/* 0x19UL, reserved */
#define MREGS_RPCNT	0x1aUL	/* Runt Packet Count              */
#define MREGS_RCCNT	0x1bUL	/* RX Collision Count             */
	/* 0x1cUL, reserved */
#define MREGS_UTEST	0x1dUL	/* User Test                      */
#define MREGS_RTEST1	0x1eUL	/* Reserved Test 1                */
#define MREGS_RTEST2	0x1fUL	/* Reserved Test 2                */
#define MREGS_REG_SIZE	0x20UL

#define MREGS_TXFCNTL_DRETRY        0x80 /* Retry disable                  */
#define MREGS_TXFCNTL_DFCS          0x08 /* Disable TX FCS                 */
#define MREGS_TXFCNTL_AUTOPAD       0x01 /* TX auto pad                    */

#define MREGS_TXFSTAT_VALID         0x80 /* TX valid                       */
#define MREGS_TXFSTAT_UNDERFLOW     0x40 /* TX underflow                   */
#define MREGS_TXFSTAT_LCOLL         0x20 /* TX late collision              */
#define MREGS_TXFSTAT_MRETRY        0x10 /* TX > 1 retries                 */
#define MREGS_TXFSTAT_ORETRY        0x08 /* TX 1 retry                     */
#define MREGS_TXFSTAT_PDEFER        0x04 /* TX pkt deferred                */
#define MREGS_TXFSTAT_CLOSS         0x02 /* TX carrier lost                */
#define MREGS_TXFSTAT_RERROR        0x01 /* TX retry error                 */

#define MREGS_TXRCNT_EDEFER         0x80 /* TX Excess defers               */
#define MREGS_TXRCNT_CMASK          0x0f /* TX retry count                 */

#define MREGS_RXFCNTL_LOWLAT        0x08 /* RX low latency                 */
#define MREGS_RXFCNTL_AREJECT       0x04 /* RX addr match rej              */
#define MREGS_RXFCNTL_AUTOSTRIP     0x01 /* RX auto strip                  */

#define MREGS_RXFSTAT_OVERFLOW      0x80 /* RX overflow                    */
#define MREGS_RXFSTAT_LCOLL         0x40 /* RX late collision              */
#define MREGS_RXFSTAT_FERROR        0x20 /* RX framing error               */
#define MREGS_RXFSTAT_FCSERROR      0x10 /* RX FCS error                   */
#define MREGS_RXFSTAT_RBCNT         0x0f /* RX msg byte count              */

#define MREGS_FFCNT_RX              0xf0 /* RX FIFO frame cnt              */
#define MREGS_FFCNT_TX              0x0f /* TX FIFO frame cnt              */

#define MREGS_IREG_JABBER           0x80 /* IRQ Jabber error               */
#define MREGS_IREG_BABBLE           0x40 /* IRQ Babble error               */
#define MREGS_IREG_COLL             0x20 /* IRQ Collision error            */
#define MREGS_IREG_RCCO             0x10 /* IRQ Collision cnt overflow     */
#define MREGS_IREG_RPKTCO           0x08 /* IRQ Runt packet count overflow */
#define MREGS_IREG_MPKTCO           0x04 /* IRQ missed packet cnt overflow */
#define MREGS_IREG_RXIRQ            0x02 /* IRQ RX'd a packet              */
#define MREGS_IREG_TXIRQ            0x01 /* IRQ TX'd a packet              */

#define MREGS_IMASK_BABBLE          0x40 /* IMASK Babble errors            */
#define MREGS_IMASK_COLL            0x20 /* IMASK Collision errors         */
#define MREGS_IMASK_MPKTCO          0x04 /* IMASK Missed pkt cnt overflow  */
#define MREGS_IMASK_RXIRQ           0x02 /* IMASK RX interrupts            */
#define MREGS_IMASK_TXIRQ           0x01 /* IMASK TX interrupts            */

#define MREGS_POLL_TXVALID          0x80 /* TX is valid                    */
#define MREGS_POLL_TDTR             0x40 /* TX data transfer request       */
#define MREGS_POLL_RDTR             0x20 /* RX data transfer request       */

#define MREGS_BCONFIG_BSWAP         0x40 /* Byte Swap                      */
#define MREGS_BCONFIG_4TS           0x00 /* 4byte transmit start point     */
#define MREGS_BCONFIG_16TS          0x10 /* 16byte transmit start point    */
#define MREGS_BCONFIG_64TS          0x20 /* 64byte transmit start point    */
#define MREGS_BCONFIG_112TS         0x30 /* 112byte transmit start point   */
#define MREGS_BCONFIG_RESET         0x01 /* SW-Reset the MACE              */

#define MREGS_FCONFIG_TXF8          0x00 /* TX fifo 8 write cycles         */
#define MREGS_FCONFIG_TXF32         0x80 /* TX fifo 32 write cycles        */
#define MREGS_FCONFIG_TXF16         0x40 /* TX fifo 16 write cycles        */
#define MREGS_FCONFIG_RXF64         0x20 /* RX fifo 64 write cycles        */
#define MREGS_FCONFIG_RXF32         0x10 /* RX fifo 32 write cycles        */
#define MREGS_FCONFIG_RXF16         0x00 /* RX fifo 16 write cycles        */
#define MREGS_FCONFIG_TFWU          0x08 /* TX fifo watermark update       */
#define MREGS_FCONFIG_RFWU          0x04 /* RX fifo watermark update       */
#define MREGS_FCONFIG_TBENAB        0x02 /* TX burst enable                */
#define MREGS_FCONFIG_RBENAB        0x01 /* RX burst enable                */

#define MREGS_MCONFIG_PROMISC       0x80 /* Promiscuous mode enable        */
#define MREGS_MCONFIG_TPDDISAB      0x40 /* TX 2part deferral enable       */
#define MREGS_MCONFIG_MBAENAB       0x20 /* Modified backoff enable        */
#define MREGS_MCONFIG_RPADISAB      0x08 /* RX physical addr disable       */
#define MREGS_MCONFIG_RBDISAB       0x04 /* RX broadcast disable           */
#define MREGS_MCONFIG_TXENAB        0x02 /* Enable transmitter             */
#define MREGS_MCONFIG_RXENAB        0x01 /* Enable receiver                */

#define MREGS_PLSCONFIG_TXMS        0x08 /* TX mode select                 */
#define MREGS_PLSCONFIG_GPSI        0x06 /* Use GPSI connector             */
#define MREGS_PLSCONFIG_DAI         0x04 /* Use DAI connector              */
#define MREGS_PLSCONFIG_TP          0x02 /* Use TwistedPair connector      */
#define MREGS_PLSCONFIG_AUI         0x00 /* Use AUI connector              */
#define MREGS_PLSCONFIG_IOENAB      0x01 /* PLS I/O enable                 */

#define MREGS_PHYCONFIG_LSTAT       0x80 /* Link status                    */
#define MREGS_PHYCONFIG_LTESTDIS    0x40 /* Disable link test logic        */
#define MREGS_PHYCONFIG_RXPOLARITY  0x20 /* RX polarity                    */
#define MREGS_PHYCONFIG_APCDISAB    0x10 /* AutoPolarityCorrect disab      */
#define MREGS_PHYCONFIG_LTENAB      0x08 /* Select low threshold           */
#define MREGS_PHYCONFIG_AUTO        0x04 /* Connector port auto-sel        */
#define MREGS_PHYCONFIG_RWU         0x02 /* Remote WakeUp                  */
#define MREGS_PHYCONFIG_AW          0x01 /* Auto Wakeup                    */

#define MREGS_IACONFIG_ACHNGE       0x80 /* Do address change              */
#define MREGS_IACONFIG_PARESET      0x04 /* Physical address reset         */
#define MREGS_IACONFIG_LARESET      0x02 /* Logical address reset          */

#define MREGS_UTEST_RTRENAB         0x80 /* Enable resv test register      */
#define MREGS_UTEST_RTRDISAB        0x40 /* Disab resv test register       */
#define MREGS_UTEST_RPACCEPT        0x20 /* Accept runt packets            */
#define MREGS_UTEST_FCOLL           0x10 /* Force collision status         */
#define MREGS_UTEST_FCSENAB         0x08 /* Enable FCS on RX               */
#define MREGS_UTEST_INTLOOPM        0x06 /* Intern lpback w/MENDEC         */
#define MREGS_UTEST_INTLOOP         0x04 /* Intern lpback                  */
#define MREGS_UTEST_EXTLOOP         0x02 /* Extern lpback                  */
#define MREGS_UTEST_NOLOOP          0x00 /* No loopback                    */

struct qe_rxd {
	u32 rx_flags;
	u32 rx_addr;
};

#define RXD_OWN      0x80000000 /* Ownership.      */
#define RXD_UPDATE   0x10000000 /* Being Updated?  */
#define RXD_LENGTH   0x000007ff /* Packet Length.  */

struct qe_txd {
	u32 tx_flags;
	u32 tx_addr;
};

#define TXD_OWN      0x80000000 /* Ownership.      */
#define TXD_SOP      0x40000000 /* Start Of Packet */
#define TXD_EOP      0x20000000 /* End Of Packet   */
#define TXD_UPDATE   0x10000000 /* Being Updated?  */
#define TXD_LENGTH   0x000007ff /* Packet Length.  */

#define TX_RING_MAXSIZE   256
#define RX_RING_MAXSIZE   256

#define TX_RING_SIZE      16
#define RX_RING_SIZE      16

#define NEXT_RX(num)       (((num) + 1) & (RX_RING_MAXSIZE - 1))
#define NEXT_TX(num)       (((num) + 1) & (TX_RING_MAXSIZE - 1))
#define PREV_RX(num)       (((num) - 1) & (RX_RING_MAXSIZE - 1))
#define PREV_TX(num)       (((num) - 1) & (TX_RING_MAXSIZE - 1))

#define TX_BUFFS_AVAIL(qp)                                    \
        (((qp)->tx_old <= (qp)->tx_new) ?                     \
	  (qp)->tx_old + (TX_RING_SIZE - 1) - (qp)->tx_new :  \
			    (qp)->tx_old - (qp)->tx_new - 1)

struct qe_init_block {
	struct qe_rxd qe_rxd[RX_RING_MAXSIZE];
	struct qe_txd qe_txd[TX_RING_MAXSIZE];
};

#define qib_offset(mem, elem) \
((__u32)((unsigned long)(&(((struct qe_init_block *)0)->mem[elem]))))

struct sunqe;

struct sunqec {
	void __iomem		*gregs;		/* QEC Global Registers         */
	struct sunqe		*qes[4];	/* Each child MACE              */
	unsigned int            qec_bursts;	/* Support burst sizes          */
	struct sbus_dev		*qec_sdev;	/* QEC's SBUS device            */
	struct sunqec		*next_module;	/* List of all QECs in system   */
};

#define PKT_BUF_SZ	1664
#define RXD_PKT_SZ	1664

struct sunqe_buffers {
	u8	tx_buf[TX_RING_SIZE][PKT_BUF_SZ];
	u8	__pad[2];
	u8	rx_buf[RX_RING_SIZE][PKT_BUF_SZ];
};

#define qebuf_offset(mem, elem) \
((__u32)((unsigned long)(&(((struct sunqe_buffers *)0)->mem[elem][0]))))

struct sunqe {
	void __iomem			*qcregs;		/* QEC per-channel Registers   */
	void __iomem			*mregs;		/* Per-channel MACE Registers  */
	struct qe_init_block      	*qe_block;	/* RX and TX descriptors       */
	__u32                      	qblock_dvma;	/* RX and TX descriptors       */
	spinlock_t			lock;		/* Protects txfull state       */
	int                        	rx_new, rx_old;	/* RX ring extents	       */
	int			   	tx_new, tx_old;	/* TX ring extents	       */
	struct sunqe_buffers		*buffers;	/* CPU visible address.        */
	__u32				buffers_dvma;	/* DVMA visible address.       */
	struct sunqec			*parent;
	u8				mconfig;	/* Base MACE mconfig value     */
	struct net_device_stats		net_stats;	/* Statistical counters        */
	struct sbus_dev			*qe_sdev;	/* QE's SBUS device struct     */
	struct net_device		*dev;		/* QE's netdevice struct       */
	int				channel;	/* Who am I?                   */
};

#endif /* !(_SUNQE_H) */
e>1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325

















































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                           
















































































                                                                                
                                 
 
                                                                    
 
                         






                                                              
                                                              

                                                            
                                                              

                      
                                                                         




























































































                                                                                            
                                 
                      
                                                                    



                                                   
                          



                                                                           
                                        
                                                                                       















































                                                                                                                               
                                 
                   
                                                                    
 
                         






                                                              
                                                              



                                                                   
                                                              























































































































































































































                                                                                 
/*
 * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
 *
 * Copyleft  (C) 2007 Kees Lemmens (adapted for kernel 2.6.20)
 * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail)
 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2003 IBM Corp.
 *
 * Many thanks to the authors of pl2303 driver: all functions in this file
 * are heavily based on pl2303 code, buffering code is a 1-to-1 copy.
 *
 * Warning! You use this driver on your own risk! The only official
 * description of this device I have is datasheet from manufacturer,
 * and it doesn't contain almost any information needed to write a driver.
 * Almost all knowlegde used while writing this driver was gathered by:
 *  - analyzing traffic between device and the M$ Windows 2000 driver,
 *  - trying different bit combinations and checking pin states
 *    with a voltmeter,
 *  - receiving malformed frames and producing buffer overflows
 *    to learn how errors are reported,
 * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE
 * CONNECTED TO IT!
 *
 * 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.
 *
 * See Documentation/usb/usb-serial.txt for more information on using this driver
 *
 * TODO:
 *  - implement correct flushing for ioctls and oti6858_close()
 *  - check how errors (rx overflow, parity error, framing error) are reported
 *  - implement oti6858_break_ctl()
 *  - implement more ioctls
 *  - test/implement flow control
 *  - allow setting custom baud rates
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <asm/uaccess.h>
#include "oti6858.h"

#define OTI6858_DESCRIPTION \
	"Ours Technology Inc. OTi-6858 USB to serial adapter driver"
#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>"
#define OTI6858_VERSION "0.1"

static struct usb_device_id id_table [] = {
	{ USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) },
	{ }
};

MODULE_DEVICE_TABLE(usb, id_table);

static struct usb_driver oti6858_driver = {
	.name =		"oti6858",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table,
	.no_dynamic_id = 	1,
};

static int debug;


/* buffering code, copied from pl2303 driver */
#define PL2303_BUF_SIZE		1024
#define PL2303_TMP_BUF_SIZE	1024

struct pl2303_buf {
	unsigned int	buf_size;
	char		*buf_buf;
	char		*buf_get;
	char		*buf_put;
};

/* requests */
#define	OTI6858_REQ_GET_STATUS		(USB_DIR_IN | USB_TYPE_VENDOR | 0x00)
#define	OTI6858_REQ_T_GET_STATUS	0x01

#define	OTI6858_REQ_SET_LINE		(USB_DIR_OUT | USB_TYPE_VENDOR | 0x00)
#define	OTI6858_REQ_T_SET_LINE		0x00

#define	OTI6858_REQ_CHECK_TXBUFF	(USB_DIR_IN | USB_TYPE_VENDOR | 0x01)
#define	OTI6858_REQ_T_CHECK_TXBUFF	0x00

/* format of the control packet */
struct oti6858_control_pkt {
	u16	divisor;	/* baud rate = 96000000 / (16 * divisor), LE */
#define OTI6858_MAX_BAUD_RATE	3000000
	u8	frame_fmt;
#define FMT_STOP_BITS_MASK	0xc0
#define FMT_STOP_BITS_1		0x00
#define FMT_STOP_BITS_2		0x40	/* 1.5 stop bits if FMT_DATA_BITS_5 */
#define FMT_PARITY_MASK		0x38
#define FMT_PARITY_NONE		0x00
#define FMT_PARITY_ODD		0x08
#define FMT_PARITY_EVEN		0x18
#define FMT_PARITY_MARK		0x28
#define FMT_PARITY_SPACE	0x38
#define FMT_DATA_BITS_MASK	0x03
#define FMT_DATA_BITS_5		0x00
#define FMT_DATA_BITS_6		0x01
#define FMT_DATA_BITS_7		0x02
#define FMT_DATA_BITS_8		0x03
	u8	something;	/* always equals 0x43 */
	u8	control;	/* settings of flow control lines */
#define CONTROL_MASK		0x0c
#define CONTROL_DTR_HIGH	0x08
#define CONTROL_RTS_HIGH	0x04
	u8	tx_status;
#define	TX_BUFFER_EMPTIED	0x09
	u8	pin_state;
#define PIN_MASK		0x3f
#define PIN_RTS			0x20	/* output pin */
#define PIN_CTS			0x10	/* input pin, active low */
#define PIN_DSR			0x08	/* input pin, active low */
#define PIN_DTR			0x04	/* output pin */
#define PIN_RI			0x02	/* input pin, active low */
#define PIN_DCD			0x01	/* input pin, active low */
	u8	rx_bytes_avail;		/* number of bytes in rx buffer */;
};

#define OTI6858_CTRL_PKT_SIZE	sizeof(struct oti6858_control_pkt)
#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \
	(    ((a)->divisor == (priv)->pending_setup.divisor) \
	  && ((a)->control == (priv)->pending_setup.control) \
	  && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) )

/* function prototypes */
static int oti6858_open(struct usb_serial_port *port, struct file *filp);
static void oti6858_close(struct usb_serial_port *port, struct file *filp);
static void oti6858_set_termios(struct usb_serial_port *port,
				struct ktermios *old);
static int oti6858_ioctl(struct usb_serial_port *port, struct file *file,
			unsigned int cmd, unsigned long arg);
static void oti6858_read_int_callback(struct urb *urb);
static void oti6858_read_bulk_callback(struct urb *urb);
static void oti6858_write_bulk_callback(struct urb *urb);
static int oti6858_write(struct usb_serial_port *port,
			const unsigned char *buf, int count);
static int oti6858_write_room(struct usb_serial_port *port);
static void oti6858_break_ctl(struct usb_serial_port *port, int break_state);
static int oti6858_chars_in_buffer(struct usb_serial_port *port);
static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file);
static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file,
				unsigned int set, unsigned int clear);
static int oti6858_startup(struct usb_serial *serial);
static void oti6858_shutdown(struct usb_serial *serial);

/* functions operating on buffers */
static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
static void pl2303_buf_free(struct pl2303_buf *pb);
static void pl2303_buf_clear(struct pl2303_buf *pb);
static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
					unsigned int count);
static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
					unsigned int count);


/* device info */
static struct usb_serial_driver oti6858_device = {
	.driver = {
		.owner =	THIS_MODULE,
		.name =		"oti6858",
	},
	.id_table =		id_table,
	.num_interrupt_in =	1,
	.num_bulk_in =		1,
	.num_bulk_out =		1,
	.num_ports =		1,
	.open =			oti6858_open,
	.close =		oti6858_close,
	.write =		oti6858_write,
	.ioctl =		oti6858_ioctl,
	.break_ctl =		oti6858_break_ctl,
	.set_termios =		oti6858_set_termios,
	.tiocmget =		oti6858_tiocmget,
	.tiocmset =		oti6858_tiocmset,
	.read_bulk_callback =	oti6858_read_bulk_callback,
	.read_int_callback =	oti6858_read_int_callback,
	.write_bulk_callback =	oti6858_write_bulk_callback,
	.write_room =		oti6858_write_room,
	.chars_in_buffer =	oti6858_chars_in_buffer,
	.attach =		oti6858_startup,
	.shutdown =		oti6858_shutdown,
};

struct oti6858_private {
	spinlock_t lock;

	struct pl2303_buf *buf;
	struct oti6858_control_pkt status;

	struct {
		u8 read_urb_in_use;
		u8 write_urb_in_use;
		u8 termios_initialized;
	} flags;
	struct delayed_work delayed_write_work;

	struct {
		u16 divisor;
		u8 frame_fmt;
		u8 control;
	} pending_setup;
	u8 transient;
	u8 setup_done;
	struct delayed_work delayed_setup_work;

	wait_queue_head_t intr_wait;
        struct usb_serial_port *port;   /* USB port with which associated */
};

#undef dbg
/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */
#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg)

static void setup_line(struct work_struct *work)
{
	struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_setup_work.work);
	struct usb_serial_port *port = priv->port;
	struct oti6858_control_pkt *new_setup;
	unsigned long flags;
	int result;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	if ((new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) {
		dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__);
		/* we will try again */
		schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2));
		return;
	}

	result = usb_control_msg(port->serial->dev,
				usb_rcvctrlpipe(port->serial->dev, 0),
				OTI6858_REQ_T_GET_STATUS,
				OTI6858_REQ_GET_STATUS,
				0, 0,
				new_setup, OTI6858_CTRL_PKT_SIZE,
				100);

	if (result != OTI6858_CTRL_PKT_SIZE) {
		dev_err(&port->dev, "%s(): error reading status", __FUNCTION__);
		kfree(new_setup);
		/* we will try again */
		schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2));
		return;
	}

	spin_lock_irqsave(&priv->lock, flags);
	if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) {
		new_setup->divisor = priv->pending_setup.divisor;
		new_setup->control = priv->pending_setup.control;
		new_setup->frame_fmt = priv->pending_setup.frame_fmt;

		spin_unlock_irqrestore(&priv->lock, flags);
		result = usb_control_msg(port->serial->dev,
					usb_sndctrlpipe(port->serial->dev, 0),
					OTI6858_REQ_T_SET_LINE,
					OTI6858_REQ_SET_LINE,
					0, 0,
					new_setup, OTI6858_CTRL_PKT_SIZE,
					100);
	} else {
		spin_unlock_irqrestore(&priv->lock, flags);
		result = 0;
	}
	kfree(new_setup);

	spin_lock_irqsave(&priv->lock, flags);
	if (result != OTI6858_CTRL_PKT_SIZE)
		priv->transient = 0;
	priv->setup_done = 1;
	spin_unlock_irqrestore(&priv->lock, flags);

	dbg("%s(): submitting interrupt urb", __FUNCTION__);
	port->interrupt_in_urb->dev = port->serial->dev;
	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
	if (result != 0) {
		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
				" with error %d\n", __FUNCTION__, result);
	}
}

void send_data(struct work_struct *work)
{
	struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_write_work.work);
	struct usb_serial_port *port = priv->port;
	int count = 0, result;
	unsigned long flags;
	unsigned char allow;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	spin_lock_irqsave(&priv->lock, flags);
	if (priv->flags.write_urb_in_use) {
		spin_unlock_irqrestore(&priv->lock, flags);
		schedule_delayed_work(&priv->delayed_write_work, msecs_to_jiffies(2));
		return;
	}
	priv->flags.write_urb_in_use = 1;

	count = pl2303_buf_data_avail(priv->buf);
	spin_unlock_irqrestore(&priv->lock, flags);
	if (count > port->bulk_out_size)
		count = port->bulk_out_size;

	if (count != 0) {
		result = usb_control_msg(port->serial->dev,
				usb_rcvctrlpipe(port->serial->dev, 0),
				OTI6858_REQ_T_CHECK_TXBUFF,
				OTI6858_REQ_CHECK_TXBUFF,
				count, 0, &allow, 1, 100);
		if (result != 1 || allow != 0)
			count = 0;
	}

	if (count == 0) {
		priv->flags.write_urb_in_use = 0;

		dbg("%s(): submitting interrupt urb", __FUNCTION__);
		port->interrupt_in_urb->dev = port->serial->dev;
		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
		if (result != 0) {
			dev_err(&port->dev, "%s(): usb_submit_urb() failed"
				" with error %d\n", __FUNCTION__, result);
		}
		return;
	}

	spin_lock_irqsave(&priv->lock, flags);
	pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, count);
	spin_unlock_irqrestore(&priv->lock, flags);

	port->write_urb->transfer_buffer_length = count;
	port->write_urb->dev = port->serial->dev;
	result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
	if (result != 0) {
		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
			       " with error %d\n", __FUNCTION__, result);
		priv->flags.write_urb_in_use = 0;
	}

	usb_serial_port_softint(port);
}

static int oti6858_startup(struct usb_serial *serial)
{
        struct usb_serial_port *port = serial->port[0];
        struct oti6858_private *priv;
	int i;

	for (i = 0; i < serial->num_ports; ++i) {
		priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL);
		if (!priv)
			break;
		priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
		if (priv->buf == NULL) {
			kfree(priv);
			break;
		}

		spin_lock_init(&priv->lock);
		init_waitqueue_head(&priv->intr_wait);
//		INIT_WORK(&priv->setup_work, setup_line, serial->port[i]);
//		INIT_WORK(&priv->write_work, send_data, serial->port[i]);
		priv->port = port;
		INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line);
		INIT_DELAYED_WORK(&priv->delayed_write_work, send_data);

		usb_set_serial_port_data(serial->port[i], priv);
	}
	if (i == serial->num_ports)
		return 0;

	for (--i; i >= 0; --i) {
		priv = usb_get_serial_port_data(serial->port[i]);
		pl2303_buf_free(priv->buf);
		kfree(priv);
		usb_set_serial_port_data(serial->port[i], NULL);
	}
	return -ENOMEM;
}

static int oti6858_write(struct usb_serial_port *port,
			const unsigned char *buf, int count)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;

	dbg("%s(port = %d, count = %d)", __FUNCTION__, port->number, count);

	if (!count)
		return count;

	spin_lock_irqsave(&priv->lock, flags);
	count = pl2303_buf_put(priv->buf, buf, count);
	spin_unlock_irqrestore(&priv->lock, flags);

	return count;
}

static int oti6858_write_room(struct usb_serial_port *port)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	int room = 0;
	unsigned long flags;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	spin_lock_irqsave(&priv->lock, flags);
	room = pl2303_buf_space_avail(priv->buf);
	spin_unlock_irqrestore(&priv->lock, flags);

	return room;
}

static int oti6858_chars_in_buffer(struct usb_serial_port *port)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	int chars = 0;
	unsigned long flags;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	spin_lock_irqsave(&priv->lock, flags);
	chars = pl2303_buf_data_avail(priv->buf);
	spin_unlock_irqrestore(&priv->lock, flags);

	return chars;
}

static void oti6858_set_termios(struct usb_serial_port *port,
				struct ktermios *old_termios)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	unsigned int cflag;
	u8 frame_fmt, control;
	u16 divisor;
	int br;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	if ((!port->tty) || (!port->tty->termios)) {
		dbg("%s(): no tty structures", __FUNCTION__);
		return;
	}

	spin_lock_irqsave(&priv->lock, flags);
	if (!priv->flags.termios_initialized) {
		*(port->tty->termios) = tty_std_termios;
		port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
		priv->flags.termios_initialized = 1;
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	cflag = port->tty->termios->c_cflag;

	spin_lock_irqsave(&priv->lock, flags);
	divisor = priv->pending_setup.divisor;
	frame_fmt = priv->pending_setup.frame_fmt;
	control = priv->pending_setup.control;
	spin_unlock_irqrestore(&priv->lock, flags);

	frame_fmt &= ~FMT_DATA_BITS_MASK;
	switch (cflag & CSIZE) {
		case CS5:
			frame_fmt |= FMT_DATA_BITS_5;
			break;
		case CS6:
			frame_fmt |= FMT_DATA_BITS_6;
			break;
		case CS7:
			frame_fmt |= FMT_DATA_BITS_7;
			break;
		default:
		case CS8:
			frame_fmt |= FMT_DATA_BITS_8;
			break;
	}

	/* manufacturer claims that this device can work with baud rates
	 * up to 3 Mbps; I've tested it only on 115200 bps, so I can't
	 * guarantee that any other baud rate will work (especially
	 * the higher ones)
	 */
	br = tty_get_baud_rate(port->tty);
	if (br == 0) {
		divisor = 0;
	} else if (br <= OTI6858_MAX_BAUD_RATE) {
		int real_br;

		divisor = (96000000 + 8 * br) / (16 * br);
		real_br = 96000000 / (16 * divisor);
		if ((((real_br - br) * 100 + br - 1) / br) > 2) {
			dbg("%s(): baud rate %d is invalid", __FUNCTION__, br);
			return;
		}
		divisor = cpu_to_le16(divisor);
	} else {
		dbg("%s(): baud rate %d is too high", __FUNCTION__, br);
		return;
	}

	frame_fmt &= ~FMT_STOP_BITS_MASK;
	if ((cflag & CSTOPB) != 0) {
		frame_fmt |= FMT_STOP_BITS_2;
	} else {
		frame_fmt |= FMT_STOP_BITS_1;
	}

	frame_fmt &= ~FMT_PARITY_MASK;
	if ((cflag & PARENB) != 0) {
		if ((cflag & PARODD) != 0) {
			frame_fmt |= FMT_PARITY_ODD;
		} else {
			frame_fmt |= FMT_PARITY_EVEN;
		}
	} else {
		frame_fmt |= FMT_PARITY_NONE;
	}

	control &= ~CONTROL_MASK;
	if ((cflag & CRTSCTS) != 0)
		control |= (CONTROL_DTR_HIGH | CONTROL_RTS_HIGH);

	/* change control lines if we are switching to or from B0 */
	/* FIXME:
	spin_lock_irqsave(&priv->lock, flags);
	control = priv->line_control;
	if ((cflag & CBAUD) == B0)
		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
	else
		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
	if (control != priv->line_control) {
		control = priv->line_control;
		spin_unlock_irqrestore(&priv->lock, flags);
		set_control_lines(serial->dev, control);
	} else {
		spin_unlock_irqrestore(&priv->lock, flags);
	}
	*/

	spin_lock_irqsave(&priv->lock, flags);
	if (divisor != priv->pending_setup.divisor
			|| control != priv->pending_setup.control
			|| frame_fmt != priv->pending_setup.frame_fmt) {
		priv->pending_setup.divisor = divisor;
		priv->pending_setup.control = control;
		priv->pending_setup.frame_fmt = frame_fmt;
	}
	spin_unlock_irqrestore(&priv->lock, flags);
}

static int oti6858_open(struct usb_serial_port *port, struct file *filp)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	struct ktermios tmp_termios;
	struct usb_serial *serial = port->serial;
	struct oti6858_control_pkt *buf;
	unsigned long flags;
	int result;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	usb_clear_halt(serial->dev, port->write_urb->pipe);
	usb_clear_halt(serial->dev, port->read_urb->pipe);

	if (port->open_count != 1)
		return 0;

	if ((buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) {
		dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__);
		return -ENOMEM;
	}

	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
				OTI6858_REQ_T_GET_STATUS,
				OTI6858_REQ_GET_STATUS,
				0, 0,
				buf, OTI6858_CTRL_PKT_SIZE,
				100);
	if (result != OTI6858_CTRL_PKT_SIZE) {
		/* assume default (after power-on reset) values */
		buf->divisor = cpu_to_le16(0x009c);	/* 38400 bps */
		buf->frame_fmt = 0x03;	/* 8N1 */
		buf->something = 0x43;
		buf->control = 0x4c;	/* DTR, RTS */
		buf->tx_status = 0x00;
		buf->pin_state = 0x5b;	/* RTS, CTS, DSR, DTR, RI, DCD */
		buf->rx_bytes_avail = 0x00;
	}

	spin_lock_irqsave(&priv->lock, flags);
	memcpy(&priv->status, buf, OTI6858_CTRL_PKT_SIZE);
	priv->pending_setup.divisor = buf->divisor;
	priv->pending_setup.frame_fmt = buf->frame_fmt;
	priv->pending_setup.control = buf->control;
	spin_unlock_irqrestore(&priv->lock, flags);
	kfree(buf);

	dbg("%s(): submitting interrupt urb", __FUNCTION__);
	port->interrupt_in_urb->dev = serial->dev;
	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
	if (result != 0) {
		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
			       " with error %d\n", __FUNCTION__, result);
		oti6858_close(port, NULL);
		return -EPROTO;
	}

	/* setup termios */
	if (port->tty)
		oti6858_set_termios(port, &tmp_termios);

	return 0;
}

static void oti6858_close(struct usb_serial_port *port, struct file *filp)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	long timeout;
	wait_queue_t wait;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	/* wait for data to drain from the buffer */
	spin_lock_irqsave(&priv->lock, flags);
	timeout = 30 * HZ;	/* PL2303_CLOSING_WAIT */
	init_waitqueue_entry(&wait, current);
	add_wait_queue(&port->tty->write_wait, &wait);
	dbg("%s(): entering wait loop", __FUNCTION__);
	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (pl2303_buf_data_avail(priv->buf) == 0
		|| timeout == 0 || signal_pending(current)
		|| !usb_get_intfdata(port->serial->interface))	/* disconnect */
			break;
		spin_unlock_irqrestore(&priv->lock, flags);
		timeout = schedule_timeout(timeout);
		spin_lock_irqsave(&priv->lock, flags);
	}
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&port->tty->write_wait, &wait);
	dbg("%s(): after wait loop", __FUNCTION__);

	/* clear out any remaining data in the buffer */
	pl2303_buf_clear(priv->buf);
	spin_unlock_irqrestore(&priv->lock, flags);

	/* wait for characters to drain from the device */
	/* (this is long enough for the entire 256 byte */
	/* pl2303 hardware buffer to drain with no flow */
	/* control for data rates of 1200 bps or more, */
	/* for lower rates we should really know how much */
	/* data is in the buffer to compute a delay */
	/* that is not unnecessarily long) */
	/* FIXME
	bps = tty_get_baud_rate(port->tty);
	if (bps > 1200)
		timeout = max((HZ*2560)/bps,HZ/10);
	else
	*/
		timeout = 2*HZ;
	schedule_timeout_interruptible(timeout);
	dbg("%s(): after schedule_timeout_interruptible()", __FUNCTION__);

	/* cancel scheduled setup */
	cancel_delayed_work(&priv->delayed_setup_work);
	cancel_delayed_work(&priv->delayed_write_work);
	flush_scheduled_work();

	/* shutdown our urbs */
	dbg("%s(): shutting down urbs", __FUNCTION__);
	usb_kill_urb(port->write_urb);
	usb_kill_urb(port->read_urb);
	usb_kill_urb(port->interrupt_in_urb);

	/*
	if (port->tty && (port->tty->termios->c_cflag) & HUPCL) {
		// drop DTR and RTS
		spin_lock_irqsave(&priv->lock, flags);
		priv->pending_setup.control &= ~CONTROL_MASK;
		spin_unlock_irqrestore(&priv->lock, flags);
	}
	*/
}

static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file,
				unsigned int set, unsigned int clear)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	u8 control;

	dbg("%s(port = %d, set = 0x%08x, clear = 0x%08x)",
				__FUNCTION__, port->number, set, clear);

	if (!usb_get_intfdata(port->serial->interface))
		return -ENODEV;

	/* FIXME: check if this is correct (active high/low) */
	spin_lock_irqsave(&priv->lock, flags);
	control = priv->pending_setup.control;
	if ((set & TIOCM_RTS) != 0)
		control |= CONTROL_RTS_HIGH;
	if ((set & TIOCM_DTR) != 0)
		control |= CONTROL_DTR_HIGH;
	if ((clear & TIOCM_RTS) != 0)
		control &= ~CONTROL_RTS_HIGH;
	if ((clear & TIOCM_DTR) != 0)
		control &= ~CONTROL_DTR_HIGH;

	if (control != priv->pending_setup.control) {
		priv->pending_setup.control = control;
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	return 0;
}

static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	unsigned pin_state;
	unsigned result = 0;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	if (!usb_get_intfdata(port->serial->interface))
		return -ENODEV;

	spin_lock_irqsave(&priv->lock, flags);
	pin_state = priv->status.pin_state & PIN_MASK;
	spin_unlock_irqrestore(&priv->lock, flags);

	/* FIXME: check if this is correct (active high/low) */
	if ((pin_state & PIN_RTS) != 0)
		result |= TIOCM_RTS;
	if ((pin_state & PIN_CTS) != 0)
		result |= TIOCM_CTS;
	if ((pin_state & PIN_DSR) != 0)
		result |= TIOCM_DSR;
	if ((pin_state & PIN_DTR) != 0)
		result |= TIOCM_DTR;
	if ((pin_state & PIN_RI) != 0)
		result |= TIOCM_RI;
	if ((pin_state & PIN_DCD) != 0)
		result |= TIOCM_CD;

	dbg("%s() = 0x%08x", __FUNCTION__, result);

	return result;
}

static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
{
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	unsigned int prev, status;
	unsigned int changed;

	spin_lock_irqsave(&priv->lock, flags);
	prev = priv->status.pin_state;
	spin_unlock_irqrestore(&priv->lock, flags);

	while (1) {
		wait_event_interruptible(priv->intr_wait, priv->status.pin_state != prev);
		if (signal_pending(current))
			return -ERESTARTSYS;

		spin_lock_irqsave(&priv->lock, flags);
		status = priv->status.pin_state & PIN_MASK;
		spin_unlock_irqrestore(&priv->lock, flags);

		changed = prev ^ status;
		/* FIXME: check if this is correct (active high/low) */
		if (	((arg & TIOCM_RNG) && (changed & PIN_RI)) ||
			((arg & TIOCM_DSR) && (changed & PIN_DSR)) ||
			((arg & TIOCM_CD)  && (changed & PIN_DCD)) ||
			((arg & TIOCM_CTS) && (changed & PIN_CTS))) {
				return 0;
		}
		prev = status;
	}

	/* NOTREACHED */
	return 0;
}

static int oti6858_ioctl(struct usb_serial_port *port, struct file *file,
			unsigned int cmd, unsigned long arg)
{
	void __user *user_arg = (void __user *) arg;
	unsigned int x;

	dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)",
				__FUNCTION__, port->number, cmd, arg);

	switch (cmd) {
		case TCFLSH:
			/* FIXME */
			return 0;

		case TIOCMBIS:
			if (copy_from_user(&x, user_arg, sizeof(x)))
				return -EFAULT;
			return oti6858_tiocmset(port, NULL, x, 0);

		case TIOCMBIC:
			if (copy_from_user(&x, user_arg, sizeof(x)))
				return -EFAULT;
			return oti6858_tiocmset(port, NULL, 0, x);

		case TIOCGSERIAL:
			if (copy_to_user(user_arg, port->tty->termios,
						sizeof(struct ktermios))) {
				return -EFAULT;
			}
                        return 0;

		case TIOCSSERIAL:
			if (copy_from_user(port->tty->termios, user_arg,
						sizeof(struct ktermios))) {
				return -EFAULT;
			}
			oti6858_set_termios(port, NULL);
			return 0;

		case TIOCMIWAIT:
			dbg("%s(): TIOCMIWAIT", __FUNCTION__);
			return wait_modem_info(port, arg);

		default:
			dbg("%s(): 0x%04x not supported", __FUNCTION__, cmd);
			break;
	}

	return -ENOIOCTLCMD;
}

static void oti6858_break_ctl(struct usb_serial_port *port, int break_state)
{
	int state;

	dbg("%s(port = %d)", __FUNCTION__, port->number);

	state = (break_state == 0) ? 0 : 1;
	dbg("%s(): turning break %s", __FUNCTION__, state ? "on" : "off");

	/* FIXME */
/*
	result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
				  BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
				  0, NULL, 0, 100);
	if (result != 0)
		dbg("%s(): error sending break", __FUNCTION__);
 */
}

static void oti6858_shutdown(struct usb_serial *serial)
{
	struct oti6858_private *priv;
	int i;

	dbg("%s()", __FUNCTION__);

	for (i = 0; i < serial->num_ports; ++i) {
		priv = usb_get_serial_port_data(serial->port[i]);
		if (priv) {
			pl2303_buf_free(priv->buf);
			kfree(priv);
			usb_set_serial_port_data(serial->port[i], NULL);
		}
	}
}

static void oti6858_read_int_callback(struct urb *urb)
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	int transient = 0, can_recv = 0, resubmit = 1;
	int status = urb->status;

	dbg("%s(port = %d, status = %d)",
				__FUNCTION__, port->number, status);

	switch (status) {
	case 0:
		/* success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
		dbg("%s(): urb shutting down with status: %d",
					__FUNCTION__, status);
		return;
	default:
		dbg("%s(): nonzero urb status received: %d",
					__FUNCTION__, status);
		break;
	}

	if (status == 0 && urb->actual_length == OTI6858_CTRL_PKT_SIZE) {
		struct oti6858_control_pkt *xs = urb->transfer_buffer;
		unsigned long flags;

		spin_lock_irqsave(&priv->lock, flags);

		if (!priv->transient) {
			if (!OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
				if (xs->rx_bytes_avail == 0) {
					priv->transient = 4;
					priv->setup_done = 0;
					resubmit = 0;
					dbg("%s(): scheduling setup_line()",
					    __FUNCTION__);
					schedule_delayed_work(&priv->delayed_setup_work, 0);
				}
			}
		} else {
			if (OTI6858_CTRL_EQUALS_PENDING(xs, priv)) {
				priv->transient = 0;
			} else if (!priv->setup_done) {
				resubmit = 0;
			} else if (--priv->transient == 0) {
				if (xs->rx_bytes_avail == 0) {
					priv->transient = 4;
					priv->setup_done = 0;
					resubmit = 0;
					dbg("%s(): scheduling setup_line()",
					    __FUNCTION__);
					schedule_delayed_work(&priv->delayed_setup_work, 0);
				}
			}
		}

		if (!priv->transient) {
			if (xs->pin_state != priv->status.pin_state)
				wake_up_interruptible(&priv->intr_wait);
			memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE);
		}

		if (!priv->transient && xs->rx_bytes_avail != 0) {
			can_recv = xs->rx_bytes_avail;
			priv->flags.read_urb_in_use = 1;
		}

		transient = priv->transient;
		spin_unlock_irqrestore(&priv->lock, flags);
	}

	if (can_recv) {
		int result;

		port->read_urb->dev = port->serial->dev;
		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
		if (result != 0) {
			priv->flags.read_urb_in_use = 0;
			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
					" error %d\n", __FUNCTION__, result);
		} else {
			resubmit = 0;
		}
	} else if (!transient) {
		unsigned long flags;

		spin_lock_irqsave(&priv->lock, flags);
		if (priv->flags.write_urb_in_use == 0
				&& pl2303_buf_data_avail(priv->buf) != 0) {
			schedule_delayed_work(&priv->delayed_write_work,0);
			resubmit = 0;
		}
		spin_unlock_irqrestore(&priv->lock, flags);
	}

	if (resubmit) {
		int result;

//		dbg("%s(): submitting interrupt urb", __FUNCTION__);
		urb->dev = port->serial->dev;
		result = usb_submit_urb(urb, GFP_ATOMIC);
		if (result != 0) {
			dev_err(&urb->dev->dev,
					"%s(): usb_submit_urb() failed with"
					" error %d\n", __FUNCTION__, result);
		}
	}
}

static void oti6858_read_bulk_callback(struct urb *urb)
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	struct tty_struct *tty;
	unsigned char *data = urb->transfer_buffer;
	unsigned long flags;
	int i, result;
	int status = urb->status;
	char tty_flag;

	dbg("%s(port = %d, status = %d)",
				__FUNCTION__, port->number, status);

	spin_lock_irqsave(&priv->lock, flags);
	priv->flags.read_urb_in_use = 0;
	spin_unlock_irqrestore(&priv->lock, flags);

	if (status != 0) {
		if (!port->open_count) {
			dbg("%s(): port is closed, exiting", __FUNCTION__);
			return;
		}
		/*
		if (status == -EPROTO) {
			// PL2303 mysteriously fails with -EPROTO reschedule the read
			dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
			result = usb_submit_urb(urb, GFP_ATOMIC);
			if (result)
				dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
			return;
		}
		*/
		dbg("%s(): unable to handle the error, exiting", __FUNCTION__);
		return;
	}

	// get tty_flag from status
	tty_flag = TTY_NORMAL;

/* FIXME: probably, errors will be signalled using interrupt pipe! */
/*
	// break takes precedence over parity,
	// which takes precedence over framing errors
	if (status & UART_BREAK_ERROR )
		tty_flag = TTY_BREAK;
	else if (status & UART_PARITY_ERROR)
		tty_flag = TTY_PARITY;
	else if (status & UART_FRAME_ERROR)
		tty_flag = TTY_FRAME;
	dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);
*/

	tty = port->tty;
	if (tty != NULL && urb->actual_length > 0) {
		tty_buffer_request_room(tty, urb->actual_length);
		for (i = 0; i < urb->actual_length; ++i)
			tty_insert_flip_char(tty, data[i], tty_flag);
		tty_flip_buffer_push(tty);
	}

	// schedule the interrupt urb if we are still open */
	if (port->open_count != 0) {
		port->interrupt_in_urb->dev = port->serial->dev;
		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
		if (result != 0) {
			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
					" error %d\n", __FUNCTION__, result);
		}
	}
}

static void oti6858_write_bulk_callback(struct urb *urb)
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
	struct oti6858_private *priv = usb_get_serial_port_data(port);
	int status = urb->status;
	int result;

	dbg("%s(port = %d, status = %d)",
				__FUNCTION__, port->number, status);

	switch (status) {
	case 0:
		/* success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
		dbg("%s(): urb shutting down with status: %d",
					__FUNCTION__, status);
		priv->flags.write_urb_in_use = 0;
		return;
	default:
		/* error in the urb, so we have to resubmit it */
		dbg("%s(): nonzero write bulk status received: %d",
					__FUNCTION__, status);
		dbg("%s(): overflow in write", __FUNCTION__);

		port->write_urb->transfer_buffer_length = 1;
		port->write_urb->dev = port->serial->dev;
		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
		if (result) {
			dev_err(&port->dev, "%s(): usb_submit_urb() failed,"
					" error %d\n", __FUNCTION__, result);
		} else {
			return;
		}
	}

	priv->flags.write_urb_in_use = 0;

	// schedule the interrupt urb if we are still open */
	port->interrupt_in_urb->dev = port->serial->dev;
	dbg("%s(): submitting interrupt urb", __FUNCTION__);
	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
	if (result != 0) {
		dev_err(&port->dev, "%s(): failed submitting int urb,"
					" error %d\n", __FUNCTION__, result);
	}
}


/*
 * pl2303_buf_alloc
 *
 * Allocate a circular buffer and all associated memory.
 */
static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
{
	struct pl2303_buf *pb;

	if (size == 0)
		return NULL;

	pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
	if (pb == NULL)
		return NULL;

	pb->buf_buf = kmalloc(size, GFP_KERNEL);
	if (pb->buf_buf == NULL) {
		kfree(pb);
		return NULL;
	}

	pb->buf_size = size;
	pb->buf_get = pb->buf_put = pb->buf_buf;

	return pb;
}

/*
 * pl2303_buf_free
 *
 * Free the buffer and all associated memory.
 */
static void pl2303_buf_free(struct pl2303_buf *pb)
{
	if (pb) {
		kfree(pb->buf_buf);
		kfree(pb);
	}
}

/*
 * pl2303_buf_clear
 *
 * Clear out all data in the circular buffer.
 */
static void pl2303_buf_clear(struct pl2303_buf *pb)
{
	if (pb != NULL) {
		/* equivalent to a get of all data available */
		pb->buf_get = pb->buf_put;
	}
}

/*
 * pl2303_buf_data_avail
 *
 * Return the number of bytes of data available in the circular
 * buffer.
 */
static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
{
	if (pb == NULL)
		return 0;
	return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
}

/*
 * pl2303_buf_space_avail
 *
 * Return the number of bytes of space available in the circular
 * buffer.
 */
static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
{
	if (pb == NULL)
		return 0;
	return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
}

/*
 * pl2303_buf_put
 *
 * Copy data data from a user buffer and put it into the circular buffer.
 * Restrict to the amount of space available.
 *
 * Return the number of bytes copied.
 */
static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
					unsigned int count)
{
	unsigned int len;

	if (pb == NULL)
		return 0;

	len  = pl2303_buf_space_avail(pb);
	if (count > len)
		count = len;

	if (count == 0)
		return 0;

	len = pb->buf_buf + pb->buf_size - pb->buf_put;
	if (count > len) {
		memcpy(pb->buf_put, buf, len);
		memcpy(pb->buf_buf, buf+len, count - len);
		pb->buf_put = pb->buf_buf + count - len;
	} else {
		memcpy(pb->buf_put, buf, count);
		if (count < len)
			pb->buf_put += count;
		else /* count == len */
			pb->buf_put = pb->buf_buf;
	}

	return count;
}

/*
 * pl2303_buf_get
 *
 * Get data from the circular buffer and copy to the given buffer.
 * Restrict to the amount of data available.
 *
 * Return the number of bytes copied.
 */
static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
					unsigned int count)
{
	unsigned int len;

	if (pb == NULL)
		return 0;

	len = pl2303_buf_data_avail(pb);
	if (count > len)
		count = len;

	if (count == 0)
		return 0;

	len = pb->buf_buf + pb->buf_size - pb->buf_get;
	if (count > len) {
		memcpy(buf, pb->buf_get, len);
		memcpy(buf+len, pb->buf_buf, count - len);
		pb->buf_get = pb->buf_buf + count - len;
	} else {
		memcpy(buf, pb->buf_get, count);
		if (count < len)
			pb->buf_get += count;
		else /* count == len */
			pb->buf_get = pb->buf_buf;
	}

	return count;
}

/* module description and (de)initialization */

static int __init oti6858_init(void)
{
	int retval;

	if ((retval = usb_serial_register(&oti6858_device)) == 0) {
		if ((retval = usb_register(&oti6858_driver)) != 0)
			usb_serial_deregister(&oti6858_device);
		else
			return 0;
	}

	return retval;
}

static void __exit oti6858_exit(void)
{
	usb_deregister(&oti6858_driver);
	usb_serial_deregister(&oti6858_device);
}

module_init(oti6858_init);
module_exit(oti6858_exit);

MODULE_DESCRIPTION(OTI6858_DESCRIPTION);
MODULE_AUTHOR(OTI6858_AUTHOR);
MODULE_VERSION(OTI6858_VERSION);
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "enable debug output");