From a8f5cda067e2eeefe49fe386caf0f61fc5c825e0 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 30 Apr 2008 00:53:45 -0700 Subject: [PATCH] Char: moxa, rework open/close - add locking to open/close/hangup and ioctl (tiocm) - add pci hot-un-plug support (hangup on board remove, wait for openers) - cleanup block_till_ready - move close code common to close/hangup into separate function to be able to call it from open when hangup occurs while block_till_ready - let ldisc flush on tty layer, it will do it after we return Signed-off-by: Jiri Slaby Tested-by: Oyvind Aabling Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/moxa.c | 320 ++++++++++++++++++++++---------------------- 1 file changed, 158 insertions(+), 162 deletions(-) diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index abcc16eba44..af8077c25cb 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -134,13 +133,11 @@ struct moxa_port { int type; int close_delay; - int count; - int blocked_open; + unsigned int count; int asyncflags; int cflag; unsigned long statusflags; wait_queue_head_t open_wait; - struct completion close_wait; u8 DCDState; u8 lineCtrl; @@ -167,6 +164,7 @@ static int ttymajor = MOXAMAJOR; static struct mon_str moxaLog; static unsigned int moxaFuncTout = HZ / 2; static unsigned int moxaLowWaterChk; +static DEFINE_MUTEX(moxa_openlock); /* Variables for insmod */ #ifdef MODULE static unsigned long baseaddr[MAX_BOARDS]; @@ -209,8 +207,6 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); static void moxa_poll(unsigned long); static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); -static int moxa_block_till_ready(struct tty_struct *, struct file *, - struct moxa_port *); static void moxa_setup_empty_event(struct tty_struct *); static void moxa_shut_down(struct moxa_port *); /* @@ -280,7 +276,7 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, { struct moxa_port *ch = tty->driver_data; void __user *argp = (void __user *)arg; - int status; + int status, ret = 0; if (tty->index == MAX_PORTS) { if (cmd != MOXA_GETDATACOUNT && cmd != MOXA_GET_IOQUEUE && @@ -292,17 +288,19 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, switch (cmd) { case MOXA_GETDATACOUNT: moxaLog.tick = jiffies; - return copy_to_user(argp, &moxaLog, sizeof(moxaLog)) ? - -EFAULT : 0; + if (copy_to_user(argp, &moxaLog, sizeof(moxaLog))) + ret = -EFAULT; + break; case MOXA_FLUSH_QUEUE: MoxaPortFlushData(ch, arg); - return 0; + break; case MOXA_GET_IOQUEUE: { struct moxaq_str __user *argm = argp; struct moxaq_str tmp; struct moxa_port *p; unsigned int i, j; + mutex_lock(&moxa_openlock); for (i = 0; i < MAX_BOARDS; i++) { p = moxa_boards[i].ports; for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { @@ -311,23 +309,29 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, tmp.inq = MoxaPortRxQueue(p); tmp.outq = MoxaPortTxQueue(p); } - if (copy_to_user(argm, &tmp, sizeof(tmp))) + if (copy_to_user(argm, &tmp, sizeof(tmp))) { + mutex_unlock(&moxa_openlock); return -EFAULT; + } } } - return 0; + mutex_unlock(&moxa_openlock); + break; } case MOXA_GET_OQUEUE: status = MoxaPortTxQueue(ch); - return put_user(status, (unsigned long __user *)argp); + ret = put_user(status, (unsigned long __user *)argp); + break; case MOXA_GET_IQUEUE: status = MoxaPortRxQueue(ch); - return put_user(status, (unsigned long __user *)argp); + ret = put_user(status, (unsigned long __user *)argp); + break; case MOXA_GETMSTATUS: { struct mxser_mstatus __user *argm = argp; struct mxser_mstatus tmp; struct moxa_port *p; unsigned int i, j; + mutex_lock(&moxa_openlock); for (i = 0; i < MAX_BOARDS; i++) { p = moxa_boards[i].ports; for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { @@ -348,18 +352,29 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, else tmp.cflag = p->tty->termios->c_cflag; copy: - if (copy_to_user(argm, &tmp, sizeof(tmp))) + if (copy_to_user(argm, &tmp, sizeof(tmp))) { + mutex_unlock(&moxa_openlock); return -EFAULT; + } } } - return 0; + mutex_unlock(&moxa_openlock); + break; } case TIOCGSERIAL: - return moxa_get_serial_info(ch, argp); + mutex_lock(&moxa_openlock); + ret = moxa_get_serial_info(ch, argp); + mutex_unlock(&moxa_openlock); + break; case TIOCSSERIAL: - return moxa_set_serial_info(ch, argp); + mutex_lock(&moxa_openlock); + ret = moxa_set_serial_info(ch, argp); + mutex_unlock(&moxa_openlock); + break; + default: + ret = -ENOIOCTLCMD; } - return -ENOIOCTLCMD; + return ret; } static void moxa_break_ctl(struct tty_struct *tty, int state) @@ -817,7 +832,6 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) p->close_delay = 5 * HZ / 10; p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; init_waitqueue_head(&p->open_wait); - init_completion(&p->close_wait); } switch (brd->boardType) { @@ -861,9 +875,29 @@ err: static void moxa_board_deinit(struct moxa_board_conf *brd) { + unsigned int a, opened; + + mutex_lock(&moxa_openlock); spin_lock_bh(&moxa_lock); brd->ready = 0; spin_unlock_bh(&moxa_lock); + + /* pci hot-un-plug support */ + for (a = 0; a < brd->numPorts; a++) + if (brd->ports[a].asyncflags & ASYNC_INITIALIZED) + tty_hangup(brd->ports[a].tty); + while (1) { + opened = 0; + for (a = 0; a < brd->numPorts; a++) + if (brd->ports[a].asyncflags & ASYNC_INITIALIZED) + opened++; + mutex_unlock(&moxa_openlock); + if (!opened) + break; + msleep(50); + mutex_lock(&moxa_openlock); + } + iounmap(brd->basemem); brd->basemem = NULL; kfree(brd->ports); @@ -1061,6 +1095,49 @@ static void __exit moxa_exit(void) module_init(moxa_init); module_exit(moxa_exit); +static void moxa_close_port(struct moxa_port *ch) +{ + moxa_shut_down(ch); + MoxaPortFlushData(ch, 2); + ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; + ch->tty->driver_data = NULL; + ch->tty = NULL; +} + +static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, + struct moxa_port *ch) +{ + DEFINE_WAIT(wait); + int retval = 0; + u8 dcd; + + while (1) { + prepare_to_wait(&ch->open_wait, &wait, TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp)) { +#ifdef SERIAL_DO_RESTART + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + spin_lock_bh(&moxa_lock); + dcd = ch->DCDState; + spin_unlock_bh(&moxa_lock); + if (dcd) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + finish_wait(&ch->open_wait, &wait); + + return retval; +} + static int moxa_open(struct tty_struct *tty, struct file *filp) { struct moxa_board_conf *brd; @@ -1072,9 +1149,13 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) if (port == MAX_PORTS) { return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; } + if (mutex_lock_interruptible(&moxa_openlock)) + return -ERESTARTSYS; brd = &moxa_boards[port / MAX_PORTS_PER_BOARD]; - if (!brd->ready) + if (!brd->ready) { + mutex_unlock(&moxa_openlock); return -ENODEV; + } ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; ch->count++; @@ -1085,19 +1166,24 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) moxa_set_tty_param(tty, tty->termios); MoxaPortLineCtrl(ch, 1, 1); MoxaPortEnable(ch); + MoxaSetFifo(ch, ch->type == PORT_16550A); ch->asyncflags |= ASYNC_INITIALIZED; } - retval = moxa_block_till_ready(tty, filp, ch); + mutex_unlock(&moxa_openlock); - moxa_unthrottle(tty); - - if (ch->type == PORT_16550A) { - MoxaSetFifo(ch, 1); - } else { - MoxaSetFifo(ch, 0); - } + retval = 0; + if (!(filp->f_flags & O_NONBLOCK) && !C_CLOCAL(tty)) + retval = moxa_block_till_ready(tty, filp, ch); + mutex_lock(&moxa_openlock); + if (retval) { + if (ch->count) /* 0 means already hung up... */ + if (--ch->count == 0) + moxa_close_port(ch); + } else + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + mutex_unlock(&moxa_openlock); - return (retval); + return retval; } static void moxa_close(struct tty_struct *tty, struct file *filp) @@ -1106,18 +1192,14 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) int port; port = tty->index; - if (port == MAX_PORTS) { - return; - } - if (tty->driver_data == NULL) { + if (port == MAX_PORTS || tty_hung_up_p(filp)) return; - } - if (tty_hung_up_p(filp)) { - return; - } - ch = (struct moxa_port *) tty->driver_data; - if ((tty->count == 1) && (ch->count != 1)) { + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (ch == NULL) + goto unlock; + if (tty->count == 1 && ch->count != 1) { printk(KERN_WARNING "moxa_close: bad serial port count; " "tty->count is 1, ch->count is %d\n", ch->count); ch->count = 1; @@ -1127,33 +1209,18 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) "device=%s\n", tty->name); ch->count = 0; } - if (ch->count) { - return; - } - ch->asyncflags |= ASYNC_CLOSING; + if (ch->count) + goto unlock; ch->cflag = tty->termios->c_cflag; if (ch->asyncflags & ASYNC_INITIALIZED) { moxa_setup_empty_event(tty); tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ } - moxa_shut_down(ch); - MoxaPortFlushData(ch, 2); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - tty_ldisc_flush(tty); - - tty->closing = 0; - ch->tty = NULL; - if (ch->blocked_open) { - if (ch->close_delay) { - msleep_interruptible(jiffies_to_msecs(ch->close_delay)); - } - wake_up_interruptible(&ch->open_wait); - } - ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - complete_all(&ch->close_wait); + moxa_close_port(ch); +unlock: + mutex_unlock(&moxa_openlock); } static int moxa_write(struct tty_struct *tty, @@ -1249,11 +1316,15 @@ static void moxa_put_char(struct tty_struct *tty, unsigned char c) static int moxa_tiocmget(struct tty_struct *tty, struct file *file) { - struct moxa_port *ch = tty->driver_data; + struct moxa_port *ch; int flag = 0, dtr, rts; - if (!ch) + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (!ch) { + mutex_unlock(&moxa_openlock); return -EINVAL; + } MoxaPortGetLineOut(ch, &dtr, &rts); if (dtr) @@ -1267,19 +1338,24 @@ static int moxa_tiocmget(struct tty_struct *tty, struct file *file) flag |= TIOCM_DSR; if (dtr & 4) flag |= TIOCM_CD; + mutex_unlock(&moxa_openlock); return flag; } static int moxa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { - struct moxa_port *ch = tty->driver_data; + struct moxa_port *ch; int port; int dtr, rts; port = tty->index; - if (!ch) + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (!ch) { + mutex_unlock(&moxa_openlock); return -EINVAL; + } MoxaPortGetLineOut(ch, &dtr, &rts); if (set & TIOCM_RTS) @@ -1291,6 +1367,7 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file, if (clear & TIOCM_DTR) dtr = 0; MoxaPortLineCtrl(ch, dtr, rts); + mutex_unlock(&moxa_openlock); return 0; } @@ -1348,13 +1425,18 @@ static void moxa_start(struct tty_struct *tty) static void moxa_hangup(struct tty_struct *tty) { - struct moxa_port *ch = (struct moxa_port *) tty->driver_data; + struct moxa_port *ch; - moxa_flush_buffer(tty); - moxa_shut_down(ch); + mutex_lock(&moxa_openlock); + ch = tty->driver_data; + if (ch == NULL) { + mutex_unlock(&moxa_openlock); + return; + } ch->count = 0; - ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; - ch->tty = NULL; + moxa_close_port(ch); + mutex_unlock(&moxa_openlock); + wake_up_interruptible(&ch->open_wait); } @@ -1363,11 +1445,8 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) dcd = !!dcd; if ((dcd != p->DCDState) && p->tty && C_CLOCAL(p->tty)) { - if (!dcd) { + if (!dcd) tty_hangup(p->tty); - p->asyncflags &= ~ASYNC_NORMAL_ACTIVE; - } - wake_up_interruptible(&p->open_wait); } p->DCDState = dcd; } @@ -1499,91 +1578,6 @@ static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_term tty_encode_baud_rate(tty, baud, baud); } -static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, - struct moxa_port *ch) -{ - DECLARE_WAITQUEUE(wait,current); - int retval; - int do_clocal = C_CLOCAL(tty); - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || (ch->asyncflags & ASYNC_CLOSING)) { - if (ch->asyncflags & ASYNC_CLOSING) - wait_for_completion_interruptible(&ch->close_wait); -#ifdef SERIAL_DO_RESTART - if (ch->asyncflags & ASYNC_HUP_NOTIFY) - return (-EAGAIN); - else - return (-ERESTARTSYS); -#else - return (-EAGAIN); -#endif - } - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if (filp->f_flags & O_NONBLOCK) { - ch->asyncflags |= ASYNC_NORMAL_ACTIVE; - return (0); - } - /* - * Block waiting for the carrier detect and the line to become free - */ - retval = 0; - add_wait_queue(&ch->open_wait, &wait); - pr_debug("block_til_ready before block: ttys%d, count = %d\n", - tty->index, ch->count); - spin_lock_bh(&moxa_lock); - if (!tty_hung_up_p(filp)) - ch->count--; - ch->blocked_open++; - spin_unlock_bh(&moxa_lock); - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(ch->asyncflags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (ch->asyncflags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || - ch->DCDState)) - break; - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&ch->open_wait, &wait); - - spin_lock_bh(&moxa_lock); - if (!tty_hung_up_p(filp)) - ch->count++; - ch->blocked_open--; - spin_unlock_bh(&moxa_lock); - pr_debug("block_til_ready after blocking: ttys%d, count = %d\n", - tty->index, ch->count); - if (retval) - return (retval); - /* FIXME: review to see if we need to use set_bit on these */ - ch->asyncflags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - static void moxa_setup_empty_event(struct tty_struct *tty) { struct moxa_port *ch = tty->driver_data; @@ -1595,22 +1589,22 @@ static void moxa_setup_empty_event(struct tty_struct *tty) static void moxa_shut_down(struct moxa_port *ch) { - struct tty_struct *tp; + struct tty_struct *tp = ch->tty; if (!(ch->asyncflags & ASYNC_INITIALIZED)) return; - tp = ch->tty; - MoxaPortDisable(ch); /* * If we're a modem control device and HUPCL is on, drop RTS & DTR. */ - if (tp->termios->c_cflag & HUPCL) + if (C_HUPCL(tp)) MoxaPortLineCtrl(ch, 0, 0); + spin_lock_bh(&moxa_lock); ch->asyncflags &= ~ASYNC_INITIALIZED; + spin_unlock_bh(&moxa_lock); } /***************************************************************************** @@ -2029,7 +2023,9 @@ static int MoxaPortLineStatus(struct moxa_port *port) val &= 0x0B; if (val & 8) val |= 4; + spin_lock_bh(&moxa_lock); moxa_new_dcdstate(port, val & 8); + spin_unlock_bh(&moxa_lock); val &= 7; return val; } -- 2.20.1