#include <linux/gfp.h>
#include <linux/ioport.h>
#include <linux/mm.h>
-#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include "mxser_new.h"
-#define MXSER_VERSION "2.0"
+#define MXSER_VERSION "2.0.1" /* 1.9.15 */
#define MXSERMAJOR 174
#define MXSERCUMAJOR 175
#define MXSER_BOARDS 4 /* Max. boards */
#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */
#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD)
-#define MXSER_ISR_PASS_LIMIT 99999L
+#define MXSER_ISR_PASS_LIMIT 100
#define MXSER_ERR_IOADDR -1
#define MXSER_ERR_IRQ -2
};
MODULE_DEVICE_TABLE(pci, mxser_pcibrds);
+static int mxvar_baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+};
+static unsigned int mxvar_baud_table1[] = {
+ 0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400,
+ B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B921600
+};
+#define BAUD_TABLE_NO ARRAY_SIZE(mxvar_baud_table)
+
+#define B_SPEC B2000000
+
static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 };
static int ttymajor = MXSERMAJOR;
static int calloutmajor = MXSERCUMAJOR;
long realbaud;
int type; /* UART type */
int flags; /* defined in tty.h */
+ int speed;
int x_char; /* xon/xoff character */
int IER; /* Interrupt Enable Register */
static int mxser_set_baud(struct mxser_port *info, long newspd)
{
+ unsigned int i;
int quot = 0;
unsigned char cval;
int ret = 0;
- unsigned long flags;
if (!info->tty || !info->tty->termios)
return ret;
return 0;
info->realbaud = newspd;
- if (newspd == 134) {
- quot = (2 * info->baud_base / 269);
- } else if (newspd) {
- quot = info->baud_base / newspd;
- if (quot == 0)
- quot = 1;
+ for (i = 0; i < BAUD_TABLE_NO; i++)
+ if (newspd == mxvar_baud_table[i])
+ break;
+ if (i == BAUD_TABLE_NO) {
+ quot = info->baud_base / info->speed;
+ if (info->speed <= 0 || info->speed > info->max_baud)
+ quot = 0;
} else {
- quot = 0;
+ if (newspd == 134) {
+ quot = (2 * info->baud_base / 269);
+ } else if (newspd) {
+ quot = info->baud_base / newspd;
+ if (quot == 0)
+ quot = 1;
+ } else {
+ quot = 0;
+ }
}
info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base);
info->timeout += HZ / 50; /* Add .02 seconds of slop */
if (quot) {
- spin_lock_irqsave(&info->slock, flags);
info->MCR |= UART_MCR_DTR;
outb(info->MCR, info->ioaddr + UART_MCR);
- spin_unlock_irqrestore(&info->slock, flags);
} else {
- spin_lock_irqsave(&info->slock, flags);
info->MCR &= ~UART_MCR_DTR;
outb(info->MCR, info->ioaddr + UART_MCR);
- spin_unlock_irqrestore(&info->slock, flags);
return ret;
}
outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */
outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */
+ if (i == BAUD_TABLE_NO) {
+ quot = info->baud_base % info->speed;
+ quot *= 8;
+ if ((quot % info->speed) > (info->speed / 2)) {
+ quot /= info->speed;
+ quot++;
+ } else {
+ quot /= info->speed;
+ }
+ SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, quot);
+ } else
+ SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, 0);
return ret;
}
int ret = 0;
unsigned char status;
long baud;
- unsigned long flags;
if (!info->tty || !info->tty->termios)
return ret;
return ret;
if (mxser_set_baud_method[info->tty->index] == 0) {
- baud = tty_get_baud_rate(info->tty);
+ if ((cflag & CBAUD) == B_SPEC)
+ baud = info->speed;
+ else
+ baud = tty_get_baud_rate(info->tty);
mxser_set_baud(info, baud);
}
}
}
if (info->board->chip_flag) {
- spin_lock_irqsave(&info->slock, flags);
SET_MOXA_MUST_XON1_VALUE(info->ioaddr, START_CHAR(info->tty));
SET_MOXA_MUST_XOFF1_VALUE(info->ioaddr, STOP_CHAR(info->tty));
if (I_IXON(info->tty)) {
} else {
DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr);
}
- spin_unlock_irqrestore(&info->slock, flags);
}
/*
* and set the speed of the serial port
*/
- spin_unlock_irqrestore(&info->slock, flags);
mxser_change_speed(info, NULL);
-
info->flags |= ASYNC_INITIALIZED;
+ spin_unlock_irqrestore(&info->slock, flags);
+
return 0;
}
static int mxser_open(struct tty_struct *tty, struct file *filp)
{
struct mxser_port *info;
+ unsigned long flags;
int retval, line;
- /* initialize driver_data in case something fails */
- tty->driver_data = NULL;
-
line = tty->index;
if (line == MXSER_PORTS)
return 0;
/*
* Start up serial port
*/
+ spin_lock_irqsave(&info->slock, flags);
info->count++;
+ spin_unlock_irqrestore(&info->slock, flags);
retval = mxser_startup(info);
if (retval)
return retval;
struct serial_struct __user *new_info)
{
struct serial_struct new_serial;
+ unsigned long sl_flags;
unsigned int flags;
int retval = 0;
process_txrx_fifo(info);
if (info->flags & ASYNC_INITIALIZED) {
- if (flags != (info->flags & ASYNC_SPD_MASK))
+ if (flags != (info->flags & ASYNC_SPD_MASK)) {
+ spin_lock_irqsave(&info->slock, sl_flags);
mxser_change_speed(info, NULL);
+ spin_unlock_irqrestore(&info->slock, sl_flags);
+ }
} else
retval = mxser_startup(info);
struct serial_icounter_struct __user *p_cuser;
unsigned long templ;
unsigned long flags;
+ unsigned int i;
void __user *argp = (void __user *)arg;
int retval;
return 0;
}
+ if (cmd == MOXA_SET_SPECIAL_BAUD_RATE) {
+ int speed;
+
+ if (get_user(speed, (int __user *)argp))
+ return -EFAULT;
+ if (speed <= 0 || speed > info->max_baud)
+ return -EFAULT;
+ if (!info->tty || !info->tty->termios || !info->ioaddr)
+ return 0;
+ info->tty->termios->c_cflag &= ~(CBAUD | CBAUDEX);
+ for (i = 0; i < BAUD_TABLE_NO; i++)
+ if (speed == mxvar_baud_table[i])
+ break;
+ if (i == BAUD_TABLE_NO) {
+ info->tty->termios->c_cflag |= B_SPEC;
+ } else if (speed != 0)
+ info->tty->termios->c_cflag |= mxvar_baud_table1[i];
+
+ info->speed = speed;
+ spin_lock_irqsave(&info->slock, flags);
+ mxser_change_speed(info, NULL);
+ spin_unlock_irqrestore(&info->slock, flags);
+
+ return 0;
+ } else if (cmd == MOXA_GET_SPECIAL_BAUD_RATE) {
+ if (copy_to_user(argp, &info->speed, sizeof(int)))
+ return -EFAULT;
+ return 0;
+ }
+
if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && cmd != TIOCGICOUNT &&
test_bit(TTY_IO_ERROR, &tty->flags))
return -EIO;
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
- case TIOCMIWAIT: {
- DECLARE_WAITQUEUE(wait, current);
- int ret;
+ case TIOCMIWAIT:
spin_lock_irqsave(&info->slock, flags);
- cprev = info->icount; /* note the counters on entry */
+ cnow = info->icount; /* note the counters on entry */
spin_unlock_irqrestore(&info->slock, flags);
- add_wait_queue(&info->delta_msr_wait, &wait);
- while (1) {
+ wait_event_interruptible(info->delta_msr_wait, ({
+ cprev = cnow;
spin_lock_irqsave(&info->slock, flags);
cnow = info->icount; /* atomic copy */
spin_unlock_irqrestore(&info->slock, flags);
- set_current_state(TASK_INTERRUPTIBLE);
- if (((arg & TIOCM_RNG) &&
- (cnow.rng != cprev.rng)) ||
- ((arg & TIOCM_DSR) &&
- (cnow.dsr != cprev.dsr)) ||
- ((arg & TIOCM_CD) &&
- (cnow.dcd != cprev.dcd)) ||
- ((arg & TIOCM_CTS) &&
- (cnow.cts != cprev.cts))) {
- ret = 0;
- break;
- }
- /* see if a signal did it */
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- cprev = cnow;
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->delta_msr_wait, &wait);
+ ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts));
+ }));
break;
- }
- /* NOTREACHED */
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
long baud;
if (get_user(baud, (long __user *)argp))
return -EFAULT;
+ spin_lock_irqsave(&info->slock, flags);
mxser_set_baud(info, baud);
+ spin_unlock_irqrestore(&info->slock, flags);
return 0;
}
case MOXA_ASPP_GETBAUD:
if ((tty->termios->c_cflag != old_termios->c_cflag) ||
(RELEVANT_IFLAG(tty->termios->c_iflag) != RELEVANT_IFLAG(old_termios->c_iflag))) {
+ spin_lock_irqsave(&info->slock, flags);
mxser_change_speed(info, old_termios);
+ spin_unlock_irqrestore(&info->slock, flags);
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
} else if (*status & UART_LSR_OE) {
flag = TTY_OVERRUN;
port->icount.overrun++;
- }
+ } else
+ flag = TTY_BREAK;
}
tty_insert_flip_char(tty, ch, flag);
cnt++;
port->mon_data.rxcnt += cnt;
port->mon_data.up_rxcnt += cnt;
+ /*
+ * We are called from an interrupt context with &port->slock
+ * being held. Drop it temporarily in order to prevent
+ * recursive locking.
+ */
+ spin_unlock(&port->slock);
tty_flip_buffer_push(tty);
+ spin_lock(&port->slock);
}
static void mxser_transmit_chars(struct mxser_port *port)
struct mxser_board *brd = NULL;
struct mxser_port *port;
int max, irqbits, bits, msr;
- int pass_counter = 0;
- unsigned int int_cnt;
+ unsigned int int_cnt, pass_counter = 0;
int handled = IRQ_NONE;
for (i = 0; i < MXSER_BOARDS; i++)
if (brd == NULL)
goto irq_stop;
max = brd->info->nports;
- while (1) {
+ while (pass_counter++ < MXSER_ISR_PASS_LIMIT) {
irqbits = inb(brd->vector) & brd->vector_mask;
if (irqbits == brd->vector_mask)
break;
} while (int_cnt++ < MXSER_ISR_PASS_LIMIT);
spin_unlock(&port->slock);
}
- if (pass_counter++ > MXSER_ISR_PASS_LIMIT)
- break; /* Prevent infinite loops */
}
irq_stop:
#ifdef CONFIG_PCI
pci_release_region(pdev, 2);
pci_release_region(pdev, 3);
- pci_dev_put(pdev);
#endif
} else {
release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
info->normal_termios = mxvar_sdriver->init_termios;
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->delta_msr_wait);
+ info->speed = 9600;
memset(&info->mon_data, 0, sizeof(struct mxser_mon));
info->err_shadow = 0;
spin_lock_init(&info->slock);
brd->info->name, brd->irq);
/* We hold resources, we need to release them. */
mxser_release_res(brd, pdev, 0);
- return retval;
}
- return 0;
+ return retval;
}
static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
}
/* mxser_initbrd will hook ISR. */
- if (mxser_initbrd(brd, pdev) < 0)
- goto err_relvec;
+ retval = mxser_initbrd(brd, pdev);
+ if (retval)
+ goto err_null;
for (i = 0; i < brd->info->nports; i++)
tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev);
pci_set_drvdata(pdev, brd);
return 0;
-err_relvec:
- pci_release_region(pdev, 3);
err_relio:
pci_release_region(pdev, 2);
+err_null:
brd->info = NULL;
err:
return retval;
tty_unregister_device(mxvar_sdriver, brd->idx + i);
mxser_release_res(brd, pdev, 1);
+ brd->info = NULL;
}
static struct pci_driver mxser_driver = {