diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/tty')
-rw-r--r-- | ANDROID_3.4.5/drivers/tty/Makefile | 1 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/tty/serial/Kconfig | 66 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/tty/serial/Makefile | 1 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/tty/serial/serial_wmt.c | 2720 | ||||
-rw-r--r-- | ANDROID_3.4.5/drivers/tty/vt/keyboard.c | 4 | ||||
-rwxr-xr-x | ANDROID_3.4.5/drivers/tty/wmt_3g.c | 3655 |
6 files changed, 6446 insertions, 1 deletions
diff --git a/ANDROID_3.4.5/drivers/tty/Makefile b/ANDROID_3.4.5/drivers/tty/Makefile index 29530595..d2d3772d 100644 --- a/ANDROID_3.4.5/drivers/tty/Makefile +++ b/ANDROID_3.4.5/drivers/tty/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-y += ipwireless/ +obj-y += wmt_3g.o diff --git a/ANDROID_3.4.5/drivers/tty/serial/Kconfig b/ANDROID_3.4.5/drivers/tty/serial/Kconfig index 070b442c..e7c4d902 100644 --- a/ANDROID_3.4.5/drivers/tty/serial/Kconfig +++ b/ANDROID_3.4.5/drivers/tty/serial/Kconfig @@ -404,6 +404,72 @@ config SERIAL_SA1100_CONSOLE "console=ttySA0". (Try "man bootparam" or see the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) + +config SERIAL_WMT + bool "WMT serial port support" + depends on ARM && ARCH_WMT + select SERIAL_CORE + ---help--- + Enable onboard serial port by enabling this option. + +config SERIAL_WMT_CONSOLE + bool "Console on WMT serial port" + depends on SERIAL_WMT + select SERIAL_CORE_CONSOLE + ---help--- + Make UART to be the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS0" or "console=ttyVT0". (Try "man bootparam" or see + the documentation of your boot loader (lilo or loadlin) about how to + pass options to the kernel at boot time.) + +config SERIAL_WMT_TTYVT + bool "ttyVT on WMT serial port" + depends on SERIAL_WMT + ---help--- + Make UARTs to be mounted on ttyVT devices. + + If you say Y here, make sure there are ttyVT devices on your root filesystem. + If you say N here, UARTs will mount on ttyS devices. + Choose N here unless you really need to mount UART on ttyVT devices. + +config SERIAL_WMT_BKSIG + bool "Break signal software detection" + depends on SERIAL_WMT + ---help--- + Since WMT UART has no break signal detection interrupt. + + If you say Y here, driver will detect break signal by software. + If you say N here, driver will not support break signal detection. + Choose N here unless you really need to detect break signal. + +config SERIAL_WMT_DMA + bool "DMA mode on WMT serial port" + depends on SERIAL_WMT + ---help--- + Since WMT UART support DMA mode transfer. + + If you say Y here, driver will select DMA mode transfer. + + If you say N here, driver will not support DMA mode transfer. + + Choose N here unless you really need to use DMA mode transfer. + +config UART_2_3_ENABLE + bool "UART_2_3 support" + depends on SERIAL_WMT + ---help--- + Since WMT UART support UART2 and UART3. + + If you say Y here, driver will select dual dma transfer. + + If you say N here, driver will not support dual dma transfer. + + Choose N here unless you really need to use dual dma transfer. + config SERIAL_MRST_MAX3110 tristate "SPI UART driver for Max3110" diff --git a/ANDROID_3.4.5/drivers/tty/serial/Makefile b/ANDROID_3.4.5/drivers/tty/serial/Makefile index 7257c5d8..4ac181a4 100644 --- a/ANDROID_3.4.5/drivers/tty/serial/Makefile +++ b/ANDROID_3.4.5/drivers/tty/serial/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_SERIAL_WMT) += serial_wmt.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o diff --git a/ANDROID_3.4.5/drivers/tty/serial/serial_wmt.c b/ANDROID_3.4.5/drivers/tty/serial/serial_wmt.c new file mode 100755 index 00000000..79246cdc --- /dev/null +++ b/ANDROID_3.4.5/drivers/tty/serial/serial_wmt.c @@ -0,0 +1,2720 @@ +/*++ +linux/drivers/serial/serial_wmt.c + +Copyright (c) 2008 WonderMedia Technologies, Inc. + +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, see <http://www.gnu.org/licenses/>. + +WonderMedia Technologies, Inc. +10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. +--*/ + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/cpufreq.h> +#include <linux/platform_device.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <mach/hardware.h> +#include <mach/dma.h> +#include <linux/serial_core.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> + + +#define PORT_WMT 54 +#if defined(CONFIG_SERIAL_WMT_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + + +#ifdef CONFIG_SERIAL_WMT_DMA +/* * DMA processing */ +#define DMA_RX_REQUEST(s, cb) wmt_request_dma(&s->rx_dmach, s->id, s->dma_rx_dev, cb, s) +#define DMA_RX_FREE(s) {wmt_free_dma(s->rx_dmach); s->rx_dmach = NULL_DMA; } +#define DMA_RX_START(s, d, l) wmt_start_dma(s->rx_dmach, d, 0, l) +#define DMA_RX_POS(s) wmt_get_dma_pos(s->rx_dmach) +#define DMA_RX_STOP(s) wmt_stop_dma(s->rx_dmach) +#define DMA_RX_CLEAR(s) wmt_clear_dma(s->rx_dmach) +#define DMA_RX_RESET(s) wmt_reset_dma(s->rx_dmach) +#define IS_NULLDMA(ch) ((ch) == NULL_DMA) +#define CHK_DMACH(ch) ((ch) != NULL_DMA) +#define DMA_TX_REQUEST(s, cb) wmt_request_dma(&s->tx_dmach, s->id, s->dma_tx_dev, cb, s) +#define DMA_TX_FREE(s) {wmt_free_dma(s->tx_dmach); s->tx_dmach = NULL_DMA; } +#define DMA_TX_START(s, d, l) wmt_start_dma(s->tx_dmach, d, 0, l) +#define DMA_TX_POS(s) wmt_get_dma_pos(s->tx_dmach) +#define DMA_TX_STOP(s) wmt_stop_dma(s->tx_dmach) +#define DMA_TX_CLEAR(s) wmt_clear_dma(s->tx_dmach) +#define DMA_TX_RESET(s) wmt_reset_dma(s->tx_dmach) +#define NULL_DMA ((dmach_t)(-1)) +#endif + + + +#ifdef DEBUG_SERIAL_WMT +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + + +#define UART_BR_115K2 115200 + +/* + * This is for saving useful I/O registers + */ + +/* + * RS232 on DB9 + * Pin No. Name Notes/Description + * 1 DCD Data Carrier Detect + * 2 RD Receive Data (a.k.a RxD, Rx) + * 3 TD Transmit Data (a.k.a TxD, Tx) + * 4 DTR Data Terminal Ready + * 5 SGND Ground + * 6 DSR Data Set Ready + * 7 RTS Request To Send + * 8 CTS Clear To Send + * 9 RI Ring Indicator + */ + +/* + * We've been assigned a range on the "Low-density serial ports" major + * + * dev Major Minor + * ttyVT0 204 90 + * ttyS0 4 64 + */ +#ifdef CONFIG_SERIAL_WMT_TTYVT + #define SERIAL_WMT_MAJOR 204 + #define MINOR_START 90 /* Start from ttyVT0 */ + #define CALLOUT_WMT_MAJOR 205 /* for callout device */ +#else + #define SERIAL_WMT_MAJOR 4 + #define MINOR_START 64 /* Start from ttyS0 */ + #define CALLOUT_WMT_MAJOR 5 /* for callout device */ +#endif + +#ifdef CONFIG_UART_2_3_ENABLE +#define NR_PORTS 4 +#else +#define NR_PORTS 2 +#endif +#define WMT_ISR_PASS_LIMIT 256 + +struct wmt_port { + struct uart_port port; + struct timer_list timer; + struct timer_list rx_timer; +#ifdef CONFIG_SERIAL_WMT_DMA + /* RX Buffer0 physical address */ + dma_addr_t uart_rx_dma_phy0_org; + /* RX Buffer1 physical address */ + dma_addr_t uart_rx_dma_phy1_org; + /* RX Buffer2 physical address */ + dma_addr_t uart_rx_dma_phy2_org; + /* RX Buffer0 virtual address */ + char *uart_rx_dma_buf0_org; + /* RX Buffer1 virtual address */ + char *uart_rx_dma_buf1_org; + /* RX Buffer2 virtual address */ + char *uart_rx_dma_buf2_org; + /* RX Buffer0 virtual address, this address is the data should be read */ + char *uart_dma_tmp_buf0; + /* RX Buffer1 virtual address, this address is the data should be read */ + char *uart_dma_tmp_buf1; + /* RX Buffer2 virtual address, this address is the data should be read */ + char *uart_dma_tmp_buf2; + /* RX Buffer0 physical address, this address is the data should be read */ + dma_addr_t uart_dma_tmp_phy0; + /* RX Buffer1 physical address, this address is the data should be read */ + dma_addr_t uart_dma_tmp_phy1; + /* RX Buffer2 physical address, this address is the data should be read */ + dma_addr_t uart_dma_tmp_phy2; + /* RX device identifier for DMA */ + enum dma_device_e dma_rx_dev; + /* TX device identifier for DMA */ + enum dma_device_e dma_tx_dev; + /* RX DMA device config */ + struct dma_device_cfg_s dma_rx_cfg; + /* TX DMA device config */ + struct dma_device_cfg_s dma_tx_cfg; + /* RX DMA channel number */ + dmach_t rx_dmach; + /* TX DMA channel number */ + dmach_t tx_dmach; + /* RX dma callback function record which buffer the DMA used */ + unsigned int buffer_used; + /* The buffer rx_char function should used (virtual address) */ + unsigned int buffer_selected; + /* The buffer rx_count function should used (physical address) */ + unsigned int buffer_rx_count_selected; + /* TX Buffer0 virtual address */ + char *uart_tx_dma_buf0_org; + /* TX Buffer0 physical address */ + dma_addr_t uart_tx_dma_phy0_org; + /* Record TX DMA running status : DMA_TX_END, DMA_TX_CHAR, DMA_TX_XMIT */ + unsigned int uart_tx_dma_flag; + /* TX dma transmit counter*/ + unsigned int uart_tx_count; + /* RX dma buffer last position*/ + unsigned int last_pos; + /* Record the number of RX dma timeout function with no data transmit*/ + unsigned int uart_rx_dma_flag; + unsigned int dma_tx_cnt; +#endif + unsigned int old_status; + /* identification string */ + char *id; + unsigned int old_urdiv; + unsigned int old_urlcr; + unsigned int old_urier; + unsigned int old_urfcr; + unsigned int old_urtod; +}; + +#ifdef CONFIG_SERIAL_WMT_DMA +#define DMA_TX_END 0 +#define DMA_TX_CHAR 1 +#define DMA_TX_XMIT 2 +#endif + + +/* + * WMT UART registers set structure. + */ +struct wmt_uart { + unsigned int volatile urtdr; /* 0x00*/ + unsigned int volatile urrdr; /* 0x04*/ + unsigned int volatile urdiv; /* 0x08*/ + unsigned int volatile urlcr; /* 0x0C*/ + unsigned int volatile uricr; /* 0x10*/ + unsigned int volatile urier; /* 0x14*/ + unsigned int volatile urisr; /* 0x18*/ + unsigned int volatile urusr; /* 0x1C*/ + unsigned int volatile urfcr; /* 0x20*/ + unsigned int volatile urfidx; /* 0x24*/ + unsigned int volatile urbkr; /* 0x28*/ + unsigned int volatile urtod; /* 0x2C*/ + unsigned int volatile resv30_FFF[0x3F4]; /* 0x0030 - 0x0FFF Reserved*/ + unsigned char volatile urtxf[32]; /* 0x1000 - 0x101F*/ + unsigned char volatile urrxf[32]; /* 0x1020 - 0x103F*/ +}; + +struct baud_info_s { + unsigned int baud; /* baud rate */ + unsigned int brd; /* baud rate divisor */ + unsigned int bcv; /* break counter value at this baud rate + * simply be calculated by baud * 0.004096 + */ +}; +#ifdef UART_DEBUG +unsigned int *DMA_pbuf =NULL; +unsigned int *COUNT_pbuf =NULL; +unsigned int *CPU_pbuf =NULL; +unsigned int dma_write_index = 0x00; +#endif +int mtk6622_tty = -1; +extern int wmt_getsyspara(char *varname, char *varval, int *varlen); + +static struct baud_info_s baud_table[] = { + { 3600, 0x100FF, 15 }, + { 7600, 0x1007F, 30 }, + { 9600, 0x2003F, 39 }, + { 14400, 0x1003F, 59 }, + { 19200, 0x2001F, 79 }, + { 28800, 0x1001F, 118 }, + { 38400, 0x2000F, 157 }, + { 57600, 0x1000F, 236 }, + { 115200, 0x10007, 472 }, + { 230400, 0x10003, 944 }, + { 460800, 0x10001, 1920 }, + { 921600, 0x10000, 3775 }, +}; + +#define BAUD_TABLE_SIZE ARRAY_SIZE(baud_table) + +#ifdef CONFIG_SERIAL_WMT_DMA +#define UART_BUFFER_SIZE (1024*16) +#endif +/* + * Macros to put URISR and URUSR into a 32-bit status variable + * URISR in bit[ 0:15] + * URUSR in bit[16:31] + */ +#define URISR_TO_SM(x) ((x) & URISR_MASK) +#define URUSR_TO_SM(x) (((x) & URUSR_MASK) << 16) +#define SM_TO_URISR(x) ((x) & 0xffff) +#define SM_TO_URUSR(x) ((x) >> 16) + +/* + * Following is a trick if we're interesting to listen break signal, + * but due to WMT UART doesn't suppout this interrupt status. + * So I make a fake interrupt status and use URISR_FER event to implement + * break signal detect. + */ +#ifdef CONFIG_SERIAL_WMT_BKSIG +#define SW_BKSIG (BIT31 | URISR_FER) +#endif +/* + * Macros to manipulate WMT UART module. + * + * s = sport, o = offset, v = value + * + * registers offset table as follows: + * + * URTDR 0x0000 + * URRDR 0x0004 + * URBRD 0x0008 + * URLCR 0x000C + * URICR 0x0010 + * URIER 0x0014 + * URISR 0x0018 + * URUSR 0x001C + * URFCR 0x0020 + * URFIDX 0x0024 + * URBKR 0x0028 + * + * Offset 0x002C-0x002F reserved + * + * URTXF 0x0030 + * URRXF 0x0040 + */ +#define PORT_TO_BASE(s) ((s)->port.membase) +#define WMT_UART_GET(s, o) __raw_readl((s)->port.membase + o) +#define WMT_UART_PUT(s, o, v) __raw_writel(v, (s)->port.membase + o) +#define WMT_UART_TXFIFO(s) (volatile unsigned char *)((s)->port.membase + URTXF) +#define WMT_UART_RXFIFO(s) (volatile unsigned short *)((s)->port.membase + URRXF) + +/* + * This is the size of our serial port register set. + */ +#define UART_PORT_SIZE 0x1040 + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +/*{2007/11/10 JHT Support the VT8500 Serial Port Driver Because the*/ +/* definition of URSIRT Bit[0] & Bit[3] are different.*/ +/* Before VT8500 these bit are defined as RO, in VT8500*/ +/* they are changed into the W1C. Therefore the xmit function*/ +/* for the FIFO mode should be modified as well.*/ +static void wmt_tx_chars(struct wmt_port *sport); +static void wmt_rx_chars(struct wmt_port *sport, unsigned int status); +static struct wmt_port wmt_ports[NR_PORTS]; + +/*}2007/11/10-JHT*/ + +enum { + SHARE_PIN_UART = 0, + SHARE_PIN_SPI, +}; +static int wmt_uart_spi_sel = SHARE_PIN_UART; /* 0:uart, 1:spi */ + +void uart_dump_reg(struct wmt_port *sport) +{ + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + unsigned int urtdr = uart->urtdr; /* 0x00*/ + unsigned int urrdr = uart->urrdr; /* 0x04*/ + unsigned int urdiv = uart->urdiv; /* 0x08*/ + unsigned int urlcr = uart->urlcr; /* 0x0C*/ + unsigned int uricr = uart->uricr; /* 0x10*/ + unsigned int urier = uart->urier; /* 0x14*/ + unsigned int urisr = uart->urisr; /* 0x18*/ + unsigned int urusr = uart->urusr; /* 0x1C*/ + unsigned int urfcr = uart->urfcr; /* 0x20*/ + unsigned int urfidx = uart->urfidx; /* 0x24*/ + unsigned int urbkr = uart->urbkr; /* 0x28*/ + unsigned int urtod = uart->urtod; /* 0x2C*/ +#if 1 + + printk("urtdr=0x%.8x urrdr=0x%.8x urdiv=0x%.8x urlcr=0x%.8x\n" \ + "uricr=0x%.8x urier=0x%.8x urisr=0x%.8x urusr=0x%.8x\n" \ + "urfcr=0x%.8x uridx=0x%.8x urbkr=0x%.8x urtod=0x%.8x\n", + urtdr, urrdr, urdiv, urlcr, + uricr, urier, urisr, urusr, + urfcr, urfidx, urbkr, urtod); +#endif +} + +static void wmt_mctrl_check(struct wmt_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void wmt_timeout(unsigned long data) +{ + struct wmt_port *sport = (struct wmt_port *)data; + unsigned long flags; + + + + if (sport->port.state) { + spin_lock_irqsave(&sport->port.lock, flags); + wmt_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +unsigned int rx_timeout = 1; + +static void wmt_rx_timeout(unsigned long data) +{ + struct wmt_port *sport = (struct wmt_port *)data; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned long flags; + unsigned int status; + unsigned int pos = 0; + + + spin_lock_irqsave(&sport->port.lock, flags); + pos = DMA_RX_POS(sport); + status = URISR_TO_SM(uart->urisr) | URUSR_TO_SM(uart->urusr); + uart->urisr |= SM_TO_URISR(status); + spin_unlock_irqrestore(&sport->port.lock, flags); + + /* DMA didn't transmit any data */ + if (sport->last_pos == pos) { + sport->uart_rx_dma_flag++; + + /* RX DMA didn't trasmit any data in two times, */ + /* enable interrupt and do the last time mod timer */ + if (sport->uart_rx_dma_flag == 2) { + sport->uart_rx_dma_flag++; + uart->urier = URIER_ERXFAF | + URIER_ERXFF | + URIER_ERXTOUT | + URIER_EPER | + URIER_EFER | + URIER_ERXDOVR; + mod_timer(&sport->rx_timer, jiffies + rx_timeout); + return; + } else if (sport->uart_rx_dma_flag == 3) { + sport->uart_rx_dma_flag = 0; + return; + } + mod_timer(&sport->rx_timer, jiffies + rx_timeout); + return; + } + + sport->uart_rx_dma_flag = 0; + + wmt_rx_chars(sport, URISR_RXFAF | URISR_RXFF | status); + + mod_timer(&sport->rx_timer, jiffies + rx_timeout); + +} + + +/* + * Interrupts should be disabled on entry. + */ +static void wmt_stop_tx(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + #ifndef CONFIG_SERIAL_WMT_DMA + uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE); + sport->port.read_status_mask &= ~URISR_TO_SM(URISR_TXFAE | URISR_TXFE); + #else + if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR) { + uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE); + sport->port.read_status_mask &= ~URISR_TO_SM(URISR_TXFAE | URISR_TXFE); + } + #endif +} +/* + * Interrupts may not be disabled on entry. + */ +static void wmt_start_tx(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + /*{2007/11/10 JHT Support the VT8500 Serial Port Driver Because the + * definition of URSIRT Bit[0] & Bit[3] are different. + * Before VT8500 these bit are defined as RO, in VT8500 + * they are changed into the W1C. Therefore the xmit function + * for the FIFO mode should be modified as well. + */ +#ifdef CONFIG_SERIAL_WMT_DMA + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + uart->urlcr |= URLCR_DMAEN; + uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE); + } else + uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE); +#else + uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE); + #endif + wmt_tx_chars(sport); + /*}2007/11/10-JHT*/ +#ifndef CONFIG_SERIAL_WMT_DMA + sport->port.read_status_mask |= URISR_TO_SM(URISR_TXFAE | URISR_TXFE); + uart->urier |= URIER_ETXFAE | URIER_ETXFE; +#else + if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR) { + sport->port.read_status_mask |= URISR_TO_SM(URISR_TXFAE | URISR_TXFE); + uart->urier |= URIER_ETXFAE | URIER_ETXFE; + } + #endif +} + +/* + * Interrupts enabled + */ +static void wmt_stop_rx(struct uart_port *port) +{ +/* + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + + uart->urier &= ~URIER_ERXFAF; +*/ +} + +/* + * No modem control lines + */ +static void wmt_enable_ms(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +#ifdef CONFIG_SERIAL_WMT_DMA +static unsigned int wmt_rx_count(struct wmt_port *sport) +{ + unsigned int rx_count = 0; + unsigned int pos; + + pos = DMA_RX_POS(sport); + + if (pos == sport->last_pos) /*have no data to drain */ + return 0; + + if (sport->buffer_rx_count_selected == 0) { + sport->buffer_selected = 0; /*shoud read buffer 0*/ + + /*pos in the range of the buffer0*/ + if ((pos >= sport->uart_dma_tmp_phy0) && + (pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE))) { + rx_count = (pos - (unsigned int)sport->uart_dma_tmp_phy0); + } else if (pos == 0) { + if (sport->uart_dma_tmp_phy0 == sport->uart_rx_dma_phy0_org) + rx_count = 0; + else { + rx_count = (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE) + - (unsigned int)sport->uart_dma_tmp_phy0; + } + } else if (((pos >= sport->uart_rx_dma_phy1_org) && + (pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE))) + || + ((pos >= sport->uart_rx_dma_phy2_org) && + (pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)))) { + /* Buffer is full, dma pos in buffer 1 or 2, read all left data*/ + rx_count = (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE) - + (unsigned int)sport->uart_dma_tmp_phy0; + } + sport->uart_dma_tmp_phy0 += rx_count; /*update tmp physical address*/ + sport->last_pos = sport->uart_dma_tmp_phy0; + + /* Buffer0 has reach to end*/ + if (sport->uart_dma_tmp_phy0 == (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)) { + sport->buffer_rx_count_selected = 1; + sport->uart_dma_tmp_phy0 = sport->uart_rx_dma_phy0_org; + } + } else if (sport->buffer_rx_count_selected == 1) { + sport->buffer_selected = 1; /*shoud read buffer 1*/ + + /*pos in the range of the buffer1*/ + if ((pos >= sport->uart_dma_tmp_phy1) && + (pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE))) { + rx_count = (pos - (unsigned int)sport->uart_dma_tmp_phy1); + } else if (pos == 0) { + if (sport->uart_dma_tmp_phy1 == sport->uart_rx_dma_phy1_org) + rx_count = 0; + else { + rx_count = (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE) - + (unsigned int)sport->uart_dma_tmp_phy1; + } + } else if (((pos >= sport->uart_rx_dma_phy0_org) && + (pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE))) + || + ((pos >= sport->uart_rx_dma_phy2_org) && + (pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)))) { + /* Buffer is full, dma pos in buffer 0 or 2, read all left data*/ + rx_count = (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE) - + (unsigned int)sport->uart_dma_tmp_phy1; + } + sport->uart_dma_tmp_phy1 += rx_count; + sport->last_pos = sport->uart_dma_tmp_phy1; + /* Buffer1 has reach to end*/ + if (sport->uart_dma_tmp_phy1 == (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)) { + sport->buffer_rx_count_selected = 2; + sport->uart_dma_tmp_phy1 = sport->uart_rx_dma_phy1_org; + } + } else if (sport->buffer_rx_count_selected == 2) { + sport->buffer_selected = 2; /*shoud read buffer 2*/ + + /*pos in the range of the buffer1*/ + if ((pos >= sport->uart_dma_tmp_phy2) && + (pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE))) { + rx_count = (pos - (unsigned int)sport->uart_dma_tmp_phy2); + } else if (pos == 0) { + if (sport->uart_dma_tmp_phy2 == sport->uart_rx_dma_phy2_org) + rx_count = 0; + else { + rx_count = (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE) - + (unsigned int)sport->uart_dma_tmp_phy2; + } + } else if (((pos >= sport->uart_rx_dma_phy0_org) && + (pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE))) + || + ((pos >= sport->uart_rx_dma_phy1_org) && + (pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)))) { + /* Buffer is full, dma pos in buffer 0 or 1, read all left data*/ + rx_count = (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE) - + (unsigned int)sport->uart_dma_tmp_phy2; + } + sport->uart_dma_tmp_phy2 += rx_count; + sport->last_pos = sport->uart_dma_tmp_phy2; + + /* Buffer2 has reach to end*/ + if (sport->uart_dma_tmp_phy2 == (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)) { + sport->buffer_rx_count_selected = 0; + sport->uart_dma_tmp_phy2 = sport->uart_rx_dma_phy2_org; + } + } + return rx_count; +} +/* +* we can use mu command to check memory content +*/ +int volatile last_submit = 0x0; +void dump_rx_dma_buf(struct wmt_port *sport) +{ + unsigned int rx_count = 0; + unsigned int pos; + int i; + int size=0; + unsigned char *tmpbuf; + pos = DMA_RX_POS(sport); + printk("sport->last_pos:0x%x,pos:0x%x,sport->buffer_rx_count_selected:0x%x,last_submit:0x%x\n",sport->last_pos,pos,sport->buffer_rx_count_selected,last_submit); + if ((sport->uart_rx_dma_phy0_org) <= pos && pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)){ + i = pos - sport->uart_rx_dma_phy0_org; + tmpbuf = sport->uart_rx_dma_buf0_org + i; + size = (unsigned int)(sport->uart_rx_dma_buf0_org) + UART_BUFFER_SIZE - (unsigned int)tmpbuf; + if(size > 16) + size = 16; + }else if ((sport->uart_rx_dma_phy1_org) <= pos && pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)){ + i = pos - sport->uart_rx_dma_phy1_org; + tmpbuf = sport->uart_rx_dma_buf1_org + i; + size =(unsigned int)( sport->uart_rx_dma_buf1_org) + UART_BUFFER_SIZE - (unsigned int)tmpbuf; + if(size > 16) + size = 16; + }else if ((sport->uart_rx_dma_phy2_org) <= pos && pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)){ + i = pos - sport->uart_rx_dma_phy2_org; + tmpbuf = sport->uart_rx_dma_buf2_org + i; + size = (unsigned int)(sport->uart_rx_dma_buf2_org) + UART_BUFFER_SIZE - (unsigned int)tmpbuf; + if(size > 16) + size = 16; + } + + for(i=0;i<size;i++) + printk("0x%x ",*tmpbuf++); + + printk("\nsport->uart_rx_dma_phy0_org:0x%x\n",sport->uart_rx_dma_phy0_org); + printk("sport->uart_rx_dma_phy1_org:0x%x\n",sport->uart_rx_dma_phy1_org); + printk("sport->uart_rx_dma_phy2_org:0x%x\n",sport->uart_rx_dma_phy2_org); +} +#endif +/* + * Inside the UART interrupt service routine dut to following + * reason: + * + * URISR_RXFAF: RX FIFO almost full (FIFO mode) + * URISR_RXDF: RX data register full (Register mode) + * URISR_RXTOUT: RX timeout + */ + +#ifndef CONFIG_SERIAL_WMT_DMA +static void +wmt_rx_chars(struct wmt_port *sport, unsigned int status) +{ + struct tty_struct *tty = sport->port.state->port.tty; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned int flg, urfidx, ignored = 0; + char ch; + + urfidx = URFIDX_RXFIDX(uart->urfidx); + + /* + * Check if there is data ready to be read. + * + * Note: We only receive characters. + */ + while ((status & URUSR_TO_SM(URUSR_RXDRDY)) && (URFIDX_RXFIDX(uart->urfidx))) { + ch = (uart->urrxf[0] & 0xFF); + /*urfidx--;*/ + + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * Check interrupt status information using status[URISR_bits]. + * + * Notice that the error handling code is out of + * the main execution path and the URISR has already + * been read by ISR. + */ + if (status & URISR_TO_SM(URISR_PER | URISR_FER | URISR_RXDOVR)) { + if (urfidx > 1) { + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char; + /* + * Pop all TTY_NORMAL data. + */ + goto error_return; + } else { + /* + * Now we have poped up to the data with + * parity error or frame error. + */ + goto handle_error; + } + } + + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char; + +error_return: + + uart_insert_char(&sport->port, (status & 0xFFFF), URISR_TO_SM(URISR_RXDOVR) , ch, flg); + +ignore_char: + status &= 0xffff; /* Keep URISR field*/ + status |= URUSR_TO_SM(uart->urusr); + } +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + /* + * Update error counters. + */ + if (status & URISR_TO_SM(URISR_PER)) + sport->port.icount.parity++; + else + if (status & URISR_TO_SM(URISR_FER)) { + + #ifdef CONFIG_SERIAL_WMT_BKSIG + /* + * Experimental software patch for break signal detection. + * + * When I got there is a frame error in next frame data, + * I check the next data to judge if it is a break signal. + * + * FIXME: Open these if Bluetooth or IrDA need this patch. + * Dec.29.2004 by Harry. + */ + if ((ch & RX_PERMASK) == 0) { + sport->port.icount.brk++; + uart_handle_break(&sport->port); + } else + sport->port.icount.frame++; + + #else /* Don't support break sinal detection */ + + sport->port.icount.frame++; + + #endif + + } + + /* + * RX Over Run event + */ + if (status & URISR_TO_SM(URISR_RXDOVR)) + sport->port.icount.overrun++; + + if (status & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + /* + * Second, handle the events which we're interesting to listen. + */ + status &= sport->port.read_status_mask; + + if (status & URISR_TO_SM(URISR_PER)) + flg = TTY_PARITY; + else + if (status & URISR_TO_SM(URISR_FER)) { + + #ifdef CONFIG_SERIAL_WMT_BKSIG + /* Software patch for break signal detection. + * + * When I got there is a frame error in next frame data, + * I check the next data to judge if it is a break signal. + * + * FIXME: Open these if Bluetooth or IrDA need this patch. + * Dec.29.2004 by Harry. + */ + if (sport->port.read_status_mask & SW_BKSIG) { + if ((ch & RX_PERMASK) == 0) { + DEBUG_INTR("handling break...."); + flg = TTY_BREAK; + /*goto error_return;*/ + } else { + flg = TTY_FRAME; + /*goto error_return;*/ + } + } else { + flg = TTY_FRAME; + /*goto error_return;*/ + } + + #else /* Don't support break sinal detection */ + + flg = TTY_FRAME; + + #endif + } + + if (status & URISR_TO_SM(URISR_RXDOVR)) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + + ch = 0; + flg = TTY_OVERRUN; + } + #ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; + #endif + goto error_return; +} +#else + + +static inline int put_strings_tty_io(struct tty_struct *tty, + const unsigned char *chars, char flag, size_t size) +{ + int i; + + i = tty_insert_flip_string_fixed_flag(tty, chars, flag, size); + tty_flip_buffer_push(tty); + return i; +} + +static void +wmt_rx_chars(struct wmt_port *sport, unsigned int status) +{ + struct tty_struct *tty = sport->port.state->port.tty; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned int receive_rx_count = 0; + unsigned long flags; + + unsigned int flg, urfidx, ignored = 0; + char ch; + char *pchar = NULL; + char rx_flag; + + urfidx = URFIDX_RXFIDX(uart->urfidx); + + /* + * Check if there is data ready to be read. + * + * Note: We only receive characters. + */ + + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + + spin_lock_irqsave(&sport->port.lock, flags); + receive_rx_count = wmt_rx_count(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + rx_flag = TTY_NORMAL; + + if (status & URISR_TO_SM(URISR_PER)) + rx_flag = TTY_PARITY; + + if (status & URISR_TO_SM(URISR_FER)) + rx_flag = TTY_FRAME; + + if (receive_rx_count > 0) { + if (sport->buffer_selected == 0) { + pchar = sport->uart_dma_tmp_buf0; + sport->uart_dma_tmp_buf0 += receive_rx_count; + put_strings_tty_io(tty, pchar, rx_flag, receive_rx_count); + + if (sport->uart_dma_tmp_buf0 == + (sport->uart_rx_dma_buf0_org + UART_BUFFER_SIZE)) { + sport->uart_dma_tmp_buf0 = sport->uart_rx_dma_buf0_org; + } + + } else if (sport->buffer_selected == 1) { + pchar = sport->uart_dma_tmp_buf1; + sport->uart_dma_tmp_buf1 += receive_rx_count; + put_strings_tty_io(tty, pchar, rx_flag, receive_rx_count); + + if (sport->uart_dma_tmp_buf1 == + (sport->uart_rx_dma_buf1_org + UART_BUFFER_SIZE)) { + sport->uart_dma_tmp_buf1 = sport->uart_rx_dma_buf1_org; + } + } else if (sport->buffer_selected == 2) { + pchar = sport->uart_dma_tmp_buf2; + sport->uart_dma_tmp_buf2 += receive_rx_count ; + put_strings_tty_io(tty, pchar, rx_flag, receive_rx_count); + + if (sport->uart_dma_tmp_buf2 == + (sport->uart_rx_dma_buf2_org + UART_BUFFER_SIZE)) { + sport->uart_dma_tmp_buf2 = sport->uart_rx_dma_buf2_org; + } + } + sport->port.icount.rx += receive_rx_count; + + if (rx_flag == TTY_PARITY) + sport->port.icount.parity += receive_rx_count; + + if (rx_flag == TTY_FRAME) + sport->port.icount.frame += receive_rx_count; + + receive_rx_count = 0; + } + tty_flip_buffer_push(tty); + return; + + } else { + + while ((status & URUSR_TO_SM(URUSR_RXDRDY)) && (URFIDX_RXFIDX(uart->urfidx))) { + + ch = (unsigned int)(uart->urrxf[0] & 0xFF); + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * Check interrupt status information using status[URISR_bits]. + * + * Notice that the error handling code is out of + * the main execution path and the URISR has already + * been read by ISR. + */ + if (status & URISR_TO_SM(URISR_PER | URISR_FER | URISR_RXDOVR)) { + if (urfidx > 1) { + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char2; + /* + * Pop all TTY_NORMAL data. + */ + goto error_return2; + } else { + /* + * Now we have poped up to the data with + * parity error or frame error. + */ + goto handle_error2; + } + } + + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char2; + +error_return2: + uart_insert_char(&sport->port, (status & 0xFFFF), + URISR_TO_SM(URISR_RXDOVR) , ch, flg); +ignore_char2: + status &= 0xffff; /* Keep URISR field*/ + status |= URUSR_TO_SM(uart->urusr); + } +out2: + tty_flip_buffer_push(tty); + return; + +handle_error2: + /* + * Update error counters. + */ + if (status & URISR_TO_SM(URISR_PER)) + sport->port.icount.parity++; + else { + if (status & URISR_TO_SM(URISR_FER)) { +#ifdef CONFIG_SERIAL_WMT_BKSIG + /* + * Experimental software patch for break signal detection. + * + * When I got there is a frame error in next frame data, + * I check the next data to judge if it is a break signal. + * + * FIXME: Open these if Bluetooth or IrDA need this patch. + * Dec.29.2004 by Harry. + */ + if ((ch & RX_PERMASK) == 0) { + sport->port.icount.brk++; + uart_handle_break(&sport->port); + } else + sport->port.icount.frame++; + +#else /* Don't support break sinal detection */ + sport->port.icount.frame++; +#endif + } + } + /* + * RX Over Run event + */ + if (status & URISR_TO_SM(URISR_RXDOVR)) + sport->port.icount.overrun++; + + if (status & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out2; + goto ignore_char2; + } + + /* + * Second, handle the events which we're interesting to listen. + */ + status &= sport->port.read_status_mask; + + if (status & URISR_TO_SM(URISR_PER)) + flg = TTY_PARITY; + else + if (status & URISR_TO_SM(URISR_FER)) { + +#ifdef CONFIG_SERIAL_WMT_BKSIG + /* Software patch for break signal detection. + * + * When I got there is a frame error in next frame data, + * I check the next data to judge if it is a break signal. + * + * FIXME: Open these if Bluetooth or IrDA need this patch. + * Dec.29.2004 by Harry. + */ + if (sport->port.read_status_mask & SW_BKSIG) { + if ((ch & RX_PERMASK) == 0) { + DEBUG_INTR("handling break...."); + flg = TTY_BREAK; + /*goto error_return;*/ + } else { + flg = TTY_FRAME; + /*goto error_return;*/ + } + } else { + flg = TTY_FRAME; + /*goto error_return;*/ + } +#else /* Don't support break sinal detection */ + flg = TTY_FRAME; +#endif + } + + if (status & URISR_TO_SM(URISR_RXDOVR)) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + + ch = 0; + flg = TTY_OVERRUN; + } + #ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; + #endif + goto error_return2; + } + +} +#endif +/* + * Inside the UART interrupt service routine dut to following + * reason: + * + * URISR_TXFAE: TX FIFO almost empty (FIFO mode) + * URISR_TXFE: TX FIFO empty(FIFO mode) + */ +#ifndef CONFIG_SERIAL_WMT_DMA +static void wmt_tx_chars(struct wmt_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + if (sport->port.x_char) { + /* + * Fill character to the TX FIFO entry. + */ + uart->urtxf[0] = sport->port.x_char; + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /*Check the modem control lines before transmitting anything.*/ + wmt_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + wmt_stop_tx(&sport->port); + return; + } + + /*{2007/11/10 JHT Support the WMT Serial Port Driver Because the + * definition of URSIRT Bit[0] & Bit[3] are different. + * Before WMT these bit are defined as RO, in WMT + * they are changed into the W1C. Therefore the xmit function + * for the FIFO mode should be modified as well. + */ + while ((uart->urfidx & 0x1F) < 16) { + if (uart_circ_empty(xmit)) + break; + + if (uart->urusr & URUSR_TXDBSY) + continue; + uart->urtxf[0] = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + /*}2007/11/10-JHT*/ + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + wmt_stop_tx(&sport->port); +} +#else + +static void wmt_tx_chars(struct wmt_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + int head = xmit->head; + int tail = xmit->tail; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + char *dma_buf = sport->uart_tx_dma_buf0_org; + unsigned int tx_count; + + if (sport->port.x_char) { + /* + * Fill character to the TX FIFO entry. + */ + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + /*Dma is still running*/ + if (sport->uart_tx_dma_flag != DMA_TX_END) + return; + + *dma_buf = sport->port.x_char; + sport->uart_tx_dma_flag = DMA_TX_CHAR; + DMA_TX_START(sport, sport->uart_tx_dma_phy0_org, 1); + } else { + uart->urtxf[0] = sport->port.x_char; + sport->port.icount.tx++; + sport->port.x_char = 0; + } + return; + } + /*Check the modem control lines before transmitting anything.*/ + wmt_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + wmt_stop_tx(&sport->port); + return; + } + + /*{2007/11/10 JHT Support the WMT Serial Port Driver Because the + * definition of URSIRT Bit[0] & Bit[3] are different. + * Before WMT these bit are defined as RO, in WMT + * they are changed into the W1C. Therefore the xmit function + * for the FIFO mode should be modified as well. + */ + + + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + while((uart->urusr & URUSR_TXON)); + if(sport->port.line == mtk6622_tty){ + while ((uart->urusr & URUSR_TXDBSY)); + } + + if (sport->uart_tx_dma_flag == DMA_TX_END) { + sport->uart_tx_dma_flag = DMA_TX_XMIT; + tx_count = 0; + head = xmit->head; + tail = xmit->tail; + sport->uart_tx_count = 0; + while (head != tail) { + *dma_buf = xmit->buf[tail]; + tail = (tail + 1) & (UART_XMIT_SIZE - 1); + dma_buf++; + tx_count++; + sport->uart_tx_count++; + + if (tx_count == UART_BUFFER_SIZE) + break; + } + DMA_TX_START(sport, sport->uart_tx_dma_phy0_org, tx_count); + } + } else { + while ((uart->urfidx & 0x1F) < 16) { + if (uart_circ_empty(xmit)) + break; + + if (uart->urusr & URUSR_TXDBSY) + continue; + uart->urtxf[0] = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + wmt_stop_tx(&sport->port); + } + +} +#endif + +static irqreturn_t wmt_int(int irq, void *dev_id) +{ + struct wmt_port *sport = dev_id; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned int status, pass_counter = 0; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + /* + * Put interrupts status information to status bit[0:15] + * Put UART status register to status bit[16:31]. + */ + + status = URISR_TO_SM(uart->urisr) | URUSR_TO_SM(uart->urusr); + uart->urisr |= SM_TO_URISR(status); + + do { + /* + * First, we handle RX events. + * + * RX FIFO Almost Full. (URUSR_RXFAF) + * RX Timeout. (URISR_RXTOUT) + * Frame error (URISR_FER) + * + * Note that also allow URISR_FER and URISR_PER event to do rx. + */ + + if (status & URISR_TO_SM(URISR_RXFAF | URISR_RXFF | URISR_RXTOUT |\ + URISR_PER | URISR_FER)) { + + if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR) + wmt_rx_chars(sport, status); + + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + uart->urier = 0; /*close uart rx interruption */ + uart->urisr |= SM_TO_URISR(status); /* clear uart rx int status */ + sport->uart_rx_dma_flag = 0; + mod_timer(&sport->rx_timer, jiffies + rx_timeout); + break; + } + } + /* + * Second, we handle TX events. + * + * If there comes a TX FIFO Almost event, try to fill TX FIFO. + */ + +#ifndef CONFIG_SERIAL_WMT_DMA + wmt_tx_chars(sport); +#else + if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR) + wmt_tx_chars(sport); +#endif + if (pass_counter++ > WMT_ISR_PASS_LIMIT) + break; + + /* + * Update UART interrupt status and general status information. + */ + status = (URISR_TO_SM(uart->urisr) | URUSR_TO_SM(uart->urusr)); + uart->urisr |= SM_TO_URISR(status); + + /* + * Inside the loop, we handle events that we're interesting. + */ + status &= sport->port.read_status_mask; + + /* + * Continue loop while following condition: + * + * TX FIFO Almost Empty. (URISR_TXFAE) + * RX FIFO Almost Full. (URISR_RXFAF) + * RX Receive Time Out. (URISR_RXTOUT) + */ + } while (status & (URISR_TXFE | + URISR_TXFAE | + URISR_RXFAF | + URISR_RXFF | + URISR_RXTOUT | + URISR_RXDOVR)); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return IRQ_HANDLED; +} + +/* wmt_tx_empty() + * + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int wmt_tx_empty(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + return (uart->urusr & URUSR_TXDBSY) ? 0 : TIOCSER_TEMT; +} + +/* wmt_get_mctrl() + * + * Returns the current state of modem control inputs. + * + * Note: Only support CTS now. + */ +static u_int wmt_get_mctrl(struct uart_port *port) +{ + u_int ret = TIOCM_DSR | TIOCM_CAR; + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + ret |= (uart->urusr & URUSR_CTS) ? TIOCM_CTS : 0; + + return ret; +} + +/* wmt_set_mctrl() + * + * This function sets the modem control lines for port described + * by 'port' to the state described by mctrl. More detail please + * refer to Documentation/serial/driver. + * + * Note: Only support RTS now. + */ +static void wmt_set_mctrl(struct uart_port *port, u_int mctrl) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + if (mctrl & TIOCM_RTS) + uart->urlcr |= URLCR_RTS; + else + uart->urlcr &= ~URLCR_RTS; + +} + +/* + * Interrupts always disabled. + */ +static void wmt_break_ctl(struct uart_port *port, int break_state) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + + if (break_state == -1) { + int i; + unsigned int urbrd = URBRD_BRD(uart->urdiv); + + /* + * This looks something tricky. + * Anyway, we need to get current baud rate divisor, + * search bcv in baud_table[], program it into + * URBKR, then generate break signal. + */ + for (i = 0; i < BAUD_TABLE_SIZE; i++) { + if ((baud_table[i].brd & URBRD_BRDMASK) == urbrd) + break; + } + + if (i < BAUD_TABLE_SIZE) { + uart->urbkr = URBKR_BCV(baud_table[i].bcv); + uart->urlcr |= URLCR_BKINIT; + } + } + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static char *wmt_uartname[] = { + "uart0", + "uart1", +#ifdef CONFIG_UART_2_3_ENABLE + "uart2", + "uart3", +#endif +}; + +#ifdef CONFIG_SERIAL_WMT_DMA +static void uart_dma_callback_rx(void *data) +{ + struct wmt_port *sport = data; +#ifdef UART_DEBUG + static int out_range =0x00; + if(dma_write_index >=4096){ + printk("out of 4096\n"); + dma_write_index = 0x00; + out_range++; + } +#endif + if (sport->buffer_used == 0) { + sport->buffer_used = 1; + DMA_RX_START(sport, sport->uart_rx_dma_phy0_org, UART_BUFFER_SIZE); + } else if (sport->buffer_used == 1) { + sport->buffer_used = 2; + DMA_RX_START(sport, sport->uart_rx_dma_phy1_org, UART_BUFFER_SIZE); + } else if (sport->buffer_used == 2) { + sport->buffer_used = 0; + DMA_RX_START(sport, sport->uart_rx_dma_phy2_org, UART_BUFFER_SIZE); + } +#ifdef UART_DEBUG + DMA_pbuf[dma_write_index] = sport->buffer_used + (out_range<<24); + //printk("0x%x dma rx DMA_pbuf[%d]:0x%x\n",(unsigned int)DMA_pbuf,dma_write_index,DMA_pbuf[dma_write_index]); + if(sport->buffer_rx_count_selected == 0){ + COUNT_pbuf[dma_write_index] = sport->uart_dma_tmp_phy0; + }else if(sport->buffer_rx_count_selected == 1) { + COUNT_pbuf[dma_write_index] = sport->uart_dma_tmp_phy1; + }else if(sport->buffer_rx_count_selected == 2) { + COUNT_pbuf[dma_write_index] = sport->uart_dma_tmp_phy2; + } + + if(sport->buffer_selected == 0){ + CPU_pbuf[dma_write_index] = sport->uart_dma_tmp_buf0; + }else if(sport->buffer_selected == 1){ + CPU_pbuf[dma_write_index] = sport->uart_dma_tmp_buf1; + }else if(sport->buffer_selected == 2){ + CPU_pbuf[dma_write_index] = sport->uart_dma_tmp_buf2; + } + dma_write_index++; +#endif + +} +#ifdef UART_DEBUG +void print_dma_count_cpu_buf_pos(struct wmt_port *sport) +{ + int i; + for(i=0;i<UART_BUFFER_SIZE;i++){ + //printk("0x%x DMA_pbuf[%d]:0x%x\n",DMA_pbuf,i,DMA_pbuf[i]); + if(DMA_pbuf[i] == 0x5a5a5a5a) + break; + printk("dma buf index:0x%x ",DMA_pbuf[i]); + if((sport->uart_rx_dma_phy0_org <= COUNT_pbuf[i]) && (COUNT_pbuf[i] <= (sport->uart_rx_dma_phy0_org+UART_BUFFER_SIZE))){ + printk("count buf index:0x00 offset:0x%x ",COUNT_pbuf[i]-sport->uart_rx_dma_phy0_org); + }else if((sport->uart_rx_dma_phy1_org <= COUNT_pbuf[i]) && (COUNT_pbuf[i] <= (sport->uart_rx_dma_phy1_org+UART_BUFFER_SIZE))){ + printk("count buf index:0x01 offset:0x%x ",COUNT_pbuf[i]-sport->uart_rx_dma_phy1_org); + }if((sport->uart_rx_dma_phy2_org <= COUNT_pbuf[i]) && (COUNT_pbuf[i] <= (sport->uart_rx_dma_phy2_org+UART_BUFFER_SIZE))){ + printk("count buf index:0x02 offset:0x%x ",COUNT_pbuf[i]-sport->uart_rx_dma_phy2_org); + } + + if((sport->uart_rx_dma_buf0_org <= CPU_pbuf[i]) && (CPU_pbuf[i] <= (sport->uart_rx_dma_buf0_org+UART_BUFFER_SIZE))){ + printk("cpu buf index:0x00 offset:0x%x\n",CPU_pbuf[i]-(unsigned int)sport->uart_rx_dma_buf0_org); + }else if((sport->uart_rx_dma_buf1_org <= CPU_pbuf[i]) && (CPU_pbuf[i] <= (sport->uart_rx_dma_buf1_org+UART_BUFFER_SIZE))){ + printk("cpu buf index:0x01 offset:0x%x\n",CPU_pbuf[i]-(unsigned int)sport->uart_rx_dma_buf1_org); + }if((sport->uart_rx_dma_buf2_org <= CPU_pbuf[i]) && (CPU_pbuf[i] <= (sport->uart_rx_dma_buf2_org+UART_BUFFER_SIZE))){ + printk("cpu buf index:0x02 offset:0x%x\n",CPU_pbuf[i]-(unsigned int)sport->uart_rx_dma_buf2_org); + } + printk("\n"); + } +} +#endif +static void uart_dma_callback_tx(void *data) +{ + struct wmt_port *sport = data; + unsigned long flags; + sport->dma_tx_cnt++; + spin_lock_irqsave(&sport->port.lock, flags); + + if (sport->uart_tx_dma_flag == DMA_TX_CHAR) { + sport->port.icount.tx++; + sport->port.x_char = 0; + } else { + sport->port.state->xmit.tail = + (sport->port.state->xmit.tail + sport->uart_tx_count) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx += sport->uart_tx_count; + } + sport->uart_tx_dma_flag = DMA_TX_END; + + wmt_tx_chars(sport); + + if (uart_circ_chars_pending(&sport->port.state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(&sport->port.state->xmit)) + wmt_stop_tx(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); + +} +#endif + +wmt_uart1_pre_init(void) +{ + GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT4 | BIT5); + auto_pll_divisor(DEV_UART1, CLK_ENABLE, 0, 0); + printk("wmt_uart1_pre_init\n"); +} + +wmt_uart1_post_deinit(void) +{ + GPIO_CTRL_GP18_UART_BYTE_VAL |= (BIT4 | BIT5); + auto_pll_divisor(DEV_UART1, CLK_DISABLE, 0, 0); + printk("wmt_uart1_post_deinit\n"); +} + +static int wmt_startup(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + char *uartname = NULL; + int retval; + int i; + unsigned long flags; + + switch (sport->port.irq) { + + case IRQ_UART0: + uartname = wmt_uartname[0]; +/*#ifdef CONFIG_SERIAL_WMT_DMA + sport->port.dma_rx_dev = UART_0_RX_DMA_REQ; + sport->port.dma_tx_dev = UART_0_TX_DMA_REQ; + sport->id = "uart0"; + sport->port.dma_rx_cfg = dma_device_cfg_table[UART_0_RX_DMA_REQ]; + sport->port.dma_tx_cfg = dma_device_cfg_table[UART_0_TX_DMA_REQ]; +#endif*/ + break; + + case IRQ_UART1: + wmt_uart1_pre_init();//added by rubbitxiao + uartname = wmt_uartname[1]; +#ifdef CONFIG_SERIAL_WMT_DMA + sport->dma_rx_dev = UART_1_RX_DMA_REQ; + sport->dma_tx_dev = UART_1_TX_DMA_REQ; + sport->id = "uart1"; + sport->dma_rx_cfg = dma_device_cfg_table[UART_1_RX_DMA_REQ]; + sport->dma_tx_cfg = dma_device_cfg_table[UART_1_TX_DMA_REQ]; +#endif + break; + +#ifdef CONFIG_UART_2_3_ENABLE + case IRQ_UART2: + uartname = wmt_uartname[2]; +#ifdef CONFIG_SERIAL_WMT_DMA + sport->dma_rx_dev = UART_2_RX_DMA_REQ; + sport->dma_tx_dev = UART_2_TX_DMA_REQ; + sport->id = "uart2"; + sport->dma_rx_cfg = dma_device_cfg_table[UART_2_RX_DMA_REQ]; + sport->dma_tx_cfg = dma_device_cfg_table[UART_2_TX_DMA_REQ]; +#endif + break; + case IRQ_UART3: + uartname = wmt_uartname[3]; +#ifdef CONFIG_SERIAL_WMT_DMA + sport->dma_rx_dev = UART_3_RX_DMA_REQ; + sport->dma_tx_dev = UART_3_TX_DMA_REQ; + sport->id = "uart3"; + sport->dma_rx_cfg = dma_device_cfg_table[UART_3_RX_DMA_REQ]; + sport->dma_tx_cfg = dma_device_cfg_table[UART_3_TX_DMA_REQ]; +#endif + break; +#endif + + } +#ifdef CONFIG_SERIAL_WMT_DMA + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + memset(sport->uart_rx_dma_buf0_org, 0x0, UART_BUFFER_SIZE); + memset(sport->uart_rx_dma_buf1_org, 0x0, UART_BUFFER_SIZE); + memset(sport->uart_rx_dma_buf2_org, 0x0, UART_BUFFER_SIZE); + memset(sport->uart_tx_dma_buf0_org, 0x0, UART_BUFFER_SIZE); + sport->uart_dma_tmp_buf0 = sport->uart_rx_dma_buf0_org; + sport->uart_dma_tmp_phy0 = sport->uart_rx_dma_phy0_org; + sport->uart_dma_tmp_buf1 = sport->uart_rx_dma_buf1_org; + sport->uart_dma_tmp_buf2 = sport->uart_rx_dma_buf2_org; + sport->uart_dma_tmp_phy1 = sport->uart_rx_dma_phy1_org; + sport->uart_dma_tmp_phy2 = sport->uart_rx_dma_phy2_org; + sport->buffer_used = 0; /*to record which buf DMA hardware used*/ + sport->buffer_selected = 0; /* to record which buf software is used to put data to kernel*/ + sport->buffer_rx_count_selected = 0; /* to record which buf rx_count is used*/ + sport->rx_dmach = NULL_DMA; + sport->tx_dmach = NULL_DMA; + sport->last_pos = 0; + sport->uart_tx_dma_flag = DMA_TX_END; + sport->uart_tx_count = 0; + sport->dma_tx_cnt = 0x00; + init_timer(&sport->rx_timer); + sport->rx_timer.function = wmt_rx_timeout; + sport->rx_timer.data = (unsigned long)sport; + DMA_RX_REQUEST(sport, uart_dma_callback_rx); + DMA_TX_REQUEST(sport, uart_dma_callback_tx); + wmt_setup_dma(sport->rx_dmach, sport->dma_rx_cfg); + wmt_setup_dma(sport->tx_dmach, sport->dma_tx_cfg); + DMA_RX_START(sport, sport->uart_rx_dma_phy0_org, UART_BUFFER_SIZE); + DMA_RX_START(sport, sport->uart_rx_dma_phy1_org, UART_BUFFER_SIZE); + DMA_RX_START(sport, sport->uart_rx_dma_phy2_org, UART_BUFFER_SIZE); +#ifdef UART_DEBUG + DMA_pbuf = kmalloc(UART_BUFFER_SIZE*sizeof(unsigned int*), GFP_KERNEL); + COUNT_pbuf = kmalloc(UART_BUFFER_SIZE*sizeof(unsigned int*), GFP_KERNEL); + CPU_pbuf = kmalloc(UART_BUFFER_SIZE*sizeof(unsigned int*), GFP_KERNEL); + if(!DMA_pbuf || !COUNT_pbuf || !CPU_pbuf){ + printk("kmalloc buf for debug buf failed\n"); + return; + }else{ + memset(DMA_pbuf,0x5a,UART_BUFFER_SIZE*sizeof(unsigned int*)); + memset(COUNT_pbuf,0x00,UART_BUFFER_SIZE*sizeof(unsigned int*)); + memset(CPU_pbuf,0x00,UART_BUFFER_SIZE*sizeof(unsigned int*)); + } + dma_write_index =0x00; +#endif + { + char uboot_buf[256]; + int varlen = sizeof(uboot_buf); + if(wmt_getsyspara("wmt.bt.tty",uboot_buf,&varlen) == 0) + { + sscanf(uboot_buf,"%d",&mtk6622_tty); + printk("mtk6622_tty:%d\n",mtk6622_tty); + if(1<=mtk6622_tty && mtk6622_tty <=3){ + printk("wmt.bt.tty is correct\n"); + }else{ + printk("wmt.bt.tty is illegal\n"); + mtk6622_tty = -1; + } + }else{ + printk("have not set uboot variant:wmt.bt.tty\n"); + } + } + } +#endif + /* + * Allocate the IRQ + */ + retval = request_irq(sport->port.irq, wmt_int, 0, uartname, sport); + if (retval) + return retval; + + /* + * Setup the UART clock divisor + */ + for (i = 0; i < BAUD_TABLE_SIZE; i++) { + if (baud_table[i].baud == 115200) + break; + } + spin_lock_irqsave(&sport->port.lock, flags); + + uart->urdiv = baud_table[i].brd; + + /* Disable TX,RX*/ + uart->urlcr = 0; + /* Disable all interrupt*/ + uart->urier = 0; + + /*Reset TX,RX Fifo*/ + uart->urfcr = URFCR_TXFRST | URFCR_RXFRST; + + while (uart->urfcr) + ; + + /* Disable Fifo*/ + uart->urfcr &= ~(URFCR_FIFOEN); + + uart->urlcr |= (URLCR_DLEN & ~URLCR_STBLEN & ~URLCR_PTYEN); +#ifdef CONFIG_SERIAL_WMT_DMA + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + uart->urfcr = URFCR_FIFOEN | URFCR_TXFLV(8) | URFCR_RXFLV(1) | URFCR_TRAIL; + uart->urtod = 0x0a; + } else + uart->urfcr = URFCR_FIFOEN | URFCR_TXFLV(8) | URFCR_RXFLV(4); +#else + /* Enable Fifo, Tx 16 , Rx 16*/ + uart->urfcr = URFCR_FIFOEN | URFCR_TXFLV(8) | URFCR_RXFLV(4); +#endif + /* Enable Fifo, Tx 8 , Rx 8*/ +#ifdef CONFIG_SERIAL_WMT_DMA + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + uart->urlcr |= URLCR_RXEN | URLCR_TXEN | URLCR_DMAEN | URLCR_RCTSSW; + uart->urier = URIER_ERXFAF | URIER_ERXFF | URIER_ERXTOUT | + URIER_EPER | URIER_EFER | URIER_ERXDOVR; + } else { + uart->urlcr |= URLCR_RXEN | URLCR_TXEN | URLCR_RCTSSW; + uart->urier = URIER_ERXFAF | URIER_ERXFF | URIER_ERXTOUT | + URIER_EPER | URIER_EFER | URIER_ERXDOVR; + } +#else + uart->urlcr |= URLCR_RXEN | URLCR_TXEN | URLCR_RCTSSW; + uart->urier = URIER_ERXFAF | URIER_ERXFF | URIER_ERXTOUT | URIER_EPER | + URIER_EFER | URIER_ERXDOVR; + +#endif + /* + * Enable RX FIFO almost full, timeout, and overrun interrupts. + */ + + /* + * Enable modem status interrupts + */ + + wmt_enable_ms(&sport->port); + spin_unlock_irqrestore(&sport->port.lock, flags); + + return 0; +} + +static void wmt_shutdown(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned long flags; + //added begin by rubbitxiao + spin_lock_irqsave(&sport->port.lock, flags); + /* Disable TX,RX*/ + uart->urlcr = 0; + /* Disable all interrupt*/ + uart->urier = 0; + /*Reset TX,RX Fifo*/ + uart->urfcr = URFCR_TXFRST | URFCR_RXFRST; + while (uart->urfcr) + ; + /* Disable Fifo*/ + uart->urfcr &= ~(URFCR_FIFOEN); + uart->urlcr |= (URLCR_DLEN & ~URLCR_STBLEN & ~URLCR_PTYEN); + spin_unlock_irqrestore(&sport->port.lock, flags); + //added end by rubbitxiao + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Free the allocated interrupt + */ + free_irq(sport->port.irq, sport); + + /* + * Disable all interrupts, port and break condition. + */ + //spin_lock_irqsave(&sport->port.lock, flags); + //uart->urier &= ~(URIER_ETXFE | URIER_ETXFAE | URIER_ERXFF | URIER_ERXFAF); + //spin_unlock_irqrestore(&sport->port.lock, flags); + +#ifdef CONFIG_SERIAL_WMT_DMA + if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) { + del_timer_sync(&sport->rx_timer); + DMA_RX_STOP(sport); + DMA_RX_CLEAR(sport); + DMA_RX_FREE(sport); + while (sport->uart_tx_dma_flag != DMA_TX_END) + msleep(1); + + DMA_TX_STOP(sport); + DMA_TX_CLEAR(sport); + DMA_TX_FREE(sport); + } +#endif + if ((unsigned int)(sport->port.membase) == UART1_BASE_ADDR) + wmt_uart1_post_deinit(); +} + +/* wmt_uart_pm() + * + * Switch on/off uart in powersave mode. + * + * Hint: Identify port by irq number. + */ +static void wmt_uart_pm(struct uart_port *port, u_int state, u_int oldstate) +{ + return; +} + +static void +wmt_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) +{ + struct wmt_port *sport = (struct wmt_port *)port; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned long flags; + unsigned int new_urlcr, old_urlcr, old_urier, tmp_urisr, baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + int i; + + /* + * If we don't support modem control lines, don't allow + * these to be set. + */ + if (0) { + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); + termios->c_cflag |= CLOCAL; + } + /* + * Only support CS7 and CS8. + */ + while ((termios->c_cflag & CSIZE) != CS7 && (termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8) + new_urlcr = URLCR_DLEN; + else + new_urlcr = 0; + + if (termios->c_cflag & CRTSCTS) + new_urlcr &= ~URLCR_RCTSSW; + else + new_urlcr |= URLCR_RCTSSW; + + if (termios->c_cflag & CSTOPB) + new_urlcr |= URLCR_STBLEN; + + if (termios->c_cflag & PARENB) { + /* + * Enable parity. + */ + new_urlcr |= URLCR_PTYEN; + + /* + * Parity mode select. + */ + if (termios->c_cflag & PARODD) + new_urlcr |= URLCR_PTYMODE; + } + + /* + * Ask the core to get baud rate, but we need to + * calculate quot by ourself. + */ + baud = uart_get_baud_rate(port, termios, old, 9600, 921600); + + /* + * We need to calculate quot by ourself. + * + * FIXME: Be careful, following result is not an + * interger quotient, fix it if need. + */ + /*quot = port->uartclk / (13 * baud);*/ + + spin_lock_irqsave(&sport->port.lock, flags); + + /* + * Mask out other interesting to listen expect TX FIFO almost empty event. + */ + sport->port.read_status_mask &= URISR_TO_SM(URISR_TXFAE | URISR_TXFE); + + /* + * We're also interested in receiving RX FIFO events. + */ + sport->port.read_status_mask |= URISR_TO_SM(URISR_RXDOVR | URISR_RXFAF | URISR_RXFF); + + /* + * Check if we need to enable frame and parity error events + * to be passed to the TTY layer. + */ + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= URISR_TO_SM(URISR_FER | URISR_PER); + +#ifdef CONFIG_SERIAL_WMT_BKSIG + /* + * check if we need to enable break events to be passed to the TTY layer. + */ + if (termios->c_iflag & (BRKINT | PARMRK)) + /* + * WMT UART doesn't support break signal detection interrupt. + * + * I try to implement this using URISR_FER. + */ + sport->port.read_status_mask |= SW_BKSIG; +#endif + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= URISR_TO_SM(URISR_FER | URISR_PER); + + if (termios->c_iflag & IGNBRK) { +#ifdef CONFIG_SERIAL_WMT_BKSIG + /* + * WMT UART doesn't support break signal detection interrupt. + * + * I try to implement this using URISR_FER. + */ + sport->port.ignore_status_mask |= BIT31;/*FIXME*/ +#endif + + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= URISR_TO_SM(URISR_RXDOVR); + } + + del_timer_sync(&sport->timer); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Disable FIFO request interrupts and drain transmitter + */ + old_urlcr = uart->urlcr; + old_urier = uart->urier; + uart->urier = old_urier & ~(URIER_ETXFAE | URIER_ERXFAF); + + /* + * Two step polling, first step polling the remaining + * entries in TX FIFO. This step make it safe to drain + * out all of remaining data in FIFO. + */ + while (URFIDX_TXFIDX(uart->urfidx)) + barrier(); + + /* + * Second step to make sure the last one data has been sent. + */ + while (uart->urusr & URUSR_TXDBSY) + barrier(); + + /* + * Disable this UART port. + */ + uart->urier = 0; + + /* + * Set the parity, stop bits and data size + */ + uart->urlcr = new_urlcr; + + /* + * Set baud rate + */ + + for (i = 0; i < BAUD_TABLE_SIZE; i++) { + if (baud_table[i].baud == baud) + break; + } + uart->urdiv = baud_table[i].brd; + + /* + * Read to clean any pending pulse interrupts. + */ + tmp_urisr = uart->urisr; + + /* + * Restore FIFO interrupt, TXEN bit, RXEN bit settings. + */ + uart->urier = old_urier; +#ifdef CONFIG_SERIAL_WMT_DMA + uart->urlcr |= old_urlcr & (URLCR_TXEN | URLCR_RXEN | URLCR_DMAEN); +#else + uart->urlcr |= old_urlcr & (URLCR_TXEN | URLCR_RXEN); +#endif + + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + wmt_enable_ms(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *wmt_type(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + + return (sport->port.type == PORT_WMT) ? "wmt serial" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void wmt_release_port(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + + release_mem_region(sport->port.mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int wmt_request_port(struct uart_port *port) +{ + struct wmt_port *sport = (struct wmt_port *)port; + + return request_mem_region(sport->port.mapbase, + UART_PORT_SIZE, + "uart") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void wmt_config_port(struct uart_port *port, int flags) +{ + struct wmt_port *sport = (struct wmt_port *)port; + + if (flags & UART_CONFIG_TYPE && wmt_request_port(&sport->port) == 0) + sport->port.type = PORT_WMT; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_WMT and PORT_UNKNOWN + */ +static int wmt_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct wmt_port *sport = (struct wmt_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_WMT) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops wmt_pops = { + .tx_empty = wmt_tx_empty, + .set_mctrl = wmt_set_mctrl, + .get_mctrl = wmt_get_mctrl, + .stop_tx = wmt_stop_tx, + .start_tx = wmt_start_tx, + .stop_rx = wmt_stop_rx, + .enable_ms = wmt_enable_ms, + .break_ctl = wmt_break_ctl, + .startup = wmt_startup, + .shutdown = wmt_shutdown, + .pm = wmt_uart_pm, + .set_termios = wmt_set_termios, + .type = wmt_type, + .release_port = wmt_release_port, + .request_port = wmt_request_port, + .config_port = wmt_config_port, + .verify_port = wmt_verify_port, +}; + +static int parse_spi1_param(void) +{ + char buf[64]; + size_t l = sizeof(buf); + int uart_spi_sel = 0; + + if (wmt_getsyspara("wmt.spi1.param", buf, &l) == 0) { + sscanf(buf, "%d", &uart_spi_sel); + } + return uart_spi_sel; +} + +/* Setup the WMT serial ports. Note that we don't include the IrDA + * port here since we have our own SIR/FIR driver (see drivers/net/irda) + * + * Note also that we support "console=ttyVTx" where "x" is either 0 to 2. + * Which serial port this ends up being depends on the machine you're + * running this kernel on. + */ +static void wmt_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + + first = 0; + + wmt_uart_spi_sel = parse_spi1_param(); + + for (i = 0; i < NR_PORTS; i++) { + wmt_ports[i].port.uartclk = 24000000; + wmt_ports[i].port.ops = &wmt_pops; + wmt_ports[i].port.fifosize = 16; + wmt_ports[i].port.line = i; + wmt_ports[i].port.iotype = SERIAL_IO_MEM; + init_timer(&wmt_ports[i].timer); + wmt_ports[i].timer.function = wmt_timeout; + wmt_ports[i].timer.data = (unsigned long)&wmt_ports[i]; + } + + /* + * Make sure all UARTs are not configured as GPIO function. + * + * This step may be redundant due to bootloader has already + * done this for us. + */ + + + /* Switch the Uart's pin from default GPIO into uart function pins for UART0 ~ UART3 */ + GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2 | BIT3 | + BIT4 | BIT5 | BIT6 | BIT7); /*UART0 UART1*/ +#ifdef CONFIG_UART_2_3_ENABLE + GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT2 | BIT3 | BIT6 | BIT7); /*UART2 UART3*/ +#endif + + /*Set Uart0 and Uart1, Uart2 and Uart3 pin share*/ + PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT9 | BIT8); + if (wmt_uart_spi_sel == SHARE_PIN_UART) + PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT10); + +#ifdef CONFIG_UART_2_3_ENABLE + //kevin modify uart0,uart1(hw flow control),uart2 + PIN_SHARING_SEL_4BYTE_VAL |= (BIT8); +#endif + + auto_pll_divisor(DEV_UART0, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_UART1, CLK_ENABLE, 0, 0); +#ifdef CONFIG_UART_2_3_ENABLE + auto_pll_divisor(DEV_UART2, CLK_ENABLE, 0, 0); + auto_pll_divisor(DEV_UART3, CLK_ENABLE, 0, 0); +#endif +} + +void __init wmt_register_uart_fns(struct wmt_port_fns *fns) +{ + if (fns->get_mctrl) + wmt_pops.get_mctrl = fns->get_mctrl; + if (fns->set_mctrl) + wmt_pops.set_mctrl = fns->set_mctrl; + + wmt_pops.pm = fns->pm; + wmt_pops.set_wake = fns->set_wake; +} + +void __init wmt_register_uart(int idx, int port) +{ + if (idx >= NR_PORTS) { + printk(KERN_ERR "%s: bad index number %d\n", __func__, idx); + return; + } + + switch (port) { + case 0: + wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART0_BASE_ADDR)); + wmt_ports[idx].port.mapbase = UART0_BASE_ADDR; + wmt_ports[idx].port.irq = IRQ_UART0; + wmt_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + case 1: + wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART1_BASE_ADDR)); + wmt_ports[idx].port.mapbase = UART1_BASE_ADDR; + wmt_ports[idx].port.irq = IRQ_UART1; + wmt_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; +#ifdef CONFIG_UART_2_3_ENABLE + case 2: + wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART2_BASE_ADDR)); + wmt_ports[idx].port.mapbase = UART2_BASE_ADDR; + wmt_ports[idx].port.irq = IRQ_UART2; + wmt_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; + case 3: + wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART3_BASE_ADDR)); + wmt_ports[idx].port.mapbase = UART3_BASE_ADDR; + wmt_ports[idx].port.irq = IRQ_UART3; + wmt_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; + break; +#endif + + default: + printk(KERN_ERR "%s: bad port number %d\n", __func__, port); + } +} + +#ifdef CONFIG_SERIAL_WMT_CONSOLE + +/* + * Interrupts are disabled on entering + * + * Note: We do console writing with UART register mode. + */ + +static void wmt_console_write(struct console *co, const char *s, u_int count) +{ + struct wmt_port *sport = &wmt_ports[co->index]; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned int i, old_urlcr, old_urier; + /*{JHT*/ + unsigned int old_urfcr; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + /*}JHT*/ + /* + * First, save URLCR and URIER. + */ + old_urlcr = uart->urlcr; + old_urier = uart->urier; + /*{JHT*/ + old_urfcr = uart->urfcr; + /*}JHT*/ + + /* + * Second, switch to register mode with follows method: + * + * Disable FIFO threshold interrupts, and enable transmitter. + */ + uart->urier &= ~(URIER_ETXFAE | URIER_ERXFAF); + uart->urlcr |= URLCR_TXEN; + /*{JHT*/ + uart->urfcr &= ~URFCR_FIFOEN; + /*}JHT*/ + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + /* + * Polling until free for transmitting. + */ + while (uart->urusr & URUSR_TXDBSY) + ; + + uart->urtdr = (unsigned int)s[i]; + + /* + * Do CR if there comes a LF. + */ + if (s[i] == '\n') { + /* + * Polling until free for transmitting. + */ + while (uart->urusr & URUSR_TXDBSY) + ; + + uart->urtdr = (unsigned int)'\r'; + } + } + + /* + * Finally, wait for transmitting done and restore URLCR and URIER. + */ + while (uart->urusr & URUSR_TXDBSY) + ; + + uart->urlcr = old_urlcr; + uart->urier = old_urier; + /*{JHT*/ + uart->urfcr = old_urfcr; + /*}JHT*/ + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +/* + * If the port was already initialised (eg, by a boot loader), try to determine + * the current setup. + */ +static void __init wmt_console_get_options(struct wmt_port *sport, int *baud, int *parity, int *bits) +{ + int i; + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + + if ((uart->urlcr & (URLCR_RXEN | URLCR_TXEN)) == (URLCR_RXEN | URLCR_TXEN)) { + /* + * Port was enabled. + */ + unsigned quot; + + *parity = 'n'; + /* + * Check parity mode, 0:evev 1:odd + */ + if (uart->urlcr & URLCR_PTYEN) { + if (uart->urlcr & URLCR_PTYMODE) + *parity = 'o'; + else + *parity = 'e'; + } + + /* + * Check data length, 0:7-bit 1:8-bit + */ + if (uart->urlcr & URLCR_DLEN) + *bits = 8; + else + *bits = 7; + + /* + * Get baud rate divisor. + */ + quot = (uart->urdiv & URBRD_BRDMASK); + /* + * FIXME: I didn't trace the console driver want me + * report baud rate whether actual baud rate or ideal + * target baud rate, current I report baud as actual + * one, if it need value as target baud rate, just + * creat an array to fix it, Dec.23 by Harry. + */ + + for (i = 0; i < BAUD_TABLE_SIZE; i++) { + if ((baud_table[i].brd & URBRD_BRDMASK) == quot) { + *baud = baud_table[i].baud; + break; + } + } + + /* + * If this condition is true, something might be wrong. + * I reprot the actual baud rate temporary. + * Check the printk information then fix it. + */ + if (i >= BAUD_TABLE_SIZE) + *baud = sport->port.uartclk / (13 * (quot + 1)); + } +} + +#ifndef CONFIG_WMT_DEFAULT_BAUDRATE +#define CONFIG_WMT_DEFAULT_BAUDRATE 115200 +#endif + +static int __init +wmt_console_setup(struct console *co, char *options) +{ + struct wmt_port *sport; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int baud = CONFIG_WMT_DEFAULT_BAUDRATE; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + + sport = &wmt_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + wmt_console_get_options(sport, &baud, &parity, &bits); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver wmt_reg; + +static struct console wmt_console = { + +#ifdef CONFIG_SERIAL_WMT_TTYVT + .name = "ttyVT", +#else + .name = "ttyS", +#endif + .write = wmt_console_write, + .device = uart_console_device, + .setup = wmt_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &wmt_reg, +}; + +static int __init wmt_rs_console_init(void) +{ + wmt_init_ports(); + register_console(&wmt_console); + return 0; +} + +console_initcall(wmt_rs_console_init); + +#define WMT_CONSOLE (&wmt_console) + +#else /* CONFIG_SERIAL_WMT_CONSOLE */ + +#define WMT_CONSOLE NULL + +#endif + +static struct uart_driver wmt_reg = { + .owner = THIS_MODULE, + +#ifdef CONFIG_SERIAL_WMT_TTYVT + .driver_name = "ttyVT", + .dev_name = "ttyVT", +#else + .driver_name = "ttyS", + .dev_name = "ttyS", +#endif + .major = SERIAL_WMT_MAJOR, + .minor = MINOR_START, + .nr = NR_PORTS, + .cons = WMT_CONSOLE, +}; + + +void wmt_serial_set_reg(void) +{ + *(volatile unsigned int *) (UART0_BASE_ADDR + 0x00000008) = BRD_115200BPS; + *(volatile unsigned int *) (UART0_BASE_ADDR + 0x0000000c) = URLCR_TXEN | + URLCR_RXEN | + URLCR_DLEN | + URLCR_RCTSSW; + + *(volatile unsigned int *) (UART0_BASE_ADDR + 0x00000014) = URIER_ERXFAF | + URIER_ERXFF | + URIER_ERXTOUT | + URIER_EPER | + URIER_EFER | + URIER_ERXDOVR; + + *(volatile unsigned int *) (UART0_BASE_ADDR + 0x00000020) = URFCR_FIFOEN | + URFCR_TXFLV(8) | + URFCR_RXFLV(8); +} + +static int wmt_serial_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct device *dev = &pdev->dev; + struct wmt_port *sport = dev_get_drvdata(dev); + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned long flags; + + if (sport) { + if (sport->port.irq != IRQ_UART0) + uart_suspend_port(&wmt_reg, &sport->port); + } + if (!sport) + return 0; + + spin_lock_irqsave(&sport->port.lock, flags); + + /* save host register */ + sport->old_urdiv = uart->urdiv; + sport->old_urlcr = uart->urlcr; + sport->old_urier = uart->urier; + sport->old_urfcr = uart->urfcr; + sport->old_urtod = uart->urtod; + + uart->urier = 0; + spin_unlock_irqrestore(&sport->port.lock, flags); + + switch (sport->port.irq) { + case IRQ_UART0: + /*auto_pll_divisor(DEV_UART0, CLK_DISABLE, 0, 0);*/ + break; + case IRQ_UART1: + auto_pll_divisor(DEV_UART1, CLK_DISABLE, 0, 0); + break; +#ifdef CONFIG_UART_2_3_ENABLE + case IRQ_UART2: + auto_pll_divisor(DEV_UART2, CLK_DISABLE, 0, 0); + break; + + case IRQ_UART3: + auto_pll_divisor(DEV_UART3, CLK_DISABLE, 0, 0); + break; +#endif + default: + break; + + } + return 0; +} + +static int wmt_serial_resume(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wmt_port *sport = dev_get_drvdata(dev); + struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport); + unsigned long flags; + + if (!sport) + return 0; + + GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2 | BIT3 | + BIT4 | BIT5 | BIT6 | BIT7); /*UART0 UART1*/ +#ifdef CONFIG_UART_2_3_ENABLE + GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT2 | BIT3 | BIT6 | BIT7); /*UART2 UART3*/ +#endif + + /*Set Uart0 and Uart1, Uart2 and Uart3 pin share*/ + PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT9 | BIT8); + if (wmt_uart_spi_sel == SHARE_PIN_UART) + PIN_SHARING_SEL_4BYTE_VAL &= ~(BIT10); + +#ifdef CONFIG_UART_2_3_ENABLE + //kevin modify, uart0,uart1(hw flow control),uart2 + PIN_SHARING_SEL_4BYTE_VAL |= (BIT8); +#endif + + switch (sport->port.irq) { + case IRQ_UART0: + auto_pll_divisor(DEV_UART0, CLK_ENABLE, 0, 0); + break; + case IRQ_UART1: + auto_pll_divisor(DEV_UART1, CLK_ENABLE, 0, 0); + break; +#ifdef CONFIG_UART_2_3_ENABLE + case IRQ_UART2: + auto_pll_divisor(DEV_UART2, CLK_ENABLE, 0, 0); + break; + case IRQ_UART3: + auto_pll_divisor(DEV_UART3, CLK_ENABLE, 0, 0); + break; +#endif + default: + break; + } + + if (sport->port.irq != IRQ_UART0) { + /* Disable TX,RX */ + uart->urlcr = 0; + /* Disable all interrupt */ + uart->urier = 0; + /* Clear all interrupt */ + uart->urisr = 0xffffffff; + + /* Disable Fifo */ + uart->urfcr &= ~(URFCR_FIFOEN); + + /* Reset TX,RX Fifo */ + uart->urfcr = URFCR_TXFRST | URFCR_RXFRST; + + while (uart->urfcr) + ; + } + + spin_lock_irqsave(&sport->port.lock, flags); + /*store back the interrupt enable status*/ + uart->urdiv = sport->old_urdiv; + uart->urfcr = sport->old_urfcr; + uart->urtod = sport->old_urtod; + uart->urier = sport->old_urier; + uart->urlcr = sport->old_urlcr; + spin_unlock_irqrestore(&sport->port.lock, flags); + + if (sport) { + if (sport->port.irq != IRQ_UART0) + uart_resume_port(&wmt_reg, &sport->port); + + } + + return 0; +} +#if 1 +void print_dma_position(struct wmt_port *sport) +{ + unsigned int *rx_dma = (unsigned int *)(0xfe001904 + (0x20*sport->rx_dmach)); + printk("address 0x%p = 0x%x(value)\n",rx_dma,*rx_dma); +} + +void print_dma_buf_pointer(struct wmt_port *sport) +{ + printk("buf0:0x%p; buf1:0x%p; buf2:0x%p\n",sport->uart_dma_tmp_buf0,sport->uart_dma_tmp_buf1,sport->uart_dma_tmp_buf2); + printk("phy0:0x%x; phy1:0x%x; phy2:0x%x\n",sport->uart_dma_tmp_phy0,sport->uart_dma_tmp_phy1,sport->uart_dma_tmp_phy2); +} +void dump_uart_info(void) +{ + unsigned long flags; + struct wmt_port * p_wmt_port = &wmt_ports[1]; + uart_dump_reg(p_wmt_port); + printk("sport1->port.icount.rx0:0x%x,uart_tx_dma_phy0_org:0x%x\n",p_wmt_port->port.icount.rx,p_wmt_port->uart_tx_dma_phy0_org); + print_dma_position(p_wmt_port); + + //spin_lock_irqsave(&(p_wmt_port->port.lock), flags); + wmt_rx_chars(p_wmt_port,URISR_RXFAF | URISR_RXFF); + //spin_unlock_irqrestore(&(p_wmt_port->port.lock), flags); + + print_dma_position(p_wmt_port); + printk("sport1->port.icount.rx1:0x%x,uart_tx_dma_phy0_org:0x%x\n",p_wmt_port->port.icount.rx,p_wmt_port->uart_tx_dma_phy0_org); + printk("uart rxxx dma channel register:\n"); + wmt_dump_dma_regs(p_wmt_port->rx_dmach); + printk("uart tttx dma channel register:\n"); + wmt_dump_dma_regs(p_wmt_port->tx_dmach); + printk("buf pointer position\n"); + print_dma_buf_pointer(p_wmt_port); + printk("#######################################\n"); + dump_rx_dma_buf(p_wmt_port); +#ifdef UART_DEBUG + printk("##########################################\n"); + print_dma_count_cpu_buf_pos(p_wmt_port); +#endif + printk("#############debug dma tx failed begin#############\n"); + printk("sport->port.icount.tx:0x%x,sport->uart_tx_count:0x%x\n",p_wmt_port->port.icount.tx, + p_wmt_port->uart_tx_count); + printk("dma_tx_cnt:0x%x\n",p_wmt_port->dma_tx_cnt); + printk("uart_tx_dma_flag:0x%x\n",p_wmt_port->uart_tx_dma_flag); + printk("uart_tx_stopped:%d\n",uart_tx_stopped(&p_wmt_port->port)); + if(uart_tx_stopped(&p_wmt_port->port)) + { + printk("stopped:%d\n",(&p_wmt_port->port)->state->port.tty->stopped); + printk("hw_stopped:%d\n",(&p_wmt_port->port)->state->port.tty->hw_stopped); + } + printk("#############debug dma tx failed end#############\n"); + +} +EXPORT_SYMBOL(dump_uart_info); +#endif +static int wmt_serial_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res = pdev->resource; + int i; + + for (i = 0; i < pdev->num_resources; i++, res++) + if (res->flags & IORESOURCE_MEM) + break; + + if (i < pdev->num_resources) { + for (i = 0; i < NR_PORTS; i++) { + if (wmt_ports[i].port.mapbase != res->start) + continue; + + wmt_ports[i].port.dev = dev; + uart_add_one_port(&wmt_reg, &wmt_ports[i].port); + dev_set_drvdata(dev, &wmt_ports[i]); + if (i >= 1) { + wmt_ports[i].uart_rx_dma_buf0_org = + dma_alloc_coherent(NULL, + UART_BUFFER_SIZE, + &wmt_ports[i].uart_rx_dma_phy0_org, + GFP_KERNEL); + wmt_ports[i].uart_rx_dma_buf1_org = + dma_alloc_coherent(NULL, + UART_BUFFER_SIZE, + &wmt_ports[i].uart_rx_dma_phy1_org, + GFP_KERNEL); + wmt_ports[i].uart_rx_dma_buf2_org = + dma_alloc_coherent(NULL, + UART_BUFFER_SIZE, + &wmt_ports[i].uart_rx_dma_phy2_org, + GFP_KERNEL); + wmt_ports[i].uart_tx_dma_buf0_org = + dma_alloc_coherent(NULL, + UART_BUFFER_SIZE, + &wmt_ports[i].uart_tx_dma_phy0_org, + GFP_KERNEL); + } + break; + } + } + return 0; +} + +static int wmt_serial_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wmt_port *sport = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (sport) + uart_remove_one_port(&wmt_reg, &sport->port); + + return 0; +} + +static struct platform_driver wmt_serial_driver = { + .driver.name = "uart", + .probe = wmt_serial_probe, + .remove = wmt_serial_remove, + .suspend = wmt_serial_suspend, + .resume = wmt_serial_resume, +}; + +static int __init wmt_serial_init(void) +{ + int ret; + + wmt_init_ports(); + ret = uart_register_driver(&wmt_reg); + + if (ret == 0) { + ret = platform_driver_register(&wmt_serial_driver); + if (ret) + uart_unregister_driver(&wmt_reg); + } +#ifndef CONFIG_SKIP_DRIVER_MSG + printk(KERN_INFO "WMT Serial driver initialized: %s\n", + (ret == 0) ? "ok" : "failed"); +#endif + return ret; +} + +static void __exit wmt_serial_exit(void) +{ + platform_driver_unregister(&wmt_serial_driver); + uart_unregister_driver(&wmt_reg); +} + +module_init(wmt_serial_init); +module_exit(wmt_serial_exit); + +MODULE_AUTHOR("WonderMedia Technologies, Inc."); +MODULE_DESCRIPTION("WMT [generic serial port] driver"); +MODULE_LICENSE("GPL"); diff --git a/ANDROID_3.4.5/drivers/tty/vt/keyboard.c b/ANDROID_3.4.5/drivers/tty/vt/keyboard.c index 3b0c4e32..518fc637 100644 --- a/ANDROID_3.4.5/drivers/tty/vt/keyboard.c +++ b/ANDROID_3.4.5/drivers/tty/vt/keyboard.c @@ -1032,6 +1032,8 @@ static inline unsigned char getleds(void) static int kbd_update_leds_helper(struct input_handle *handle, void *data) { +// yumzhu mask for EV_LED leading to suspend failed on platform 8880 +#if 0 unsigned char leds = *(unsigned char *)data; if (test_bit(EV_LED, handle->dev->evbit)) { @@ -1040,7 +1042,7 @@ static int kbd_update_leds_helper(struct input_handle *handle, void *data) input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); input_inject_event(handle, EV_SYN, SYN_REPORT, 0); } - +#endif return 0; } diff --git a/ANDROID_3.4.5/drivers/tty/wmt_3g.c b/ANDROID_3.4.5/drivers/tty/wmt_3g.c new file mode 100755 index 00000000..e0df5b9b --- /dev/null +++ b/ANDROID_3.4.5/drivers/tty/wmt_3g.c @@ -0,0 +1,3655 @@ +/* + * RocketPort device driver for Linux + * + * Written by Theodore Ts'o, 1995, 1996, 1997, 1998, 1999, 2000. + * + * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2003 by Comtrol, Inc. + * + * 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. + */ + +/* + * Kernel Synchronization: + * + * This driver has 2 kernel control paths - exception handlers (calls into the driver + * from user mode) and the timer bottom half (tasklet). This is a polled driver, interrupts + * are not used. + * + * Critical data: + * - wmt_3g_table[], accessed through passed "info" pointers, is a global (static) array of + * serial port state information and the xmit_buf circular buffer. Protected by + * a per port spinlock. + * - xmit_flags[], an array of ints indexed by line (port) number, indicating that there + * is data to be transmitted. Protected by atomic bit operations. + * - wmt_3g_num_ports, int indicating number of open ports, protected by atomic operations. + * + * wmt_3g_write() and wmt_3g_write_char() functions use a per port semaphore to protect against + * simultaneous access to the same port by more than one process. + */ + +/****** Defines ******/ +#define ROCKET_PARANOIA_CHECK +#define ROCKET_DISABLE_SIMUSAGE + +#if 1 +#undef ROCKET_SOFT_FLOW +#undef ROCKET_DEBUG_OPEN +#undef ROCKET_DEBUG_INTR +#undef ROCKET_DEBUG_WRITE +#undef ROCKET_DEBUG_FLOW +#undef ROCKET_DEBUG_THROTTLE +#undef ROCKET_DEBUG_WAIT_UNTIL_SENT +#undef ROCKET_DEBUG_RECEIVE +#undef ROCKET_DEBUG_HANGUP +#undef REV_PCI_ORDER +#undef ROCKET_DEBUG_IO +#else +#define ROCKET_SOFT_FLOW 1 +#define ROCKET_DEBUG_OPEN 1 +#define ROCKET_DEBUG_INTR 1 +#define ROCKET_DEBUG_WRITE 1 +#define ROCKET_DEBUG_FLOW 1 +#undef ROCKET_DEBUG_THROTTLE +#define ROCKET_DEBUG_WAIT_UNTIL_SENT 1 +#define ROCKET_DEBUG_RECEIVE 1 +#define ROCKET_DEBUG_HANGUP 1 +#define REV_PCI_ORDER 1 +#define ROCKET_DEBUG_IO 1 +#endif + +#define POLL_PERIOD HZ/100 /* Polling period .01 seconds (10ms) */ + +/****** Kernel includes ******/ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/mutex.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/completion.h> +#include <linux/wait.h> +#include <linux/pci.h> +#include <linux/uaccess.h> +#include <asm/atomic.h> +#include <asm/unaligned.h> +#include <linux/bitops.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kthread.h> +/****** RocketPort includes ******/ + +#include "rocket_int.h" +#include "rocket.h" + +#define ROCKET_EXIST 0 + +#define ROCKET_VERSION "2.09" +#define ROCKET_DATE "12-June-2003" + +/****** RocketPort Local Variables ******/ + +#if 1 +void WMTDBG(char *fmt, ...) { + +} +#else +#define WMTDBG //printk +#endif +#define WMT_3G_MAX (1024*4) + +typedef struct wmt_3g_fifo_s{ + unsigned char buf[WMT_3G_MAX]; + int readp; + int writep; + struct mutex mtx; +} WMT_3G_FIFO_T; + +WMT_3G_FIFO_T wmt_3g_usb_fifo; + +int wmt_3g_buf_init(WMT_3G_FIFO_T * fifo) +{ + mutex_init(&fifo->mtx); + fifo->readp = fifo->writep = 0; + memset(fifo->buf,0,WMT_3G_MAX); + return 0; + +} + +int wmt_3g_write_bufsize(WMT_3G_FIFO_T * fifo) +{ + if(fifo->writep<fifo->readp){ + return fifo->readp - fifo->writep-1; + } + if(fifo->writep>fifo->readp){ + return WMT_3G_MAX - (fifo->writep- fifo->readp); + } + if(fifo->writep==fifo->readp){ + //printk("\n\n\nwmt_3g_usb_bufsize error\n\n"); + return WMT_3G_MAX - (fifo->writep- fifo->readp); + } + return 0; +} + + +int wmt_3g_read_bufsize(WMT_3G_FIFO_T * fifo) +{ + if(fifo->readp<fifo->writep){ + return fifo->writep- fifo->readp-1; + } + if(fifo->readp>fifo->writep){ + return WMT_3G_MAX - (fifo->readp- fifo->writep); + } + if(fifo->readp==fifo->writep){ + //printk("\n\n\nwmt_3g_usb_bufsize error\n\n"); + return 0; + } + return 0; +} + +int wmt_3g_buf_read(WMT_3G_FIFO_T * fifo,unsigned char * buf,int size){ + int count; + mutex_lock(&fifo->mtx); + count = min(size,wmt_3g_read_bufsize(fifo)); + if(WMT_3G_MAX-1 - fifo->readp >count){ + memcpy(buf,fifo->buf+fifo->readp,count); + fifo->readp +=count; + }else{ + int tmp_count1,tmp_count2; + + tmp_count1 = WMT_3G_MAX-1 - fifo->readp; + memcpy(buf,fifo->buf+fifo->readp,tmp_count1); + fifo->readp = 0; + + tmp_count2 = count - tmp_count1; + memcpy(buf+tmp_count1,fifo->buf+fifo->readp,tmp_count2); + fifo->readp +=tmp_count2; + + } + + mutex_unlock(&fifo->mtx); + return count; +} + + +int wmt_3g_buf_write(WMT_3G_FIFO_T * fifo,unsigned char * buf,int size){ + int count; + mutex_lock(&fifo->mtx); + count = min(size,wmt_3g_write_bufsize(fifo)); + if(WMT_3G_MAX-1 - fifo->writep>count){ + memcpy(fifo->buf+fifo->writep,buf,count); + fifo->writep +=count; + }else{ + int tmp_count1,tmp_count2; + + tmp_count1 = WMT_3G_MAX-1 - fifo->writep; + memcpy(fifo->buf+fifo->writep,buf,tmp_count1); + fifo->writep = 0; + + tmp_count2 = count - tmp_count1; + memcpy(fifo->buf+fifo->writep,buf+tmp_count1,tmp_count2); + fifo->writep +=tmp_count2; + + } + + mutex_unlock(&fifo->mtx); + printk("wmt_3g_buf_write %d / %d\n",count,size); + return count; +} + + + + + + + +static void wmt_3g_do_poll(unsigned long dummy); + +static struct tty_driver *rocket_driver; + +static struct rocket_version driver_version = { + ROCKET_VERSION, ROCKET_DATE +}; + +static struct r_port *wmt_3g_table[MAX_RP_PORTS]; /* The main repository of serial port state information. */ +static unsigned int xmit_flags[NUM_BOARDS]; /* Bit significant, indicates port had data to transmit. */ + /* eg. Bit 0 indicates port 0 has xmit data, ... */ +static atomic_t wmt_3g_num_ports_open; /* Number of serial ports open */ +static DEFINE_TIMER(rocket_timer, wmt_3g_do_poll, 0, 0); + +static unsigned long board1; /* ISA addresses, retrieved from rocketport.conf */ +static unsigned long board2; +static unsigned long board3; +static unsigned long board4; +static unsigned long controller; +//static int support_low_speed; +static unsigned long modem1; +static unsigned long modem2; +static unsigned long modem3; +static unsigned long modem4; +static unsigned long pc104_1[8]; +static unsigned long pc104_2[8]; +static unsigned long pc104_3[8]; +static unsigned long pc104_4[8]; +//static unsigned long *pc104[4] = { pc104_1, pc104_2, pc104_3, pc104_4 }; + +static int wmt_3g_baud_base[NUM_BOARDS]; /* Board config info (Someday make a per-board structure) */ +static unsigned long rcktpt_io_addr[NUM_BOARDS]; +static int rcktpt_type[NUM_BOARDS]; +static int is_PCI[NUM_BOARDS]; +static rocketModel_t rocketModel[NUM_BOARDS]; +static int max_board; +static const struct tty_port_operations rocket_port_ops; + +/* + * The following arrays define the interrupt bits corresponding to each AIOP. + * These bits are different between the ISA and regular PCI boards and the + * Universal PCI boards. + */ +#if 0 + +static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { + AIOP_INTR_BIT_0, + AIOP_INTR_BIT_1, + AIOP_INTR_BIT_2, + AIOP_INTR_BIT_3 +}; + +static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { + UPCI_AIOP_INTR_BIT_0, + UPCI_AIOP_INTR_BIT_1, + UPCI_AIOP_INTR_BIT_2, + UPCI_AIOP_INTR_BIT_3 +}; + +static Byte_t RData[RDATASIZE] = { + 0x00, 0x09, 0xf6, 0x82, + 0x02, 0x09, 0x86, 0xfb, + 0x04, 0x09, 0x00, 0x0a, + 0x06, 0x09, 0x01, 0x0a, + 0x08, 0x09, 0x8a, 0x13, + 0x0a, 0x09, 0xc5, 0x11, + 0x0c, 0x09, 0x86, 0x85, + 0x0e, 0x09, 0x20, 0x0a, + 0x10, 0x09, 0x21, 0x0a, + 0x12, 0x09, 0x41, 0xff, + 0x14, 0x09, 0x82, 0x00, + 0x16, 0x09, 0x82, 0x7b, + 0x18, 0x09, 0x8a, 0x7d, + 0x1a, 0x09, 0x88, 0x81, + 0x1c, 0x09, 0x86, 0x7a, + 0x1e, 0x09, 0x84, 0x81, + 0x20, 0x09, 0x82, 0x7c, + 0x22, 0x09, 0x0a, 0x0a +}; +static Byte_t RRegData[RREGDATASIZE] = { + 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ + 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ + 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ + 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ + 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ + 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ + 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ + 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ + 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ + 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ + 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ + 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ + 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ +}; +#endif +static CONTROLLER_T sController[CTL_SIZE] = { + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}}, + {-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, + {0, 0, 0, 0}, {-1, -1, -1, -1}, {0, 0, 0, 0}} +}; + +static Byte_t sBitMapClrTbl[8] = { + 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f +}; + +static Byte_t sBitMapSetTbl[8] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; + +//static int sClockPrescale = 0x14; + +/* + * Line number is the ttySIx number (x), the Minor number. We + * assign them sequentially, starting at zero. The following + * array keeps track of the line number assigned to a given board/aiop/channel. + */ +static unsigned char lineNumbers[MAX_RP_PORTS]; +//static unsigned long nextLineNumber; + +/***** RocketPort Static Prototypes *********/ +//static int __init init_ISA(int i); +static void wmt_3g_wait_until_sent(struct tty_struct *tty, int timeout); +static void wmt_3g_flush_buffer(struct tty_struct *tty); +static void wmt_3g_start(struct tty_struct *tty); + +static unsigned char GetLineNumber(int ctrl, int aiop, int ch); +#if 0 +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model); + +static unsigned char SetLineNumber(int ctrl, int aiop, int ch); + +static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, + int ChanNum); +#endif +static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode); +#if 0 +static void sFlushRxFIFO(CHANNEL_T * ChP); +static void sFlushTxFIFO(CHANNEL_T * ChP); +static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags); +#endif +static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); +static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); +static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); +static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); +#if 0 +static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, + ByteIO_t * AiopIOList, int AiopIOListSize, + WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, + int PeriodicOnly, int altChanRingIndicator, + int UPCIRingInd); + +static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, + ByteIO_t * AiopIOList, int AiopIOListSize, + int IRQNum, Byte_t Frequency, int PeriodicOnly); +static int sReadAiopID(ByteIO_t io); +static int sReadAiopNumChan(WordIO_t io); +#endif + +MODULE_AUTHOR("Theodore Ts'o"); +MODULE_DESCRIPTION("Comtrol RocketPort driver"); +#if 0 +module_param(board1, ulong, 0); +MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1"); +module_param(board2, ulong, 0); +MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2"); +module_param(board3, ulong, 0); +MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3"); +module_param(board4, ulong, 0); +MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4"); +module_param(controller, ulong, 0); +MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller"); +module_param(support_low_speed, bool, 0); +MODULE_PARM_DESC(support_low_speed, "1 means support 50 baud, 0 means support 460400 baud"); +module_param(modem1, ulong, 0); +MODULE_PARM_DESC(modem1, "1 means (ISA) board #1 is a RocketModem"); +module_param(modem2, ulong, 0); +MODULE_PARM_DESC(modem2, "1 means (ISA) board #2 is a RocketModem"); +module_param(modem3, ulong, 0); +MODULE_PARM_DESC(modem3, "1 means (ISA) board #3 is a RocketModem"); +module_param(modem4, ulong, 0); +MODULE_PARM_DESC(modem4, "1 means (ISA) board #4 is a RocketModem"); +module_param_array(pc104_1, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_1, "set interface types for ISA(PC104) board #1 (e.g. pc104_1=232,232,485,485,..."); +module_param_array(pc104_2, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_2, "set interface types for ISA(PC104) board #2 (e.g. pc104_2=232,232,485,485,..."); +module_param_array(pc104_3, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_3, "set interface types for ISA(PC104) board #3 (e.g. pc104_3=232,232,485,485,..."); +module_param_array(pc104_4, ulong, NULL, 0); +MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,..."); +#endif +static int wmt_3g_init(void); +static void wmt_3g_cleanup_module(void); + +module_init(wmt_3g_init); +module_exit(wmt_3g_cleanup_module); + + +MODULE_LICENSE("Dual BSD/GPL"); + +/*************************************************************************/ +/* Module code starts here */ + +static inline int rocket_paranoia_check(struct r_port *info, + const char *routine) +{ +#ifdef ROCKET_PARANOIA_CHECK + if (!info) + return 1; + if (info->magic != RPORT_MAGIC) { + printk(KERN_WARNING "Warning: bad magic number for rocketport " + "struct in %s\n", routine); + return 1; + } +#endif + return 0; +} + + +/* Serial port receive data function. Called (from timer poll) when an AIOPIC signals + * that receive data is present on a serial port. Pulls data from FIFO, moves it into the + * tty layer. + */ +static void wmt_3g_do_receive(struct r_port *info, + struct tty_struct *tty, + CHANNEL_t * cp, unsigned int ChanStatus) +{ +#if ROCKET_EXIST + + unsigned int CharNStat; + int ToRecv, wRecv, space; + unsigned char *cbuf; + + ToRecv = sGetRxCnt(cp); +#ifdef ROCKET_DEBUG_INTR + printk(KERN_INFO "wmt_3g_do_receive(%d)...\n", ToRecv); +#endif + if (ToRecv == 0) + return; + + /* + * if status indicates there are errored characters in the + * FIFO, then enter status mode (a word in FIFO holds + * character and status). + */ + if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { + if (!(ChanStatus & STATMODE)) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "Entering STATMODE...\n"); +#endif + ChanStatus |= STATMODE; + sEnRxStatusMode(cp); + } + } + + /* + * if we previously entered status mode, then read down the + * FIFO one word at a time, pulling apart the character and + * the status. Update error counters depending on status + */ + if (ChanStatus & STATMODE) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "Ignore %x, read %x...\n", + info->ignore_status_mask, info->read_status_mask); +#endif + while (ToRecv) { + char flag; + + CharNStat = sInW(sGetTxRxDataIO(cp)); +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "%x...\n", CharNStat); +#endif + if (CharNStat & STMBREAKH) + CharNStat &= ~(STMFRAMEH | STMPARITYH); + if (CharNStat & info->ignore_status_mask) { + ToRecv--; + continue; + } + CharNStat &= info->read_status_mask; + if (CharNStat & STMBREAKH) + flag = TTY_BREAK; + else if (CharNStat & STMPARITYH) + flag = TTY_PARITY; + else if (CharNStat & STMFRAMEH) + flag = TTY_FRAME; + else if (CharNStat & STMRCVROVRH) + flag = TTY_OVERRUN; + else + flag = TTY_NORMAL; + tty_insert_flip_char(tty, CharNStat & 0xff, flag); + ToRecv--; + } + + /* + * after we've emptied the FIFO in status mode, turn + * status mode back off + */ + if (sGetRxCnt(cp) == 0) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "Status mode off.\n"); +#endif + sDisRxStatusMode(cp); + } + } else { + /* + * we aren't in status mode, so read down the FIFO two + * characters at time by doing repeated word IO + * transfer. + */ + space = tty_prepare_flip_string(tty, &cbuf, ToRecv); + if (space < ToRecv) { +#ifdef ROCKET_DEBUG_RECEIVE + printk(KERN_INFO "wmt_3g_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space); +#endif + if (space <= 0) + return; + ToRecv = space; + } + wRecv = ToRecv >> 1; + if (wRecv) + sInStrW(sGetTxRxDataIO(cp), (unsigned short *) cbuf, wRecv); + if (ToRecv & 1) + cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp)); + } + /* Push the data up to the tty layer */ + tty_flip_buffer_push(tty); +#endif +} + +/* + * Serial port transmit data function. Called from the timer polling loop as a + * result of a bit set in xmit_flags[], indicating data (from the tty layer) is ready + * to be sent out the serial port. Data is buffered in wmt_3g_table[line].xmit_buf, it is + * moved to the port's xmit FIFO. *info is critical data, protected by spinlocks. + */ +static void wmt_3g_do_transmit(struct r_port *info) +{ +#if ROCKET_EXIST + + int c; + CHANNEL_t *cp = &info->channel; + struct tty_struct *tty; + unsigned long flags; + +#ifdef ROCKET_DEBUG_INTR + printk(KERN_DEBUG "%s\n", __func__); +#endif + if (!info) + return; + tty = tty_port_tty_get(&info->port); + + if (tty == NULL) { + printk(KERN_WARNING "rp: WARNING %s called with tty==NULL\n", __func__); + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + return; + } + + spin_lock_irqsave(&info->slock, flags); + info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); + + /* Loop sending data to FIFO until done or FIFO full */ + while (1) { + if (tty->stopped || tty->hw_stopped) + break; + c = min(info->xmit_fifo_room, info->xmit_cnt); + c = min(c, XMIT_BUF_SIZE - info->xmit_tail); + if (c <= 0 || info->xmit_fifo_room <= 0) + break; + sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) (info->xmit_buf + info->xmit_tail), c / 2); + if (c & 1) + sOutB(sGetTxRxDataIO(cp), info->xmit_buf[info->xmit_tail + c - 1]); + info->xmit_tail += c; + info->xmit_tail &= XMIT_BUF_SIZE - 1; + info->xmit_cnt -= c; + info->xmit_fifo_room -= c; +#ifdef ROCKET_DEBUG_INTR + printk(KERN_INFO "tx %d chars...\n", c); +#endif + } + + if (info->xmit_cnt == 0) + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + + if (info->xmit_cnt < WAKEUP_CHARS) { + tty_wakeup(tty); +#ifdef ROCKETPORT_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } + + spin_unlock_irqrestore(&info->slock, flags); + tty_kref_put(tty); + +#ifdef ROCKET_DEBUG_INTR + printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head, + info->xmit_tail, info->xmit_fifo_room); +#endif +#endif +} + +/* + * Called when a serial port signals it has read data in it's RX FIFO. + * It checks what interrupts are pending and services them, including + * receiving serial data. + */ +static void wmt_3g_handle_port(struct r_port *info) +{ + CHANNEL_t *cp; + struct tty_struct *tty; + unsigned int IntMask, ChanStatus; + + if (!info) + return; + + if ((info->port.flags & ASYNC_INITIALIZED) == 0) { + printk(KERN_WARNING "rp: WARNING: wmt_3g_handle_port called with " + "info->flags & NOT_INIT\n"); + return; + } + tty = tty_port_tty_get(&info->port); + if (!tty) { + printk(KERN_WARNING "rp: WARNING: wmt_3g_handle_port called with " + "tty==NULL\n"); + return; + } + cp = &info->channel; + + IntMask = sGetChanIntID(cp) & info->intmask; +#ifdef ROCKET_DEBUG_INTR + printk(KERN_INFO "wmt_3g_interrupt %02x...\n", IntMask); +#endif + ChanStatus = sGetChanStatus(cp); + if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ + wmt_3g_do_receive(info, tty, cp, ChanStatus); + } + if (IntMask & DELTA_CD) { /* CD change */ +#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) + printk(KERN_INFO "ttyR%d CD now %s...\n", info->line, + (ChanStatus & CD_ACT) ? "on" : "off"); +#endif + if (!(ChanStatus & CD_ACT) && info->cd_status) { +#ifdef ROCKET_DEBUG_HANGUP + printk(KERN_INFO "CD drop, calling hangup.\n"); +#endif + tty_hangup(tty); + } + info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; + wake_up_interruptible(&info->port.open_wait); + } +#ifdef ROCKET_DEBUG_INTR + if (IntMask & DELTA_CTS) { /* CTS change */ + printk(KERN_INFO "CTS change...\n"); + } + if (IntMask & DELTA_DSR) { /* DSR change */ + printk(KERN_INFO "DSR change...\n"); + } +#endif + tty_kref_put(tty); +} + +/* + * The top level polling routine. Repeats every 1/100 HZ (10ms). + */ +static void wmt_3g_do_poll(unsigned long dummy) +{ + CONTROLLER_t *ctlp; + int ctrl, aiop, ch, line; + unsigned int xmitmask, i; + unsigned int CtlMask; + unsigned char AiopMask; + Word_t bit; + + /* Walk through all the boards (ctrl's) */ + for (ctrl = 0; ctrl < max_board; ctrl++) { + if (rcktpt_io_addr[ctrl] <= 0) + continue; + + /* Get a ptr to the board's control struct */ + ctlp = sCtlNumToCtlPtr(ctrl); + + /* Get the interrupt status from the board */ +#ifdef CONFIG_PCI + if (ctlp->BusType == isPCI) + CtlMask = sPCIGetControllerIntStatus(ctlp); + else +#endif + CtlMask = sGetControllerIntStatus(ctlp); + + /* Check if any AIOP read bits are set */ + for (aiop = 0; CtlMask; aiop++) { + bit = ctlp->AiopIntrBits[aiop]; + if (CtlMask & bit) { + CtlMask &= ~bit; + AiopMask = sGetAiopIntStatus(ctlp, aiop); + + /* Check if any port read bits are set */ + for (ch = 0; AiopMask; AiopMask >>= 1, ch++) { + if (AiopMask & 1) { + + /* Get the line number (/dev/ttyRx number). */ + /* Read the data from the port. */ + line = GetLineNumber(ctrl, aiop, ch); + wmt_3g_handle_port(wmt_3g_table[line]); + } + } + } + } + + xmitmask = xmit_flags[ctrl]; + + /* + * xmit_flags contains bit-significant flags, indicating there is data + * to xmit on the port. Bit 0 is port 0 on this board, bit 1 is port + * 1, ... (32 total possible). The variable i has the aiop and ch + * numbers encoded in it (port 0-7 are aiop0, 8-15 are aiop1, etc). + */ + if (xmitmask) { + for (i = 0; i < rocketModel[ctrl].numPorts; i++) { + if (xmitmask & (1 << i)) { + aiop = (i & 0x18) >> 3; + ch = i & 0x07; + line = GetLineNumber(ctrl, aiop, ch); + wmt_3g_do_transmit(wmt_3g_table[line]); + } + } + } + } + + /* + * Reset the timer so we get called at the next clock tick (10ms). + */ + if (atomic_read(&wmt_3g_num_ports_open)) + mod_timer(&rocket_timer, jiffies + POLL_PERIOD); +} + +struct device *wmt_3g_dev0; +struct device *wmt_3g_dev1; + +static int wmt_3g_dev_alloc(struct device *dev,int index) +{ + char name[16]; + int ret = -ENOMEM; + + dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev) + goto out; + + device_initialize(dev); + sprintf(name,"ttyR%d",index); + ret = dev_set_name(dev, "%s", name); + if (ret) + goto free_dev; + + //dev_set_drvdata(dev, pmu); + //dev->bus = &wmt_3g_bus; + //dev->release = wmt_3g_dev_release; + ret = device_add(dev); + if (ret) + goto free_dev; + +out: + return ret; + +free_dev: + put_device(dev); + goto out; +} + + +/* + * Initializes the r_port structure for a port, as well as enabling the port on + * the board. + * Inputs: board, aiop, chan numbers + */ +static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev,int index) +{ +// unsigned rocketMode; + struct r_port *info; + int line=0; + CONTROLLER_T *ctlp=NULL; +#if ROCKET_EXIST + /* Get the next available line number */ + line = SetLineNumber(board, aiop, chan); + + ctlp = sCtlNumToCtlPtr(board); +#endif + /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */ + info = kzalloc(sizeof (struct r_port), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "Couldn't allocate info struct for line #%d\n", + line); + return; + } + + info->magic = RPORT_MAGIC; + info->line = line; + info->ctlp = ctlp; + info->board = board; + info->aiop = aiop; + info->chan = chan; + tty_port_init(&info->port); + info->port.ops = &rocket_port_ops; + + + init_completion(&info->close_wait); + info->flags &= ~ROCKET_MODE_MASK; + +#if ROCKET_EXIST + + switch (pc104[board][line]) { + case 422: + info->flags |= ROCKET_MODE_RS422; + break; + case 485: + info->flags |= ROCKET_MODE_RS485; + break; + case 232: + default: + info->flags |= ROCKET_MODE_RS232; + break; + } + + info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; + if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { + printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", + board, aiop, chan); + kfree(info); + return; + } + + rocketMode = info->flags & ROCKET_MODE_MASK; + + if ((info->flags & ROCKET_RTS_TOGGLE) || (rocketMode == ROCKET_MODE_RS485)) + sEnRTSToggle(&info->channel); + else + sDisRTSToggle(&info->channel); + + if (ctlp->boardType == ROCKET_TYPE_PC104) { + switch (rocketMode) { + case ROCKET_MODE_RS485: + sSetInterfaceMode(&info->channel, InterfaceModeRS485); + break; + case ROCKET_MODE_RS422: + sSetInterfaceMode(&info->channel, InterfaceModeRS422); + break; + case ROCKET_MODE_RS232: + default: + if (info->flags & ROCKET_RTS_TOGGLE) + sSetInterfaceMode(&info->channel, InterfaceModeRS232T); + else + sSetInterfaceMode(&info->channel, InterfaceModeRS232); + break; + } + } +#endif + + + spin_lock_init(&info->slock); + printk("%s %d\n",__FUNCTION__,__LINE__); + mutex_init(&info->write_mtx); + wmt_3g_table[index] = info; + + wmt_3g_dev_alloc(wmt_3g_dev0,index); + + tty_register_device(rocket_driver, index, wmt_3g_dev0); + +} + +/* + * Configures a rocketport port according to its termio settings. Called from + * user mode into the driver (exception handler). *info CD manipulation is spinlock protected. + */ +static void configure_r_port(struct tty_struct *tty, struct r_port *info, + struct ktermios *old_termios) +{ + unsigned cflag; + unsigned long flags; + unsigned rocketMode; + int bits, baud, divisor; + CHANNEL_t *cp; + struct ktermios *t = tty->termios; + + cp = &info->channel; + cflag = t->c_cflag; + + /* Byte size and parity */ + if ((cflag & CSIZE) == CS8) { + sSetData8(cp); + bits = 10; + } else { + sSetData7(cp); + bits = 9; + } + if (cflag & CSTOPB) { + sSetStop2(cp); + bits++; + } else { + sSetStop1(cp); + } + + if (cflag & PARENB) { + sEnParity(cp); + bits++; + if (cflag & PARODD) { + sSetOddParity(cp); + } else { + sSetEvenParity(cp); + } + } else { + sDisParity(cp); + } + + /* baud rate */ + baud = tty_get_baud_rate(tty); + if (!baud) + baud = 9600; + divisor = ((wmt_3g_baud_base[info->board] + (baud >> 1)) / baud) - 1; + if ((divisor >= 8192 || divisor < 0) && old_termios) { + baud = tty_termios_baud_rate(old_termios); + if (!baud) + baud = 9600; + divisor = (wmt_3g_baud_base[info->board] / baud) - 1; + } + if (divisor >= 8192 || divisor < 0) { + baud = 9600; + divisor = (wmt_3g_baud_base[info->board] / baud) - 1; + } + info->cps = baud / bits; + sSetBaud(cp, divisor); + + /* FIXME: Should really back compute a baud rate from the divisor */ + tty_encode_baud_rate(tty, baud, baud); + + if (cflag & CRTSCTS) { + info->intmask |= DELTA_CTS; + sEnCTSFlowCtl(cp); + } else { + info->intmask &= ~DELTA_CTS; + sDisCTSFlowCtl(cp); + } + if (cflag & CLOCAL) { + info->intmask &= ~DELTA_CD; + } else { + spin_lock_irqsave(&info->slock, flags); + if (sGetChanStatus(cp) & CD_ACT) + info->cd_status = 1; + else + info->cd_status = 0; + info->intmask |= DELTA_CD; + spin_unlock_irqrestore(&info->slock, flags); + } + + /* + * Handle software flow control in the board + */ +#ifdef ROCKET_SOFT_FLOW + if (I_IXON(tty)) { + sEnTxSoftFlowCtl(cp); + if (I_IXANY(tty)) { + sEnIXANY(cp); + } else { + sDisIXANY(cp); + } + sSetTxXONChar(cp, START_CHAR(tty)); + sSetTxXOFFChar(cp, STOP_CHAR(tty)); + } else { + sDisTxSoftFlowCtl(cp); + sDisIXANY(cp); + sClrTxXOFF(cp); + } +#endif + + /* + * Set up ignore/read mask words + */ + info->read_status_mask = STMRCVROVRH | 0xFF; + if (I_INPCK(tty)) + info->read_status_mask |= STMFRAMEH | STMPARITYH; + if (I_BRKINT(tty) || I_PARMRK(tty)) + info->read_status_mask |= STMBREAKH; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(tty)) + info->ignore_status_mask |= STMFRAMEH | STMPARITYH; + if (I_IGNBRK(tty)) { + info->ignore_status_mask |= STMBREAKH; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too. (For real raw support). + */ + if (I_IGNPAR(tty)) + info->ignore_status_mask |= STMRCVROVRH; + } + + rocketMode = info->flags & ROCKET_MODE_MASK; + + if ((info->flags & ROCKET_RTS_TOGGLE) + || (rocketMode == ROCKET_MODE_RS485)) + sEnRTSToggle(cp); + else + sDisRTSToggle(cp); + + sSetRTS(&info->channel); + + if (cp->CtlP->boardType == ROCKET_TYPE_PC104) { + switch (rocketMode) { + case ROCKET_MODE_RS485: + sSetInterfaceMode(cp, InterfaceModeRS485); + break; + case ROCKET_MODE_RS422: + sSetInterfaceMode(cp, InterfaceModeRS422); + break; + case ROCKET_MODE_RS232: + default: + if (info->flags & ROCKET_RTS_TOGGLE) + sSetInterfaceMode(cp, InterfaceModeRS232T); + else + sSetInterfaceMode(cp, InterfaceModeRS232); + break; + } + } +} + +static int carrier_raised(struct tty_port *port) +{ + struct r_port *info = container_of(port, struct r_port, port); + return (sGetChanStatusLo(&info->channel) & CD_ACT) ? 1 : 0; +} + +static void dtr_rts(struct tty_port *port, int on) +{ +#if ROCKET_EXIST + struct r_port *info = container_of(port, struct r_port, port); + if (on) { + sSetDTR(&info->channel); + sSetRTS(&info->channel); + } else { + sClrDTR(&info->channel); + sClrRTS(&info->channel); + } +#endif +} + + + +struct file *wmt_3g_data_port_w=NULL; +struct file *wmt_3g_data_port_r=NULL; + +struct file *wmt_3g_at_port=NULL; + + + +/* + * Exception handler that opens a serial port. Creates xmit_buf storage, fills in + * port's r_port struct. Initializes the port hardware. + */ +static int wmt_3g_open(struct tty_struct *tty, struct file *filp) +{ + struct r_port *info; + struct tty_port *port; + int line = 0, retval; +// CHANNEL_t *cp; + unsigned long page; + + + //tty->icanon = 0; + + //printk("%s %d %x %s\n",__FUNCTION__,__LINE__,tty->index,tty->name); + if((tty->termios->c_lflag&ECHO)!=0){ + printk("xxxxxxxxxxxxxxxxxxxxxECHO detect\n\n\n\n"); + tty->termios->c_lflag &= ~ECHO; + } + +/* + if(tty->index==1){ + struct tty_struct * other_tty; + char flag ; + + if(wmt_3g_table[3] == NULL){ + printk("error %s %d\n",__FUNCTION__,__LINE__); + goto out1; + } + + other_tty = tty_port_tty_get(&(wmt_3g_table[3]->port)); + + if(other_tty ==NULL){ + printk("error %s %d\n",__FUNCTION__,__LINE__); + goto out1; + } + + + flag = TTY_NORMAL; + int j; + char buf[512]; + memset(buf,0x5a,512); + tty_insert_flip_string(other_tty,buf,123); + + tty_flip_buffer_push(other_tty); + + tty_kref_put(other_tty); + } + + */ + line = tty->index; + if (line < 0 || line >= MAX_RP_PORTS || ((info = wmt_3g_table[line]) == NULL)) + return -ENXIO; + + port = &info->port; + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + if (port->flags & ASYNC_CLOSING) { + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + retval = wait_for_completion_interruptible(&info->close_wait); + free_page(page); + if (retval) + return retval; + return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); + } + + /* + * We must not sleep from here until the port is marked fully in use. + */ + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; + + tty->driver_data = info; + tty_port_tty_set(port, tty); + + if (port->count++ == 0) { + atomic_inc(&wmt_3g_num_ports_open); + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rocket mod++ = %d...\n", + atomic_read(&wmt_3g_num_ports_open)); +#endif + } +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "wmt_3g_open ttyR%d, count=%d\n", info->line, info->port.count); +#endif + + +#if ROCKET_EXIST + + /* + * Info->count is now 1; so it's safe to sleep now. + */ + if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { + cp = &info->channel; + sSetRxTrigger(cp, TRIG_1); + if (sGetChanStatus(cp) & CD_ACT) + info->cd_status = 1; + else + info->cd_status = 0; + sDisRxStatusMode(cp); + sFlushRxFIFO(cp); + sFlushTxFIFO(cp); + + sEnInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); + sSetRxTrigger(cp, TRIG_1); + + sGetChanStatus(cp); + sDisRxStatusMode(cp); + sClrTxXOFF(cp); + + sDisCTSFlowCtl(cp); + sDisTxSoftFlowCtl(cp); + + sEnRxFIFO(cp); + sEnTransmit(cp); + + set_bit(ASYNCB_INITIALIZED, &info->port.flags); + + /* + * Set up the tty->alt_speed kludge + */ + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) + tty->alt_speed = 57600; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) + tty->alt_speed = 115200; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) + tty->alt_speed = 230400; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) + tty->alt_speed = 460800; + + configure_r_port(tty, info, NULL); + if (tty->termios->c_cflag & CBAUD) { + sSetDTR(cp); + sSetRTS(cp); + } + } + + + /* Starts (or resets) the maint polling loop */ + mod_timer(&rocket_timer, jiffies + POLL_PERIOD); + + retval = tty_port_block_til_ready(port, tty, filp); + if (retval) { + printk("%s %d\n",__FUNCTION__,__LINE__); + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "wmt_3g_open returning after block_til_ready with %d\n", retval); +#endif + return retval; + } +#endif + + + return 0; +} + +/* + * Exception handler that closes a serial port. info->port.count is considered critical. + */ +static void wmt_3g_close(struct tty_struct *tty, struct file *filp) +{ + struct r_port *info = tty->driver_data; + struct tty_port *port = &info->port; +// int timeout; +// CHANNEL_t *cp; + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + if (rocket_paranoia_check(info, "wmt_3g_close")) + return; + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "wmt_3g_close ttyR%d, count = %d\n", info->line, info->port.count); +#endif + + if (tty_port_close_start(port, tty, filp) == 0) + return; + + mutex_lock(&port->mutex); +#if ROCKET_EXIST + + cp = &info->channel; + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = (sGetTxCnt(cp) + 1) * HZ / info->cps; + if (timeout == 0) + timeout = 1; + wmt_3g_wait_until_sent(tty, timeout); + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + + sDisTransmit(cp); + sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); + sDisCTSFlowCtl(cp); + sDisTxSoftFlowCtl(cp); + sClrTxXOFF(cp); + sFlushRxFIFO(cp); + sFlushTxFIFO(cp); + sClrRTS(cp); + if (C_HUPCL(tty)) + sClrDTR(cp); + +#endif + + + wmt_3g_flush_buffer(tty); + + tty_ldisc_flush(tty); + + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + + /* We can't yet use tty_port_close_end as the buffer handling in this + driver is a bit different to the usual */ + + if (port->blocked_open) { + if (port->close_delay) { + msleep_interruptible(jiffies_to_msecs(port->close_delay)); + } + wake_up_interruptible(&port->open_wait); + } else { + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = NULL; + } + } + + + spin_lock_irq(&port->lock); + info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE); + tty->closing = 0; + + spin_unlock_irq(&port->lock); + + mutex_unlock(&port->mutex); + + + + + + tty_port_tty_set(port, NULL); + + wake_up_interruptible(&port->close_wait); + complete_all(&info->close_wait); + atomic_dec(&wmt_3g_num_ports_open); + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "rocket mod-- = %d...\n", + atomic_read(&wmt_3g_num_ports_open)); + printk(KERN_INFO "wmt_3g_close ttyR%d complete shutdown\n", info->line); +#endif + +} + +static void wmt_3g_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned cflag; +//printk("%s %d\n",__FUNCTION__,__LINE__); + if (rocket_paranoia_check(info, "wmt_3g_set_termios")) + return; + + + cflag = tty->termios->c_cflag; + + /* + * This driver doesn't support CS5 or CS6 + */ + if (((cflag & CSIZE) == CS5) || ((cflag & CSIZE) == CS6)) + tty->termios->c_cflag = + ((cflag & ~CSIZE) | (old_termios->c_cflag & CSIZE)); + /* Or CMSPAR */ + tty->termios->c_cflag &= ~CMSPAR; + + return; + + + configure_r_port(tty, info, old_termios); + + cp = &info->channel; + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { + sClrDTR(cp); + sClrRTS(cp); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { + if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) + sSetRTS(cp); + sSetDTR(cp); + } + + if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + wmt_3g_start(tty); + } +} + +static int wmt_3g_break(struct tty_struct *tty, int break_state) +{ + struct r_port *info = tty->driver_data; + unsigned long flags; +printk("%s %d\n",__FUNCTION__,__LINE__); + if (rocket_paranoia_check(info, "wmt_3g_break")) + return -EINVAL; + + spin_lock_irqsave(&info->slock, flags); + if (break_state == -1) + sSendBreak(&info->channel); + else + sClrBreak(&info->channel); + spin_unlock_irqrestore(&info->slock, flags); + return 0; +} + +/* + * sGetChanRI used to be a macro in rocket_int.h. When the functionality for + * the UPCI boards was added, it was decided to make this a function because + * the macro was getting too complicated. All cases except the first one + * (UPCIRingInd) are taken directly from the original macro. + */ +static int sGetChanRI(CHANNEL_T * ChP) +{ + CONTROLLER_t *CtlP = ChP->CtlP; + int ChanNum = ChP->ChanNum; + int RingInd = 0; + + if (CtlP->UPCIRingInd) + RingInd = !(sInB(CtlP->UPCIRingInd) & sBitMapSetTbl[ChanNum]); + else if (CtlP->AltChanRingIndicator) + RingInd = sInB((ByteIO_t) (ChP->ChanStat + 8)) & DSR_ACT; + else if (CtlP->boardType == ROCKET_TYPE_PC104) + RingInd = !(sInB(CtlP->AiopIO[3]) & sBitMapSetTbl[ChanNum]); + + return RingInd; +} + +/********************************************************************************************/ +/* Here are the routines used by wmt_3g_ioctl. These are all called from exception handlers. */ + +/* + * Returns the state of the serial modem control lines. These next 2 functions + * are the way kernel versions > 2.5 handle modem control lines rather than IOCTLs. + */ +static int wmt_3g_tiocmget(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + unsigned int control, result, ChanStatus; +printk("%s %d\n",__FUNCTION__,__LINE__); + ChanStatus = sGetChanStatusLo(&info->channel); + control = info->channel.TxControl[3]; + result = ((control & SET_RTS) ? TIOCM_RTS : 0) | + ((control & SET_DTR) ? TIOCM_DTR : 0) | + ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | + (sGetChanRI(&info->channel) ? TIOCM_RNG : 0) | + ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | + ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0); + + return result; +} + +/* + * Sets the modem control lines + */ +static int wmt_3g_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ +// struct r_port *info = tty->driver_data; +printk("%s %d\n",__FUNCTION__,__LINE__); + + if (set & TIOCM_RTS) + printk("SET_RTS\n"); + if (set & TIOCM_DTR) + printk("SET_DTR\n"); + if (clear & TIOCM_RTS) + printk("CLR_RTS\n"); + if (clear & TIOCM_DTR) + printk("CLR_DTR\n"); + +#if ROCKET_EXIST + + if (set & TIOCM_RTS) + info->channel.TxControl[3] |= SET_RTS; + if (set & TIOCM_DTR) + info->channel.TxControl[3] |= SET_DTR; + if (clear & TIOCM_RTS) + info->channel.TxControl[3] &= ~SET_RTS; + if (clear & TIOCM_DTR) + info->channel.TxControl[3] &= ~SET_DTR; + + out32(info->channel.IndexAddr, info->channel.TxControl); +#endif + return 0; +} + +static int get_config(struct r_port *info, struct rocket_config __user *retinfo) +{ + struct rocket_config tmp; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof (tmp)); + mutex_lock(&info->port.mutex); + tmp.line = info->line; + tmp.flags = info->flags; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; + tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; + mutex_unlock(&info->port.mutex); + + if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) + return -EFAULT; + return 0; +} + +static int set_config(struct tty_struct *tty, struct r_port *info, + struct rocket_config __user *new_info) +{ + struct rocket_config new_serial; + + if (copy_from_user(&new_serial, new_info, sizeof (new_serial))) + return -EFAULT; + + mutex_lock(&info->port.mutex); + if (!capable(CAP_SYS_ADMIN)) + { + if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) { + mutex_unlock(&info->port.mutex); + return -EPERM; + } + info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK)); + configure_r_port(tty, info, NULL); + mutex_unlock(&info->port.mutex); + return 0; + } + + info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; + + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) + tty->alt_speed = 57600; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) + tty->alt_speed = 115200; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) + tty->alt_speed = 230400; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) + tty->alt_speed = 460800; + mutex_unlock(&info->port.mutex); + + configure_r_port(tty, info, NULL); + return 0; +} + +/* + * This function fills in a rocket_ports struct with information + * about what boards/ports are in the system. This info is passed + * to user space. See setrocket.c where the info is used to create + * the /dev/ttyRx ports. + */ +static int get_ports(struct r_port *info, struct rocket_ports __user *retports) +{ + struct rocket_ports tmp; + int board; + + if (!retports) + return -EFAULT; + memset(&tmp, 0, sizeof (tmp)); + tmp.tty_major = rocket_driver->major; + + for (board = 0; board < 4; board++) { + tmp.rocketModel[board].model = rocketModel[board].model; + strcpy(tmp.rocketModel[board].modelString, rocketModel[board].modelString); + tmp.rocketModel[board].numPorts = rocketModel[board].numPorts; + tmp.rocketModel[board].loadrm2 = rocketModel[board].loadrm2; + tmp.rocketModel[board].startingPortNumber = rocketModel[board].startingPortNumber; + } + if (copy_to_user(retports, &tmp, sizeof (*retports))) + return -EFAULT; + return 0; +} + +static int reset_rm2(struct r_port *info, void __user *arg) +{ + int reset; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(&reset, arg, sizeof (int))) + return -EFAULT; + if (reset) + reset = 1; + + if (rcktpt_type[info->board] != ROCKET_TYPE_MODEMII && + rcktpt_type[info->board] != ROCKET_TYPE_MODEMIII) + return -EINVAL; + + if (info->ctlp->BusType == isISA) + sModemReset(info->ctlp, info->chan, reset); + else + sPCIModemReset(info->ctlp, info->chan, reset); + + return 0; +} + +static int get_version(struct r_port *info, struct rocket_version __user *retvers) +{ + if (copy_to_user(retvers, &driver_version, sizeof (*retvers))) + return -EFAULT; + return 0; +} + +/* IOCTL call handler into the driver */ +static int wmt_3g_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct r_port *info = tty->driver_data; + void __user *argp = (void __user *)arg; + int ret = 0; + //printk("%s %d cmd %x\n",__FUNCTION__,__LINE__,cmd); + if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "wmt_3g_ioctl")){ + printk("%s %d\n",__FUNCTION__,__LINE__); + return -ENXIO; + } + switch (cmd) { + case RCKP_GET_STRUCT: + if (copy_to_user(argp, info, sizeof (struct r_port))) + ret = -EFAULT; + break; + case RCKP_GET_CONFIG: + ret = get_config(info, argp); + break; + case RCKP_SET_CONFIG: + ret = set_config(tty, info, argp); + break; + case RCKP_GET_PORTS: + ret = get_ports(info, argp); + break; + case RCKP_RESET_RM2: + ret = reset_rm2(info, argp); + break; + case RCKP_GET_VERSION: + ret = get_version(info, argp); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void wmt_3g_send_xchar(struct tty_struct *tty, char ch) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; +printk("%s %d\n",__FUNCTION__,__LINE__); + if (rocket_paranoia_check(info, "wmt_3g_send_xchar")) + return; + + cp = &info->channel; + if (sGetTxCnt(cp)) + sWriteTxPrioByte(cp, ch); + else + sWriteTxByte(sGetTxRxDataIO(cp), ch); +} + +static void wmt_3g_throttle(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + +#ifdef ROCKET_DEBUG_THROTTLE + printk(KERN_INFO "throttle %s: %d....\n", tty->name, + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (rocket_paranoia_check(info, "wmt_3g_throttle")) + return; + + return; + + if (I_IXOFF(tty)) + wmt_3g_send_xchar(tty, STOP_CHAR(tty)); + + sClrRTS(&info->channel); +} + +static void wmt_3g_unthrottle(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + +#ifdef ROCKET_DEBUG_THROTTLE + printk(KERN_INFO "unthrottle %s: %d....\n", tty->name, + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (rocket_paranoia_check(info, "wmt_3g_throttle")) + return; + + return; + + if (I_IXOFF(tty)) + wmt_3g_send_xchar(tty, START_CHAR(tty)); + + sSetRTS(&info->channel); +} + +/* + * ------------------------------------------------------------ + * wmt_3g_stop() and wmt_3g_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void wmt_3g_stop(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; +printk("%s %d\n",__FUNCTION__,__LINE__); +#ifdef ROCKET_DEBUG_FLOW + printk(KERN_INFO "stop %s: %d %d....\n", tty->name, + info->xmit_cnt, info->xmit_fifo_room); +#endif + + if (rocket_paranoia_check(info, "wmt_3g_stop")) + return; + + if (sGetTxCnt(&info->channel)) + sDisTransmit(&info->channel); +} + +static void wmt_3g_start(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; +printk("%s %d\n",__FUNCTION__,__LINE__); +#ifdef ROCKET_DEBUG_FLOW + printk(KERN_INFO "start %s: %d %d....\n", tty->name, + info->xmit_cnt, info->xmit_fifo_room); +#endif + + if (rocket_paranoia_check(info, "wmt_3g_stop")) + return; + + sEnTransmit(&info->channel); + set_bit((info->aiop * 8) + info->chan, + (void *) &xmit_flags[info->board]); +} + +/* + * wmt_3g_wait_until_sent() --- wait until the transmitter is empty + */ +static void wmt_3g_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned long orig_jiffies; + int check_time, exit_time; + int txcnt; +printk("%s %d\n",__FUNCTION__,__LINE__); + if (rocket_paranoia_check(info, "wmt_3g_wait_until_sent")) + return; + schedule_timeout(msecs_to_jiffies(1000)); + return; + + cp = &info->channel; + + orig_jiffies = jiffies; +#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT + printk(KERN_INFO "In wmt_3g_wait_until_sent(%d) (jiff=%lu)...\n", timeout, + jiffies); + printk(KERN_INFO "cps=%d...\n", info->cps); +#endif + while (1) { + txcnt = sGetTxCnt(cp); + if (!txcnt) { + if (sGetChanStatusLo(cp) & TXSHRMT) + break; + check_time = (HZ / info->cps) / 5; + } else { + check_time = HZ * txcnt / info->cps; + } + if (timeout) { + exit_time = orig_jiffies + timeout - jiffies; + if (exit_time <= 0) + break; + if (exit_time < check_time) + check_time = exit_time; + } + if (check_time == 0) + check_time = 1; +#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT + printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt, + jiffies, check_time); +#endif + msleep_interruptible(jiffies_to_msecs(check_time)); + if (signal_pending(current)) + break; + } + __set_current_state(TASK_RUNNING); +#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT + printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies); +#endif +} + +/* + * wmt_3g_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void wmt_3g_hangup(struct tty_struct *tty) +{ + CHANNEL_t *cp; + struct r_port *info = tty->driver_data; + unsigned long flags; +printk("%s %d\n",__FUNCTION__,__LINE__); + if (rocket_paranoia_check(info, "wmt_3g_hangup")) + return; + +#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP)) + printk(KERN_INFO "wmt_3g_hangup of ttyR%d...\n", info->line); +#endif + wmt_3g_flush_buffer(tty); + spin_lock_irqsave(&info->port.lock, flags); + if (info->port.flags & ASYNC_CLOSING) { + spin_unlock_irqrestore(&info->port.lock, flags); + return; + } + if (info->port.count) + atomic_dec(&wmt_3g_num_ports_open); + clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + spin_unlock_irqrestore(&info->port.lock, flags); + + tty_port_hangup(&info->port); + + cp = &info->channel; + sDisRxFIFO(cp); + sDisTransmit(cp); + sDisInterrupts(cp, (TXINT_EN | MCINT_EN | RXINT_EN | SRCINT_EN | CHANINT_EN)); + sDisCTSFlowCtl(cp); + sDisTxSoftFlowCtl(cp); + sClrTxXOFF(cp); + clear_bit(ASYNCB_INITIALIZED, &info->port.flags); + + wake_up_interruptible(&info->port.open_wait); +} + +/* + * Exception handler - write char routine. The RocketPort driver uses a + * double-buffering strategy, with the twist that if the in-memory CPU + * buffer is empty, and there's space in the transmit FIFO, the + * writing routines will write directly to transmit FIFO. + * Write buffer and counters protected by spinlocks + */ + #if 0 +static int wmt_3g_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct r_port *info = tty->driver_data; + CHANNEL_t *cp; + unsigned long flags; + WMTDBG("%s %d index %d\n",__FUNCTION__,__LINE__,tty->index); + + if (rocket_paranoia_check(info, "wmt_3g_put_char")) + return 0; + + /* + * Grab the port write mutex, locking out other processes that try to + * write to this port + */ + mutex_lock(&info->write_mtx); + printk(KERN_INFO "wmt_3g_put_char %c %d...\n", ch,ch); + +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "wmt_3g_put_char %c %d...\n", ch,ch); +#endif + + spin_lock_irqsave(&info->slock, flags); +#if ROCKET_EXIST + + cp = &info->channel; + + if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0) + info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); + + if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= XMIT_BUF_SIZE - 1; + info->xmit_cnt++; + set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + } else { + sOutB(sGetTxRxDataIO(cp), ch); + info->xmit_fifo_room--; + } +#else +#endif + + + spin_unlock_irqrestore(&info->slock, flags); + mutex_unlock(&info->write_mtx); + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + + return 1; +} +#endif +/* + * Exception handler - write routine, called when user app writes to the device. + * A per port write mutex is used to protect from another process writing to + * this port at the same time. This other process could be running on the other CPU + * or get control of the CPU if the copy_from_user() blocks due to a page fault (swapped out). + * Spinlocks protect the info xmit members. + */ +static int wmt_3g_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct r_port *info = tty->driver_data; + //CHANNEL_t *cp; + //const unsigned char *b; + int retval = 0; +// unsigned long flags; + int j; + + + //printk("%s %d index %d\n",__FUNCTION__,__LINE__,tty->index); +#if 0 + if (wmt_3g_data_port_w==NULL) { + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + wmt_3g_data_port_w = filp_open("/dev/ttyUSB0", O_RDWR, 0644); + if (IS_ERR(wmt_3g_data_port_w) ) + printk("create wmt_3g_data_port error\n"); + } + + + + if (wmt_3g_data_port_r==NULL) { + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + wmt_3g_data_port_r = filp_open("/dev/ttyUSB0", O_RDONLY|O_NONBLOCK , 0644); + if (IS_ERR(wmt_3g_data_port_r) ) + printk("create wmt_3g_data_port error\n"); + } +#endif + if (count <= 0 || rocket_paranoia_check(info, "wmt_3g_write")) + return 0; + //printk("%s %d index %d %x %x\n",__FUNCTION__,__LINE__,tty->index,info,&info->write_mtx); + +//printk("wmt_3g_write 1\n"); +if(0){ + char * p; + int i; + printk("mutex:"); + p = (char * )&info->write_mtx; + for(i=0;i<sizeof(info->write_mtx);i++){ + printk("%x ",(int)p[i]); + } + +} + + if (mutex_lock_interruptible(&info->write_mtx)) + return -ERESTARTSYS; + //printk("%s %d\n",__FUNCTION__,__LINE__); + +//printk("wmt_3g_write 2\n"); + +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "wmt_3g_write %d chars...\n", count); +#endif + + +#if ROCKET_EXIST + + cp = &info->channel; + + if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count) + info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); + + /* + * If the write queue for the port is empty, and there is FIFO space, stuff bytes + * into FIFO. Use the write queue for temp storage. + */ + if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { + c = min(count, info->xmit_fifo_room); + b = buf; + + /* Push data into FIFO, 2 bytes at a time */ + sOutStrW(sGetTxRxDataIO(cp), (unsigned short *) b, c / 2); + + /* If there is a byte remaining, write it */ + if (c & 1) + sOutB(sGetTxRxDataIO(cp), b[c - 1]); + + retval += c; + buf += c; + count -= c; + + spin_lock_irqsave(&info->slock, flags); + info->xmit_fifo_room -= c; + spin_unlock_irqrestore(&info->slock, flags); + } + + /* If count is zero, we wrote it all and are done */ + if (!count) + goto end; + + /* Write remaining data into the port's xmit_buf */ + while (1) { + /* Hung up ? */ + if (!test_bit(ASYNCB_NORMAL_ACTIVE, &info->port.flags)) + goto end; + c = min(count, XMIT_BUF_SIZE - info->xmit_cnt - 1); + c = min(c, XMIT_BUF_SIZE - info->xmit_head); + if (c <= 0) + break; + + b = buf; + memcpy(info->xmit_buf + info->xmit_head, b, c); + + spin_lock_irqsave(&info->slock, flags); + info->xmit_head = + (info->xmit_head + c) & (XMIT_BUF_SIZE - 1); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->slock, flags); + + buf += c; + count -= c; + retval += c; + } + + if ((retval > 0) && !tty->stopped && !tty->hw_stopped) + set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); + +end: + + + +#else + + #if 0 + int i; + + printk("write index %d:\n",tty->index); + for(i=0;i<count;i++) + printk("%d %c\n",buf[i],buf[i]); + #endif + +#if 0 +{ + + + + + mm_segment_t fs; + int n,pos; + fs = get_fs(); + set_fs(KERNEL_DS); + + n = wmt_3g_data_port_w->f_op->write(wmt_3g_data_port_w, buf, count,&wmt_3g_data_port_w->f_pos); + + //n = vfs_write(wmt_3g_data_port, buf, count, &pos); + set_fs(fs); +} + retval+=count; + +printk("%s %d\n",__FUNCTION__,__LINE__); +// retval +=wmt_3g_buf_write(&wmt_3g_usb_fifo, buf, count); +printk("%s %d\n",__FUNCTION__,__LINE__); +#else + //printk("%s %d index %d\n",__FUNCTION__,__LINE__,tty->index); + + if(tty->index == 0){ + + struct tty_struct * other_tty; + char flag ; + + if(wmt_3g_table[3] == NULL){ + printk("error %s %d\n",__FUNCTION__,__LINE__); + goto out1; + } + + other_tty = tty_port_tty_get(&(wmt_3g_table[3]->port)); + + if(other_tty ==NULL){ + printk("error %s %d\n",__FUNCTION__,__LINE__); + goto out1; + } + + + flag = TTY_NORMAL; + for(j=0;j<count;j++) + tty_insert_flip_char(other_tty, buf[j],flag); + + tty_flip_buffer_push(other_tty); + + tty_kref_put(other_tty); + out1: + //printk("%s %d\n",__FUNCTION__,__LINE__); + + retval+=count; + + }else if(tty->index == 3){ + + struct tty_struct *other_tty; + char flag; + + if(wmt_3g_table[0] == NULL){ + printk("error %s %d\n",__FUNCTION__,__LINE__); + goto out3; + } + + other_tty = tty_port_tty_get(&(wmt_3g_table[0]->port)); + + if(other_tty ==NULL){ + printk("error %s %d\n",__FUNCTION__,__LINE__); + goto out3; + + } + + + flag = TTY_NORMAL; + for(j=0;j<count;j++) + tty_insert_flip_char(other_tty, buf[j],flag); + + tty_flip_buffer_push(other_tty); + + tty_kref_put(other_tty); + out3: + retval+=count; + + }else{ + printk("error %s %d\n",__FUNCTION__,__LINE__); + retval+=count; + + + } + + +#endif +#endif + + + if (info->xmit_cnt < WAKEUP_CHARS) { + tty_wakeup(tty); +#ifdef ROCKETPORT_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } + + + + //printk("wmt_3g_write 3\n"); + + mutex_unlock(&info->write_mtx); + //printk("%s %d\n",__FUNCTION__,__LINE__); + + + return retval; +} + +/* + * Return the number of characters that can be sent. We estimate + * only using the in-memory transmit buffer only, and ignore the + * potential space in the transmit FIFO. + */ +static int wmt_3g_write_room(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + int ret; + //WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + if (rocket_paranoia_check(info, "wmt_3g_write_room")) + return 0; + + return 512; + + ret = XMIT_BUF_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "wmt_3g_write_room returns %d...\n", ret); +#endif + return ret; +} + +/* + * Return the number of characters in the buffer. Again, this only + * counts those characters in the in-memory transmit buffer. + */ +static int wmt_3g_chars_in_buffer(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + if (rocket_paranoia_check(info, "wmt_3g_chars_in_buffer")) + return 0; + + schedule_timeout(msecs_to_jiffies(100)); + return 0; + +#ifdef ROCKET_DEBUG_WRITE + printk(KERN_INFO "wmt_3g_chars_in_buffer returns %d...\n", info->xmit_cnt); +#endif + return info->xmit_cnt; +} + +/* + * Flushes the TX fifo for a port, deletes data in the xmit_buf stored in the + * r_port struct for the port. Note that spinlock are used to protect info members, + * do not call this function if the spinlock is already held. + */ +static void wmt_3g_flush_buffer(struct tty_struct *tty) +{ + struct r_port *info = tty->driver_data; +// CHANNEL_t *cp; + unsigned long flags; + WMTDBG("%s %d\n",__FUNCTION__,__LINE__); + + if (rocket_paranoia_check(info, "wmt_3g_flush_buffer")) + return; + + spin_lock_irqsave(&info->slock, flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + spin_unlock_irqrestore(&info->slock, flags); + +#ifdef ROCKETPORT_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + tty_wakeup(tty); +#if ROCKET_EXIST + cp = &info->channel; + sFlushTxFIFO(cp); +#endif +} + +#if 0//def CONFIG_PCI + +static struct pci_device_id __devinitdata __used rocket_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(pci, rocket_pci_ids); + +/* + * Called when a PCI card is found. Retrieves and stores model information, + * init's aiopic and serial port hardware. + * Inputs: i is the board number (0-n) + */ +static __init int register_PCI(int i, struct pci_dev *dev) +{ + int num_aiops, aiop, max_num_aiops, num_chan, chan; + unsigned int aiopio[MAX_AIOPS_PER_BOARD]; + CONTROLLER_t *ctlp; + + int fast_clock = 0; + int altChanRingIndicator = 0; + int ports_per_aiop = 8; + WordIO_t ConfigIO = 0; + ByteIO_t UPCIRingInd = 0; + + if (!dev || pci_enable_device(dev)) + return 0; + + rcktpt_io_addr[i] = pci_resource_start(dev, 0); + + rcktpt_type[i] = ROCKET_TYPE_NORMAL; + rocketModel[i].loadrm2 = 0; + rocketModel[i].startingPortNumber = nextLineNumber; + + /* Depending on the model, set up some config variables */ + switch (dev->device) { + case PCI_DEVICE_ID_RP4QUAD: + max_num_aiops = 1; + ports_per_aiop = 4; + rocketModel[i].model = MODEL_RP4QUAD; + strcpy(rocketModel[i].modelString, "RocketPort 4 port w/quad cable"); + rocketModel[i].numPorts = 4; + break; + case PCI_DEVICE_ID_RP8OCTA: + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8OCTA; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/octa cable"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_URP8OCTA: + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RP8OCTA; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/octa cable"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP8INTF: + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8INTF; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/external I/F"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_URP8INTF: + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RP8INTF; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 8 port w/external I/F"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP8J: + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8J; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/RJ11 connectors"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP4J: + max_num_aiops = 1; + ports_per_aiop = 4; + rocketModel[i].model = MODEL_RP4J; + strcpy(rocketModel[i].modelString, "RocketPort 4 port w/RJ45 connectors"); + rocketModel[i].numPorts = 4; + break; + case PCI_DEVICE_ID_RP8SNI: + max_num_aiops = 1; + rocketModel[i].model = MODEL_RP8SNI; + strcpy(rocketModel[i].modelString, "RocketPort 8 port w/ custom DB78"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP16SNI: + max_num_aiops = 2; + rocketModel[i].model = MODEL_RP16SNI; + strcpy(rocketModel[i].modelString, "RocketPort 16 port w/ custom DB78"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_RP16INTF: + max_num_aiops = 2; + rocketModel[i].model = MODEL_RP16INTF; + strcpy(rocketModel[i].modelString, "RocketPort 16 port w/external I/F"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_URP16INTF: + max_num_aiops = 2; + rocketModel[i].model = MODEL_UPCI_RP16INTF; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 16 port w/external I/F"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_CRP16INTF: + max_num_aiops = 2; + rocketModel[i].model = MODEL_CPCI_RP16INTF; + strcpy(rocketModel[i].modelString, "RocketPort Compact PCI 16 port w/external I/F"); + rocketModel[i].numPorts = 16; + break; + case PCI_DEVICE_ID_RP32INTF: + max_num_aiops = 4; + rocketModel[i].model = MODEL_RP32INTF; + strcpy(rocketModel[i].modelString, "RocketPort 32 port w/external I/F"); + rocketModel[i].numPorts = 32; + break; + case PCI_DEVICE_ID_URP32INTF: + max_num_aiops = 4; + rocketModel[i].model = MODEL_UPCI_RP32INTF; + strcpy(rocketModel[i].modelString, "RocketPort UPCI 32 port w/external I/F"); + rocketModel[i].numPorts = 32; + break; + case PCI_DEVICE_ID_RPP4: + max_num_aiops = 1; + ports_per_aiop = 4; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RPP4; + strcpy(rocketModel[i].modelString, "RocketPort Plus 4 port"); + rocketModel[i].numPorts = 4; + break; + case PCI_DEVICE_ID_RPP8: + max_num_aiops = 2; + ports_per_aiop = 4; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RPP8; + strcpy(rocketModel[i].modelString, "RocketPort Plus 8 port"); + rocketModel[i].numPorts = 8; + break; + case PCI_DEVICE_ID_RP2_232: + max_num_aiops = 1; + ports_per_aiop = 2; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RP2_232; + strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS232"); + rocketModel[i].numPorts = 2; + break; + case PCI_DEVICE_ID_RP2_422: + max_num_aiops = 1; + ports_per_aiop = 2; + altChanRingIndicator++; + fast_clock++; + rocketModel[i].model = MODEL_RP2_422; + strcpy(rocketModel[i].modelString, "RocketPort Plus 2 port RS422"); + rocketModel[i].numPorts = 2; + break; + case PCI_DEVICE_ID_RP6M: + + max_num_aiops = 1; + ports_per_aiop = 6; + + /* If revision is 1, the rocketmodem flash must be loaded. + * If it is 2 it is a "socketed" version. */ + if (dev->revision == 1) { + rcktpt_type[i] = ROCKET_TYPE_MODEMII; + rocketModel[i].loadrm2 = 1; + } else { + rcktpt_type[i] = ROCKET_TYPE_MODEM; + } + + rocketModel[i].model = MODEL_RP6M; + strcpy(rocketModel[i].modelString, "RocketModem 6 port"); + rocketModel[i].numPorts = 6; + break; + case PCI_DEVICE_ID_RP4M: + max_num_aiops = 1; + ports_per_aiop = 4; + if (dev->revision == 1) { + rcktpt_type[i] = ROCKET_TYPE_MODEMII; + rocketModel[i].loadrm2 = 1; + } else { + rcktpt_type[i] = ROCKET_TYPE_MODEM; + } + + rocketModel[i].model = MODEL_RP4M; + strcpy(rocketModel[i].modelString, "RocketModem 4 port"); + rocketModel[i].numPorts = 4; + break; + default: + max_num_aiops = 0; + break; + } + + /* + * Check for UPCI boards. + */ + + switch (dev->device) { + case PCI_DEVICE_ID_URP32INTF: + case PCI_DEVICE_ID_URP8INTF: + case PCI_DEVICE_ID_URP16INTF: + case PCI_DEVICE_ID_CRP16INTF: + case PCI_DEVICE_ID_URP8OCTA: + rcktpt_io_addr[i] = pci_resource_start(dev, 2); + ConfigIO = pci_resource_start(dev, 1); + if (dev->device == PCI_DEVICE_ID_URP8OCTA) { + UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; + + /* + * Check for octa or quad cable. + */ + if (! + (sInW(ConfigIO + _PCI_9030_GPIO_CTRL) & + PCI_GPIO_CTRL_8PORT)) { + ports_per_aiop = 4; + rocketModel[i].numPorts = 4; + } + } + break; + case PCI_DEVICE_ID_UPCI_RM3_8PORT: + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RM3_8PORT; + strcpy(rocketModel[i].modelString, "RocketModem III 8 port"); + rocketModel[i].numPorts = 8; + rcktpt_io_addr[i] = pci_resource_start(dev, 2); + UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; + ConfigIO = pci_resource_start(dev, 1); + rcktpt_type[i] = ROCKET_TYPE_MODEMIII; + break; + case PCI_DEVICE_ID_UPCI_RM3_4PORT: + max_num_aiops = 1; + rocketModel[i].model = MODEL_UPCI_RM3_4PORT; + strcpy(rocketModel[i].modelString, "RocketModem III 4 port"); + rocketModel[i].numPorts = 4; + rcktpt_io_addr[i] = pci_resource_start(dev, 2); + UPCIRingInd = rcktpt_io_addr[i] + _PCI_9030_RING_IND; + ConfigIO = pci_resource_start(dev, 1); + rcktpt_type[i] = ROCKET_TYPE_MODEMIII; + break; + default: + break; + } + + if (fast_clock) { + sClockPrescale = 0x12; /* mod 2 (divide by 3) */ + wmt_3g_baud_base[i] = 921600; + } else { + /* + * If support_low_speed is set, use the slow clock + * prescale, which supports 50 bps + */ + if (support_low_speed) { + /* mod 9 (divide by 10) prescale */ + sClockPrescale = 0x19; + wmt_3g_baud_base[i] = 230400; + } else { + /* mod 4 (divide by 5) prescale */ + sClockPrescale = 0x14; + wmt_3g_baud_base[i] = 460800; + } + } + + for (aiop = 0; aiop < max_num_aiops; aiop++) + aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40); + ctlp = sCtlNumToCtlPtr(i); + num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, ConfigIO, 0, FREQ_DIS, 0, altChanRingIndicator, UPCIRingInd); + for (aiop = 0; aiop < max_num_aiops; aiop++) + ctlp->AiopNumChan[aiop] = ports_per_aiop; + + dev_info(&dev->dev, "comtrol PCI controller #%d found at " + "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n", + i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString, + rocketModel[i].startingPortNumber, + rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1); + + if (num_aiops <= 0) { + rcktpt_io_addr[i] = 0; + return (0); + } + is_PCI[i] = 1; + + /* Reset the AIOPIC, init the serial ports */ + for (aiop = 0; aiop < num_aiops; aiop++) { + sResetAiopByNum(ctlp, aiop); + num_chan = ports_per_aiop; + for (chan = 0; chan < num_chan; chan++) + init_r_port(i, aiop, chan, dev,0); + } + + /* Rocket modems must be reset */ + if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || + (rcktpt_type[i] == ROCKET_TYPE_MODEMII) || + (rcktpt_type[i] == ROCKET_TYPE_MODEMIII)) { + num_chan = ports_per_aiop; + for (chan = 0; chan < num_chan; chan++) + sPCIModemReset(ctlp, chan, 1); + msleep(500); + for (chan = 0; chan < num_chan; chan++) + sPCIModemReset(ctlp, chan, 0); + msleep(500); + rmSpeakerReset(ctlp, rocketModel[i].model); + } + return (1); +} + +/* + * Probes for PCI cards, inits them if found + * Input: board_found = number of ISA boards already found, or the + * starting board number + * Returns: Number of PCI boards found + */ +static int __init init_PCI(int boards_found) +{ + struct pci_dev *dev = NULL; + int count = 0; + + /* Work through the PCI device list, pulling out ours */ + while ((dev = pci_get_device(PCI_VENDOR_ID_RP, PCI_ANY_ID, dev))) { + if (register_PCI(count + boards_found, dev)) + count++; + } + return (count); +} + +#endif /* CONFIG_PCI */ + +/* + * Probes for ISA cards + * Input: i = the board number to look for + * Returns: 1 if board found, 0 else + */ + #if 0 +static int __init init_ISA(int i) +{ + int num_aiops, num_chan = 0, total_num_chan = 0; + int aiop, chan; + unsigned int aiopio[MAX_AIOPS_PER_BOARD]; + CONTROLLER_t *ctlp; + char *type_string; + + /* If io_addr is zero, no board configured */ + if (rcktpt_io_addr[i] == 0) + return (0); + + /* Reserve the IO region */ + if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) { + printk(KERN_ERR "Unable to reserve IO region for configured " + "ISA RocketPort at address 0x%lx, board not " + "installed...\n", rcktpt_io_addr[i]); + rcktpt_io_addr[i] = 0; + return (0); + } + + ctlp = sCtlNumToCtlPtr(i); + + ctlp->boardType = rcktpt_type[i]; + + switch (rcktpt_type[i]) { + case ROCKET_TYPE_PC104: + type_string = "(PC104)"; + break; + case ROCKET_TYPE_MODEM: + type_string = "(RocketModem)"; + break; + case ROCKET_TYPE_MODEMII: + type_string = "(RocketModem II)"; + break; + default: + type_string = ""; + break; + } + + /* + * If support_low_speed is set, use the slow clock prescale, + * which supports 50 bps + */ + if (support_low_speed) { + sClockPrescale = 0x19; /* mod 9 (divide by 10) prescale */ + wmt_3g_baud_base[i] = 230400; + } else { + sClockPrescale = 0x14; /* mod 4 (divide by 5) prescale */ + wmt_3g_baud_base[i] = 460800; + } + + for (aiop = 0; aiop < MAX_AIOPS_PER_BOARD; aiop++) + aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x400); + + num_aiops = sInitController(ctlp, i, controller + (i * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); + + if (ctlp->boardType == ROCKET_TYPE_PC104) { + sEnAiop(ctlp, 2); /* only one AIOPIC, but these */ + sEnAiop(ctlp, 3); /* CSels used for other stuff */ + } + + /* If something went wrong initing the AIOP's release the ISA IO memory */ + if (num_aiops <= 0) { + release_region(rcktpt_io_addr[i], 64); + rcktpt_io_addr[i] = 0; + return (0); + } + + rocketModel[i].startingPortNumber = nextLineNumber; + + for (aiop = 0; aiop < num_aiops; aiop++) { + sResetAiopByNum(ctlp, aiop); + sEnAiop(ctlp, aiop); + num_chan = sGetAiopNumChan(ctlp, aiop); + total_num_chan += num_chan; + for (chan = 0; chan < num_chan; chan++) + init_r_port(i, aiop, chan, NULL,0); + } + is_PCI[i] = 0; + if ((rcktpt_type[i] == ROCKET_TYPE_MODEM) || (rcktpt_type[i] == ROCKET_TYPE_MODEMII)) { + num_chan = sGetAiopNumChan(ctlp, 0); + total_num_chan = num_chan; + for (chan = 0; chan < num_chan; chan++) + sModemReset(ctlp, chan, 1); + msleep(500); + for (chan = 0; chan < num_chan; chan++) + sModemReset(ctlp, chan, 0); + msleep(500); + strcpy(rocketModel[i].modelString, "RocketModem ISA"); + } else { + strcpy(rocketModel[i].modelString, "RocketPort ISA"); + } + rocketModel[i].numPorts = total_num_chan; + rocketModel[i].model = MODEL_ISA; + + printk(KERN_INFO "RocketPort ISA card #%d found at 0x%lx - %d AIOPs %s\n", + i, rcktpt_io_addr[i], num_aiops, type_string); + + printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n", + rocketModel[i].modelString, + rocketModel[i].startingPortNumber, + rocketModel[i].startingPortNumber + + rocketModel[i].numPorts - 1); + + return (1); +} + +#endif + +static const struct tty_operations rocket_ops = { + .open = wmt_3g_open, + .close = wmt_3g_close, + .write = wmt_3g_write, +// .put_char = wmt_3g_put_char, + .write_room = wmt_3g_write_room, + .chars_in_buffer = wmt_3g_chars_in_buffer, + .flush_buffer = wmt_3g_flush_buffer, + .ioctl = wmt_3g_ioctl, + .throttle = wmt_3g_throttle, + .unthrottle = wmt_3g_unthrottle, + .set_termios = wmt_3g_set_termios, + .stop = wmt_3g_stop, + .start = wmt_3g_start, + .hangup = wmt_3g_hangup, + .break_ctl = wmt_3g_break, + .send_xchar = wmt_3g_send_xchar, + .wait_until_sent = wmt_3g_wait_until_sent, + .tiocmget = wmt_3g_tiocmget, + .tiocmset = wmt_3g_tiocmset, +}; + +static const struct tty_port_operations rocket_port_ops = { + .carrier_raised = carrier_raised, + .dtr_rts = dtr_rts, +}; + + + +/* + * The module "startup" routine; it's run when the module is loaded. + */ +static int __init wmt_3g_init(void) +{ + int ret = -ENOMEM;//, pci_boards_found, isa_boards_found; + int i=0; + + printk(KERN_INFO "RocketPort device driver module, version %s, %s\n", + ROCKET_VERSION, ROCKET_DATE); + + rocket_driver = alloc_tty_driver(MAX_RP_PORTS); + if (!rocket_driver) + goto err; + + /* + * If board 1 is non-zero, there is at least one ISA configured. If controller is + * zero, use the default controller IO address of board1 + 0x40. + */ + if (board1) { + if (controller == 0) + controller = board1 + 0x40; + } else { + controller = 0; /* Used as a flag, meaning no ISA boards */ + } + + /* If an ISA card is configured, reserve the 4 byte IO space for the Mudbac controller */ + if (controller && (!request_region(controller, 4, "Comtrol RocketPort"))) { + printk(KERN_ERR "Unable to reserve IO region for first " + "configured ISA RocketPort controller 0x%lx. " + "Driver exiting\n", controller); + ret = -EBUSY; + goto err_tty; + } + + /* Store ISA variable retrieved from command line or .conf file. */ + rcktpt_io_addr[0] = board1; + rcktpt_io_addr[1] = board2; + rcktpt_io_addr[2] = board3; + rcktpt_io_addr[3] = board4; + + rcktpt_type[0] = modem1 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[0] = pc104_1[0] ? ROCKET_TYPE_PC104 : rcktpt_type[0]; + rcktpt_type[1] = modem2 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[1] = pc104_2[0] ? ROCKET_TYPE_PC104 : rcktpt_type[1]; + rcktpt_type[2] = modem3 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[2] = pc104_3[0] ? ROCKET_TYPE_PC104 : rcktpt_type[2]; + rcktpt_type[3] = modem4 ? ROCKET_TYPE_MODEM : ROCKET_TYPE_NORMAL; + rcktpt_type[3] = pc104_4[0] ? ROCKET_TYPE_PC104 : rcktpt_type[3]; + + /* + * Set up the tty driver structure and then register this + * driver with the tty layer. + */ + + rocket_driver->owner = THIS_MODULE; + rocket_driver->flags = TTY_DRIVER_DYNAMIC_DEV; + rocket_driver->name = "ttyWMT"; + rocket_driver->driver_name = "Comtrol RocketPort"; + rocket_driver->major = TTY_ROCKET_MAJOR; + rocket_driver->minor_start = 0; + rocket_driver->type = TTY_DRIVER_TYPE_SERIAL; + rocket_driver->subtype = SERIAL_TYPE_NORMAL; + rocket_driver->init_termios = tty_std_termios; + //kevin add + rocket_driver->init_termios.c_lflag &= ~ECHO; + rocket_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + rocket_driver->init_termios.c_ispeed = 9600; + rocket_driver->init_termios.c_ospeed = 9600; +#ifdef ROCKET_SOFT_FLOW + rocket_driver->flags |= TTY_DRIVER_REAL_RAW; +#endif + tty_set_operations(rocket_driver, &rocket_ops); + + ret = tty_register_driver(rocket_driver); + if (ret < 0) { + printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); + goto err_controller; + } + +#ifdef ROCKET_DEBUG_OPEN + printk(KERN_INFO "RocketPort driver is major %d\n", rocket_driver->major); +#endif + +#if ROCKET_EXIST + /* + * OK, let's probe each of the controllers looking for boards. Any boards found + * will be initialized here. + */ + isa_boards_found = 0; + pci_boards_found = 0; + + for (i = 0; i < NUM_BOARDS; i++) { + if (init_ISA(i)) + isa_boards_found++; + } + +#ifdef CONFIG_PCI + if (isa_boards_found < NUM_BOARDS) + pci_boards_found = init_PCI(isa_boards_found); +#endif + + max_board = pci_boards_found + isa_boards_found; + + if (max_board == 0) { + printk(KERN_ERR "No rocketport ports found; unloading driver\n"); + ret = -ENXIO; + goto err_ttyu; + } +#endif + + init_r_port(i, 0, 0, NULL,0); + init_r_port(i, 0, 0, NULL,1); + init_r_port(i, 0, 0, NULL,2); + init_r_port(i, 0, 0, NULL,3); + return 0; +//err_ttyu: +// tty_unregister_driver(rocket_driver); +err_controller: + if (controller) + release_region(controller, 4); +err_tty: + put_tty_driver(rocket_driver); +err: + return ret; +} + + +static void wmt_3g_cleanup_module(void) +{ + int retval; + int i; + + del_timer_sync(&rocket_timer); + + retval = tty_unregister_driver(rocket_driver); + if (retval) + printk(KERN_ERR "Error %d while trying to unregister " + "rocketport driver\n", -retval); + + for (i = 0; i < MAX_RP_PORTS; i++) + if (wmt_3g_table[i]) { + tty_unregister_device(rocket_driver, i); + kfree(wmt_3g_table[i]); + } + + put_tty_driver(rocket_driver); + + for (i = 0; i < NUM_BOARDS; i++) { + if (rcktpt_io_addr[i] <= 0 || is_PCI[i]) + continue; + release_region(rcktpt_io_addr[i], 64); + } + if (controller) + release_region(controller, 4); +} + +/*************************************************************************** +Function: sInitController +Purpose: Initialization of controller global registers and controller + structure. +Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, + IRQNum,Frequency,PeriodicOnly) + CONTROLLER_T *CtlP; Ptr to controller structure + int CtlNum; Controller number + ByteIO_t MudbacIO; Mudbac base I/O address. + ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. + This list must be in the order the AIOPs will be found on the + controller. Once an AIOP in the list is not found, it is + assumed that there are no more AIOPs on the controller. + int AiopIOListSize; Number of addresses in AiopIOList + int IRQNum; Interrupt Request number. Can be any of the following: + 0: Disable global interrupts + 3: IRQ 3 + 4: IRQ 4 + 5: IRQ 5 + 9: IRQ 9 + 10: IRQ 10 + 11: IRQ 11 + 12: IRQ 12 + 15: IRQ 15 + Byte_t Frequency: A flag identifying the frequency + of the periodic interrupt, can be any one of the following: + FREQ_DIS - periodic interrupt disabled + FREQ_137HZ - 137 Hertz + FREQ_69HZ - 69 Hertz + FREQ_34HZ - 34 Hertz + FREQ_17HZ - 17 Hertz + FREQ_9HZ - 9 Hertz + FREQ_4HZ - 4 Hertz + If IRQNum is set to 0 the Frequency parameter is + overidden, it is forced to a value of FREQ_DIS. + int PeriodicOnly: 1 if all interrupts except the periodic + interrupt are to be blocked. + 0 is both the periodic interrupt and + other channel interrupts are allowed. + If IRQNum is set to 0 the PeriodicOnly parameter is + overidden, it is forced to a value of 0. +Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller + initialization failed. + +Comments: + If periodic interrupts are to be disabled but AIOP interrupts + are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. + + If interrupts are to be completely disabled set IRQNum to 0. + + Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an + invalid combination. + + This function performs initialization of global interrupt modes, + but it does not actually enable global interrupts. To enable + and disable global interrupts use functions sEnGlobalInt() and + sDisGlobalInt(). Enabling of global interrupts is normally not + done until all other initializations are complete. + + Even if interrupts are globally enabled, they must also be + individually enabled for each channel that is to generate + interrupts. + +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. + + After this function all AIOPs on the controller are disabled, + they can be enabled with sEnAiop(). +*/ +#if 0 +static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, + ByteIO_t * AiopIOList, int AiopIOListSize, + int IRQNum, Byte_t Frequency, int PeriodicOnly) +{ + int i; + ByteIO_t io; + int done; + + CtlP->AiopIntrBits = aiop_intr_bits; + CtlP->AltChanRingIndicator = 0; + CtlP->CtlNum = CtlNum; + CtlP->CtlID = CTLID_0001; /* controller release 1 */ + CtlP->BusType = isISA; + CtlP->MBaseIO = MudbacIO; + CtlP->MReg1IO = MudbacIO + 1; + CtlP->MReg2IO = MudbacIO + 2; + CtlP->MReg3IO = MudbacIO + 3; +#if 1 + CtlP->MReg2 = 0; /* interrupt disable */ + CtlP->MReg3 = 0; /* no periodic interrupts */ +#else + if (sIRQMap[IRQNum] == 0) { /* interrupts globally disabled */ + CtlP->MReg2 = 0; /* interrupt disable */ + CtlP->MReg3 = 0; /* no periodic interrupts */ + } else { + CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ + CtlP->MReg3 = Frequency; /* set frequency */ + if (PeriodicOnly) { /* periodic interrupt only */ + CtlP->MReg3 |= PERIODIC_ONLY; + } + } +#endif + sOutB(CtlP->MReg2IO, CtlP->MReg2); + sOutB(CtlP->MReg3IO, CtlP->MReg3); + sControllerEOI(CtlP); /* clear EOI if warm init */ + /* Init AIOPs */ + CtlP->NumAiop = 0; + for (i = done = 0; i < AiopIOListSize; i++) { + io = AiopIOList[i]; + CtlP->AiopIO[i] = (WordIO_t) io; + CtlP->AiopIntChanIO[i] = io + _INT_CHAN; + sOutB(CtlP->MReg2IO, CtlP->MReg2 | (i & 0x03)); /* AIOP index */ + sOutB(MudbacIO, (Byte_t) (io >> 6)); /* set up AIOP I/O in MUDBAC */ + if (done) + continue; + sEnAiop(CtlP, i); /* enable the AIOP */ + CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ + if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ + done = 1; /* done looking for AIOPs */ + else { + CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ + sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ + sOutB(io + _INDX_DATA, sClockPrescale); + CtlP->NumAiop++; /* bump count of AIOPs */ + } + sDisAiop(CtlP, i); /* disable AIOP */ + } + + if (CtlP->NumAiop == 0) + return (-1); + else + return (CtlP->NumAiop); +} +#endif +/*************************************************************************** +Function: sPCIInitController +Purpose: Initialization of controller global registers and controller + structure. +Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, + IRQNum,Frequency,PeriodicOnly) + CONTROLLER_T *CtlP; Ptr to controller structure + int CtlNum; Controller number + ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. + This list must be in the order the AIOPs will be found on the + controller. Once an AIOP in the list is not found, it is + assumed that there are no more AIOPs on the controller. + int AiopIOListSize; Number of addresses in AiopIOList + int IRQNum; Interrupt Request number. Can be any of the following: + 0: Disable global interrupts + 3: IRQ 3 + 4: IRQ 4 + 5: IRQ 5 + 9: IRQ 9 + 10: IRQ 10 + 11: IRQ 11 + 12: IRQ 12 + 15: IRQ 15 + Byte_t Frequency: A flag identifying the frequency + of the periodic interrupt, can be any one of the following: + FREQ_DIS - periodic interrupt disabled + FREQ_137HZ - 137 Hertz + FREQ_69HZ - 69 Hertz + FREQ_34HZ - 34 Hertz + FREQ_17HZ - 17 Hertz + FREQ_9HZ - 9 Hertz + FREQ_4HZ - 4 Hertz + If IRQNum is set to 0 the Frequency parameter is + overidden, it is forced to a value of FREQ_DIS. + int PeriodicOnly: 1 if all interrupts except the periodic + interrupt are to be blocked. + 0 is both the periodic interrupt and + other channel interrupts are allowed. + If IRQNum is set to 0 the PeriodicOnly parameter is + overidden, it is forced to a value of 0. +Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller + initialization failed. + +Comments: + If periodic interrupts are to be disabled but AIOP interrupts + are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. + + If interrupts are to be completely disabled set IRQNum to 0. + + Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an + invalid combination. + + This function performs initialization of global interrupt modes, + but it does not actually enable global interrupts. To enable + and disable global interrupts use functions sEnGlobalInt() and + sDisGlobalInt(). Enabling of global interrupts is normally not + done until all other initializations are complete. + + Even if interrupts are globally enabled, they must also be + individually enabled for each channel that is to generate + interrupts. + +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. + + After this function all AIOPs on the controller are disabled, + they can be enabled with sEnAiop(). +*/ +#if 0 +static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, + ByteIO_t * AiopIOList, int AiopIOListSize, + WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, + int PeriodicOnly, int altChanRingIndicator, + int UPCIRingInd) +{ + int i; + ByteIO_t io; + + CtlP->AltChanRingIndicator = altChanRingIndicator; + CtlP->UPCIRingInd = UPCIRingInd; + CtlP->CtlNum = CtlNum; + CtlP->CtlID = CTLID_0001; /* controller release 1 */ + CtlP->BusType = isPCI; /* controller release 1 */ + + if (ConfigIO) { + CtlP->isUPCI = 1; + CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; + CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; + CtlP->AiopIntrBits = upci_aiop_intr_bits; + } else { + CtlP->isUPCI = 0; + CtlP->PCIIO = + (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); + CtlP->AiopIntrBits = aiop_intr_bits; + } + + sPCIControllerEOI(CtlP); /* clear EOI if warm init */ + /* Init AIOPs */ + CtlP->NumAiop = 0; + for (i = 0; i < AiopIOListSize; i++) { + io = AiopIOList[i]; + CtlP->AiopIO[i] = (WordIO_t) io; + CtlP->AiopIntChanIO[i] = io + _INT_CHAN; + + CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ + if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ + break; /* done looking for AIOPs */ + + CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ + sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ + sOutB(io + _INDX_DATA, sClockPrescale); + CtlP->NumAiop++; /* bump count of AIOPs */ + } + + if (CtlP->NumAiop == 0) + return (-1); + else + return (CtlP->NumAiop); +} +#endif +/*************************************************************************** +Function: sReadAiopID +Purpose: Read the AIOP idenfication number directly from an AIOP. +Call: sReadAiopID(io) + ByteIO_t io: AIOP base I/O address +Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X + is replace by an identifying number. + Flag AIOPID_NULL if no valid AIOP is found +Warnings: No context switches are allowed while executing this function. + +*/ +#if 0 +static int sReadAiopID(ByteIO_t io) +{ + Byte_t AiopID; /* ID byte from AIOP */ + + sOutB(io + _CMD_REG, RESET_ALL); /* reset AIOP */ + sOutB(io + _CMD_REG, 0x0); + AiopID = sInW(io + _CHN_STAT0) & 0x07; + if (AiopID == 0x06) + return (1); + else /* AIOP does not exist */ + return (-1); +} +#endif +/*************************************************************************** +Function: sReadAiopNumChan +Purpose: Read the number of channels available in an AIOP directly from + an AIOP. +Call: sReadAiopNumChan(io) + WordIO_t io: AIOP base I/O address +Return: int: The number of channels available +Comments: The number of channels is determined by write/reads from identical + offsets within the SRAM address spaces for channels 0 and 4. + If the channel 4 space is mirrored to channel 0 it is a 4 channel + AIOP, otherwise it is an 8 channel. +Warnings: No context switches are allowed while executing this function. +*/ +#if 0 +static int sReadAiopNumChan(WordIO_t io) +{ + Word_t x; + static Byte_t R[4] = { 0x00, 0x00, 0x34, 0x12 }; + + /* write to chan 0 SRAM */ + out32((DWordIO_t) io + _INDX_ADDR, R); + sOutW(io + _INDX_ADDR, 0); /* read from SRAM, chan 0 */ + x = sInW(io + _INDX_DATA); + sOutW(io + _INDX_ADDR, 0x4000); /* read from SRAM, chan 4 */ + if (x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ + return (8); + else + return (4); +} +#endif +/*************************************************************************** +Function: sInitChan +Purpose: Initialization of a channel and channel structure +Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) + CONTROLLER_T *CtlP; Ptr to controller structure + CHANNEL_T *ChP; Ptr to channel structure + int AiopNum; AIOP number within controller + int ChanNum; Channel number within AIOP +Return: int: 1 if initialization succeeded, 0 if it fails because channel + number exceeds number of channels available in AIOP. +Comments: This function must be called before a channel can be used. +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. +*/ +#if 0 +static int sInitChan(CONTROLLER_T * CtlP, CHANNEL_T * ChP, int AiopNum, + int ChanNum) +{ + int i; + WordIO_t AiopIO; + WordIO_t ChIOOff; + Byte_t *ChR; + Word_t ChOff; + static Byte_t R[4]; + int brd9600; + + if (ChanNum >= CtlP->AiopNumChan[AiopNum]) + return 0; /* exceeds num chans in AIOP */ + + /* Channel, AIOP, and controller identifiers */ + ChP->CtlP = CtlP; + ChP->ChanID = CtlP->AiopID[AiopNum]; + ChP->AiopNum = AiopNum; + ChP->ChanNum = ChanNum; + + /* Global direct addresses */ + AiopIO = CtlP->AiopIO[AiopNum]; + ChP->Cmd = (ByteIO_t) AiopIO + _CMD_REG; + ChP->IntChan = (ByteIO_t) AiopIO + _INT_CHAN; + ChP->IntMask = (ByteIO_t) AiopIO + _INT_MASK; + ChP->IndexAddr = (DWordIO_t) AiopIO + _INDX_ADDR; + ChP->IndexData = AiopIO + _INDX_DATA; + + /* Channel direct addresses */ + ChIOOff = AiopIO + ChP->ChanNum * 2; + ChP->TxRxData = ChIOOff + _TD0; + ChP->ChanStat = ChIOOff + _CHN_STAT0; + ChP->TxRxCount = ChIOOff + _FIFO_CNT0; + ChP->IntID = (ByteIO_t) AiopIO + ChP->ChanNum + _INT_ID0; + + /* Initialize the channel from the RData array */ + for (i = 0; i < RDATASIZE; i += 4) { + R[0] = RData[i]; + R[1] = RData[i + 1] + 0x10 * ChanNum; + R[2] = RData[i + 2]; + R[3] = RData[i + 3]; + out32(ChP->IndexAddr, R); + } + + ChR = ChP->R; + for (i = 0; i < RREGDATASIZE; i += 4) { + ChR[i] = RRegData[i]; + ChR[i + 1] = RRegData[i + 1] + 0x10 * ChanNum; + ChR[i + 2] = RRegData[i + 2]; + ChR[i + 3] = RRegData[i + 3]; + } + + /* Indexed registers */ + ChOff = (Word_t) ChanNum *0x1000; + + if (sClockPrescale == 0x14) + brd9600 = 47; + else + brd9600 = 23; + + ChP->BaudDiv[0] = (Byte_t) (ChOff + _BAUD); + ChP->BaudDiv[1] = (Byte_t) ((ChOff + _BAUD) >> 8); + ChP->BaudDiv[2] = (Byte_t) brd9600; + ChP->BaudDiv[3] = (Byte_t) (brd9600 >> 8); + out32(ChP->IndexAddr, ChP->BaudDiv); + + ChP->TxControl[0] = (Byte_t) (ChOff + _TX_CTRL); + ChP->TxControl[1] = (Byte_t) ((ChOff + _TX_CTRL) >> 8); + ChP->TxControl[2] = 0; + ChP->TxControl[3] = 0; + out32(ChP->IndexAddr, ChP->TxControl); + + ChP->RxControl[0] = (Byte_t) (ChOff + _RX_CTRL); + ChP->RxControl[1] = (Byte_t) ((ChOff + _RX_CTRL) >> 8); + ChP->RxControl[2] = 0; + ChP->RxControl[3] = 0; + out32(ChP->IndexAddr, ChP->RxControl); + + ChP->TxEnables[0] = (Byte_t) (ChOff + _TX_ENBLS); + ChP->TxEnables[1] = (Byte_t) ((ChOff + _TX_ENBLS) >> 8); + ChP->TxEnables[2] = 0; + ChP->TxEnables[3] = 0; + out32(ChP->IndexAddr, ChP->TxEnables); + + ChP->TxCompare[0] = (Byte_t) (ChOff + _TXCMP1); + ChP->TxCompare[1] = (Byte_t) ((ChOff + _TXCMP1) >> 8); + ChP->TxCompare[2] = 0; + ChP->TxCompare[3] = 0; + out32(ChP->IndexAddr, ChP->TxCompare); + + ChP->TxReplace1[0] = (Byte_t) (ChOff + _TXREP1B1); + ChP->TxReplace1[1] = (Byte_t) ((ChOff + _TXREP1B1) >> 8); + ChP->TxReplace1[2] = 0; + ChP->TxReplace1[3] = 0; + out32(ChP->IndexAddr, ChP->TxReplace1); + + ChP->TxReplace2[0] = (Byte_t) (ChOff + _TXREP2); + ChP->TxReplace2[1] = (Byte_t) ((ChOff + _TXREP2) >> 8); + ChP->TxReplace2[2] = 0; + ChP->TxReplace2[3] = 0; + out32(ChP->IndexAddr, ChP->TxReplace2); + + ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; + ChP->TxFIFO = ChOff + _TX_FIFO; + + sOutB(ChP->Cmd, (Byte_t) ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ + sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Tx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ + sOutW(ChP->IndexData, 0); + ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; + ChP->RxFIFO = ChOff + _RX_FIFO; + + sOutB(ChP->Cmd, (Byte_t) ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ + sOutB(ChP->Cmd, (Byte_t) ChanNum); /* remove reset Rx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ + sOutW(ChP->IndexData, 0); + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ + sOutW(ChP->IndexData, 0); + ChP->TxPrioCnt = ChOff + _TXP_CNT; + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioCnt); + sOutB(ChP->IndexData, 0); + ChP->TxPrioPtr = ChOff + _TXP_PNTR; + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxPrioPtr); + sOutB(ChP->IndexData, 0); + ChP->TxPrioBuf = ChOff + _TXP_BUF; + sEnRxProcessor(ChP); /* start the Rx processor */ + + return 1; +} +#endif +/*************************************************************************** +Function: sStopRxProcessor +Purpose: Stop the receive processor from processing a channel. +Call: sStopRxProcessor(ChP) + CHANNEL_T *ChP; Ptr to channel structure + +Comments: The receive processor can be started again with sStartRxProcessor(). + This function causes the receive processor to skip over the + stopped channel. It does not stop it from processing other channels. + +Warnings: No context switches are allowed while executing this function. + + Do not leave the receive processor stopped for more than one + character time. + + After calling this function a delay of 4 uS is required to ensure + that the receive processor is no longer processing this channel. +*/ +#if 0 +static void sStopRxProcessor(CHANNEL_T * ChP) +{ + Byte_t R[4]; + + R[0] = ChP->R[0]; + R[1] = ChP->R[1]; + R[2] = 0x0a; + R[3] = ChP->R[3]; + out32(ChP->IndexAddr, R); +} +#endif +/*************************************************************************** +Function: sFlushRxFIFO +Purpose: Flush the Rx FIFO +Call: sFlushRxFIFO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: void +Comments: To prevent data from being enqueued or dequeued in the Tx FIFO + while it is being flushed the receive processor is stopped + and the transmitter is disabled. After these operations a + 4 uS delay is done before clearing the pointers to allow + the receive processor to stop. These items are handled inside + this function. +Warnings: No context switches are allowed while executing this function. +*/ +#if 0 +static void sFlushRxFIFO(CHANNEL_T * ChP) +{ + int i; + Byte_t Ch; /* channel number within AIOP */ + int RxFIFOEnabled; /* 1 if Rx FIFO enabled */ + + if (sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ + return; /* don't need to flush */ + + RxFIFOEnabled = 0; + if (ChP->R[0x32] == 0x08) { /* Rx FIFO is enabled */ + RxFIFOEnabled = 1; + sDisRxFIFO(ChP); /* disable it */ + for (i = 0; i < 2000 / 200; i++) /* delay 2 uS to allow proc to disable FIFO */ + sInB(ChP->IntChan); /* depends on bus i/o timing */ + } + sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ + Ch = (Byte_t) sGetChanNum(ChP); + sOutB(ChP->Cmd, Ch | RESRXFCNT); /* apply reset Rx FIFO count */ + sOutB(ChP->Cmd, Ch); /* remove reset Rx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs); /* clear Rx out ptr */ + sOutW(ChP->IndexData, 0); + sOutW((WordIO_t) ChP->IndexAddr, ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ + sOutW(ChP->IndexData, 0); + if (RxFIFOEnabled) + sEnRxFIFO(ChP); /* enable Rx FIFO */ +} +#endif +/*************************************************************************** +Function: sFlushTxFIFO +Purpose: Flush the Tx FIFO +Call: sFlushTxFIFO(ChP) + CHANNEL_T *ChP; Ptr to channel structure +Return: void +Comments: To prevent data from being enqueued or dequeued in the Tx FIFO + while it is being flushed the receive processor is stopped + and the transmitter is disabled. After these operations a + 4 uS delay is done before clearing the pointers to allow + the receive processor to stop. These items are handled inside + this function. +Warnings: No context switches are allowed while executing this function. +*/ +#if 0 +static void sFlushTxFIFO(CHANNEL_T * ChP) +{ + int i; + Byte_t Ch; /* channel number within AIOP */ + int TxEnabled; /* 1 if transmitter enabled */ + + if (sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ + return; /* don't need to flush */ + + TxEnabled = 0; + if (ChP->TxControl[3] & TX_ENABLE) { + TxEnabled = 1; + sDisTransmit(ChP); /* disable transmitter */ + } + sStopRxProcessor(ChP); /* stop Rx processor */ + for (i = 0; i < 4000 / 200; i++) /* delay 4 uS to allow proc to stop */ + sInB(ChP->IntChan); /* depends on bus i/o timing */ + Ch = (Byte_t) sGetChanNum(ChP); + sOutB(ChP->Cmd, Ch | RESTXFCNT); /* apply reset Tx FIFO count */ + sOutB(ChP->Cmd, Ch); /* remove reset Tx FIFO count */ + sOutW((WordIO_t) ChP->IndexAddr, ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ + sOutW(ChP->IndexData, 0); + if (TxEnabled) + sEnTransmit(ChP); /* enable transmitter */ + sStartRxProcessor(ChP); /* restart Rx processor */ +} +#endif +/*************************************************************************** +Function: sWriteTxPrioByte +Purpose: Write a byte of priority transmit data to a channel +Call: sWriteTxPrioByte(ChP,Data) + CHANNEL_T *ChP; Ptr to channel structure + Byte_t Data; The transmit data byte + +Return: int: 1 if the bytes is successfully written, otherwise 0. + +Comments: The priority byte is transmitted before any data in the Tx FIFO. + +Warnings: No context switches are allowed while executing this function. +*/ +static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data) +{ + Byte_t DWBuf[4]; /* buffer for double word writes */ + Word_t *WordPtr; /* must be far because Win SS != DS */ + register DWordIO_t IndexAddr; + + if (sGetTxCnt(ChP) > 1) { /* write it to Tx priority buffer */ + IndexAddr = ChP->IndexAddr; + sOutW((WordIO_t) IndexAddr, ChP->TxPrioCnt); /* get priority buffer status */ + if (sInB((ByteIO_t) ChP->IndexData) & PRI_PEND) /* priority buffer busy */ + return (0); /* nothing sent */ + + WordPtr = (Word_t *) (&DWBuf[0]); + *WordPtr = ChP->TxPrioBuf; /* data byte address */ + + DWBuf[2] = Data; /* data byte value */ + out32(IndexAddr, DWBuf); /* write it out */ + + *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ + + DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ + DWBuf[3] = 0; /* priority buffer pointer */ + out32(IndexAddr, DWBuf); /* write it out */ + } else { /* write it to Tx FIFO */ + + sWriteTxByte(sGetTxRxDataIO(ChP), Data); + } + return (1); /* 1 byte sent */ +} + +/*************************************************************************** +Function: sEnInterrupts +Purpose: Enable one or more interrupts for a channel +Call: sEnInterrupts(ChP,Flags) + CHANNEL_T *ChP; Ptr to channel structure + Word_t Flags: Interrupt enable flags, can be any combination + of the following flags: + TXINT_EN: Interrupt on Tx FIFO empty + RXINT_EN: Interrupt on Rx FIFO at trigger level (see + sSetRxTrigger()) + SRCINT_EN: Interrupt on SRC (Special Rx Condition) + MCINT_EN: Interrupt on modem input change + CHANINT_EN: Allow channel interrupt signal to the AIOP's + Interrupt Channel Register. +Return: void +Comments: If an interrupt enable flag is set in Flags, that interrupt will be + enabled. If an interrupt enable flag is not set in Flags, that + interrupt will not be changed. Interrupts can be disabled with + function sDisInterrupts(). + + This function sets the appropriate bit for the channel in the AIOP's + Interrupt Mask Register if the CHANINT_EN flag is set. This allows + this channel's bit to be set in the AIOP's Interrupt Channel Register. + + Interrupts must also be globally enabled before channel interrupts + will be passed on to the host. This is done with function + sEnGlobalInt(). + + In some cases it may be desirable to disable interrupts globally but + enable channel interrupts. This would allow the global interrupt + status register to be used to determine which AIOPs need service. +*/ +#if 0 +static void sEnInterrupts(CHANNEL_T * ChP, Word_t Flags) +{ + Byte_t Mask; /* Interrupt Mask Register */ + + ChP->RxControl[2] |= + ((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); + + out32(ChP->IndexAddr, ChP->RxControl); + + ChP->TxControl[2] |= ((Byte_t) Flags & TXINT_EN); + + out32(ChP->IndexAddr, ChP->TxControl); + + if (Flags & CHANINT_EN) { + Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; + sOutB(ChP->IntMask, Mask); + } +} +#endif +/*************************************************************************** +Function: sDisInterrupts +Purpose: Disable one or more interrupts for a channel +Call: sDisInterrupts(ChP,Flags) + CHANNEL_T *ChP; Ptr to channel structure + Word_t Flags: Interrupt flags, can be any combination + of the following flags: + TXINT_EN: Interrupt on Tx FIFO empty + RXINT_EN: Interrupt on Rx FIFO at trigger level (see + sSetRxTrigger()) + SRCINT_EN: Interrupt on SRC (Special Rx Condition) + MCINT_EN: Interrupt on modem input change + CHANINT_EN: Disable channel interrupt signal to the + AIOP's Interrupt Channel Register. +Return: void +Comments: If an interrupt flag is set in Flags, that interrupt will be + disabled. If an interrupt flag is not set in Flags, that + interrupt will not be changed. Interrupts can be enabled with + function sEnInterrupts(). + + This function clears the appropriate bit for the channel in the AIOP's + Interrupt Mask Register if the CHANINT_EN flag is set. This blocks + this channel's bit from being set in the AIOP's Interrupt Channel + Register. +*/ +static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags) +{ + Byte_t Mask; /* Interrupt Mask Register */ + + ChP->RxControl[2] &= + ~((Byte_t) Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); + out32(ChP->IndexAddr, ChP->RxControl); + ChP->TxControl[2] &= ~((Byte_t) Flags & TXINT_EN); + out32(ChP->IndexAddr, ChP->TxControl); + + if (Flags & CHANINT_EN) { + Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; + sOutB(ChP->IntMask, Mask); + } +} + +static void sSetInterfaceMode(CHANNEL_T * ChP, Byte_t mode) +{ + sOutB(ChP->CtlP->AiopIO[2], (mode & 0x18) | ChP->ChanNum); +} + +/* + * Not an official SSCI function, but how to reset RocketModems. + * ISA bus version + */ +static void sModemReset(CONTROLLER_T * CtlP, int chan, int on) +{ + ByteIO_t addr; + Byte_t val; + + addr = CtlP->AiopIO[0] + 0x400; + val = sInB(CtlP->MReg3IO); + /* if AIOP[1] is not enabled, enable it */ + if ((val & 2) == 0) { + val = sInB(CtlP->MReg2IO); + sOutB(CtlP->MReg2IO, (val & 0xfc) | (1 & 0x03)); + sOutB(CtlP->MBaseIO, (unsigned char) (addr >> 6)); + } + + sEnAiop(CtlP, 1); + if (!on) + addr += 8; + sOutB(addr + chan, 0); /* apply or remove reset */ + sDisAiop(CtlP, 1); +} + +/* + * Not an official SSCI function, but how to reset RocketModems. + * PCI bus version + */ +static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) +{ + ByteIO_t addr; + + addr = CtlP->AiopIO[0] + 0x40; /* 2nd AIOP */ + if (!on) + addr += 8; + sOutB(addr + chan, 0); /* apply or remove reset */ +} +#if 0 +/* Resets the speaker controller on RocketModem II and III devices */ +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) +{ + ByteIO_t addr; + + /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ + if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { + addr = CtlP->AiopIO[0] + 0x4F; + sOutB(addr, 0); + } + + /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ + if ((model == MODEL_UPCI_RM3_8PORT) + || (model == MODEL_UPCI_RM3_4PORT)) { + addr = CtlP->AiopIO[0] + 0x88; + sOutB(addr, 0); + } +} +#endif +/* Returns the line number given the controller (board), aiop and channel number */ +static unsigned char GetLineNumber(int ctrl, int aiop, int ch) +{ + return lineNumbers[(ctrl << 5) | (aiop << 3) | ch]; +} + +/* + * Stores the line number associated with a given controller (board), aiop + * and channel number. + * Returns: The line number assigned + */ + #if 0 +static unsigned char SetLineNumber(int ctrl, int aiop, int ch) +{ + lineNumbers[(ctrl << 5) | (aiop << 3) | ch] = nextLineNumber++; + return (nextLineNumber - 1); +} + #endif |