diff options
67 files changed, 1876 insertions, 1956 deletions
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c index 678dd1d..292d4aa 100644 --- a/arch/arm/mach-omap2/serial.c +++ b/arch/arm/mach-omap2/serial.c @@ -245,14 +245,6 @@ void __init omap_serial_init_port(struct omap_board_data *bdata, omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate; omap_up.autosuspend_timeout = info->autosuspend_timeout; - /* Enable the MDR1 Errata i202 for OMAP2430/3xxx/44xx */ - if (!cpu_is_omap2420() && !cpu_is_ti816x()) - omap_up.errata |= UART_ERRATA_i202_MDR1_ACCESS; - - /* Enable DMA Mode Force Idle Errata i291 for omap34xx/3630 */ - if (cpu_is_omap34xx() || cpu_is_omap3630()) - omap_up.errata |= UART_ERRATA_i291_DMA_FORCEIDLE; - pdata = &omap_up; pdata_size = sizeof(struct omap_uart_port_info); diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index c00aadb..222182e 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/pda_power.h> @@ -52,6 +53,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTD, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index 330afdf..d0735c7 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/gpio_keys.h> @@ -55,6 +56,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTA, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, @@ -65,6 +67,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTC, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index a0184fb..20743bc 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/i2c.h> #include <linux/delay.h> #include <linux/input.h> @@ -48,6 +49,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { /* Memory and IRQ filled in before registration */ .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c index f6f5b6a..0a00183 100644 --- a/arch/arm/mach-tegra/board-trimslice.c +++ b/arch/arm/mach-tegra/board-trimslice.c @@ -22,6 +22,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/serial_8250.h> +#include <linux/of_serial.h> #include <linux/io.h> #include <linux/i2c.h> #include <linux/gpio.h> @@ -49,6 +50,7 @@ static struct plat_serial8250_port debug_uart_platform_data[] = { .irq = INT_UARTA, .flags = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE, .type = PORT_TEGRA, + .handle_break = tegra_serial_handle_break, .iotype = UPIO_MEM, .regshift = 2, .uartclk = 216000000, diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h index 9ff4444..1a52725 100644 --- a/arch/arm/plat-omap/include/plat/omap-serial.h +++ b/arch/arm/plat-omap/include/plat/omap-serial.h @@ -65,7 +65,6 @@ struct omap_uart_port_info { bool dma_enabled; /* To specify DMA Mode */ unsigned int uartclk; /* UART clock rate */ upf_t flags; /* UPF_* flags */ - u32 errata; unsigned int dma_rx_buf_size; unsigned int dma_rx_timeout; unsigned int autosuspend_timeout; diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index d9f5524..8c610fa 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -46,7 +46,6 @@ static DEFINE_MUTEX(isdn_mutex); static char *isdn_revision = "$Revision: 1.1.2.3 $"; extern char *isdn_net_revision; -extern char *isdn_tty_revision; #ifdef CONFIG_ISDN_PPP extern char *isdn_ppp_revision; #else @@ -2327,8 +2326,6 @@ static int __init isdn_init(void) dev->chanmap[i] = -1; dev->m_idx[i] = -1; strcpy(dev->num[i], "???"); - init_waitqueue_head(&dev->mdm.info[i].open_wait); - init_waitqueue_head(&dev->mdm.info[i].close_wait); } if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { printk(KERN_WARNING "isdn: Could not register control devices\n"); @@ -2353,8 +2350,6 @@ static int __init isdn_init(void) strcpy(tmprev, isdn_revision); printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev)); - strcpy(tmprev, isdn_tty_revision); - printk("%s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_net_revision); printk("%s/", isdn_getrev(tmprev)); strcpy(tmprev, isdn_ppp_revision); diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 3831abd..7bc5067 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1,5 +1,4 @@ -/* $Id: isdn_tty.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $ - * +/* * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). * * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) @@ -12,6 +11,7 @@ #undef ISDN_TTY_STAT_DEBUG #include <linux/isdn.h> +#include <linux/serial.h> /* ASYNC_* flags */ #include <linux/slab.h> #include <linux/delay.h> #include <linux/mutex.h> @@ -48,9 +48,6 @@ static int bit2si[8] = static int si2bit[8] = {4, 1, 4, 4, 4, 4, 4, 4}; -char *isdn_tty_revision = "$Revision: 1.1.2.3 $"; - - /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() * to stuff incoming data directly into a tty's flip-buffer. This * is done to speed up tty-receiving if the receive-queue is empty. @@ -68,49 +65,54 @@ isdn_tty_try_read(modem_info *info, struct sk_buff *skb) struct tty_struct *tty; char last; - if (info->online) { - if ((tty = info->tty)) { - if (info->mcr & UART_MCR_RTS) { - len = skb->len + if (!info->online) + return 0; + + tty = info->port.tty; + if (!tty) + return 0; + + if (!(info->mcr & UART_MCR_RTS)) + return 0; + + len = skb->len #ifdef CONFIG_ISDN_AUDIO - + ISDN_AUDIO_SKB_DLECOUNT(skb) + + ISDN_AUDIO_SKB_DLECOUNT(skb) #endif - ; + ; + + c = tty_buffer_request_room(tty, len); + if (c < len) + return 0; - c = tty_buffer_request_room(tty, len); - if (c >= len) { -#ifdef CONFIG_ISDN_AUDIO - if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { - int l = skb->len; - unsigned char *dp = skb->data; - while (--l) { - if (*dp == DLE) - tty_insert_flip_char(tty, DLE, 0); - tty_insert_flip_char(tty, *dp++, 0); - } - if (*dp == DLE) - tty_insert_flip_char(tty, DLE, 0); - last = *dp; - } else { -#endif - if (len > 1) - tty_insert_flip_string(tty, skb->data, len - 1); - last = skb->data[len - 1]; #ifdef CONFIG_ISDN_AUDIO - } + if (ISDN_AUDIO_SKB_DLECOUNT(skb)) { + int l = skb->len; + unsigned char *dp = skb->data; + while (--l) { + if (*dp == DLE) + tty_insert_flip_char(tty, DLE, 0); + tty_insert_flip_char(tty, *dp++, 0); + } + if (*dp == DLE) + tty_insert_flip_char(tty, DLE, 0); + last = *dp; + } else { #endif - if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP) - tty_insert_flip_char(tty, last, 0xFF); - else - tty_insert_flip_char(tty, last, TTY_NORMAL); - tty_flip_buffer_push(tty); - kfree_skb(skb); - return 1; - } - } - } + if (len > 1) + tty_insert_flip_string(tty, skb->data, len - 1); + last = skb->data[len - 1]; +#ifdef CONFIG_ISDN_AUDIO } - return 0; +#endif + if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP) + tty_insert_flip_char(tty, last, 0xFF); + else + tty_insert_flip_char(tty, last, TTY_NORMAL); + tty_flip_buffer_push(tty); + kfree_skb(skb); + + return 1; } /* isdn_tty_readmodem() is called periodically from within timer-interrupt. @@ -128,35 +130,39 @@ isdn_tty_readmodem(void) modem_info *info; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { - if ((midx = dev->m_idx[i]) >= 0) { - info = &dev->mdm.info[midx]; - if (info->online) { - r = 0; + midx = dev->m_idx[i]; + if (midx < 0) + continue; + + info = &dev->mdm.info[midx]; + if (!info->online) + continue; + + r = 0; #ifdef CONFIG_ISDN_AUDIO - isdn_audio_eval_dtmf(info); - if ((info->vonline & 1) && (info->emu.vpar[1])) - isdn_audio_eval_silence(info); -#endif - if ((tty = info->tty)) { - if (info->mcr & UART_MCR_RTS) { - /* CISCO AsyncPPP Hack */ - if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP)) - r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 0); - else - r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 1); - if (r) - tty_flip_buffer_push(tty); - } else - r = 1; - } else - r = 1; - if (r) { - info->rcvsched = 0; - resched = 1; - } else - info->rcvsched = 1; - } - } + isdn_audio_eval_dtmf(info); + if ((info->vonline & 1) && (info->emu.vpar[1])) + isdn_audio_eval_silence(info); +#endif + tty = info->port.tty; + if (tty) { + if (info->mcr & UART_MCR_RTS) { + /* CISCO AsyncPPP Hack */ + if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP)) + r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 0); + else + r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 1); + if (r) + tty_flip_buffer_push(tty); + } else + r = 1; + } else + r = 1; + if (r) { + info->rcvsched = 0; + resched = 1; + } else + info->rcvsched = 1; } if (!resched) isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0); @@ -294,7 +300,7 @@ isdn_tty_tint(modem_info *info) len = skb->len; if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, info->isdn_channel, 1, skb)) == len) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; info->send_outstanding++; info->msr &= ~UART_MSR_CTS; info->lsr &= ~UART_LSR_TEMT; @@ -327,7 +333,7 @@ isdn_tty_countDLE(unsigned char *buf, int len) static int isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len) { - unsigned char *p = &info->xmit_buf[info->xmit_count]; + unsigned char *p = &info->port.xmit_buf[info->xmit_count]; int count = 0; while (len > 0) { @@ -471,7 +477,7 @@ isdn_tty_senddown(modem_info *info) return; } skb_reserve(skb, skb_res); - memcpy(skb_put(skb, buflen), info->xmit_buf, buflen); + memcpy(skb_put(skb, buflen), info->port.xmit_buf, buflen); info->xmit_count = 0; #ifdef CONFIG_ISDN_AUDIO if (info->vonline & 2) { @@ -699,7 +705,7 @@ isdn_tty_modem_hup(modem_info *info, int local) printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); #endif info->rcvsched = 0; - isdn_tty_flush_buffer(info->tty); + isdn_tty_flush_buffer(info->port.tty); if (info->online) { info->last_lhup = local; info->online = 0; @@ -997,20 +1003,21 @@ isdn_tty_paranoia_check(modem_info *info, char *name, const char *routine) static void isdn_tty_change_speed(modem_info *info) { + struct tty_port *port = &info->port; uint cflag, cval, quot; int i; - if (!info->tty || !info->tty->termios) + if (!port->tty || !port->tty->termios) return; - cflag = info->tty->termios->c_cflag; + cflag = port->tty->termios->c_cflag; quot = i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) - info->tty->termios->c_cflag &= ~CBAUDEX; + port->tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } @@ -1040,20 +1047,20 @@ isdn_tty_change_speed(modem_info *info) /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) { - info->flags |= ISDN_ASYNC_CTS_FLOW; + port->flags |= ASYNC_CTS_FLOW; } else - info->flags &= ~ISDN_ASYNC_CTS_FLOW; + port->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->flags &= ~ISDN_ASYNC_CHECK_CD; + port->flags &= ~ASYNC_CHECK_CD; else { - info->flags |= ISDN_ASYNC_CHECK_CD; + port->flags |= ASYNC_CHECK_CD; } } static int isdn_tty_startup(modem_info *info) { - if (info->flags & ISDN_ASYNC_INITIALIZED) + if (info->port.flags & ASYNC_INITIALIZED) return 0; isdn_lock_drivers(); #ifdef ISDN_DEBUG_MODEM_OPEN @@ -1063,14 +1070,14 @@ isdn_tty_startup(modem_info *info) * Now, initialize the UART */ info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); /* * and set the speed of the serial port */ isdn_tty_change_speed(info); - info->flags |= ISDN_ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; info->msr |= (UART_MSR_DSR | UART_MSR_CTS); info->send_outstanding = 0; return 0; @@ -1083,14 +1090,14 @@ isdn_tty_startup(modem_info *info) static void isdn_tty_shutdown(modem_info *info) { - if (!(info->flags & ISDN_ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line); #endif isdn_unlock_drivers(); info->msr &= ~UART_MSR_RI; - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0); @@ -1100,10 +1107,10 @@ isdn_tty_shutdown(modem_info *info) isdn_tty_modem_hup(info, 1); } } - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->flags &= ~ISDN_ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; } /* isdn_tty_write() is the main send-routine. It is called from the upper @@ -1146,7 +1153,7 @@ isdn_tty_write(struct tty_struct *tty, const u_char *buf, int count) isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, &(m->pluscount), &(m->lastplus)); - memcpy(&(info->xmit_buf[info->xmit_count]), buf, c); + memcpy(&info->port.xmit_buf[info->xmit_count], buf, c); #ifdef CONFIG_ISDN_AUDIO if (info->vonline) { int cc = isdn_tty_handleDLEdown(info, m, c); @@ -1478,107 +1485,6 @@ isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) * isdn_tty_open() and friends * ------------------------------------------------------------ */ -static int -isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info *info) -{ - DECLARE_WAITQUEUE(wait, NULL); - int do_clocal = 0; - int retval; - - /* - * 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) || - (info->flags & ISDN_ASYNC_CLOSING)) { - if (info->flags & ISDN_ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); -#ifdef MODEM_DO_RESTART - if (info->flags & ISDN_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) || - (tty->flags & (1 << TTY_IO_ERROR))) { - if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) - return -EBUSY; - info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; - return 0; - } - if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) { - if (info->normal_termios.c_cflag & CLOCAL) - do_clocal = 1; - } else { - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - } - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * isdn_tty_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef ISDN_DEBUG_MODEM_OPEN - printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n", - info->line, info->count); -#endif - if (!(tty_hung_up_p(filp))) - info->count--; - info->blocked_open++; - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ISDN_ASYNC_INITIALIZED)) { -#ifdef MODEM_DO_RESTART - if (info->flags & ISDN_ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && - !(info->flags & ISDN_ASYNC_CLOSING) && - (do_clocal || (info->msr & UART_MSR_DCD))) { - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef ISDN_DEBUG_MODEM_OPEN - printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n", - info->line, info->count); -#endif - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; -#ifdef ISDN_DEBUG_MODEM_OPEN - printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n", - info->line, info->count); -#endif - if (retval) - return retval; - info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; - return 0; -} /* * This routine is called whenever a serial port is opened. It @@ -1589,23 +1495,22 @@ isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * static int isdn_tty_open(struct tty_struct *tty, struct file *filp) { + struct tty_port *port; modem_info *info; int retval; info = &dev->mdm.info[tty->index]; if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open")) return -ENODEV; - if (!try_module_get(info->owner)) { - printk(KERN_WARNING "%s: cannot reserve module\n", __func__); - return -ENODEV; - } + port = &info->port; #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, - info->count); + port->count); #endif - info->count++; + port->count++; tty->driver_data = info; - info->tty = tty; + port->tty = tty; + tty->port = port; /* * Start up serial port */ @@ -1614,15 +1519,13 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp) #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open return after startup\n"); #endif - module_put(info->owner); return retval; } - retval = isdn_tty_block_til_ready(tty, filp, info); + retval = tty_port_block_til_ready(port, tty, filp); if (retval) { #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n"); #endif - module_put(info->owner); return retval; } #ifdef ISDN_DEBUG_MODEM_OPEN @@ -1639,6 +1542,7 @@ static void isdn_tty_close(struct tty_struct *tty, struct file *filp) { modem_info *info = (modem_info *) tty->driver_data; + struct tty_port *port = &info->port; ulong timeout; if (!info || isdn_tty_paranoia_check(info, tty->name, "isdn_tty_close")) @@ -1649,7 +1553,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) #endif return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (port->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -1658,30 +1562,21 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * serial port won't be shutdown. */ printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "info->count is %d\n", port->count); + port->count = 1; } - if (--info->count < 0) { + if (--port->count < 0) { printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, port->count); + port->count = 0; } - if (info->count) { + if (port->count) { #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n"); #endif - module_put(info->owner); return; } - info->flags |= ISDN_ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; + port->flags |= ASYNC_CLOSING; tty->closing = 1; /* @@ -1690,7 +1585,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * interrupt driver to stop checking the data ready bit in the * line status register. */ - if (info->flags & ISDN_ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter @@ -1708,16 +1603,10 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) isdn_tty_shutdown(info); isdn_tty_flush_buffer(tty); tty_ldisc_flush(tty); - info->tty = NULL; + port->tty = NULL; info->ncarrier = 0; - tty->closing = 0; - module_put(info->owner); - if (info->blocked_open) { - msleep_interruptible(500); - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + + tty_port_close_end(port, tty); #ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "isdn_tty_close normal exit\n"); #endif @@ -1730,14 +1619,15 @@ static void isdn_tty_hangup(struct tty_struct *tty) { modem_info *info = (modem_info *) tty->driver_data; + struct tty_port *port = &info->port; if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup")) return; isdn_tty_shutdown(info); - info->count = 0; - info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE); - info->tty = NULL; - wake_up_interruptible(&info->open_wait); + port->count = 0; + port->flags &= ~ASYNC_NORMAL_ACTIVE; + port->tty = NULL; + wake_up_interruptible(&port->open_wait); } /* This routine initializes all emulator-data. @@ -1864,6 +1754,16 @@ static const struct tty_operations modem_ops = { .tiocmset = isdn_tty_tiocmset, }; +static int isdn_tty_carrier_raised(struct tty_port *port) +{ + modem_info *info = container_of(port, modem_info, port); + return info->msr & UART_MSR_DCD; +} + +static const struct tty_port_operations isdn_tty_port_ops = { + .carrier_raised = isdn_tty_carrier_raised, +}; + int isdn_tty_modem_init(void) { @@ -1899,9 +1799,8 @@ isdn_tty_modem_init(void) goto err_unregister; } #endif -#ifdef MODULE - info->owner = THIS_MODULE; -#endif + tty_port_init(&info->port); + info->port.ops = &isdn_tty_port_ops; spin_lock_init(&info->readlock); sprintf(info->last_cause, "0000"); sprintf(info->last_num, "none"); @@ -1913,12 +1812,7 @@ isdn_tty_modem_init(void) isdn_tty_modem_reset_regs(info, 1); info->magic = ISDN_ASYNC_MAGIC; info->line = i; - info->tty = NULL; info->x_char = 0; - info->count = 0; - info->blocked_open = 0; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); info->isdn_driver = -1; info->isdn_channel = -1; info->drv_index = -1; @@ -1930,13 +1824,15 @@ isdn_tty_modem_init(void) #ifdef CONFIG_ISDN_AUDIO skb_queue_head_init(&info->dtmf_queue); #endif - if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) { + info->port.xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, + GFP_KERNEL); + if (!info->port.xmit_buf) { printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); retval = -ENOMEM; goto err_unregister; } /* Make room for T.70 header */ - info->xmit_buf += 4; + info->port.xmit_buf += 4; } return 0; err_unregister: @@ -1945,7 +1841,7 @@ err_unregister: #ifdef CONFIG_ISDN_TTY_FAX kfree(info->fax); #endif - kfree(info->xmit_buf - 4); + kfree(info->port.xmit_buf - 4); } tty_unregister_driver(m->tty_modem); err: @@ -1966,7 +1862,7 @@ isdn_tty_exit(void) #ifdef CONFIG_ISDN_TTY_FAX kfree(info->fax); #endif - kfree(info->xmit_buf - 4); + kfree(info->port.xmit_buf - 4); } tty_unregister_driver(dev->mdm.tty_modem); put_tty_driver(dev->mdm.tty_modem); @@ -2068,7 +1964,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { modem_info *info = &dev->mdm.info[i]; - if (info->count == 0) + if (info->port.count == 0) continue; if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */ (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ @@ -2076,12 +1972,12 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, - info->flags, info->isdn_driver, info->isdn_channel, - dev->usage[idx]); + info->port.flags, info->isdn_driver, + info->isdn_channel, dev->usage[idx]); #endif if ( #ifndef FIX_FILE_TRANSFER - (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && + (info->port.flags & ASYNC_NORMAL_ACTIVE) && #endif (info->isdn_driver == -1) && (info->isdn_channel == -1) && @@ -2120,8 +2016,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup) return (wret == 2) ? 3 : 0; } -#define TTY_IS_ACTIVE(info) \ - (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) +#define TTY_IS_ACTIVE(info) (info->port.flags & ASYNC_NORMAL_ACTIVE) int isdn_tty_stat_callback(int i, isdn_ctrl *c) @@ -2212,9 +2107,9 @@ isdn_tty_stat_callback(int i, isdn_ctrl *c) * for incoming call of this device when * DCD follow the state of incoming carrier */ - if (info->blocked_open && + if (info->port.blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD)) { - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } /* Schedule CONNECT-Message to any tty @@ -2222,7 +2117,8 @@ isdn_tty_stat_callback(int i, isdn_ctrl *c) * set DCD-bit of its modem-status. */ if (TTY_IS_ACTIVE(info) || - (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { + (info->port.blocked_open && + (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { info->msr |= UART_MSR_DCD; info->emu.charge = 0; if (info->dialing & 0xf) @@ -2339,8 +2235,8 @@ isdn_tty_at_cout(char *msg, modem_info *info) l = strlen(msg); spin_lock_irqsave(&info->readlock, flags); - tty = info->tty; - if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { + tty = info->port.tty; + if ((info->port.flags & ASYNC_CLOSING) || (!tty)) { spin_unlock_irqrestore(&info->readlock, flags); return; } @@ -2490,15 +2386,15 @@ isdn_tty_modem_result(int code, modem_info *info) case RESULT_NO_CARRIER: #ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", - (info->flags & ISDN_ASYNC_CLOSING), - (!info->tty)); + (info->port.flags & ASYNC_CLOSING), + (!info->port.tty)); #endif m->mdmreg[REG_RINGCNT] = 0; del_timer(&info->nc_timer); info->ncarrier = 0; - if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { + if ((info->port.flags & ASYNC_CLOSING) || (!info->port.tty)) return; - } + #ifdef CONFIG_ISDN_AUDIO if (info->vonline & 1) { #ifdef ISDN_DEBUG_MODEM_VOICE @@ -2629,14 +2525,11 @@ isdn_tty_modem_result(int code, modem_info *info) } } if (code == RESULT_NO_CARRIER) { - if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { + if ((info->port.flags & ASYNC_CLOSING) || (!info->port.tty)) return; - } - if ((info->flags & ISDN_ASYNC_CHECK_CD) && - (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && - (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) { - tty_hangup(info->tty); - } + + if (info->port.flags & ASYNC_CHECK_CD) + tty_hangup(info->port.tty); } } @@ -3803,19 +3696,19 @@ isdn_tty_modem_escape(void) int midx; for (i = 0; i < ISDN_MAX_CHANNELS; i++) - if (USG_MODEM(dev->usage[i])) - if ((midx = dev->m_idx[i]) >= 0) { - modem_info *info = &dev->mdm.info[midx]; - if (info->online) { - ton = 1; - if ((info->emu.pluscount == 3) && - time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) { - info->emu.pluscount = 0; - info->online = 0; - isdn_tty_modem_result(RESULT_OK, info); - } + if (USG_MODEM(dev->usage[i]) && (midx = dev->m_idx[i]) >= 0) { + modem_info *info = &dev->mdm.info[midx]; + if (info->online) { + ton = 1; + if ((info->emu.pluscount == 3) && + time_after(jiffies, + info->emu.lastplus + PLUSWAIT2)) { + info->emu.pluscount = 0; + info->online = 0; + isdn_tty_modem_result(RESULT_OK, info); } } + } isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton); } @@ -3873,15 +3766,14 @@ isdn_tty_carrier_timeout(void) for (i = 0; i < ISDN_MAX_CHANNELS; i++) { modem_info *info = &dev->mdm.info[i]; - if (info->dialing) { - if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) { - info->dialing = 0; - isdn_tty_modem_result(RESULT_NO_CARRIER, info); - isdn_tty_modem_hup(info, 1); - } - else - ton = 1; - } + if (!info->dialing) + continue; + if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) { + info->dialing = 0; + isdn_tty_modem_result(RESULT_NO_CARRIER, info); + isdn_tty_modem_hup(info, 1); + } else + ton = 1; } isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton); } diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 042c1a9..62f30b4 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -106,13 +106,6 @@ #define MAX_RX_URBS 2 -static inline struct hso_serial *get_serial_by_tty(struct tty_struct *tty) -{ - if (tty) - return tty->driver_data; - return NULL; -} - /*****************************************************************************/ /* Debugging functions */ /*****************************************************************************/ @@ -255,9 +248,8 @@ struct hso_serial { u8 dtr_state; unsigned tx_urb_used:1; + struct tty_port port; /* from usb_serial_port */ - struct tty_struct *tty; - int open_count; spinlock_t serial_lock; int (*write_data) (struct hso_serial *serial); @@ -1114,7 +1106,7 @@ static void hso_init_termios(struct ktermios *termios) static void _hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; struct ktermios *termios; if (!serial) { @@ -1190,7 +1182,7 @@ static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial) struct urb *urb; urb = serial->rx_urb[0]; - if (serial->open_count > 0) { + if (serial->port.count > 0) { count = put_rxbuf_data(urb, serial); if (count == -1) return; @@ -1226,7 +1218,7 @@ static void hso_std_serial_read_bulk_callback(struct urb *urb) DUMP1(urb->transfer_buffer, urb->actual_length); /* Anyone listening? */ - if (serial->open_count == 0) + if (serial->port.count == 0) return; if (status == 0) { @@ -1268,7 +1260,7 @@ static void hso_unthrottle_tasklet(struct hso_serial *serial) static void hso_unthrottle(struct tty_struct *tty) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; tasklet_hi_schedule(&serial->unthrottle_tasklet); } @@ -1304,15 +1296,12 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) kref_get(&serial->parent->ref); /* setup */ - spin_lock_irq(&serial->serial_lock); tty->driver_data = serial; - tty_kref_put(serial->tty); - serial->tty = tty_kref_get(tty); - spin_unlock_irq(&serial->serial_lock); + tty_port_tty_set(&serial->port, tty); /* check for port already opened, if not set the termios */ - serial->open_count++; - if (serial->open_count == 1) { + serial->port.count++; + if (serial->port.count == 1) { serial->rx_state = RX_IDLE; /* Force default termio settings */ _hso_serial_set_termios(tty, NULL); @@ -1324,7 +1313,7 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) result = hso_start_serial_device(serial->parent, GFP_KERNEL); if (result) { hso_stop_serial_device(serial->parent); - serial->open_count--; + serial->port.count--; kref_put(&serial->parent->ref, hso_serial_ref_free); } } else { @@ -1361,17 +1350,11 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) /* reset the rts and dtr */ /* do the actual close */ - serial->open_count--; + serial->port.count--; - if (serial->open_count <= 0) { - serial->open_count = 0; - spin_lock_irq(&serial->serial_lock); - if (serial->tty == tty) { - serial->tty->driver_data = NULL; - serial->tty = NULL; - tty_kref_put(tty); - } - spin_unlock_irq(&serial->serial_lock); + if (serial->port.count <= 0) { + serial->port.count = 0; + tty_port_tty_set(&serial->port, NULL); if (!usb_gone) hso_stop_serial_device(serial->parent); tasklet_kill(&serial->unthrottle_tasklet); @@ -1390,7 +1373,7 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) static int hso_serial_write(struct tty_struct *tty, const unsigned char *buf, int count) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; int space, tx_bytes; unsigned long flags; @@ -1422,7 +1405,7 @@ out: /* how much room is there for writing */ static int hso_serial_write_room(struct tty_struct *tty) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; int room; unsigned long flags; @@ -1437,7 +1420,7 @@ static int hso_serial_write_room(struct tty_struct *tty) /* setup the term */ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; unsigned long flags; if (old) @@ -1446,7 +1429,7 @@ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) /* the actual setup */ spin_lock_irqsave(&serial->serial_lock, flags); - if (serial->open_count) + if (serial->port.count) _hso_serial_set_termios(tty, old); else tty->termios = old; @@ -1458,7 +1441,7 @@ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) /* how many characters in the buffer */ static int hso_serial_chars_in_buffer(struct tty_struct *tty) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; int chars; unsigned long flags; @@ -1629,7 +1612,7 @@ static int hso_get_count(struct tty_struct *tty, struct serial_icounter_struct *icount) { struct uart_icount cnow; - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; struct hso_tiocmget *tiocmget = serial->tiocmget; memset(icount, 0, sizeof(struct serial_icounter_struct)); @@ -1659,7 +1642,7 @@ static int hso_get_count(struct tty_struct *tty, static int hso_serial_tiocmget(struct tty_struct *tty) { int retval; - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; struct hso_tiocmget *tiocmget; u16 UART_state_bitmap; @@ -1693,7 +1676,7 @@ static int hso_serial_tiocmset(struct tty_struct *tty, int val = 0; unsigned long flags; int if_num; - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; /* sanity check */ if (!serial) { @@ -1733,7 +1716,7 @@ static int hso_serial_tiocmset(struct tty_struct *tty, static int hso_serial_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { - struct hso_serial *serial = get_serial_by_tty(tty); + struct hso_serial *serial = tty->driver_data; int ret = 0; D4("IOCTL cmd: %d, arg: %ld", cmd, arg); @@ -1905,7 +1888,7 @@ static void intr_callback(struct urb *urb) D1("Pending read interrupt on port %d\n", i); spin_lock(&serial->serial_lock); if (serial->rx_state == RX_IDLE && - serial->open_count > 0) { + serial->port.count > 0) { /* Setup and send a ctrl req read on * port i */ if (!serial->rx_urb_filled[0]) { @@ -1954,14 +1937,13 @@ static void hso_std_serial_write_bulk_callback(struct urb *urb) spin_lock(&serial->serial_lock); serial->tx_urb_used = 0; - tty = tty_kref_get(serial->tty); spin_unlock(&serial->serial_lock); if (status) { handle_usb_error(status, __func__, serial->parent); - tty_kref_put(tty); return; } hso_put_activity(serial->parent); + tty = tty_port_tty_get(&serial->port); if (tty) { tty_wakeup(tty); tty_kref_put(tty); @@ -2001,7 +1983,6 @@ static void ctrl_callback(struct urb *urb) struct hso_serial *serial = urb->context; struct usb_ctrlrequest *req; int status = urb->status; - struct tty_struct *tty; /* sanity check */ if (!serial) @@ -2009,11 +1990,9 @@ static void ctrl_callback(struct urb *urb) spin_lock(&serial->serial_lock); serial->tx_urb_used = 0; - tty = tty_kref_get(serial->tty); spin_unlock(&serial->serial_lock); if (status) { handle_usb_error(status, __func__, serial->parent); - tty_kref_put(tty); return; } @@ -2031,13 +2010,15 @@ static void ctrl_callback(struct urb *urb) put_rxbuf_data_and_resubmit_ctrl_urb(serial); spin_unlock(&serial->serial_lock); } else { + struct tty_struct *tty = tty_port_tty_get(&serial->port); hso_put_activity(serial->parent); - if (tty) + if (tty) { tty_wakeup(tty); + tty_kref_put(tty); + } /* response to a write command */ hso_kick_transmit(serial); } - tty_kref_put(tty); } /* handle RX data for serial port */ @@ -2053,8 +2034,7 @@ static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial) return -2; } - /* All callers to put_rxbuf_data hold serial_lock */ - tty = tty_kref_get(serial->tty); + tty = tty_port_tty_get(&serial->port); /* Push data to tty */ if (tty) { @@ -2074,12 +2054,12 @@ static int put_rxbuf_data(struct urb *urb, struct hso_serial *serial) write_length_remaining -= curr_write_len; tty_flip_buffer_push(tty); } + tty_kref_put(tty); } if (write_length_remaining == 0) { serial->curr_rx_urb_offset = 0; serial->rx_urb_filled[hso_urb_to_index(serial, urb)] = 0; } - tty_kref_put(tty); return write_length_remaining; } @@ -2320,6 +2300,7 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, serial->minor = minor; serial->magic = HSO_SERIAL_MAGIC; spin_lock_init(&serial->serial_lock); + tty_port_init(&serial->port); serial->num_rx_urbs = num_urbs; /* RX, allocate urb and initialize */ @@ -3098,7 +3079,7 @@ static int hso_resume(struct usb_interface *iface) /* Start all serial ports */ for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == iface)) { - if (dev2ser(serial_table[i])->open_count) { + if (dev2ser(serial_table[i])->port.count) { result = hso_start_serial_device(serial_table[i], GFP_NOIO); hso_kick_transmit(dev2ser(serial_table[i])); @@ -3172,13 +3153,12 @@ static void hso_free_interface(struct usb_interface *interface) if (serial_table[i] && (serial_table[i]->interface == interface)) { hso_dev = dev2ser(serial_table[i]); - spin_lock_irq(&hso_dev->serial_lock); - tty = tty_kref_get(hso_dev->tty); - spin_unlock_irq(&hso_dev->serial_lock); - if (tty) + tty = tty_port_tty_get(&hso_dev->port); + if (tty) { tty_hangup(tty); + tty_kref_put(tty); + } mutex_lock(&hso_dev->parent->mutex); - tty_kref_put(tty); hso_dev->parent->usb_gone = 1; mutex_unlock(&hso_dev->parent->mutex); kref_put(&serial_table[i]->ref, hso_serial_ref_free); @@ -3313,7 +3293,6 @@ static int __init hso_init(void) return -ENOMEM; /* fill in all needed values */ - tty_drv->magic = TTY_DRIVER_MAGIC; tty_drv->driver_name = driver_name; tty_drv->name = tty_filename; @@ -3334,7 +3313,7 @@ static int __init hso_init(void) if (result) { printk(KERN_ERR "%s - tty_register_driver failed(%d)\n", __func__, result); - return result; + goto err_free_tty; } /* register this module as an usb driver */ @@ -3342,13 +3321,16 @@ static int __init hso_init(void) if (result) { printk(KERN_ERR "Could not register hso driver? error: %d\n", result); - /* cleanup serial interface */ - tty_unregister_driver(tty_drv); - return result; + goto err_unreg_tty; } /* done */ return 0; +err_unreg_tty: + tty_unregister_driver(tty_drv); +err_free_tty: + put_tty_driver(tty_drv); + return result; } static void __exit hso_exit(void) @@ -3356,6 +3338,7 @@ static void __exit hso_exit(void) printk(KERN_INFO "hso: unloaded\n"); tty_unregister_driver(tty_drv); + put_tty_driver(tty_drv); /* deregister the usb driver */ usb_deregister(&hso_driver); } diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 4f9f1dc..6c0116d 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -20,6 +20,7 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/reboot.h> +#include <linux/serial.h> /* ASYNC_* flags */ #include <linux/slab.h> #include <asm/ccwdev.h> #include <asm/cio.h> @@ -44,14 +45,11 @@ #define RAW3215_TIMEOUT HZ/10 /* time for delayed output */ #define RAW3215_FIXED 1 /* 3215 console device is not be freed */ -#define RAW3215_ACTIVE 2 /* set if the device is in use */ #define RAW3215_WORKING 4 /* set if a request is being worked on */ #define RAW3215_THROTTLED 8 /* set if reading is disabled */ #define RAW3215_STOPPED 16 /* set if writing is disabled */ -#define RAW3215_CLOSING 32 /* set while in close process */ #define RAW3215_TIMER_RUNS 64 /* set if the output delay timer is on */ #define RAW3215_FLUSHING 128 /* set to flush buffer (no delay) */ -#define RAW3215_FROZEN 256 /* set if 3215 is frozen for suspend */ #define TAB_STOP_SIZE 8 /* tab stop size */ @@ -76,6 +74,7 @@ struct raw3215_req { } __attribute__ ((aligned(8))); struct raw3215_info { + struct tty_port port; struct ccw_device *cdev; /* device for tty driver */ spinlock_t *lock; /* pointer to irq lock */ int flags; /* state flags */ @@ -84,7 +83,6 @@ struct raw3215_info { int head; /* first free byte in output buffer */ int count; /* number of bytes in output buffer */ int written; /* number of bytes in write requests */ - struct tty_struct *tty; /* pointer to tty structure if present */ struct raw3215_req *queued_read; /* pointer to queued read requests */ struct raw3215_req *queued_write;/* pointer to queued write requests */ struct tasklet_struct tlet; /* tasklet to invoke tty_wakeup */ @@ -293,7 +291,7 @@ static void raw3215_timeout(unsigned long __data) if (raw->flags & RAW3215_TIMER_RUNS) { del_timer(&raw->timer); raw->flags &= ~RAW3215_TIMER_RUNS; - if (!(raw->flags & RAW3215_FROZEN)) { + if (!(raw->port.flags & ASYNC_SUSPENDED)) { raw3215_mk_write_req(raw); raw3215_start_io(raw); } @@ -309,7 +307,8 @@ static void raw3215_timeout(unsigned long __data) */ static inline void raw3215_try_io(struct raw3215_info *raw) { - if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FROZEN)) + if (!(raw->port.flags & ASYNC_INITIALIZED) || + (raw->port.flags & ASYNC_SUSPENDED)) return; if (raw->queued_read != NULL) raw3215_start_io(raw); @@ -324,10 +323,7 @@ static inline void raw3215_try_io(struct raw3215_info *raw) } } else if (!(raw->flags & RAW3215_TIMER_RUNS)) { /* delay small writes */ - init_timer(&raw->timer); raw->timer.expires = RAW3215_TIMEOUT + jiffies; - raw->timer.data = (unsigned long) raw; - raw->timer.function = raw3215_timeout; add_timer(&raw->timer); raw->flags |= RAW3215_TIMER_RUNS; } @@ -340,17 +336,21 @@ static inline void raw3215_try_io(struct raw3215_info *raw) static void raw3215_wakeup(unsigned long data) { struct raw3215_info *raw = (struct raw3215_info *) data; - tty_wakeup(raw->tty); + struct tty_struct *tty; + + tty = tty_port_tty_get(&raw->port); + tty_wakeup(tty); + tty_kref_put(tty); } /* * Try to start the next IO and wake up processes waiting on the tty. */ -static void raw3215_next_io(struct raw3215_info *raw) +static void raw3215_next_io(struct raw3215_info *raw, struct tty_struct *tty) { raw3215_mk_write_req(raw); raw3215_try_io(raw); - if (raw->tty && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) + if (tty && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) tasklet_schedule(&raw->tlet); } @@ -368,10 +368,11 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, raw = dev_get_drvdata(&cdev->dev); req = (struct raw3215_req *) intparm; + tty = tty_port_tty_get(&raw->port); cstat = irb->scsw.cmd.cstat; dstat = irb->scsw.cmd.dstat; if (cstat != 0) - raw3215_next_io(raw); + raw3215_next_io(raw, tty); if (dstat & 0x01) { /* we got a unit exception */ dstat &= ~0x01; /* we can ignore it */ } @@ -381,13 +382,13 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, break; /* Attention interrupt, someone hit the enter key */ raw3215_mk_read_req(raw); - raw3215_next_io(raw); + raw3215_next_io(raw, tty); break; case 0x08: case 0x0C: /* Channel end interrupt. */ if ((raw = req->info) == NULL) - return; /* That shouldn't happen ... */ + goto put_tty; /* That shouldn't happen ... */ if (req->type == RAW3215_READ) { /* store residual count, then wait for device end */ req->residual = irb->scsw.cmd.count; @@ -397,11 +398,10 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, case 0x04: /* Device end interrupt. */ if ((raw = req->info) == NULL) - return; /* That shouldn't happen ... */ - if (req->type == RAW3215_READ && raw->tty != NULL) { + goto put_tty; /* That shouldn't happen ... */ + if (req->type == RAW3215_READ && tty != NULL) { unsigned int cchar; - tty = raw->tty; count = 160 - req->residual; EBCASC(raw->inbuf, count); cchar = ctrlchar_handle(raw->inbuf, count, tty); @@ -411,7 +411,7 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, case CTRLCHAR_CTRL: tty_insert_flip_char(tty, cchar, TTY_NORMAL); - tty_flip_buffer_push(raw->tty); + tty_flip_buffer_push(tty); break; case CTRLCHAR_NONE: @@ -424,7 +424,7 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, } else count -= 2; tty_insert_flip_string(tty, raw->inbuf, count); - tty_flip_buffer_push(raw->tty); + tty_flip_buffer_push(tty); break; } } else if (req->type == RAW3215_WRITE) { @@ -439,7 +439,7 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, raw->queued_read == NULL) { wake_up_interruptible(&raw->empty_wait); } - raw3215_next_io(raw); + raw3215_next_io(raw, tty); break; default: /* Strange interrupt, I'll do my best to clean up */ @@ -451,9 +451,10 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, raw->flags &= ~RAW3215_WORKING; raw3215_free_req(req); } - raw3215_next_io(raw); + raw3215_next_io(raw, tty); } - return; +put_tty: + tty_kref_put(tty); } /* @@ -487,7 +488,7 @@ static void raw3215_make_room(struct raw3215_info *raw, unsigned int length) /* While console is frozen for suspend we have no other * choice but to drop message from the buffer to make * room for even more messages. */ - if (raw->flags & RAW3215_FROZEN) { + if (raw->port.flags & ASYNC_SUSPENDED) { raw3215_drop_line(raw); continue; } @@ -609,10 +610,10 @@ static int raw3215_startup(struct raw3215_info *raw) { unsigned long flags; - if (raw->flags & RAW3215_ACTIVE) + if (raw->port.flags & ASYNC_INITIALIZED) return 0; raw->line_pos = 0; - raw->flags |= RAW3215_ACTIVE; + raw->port.flags |= ASYNC_INITIALIZED; spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_try_io(raw); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -628,14 +629,15 @@ static void raw3215_shutdown(struct raw3215_info *raw) DECLARE_WAITQUEUE(wait, current); unsigned long flags; - if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FIXED)) + if (!(raw->port.flags & ASYNC_INITIALIZED) || + (raw->flags & RAW3215_FIXED)) return; /* Wait for outstanding requests, then free irq */ spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); if ((raw->flags & RAW3215_WORKING) || raw->queued_write != NULL || raw->queued_read != NULL) { - raw->flags |= RAW3215_CLOSING; + raw->port.flags |= ASYNC_CLOSING; add_wait_queue(&raw->empty_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -643,11 +645,41 @@ static void raw3215_shutdown(struct raw3215_info *raw) spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); remove_wait_queue(&raw->empty_wait, &wait); set_current_state(TASK_RUNNING); - raw->flags &= ~(RAW3215_ACTIVE | RAW3215_CLOSING); + raw->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING); } spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } +static struct raw3215_info *raw3215_alloc_info(void) +{ + struct raw3215_info *info; + + info = kzalloc(sizeof(struct raw3215_info), GFP_KERNEL | GFP_DMA); + if (!info) + return NULL; + + info->buffer = kzalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); + info->inbuf = kzalloc(RAW3215_INBUF_SIZE, GFP_KERNEL | GFP_DMA); + if (!info->buffer || !info->inbuf) { + kfree(info); + return NULL; + } + + setup_timer(&info->timer, raw3215_timeout, (unsigned long)info); + init_waitqueue_head(&info->empty_wait); + tasklet_init(&info->tlet, raw3215_wakeup, (unsigned long)info); + tty_port_init(&info->port); + + return info; +} + +static void raw3215_free_info(struct raw3215_info *raw) +{ + kfree(raw->inbuf); + kfree(raw->buffer); + kfree(raw); +} + static int raw3215_probe (struct ccw_device *cdev) { struct raw3215_info *raw; @@ -656,11 +688,15 @@ static int raw3215_probe (struct ccw_device *cdev) /* Console is special. */ if (raw3215[0] && (raw3215[0] == dev_get_drvdata(&cdev->dev))) return 0; - raw = kmalloc(sizeof(struct raw3215_info) + - RAW3215_INBUF_SIZE, GFP_KERNEL|GFP_DMA); + + raw = raw3215_alloc_info(); if (raw == NULL) return -ENOMEM; + raw->cdev = cdev; + dev_set_drvdata(&cdev->dev, raw); + cdev->handler = raw3215_irq; + spin_lock(&raw3215_device_lock); for (line = 0; line < NR_3215; line++) { if (!raw3215[line]) { @@ -670,28 +706,10 @@ static int raw3215_probe (struct ccw_device *cdev) } spin_unlock(&raw3215_device_lock); if (line == NR_3215) { - kfree(raw); + raw3215_free_info(raw); return -ENODEV; } - raw->cdev = cdev; - raw->inbuf = (char *) raw + sizeof(struct raw3215_info); - memset(raw, 0, sizeof(struct raw3215_info)); - raw->buffer = kmalloc(RAW3215_BUFFER_SIZE, - GFP_KERNEL|GFP_DMA); - if (raw->buffer == NULL) { - spin_lock(&raw3215_device_lock); - raw3215[line] = NULL; - spin_unlock(&raw3215_device_lock); - kfree(raw); - return -ENOMEM; - } - init_waitqueue_head(&raw->empty_wait); - tasklet_init(&raw->tlet, raw3215_wakeup, (unsigned long) raw); - - dev_set_drvdata(&cdev->dev, raw); - cdev->handler = raw3215_irq; - return 0; } @@ -703,8 +721,7 @@ static void raw3215_remove (struct ccw_device *cdev) raw = dev_get_drvdata(&cdev->dev); if (raw) { dev_set_drvdata(&cdev->dev, NULL); - kfree(raw->buffer); - kfree(raw); + raw3215_free_info(raw); } } @@ -741,7 +758,7 @@ static int raw3215_pm_stop(struct ccw_device *cdev) raw = dev_get_drvdata(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_make_room(raw, RAW3215_BUFFER_SIZE); - raw->flags |= RAW3215_FROZEN; + raw->port.flags |= ASYNC_SUSPENDED; spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); return 0; } @@ -754,7 +771,7 @@ static int raw3215_pm_start(struct ccw_device *cdev) /* Allow I/O again and flush output buffer. */ raw = dev_get_drvdata(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); - raw->flags &= ~RAW3215_FROZEN; + raw->port.flags &= ~ASYNC_SUSPENDED; raw->flags |= RAW3215_FLUSHING; raw3215_try_io(raw); raw->flags &= ~RAW3215_FLUSHING; @@ -827,7 +844,7 @@ static void con3215_flush(void) unsigned long flags; raw = raw3215[0]; /* console 3215 is the first one */ - if (raw->flags & RAW3215_FROZEN) + if (raw->port.flags & ASYNC_SUSPENDED) /* The console is still frozen for suspend. */ if (ccw_device_force_console()) /* Forcing didn't work, no panic message .. */ @@ -897,23 +914,16 @@ static int __init con3215_init(void) if (IS_ERR(cdev)) return -ENODEV; - raw3215[0] = raw = (struct raw3215_info *) - kzalloc(sizeof(struct raw3215_info), GFP_KERNEL | GFP_DMA); - raw->buffer = kzalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); - raw->inbuf = kzalloc(RAW3215_INBUF_SIZE, GFP_KERNEL | GFP_DMA); + raw3215[0] = raw = raw3215_alloc_info(); raw->cdev = cdev; dev_set_drvdata(&cdev->dev, raw); cdev->handler = raw3215_irq; raw->flags |= RAW3215_FIXED; - init_waitqueue_head(&raw->empty_wait); - tasklet_init(&raw->tlet, raw3215_wakeup, (unsigned long) raw); /* Request the console irq */ if (raw3215_startup(raw) != 0) { - kfree(raw->inbuf); - kfree(raw->buffer); - kfree(raw); + raw3215_free_info(raw); raw3215[0] = NULL; return -ENODEV; } @@ -940,7 +950,7 @@ static int tty3215_open(struct tty_struct *tty, struct file * filp) return -ENODEV; tty->driver_data = raw; - raw->tty = tty; + tty_port_tty_set(&raw->port, tty); tty->low_latency = 0; /* don't use bottom half for pushing chars */ /* @@ -971,7 +981,7 @@ static void tty3215_close(struct tty_struct *tty, struct file * filp) raw3215_shutdown(raw); tasklet_kill(&raw->tlet); tty->closing = 0; - raw->tty = NULL; + tty_port_tty_set(&raw->port, NULL); } /* diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index 8065881..7ef9cfd 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -199,7 +199,7 @@ handle_diacr(struct kbd_data *kbd, unsigned int ch) if (ch == ' ' || ch == d) return d; - kbd_put_queue(kbd->tty, d); + kbd_put_queue(kbd->port, d); return ch; } @@ -221,7 +221,7 @@ k_self(struct kbd_data *kbd, unsigned char value) { if (kbd->diacr) value = handle_diacr(kbd, value); - kbd_put_queue(kbd->tty, value); + kbd_put_queue(kbd->port, value); } /* @@ -239,7 +239,7 @@ static void k_fn(struct kbd_data *kbd, unsigned char value) { if (kbd->func_table[value]) - kbd_puts_queue(kbd->tty, kbd->func_table[value]); + kbd_puts_queue(kbd->port, kbd->func_table[value]); } static void @@ -257,20 +257,20 @@ k_spec(struct kbd_data *kbd, unsigned char value) * but we need only 16 bits here */ static void -to_utf8(struct tty_struct *tty, ushort c) +to_utf8(struct tty_port *port, ushort c) { if (c < 0x80) /* 0******* */ - kbd_put_queue(tty, c); + kbd_put_queue(port, c); else if (c < 0x800) { /* 110***** 10****** */ - kbd_put_queue(tty, 0xc0 | (c >> 6)); - kbd_put_queue(tty, 0x80 | (c & 0x3f)); + kbd_put_queue(port, 0xc0 | (c >> 6)); + kbd_put_queue(port, 0x80 | (c & 0x3f)); } else { /* 1110**** 10****** 10****** */ - kbd_put_queue(tty, 0xe0 | (c >> 12)); - kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f)); - kbd_put_queue(tty, 0x80 | (c & 0x3f)); + kbd_put_queue(port, 0xe0 | (c >> 12)); + kbd_put_queue(port, 0x80 | ((c >> 6) & 0x3f)); + kbd_put_queue(port, 0x80 | (c & 0x3f)); } } @@ -283,7 +283,7 @@ kbd_keycode(struct kbd_data *kbd, unsigned int keycode) unsigned short keysym; unsigned char type, value; - if (!kbd || !kbd->tty) + if (!kbd) return; if (keycode >= 384) @@ -323,7 +323,7 @@ kbd_keycode(struct kbd_data *kbd, unsigned int keycode) #endif (*k_handler[type])(kbd, value); } else - to_utf8(kbd->tty, keysym); + to_utf8(kbd->port, keysym); } /* @@ -457,6 +457,7 @@ do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs, int kbd_ioctl(struct kbd_data *kbd, unsigned int cmd, unsigned long arg) { + struct tty_struct *tty; void __user *argp; unsigned int ct; int perm; @@ -467,7 +468,10 @@ int kbd_ioctl(struct kbd_data *kbd, unsigned int cmd, unsigned long arg) * To have permissions to do most of the vt ioctls, we either have * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. */ - perm = current->signal->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG); + tty = tty_port_tty_get(kbd->port); + /* FIXME this test is pretty racy */ + perm = current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG); + tty_kref_put(tty); switch (cmd) { case KDGKBTYPE: return put_user(KB_101, (char __user *)argp); diff --git a/drivers/s390/char/keyboard.h b/drivers/s390/char/keyboard.h index 7e736aa..f682f4e 100644 --- a/drivers/s390/char/keyboard.h +++ b/drivers/s390/char/keyboard.h @@ -21,7 +21,7 @@ typedef void (fn_handler_fn)(struct kbd_data *); */ struct kbd_data { - struct tty_struct *tty; + struct tty_port *port; unsigned short **key_maps; char **func_table; fn_handler_fn **fn_handler; @@ -42,16 +42,24 @@ int kbd_ioctl(struct kbd_data *, unsigned int, unsigned long); * Helper Functions. */ static inline void -kbd_put_queue(struct tty_struct *tty, int ch) +kbd_put_queue(struct tty_port *port, int ch) { + struct tty_struct *tty = tty_port_tty_get(port); + if (!tty) + return; tty_insert_flip_char(tty, ch, 0); tty_schedule_flip(tty); + tty_kref_put(tty); } static inline void -kbd_puts_queue(struct tty_struct *tty, char *cp) +kbd_puts_queue(struct tty_port *port, char *cp) { + struct tty_struct *tty = tty_port_tty_get(port); + if (!tty) + return; while (*cp) tty_insert_flip_char(tty, *cp++, 0); tty_schedule_flip(tty); + tty_kref_put(tty); } diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 40a9d69..e66a75b 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -48,7 +48,7 @@ static struct sclp_buffer *sclp_ttybuf; /* Timer for delayed output of console messages. */ static struct timer_list sclp_tty_timer; -static struct tty_struct *sclp_tty; +static struct tty_port sclp_port; static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; static unsigned short int sclp_tty_chars_count; @@ -64,7 +64,7 @@ static int sclp_tty_columns = 80; static int sclp_tty_open(struct tty_struct *tty, struct file *filp) { - sclp_tty = tty; + tty_port_tty_set(&sclp_port, tty); tty->driver_data = NULL; tty->low_latency = 0; return 0; @@ -76,7 +76,7 @@ sclp_tty_close(struct tty_struct *tty, struct file *filp) { if (tty->count > 1) return; - sclp_tty = NULL; + tty_port_tty_set(&sclp_port, NULL); } /* @@ -108,6 +108,7 @@ sclp_tty_write_room (struct tty_struct *tty) static void sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) { + struct tty_struct *tty; unsigned long flags; void *page; @@ -126,8 +127,10 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) spin_unlock_irqrestore(&sclp_tty_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); /* check if the tty needs a wake up call */ - if (sclp_tty != NULL) { - tty_wakeup(sclp_tty); + tty = tty_port_tty_get(&sclp_port); + if (tty != NULL) { + tty_wakeup(tty); + tty_kref_put(tty); } } @@ -326,21 +329,22 @@ sclp_tty_flush_buffer(struct tty_struct *tty) static void sclp_tty_input(unsigned char* buf, unsigned int count) { + struct tty_struct *tty = tty_port_tty_get(&sclp_port); unsigned int cchar; /* * If this tty driver is currently closed * then throw the received input away. */ - if (sclp_tty == NULL) + if (tty == NULL) return; - cchar = ctrlchar_handle(buf, count, sclp_tty); + cchar = ctrlchar_handle(buf, count, tty); switch (cchar & CTRLCHAR_MASK) { case CTRLCHAR_SYSRQ: break; case CTRLCHAR_CTRL: - tty_insert_flip_char(sclp_tty, cchar, TTY_NORMAL); - tty_flip_buffer_push(sclp_tty); + tty_insert_flip_char(tty, cchar, TTY_NORMAL); + tty_flip_buffer_push(tty); break; case CTRLCHAR_NONE: /* send (normal) input to line discipline */ @@ -348,13 +352,14 @@ sclp_tty_input(unsigned char* buf, unsigned int count) (strncmp((const char *) buf + count - 2, "^n", 2) && strncmp((const char *) buf + count - 2, "\252n", 2))) { /* add the auto \n */ - tty_insert_flip_string(sclp_tty, buf, count); - tty_insert_flip_char(sclp_tty, '\n', TTY_NORMAL); + tty_insert_flip_string(tty, buf, count); + tty_insert_flip_char(tty, '\n', TTY_NORMAL); } else - tty_insert_flip_string(sclp_tty, buf, count - 2); - tty_flip_buffer_push(sclp_tty); + tty_insert_flip_string(tty, buf, count - 2); + tty_flip_buffer_push(tty); break; } + tty_kref_put(tty); } /* @@ -543,7 +548,7 @@ sclp_tty_init(void) sclp_tty_tolower = 1; } sclp_tty_chars_count = 0; - sclp_tty = NULL; + tty_port_init(&sclp_port); rc = sclp_register(&sclp_input_event); if (rc) { diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index b635472..edfc0fd 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -34,7 +34,6 @@ #define SCLP_VT220_DEVICE_NAME "ttysclp" #define SCLP_VT220_CONSOLE_NAME "ttyS" #define SCLP_VT220_CONSOLE_INDEX 1 /* console=ttyS1 */ -#define SCLP_VT220_BUF_SIZE 80 /* Representation of a single write request */ struct sclp_vt220_request { @@ -56,8 +55,7 @@ struct sclp_vt220_sccb { /* Structures and data needed to register tty driver */ static struct tty_driver *sclp_vt220_driver; -/* The tty_struct that the kernel associated with us */ -static struct tty_struct *sclp_vt220_tty; +static struct tty_port sclp_vt220_port; /* Lock to protect internal data from concurrent access */ static spinlock_t sclp_vt220_lock; @@ -116,6 +114,7 @@ static struct sclp_register sclp_vt220_register = { static void sclp_vt220_process_queue(struct sclp_vt220_request *request) { + struct tty_struct *tty; unsigned long flags; void *page; @@ -141,8 +140,10 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) if (request == NULL && sclp_vt220_flush_later) sclp_vt220_emit_current(); /* Check if the tty needs a wake up call */ - if (sclp_vt220_tty != NULL) { - tty_wakeup(sclp_vt220_tty); + tty = tty_port_tty_get(&sclp_vt220_port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); } } @@ -460,11 +461,12 @@ sclp_vt220_write(struct tty_struct *tty, const unsigned char *buf, int count) static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf) { + struct tty_struct *tty = tty_port_tty_get(&sclp_vt220_port); char *buffer; unsigned int count; /* Ignore input if device is not open */ - if (sclp_vt220_tty == NULL) + if (tty == NULL) return; buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header)); @@ -478,10 +480,11 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf) /* Send input to line discipline */ buffer++; count--; - tty_insert_flip_string(sclp_vt220_tty, buffer, count); - tty_flip_buffer_push(sclp_vt220_tty); + tty_insert_flip_string(tty, buffer, count); + tty_flip_buffer_push(tty); break; } + tty_kref_put(tty); } /* @@ -491,10 +494,7 @@ static int sclp_vt220_open(struct tty_struct *tty, struct file *filp) { if (tty->count == 1) { - sclp_vt220_tty = tty; - tty->driver_data = kmalloc(SCLP_VT220_BUF_SIZE, GFP_KERNEL); - if (tty->driver_data == NULL) - return -ENOMEM; + tty_port_tty_set(&sclp_vt220_port, tty); tty->low_latency = 0; if (!tty->winsize.ws_row && !tty->winsize.ws_col) { tty->winsize.ws_row = 24; @@ -510,11 +510,8 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp) static void sclp_vt220_close(struct tty_struct *tty, struct file *filp) { - if (tty->count == 1) { - sclp_vt220_tty = NULL; - kfree(tty->driver_data); - tty->driver_data = NULL; - } + if (tty->count == 1) + tty_port_tty_set(&sclp_vt220_port, NULL); } /* @@ -635,9 +632,9 @@ static int __init __sclp_vt220_init(int num_pages) INIT_LIST_HEAD(&sclp_vt220_empty); INIT_LIST_HEAD(&sclp_vt220_outqueue); init_timer(&sclp_vt220_timer); + tty_port_init(&sclp_vt220_port); sclp_vt220_current_request = NULL; sclp_vt220_buffered_chars = 0; - sclp_vt220_tty = NULL; sclp_vt220_flush_later = 0; /* Allocate pages for output buffering */ diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index b43445a..10ec690 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -61,7 +61,7 @@ struct tty3270_line { */ struct tty3270 { struct raw3270_view view; - struct tty_struct *tty; /* Pointer to tty structure */ + struct tty_port port; void **freemem_pages; /* Array of pages used for freemem. */ struct list_head freemem; /* List of free memory for strings. */ @@ -324,9 +324,8 @@ tty3270_blank_line(struct tty3270 *tp) static void tty3270_write_callback(struct raw3270_request *rq, void *data) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(rq->view, struct tty3270, view); - tp = (struct tty3270 *) rq->view; if (rq->rc != 0) { /* Write wasn't successful. Refresh all. */ tp->update_flags = TTY_UPDATE_ALL; @@ -450,10 +449,9 @@ tty3270_rcl_add(struct tty3270 *tp, char *input, int len) static void tty3270_rcl_backward(struct kbd_data *kbd) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); struct string *s; - tp = kbd->tty->driver_data; spin_lock_bh(&tp->view.lock); if (tp->inattr == TF_INPUT) { if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) @@ -478,9 +476,8 @@ tty3270_rcl_backward(struct kbd_data *kbd) static void tty3270_exit_tty(struct kbd_data *kbd) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); - tp = kbd->tty->driver_data; raw3270_deactivate_view(&tp->view); } @@ -490,10 +487,9 @@ tty3270_exit_tty(struct kbd_data *kbd) static void tty3270_scroll_forward(struct kbd_data *kbd) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); int nr_up; - tp = kbd->tty->driver_data; spin_lock_bh(&tp->view.lock); nr_up = tp->nr_up - tp->view.rows + 2; if (nr_up < 0) @@ -513,10 +509,9 @@ tty3270_scroll_forward(struct kbd_data *kbd) static void tty3270_scroll_backward(struct kbd_data *kbd) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); int nr_up; - tp = kbd->tty->driver_data; spin_lock_bh(&tp->view.lock); nr_up = tp->nr_up + tp->view.rows - 2; if (nr_up + tp->view.rows - 2 > tp->nr_lines) @@ -537,11 +532,10 @@ static void tty3270_read_tasklet(struct raw3270_request *rrq) { static char kreset_data = TW_KR; - struct tty3270 *tp; + struct tty3270 *tp = container_of(rrq->view, struct tty3270, view); char *input; int len; - tp = (struct tty3270 *) rrq->view; spin_lock_bh(&tp->view.lock); /* * Two AID keys are special: For 0x7d (enter) the input line @@ -577,13 +571,10 @@ tty3270_read_tasklet(struct raw3270_request *rrq) raw3270_request_add_data(tp->kreset, &kreset_data, 1); raw3270_start(&tp->view, tp->kreset); - /* Emit input string. */ - if (tp->tty) { - while (len-- > 0) - kbd_keycode(tp->kbd, *input++); - /* Emit keycode for AID byte. */ - kbd_keycode(tp->kbd, 256 + tp->input->string[0]); - } + while (len-- > 0) + kbd_keycode(tp->kbd, *input++); + /* Emit keycode for AID byte. */ + kbd_keycode(tp->kbd, 256 + tp->input->string[0]); raw3270_request_reset(rrq); xchg(&tp->read, rrq); @@ -596,9 +587,10 @@ tty3270_read_tasklet(struct raw3270_request *rrq) static void tty3270_read_callback(struct raw3270_request *rq, void *data) { + struct tty3270 *tp = container_of(rq->view, struct tty3270, view); raw3270_get_view(rq->view); /* Schedule tasklet to pass input to tty. */ - tasklet_schedule(&((struct tty3270 *) rq->view)->readlet); + tasklet_schedule(&tp->readlet); } /* @@ -635,9 +627,8 @@ tty3270_issue_read(struct tty3270 *tp, int lock) static int tty3270_activate(struct raw3270_view *view) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(view, struct tty3270, view); - tp = (struct tty3270 *) view; tp->update_flags = TTY_UPDATE_ALL; tty3270_set_timer(tp, 1); return 0; @@ -646,9 +637,8 @@ tty3270_activate(struct raw3270_view *view) static void tty3270_deactivate(struct raw3270_view *view) { - struct tty3270 *tp; + struct tty3270 *tp = container_of(view, struct tty3270, view); - tp = (struct tty3270 *) view; del_timer(&tp->timer); } @@ -690,6 +680,17 @@ tty3270_alloc_view(void) if (!tp->freemem_pages) goto out_tp; INIT_LIST_HEAD(&tp->freemem); + INIT_LIST_HEAD(&tp->lines); + INIT_LIST_HEAD(&tp->update); + INIT_LIST_HEAD(&tp->rcl_lines); + tp->rcl_max = 20; + tty_port_init(&tp->port); + setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, + (unsigned long) tp); + tasklet_init(&tp->readlet, + (void (*)(unsigned long)) tty3270_read_tasklet, + (unsigned long) tp->read); + for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { tp->freemem_pages[pages] = (void *) __get_free_pages(GFP_KERNEL|GFP_DMA, 0); @@ -794,16 +795,15 @@ tty3270_free_screen(struct tty3270 *tp) static void tty3270_release(struct raw3270_view *view) { - struct tty3270 *tp; - struct tty_struct *tty; + struct tty3270 *tp = container_of(view, struct tty3270, view); + struct tty_struct *tty = tty_port_tty_get(&tp->port); - tp = (struct tty3270 *) view; - tty = tp->tty; if (tty) { tty->driver_data = NULL; - tp->tty = tp->kbd->tty = NULL; + tty_port_tty_set(&tp->port, NULL); tty_hangup(tty); raw3270_put_view(&tp->view); + tty_kref_put(tty); } } @@ -813,8 +813,9 @@ tty3270_release(struct raw3270_view *view) static void tty3270_free(struct raw3270_view *view) { - tty3270_free_screen((struct tty3270 *) view); - tty3270_free_view((struct tty3270 *) view); + struct tty3270 *tp = container_of(view, struct tty3270, view); + tty3270_free_screen(tp); + tty3270_free_view(tp); } /* @@ -823,14 +824,13 @@ tty3270_free(struct raw3270_view *view) static void tty3270_del_views(void) { - struct tty3270 *tp; int i; for (i = 0; i < tty3270_max_index; i++) { - tp = (struct tty3270 *) + struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i + RAW3270_FIRSTMINOR); - if (!IS_ERR(tp)) - raw3270_del_view(&tp->view); + if (!IS_ERR(view)) + raw3270_del_view(view); } } @@ -848,22 +848,23 @@ static struct raw3270_fn tty3270_fn = { static int tty3270_open(struct tty_struct *tty, struct file * filp) { + struct raw3270_view *view; struct tty3270 *tp; int i, rc; if (tty->count > 1) return 0; /* Check if the tty3270 is already there. */ - tp = (struct tty3270 *) - raw3270_find_view(&tty3270_fn, + view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); - if (!IS_ERR(tp)) { + if (!IS_ERR(view)) { + tp = container_of(view, struct tty3270, view); tty->driver_data = tp; tty->winsize.ws_row = tp->view.rows - 2; tty->winsize.ws_col = tp->view.cols; tty->low_latency = 0; - tp->tty = tty; - tp->kbd->tty = tty; + /* why to reassign? */ + tty_port_tty_set(&tp->port, tty); tp->inattr = TF_INPUT; return 0; } @@ -871,7 +872,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp) tty3270_max_index = tty->index + 1; /* Quick exit if there is no device for tty->index. */ - if (PTR_ERR(tp) == -ENODEV) + if (PTR_ERR(view) == -ENODEV) return -ENODEV; /* Allocate tty3270 structure on first open. */ @@ -879,16 +880,6 @@ tty3270_open(struct tty_struct *tty, struct file * filp) if (IS_ERR(tp)) return PTR_ERR(tp); - INIT_LIST_HEAD(&tp->lines); - INIT_LIST_HEAD(&tp->update); - INIT_LIST_HEAD(&tp->rcl_lines); - tp->rcl_max = 20; - setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, - (unsigned long) tp); - tasklet_init(&tp->readlet, - (void (*)(unsigned long)) tty3270_read_tasklet, - (unsigned long) tp->read); - rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index + RAW3270_FIRSTMINOR); if (rc) { @@ -903,7 +894,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp) return rc; } - tp->tty = tty; + tty_port_tty_set(&tp->port, tty); tty->low_latency = 0; tty->driver_data = tp; tty->winsize.ws_row = tp->view.rows - 2; @@ -917,7 +908,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp) for (i = 0; i < tp->view.rows - 2; i++) tty3270_blank_line(tp); - tp->kbd->tty = tty; + tp->kbd->port = &tp->port; tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty; tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward; tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward; @@ -935,14 +926,13 @@ tty3270_open(struct tty_struct *tty, struct file * filp) static void tty3270_close(struct tty_struct *tty, struct file * filp) { - struct tty3270 *tp; + struct tty3270 *tp = tty->driver_data; if (tty->count > 1) return; - tp = (struct tty3270 *) tty->driver_data; if (tp) { tty->driver_data = NULL; - tp->tty = tp->kbd->tty = NULL; + tty_port_tty_set(&tp->port, NULL); raw3270_put_view(&tp->view); } } @@ -1391,7 +1381,7 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch) tty3270_lf(tp); break; case 'Z': /* Respond ID. */ - kbd_puts_queue(tp->tty, "\033[?6c"); + kbd_puts_queue(&tp->port, "\033[?6c"); break; case '7': /* Save cursor position. */ tp->saved_cx = tp->cx; @@ -1437,11 +1427,11 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch) tp->esc_state = ESnormal; if (ch == 'n' && !tp->esc_ques) { if (tp->esc_par[0] == 5) /* Status report. */ - kbd_puts_queue(tp->tty, "\033[0n"); + kbd_puts_queue(&tp->port, "\033[0n"); else if (tp->esc_par[0] == 6) { /* Cursor report. */ char buf[40]; sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); - kbd_puts_queue(tp->tty, buf); + kbd_puts_queue(&tp->port, buf); } return; } @@ -1513,12 +1503,13 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch) * String write routine for 3270 ttys */ static void -tty3270_do_write(struct tty3270 *tp, const unsigned char *buf, int count) +tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, + const unsigned char *buf, int count) { int i_msg, i; spin_lock_bh(&tp->view.lock); - for (i_msg = 0; !tp->tty->stopped && i_msg < count; i_msg++) { + for (i_msg = 0; !tty->stopped && i_msg < count; i_msg++) { if (tp->esc_state != 0) { /* Continue escape sequence. */ tty3270_escape_sequence(tp, buf[i_msg]); @@ -1595,10 +1586,10 @@ tty3270_write(struct tty_struct * tty, if (!tp) return 0; if (tp->char_count > 0) { - tty3270_do_write(tp, tp->char_buf, tp->char_count); + tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); tp->char_count = 0; } - tty3270_do_write(tp, buf, count); + tty3270_do_write(tp, tty, buf, count); return count; } @@ -1629,7 +1620,7 @@ tty3270_flush_chars(struct tty_struct *tty) if (!tp) return; if (tp->char_count > 0) { - tty3270_do_write(tp, tp->char_buf, tp->char_count); + tty3270_do_write(tp, tty, tp->char_buf, tp->char_count); tp->char_count = 0; } } diff --git a/drivers/staging/serial/68360serial.c b/drivers/staging/serial/68360serial.c index daf0b1d..23ee50e 100644 --- a/drivers/staging/serial/68360serial.c +++ b/drivers/staging/serial/68360serial.c @@ -1859,9 +1859,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, state->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 6cc4358..35819e3 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); - tty_lock(); + tty_lock(tty); tmp.line = tty->index; tmp.port = state->port; tmp.flags = state->tport.flags; @@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, tmp.close_delay = state->tport.close_delay; tmp.closing_wait = state->tport.closing_wait; tmp.custom_divisor = state->custom_divisor; - tty_unlock(); + tty_unlock(tty); if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0; @@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; - tty_lock(); + tty_lock(tty); change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) || new_serial.custom_divisor != state->custom_divisor; if (new_serial.irq || new_serial.port != state->port || new_serial.xmit_fifo_size != state->xmit_fifo_size) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) { - tty_unlock(); + tty_unlock(tty); return -EPERM; } port->flags = ((port->flags & ~ASYNC_USR_MASK) | @@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, } if (new_serial.baud_base < 9600) { - tty_unlock(); + tty_unlock(tty); return -EINVAL; } @@ -1116,7 +1116,7 @@ check_and_exit: } } else retval = startup(tty, state); - tty_unlock(); + tty_unlock(tty); return retval; } diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c index 946f799..61fc74f 100644 --- a/drivers/tty/bfin_jtag_comm.c +++ b/drivers/tty/bfin_jtag_comm.c @@ -62,9 +62,7 @@ static inline uint32_t bfin_write_emudat_chars(char a, char b, char c, char d) static struct tty_driver *bfin_jc_driver; static struct task_struct *bfin_jc_kthread; -static struct tty_struct * volatile bfin_jc_tty; -static unsigned long bfin_jc_count; -static DEFINE_MUTEX(bfin_jc_tty_mutex); +static struct tty_port port; static volatile struct circ_buf bfin_jc_write_buf; static int @@ -73,18 +71,21 @@ bfin_jc_emudat_manager(void *arg) uint32_t inbound_len = 0, outbound_len = 0; while (!kthread_should_stop()) { + struct tty_struct *tty = tty_port_tty_get(&port); /* no one left to give data to, so sleep */ - if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) { + if (tty == NULL && circ_empty(&bfin_jc_write_buf)) { pr_debug("waiting for readers\n"); __set_current_state(TASK_UNINTERRUPTIBLE); schedule(); __set_current_state(TASK_RUNNING); + continue; } /* no data available, so just chill */ if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) { pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n", inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head); + tty_kref_put(tty); if (inbound_len) schedule(); else @@ -94,9 +95,6 @@ bfin_jc_emudat_manager(void *arg) /* if incoming data is ready, eat it */ if (bfin_read_DBGSTAT() & EMUDIF) { - struct tty_struct *tty; - mutex_lock(&bfin_jc_tty_mutex); - tty = (struct tty_struct *)bfin_jc_tty; if (tty != NULL) { uint32_t emudat = bfin_read_emudat(); if (inbound_len == 0) { @@ -110,7 +108,6 @@ bfin_jc_emudat_manager(void *arg) tty_flip_buffer_push(tty); } } - mutex_unlock(&bfin_jc_tty_mutex); } /* if outgoing data is ready, post it */ @@ -120,7 +117,6 @@ bfin_jc_emudat_manager(void *arg) bfin_write_emudat(outbound_len); pr_debug("outgoing length: 0x%08x\n", outbound_len); } else { - struct tty_struct *tty; int tail = bfin_jc_write_buf.tail; size_t ate = (4 <= outbound_len ? 4 : outbound_len); uint32_t emudat = @@ -132,14 +128,12 @@ bfin_jc_emudat_manager(void *arg) ); bfin_jc_write_buf.tail += ate; outbound_len -= ate; - mutex_lock(&bfin_jc_tty_mutex); - tty = (struct tty_struct *)bfin_jc_tty; if (tty) tty_wakeup(tty); - mutex_unlock(&bfin_jc_tty_mutex); pr_debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate); } } + tty_kref_put(tty); } __set_current_state(TASK_RUNNING); @@ -149,24 +143,28 @@ bfin_jc_emudat_manager(void *arg) static int bfin_jc_open(struct tty_struct *tty, struct file *filp) { - mutex_lock(&bfin_jc_tty_mutex); - pr_debug("open %lu\n", bfin_jc_count); - ++bfin_jc_count; - bfin_jc_tty = tty; + unsigned long flags; + + spin_lock_irqsave(&port.lock, flags); + port.count++; + spin_unlock_irqrestore(&port.lock, flags); + tty_port_tty_set(&port, tty); wake_up_process(bfin_jc_kthread); - mutex_unlock(&bfin_jc_tty_mutex); return 0; } static void bfin_jc_close(struct tty_struct *tty, struct file *filp) { - mutex_lock(&bfin_jc_tty_mutex); - pr_debug("close %lu\n", bfin_jc_count); - if (--bfin_jc_count == 0) - bfin_jc_tty = NULL; + unsigned long flags; + bool last; + + spin_lock_irqsave(&port.lock, flags); + last = --port.count == 0; + spin_unlock_irqrestore(&port.lock, flags); + if (last) + tty_port_tty_set(&port, NULL); wake_up_process(bfin_jc_kthread); - mutex_unlock(&bfin_jc_tty_mutex); } /* XXX: we dont handle the put_char() case where we must handle count = 1 */ @@ -242,6 +240,8 @@ static int __init bfin_jc_init(void) { int ret; + tty_port_init(&port); + bfin_jc_kthread = kthread_create(bfin_jc_emudat_manager, NULL, DRV_NAME); if (IS_ERR(bfin_jc_kthread)) return PTR_ERR(bfin_jc_kthread); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index e61cabd..6984e1a 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->port.close_wait, + wait_event_interruptible_tty(tty, info->port.close_wait, !(info->port.flags & ASYNC_CLOSING)); return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 8880adf..2d691eb 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -107,7 +107,7 @@ static struct hvc_struct *hvc_get_by_index(int index) list_for_each_entry(hp, &hvc_structs, next) { spin_lock_irqsave(&hp->lock, flags); if (hp->index == index) { - kref_get(&hp->kref); + tty_port_get(&hp->port); spin_unlock_irqrestore(&hp->lock, flags); spin_unlock(&hvc_structs_lock); return hp; @@ -229,9 +229,9 @@ static int __init hvc_console_init(void) console_initcall(hvc_console_init); /* callback when the kboject ref count reaches zero. */ -static void destroy_hvc_struct(struct kref *kref) +static void hvc_port_destruct(struct tty_port *port) { - struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref); + struct hvc_struct *hp = container_of(port, struct hvc_struct, port); unsigned long flags; spin_lock(&hvc_structs_lock); @@ -264,7 +264,7 @@ int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) /* make sure no no tty has been registered in this index */ hp = hvc_get_by_index(index); if (hp) { - kref_put(&hp->kref, destroy_hvc_struct); + tty_port_put(&hp->port); return -1; } @@ -313,20 +313,17 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) if (!(hp = hvc_get_by_index(tty->index))) return -ENODEV; - spin_lock_irqsave(&hp->lock, flags); + spin_lock_irqsave(&hp->port.lock, flags); /* Check and then increment for fast path open. */ - if (hp->count++ > 0) { - tty_kref_get(tty); - spin_unlock_irqrestore(&hp->lock, flags); + if (hp->port.count++ > 0) { + spin_unlock_irqrestore(&hp->port.lock, flags); hvc_kick(); return 0; } /* else count == 0 */ + spin_unlock_irqrestore(&hp->port.lock, flags); tty->driver_data = hp; - - hp->tty = tty_kref_get(tty); - - spin_unlock_irqrestore(&hp->lock, flags); + tty_port_tty_set(&hp->port, tty); if (hp->ops->notifier_add) rc = hp->ops->notifier_add(hp, hp->data); @@ -338,12 +335,9 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) * tty fields and return the kref reference. */ if (rc) { - spin_lock_irqsave(&hp->lock, flags); - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); - tty_kref_put(tty); + tty_port_tty_set(&hp->port, NULL); tty->driver_data = NULL; - kref_put(&hp->kref, destroy_hvc_struct); + tty_port_put(&hp->port); printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); } /* Force wakeup of the polling thread */ @@ -370,12 +364,12 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) hp = tty->driver_data; - spin_lock_irqsave(&hp->lock, flags); + spin_lock_irqsave(&hp->port.lock, flags); - if (--hp->count == 0) { + if (--hp->port.count == 0) { + spin_unlock_irqrestore(&hp->port.lock, flags); /* We are done with the tty pointer now. */ - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); + tty_port_tty_set(&hp->port, NULL); if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); @@ -390,14 +384,13 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) */ tty_wait_until_sent_from_close(tty, HVC_CLOSE_WAIT); } else { - if (hp->count < 0) + if (hp->port.count < 0) printk(KERN_ERR "hvc_close %X: oops, count is %d\n", - hp->vtermno, hp->count); - spin_unlock_irqrestore(&hp->lock, flags); + hp->vtermno, hp->port.count); + spin_unlock_irqrestore(&hp->port.lock, flags); } - tty_kref_put(tty); - kref_put(&hp->kref, destroy_hvc_struct); + tty_port_put(&hp->port); } static void hvc_hangup(struct tty_struct *tty) @@ -412,32 +405,31 @@ static void hvc_hangup(struct tty_struct *tty) /* cancel pending tty resize work */ cancel_work_sync(&hp->tty_resize); - spin_lock_irqsave(&hp->lock, flags); + spin_lock_irqsave(&hp->port.lock, flags); /* * The N_TTY line discipline has problems such that in a close vs * open->hangup case this can be called after the final close so prevent * that from happening for now. */ - if (hp->count <= 0) { - spin_unlock_irqrestore(&hp->lock, flags); + if (hp->port.count <= 0) { + spin_unlock_irqrestore(&hp->port.lock, flags); return; } - temp_open_count = hp->count; - hp->count = 0; - hp->n_outbuf = 0; - hp->tty = NULL; + temp_open_count = hp->port.count; + hp->port.count = 0; + spin_unlock_irqrestore(&hp->port.lock, flags); + tty_port_tty_set(&hp->port, NULL); - spin_unlock_irqrestore(&hp->lock, flags); + hp->n_outbuf = 0; if (hp->ops->notifier_hangup) hp->ops->notifier_hangup(hp, hp->data); while(temp_open_count) { --temp_open_count; - tty_kref_put(tty); - kref_put(&hp->kref, destroy_hvc_struct); + tty_port_put(&hp->port); } } @@ -478,7 +470,8 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count if (!hp) return -EPIPE; - if (hp->count <= 0) + /* FIXME what's this (unprotected) check for? */ + if (hp->port.count <= 0) return -EIO; spin_lock_irqsave(&hp->lock, flags); @@ -526,13 +519,12 @@ static void hvc_set_winsz(struct work_struct *work) hp = container_of(work, struct hvc_struct, tty_resize); - spin_lock_irqsave(&hp->lock, hvc_flags); - if (!hp->tty) { - spin_unlock_irqrestore(&hp->lock, hvc_flags); + tty = tty_port_tty_get(&hp->port); + if (!tty) return; - } - ws = hp->ws; - tty = tty_kref_get(hp->tty); + + spin_lock_irqsave(&hp->lock, hvc_flags); + ws = hp->ws; spin_unlock_irqrestore(&hp->lock, hvc_flags); tty_do_resize(tty, &ws); @@ -601,7 +593,7 @@ int hvc_poll(struct hvc_struct *hp) } /* No tty attached, just skip */ - tty = tty_kref_get(hp->tty); + tty = tty_port_tty_get(&hp->port); if (tty == NULL) goto bail; @@ -681,8 +673,7 @@ int hvc_poll(struct hvc_struct *hp) tty_flip_buffer_push(tty); } - if (tty) - tty_kref_put(tty); + tty_kref_put(tty); return poll_mask; } @@ -817,6 +808,10 @@ static const struct tty_operations hvc_ops = { #endif }; +static const struct tty_port_operations hvc_port_ops = { + .destruct = hvc_port_destruct, +}; + struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, const struct hv_ops *ops, int outbuf_size) @@ -842,7 +837,8 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, hp->outbuf_size = outbuf_size; hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; - kref_init(&hp->kref); + tty_port_init(&hp->port); + hp->port.ops = &hvc_port_ops; INIT_WORK(&hp->tty_resize, hvc_set_winsz); spin_lock_init(&hp->lock); @@ -875,9 +871,9 @@ int hvc_remove(struct hvc_struct *hp) unsigned long flags; struct tty_struct *tty; - spin_lock_irqsave(&hp->lock, flags); - tty = tty_kref_get(hp->tty); + tty = tty_port_tty_get(&hp->port); + spin_lock_irqsave(&hp->lock, flags); if (hp->index < MAX_NR_HVC_CONSOLES) vtermnos[hp->index] = -1; @@ -891,7 +887,7 @@ int hvc_remove(struct hvc_struct *hp) * kref cause it to be removed, which will probably be the tty_vhangup * below. */ - kref_put(&hp->kref, destroy_hvc_struct); + tty_port_put(&hp->port); /* * This function call will auto chain call hvc_hangup. diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index c335a14..674d23c 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -46,10 +46,9 @@ #define HVC_ALLOC_TTY_ADAPTERS 8 struct hvc_struct { + struct tty_port port; spinlock_t lock; int index; - struct tty_struct *tty; - int count; int do_wakeup; char *outbuf; int outbuf_size; @@ -61,7 +60,6 @@ struct hvc_struct { struct winsize ws; struct work_struct tty_resize; struct list_head next; - struct kref kref; /* ref count & hvc_struct lifetime */ }; /* implemented by a low level driver */ diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 83d5c88..d3d91da 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -430,9 +430,9 @@ static int __devinit xencons_probe(struct xenbus_device *dev, if (devid == 0) return -ENODEV; - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) - goto error_nomem; + return -ENOMEM; dev_set_drvdata(&dev->dev, info); info->xbdev = dev; info->vtermno = xenbus_devid_to_vtermno(devid); diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 3436436..d56788c 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -261,6 +261,7 @@ static DEFINE_SPINLOCK(hvcs_pi_lock); /* One vty-server per hvcs_struct */ struct hvcs_struct { + struct tty_port port; spinlock_t lock; /* @@ -269,9 +270,6 @@ struct hvcs_struct { */ unsigned int index; - struct tty_struct *tty; - int open_count; - /* * Used to tell the driver kernel_thread what operations need to take * place upon this hvcs_struct instance. @@ -290,12 +288,11 @@ struct hvcs_struct { int chars_in_buffer; /* - * Any variable below the kref is valid before a tty is connected and + * Any variable below is valid before a tty is connected and * stays valid after the tty is disconnected. These shouldn't be * whacked until the kobject refcount reaches zero though some entries * may be changed via sysfs initiatives. */ - struct kref kref; /* ref count & hvcs_struct lifetime */ int connected; /* is the vty-server currently connected to a vty? */ uint32_t p_unit_address; /* partner unit address */ uint32_t p_partition_ID; /* partner partition ID */ @@ -304,9 +301,6 @@ struct hvcs_struct { struct vio_dev *vdev; }; -/* Required to back map a kref to its containing object */ -#define from_kref(k) container_of(k, struct hvcs_struct, kref) - static LIST_HEAD(hvcs_structs); static DEFINE_SPINLOCK(hvcs_structs_lock); static DEFINE_MUTEX(hvcs_init_mutex); @@ -422,7 +416,7 @@ static ssize_t hvcs_vterm_state_store(struct device *dev, struct device_attribut spin_lock_irqsave(&hvcsd->lock, flags); - if (hvcsd->open_count > 0) { + if (hvcsd->port.count > 0) { spin_unlock_irqrestore(&hvcsd->lock, flags); printk(KERN_INFO "HVCS: vterm state unchanged. " "The hvcs device node is still in use.\n"); @@ -564,7 +558,7 @@ static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance) static void hvcs_try_write(struct hvcs_struct *hvcsd) { uint32_t unit_address = hvcsd->vdev->unit_address; - struct tty_struct *tty = hvcsd->tty; + struct tty_struct *tty = hvcsd->port.tty; int sent; if (hvcsd->todo_mask & HVCS_TRY_WRITE) { @@ -602,7 +596,7 @@ static int hvcs_io(struct hvcs_struct *hvcsd) spin_lock_irqsave(&hvcsd->lock, flags); unit_address = hvcsd->vdev->unit_address; - tty = hvcsd->tty; + tty = hvcsd->port.tty; hvcs_try_write(hvcsd); @@ -701,10 +695,9 @@ static void hvcs_return_index(int index) hvcs_index_list[index] = -1; } -/* callback when the kref ref count reaches zero */ -static void destroy_hvcs_struct(struct kref *kref) +static void hvcs_destruct_port(struct tty_port *p) { - struct hvcs_struct *hvcsd = from_kref(kref); + struct hvcs_struct *hvcsd = container_of(p, struct hvcs_struct, port); struct vio_dev *vdev; unsigned long flags; @@ -741,6 +734,10 @@ static void destroy_hvcs_struct(struct kref *kref) kfree(hvcsd); } +static const struct tty_port_operations hvcs_port_ops = { + .destruct = hvcs_destruct_port, +}; + static int hvcs_get_index(void) { int i; @@ -789,10 +786,9 @@ static int __devinit hvcs_probe( if (!hvcsd) return -ENODEV; - + tty_port_init(&hvcsd->port); + hvcsd->port.ops = &hvcs_port_ops; spin_lock_init(&hvcsd->lock); - /* Automatically incs the refcount the first time */ - kref_init(&hvcsd->kref); hvcsd->vdev = dev; dev_set_drvdata(&dev->dev, hvcsd); @@ -852,7 +848,7 @@ static int __devexit hvcs_remove(struct vio_dev *dev) spin_lock_irqsave(&hvcsd->lock, flags); - tty = hvcsd->tty; + tty = hvcsd->port.tty; spin_unlock_irqrestore(&hvcsd->lock, flags); @@ -860,7 +856,7 @@ static int __devexit hvcs_remove(struct vio_dev *dev) * Let the last holder of this object cause it to be removed, which * would probably be tty_hangup below. */ - kref_put(&hvcsd->kref, destroy_hvcs_struct); + tty_port_put(&hvcsd->port); /* * The hangup is a scheduled function which will auto chain call @@ -1094,7 +1090,7 @@ static struct hvcs_struct *hvcs_get_by_index(int index) list_for_each_entry(hvcsd, &hvcs_structs, next) { spin_lock_irqsave(&hvcsd->lock, flags); if (hvcsd->index == index) { - kref_get(&hvcsd->kref); + tty_port_get(&hvcsd->port); spin_unlock_irqrestore(&hvcsd->lock, flags); spin_unlock(&hvcs_structs_lock); return hvcsd; @@ -1138,8 +1134,8 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) if ((retval = hvcs_partner_connect(hvcsd))) goto error_release; - hvcsd->open_count = 1; - hvcsd->tty = tty; + hvcsd->port.count = 1; + hvcsd->port.tty = tty; tty->driver_data = hvcsd; memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); @@ -1160,7 +1156,7 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) * and will grab the spinlock and free the connection if it fails. */ if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { - kref_put(&hvcsd->kref, destroy_hvcs_struct); + tty_port_put(&hvcsd->port); printk(KERN_WARNING "HVCS: enable device failed.\n"); return rc; } @@ -1171,8 +1167,8 @@ fast_open: hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); - kref_get(&hvcsd->kref); - hvcsd->open_count++; + tty_port_get(&hvcsd->port); + hvcsd->port.count++; hvcsd->todo_mask |= HVCS_SCHED_READ; spin_unlock_irqrestore(&hvcsd->lock, flags); @@ -1186,7 +1182,7 @@ open_success: error_release: spin_unlock_irqrestore(&hvcsd->lock, flags); - kref_put(&hvcsd->kref, destroy_hvcs_struct); + tty_port_put(&hvcsd->port); printk(KERN_WARNING "HVCS: partner connect failed.\n"); return retval; @@ -1216,7 +1212,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); - if (--hvcsd->open_count == 0) { + if (--hvcsd->port.count == 0) { vio_disable_interrupts(hvcsd->vdev); @@ -1225,7 +1221,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) * execute any operations on the TTY even though it is obligated * to deliver any pending I/O to the hypervisor. */ - hvcsd->tty = NULL; + hvcsd->port.tty = NULL; irq = hvcsd->vdev->irq; spin_unlock_irqrestore(&hvcsd->lock, flags); @@ -1240,16 +1236,16 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) tty->driver_data = NULL; free_irq(irq, hvcsd); - kref_put(&hvcsd->kref, destroy_hvcs_struct); + tty_port_put(&hvcsd->port); return; - } else if (hvcsd->open_count < 0) { + } else if (hvcsd->port.count < 0) { printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" " is missmanaged.\n", - hvcsd->vdev->unit_address, hvcsd->open_count); + hvcsd->vdev->unit_address, hvcsd->port.count); } spin_unlock_irqrestore(&hvcsd->lock, flags); - kref_put(&hvcsd->kref, destroy_hvcs_struct); + tty_port_put(&hvcsd->port); } static void hvcs_hangup(struct tty_struct * tty) @@ -1261,7 +1257,7 @@ static void hvcs_hangup(struct tty_struct * tty) spin_lock_irqsave(&hvcsd->lock, flags); /* Preserve this so that we know how many kref refs to put */ - temp_open_count = hvcsd->open_count; + temp_open_count = hvcsd->port.count; /* * Don't kref put inside the spinlock because the destruction @@ -1273,10 +1269,10 @@ static void hvcs_hangup(struct tty_struct * tty) hvcsd->todo_mask = 0; /* I don't think the tty needs the hvcs_struct pointer after a hangup */ - hvcsd->tty->driver_data = NULL; - hvcsd->tty = NULL; + tty->driver_data = NULL; + hvcsd->port.tty = NULL; - hvcsd->open_count = 0; + hvcsd->port.count = 0; /* This will drop any buffered data on the floor which is OK in a hangup * scenario. */ @@ -1301,7 +1297,7 @@ static void hvcs_hangup(struct tty_struct * tty) * NOTE: If this hangup was signaled from user space then the * final put will never happen. */ - kref_put(&hvcsd->kref, destroy_hvcs_struct); + tty_port_put(&hvcsd->port); } } @@ -1347,7 +1343,7 @@ static int hvcs_write(struct tty_struct *tty, * the middle of a write operation? This is a crummy place to do this * but we want to keep it all in the spinlock. */ - if (hvcsd->open_count <= 0) { + if (hvcsd->port.count <= 0) { spin_unlock_irqrestore(&hvcsd->lock, flags); return -ENODEV; } @@ -1421,7 +1417,7 @@ static int hvcs_write_room(struct tty_struct *tty) { struct hvcs_struct *hvcsd = tty->driver_data; - if (!hvcsd || hvcsd->open_count <= 0) + if (!hvcsd || hvcsd->port.count <= 0) return 0; return HVCS_BUFF_LEN - hvcsd->chars_in_buffer; diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index a7488b7..6f5bc49 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -69,14 +69,13 @@ #define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) struct hvsi_struct { + struct tty_port port; struct delayed_work writer; struct work_struct handshaker; wait_queue_head_t emptyq; /* woken when outbuf is emptied */ wait_queue_head_t stateq; /* woken when HVSI state changes */ spinlock_t lock; int index; - struct tty_struct *tty; - int count; uint8_t throttle_buf[128]; uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ /* inbuf is for packet reassembly. leave a little room for leftovers. */ @@ -237,7 +236,7 @@ static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) } static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, - struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) + struct tty_struct *tty, struct hvsi_struct **to_handshake) { struct hvsi_control *header = (struct hvsi_control *)packet; @@ -247,9 +246,8 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, /* CD went away; no more connection */ pr_debug("hvsi%i: CD dropped\n", hp->index); hp->mctrl &= TIOCM_CD; - /* If userland hasn't done an open(2) yet, hp->tty is NULL. */ - if (hp->tty && !(hp->tty->flags & CLOCAL)) - *to_hangup = hp->tty; + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); } break; case VSV_CLOSE_PROTOCOL: @@ -331,7 +329,8 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) } } -static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) +static void hvsi_insert_chars(struct hvsi_struct *hp, struct tty_struct *tty, + const char *buf, int len) { int i; @@ -347,7 +346,7 @@ static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) continue; } #endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(hp->tty, c, 0); + tty_insert_flip_char(tty, c, 0); } } @@ -360,7 +359,7 @@ static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) * revisited. */ #define TTY_THRESHOLD_THROTTLE 128 -static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, +static bool hvsi_recv_data(struct hvsi_struct *hp, struct tty_struct *tty, const uint8_t *packet) { const struct hvsi_header *header = (const struct hvsi_header *)packet; @@ -371,14 +370,14 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); if (datalen == 0) - return NULL; + return false; if (overflow > 0) { pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); datalen = TTY_THRESHOLD_THROTTLE; } - hvsi_insert_chars(hp, data, datalen); + hvsi_insert_chars(hp, tty, data, datalen); if (overflow > 0) { /* @@ -390,7 +389,7 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, hp->n_throttle = overflow; } - return hp->tty; + return true; } /* @@ -399,14 +398,13 @@ static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, * machine during console handshaking (in which case tty = NULL and we ignore * incoming data). */ -static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, - struct tty_struct **hangup, struct hvsi_struct **handshake) +static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, + struct hvsi_struct **handshake) { uint8_t *packet = hp->inbuf; int chunklen; + bool flip = false; - *flip = NULL; - *hangup = NULL; *handshake = NULL; chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); @@ -440,12 +438,12 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, case VS_DATA_PACKET_HEADER: if (!is_open(hp)) break; - if (hp->tty == NULL) + if (tty == NULL) break; /* no tty buffer to put data in */ - *flip = hvsi_recv_data(hp, packet); + flip = hvsi_recv_data(hp, tty, packet); break; case VS_CONTROL_PACKET_HEADER: - hvsi_recv_control(hp, packet, hangup, handshake); + hvsi_recv_control(hp, packet, tty, handshake); break; case VS_QUERY_RESPONSE_PACKET_HEADER: hvsi_recv_response(hp, packet); @@ -462,28 +460,26 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, packet += len_packet(packet); - if (*hangup || *handshake) { - pr_debug("%s: hangup or handshake\n", __func__); - /* - * we need to send the hangup now before receiving any more data. - * If we get "data, hangup, data", we can't deliver the second - * data before the hangup. - */ + if (*handshake) { + pr_debug("%s: handshake\n", __func__); break; } } compact_inbuf(hp, packet); + if (flip) + tty_flip_buffer_push(tty); + return 1; } -static void hvsi_send_overflow(struct hvsi_struct *hp) +static void hvsi_send_overflow(struct hvsi_struct *hp, struct tty_struct *tty) { pr_debug("%s: delivering %i bytes overflow\n", __func__, hp->n_throttle); - hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); + hvsi_insert_chars(hp, tty, hp->throttle_buf, hp->n_throttle); hp->n_throttle = 0; } @@ -494,35 +490,20 @@ static void hvsi_send_overflow(struct hvsi_struct *hp) static irqreturn_t hvsi_interrupt(int irq, void *arg) { struct hvsi_struct *hp = (struct hvsi_struct *)arg; - struct tty_struct *flip; - struct tty_struct *hangup; struct hvsi_struct *handshake; + struct tty_struct *tty; unsigned long flags; int again = 1; pr_debug("%s\n", __func__); + tty = tty_port_tty_get(&hp->port); + while (again) { spin_lock_irqsave(&hp->lock, flags); - again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); + again = hvsi_load_chunk(hp, tty, &handshake); spin_unlock_irqrestore(&hp->lock, flags); - /* - * we have to call tty_flip_buffer_push() and tty_hangup() outside our - * spinlock. But we also have to keep going until we've read all the - * available data. - */ - - if (flip) { - /* there was data put in the tty flip buffer */ - tty_flip_buffer_push(flip); - flip = NULL; - } - - if (hangup) { - tty_hangup(hangup); - } - if (handshake) { pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); schedule_work(&handshake->handshaker); @@ -530,18 +511,15 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg) } spin_lock_irqsave(&hp->lock, flags); - if (hp->tty && hp->n_throttle - && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { - /* we weren't hung up and we weren't throttled, so we can deliver the - * rest now */ - flip = hp->tty; - hvsi_send_overflow(hp); + if (tty && hp->n_throttle && !test_bit(TTY_THROTTLED, &tty->flags)) { + /* we weren't hung up and we weren't throttled, so we can + * deliver the rest now */ + hvsi_send_overflow(hp, tty); + tty_flip_buffer_push(tty); } spin_unlock_irqrestore(&hp->lock, flags); - if (flip) { - tty_flip_buffer_push(flip); - } + tty_kref_put(tty); return IRQ_HANDLED; } @@ -749,9 +727,9 @@ static int hvsi_open(struct tty_struct *tty, struct file *filp) if (hp->state == HVSI_FSP_DIED) return -EIO; + tty_port_tty_set(&hp->port, tty); spin_lock_irqsave(&hp->lock, flags); - hp->tty = tty; - hp->count++; + hp->port.count++; atomic_set(&hp->seqno, 0); h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); spin_unlock_irqrestore(&hp->lock, flags); @@ -808,8 +786,8 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&hp->lock, flags); - if (--hp->count == 0) { - hp->tty = NULL; + if (--hp->port.count == 0) { + tty_port_tty_set(&hp->port, NULL); hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ /* only close down connection if it is not the console */ @@ -841,9 +819,9 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&hp->lock, flags); } - } else if (hp->count < 0) + } else if (hp->port.count < 0) printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", - hp - hvsi_ports, hp->count); + hp - hvsi_ports, hp->port.count); spin_unlock_irqrestore(&hp->lock, flags); } @@ -855,12 +833,11 @@ static void hvsi_hangup(struct tty_struct *tty) pr_debug("%s\n", __func__); - spin_lock_irqsave(&hp->lock, flags); + tty_port_tty_set(&hp->port, NULL); - hp->count = 0; + spin_lock_irqsave(&hp->lock, flags); + hp->port.count = 0; hp->n_outbuf = 0; - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); } @@ -888,6 +865,7 @@ static void hvsi_write_worker(struct work_struct *work) { struct hvsi_struct *hp = container_of(work, struct hvsi_struct, writer.work); + struct tty_struct *tty; unsigned long flags; #ifdef DEBUG static long start_j = 0; @@ -921,7 +899,11 @@ static void hvsi_write_worker(struct work_struct *work) start_j = 0; #endif /* DEBUG */ wake_up_all(&hp->emptyq); - tty_wakeup(hp->tty); + tty = tty_port_tty_get(&hp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } } out: @@ -966,8 +948,8 @@ static int hvsi_write(struct tty_struct *tty, * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls * will see there is no room in outbuf and return. */ - while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { - int chunksize = min(count, hvsi_write_room(hp->tty)); + while ((count > 0) && (hvsi_write_room(tty) > 0)) { + int chunksize = min(count, hvsi_write_room(tty)); BUG_ON(hp->n_outbuf < 0); memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); @@ -1014,19 +996,16 @@ static void hvsi_unthrottle(struct tty_struct *tty) { struct hvsi_struct *hp = tty->driver_data; unsigned long flags; - int shouldflip = 0; pr_debug("%s\n", __func__); spin_lock_irqsave(&hp->lock, flags); if (hp->n_throttle) { - hvsi_send_overflow(hp); - shouldflip = 1; + hvsi_send_overflow(hp, tty); + tty_flip_buffer_push(tty); } spin_unlock_irqrestore(&hp->lock, flags); - if (shouldflip) - tty_flip_buffer_push(hp->tty); h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); } @@ -1228,6 +1207,7 @@ static int __init hvsi_console_init(void) init_waitqueue_head(&hp->emptyq); init_waitqueue_head(&hp->stateq); spin_lock_init(&hp->lock); + tty_port_init(&hp->port); hp->index = hvsi_count; hp->inbuf_end = hp->inbuf; hp->state = HVSI_CLOSED; diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 6f4dd83..59c135d 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -377,7 +377,7 @@ int hvsilib_open(struct hvsi_priv *pv, struct hvc_struct *hp) pr_devel("HVSI@%x: open !\n", pv->termno); /* Keep track of the tty data structure */ - pv->tty = tty_kref_get(hp->tty); + pv->tty = tty_port_tty_get(&hp->port); hvsilib_establish(pv); diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 4daf962..f8b5fa0 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -44,14 +44,13 @@ #define TTYTYPE_RAS_RAW (2) struct ipw_tty { + struct tty_port port; int index; struct ipw_hardware *hardware; unsigned int channel_idx; unsigned int secondary_channel_idx; int tty_type; struct ipw_network *network; - struct tty_struct *linux_tty; - int open_count; unsigned int control_lines; struct mutex ipw_tty_mutex; int tx_bytes_queued; @@ -73,23 +72,6 @@ static char *tty_type_name(int tty_type) return channel_names[tty_type]; } -static void report_registering(struct ipw_tty *tty) -{ - char *iftype = tty_type_name(tty->tty_type); - - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": registering %s device ttyIPWp%d\n", iftype, tty->index); -} - -static void report_deregistering(struct ipw_tty *tty) -{ - char *iftype = tty_type_name(tty->tty_type); - - printk(KERN_INFO IPWIRELESS_PCCARD_NAME - ": deregistering %s device ttyIPWp%d\n", iftype, - tty->index); -} - static struct ipw_tty *get_tty(int index) { /* @@ -117,12 +99,12 @@ static int ipw_open(struct tty_struct *linux_tty, struct file *filp) mutex_unlock(&tty->ipw_tty_mutex); return -ENODEV; } - if (tty->open_count == 0) + if (tty->port.count == 0) tty->tx_bytes_queued = 0; - tty->open_count++; + tty->port.count++; - tty->linux_tty = linux_tty; + tty->port.tty = linux_tty; linux_tty->driver_data = tty; linux_tty->low_latency = 1; @@ -136,13 +118,13 @@ static int ipw_open(struct tty_struct *linux_tty, struct file *filp) static void do_ipw_close(struct ipw_tty *tty) { - tty->open_count--; + tty->port.count--; - if (tty->open_count == 0) { - struct tty_struct *linux_tty = tty->linux_tty; + if (tty->port.count == 0) { + struct tty_struct *linux_tty = tty->port.tty; if (linux_tty != NULL) { - tty->linux_tty = NULL; + tty->port.tty = NULL; linux_tty->driver_data = NULL; if (tty->tty_type == TTYTYPE_MODEM) @@ -159,7 +141,7 @@ static void ipw_hangup(struct tty_struct *linux_tty) return; mutex_lock(&tty->ipw_tty_mutex); - if (tty->open_count == 0) { + if (tty->port.count == 0) { mutex_unlock(&tty->ipw_tty_mutex); return; } @@ -182,13 +164,13 @@ void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, int work = 0; mutex_lock(&tty->ipw_tty_mutex); - linux_tty = tty->linux_tty; + linux_tty = tty->port.tty; if (linux_tty == NULL) { mutex_unlock(&tty->ipw_tty_mutex); return; } - if (!tty->open_count) { + if (!tty->port.count) { mutex_unlock(&tty->ipw_tty_mutex); return; } @@ -230,7 +212,7 @@ static int ipw_write(struct tty_struct *linux_tty, return -ENODEV; mutex_lock(&tty->ipw_tty_mutex); - if (!tty->open_count) { + if (!tty->port.count) { mutex_unlock(&tty->ipw_tty_mutex); return -EINVAL; } @@ -270,7 +252,7 @@ static int ipw_write_room(struct tty_struct *linux_tty) if (!tty) return -ENODEV; - if (!tty->open_count) + if (!tty->port.count) return -EINVAL; room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; @@ -312,7 +294,7 @@ static int ipw_chars_in_buffer(struct tty_struct *linux_tty) if (!tty) return 0; - if (!tty->open_count) + if (!tty->port.count) return 0; return tty->tx_bytes_queued; @@ -393,7 +375,7 @@ static int ipw_tiocmget(struct tty_struct *linux_tty) if (!tty) return -ENODEV; - if (!tty->open_count) + if (!tty->port.count) return -EINVAL; return get_control_lines(tty); @@ -409,7 +391,7 @@ ipw_tiocmset(struct tty_struct *linux_tty, if (!tty) return -ENODEV; - if (!tty->open_count) + if (!tty->port.count) return -EINVAL; return set_control_lines(tty, set, clear); @@ -423,7 +405,7 @@ static int ipw_ioctl(struct tty_struct *linux_tty, if (!tty) return -ENODEV; - if (!tty->open_count) + if (!tty->port.count) return -EINVAL; /* FIXME: Exactly how is the tty object locked here .. */ @@ -492,6 +474,7 @@ static int add_tty(int j, ttys[j]->network = network; ttys[j]->tty_type = tty_type; mutex_init(&ttys[j]->ipw_tty_mutex); + tty_port_init(&ttys[j]->port); tty_register_device(ipw_tty_driver, j, NULL); ipwireless_associate_network_tty(network, channel_idx, ttys[j]); @@ -500,8 +483,12 @@ static int add_tty(int j, ipwireless_associate_network_tty(network, secondary_channel_idx, ttys[j]); - if (get_tty(j) == ttys[j]) - report_registering(ttys[j]); + /* check if we provide raw device (if loopback is enabled) */ + if (get_tty(j)) + printk(KERN_INFO IPWIRELESS_PCCARD_NAME + ": registering %s device ttyIPWp%d\n", + tty_type_name(tty_type), j); + return 0; } @@ -560,19 +547,21 @@ void ipwireless_tty_free(struct ipw_tty *tty) if (ttyj) { mutex_lock(&ttyj->ipw_tty_mutex); - if (get_tty(j) == ttyj) - report_deregistering(ttyj); + if (get_tty(j)) + printk(KERN_INFO IPWIRELESS_PCCARD_NAME + ": deregistering %s device ttyIPWp%d\n", + tty_type_name(ttyj->tty_type), j); ttyj->closing = 1; - if (ttyj->linux_tty != NULL) { + if (ttyj->port.tty != NULL) { mutex_unlock(&ttyj->ipw_tty_mutex); - tty_hangup(ttyj->linux_tty); - /* Wait till the tty_hangup has completed */ - flush_work_sync(&ttyj->linux_tty->hangup_work); + tty_vhangup(ttyj->port.tty); /* FIXME: Exactly how is the tty object locked here against a parallel ioctl etc */ + /* FIXME2: hangup does not mean all processes + * are gone */ mutex_lock(&ttyj->ipw_tty_mutex); } - while (ttyj->open_count) + while (ttyj->port.count) do_ipw_close(ttyj); ipwireless_disassociate_network_ttys(network, ttyj->channel_idx); @@ -661,8 +650,8 @@ ipwireless_tty_notify_control_line_change(struct ipw_tty *tty, */ if ((old_control_lines & IPW_CONTROL_LINE_DCD) && !(tty->control_lines & IPW_CONTROL_LINE_DCD) - && tty->linux_tty) { - tty_hangup(tty->linux_tty); + && tty->port.tty) { + tty_hangup(tty->port.tty); } } diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index c6f372d..90cc680 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2326,7 +2326,7 @@ static const struct tty_operations mxser_ops = { .get_icount = mxser_get_icount, }; -struct tty_port_operations mxser_port_ops = { +static struct tty_port_operations mxser_port_ops = { .carrier_raised = mxser_carrier_raised, .dtr_rts = mxser_dtr_rts, .activate = mxser_activate, diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 5c6c314..656ad93 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1065,7 +1065,8 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(); + /* FIXME: should use a private lock */ + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1077,7 +1078,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, + wait_event_interruptible_tty(tty, pInfo->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1107,7 +1108,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(); + tty_unlock(tty); return ret; } @@ -1156,7 +1157,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(); + tty_lock(tty); pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1175,7 +1176,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(); + tty_unlock(tty); return 0; } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 94b6eda..ee1c268 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1630,6 +1630,7 @@ static int copy_from_read_buf(struct tty_struct *tty, int retval; size_t n; unsigned long flags; + bool is_eof; retval = 0; spin_lock_irqsave(&tty->read_lock, flags); @@ -1639,15 +1640,15 @@ static int copy_from_read_buf(struct tty_struct *tty, if (n) { retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); n -= retval; + is_eof = n == 1 && + tty->read_buf[tty->read_tail] == EOF_CHAR(tty); tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n); spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && tty->icanon && n == 1) { - if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty)) - n--; - } + if (L_EXTPROC(tty) && tty->icanon && is_eof && !tty->read_cnt) + n = 0; spin_unlock_irqrestore(&tty->read_lock, flags); *b += n; *nr -= n; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index eeae7fa..59af394 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -26,11 +26,13 @@ #include <linux/bitops.h> #include <linux/devpts_fs.h> #include <linux/slab.h> +#include <linux/mutex.h> #ifdef CONFIG_UNIX98_PTYS static struct tty_driver *ptm_driver; static struct tty_driver *pts_driver; +static DEFINE_MUTEX(devpts_mutex); #endif static void pty_close(struct tty_struct *tty, struct file *filp) @@ -45,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; + /* Review - krefs on tty_link ?? */ if (!tty->link) return; tty->link->packet = 0; @@ -54,12 +57,15 @@ static void pty_close(struct tty_struct *tty, struct file *filp) if (tty->driver->subtype == PTY_TYPE_MASTER) { set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS - if (tty->driver == ptm_driver) + if (tty->driver == ptm_driver) { + mutex_lock(&devpts_mutex); devpts_pty_kill(tty->link); + mutex_unlock(&devpts_mutex); + } #endif - tty_unlock(); + tty_unlock(tty); tty_vhangup(tty->link); - tty_lock(); + tty_lock(tty); } } @@ -475,13 +481,17 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, * @idx: tty index * * Look up a pty master device. Called under the tty_mutex for now. - * This provides our locking. + * This provides our locking for the tty pointer. */ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, struct inode *pts_inode, int idx) { - struct tty_struct *tty = devpts_get_tty(pts_inode, idx); + struct tty_struct *tty; + + mutex_lock(&devpts_mutex); + tty = devpts_get_tty(pts_inode, idx); + mutex_unlock(&devpts_mutex); /* Master must be open before slave */ if (!tty) return ERR_PTR(-EIO); @@ -613,24 +623,29 @@ static int ptmx_open(struct inode *inode, struct file *filp) return retval; /* find a device that is not in use. */ - tty_lock(); + mutex_lock(&devpts_mutex); index = devpts_new_index(inode); - tty_unlock(); if (index < 0) { retval = index; goto err_file; } + mutex_unlock(&devpts_mutex); + mutex_lock(&tty_mutex); - tty_lock(); + mutex_lock(&devpts_mutex); tty = tty_init_dev(ptm_driver, index); - mutex_unlock(&tty_mutex); if (IS_ERR(tty)) { retval = PTR_ERR(tty); goto out; } + /* The tty returned here is locked so we can safely + drop the mutex */ + mutex_unlock(&devpts_mutex); + mutex_unlock(&tty_mutex); + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ tty_add_file(tty, filp); @@ -643,16 +658,17 @@ static int ptmx_open(struct inode *inode, struct file *filp) if (retval) goto err_release; - tty_unlock(); + tty_unlock(tty); return 0; err_release: - tty_unlock(); + tty_unlock(tty); tty_release(inode, filp); return retval; out: + mutex_unlock(&tty_mutex); devpts_kill_index(inode, index); - tty_unlock(); err_file: + mutex_unlock(&devpts_mutex); tty_free_file(filp); return retval; } diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 5ce78252..3ed20e4 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -17,6 +17,7 @@ #include <asm/dbg.h> #include <linux/module.h> #include <linux/errno.h> +#include <linux/serial.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/timer.h> @@ -56,8 +57,6 @@ #endif /* CONFIG_M68VZ328 */ #endif /* CONFIG_M68EZ328 */ -#include "68328serial.h" - /* Turn off usage of real serial interrupt code, to "support" Copilot */ #ifdef CONFIG_XCOPILOT_BUGS #undef USE_INTS @@ -65,33 +64,82 @@ #define USE_INTS #endif -static struct m68k_serial m68k_soft[NR_PORTS]; +/* + * I believe this is the optimal setting that reduces the number of interrupts. + * At high speeds the output might become a little "bursted" (use USTCNT_TXHE + * if that bothers you), but in most cases it will not, since we try to + * transmit characters every time rs_interrupt is called. Thus, quite often + * you'll see that a receive interrupt occures before the transmit one. + * -- Vladimir Gurevich + */ +#define USTCNT_TX_INTR_MASK (USTCNT_TXEE) -static unsigned int uart_irqs[NR_PORTS] = UART_IRQ_DEFNS; +/* + * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special + * "Old data interrupt" which occures whenever the data stay in the FIFO + * longer than 30 bits time. This allows us to use FIFO without compromising + * latency. '328 does not have this feature and without the real 328-based + * board I would assume that RXRE is the safest setting. + * + * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of + * interrupts. RXFE (receive queue full) causes the system to lose data + * at least at 115200 baud + * + * If your board is busy doing other stuff, you might consider to use + * RXRE (data ready intrrupt) instead. + * + * The other option is to make these INTR masks run-time configurable, so + * that people can dynamically adapt them according to the current usage. + * -- Vladimir Gurevich + */ -/* multiple ports are contiguous in memory */ -m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR; +/* (es) */ +#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328) +#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN) +#elif defined(CONFIG_M68328) +#define USTCNT_RX_INTR_MASK (USTCNT_RXRE) +#else +#error Please, define the Rx interrupt events for your CPU +#endif +/* (/es) */ -struct tty_struct m68k_ttys; -struct m68k_serial *m68k_consinfo = 0; +/* + * This is our internal structure for each serial port's state. + */ +struct m68k_serial { + struct tty_port tport; + char is_cons; /* Is this our console. */ + int magic; + int baud_base; + int port; + int irq; + int type; /* UART type */ + int custom_divisor; + int x_char; /* xon/xoff character */ + int line; + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; +}; -#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */ +#define SERIAL_MAGIC 0x5301 -struct tty_driver *serial_driver; +/* + * Define the number of ports supported and their irqs. + */ +#define NR_PORTS 1 -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 +static struct m68k_serial m68k_soft[NR_PORTS]; -/* Debugging... DEBUG_INTR is bad to use when one of the zs - * lines is your console ;( - */ -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW +static unsigned int uart_irqs[NR_PORTS] = { UART_IRQ_NUM }; -#define RS_ISR_PASS_LIMIT 256 +/* multiple ports are contiguous in memory */ +m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR; + +struct tty_driver *serial_driver; -static void change_speed(struct m68k_serial *info); +static void change_speed(struct m68k_serial *info, struct tty_struct *tty); /* * Setup for console. Argument comes from the boot command line. @@ -143,17 +191,6 @@ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0 }; -/* Sets or clears DTR/RTS on the requested line */ -static inline void m68k_rtsdtr(struct m68k_serial *ss, int set) -{ - if (set) { - /* set the RTS/CTS line */ - } else { - /* clear it */ - } - return; -} - /* Utility routines */ static inline int get_baud(struct m68k_serial *ss) { @@ -189,7 +226,8 @@ static void rs_stop(struct tty_struct *tty) static int rs_put_char(char ch) { - int flags, loops = 0; + unsigned long flags; + int loops = 0; local_irq_save(flags); @@ -224,28 +262,9 @@ static void rs_start(struct tty_struct *tty) local_irq_restore(flags); } -/* Drop into either the boot monitor or kadb upon receiving a break - * from keyboard/console input. - */ -static void batten_down_hatches(void) -{ - /* Drop into the debugger */ -} - -static void status_handle(struct m68k_serial *info, unsigned short status) +static void receive_chars(struct m68k_serial *info, struct tty_struct *tty, + unsigned short rx) { - /* If this is console input and this is a - * 'break asserted' status change interrupt - * see if we can drop into the debugger - */ - if((status & URX_BREAK) && info->break_abort) - batten_down_hatches(); - return; -} - -static void receive_chars(struct m68k_serial *info, unsigned short rx) -{ - struct tty_struct *tty = info->tty; m68328_uart *uart = &uart_addr[info->line]; unsigned char ch, flag; @@ -259,7 +278,6 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx) if(info->is_cons) { if(URX_BREAK & rx) { /* whee, break received */ - status_handle(info, rx); return; #ifdef CONFIG_MAGIC_SYSRQ } else if (ch == 0x10) { /* ^P */ @@ -280,16 +298,13 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx) flag = TTY_NORMAL; - if(rx & URX_PARITY_ERROR) { + if (rx & URX_PARITY_ERROR) flag = TTY_PARITY; - status_handle(info, rx); - } else if(rx & URX_OVRUN) { + else if (rx & URX_OVRUN) flag = TTY_OVERRUN; - status_handle(info, rx); - } else if(rx & URX_FRAME_ERROR) { + else if (rx & URX_FRAME_ERROR) flag = TTY_FRAME; - status_handle(info, rx); - } + tty_insert_flip_char(tty, ch, flag); #ifndef CONFIG_XCOPILOT_BUGS } while((rx = uart->urx.w) & URX_DATA_READY); @@ -301,7 +316,7 @@ clear_and_exit: return; } -static void transmit_chars(struct m68k_serial *info) +static void transmit_chars(struct m68k_serial *info, struct tty_struct *tty) { m68328_uart *uart = &uart_addr[info->line]; @@ -312,7 +327,7 @@ static void transmit_chars(struct m68k_serial *info) goto clear_and_return; } - if((info->xmit_cnt <= 0) || info->tty->stopped) { + if ((info->xmit_cnt <= 0) || !tty || tty->stopped) { /* That's peculiar... TX ints off */ uart->ustcnt &= ~USTCNT_TX_INTR_MASK; goto clear_and_return; @@ -340,6 +355,7 @@ clear_and_return: irqreturn_t rs_interrupt(int irq, void *dev_id) { struct m68k_serial *info = dev_id; + struct tty_struct *tty = tty_port_tty_get(&info->tport); m68328_uart *uart; unsigned short rx; unsigned short tx; @@ -350,20 +366,24 @@ irqreturn_t rs_interrupt(int irq, void *dev_id) #ifdef USE_INTS tx = uart->utx.w; - if (rx & URX_DATA_READY) receive_chars(info, rx); - if (tx & UTX_TX_AVAIL) transmit_chars(info); + if (rx & URX_DATA_READY) + receive_chars(info, tty, rx); + if (tx & UTX_TX_AVAIL) + transmit_chars(info, tty); #else - receive_chars(info, rx); + receive_chars(info, tty, rx); #endif + tty_kref_put(tty); + return IRQ_HANDLED; } -static int startup(struct m68k_serial * info) +static int startup(struct m68k_serial *info, struct tty_struct *tty) { m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; - if (info->flags & S_INITIALIZED) + if (info->tport.flags & ASYNC_INITIALIZED) return 0; if (!info->xmit_buf) { @@ -380,7 +400,6 @@ static int startup(struct m68k_serial * info) */ uart->ustcnt = USTCNT_UEN; - info->xmit_fifo_size = 1; uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN; (void)uart->urx.w; @@ -394,17 +413,17 @@ static int startup(struct m68k_serial * info) uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK; #endif - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (tty) + clear_bit(TTY_IO_ERROR, &tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * and set the speed of the serial port */ - change_speed(info); + change_speed(info, tty); - info->flags |= S_INITIALIZED; + info->tport.flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; } @@ -413,13 +432,13 @@ static int startup(struct m68k_serial * info) * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */ -static void shutdown(struct m68k_serial * info) +static void shutdown(struct m68k_serial *info, struct tty_struct *tty) { m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; uart->ustcnt = 0; /* All off! */ - if (!(info->flags & S_INITIALIZED)) + if (!(info->tport.flags & ASYNC_INITIALIZED)) return; local_irq_save(flags); @@ -429,10 +448,10 @@ static void shutdown(struct m68k_serial * info) info->xmit_buf = 0; } - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); - info->flags &= ~S_INITIALIZED; + info->tport.flags &= ~ASYNC_INITIALIZED; local_irq_restore(flags); } @@ -488,7 +507,7 @@ struct { * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ -static void change_speed(struct m68k_serial *info) +static void change_speed(struct m68k_serial *info, struct tty_struct *tty) { m68328_uart *uart = &uart_addr[info->line]; unsigned short port; @@ -496,9 +515,7 @@ static void change_speed(struct m68k_serial *info) unsigned cflag; int i; - if (!info->tty || !info->tty->termios) - return; - cflag = info->tty->termios->c_cflag; + cflag = tty->termios->c_cflag; if (!(port = info->port)) return; @@ -510,7 +527,6 @@ static void change_speed(struct m68k_serial *info) i = (i & ~CBAUDEX) + B38400; } - info->baud = baud_table[i]; uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); @@ -807,10 +823,10 @@ static int get_serial_info(struct m68k_serial * info, tmp.line = info->line; tmp.port = info->port; tmp.irq = info->irq; - tmp.flags = info->flags; + tmp.flags = info->tport.flags; tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->tport.close_delay; + tmp.closing_wait = info->tport.closing_wait; tmp.custom_divisor = info->custom_divisor; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -818,9 +834,10 @@ static int get_serial_info(struct m68k_serial * info, return 0; } -static int set_serial_info(struct m68k_serial * info, +static int set_serial_info(struct m68k_serial *info, struct tty_struct *tty, struct serial_struct * new_info) { + struct tty_port *port = &info->tport; struct serial_struct new_serial; struct m68k_serial old_info; int retval = 0; @@ -834,17 +851,17 @@ static int set_serial_info(struct m68k_serial * info, if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.baud_base != info->baud_base) || (new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~S_USR_MASK) != - (info->flags & ~S_USR_MASK))) + (new_serial.close_delay != port->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~S_USR_MASK) | - (new_serial.flags & S_USR_MASK)); + port->flags = ((port->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } - if (info->count > 1) + if (port->count > 1) return -EBUSY; /* @@ -853,14 +870,14 @@ static int set_serial_info(struct m68k_serial * info, */ info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~S_FLAGS) | - (new_serial.flags & S_FLAGS)); + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; + port->close_delay = new_serial.close_delay; + port->closing_wait = new_serial.closing_wait; check_and_exit: - retval = startup(info); + retval = startup(info, tty); return retval; } @@ -946,7 +963,7 @@ static int rs_ioctl(struct tty_struct *tty, return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: - return set_serial_info(info, + return set_serial_info(info, tty, (struct serial_struct *) arg); case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); @@ -965,7 +982,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - change_speed(info); + change_speed(info, tty); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -988,6 +1005,7 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) static void rs_close(struct tty_struct *tty, struct file * filp) { struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; + struct tty_port *port = &info->tport; m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; @@ -1001,7 +1019,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) return; } - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (port->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -1010,26 +1028,26 @@ static void rs_close(struct tty_struct *tty, struct file * filp) * serial port won't be shutdown. */ printk("rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "port->count is %d\n", port->count); + port->count = 1; } - if (--info->count < 0) { + if (--port->count < 0) { printk("rs_close: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, port->count); + port->count = 0; } - if (info->count) { + if (port->count) { local_irq_restore(flags); return; } - info->flags |= S_CLOSING; + port->flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != S_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1040,13 +1058,12 @@ static void rs_close(struct tty_struct *tty, struct file * filp) uart->ustcnt &= ~USTCNT_RXEN; uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK); - shutdown(info); + shutdown(info, tty); rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->event = 0; - info->tty = NULL; + tty_port_tty_set(&info->tport, NULL); #warning "This is not and has never been valid so fix it" #if 0 if (tty->ldisc.num != ldiscs[N_TTY].num) { @@ -1058,14 +1075,13 @@ static void rs_close(struct tty_struct *tty, struct file * filp) (tty->ldisc.open)(tty); } #endif - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); + if (port->blocked_open) { + if (port->close_delay) + msleep_interruptible(jiffies_to_msecs(port->close_delay)); + wake_up_interruptible(&port->open_wait); } - info->flags &= ~(S_NORMAL_ACTIVE|S_CLOSING); - wake_up_interruptible(&info->close_wait); + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); local_irq_restore(flags); } @@ -1080,107 +1096,14 @@ void rs_hangup(struct tty_struct *tty) return; rs_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~S_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); + shutdown(info, tty); + info->tport.count = 0; + info->tport.flags &= ~ASYNC_NORMAL_ACTIVE; + tty_port_tty_set(&info->tport, NULL); + wake_up_interruptible(&info->tport.open_wait); } /* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct m68k_serial *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & S_CLOSING) { - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - if (info->flags & S_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= S_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); - - info->count--; - info->blocked_open++; - while (1) { - local_irq_disable(); - m68k_rtsdtr(info, 1); - local_irq_enable(); - current->state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) || - !(info->flags & S_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & S_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & S_CLOSING) && do_clocal) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - tty_unlock(); - schedule(); - tty_lock(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - - if (retval) - return retval; - info->flags |= S_NORMAL_ACTIVE; - return 0; -} - -/* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its S structure into * the IRQ chain. It also performs the serial-specific @@ -1196,18 +1119,18 @@ int rs_open(struct tty_struct *tty, struct file * filp) if (serial_paranoia_check(info, tty->name, "rs_open")) return -ENODEV; - info->count++; + info->tport.count++; tty->driver_data = info; - info->tty = tty; + tty_port_tty_set(&info->tport, tty); /* * Start up serial port */ - retval = startup(info); + retval = startup(info, tty); if (retval) return retval; - return block_til_ready(tty, filp, info); + return tty_port_block_til_ready(&info->tport, tty, filp); } /* Finally, routines used to initialize the serial driver. */ @@ -1235,11 +1158,15 @@ static const struct tty_operations rs_ops = { .set_ldisc = rs_set_ldisc, }; +static const struct tty_port_operations rs_port_ops = { +}; + /* rs_init inits the driver */ static int __init rs68328_init(void) { - int flags, i; + unsigned long flags; + int i; struct m68k_serial *info; serial_driver = alloc_tty_driver(NR_PORTS); @@ -1273,19 +1200,13 @@ rs68328_init(void) for(i=0;i<NR_PORTS;i++) { info = &m68k_soft[i]; + tty_port_init(&info->tport); + info->tport.ops = &rs_port_ops; info->magic = SERIAL_MAGIC; info->port = (int) &uart_addr[i]; - info->tty = NULL; info->irq = uart_irqs[i]; info->custom_divisor = 16; - info->close_delay = 50; - info->closing_wait = 3000; info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); info->line = i; info->is_cons = 1; /* Means shortcuts work */ diff --git a/drivers/tty/serial/68328serial.h b/drivers/tty/serial/68328serial.h deleted file mode 100644 index 3d2faab..0000000 --- a/drivers/tty/serial/68328serial.h +++ /dev/null @@ -1,186 +0,0 @@ -/* 68328serial.h: Definitions for the mc68328 serial driver. - * - * Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu> - * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> - * Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org> - * Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com> - * - * VZ Support/Fixes Evan Stawnyczy <e@lineo.ca> - */ - -#ifndef _MC683XX_SERIAL_H -#define _MC683XX_SERIAL_H - - -struct serial_struct { - int type; - int line; - int port; - int irq; - int flags; - int xmit_fifo_size; - int custom_divisor; - int baud_base; - unsigned short close_delay; - char reserved_char[2]; - int hub6; /* FIXME: We don't have AT&T Hub6 boards! */ - unsigned short closing_wait; /* time to wait before closing */ - unsigned short closing_wait2; /* no longer used... */ - int reserved[4]; -}; - -/* - * For the close wait times, 0 means wait forever for serial port to - * flush its output. 65535 means don't wait at all. - */ -#define S_CLOSING_WAIT_INF 0 -#define S_CLOSING_WAIT_NONE 65535 - -/* - * Definitions for S_struct (and serial_struct) flags field - */ -#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes - on the callout port */ -#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ -#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */ -#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ - -#define S_SPD_MASK 0x0030 -#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ - -#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ -#define S_SPD_CUST 0x0030 /* Use user-specified divisor */ - -#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ -#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ -#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ -#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ -#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ - -#define S_FLAGS 0x0FFF /* Possible legal S flags */ -#define S_USR_MASK 0x0430 /* Legal flags that non-privileged - * users can set or reset */ - -/* Internal flags used only by kernel/chr_drv/serial.c */ -#define S_INITIALIZED 0x80000000 /* Serial port was initialized */ -#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ -#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ -#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ -#define S_CLOSING 0x08000000 /* Serial port is closing */ -#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */ - -/* Software state per channel */ - -#ifdef __KERNEL__ - -/* - * I believe this is the optimal setting that reduces the number of interrupts. - * At high speeds the output might become a little "bursted" (use USTCNT_TXHE - * if that bothers you), but in most cases it will not, since we try to - * transmit characters every time rs_interrupt is called. Thus, quite often - * you'll see that a receive interrupt occures before the transmit one. - * -- Vladimir Gurevich - */ -#define USTCNT_TX_INTR_MASK (USTCNT_TXEE) - -/* - * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special - * "Old data interrupt" which occures whenever the data stay in the FIFO - * longer than 30 bits time. This allows us to use FIFO without compromising - * latency. '328 does not have this feature and without the real 328-based - * board I would assume that RXRE is the safest setting. - * - * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of - * interrupts. RXFE (receive queue full) causes the system to lose data - * at least at 115200 baud - * - * If your board is busy doing other stuff, you might consider to use - * RXRE (data ready intrrupt) instead. - * - * The other option is to make these INTR masks run-time configurable, so - * that people can dynamically adapt them according to the current usage. - * -- Vladimir Gurevich - */ - -/* (es) */ -#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328) -#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN) -#elif defined(CONFIG_M68328) -#define USTCNT_RX_INTR_MASK (USTCNT_RXRE) -#else -#error Please, define the Rx interrupt events for your CPU -#endif -/* (/es) */ - -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -struct m68k_serial { - char soft_carrier; /* Use soft carrier on this channel */ - char break_abort; /* Is serial console in, so process brk/abrt */ - char is_cons; /* Is this our console. */ - - /* We need to know the current clock divisor - * to read the bps rate the chip has currently - * loaded. - */ - unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */ - int baud; - int magic; - int baud_base; - int port; - int irq; - int flags; /* defined in tty.h */ - int type; /* UART type */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int timeout; - int xmit_fifo_size; - int custom_divisor; - int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - unsigned long last_active; - int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; -}; - - -#define SERIAL_MAGIC 0x5301 - -/* - * The size of the serial xmit buffer is 1 page, or 4096 bytes - */ -#define SERIAL_XMIT_SIZE 4096 - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define RS_EVENT_WRITE_WAKEUP 0 - -/* - * Define the number of ports supported and their irqs. - */ -#define NR_PORTS 1 -#define UART_IRQ_DEFNS {UART_IRQ_NUM} - -#endif /* __KERNEL__ */ -#endif /* !(_MC683XX_SERIAL_H) */ diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 5c27f7e..47d061b 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -284,7 +284,20 @@ static const struct serial8250_config uart_config[] = { }, }; -#if defined(CONFIG_MIPS_ALCHEMY) +/* Uart divisor latch read */ +static int default_serial_dl_read(struct uart_8250_port *up) +{ + return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8; +} + +/* Uart divisor latch write */ +static void default_serial_dl_write(struct uart_8250_port *up, int value) +{ + serial_out(up, UART_DLL, value & 0xff); + serial_out(up, UART_DLM, value >> 8 & 0xff); +} + +#ifdef CONFIG_MIPS_ALCHEMY /* Au1x00 UART hardware has a weird register layout */ static const u8 au_io_in_map[] = { @@ -305,22 +318,32 @@ static const u8 au_io_out_map[] = { [UART_MCR] = 6, }; -/* sane hardware needs no mapping */ -static inline int map_8250_in_reg(struct uart_port *p, int offset) +static unsigned int au_serial_in(struct uart_port *p, int offset) +{ + offset = au_io_in_map[offset] << p->regshift; + return __raw_readl(p->membase + offset); +} + +static void au_serial_out(struct uart_port *p, int offset, int value) +{ + offset = au_io_out_map[offset] << p->regshift; + __raw_writel(value, p->membase + offset); +} + +/* Au1x00 haven't got a standard divisor latch */ +static int au_serial_dl_read(struct uart_8250_port *up) { - if (p->iotype != UPIO_AU) - return offset; - return au_io_in_map[offset]; + return __raw_readl(up->port.membase + 0x28); } -static inline int map_8250_out_reg(struct uart_port *p, int offset) +static void au_serial_dl_write(struct uart_8250_port *up, int value) { - if (p->iotype != UPIO_AU) - return offset; - return au_io_out_map[offset]; + __raw_writel(value, up->port.membase + 0x28); } -#elif defined(CONFIG_SERIAL_8250_RM9K) +#endif + +#ifdef CONFIG_SERIAL_8250_RM9K static const u8 regmap_in[8] = { @@ -344,87 +367,79 @@ static const u8 [UART_SCR] = 0x2c }; -static inline int map_8250_in_reg(struct uart_port *p, int offset) +static unsigned int rm9k_serial_in(struct uart_port *p, int offset) { - if (p->iotype != UPIO_RM9000) - return offset; - return regmap_in[offset]; + offset = regmap_in[offset] << p->regshift; + return readl(p->membase + offset); } -static inline int map_8250_out_reg(struct uart_port *p, int offset) +static void rm9k_serial_out(struct uart_port *p, int offset, int value) { - if (p->iotype != UPIO_RM9000) - return offset; - return regmap_out[offset]; + offset = regmap_out[offset] << p->regshift; + writel(value, p->membase + offset); } -#else +static int rm9k_serial_dl_read(struct uart_8250_port *up) +{ + return ((__raw_readl(up->port.membase + 0x10) << 8) | + (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff; +} -/* sane hardware needs no mapping */ -#define map_8250_in_reg(up, offset) (offset) -#define map_8250_out_reg(up, offset) (offset) +static void rm9k_serial_dl_write(struct uart_8250_port *up, int value) +{ + __raw_writel(value, up->port.membase + 0x08); + __raw_writel(value >> 8, up->port.membase + 0x10); +} #endif static unsigned int hub6_serial_in(struct uart_port *p, int offset) { - offset = map_8250_in_reg(p, offset) << p->regshift; + offset = offset << p->regshift; outb(p->hub6 - 1 + offset, p->iobase); return inb(p->iobase + 1); } static void hub6_serial_out(struct uart_port *p, int offset, int value) { - offset = map_8250_out_reg(p, offset) << p->regshift; + offset = offset << p->regshift; outb(p->hub6 - 1 + offset, p->iobase); outb(value, p->iobase + 1); } static unsigned int mem_serial_in(struct uart_port *p, int offset) { - offset = map_8250_in_reg(p, offset) << p->regshift; + offset = offset << p->regshift; return readb(p->membase + offset); } static void mem_serial_out(struct uart_port *p, int offset, int value) { - offset = map_8250_out_reg(p, offset) << p->regshift; + offset = offset << p->regshift; writeb(value, p->membase + offset); } static void mem32_serial_out(struct uart_port *p, int offset, int value) { - offset = map_8250_out_reg(p, offset) << p->regshift; + offset = offset << p->regshift; writel(value, p->membase + offset); } static unsigned int mem32_serial_in(struct uart_port *p, int offset) { - offset = map_8250_in_reg(p, offset) << p->regshift; + offset = offset << p->regshift; return readl(p->membase + offset); } -static unsigned int au_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return __raw_readl(p->membase + offset); -} - -static void au_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - __raw_writel(value, p->membase + offset); -} - static unsigned int io_serial_in(struct uart_port *p, int offset) { - offset = map_8250_in_reg(p, offset) << p->regshift; + offset = offset << p->regshift; return inb(p->iobase + offset); } static void io_serial_out(struct uart_port *p, int offset, int value) { - offset = map_8250_out_reg(p, offset) << p->regshift; + offset = offset << p->regshift; outb(value, p->iobase + offset); } @@ -434,6 +449,10 @@ static void set_io_from_upio(struct uart_port *p) { struct uart_8250_port *up = container_of(p, struct uart_8250_port, port); + + up->dl_read = default_serial_dl_read; + up->dl_write = default_serial_dl_write; + switch (p->iotype) { case UPIO_HUB6: p->serial_in = hub6_serial_in; @@ -445,16 +464,28 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = mem_serial_out; break; - case UPIO_RM9000: case UPIO_MEM32: p->serial_in = mem32_serial_in; p->serial_out = mem32_serial_out; break; +#ifdef CONFIG_SERIAL_8250_RM9K + case UPIO_RM9000: + p->serial_in = rm9k_serial_in; + p->serial_out = rm9k_serial_out; + up->dl_read = rm9k_serial_dl_read; + up->dl_write = rm9k_serial_dl_write; + break; +#endif + +#ifdef CONFIG_MIPS_ALCHEMY case UPIO_AU: p->serial_in = au_serial_in; p->serial_out = au_serial_out; + up->dl_read = au_serial_dl_read; + up->dl_write = au_serial_dl_write; break; +#endif default: p->serial_in = io_serial_in; @@ -481,59 +512,6 @@ serial_port_out_sync(struct uart_port *p, int offset, int value) } } -/* Uart divisor latch read */ -static inline int _serial_dl_read(struct uart_8250_port *up) -{ - return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8; -} - -/* Uart divisor latch write */ -static inline void _serial_dl_write(struct uart_8250_port *up, int value) -{ - serial_out(up, UART_DLL, value & 0xff); - serial_out(up, UART_DLM, value >> 8 & 0xff); -} - -#if defined(CONFIG_MIPS_ALCHEMY) -/* Au1x00 haven't got a standard divisor latch */ -static int serial_dl_read(struct uart_8250_port *up) -{ - if (up->port.iotype == UPIO_AU) - return __raw_readl(up->port.membase + 0x28); - else - return _serial_dl_read(up); -} - -static void serial_dl_write(struct uart_8250_port *up, int value) -{ - if (up->port.iotype == UPIO_AU) - __raw_writel(value, up->port.membase + 0x28); - else - _serial_dl_write(up, value); -} -#elif defined(CONFIG_SERIAL_8250_RM9K) -static int serial_dl_read(struct uart_8250_port *up) -{ - return (up->port.iotype == UPIO_RM9000) ? - (((__raw_readl(up->port.membase + 0x10) << 8) | - (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) : - _serial_dl_read(up); -} - -static void serial_dl_write(struct uart_8250_port *up, int value) -{ - if (up->port.iotype == UPIO_RM9000) { - __raw_writel(value, up->port.membase + 0x08); - __raw_writel(value >> 8, up->port.membase + 0x10); - } else { - _serial_dl_write(up, value); - } -} -#else -#define serial_dl_read(up) _serial_dl_read(up) -#define serial_dl_write(up, value) _serial_dl_write(up, value) -#endif - /* * For the 16C950 */ @@ -568,6 +546,16 @@ static void serial8250_clear_fifos(struct uart_8250_port *p) } } +void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) +{ + unsigned char fcr; + + serial8250_clear_fifos(p); + fcr = uart_config[p->port.type].fcr; + serial_out(p, UART_FCR, fcr); +} +EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); + /* * IER sleep support. UARTs which have EFRs need the "extended * capability" bit enabled. Note that on XR16C850s, we need to @@ -1332,27 +1320,6 @@ static void serial8250_enable_ms(struct uart_port *port) } /* - * Clear the Tegra rx fifo after a break - * - * FIXME: This needs to become a port specific callback once we have a - * framework for this - */ -static void clear_rx_fifo(struct uart_8250_port *up) -{ - unsigned int status, tmout = 10000; - do { - status = serial_in(up, UART_LSR); - if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) - status = serial_in(up, UART_RX); - else - break; - if (--tmout == 0) - break; - udelay(1); - } while (1); -} - -/* * serial8250_rx_chars: processes according to the passed in LSR * value, and returns the remaining LSR bits not handled * by this Rx routine. @@ -1386,20 +1353,10 @@ serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) up->lsr_saved_flags = 0; if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ if (lsr & UART_LSR_BI) { lsr &= ~(UART_LSR_FE | UART_LSR_PE); port->icount.brk++; /* - * If tegra port then clear the rx fifo to - * accept another break/character. - */ - if (port->type == PORT_TEGRA) - clear_rx_fifo(up); - - /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask @@ -2280,10 +2237,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, quot++; if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { - if (baud < 2400) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; - else - fcr = uart_config[port->type].fcr; + fcr = uart_config[port->type].fcr; + if (baud < 2400) { + fcr &= ~UART_FCR_TRIGGER_MASK; + fcr |= UART_FCR_TRIGGER_1; + } } /* @@ -3037,6 +2995,7 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.serial_in = p->serial_in; port.serial_out = p->serial_out; port.handle_irq = p->handle_irq; + port.handle_break = p->handle_break; port.set_termios = p->set_termios; port.pm = p->pm; port.dev = &dev->dev; @@ -3153,7 +3112,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * } /** - * serial8250_register_port - register a serial port + * serial8250_register_8250_port - register a serial port * @port: serial port template * * Configure the serial port specified by the request. If the @@ -3165,50 +3124,56 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * * * On success the port is ready to use and the line number is returned. */ -int serial8250_register_port(struct uart_port *port) +int serial8250_register_8250_port(struct uart_8250_port *up) { struct uart_8250_port *uart; int ret = -ENOSPC; - if (port->uartclk == 0) + if (up->port.uartclk == 0) return -EINVAL; mutex_lock(&serial_mutex); - uart = serial8250_find_match_or_unused(port); + uart = serial8250_find_match_or_unused(&up->port); if (uart) { uart_remove_one_port(&serial8250_reg, &uart->port); - uart->port.iobase = port->iobase; - uart->port.membase = port->membase; - uart->port.irq = port->irq; - uart->port.irqflags = port->irqflags; - uart->port.uartclk = port->uartclk; - uart->port.fifosize = port->fifosize; - uart->port.regshift = port->regshift; - uart->port.iotype = port->iotype; - uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; - uart->port.mapbase = port->mapbase; - uart->port.private_data = port->private_data; - if (port->dev) - uart->port.dev = port->dev; - - if (port->flags & UPF_FIXED_TYPE) - serial8250_init_fixed_type_port(uart, port->type); + uart->port.iobase = up->port.iobase; + uart->port.membase = up->port.membase; + uart->port.irq = up->port.irq; + uart->port.irqflags = up->port.irqflags; + uart->port.uartclk = up->port.uartclk; + uart->port.fifosize = up->port.fifosize; + uart->port.regshift = up->port.regshift; + uart->port.iotype = up->port.iotype; + uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF; + uart->port.mapbase = up->port.mapbase; + uart->port.private_data = up->port.private_data; + if (up->port.dev) + uart->port.dev = up->port.dev; + + if (up->port.flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(uart, up->port.type); set_io_from_upio(&uart->port); /* Possibly override default I/O functions. */ - if (port->serial_in) - uart->port.serial_in = port->serial_in; - if (port->serial_out) - uart->port.serial_out = port->serial_out; - if (port->handle_irq) - uart->port.handle_irq = port->handle_irq; + if (up->port.serial_in) + uart->port.serial_in = up->port.serial_in; + if (up->port.serial_out) + uart->port.serial_out = up->port.serial_out; + if (up->port.handle_irq) + uart->port.handle_irq = up->port.handle_irq; /* Possibly override set_termios call */ - if (port->set_termios) - uart->port.set_termios = port->set_termios; - if (port->pm) - uart->port.pm = port->pm; + if (up->port.set_termios) + uart->port.set_termios = up->port.set_termios; + if (up->port.pm) + uart->port.pm = up->port.pm; + if (up->port.handle_break) + uart->port.handle_break = up->port.handle_break; + if (up->dl_read) + uart->dl_read = up->dl_read; + if (up->dl_write) + uart->dl_write = up->dl_write; if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, @@ -3222,6 +3187,29 @@ int serial8250_register_port(struct uart_port *port) return ret; } +EXPORT_SYMBOL(serial8250_register_8250_port); + +/** + * serial8250_register_port - register a serial port + * @port: serial port template + * + * Configure the serial port specified by the request. If the + * port exists and is in use, it is hung up and unregistered + * first. + * + * The port is then probed and if necessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int serial8250_register_port(struct uart_port *port) +{ + struct uart_8250_port up; + + memset(&up, 0, sizeof(up)); + memcpy(&up.port, port, sizeof(*port)); + return serial8250_register_8250_port(&up); +} EXPORT_SYMBOL(serial8250_register_port); /** diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 2868a1d..f9719d1 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -37,6 +37,10 @@ struct uart_8250_port { unsigned char lsr_saved_flags; #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; + + /* 8250 specific callbacks */ + int (*dl_read)(struct uart_8250_port *); + void (*dl_write)(struct uart_8250_port *, int); }; struct old_serial_port { @@ -96,6 +100,18 @@ static inline void serial_out(struct uart_8250_port *up, int offset, int value) up->port.serial_out(&up->port, offset, value); } +void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p); + +static inline int serial_dl_read(struct uart_8250_port *up) +{ + return up->dl_read(up); +} + +static inline void serial_dl_write(struct uart_8250_port *up, int value) +{ + up->dl_write(up, value); +} + #if defined(__alpha__) && !defined(CONFIG_PCI) /* * Digital did something really horribly wrong with the OUT1 and OUT2 diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c new file mode 100644 index 0000000..3a0363e --- /dev/null +++ b/drivers/tty/serial/8250/8250_em.c @@ -0,0 +1,186 @@ +/* + * Renesas Emma Mobile 8250 driver + * + * Copyright (C) 2012 Magnus Damm + * + * 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 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/slab.h> + +#include "8250.h" + +#define UART_DLL_EM 9 +#define UART_DLM_EM 10 + +struct serial8250_em_priv { + struct clk *sclk; + int line; +}; + +static void serial8250_em_serial_out(struct uart_port *p, int offset, int value) +{ + switch (offset) { + case UART_TX: /* TX @ 0x00 */ + writeb(value, p->membase); + break; + case UART_FCR: /* FCR @ 0x0c (+1) */ + case UART_LCR: /* LCR @ 0x10 (+1) */ + case UART_MCR: /* MCR @ 0x14 (+1) */ + case UART_SCR: /* SCR @ 0x20 (+1) */ + writel(value, p->membase + ((offset + 1) << 2)); + break; + case UART_IER: /* IER @ 0x04 */ + value &= 0x0f; /* only 4 valid bits - not Xscale */ + /* fall-through */ + case UART_DLL_EM: /* DLL @ 0x24 (+9) */ + case UART_DLM_EM: /* DLM @ 0x28 (+9) */ + writel(value, p->membase + (offset << 2)); + } +} + +static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset) +{ + switch (offset) { + case UART_RX: /* RX @ 0x00 */ + return readb(p->membase); + case UART_MCR: /* MCR @ 0x14 (+1) */ + case UART_LSR: /* LSR @ 0x18 (+1) */ + case UART_MSR: /* MSR @ 0x1c (+1) */ + case UART_SCR: /* SCR @ 0x20 (+1) */ + return readl(p->membase + ((offset + 1) << 2)); + case UART_IER: /* IER @ 0x04 */ + case UART_IIR: /* IIR @ 0x08 */ + case UART_DLL_EM: /* DLL @ 0x24 (+9) */ + case UART_DLM_EM: /* DLM @ 0x28 (+9) */ + return readl(p->membase + (offset << 2)); + } + return 0; +} + +static int serial8250_em_serial_dl_read(struct uart_8250_port *up) +{ + return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8; +} + +static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value) +{ + serial_out(up, UART_DLL_EM, value & 0xff); + serial_out(up, UART_DLM_EM, value >> 8 & 0xff); +} + +static int __devinit serial8250_em_probe(struct platform_device *pdev) +{ + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + struct serial8250_em_priv *priv; + struct uart_8250_port up; + int ret = -EINVAL; + + if (!regs || !irq) { + dev_err(&pdev->dev, "missing registers or irq\n"); + goto err0; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "unable to allocate private data\n"); + ret = -ENOMEM; + goto err0; + } + + priv->sclk = clk_get(&pdev->dev, "sclk"); + if (IS_ERR(priv->sclk)) { + dev_err(&pdev->dev, "unable to get clock\n"); + ret = PTR_ERR(priv->sclk); + goto err1; + } + + memset(&up, 0, sizeof(up)); + up.port.mapbase = regs->start; + up.port.irq = irq->start; + up.port.type = PORT_UNKNOWN; + up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP; + up.port.dev = &pdev->dev; + up.port.private_data = priv; + + clk_enable(priv->sclk); + up.port.uartclk = clk_get_rate(priv->sclk); + + up.port.iotype = UPIO_MEM32; + up.port.serial_in = serial8250_em_serial_in; + up.port.serial_out = serial8250_em_serial_out; + up.dl_read = serial8250_em_serial_dl_read; + up.dl_write = serial8250_em_serial_dl_write; + + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register 8250 port\n"); + goto err2; + } + + priv->line = ret; + platform_set_drvdata(pdev, priv); + return 0; + + err2: + clk_disable(priv->sclk); + clk_put(priv->sclk); + err1: + kfree(priv); + err0: + return ret; +} + +static int __devexit serial8250_em_remove(struct platform_device *pdev) +{ + struct serial8250_em_priv *priv = platform_get_drvdata(pdev); + + serial8250_unregister_port(priv->line); + clk_disable(priv->sclk); + clk_put(priv->sclk); + kfree(priv); + return 0; +} + +static const struct of_device_id serial8250_em_dt_ids[] __devinitconst = { + { .compatible = "renesas,em-uart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, serial8250_em_dt_ids); + +static struct platform_driver serial8250_em_platform_driver = { + .driver = { + .name = "serial8250-em", + .of_match_table = serial8250_em_dt_ids, + .owner = THIS_MODULE, + }, + .probe = serial8250_em_probe, + .remove = __devexit_p(serial8250_em_remove), +}; + +module_platform_driver(serial8250_em_platform_driver); + +MODULE_AUTHOR("Magnus Damm"); +MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 858dca8..28e7c7c 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/tty.h> +#include <linux/serial_reg.h> #include <linux/serial_core.h> #include <linux/8250_pci.h> #include <linux/bitops.h> @@ -1092,11 +1093,49 @@ static int skip_tx_en_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static void kt_handle_break(struct uart_port *p) +{ + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); + /* + * On receipt of a BI, serial device in Intel ME (Intel + * management engine) needs to have its fifos cleared for sane + * SOL (Serial Over Lan) output. + */ + serial8250_clear_and_reinit_fifos(up); +} + +static unsigned int kt_serial_in(struct uart_port *p, int offset) +{ + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); + unsigned int val; + + /* + * When the Intel ME (management engine) gets reset its serial + * port registers could return 0 momentarily. Functions like + * serial8250_console_write, read and save the IER, perform + * some operation and then restore it. In order to avoid + * setting IER register inadvertently to 0, if the value read + * is 0, double check with ier value in uart_8250_port and use + * that instead. up->ier should be the same value as what is + * currently configured. + */ + val = inb(p->iobase + offset); + if (offset == UART_IER) { + if (val == 0) + val = up->ier; + } + return val; +} + static int kt_serial_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_port *port, int idx) { port->flags |= UPF_BUG_THRE; + port->serial_in = kt_serial_in; + port->handle_break = kt_handle_break; return skip_tx_en_setup(priv, board, port, idx); } @@ -1609,54 +1648,72 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8812, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8813, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8814, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x8027, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x8028, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x8029, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x800C, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x800D, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .init = pci_eg20t_init, .setup = pci_default_setup, }, @@ -2775,6 +2832,12 @@ void pciserial_suspend_ports(struct serial_private *priv) for (i = 0; i < priv->nr; i++) if (priv->line[i] >= 0) serial8250_suspend_port(priv->line[i]); + + /* + * Ensure that every init quirk is properly torn down + */ + if (priv->quirk->exit) + priv->quirk->exit(priv->dev); } EXPORT_SYMBOL_GPL(pciserial_suspend_ports); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 591f801..8bc7ecb 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -278,3 +278,11 @@ config SERIAL_8250_DW help Selecting this option will enable handling of the extra features present in the Synopsys DesignWare APB UART. + +config SERIAL_8250_EM + tristate "Support for Emma Mobile intergrated serial port" + depends on SERIAL_8250 && ARM && HAVE_CLK + help + Selecting this option will add support for the integrated serial + port hardware found on the Emma Mobile line of processors. + If unsure, say N. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 867bba7..3f35eac 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o +obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 062ef8c..4ad721f 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -68,30 +68,6 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) - -#define UART_WA_SAVE_NR 14 - -static void pl011_lockup_wa(unsigned long data); -static const u32 uart_wa_reg[UART_WA_SAVE_NR] = { - ST_UART011_DMAWM, - ST_UART011_TIMEOUT, - ST_UART011_LCRH_RX, - UART011_IBRD, - UART011_FBRD, - ST_UART011_LCRH_TX, - UART011_IFLS, - ST_UART011_XFCR, - ST_UART011_XON1, - ST_UART011_XON2, - ST_UART011_XOFF1, - ST_UART011_XOFF2, - UART011_CR, - UART011_IMSC -}; - -static u32 uart_wa_regdata[UART_WA_SAVE_NR]; -static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0); - /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; @@ -101,6 +77,7 @@ struct vendor_data { bool oversampling; bool interrupt_may_hang; /* vendor-specific */ bool dma_threshold; + bool cts_event_workaround; }; static struct vendor_data vendor_arm = { @@ -110,6 +87,7 @@ static struct vendor_data vendor_arm = { .lcrh_rx = UART011_LCRH, .oversampling = false, .dma_threshold = false, + .cts_event_workaround = false, }; static struct vendor_data vendor_st = { @@ -120,6 +98,7 @@ static struct vendor_data vendor_st = { .oversampling = true, .interrupt_may_hang = true, .dma_threshold = true, + .cts_event_workaround = true, }; static struct uart_amba_port *amba_ports[UART_NR]; @@ -1055,69 +1034,6 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) #define pl011_dma_flush_buffer NULL #endif - -/* - * pl011_lockup_wa - * This workaround aims to break the deadlock situation - * when after long transfer over uart in hardware flow - * control, uart interrupt registers cannot be cleared. - * Hence uart transfer gets blocked. - * - * It is seen that during such deadlock condition ICR - * don't get cleared even on multiple write. This leads - * pass_counter to decrease and finally reach zero. This - * can be taken as trigger point to run this UART_BT_WA. - * - */ -static void pl011_lockup_wa(unsigned long data) -{ - struct uart_amba_port *uap = amba_ports[0]; - void __iomem *base = uap->port.membase; - struct circ_buf *xmit = &uap->port.state->xmit; - struct tty_struct *tty = uap->port.state->port.tty; - int buf_empty_retries = 200; - int loop; - - /* Stop HCI layer from submitting data for tx */ - tty->hw_stopped = 1; - while (!uart_circ_empty(xmit)) { - if (buf_empty_retries-- == 0) - break; - udelay(100); - } - - /* Backup registers */ - for (loop = 0; loop < UART_WA_SAVE_NR; loop++) - uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]); - - /* Disable UART so that FIFO data is flushed out */ - writew(0x00, uap->port.membase + UART011_CR); - - /* Soft reset UART module */ - if (uap->port.dev->platform_data) { - struct amba_pl011_data *plat; - - plat = uap->port.dev->platform_data; - if (plat->reset) - plat->reset(); - } - - /* Restore registers */ - for (loop = 0; loop < UART_WA_SAVE_NR; loop++) - writew(uart_wa_regdata[loop] , - uap->port.membase + uart_wa_reg[loop]); - - /* Initialise the old status of the modem signals */ - uap->old_status = readw(uap->port.membase + UART01x_FR) & - UART01x_FR_MODEM_ANY; - - if (readl(base + UART011_MIS) & 0x2) - printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n"); - - /* Start Tx/Rx */ - tty->hw_stopped = 0; -} - static void pl011_stop_tx(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1246,12 +1162,26 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned long flags; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; + unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); status = readw(uap->port.membase + UART011_MIS); if (status) { do { + if (uap->vendor->cts_event_workaround) { + /* workaround to make sure that all bits are unlocked.. */ + writew(0x00, uap->port.membase + UART011_ICR); + + /* + * WA: introduce 26ns(1 uart clk) delay before W1C; + * single apb access will incur 2 pclk(133.12Mhz) delay, + * so add 2 dummy reads + */ + dummy_read = readw(uap->port.membase + UART011_ICR); + dummy_read = readw(uap->port.membase + UART011_ICR); + } + writew(status & ~(UART011_TXIS|UART011_RTIS| UART011_RXIS), uap->port.membase + UART011_ICR); @@ -1268,11 +1198,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (status & UART011_TXIS) pl011_tx_chars(uap); - if (pass_counter-- == 0) { - if (uap->interrupt_may_hang) - tasklet_schedule(&pl011_lockup_tlet); + if (pass_counter-- == 0) break; - } status = readw(uap->port.membase + UART011_MIS); } while (status != 0); diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 5832fde..bd97db2 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip Serial Driver * - * Copyright 2006-2010 Analog Devices Inc. + * Copyright 2006-2011 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -35,10 +35,6 @@ #include <asm/portmux.h> #include <asm/cacheflush.h> #include <asm/dma.h> - -#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) -#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) -#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) #include <asm/bfin_serial.h> #ifdef CONFIG_SERIAL_BFIN_MODULE @@ -166,7 +162,7 @@ static void bfin_serial_stop_tx(struct uart_port *port) uart->tx_count = 0; uart->tx_done = 1; #else -#ifdef CONFIG_BF54x +#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x) /* Clear TFI bit */ UART_PUT_LSR(uart, TFI); #endif @@ -337,7 +333,7 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) struct circ_buf *xmit = &uart->port.state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { -#ifdef CONFIG_BF54x +#if defined(CONFIG_BF54x) || defined(CONFIG_BF60x) /* Clear TFI bit */ UART_PUT_LSR(uart, TFI); #endif @@ -536,7 +532,7 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) */ UART_CLEAR_IER(uart, ETBEI); uart->port.icount.tx += uart->tx_count; - if (!uart_circ_empty(xmit)) { + if (!(xmit->tail == 0 && xmit->head == 0)) { xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -553,7 +549,7 @@ static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; - unsigned short irqstat; + unsigned int irqstat; int x_pos, pos; spin_lock(&uart->rx_lock); @@ -586,7 +582,7 @@ static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id) static unsigned int bfin_serial_tx_empty(struct uart_port *port) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short lsr; + unsigned int lsr; lsr = UART_GET_LSR(uart); if (lsr & TEMT) @@ -598,7 +594,7 @@ static unsigned int bfin_serial_tx_empty(struct uart_port *port) static void bfin_serial_break_ctl(struct uart_port *port, int break_state) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - u16 lcr = UART_GET_LCR(uart); + u32 lcr = UART_GET_LCR(uart); if (break_state) lcr |= SB; else @@ -745,7 +741,7 @@ static int bfin_serial_startup(struct uart_port *port) } /* CTS RTS PINs are negative assertive. */ - UART_PUT_MCR(uart, ACTS); + UART_PUT_MCR(uart, UART_GET_MCR(uart) | ACTS); UART_SET_IER(uart, EDSSI); } #endif @@ -803,7 +799,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, struct bfin_serial_port *uart = (struct bfin_serial_port *)port; unsigned long flags; unsigned int baud, quot; - unsigned short val, ier, lcr = 0; + unsigned int ier, lcr = 0; switch (termios->c_cflag & CSIZE) { case CS8: @@ -875,26 +871,23 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, /* Disable UART */ ier = UART_GET_IER(uart); + UART_PUT_GCTL(uart, UART_GET_GCTL(uart) & ~UCEN); UART_DISABLE_INTS(uart); - /* Set DLAB in LCR to Access DLL and DLH */ + /* Set DLAB in LCR to Access CLK */ UART_SET_DLAB(uart); - UART_PUT_DLL(uart, quot & 0xFF); - UART_PUT_DLH(uart, (quot >> 8) & 0xFF); + UART_PUT_CLK(uart, quot); SSYNC(); /* Clear DLAB in LCR to Access THR RBR IER */ UART_CLEAR_DLAB(uart); - UART_PUT_LCR(uart, lcr); + UART_PUT_LCR(uart, (UART_GET_LCR(uart) & ~LCR_MASK) | lcr); /* Enable UART */ UART_ENABLE_INTS(uart, ier); - - val = UART_GET_GCTL(uart); - val |= UCEN; - UART_PUT_GCTL(uart, val); + UART_PUT_GCTL(uart, UART_GET_GCTL(uart) | UCEN); /* Port speed changed, update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); @@ -954,17 +947,17 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) static void bfin_serial_set_ldisc(struct uart_port *port, int ld) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short val; + unsigned int val; switch (ld) { case N_IRDA: val = UART_GET_GCTL(uart); - val |= (IREN | RPOLC); + val |= (UMOD_IRDA | RPOLC); UART_PUT_GCTL(uart, val); break; default: val = UART_GET_GCTL(uart); - val &= ~(IREN | RPOLC); + val &= ~(UMOD_MASK | RPOLC); UART_PUT_GCTL(uart, val); } } @@ -972,13 +965,13 @@ static void bfin_serial_set_ldisc(struct uart_port *port, int ld) static void bfin_serial_reset_irda(struct uart_port *port) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short val; + unsigned int val; val = UART_GET_GCTL(uart); - val &= ~(IREN | RPOLC); + val &= ~(UMOD_MASK | RPOLC); UART_PUT_GCTL(uart, val); SSYNC(); - val |= (IREN | RPOLC); + val |= (UMOD_IRDA | RPOLC); UART_PUT_GCTL(uart, val); SSYNC(); } @@ -1070,12 +1063,12 @@ static void __init bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, int *parity, int *bits) { - unsigned short status; + unsigned int status; status = UART_GET_IER(uart) & (ERBFI | ETBEI); if (status == (ERBFI | ETBEI)) { /* ok, the port was enabled */ - u16 lcr, dlh, dll; + u32 lcr, clk; lcr = UART_GET_LCR(uart); @@ -1086,30 +1079,17 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, else *parity = 'o'; } - switch (lcr & 0x03) { - case 0: - *bits = 5; - break; - case 1: - *bits = 6; - break; - case 2: - *bits = 7; - break; - case 3: - *bits = 8; - break; - } - /* Set DLAB in LCR to Access DLL and DLH */ + *bits = ((lcr & WLS_MASK) >> WLS_OFFSET) + 5; + + /* Set DLAB in LCR to Access CLK */ UART_SET_DLAB(uart); - dll = UART_GET_DLL(uart); - dlh = UART_GET_DLH(uart); + clk = UART_GET_CLK(uart); /* Clear DLAB in LCR to Access THR RBR IER */ UART_CLEAR_DLAB(uart); - *baud = get_sclk() / (16*(dll | dlh << 8)); + *baud = get_sclk() / (16*clk); } pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits); } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 5b07c0c..7264d4d 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -952,19 +952,6 @@ static const struct control_pins e100_modem_pins[NR_PORTS] = /* Input */ #define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask) - -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the memcpy_fromfs blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static unsigned char *tmp_buf; -static DEFINE_MUTEX(tmp_buf_mutex); - /* Calculate the chartime depending on baudrate, numbor of bits etc. */ static void update_char_time(struct e100_serial * info) { @@ -3150,7 +3137,7 @@ static int rs_raw_write(struct tty_struct *tty, /* first some sanity checks */ - if (!tty || !info->xmit.buf || !tmp_buf) + if (!tty || !info->xmit.buf) return 0; #ifdef SERIAL_DEBUG_DATA @@ -3989,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4065,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4106,7 +4093,6 @@ rs_open(struct tty_struct *tty, struct file * filp) { struct e100_serial *info; int retval; - unsigned long page; int allocated_resources = 0; info = rs_table + tty->index; @@ -4124,23 +4110,12 @@ rs_open(struct tty_struct *tty, struct file * filp) tty->low_latency = !!(info->flags & ASYNC_LOW_LATENCY); - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) { - return -ENOMEM; - } - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - } - /* * If the port is in the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, + wait_event_interruptible_tty(tty, info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? @@ -4487,6 +4462,7 @@ static int __init rs_init(void) info->enabled = 0; } } + tty_port_init(&info->port); info->uses_dma_in = 0; info->uses_dma_out = 0; info->line = i; diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 7081600..ec56d83 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -370,6 +370,8 @@ static void mxs_auart_settermios(struct uart_port *u, writel(ctrl, u->membase + AUART_LINECTRL); writel(ctrl2, u->membase + AUART_CTRL2); + + uart_update_timeout(u, termios->c_cflag, baud); } static irqreturn_t mxs_auart_irq_handle(int irq, void *context) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index e8c9cee..5410c06 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -12,10 +12,13 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/serial_core.h> #include <linux/serial_8250.h> +#include <linux/serial_reg.h> #include <linux/of_address.h> #include <linux/of_irq.h> +#include <linux/of_serial.h> #include <linux/of_platform.h> #include <linux/nwpserial.h> @@ -24,6 +27,26 @@ struct of_serial_info { int line; }; +#ifdef CONFIG_ARCH_TEGRA +void tegra_serial_handle_break(struct uart_port *p) +{ + unsigned int status, tmout = 10000; + + do { + status = p->serial_in(p, UART_LSR); + if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) + status = p->serial_in(p, UART_RX); + else + break; + if (--tmout == 0) + break; + udelay(1); + } while (1); +} +/* FIXME remove this export when tegra finishes conversion to open firmware */ +EXPORT_SYMBOL_GPL(tegra_serial_handle_break); +#endif + /* * Fill a struct uart_port for a given device node */ @@ -84,6 +107,9 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; + if (type == PORT_TEGRA) + port->handle_break = tegra_serial_handle_break; + return 0; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index d00b38e..d3cda0c 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -44,6 +44,13 @@ #include <plat/dmtimer.h> #include <plat/omap-serial.h> +#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) + +#define OMAP_UART_REV_42 0x0402 +#define OMAP_UART_REV_46 0x0406 +#define OMAP_UART_REV_52 0x0502 +#define OMAP_UART_REV_63 0x0603 + #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/ /* SCR register bitmasks */ @@ -53,6 +60,17 @@ #define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT 6 #define OMAP_UART_FCR_RX_FIFO_TRIG_MASK (0x3 << 6) +/* MVR register bitmasks */ +#define OMAP_UART_MVR_SCHEME_SHIFT 30 + +#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0 +#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4 +#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f + +#define OMAP_UART_MVR_MAJ_MASK 0x700 +#define OMAP_UART_MVR_MAJ_SHIFT 8 +#define OMAP_UART_MVR_MIN_MASK 0x3f + static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ @@ -1346,6 +1364,59 @@ static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) return; } +static void omap_serial_fill_features_erratas(struct uart_omap_port *up) +{ + u32 mvr, scheme; + u16 revision, major, minor; + + mvr = serial_in(up, UART_OMAP_MVER); + + /* Check revision register scheme */ + scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; + + switch (scheme) { + case 0: /* Legacy Scheme: OMAP2/3 */ + /* MINOR_REV[0:4], MAJOR_REV[4:7] */ + major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >> + OMAP_UART_LEGACY_MVR_MAJ_SHIFT; + minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK); + break; + case 1: + /* New Scheme: OMAP4+ */ + /* MINOR_REV[0:5], MAJOR_REV[8:10] */ + major = (mvr & OMAP_UART_MVR_MAJ_MASK) >> + OMAP_UART_MVR_MAJ_SHIFT; + minor = (mvr & OMAP_UART_MVR_MIN_MASK); + break; + default: + dev_warn(&up->pdev->dev, + "Unknown %s revision, defaulting to highest\n", + up->name); + /* highest possible revision */ + major = 0xff; + minor = 0xff; + } + + /* normalize revision for the driver */ + revision = UART_BUILD_REVISION(major, minor); + + switch (revision) { + case OMAP_UART_REV_46: + up->errata |= (UART_ERRATA_i202_MDR1_ACCESS | + UART_ERRATA_i291_DMA_FORCEIDLE); + break; + case OMAP_UART_REV_52: + up->errata |= (UART_ERRATA_i202_MDR1_ACCESS | + UART_ERRATA_i291_DMA_FORCEIDLE); + break; + case OMAP_UART_REV_63: + up->errata |= UART_ERRATA_i202_MDR1_ACCESS; + break; + default: + break; + } +} + static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) { struct omap_uart_port_info *omap_up_info; @@ -1439,7 +1510,6 @@ static int serial_omap_probe(struct platform_device *pdev) "%d\n", DEFAULT_CLK_SPEED); } up->uart_dma.uart_base = mem->start; - up->errata = omap_up_info->errata; if (omap_up_info->dma_enabled) { up->uart_dma.uart_dma_tx = dma_tx->start; @@ -1469,6 +1539,8 @@ static int serial_omap_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); + omap_serial_fill_features_erratas(up); + ui[up->port.line] = up; serial_omap_add_console_port(up); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index c2816f4..4fdec6a6 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -39,6 +39,7 @@ enum { PCH_UART_HANDLED_RX_ERR_INT_SHIFT, PCH_UART_HANDLED_RX_TRG_INT_SHIFT, PCH_UART_HANDLED_MS_INT_SHIFT, + PCH_UART_HANDLED_LS_INT_SHIFT, }; enum { @@ -63,6 +64,8 @@ enum { PCH_UART_HANDLED_RX_TRG_INT_SHIFT)<<1)) #define PCH_UART_HANDLED_MS_INT (1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_LS_INT (1<<((PCH_UART_HANDLED_LS_INT_SHIFT)<<1)) + #define PCH_UART_RBR 0x00 #define PCH_UART_THR 0x00 @@ -229,7 +232,6 @@ struct eg20t_port { int start_tx; int start_rx; int tx_empty; - int int_dis_flag; int trigger; int trigger_level; struct pch_uart_buffer rxbuf; @@ -237,7 +239,6 @@ struct eg20t_port { unsigned int fcr; unsigned int mcr; unsigned int use_dma; - unsigned int use_dma_flag; struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx; struct pch_dma_slave param_tx; @@ -560,14 +561,10 @@ static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf, return i; } -static unsigned int pch_uart_hal_get_iid(struct eg20t_port *priv) +static unsigned char pch_uart_hal_get_iid(struct eg20t_port *priv) { - unsigned int iir; - int ret; - - iir = ioread8(priv->membase + UART_IIR); - ret = (iir & (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP)); - return ret; + return ioread8(priv->membase + UART_IIR) &\ + (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP); } static u8 pch_uart_hal_get_line_status(struct eg20t_port *priv) @@ -666,10 +663,13 @@ static void pch_free_dma(struct uart_port *port) dma_release_channel(priv->chan_rx); priv->chan_rx = NULL; } - if (sg_dma_address(&priv->sg_rx)) - dma_free_coherent(port->dev, port->fifosize, - sg_virt(&priv->sg_rx), - sg_dma_address(&priv->sg_rx)); + + if (priv->rx_buf_dma) { + dma_free_coherent(port->dev, port->fifosize, priv->rx_buf_virt, + priv->rx_buf_dma); + priv->rx_buf_virt = NULL; + priv->rx_buf_dma = 0; + } return; } @@ -1053,12 +1053,17 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) unsigned int handled; u8 lsr; int ret = 0; - unsigned int iid; + unsigned char iid; unsigned long flags; + int next = 1; + u8 msr; spin_lock_irqsave(&priv->port.lock, flags); handled = 0; - while ((iid = pch_uart_hal_get_iid(priv)) > 1) { + while (next) { + iid = pch_uart_hal_get_iid(priv); + if (iid & PCH_UART_IIR_IP) /* No Interrupt */ + break; switch (iid) { case PCH_UART_IID_RLS: /* Receiver Line Status */ lsr = pch_uart_hal_get_line_status(priv); @@ -1066,6 +1071,8 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) UART_LSR_PE | UART_LSR_OE)) { pch_uart_err_ir(priv, lsr); ret = PCH_UART_HANDLED_RX_ERR_INT; + } else { + ret = PCH_UART_HANDLED_LS_INT; } break; case PCH_UART_IID_RDR: /* Received Data Ready */ @@ -1092,20 +1099,22 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) ret = handle_tx(priv); break; case PCH_UART_IID_MS: /* Modem Status */ - ret = PCH_UART_HANDLED_MS_INT; + msr = pch_uart_hal_get_modem(priv); + next = 0; /* MS ir prioirty is the lowest. So, MS ir + means final interrupt */ + if ((msr & UART_MSR_ANY_DELTA) == 0) + break; + ret |= PCH_UART_HANDLED_MS_INT; break; default: /* Never junp to this label */ - dev_err(priv->port.dev, "%s:iid=%d (%lu)\n", __func__, + dev_err(priv->port.dev, "%s:iid=%02x (%lu)\n", __func__, iid, jiffies); ret = -1; + next = 0; break; } handled |= (unsigned int)ret; } - if (handled == 0 && iid <= 1) { - if (priv->int_dis_flag) - priv->int_dis_flag = 0; - } spin_unlock_irqrestore(&priv->port.lock, flags); return IRQ_RETVAL(handled); @@ -1200,7 +1209,6 @@ static void pch_uart_stop_rx(struct uart_port *port) priv = container_of(port, struct eg20t_port, port); priv->start_rx = 0; pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); - priv->int_dis_flag = 1; } /* Enable the modem status interrupts. */ @@ -1447,7 +1455,6 @@ static int pch_uart_verify_port(struct uart_port *port, __func__); return -EOPNOTSUPP; #endif - priv->use_dma_flag = 1; dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n"); if (!priv->use_dma) pch_request_dma(port); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9c4c05b..246b823 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2282,6 +2282,7 @@ void uart_unregister_driver(struct uart_driver *drv) tty_unregister_driver(p); put_tty_driver(p); kfree(drv->state); + drv->state = NULL; drv->tty_driver = NULL; } diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 593d40a..5ed0daa 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("%s(%d):block_til_ready blocking on %s count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aa1debf..45b43f1 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } DBGINFO(("%s block_til_ready wait\n", tty->driver->name)); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index a3dddc1..4a1e4f0 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, printk("%s(%d):%s block_til_ready() count=%d\n", __FILE__,__LINE__, tty->driver->name, port->count ); - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6c9b7cd..91e326f 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -185,25 +185,19 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ } - /** - * tty_buffer_request_room - grow tty buffer if needed + * __tty_buffer_request_room - grow tty buffer if needed * @tty: tty structure * @size: size desired * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. - * - * Locking: Takes tty->buf.lock + * Locking: Caller must hold tty->buf.lock */ -int tty_buffer_request_room(struct tty_struct *tty, size_t size) +static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) { struct tty_buffer *b, *n; int left; - unsigned long flags; - - spin_lock_irqsave(&tty->buf.lock, flags); - /* OPTIMISATION: We could keep a per tty "zero" sized buffer to remove this conditional if its worth it. This would be invisible to the callers */ @@ -225,9 +219,30 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) size = left; } - spin_unlock_irqrestore(&tty->buf.lock, flags); return size; } + + +/** + * tty_buffer_request_room - grow tty buffer if needed + * @tty: tty structure + * @size: size desired + * + * Make at least size bytes of linear space available for the tty + * buffer. If we fail return the size we managed to find. + * + * Locking: Takes tty->buf.lock + */ +int tty_buffer_request_room(struct tty_struct *tty, size_t size) +{ + unsigned long flags; + int length; + + spin_lock_irqsave(&tty->buf.lock, flags); + length = __tty_buffer_request_room(tty, size); + spin_unlock_irqrestore(&tty->buf.lock, flags); + return length; +} EXPORT_SYMBOL_GPL(tty_buffer_request_room); /** @@ -249,14 +264,22 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space = tty_buffer_request_room(tty, goal); - struct tty_buffer *tb = tty->buf.tail; + int space; + unsigned long flags; + struct tty_buffer *tb; + + spin_lock_irqsave(&tty->buf.lock, flags); + space = __tty_buffer_request_room(tty, goal); + tb = tty->buf.tail; /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) + if (unlikely(space == 0)) { + spin_unlock_irqrestore(&tty->buf.lock, flags); break; + } memcpy(tb->char_buf_ptr + tb->used, chars, space); memset(tb->flag_buf_ptr + tb->used, flag, space); tb->used += space; + spin_unlock_irqrestore(&tty->buf.lock, flags); copied += space; chars += space; /* There is a small chance that we need to split the data over @@ -286,14 +309,22 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space = tty_buffer_request_room(tty, goal); - struct tty_buffer *tb = tty->buf.tail; + int space; + unsigned long __flags; + struct tty_buffer *tb; + + spin_lock_irqsave(&tty->buf.lock, __flags); + space = __tty_buffer_request_room(tty, goal); + tb = tty->buf.tail; /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) + if (unlikely(space == 0)) { + spin_unlock_irqrestore(&tty->buf.lock, __flags); break; + } memcpy(tb->char_buf_ptr + tb->used, chars, space); memcpy(tb->flag_buf_ptr + tb->used, flags, space); tb->used += space; + spin_unlock_irqrestore(&tty->buf.lock, __flags); copied += space; chars += space; flags += space; @@ -344,13 +375,20 @@ EXPORT_SYMBOL(tty_schedule_flip); int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size) { - int space = tty_buffer_request_room(tty, size); + int space; + unsigned long flags; + struct tty_buffer *tb; + + spin_lock_irqsave(&tty->buf.lock, flags); + space = __tty_buffer_request_room(tty, size); + + tb = tty->buf.tail; if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; *chars = tb->char_buf_ptr + tb->used; memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); tb->used += space; } + spin_unlock_irqrestore(&tty->buf.lock, flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); @@ -374,13 +412,20 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) { - int space = tty_buffer_request_room(tty, size); + int space; + unsigned long __flags; + struct tty_buffer *tb; + + spin_lock_irqsave(&tty->buf.lock, __flags); + space = __tty_buffer_request_room(tty, size); + + tb = tty->buf.tail; if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; *chars = tb->char_buf_ptr + tb->used; *flags = tb->flag_buf_ptr + tb->used; tb->used += space; } + spin_unlock_irqrestore(&tty->buf.lock, __flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d939bd7..9e930c0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *tty) put_device(tty->dev); kfree(tty->write_buf); tty_buffer_free_all(tty); + tty->magic = 0xDEADDEAD; kfree(tty); } @@ -573,7 +574,7 @@ void __tty_hangup(struct tty_struct *tty) } spin_unlock(&redirect_lock); - tty_lock(); + tty_lock(tty); /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -666,7 +667,7 @@ void __tty_hangup(struct tty_struct *tty) clear_bit(TTY_HUPPING, &tty->flags); tty_ldisc_enable(tty); - tty_unlock(); + tty_unlock(tty); if (f) fput(f); @@ -855,10 +856,11 @@ void disassociate_ctty(int on_exit) */ void no_tty(void) { + /* FIXME: Review locking here. The tty_lock never covered any race + between a new association and proc_clear_tty but possible we need + to protect against this anyway */ struct task_struct *tsk = current; - tty_lock(); disassociate_ctty(0); - tty_unlock(); proc_clear_tty(tsk); } @@ -1102,12 +1104,12 @@ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty) { mutex_lock(&tty->atomic_write_lock); - tty_lock(); + tty_lock(tty); if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); + tty_unlock(tty); tty->ops->write(tty, msg, strlen(msg)); } else - tty_unlock(); + tty_unlock(tty); tty_write_unlock(tty); } return; @@ -1402,6 +1404,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) } initialize_tty_struct(tty, driver, idx); + tty_lock(tty); retval = tty_driver_install_tty(driver, tty); if (retval < 0) goto err_deinit_tty; @@ -1414,9 +1417,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) retval = tty_ldisc_setup(tty, tty->link); if (retval) goto err_release_tty; + /* Return the tty locked so that it cannot vanish under the caller */ return tty; err_deinit_tty: + tty_unlock(tty); deinitialize_tty_struct(tty); free_tty_struct(tty); err_module_put: @@ -1425,6 +1430,7 @@ err_module_put: /* call the tty release_tty routine to clean out this slot */ err_release_tty: + tty_unlock(tty); printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); @@ -1627,7 +1633,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty_paranoia_check(tty, inode, __func__)) return 0; - tty_lock(); + tty_lock(tty); check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1636,10 +1642,11 @@ int tty_release(struct inode *inode, struct file *filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; + /* Review: parallel close */ o_tty = tty->link; if (tty_release_checks(tty, o_tty, idx)) { - tty_unlock(); + tty_unlock(tty); return 0; } @@ -1651,7 +1658,7 @@ int tty_release(struct inode *inode, struct file *filp) if (tty->ops->close) tty->ops->close(tty, filp); - tty_unlock(); + tty_unlock(tty); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1674,7 +1681,7 @@ int tty_release(struct inode *inode, struct file *filp) opens on /dev/tty */ mutex_lock(&tty_mutex); - tty_lock(); + tty_lock_pair(tty, o_tty); tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); @@ -1705,7 +1712,7 @@ int tty_release(struct inode *inode, struct file *filp) printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", __func__, tty_name(tty, buf)); - tty_unlock(); + tty_unlock_pair(tty, o_tty); mutex_unlock(&tty_mutex); schedule(); } @@ -1768,7 +1775,7 @@ int tty_release(struct inode *inode, struct file *filp) /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock(); + tty_unlock_pair(tty, o_tty); return 0; } @@ -1781,14 +1788,16 @@ int tty_release(struct inode *inode, struct file *filp) tty_ldisc_release(tty, o_tty); /* * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. + * the slots and preserving the termios structure. The tty_unlock_pair + * should be safe as we keep a kref while the tty is locked (so the + * unlock never unlocks a freed tty). */ release_tty(tty, idx); + tty_unlock_pair(tty, o_tty); /* Make this pty number available for reallocation */ if (devpts) devpts_kill_index(inode, idx); - tty_unlock(); return 0; } @@ -1800,6 +1809,9 @@ int tty_release(struct inode *inode, struct file *filp) * * We cannot return driver and index like for the other nodes because * devpts will not work then. It expects inodes to be from devpts FS. + * + * We need to move to returning a refcounted object from all the lookup + * paths including this one. */ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) { @@ -1816,6 +1828,7 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) /* noctty = 1; */ tty_kref_put(tty); /* FIXME: we put a reference and return a TTY! */ + /* This is only safe because the caller holds tty_mutex */ return tty; } @@ -1888,6 +1901,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand + * + * Note: the tty_unlock/lock cases without a ref are only safe due to + * tty_mutex */ static int tty_open(struct inode *inode, struct file *filp) @@ -1911,8 +1927,7 @@ retry_open: retval = 0; mutex_lock(&tty_mutex); - tty_lock(); - + /* This is protected by the tty_mutex */ tty = tty_open_current_tty(device, filp); if (IS_ERR(tty)) { retval = PTR_ERR(tty); @@ -1933,17 +1948,19 @@ retry_open: } if (tty) { + tty_lock(tty); retval = tty_reopen(tty); - if (retval) + if (retval < 0) { + tty_unlock(tty); tty = ERR_PTR(retval); - } else + } + } else /* Returns with the tty_lock held for now */ tty = tty_init_dev(driver, index); mutex_unlock(&tty_mutex); if (driver) tty_driver_kref_put(driver); if (IS_ERR(tty)) { - tty_unlock(); retval = PTR_ERR(tty); goto err_file; } @@ -1972,7 +1989,7 @@ retry_open: printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, retval, tty->name); #endif - tty_unlock(); /* need to call tty_release without BTM */ + tty_unlock(tty); /* need to call tty_release without BTM */ tty_release(inode, filp); if (retval != -ERESTARTSYS) return retval; @@ -1984,17 +2001,15 @@ retry_open: /* * Need to reset f_op in case a hangup happened. */ - tty_lock(); if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; - tty_unlock(); goto retry_open; } - tty_unlock(); + tty_unlock(tty); mutex_lock(&tty_mutex); - tty_lock(); + tty_lock(tty); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && @@ -2002,11 +2017,10 @@ retry_open: tty->session == NULL) __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); + tty_unlock(tty); mutex_unlock(&tty_mutex); return 0; err_unlock: - tty_unlock(); mutex_unlock(&tty_mutex); /* after locks to avoid deadlock */ if (!IS_ERR_OR_NULL(driver)) @@ -2089,10 +2103,13 @@ out: static int tty_fasync(int fd, struct file *filp, int on) { + struct tty_struct *tty = file_tty(filp); int retval; - tty_lock(); + + tty_lock(tty); retval = __tty_fasync(fd, filp, on); - tty_unlock(); + tty_unlock(tty); + return retval; } @@ -2929,6 +2946,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; tty->overrun_time = jiffies; tty_buffer_init(tty); + mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); init_waitqueue_head(&tty->write_wait); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 24b95db..173a900 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -28,7 +28,6 @@ static DEFINE_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_idle); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; @@ -65,7 +64,7 @@ static void put_ldisc(struct tty_ldisc *ld) return; } local_irq_restore(flags); - wake_up(&tty_ldisc_idle); + wake_up(&ld->wq_idle); } /** @@ -200,6 +199,8 @@ static struct tty_ldisc *tty_ldisc_get(int disc) ld->ops = ldops; atomic_set(&ld->users, 1); + init_waitqueue_head(&ld->wq_idle); + return ld; } @@ -538,7 +539,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) { long ret; - ret = wait_event_timeout(tty_ldisc_idle, + ret = wait_event_timeout(tty->ldisc->wq_idle, atomic_read(&tty->ldisc->users) == 1, timeout); return ret > 0 ? 0 : -EBUSY; } @@ -567,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(); + tty_lock(tty); /* * We need to look at the tty locking here for pty/tty pairs * when both sides try to change in parallel. @@ -581,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); + tty_unlock(tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(); + tty_unlock(tty); /* * Problem: What do we do if this blocks ? * We could deadlock here @@ -594,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_wait_until_sent(tty, 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* @@ -604,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); wait_event(tty_ldisc_wait, test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); } @@ -622,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) o_ldisc = tty->ldisc; - tty_unlock(); + tty_unlock(tty); /* * Make sure we don't change while someone holds a * reference to the line discipline. The TTY_LDISC bit @@ -649,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) retval = tty_ldisc_wait_idle(tty, 5 * HZ); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* handle wait idle failure locked */ @@ -664,7 +665,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) clear_bit(TTY_LDISC_CHANGING, &tty->flags); mutex_unlock(&tty->ldisc_mutex); tty_ldisc_put(new_ldisc); - tty_unlock(); + tty_unlock(tty); return -EIO; } @@ -707,7 +708,7 @@ enable: if (o_work) schedule_work(&o_tty->buf.work); mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); + tty_unlock(tty); return retval; } @@ -815,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) * need to wait for another function taking the BTM */ clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); + tty_unlock(tty); cancel_work_sync(&tty->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* At this point we have a closed ldisc and we want to @@ -830,7 +831,7 @@ retry: if (atomic_read(&tty->ldisc->users) != 1) { char cur_n[TASK_COMM_LEN], tty_n[64]; long timeout = 3 * HZ; - tty_unlock(); + tty_unlock(tty); while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { timeout = MAX_SCHEDULE_TIMEOUT; @@ -911,10 +912,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) * race with the set_ldisc code path. */ - tty_unlock(); + tty_unlock(tty); tty_ldisc_halt(tty); tty_ldisc_flush_works(tty); - tty_lock(); + tty_lock(tty); mutex_lock(&tty->ldisc_mutex); /* diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 9ff986c..69adc80 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -4,29 +4,59 @@ #include <linux/semaphore.h> #include <linux/sched.h> -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); +/* Legacy tty mutex glue */ /* * Getting the big tty mutex. */ -void __lockfunc tty_lock(void) + +void __lockfunc tty_lock(struct tty_struct *tty) { - mutex_lock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "L Bad %p\n", tty); + WARN_ON(1); + return; + } + tty_kref_get(tty); + mutex_lock(&tty->legacy_mutex); } EXPORT_SYMBOL(tty_lock); -void __lockfunc tty_unlock(void) +void __lockfunc tty_unlock(struct tty_struct *tty) { - mutex_unlock(&big_tty_mutex); + if (tty->magic != TTY_MAGIC) { + printk(KERN_ERR "U Bad %p\n", tty); + WARN_ON(1); + return; + } + mutex_unlock(&tty->legacy_mutex); + tty_kref_put(tty); } EXPORT_SYMBOL(tty_unlock); + +/* + * Getting the big tty mutex for a pair of ttys with lock ordering + * On a non pty/tty pair tty2 can be NULL which is just fine. + */ +void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + if (tty < tty2) { + tty_lock(tty); + tty_lock(tty2); + } else { + if (tty2 && tty2 != tty) + tty_lock(tty2); + tty_lock(tty); + } +} +EXPORT_SYMBOL(tty_lock_pair); + +void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_unlock(tty); + if (tty2 && tty2 != tty) + tty_unlock(tty2); +} +EXPORT_SYMBOL(tty_unlock_pair); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index bf6e238..d9cca95 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -230,7 +230,7 @@ int tty_port_block_til_ready(struct tty_port *port, /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, + wait_event_interruptible_tty(tty, port->close_wait, !(port->flags & ASYNC_CLOSING)); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; @@ -296,9 +296,9 @@ int tty_port_block_til_ready(struct tty_port *port, retval = -ERESTARTSYS; break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } finish_wait(&port->open_wait, &wait); diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 8308fc7..2aaa0c2 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/tty.h> #include <asm/uaccess.h> +#include <linux/console.h> #include <linux/consolemap.h> #include <linux/vt_kern.h> @@ -312,6 +313,7 @@ int con_set_trans_old(unsigned char __user * arg) if (!access_ok(VERIFY_READ, arg, E_TABSZ)) return -EFAULT; + console_lock(); for (i=0; i<E_TABSZ ; i++) { unsigned char uc; __get_user(uc, arg+i); @@ -319,6 +321,7 @@ int con_set_trans_old(unsigned char __user * arg) } update_user_maps(); + console_unlock(); return 0; } @@ -330,11 +333,13 @@ int con_get_trans_old(unsigned char __user * arg) if (!access_ok(VERIFY_WRITE, arg, E_TABSZ)) return -EFAULT; + console_lock(); for (i=0; i<E_TABSZ ; i++) - { - ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); - __put_user((ch & ~0xff) ? 0 : ch, arg+i); - } + { + ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); + __put_user((ch & ~0xff) ? 0 : ch, arg+i); + } + console_unlock(); return 0; } @@ -346,6 +351,7 @@ int con_set_trans_new(ushort __user * arg) if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short))) return -EFAULT; + console_lock(); for (i=0; i<E_TABSZ ; i++) { unsigned short us; __get_user(us, arg+i); @@ -353,6 +359,7 @@ int con_set_trans_new(ushort __user * arg) } update_user_maps(); + console_unlock(); return 0; } @@ -364,8 +371,10 @@ int con_get_trans_new(ushort __user * arg) if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short))) return -EFAULT; + console_lock(); for (i=0; i<E_TABSZ ; i++) __put_user(p[i], arg+i); + console_unlock(); return 0; } @@ -407,6 +416,7 @@ static void con_release_unimap(struct uni_pagedir *p) } } +/* Caller must hold the console lock */ void con_free_unimap(struct vc_data *vc) { struct uni_pagedir *p; @@ -487,17 +497,21 @@ con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) return 0; } -/* ui is a leftover from using a hashtable, but might be used again */ -int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui) +/* ui is a leftover from using a hashtable, but might be used again + Caller must hold the lock */ +static int con_do_clear_unimap(struct vc_data *vc, struct unimapinit *ui) { struct uni_pagedir *p, *q; - + p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p && p->readonly) return -EIO; + if (p && p->readonly) + return -EIO; + if (!p || --p->refcount) { q = kzalloc(sizeof(*p), GFP_KERNEL); if (!q) { - if (p) p->refcount++; + if (p) + p->refcount++; return -ENOMEM; } q->refcount=1; @@ -511,23 +525,43 @@ int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui) return 0; } +int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui) +{ + int ret; + console_lock(); + ret = con_do_clear_unimap(vc, ui); + console_unlock(); + return ret; +} + int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) { int err = 0, err1, i; struct uni_pagedir *p, *q; + console_lock(); + /* Save original vc_unipagdir_loc in case we allocate a new one */ p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p->readonly) return -EIO; + if (p->readonly) { + console_unlock(); + return -EIO; + } - if (!ct) return 0; + if (!ct) { + console_unlock(); + return 0; + } if (p->refcount > 1) { int j, k; u16 **p1, *p2, l; - err1 = con_clear_unimap(vc, NULL); - if (err1) return err1; + err1 = con_do_clear_unimap(vc, NULL); + if (err1) { + console_unlock(); + return err1; + } /* * Since refcount was > 1, con_clear_unimap() allocated a @@ -558,7 +592,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) *vc->vc_uni_pagedir_loc = (unsigned long)p; con_release_unimap(q); kfree(q); - return err1; + console_unlock(); + return err1; } } } else { @@ -592,21 +627,30 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) /* * Merge with fontmaps of any other virtual consoles. */ - if (con_unify_unimap(vc, p)) + if (con_unify_unimap(vc, p)) { + console_unlock(); return err; + } for (i = 0; i <= 3; i++) set_inverse_transl(vc, p, i); /* Update inverse translations */ set_inverse_trans_unicode(vc, p); - + + console_unlock(); return err; } -/* Loads the unimap for the hardware font, as defined in uni_hash.tbl. - The representation used was the most compact I could come up - with. This routine is executed at sys_setup time, and when the - PIO_FONTRESET ioctl is called. */ - +/** + * con_set_default_unimap - set default unicode map + * @vc: the console we are updating + * + * Loads the unimap for the hardware font, as defined in uni_hash.tbl. + * The representation used was the most compact I could come up + * with. This routine is executed at video setup, and when the + * PIO_FONTRESET ioctl is called. + * + * The caller must hold the console lock + */ int con_set_default_unimap(struct vc_data *vc) { int i, j, err = 0, err1; @@ -617,6 +661,7 @@ int con_set_default_unimap(struct vc_data *vc) p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; if (p == dflt) return 0; + dflt->refcount++; *vc->vc_uni_pagedir_loc = (unsigned long)dflt; if (p && !--p->refcount) { @@ -628,8 +673,9 @@ int con_set_default_unimap(struct vc_data *vc) /* The default font is always 256 characters */ - err = con_clear_unimap(vc, NULL); - if (err) return err; + err = con_do_clear_unimap(vc, NULL); + if (err) + return err; p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; q = dfont_unitable; @@ -654,6 +700,13 @@ int con_set_default_unimap(struct vc_data *vc) } EXPORT_SYMBOL(con_set_default_unimap); +/** + * con_copy_unimap - copy unimap between two vts + * @dst_vc: target + * @src_vt: source + * + * The caller must hold the console lock when invoking this method + */ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc) { struct uni_pagedir *q; @@ -668,13 +721,23 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc) *dst_vc->vc_uni_pagedir_loc = (long)q; return 0; } +EXPORT_SYMBOL(con_copy_unimap); +/** + * con_get_unimap - get the unicode map + * @vc: the console to read from + * + * Read the console unicode data for this console. Called from the ioctl + * handlers. + */ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) { int i, j, k, ect; u16 **p1, *p2; struct uni_pagedir *p; + console_lock(); + ect = 0; if (*vc->vc_uni_pagedir_loc) { p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; @@ -694,22 +757,19 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni } } __put_user(ect, uct); + console_unlock(); return ((ect <= ct) ? 0 : -ENOMEM); } -void con_protect_unimap(struct vc_data *vc, int rdonly) -{ - struct uni_pagedir *p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - - if (p) - p->readonly = rdonly; -} - /* * Always use USER_MAP. These functions are used by the keyboard, * which shouldn't be affected by G0/G1 switching, etc. * If the user map still contains default values, i.e. the * direct-to-font mapping, then assume user is using Latin1. + * + * FIXME: at some point we need to decide if we want to lock the table + * update element itself via the keyboard_event_lock for consistency with the + * keyboard driver as well as the consoles */ /* may be called during an interrupt */ u32 conv_8bit_to_uni(unsigned char c) @@ -777,4 +837,3 @@ console_map_init(void) con_set_default_unimap(vc_cons[i].d); } -EXPORT_SYMBOL(con_copy_unimap); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 2156188..84cbf29 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3892,36 +3892,6 @@ static void set_palette(struct vc_data *vc) vc->vc_sw->con_set_palette(vc, color_table); } -static int set_get_cmap(unsigned char __user *arg, int set) -{ - int i, j, k; - - WARN_CONSOLE_UNLOCKED(); - - for (i = 0; i < 16; i++) - if (set) { - get_user(default_red[i], arg++); - get_user(default_grn[i], arg++); - get_user(default_blu[i], arg++); - } else { - put_user(default_red[i], arg++); - put_user(default_grn[i], arg++); - put_user(default_blu[i], arg++); - } - if (set) { - for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc_cons_allocated(i)) { - for (j = k = 0; j < 16; j++) { - vc_cons[i].d->vc_palette[k++] = default_red[j]; - vc_cons[i].d->vc_palette[k++] = default_grn[j]; - vc_cons[i].d->vc_palette[k++] = default_blu[j]; - } - set_palette(vc_cons[i].d); - } - } - return 0; -} - /* * Load palette into the DAC registers. arg points to a colour * map, 3 bytes per colour, 16 colours, range from 0 to 255. @@ -3929,24 +3899,50 @@ static int set_get_cmap(unsigned char __user *arg, int set) int con_set_cmap(unsigned char __user *arg) { - int rc; + int i, j, k; + unsigned char colormap[3*16]; + + if (copy_from_user(colormap, arg, sizeof(colormap))) + return -EFAULT; console_lock(); - rc = set_get_cmap (arg,1); + for (i = k = 0; i < 16; i++) { + default_red[i] = colormap[k++]; + default_grn[i] = colormap[k++]; + default_blu[i] = colormap[k++]; + } + for (i = 0; i < MAX_NR_CONSOLES; i++) { + if (!vc_cons_allocated(i)) + continue; + for (j = k = 0; j < 16; j++) { + vc_cons[i].d->vc_palette[k++] = default_red[j]; + vc_cons[i].d->vc_palette[k++] = default_grn[j]; + vc_cons[i].d->vc_palette[k++] = default_blu[j]; + } + set_palette(vc_cons[i].d); + } console_unlock(); - return rc; + return 0; } int con_get_cmap(unsigned char __user *arg) { - int rc; + int i, k; + unsigned char colormap[3*16]; console_lock(); - rc = set_get_cmap (arg,0); + for (i = k = 0; i < 16; i++) { + colormap[k++] = default_red[i]; + colormap[k++] = default_grn[i]; + colormap[k++] = default_blu[i]; + } console_unlock(); - return rc; + if (copy_to_user(arg, colormap, sizeof(colormap))) + return -EFAULT; + + return 0; } void reset_palette(struct vc_data *vc) diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index ede2ef1..6461854 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -910,7 +910,9 @@ int vt_ioctl(struct tty_struct *tty, ret = con_font_op(vc_cons[fg_console].d, &op); if (ret) break; + console_lock(); con_set_default_unimap(vc_cons[fg_console].d); + console_unlock(); break; } #endif @@ -934,33 +936,23 @@ int vt_ioctl(struct tty_struct *tty, case PIO_SCRNMAP: if (!perm) ret = -EPERM; - else { - tty_lock(); + else ret = con_set_trans_old(up); - tty_unlock(); - } break; case GIO_SCRNMAP: - tty_lock(); ret = con_get_trans_old(up); - tty_unlock(); break; case PIO_UNISCRNMAP: if (!perm) ret = -EPERM; - else { - tty_lock(); + else ret = con_set_trans_new(up); - tty_unlock(); - } break; case GIO_UNISCRNMAP: - tty_lock(); ret = con_get_trans_new(up); - tty_unlock(); break; case PIO_UNIMAPCLR: @@ -970,19 +962,14 @@ int vt_ioctl(struct tty_struct *tty, ret = copy_from_user(&ui, up, sizeof(struct unimapinit)); if (ret) ret = -EFAULT; - else { - tty_lock(); + else con_clear_unimap(vc, &ui); - tty_unlock(); - } break; } case PIO_UNIMAP: case GIO_UNIMAP: - tty_lock(); ret = do_unimap_ioctl(cmd, up, perm, vc); - tty_unlock(); break; case VT_LOCKSWITCH: @@ -1196,9 +1183,7 @@ long vt_compat_ioctl(struct tty_struct *tty, case PIO_UNIMAP: case GIO_UNIMAP: - tty_lock(); ret = compat_unimap_ioctl(cmd, up, perm, vc); - tty_unlock(); break; /* diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 380a87f..15a42c8 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -94,17 +94,14 @@ struct gs_buf { * (and thus for each /dev/ node). */ struct gs_port { + struct tty_port port; spinlock_t port_lock; /* guard port_* access */ struct gserial *port_usb; - struct tty_struct *port_tty; - unsigned open_count; bool openclose; /* open/close in progress */ u8 port_num; - wait_queue_head_t close_wait; /* wait for last close */ - struct list_head read_pool; int read_started; int read_allocated; @@ -412,8 +409,8 @@ __acquires(&port->port_lock) break; } - if (do_tty_wake && port->port_tty) - tty_wakeup(port->port_tty); + if (do_tty_wake && port->port.tty) + tty_wakeup(port->port.tty); return status; } @@ -435,7 +432,7 @@ __acquires(&port->port_lock) struct tty_struct *tty; /* no more rx if closed */ - tty = port->port_tty; + tty = port->port.tty; if (!tty) break; @@ -488,7 +485,7 @@ static void gs_rx_push(unsigned long _port) /* hand any queued data to the tty */ spin_lock_irq(&port->port_lock); - tty = port->port_tty; + tty = port->port.tty; while (!list_empty(queue)) { struct usb_request *req; @@ -699,7 +696,7 @@ static int gs_start_io(struct gs_port *port) /* unblock any pending writes into our circular buffer */ if (started) { - tty_wakeup(port->port_tty); + tty_wakeup(port->port.tty); } else { gs_free_requests(ep, head, &port->read_allocated); gs_free_requests(port->port_usb->in, &port->write_pool, @@ -734,9 +731,9 @@ static int gs_open(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); /* already open? Great. */ - if (port->open_count) { + if (port->port.count) { status = 0; - port->open_count++; + port->port.count++; /* currently opening/closing? wait ... */ } else if (port->openclose) { @@ -793,9 +790,9 @@ static int gs_open(struct tty_struct *tty, struct file *file) /* REVISIT maybe wait for "carrier detect" */ tty->driver_data = port; - port->port_tty = tty; + port->port.tty = tty; - port->open_count = 1; + port->port.count = 1; port->openclose = false; /* if connected, start the I/O stream */ @@ -837,11 +834,11 @@ static void gs_close(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); - if (port->open_count != 1) { - if (port->open_count == 0) + if (port->port.count != 1) { + if (port->port.count == 0) WARN_ON(1); else - --port->open_count; + --port->port.count; goto exit; } @@ -851,7 +848,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) * and sleep if necessary */ port->openclose = true; - port->open_count = 0; + port->port.count = 0; gser = port->port_usb; if (gser && gser->disconnect) @@ -879,14 +876,14 @@ static void gs_close(struct tty_struct *tty, struct file *file) gs_buf_clear(&port->port_write_buf); tty->driver_data = NULL; - port->port_tty = NULL; + port->port.tty = NULL; port->openclose = false; pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); - wake_up_interruptible(&port->close_wait); + wake_up_interruptible(&port->port.close_wait); exit: spin_unlock_irq(&port->port_lock); } @@ -1034,8 +1031,8 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) if (port == NULL) return -ENOMEM; + tty_port_init(&port->port); spin_lock_init(&port->port_lock); - init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->drain_wait); tasklet_init(&port->push, gs_rx_push, (unsigned long) port); @@ -1155,7 +1152,7 @@ static int gs_closed(struct gs_port *port) int cond; spin_lock_irq(&port->port_lock); - cond = (port->open_count == 0) && !port->openclose; + cond = (port->port.count == 0) && !port->openclose; spin_unlock_irq(&port->port_lock); return cond; } @@ -1194,7 +1191,7 @@ void gserial_cleanup(void) tasklet_kill(&port->push); /* wait for old opens to finish */ - wait_event(port->close_wait, gs_closed(port)); + wait_event(port->port.close_wait, gs_closed(port)); WARN_ON(port->port_usb != NULL); @@ -1268,7 +1265,7 @@ int gserial_connect(struct gserial *gser, u8 port_num) /* if it's already open, start I/O ... and notify the serial * protocol about open/close status (connect/disconnect). */ - if (port->open_count) { + if (port->port.count) { pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); gs_start_io(port); if (gser->connect) @@ -1315,10 +1312,10 @@ void gserial_disconnect(struct gserial *gser) port->port_usb = NULL; gser->ioport = NULL; - if (port->open_count > 0 || port->openclose) { + if (port->port.count > 0 || port->openclose) { wake_up_interruptible(&port->drain_wait); - if (port->port_tty) - tty_hangup(port->port_tty); + if (port->port.tty) + tty_hangup(port->port.tty); } spin_unlock_irqrestore(&port->port_lock, flags); @@ -1331,7 +1328,7 @@ void gserial_disconnect(struct gserial *gser) /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); - if (port->open_count == 0 && !port->openclose) + if (port->port.count == 0 && !port->openclose) gs_buf_free(&port->port_write_buf); gs_free_requests(gser->out, &port->read_pool, NULL); gs_free_requests(gser->out, &port->read_queue, NULL); diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h index fadff28..79b3eb3 100644 --- a/include/linux/generic_serial.h +++ b/include/linux/generic_serial.h @@ -4,7 +4,6 @@ * Copyright (C) 1998 R.E.Wolff@BitWizard.nl * * written for the SX serial driver. - * Contains the code that should be shared over all the serial drivers. * * Version 0.1 -- December, 1998. */ @@ -12,45 +11,8 @@ #ifndef GENERIC_SERIAL_H #define GENERIC_SERIAL_H -#ifdef __KERNEL__ -#include <linux/mutex.h> -#include <linux/tty.h> - -struct real_driver { - void (*disable_tx_interrupts) (void *); - void (*enable_tx_interrupts) (void *); - void (*disable_rx_interrupts) (void *); - void (*enable_rx_interrupts) (void *); - void (*shutdown_port) (void*); - int (*set_real_termios) (void*); - int (*chars_in_buffer) (void*); - void (*close) (void*); - void (*hungup) (void*); - void (*getserial) (void*, struct serial_struct *sp); -}; - - - -struct gs_port { - int magic; - struct tty_port port; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - struct mutex port_write_mutex; - unsigned long event; - unsigned short closing_wait; - int close_delay; - struct real_driver *rd; - int wakeup_chars; - int baud_base; - int baud; - int custom_divisor; - spinlock_t driver_lock; -}; - -#endif /* __KERNEL__ */ +#warning Use of this header is deprecated. +#warning Since nobody sets the constants defined here for you, you should not, in any case, use them. Including the header is thus pointless. /* Flags */ /* Warning: serial.h defines some ASYNC_ flags, they say they are "only" @@ -60,8 +22,6 @@ struct gs_port { #define GS_RX_INTEN 0x00400000 #define GS_ACTIVE 0x00200000 - - #define GS_TYPE_NORMAL 1 #define GS_DEBUG_FLUSH 0x00000001 @@ -72,24 +32,4 @@ struct gs_port { #define GS_DEBUG_FLOW 0x00000020 #define GS_DEBUG_WRITE 0x00000040 -#ifdef __KERNEL__ -int gs_put_char(struct tty_struct *tty, unsigned char ch); -int gs_write(struct tty_struct *tty, - const unsigned char *buf, int count); -int gs_write_room(struct tty_struct *tty); -int gs_chars_in_buffer(struct tty_struct *tty); -void gs_flush_buffer(struct tty_struct *tty); -void gs_flush_chars(struct tty_struct *tty); -void gs_stop(struct tty_struct *tty); -void gs_start(struct tty_struct *tty); -void gs_hangup(struct tty_struct *tty); -int gs_block_til_ready(void *port, struct file *filp); -void gs_close(struct tty_struct *tty, struct file *filp); -void gs_set_termios (struct tty_struct * tty, - struct ktermios * old_termios); -int gs_init_port(struct gs_port *port); -int gs_setserial(struct gs_port *port, struct serial_struct __user *sp); -int gs_getserial(struct gs_port *port, struct serial_struct __user *sp); -void gs_got_break(struct gs_port *port); -#endif /* __KERNEL__ */ #endif diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 292f27a..215c416 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -15,6 +15,7 @@ #define __ISDN_H__ #include <linux/ioctl.h> +#include <linux/tty.h> #define ISDN_MAX_DRIVERS 32 #define ISDN_MAX_CHANNELS 64 @@ -392,21 +393,8 @@ typedef struct isdn_net_dev_s { /*======================= Start of ISDN-tty stuff ===========================*/ #define ISDN_ASYNC_MAGIC 0x49344C01 /* for paranoia-checking */ -#define ISDN_ASYNC_INITIALIZED 0x80000000 /* port was initialized */ -#define ISDN_ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device active */ -#define ISDN_ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device active */ -#define ISDN_ASYNC_CLOSING 0x08000000 /* Serial port is closing */ -#define ISDN_ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define ISDN_ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */ -#define ISDN_ASYNC_HUP_NOTIFY 0x0001 /* Notify tty on hangups/closes */ -#define ISDN_ASYNC_SESSION_LOCKOUT 0x0100 /* Lock cua opens on session */ -#define ISDN_ASYNC_PGRP_LOCKOUT 0x0200 /* Lock cua opens on pgrp */ -#define ISDN_ASYNC_CALLOUT_NOHUP 0x0400 /* No hangup for cui */ -#define ISDN_ASYNC_SPLIT_TERMIOS 0x0008 /* Sep. termios for dialin/out */ #define ISDN_SERIAL_XMIT_SIZE 1024 /* Default bufsize for write */ #define ISDN_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */ -#define ISDN_SERIAL_TYPE_NORMAL 1 -#define ISDN_SERIAL_TYPE_CALLOUT 2 #ifdef CONFIG_ISDN_AUDIO /* For using sk_buffs with audio we need some private variables @@ -448,17 +436,12 @@ typedef struct atemu { /* Private data (similar to async_struct in <linux/serial.h>) */ typedef struct modem_info { int magic; - struct module *owner; - int flags; /* defined in tty.h */ + struct tty_port port; int x_char; /* xon/xoff character */ int mcr; /* Modem control register */ int msr; /* Modem status register */ int lsr; /* Line status register */ int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - long session; /* Session of opening process */ - long pgrp; /* pgrp of opening process */ int online; /* 1 = B-Channel is up, drop data */ /* 2 = B-Channel is up, deliver d.*/ int dialing; /* Dial in progress or ATA */ @@ -478,7 +461,6 @@ typedef struct modem_info { int send_outstanding;/* # of outstanding send-requests */ int xmit_size; /* max. # of chars in xmit_buf */ int xmit_count; /* # of chars in xmit_buf */ - unsigned char *xmit_buf; /* transmit buffer */ struct sk_buff_head xmit_queue; /* transmit queue */ atomic_t xmit_lock; /* Semaphore for isdn_tty_write */ #ifdef CONFIG_ISDN_AUDIO @@ -496,11 +478,7 @@ typedef struct modem_info { struct T30_s *fax; /* T30 Fax Group 3 data/interface */ int faxonline; /* Fax-channel status */ #endif - struct tty_struct *tty; /* Pointer to corresponding tty */ atemu emu; /* AT-emulator data */ - struct ktermios normal_termios; /* For saving termios structs */ - struct ktermios callout_termios; - wait_queue_head_t open_wait, close_wait; spinlock_t readlock; } modem_info; diff --git a/include/linux/of_serial.h b/include/linux/of_serial.h new file mode 100644 index 0000000..4a73ed8 --- /dev/null +++ b/include/linux/of_serial.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_OF_SERIAL_H +#define __LINUX_OF_SERIAL_H + +/* + * FIXME remove this file when tegra finishes conversion to open firmware, + * expectation is that all quirks will then be self-contained in + * drivers/tty/serial/of_serial.c. + */ +#ifdef CONFIG_ARCH_TEGRA +extern void tegra_serial_handle_break(struct uart_port *port); +#else +static inline void tegra_serial_handle_break(struct uart_port *port) +{ +} +#endif + +#endif /* __LINUX_OF_SERIAL */ diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 8f012f8..10dbce5 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -38,6 +38,7 @@ struct plat_serial8250_port { int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned old); + void (*handle_break)(struct uart_port *); }; /* @@ -68,6 +69,7 @@ enum { struct uart_port; struct uart_8250_port; +int serial8250_register_8250_port(struct uart_8250_port *); int serial8250_register_port(struct uart_port *); void serial8250_unregister_port(int line); void serial8250_suspend_port(int line); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 2db407a..65db992 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -310,6 +310,7 @@ struct uart_port { int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); + void (*handle_break)(struct uart_port *); unsigned int irq; /* irq number */ unsigned long irqflags; /* irq flags */ unsigned int uartclk; /* base uart clock */ @@ -533,6 +534,10 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) static inline int uart_handle_break(struct uart_port *port) { struct uart_state *state = port->state; + + if (port->handle_break) + port->handle_break(port); + #ifdef SUPPORT_SYSRQ if (port->cons && port->cons->index == port->line) { if (!port->sysrq) { diff --git a/include/linux/tty.h b/include/linux/tty.h index 9f47ab5..4990ef2 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -268,6 +268,7 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ @@ -605,8 +606,12 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* tty_mutex.c */ /* functions for preparation of BKL removal */ -extern void __lockfunc tty_lock(void) __acquires(tty_lock); -extern void __lockfunc tty_unlock(void) __releases(tty_lock); +extern void __lockfunc tty_lock(struct tty_struct *tty); +extern void __lockfunc tty_unlock(struct tty_struct *tty); +extern void __lockfunc tty_lock_pair(struct tty_struct *tty, + struct tty_struct *tty2); +extern void __lockfunc tty_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2); /* * this shall be called only from where BTM is held (like close) @@ -621,9 +626,9 @@ extern void __lockfunc tty_unlock(void) __releases(tty_lock); static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, long timeout) { - tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */ + tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */ tty_wait_until_sent(tty, timeout); - tty_lock(); + tty_lock(tty); } /* @@ -638,16 +643,16 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, * * Do not use in new code. */ -#define wait_event_interruptible_tty(wq, condition) \ +#define wait_event_interruptible_tty(tty, wq, condition) \ ({ \ int __ret = 0; \ if (!(condition)) { \ - __wait_event_interruptible_tty(wq, condition, __ret); \ + __wait_event_interruptible_tty(tty, wq, condition, __ret); \ } \ __ret; \ }) -#define __wait_event_interruptible_tty(wq, condition, ret) \ +#define __wait_event_interruptible_tty(tty, wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ \ @@ -656,9 +661,9 @@ do { \ if (condition) \ break; \ if (!signal_pending(current)) { \ - tty_unlock(); \ + tty_unlock(tty); \ schedule(); \ - tty_lock(); \ + tty_lock(tty); \ continue; \ } \ ret = -ERESTARTSYS; \ diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index ff7dc08..fb79dd8d 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -110,6 +110,7 @@ #include <linux/fs.h> #include <linux/wait.h> #include <linux/pps_kernel.h> +#include <linux/wait.h> struct tty_ldisc_ops { int magic; @@ -154,6 +155,7 @@ struct tty_ldisc_ops { struct tty_ldisc { struct tty_ldisc_ops *ops; atomic_t users; + wait_queue_head_t wq_idle; }; #define TTY_LDISC_MAGIC 0x5403 diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index e33d77f..50ae7d0 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -70,7 +70,6 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list); int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list); int con_set_default_unimap(struct vc_data *vc); void con_free_unimap(struct vc_data *vc); -void con_protect_unimap(struct vc_data *vc, int rdonly); int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); #define vc_translate(vc, c) ((vc)->vc_translate[(c) | \ diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 4bf54b3..aa5d73b 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -48,13 +48,12 @@ static struct tty_driver *rfcomm_tty_driver; struct rfcomm_dev { + struct tty_port port; struct list_head list; - atomic_t refcnt; char name[12]; int id; unsigned long flags; - atomic_t opened; int err; bdaddr_t src; @@ -64,9 +63,7 @@ struct rfcomm_dev { uint modem_status; struct rfcomm_dlc *dlc; - struct tty_struct *tty; wait_queue_head_t wait; - struct work_struct wakeup_task; struct device *tty_dev; @@ -82,11 +79,18 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); -static void rfcomm_tty_wakeup(struct work_struct *work); - /* ---- Device functions ---- */ -static void rfcomm_dev_destruct(struct rfcomm_dev *dev) + +/* + * The reason this isn't actually a race, as you no doubt have a little voice + * screaming at you in your head, is that the refcount should never actually + * reach zero unless the device has already been taken off the list, in + * rfcomm_dev_del(). And if that's not true, we'll hit the BUG() in + * rfcomm_dev_destruct() anyway. + */ +static void rfcomm_dev_destruct(struct tty_port *port) { + struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); struct rfcomm_dlc *dlc = dev->dlc; BT_DBG("dev %p dlc %p", dev, dlc); @@ -113,23 +117,9 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *dev) module_put(THIS_MODULE); } -static inline void rfcomm_dev_hold(struct rfcomm_dev *dev) -{ - atomic_inc(&dev->refcnt); -} - -static inline void rfcomm_dev_put(struct rfcomm_dev *dev) -{ - /* The reason this isn't actually a race, as you no - doubt have a little voice screaming at you in your - head, is that the refcount should never actually - reach zero unless the device has already been taken - off the list, in rfcomm_dev_del(). And if that's not - true, we'll hit the BUG() in rfcomm_dev_destruct() - anyway. */ - if (atomic_dec_and_test(&dev->refcnt)) - rfcomm_dev_destruct(dev); -} +static const struct tty_port_operations rfcomm_port_ops = { + .destruct = rfcomm_dev_destruct, +}; static struct rfcomm_dev *__rfcomm_dev_get(int id) { @@ -154,7 +144,7 @@ static inline struct rfcomm_dev *rfcomm_dev_get(int id) if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) dev = NULL; else - rfcomm_dev_hold(dev); + tty_port_get(&dev->port); } spin_unlock(&rfcomm_dev_lock); @@ -241,7 +231,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) sprintf(dev->name, "rfcomm%d", dev->id); list_add(&dev->list, head); - atomic_set(&dev->refcnt, 1); bacpy(&dev->src, &req->src); bacpy(&dev->dst, &req->dst); @@ -250,10 +239,9 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) dev->flags = req->flags & ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); - atomic_set(&dev->opened, 0); - + tty_port_init(&dev->port); + dev->port.ops = &rfcomm_port_ops; init_waitqueue_head(&dev->wait); - INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup); skb_queue_head_init(&dev->pending); @@ -320,18 +308,23 @@ free: static void rfcomm_dev_del(struct rfcomm_dev *dev) { + unsigned long flags; BT_DBG("dev %p", dev); BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)); - if (atomic_read(&dev->opened) > 0) + spin_lock_irqsave(&dev->port.lock, flags); + if (dev->port.count > 0) { + spin_unlock_irqrestore(&dev->port.lock, flags); return; + } + spin_unlock_irqrestore(&dev->port.lock, flags); spin_lock(&rfcomm_dev_lock); list_del_init(&dev->list); spin_unlock(&rfcomm_dev_lock); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); } /* ---- Send buffer ---- */ @@ -345,15 +338,16 @@ static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) static void rfcomm_wfree(struct sk_buff *skb) { struct rfcomm_dev *dev = (void *) skb->sk; + struct tty_struct *tty = dev->port.tty; atomic_sub(skb->truesize, &dev->wmem_alloc); - if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) - queue_work(system_nrt_wq, &dev->wakeup_task); - rfcomm_dev_put(dev); + if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty) + tty_wakeup(tty); + tty_port_put(&dev->port); } static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) { - rfcomm_dev_hold(dev); + tty_port_get(&dev->port); atomic_add(skb->truesize, &dev->wmem_alloc); skb->sk = (void *) dev; skb->destructor = rfcomm_wfree; @@ -432,7 +426,7 @@ static int rfcomm_release_dev(void __user *arg) return -ENODEV; if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) { - rfcomm_dev_put(dev); + tty_port_put(&dev->port); return -EPERM; } @@ -440,12 +434,12 @@ static int rfcomm_release_dev(void __user *arg) rfcomm_dlc_close(dev->dlc, 0); /* Shut down TTY synchronously before freeing rfcomm_dev */ - if (dev->tty) - tty_vhangup(dev->tty); + if (dev->port.tty) + tty_vhangup(dev->port.tty); if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) rfcomm_dev_del(dev); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); return 0; } @@ -523,7 +517,7 @@ static int rfcomm_get_dev_info(void __user *arg) if (copy_to_user(arg, &di, sizeof(di))) err = -EFAULT; - rfcomm_dev_put(dev); + tty_port_put(&dev->port); return err; } @@ -559,7 +553,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) return; } - tty = dev->tty; + tty = dev->port.tty; if (!tty || !skb_queue_empty(&dev->pending)) { skb_queue_tail(&dev->pending, skb); return; @@ -585,13 +579,13 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) wake_up_interruptible(&dev->wait); if (dlc->state == BT_CLOSED) { - if (!dev->tty) { + if (!dev->port.tty) { if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { /* Drop DLC lock here to avoid deadlock * 1. rfcomm_dev_get will take rfcomm_dev_lock * but in rfcomm_dev_add there's lock order: * rfcomm_dev_lock -> dlc lock - * 2. rfcomm_dev_put will deadlock if it's + * 2. tty_port_put will deadlock if it's * the last reference */ rfcomm_dlc_unlock(dlc); @@ -601,11 +595,11 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) } rfcomm_dev_del(dev); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); rfcomm_dlc_lock(dlc); } } else - tty_hangup(dev->tty); + tty_hangup(dev->port.tty); } } @@ -618,8 +612,8 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) { - if (dev->tty && !C_CLOCAL(dev->tty)) - tty_hangup(dev->tty); + if (dev->port.tty && !C_CLOCAL(dev->port.tty)) + tty_hangup(dev->port.tty); } dev->modem_status = @@ -630,21 +624,9 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) } /* ---- TTY functions ---- */ -static void rfcomm_tty_wakeup(struct work_struct *work) -{ - struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev, - wakeup_task); - struct tty_struct *tty = dev->tty; - if (!tty) - return; - - BT_DBG("dev %p tty %p", dev, tty); - tty_wakeup(tty); -} - static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev) { - struct tty_struct *tty = dev->tty; + struct tty_struct *tty = dev->port.tty; struct sk_buff *skb; int inserted = 0; @@ -671,6 +653,7 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) DECLARE_WAITQUEUE(wait, current); struct rfcomm_dev *dev; struct rfcomm_dlc *dlc; + unsigned long flags; int err, id; id = tty->index; @@ -686,10 +669,14 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) return -ENODEV; BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), - dev->channel, atomic_read(&dev->opened)); + dev->channel, dev->port.count); - if (atomic_inc_return(&dev->opened) > 1) + spin_lock_irqsave(&dev->port.lock, flags); + if (++dev->port.count > 1) { + spin_unlock_irqrestore(&dev->port.lock, flags); return 0; + } + spin_unlock_irqrestore(&dev->port.lock, flags); dlc = dev->dlc; @@ -697,7 +684,7 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) rfcomm_dlc_lock(dlc); tty->driver_data = dev; - dev->tty = tty; + dev->port.tty = tty; rfcomm_dlc_unlock(dlc); set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); @@ -723,9 +710,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) break; } - tty_unlock(); + tty_unlock(tty); schedule(); - tty_lock(); + tty_lock(tty); } set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); @@ -744,13 +731,17 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + unsigned long flags; + if (!dev) return; BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, - atomic_read(&dev->opened)); + dev->port.count); - if (atomic_dec_and_test(&dev->opened)) { + spin_lock_irqsave(&dev->port.lock, flags); + if (!--dev->port.count) { + spin_unlock_irqrestore(&dev->port.lock, flags); if (dev->tty_dev->parent) device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); @@ -758,11 +749,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) rfcomm_dlc_close(dev->dlc, 0); clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - cancel_work_sync(&dev->wakeup_task); rfcomm_dlc_lock(dev->dlc); tty->driver_data = NULL; - dev->tty = NULL; + dev->port.tty = NULL; rfcomm_dlc_unlock(dev->dlc); if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) { @@ -770,11 +760,12 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) list_del_init(&dev->list); spin_unlock(&rfcomm_dev_lock); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); } - } + } else + spin_unlock_irqrestore(&dev->port.lock, flags); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); } static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) @@ -1083,7 +1074,7 @@ static void rfcomm_tty_hangup(struct tty_struct *tty) if (rfcomm_dev_get(dev->id) == NULL) return; rfcomm_dev_del(dev); - rfcomm_dev_put(dev); + tty_port_put(&dev->port); } } |