diff options
108 files changed, 2886 insertions, 4405 deletions
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 3d2d0c29f02..cc8093c15cf 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -287,14 +287,6 @@ Who: Glauber Costa <gcosta@redhat.com> --------------------------- -What: old style serial driver for ColdFire (CONFIG_SERIAL_COLDFIRE) -When: 2.6.28 -Why: This driver still uses the old interface and has been replaced - by CONFIG_SERIAL_MCF. -Who: Sebastian Siewior <sebastian@breakpoint.cc> - ---------------------------- - What: /sys/o2cb symlink When: January 2010 Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb diff --git a/MAINTAINERS b/MAINTAINERS index 6ba3ee82283..74b80820531 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2797,15 +2797,6 @@ L: linux-mtd@lists.infradead.org T: git git://git.infradead.org/mtd-2.6.git S: Maintained -MEI MN10300/AM33 PORT -P: David Howells -M: dhowells@redhat.com -P: Koichi Yasutake -M: yasutake.koichi@jp.panasonic.com -L: linux-am33-list@redhat.com (moderated for non-subscribers) -W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/ -S: Maintained - MICROTEK X6 SCANNER P: Oliver Neukum M: oliver@neukum.name @@ -3170,6 +3161,15 @@ M: olof@lixom.net L: i2c@lm-sensors.org S: Maintained +PANASONIC MN10300/AM33 PORT +P: David Howells +M: dhowells@redhat.com +P: Koichi Yasutake +M: yasutake.koichi@jp.panasonic.com +L: linux-am33-list@redhat.com (moderated for non-subscribers) +W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/ +S: Maintained + PARALLEL PORT SUPPORT L: linux-parport@lists.infradead.org (subscribers-only) S: Orphan diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index 93229b3d6e3..339293d677c 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -117,15 +117,14 @@ int request_dma(unsigned int channel, char *device_id) #ifdef CONFIG_BF54x if (channel >= CH_UART2_RX && channel <= CH_UART3_TX) { - if (strncmp(device_id, "BFIN_UART", 9) == 0) { - dma_ch[channel].regs->peripheral_map &= 0x0FFF; - dma_ch[channel].regs->peripheral_map |= + unsigned int per_map; + per_map = dma_ch[channel].regs->peripheral_map & 0xFFF; + if (strncmp(device_id, "BFIN_UART", 9) == 0) + dma_ch[channel].regs->peripheral_map = per_map | ((channel - CH_UART2_RX + 0xC)<<12); - } else { - dma_ch[channel].regs->peripheral_map &= 0x0FFF; - dma_ch[channel].regs->peripheral_map |= + else + dma_ch[channel].regs->peripheral_map = per_map | ((channel - CH_UART2_RX + 0x6)<<12); - } } #endif diff --git a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h index 2526b6ed6fa..75722d6008b 100644 --- a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h @@ -78,6 +78,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -119,7 +122,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -164,8 +166,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h index ebf592b59aa..815bfe5dd1a 100644 --- a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h @@ -69,6 +69,8 @@ # endif #endif +#define BFIN_UART_TX_FIFO_SIZE 2 + struct bfin_serial_port { struct uart_port port; unsigned int old_status; @@ -111,7 +113,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -142,7 +143,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #define DRIVER_NAME "bfin-uart" -int nr_ports = BFIN_UART_NR_PORTS; static void bfin_serial_hw_init(struct bfin_serial_port *uart) { diff --git a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h index 1bf56ffa22f..b3f87e1d16a 100644 --- a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h @@ -78,6 +78,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -119,7 +122,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -164,8 +166,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h index 5e29446a8e0..e4cf35e7ab9 100644 --- a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h @@ -82,6 +82,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -105,7 +108,6 @@ struct bfin_serial_port { #endif }; -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -170,8 +172,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h index 8aa02780e64..e0ce0c1843d 100644 --- a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h @@ -69,6 +69,8 @@ # endif #endif +#define BFIN_UART_TX_FIFO_SIZE 2 + struct bfin_serial_port { struct uart_port port; unsigned int old_status; @@ -111,7 +113,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -142,7 +143,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #define DRIVER_NAME "bfin-uart" -int nr_ports = BFIN_UART_NR_PORTS; static void bfin_serial_hw_init(struct bfin_serial_port *uart) { diff --git a/arch/sparc/include/asm/serial.h b/arch/sparc/include/asm/serial.h new file mode 100644 index 00000000000..f90d61c2805 --- /dev/null +++ b/arch/sparc/include/asm/serial.h @@ -0,0 +1,6 @@ +#ifndef __SPARC_SERIAL_H +#define __SPARC_SERIAL_H + +#define BASE_BAUD ( 1843200 / 16 ) + +#endif /* __SPARC_SERIAL_H */ diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index d741f35d7b3..14a102e877d 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -275,6 +275,8 @@ int line_ioctl(struct tty_struct *tty, struct file * file, case TIOCGLTC: case TIOCSLTC: #endif + /* Note: these are out of date as we now have TCGETS2 etc but this + whole lot should probably go away */ case TCGETS: case TCSETSF: case TCSETSW: diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 8dfcf77cb71..4426bb552bd 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -484,7 +484,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, return -EUNATCH; default: - err = n_tty_ioctl(tty, file, cmd, arg); + err = n_tty_ioctl_helper(tty, file, cmd, arg); break; }; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index caff85149b9..700ff967945 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -350,7 +350,7 @@ config STALDRV config STALLION tristate "Stallion EasyIO or EC8/32 support" - depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) + depends on STALDRV && (ISA || EISA || PCI) help If you have an EasyIO or EasyConnection 8/32 multiport Stallion card, then this is for you; say Y. Make sure to read @@ -361,7 +361,7 @@ config STALLION config ISTALLION tristate "Stallion EC8/64, ONboard, Brumby support" - depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) + depends on STALDRV && (ISA || EISA || PCI) help If you have an EasyConnection 8/64, ONboard, Brumby or Stallion serial multiport card, say Y here. Make sure to read diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 6850f6da757..1a4247dccac 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 6e763e3f5a8..98821f97583 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -837,9 +837,6 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch) struct async_struct *info; unsigned long flags; - if (!tty) - return 0; - info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_put_char")) @@ -892,9 +889,6 @@ static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count struct async_struct *info; unsigned long flags; - if (!tty) - return 0; - info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_write")) diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 31d08b641f5..b899d9182c7 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -712,8 +712,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un IndexCard = adgl->num_card-1; - if(cmd != 0 && cmd != 6 && - ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { + if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { static int warncount = 10; if (warncount) { printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1); @@ -832,8 +831,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un } break; default: - printk(KERN_INFO "APPLICOM driver ioctl, unknown function code %d\n",cmd) ; - ret = -EINVAL; + ret = -ENOTTY; break; } Dummy = readb(apbs[IndexCard].RamIO + VERS); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index fe6d774fe2e..5e5b1dc1a0a 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -4993,12 +4993,14 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { card_name = "Cyclom-Y"; - addr0 = pci_iomap(pdev, 0, CyPCI_Yctl); + addr0 = ioremap_nocache(pci_resource_start(pdev, 0), + CyPCI_Yctl); if (addr0 == NULL) { dev_err(&pdev->dev, "can't remap ctl region\n"); goto err_reg; } - addr2 = pci_iomap(pdev, 2, CyPCI_Ywin); + addr2 = ioremap_nocache(pci_resource_start(pdev, 2), + CyPCI_Ywin); if (addr2 == NULL) { dev_err(&pdev->dev, "can't remap base region\n"); goto err_unmap; @@ -5013,7 +5015,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { struct RUNTIME_9060 __iomem *ctl_addr; - ctl_addr = addr0 = pci_iomap(pdev, 0, CyPCI_Zctl); + ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0), + CyPCI_Zctl); if (addr0 == NULL) { dev_err(&pdev->dev, "can't remap ctl region\n"); goto err_reg; @@ -5026,8 +5029,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, mailbox = (u32)readl(&ctl_addr->mail_box_0); - addr2 = pci_iomap(pdev, 2, mailbox == ZE_V1 ? - CyPCI_Ze_win : CyPCI_Zwin); + addr2 = ioremap_nocache(pci_resource_start(pdev, 2), + mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); if (addr2 == NULL) { dev_err(&pdev->dev, "can't remap base region\n"); goto err_unmap; @@ -5159,9 +5162,9 @@ err_null: cy_card[card_no].base_addr = NULL; free_irq(irq, &cy_card[card_no]); err_unmap: - pci_iounmap(pdev, addr0); + iounmap(addr0); if (addr2) - pci_iounmap(pdev, addr2); + iounmap(addr2); err_reg: pci_release_regions(pdev); err_dis: @@ -5186,9 +5189,9 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev) cy_writew(cinfo->ctl_addr + 0x68, readw(cinfo->ctl_addr + 0x68) & ~0x0900); - pci_iounmap(pdev, cinfo->base_addr); + iounmap(cinfo->base_addr); if (cinfo->ctl_addr) - pci_iounmap(pdev, cinfo->ctl_addr); + iounmap(cinfo->ctl_addr); if (cinfo->irq #ifndef CONFIG_CYZ_INTR && !IS_CYC_Z(*cinfo) diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 456e4ede049..4998b2761e8 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -1376,6 +1376,7 @@ static void post_fep_init(unsigned int crd) unsigned long flags; u16 tseg, rseg; + tty_port_init(&ch->port); ch->brdchan = bc; ch->mailbox = gd; INIT_WORK(&ch->tqueue, do_softint); @@ -1510,10 +1511,6 @@ static void post_fep_init(unsigned int crd) ch->fepstopca = 0; ch->close_delay = 50; - ch->port.count = 0; - ch->port.blocked_open = 0; - init_waitqueue_head(&ch->port.open_wait); - init_waitqueue_head(&ch->port.close_wait); spin_unlock_irqrestore(&epca_lock, flags); } diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 19d3afb0e50..c6090f84a2e 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -54,8 +54,6 @@ int gs_put_char(struct tty_struct * tty, unsigned char ch) func_enter (); - if (!tty) return 0; - port = tty->driver_data; if (!port) return 0; @@ -97,8 +95,6 @@ int gs_write(struct tty_struct * tty, func_enter (); - if (!tty) return 0; - port = tty->driver_data; if (!port) return 0; @@ -185,7 +181,6 @@ static int gs_real_chars_in_buffer(struct tty_struct *tty) struct gs_port *port; func_enter (); - if (!tty) return 0; port = tty->driver_data; if (!port->rd) return 0; @@ -274,8 +269,6 @@ void gs_flush_buffer(struct tty_struct *tty) func_enter (); - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -296,8 +289,6 @@ void gs_flush_chars(struct tty_struct * tty) func_enter (); - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -321,8 +312,6 @@ void gs_stop(struct tty_struct * tty) func_enter (); - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -341,8 +330,6 @@ void gs_start(struct tty_struct * tty) { struct gs_port *port; - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -393,8 +380,6 @@ void gs_hangup(struct tty_struct *tty) func_enter (); - if (!tty) return; - port = tty->driver_data; tty = port->port.tty; if (!tty) @@ -426,8 +411,6 @@ int gs_block_til_ready(void *port_, struct file * filp) tty = port->port.tty; - if (!tty) return 0; - gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n"); /* * If the device is in the middle of being closed, then block @@ -523,8 +506,6 @@ void gs_close(struct tty_struct * tty, struct file * filp) func_enter (); - if (!tty) return; - port = (struct gs_port *) tty->driver_data; if (!port) return; @@ -621,8 +602,6 @@ void gs_set_termios (struct tty_struct * tty, func_enter(); - if (!tty) return; - port = tty->driver_data; if (!port) return; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index fd64137b1ab..ec7aded0a2d 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -819,11 +819,11 @@ static int hvc_init(void) hvc_driver = drv; return 0; -put_tty: - put_tty_driver(hvc_driver); stop_thread: kthread_stop(hvc_task); hvc_task = NULL; +put_tty: + put_tty_driver(drv); out: return err; } diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile index 939618f62fe..bc397d92b49 100644 --- a/drivers/char/ip2/Makefile +++ b/drivers/char/ip2/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_COMPUTONE) += ip2.o -ip2-objs := ip2base.o ip2main.o +ip2-objs := ip2main.o diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c index 3601017f58c..29db44de399 100644 --- a/drivers/char/ip2/i2ellis.c +++ b/drivers/char/ip2/i2ellis.c @@ -69,38 +69,6 @@ static DEFINE_RWLOCK(Dl_spinlock); //======================================================= //****************************************************************************** -// Function: iiEllisInit() -// Parameters: None -// -// Returns: Nothing -// -// Description: -// -// This routine performs any required initialization of the iiEllis subsystem. -// -//****************************************************************************** -static void -iiEllisInit(void) -{ -} - -//****************************************************************************** -// Function: iiEllisCleanup() -// Parameters: None -// -// Returns: Nothing -// -// Description: -// -// This routine performs any required cleanup of the iiEllis subsystem. -// -//****************************************************************************** -static void -iiEllisCleanup(void) -{ -} - -//****************************************************************************** // Function: iiSetAddress(pB, address, delay) // Parameters: pB - pointer to the board structure // address - the purported I/O address of the board diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h index c88a64e527a..fb6df245601 100644 --- a/drivers/char/ip2/i2ellis.h +++ b/drivers/char/ip2/i2ellis.h @@ -511,7 +511,6 @@ typedef void (*delayFunc_t)(unsigned int); // // Initialization of a board & structure is in four (five!) parts: // -// 0) iiEllisInit() - Initialize iiEllis subsystem. // 1) iiSetAddress() - Define the board address & delay function for a board. // 2) iiReset() - Reset the board (provided it exists) // -- Note you may do this to several boards -- @@ -523,7 +522,6 @@ typedef void (*delayFunc_t)(unsigned int); // loadware. To change loadware, you must begin again with step 2, resetting // the board again (step 1 not needed). -static void iiEllisInit(void); static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); static int iiReset(i2eBordStrPtr); static int iiResetDelay(i2eBordStrPtr); diff --git a/drivers/char/ip2/ip2base.c b/drivers/char/ip2/ip2base.c deleted file mode 100644 index 8155e247c04..00000000000 --- a/drivers/char/ip2/ip2base.c +++ /dev/null @@ -1,108 +0,0 @@ -// ip2.c -// This is a dummy module to make the firmware available when needed -// and allows it to be unloaded when not. Rumor is the __initdata -// macro doesn't always works on all platforms so we use this kludge. -// If not compiled as a module it just makes fip_firm avaliable then -// __initdata should work as advertized -// - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/wait.h> - -#ifndef __init -#define __init -#endif -#ifndef __initfunc -#define __initfunc(a) a -#endif -#ifndef __initdata -#define __initdata -#endif - -#include "ip2types.h" - -int -ip2_loadmain(int *, int *); // ref into ip2main.c - -/* Note: Add compiled in defaults to these arrays, not to the structure - in ip2.h any longer. That structure WILL get overridden - by these values, or command line values, or insmod values!!! =mhw= -*/ -static int io[IP2_MAX_BOARDS]= { 0, 0, 0, 0 }; -static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 }; - -static int poll_only = 0; - -MODULE_AUTHOR("Doug McNash"); -MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); -module_param_array(irq, int, NULL, 0); -MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); -module_param_array(io, int, NULL, 0); -MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); -module_param(poll_only, bool, 0); -MODULE_PARM_DESC(poll_only,"Do not use card interrupts"); - - -static int __init ip2_init(void) -{ - if( poll_only ) { - /* Hard lock the interrupts to zero */ - irq[0] = irq[1] = irq[2] = irq[3] = 0; - } - - return ip2_loadmain(io, irq); -} -module_init(ip2_init); - -MODULE_LICENSE("GPL"); - -#ifndef MODULE -/****************************************************************************** - * ip2_setup: - * str: kernel command line string - * - * Can't autoprobe the boards so user must specify configuration on - * kernel command line. Sane people build it modular but the others - * come here. - * - * Alternating pairs of io,irq for up to 4 boards. - * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3 - * - * io=0 => No board - * io=1 => PCI - * io=2 => EISA - * else => ISA I/O address - * - * irq=0 or invalid for ISA will revert to polling mode - * - * Any value = -1, do not overwrite compiled in value. - * - ******************************************************************************/ -static int __init ip2_setup(char *str) -{ - int ints[10]; /* 4 boards, 2 parameters + 2 */ - int i, j; - - str = get_options (str, ARRAY_SIZE(ints), ints); - - for( i = 0, j = 1; i < 4; i++ ) { - if( j > ints[0] ) { - break; - } - if( ints[j] >= 0 ) { - io[i] = ints[j]; - } - j++; - if( j > ints[0] ) { - break; - } - if( ints[j] >= 0 ) { - irq[i] = ints[j]; - } - j++; - } - return 1; -} -__setup("ip2=", ip2_setup); -#endif /* !MODULE */ diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 689f9dcd3b8..6774572d375 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -150,15 +150,12 @@ static int ip2_read_proc(char *, char **, off_t, int, int *, void * ); /*************/ /* String constants to identify ourselves */ -static char *pcName = "Computone IntelliPort Plus multiport driver"; -static char *pcVersion = "1.2.14"; +static const char pcName[] = "Computone IntelliPort Plus multiport driver"; +static const char pcVersion[] = "1.2.14"; /* String constants for port names */ -static char *pcDriver_name = "ip2"; -static char *pcIpl = "ip2ipl"; - -// cheezy kludge or genius - you decide? -int ip2_loadmain(int *, int *); +static const char pcDriver_name[] = "ip2"; +static const char pcIpl[] = "ip2ipl"; /***********************/ /* Function Prototypes */ @@ -240,8 +237,8 @@ static const struct file_operations ip2_ipl = { .open = ip2_ipl_open, }; -static unsigned long irq_counter = 0; -static unsigned long bh_counter = 0; +static unsigned long irq_counter; +static unsigned long bh_counter; // Use immediate queue to service interrupts #define USE_IQI @@ -252,7 +249,6 @@ static unsigned long bh_counter = 0; */ #define POLL_TIMEOUT (jiffies + 1) static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0); -static char TimerOn; #ifdef IP2DEBUG_TRACE /* Trace (debug) buffer data */ @@ -268,8 +264,8 @@ static int tracewrap; /**********/ #if defined(MODULE) && defined(IP2DEBUG_OPEN) -#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ - tty->name,(pCh->flags),ip2_tty_driver->refcount, \ +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \ + tty->name,(pCh->flags), \ tty->count,/*GET_USE_COUNT(module)*/0,s) #else #define DBG_CNT(s) @@ -287,8 +283,9 @@ static int tracewrap; MODULE_AUTHOR("Doug McNash"); MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_LICENSE("GPL"); -static int poll_only = 0; +static int poll_only; static int Eisa_irq; static int Eisa_slot; @@ -297,34 +294,46 @@ static int iindx; static char rirqs[IP2_MAX_BOARDS]; static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; +/* Note: Add compiled in defaults to these arrays, not to the structure + in ip2.h any longer. That structure WILL get overridden + by these values, or command line values, or insmod values!!! =mhw= +*/ +static int io[IP2_MAX_BOARDS]; +static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 }; + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards"); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards"); +module_param(poll_only, bool, 0); +MODULE_PARM_DESC(poll_only, "Do not use card interrupts"); + /* for sysfs class support */ static struct class *ip2_class; -// Some functions to keep track of what irq's we have +/* Some functions to keep track of what irqs we have */ -static int -is_valid_irq(int irq) +static int __init is_valid_irq(int irq) { int *i = Valid_Irqs; - while ((*i != 0) && (*i != irq)) { + while (*i != 0 && *i != irq) i++; - } - return (*i); + + return *i; } -static void -mark_requested_irq( char irq ) +static void __init mark_requested_irq(char irq) { rirqs[iindx++] = irq; } -#ifdef MODULE -static int -clear_requested_irq( char irq ) +static int __exit clear_requested_irq(char irq) { int i; - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { if (rirqs[i] == irq) { rirqs[i] = 0; return 1; @@ -332,17 +341,15 @@ clear_requested_irq( char irq ) } return 0; } -#endif -static int -have_requested_irq( char irq ) +static int have_requested_irq(char irq) { - // array init to zeros so 0 irq will not be requested as a side effect + /* array init to zeros so 0 irq will not be requested as a side + * effect */ int i; - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) if (rirqs[i] == irq) return 1; - } return 0; } @@ -361,53 +368,45 @@ have_requested_irq( char irq ) /* handle subsequent installations of the driver. All memory allocated by the */ /* driver should be returned since it may be unloaded from memory. */ /******************************************************************************/ -#ifdef MODULE -void __exit -ip2_cleanup_module(void) +static void __exit ip2_cleanup_module(void) { int err; int i; -#ifdef IP2DEBUG_INIT - printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion ); -#endif - /* Stop poll timer if we had one. */ - if ( TimerOn ) { - del_timer ( &PollTimer ); - TimerOn = 0; - } + del_timer_sync(&PollTimer); /* Reset the boards we have. */ - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( i2BoardPtrTable[i] ) { - iiReset( i2BoardPtrTable[i] ); - } - } + for (i = 0; i < IP2_MAX_BOARDS; i++) + if (i2BoardPtrTable[i]) + iiReset(i2BoardPtrTable[i]); /* The following is done at most once, if any boards were installed. */ - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( i2BoardPtrTable[i] ) { - iiResetDelay( i2BoardPtrTable[i] ); + for (i = 0; i < IP2_MAX_BOARDS; i++) { + if (i2BoardPtrTable[i]) { + iiResetDelay(i2BoardPtrTable[i]); /* free io addresses and Tibet */ - release_region( ip2config.addr[i], 8 ); + release_region(ip2config.addr[i], 8); device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i)); - device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i + 1)); + device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, + 4 * i + 1)); } /* Disable and remove interrupt handler. */ - if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) { - free_irq ( ip2config.irq[i], (void *)&pcName); - clear_requested_irq( ip2config.irq[i]); + if (ip2config.irq[i] > 0 && + have_requested_irq(ip2config.irq[i])) { + free_irq(ip2config.irq[i], (void *)&pcName); + clear_requested_irq(ip2config.irq[i]); } } class_destroy(ip2_class); - if ( ( err = tty_unregister_driver ( ip2_tty_driver ) ) ) { - printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); - } + err = tty_unregister_driver(ip2_tty_driver); + if (err) + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", + err); put_tty_driver(ip2_tty_driver); unregister_chrdev(IP2_IPL_MAJOR, pcIpl); remove_proc_entry("ip2mem", NULL); - // free memory + /* free memory */ for (i = 0; i < IP2_MAX_BOARDS; i++) { void *pB; #ifdef CONFIG_PCI @@ -417,24 +416,18 @@ ip2_cleanup_module(void) ip2config.pci_dev[i] = NULL; } #endif - if ((pB = i2BoardPtrTable[i]) != 0 ) { - kfree ( pB ); + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + kfree(pB); i2BoardPtrTable[i] = NULL; } - if ((DevTableMem[i]) != NULL ) { - kfree ( DevTableMem[i] ); + if (DevTableMem[i] != NULL) { + kfree(DevTableMem[i]); DevTableMem[i] = NULL; } } - - /* Cleanup the iiEllis subsystem. */ - iiEllisCleanup(); -#ifdef IP2DEBUG_INIT - printk (KERN_DEBUG "IP2 Unloaded\n" ); -#endif } module_exit(ip2_cleanup_module); -#endif /* MODULE */ static const struct tty_operations ip2_ops = { .open = ip2_open, @@ -494,139 +487,168 @@ static const struct firmware *ip2_request_firmware(void) return fw; } -int -ip2_loadmain(int *iop, int *irqp) +#ifndef MODULE +/****************************************************************************** + * ip2_setup: + * str: kernel command line string + * + * Can't autoprobe the boards so user must specify configuration on + * kernel command line. Sane people build it modular but the others + * come here. + * + * Alternating pairs of io,irq for up to 4 boards. + * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3 + * + * io=0 => No board + * io=1 => PCI + * io=2 => EISA + * else => ISA I/O address + * + * irq=0 or invalid for ISA will revert to polling mode + * + * Any value = -1, do not overwrite compiled in value. + * + ******************************************************************************/ +static int __init ip2_setup(char *str) +{ + int j, ints[10]; /* 4 boards, 2 parameters + 2 */ + unsigned int i; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + for (i = 0, j = 1; i < 4; i++) { + if (j > ints[0]) + break; + if (ints[j] >= 0) + io[i] = ints[j]; + j++; + if (j > ints[0]) + break; + if (ints[j] >= 0) + irq[i] = ints[j]; + j++; + } + return 1; +} +__setup("ip2=", ip2_setup); +#endif /* !MODULE */ + +static int __init ip2_loadmain(void) { int i, j, box; int err = 0; - static int loaded; i2eBordStrPtr pB = NULL; int rc = -1; - static struct pci_dev *pci_dev_i = NULL; + struct pci_dev *pdev = NULL; const struct firmware *fw = NULL; - ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); + if (poll_only) { + /* Hard lock the interrupts to zero */ + irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0; + } + + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0); /* process command line arguments to modprobe or insmod i.e. iop & irqp */ /* irqp and iop should ALWAYS be specified now... But we check them individually just to be sure, anyways... */ - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if (iop) { - ip2config.addr[i] = iop[i]; - if (irqp) { - if( irqp[i] >= 0 ) { - ip2config.irq[i] = irqp[i]; - } else { - ip2config.irq[i] = 0; - } - // This is a little bit of a hack. If poll_only=1 on command - // line back in ip2.c OR all IRQs on all specified boards are - // explicitly set to 0, then drop to poll only mode and override - // PCI or EISA interrupts. This superceeds the old hack of - // triggering if all interrupts were zero (like da default). - // Still a hack but less prone to random acts of terrorism. - // - // What we really should do, now that the IRQ default is set - // to -1, is to use 0 as a hard coded, do not probe. - // - // /\/\|=mhw=|\/\/ - poll_only |= irqp[i]; - } - } + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + ip2config.addr[i] = io[i]; + if (irq[i] >= 0) + ip2config.irq[i] = irq[i]; + else + ip2config.irq[i] = 0; + /* This is a little bit of a hack. If poll_only=1 on command + line back in ip2.c OR all IRQs on all specified boards are + explicitly set to 0, then drop to poll only mode and override + PCI or EISA interrupts. This superceeds the old hack of + triggering if all interrupts were zero (like da default). + Still a hack but less prone to random acts of terrorism. + + What we really should do, now that the IRQ default is set + to -1, is to use 0 as a hard coded, do not probe. + + /\/\|=mhw=|\/\/ + */ + poll_only |= irq[i]; } poll_only = !poll_only; /* Announce our presence */ - printk( KERN_INFO "%s version %s\n", pcName, pcVersion ); - - // ip2 can be unloaded and reloaded for no good reason - // we can't let that happen here or bad things happen - // second load hoses board but not system - fixme later - if (loaded) { - printk( KERN_INFO "Still loaded\n" ); - return 0; - } - loaded++; + printk(KERN_INFO "%s version %s\n", pcName, pcVersion); ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS); if (!ip2_tty_driver) return -ENOMEM; - /* Initialise the iiEllis subsystem. */ - iiEllisInit(); - - /* Initialize arrays. */ - memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable ); - memset( DevTable, 0, sizeof DevTable ); - /* Initialise all the boards we can find (up to the maximum). */ - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - switch ( ip2config.addr[i] ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + switch (ip2config.addr[i]) { case 0: /* skip this slot even if card is present */ break; default: /* ISA */ /* ISA address must be specified */ - if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) { - printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n", - i, ip2config.addr[i] ); + if (ip2config.addr[i] < 0x100 || + ip2config.addr[i] > 0x3f8) { + printk(KERN_ERR "IP2: Bad ISA board %d " + "address %x\n", i, + ip2config.addr[i]); ip2config.addr[i] = 0; - } else { - ip2config.type[i] = ISA; - - /* Check for valid irq argument, set for polling if invalid */ - if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) { - printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]); - ip2config.irq[i] = 0;// 0 is polling and is valid in that sense - } + break; + } + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if + * invalid */ + if (ip2config.irq[i] && + !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n", + ip2config.irq[i]); + /* 0 is polling and is valid in that sense */ + ip2config.irq[i] = 0; } break; case PCI: #ifdef CONFIG_PCI - { - int status; + { + u32 addr; + int status; - pci_dev_i = pci_get_device(PCI_VENDOR_ID_COMPUTONE, - PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); - if (pci_dev_i != NULL) { - unsigned int addr; - - if (pci_enable_device(pci_dev_i)) { - printk( KERN_ERR "IP2: can't enable PCI device at %s\n", - pci_name(pci_dev_i)); - break; - } - ip2config.type[i] = PCI; - ip2config.pci_dev[i] = pci_dev_get(pci_dev_i); - status = - pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); - if ( addr & 1 ) { - ip2config.addr[i]=(USHORT)(addr&0xfffe); - } else { - printk( KERN_ERR "IP2: PCI I/O address error\n"); - } + pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev); + if (pdev == NULL) { + ip2config.addr[i] = 0; + printk(KERN_ERR "IP2: PCI board %d not " + "found\n", i); + break; + } -// If the PCI BIOS assigned it, lets try and use it. If we -// can't acquire it or it screws up, deal with it then. - -// if (!is_valid_irq(pci_irq)) { -// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); -// pci_irq = 0; -// } - ip2config.irq[i] = pci_dev_i->irq; - } else { // ann error - ip2config.addr[i] = 0; - printk(KERN_ERR "IP2: PCI board %d not found\n", i); - } + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, "can't enable device\n"); + break; } + ip2config.type[i] = PCI; + ip2config.pci_dev[i] = pci_dev_get(pdev); + status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, + &addr); + if (addr & 1) + ip2config.addr[i] = (USHORT)(addr & 0xfffe); + else + dev_err(&pdev->dev, "I/O address error\n"); + + ip2config.irq[i] = pdev->irq; + } #else - printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); - printk( KERN_ERR "IP2: configured in this kernel.\n"); - printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n"); + printk(KERN_ERR "IP2: PCI card specified but PCI " + "support not enabled.\n"); + printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI " + "defined!\n"); #endif /* CONFIG_PCI */ break; case EISA: - if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) { + ip2config.addr[i] = find_eisa_board(Eisa_slot + 1); + if (ip2config.addr[i] != 0) { /* Eisa_irq set as side effect, boo */ ip2config.type[i] = EISA; } @@ -634,31 +656,32 @@ ip2_loadmain(int *iop, int *irqp) break; } /* switch */ } /* for */ - if (pci_dev_i) - pci_dev_put(pci_dev_i); + pci_dev_put(pdev); - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( ip2config.addr[i] ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (ip2config.addr[i]) { pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL); if (pB) { i2BoardPtrTable[i] = pB; - iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer ); - iiReset( pB ); - } else { - printk(KERN_ERR "IP2: board memory allocation error\n"); - } + iiSetAddress(pB, ip2config.addr[i], + ii2DelayTimer); + iiReset(pB); + } else + printk(KERN_ERR "IP2: board memory allocation " + "error\n"); } } - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( ( pB = i2BoardPtrTable[i] ) != NULL ) { - iiResetDelay( pB ); + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + iiResetDelay(pB); break; } } - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { /* We don't want to request the firmware unless we have at least one board */ - if ( i2BoardPtrTable[i] != NULL ) { + if (i2BoardPtrTable[i] != NULL) { if (!fw) fw = ip2_request_firmware(); if (!fw) @@ -669,7 +692,7 @@ ip2_loadmain(int *iop, int *irqp) if (fw) release_firmware(fw); - ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 ); + ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0); ip2_tty_driver->owner = THIS_MODULE; ip2_tty_driver->name = "ttyF"; @@ -680,20 +703,23 @@ ip2_loadmain(int *iop, int *irqp) ip2_tty_driver->subtype = SERIAL_TYPE_NORMAL; ip2_tty_driver->init_termios = tty_std_termios; ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; - ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(ip2_tty_driver, &ip2_ops); - ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 ); + ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0); - /* Register the tty devices. */ - if ( ( err = tty_register_driver ( ip2_tty_driver ) ) ) { - printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err); + err = tty_register_driver(ip2_tty_driver); + if (err) { + printk(KERN_ERR "IP2: failed to register tty driver\n"); put_tty_driver(ip2_tty_driver); - return -EINVAL; - } else - /* Register the IPL driver. */ - if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { - printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); + return err; /* leaking resources */ + } + + err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl); + if (err) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", + err); } else { /* create the sysfs class */ ip2_class = class_create(THIS_MODULE, "ip2"); @@ -705,84 +731,86 @@ ip2_loadmain(int *iop, int *irqp) /* Register the read_procmem thing */ if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) { printk(KERN_ERR "IP2: failed to register read_procmem\n"); - } else { + return -EIO; /* leaking resources */ + } - ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 ); - /* Register the interrupt handler or poll handler, depending upon the - * specified interrupt. - */ + ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0); + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( 0 == ip2config.addr[i] ) { - continue; - } + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (ip2config.addr[i] == 0) + continue; - if ( NULL != ( pB = i2BoardPtrTable[i] ) ) { - device_create_drvdata(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i), - NULL, "ipl%d", i); - device_create_drvdata(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i + 1), - NULL, "stat%d", i); - - for ( box = 0; box < ABS_MAX_BOXES; ++box ) - { - for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) - { - if ( pB->i2eChannelMap[box] & (1 << j) ) - { - tty_register_device(ip2_tty_driver, - j + ABS_BIGGEST_BOX * - (box+i*ABS_MAX_BOXES), NULL); - } - } - } - } + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + device_create_drvdata(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i), + NULL, "ipl%d", i); + device_create_drvdata(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + NULL, "stat%d", i); + + for (box = 0; box < ABS_MAX_BOXES; box++) + for (j = 0; j < ABS_BIGGEST_BOX; j++) + if (pB->i2eChannelMap[box] & (1 << j)) + tty_register_device( + ip2_tty_driver, + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES), + NULL); + } - if (poll_only) { -// Poll only forces driver to only use polling and -// to ignore the probed PCI or EISA interrupts. - ip2config.irq[i] = CIR_POLL; - } - if ( ip2config.irq[i] == CIR_POLL ) { + if (poll_only) { + /* Poll only forces driver to only use polling and + to ignore the probed PCI or EISA interrupts. */ + ip2config.irq[i] = CIR_POLL; + } + if (ip2config.irq[i] == CIR_POLL) { retry: - if (!TimerOn) { - PollTimer.expires = POLL_TIMEOUT; - add_timer ( &PollTimer ); - TimerOn = 1; - printk( KERN_INFO "IP2: polling\n"); - } - } else { - if (have_requested_irq(ip2config.irq[i])) - continue; - rc = request_irq( ip2config.irq[i], ip2_interrupt, - IP2_SA_FLAGS | (ip2config.type[i] == PCI ? IRQF_SHARED : 0), - pcName, i2BoardPtrTable[i]); - if (rc) { - printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc); - ip2config.irq[i] = CIR_POLL; - printk( KERN_INFO "IP2: Polling %ld/sec.\n", - (POLL_TIMEOUT - jiffies)); - goto retry; - } - mark_requested_irq(ip2config.irq[i]); - /* Initialise the interrupt handler bottom half (aka slih). */ + if (!timer_pending(&PollTimer)) { + mod_timer(&PollTimer, POLL_TIMEOUT); + printk(KERN_INFO "IP2: polling\n"); } - } - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( i2BoardPtrTable[i] ) { - set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */ + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq(ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | + (ip2config.type[i] == PCI ? IRQF_SHARED : 0), + pcName, i2BoardPtrTable[i]); + if (rc) { + printk(KERN_ERR "IP2: request_irq failed: " + "error %d\n", rc); + ip2config.irq[i] = CIR_POLL; + printk(KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half + * (aka slih). */ } } - ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); - goto out; + + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (i2BoardPtrTable[i]) { + /* set and enable board interrupt */ + set_irq(i, ip2config.irq[i]); + } + } + + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0); + + return 0; out_chrdev: unregister_chrdev(IP2_IPL_MAJOR, "ip2"); -out: + /* unregister and put tty here */ return err; } +module_init(ip2_loadmain); /******************************************************************************/ /* Function: ip2_init_board() */ @@ -1199,9 +1227,8 @@ ip2_polled_interrupt(void) { int i; i2eBordStrPtr pB; - const int irq = 0; - ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq ); + ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0); /* Service just the boards on the list using this irq */ for( i = 0; i < i2nBoards; ++i ) { @@ -1210,9 +1237,8 @@ ip2_polled_interrupt(void) // Only process those boards which match our IRQ. // IRQ = 0 for polled boards, we won't poll "IRQ" boards - if ( pB && (pB->i2eUsingIrq == irq) ) { + if (pB && pB->i2eUsingIrq == 0) ip2_irq_work(pB); - } } ++irq_counter; @@ -1250,16 +1276,12 @@ ip2_poll(unsigned long arg) { ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); - TimerOn = 0; // it's the truth but not checked in service - // Just polled boards, IRQ = 0 will hit all non-interrupt boards. // It will NOT poll boards handled by hard interrupts. // The issue of queued BH interrupts is handled in ip2_interrupt(). ip2_polled_interrupt(); - PollTimer.expires = POLL_TIMEOUT; - add_timer( &PollTimer ); - TimerOn = 1; + mod_timer(&PollTimer, POLL_TIMEOUT); ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); } @@ -2871,7 +2893,7 @@ ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg ) case 13: switch ( cmd ) { case 64: /* Driver - ip2stat */ - rc = put_user(ip2_tty_driver->refcount, pIndex++ ); + rc = put_user(-1, pIndex++ ); rc = put_user(irq_counter, pIndex++ ); rc = put_user(bh_counter, pIndex++ ); break; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 8f7cc190b62..7d30ee1d3fc 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -421,17 +421,16 @@ static void isicom_tx(unsigned long _data) if (retries >= 100) goto unlock; + tty = tty_port_tty_get(&port->port); + if (tty == NULL) + goto put_unlock; + for (; count > 0; count--, port++) { /* port not active or tx disabled to force flow control */ if (!(port->port.flags & ASYNC_INITIALIZED) || !(port->status & ISI_TXOK)) continue; - tty = port->port.tty; - - if (tty == NULL) - continue; - txcount = min_t(short, TX_SIZE, port->xmit_cnt); if (txcount <= 0 || tty->stopped || tty->hw_stopped) continue; @@ -489,6 +488,8 @@ static void isicom_tx(unsigned long _data) tty_wakeup(tty); } +put_unlock: + tty_kref_put(tty); unlock: spin_unlock_irqrestore(&isi_card[card].card_lock, flags); /* schedule another tx for hopefully in about 10ms */ @@ -547,7 +548,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty == NULL) { word_count = byte_count >> 1; while (byte_count > 1) { @@ -588,7 +589,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) } if (port->port.flags & ASYNC_CTS_FLOW) { - if (port->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (header & ISI_CTS) { port->port.tty->hw_stopped = 0; /* start tx ing */ @@ -597,7 +598,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) tty_wakeup(tty); } } else if (!(header & ISI_CTS)) { - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; /* stop tx ing */ port->status &= ~(ISI_TXOK | ISI_CTS); } @@ -660,24 +661,21 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) } outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); + tty_kref_put(tty); return IRQ_HANDLED; } -static void isicom_config_port(struct isi_port *port) +static void isicom_config_port(struct tty_struct *tty) { + struct isi_port *port = tty->driver_data; struct isi_board *card = port->card; - struct tty_struct *tty; unsigned long baud; unsigned long base = card->base; u16 channel_setup, channel = port->channel, shift_count = card->shift_count; unsigned char flow_ctrl; - tty = port->port.tty; - - if (tty == NULL) - return; /* FIXME: Switch to new tty baud API */ baud = C_BAUD(tty); if (baud & CBAUDEX) { @@ -690,7 +688,7 @@ static void isicom_config_port(struct isi_port *port) /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ if (baud < 1 || baud > 4) - port->port.tty->termios->c_cflag &= ~CBAUDEX; + tty->termios->c_cflag &= ~CBAUDEX; else baud += 15; } @@ -797,8 +795,9 @@ static inline void isicom_setup_board(struct isi_board *bp) spin_unlock_irqrestore(&bp->card_lock, flags); } -static int isicom_setup_port(struct isi_port *port) +static int isicom_setup_port(struct tty_struct *tty) { + struct isi_port *port = tty->driver_data; struct isi_board *card = port->card; unsigned long flags; @@ -808,8 +807,7 @@ static int isicom_setup_port(struct isi_port *port) return -ENOMEM; spin_lock_irqsave(&card->card_lock, flags); - if (port->port.tty) - clear_bit(TTY_IO_ERROR, &port->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); if (port->port.count == 1) card->count++; @@ -823,7 +821,7 @@ static int isicom_setup_port(struct isi_port *port) InterruptTheCard(card->base); } - isicom_config_port(port); + isicom_config_port(tty); port->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); @@ -934,8 +932,8 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) port->port.count++; tty->driver_data = port; - port->port.tty = tty; - error = isicom_setup_port(port); + tty_port_tty_set(&port->port, tty); + error = isicom_setup_port(tty); if (error == 0) error = block_til_ready(tty, filp, port); return error; @@ -955,15 +953,17 @@ static void isicom_shutdown_port(struct isi_port *port) struct isi_board *card = port->card; struct tty_struct *tty; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); - if (!(port->port.flags & ASYNC_INITIALIZED)) + if (!(port->port.flags & ASYNC_INITIALIZED)) { + tty_kref_put(tty); return; + } tty_port_free_xmit_buf(&port->port); port->port.flags &= ~ASYNC_INITIALIZED; /* 3rd October 2000 : Vinayak P Risbud */ - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); /*Fix done by Anil .S on 30-04-2001 remote login through isi port has dtr toggle problem @@ -1243,9 +1243,10 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static int isicom_set_serial_info(struct isi_port *port, - struct serial_struct __user *info) +static int isicom_set_serial_info(struct tty_struct *tty, + struct serial_struct __user *info) { + struct isi_port *port = tty->driver_data; struct serial_struct newinfo; int reconfig_port; @@ -1276,7 +1277,7 @@ static int isicom_set_serial_info(struct isi_port *port, if (reconfig_port) { unsigned long flags; spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(port); + isicom_config_port(tty); spin_unlock_irqrestore(&port->card->card_lock, flags); } unlock_kernel(); @@ -1318,7 +1319,7 @@ static int isicom_ioctl(struct tty_struct *tty, struct file *filp, return isicom_get_serial_info(port, argp); case TIOCSSERIAL: - return isicom_set_serial_info(port, argp); + return isicom_set_serial_info(tty, argp); default: return -ENOIOCTLCMD; @@ -1341,7 +1342,7 @@ static void isicom_set_termios(struct tty_struct *tty, return; spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(port); + isicom_config_port(tty); spin_unlock_irqrestore(&port->card->card_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && @@ -1419,7 +1420,7 @@ static void isicom_hangup(struct tty_struct *tty) port->port.count = 0; port->port.flags &= ~ASYNC_NORMAL_ACTIVE; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); wake_up_interruptible(&port->port.open_wait); } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 843a2afaf20..505d7a1f6b8 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -623,24 +623,25 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp); static void stli_poll(unsigned long arg); static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp); -static int stli_initopen(struct stlibrd *brdp, struct stliport *portp); +static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp); static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); -static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp); -static int stli_setport(struct stliport *portp); +static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, + struct stliport *portp, struct file *filp); +static int stli_setport(struct tty_struct *tty); static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp); -static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermios *tiosp); +static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp); static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts); static long stli_mktiocm(unsigned long sigvalue); static void stli_read(struct stlibrd *brdp, struct stliport *portp); static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp); -static int stli_setserial(struct stliport *portp, struct serial_struct __user *sp); +static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp); static int stli_getbrdstats(combrd_t __user *bp); -static int stli_getportstats(struct stliport *portp, comstats_t __user *cp); -static int stli_portcmdstats(struct stliport *portp); +static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp); +static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp); static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp); static int stli_getportstruct(struct stliport __user *arg); static int stli_getbrdstruct(struct stlibrd __user *arg); @@ -731,12 +732,16 @@ static void stli_cleanup_ports(struct stlibrd *brdp) { struct stliport *portp; unsigned int j; + struct tty_struct *tty; for (j = 0; j < STL_MAXPORTS; j++) { portp = brdp->ports[j]; if (portp != NULL) { - if (portp->port.tty != NULL) - tty_hangup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty != NULL) { + tty_hangup(tty); + tty_kref_put(tty); + } kfree(portp); } } @@ -824,7 +829,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * requires several commands to the board we will need to wait for any * other open that is already initializing the port. */ - portp->port.tty = tty; + tty_port_tty_set(&portp->port, tty); tty->driver_data = portp; portp->port.count++; @@ -835,7 +840,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { set_bit(ST_INITIALIZING, &portp->state); - if ((rc = stli_initopen(brdp, portp)) >= 0) { + if ((rc = stli_initopen(tty, brdp, portp)) >= 0) { portp->port.flags |= ASYNC_INITIALIZED; clear_bit(TTY_IO_ERROR, &tty->flags); } @@ -864,7 +869,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * then also we might have to wait for carrier. */ if (!(filp->f_flags & O_NONBLOCK)) { - if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0) + if ((rc = stli_waitcarrier(tty, brdp, portp, filp)) != 0) return rc; } portp->port.flags |= ASYNC_NORMAL_ACTIVE; @@ -930,7 +935,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) stli_flushbuffer(tty); tty->closing = 0; - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); if (portp->openwaitcnt) { if (portp->close_delay) @@ -952,9 +957,9 @@ static void stli_close(struct tty_struct *tty, struct file *filp) * this still all happens pretty quickly. */ -static int stli_initopen(struct stlibrd *brdp, struct stliport *portp) +static int stli_initopen(struct tty_struct *tty, + struct stlibrd *brdp, struct stliport *portp) { - struct tty_struct *tty; asynotify_t nt; asyport_t aport; int rc; @@ -969,10 +974,7 @@ static int stli_initopen(struct stlibrd *brdp, struct stliport *portp) sizeof(asynotify_t), 0)) < 0) return rc; - tty = portp->port.tty; - if (tty == NULL) - return -ENODEV; - stli_mkasyport(portp, &aport, tty->termios); + stli_mkasyport(tty, portp, &aport, tty->termios); if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) return rc; @@ -1161,22 +1163,21 @@ static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned l * waiting for the command to complete - so must have user context. */ -static int stli_setport(struct stliport *portp) +static int stli_setport(struct tty_struct *tty) { + struct stliport *portp = tty->driver_data; struct stlibrd *brdp; asyport_t aport; if (portp == NULL) return -ENODEV; - if (portp->port.tty == NULL) - return -ENODEV; if (portp->brdnr >= stli_nrbrds) return -ENODEV; brdp = stli_brds[portp->brdnr]; if (brdp == NULL) return -ENODEV; - stli_mkasyport(portp, &aport, portp->port.tty->termios); + stli_mkasyport(tty, portp, &aport, tty->termios); return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)); } @@ -1187,7 +1188,8 @@ static int stli_setport(struct stliport *portp) * maybe because if we are clocal then we don't need to wait... */ -static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp) +static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, + struct stliport *portp, struct file *filp) { unsigned long flags; int rc, doclocal; @@ -1195,7 +1197,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct rc = 0; doclocal = 0; - if (portp->port.tty->termios->c_cflag & CLOCAL) + if (tty->termios->c_cflag & CLOCAL) doclocal++; spin_lock_irqsave(&stli_lock, flags); @@ -1373,8 +1375,6 @@ static void stli_flushchars(struct tty_struct *tty) stli_txcookrealsize = 0; stli_txcooktty = NULL; - if (tty == NULL) - return; if (cooktty == NULL) return; if (tty != cooktty) @@ -1572,10 +1572,11 @@ static int stli_getserial(struct stliport *portp, struct serial_struct __user *s * just quietly ignore any requests to change irq, etc. */ -static int stli_setserial(struct stliport *portp, struct serial_struct __user *sp) +static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp) { struct serial_struct sio; int rc; + struct stliport *portp = tty->driver_data; if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) return -EFAULT; @@ -1594,7 +1595,7 @@ static int stli_setserial(struct stliport *portp, struct serial_struct __user *s portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; - if ((rc = stli_setport(portp)) < 0) + if ((rc = stli_setport(tty)) < 0) return rc; return 0; } @@ -1685,17 +1686,17 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm rc = stli_getserial(portp, argp); break; case TIOCSSERIAL: - rc = stli_setserial(portp, argp); + rc = stli_setserial(tty, argp); break; case STL_GETPFLAG: rc = put_user(portp->pflag, (unsigned __user *)argp); break; case STL_SETPFLAG: if ((rc = get_user(portp->pflag, (unsigned __user *)argp)) == 0) - stli_setport(portp); + stli_setport(tty); break; case COM_GETPORTSTATS: - rc = stli_getportstats(portp, argp); + rc = stli_getportstats(tty, portp, argp); break; case COM_CLRPORTSTATS: rc = stli_clrportstats(portp, argp); @@ -1729,8 +1730,6 @@ static void stli_settermios(struct tty_struct *tty, struct ktermios *old) struct ktermios *tiosp; asyport_t aport; - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1742,7 +1741,7 @@ static void stli_settermios(struct tty_struct *tty, struct ktermios *old) tiosp = tty->termios; - stli_mkasyport(portp, &aport, tiosp); + stli_mkasyport(tty, portp, &aport, tiosp); stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1); stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, @@ -1854,7 +1853,7 @@ static void stli_hangup(struct tty_struct *tty) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; portp->port.count = 0; spin_unlock_irqrestore(&stli_lock, flags); @@ -1935,8 +1934,6 @@ static void stli_waituntilsent(struct tty_struct *tty, int timeout) struct stliport *portp; unsigned long tend; - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1998,7 +1995,7 @@ static int stli_portinfo(struct stlibrd *brdp, struct stliport *portp, int portn char *sp, *uart; int rc, cnt; - rc = stli_portcmdstats(portp); + rc = stli_portcmdstats(NULL, portp); uart = "UNKNOWN"; if (brdp->state & BST_STARTED) { @@ -2188,7 +2185,7 @@ static void stli_read(struct stlibrd *brdp, struct stliport *portp) if (test_bit(ST_RXSTOP, &portp->state)) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -2230,6 +2227,7 @@ static void stli_read(struct stlibrd *brdp, struct stliport *portp) set_bit(ST_RXING, &portp->state); tty_schedule_flip(tty); + tty_kref_put(tty); } /*****************************************************************************/ @@ -2362,7 +2360,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) if (ap->notify) { nt = ap->changed; ap->notify = 0; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (nt.signal & SG_DCD) { oldsigs = portp->sigs; @@ -2399,6 +2397,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) tty_schedule_flip(tty); } } + tty_kref_put(tty); if (nt.data & DT_RXBUSY) { donerx++; @@ -2535,14 +2534,15 @@ static void stli_poll(unsigned long arg) * the slave. */ -static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermios *tiosp) +static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, + asyport_t *pp, struct ktermios *tiosp) { memset(pp, 0, sizeof(asyport_t)); /* * Start of by setting the baud, char size, parity and stop bit info. */ - pp->baudout = tty_get_baud_rate(portp->port.tty); + pp->baudout = tty_get_baud_rate(tty); if ((tiosp->c_cflag & CBAUD) == B38400) { if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) pp->baudout = 57600; @@ -2695,7 +2695,7 @@ static int stli_initports(struct stlibrd *brdp) printk("STALLION: failed to allocate port structure\n"); continue; } - + tty_port_init(&portp->port); portp->magic = STLI_PORTMAGIC; portp->portnr = i; portp->brdnr = brdp->brdnr; @@ -4220,7 +4220,7 @@ static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, * what port to get stats for (used through board control device). */ -static int stli_portcmdstats(struct stliport *portp) +static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp) { unsigned long flags; struct stlibrd *brdp; @@ -4249,15 +4249,15 @@ static int stli_portcmdstats(struct stliport *portp) stli_comstats.flags = portp->port.flags; spin_lock_irqsave(&brd_lock, flags); - if (portp->port.tty != NULL) { - if (portp->port.tty->driver_data == portp) { - stli_comstats.ttystate = portp->port.tty->flags; + if (tty != NULL) { + if (portp->port.tty == tty) { + stli_comstats.ttystate = tty->flags; stli_comstats.rxbuffered = -1; - if (portp->port.tty->termios != NULL) { - stli_comstats.cflags = portp->port.tty->termios->c_cflag; - stli_comstats.iflags = portp->port.tty->termios->c_iflag; - stli_comstats.oflags = portp->port.tty->termios->c_oflag; - stli_comstats.lflags = portp->port.tty->termios->c_lflag; + if (tty->termios != NULL) { + stli_comstats.cflags = tty->termios->c_cflag; + stli_comstats.iflags = tty->termios->c_iflag; + stli_comstats.oflags = tty->termios->c_oflag; + stli_comstats.lflags = tty->termios->c_lflag; } } } @@ -4294,7 +4294,8 @@ static int stli_portcmdstats(struct stliport *portp) * what port to get stats for (used through board control device). */ -static int stli_getportstats(struct stliport *portp, comstats_t __user *cp) +static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, + comstats_t __user *cp) { struct stlibrd *brdp; int rc; @@ -4312,7 +4313,7 @@ static int stli_getportstats(struct stliport *portp, comstats_t __user *cp) if (!brdp) return -ENODEV; - if ((rc = stli_portcmdstats(portp)) < 0) + if ((rc = stli_portcmdstats(tty, portp)) < 0) return rc; return copy_to_user(cp, &stli_comstats, sizeof(comstats_t)) ? @@ -4427,7 +4428,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un switch (cmd) { case COM_GETPORTSTATS: - rc = stli_getportstats(NULL, argp); + rc = stli_getportstats(NULL, NULL, argp); done++; break; case COM_CLRPORTSTATS: diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index d3d7864e0c1..5df4003ad87 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -205,7 +205,7 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file, static void moxa_poll(unsigned long); static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); static void moxa_setup_empty_event(struct tty_struct *); -static void moxa_shut_down(struct moxa_port *); +static void moxa_shut_down(struct tty_struct *); /* * moxa board interface functions: */ @@ -217,7 +217,7 @@ static void MoxaPortLineCtrl(struct moxa_port *, int, int); static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); static int MoxaPortLineStatus(struct moxa_port *); static void MoxaPortFlushData(struct moxa_port *, int); -static int MoxaPortWriteData(struct moxa_port *, const unsigned char *, int); +static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int); static int MoxaPortReadData(struct moxa_port *); static int MoxaPortTxQueue(struct moxa_port *); static int MoxaPortRxQueue(struct moxa_port *); @@ -332,6 +332,7 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, for (i = 0; i < MAX_BOARDS; i++) { p = moxa_boards[i].ports; for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { + struct tty_struct *ttyp; memset(&tmp, 0, sizeof(tmp)); if (!moxa_boards[i].ready) goto copy; @@ -344,10 +345,12 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, if (status & 4) tmp.dcd = 1; - if (!p->port.tty || !p->port.tty->termios) + ttyp = tty_port_tty_get(&p->port); + if (!ttyp || !ttyp->termios) tmp.cflag = p->cflag; else - tmp.cflag = p->port.tty->termios->c_cflag; + tmp.cflag = ttyp->termios->c_cflag; + tty_kref_put(tty); copy: if (copy_to_user(argm, &tmp, sizeof(tmp))) { mutex_unlock(&moxa_openlock); @@ -880,8 +883,14 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) /* pci hot-un-plug support */ for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) - tty_hangup(brd->ports[a].port.tty); + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { + struct tty_struct *tty = tty_port_tty_get( + &brd->ports[a].port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + } while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1096,13 +1105,14 @@ static void __exit moxa_exit(void) module_init(moxa_init); module_exit(moxa_exit); -static void moxa_close_port(struct moxa_port *ch) +static void moxa_close_port(struct tty_struct *tty) { - moxa_shut_down(ch); + struct moxa_port *ch = tty->driver_data; + moxa_shut_down(tty); MoxaPortFlushData(ch, 2); ch->port.flags &= ~ASYNC_NORMAL_ACTIVE; - ch->port.tty->driver_data = NULL; - ch->port.tty = NULL; + tty->driver_data = NULL; + tty_port_tty_set(&ch->port, NULL); } static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, @@ -1161,7 +1171,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; ch->port.count++; tty->driver_data = ch; - ch->port.tty = tty; + tty_port_tty_set(&ch->port, tty); if (!(ch->port.flags & ASYNC_INITIALIZED)) { ch->statusflags = 0; moxa_set_tty_param(tty, tty->termios); @@ -1179,7 +1189,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) if (retval) { if (ch->port.count) /* 0 means already hung up... */ if (--ch->port.count == 0) - moxa_close_port(ch); + moxa_close_port(tty); } else ch->port.flags |= ASYNC_NORMAL_ACTIVE; mutex_unlock(&moxa_openlock); @@ -1219,7 +1229,7 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ } - moxa_close_port(ch); + moxa_close_port(tty); unlock: mutex_unlock(&moxa_openlock); } @@ -1234,7 +1244,7 @@ static int moxa_write(struct tty_struct *tty, return 0; spin_lock_bh(&moxa_lock); - len = MoxaPortWriteData(ch, buf, count); + len = MoxaPortWriteData(tty, buf, count); spin_unlock_bh(&moxa_lock); ch->statusflags |= LOWWAIT; @@ -1409,7 +1419,7 @@ static void moxa_hangup(struct tty_struct *tty) return; } ch->port.count = 0; - moxa_close_port(ch); + moxa_close_port(tty); mutex_unlock(&moxa_openlock); wake_up_interruptible(&ch->port.open_wait); @@ -1417,11 +1427,14 @@ static void moxa_hangup(struct tty_struct *tty) static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { + struct tty_struct *tty; dcd = !!dcd; - if (dcd != p->DCDState && p->port.tty && C_CLOCAL(p->port.tty)) { - if (!dcd) - tty_hangup(p->port.tty); + if (dcd != p->DCDState) { + tty = tty_port_tty_get(&p->port); + if (tty && C_CLOCAL(tty) && !dcd) + tty_hangup(tty); + tty_kref_put(tty); } p->DCDState = dcd; } @@ -1429,7 +1442,7 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) static int moxa_poll_port(struct moxa_port *p, unsigned int handle, u16 __iomem *ip) { - struct tty_struct *tty = p->port.tty; + struct tty_struct *tty = tty_port_tty_get(&p->port); void __iomem *ofsAddr; unsigned int inited = p->port.flags & ASYNC_INITIALIZED; u16 intr; @@ -1476,6 +1489,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, tty_insert_flip_char(tty, 0, TTY_BREAK); tty_schedule_flip(tty); } + tty_kref_put(tty); if (intr & IntrLine) moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state); @@ -1560,9 +1574,9 @@ static void moxa_setup_empty_event(struct tty_struct *tty) spin_unlock_bh(&moxa_lock); } -static void moxa_shut_down(struct moxa_port *ch) +static void moxa_shut_down(struct tty_struct *tty) { - struct tty_struct *tp = ch->port.tty; + struct moxa_port *ch = tty->driver_data; if (!(ch->port.flags & ASYNC_INITIALIZED)) return; @@ -1572,7 +1586,7 @@ static void moxa_shut_down(struct moxa_port *ch) /* * If we're a modem control device and HUPCL is on, drop RTS & DTR. */ - if (C_HUPCL(tp)) + if (C_HUPCL(tty)) MoxaPortLineCtrl(ch, 0, 0); spin_lock_bh(&moxa_lock); @@ -1953,9 +1967,10 @@ static int MoxaPortLineStatus(struct moxa_port *port) return val; } -static int MoxaPortWriteData(struct moxa_port *port, +static int MoxaPortWriteData(struct tty_struct *tty, const unsigned char *buffer, int len) { + struct moxa_port *port = tty->driver_data; void __iomem *baseAddr, *ofsAddr, *ofs; unsigned int c, total; u16 head, tail, tx_mask, spage, epage; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index b638403e8e9..8beef50f95a 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -610,15 +610,13 @@ static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, return 0; } -static int mxser_set_baud(struct mxser_port *info, long newspd) +static int mxser_set_baud(struct tty_struct *tty, long newspd) { + struct mxser_port *info = tty->driver_data; int quot = 0, baud; unsigned char cval; - if (!info->port.tty || !info->port.tty->termios) - return -1; - - if (!(info->ioaddr)) + if (!info->ioaddr) return -1; if (newspd > info->max_baud) @@ -626,13 +624,13 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) if (newspd == 134) { quot = 2 * info->baud_base / 269; - tty_encode_baud_rate(info->port.tty, 134, 134); + tty_encode_baud_rate(tty, 134, 134); } else if (newspd) { quot = info->baud_base / newspd; if (quot == 0) quot = 1; baud = info->baud_base/quot; - tty_encode_baud_rate(info->port.tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); } else { quot = 0; } @@ -658,7 +656,7 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ #ifdef BOTHER - if (C_BAUD(info->port.tty) == BOTHER) { + if (C_BAUD(tty) == BOTHER) { quot = info->baud_base % newspd; quot *= 8; if (quot % newspd > newspd / 2) { @@ -679,21 +677,20 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ -static int mxser_change_speed(struct mxser_port *info, - struct ktermios *old_termios) +static int mxser_change_speed(struct tty_struct *tty, + struct ktermios *old_termios) { + struct mxser_port *info = tty->driver_data; unsigned cflag, cval, fcr; int ret = 0; unsigned char status; - if (!info->port.tty || !info->port.tty->termios) - return ret; - cflag = info->port.tty->termios->c_cflag; - if (!(info->ioaddr)) + cflag = tty->termios->c_cflag; + if (!info->ioaddr) return ret; - if (mxser_set_baud_method[info->port.tty->index] == 0) - mxser_set_baud(info, tty_get_baud_rate(info->port.tty)); + if (mxser_set_baud_method[tty->index] == 0) + mxser_set_baud(tty, tty_get_baud_rate(tty)); /* byte size and parity */ switch (cflag & CSIZE) { @@ -762,9 +759,9 @@ static int mxser_change_speed(struct mxser_port *info, info->MCR |= UART_MCR_AFE; } else { status = inb(info->ioaddr + UART_MSR); - if (info->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (status & UART_MSR_CTS) { - info->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; if (info->type != PORT_16550A && !info->board->chip_flag) { outb(info->IER & ~UART_IER_THRI, @@ -774,11 +771,11 @@ static int mxser_change_speed(struct mxser_port *info, outb(info->IER, info->ioaddr + UART_IER); } - tty_wakeup(info->port.tty); + tty_wakeup(tty); } } else { if (!(status & UART_MSR_CTS)) { - info->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; if ((info->type != PORT_16550A) && (!info->board->chip_flag)) { info->IER &= ~UART_IER_THRI; @@ -804,21 +801,21 @@ static int mxser_change_speed(struct mxser_port *info, * Set up parity check flag */ info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->port.tty)) + if (I_INPCK(tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + if (I_BRKINT(tty) || I_PARMRK(tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0; - if (I_IGNBRK(info->port.tty)) { + if (I_IGNBRK(tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ - if (I_IGNPAR(info->port.tty)) { + if (I_IGNPAR(tty)) { info->ignore_status_mask |= UART_LSR_OE | UART_LSR_PE | @@ -830,16 +827,16 @@ static int mxser_change_speed(struct mxser_port *info, } } if (info->board->chip_flag) { - mxser_set_must_xon1_value(info->ioaddr, START_CHAR(info->port.tty)); - mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(info->port.tty)); - if (I_IXON(info->port.tty)) { + mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty)); + mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty)); + if (I_IXON(tty)) { mxser_enable_must_rx_software_flow_control( info->ioaddr); } else { mxser_disable_must_rx_software_flow_control( info->ioaddr); } - if (I_IXOFF(info->port.tty)) { + if (I_IXOFF(tty)) { mxser_enable_must_tx_software_flow_control( info->ioaddr); } else { @@ -855,7 +852,8 @@ static int mxser_change_speed(struct mxser_port *info, return ret; } -static void mxser_check_modem_status(struct mxser_port *port, int status) +static void mxser_check_modem_status(struct tty_struct *tty, + struct mxser_port *port, int status) { /* update input line counters */ if (status & UART_MSR_TERI) @@ -874,10 +872,11 @@ static void mxser_check_modem_status(struct mxser_port *port, int status) wake_up_interruptible(&port->port.open_wait); } + tty = tty_port_tty_get(&port->port); if (port->port.flags & ASYNC_CTS_FLOW) { - if (port->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (status & UART_MSR_CTS) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; if ((port->type != PORT_16550A) && (!port->board->chip_flag)) { @@ -887,11 +886,11 @@ static void mxser_check_modem_status(struct mxser_port *port, int status) outb(port->IER, port->ioaddr + UART_IER); } - tty_wakeup(port->port.tty); + tty_wakeup(tty); } } else { if (!(status & UART_MSR_CTS)) { - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; if (port->type != PORT_16550A && !port->board->chip_flag) { port->IER &= ~UART_IER_THRI; @@ -903,8 +902,9 @@ static void mxser_check_modem_status(struct mxser_port *port, int status) } } -static int mxser_startup(struct mxser_port *info) +static int mxser_startup(struct tty_struct *tty) { + struct mxser_port *info = tty->driver_data; unsigned long page; unsigned long flags; @@ -921,8 +921,7 @@ static int mxser_startup(struct mxser_port *info) } if (!info->ioaddr || !info->type) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); free_page(page); spin_unlock_irqrestore(&info->slock, flags); return 0; @@ -952,8 +951,8 @@ static int mxser_startup(struct mxser_port *info) if (inb(info->ioaddr + UART_LSR) == 0xff) { spin_unlock_irqrestore(&info->slock, flags); if (capable(CAP_SYS_ADMIN)) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); return 0; } else return -ENODEV; @@ -991,14 +990,13 @@ static int mxser_startup(struct mxser_port *info) (void) inb(info->ioaddr + UART_IIR); (void) inb(info->ioaddr + UART_MSR); - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + 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 */ - mxser_change_speed(info, NULL); + mxser_change_speed(tty, NULL); info->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&info->slock, flags); @@ -1009,8 +1007,9 @@ static int mxser_startup(struct mxser_port *info) * This routine will shutdown a serial port; interrupts maybe disabled, and * DTR is dropped if the hangup on close termio flag is on. */ -static void mxser_shutdown(struct mxser_port *info) +static void mxser_shutdown(struct tty_struct *tty) { + struct mxser_port *info = tty->driver_data; unsigned long flags; if (!(info->port.flags & ASYNC_INITIALIZED)) @@ -1035,7 +1034,7 @@ static void mxser_shutdown(struct mxser_port *info) info->IER = 0; outb(0x00, info->ioaddr + UART_IER); - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) + if (tty->termios->c_cflag & HUPCL) info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); outb(info->MCR, info->ioaddr + UART_MCR); @@ -1051,8 +1050,7 @@ static void mxser_shutdown(struct mxser_port *info) /* read data port to reset things */ (void) inb(info->ioaddr + UART_RX); - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; @@ -1084,14 +1082,14 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) return -ENODEV; tty->driver_data = info; - info->port.tty = tty; + tty_port_tty_set(&info->port, tty); /* * Start up serial port */ spin_lock_irqsave(&info->slock, flags); info->port.count++; spin_unlock_irqrestore(&info->slock, flags); - retval = mxser_startup(info); + retval = mxser_startup(tty); if (retval) return retval; @@ -1209,13 +1207,13 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) break; } } - mxser_shutdown(info); + mxser_shutdown(tty); mxser_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->port.tty = NULL; + tty_port_tty_set(&info->port, NULL); if (info->port.blocked_open) { if (info->port.close_delay) schedule_timeout_interruptible(info->port.close_delay); @@ -1337,12 +1335,13 @@ static int mxser_chars_in_buffer(struct tty_struct *tty) * friends of mxser_ioctl() * ------------------------------------------------------------ */ -static int mxser_get_serial_info(struct mxser_port *info, +static int mxser_get_serial_info(struct tty_struct *tty, struct serial_struct __user *retinfo) { + struct mxser_port *info = tty->driver_data; struct serial_struct tmp = { .type = info->type, - .line = info->port.tty->index, + .line = tty->index, .port = info->ioaddr, .irq = info->board->irq, .flags = info->port.flags, @@ -1357,9 +1356,10 @@ static int mxser_get_serial_info(struct mxser_port *info, return 0; } -static int mxser_set_serial_info(struct mxser_port *info, +static int mxser_set_serial_info(struct tty_struct *tty, struct serial_struct __user *new_info) { + struct mxser_port *info = tty->driver_data; struct serial_struct new_serial; speed_t baud; unsigned long sl_flags; @@ -1393,14 +1393,14 @@ static int mxser_set_serial_info(struct mxser_port *info, (new_serial.flags & ASYNC_FLAGS)); info->port.close_delay = new_serial.close_delay * HZ / 100; info->port.closing_wait = new_serial.closing_wait * HZ / 100; - info->port.tty->low_latency = - (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) + ? 1 : 0; if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && (new_serial.baud_base != info->baud_base || new_serial.custom_divisor != info->custom_divisor)) { baud = new_serial.baud_base / new_serial.custom_divisor; - tty_encode_baud_rate(info->port.tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); } } @@ -1411,11 +1411,11 @@ static int mxser_set_serial_info(struct mxser_port *info, if (info->port.flags & ASYNC_INITIALIZED) { if (flags != (info->port.flags & ASYNC_SPD_MASK)) { spin_lock_irqsave(&info->slock, sl_flags); - mxser_change_speed(info, NULL); + mxser_change_speed(tty, NULL); spin_unlock_irqrestore(&info->slock, sl_flags); } } else - retval = mxser_startup(info); + retval = mxser_startup(tty); return retval; } @@ -1461,7 +1461,7 @@ static int mxser_tiocmget(struct tty_struct *tty, struct file *file) spin_lock_irqsave(&info->slock, flags); status = inb(info->ioaddr + UART_MSR); if (status & UART_MSR_ANY_DELTA) - mxser_check_modem_status(info, status); + mxser_check_modem_status(tty, info, status); spin_unlock_irqrestore(&info->slock, flags); return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | @@ -1606,6 +1606,7 @@ static int __init mxser_read_register(int port, unsigned short *regs) static int mxser_ioctl_special(unsigned int cmd, void __user *argp) { struct mxser_port *port; + struct tty_struct *tty; int result, status; unsigned int i, j; int ret = 0; @@ -1643,12 +1644,14 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) if (!port->ioaddr) goto copy; + + tty = tty_port_tty_get(&port->port); - if (!port->port.tty || !port->port.tty->termios) + if (!tty || !tty->termios) ms.cflag = port->normal_termios.c_cflag; else - ms.cflag = port->port.tty->termios->c_cflag; - + ms.cflag = tty->termios->c_cflag; + tty_kref_put(tty); status = inb(port->ioaddr + UART_MSR); if (status & UART_MSR_DCD) ms.dcd = 1; @@ -1704,15 +1707,18 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) me->up_txcnt[p] = port->mon_data.up_txcnt; me->modem_status[p] = port->mon_data.modem_status; - me->baudrate[p] = tty_get_baud_rate(port->port.tty); + tty = tty_port_tty_get(&port->port); - if (!port->port.tty || !port->port.tty->termios) { + if (!tty || !tty->termios) { cflag = port->normal_termios.c_cflag; iflag = port->normal_termios.c_iflag; + me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios); } else { - cflag = port->port.tty->termios->c_cflag; - iflag = port->port.tty->termios->c_iflag; + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; + me->baudrate[p] = tty_get_baud_rate(tty); } + tty_kref_put(tty); me->databits[p] = cflag & CSIZE; me->stopbits[p] = cflag & CSTOPB; @@ -1822,12 +1828,12 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, switch (cmd) { case TIOCGSERIAL: lock_kernel(); - retval = mxser_get_serial_info(info, argp); + retval = mxser_get_serial_info(tty, argp); unlock_kernel(); return retval; case TIOCSSERIAL: lock_kernel(); - retval = mxser_set_serial_info(info, argp); + retval = mxser_set_serial_info(tty, argp); unlock_kernel(); return retval; case TIOCSERGETLSR: /* Get line status register */ @@ -1896,7 +1902,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, lock_kernel(); status = mxser_get_msr(info->ioaddr, 1, tty->index); - mxser_check_modem_status(info, status); + mxser_check_modem_status(tty, info, status); mcr = inb(info->ioaddr + UART_MCR); if (mcr & MOXA_MUST_MCR_XON_FLAG) @@ -1909,7 +1915,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, else info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; - if (info->port.tty->hw_stopped) + if (tty->hw_stopped) info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; else info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; @@ -1958,7 +1964,7 @@ static void mxser_stoprx(struct tty_struct *tty) } } - if (info->port.tty->termios->c_cflag & CRTSCTS) { + if (tty->termios->c_cflag & CRTSCTS) { info->MCR &= ~UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -1995,7 +2001,7 @@ static void mxser_unthrottle(struct tty_struct *tty) } } - if (info->port.tty->termios->c_cflag & CRTSCTS) { + if (tty->termios->c_cflag & CRTSCTS) { info->MCR |= UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -2040,7 +2046,7 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi unsigned long flags; spin_lock_irqsave(&info->slock, flags); - mxser_change_speed(info, old_termios); + mxser_change_speed(tty, old_termios); spin_unlock_irqrestore(&info->slock, flags); if ((old_termios->c_cflag & CRTSCTS) && @@ -2138,10 +2144,10 @@ static void mxser_hangup(struct tty_struct *tty) struct mxser_port *info = tty->driver_data; mxser_flush_buffer(tty); - mxser_shutdown(info); + mxser_shutdown(tty); info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; + tty_port_tty_set(&info->port, NULL); wake_up_interruptible(&info->port.open_wait); } @@ -2164,9 +2170,9 @@ static int mxser_rs_break(struct tty_struct *tty, int break_state) return 0; } -static void mxser_receive_chars(struct mxser_port *port, int *status) +static void mxser_receive_chars(struct tty_struct *tty, + struct mxser_port *port, int *status) { - struct tty_struct *tty = port->port.tty; unsigned char ch, gdl; int ignored = 0; int cnt = 0; @@ -2174,9 +2180,8 @@ static void mxser_receive_chars(struct mxser_port *port, int *status) int max = 256; recv_room = tty->receive_room; - if ((recv_room == 0) && (!port->ldisc_stop_rx)) + if (recv_room == 0 && !port->ldisc_stop_rx) mxser_stoprx(tty); - if (port->board->chip_flag != MOXA_OTHER_UART) { if (*status & UART_LSR_SPECIAL) @@ -2253,7 +2258,7 @@ intr_old: } while (*status & UART_LSR_DR); end_intr: - mxvar_log.rxcnt[port->port.tty->index] += cnt; + mxvar_log.rxcnt[tty->index] += cnt; port->mon_data.rxcnt += cnt; port->mon_data.up_rxcnt += cnt; @@ -2267,14 +2272,14 @@ end_intr: spin_lock(&port->slock); } -static void mxser_transmit_chars(struct mxser_port *port) +static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port) { int count, cnt; if (port->x_char) { outb(port->x_char, port->ioaddr + UART_TX); port->x_char = 0; - mxvar_log.txcnt[port->port.tty->index]++; + mxvar_log.txcnt[tty->index]++; port->mon_data.txcnt++; port->mon_data.up_txcnt++; port->icount.tx++; @@ -2284,8 +2289,8 @@ static void mxser_transmit_chars(struct mxser_port *port) if (port->port.xmit_buf == NULL) return; - if ((port->xmit_cnt <= 0) || port->port.tty->stopped || - (port->port.tty->hw_stopped && + if (port->xmit_cnt <= 0 || tty->stopped || + (tty->hw_stopped && (port->type != PORT_16550A) && (!port->board->chip_flag))) { port->IER &= ~UART_IER_THRI; @@ -2302,14 +2307,14 @@ static void mxser_transmit_chars(struct mxser_port *port) if (--port->xmit_cnt <= 0) break; } while (--count > 0); - mxvar_log.txcnt[port->port.tty->index] += (cnt - port->xmit_cnt); + mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt); port->mon_data.txcnt += (cnt - port->xmit_cnt); port->mon_data.up_txcnt += (cnt - port->xmit_cnt); port->icount.tx += (cnt - port->xmit_cnt); - if (port->xmit_cnt < WAKEUP_CHARS) - tty_wakeup(port->port.tty); + if (port->xmit_cnt < WAKEUP_CHARS && tty) + tty_wakeup(tty); if (port->xmit_cnt <= 0) { port->IER &= ~UART_IER_THRI; @@ -2328,6 +2333,7 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) int max, irqbits, bits, msr; unsigned int int_cnt, pass_counter = 0; int handled = IRQ_NONE; + struct tty_struct *tty; for (i = 0; i < MXSER_BOARDS; i++) if (dev_id == &mxser_boards[i]) { @@ -2360,13 +2366,15 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) if (iir & UART_IIR_NO_INT) break; iir &= MOXA_MUST_IIR_MASK; - if (!port->port.tty || + tty = tty_port_tty_get(&port->port); + if (!tty || (port->port.flags & ASYNC_CLOSING) || !(port->port.flags & ASYNC_INITIALIZED)) { status = inb(port->ioaddr + UART_LSR); outb(0x27, port->ioaddr + UART_FCR); inb(port->ioaddr + UART_MSR); + tty_kref_put(tty); break; } @@ -2387,27 +2395,28 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) iir == MOXA_MUST_IIR_RDA || iir == MOXA_MUST_IIR_RTO || iir == MOXA_MUST_IIR_LSR) - mxser_receive_chars(port, + mxser_receive_chars(tty, port, &status); } else { status &= port->read_status_mask; if (status & UART_LSR_DR) - mxser_receive_chars(port, + mxser_receive_chars(tty, port, &status); } msr = inb(port->ioaddr + UART_MSR); if (msr & UART_MSR_ANY_DELTA) - mxser_check_modem_status(port, msr); + mxser_check_modem_status(tty, port, msr); if (port->board->chip_flag) { if (iir == 0x02 && (status & UART_LSR_THRE)) - mxser_transmit_chars(port); + mxser_transmit_chars(tty, port); } else { if (status & UART_LSR_THRE) - mxser_transmit_chars(port); + mxser_transmit_chars(tty, port); } + tty_kref_put(tty); } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); spin_unlock(&port->slock); } diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 69ec6399c71..bacb3e2872a 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -764,7 +764,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, break; default: - error = n_tty_ioctl (tty, file, cmd, arg); + error = n_tty_ioctl_helper(tty, file, cmd, arg); break; } return error; diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index ae377aa473b..4a8215a89ad 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -372,14 +372,8 @@ static void remove_from_rx_queue(struct r3964_info *pInfo, static void put_char(struct r3964_info *pInfo, unsigned char ch) { struct tty_struct *tty = pInfo->tty; - - if (tty == NULL) - return; - /* FIXME: put_char should not be called from an IRQ */ - if (tty->ops->put_char) { - tty->ops->put_char(tty, ch); - } + tty_put_char(tty, ch); pInfo->bcc ^= ch; } diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 708c2b1dbe5..efbfe961265 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -26,7 +26,7 @@ * * 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to * waiting writing processes-Sapan Bhatia <sapan@corewars.org>. - * Also fixed a bug in BLOCKING mode where write_chan returns + * Also fixed a bug in BLOCKING mode where n_tty_write returns * EAGAIN */ @@ -99,6 +99,7 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, static void n_tty_set_room(struct tty_struct *tty) { + /* tty->read_cnt is not read locked ? */ int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; /* @@ -121,6 +122,16 @@ static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) } } +/** + * put_tty_queue - add character to tty + * @c: character + * @tty: tty device + * + * Add a character to the tty read_buf queue. This is done under the + * read_lock to serialize character addition and also to protect us + * against parallel reads or flushes + */ + static void put_tty_queue(unsigned char c, struct tty_struct *tty) { unsigned long flags; @@ -137,14 +148,11 @@ static void put_tty_queue(unsigned char c, struct tty_struct *tty) * check_unthrottle - allow new receive data * @tty; tty device * - * Check whether to call the driver.unthrottle function. - * We test the TTY_THROTTLED bit first so that it always - * indicates the current state. The decision about whether - * it is worth allowing more input has been taken by the caller. + * Check whether to call the driver unthrottle functions + * * Can sleep, may be called under the atomic_read_lock mutex but * this is not guaranteed. */ - static void check_unthrottle(struct tty_struct *tty) { if (tty->count) @@ -158,6 +166,8 @@ static void check_unthrottle(struct tty_struct *tty) * Reset the read buffer counters, clear the flags, * and make sure the driver is unthrottled. Called * from n_tty_open() and n_tty_flush_buffer(). + * + * Locking: tty_read_lock for read fields. */ static void reset_buffer_flags(struct tty_struct *tty) { @@ -181,7 +191,7 @@ static void reset_buffer_flags(struct tty_struct *tty) * at hangup) or when the N_TTY line discipline internally has to * clean the pending queue (for example some signals). * - * Locking: ctrl_lock + * Locking: ctrl_lock, read_lock. */ static void n_tty_flush_buffer(struct tty_struct *tty) @@ -207,6 +217,8 @@ static void n_tty_flush_buffer(struct tty_struct *tty) * * Report the number of characters buffered to be delivered to user * at this instant in time. + * + * Locking: read_lock */ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) @@ -346,7 +358,7 @@ static int opost(unsigned char c, struct tty_struct *tty) * the simple cases normally found and helps to generate blocks of * symbols for the console driver and thus improve performance. * - * Called from write_chan under the tty layer write lock. Relies + * Called from n_tty_write under the tty layer write lock. Relies * on lock_kernel for the tty->column state. */ @@ -410,6 +422,8 @@ break_out: * * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * Relies on BKL for tty column locking */ static void echo_char(unsigned char c, struct tty_struct *tty) @@ -422,6 +436,12 @@ static void echo_char(unsigned char c, struct tty_struct *tty) opost(c, tty); } +/** + * finsh_erasing - complete erase + * @tty: tty doing the erase + * + * Relies on BKL for tty column locking + */ static inline void finish_erasing(struct tty_struct *tty) { if (tty->erasing) { @@ -439,6 +459,8 @@ static inline void finish_erasing(struct tty_struct *tty) * Perform erase and necessary output when an erase character is * present in the stream from the driver layer. Handles the complexities * of UTF-8 multibyte symbols. + * + * Locking: read_lock for tty buffers, BKL for column/erasing state */ static void eraser(unsigned char c, struct tty_struct *tty) @@ -447,6 +469,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) int head, seen_alnums, cnt; unsigned long flags; + /* FIXME: locking needed ? */ if (tty->read_head == tty->canon_head) { /* opost('\a', tty); */ /* what do you think? */ return; @@ -481,6 +504,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) } seen_alnums = 0; + /* FIXME: Locking ?? */ while (tty->read_head != tty->canon_head) { head = tty->read_head; @@ -583,6 +607,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) * may caus terminal flushing to take place according to the termios * settings and character used. Called from the driver receive_buf * path so serialized. + * + * Locking: ctrl_lock, read_lock (both via flush buffer) */ static inline void isig(int sig, struct tty_struct *tty, int flush) @@ -1007,12 +1033,26 @@ int is_ignored(int sig) * and is protected from re-entry by the tty layer. The user is * guaranteed that this function will not be re-entered or in progress * when the ldisc is closed. + * + * Locking: Caller holds tty->termios_mutex */ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { - if (!tty) - return; + int canon_change = 1; + BUG_ON(!tty); + + if (old) + canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; + if (canon_change) { + memset(&tty->read_flags, 0, sizeof tty->read_flags); + tty->canon_head = tty->read_tail; + tty->canon_data = 0; + tty->erasing = 0; + } + + if (canon_change && !L_ICANON(tty) && tty->read_cnt) + wake_up_interruptible(&tty->read_wait); tty->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { @@ -1143,7 +1183,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * @b: user data * @nr: size of data * - * Helper function to speed up read_chan. It is only called when + * Helper function to speed up n_tty_read. It is only called when * ICANON is off; it copies characters straight from the tty queue to * user space directly. It can be profitably called twice; once to * drain the space from the tail pointer to the (physical) end of the @@ -1210,7 +1250,7 @@ static int job_control(struct tty_struct *tty, struct file *file) if (file->f_op->write != redirected_tty_write && current->signal->tty == tty) { if (!tty->pgrp) - printk(KERN_ERR "read_chan: no tty->pgrp!\n"); + printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); else if (task_pgrp(current) != tty->pgrp) { if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) @@ -1225,7 +1265,7 @@ static int job_control(struct tty_struct *tty, struct file *file) /** - * read_chan - read function for tty + * n_tty_read - read function for tty * @tty: tty device * @file: file object * @buf: userspace buffer pointer @@ -1239,7 +1279,7 @@ static int job_control(struct tty_struct *tty, struct file *file) * This code must be sure never to sleep through a hangup. */ -static ssize_t read_chan(struct tty_struct *tty, struct file *file, +static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { unsigned char __user *b = buf; @@ -1254,10 +1294,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, do_it_again: - if (!tty->read_buf) { - printk(KERN_ERR "n_tty_read_chan: read_buf == NULL?!?\n"); - return -EIO; - } + BUG_ON(!tty->read_buf); c = job_control(tty, file); if (c < 0) @@ -1444,7 +1481,7 @@ do_it_again: } /** - * write_chan - write function for tty + * n_tty_write - write function for tty * @tty: tty device * @file: file object * @buf: userspace buffer pointer @@ -1458,7 +1495,7 @@ do_it_again: * This code must be sure never to sleep through a hangup. */ -static ssize_t write_chan(struct tty_struct *tty, struct file *file, +static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) { const unsigned char *b = buf; @@ -1532,7 +1569,7 @@ break_out: } /** - * normal_poll - poll method for N_TTY + * n_tty_poll - poll method for N_TTY * @tty: terminal device * @file: file accessing it * @wait: poll table @@ -1545,7 +1582,7 @@ break_out: * Called without the kernel lock held - fine */ -static unsigned int normal_poll(struct tty_struct *tty, struct file *file, +static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { unsigned int mask = 0; @@ -1573,6 +1610,44 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, return mask; } +static unsigned long inq_canon(struct tty_struct *tty) +{ + int nr, head, tail; + + if (!tty->canon_data) + return 0; + head = tty->canon_head; + tail = tty->read_tail; + nr = (head - tail) & (N_TTY_BUF_SIZE-1); + /* Skip EOF-chars.. */ + while (head != tail) { + if (test_bit(tail, tty->read_flags) && + tty->read_buf[tail] == __DISABLED_CHAR) + nr--; + tail = (tail+1) & (N_TTY_BUF_SIZE-1); + } + return nr; +} + +static int n_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval; + + switch (cmd) { + case TIOCOUTQ: + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); + case TIOCINQ: + /* FIXME: Locking */ + retval = tty->read_cnt; + if (L_ICANON(tty)) + retval = inq_canon(tty); + return put_user(retval, (unsigned int __user *) arg); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -1580,11 +1655,11 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .close = n_tty_close, .flush_buffer = n_tty_flush_buffer, .chars_in_buffer = n_tty_chars_in_buffer, - .read = read_chan, - .write = write_chan, + .read = n_tty_read, + .write = n_tty_write, .ioctl = n_tty_ioctl, .set_termios = n_tty_set_termios, - .poll = normal_poll, + .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup }; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 66a0f931c66..9a34a193528 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1599,7 +1599,10 @@ static int ntty_open(struct tty_struct *tty, struct file *file) return 0; } -/* Called when the userspace process close the tty, /dev/noz*. */ +/* Called when the userspace process close the tty, /dev/noz*. Also + called immediately if ntty_open fails in which case tty->driver_data + will be NULL an we exit by the first return */ + static void ntty_close(struct tty_struct *tty, struct file *file) { struct nozomi *dc = get_dc_by_tty(tty); diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 3a23e7694d5..569f2f7743a 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -276,6 +276,7 @@ static int ipw_write_room(struct tty_struct *linux_tty) struct ipw_tty *tty = linux_tty->driver_data; int room; + /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; @@ -397,6 +398,7 @@ static int set_control_lines(struct ipw_tty *tty, unsigned int set, static int ipw_tiocmget(struct tty_struct *linux_tty, struct file *file) { struct ipw_tty *tty = linux_tty->driver_data; + /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; @@ -412,6 +414,7 @@ ipw_tiocmset(struct tty_struct *linux_tty, struct file *file, unsigned int set, unsigned int clear) { struct ipw_tty *tty = linux_tty->driver_data; + /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; @@ -433,6 +436,8 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, if (!tty->open_count) return -EINVAL; + /* FIXME: Exactly how is the tty object locked here .. */ + switch (cmd) { case TIOCGSERIAL: return ipwireless_get_serial_info(tty, (void __user *) arg); @@ -467,13 +472,6 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, } return 0; - case TCGETS: - case TCGETA: - return n_tty_ioctl(linux_tty, file, cmd, arg); - - case TCFLSH: - return n_tty_ioctl(linux_tty, file, cmd, arg); - case FIONREAD: { int val = 0; @@ -482,10 +480,11 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, return -EFAULT; } return 0; + case TCFLSH: + return tty_perform_flush(linux_tty, arg); } } - - return -ENOIOCTLCMD; + return tty_mode_ioctl(linux_tty, file, cmd , arg); } static int add_tty(dev_node_t *nodesp, int j, @@ -588,6 +587,8 @@ void ipwireless_tty_free(struct ipw_tty *tty) tty_hangup(ttyj->linux_tty); /* Wait till the tty_hangup has completed */ flush_scheduled_work(); + /* FIXME: Exactly how is the tty object locked here + against a parallel ioctl etc */ mutex_lock(&ttyj->ipw_tty_mutex); } while (ttyj->open_count) diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 76b27932d22..6d4582712b1 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -8,10 +8,12 @@ * Added TTY_DO_WRITE_WAKEUP to enable n_tty to send POLL_OUT to * waiting writers -- Sapan Bhatia <sapan@corewars.org> * - * + * When reading this code see also fs/devpts. In particular note that the + * driver_data field is used by the devpts side as a binding to the devpts + * inode. */ -#include <linux/module.h> /* For EXPORT_SYMBOL */ +#include <linux/module.h> #include <linux/errno.h> #include <linux/interrupt.h> @@ -23,26 +25,25 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/sysctl.h> - -#include <asm/uaccess.h> -#include <asm/system.h> +#include <linux/device.h> +#include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/devpts_fs.h> +#include <asm/system.h> + /* These are global because they are accessed in tty_io.c */ #ifdef CONFIG_UNIX98_PTYS struct tty_driver *ptm_driver; static struct tty_driver *pts_driver; #endif -static void pty_close(struct tty_struct * tty, struct file * filp) +static void pty_close(struct tty_struct *tty, struct file *filp) { - if (!tty) - return; - if (tty->driver->subtype == PTY_TYPE_MASTER) { - if (tty->count > 1) - printk("master pty_close: count = %d!!\n", tty->count); - } else { + BUG_ON(!tty); + if (tty->driver->subtype == PTY_TYPE_MASTER) + WARN_ON(tty->count > 1); + else { if (tty->count > 2) return; } @@ -59,7 +60,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp) set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) - devpts_pty_kill(tty->index); + devpts_pty_kill(tty->link); #endif tty_vhangup(tty->link); } @@ -69,13 +70,13 @@ static void pty_close(struct tty_struct * tty, struct file * filp) * The unthrottle routine is called by the line discipline to signal * that it can receive more characters. For PTY's, the TTY_THROTTLED * flag is always set, to force the line discipline to always call the - * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE + * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE * characters in the queue. This is necessary since each time this * happens, we need to wake up any sleeping processes that could be * (1) trying to send data to the pty, or (2) waiting in wait_until_sent() * for the pty buffer to be drained. */ -static void pty_unthrottle(struct tty_struct * tty) +static void pty_unthrottle(struct tty_struct *tty) { struct tty_struct *o_tty = tty->link; @@ -87,7 +88,7 @@ static void pty_unthrottle(struct tty_struct * tty) } /* - * WSH 05/24/97: modified to + * WSH 05/24/97: modified to * (1) use space in tty->flip instead of a shared temp buffer * The flip buffers aren't being used for a pty, so there's lots * of space available. The buffer is protected by a per-pty @@ -100,7 +101,8 @@ static void pty_unthrottle(struct tty_struct * tty) * not our partners. We can't just take the other one blindly without * risking deadlocks. */ -static int pty_write(struct tty_struct * tty, const unsigned char *buf, int count) +static int pty_write(struct tty_struct *tty, const unsigned char *buf, + int count) { struct tty_struct *to = tty->link; int c; @@ -112,7 +114,7 @@ static int pty_write(struct tty_struct * tty, const unsigned char *buf, int coun if (c > count) c = count; to->ldisc.ops->receive_buf(to, buf, NULL, c); - + return c; } @@ -128,17 +130,17 @@ static int pty_write_room(struct tty_struct *tty) /* * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior - * The chars_in_buffer() value is used by the ldisc select() function + * The chars_in_buffer() value is used by the ldisc select() function * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). * The pty driver chars_in_buffer() Master/Slave must behave differently: * * The Master side needs to allow typed-ahead commands to accumulate * while being canonicalized, so we report "our buffer" as empty until * some threshold is reached, and then report the count. (Any count > - * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock - * the count returned must be 0 if no canonical data is available to be + * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock + * the count returned must be 0 if no canonical data is available to be * read. (The N_TTY ldisc.chars_in_buffer now knows this.) - * + * * The Slave side passes all characters in raw mode to the Master side's * buffer where they can be read immediately, so in this case we can * return the true count in the buffer. @@ -155,21 +157,22 @@ static int pty_chars_in_buffer(struct tty_struct *tty) /* The ldisc must report 0 if no characters available to be read */ count = to->ldisc.ops->chars_in_buffer(to); - if (tty->driver->subtype == PTY_TYPE_SLAVE) return count; + if (tty->driver->subtype == PTY_TYPE_SLAVE) + return count; - /* Master side driver ... if the other side's read buffer is less than + /* Master side driver ... if the other side's read buffer is less than * half full, return 0 to allow writers to proceed; otherwise return - * the count. This leaves a comfortable margin to avoid overflow, + * the count. This leaves a comfortable margin to avoid overflow, * and still allows half a buffer's worth of typed-ahead commands. */ - return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); + return (count < N_TTY_BUF_SIZE/2) ? 0 : count; } /* Set the lock flag on a pty */ -static int pty_set_lock(struct tty_struct *tty, int __user * arg) +static int pty_set_lock(struct tty_struct *tty, int __user *arg) { int val; - if (get_user(val,arg)) + if (get_user(val, arg)) return -EFAULT; if (val) set_bit(TTY_PTY_LOCK, &tty->flags); @@ -182,13 +185,13 @@ static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; unsigned long flags; - + if (!to) return; - + if (to->ldisc.ops->flush_buffer) to->ldisc.ops->flush_buffer(to); - + if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; @@ -197,7 +200,7 @@ static void pty_flush_buffer(struct tty_struct *tty) } } -static int pty_open(struct tty_struct *tty, struct file * filp) +static int pty_open(struct tty_struct *tty, struct file *filp) { int retval = -ENODEV; @@ -220,13 +223,65 @@ out: return retval; } -static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +static void pty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + tty->termios->c_cflag &= ~(CSIZE | PARENB); + tty->termios->c_cflag |= (CS8 | CREAD); +} + +static int pty_install(struct tty_driver *driver, struct tty_struct *tty) { - tty->termios->c_cflag &= ~(CSIZE | PARENB); - tty->termios->c_cflag |= (CS8 | CREAD); + struct tty_struct *o_tty; + int idx = tty->index; + int retval; + + o_tty = alloc_tty_struct(); + if (!o_tty) + return -ENOMEM; + if (!try_module_get(driver->other->owner)) { + /* This cannot in fact currently happen */ + free_tty_struct(o_tty); + return -ENOMEM; + } + initialize_tty_struct(o_tty, driver->other, idx); + + /* We always use new tty termios data so we can do this + the easy way .. */ + retval = tty_init_termios(tty); + if (retval) + goto free_mem_out; + + retval = tty_init_termios(o_tty); + if (retval) { + tty_free_termios(tty); + goto free_mem_out; + } + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->ttys[idx] = o_tty; + tty_driver_kref_get(driver->other); + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; + + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; +free_mem_out: + module_put(o_tty->driver->owner); + free_tty_struct(o_tty); + return -ENOMEM; } + static const struct tty_operations pty_ops = { + .install = pty_install, .open = pty_open, .close = pty_close, .write = pty_write, @@ -329,8 +384,11 @@ static inline void legacy_pty_init(void) { } * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. */ int pty_limit = NR_UNIX98_PTY_DEFAULT; -static int pty_limit_min = 0; +static int pty_limit_min; static int pty_limit_max = NR_UNIX98_PTY_MAX; +static int pty_count; + +static struct cdev ptmx_cdev; static struct ctl_table pty_table[] = { { @@ -348,6 +406,7 @@ static struct ctl_table pty_table[] = { .procname = "nr", .maxlen = sizeof(int), .mode = 0444, + .data = &pty_count, .proc_handler = &proc_dointvec, }, { .ctl_name = 0 @@ -388,7 +447,127 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } +/** + * ptm_unix98_lookup - find a pty master + * @driver: ptm driver + * @idx: tty index + * + * Look up a pty master device. Called under the tty_mutex for now. + * This provides our locking. + */ + +static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, + struct inode *ptm_inode, int idx) +{ + struct tty_struct *tty = devpts_get_tty(ptm_inode, idx); + if (tty) + tty = tty->link; + return tty; +} + +/** + * pts_unix98_lookup - find a pty slave + * @driver: pts driver + * @idx: tty index + * + * Look up a pty master device. Called under the tty_mutex for now. + * This provides our locking. + */ + +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); + /* Master must be open before slave */ + if (!tty) + return ERR_PTR(-EIO); + return tty; +} + +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + /* We have our own method as we don't use the tty index */ + kfree(tty->termios); +} + +/* We have no need to install and remove our tty objects as devpts does all + the work for us */ + +static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct tty_struct *o_tty; + int idx = tty->index; + + o_tty = alloc_tty_struct(); + if (!o_tty) + return -ENOMEM; + if (!try_module_get(driver->other->owner)) { + /* This cannot in fact currently happen */ + free_tty_struct(o_tty); + return -ENOMEM; + } + initialize_tty_struct(o_tty, driver->other, idx); + + tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (tty->termios == NULL) + goto free_mem_out; + *tty->termios = driver->init_termios; + tty->termios_locked = tty->termios + 1; + + o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (o_tty->termios == NULL) + goto free_mem_out; + *o_tty->termios = driver->other->init_termios; + o_tty->termios_locked = o_tty->termios + 1; + + tty_driver_kref_get(driver->other); + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_tty to clean up, so + * there's no need to null out the local pointers. + */ + tty_driver_kref_get(driver); + tty->count++; + pty_count++; + return 0; +free_mem_out: + kfree(o_tty->termios); + module_put(o_tty->driver->owner); + free_tty_struct(o_tty); + kfree(tty->termios); + return -ENOMEM; +} + +static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + pty_count--; +} + +static const struct tty_operations ptm_unix98_ops = { + .lookup = ptm_unix98_lookup, + .install = pty_unix98_install, + .remove = pty_unix98_remove, + .open = pty_open, + .close = pty_close, + .write = pty_write, + .write_room = pty_write_room, + .flush_buffer = pty_flush_buffer, + .chars_in_buffer = pty_chars_in_buffer, + .unthrottle = pty_unthrottle, + .set_termios = pty_set_termios, + .ioctl = pty_unix98_ioctl, + .shutdown = pty_unix98_shutdown +}; + static const struct tty_operations pty_unix98_ops = { + .lookup = pts_unix98_lookup, + .install = pty_unix98_install, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -397,9 +576,73 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, - .ioctl = pty_unix98_ioctl + .shutdown = pty_unix98_shutdown }; +/** + * ptmx_open - open a unix 98 pty master + * @inode: inode of device file + * @filp: file pointer to tty + * + * Allocate a unix98 pty master device from the ptmx driver. + * + * Locking: tty_mutex protects the init_dev work. tty->count should + * protect the rest. + * allocated_ptys_lock handles the list of free pty numbers + */ + +static int __ptmx_open(struct inode *inode, struct file *filp) +{ + struct tty_struct *tty; + int retval; + int index; + + nonseekable_open(inode, filp); + + /* find a device that is not in use. */ + index = devpts_new_index(inode); + if (index < 0) + return index; + + mutex_lock(&tty_mutex); + tty = tty_init_dev(ptm_driver, index, 1); + mutex_unlock(&tty_mutex); + + if (IS_ERR(tty)) { + retval = PTR_ERR(tty); + goto out; + } + + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ + filp->private_data = tty; + file_move(filp, &tty->tty_files); + + retval = devpts_pty_new(inode, tty->link); + if (retval) + goto out1; + + retval = ptm_driver->ops->open(tty, filp); + if (!retval) + return 0; +out1: + tty_release_dev(filp); + return retval; +out: + devpts_kill_index(inode, index); + return retval; +} + +static int ptmx_open(struct inode *inode, struct file *filp) +{ + int ret; + + lock_kernel(); + ret = __ptmx_open(inode, filp); + unlock_kernel(); + return ret; +} + +static struct file_operations ptmx_fops; static void __init unix98_pty_init(void) { @@ -427,7 +670,7 @@ static void __init unix98_pty_init(void) ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; ptm_driver->other = pts_driver; - tty_set_operations(ptm_driver, &pty_unix98_ops); + tty_set_operations(ptm_driver, &ptm_unix98_ops); pts_driver->owner = THIS_MODULE; pts_driver->driver_name = "pty_slave"; @@ -443,16 +686,26 @@ static void __init unix98_pty_init(void) pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; pts_driver->other = ptm_driver; - tty_set_operations(pts_driver, &pty_ops); - + tty_set_operations(pts_driver, &pty_unix98_ops); + if (tty_register_driver(ptm_driver)) panic("Couldn't register Unix98 ptm driver"); if (tty_register_driver(pts_driver)) panic("Couldn't register Unix98 pts driver"); - pty_table[1].data = &ptm_driver->refcount; register_sysctl_table(pty_root_table); + + /* Now create the /dev/ptmx special device */ + tty_default_fops(&ptmx_fops); + ptmx_fops.open = ptmx_open; + + cdev_init(&ptmx_cdev, &ptmx_fops); + if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || + register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) + panic("Couldn't register /dev/ptmx driver\n"); + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); } + #else static inline void unix98_pty_init(void) { } #endif diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 19db1eb87c2..8b8f07a7f50 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -405,9 +405,9 @@ static unsigned int stl_baudrates[] = { static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); static int stl_brdinit(struct stlbrd *brdp); -static int stl_getportstats(struct stlport *portp, comstats_t __user *cp); +static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp); static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp); -static int stl_waitcarrier(struct stlport *portp, struct file *filp); +static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, struct file *filp); /* * CD1400 uart specific handling functions. @@ -612,8 +612,9 @@ static struct class *stallion_class; static void stl_cd_change(struct stlport *portp) { unsigned int oldsigs = portp->sigs; + struct tty_struct *tty = tty_port_tty_get(&portp->port); - if (!portp->port.tty) + if (!tty) return; portp->sigs = stl_getsignals(portp); @@ -623,7 +624,8 @@ static void stl_cd_change(struct stlport *portp) if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) if (portp->port.flags & ASYNC_CHECK_CD) - tty_hangup(portp->port.tty); + tty_hangup(tty); + tty_kref_put(tty); } /* @@ -734,7 +736,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * On the first open of the device setup the port hardware, and * initialize the per port data structure. */ - portp->port.tty = tty; + tty_port_tty_set(&portp->port, tty); tty->driver_data = portp; portp->port.count++; @@ -774,7 +776,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * then also we might have to wait for carrier. */ if (!(filp->f_flags & O_NONBLOCK)) - if ((rc = stl_waitcarrier(portp, filp)) != 0) + if ((rc = stl_waitcarrier(tty, portp, filp)) != 0) return rc; portp->port.flags |= ASYNC_NORMAL_ACTIVE; @@ -789,7 +791,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * maybe because if we are clocal then we don't need to wait... */ -static int stl_waitcarrier(struct stlport *portp, struct file *filp) +static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, + struct file *filp) { unsigned long flags; int rc, doclocal; @@ -801,7 +804,7 @@ static int stl_waitcarrier(struct stlport *portp, struct file *filp) spin_lock_irqsave(&stallion_lock, flags); - if (portp->port.tty->termios->c_cflag & CLOCAL) + if (tty->termios->c_cflag & CLOCAL) doclocal++; portp->openwaitcnt++; @@ -846,8 +849,6 @@ static void stl_flushbuffer(struct tty_struct *tty) pr_debug("stl_flushbuffer(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -865,8 +866,6 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout) pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -949,7 +948,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); if (portp->openwaitcnt) { if (portp->close_delay) @@ -1033,8 +1032,6 @@ static int stl_putchar(struct tty_struct *tty, unsigned char ch) pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch); - if (tty == NULL) - return -EINVAL; portp = tty->driver_data; if (portp == NULL) return -EINVAL; @@ -1070,8 +1067,6 @@ static void stl_flushchars(struct tty_struct *tty) pr_debug("stl_flushchars(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1090,8 +1085,6 @@ static int stl_writeroom(struct tty_struct *tty) pr_debug("stl_writeroom(tty=%p)\n", tty); - if (tty == NULL) - return 0; portp = tty->driver_data; if (portp == NULL) return 0; @@ -1122,8 +1115,6 @@ static int stl_charsinbuffer(struct tty_struct *tty) pr_debug("stl_charsinbuffer(tty=%p)\n", tty); - if (tty == NULL) - return 0; portp = tty->driver_data; if (portp == NULL) return 0; @@ -1183,8 +1174,9 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp) * just quietly ignore any requests to change irq, etc. */ -static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp) +static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp) { + struct stlport * portp = tty->driver_data; struct serial_struct sio; pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp); @@ -1205,7 +1197,7 @@ static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp) portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; - stl_setport(portp, portp->port.tty->termios); + stl_setport(portp, tty->termios); return 0; } @@ -1215,8 +1207,6 @@ static int stl_tiocmget(struct tty_struct *tty, struct file *file) { struct stlport *portp; - if (tty == NULL) - return -ENODEV; portp = tty->driver_data; if (portp == NULL) return -ENODEV; @@ -1232,8 +1222,6 @@ static int stl_tiocmset(struct tty_struct *tty, struct file *file, struct stlport *portp; int rts = -1, dtr = -1; - if (tty == NULL) - return -ENODEV; portp = tty->driver_data; if (portp == NULL) return -ENODEV; @@ -1262,8 +1250,6 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd pr_debug("stl_ioctl(tty=%p,file=%p,cmd=%x,arg=%lx)\n", tty, file, cmd, arg); - if (tty == NULL) - return -ENODEV; portp = tty->driver_data; if (portp == NULL) return -ENODEV; @@ -1282,10 +1268,10 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd rc = stl_getserial(portp, argp); break; case TIOCSSERIAL: - rc = stl_setserial(portp, argp); + rc = stl_setserial(tty, argp); break; case COM_GETPORTSTATS: - rc = stl_getportstats(portp, argp); + rc = stl_getportstats(tty, portp, argp); break; case COM_CLRPORTSTATS: rc = stl_clrportstats(portp, argp); @@ -1317,8 +1303,6 @@ static void stl_start(struct tty_struct *tty) pr_debug("stl_start(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1334,8 +1318,6 @@ static void stl_settermios(struct tty_struct *tty, struct ktermios *old) pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1369,8 +1351,6 @@ static void stl_throttle(struct tty_struct *tty) pr_debug("stl_throttle(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1389,8 +1369,6 @@ static void stl_unthrottle(struct tty_struct *tty) pr_debug("stl_unthrottle(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1410,8 +1388,6 @@ static void stl_stop(struct tty_struct *tty) pr_debug("stl_stop(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1432,8 +1408,6 @@ static void stl_hangup(struct tty_struct *tty) pr_debug("stl_hangup(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1452,7 +1426,7 @@ static void stl_hangup(struct tty_struct *tty) portp->tx.head = NULL; portp->tx.tail = NULL; } - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; portp->port.count = 0; wake_up_interruptible(&portp->port.open_wait); @@ -1466,8 +1440,6 @@ static int stl_breakctl(struct tty_struct *tty, int state) pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state); - if (tty == NULL) - return -EINVAL; portp = tty->driver_data; if (portp == NULL) return -EINVAL; @@ -1484,8 +1456,6 @@ static void stl_sendxchar(struct tty_struct *tty, char ch) pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1805,7 +1775,7 @@ static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) "(size=%Zd)\n", sizeof(struct stlport)); break; } - + tty_port_init(&portp->port); portp->magic = STL_PORTMAGIC; portp->portnr = i; portp->brdnr = panelp->brdnr; @@ -1832,6 +1802,7 @@ static void stl_cleanup_panels(struct stlbrd *brdp) struct stlpanel *panelp; struct stlport *portp; unsigned int j, k; + struct tty_struct *tty; for (j = 0; j < STL_MAXPANELS; j++) { panelp = brdp->panels[j]; @@ -1841,8 +1812,11 @@ static void stl_cleanup_panels(struct stlbrd *brdp) portp = panelp->ports[k]; if (portp == NULL) continue; - if (portp->port.tty != NULL) - stl_hangup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty != NULL) { + stl_hangup(tty); + tty_kref_put(tty); + } kfree(portp->tx.buf); kfree(portp); } @@ -2498,7 +2472,7 @@ static struct stlport *stl_getport(int brdnr, int panelnr, int portnr) * what port to get stats for (used through board control device). */ -static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) +static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp) { comstats_t stl_comstats; unsigned char *head, *tail; @@ -2525,18 +2499,17 @@ static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) portp->stats.rxbuffered = 0; spin_lock_irqsave(&stallion_lock, flags); - if (portp->port.tty != NULL) - if (portp->port.tty->driver_data == portp) { - portp->stats.ttystate = portp->port.tty->flags; - /* No longer available as a statistic */ - portp->stats.rxbuffered = 1; /*portp->port.tty->flip.count; */ - if (portp->port.tty->termios != NULL) { - portp->stats.cflags = portp->port.tty->termios->c_cflag; - portp->stats.iflags = portp->port.tty->termios->c_iflag; - portp->stats.oflags = portp->port.tty->termios->c_oflag; - portp->stats.lflags = portp->port.tty->termios->c_lflag; - } + if (tty != NULL && portp->port.tty == tty) { + portp->stats.ttystate = tty->flags; + /* No longer available as a statistic */ + portp->stats.rxbuffered = 1; /*tty->flip.count; */ + if (tty->termios != NULL) { + portp->stats.cflags = tty->termios->c_cflag; + portp->stats.iflags = tty->termios->c_iflag; + portp->stats.oflags = tty->termios->c_oflag; + portp->stats.lflags = tty->termios->c_lflag; } + } spin_unlock_irqrestore(&stallion_lock, flags); head = portp->tx.head; @@ -2640,7 +2613,7 @@ static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, uns switch (cmd) { case COM_GETPORTSTATS: - rc = stl_getportstats(NULL, argp); + rc = stl_getportstats(NULL, NULL, argp); break; case COM_CLRPORTSTATS: rc = stl_clrportstats(NULL, argp); @@ -3243,7 +3216,7 @@ static void stl_cd1400flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -3288,6 +3261,7 @@ static void stl_cd1400flowctrl(struct stlport *portp, int state) BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -3305,7 +3279,7 @@ static void stl_cd1400sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -3325,6 +3299,7 @@ static void stl_cd1400sendflow(struct stlport *portp, int state) } BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -3478,6 +3453,7 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) int len, stlen; char *head, *tail; unsigned char ioack, srer; + struct tty_struct *tty; pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr); @@ -3504,8 +3480,11 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->port.tty) - tty_wakeup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } } if (len == 0) { @@ -3569,7 +3548,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) return; } portp = panelp->ports[(ioack >> 3)]; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { outb((RDCR + portp->uartaddr), ioaddr); @@ -3633,10 +3612,12 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) } } else { printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + tty_kref_put(tty); return; } stl_rxalldone: + tty_kref_put(tty); outb((EOSRR + portp->uartaddr), ioaddr); outb(0, (ioaddr + EREG_DATA)); } @@ -4175,7 +4156,7 @@ static void stl_sc26198flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -4226,6 +4207,7 @@ static void stl_sc26198flowctrl(struct stlport *portp, int state) BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -4244,7 +4226,7 @@ static void stl_sc26198sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -4269,6 +4251,7 @@ static void stl_sc26198sendflow(struct stlport *portp, int state) } BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -4408,6 +4391,7 @@ static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase) static void stl_sc26198txisr(struct stlport *portp) { + struct tty_struct *tty; unsigned int ioaddr; unsigned char mr0; int len, stlen; @@ -4422,8 +4406,11 @@ static void stl_sc26198txisr(struct stlport *portp) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->port.tty) - tty_wakeup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } } if (len == 0) { @@ -4476,7 +4463,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack); - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); ioaddr = portp->ioaddr; outb(GIBCR, (ioaddr + XP_ADDR)); len = inb(ioaddr + XP_DATA) + 1; @@ -4515,6 +4502,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) stl_sc26198txunflow(portp, tty); } } + tty_kref_put(tty); } /*****************************************************************************/ @@ -4528,7 +4516,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char struct tty_struct *tty; unsigned int ioaddr; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); ioaddr = portp->ioaddr; if (status & SR_RXPARITY) @@ -4566,6 +4554,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char if (status == 0) portp->stats.rxtotal++; } + tty_kref_put(tty); } /*****************************************************************************/ diff --git a/drivers/char/sx.c b/drivers/char/sx.c index c385206f9db..5b8d7a1aa3e 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -2504,7 +2504,7 @@ static void __devexit sx_remove_card(struct sx_board *board, del_timer(&board->timer); if (pdev) { #ifdef CONFIG_PCI - pci_iounmap(pdev, board->base); + pci_iounmap(pdev, board->base2); pci_release_region(pdev, IS_CF_BOARD(board) ? 3 : 2); #endif } else { @@ -2703,7 +2703,7 @@ static int __devinit sx_pci_probe(struct pci_dev *pdev, return 0; err_unmap: - pci_iounmap(pdev, board->base); + pci_iounmap(pdev, board->base2); err_reg: pci_release_region(pdev, reg); err_flag: diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c index 3582f43345a..5787249934c 100644 --- a/drivers/char/tty_audit.c +++ b/drivers/char/tty_audit.c @@ -93,7 +93,7 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); - audit_log_n_untrustedstring(ab, buf->data, buf->valid); + audit_log_n_hex(ab, buf->data, buf->valid); audit_log_end(ab); } buf->valid = 0; diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c new file mode 100644 index 00000000000..810ee25d66a --- /dev/null +++ b/drivers/char/tty_buffer.c @@ -0,0 +1,511 @@ +/* + * Tty buffer allocation management + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/module.h> + +/** + * tty_buffer_free_all - free buffers used by a tty + * @tty: tty to free from + * + * Remove all the buffers pending on a tty whether queued with data + * or in the free ring. Must be called when the tty is no longer in use + * + * Locking: none + */ + +void tty_buffer_free_all(struct tty_struct *tty) +{ + struct tty_buffer *thead; + while ((thead = tty->buf.head) != NULL) { + tty->buf.head = thead->next; + kfree(thead); + } + while ((thead = tty->buf.free) != NULL) { + tty->buf.free = thead->next; + kfree(thead); + } + tty->buf.tail = NULL; + tty->buf.memory_used = 0; +} + +/** + * tty_buffer_alloc - allocate a tty buffer + * @tty: tty device + * @size: desired size (characters) + * + * Allocate a new tty buffer to hold the desired number of characters. + * Return NULL if out of memory or the allocation would exceed the + * per device queue + * + * Locking: Caller must hold tty->buf.lock + */ + +static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +{ + struct tty_buffer *p; + + if (tty->buf.memory_used + size > 65536) + return NULL; + p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); + if (p == NULL) + return NULL; + p->used = 0; + p->size = size; + p->next = NULL; + p->commit = 0; + p->read = 0; + p->char_buf_ptr = (char *)(p->data); + p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; + tty->buf.memory_used += size; + return p; +} + +/** + * tty_buffer_free - free a tty buffer + * @tty: tty owning the buffer + * @b: the buffer to free + * + * Free a tty buffer, or add it to the free list according to our + * internal strategy + * + * Locking: Caller must hold tty->buf.lock + */ + +static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +{ + /* Dumb strategy for now - should keep some stats */ + tty->buf.memory_used -= b->size; + WARN_ON(tty->buf.memory_used < 0); + + if (b->size >= 512) + kfree(b); + else { + b->next = tty->buf.free; + tty->buf.free = b; + } +} + +/** + * __tty_buffer_flush - flush full tty buffers + * @tty: tty to flush + * + * flush all the buffers containing receive data. Caller must + * hold the buffer lock and must have ensured no parallel flush to + * ldisc is running. + * + * Locking: Caller must hold tty->buf.lock + */ + +static void __tty_buffer_flush(struct tty_struct *tty) +{ + struct tty_buffer *thead; + + while ((thead = tty->buf.head) != NULL) { + tty->buf.head = thead->next; + tty_buffer_free(tty, thead); + } + tty->buf.tail = NULL; +} + +/** + * tty_buffer_flush - flush full tty buffers + * @tty: tty to flush + * + * flush all the buffers containing receive data. If the buffer is + * being processed by flush_to_ldisc then we defer the processing + * to that function + * + * Locking: none + */ + +void tty_buffer_flush(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + + /* If the data is being pushed to the tty layer then we can't + process it here. Instead set a flag and the flush_to_ldisc + path will process the flush request before it exits */ + if (test_bit(TTY_FLUSHING, &tty->flags)) { + set_bit(TTY_FLUSHPENDING, &tty->flags); + spin_unlock_irqrestore(&tty->buf.lock, flags); + wait_event(tty->read_wait, + test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); + return; + } else + __tty_buffer_flush(tty); + spin_unlock_irqrestore(&tty->buf.lock, flags); +} + +/** + * tty_buffer_find - find a free tty buffer + * @tty: tty owning the buffer + * @size: characters wanted + * + * Locate an existing suitable tty buffer or if we are lacking one then + * allocate a new one. We round our buffers off in 256 character chunks + * to get better allocation behaviour. + * + * Locking: Caller must hold tty->buf.lock + */ + +static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +{ + struct tty_buffer **tbh = &tty->buf.free; + while ((*tbh) != NULL) { + struct tty_buffer *t = *tbh; + if (t->size >= size) { + *tbh = t->next; + t->next = NULL; + t->used = 0; + t->commit = 0; + t->read = 0; + tty->buf.memory_used += t->size; + return t; + } + tbh = &((*tbh)->next); + } + /* Round the buffer size out */ + size = (size + 0xFF) & ~0xFF; + return tty_buffer_alloc(tty, 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: 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) +{ + 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 */ + if ((b = tty->buf.tail) != NULL) + left = b->size - b->used; + else + left = 0; + + if (left < size) { + /* This is the slow path - looking for new buffers to use */ + if ((n = tty_buffer_find(tty, size)) != NULL) { + if (b != NULL) { + b->next = n; + b->commit = b->used; + } else + tty->buf.head = n; + tty->buf.tail = n; + } else + size = left; + } + + spin_unlock_irqrestore(&tty->buf.lock, flags); + return size; +} +EXPORT_SYMBOL_GPL(tty_buffer_request_room); + +/** + * tty_insert_flip_string - Add characters to the tty buffer + * @tty: tty structure + * @chars: characters + * @size: size + * + * Queue a series of bytes to the tty buffering. All the characters + * passed are marked as without error. Returns the number added. + * + * Locking: Called functions may take tty->buf.lock + */ + +int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, + size_t size) +{ + int copied = 0; + do { + int space = tty_buffer_request_room(tty, size - copied); + struct tty_buffer *tb = tty->buf.tail; + /* If there is no space then tb may be NULL */ + if (unlikely(space == 0)) + break; + memcpy(tb->char_buf_ptr + tb->used, chars, space); + memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + tb->used += space; + copied += space; + chars += space; + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); + return copied; +} +EXPORT_SYMBOL(tty_insert_flip_string); + +/** + * tty_insert_flip_string_flags - Add characters to the tty buffer + * @tty: tty structure + * @chars: characters + * @flags: flag bytes + * @size: size + * + * Queue a series of bytes to the tty buffering. For each character + * the flags array indicates the status of the character. Returns the + * number added. + * + * Locking: Called functions may take tty->buf.lock + */ + +int tty_insert_flip_string_flags(struct tty_struct *tty, + const unsigned char *chars, const char *flags, size_t size) +{ + int copied = 0; + do { + int space = tty_buffer_request_room(tty, size - copied); + struct tty_buffer *tb = tty->buf.tail; + /* If there is no space then tb may be NULL */ + if (unlikely(space == 0)) + break; + memcpy(tb->char_buf_ptr + tb->used, chars, space); + memcpy(tb->flag_buf_ptr + tb->used, flags, space); + tb->used += space; + copied += space; + chars += space; + flags += space; + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); + return copied; +} +EXPORT_SYMBOL(tty_insert_flip_string_flags); + +/** + * tty_schedule_flip - push characters to ldisc + * @tty: tty to push from + * + * Takes any pending buffers and transfers their ownership to the + * ldisc side of the queue. It then schedules those characters for + * processing by the line discipline. + * + * Locking: Takes tty->buf.lock + */ + +void tty_schedule_flip(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) + tty->buf.tail->commit = tty->buf.tail->used; + spin_unlock_irqrestore(&tty->buf.lock, flags); + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_schedule_flip); + +/** + * tty_prepare_flip_string - make room for characters + * @tty: tty + * @chars: return pointer for character write area + * @size: desired size + * + * Prepare a block of space in the buffer for data. Returns the length + * available and buffer pointer to the space which is now allocated and + * accounted for as ready for normal characters. This is used for drivers + * that need their own block copy routines into the buffer. There is no + * guarantee the buffer is a DMA target! + * + * Locking: May call functions taking tty->buf.lock + */ + +int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, + size_t size) +{ + int space = tty_buffer_request_room(tty, size); + 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; + } + return space; +} +EXPORT_SYMBOL_GPL(tty_prepare_flip_string); + +/** + * tty_prepare_flip_string_flags - make room for characters + * @tty: tty + * @chars: return pointer for character write area + * @flags: return pointer for status flag write area + * @size: desired size + * + * Prepare a block of space in the buffer for data. Returns the length + * available and buffer pointer to the space which is now allocated and + * accounted for as ready for characters. This is used for drivers + * that need their own block copy routines into the buffer. There is no + * guarantee the buffer is a DMA target! + * + * Locking: May call functions taking tty->buf.lock + */ + +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); + 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; + } + return space; +} +EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); + + + +/** + * flush_to_ldisc + * @work: tty structure passed from work queue. + * + * This routine is called out of the software interrupt to flush data + * from the buffer chain to the line discipline. + * + * Locking: holds tty->buf.lock to guard buffer list. Drops the lock + * while invoking the line discipline receive_buf method. The + * receive_buf method is single threaded for each tty instance. + */ + +static void flush_to_ldisc(struct work_struct *work) +{ + struct tty_struct *tty = + container_of(work, struct tty_struct, buf.work.work); + unsigned long flags; + struct tty_ldisc *disc; + struct tty_buffer *tbuf, *head; + char *char_buf; + unsigned char *flag_buf; + + disc = tty_ldisc_ref(tty); + if (disc == NULL) /* !TTY_LDISC */ + return; + + spin_lock_irqsave(&tty->buf.lock, flags); + /* So we know a flush is running */ + set_bit(TTY_FLUSHING, &tty->flags); + head = tty->buf.head; + if (head != NULL) { + tty->buf.head = NULL; + for (;;) { + int count = head->commit - head->read; + if (!count) { + if (head->next == NULL) + break; + tbuf = head; + head = head->next; + tty_buffer_free(tty, tbuf); + continue; + } + /* Ldisc or user is trying to flush the buffers + we are feeding to the ldisc, stop feeding the + line discipline as we want to empty the queue */ + if (test_bit(TTY_FLUSHPENDING, &tty->flags)) + break; + if (!tty->receive_room) { + schedule_delayed_work(&tty->buf.work, 1); + break; + } + if (count > tty->receive_room) + count = tty->receive_room; + char_buf = head->char_buf_ptr + head->read; + flag_buf = head->flag_buf_ptr + head->read; + head->read += count; + spin_unlock_irqrestore(&tty->buf.lock, flags); + disc->ops->receive_buf(tty, char_buf, + flag_buf, count); + spin_lock_irqsave(&tty->buf.lock, flags); + } + /* Restore the queue head */ + tty->buf.head = head; + } + /* We may have a deferred request to flush the input buffer, + if so pull the chain under the lock and empty the queue */ + if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { + __tty_buffer_flush(tty); + clear_bit(TTY_FLUSHPENDING, &tty->flags); + wake_up(&tty->read_wait); + } + clear_bit(TTY_FLUSHING, &tty->flags); + spin_unlock_irqrestore(&tty->buf.lock, flags); + + tty_ldisc_deref(disc); +} + +/** + * tty_flip_buffer_push - terminal + * @tty: tty to push + * + * Queue a push of the terminal flip buffers to the line discipline. This + * function must not be called from IRQ context if tty->low_latency is set. + * + * In the event of the queue being busy for flipping the work will be + * held off and retried later. + * + * Locking: tty buffer lock. Driver locks in low latency mode. + */ + +void tty_flip_buffer_push(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) + tty->buf.tail->commit = tty->buf.tail->used; + spin_unlock_irqrestore(&tty->buf.lock, flags); + + if (tty->low_latency) + flush_to_ldisc(&tty->buf.work.work); + else + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_flip_buffer_push); + +/** + * tty_buffer_init - prepare a tty buffer structure + * @tty: tty to initialise + * + * Set up the initial state of the buffer management for a tty device. + * Must be called before the other tty buffer functions are used. + * + * Locking: none + */ + +void tty_buffer_init(struct tty_struct *tty) +{ + spin_lock_init(&tty->buf.lock); + tty->buf.head = NULL; + tty->buf.tail = NULL; + tty->buf.free = NULL; + tty->buf.memory_used = 0; + INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); +} + diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index e4dce870954..7053d633369 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -49,7 +49,7 @@ * implement CONFIG_VT and generalize console device interface. * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 * - * Rewrote init_dev and release_dev to eliminate races. + * Rewrote tty_init_dev and tty_release_dev to eliminate races. * -- Bill Hawes <whawes@star.net>, June 97 * * Added devfs support. @@ -136,13 +136,6 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ DEFINE_MUTEX(tty_mutex); EXPORT_SYMBOL(tty_mutex); -#ifdef CONFIG_UNIX98_PTYS -extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ -static int ptmx_open(struct inode *, struct file *); -#endif - -static void initialize_tty_struct(struct tty_struct *tty); - static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); ssize_t redirected_tty_write(struct file *, const char __user *, @@ -171,13 +164,11 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); * Locking: none */ -static struct tty_struct *alloc_tty_struct(void) +struct tty_struct *alloc_tty_struct(void) { return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); } -static void tty_buffer_free_all(struct tty_struct *); - /** * free_tty_struct - free a disused tty * @tty: tty struct to free @@ -187,7 +178,7 @@ static void tty_buffer_free_all(struct tty_struct *); * Locking: none. Must be called after tty is definitely unused */ -static inline void free_tty_struct(struct tty_struct *tty) +void free_tty_struct(struct tty_struct *tty) { kfree(tty->write_buf); tty_buffer_free_all(tty); @@ -263,398 +254,6 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) return 0; } -/* - * Tty buffer allocation management - */ - -/** - * tty_buffer_free_all - free buffers used by a tty - * @tty: tty to free from - * - * Remove all the buffers pending on a tty whether queued with data - * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none - */ - -static void tty_buffer_free_all(struct tty_struct *tty) -{ - struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - kfree(thead); - } - while ((thead = tty->buf.free) != NULL) { - tty->buf.free = thead->next; - kfree(thead); - } - tty->buf.tail = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_init - prepare a tty buffer structure - * @tty: tty to initialise - * - * Set up the initial state of the buffer management for a tty device. - * Must be called before the other tty buffer functions are used. - * - * Locking: none - */ - -static void tty_buffer_init(struct tty_struct *tty) -{ - spin_lock_init(&tty->buf.lock); - tty->buf.head = NULL; - tty->buf.tail = NULL; - tty->buf.free = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_alloc - allocate a tty buffer - * @tty: tty device - * @size: desired size (characters) - * - * Allocate a new tty buffer to hold the desired number of characters. - * Return NULL if out of memory or the allocation would exceed the - * per device queue - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *p; - - if (tty->buf.memory_used + size > 65536) - return NULL; - p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); - if (p == NULL) - return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; - return p; -} - -/** - * tty_buffer_free - free a tty buffer - * @tty: tty owning the buffer - * @b: the buffer to free - * - * Free a tty buffer, or add it to the free list according to our - * internal strategy - * - * Locking: Caller must hold tty->buf.lock - */ - -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) -{ - /* Dumb strategy for now - should keep some stats */ - tty->buf.memory_used -= b->size; - WARN_ON(tty->buf.memory_used < 0); - - if (b->size >= 512) - kfree(b); - else { - b->next = tty->buf.free; - tty->buf.free = b; - } -} - -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock - */ - -static void __tty_buffer_flush(struct tty_struct *tty) -{ - struct tty_buffer *thead; - - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - tty_buffer_free(tty, thead); - } - tty->buf.tail = NULL; -} - -/** - * tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. If the buffer is - * being processed by flush_to_ldisc then we defer the processing - * to that function - * - * Locking: none - */ - -static void tty_buffer_flush(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - - /* If the data is being pushed to the tty layer then we can't - process it here. Instead set a flag and the flush_to_ldisc - path will process the flush request before it exits */ - if (test_bit(TTY_FLUSHING, &tty->flags)) { - set_bit(TTY_FLUSHPENDING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); - wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); - return; - } else - __tty_buffer_flush(tty); - spin_unlock_irqrestore(&tty->buf.lock, flags); -} - -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) -{ - struct tty_buffer **tbh = &tty->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { - *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; - tty->buf.memory_used += t->size; - return t; - } - tbh = &((*tbh)->next); - } - /* Round the buffer size out */ - size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, 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: 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) -{ - 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 */ - if ((b = tty->buf.tail) != NULL) - left = b->size - b->used; - else - left = 0; - - if (left < size) { - /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - tty->buf.head = n; - tty->buf.tail = n; - } else - size = left; - } - - spin_unlock_irqrestore(&tty->buf.lock, flags); - return size; -} -EXPORT_SYMBOL_GPL(tty_buffer_request_room); - -/** - * tty_insert_flip_string - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @size: size - * - * Queue a series of bytes to the tty buffering. All the characters - * passed are marked as without error. Returns the number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, - size_t size) -{ - int copied = 0; - do { - int space = tty_buffer_request_room(tty, size - copied); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); - tb->used += space; - copied += space; - chars += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string); - -/** - * tty_insert_flip_string_flags - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @flags: flag bytes - * @size: size - * - * Queue a series of bytes to the tty buffering. For each character - * the flags array indicates the status of the character. Returns the - * number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string_flags(struct tty_struct *tty, - const unsigned char *chars, const char *flags, size_t size) -{ - int copied = 0; - do { - int space = tty_buffer_request_room(tty, size - copied); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); - tb->used += space; - copied += space; - chars += space; - flags += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string_flags); - -/** - * tty_schedule_flip - push characters to ldisc - * @tty: tty to push from - * - * Takes any pending buffers and transfers their ownership to the - * ldisc side of the queue. It then schedules those characters for - * processing by the line discipline. - * - * Locking: Takes tty->buf.lock - */ - -void tty_schedule_flip(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - schedule_delayed_work(&tty->buf.work, 1); -} -EXPORT_SYMBOL(tty_schedule_flip); - -/** - * tty_prepare_flip_string - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for normal characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking tty->buf.lock - */ - -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) -{ - int space = tty_buffer_request_room(tty, size); - 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; - } - return space; -} - -EXPORT_SYMBOL_GPL(tty_prepare_flip_string); - -/** - * tty_prepare_flip_string_flags - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @flags: return pointer for status flag write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking tty->buf.lock - */ - -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); - 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; - } - return space; -} - -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); - - - /** * get_tty_driver - find device of a tty * @dev_t: device identifier @@ -675,7 +274,7 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index) if (device < base || device >= base + p->num) continue; *index = device - base; - return p; + return tty_driver_kref_get(p); } return NULL; } @@ -719,7 +318,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) if (tty_line >= 0 && tty_line <= p->num && p->ops && p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) { - res = p; + res = tty_driver_kref_get(p); *line = tty_line; break; } @@ -819,20 +418,6 @@ static const struct file_operations tty_fops = { .fasync = tty_fasync, }; -#ifdef CONFIG_UNIX98_PTYS -static const struct file_operations ptmx_fops = { - .llseek = no_llseek, - .read = tty_read, - .write = tty_write, - .poll = tty_poll, - .unlocked_ioctl = tty_ioctl, - .compat_ioctl = tty_compat_ioctl, - .open = ptmx_open, - .release = tty_release, - .fasync = tty_fasync, -}; -#endif - static const struct file_operations console_fops = { .llseek = no_llseek, .read = tty_read, @@ -953,6 +538,7 @@ static void do_tty_hangup(struct work_struct *work) struct tty_ldisc *ld; int closecount = 0, n; unsigned long flags; + int refs = 0; if (!tty) return; @@ -1019,8 +605,12 @@ static void do_tty_hangup(struct work_struct *work) if (tty->session) { do_each_pid_task(tty->session, PIDTYPE_SID, p) { spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) + if (p->signal->tty == tty) { p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } if (!p->signal->leader) { spin_unlock_irq(&p->sighand->siglock); continue; @@ -1046,6 +636,10 @@ static void do_tty_hangup(struct work_struct *work) tty->ctrl_status = 0; spin_unlock_irqrestore(&tty->ctrl_lock, flags); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); + /* * If one of the devices matches a console pointer, we * cannot just call hangup() because that will cause @@ -1115,6 +709,23 @@ void tty_vhangup(struct tty_struct *tty) EXPORT_SYMBOL(tty_vhangup); /** + * tty_vhangup_self - process vhangup for own ctty + * + * Perform a vhangup on the current controlling tty + */ + +void tty_vhangup_self(void) +{ + struct tty_struct *tty; + + tty = get_current_tty(); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } +} + +/** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty * @@ -1167,16 +778,14 @@ void disassociate_ctty(int on_exit) struct pid *tty_pgrp = NULL; - mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { tty_pgrp = get_pid(tty->pgrp); lock_kernel(); - mutex_unlock(&tty_mutex); - /* XXX: here we race, there is nothing protecting tty */ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); unlock_kernel(); + tty_kref_put(tty); } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -1188,7 +797,6 @@ void disassociate_ctty(int on_exit) kill_pgrp(old_pgrp, SIGCONT, on_exit); put_pid(old_pgrp); } - mutex_unlock(&tty_mutex); return; } if (tty_pgrp) { @@ -1203,8 +811,6 @@ void disassociate_ctty(int on_exit) current->signal->tty_old_pgrp = NULL; spin_unlock_irq(¤t->sighand->siglock); - mutex_lock(&tty_mutex); - /* It is possible that do_tty_hangup has free'd this tty */ tty = get_current_tty(); if (tty) { unsigned long flags; @@ -1214,13 +820,13 @@ void disassociate_ctty(int on_exit) tty->session = NULL; tty->pgrp = NULL; spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty_kref_put(tty); } else { #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error attempted to write to tty [0x%p]" " = NULL", tty); #endif } - mutex_unlock(&tty_mutex); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); @@ -1420,19 +1026,19 @@ static inline ssize_t do_tty_write( /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ if (tty->write_cnt < chunk) { - unsigned char *buf; + unsigned char *buf_chunk; if (chunk < 1024) chunk = 1024; - buf = kmalloc(chunk, GFP_KERNEL); - if (!buf) { + buf_chunk = kmalloc(chunk, GFP_KERNEL); + if (!buf_chunk) { ret = -ENOMEM; goto out; } kfree(tty->write_buf); tty->write_cnt = chunk; - tty->write_buf = buf; + tty->write_buf = buf_chunk; } /* Do the write .. */ @@ -1466,6 +1072,31 @@ out: return ret; } +/** + * tty_write_message - write a message to a certain tty, not just the console. + * @tty: the destination tty_struct + * @msg: the message to write + * + * This is used for messages that need to be redirected to a specific tty. + * We don't put it into the syslog queue right now maybe in the future if + * really needed. + * + * We must still hold the BKL and test the CLOSING flag for the moment. + */ + +void tty_write_message(struct tty_struct *tty, char *msg) +{ + lock_kernel(); + if (tty) { + mutex_lock(&tty->atomic_write_lock); + if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) + tty->ops->write(tty, msg, strlen(msg)); + tty_write_unlock(tty); + } + unlock_kernel(); + return; +} + /** * tty_write - write method for tty device file @@ -1533,42 +1164,6 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, return tty_write(file, buf, count, ppos); } -void tty_port_init(struct tty_port *port) -{ - memset(port, 0, sizeof(*port)); - init_waitqueue_head(&port->open_wait); - init_waitqueue_head(&port->close_wait); - mutex_init(&port->mutex); - port->close_delay = (50 * HZ) / 100; - port->closing_wait = (3000 * HZ) / 100; -} -EXPORT_SYMBOL(tty_port_init); - -int tty_port_alloc_xmit_buf(struct tty_port *port) -{ - /* We may sleep in get_zeroed_page() */ - mutex_lock(&port->mutex); - if (port->xmit_buf == NULL) - port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - mutex_unlock(&port->mutex); - if (port->xmit_buf == NULL) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL(tty_port_alloc_xmit_buf); - -void tty_port_free_xmit_buf(struct tty_port *port) -{ - mutex_lock(&port->mutex); - if (port->xmit_buf != NULL) { - free_page((unsigned long)port->xmit_buf); - port->xmit_buf = NULL; - } - mutex_unlock(&port->mutex); -} -EXPORT_SYMBOL(tty_port_free_xmit_buf); - - static char ptychar[] = "pqrstuvwxyzabcde"; /** @@ -1592,7 +1187,7 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) } /** - * pty_line_name - generate name for a tty + * tty_line_name - generate name for a tty * @driver: the tty driver in use * @index: the minor number * @p: output buffer of at least 7 bytes @@ -1608,10 +1203,148 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) } /** - * init_dev - initialise a tty device + * tty_driver_lookup_tty() - find an existing tty, if any + * @driver: the driver for the tty + * @idx: the minor number + * + * Return the tty, if found or ERR_PTR() otherwise. + * + * Locking: tty_mutex must be held. If tty is found, the mutex must + * be held until the 'fast-open' is also done. Will change once we + * have refcounting in the driver and per driver locking + */ +struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, + struct inode *inode, int idx) +{ + struct tty_struct *tty; + + if (driver->ops->lookup) + return driver->ops->lookup(driver, inode, idx); + + tty = driver->ttys[idx]; + return tty; +} + +/** + * tty_init_termios - helper for termios setup + * @tty: the tty to set up + * + * Initialise the termios structures for this tty. Thus runs under + * the tty_mutex currently so we can be relaxed about ordering. + */ + +int tty_init_termios(struct tty_struct *tty) +{ + struct ktermios *tp; + int idx = tty->index; + + tp = tty->driver->termios[idx]; + if (tp == NULL) { + tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (tp == NULL) + return -ENOMEM; + memcpy(tp, &tty->driver->init_termios, + sizeof(struct ktermios)); + tty->driver->termios[idx] = tp; + } + tty->termios = tp; + tty->termios_locked = tp + 1; + + /* Compatibility until drivers always set this */ + tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); + tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); + return 0; +} + +/** + * tty_driver_install_tty() - install a tty entry in the driver + * @driver: the driver for the tty + * @tty: the tty + * + * Install a tty object into the driver tables. The tty->index field + * will be set by the time this is called. This method is responsible + * for ensuring any need additional structures are allocated and + * configured. + * + * Locking: tty_mutex for now + */ +static int tty_driver_install_tty(struct tty_driver *driver, + struct tty_struct *tty) +{ + int idx = tty->index; + + if (driver->ops->install) + return driver->ops->install(driver, tty); + + if (tty_init_termios(tty) == 0) { + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; + } + return -ENOMEM; +} + +/** + * tty_driver_remove_tty() - remove a tty from the driver tables + * @driver: the driver for the tty + * @idx: the minor number + * + * Remvoe a tty object from the driver tables. The tty->index field + * will be set by the time this is called. + * + * Locking: tty_mutex for now + */ +static void tty_driver_remove_tty(struct tty_driver *driver, + struct tty_struct *tty) +{ + if (driver->ops->remove) + driver->ops->remove(driver, tty); + else + driver->ttys[tty->index] = NULL; +} + +/* + * tty_reopen() - fast re-open of an open tty + * @tty - the tty to open + * + * Return 0 on success, -errno on error. + * + * Locking: tty_mutex must be held from the time the tty was found + * till this open completes. + */ +static int tty_reopen(struct tty_struct *tty) +{ + struct tty_driver *driver = tty->driver; + + if (test_bit(TTY_CLOSING, &tty->flags)) + return -EIO; + + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) + return -EIO; + + tty->link->count++; + } + tty->count++; + tty->driver = driver; /* N.B. why do this every time?? */ + + WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); + + return 0; +} + +/** + * tty_init_dev - initialise a tty device * @driver: tty driver we are opening a device on * @idx: device index - * @tty: returned tty structure + * @ret_tty: returned tty structure + * @first_ok: ok to open a new device (used by ptmx) * * Prepare a tty device. This may not be a "new" clean device but * could also be an active device. The pty drivers require special @@ -1631,37 +1364,16 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) * relaxed for the (most common) case of reopening a tty. */ -static int init_dev(struct tty_driver *driver, int idx, - struct tty_struct **ret_tty) +struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok) { - struct tty_struct *tty, *o_tty; - struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; - struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; - int retval = 0; + struct tty_struct *tty; + int retval; - /* check whether we're reopening an existing tty */ - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - tty = devpts_get_tty(idx); - /* - * If we don't have a tty here on a slave open, it's because - * the master already started the close process and there's - * no relation between devpts file and tty anymore. - */ - if (!tty && driver->subtype == PTY_TYPE_SLAVE) { - retval = -EIO; - goto end_init; - } - /* - * It's safe from now on because init_dev() is called with - * tty_mutex held and release_dev() won't change tty->count - * or tty->flags without having to grab tty_mutex - */ - if (tty && driver->subtype == PTY_TYPE_MASTER) - tty = tty->link; - } else { - tty = driver->ttys[idx]; - } - if (tty) goto fast_track; + /* Check if pty master is being opened multiple times */ + if (driver->subtype == PTY_TYPE_MASTER && + (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) + return ERR_PTR(-EIO); /* * First time open is complex, especially for PTY devices. @@ -1671,189 +1383,69 @@ static int init_dev(struct tty_driver *driver, int idx, * and locked termios may be retained.) */ - if (!try_module_get(driver->owner)) { - retval = -ENODEV; - goto end_init; - } - - o_tty = NULL; - tp = o_tp = NULL; - ltp = o_ltp = NULL; + if (!try_module_get(driver->owner)) + return ERR_PTR(-ENODEV); tty = alloc_tty_struct(); if (!tty) goto fail_no_mem; - initialize_tty_struct(tty); - tty->driver = driver; - tty->ops = driver->ops; - tty->index = idx; - tty_line_name(driver, idx, tty->name); - - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - tp_loc = &tty->termios; - ltp_loc = &tty->termios_locked; - } else { - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; - } - - if (!*tp_loc) { - tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!tp) - goto free_mem_out; - *tp = driver->init_termios; - } - - if (!*ltp_loc) { - ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!ltp) - goto free_mem_out; - } - - if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty = alloc_tty_struct(); - if (!o_tty) - goto free_mem_out; - initialize_tty_struct(o_tty); - o_tty->driver = driver->other; - o_tty->ops = driver->ops; - o_tty->index = idx; - tty_line_name(driver->other, idx, o_tty->name); + initialize_tty_struct(tty, driver, idx); - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - o_tp_loc = &o_tty->termios; - o_ltp_loc = &o_tty->termios_locked; - } else { - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; - } - - if (!*o_tp_loc) { - o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!o_tp) - goto free_mem_out; - *o_tp = driver->other->init_termios; - } - - if (!*o_ltp_loc) { - o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!o_ltp) - goto free_mem_out; - } - - /* - * Everything allocated ... set up the o_tty structure. - */ - if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) - driver->other->ttys[idx] = o_tty; - if (!*o_tp_loc) - *o_tp_loc = o_tp; - if (!*o_ltp_loc) - *o_ltp_loc = o_ltp; - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - driver->other->refcount++; - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; + retval = tty_driver_install_tty(driver, tty); + if (retval < 0) { + free_tty_struct(tty); + module_put(driver->owner); + return ERR_PTR(retval); } /* - * All structures have been allocated, so now we install them. - * Failures after this point use release_tty to clean up, so - * there's no need to null out the local pointers. - */ - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) - driver->ttys[idx] = tty; - - if (!*tp_loc) - *tp_loc = tp; - if (!*ltp_loc) - *ltp_loc = ltp; - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - /* Compatibility until drivers always set this */ - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); - driver->refcount++; - tty->count++; - - /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ - retval = tty_ldisc_setup(tty, o_tty); - + retval = tty_ldisc_setup(tty, tty->link); if (retval) goto release_mem_out; - goto success; - - /* - * This fast open can be used if the tty is already open. - * No memory is allocated, and the only failures are from - * attempting to open a closing tty or attempting multiple - * opens on a pty master. - */ -fast_track: - if (test_bit(TTY_CLOSING, &tty->flags)) { - retval = -EIO; - goto end_init; - } - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER) { - /* - * special case for PTY masters: only one open permitted, - * and the slave side open count is incremented as well. - */ - if (tty->count) { - retval = -EIO; - goto end_init; - } - tty->link->count++; - } - tty->count++; - tty->driver = driver; /* N.B. why do this every time?? */ - - /* FIXME */ - if (!test_bit(TTY_LDISC, &tty->flags)) - printk(KERN_ERR "init_dev but no ldisc\n"); -success: - *ret_tty = tty; - - /* All paths come through here to release the mutex */ -end_init: - return retval; - - /* Release locally allocated memory ... nothing placed in slots */ -free_mem_out: - kfree(o_tp); - if (o_tty) - free_tty_struct(o_tty); - kfree(ltp); - kfree(tp); - free_tty_struct(tty); + return tty; fail_no_mem: module_put(driver->owner); - retval = -ENOMEM; - goto end_init; + return ERR_PTR(-ENOMEM); /* call the tty release_tty routine to clean out this slot */ release_mem_out: if (printk_ratelimit()) - printk(KERN_INFO "init_dev: ldisc open failed, " + printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); - goto end_init; + return ERR_PTR(retval); } +void tty_free_termios(struct tty_struct *tty) +{ + struct ktermios *tp; + int idx = tty->index; + /* Kill this flag and push into drivers for locking etc */ + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { + /* FIXME: Locking on ->termios array */ + tp = tty->termios; + tty->driver->termios[idx] = NULL; + kfree(tp); + } +} +EXPORT_SYMBOL(tty_free_termios); + +void tty_shutdown(struct tty_struct *tty) +{ + tty_driver_remove_tty(tty->driver, tty); + tty_free_termios(tty); +} +EXPORT_SYMBOL(tty_shutdown); + /** * release_one_tty - release tty structure memory + * @kref: kref of tty we are obliterating * * Releases memory associated with a tty structure, and clears out the * driver table slots. This function is called when a device is no longer @@ -1863,31 +1455,19 @@ release_mem_out: * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. - * FIXME: should we require tty_mutex is held here ?? */ -static void release_one_tty(struct tty_struct *tty, int idx) +static void release_one_tty(struct kref *kref) { - int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; - struct ktermios *tp; - - if (!devpts) - tty->driver->ttys[idx] = NULL; - - if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - tp = tty->termios; - if (!devpts) - tty->driver->termios[idx] = NULL; - kfree(tp); - - tp = tty->termios_locked; - if (!devpts) - tty->driver->termios_locked[idx] = NULL; - kfree(tp); - } - + struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + struct tty_driver *driver = tty->driver; + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + else + tty_shutdown(tty); tty->magic = 0; - tty->driver->refcount--; + tty_driver_kref_put(driver); + module_put(driver->owner); file_list_lock(); list_del_init(&tty->tty_files); @@ -1897,6 +1477,21 @@ static void release_one_tty(struct tty_struct *tty, int idx) } /** + * tty_kref_put - release a tty kref + * @tty: tty device + * + * Release a reference to a tty device and if need be let the kref + * layer destruct the object for us + */ + +void tty_kref_put(struct tty_struct *tty) +{ + if (tty) + kref_put(&tty->kref, release_one_tty); +} +EXPORT_SYMBOL(tty_kref_put); + +/** * release_tty - release tty structure memory * * Release both @tty and a possible linked partner (think pty pair), @@ -1907,15 +1502,16 @@ static void release_one_tty(struct tty_struct *tty, int idx) * takes the file list lock internally when working on the list * of ttys that the driver keeps. * FIXME: should we require tty_mutex is held here ?? + * */ static void release_tty(struct tty_struct *tty, int idx) { - struct tty_driver *driver = tty->driver; + /* This should always be true but check for the moment */ + WARN_ON(tty->index != idx); if (tty->link) - release_one_tty(tty->link, idx); - release_one_tty(tty, idx); - module_put(driver->owner); + tty_kref_put(tty->link); + tty_kref_put(tty); } /* @@ -1926,20 +1522,21 @@ static void release_tty(struct tty_struct *tty, int idx) * WSH 09/09/97: rewritten to avoid some nasty race conditions that could * lead to double frees or releasing memory still in use. */ -static void release_dev(struct file *filp) +void tty_release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; char buf[64]; + struct inode *inode; + inode = filp->f_path.dentry->d_inode; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, - "release_dev")) + if (tty_paranoia_check(tty, inode, "tty_release_dev")) return; - check_tty_count(tty, "release_dev"); + check_tty_count(tty, "tty_release_dev"); tty_fasync(-1, filp, 0); @@ -1951,33 +1548,27 @@ static void release_dev(struct file *filp) #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "release_dev: bad idx when trying to " + printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " "free (%s)\n", tty->name); return; } - if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { + if (!devpts) { if (tty != tty->driver->ttys[idx]) { - printk(KERN_DEBUG "release_dev: driver.table[%d] not tty " + printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " "for (%s)\n", idx, tty->name); return; } if (tty->termios != tty->driver->termios[idx]) { - printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios " + printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " "for (%s)\n", idx, tty->name); return; } - if (tty->termios_locked != tty->driver->termios_locked[idx]) { - printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not " - "termios_locked for (%s)\n", - idx, tty->name); - return; - } } #endif #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "release_dev of %s (tty count=%d)...", + printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", tty_name(tty, buf), tty->count); #endif @@ -1985,26 +1576,19 @@ static void release_dev(struct file *filp) if (tty->driver->other && !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (o_tty != tty->driver->other->ttys[idx]) { - printk(KERN_DEBUG "release_dev: other->table[%d] " + printk(KERN_DEBUG "tty_release_dev: other->table[%d] " "not o_tty for (%s)\n", idx, tty->name); return; } if (o_tty->termios != tty->driver->other->termios[idx]) { - printk(KERN_DEBUG "release_dev: other->termios[%d] " + printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " "not o_termios for (%s)\n", idx, tty->name); return; } - if (o_tty->termios_locked != - tty->driver->other->termios_locked[idx]) { - printk(KERN_DEBUG "release_dev: other->termios_locked[" - "%d] not o_termios_locked for (%s)\n", - idx, tty->name); - return; - } if (o_tty->link != tty) { - printk(KERN_DEBUG "release_dev: bad pty pointers\n"); + printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); return; } } @@ -2062,7 +1646,7 @@ static void release_dev(struct file *filp) if (!do_sleep) break; - printk(KERN_WARNING "release_dev: %s: read/write wait queue " + printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " "active!\n", tty_name(tty, buf)); mutex_unlock(&tty_mutex); schedule(); @@ -2075,14 +1659,14 @@ static void release_dev(struct file *filp) */ if (pty_master) { if (--o_tty->count < 0) { - printk(KERN_WARNING "release_dev: bad pty slave count " + printk(KERN_WARNING "tty_release_dev: bad pty slave count " "(%d) for %s\n", o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { - printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n", + printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", tty->count, tty_name(tty, buf)); tty->count = 0; } @@ -2145,11 +1729,11 @@ static void release_dev(struct file *filp) /* Make this pty number available for reallocation */ if (devpts) - devpts_kill_index(idx); + devpts_kill_index(inode, idx); } /** - * tty_open - open a tty device + * __tty_open - open a tty device * @inode: inode of device file * @filp: file pointer to tty * @@ -2164,14 +1748,14 @@ static void release_dev(struct file *filp) * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * - * Locking: tty_mutex protects tty, get_tty_driver and init_dev work. + * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand */ static int __tty_open(struct inode *inode, struct file *filp) { - struct tty_struct *tty; + struct tty_struct *tty = NULL; int noctty, retval; struct tty_driver *driver; int index; @@ -2193,23 +1777,25 @@ retry_open: mutex_unlock(&tty_mutex); return -ENXIO; } - driver = tty->driver; + driver = tty_driver_kref_get(tty->driver); index = tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ + /* FIXME: Should we take a driver reference ? */ + tty_kref_put(tty); goto got_driver; } #ifdef CONFIG_VT if (device == MKDEV(TTY_MAJOR, 0)) { extern struct tty_driver *console_driver; - driver = console_driver; + driver = tty_driver_kref_get(console_driver); index = fg_console; noctty = 1; goto got_driver; } #endif if (device == MKDEV(TTYAUX_MAJOR, 1)) { - driver = console_device(&index); + driver = tty_driver_kref_get(console_device(&index)); if (driver) { /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; @@ -2226,10 +1812,25 @@ retry_open: return -ENODEV; } got_driver: - retval = init_dev(driver, index, &tty); + if (!tty) { + /* check whether we're reopening an existing tty */ + tty = tty_driver_lookup_tty(driver, inode, index); + + if (IS_ERR(tty)) + return PTR_ERR(tty); + } + + if (tty) { + retval = tty_reopen(tty); + if (retval) + tty = ERR_PTR(retval); + } else + tty = tty_init_dev(driver, index, 0); + mutex_unlock(&tty_mutex); - if (retval) - return retval; + tty_driver_kref_put(driver); + if (IS_ERR(tty)) + return PTR_ERR(tty); filp->private_data = tty; file_move(filp, &tty->tty_files); @@ -2257,7 +1858,7 @@ got_driver: printk(KERN_DEBUG "error %d in opening %s...", retval, tty->name); #endif - release_dev(filp); + tty_release_dev(filp); if (retval != -ERESTARTSYS) return retval; if (signal_pending(current)) @@ -2296,69 +1897,6 @@ static int tty_open(struct inode *inode, struct file *filp) -#ifdef CONFIG_UNIX98_PTYS -/** - * ptmx_open - open a unix 98 pty master - * @inode: inode of device file - * @filp: file pointer to tty - * - * Allocate a unix98 pty master device from the ptmx driver. - * - * Locking: tty_mutex protects theinit_dev work. tty->count should - * protect the rest. - * allocated_ptys_lock handles the list of free pty numbers - */ - -static int __ptmx_open(struct inode *inode, struct file *filp) -{ - struct tty_struct *tty; - int retval; - int index; - - nonseekable_open(inode, filp); - - /* find a device that is not in use. */ - index = devpts_new_index(); - if (index < 0) - return index; - - mutex_lock(&tty_mutex); - retval = init_dev(ptm_driver, index, &tty); - mutex_unlock(&tty_mutex); - - if (retval) - goto out; - - set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ - filp->private_data = tty; - file_move(filp, &tty->tty_files); - - retval = devpts_pty_new(tty->link); - if (retval) - goto out1; - - check_tty_count(tty, "ptmx_open"); - retval = ptm_driver->ops->open(tty, filp); - if (!retval) - return 0; -out1: - release_dev(filp); - return retval; -out: - devpts_kill_index(index); - return retval; -} - -static int ptmx_open(struct inode *inode, struct file *filp) -{ - int ret; - - lock_kernel(); - ret = __ptmx_open(inode, filp); - unlock_kernel(); - return ret; -} -#endif /** * tty_release - vfs callback for close @@ -2369,13 +1907,13 @@ static int ptmx_open(struct inode *inode, struct file *filp) * this tty. There may however be several such references. * * Locking: - * Takes bkl. See release_dev + * Takes bkl. See tty_release_dev */ static int tty_release(struct inode *inode, struct file *filp) { lock_kernel(); - release_dev(filp); + tty_release_dev(filp); unlock_kernel(); return 0; } @@ -2524,7 +2062,7 @@ int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, /* For a PTY we need to lock the tty side */ mutex_lock(&real_tty->termios_mutex); - if (!memcmp(ws, &tty->winsize, sizeof(*ws))) + if (!memcmp(ws, &real_tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can avoid holding the tty ctrl lock while sending signals */ @@ -2996,7 +2534,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCSTI: return tiocsti(tty, p); case TIOCGWINSZ: - return tiocgwinsz(tty, p); + return tiocgwinsz(real_tty, p); case TIOCSWINSZ: return tiocswinsz(tty, real_tty, p); case TIOCCONS: @@ -3026,10 +2564,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return put_user(tty->ldisc.ops->num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); -#ifdef CONFIG_VT - case TIOCLINUX: - return tioclinux(tty, arg); -#endif /* * Break handling */ @@ -3220,113 +2754,6 @@ void do_SAK(struct tty_struct *tty) EXPORT_SYMBOL(do_SAK); /** - * flush_to_ldisc - * @work: tty structure passed from work queue. - * - * This routine is called out of the software interrupt to flush data - * from the buffer chain to the line discipline. - * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. - */ - -static void flush_to_ldisc(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work.work); - unsigned long flags; - struct tty_ldisc *disc; - struct tty_buffer *tbuf, *head; - char *char_buf; - unsigned char *flag_buf; - - disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ - return; - - spin_lock_irqsave(&tty->buf.lock, flags); - /* So we know a flush is running */ - set_bit(TTY_FLUSHING, &tty->flags); - head = tty->buf.head; - if (head != NULL) { - tty->buf.head = NULL; - for (;;) { - int count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - tbuf = head; - head = head->next; - tty_buffer_free(tty, tbuf); - continue; - } - /* Ldisc or user is trying to flush the buffers - we are feeding to the ldisc, stop feeding the - line discipline as we want to empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) - break; - if (!tty->receive_room) { - schedule_delayed_work(&tty->buf.work, 1); - break; - } - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; - spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); - spin_lock_irqsave(&tty->buf.lock, flags); - } - /* Restore the queue head */ - tty->buf.head = head; - } - /* We may have a deferred request to flush the input buffer, - if so pull the chain under the lock and empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { - __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); - wake_up(&tty->read_wait); - } - clear_bit(TTY_FLUSHING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); - - tty_ldisc_deref(disc); -} - -/** - * tty_flip_buffer_push - terminal - * @tty: tty to push - * - * Queue a push of the terminal flip buffers to the line discipline. This - * function must not be called from IRQ context if tty->low_latency is set. - * - * In the event of the queue being busy for flipping the work will be - * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. - */ - -void tty_flip_buffer_push(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - - if (tty->low_latency) - flush_to_ldisc(&tty->buf.work.work); - else - schedule_delayed_work(&tty->buf.work, 1); -} - -EXPORT_SYMBOL(tty_flip_buffer_push); - - -/** * initialize_tty_struct * @tty: tty to initialize * @@ -3336,9 +2763,11 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * Locking: none - tty in question must not be exposed at this point */ -static void initialize_tty_struct(struct tty_struct *tty) +void initialize_tty_struct(struct tty_struct *tty, + struct tty_driver *driver, int idx) { memset(tty, 0, sizeof(struct tty_struct)); + kref_init(&tty->kref); tty->magic = TTY_MAGIC; tty_ldisc_init(tty); tty->session = NULL; @@ -3346,7 +2775,6 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->overrun_time = jiffies; tty->buf.head = tty->buf.tail = NULL; tty_buffer_init(tty); - INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); mutex_init(&tty->termios_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); @@ -3357,6 +2785,11 @@ static void initialize_tty_struct(struct tty_struct *tty) spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); + + tty->driver = driver; + tty->ops = driver->ops; + tty->index = idx; + tty_line_name(driver, idx, tty->name); } /** @@ -3377,10 +2810,9 @@ int tty_put_char(struct tty_struct *tty, unsigned char ch) return tty->ops->put_char(tty, ch); return tty->ops->write(tty, &ch, 1); } - EXPORT_SYMBOL_GPL(tty_put_char); -static struct class *tty_class; +struct class *tty_class; /** * tty_register_device - register a tty device @@ -3420,6 +2852,7 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, return device_create_drvdata(tty_class, device, dev, NULL, name); } +EXPORT_SYMBOL(tty_register_device); /** * tty_unregister_device - unregister a tty device @@ -3437,8 +2870,6 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); } - -EXPORT_SYMBOL(tty_register_device); EXPORT_SYMBOL(tty_unregister_device); struct tty_driver *alloc_tty_driver(int lines) @@ -3447,27 +2878,65 @@ struct tty_driver *alloc_tty_driver(int lines) driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); if (driver) { + kref_init(&driver->kref); driver->magic = TTY_DRIVER_MAGIC; driver->num = lines; /* later we'll move allocation of tables here */ } return driver; } +EXPORT_SYMBOL(alloc_tty_driver); -void put_tty_driver(struct tty_driver *driver) +static void destruct_tty_driver(struct kref *kref) { + struct tty_driver *driver = container_of(kref, struct tty_driver, kref); + int i; + struct ktermios *tp; + void *p; + + if (driver->flags & TTY_DRIVER_INSTALLED) { + /* + * Free the termios and termios_locked structures because + * we don't want to get memory leaks when modular tty + * drivers are removed from the kernel. + */ + for (i = 0; i < driver->num; i++) { + tp = driver->termios[i]; + if (tp) { + driver->termios[i] = NULL; + kfree(tp); + } + if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) + tty_unregister_device(driver, i); + } + p = driver->ttys; + proc_tty_unregister_driver(driver); + driver->ttys = NULL; + driver->termios = NULL; + kfree(p); + cdev_del(&driver->cdev); + } kfree(driver); } +void tty_driver_kref_put(struct tty_driver *driver) +{ + kref_put(&driver->kref, destruct_tty_driver); +} +EXPORT_SYMBOL(tty_driver_kref_put); + void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { driver->ops = op; }; +EXPORT_SYMBOL(tty_set_operations); -EXPORT_SYMBOL(alloc_tty_driver); +void put_tty_driver(struct tty_driver *d) +{ + tty_driver_kref_put(d); +} EXPORT_SYMBOL(put_tty_driver); -EXPORT_SYMBOL(tty_set_operations); /* * Called by a tty driver to register itself. @@ -3479,11 +2948,8 @@ int tty_register_driver(struct tty_driver *driver) dev_t dev; void **p = NULL; - if (driver->flags & TTY_DRIVER_INSTALLED) - return 0; - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { - p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL); + p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL); if (!p) return -ENOMEM; } @@ -3507,12 +2973,9 @@ int tty_register_driver(struct tty_driver *driver) if (p) { driver->ttys = (struct tty_struct **)p; driver->termios = (struct ktermios **)(p + driver->num); - driver->termios_locked = (struct ktermios **) - (p + driver->num * 2); } else { driver->ttys = NULL; driver->termios = NULL; - driver->termios_locked = NULL; } cdev_init(&driver->cdev, &tty_fops); @@ -3521,7 +2984,7 @@ int tty_register_driver(struct tty_driver *driver) if (error) { unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; - driver->termios = driver->termios_locked = NULL; + driver->termios = NULL; kfree(p); return error; } @@ -3535,6 +2998,7 @@ int tty_register_driver(struct tty_driver *driver) tty_register_device(driver, i, NULL); } proc_tty_register_driver(driver); + driver->flags |= TTY_DRIVER_INSTALLED; return 0; } @@ -3545,46 +3009,19 @@ EXPORT_SYMBOL(tty_register_driver); */ int tty_unregister_driver(struct tty_driver *driver) { - int i; - struct ktermios *tp; - void *p; - +#if 0 + /* FIXME */ if (driver->refcount) return -EBUSY; - +#endif unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); mutex_lock(&tty_mutex); list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); - - /* - * Free the termios and termios_locked structures because - * we don't want to get memory leaks when modular tty - * drivers are removed from the kernel. - */ - for (i = 0; i < driver->num; i++) { - tp = driver->termios[i]; - if (tp) { - driver->termios[i] = NULL; - kfree(tp); - } - tp = driver->termios_locked[i]; - if (tp) { - driver->termios_locked[i] = NULL; - kfree(tp); - } - if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) - tty_unregister_device(driver, i); - } - p = driver->ttys; - proc_tty_unregister_driver(driver); - driver->ttys = NULL; - driver->termios = driver->termios_locked = NULL; - kfree(p); - cdev_del(&driver->cdev); return 0; } + EXPORT_SYMBOL(tty_unregister_driver); dev_t tty_devnum(struct tty_struct *tty) @@ -3595,9 +3032,12 @@ EXPORT_SYMBOL(tty_devnum); void proc_clear_tty(struct task_struct *p) { + struct tty_struct *tty; spin_lock_irq(&p->sighand->siglock); + tty = p->signal->tty; p->signal->tty = NULL; spin_unlock_irq(&p->sighand->siglock); + tty_kref_put(tty); } /* Called under the sighand lock */ @@ -3613,9 +3053,13 @@ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) tty->pgrp = get_pid(task_pgrp(tsk)); spin_unlock_irqrestore(&tty->ctrl_lock, flags); tty->session = get_pid(task_session(tsk)); + if (tsk->signal->tty) { + printk(KERN_DEBUG "tty not NULL!!\n"); + tty_kref_put(tsk->signal->tty); + } } put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty; + tsk->signal->tty = tty_kref_get(tty); tsk->signal->tty_old_pgrp = NULL; } @@ -3629,18 +3073,20 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) struct tty_struct *get_current_tty(void) { struct tty_struct *tty; - WARN_ON_ONCE(!mutex_is_locked(&tty_mutex)); - tty = current->signal->tty; - /* - * session->tty can be changed/cleared from under us, make sure we - * issue the load. The obtained pointer, when not NULL, is valid as - * long as we hold tty_mutex. - */ - barrier(); + unsigned long flags; + + spin_lock_irqsave(¤t->sighand->siglock, flags); + tty = tty_kref_get(current->signal->tty); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); return tty; } EXPORT_SYMBOL_GPL(get_current_tty); +void tty_default_fops(struct file_operations *fops) +{ + *fops = tty_fops; +} + /* * Initialize the console device. This is called *early*, so * we can't necessarily depend on lots of kernel help here. @@ -3678,12 +3124,6 @@ postcore_initcall(tty_class_init); /* 3/2004 jmc: why do these devices exist? */ static struct cdev tty_cdev, console_cdev; -#ifdef CONFIG_UNIX98_PTYS -static struct cdev ptmx_cdev; -#endif -#ifdef CONFIG_VT -static struct cdev vc0_cdev; -#endif /* * Ok, now we can initialize the rest of the tty devices and can count @@ -3695,32 +3135,18 @@ static int __init tty_init(void) if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) panic("Couldn't register /dev/tty driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty"); cdev_init(&console_cdev, &console_fops); if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) panic("Couldn't register /dev/console driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console"); -#ifdef CONFIG_UNIX98_PTYS - cdev_init(&ptmx_cdev, &ptmx_fops); - if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || - register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) - panic("Couldn't register /dev/ptmx driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); -#endif - #ifdef CONFIG_VT - cdev_init(&vc0_cdev, &console_fops); - if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || - register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) - panic("Couldn't register /dev/tty0 driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); - - vty_init(); + vty_init(&console_fops); #endif return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index bf34e459742..a408c8e487e 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -40,6 +40,15 @@ #define TERMIOS_OLD 8 +/** + * tty_chars_in_buffer - characters pending + * @tty: terminal + * + * Return the number of bytes of data in the device private + * output queue. If no private method is supplied there is assumed + * to be no queue on the device. + */ + int tty_chars_in_buffer(struct tty_struct *tty) { if (tty->ops->chars_in_buffer) @@ -47,26 +56,49 @@ int tty_chars_in_buffer(struct tty_struct *tty) else return 0; } - EXPORT_SYMBOL(tty_chars_in_buffer); +/** + * tty_write_room - write queue space + * @tty: terminal + * + * Return the number of bytes that can be queued to this device + * at the present time. The result should be treated as a guarantee + * and the driver cannot offer a value it later shrinks by more than + * the number of bytes written. If no method is provided 2K is always + * returned and data may be lost as there will be no flow control. + */ + int tty_write_room(struct tty_struct *tty) { if (tty->ops->write_room) return tty->ops->write_room(tty); return 2048; } - EXPORT_SYMBOL(tty_write_room); +/** + * tty_driver_flush_buffer - discard internal buffer + * @tty: terminal + * + * Discard the internal output buffer for this device. If no method + * is provided then either the buffer cannot be hardware flushed or + * there is no buffer driver side. + */ void tty_driver_flush_buffer(struct tty_struct *tty) { if (tty->ops->flush_buffer) tty->ops->flush_buffer(tty); } - EXPORT_SYMBOL(tty_driver_flush_buffer); +/** + * tty_throttle - flow control + * @tty: terminal + * + * Indicate that a tty should stop transmitting data down the stack. + */ + void tty_throttle(struct tty_struct *tty) { /* check TTY_THROTTLED first so it indicates our state */ @@ -76,6 +108,13 @@ void tty_throttle(struct tty_struct *tty) } EXPORT_SYMBOL(tty_throttle); +/** + * tty_unthrottle - flow control + * @tty: terminal + * + * Indicate that a tty may continue transmitting data down the stack. + */ + void tty_unthrottle(struct tty_struct *tty) { if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && @@ -112,6 +151,11 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) } EXPORT_SYMBOL(tty_wait_until_sent); + +/* + * Termios Helper Methods + */ + static void unset_locked_termios(struct ktermios *termios, struct ktermios *old, struct ktermios *locked) @@ -346,6 +390,16 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, } EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); +/** + * tty_encode_baud_rate - set baud rate of the tty + * @ibaud: input baud rate + * @obad: output baud rate + * + * Update the current termios data for the tty with the new speed + * settings. The caller must hold the termios_mutex for the tty in + * question. + */ + void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) { tty_termios_encode_baud_rate(tty->termios, ibaud, obaud); @@ -430,12 +484,11 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * - * Locking: termios_sem + * Locking: termios_mutex */ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) { - int canon_change; struct ktermios old_termios; struct tty_ldisc *ld; unsigned long flags; @@ -451,18 +504,6 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) old_termios = *tty->termios; *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); - canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; - if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; - tty->erasing = 0; - } - - /* This bit should be in the ldisc code */ - if (canon_change && !L_ICANON(tty) && tty->read_cnt) - /* Get characters left over from canonical mode. */ - wake_up_interruptible(&tty->read_wait); /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { @@ -508,7 +549,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) * functions before using change_termios to do the actual changes. * * Locking: - * Called functions take ldisc and termios_sem locks + * Called functions take ldisc and termios_mutex locks */ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -579,25 +620,51 @@ static int get_termio(struct tty_struct *tty, struct termio __user *termio) return 0; } -static unsigned long inq_canon(struct tty_struct *tty) + +#ifdef TCGETX + +/** + * set_termiox - set termiox fields if possible + * @tty: terminal + * @arg: termiox structure from user + * @opt: option flags for ioctl type + * + * Implement the device calling points for the SYS5 termiox ioctl + * interface in Linux + */ + +static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) { - int nr, head, tail; + struct termiox tnew; + struct tty_ldisc *ld; - if (!tty->canon_data || !tty->read_buf) - return 0; - head = tty->canon_head; - tail = tty->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); - /* Skip EOF-chars.. */ - while (head != tail) { - if (test_bit(tail, tty->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) - nr--; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); + if (tty->termiox == NULL) + return -EINVAL; + if (copy_from_user(&tnew, arg, sizeof(struct termiox))) + return -EFAULT; + + ld = tty_ldisc_ref(tty); + if (ld != NULL) { + if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); + tty_ldisc_deref(ld); } - return nr; + if (opt & TERMIOS_WAIT) { + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + } + + mutex_lock(&tty->termios_mutex); + if (tty->ops->set_termiox) + tty->ops->set_termiox(tty, &tnew); + mutex_unlock(&tty->termios_mutex); + return 0; } +#endif + + #ifdef TIOCGETP /* * These are deprecated, but there is limited support.. @@ -671,7 +738,7 @@ static void set_sgflags(struct ktermios *termios, int flags) * Updates a terminal from the legacy BSD style terminal information * structure. * - * Locking: termios_sem + * Locking: termios_mutex */ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) @@ -849,6 +916,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, { struct tty_struct *real_tty; void __user *p = (void __user *)arg; + int ret = 0; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -884,18 +952,24 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, return set_termios(real_tty, p, TERMIOS_OLD); #ifndef TCGETS2 case TCGETS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; #else case TCGETS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TCGETS2: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TCSETSF2: return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT); case TCSETSW2: @@ -913,34 +987,59 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, return set_termios(real_tty, p, TERMIOS_TERMIO); #ifndef TCGETS2 case TIOCGLCKTRMIOS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TIOCSLCKTRMIOS: if (!capable(CAP_SYS_ADMIN)) return -EPERM; + mutex_lock(&real_tty->termios_mutex); if (user_termios_to_kernel_termios(real_tty->termios_locked, (struct termios __user *) arg)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; #else case TIOCGLCKTRMIOS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TIOCSLCKTRMIOS: if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + ret = -EPERM; + mutex_lock(&real_tty->termios_mutex); if (user_termios_to_kernel_termios_1(real_tty->termios_locked, (struct termios __user *) arg)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; #endif +#ifdef TCGETX + case TCGETX: + if (real_tty->termiox == NULL) + return -EINVAL; + mutex_lock(&real_tty->termios_mutex); + if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox))) + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; + case TCSETX: + return set_termiox(real_tty, p, 0); + case TCSETXW: + return set_termiox(real_tty, p, TERMIOS_WAIT); + case TCSETXF: + return set_termiox(real_tty, p, TERMIOS_FLUSH); +#endif case TIOCGSOFTCAR: - /* FIXME: for correctness we may need to take the termios - lock here - review */ - return put_user(C_CLOCAL(real_tty) ? 1 : 0, + mutex_lock(&real_tty->termios_mutex); + ret = put_user(C_CLOCAL(real_tty) ? 1 : 0, (int __user *)arg); + mutex_unlock(&real_tty->termios_mutex); + return ret; case TIOCSSOFTCAR: if (get_user(arg, (unsigned int __user *) arg)) return -EFAULT; @@ -980,7 +1079,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) } EXPORT_SYMBOL_GPL(tty_perform_flush); -int n_tty_ioctl(struct tty_struct *tty, struct file *file, +int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long flags; @@ -1018,13 +1117,6 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return 0; case TCFLSH: return tty_perform_flush(tty, arg); - case TIOCOUTQ: - return put_user(tty_chars_in_buffer(tty), (int __user *) arg); - case TIOCINQ: - retval = tty->read_cnt; - if (L_ICANON(tty)) - retval = inq_canon(tty); - return put_user(retval, (unsigned int __user *) arg); case TIOCPKT: { int pktmode; @@ -1050,4 +1142,4 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return tty_mode_ioctl(tty, file, cmd, arg); } } -EXPORT_SYMBOL(n_tty_ioctl); +EXPORT_SYMBOL(n_tty_ioctl_helper); diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c new file mode 100644 index 00000000000..553b0e9d8d1 --- /dev/null +++ b/drivers/char/tty_port.c @@ -0,0 +1,96 @@ +/* + * Tty port functions + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/module.h> + +void tty_port_init(struct tty_port *port) +{ + memset(port, 0, sizeof(*port)); + init_waitqueue_head(&port->open_wait); + init_waitqueue_head(&port->close_wait); + mutex_init(&port->mutex); + spin_lock_init(&port->lock); + port->close_delay = (50 * HZ) / 100; + port->closing_wait = (3000 * HZ) / 100; +} +EXPORT_SYMBOL(tty_port_init); + +int tty_port_alloc_xmit_buf(struct tty_port *port) +{ + /* We may sleep in get_zeroed_page() */ + mutex_lock(&port->mutex); + if (port->xmit_buf == NULL) + port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + mutex_unlock(&port->mutex); + if (port->xmit_buf == NULL) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(tty_port_alloc_xmit_buf); + +void tty_port_free_xmit_buf(struct tty_port *port) +{ + mutex_lock(&port->mutex); + if (port->xmit_buf != NULL) { + free_page((unsigned long)port->xmit_buf); + port->xmit_buf = NULL; + } + mutex_unlock(&port->mutex); +} +EXPORT_SYMBOL(tty_port_free_xmit_buf); + + +/** + * tty_port_tty_get - get a tty reference + * @port: tty port + * + * Return a refcount protected tty instance or NULL if the port is not + * associated with a tty (eg due to close or hangup) + */ + +struct tty_struct *tty_port_tty_get(struct tty_port *port) +{ + unsigned long flags; + struct tty_struct *tty; + + spin_lock_irqsave(&port->lock, flags); + tty = tty_kref_get(port->tty); + spin_unlock_irqrestore(&port->lock, flags); + return tty; +} +EXPORT_SYMBOL(tty_port_tty_get); + +/** + * tty_port_tty_set - set the tty of a port + * @port: tty port + * @tty: the tty + * + * Associate the port and tty pair. Manages any internal refcounts. + * Pass NULL to deassociate a port + */ + +void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (port->tty) + tty_kref_put(port->tty); + port->tty = tty; + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL(tty_port_tty_set); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 60359c36091..57029fefd64 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -100,10 +100,10 @@ #include <linux/font.h> #include <linux/bitops.h> #include <linux/notifier.h> - -#include <asm/io.h> +#include <linux/device.h> +#include <linux/io.h> #include <asm/system.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #define MAX_NR_CON_DRIVER 16 @@ -2136,27 +2136,9 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co release_console_sem(); return 0; } - release_console_sem(); - orig_buf = buf; orig_count = count; - /* At this point 'buf' is guaranteed to be a kernel buffer - * and therefore no access to userspace (and therefore sleeping) - * will be needed. The con_buf_mtx serializes all tty based - * console rendering and vcs write/read operations. We hold - * the console spinlock during the entire write. - */ - - acquire_console_sem(); - - vc = tty->driver_data; - if (vc == NULL) { - printk(KERN_ERR "vt: argh, driver_data _became_ NULL !\n"); - release_console_sem(); - goto out; - } - himask = vc->vc_hi_font_mask; charmask = himask ? 0x1ff : 0xff; @@ -2370,8 +2352,6 @@ rescan_last_byte: FLUSH console_conditional_schedule(); release_console_sem(); - -out: notify_update(vc); return n; #undef FLUSH @@ -2583,8 +2563,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) int lines; int ret; - if (tty->driver->type != TTY_DRIVER_TYPE_CONSOLE) - return -EINVAL; if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(type, p)) @@ -2778,6 +2756,12 @@ static int con_open(struct tty_struct *tty, struct file *filp) ret = vc_allocate(currcons); if (ret == 0) { struct vc_data *vc = vc_cons[currcons].d; + + /* Still being freed */ + if (vc->vc_tty) { + release_console_sem(); + return -ERESTARTSYS; + } tty->driver_data = vc; vc->vc_tty = tty; @@ -2798,34 +2782,20 @@ static int con_open(struct tty_struct *tty, struct file *filp) return ret; } -/* - * We take tty_mutex in here to prevent another thread from coming in via init_dev - * and taking a ref against the tty while we're in the process of forgetting - * about it and cleaning things up. - * - * This is because vcs_remove_sysfs() can sleep and will drop the BKL. - */ static void con_close(struct tty_struct *tty, struct file *filp) { - mutex_lock(&tty_mutex); - acquire_console_sem(); - if (tty && tty->count == 1) { - struct vc_data *vc = tty->driver_data; + /* Nothing to do - we defer to shutdown */ +} - if (vc) - vc->vc_tty = NULL; - tty->driver_data = NULL; - vcs_remove_sysfs(tty); - release_console_sem(); - mutex_unlock(&tty_mutex); - /* - * tty_mutex is released, but we still hold BKL, so there is - * still exclusion against init_dev() - */ - return; - } +static void con_shutdown(struct tty_struct *tty) +{ + struct vc_data *vc = tty->driver_data; + BUG_ON(vc == NULL); + acquire_console_sem(); + vc->vc_tty = NULL; + vcs_remove_sysfs(tty); release_console_sem(); - mutex_unlock(&tty_mutex); + tty_shutdown(tty); } static int default_italic_color = 2; // green (ASCII) @@ -2950,10 +2920,19 @@ static const struct tty_operations con_ops = { .throttle = con_throttle, .unthrottle = con_unthrottle, .resize = vt_resize, + .shutdown = con_shutdown }; -int __init vty_init(void) +static struct cdev vc0_cdev; + +int __init vty_init(const struct file_operations *console_fops) { + cdev_init(&vc0_cdev, console_fops); + if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || + register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) + panic("Couldn't register /dev/tty0 driver\n"); + device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); + vcs_init(); console_driver = alloc_tty_driver(MAX_NR_CONSOLES); @@ -2972,7 +2951,6 @@ int __init vty_init(void) tty_set_operations(console_driver, &con_ops); if (tty_register_driver(console_driver)) panic("Couldn't register console driver\n"); - kbd_init(); console_map_init(); #ifdef CONFIG_PROM_CONSOLE @@ -3466,7 +3444,7 @@ int register_con_driver(const struct consw *csw, int first, int last) if (retval) goto err; - con_driver->dev = device_create_drvdata(vtconsole_class, NULL, + con_driver->dev = device_create(vtconsole_class, NULL, MKDEV(0, con_driver->node), NULL, "vtcon%i", con_driver->node); @@ -3577,7 +3555,7 @@ static int __init vtconsole_class_init(void) struct con_driver *con = ®istered_con_driver[i]; if (con->con && !con->dev) { - con->dev = device_create_drvdata(vtconsole_class, NULL, + con->dev = device_create(vtconsole_class, NULL, MKDEV(0, con->node), NULL, "vtcon%i", con->node); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index c904e9ad4a7..8944ce508e2 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -395,6 +395,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, kbd = kbd_table + console; switch (cmd) { + case TIOCLINUX: + return tioclinux(tty, arg); case KIOCSOUND: if (!perm) goto eperm; diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 871b0cbca5e..798d7f3e42e 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1231,7 +1231,7 @@ static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file, int error = 0; switch (cmd) { default: - error = n_tty_ioctl (tty, file, cmd, arg); + error = n_tty_ioctl_helper(tty, file, cmd, arg); break; } return error; diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 5e89fa17781..07052ed2a0c 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -571,6 +571,7 @@ gigaset_tty_close(struct tty_struct *tty) } /* prevent other callers from entering ldisc methods */ + /* FIXME: should use the tty state flags */ tty->disc_data = NULL; if (!cs->hw.ser) @@ -642,10 +643,11 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, return -ENXIO; switch (cmd) { - case TCGETS: - case TCGETA: - /* pass through to underlying serial device */ - rc = n_tty_ioctl(tty, file, cmd, arg); + + case FIONREAD: + /* unused, always return zero */ + val = 0; + rc = put_user(val, p); break; case TCFLSH: @@ -659,20 +661,13 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, flush_send_queue(cs); break; } - /* flush the serial port's buffer */ - rc = n_tty_ioctl(tty, file, cmd, arg); - break; - - case FIONREAD: - /* unused, always return zero */ - val = 0; - rc = put_user(val, p); - break; + /* Pass through */ default: - rc = -ENOIOCTLCMD; + /* pass through to underlying serial device */ + rc = n_tty_ioctl_helper(tty, file, cmd, arg); + break; } - cs_put(cs); return rc; } @@ -680,6 +675,8 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, /* * Poll on the tty. * Unused, always return zero. + * + * FIXME: should probably return an exception - especially on hangup */ static unsigned int gigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index 2ae2ec40015..21efd99b929 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -205,7 +205,7 @@ config WANXL_BUILD_FIRMWARE config PC300 tristate "Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)" - depends on HDLC && PCI + depends on HDLC && PCI && BROKEN ---help--- Driver for the Cyclades-PC300 synchronous communication boards. diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index d18e6d2e0b4..40759c33477 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -418,25 +418,22 @@ fs3270_open(struct inode *inode, struct file *filp) { struct fs3270 *fp; struct idal_buffer *ib; - int minor, rc; + int minor, rc = 0; if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR) return -ENODEV; - lock_kernel(); minor = iminor(filp->f_path.dentry->d_inode); /* Check for minor 0 multiplexer. */ if (minor == 0) { - struct tty_struct *tty; - mutex_lock(&tty_mutex); - tty = get_current_tty(); + struct tty_struct *tty = get_current_tty(); if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { - mutex_unlock(&tty_mutex); - rc = -ENODEV; - goto out; + tty_kref_put(tty); + return -ENODEV; } minor = tty->index + RAW3270_FIRSTMINOR; - mutex_unlock(&tty_mutex); + tty_kref_put(tty); } + lock_kernel(); /* Check if some other program is already using fullscreen mode. */ fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); if (!IS_ERR(fp)) { @@ -478,7 +475,7 @@ fs3270_open(struct inode *inode, struct file *filp) filp->private_data = fp; out: unlock_kernel(); - return 0; + return rc; } /* diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 9ccc563d873..d4104a3bbe8 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -44,6 +44,10 @@ #include "8250.h" +#ifdef CONFIG_SPARC +#include "suncore.h" +#endif + /* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option @@ -53,6 +57,13 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; +static struct uart_driver serial8250_reg; + +static int serial_index(struct uart_port *port) +{ + return (serial8250_reg.minor - 64) + port->line; +} + /* * Debugging. */ @@ -536,7 +547,7 @@ static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) /* * FIFO support. */ -static inline void serial8250_clear_fifos(struct uart_8250_port *p) +static void serial8250_clear_fifos(struct uart_8250_port *p) { if (p->capabilities & UART_CAP_FIFO) { serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); @@ -551,7 +562,7 @@ static inline void serial8250_clear_fifos(struct uart_8250_port *p) * capability" bit enabled. Note that on XR16C850s, we need to * reset LCR to write to IER. */ -static inline void serial8250_set_sleep(struct uart_8250_port *p, int sleep) +static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { @@ -993,7 +1004,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) return; DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", - up->port.line, up->port.iobase, up->port.membase); + serial_index(&up->port), up->port.iobase, up->port.membase); /* * We really do need global IRQs disabled here - we're going to @@ -1128,8 +1139,8 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) if (up->capabilities != uart_config[up->port.type].flags) { printk(KERN_WARNING "ttyS%d: detected caps %08x should be %08x\n", - up->port.line, up->capabilities, - uart_config[up->port.type].flags); + serial_index(&up->port), up->capabilities, + uart_config[up->port.type].flags); } up->port.fifosize = uart_config[up->port.type].fifo_size; @@ -1424,8 +1435,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) /* * This handles the interrupt from one port. */ -static inline void -serial8250_handle_port(struct uart_8250_port *up) +static void serial8250_handle_port(struct uart_8250_port *up) { unsigned int status; unsigned long flags; @@ -1719,7 +1729,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) /* * Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct uart_8250_port *up, int bits) +static void wait_for_xmitr(struct uart_8250_port *up, int bits) { unsigned int status, tmout = 10000; @@ -1854,7 +1864,8 @@ static int serial8250_startup(struct uart_port *port) */ if (!(up->port.flags & UPF_BUGGY_UART) && (serial_inp(up, UART_LSR) == 0xff)) { - printk("ttyS%d: LSR safety check engaged!\n", up->port.line); + printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n", + serial_index(&up->port)); return -ENODEV; } @@ -1909,7 +1920,8 @@ static int serial8250_startup(struct uart_port *port) */ if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { up->bugs |= UART_BUG_THRE; - pr_debug("ttyS%d - using backup timer\n", port->line); + pr_debug("ttyS%d - using backup timer\n", + serial_index(port)); } } @@ -1969,7 +1981,7 @@ static int serial8250_startup(struct uart_port *port) if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; pr_debug("ttyS%d - enabling bad tx status workarounds\n", - port->line); + serial_index(port)); } } else { up->bugs &= ~UART_BUG_TXEN; @@ -2630,7 +2642,6 @@ static int serial8250_console_early_setup(void) return serial8250_find_port_for_earlycon(); } -static struct uart_driver serial8250_reg; static struct console serial8250_console = { .name = "ttyS", .write = serial8250_console_write, @@ -2677,7 +2688,6 @@ static struct uart_driver serial8250_reg = { .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, - .nr = UART_NR, .cons = SERIAL8250_CONSOLE, }; @@ -2959,10 +2969,12 @@ static int __init serial8250_init(void) "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); - for (i = 0; i < NR_IRQS; i++) - spin_lock_init(&irq_lists[i].lock); - +#ifdef CONFIG_SPARC + ret = sunserial_register_minors(&serial8250_reg, UART_NR); +#else + serial8250_reg.nr = UART_NR; ret = uart_register_driver(&serial8250_reg); +#endif if (ret) goto out; @@ -2987,7 +2999,11 @@ static int __init serial8250_init(void) put_dev: platform_device_put(serial8250_isa_devs); unreg_uart_drv: +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else uart_unregister_driver(&serial8250_reg); +#endif out: return ret; } @@ -3006,7 +3022,11 @@ static void __exit serial8250_exit(void) platform_driver_unregister(&serial8250_isa_driver); platform_device_unregister(isa_dev); +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else uart_unregister_driver(&serial8250_reg); +#endif } module_init(serial8250_init); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index c2f23933155..c014ffb110e 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -2041,9 +2041,9 @@ static int pciserial_resume_one(struct pci_dev *dev) * The device may have been disabled. Re-enable it. */ err = pci_enable_device(dev); + /* FIXME: We cannot simply error out here */ if (err) - return err; - + printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); pciserial_resume_ports(priv); } return 0; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 77cb34270fc..31786b3b0a6 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -9,7 +9,6 @@ menu "Serial drivers" # The new 8250/16550 serial drivers config SERIAL_8250 tristate "8250/16550 and compatible serial support" - depends on (BROKEN || !SPARC) select SERIAL_CORE ---help--- This selects whether you want to include the driver for the standard @@ -994,24 +993,12 @@ config SERIAL_68328_RTS_CTS bool "Support RTS/CTS on 68328 serial port" depends on SERIAL_68328 -config SERIAL_COLDFIRE - bool "ColdFire serial support (DEPRECATED)" - depends on COLDFIRE - help - This driver supports the built-in serial ports of the Motorola ColdFire - family of CPUs. - This driver is deprecated because it supports only the old interface - for serial drivers and features like magic keys are not working. - Please switch to the new style driver because this driver will be - removed soon. - config SERIAL_MCF - bool "Coldfire serial support (new style driver)" + bool "Coldfire serial support" depends on COLDFIRE select SERIAL_CORE help - This new serial driver supports the Freescale Coldfire serial ports - using the new serial driver subsystem. + This serial driver supports the Freescale Coldfire serial ports. config SERIAL_MCF_BAUDRATE int "Default baudrate for Coldfire serial ports" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 7e7383e890d..0c17c8ddb19 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -4,6 +4,16 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o + +# These Sparc drivers have to appear before others such as 8250 +# which share ttySx minor node space. Otherwise console device +# names change and other unplesantries. +obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o +obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o +obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o +obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o +obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o + obj-$(CONFIG_SERIAL_8250) += 8250.o obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o @@ -31,16 +41,10 @@ obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o -obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o -obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o -obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o -obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o -obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o obj-$(CONFIG_SERIAL_68360) += 68360serial.o -obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index 4a0d30bed9f..569f0e2476c 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip Serial Driver * - * Copyright 2006-2007 Analog Devices Inc. + * Copyright 2006-2008 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -42,6 +42,9 @@ #define BFIN_SERIAL_MAJOR 204 #define BFIN_SERIAL_MINOR 64 +static struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; +static int nr_active_ports = ARRAY_SIZE(bfin_serial_resource); + /* * Setup for console. Argument comes from the menuconfig */ @@ -126,13 +129,13 @@ static int kgdb_entry_state; void kgdb_put_debug_char(int chr) { struct bfin_serial_port *uart; - + if (CONFIG_KGDB_UART_PORT < 0 || CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS) uart = &bfin_serial_ports[0]; else uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT]; - + while (!(UART_GET_LSR(uart) & THRE)) { SSYNC(); } @@ -152,7 +155,7 @@ int kgdb_get_debug_char(void) uart = &bfin_serial_ports[0]; else uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT]; - + while(!(UART_GET_LSR(uart) & DR)) { SSYNC(); } @@ -298,7 +301,11 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) bfin_serial_mctrl_check(uart); if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { - bfin_serial_stop_tx(&uart->port); +#ifdef CONFIG_BF54x + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); +#endif + UART_CLEAR_IER(uart, ETBEI); return; } @@ -317,9 +324,6 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uart->port); - - if (uart_circ_empty(xmit)) - bfin_serial_stop_tx(&uart->port); } static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) @@ -645,6 +649,42 @@ static int bfin_serial_startup(struct uart_port *port) free_irq(uart->port.irq, uart); return -EBUSY; } + +# ifdef CONFIG_BF54x + { + unsigned uart_dma_ch_rx, uart_dma_ch_tx; + + switch (uart->port.irq) { + case IRQ_UART3_RX: + uart_dma_ch_rx = CH_UART3_RX; + uart_dma_ch_tx = CH_UART3_TX; + break; + case IRQ_UART2_RX: + uart_dma_ch_rx = CH_UART2_RX; + uart_dma_ch_tx = CH_UART2_TX; + break; + default: + uart_dma_ch_rx = uart_dma_ch_tx = 0; + break; + }; + + if (uart_dma_ch_rx && + request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { + printk(KERN_NOTICE"Fail to attach UART interrupt\n"); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, uart); + return -EBUSY; + } + if (uart_dma_ch_tx && + request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) { + printk(KERN_NOTICE "Fail to attach UART interrupt\n"); + free_dma(uart_dma_ch_rx); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, uart); + return -EBUSY; + } + } +# endif #endif UART_SET_IER(uart, ERBFI); return 0; @@ -662,6 +702,20 @@ static void bfin_serial_shutdown(struct uart_port *port) del_timer(&(uart->rx_dma_timer)); dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0); #else +#ifdef CONFIG_BF54x + switch (uart->port.irq) { + case IRQ_UART3_RX: + free_dma(CH_UART3_RX); + free_dma(CH_UART3_TX); + break; + case IRQ_UART2_RX: + free_dma(CH_UART2_RX); + free_dma(CH_UART2_TX); + break; + default: + break; + }; +#endif #ifdef CONFIG_KGDB_UART if (uart->port.line != CONFIG_KGDB_UART_PORT) #endif @@ -757,6 +811,9 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, val |= UCEN; UART_PUT_GCTL(uart, val); + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&uart->port.lock, flags); } @@ -859,8 +916,9 @@ static void __init bfin_serial_init_ports(void) return; first = 0; - for (i = 0; i < nr_ports; i++) { + for (i = 0; i < nr_active_ports; i++) { bfin_serial_ports[i].port.uartclk = get_sclk(); + bfin_serial_ports[i].port.fifosize = BFIN_UART_TX_FIFO_SIZE; bfin_serial_ports[i].port.ops = &bfin_serial_pops; bfin_serial_ports[i].port.line = i; bfin_serial_ports[i].port.iotype = UPIO_MEM; @@ -961,7 +1019,7 @@ bfin_serial_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index == -1 || co->index >= nr_ports) + if (co->index == -1 || co->index >= nr_active_ports) co->index = 0; uart = &bfin_serial_ports[co->index]; @@ -1056,7 +1114,7 @@ static __init void early_serial_write(struct console *con, const char *s, } } -static struct __init console bfin_early_serial_console = { +static struct __initdata console bfin_early_serial_console = { .name = "early_BFuart", .write = early_serial_write, .device = uart_console_device, @@ -1072,7 +1130,7 @@ struct console __init *bfin_earlyserial_init(unsigned int port, struct bfin_serial_port *uart; struct ktermios t; - if (port == -1 || port >= nr_ports) + if (port == -1 || port >= nr_active_ports) port = 0; bfin_serial_init_ports(); bfin_early_serial_console.index = port; @@ -1100,20 +1158,26 @@ static struct uart_driver bfin_serial_reg = { static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state) { - struct bfin_serial_port *uart = platform_get_drvdata(dev); + int i; - if (uart) - uart_suspend_port(&bfin_serial_reg, &uart->port); + for (i = 0; i < nr_active_ports; i++) { + if (bfin_serial_ports[i].port.dev != &dev->dev) + continue; + uart_suspend_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + } return 0; } static int bfin_serial_resume(struct platform_device *dev) { - struct bfin_serial_port *uart = platform_get_drvdata(dev); + int i; - if (uart) - uart_resume_port(&bfin_serial_reg, &uart->port); + for (i = 0; i < nr_active_ports; i++) { + if (bfin_serial_ports[i].port.dev != &dev->dev) + continue; + uart_resume_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + } return 0; } @@ -1128,32 +1192,31 @@ static int bfin_serial_probe(struct platform_device *dev) break; if (i < dev->num_resources) { - for (i = 0; i < nr_ports; i++, res++) { + for (i = 0; i < nr_active_ports; i++, res++) { if (bfin_serial_ports[i].port.mapbase != res->start) continue; bfin_serial_ports[i].port.dev = &dev->dev; uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); - platform_set_drvdata(dev, &bfin_serial_ports[i]); } } return 0; } -static int bfin_serial_remove(struct platform_device *pdev) +static int bfin_serial_remove(struct platform_device *dev) { - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - + int i; + for (i = 0; i < nr_active_ports; i++) { + if (bfin_serial_ports[i].port.dev != &dev->dev) + continue; + uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + bfin_serial_ports[i].port.dev = NULL; #ifdef CONFIG_SERIAL_BFIN_CTSRTS - gpio_free(uart->cts_pin); - gpio_free(uart->rts_pin); + gpio_free(bfin_serial_ports[i].cts_pin); + gpio_free(bfin_serial_ports[i].rts_pin); #endif - - platform_set_drvdata(pdev, NULL); - - if (uart) - uart_remove_one_port(&bfin_serial_reg, &uart->port); + } return 0; } diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index bf94a770bb4..211c21797ce 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -457,7 +457,6 @@ static struct e100_serial rs_table[] = { #define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial)) static struct ktermios *serial_termios[NR_PORTS]; -static struct ktermios *serial_termios_locked[NR_PORTS]; #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER static struct fast_timer fast_timers[NR_PORTS]; #endif @@ -4419,6 +4418,7 @@ rs_init(void) rs485_pa_bit)) { printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); + put_tty_driver(driver); return -EBUSY; } #endif @@ -4427,6 +4427,7 @@ rs_init(void) rs485_port_g_bit)) { printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); + put_tty_driver(driver); return -EBUSY; } #endif @@ -4446,8 +4447,6 @@ rs_init(void) driver->init_termios.c_ispeed = 115200; driver->init_termios.c_ospeed = 115200; driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - driver->termios = serial_termios; - driver->termios_locked = serial_termios_locked; tty_set_operations(driver, &rs_ops); serial_driver = driver; diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c deleted file mode 100644 index fbe3835f6b7..00000000000 --- a/drivers/serial/mcfserial.c +++ /dev/null @@ -1,1965 +0,0 @@ -#warning This driver is deprecated. Check Kconfig for details. -/* - * mcfserial.c -- serial driver for ColdFire internal UARTS. - * - * Copyright (C) 1999-2003 Greg Ungerer <gerg@snapgear.com> - * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com> - * Copyright (C) 2001-2002 SnapGear Inc. <www.snapgear.com> - * - * Based on code from 68332serial.c which was: - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 TSHG - * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org> - * - * Changes: - * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> - * some cleanups in mcfrs_write. - * - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/wait.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/serial.h> -#include <linux/serialP.h> -#include <linux/console.h> -#include <linux/init.h> -#include <linux/bitops.h> -#include <linux/delay.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> -#include <asm/delay.h> -#include <asm/coldfire.h> -#include <asm/mcfsim.h> -#include <asm/mcfuart.h> -#include <asm/nettel.h> -#include <asm/uaccess.h> -#include "mcfserial.h" - -struct timer_list mcfrs_timer_struct; - -/* - * Default console baud rate, we use this as the default - * for all ports so init can just open /dev/console and - * keep going. Perhaps one day the cflag settings for the - * console can be used instead. - */ -#if defined(CONFIG_HW_FEITH) -#define CONSOLE_BAUD_RATE 38400 -#define DEFAULT_CBAUD B38400 -#elif defined(CONFIG_MOD5272) || defined(CONFIG_M5208EVB) || \ - defined(CONFIG_M5329EVB) || defined(CONFIG_GILBARCO) -#define CONSOLE_BAUD_RATE 115200 -#define DEFAULT_CBAUD B115200 -#elif defined(CONFIG_ARNEWSH) || defined(CONFIG_FREESCALE) || \ - defined(CONFIG_senTec) || defined(CONFIG_SNEHA) || defined(CONFIG_AVNET) -#define CONSOLE_BAUD_RATE 19200 -#define DEFAULT_CBAUD B19200 -#endif - -#ifndef CONSOLE_BAUD_RATE -#define CONSOLE_BAUD_RATE 9600 -#define DEFAULT_CBAUD B9600 -#endif - -int mcfrs_console_inited = 0; -int mcfrs_console_port = -1; -int mcfrs_console_baud = CONSOLE_BAUD_RATE; -int mcfrs_console_cbaud = DEFAULT_CBAUD; - -/* - * Driver data structures. - */ -static struct tty_driver *mcfrs_serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* Debugging... - */ -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW - -#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) -#define IRQBASE (MCFINT_VECBASE+MCFINT_UART0) -#else -#define IRQBASE 73 -#endif - -/* - * Configuration table, UARTs to look for at startup. - */ -static struct mcf_serial mcfrs_table[] = { - { /* ttyS0 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE1), - .irq = IRQBASE, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#ifdef MCFUART_BASE2 - { /* ttyS1 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE2), - .irq = IRQBASE+1, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#endif -#ifdef MCFUART_BASE3 - { /* ttyS2 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE3), - .irq = IRQBASE+2, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#endif -#ifdef MCFUART_BASE4 - { /* ttyS3 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE4), - .irq = IRQBASE+3, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#endif -}; - - -#define NR_PORTS (sizeof(mcfrs_table) / sizeof(struct mcf_serial)) - -/* - * This is used to figure out the divisor speeds and the timeouts. - */ -static int mcfrs_baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 -}; -#define MCFRS_BAUD_TABLE_SIZE \ - (sizeof(mcfrs_baud_table)/sizeof(mcfrs_baud_table[0])) - - -#ifdef CONFIG_MAGIC_SYSRQ -/* - * Magic system request keys. Used for debugging... - */ -extern int magic_sysrq_key(int ch); -#endif - - -/* - * Forware declarations... - */ -static void mcfrs_change_speed(struct mcf_serial *info); -static void mcfrs_wait_until_sent(struct tty_struct *tty, int timeout); - - -static inline int serial_paranoia_check(struct mcf_serial *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char badmagic[] = - "MCFRS(warning): bad magic number for serial struct %s in %s\n"; - static const char badinfo[] = - "MCFRS(warning): null mcf_serial for %s in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * Sets or clears DTR and RTS on the requested line. - */ -static void mcfrs_setsignals(struct mcf_serial *info, int dtr, int rts) -{ - volatile unsigned char *uartp; - unsigned long flags; - -#if 0 - printk("%s(%d): mcfrs_setsignals(info=%x,dtr=%d,rts=%d)\n", - __FILE__, __LINE__, info, dtr, rts); -#endif - - local_irq_save(flags); - if (dtr >= 0) { -#ifdef MCFPP_DTR0 - if (info->line) - mcf_setppdata(MCFPP_DTR1, (dtr ? 0 : MCFPP_DTR1)); - else - mcf_setppdata(MCFPP_DTR0, (dtr ? 0 : MCFPP_DTR0)); -#endif - } - if (rts >= 0) { - uartp = info->addr; - if (rts) { - info->sigs |= TIOCM_RTS; - uartp[MCFUART_UOP1] = MCFUART_UOP_RTS; - } else { - info->sigs &= ~TIOCM_RTS; - uartp[MCFUART_UOP0] = MCFUART_UOP_RTS; - } - } - local_irq_restore(flags); - return; -} - -/* - * Gets values of serial signals. - */ -static int mcfrs_getsignals(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - unsigned long flags; - int sigs; -#if defined(CONFIG_NETtel) && defined(CONFIG_M5307) - unsigned short ppdata; -#endif - -#if 0 - printk("%s(%d): mcfrs_getsignals(info=%x)\n", __FILE__, __LINE__); -#endif - - local_irq_save(flags); - uartp = info->addr; - sigs = (uartp[MCFUART_UIPR] & MCFUART_UIPR_CTS) ? 0 : TIOCM_CTS; - sigs |= (info->sigs & TIOCM_RTS); - -#ifdef MCFPP_DCD0 -{ - unsigned int ppdata; - ppdata = mcf_getppdata(); - if (info->line == 0) { - sigs |= (ppdata & MCFPP_DCD0) ? 0 : TIOCM_CD; - sigs |= (ppdata & MCFPP_DTR0) ? 0 : TIOCM_DTR; - } else if (info->line == 1) { - sigs |= (ppdata & MCFPP_DCD1) ? 0 : TIOCM_CD; - sigs |= (ppdata & MCFPP_DTR1) ? 0 : TIOCM_DTR; - } -} -#endif - - local_irq_restore(flags); - return(sigs); -} - -/* - * ------------------------------------------------------------ - * mcfrs_stop() and mcfrs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void mcfrs_stop(struct tty_struct *tty) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_stop")) - return; - - local_irq_save(flags); - uartp = info->addr; - info->imr &= ~MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - local_irq_restore(flags); -} - -static void mcfrs_start(struct tty_struct *tty) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_start")) - return; - - local_irq_save(flags); - if (info->xmit_cnt && info->xmit_buf) { - uartp = info->addr; - info->imr |= MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - } - local_irq_restore(flags); -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * mcfrs_interrupt(). They were separated out for readability's sake. - * - * Note: mcfrs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * mcfrs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -static inline void receive_chars(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - struct tty_struct *tty = info->port.tty; - unsigned char status, ch, flag; - - if (!tty) - return; - - uartp = info->addr; - - while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) { - ch = uartp[MCFUART_URB]; - info->stats.rx++; - -#ifdef CONFIG_MAGIC_SYSRQ - if (mcfrs_console_inited && (info->line == mcfrs_console_port)) { - if (magic_sysrq_key(ch)) - continue; - } -#endif - - flag = TTY_NORMAL; - if (status & MCFUART_USR_RXERR) { - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETERR; - if (status & MCFUART_USR_RXBREAK) { - info->stats.rxbreak++; - flag = TTY_BREAK; - } else if (status & MCFUART_USR_RXPARITY) { - info->stats.rxparity++; - flag = TTY_PARITY; - } else if (status & MCFUART_USR_RXOVERRUN) { - info->stats.rxoverrun++; - flag = TTY_OVERRUN; - } else if (status & MCFUART_USR_RXFRAMING) { - info->stats.rxframing++; - flag = TTY_FRAME; - } - } - tty_insert_flip_char(tty, ch, flag); - } - tty_schedule_flip(tty); - return; -} - -static inline void transmit_chars(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - - uartp = info->addr; - - if (info->x_char) { - /* Send special char - probably flow control */ - uartp[MCFUART_UTB] = info->x_char; - info->x_char = 0; - info->stats.tx++; - } - - if ((info->xmit_cnt <= 0) || info->port.tty->stopped) { - info->imr &= ~MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - return; - } - - while (uartp[MCFUART_USR] & MCFUART_USR_TXREADY) { - uartp[MCFUART_UTB] = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->stats.tx++; - if (--info->xmit_cnt <= 0) - break; - } - - if (info->xmit_cnt < WAKEUP_CHARS) - schedule_work(&info->tqueue); - return; -} - -/* - * This is the serial driver's generic interrupt routine - */ -irqreturn_t mcfrs_interrupt(int irq, void *dev_id) -{ - struct mcf_serial *info; - unsigned char isr; - - info = &mcfrs_table[(irq - IRQBASE)]; - isr = info->addr[MCFUART_UISR] & info->imr; - - if (isr & MCFUART_UIR_RXREADY) - receive_chars(info); - if (isr & MCFUART_UIR_TXREADY) - transmit_chars(info); - return IRQ_HANDLED; -} - -/* - * ------------------------------------------------------------------- - * Here ends the serial interrupt routines. - * ------------------------------------------------------------------- - */ - -static void mcfrs_offintr(struct work_struct *work) -{ - struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue); - struct tty_struct *tty = info->port.tty; - - if (tty) - tty_wakeup(tty); -} - - -/* - * Change of state on a DCD line. - */ -void mcfrs_modem_change(struct mcf_serial *info, int dcd) -{ - if (info->count == 0) - return; - - if (info->flags & ASYNC_CHECK_CD) { - if (dcd) - wake_up_interruptible(&info->open_wait); - else - schedule_work(&info->tqueue_hangup); - } -} - - -#ifdef MCFPP_DCD0 - -unsigned short mcfrs_ppstatus; - -/* - * This subroutine is called when the RS_TIMER goes off. It is used - * to monitor the state of the DCD lines - since they have no edge - * sensors and interrupt generators. - */ -static void mcfrs_timer(void) -{ - unsigned int ppstatus, dcdval, i; - - ppstatus = mcf_getppdata() & (MCFPP_DCD0 | MCFPP_DCD1); - - if (ppstatus != mcfrs_ppstatus) { - for (i = 0; (i < 2); i++) { - dcdval = (i ? MCFPP_DCD1 : MCFPP_DCD0); - if ((ppstatus & dcdval) != (mcfrs_ppstatus & dcdval)) { - mcfrs_modem_change(&mcfrs_table[i], - ((ppstatus & dcdval) ? 0 : 1)); - } - } - } - mcfrs_ppstatus = ppstatus; - - /* Re-arm timer */ - mcfrs_timer_struct.expires = jiffies + HZ/25; - add_timer(&mcfrs_timer_struct); -} - -#endif /* MCFPP_DCD0 */ - - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> mcfrs_hangup() - * - */ -static void do_serial_hangup(struct work_struct *work) -{ - struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue_hangup); - struct tty_struct *tty = info->port.tty; - - if (tty) - tty_hangup(tty); -} - -static int startup(struct mcf_serial * info) -{ - volatile unsigned char *uartp; - unsigned long flags; - - if (info->flags & ASYNC_INITIALIZED) - return 0; - - if (!info->xmit_buf) { - info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL); - if (!info->xmit_buf) - return -ENOMEM; - } - - local_irq_save(flags); - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq); -#endif - - /* - * Reset UART, get it into known state... - */ - uartp = info->addr; - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - mcfrs_setsignals(info, 1, 1); - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - mcfrs_change_speed(info); - - /* - * Lastly enable the UART transmitter and receiver, and - * interrupt enables. - */ - info->imr = MCFUART_UIR_RXREADY; - uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; - uartp[MCFUART_UIMR] = info->imr; - - info->flags |= ASYNC_INITIALIZED; - local_irq_restore(flags); - return 0; -} - -/* - * 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 mcf_serial * info) -{ - volatile unsigned char *uartp; - unsigned long flags; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....\n", info->line, - info->irq); -#endif - - local_irq_save(flags); - - uartp = info->addr; - uartp[MCFUART_UIMR] = 0; /* mask all interrupts */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) - mcfrs_setsignals(info, 0, 0); - - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; - } - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); -} - - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void mcfrs_change_speed(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - unsigned int baudclk, cflag; - unsigned long flags; - unsigned char mr1, mr2; - int i; -#ifdef CONFIG_M5272 - unsigned int fraction; -#endif - - if (!info->port.tty || !info->port.tty->termios) - return; - cflag = info->port.tty->termios->c_cflag; - if (info->addr == 0) - return; - -#if 0 - printk("%s(%d): mcfrs_change_speed()\n", __FILE__, __LINE__); -#endif - - i = cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 4) - info->port.tty->termios->c_cflag &= ~CBAUDEX; - else - i += 15; - } - if (i == 0) { - mcfrs_setsignals(info, 0, -1); - return; - } - - /* compute the baudrate clock */ -#ifdef CONFIG_M5272 - /* - * For the MCF5272, also compute the baudrate fraction. - */ - baudclk = (MCF_BUSCLK / mcfrs_baud_table[i]) / 32; - fraction = MCF_BUSCLK - (baudclk * 32 * mcfrs_baud_table[i]); - fraction *= 16; - fraction /= (32 * mcfrs_baud_table[i]); -#else - baudclk = ((MCF_BUSCLK / mcfrs_baud_table[i]) + 16) / 32; -#endif - - info->baud = mcfrs_baud_table[i]; - - mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; - mr2 = 0; - - switch (cflag & CSIZE) { - case CS5: mr1 |= MCFUART_MR1_CS5; break; - case CS6: mr1 |= MCFUART_MR1_CS6; break; - case CS7: mr1 |= MCFUART_MR1_CS7; break; - case CS8: - default: mr1 |= MCFUART_MR1_CS8; break; - } - - if (cflag & PARENB) { - if (cflag & CMSPAR) { - if (cflag & PARODD) - mr1 |= MCFUART_MR1_PARITYMARK; - else - mr1 |= MCFUART_MR1_PARITYSPACE; - } else { - if (cflag & PARODD) - mr1 |= MCFUART_MR1_PARITYODD; - else - mr1 |= MCFUART_MR1_PARITYEVEN; - } - } else { - mr1 |= MCFUART_MR1_PARITYNONE; - } - - if (cflag & CSTOPB) - mr2 |= MCFUART_MR2_STOP2; - else - mr2 |= MCFUART_MR2_STOP1; - - if (cflag & CRTSCTS) { - mr1 |= MCFUART_MR1_RXRTS; - mr2 |= MCFUART_MR2_TXCTS; - } - - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - uartp = info->addr; - - local_irq_save(flags); -#if 0 - printk("%s(%d): mr1=%x mr2=%x baudclk=%x\n", __FILE__, __LINE__, - mr1, mr2, baudclk); -#endif - /* - Note: pg 12-16 of MCF5206e User's Manual states that a - software reset should be performed prior to changing - UMR1,2, UCSR, UACR, bit 7 - */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */ - uartp[MCFUART_UMR] = mr1; - uartp[MCFUART_UMR] = mr2; - uartp[MCFUART_UBG1] = (baudclk & 0xff00) >> 8; /* set msb byte */ - uartp[MCFUART_UBG2] = (baudclk & 0xff); /* set lsb byte */ -#ifdef CONFIG_M5272 - uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */ -#endif - uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER; - uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; - mcfrs_setsignals(info, 1, -1); - local_irq_restore(flags); - return; -} - -static void mcfrs_flush_chars(struct tty_struct *tty) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_flush_chars")) - return; - - uartp = (volatile unsigned char *) info->addr; - - /* - * re-enable receiver interrupt - */ - local_irq_save(flags); - if ((!(info->imr & MCFUART_UIR_RXREADY)) && - (info->flags & ASYNC_INITIALIZED) ) { - info->imr |= MCFUART_UIR_RXREADY; - uartp[MCFUART_UIMR] = info->imr; - } - local_irq_restore(flags); - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) - return; - - /* Enable transmitter */ - local_irq_save(flags); - info->imr |= MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - local_irq_restore(flags); -} - -static int mcfrs_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - int c, total = 0; - -#if 0 - printk("%s(%d): mcfrs_write(tty=%x,buf=%x,count=%d)\n", - __FILE__, __LINE__, (int)tty, (int)buf, count); -#endif - - if (serial_paranoia_check(info, tty->name, "mcfrs_write")) - return 0; - - if (!tty || !info->xmit_buf) - return 0; - - local_save_flags(flags); - while (1) { - local_irq_disable(); - c = min(count, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1, - ((int)SERIAL_XMIT_SIZE) - info->xmit_head)); - local_irq_restore(flags); - - if (c <= 0) - break; - - memcpy(info->xmit_buf + info->xmit_head, buf, c); - - local_irq_disable(); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - local_irq_restore(flags); - - buf += c; - count -= c; - total += c; - } - - local_irq_disable(); - uartp = info->addr; - info->imr |= MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - local_irq_restore(flags); - - return total; -} - -static int mcfrs_write_room(struct tty_struct *tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "mcfrs_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int mcfrs_chars_in_buffer(struct tty_struct *tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "mcfrs_chars_in_buffer")) - return 0; - return info->xmit_cnt; -} - -static void mcfrs_flush_buffer(struct tty_struct *tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_flush_buffer")) - return; - - local_irq_save(flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - local_irq_restore(flags); - - tty_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * mcfrs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void mcfrs_throttle(struct tty_struct * tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "mcfrs_throttle")) - return; - - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); - - /* Turn off RTS line (do this atomic) */ -} - -static void mcfrs_unthrottle(struct tty_struct * tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "mcfrs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } - - /* Assert RTS line (do this atomic) */ -} - -/* - * ------------------------------------------------------------ - * mcfrs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct mcf_serial * info, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = (unsigned int) info->addr; - tmp.irq = info->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0; -} - -static int set_serial_info(struct mcf_serial * info, - struct serial_struct * new_info) -{ - struct serial_struct new_serial; - struct mcf_serial old_info; - int retval = 0; - - if (!new_info) - return -EFAULT; - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) - return -EFAULT; - old_info = *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 & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->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) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->flags = ((info->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; - -check_and_exit: - retval = startup(info); - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct mcf_serial * info, unsigned int *value) -{ - volatile unsigned char *uartp; - unsigned long flags; - unsigned char status; - - local_irq_save(flags); - uartp = info->addr; - status = (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) ? TIOCSER_TEMT : 0; - local_irq_restore(flags); - - return put_user(status,value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break( struct mcf_serial * info, int duration) -{ - volatile unsigned char *uartp; - unsigned long flags; - - if (!info->addr) - return; - set_current_state(TASK_INTERRUPTIBLE); - uartp = info->addr; - - local_irq_save(flags); - uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTART; - schedule_timeout(duration); - uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTOP; - local_irq_restore(flags); -} - -static int mcfrs_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - return mcfrs_getsignals(info); -} - -static int mcfrs_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - int rts = -1, dtr = -1; - - if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 0; - if (clear & TIOCM_DTR) - dtr = 0; - - mcfrs_setsignals(info, dtr, rts); - - return 0; -} - -static int mcfrs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - int retval, error; - - if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, HZ/4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(HZ/10) : HZ/4); - return 0; - case TIOCGSERIAL: - if (access_ok(VERIFY_WRITE, (void *) arg, - sizeof(struct serial_struct))) - return get_serial_info(info, - (struct serial_struct *) arg); - return -EFAULT; - case TIOCSSERIAL: - return set_serial_info(info, - (struct serial_struct *) arg); - case TIOCSERGETLSR: /* Get line status register */ - if (access_ok(VERIFY_WRITE, (void *) arg, - sizeof(unsigned int))) - return get_lsr_info(info, (unsigned int *) arg); - return -EFAULT; - case TIOCSERGSTRUCT: - error = copy_to_user((struct mcf_serial *) arg, - info, sizeof(struct mcf_serial)); - if (error) - return -EFAULT; - return 0; - -#ifdef TIOCSET422 - case TIOCSET422: { - unsigned int val; - get_user(val, (unsigned int *) arg); - mcf_setpa(MCFPP_PA11, (val ? 0 : MCFPP_PA11)); - break; - } - case TIOCGET422: { - unsigned int val; - val = (mcf_getpa() & MCFPP_PA11) ? 0 : 1; - put_user(val, (unsigned int *) arg); - break; - } -#endif - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void mcfrs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - - mcfrs_change_speed(info); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - mcfrs_setsignals(info, -1, 1); -#if 0 - mcfrs_start(tty); -#endif - } -} - -/* - * ------------------------------------------------------------ - * mcfrs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * S structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void mcfrs_close(struct tty_struct *tty, struct file * filp) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (!info || serial_paranoia_check(info, tty->name, "mcfrs_close")) - return; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - local_irq_restore(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_close ttyS%d, count = %d\n", info->line, info->count); -#endif - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("MCFRS: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - if (--info->count < 0) { - printk("MCFRS: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; - } - if (info->count) { - local_irq_restore(flags); - return; - } - info->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 != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->imr &= ~MCFUART_UIR_RXREADY; - uartp = info->addr; - uartp[MCFUART_UIMR] = info->imr; - -#if 0 - /* FIXME: do we need to keep this enabled for console?? */ - if (mcfrs_console_inited && (mcfrs_console_port == info->line)) { - /* Do not disable the UART */ ; - } else -#endif - shutdown(info); - mcfrs_flush_buffer(tty); - tty_ldisc_flush(tty); - - tty->closing = 0; - info->event = 0; - info->port.tty = NULL; -#if 0 - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (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); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); -} - -/* - * mcfrs_wait_until_sent() --- wait until the transmitter is empty - */ -static void -mcfrs_wait_until_sent(struct tty_struct *tty, int timeout) -{ -#ifdef CONFIG_M5272 -#define MCF5272_FIFO_SIZE 25 /* fifo size + shift reg */ - - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - volatile unsigned char *uartp; - unsigned long orig_jiffies, fifo_time, char_time, fifo_cnt; - - if (serial_paranoia_check(info, tty->name, "mcfrs_wait_until_sent")) - return; - - orig_jiffies = jiffies; - - /* - * Set the check interval to be 1/5 of the approximate time - * to send the entire fifo, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - lock_kernel(); - - fifo_time = (MCF5272_FIFO_SIZE * HZ * 10) / info->baud; - char_time = fifo_time / 5; - if (char_time == 0) - char_time = 1; - if (timeout && timeout < char_time) - char_time = timeout; - - /* - * Clamp the timeout period at 2 * the time to empty the - * fifo. Just to be safe, set the minimum at .5 seconds. - */ - fifo_time *= 2; - if (fifo_time < (HZ/2)) - fifo_time = HZ/2; - if (!timeout || timeout > fifo_time) - timeout = fifo_time; - - /* - * Account for the number of bytes in the UART - * transmitter FIFO plus any byte being shifted out. - */ - uartp = (volatile unsigned char *) info->addr; - for (;;) { - fifo_cnt = (uartp[MCFUART_UTF] & MCFUART_UTF_TXB); - if ((uartp[MCFUART_USR] & (MCFUART_USR_TXREADY| - MCFUART_USR_TXEMPTY)) == - MCFUART_USR_TXREADY) - fifo_cnt++; - if (fifo_cnt == 0) - break; - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - unlock_kernel(); -#else - /* - * For the other coldfire models, assume all data has been sent - */ -#endif -} - -/* - * mcfrs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void mcfrs_hangup(struct tty_struct *tty) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "mcfrs_hangup")) - return; - - mcfrs_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * mcfrs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct mcf_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 & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_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 |= ASYNC_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 - * mcfrs_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 SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); -#endif - info->count--; - info->blocked_open++; - while (1) { - local_irq_disable(); - mcfrs_setsignals(info, 1, 1); - local_irq_enable(); - current->state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ASYNC_CLOSING) && - (do_clocal || (mcfrs_getsignals(info) & TIOCM_CD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyS%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 SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -int mcfrs_open(struct tty_struct *tty, struct file * filp) -{ - struct mcf_serial *info; - int retval, line; - - line = tty->index; - if ((line < 0) || (line >= NR_PORTS)) - return -ENODEV; - info = mcfrs_table + line; - if (serial_paranoia_check(info, tty->name, "mcfrs_open")) - return -ENODEV; -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_open %s, count = %d\n", tty->name, info->count); -#endif - info->count++; - tty->driver_data = info; - info->port.tty = tty; - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) - return retval; - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_open returning after block_til_ready with %d\n", - retval); -#endif - return retval; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_open %s successful...\n", tty->name); -#endif - return 0; -} - -/* - * Based on the line number set up the internal interrupt stuff. - */ -static void mcfrs_irqinit(struct mcf_serial *info) -{ -#if defined(CONFIG_M5272) - volatile unsigned long *icrp; - volatile unsigned long *portp; - volatile unsigned char *uartp; - - uartp = info->addr; - icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR2); - - switch (info->line) { - case 0: - *icrp = 0xe0000000; - break; - case 1: - *icrp = 0x0e000000; - break; - default: - printk("MCFRS: don't know how to handle UART %d interrupt?\n", - info->line); - return; - } - - /* Enable the output lines for the serial ports */ - portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PBCNT); - *portp = (*portp & ~0x000000ff) | 0x00000055; - portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PDCNT); - *portp = (*portp & ~0x000003fc) | 0x000002a8; -#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) - volatile unsigned char *icrp, *uartp; - volatile unsigned long *imrp; - - uartp = info->addr; - - icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_ICR0 + MCFINT_UART0 + info->line); - *icrp = 0x30 + info->line; /* level 6, line based priority */ - - imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_IMRL); - *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1); -#if defined(CONFIG_M527x) - { - /* - * External Pin Mask Setting & Enable External Pin for Interface - * mrcbis@aliceposta.it - */ - u16 *serpin_enable_mask; - serpin_enable_mask = (u16 *) (MCF_IPSBAR + MCF_GPIO_PAR_UART); - if (info->line == 0) - *serpin_enable_mask |= UART0_ENABLE_MASK; - else if (info->line == 1) - *serpin_enable_mask |= UART1_ENABLE_MASK; - else if (info->line == 2) - *serpin_enable_mask |= UART2_ENABLE_MASK; - } -#endif -#if defined(CONFIG_M528x) - /* make sure PUAPAR is set for UART0 and UART1 */ - if (info->line < 2) { - volatile unsigned char *portp = (volatile unsigned char *) (MCF_MBAR + MCF5282_GPIO_PUAPAR); - *portp |= (0x03 << (info->line * 2)); - } -#endif -#elif defined(CONFIG_M520x) - volatile unsigned char *icrp, *uartp; - volatile unsigned long *imrp; - - uartp = info->addr; - - icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_ICR0 + MCFINT_UART0 + info->line); - *icrp = 0x03; - - imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_IMRL); - *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1); - if (info->line < 2) { - unsigned short *uart_par; - uart_par = (unsigned short *)(MCF_IPSBAR + MCF_GPIO_PAR_UART); - if (info->line == 0) - *uart_par |= MCF_GPIO_PAR_UART_PAR_UTXD0 - | MCF_GPIO_PAR_UART_PAR_URXD0; - else if (info->line == 1) - *uart_par |= MCF_GPIO_PAR_UART_PAR_UTXD1 - | MCF_GPIO_PAR_UART_PAR_URXD1; - } else if (info->line == 2) { - unsigned char *feci2c_par; - feci2c_par = (unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FECI2C); - *feci2c_par &= ~0x0F; - *feci2c_par |= MCF_GPIO_PAR_FECI2C_PAR_SCL_UTXD2 - | MCF_GPIO_PAR_FECI2C_PAR_SDA_URXD2; - } -#elif defined(CONFIG_M532x) - volatile unsigned char *uartp; - uartp = info->addr; - switch (info->line) { - case 0: - MCF_INTC0_ICR26 = 0x3; - MCF_INTC0_CIMR = 26; - /* GPIO initialization */ - MCF_GPIO_PAR_UART |= 0x000F; - break; - case 1: - MCF_INTC0_ICR27 = 0x3; - MCF_INTC0_CIMR = 27; - /* GPIO initialization */ - MCF_GPIO_PAR_UART |= 0x0FF0; - break; - case 2: - MCF_INTC0_ICR28 = 0x3; - MCF_INTC0_CIMR = 28; - /* GPIOs also must be initalized, depends on board */ - break; - } -#else - volatile unsigned char *icrp, *uartp; - - switch (info->line) { - case 0: - icrp = (volatile unsigned char *) (MCF_MBAR + MCFSIM_UART1ICR); - *icrp = /*MCFSIM_ICR_AUTOVEC |*/ MCFSIM_ICR_LEVEL6 | - MCFSIM_ICR_PRI1; - mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_UART1); - break; - case 1: - icrp = (volatile unsigned char *) (MCF_MBAR + MCFSIM_UART2ICR); - *icrp = /*MCFSIM_ICR_AUTOVEC |*/ MCFSIM_ICR_LEVEL6 | - MCFSIM_ICR_PRI2; - mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_UART2); - break; - default: - printk("MCFRS: don't know how to handle UART %d interrupt?\n", - info->line); - return; - } - - uartp = info->addr; - uartp[MCFUART_UIVR] = info->irq; -#endif - - /* Clear mask, so no surprise interrupts. */ - uartp[MCFUART_UIMR] = 0; - - if (request_irq(info->irq, mcfrs_interrupt, IRQF_DISABLED, - "ColdFire UART", NULL)) { - printk("MCFRS: Unable to attach ColdFire UART %d interrupt " - "vector=%d\n", info->line, info->irq); - } - - return; -} - - -char *mcfrs_drivername = "ColdFire internal UART serial driver version 1.00\n"; - - -/* - * Serial stats reporting... - */ -int mcfrs_readproc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct mcf_serial *info; - char str[20]; - int len, sigs, i; - - len = sprintf(page, mcfrs_drivername); - for (i = 0; (i < NR_PORTS); i++) { - info = &mcfrs_table[i]; - len += sprintf((page + len), "%d: port:%x irq=%d baud:%d ", - i, (unsigned int) info->addr, info->irq, info->baud); - if (info->stats.rx || info->stats.tx) - len += sprintf((page + len), "tx:%d rx:%d ", - info->stats.tx, info->stats.rx); - if (info->stats.rxframing) - len += sprintf((page + len), "fe:%d ", - info->stats.rxframing); - if (info->stats.rxparity) - len += sprintf((page + len), "pe:%d ", - info->stats.rxparity); - if (info->stats.rxbreak) - len += sprintf((page + len), "brk:%d ", - info->stats.rxbreak); - if (info->stats.rxoverrun) - len += sprintf((page + len), "oe:%d ", - info->stats.rxoverrun); - - str[0] = str[1] = 0; - if ((sigs = mcfrs_getsignals(info))) { - if (sigs & TIOCM_RTS) - strcat(str, "|RTS"); - if (sigs & TIOCM_CTS) - strcat(str, "|CTS"); - if (sigs & TIOCM_DTR) - strcat(str, "|DTR"); - if (sigs & TIOCM_CD) - strcat(str, "|CD"); - } - - len += sprintf((page + len), "%s\n", &str[1]); - } - - return(len); -} - - -/* Finally, routines used to initialize the serial driver. */ - -static void show_serial_version(void) -{ - printk(mcfrs_drivername); -} - -static const struct tty_operations mcfrs_ops = { - .open = mcfrs_open, - .close = mcfrs_close, - .write = mcfrs_write, - .flush_chars = mcfrs_flush_chars, - .write_room = mcfrs_write_room, - .chars_in_buffer = mcfrs_chars_in_buffer, - .flush_buffer = mcfrs_flush_buffer, - .ioctl = mcfrs_ioctl, - .throttle = mcfrs_throttle, - .unthrottle = mcfrs_unthrottle, - .set_termios = mcfrs_set_termios, - .stop = mcfrs_stop, - .start = mcfrs_start, - .hangup = mcfrs_hangup, - .read_proc = mcfrs_readproc, - .wait_until_sent = mcfrs_wait_until_sent, - .tiocmget = mcfrs_tiocmget, - .tiocmset = mcfrs_tiocmset, -}; - -/* mcfrs_init inits the driver */ -static int __init -mcfrs_init(void) -{ - struct mcf_serial *info; - unsigned long flags; - int i; - - /* Setup base handler, and timer table. */ -#ifdef MCFPP_DCD0 - init_timer(&mcfrs_timer_struct); - mcfrs_timer_struct.function = mcfrs_timer; - mcfrs_timer_struct.data = 0; - mcfrs_timer_struct.expires = jiffies + HZ/25; - add_timer(&mcfrs_timer_struct); - mcfrs_ppstatus = mcf_getppdata() & (MCFPP_DCD0 | MCFPP_DCD1); -#endif - mcfrs_serial_driver = alloc_tty_driver(NR_PORTS); - if (!mcfrs_serial_driver) - return -ENOMEM; - - show_serial_version(); - - /* Initialize the tty_driver structure */ - mcfrs_serial_driver->owner = THIS_MODULE; - mcfrs_serial_driver->name = "ttyS"; - mcfrs_serial_driver->driver_name = "mcfserial"; - mcfrs_serial_driver->major = TTY_MAJOR; - mcfrs_serial_driver->minor_start = 64; - mcfrs_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - mcfrs_serial_driver->subtype = SERIAL_TYPE_NORMAL; - mcfrs_serial_driver->init_termios = tty_std_termios; - - mcfrs_serial_driver->init_termios.c_cflag = - mcfrs_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL; - mcfrs_serial_driver->flags = TTY_DRIVER_REAL_RAW; - - tty_set_operations(mcfrs_serial_driver, &mcfrs_ops); - - if (tty_register_driver(mcfrs_serial_driver)) { - printk("MCFRS: Couldn't register serial driver\n"); - put_tty_driver(mcfrs_serial_driver); - return(-EBUSY); - } - - local_irq_save(flags); - - /* - * Configure all the attached serial ports. - */ - for (i = 0, info = mcfrs_table; (i < NR_PORTS); i++, info++) { - info->magic = SERIAL_MAGIC; - info->line = i; - info->port.tty = NULL; - 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_WORK(&info->tqueue, mcfrs_offintr); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - - info->imr = 0; - mcfrs_setsignals(info, 0, 0); - mcfrs_irqinit(info); - - printk("ttyS%d at 0x%04x (irq = %d)", info->line, - (unsigned int) info->addr, info->irq); - printk(" is a builtin ColdFire UART\n"); - } - - local_irq_restore(flags); - return 0; -} - -module_init(mcfrs_init); - -/****************************************************************************/ -/* Serial Console */ -/****************************************************************************/ - -/* - * Quick and dirty UART initialization, for console output. - */ - -void mcfrs_init_console(void) -{ - volatile unsigned char *uartp; - unsigned int clk; - - /* - * Reset UART, get it into known state... - */ - uartp = (volatile unsigned char *) (MCF_MBAR + - (mcfrs_console_port ? MCFUART_BASE2 : MCFUART_BASE1)); - - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */ - - /* - * Set port for defined baud , 8 data bits, 1 stop bit, no parity. - */ - uartp[MCFUART_UMR] = MCFUART_MR1_PARITYNONE | MCFUART_MR1_CS8; - uartp[MCFUART_UMR] = MCFUART_MR2_STOP1; - -#ifdef CONFIG_M5272 -{ - /* - * For the MCF5272, also compute the baudrate fraction. - */ - int fraction = MCF_BUSCLK - (clk * 32 * mcfrs_console_baud); - fraction *= 16; - fraction /= (32 * mcfrs_console_baud); - uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */ - clk = (MCF_BUSCLK / mcfrs_console_baud) / 32; -} -#else - clk = ((MCF_BUSCLK / mcfrs_console_baud) + 16) / 32; /* set baud */ -#endif - - uartp[MCFUART_UBG1] = (clk & 0xff00) >> 8; /* set msb baud */ - uartp[MCFUART_UBG2] = (clk & 0xff); /* set lsb baud */ - uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER; - uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; - - mcfrs_console_inited++; - return; -} - - -/* - * Setup for console. Argument comes from the boot command line. - */ - -int mcfrs_console_setup(struct console *cp, char *arg) -{ - int i, n = CONSOLE_BAUD_RATE; - - if (!cp) - return(-1); - - if (!strncmp(cp->name, "ttyS", 4)) - mcfrs_console_port = cp->index; - else if (!strncmp(cp->name, "cua", 3)) - mcfrs_console_port = cp->index; - else - return(-1); - - if (arg) - n = simple_strtoul(arg,NULL,0); - for (i = 0; i < MCFRS_BAUD_TABLE_SIZE; i++) - if (mcfrs_baud_table[i] == n) - break; - if (i < MCFRS_BAUD_TABLE_SIZE) { - mcfrs_console_baud = n; - mcfrs_console_cbaud = 0; - if (i > 15) { - mcfrs_console_cbaud |= CBAUDEX; - i -= 15; - } - mcfrs_console_cbaud |= i; - } - mcfrs_init_console(); /* make sure baud rate changes */ - return(0); -} - - -static struct tty_driver *mcfrs_console_device(struct console *c, int *index) -{ - *index = c->index; - return mcfrs_serial_driver; -} - - -/* - * Output a single character, using UART polled mode. - * This is used for console output. - */ - -int mcfrs_put_char(char ch) -{ - volatile unsigned char *uartp; - unsigned long flags; - int i; - - uartp = (volatile unsigned char *) (MCF_MBAR + - (mcfrs_console_port ? MCFUART_BASE2 : MCFUART_BASE1)); - - local_irq_save(flags); - for (i = 0; (i < 0x10000); i++) { - if (uartp[MCFUART_USR] & MCFUART_USR_TXREADY) - break; - } - if (i < 0x10000) { - uartp[MCFUART_UTB] = ch; - for (i = 0; (i < 0x10000); i++) - if (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) - break; - } - if (i >= 0x10000) - mcfrs_init_console(); /* try and get it back */ - local_irq_restore(flags); - - return 1; -} - - -/* - * rs_console_write is registered for printk output. - */ - -void mcfrs_console_write(struct console *cp, const char *p, unsigned len) -{ - if (!mcfrs_console_inited) - mcfrs_init_console(); - while (len-- > 0) { - if (*p == '\n') - mcfrs_put_char('\r'); - mcfrs_put_char(*p++); - } -} - -/* - * declare our consoles - */ - -struct console mcfrs_console = { - .name = "ttyS", - .write = mcfrs_console_write, - .device = mcfrs_console_device, - .setup = mcfrs_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -static int __init mcfrs_console_init(void) -{ - register_console(&mcfrs_console); - return 0; -} - -console_initcall(mcfrs_console_init); - -/****************************************************************************/ diff --git a/drivers/serial/mcfserial.h b/drivers/serial/mcfserial.h deleted file mode 100644 index 56420e2cb11..00000000000 --- a/drivers/serial/mcfserial.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * mcfserial.c -- serial driver for ColdFire internal UARTS. - * - * Copyright (c) 1999 Greg Ungerer <gerg@snapgear.com> - * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com> - * Copyright (c) 2002 SnapGear Inc., <www.snapgear.com> - * - * Based on code from 68332serial.c which was: - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 TSHG - * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org> - */ -#ifndef _MCF_SERIAL_H -#define _MCF_SERIAL_H - -#include <linux/serial.h> - -#ifdef __KERNEL__ - -/* - * Define a local serial stats structure. - */ - -struct mcf_stats { - unsigned int rx; - unsigned int tx; - unsigned int rxbreak; - unsigned int rxframing; - unsigned int rxparity; - unsigned int rxoverrun; -}; - - -/* - * This is our internal structure for each serial port's state. - * Each serial port has one of these structures associated with it. - */ - -struct mcf_serial { - int magic; - volatile unsigned char *addr; /* UART memory address */ - int irq; - int flags; /* defined in tty.h */ - int type; /* UART type */ - struct tty_struct *tty; - unsigned char imr; /* Software imr register */ - unsigned int baud; - int sigs; - int custom_divisor; - int x_char; /* xon/xoff character */ - int baud_base; - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - 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; - struct mcf_stats stats; - struct work_struct tqueue; - struct work_struct tqueue_hangup; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - -}; - -#endif /* __KERNEL__ */ - -#endif /* _MCF_SERIAL_H */ diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index f977c98cfa9..6bdf3362e3b 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -2051,7 +2051,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) "transmitter\n", port->dev ? port->dev->bus_id : "", port->dev ? ": " : "", - drv->dev_name, port->line); + drv->dev_name, + drv->tty_driver->name_base + port->line); ops->shutdown(port); } @@ -2154,12 +2155,11 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) switch (port->iotype) { case UPIO_PORT: - snprintf(address, sizeof(address), - "I/O 0x%x", port->iobase); + snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase); break; case UPIO_HUB6: snprintf(address, sizeof(address), - "I/O 0x%x offset 0x%x", port->iobase, port->hub6); + "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); break; case UPIO_MEM: case UPIO_MEM32: @@ -2177,7 +2177,9 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", port->dev ? port->dev->bus_id : "", port->dev ? ": " : "", - drv->dev_name, port->line, address, port->irq, uart_type(port)); + drv->dev_name, + drv->tty_driver->name_base + port->line, + address, port->irq, uart_type(port)); } static void diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 79ea98c66fa..99fb7dc59c4 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work) * 64 bytes, to ensure I do not get throttled. * Ask USB mailing list for better aproach. */ - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { schedule_work(&priv->rx_work); @@ -283,12 +283,13 @@ static void aircable_read(struct work_struct *work) count = min(64, serial_buf_data_avail(priv->rx_buf)); if (count <= 0) - return; /* We have finished sending everything. */ + goto out; /* We have finished sending everything. */ tty_prepare_flip_string(tty, &data, count); if (!data) { - err("%s- kzalloc(%d) failed.", __func__, count); - return; + dev_err(&port->dev, "%s- kzalloc(%d) failed.", + __func__, count); + goto out; } serial_buf_get(priv->rx_buf, data, count); @@ -297,7 +298,8 @@ static void aircable_read(struct work_struct *work) if (serial_buf_data_avail(priv->rx_buf)) schedule_work(&priv->rx_work); - +out: + tty_kref_put(tty); return; } /* End of private methods */ @@ -495,7 +497,7 @@ static void aircable_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, urb->transfer_buffer); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { if (urb->actual_length <= 2) { /* This is an incomplete package */ @@ -527,6 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb) } aircable_read(&priv->rx_work); } + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 2ebe06c3405..1913bc7c5f0 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -322,7 +322,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* Overrun Error */ if (priv->last_lsr & BELKIN_SA_LSR_OE) { } @@ -335,6 +335,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) /* Break Indicator */ if (priv->last_lsr & BELKIN_SA_LSR_BI) { } + tty_kref_put(tty); } #endif spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index e980766bb84..5b20de130e0 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -117,7 +117,7 @@ static int usb_console_setup(struct console *co, char *options) } port = serial->port[0]; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); info->port = port; @@ -143,7 +143,7 @@ static int usb_console_setup(struct console *co, char *options) } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); } /* only call the device specific open if this @@ -163,7 +163,7 @@ static int usb_console_setup(struct console *co, char *options) tty_termios_encode_baud_rate(termios, baud, baud); serial->type->set_termios(tty, port, &dummy); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); kfree(termios); kfree(tty); } @@ -176,7 +176,7 @@ out: return retval; free_termios: kfree(termios); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); free_tty: kfree(tty); reset_open_count: diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index b4d72351cb9..94ef36c4764 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -384,7 +384,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - ignoring since device not open\n", __func__); return; @@ -394,6 +394,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); spin_lock(&priv->lock); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 22837a3f2f8..f3514a91f91 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1286,7 +1286,7 @@ static void cypress_read_int_callback(struct urb *urb) } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1362,7 +1362,7 @@ static void cypress_read_int_callback(struct urb *urb) data[i]); tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push(port->port.tty); + tty_flip_buffer_push(tty); } spin_lock_irqsave(&priv->lock, flags); @@ -1371,6 +1371,7 @@ static void cypress_read_int_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); continue_read: + tty_kref_put(tty); /* Continue trying to always read... unless the port has closed. */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 240aad1acaa..5756ac6d6c9 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -604,7 +604,9 @@ static void digi_wakeup_write_lock(struct work_struct *work) static void digi_wakeup_write(struct usb_serial_port *port) { - tty_wakeup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_wakeup(tty); + tty_kref_put(tty); } @@ -1668,7 +1670,7 @@ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; @@ -1692,6 +1694,7 @@ static int digi_read_inb_callback(struct urb *urb) return -1; } + tty = tty_port_tty_get(&port->port); spin_lock(&priv->dp_port_lock); /* check for throttle; if set, do not resubmit read urb */ @@ -1735,6 +1738,7 @@ static int digi_read_inb_callback(struct urb *urb) } } spin_unlock(&priv->dp_port_lock); + tty_kref_put(tty); if (opcode == DIGI_CMD_RECEIVE_DISABLE) dbg("%s: got RECEIVE_DISABLE", __func__); @@ -1760,6 +1764,7 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; + struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode, line, status, val; int i; @@ -1787,10 +1792,11 @@ static int digi_read_oob_callback(struct urb *urb) if (priv == NULL) return -1; + tty = tty_port_tty_get(&port->port); rts = 0; if (port->port.count) - rts = port->port.tty->termios->c_cflag & CRTSCTS; - + rts = tty->termios->c_cflag & CRTSCTS; + if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) { spin_lock(&priv->dp_port_lock); /* convert from digi flags to termiox flags */ @@ -1798,14 +1804,14 @@ static int digi_read_oob_callback(struct urb *urb) priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ if (rts) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; digi_wakeup_write(port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ if (rts) - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; } if (val & DIGI_READ_INPUT_SIGNALS_DSR) priv->dp_modem_signals |= TIOCM_DSR; @@ -1830,6 +1836,7 @@ static int digi_read_oob_callback(struct urb *urb) } else if (opcode == DIGI_CMD_IFLUSH_FIFO) { wake_up_interruptible(&priv->dp_flush_wait); } + tty_kref_put(tty); } return 0; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index a6ab5b58d9c..1072e847280 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -33,9 +33,8 @@ * Moved MOD_DEC_USE_COUNT to end of empeg_close(). * * (12/03/2000) gb - * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to - * empeg_open(). This notifies the tty driver that the termios have - * changed. + * Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open(). + * This notifies the tty driver that the termios have changed. * * (11/13/2000) gb * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to @@ -354,7 +353,7 @@ static void empeg_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); @@ -362,6 +361,7 @@ static void empeg_read_bulk_callback(struct urb *urb) tty_flip_buffer_push(tty); bytes_in += urb->actual_length; } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb( diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3dc93b542b3..c2ac129557a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -860,7 +860,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, kfree(buf); if (rv < 0) { - err("%s Error from MODEM_CTRL urb: DTR %s, RTS %s", + dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s", __func__, (set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged", @@ -1808,7 +1808,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) if (port->port.count <= 0) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1817,7 +1817,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __func__); - return; + goto out; } if (urb != port->read_urb) @@ -1827,7 +1827,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) /* This will happen at close every time so it is a dbg not an err */ dbg("(this is ok on close) nonzero read bulk status received: %d", status); - return; + goto out; } /* count data bytes, but not status bytes */ @@ -1838,7 +1838,8 @@ static void ftdi_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->rx_lock, flags); ftdi_process_read(&priv->rx_work.work); - +out: + tty_kref_put(tty); } /* ftdi_read_bulk_callback */ @@ -1863,7 +1864,7 @@ static void ftdi_process_read(struct work_struct *work) if (port->port.count <= 0) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1872,13 +1873,13 @@ static void ftdi_process_read(struct work_struct *work) priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __func__); - return; + goto out; } urb = port->read_urb; if (!urb) { dbg("%s - bad read_urb pointer - exiting", __func__); - return; + goto out; } data = urb->transfer_buffer; @@ -2020,7 +2021,7 @@ static void ftdi_process_read(struct work_struct *work) schedule_delayed_work(&priv->rx_work, 1); else dbg("%s - port is closed", __func__); - return; + goto out; } /* urb is completely processed */ @@ -2041,6 +2042,8 @@ static void ftdi_process_read(struct work_struct *work) err("%s - failed resubmitting read urb, error %d", __func__, result); } +out: + tty_kref_put(tty); } /* ftdi_process_read */ @@ -2256,7 +2259,7 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) 0, 0, buf, 1, WDR_TIMEOUT); if (ret < 0) { - err("%s Could not get modem status of device - err: %d", __func__, + dbg("%s Could not get modem status of device - err: %d", __func__, ret); return ret; } @@ -2275,7 +2278,7 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) 0, priv->interface, buf, 2, WDR_TIMEOUT); if (ret < 0) { - err("%s Could not get modem status of device - err: %d", __func__, + dbg("%s Could not get modem status of device - err: %d", __func__, ret); return ret; } diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index d9538208807..2ad0569bcf1 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -276,7 +276,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, char *data, unsigned int actual_length) { - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); if (tty && actual_length) { @@ -287,6 +287,7 @@ static void send_to_tty(struct usb_serial_port *port, tty_insert_flip_string(tty, data, actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index fe84c88ec20..814909f1ee6 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -330,7 +330,7 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); int room; /* Push data to tty */ @@ -341,6 +341,7 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) tty_flip_buffer_push(tty); } } + tty_kref_put(tty); resubmit_read_urb(port, GFP_ATOMIC); } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index bfa508ddb0f..611f97fd62f 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -600,6 +600,7 @@ static void edge_interrupt_callback(struct urb *urb) struct edgeport_serial *edge_serial = urb->context; struct edgeport_port *edge_port; struct usb_serial_port *port; + struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int length = urb->actual_length; int bytes_avail; @@ -675,9 +676,12 @@ static void edge_interrupt_callback(struct urb *urb) /* tell the tty driver that something has changed */ - if (edge_port->port->port.tty) - tty_wakeup(edge_port->port->port.tty); - + tty = tty_port_tty_get( + &edge_port->port->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } /* Since we have more credit, check if more data can be sent */ send_more_port_data(edge_serial, @@ -778,13 +782,14 @@ static void edge_bulk_out_data_callback(struct urb *urb) __func__, status); } - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ tty_wakeup(tty); } + tty_kref_put(tty); /* Release the Write URB */ edge_port->write_in_progress = false; @@ -826,11 +831,12 @@ static void edge_bulk_out_cmd_callback(struct urb *urb) } /* Get pointer to tty */ - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); /* tell the tty driver that something has changed */ if (tty && edge_port->open) tty_wakeup(tty); + tty_kref_put(tty); /* we have completed the command */ edge_port->commandPending = false; @@ -1932,11 +1938,13 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port->open) { - tty = edge_port->port->port.tty; + tty = tty_port_tty_get( + &edge_port->port->port); if (tty) { dbg("%s - Sending %d bytes to TTY for port %d", __func__, rxLen, edge_serial->rxPort); edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); + tty_kref_put(tty); } edge_port->icount.rx += rxLen; } @@ -1971,6 +1979,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, { struct usb_serial_port *port; struct edgeport_port *edge_port; + struct tty_struct *tty; __u8 code = edge_serial->rxStatusCode; /* switch the port pointer to the one being currently talked about */ @@ -2020,10 +2029,12 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, /* send the current line settings to the port so we are in sync with any further termios calls */ - /* FIXME: locking on tty */ - if (edge_port->port->port.tty) - change_port_settings(edge_port->port->port.tty, - edge_port, edge_port->port->port.tty->termios); + tty = tty_port_tty_get(&edge_port->port->port); + if (tty) { + change_port_settings(tty, + edge_port, tty->termios); + tty_kref_put(tty); + } /* we have completed the open */ edge_port->openPending = false; @@ -2163,10 +2174,14 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, } /* Place LSR data byte into Rx buffer */ - if (lsrData && edge_port->port->port.tty) - edge_tty_recv(&edge_port->port->dev, - edge_port->port->port.tty, &data, 1); - + if (lsrData) { + struct tty_struct *tty = + tty_port_tty_get(&edge_port->port->port); + if (tty) { + edge_tty_recv(&edge_port->port->dev, tty, &data, 1); + tty_kref_put(tty); + } + } /* update input line counters */ icount = &edge_port->icount; if (newLsr & LSR_BREAK) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index cb4c54316cf..541dd8e6e7a 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -572,7 +572,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, int flush) { int baud_rate; - struct tty_struct *tty = port->port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port->port); wait_queue_t wait; unsigned long flags; @@ -599,6 +599,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, if (flush) edge_buf_clear(port->ep_out_buf); spin_unlock_irqrestore(&port->ep_lock, flags); + tty_kref_put(tty); /* wait for data to drain from the device */ timeout += jiffies; @@ -1554,7 +1555,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) /* Save the new modem status */ edge_port->shadow_msr = msr & 0xf0; - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { if (msr & EDGEPORT_MSR_CTS) { @@ -1564,6 +1565,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) tty->hw_stopped = 1; } } + tty_kref_put(tty); return; } @@ -1574,6 +1576,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, struct async_icount *icount; __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); + struct tty_struct *tty; dbg("%s - %02x", __func__, new_lsr); @@ -1587,8 +1590,13 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); /* Place LSR data byte into Rx buffer */ - if (lsr_data && edge_port->port->port.tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); + if (lsr_data) { + tty = tty_port_tty_get(&edge_port->port->port); + if (tty) { + edge_tty_recv(&edge_port->port->dev, tty, &data, 1); + tty_kref_put(tty); + } + } /* update input line counters */ icount = &edge_port->icount; @@ -1749,7 +1757,7 @@ static void edge_bulk_in_callback(struct urb *urb) ++data; } - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); if (tty && urb->actual_length) { usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data); @@ -1761,6 +1769,7 @@ static void edge_bulk_in_callback(struct urb *urb) urb->actual_length); edge_port->icount.rx += urb->actual_length; } + tty_kref_put(tty); exit: /* continue read unless stopped */ @@ -1796,6 +1805,7 @@ static void edge_bulk_out_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status = urb->status; + struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); @@ -1818,7 +1828,9 @@ static void edge_bulk_out_callback(struct urb *urb) } /* send any buffered data */ - edge_send(port->port.tty); + tty = tty_port_tty_get(&port->port); + edge_send(tty); + tty_kref_put(tty); } static int edge_open(struct tty_struct *tty, @@ -1876,7 +1888,7 @@ static int edge_open(struct tty_struct *tty, /* set up the port settings */ if (tty) - edge_set_termios(tty, port, port->port.tty->termios); + edge_set_termios(tty, port, tty->termios); /* open up the port */ diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index cd9a2e138c8..2affa9c118b 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -764,13 +764,14 @@ static void ipaq_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); bytes_in += urb->actual_length; } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index a842025b9b5..480cac27d64 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -170,12 +170,13 @@ static void ipw_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index e59155c6607..45d4043e04a 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -465,11 +465,12 @@ static void ir_read_bulk_callback(struct urb *urb) ir_baud = *data & 0x0f; usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty_buffer_request_room(tty, urb->actual_length - 1)) { tty_insert_flip_string(tty, data+1, urb->actual_length - 1); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* * No break here. diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index ddff37fa633..53710aa7ead 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -629,13 +629,14 @@ static void read_buf_callback(struct urb *urb) } dbg("%s - %i chars to write", __func__, urb->actual_length); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (data == NULL) dbg("%s - data is NULL !!!", __func__); if (tty && urb->actual_length && data) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); iuu_led_activity_on(urb); } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 704716f6f6d..15447af4869 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -430,7 +430,7 @@ static void usa26_indat_callback(struct urb *urb) } port = urb->context; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -459,6 +459,7 @@ static void usa26_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -513,6 +514,7 @@ static void usa26_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -553,12 +555,11 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -604,11 +605,12 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty = port->port.tty; - if (urb->actual_length) { + tty =tty_port_tty_get(&port->port); + if (tty && urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -652,6 +654,7 @@ static void usa28_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state; int status = urb->status; @@ -689,12 +692,11 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if( old_dcd_state != p_priv->dcd_state && old_dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -785,12 +787,11 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -827,7 +828,7 @@ static void usa49_indat_callback(struct urb *urb) } port = urb->context; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -850,6 +851,7 @@ static void usa49_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -893,7 +895,7 @@ static void usa49wg_indat_callback(struct urb *urb) return; } port = serial->port[data[i++]]; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); len = data[i++]; /* 0x80 bit is error flag */ @@ -927,6 +929,7 @@ static void usa49wg_indat_callback(struct urb *urb) } if (port->port.count) tty_flip_buffer_push(tty); + tty_kref_put(tty); } } @@ -967,8 +970,8 @@ static void usa90_indat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - tty = port->port.tty; if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); /* if current mode is DMA, looks like usa28 format otherwise looks like usa26 data format */ @@ -1004,6 +1007,7 @@ static void usa90_indat_callback(struct urb *urb) } } tty_flip_buffer_push(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1025,6 +1029,7 @@ static void usa90_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -1053,12 +1058,11 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1130,12 +1134,11 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1332,7 +1335,7 @@ static void keyspan_close(struct tty_struct *tty, stop_urb(p_priv->out_urbs[i]); } } - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); } /* download the firmware to a pre-renumeration device */ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 040040a267d..99e9a14c5bf 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -172,8 +172,9 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - - tty_wakeup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_wakeup(tty); + tty_kref_put(tty); } static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -205,7 +206,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) static void keyspan_pda_rx_interrupt(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); unsigned char *data = urb->transfer_buffer; int retval; int status = urb->status; @@ -222,7 +223,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __func__, status); - return; + goto out; default: dbg("%s - nonzero urb status received: %d", __func__, status); @@ -261,8 +262,11 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err("%s - usb_submit_urb failed with result %d", - __func__, retval); + dev_err(&port->dev, + "%s - usb_submit_urb failed with result %d", + __func__, retval); +out: + tty_kref_put(tty); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index b84dddc7112..ff3a07f5102 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -658,7 +658,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb) } else { int bytes_sent = ((__u8 *) data)[0] + ((unsigned int) ((__u8 *) data)[1] << 8); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* we should immediately resubmit the URB, before attempting * to pass the data on to the tty layer. But that needs locking * against re-entry an then mixed-up data because of @@ -679,6 +679,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb) tty_buffer_request_room(tty, bytes_sent); tty_insert_flip_string(tty, data + 2, bytes_sent); tty_flip_buffer_push(tty); + tty_kref_put(tty); /* again lockless, but debug info only */ priv->bytes_in += bytes_sent; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index deba28ec77e..cfcf37c2b95 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -383,7 +383,7 @@ static void kobil_read_int_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { /* BEGIN DEBUG */ @@ -405,6 +405,7 @@ static void kobil_read_int_callback(struct urb *urb) tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 0ded8bd6ec8..9b2cef81cde 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -563,10 +563,11 @@ static void mct_u232_read_int_callback(struct urb *urb) * Work-a-round: handle the 'usual' bulk-in pipe here */ if (urb->transfer_buffer_length > 2) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); + tty_kref_put(tty); } goto exit; } @@ -591,7 +592,7 @@ static void mct_u232_read_int_callback(struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & MCT_U232_LSR_ERR) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* Overrun Error */ if (priv->last_lsr & MCT_U232_LSR_OE) { } @@ -604,6 +605,7 @@ static void mct_u232_read_int_callback(struct urb *urb) /* Break Indicator */ if (priv->last_lsr & MCT_U232_LSR_BI) { } + tty_kref_put(tty); } #endif spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 7c4917d77c0..7b538caec37 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -216,12 +216,13 @@ static void mos7720_bulk_in_callback(struct urb *urb) data = urb->transfer_buffer; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); if (!port->read_urb) { dbg("URB KILLED !!!"); @@ -262,10 +263,11 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) dbg("Entering ........."); - tty = mos7720_port->port->port.tty; + tty = tty_port_tty_get(&mos7720_port->port->port); if (tty && mos7720_port->open) tty_wakeup(tty); + tty_kref_put(tty); } /* @@ -1267,29 +1269,6 @@ static int get_lsr_info(struct tty_struct *tty, return 0; } -/* - * get_number_bytes_avail - get number of bytes available - * - * Purpose: Let user call ioctl to get the count of number of bytes available. - */ -static int get_number_bytes_avail(struct moschip_port *mos7720_port, - unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = mos7720_port->port->port.tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, mos7720_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - - return -ENOIOCTLCMD; -} - static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, unsigned int __user *value) { @@ -1409,13 +1388,6 @@ static int mos7720_ioctl(struct tty_struct *tty, struct file *file, dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - case TIOCINQ: - /* return number of bytes available */ - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(mos7720_port, - (unsigned int __user *)arg); - break; - case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return get_lsr_info(tty, mos7720_port, diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 09d82062b97..60543d79ef5 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -709,12 +709,13 @@ static void mos7840_bulk_in_callback(struct urb *urb) dbg("%s", "Entering ........... \n"); if (urb->actual_length) { - tty = mos7840_port->port->port.tty; + tty = tty_port_tty_get(&mos7840_port->port->port); if (tty) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); dbg(" %s \n", data); tty_flip_buffer_push(tty); + tty_kref_put(tty); } mos7840_port->icount.rx += urb->actual_length; smp_wmb(); @@ -773,10 +774,10 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) dbg("%s \n", "Entering ........."); - tty = mos7840_port->port->port.tty; - + tty = tty_port_tty_get(&mos7840_port->port->port); if (tty && mos7840_port->open) tty_wakeup(tty); + tty_kref_put(tty); } diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index d6736531a0f..bcdcbb82270 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,12 +64,13 @@ static void navman_read_int_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); exit: result = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index ae8e227f3db..c4d70b0f1e4 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -172,7 +172,7 @@ static int omninet_open(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); wport = serial->port[1]; - wport->port.tty = tty; /* FIXME */ + tty_port_tty_set(&wport->port, tty); /* Start reading from the device */ usb_fill_bulk_urb(port->read_urb, serial->dev, @@ -229,9 +229,11 @@ static void omninet_read_bulk_callback(struct urb *urb) } if (urb->actual_length && header->oh_len) { - tty_insert_flip_string(port->port.tty, - data + OMNINET_DATAOFFSET, header->oh_len); - tty_flip_buffer_push(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET, + header->oh_len); + tty_flip_buffer_push(tty); + tty_kref_put(tty); } /* Continue trying to always read */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 73f8277f88f..6b1727e751e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -571,14 +571,14 @@ static void option_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); - } else { + } else dbg("%s: empty read urb received", __func__); - } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN) { @@ -647,9 +647,13 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) && - old_dcd_state && !portdata->dcd_state) - tty_hangup(port->port.tty); + if (old_dcd_state && !portdata->dcd_state) { + struct tty_struct *tty = + tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); + } } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -793,7 +797,7 @@ static void option_close(struct tty_struct *tty, for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - port->port.tty = NULL; /* FIXME */ + tty_port_tty_set(&port->port, NULL); } /* Helper functions used by option_setup_urbs */ diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 81db5715ee2..ba551f00f16 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -224,10 +224,6 @@ struct oti6858_private { struct usb_serial_port *port; /* USB port with which associated */ }; -#undef dbg -/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */ -#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg) - static void setup_line(struct work_struct *work) { struct oti6858_private *priv = container_of(work, @@ -1002,11 +998,12 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty != NULL && urb->actual_length > 0) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* schedule the interrupt urb if we are still open */ if (port->port.count != 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1ede1441cb1..90843784716 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -154,7 +154,6 @@ struct pl2303_private { wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; - u8 termios_initialized; enum pl2303_type type; }; @@ -526,16 +525,6 @@ static void pl2303_set_termios(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); - /* The PL2303 is reported to lose bytes if you change serial settings even to the same values as before. Thus we actually need to filter in this specific case */ @@ -1057,7 +1046,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -1067,7 +1056,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_insert_flip_char(tty, data[i], tty_flag); tty_flip_buffer_push(tty); } - + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index def52d07a4e..72903ac9f5c 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -217,6 +217,7 @@ static void safe_read_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; unsigned char length = urb->actual_length; + struct tty_struct *tty; int result; int status = urb->status; @@ -242,6 +243,7 @@ static void safe_read_bulk_callback(struct urb *urb) printk("\n"); } #endif + tty = tty_port_tty_get(&port->port); if (safe) { __u16 fcs; fcs = fcs_compute10(data, length, CRC10_INITFCS); @@ -250,9 +252,9 @@ static void safe_read_bulk_callback(struct urb *urb) if (actual_length <= (length - 2)) { info("%s - actual: %d", __func__, actual_length); - tty_insert_flip_string(port->port.tty, + tty_insert_flip_string(tty, data, actual_length); - tty_flip_buffer_push(port->port.tty); + tty_flip_buffer_push(tty); } else { err("%s - inconsistent lengths %d:%d", __func__, actual_length, length); @@ -261,9 +263,10 @@ static void safe_read_bulk_callback(struct urb *urb) err("%s - bad CRC %x", __func__, fcs); } } else { - tty_insert_flip_string(port->port.tty, data, length); - tty_flip_buffer_push(port->port.tty); + tty_insert_flip_string(tty, data, length); + tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(urb, port->serial->dev, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ea1a103c99b..8b9eaf38367 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -440,14 +440,14 @@ static void sierra_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->port.tty; if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); - } else { + tty_kref_put(tty); + } else dbg("%s: empty read urb received", __func__); - } /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN) { @@ -485,6 +485,7 @@ static void sierra_instat_callback(struct urb *urb) unsigned char signals = *((unsigned char *) urb->transfer_buffer + sizeof(struct usb_ctrlrequest)); + struct tty_struct *tty; dbg("%s: signal x%x", __func__, signals); @@ -494,9 +495,11 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) && + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->port.tty); + tty_hangup(tty); + tty_kref_put(tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -616,8 +619,7 @@ static void sierra_close(struct tty_struct *tty, } usb_kill_urb(port->interrupt_in_urb); - - port->port.tty = NULL; /* FIXME */ + tty_port_tty_set(&port->port, NULL); } static int sierra_startup(struct usb_serial *serial) diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 283cf6b36b2..1533d6e1223 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -755,7 +755,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -765,6 +765,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_insert_flip_char(tty, data[i], tty_flag); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 9a3e495c769..c90237d48b0 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -179,7 +179,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr); static int ti_get_lsr(struct ti_port *tport); static int ti_get_serial_info(struct ti_port *tport, struct serial_struct __user *ret_arg); -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg); static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); @@ -857,8 +857,8 @@ static int ti_ioctl(struct tty_struct *tty, struct file *file, (struct serial_struct __user *)arg); case TIOCSSERIAL: dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); - return ti_set_serial_info(tport, - (struct serial_struct __user *)arg); + return ti_set_serial_info(tty, tport, + (struct serial_struct __user *)arg); case TIOCMIWAIT: dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); cprev = tport->tp_icount; @@ -1211,6 +1211,7 @@ static void ti_bulk_in_callback(struct urb *urb) struct device *dev = &urb->dev->dev; int status = urb->status; int retval = 0; + struct tty_struct *tty; dbg("%s", __func__); @@ -1239,20 +1240,22 @@ static void ti_bulk_in_callback(struct urb *urb) return; } - if (port->port.tty && urb->actual_length) { + tty = tty_port_tty_get(&port->port); + if (tty && urb->actual_length) { usb_serial_debug_data(debug, dev, __func__, urb->actual_length, urb->transfer_buffer); if (!tport->tp_is_open) dbg("%s - port closed, dropping data", __func__); else - ti_recv(&urb->dev->dev, port->port.tty, + ti_recv(&urb->dev->dev, tty, urb->transfer_buffer, urb->actual_length); spin_lock(&tport->tp_lock); tport->tp_icount.rx += urb->actual_length; spin_unlock(&tport->tp_lock); + tty_kref_put(tty); } exit: @@ -1330,7 +1333,7 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = port->port.tty; /* FIXME */ + struct tty_struct *tty = tty_port_tty_get(&port->port); /* FIXME */ unsigned long flags; @@ -1338,19 +1341,15 @@ static void ti_send(struct ti_port *tport) spin_lock_irqsave(&tport->tp_lock, flags); - if (tport->tp_write_urb_in_use) { - spin_unlock_irqrestore(&tport->tp_lock, flags); - return; - } + if (tport->tp_write_urb_in_use) + goto unlock; count = ti_buf_get(tport->tp_write_buf, port->write_urb->transfer_buffer, port->bulk_out_size); - if (count == 0) { - spin_unlock_irqrestore(&tport->tp_lock, flags); - return; - } + if (count == 0) + goto unlock; tport->tp_write_urb_in_use = 1; @@ -1380,7 +1379,13 @@ static void ti_send(struct ti_port *tport) /* more room in the buffer for new writes, wakeup */ if (tty) tty_wakeup(tty); + tty_kref_put(tty); wake_up_interruptible(&tport->tp_write_wait); + return; +unlock: + spin_unlock_irqrestore(&tport->tp_lock, flags); + tty_kref_put(tty); + return; } @@ -1464,20 +1469,16 @@ static int ti_get_serial_info(struct ti_port *tport, } -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg) { - struct usb_serial_port *port = tport->tp_port; struct serial_struct new_serial; if (copy_from_user(&new_serial, new_arg, sizeof(new_serial))) return -EFAULT; tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - /* FIXME */ - if (port->port.tty) - port->port.tty->low_latency = - (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; tport->tp_closing_wait = new_serial.closing_wait; return 0; @@ -1510,7 +1511,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tport->tp_msr = msr & TI_MSR_MASK; /* handle CTS flow control */ - tty = tport->tp_port->port.tty; + tty = tty_port_tty_get(&tport->tp_port->port); if (tty && C_CRTSCTS(tty)) { if (msr & TI_MSR_CTS) { tty->hw_stopped = 0; @@ -1519,6 +1520,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tty->hw_stopped = 1; } } + tty_kref_put(tty); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4f7f9e3ae0a..e7d4246027b 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -214,7 +214,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) /* set up our port structure making the tty driver * remember our port object, and us it */ tty->driver_data = port; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); if (port->port.count == 1) { @@ -246,7 +246,7 @@ bailout_module_put: bailout_mutex_unlock: port->port.count = 0; tty->driver_data = NULL; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); mutex_unlock(&port->mutex); bailout_kref_put: usb_serial_put(serial); @@ -276,10 +276,11 @@ static void serial_close(struct tty_struct *tty, struct file *filp) port->serial->type->close(tty, port, filp); if (port->port.count == (port->console? 1 : 0)) { - if (port->port.tty) { - if (port->port.tty->driver_data) - port->port.tty->driver_data = NULL; - port->port.tty = NULL; + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty) { + if (tty->driver_data) + tty->driver_data = NULL; + tty_port_tty_set(&port->port, NULL); } } @@ -508,11 +509,12 @@ static void usb_serial_port_work(struct work_struct *work) if (!port) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) return; tty_wakeup(tty); + tty_kref_put(tty); } static void port_release(struct device *dev) @@ -819,6 +821,7 @@ int usb_serial_probe(struct usb_interface *interface, port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) goto probe_error; + tty_port_init(&port->port); port->serial = serial; spin_lock_init(&port->lock); mutex_init(&port->mutex); @@ -1040,8 +1043,11 @@ void usb_serial_disconnect(struct usb_interface *interface) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { - if (port->port.tty) - tty_hangup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } kill_traffic(port); } } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index cf8924f9a2c..a6d1c75a1c8 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -499,7 +499,7 @@ static void visor_read_bulk_callback(struct urb *urb) int status = urb->status; struct tty_struct *tty; int result; - int available_room; + int available_room = 0; dbg("%s - port %d", __func__, port->number); @@ -512,13 +512,17 @@ static void visor_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; - if (tty && urb->actual_length) { - available_room = tty_buffer_request_room(tty, + if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); + if (tty) { + available_room = tty_buffer_request_room(tty, urb->actual_length); - if (available_room) { - tty_insert_flip_string(tty, data, available_room); - tty_flip_buffer_push(tty); + if (available_room) { + tty_insert_flip_string(tty, data, + available_room); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); } spin_lock(&priv->lock); priv->bytes_in += available_room; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 3a9d14384a4..11c8b97a517 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1481,7 +1481,7 @@ static void rx_data_softint(struct work_struct *work) struct whiteheat_private *info = container_of(work, struct whiteheat_private, rx_work); struct usb_serial_port *port = info->port; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); struct whiteheat_urb_wrap *wrap; struct urb *urb; unsigned long flags; @@ -1493,7 +1493,7 @@ static void rx_data_softint(struct work_struct *work) spin_lock_irqsave(&info->lock, flags); if (info->flags & THROTTLED) { spin_unlock_irqrestore(&info->lock, flags); - return; + goto out; } list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { @@ -1513,7 +1513,7 @@ static void rx_data_softint(struct work_struct *work) spin_unlock_irqrestore(&info->lock, flags); tty_flip_buffer_push(tty); schedule_work(&info->rx_work); - return; + goto out; } tty_insert_flip_string(tty, urb->transfer_buffer, len); sent += len; @@ -1536,6 +1536,8 @@ static void rx_data_softint(struct work_struct *work) if (sent) tty_flip_buffer_push(tty); +out: + tty_kref_put(tty); } diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 488eb424f66..a70d5d0890c 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -27,6 +27,7 @@ #define DEVPTS_SUPER_MAGIC 0x1cd1 #define DEVPTS_DEFAULT_MODE 0600 +#define PTMX_MINOR 2 extern int pty_limit; /* Config limit on Unix98 ptys */ static DEFINE_IDA(allocated_ptys); @@ -169,15 +170,7 @@ static struct file_system_type devpts_fs_type = { * to the System V naming convention */ -static struct dentry *get_node(int num) -{ - char s[12]; - struct dentry *root = devpts_root; - mutex_lock(&root->d_inode->i_mutex); - return lookup_one_len(s, root, sprintf(s, "%d", num)); -} - -int devpts_new_index(void) +int devpts_new_index(struct inode *ptmx_inode) { int index; int ida_ret; @@ -205,20 +198,21 @@ retry: return index; } -void devpts_kill_index(int idx) +void devpts_kill_index(struct inode *ptmx_inode, int idx) { mutex_lock(&allocated_ptys_lock); ida_remove(&allocated_ptys, idx); mutex_unlock(&allocated_ptys_lock); } -int devpts_pty_new(struct tty_struct *tty) +int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) { int number = tty->index; /* tty layer puts index from devpts_new_index() in here */ struct tty_driver *driver = tty->driver; dev_t device = MKDEV(driver->major, driver->minor_start+number); struct dentry *dentry; struct inode *inode = new_inode(devpts_mnt->mnt_sb); + char s[12]; /* We're supposed to be given the slave end of a pty */ BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY); @@ -233,10 +227,15 @@ int devpts_pty_new(struct tty_struct *tty) inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; init_special_inode(inode, S_IFCHR|config.mode, device); inode->i_private = tty; + tty->driver_data = inode; - dentry = get_node(number); - if (!IS_ERR(dentry) && !dentry->d_inode) { - d_instantiate(dentry, inode); + sprintf(s, "%d", number); + + mutex_lock(&devpts_root->d_inode->i_mutex); + + dentry = d_alloc_name(devpts_root, s); + if (!IS_ERR(dentry)) { + d_add(dentry, inode); fsnotify_create(devpts_root->d_inode, dentry); } @@ -245,36 +244,31 @@ int devpts_pty_new(struct tty_struct *tty) return 0; } -struct tty_struct *devpts_get_tty(int number) +struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) { - struct dentry *dentry = get_node(number); - struct tty_struct *tty; - - tty = NULL; - if (!IS_ERR(dentry)) { - if (dentry->d_inode) - tty = dentry->d_inode->i_private; - dput(dentry); - } + BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); - mutex_unlock(&devpts_root->d_inode->i_mutex); - - return tty; + if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) + return (struct tty_struct *)pts_inode->i_private; + return NULL; } -void devpts_pty_kill(int number) +void devpts_pty_kill(struct tty_struct *tty) { - struct dentry *dentry = get_node(number); + struct inode *inode = tty->driver_data; + struct dentry *dentry; - if (!IS_ERR(dentry)) { - struct inode *inode = dentry->d_inode; - if (inode) { - inode->i_nlink--; - d_delete(dentry); - dput(dentry); - } + BUG_ON(inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); + + mutex_lock(&devpts_root->d_inode->i_mutex); + + dentry = d_find_alias(inode); + if (dentry && !IS_ERR(dentry)) { + inode->i_nlink--; + d_delete(dentry); dput(dentry); } + mutex_unlock(&devpts_root->d_inode->i_mutex); } diff --git a/fs/dquot.c b/fs/dquot.c index 8ec4d6cc763..ad7e59003e0 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -895,10 +895,9 @@ static void print_warning(struct dquot *dquot, const int warntype) warntype == QUOTA_NL_BSOFTBELOW || !need_print_warning(dquot)) return; - mutex_lock(&tty_mutex); tty = get_current_tty(); if (!tty) - goto out_lock; + return; tty_write_message(tty, dquot->dq_sb->s_id); if (warntype == QUOTA_NL_ISOFTWARN || warntype == QUOTA_NL_BSOFTWARN) tty_write_message(tty, ": warning, "); @@ -926,8 +925,7 @@ static void print_warning(struct dquot *dquot, const int warntype) break; } tty_write_message(tty, msg); -out_lock: - mutex_unlock(&tty_mutex); + tty_kref_put(tty); } #endif diff --git a/fs/open.c b/fs/open.c index 07da9359481..5596049863b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1141,8 +1141,7 @@ EXPORT_SYMBOL(sys_close); asmlinkage long sys_vhangup(void) { if (capable(CAP_SYS_TTY_CONFIG)) { - /* XXX: this needs locking */ - tty_vhangup(current->signal->tty); + tty_vhangup_self(); return 0; } return -EPERM; diff --git a/include/asm-x86/ioctls.h b/include/asm-x86/ioctls.h index 33660351239..06752a64904 100644 --- a/include/asm-x86/ioctls.h +++ b/include/asm-x86/ioctls.h @@ -51,9 +51,15 @@ #define TCSETS2 _IOW('T', 0x2B, struct termios2) #define TCSETSW2 _IOW('T', 0x2C, struct termios2) #define TCSETSF2 _IOW('T', 0x2D, struct termios2) +#define TIOCGRS485 0x542E +#define TIOCSRS485 0x542F #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */ +#define TCSETX 0x5433 +#define TCSETXF 0x5434 +#define TCSETXW 0x5435 #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 154769cad3f..5ce0e5fd712 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -17,20 +17,31 @@ #ifdef CONFIG_UNIX98_PTYS -int devpts_new_index(void); -void devpts_kill_index(int idx); -int devpts_pty_new(struct tty_struct *tty); /* mknod in devpts */ -struct tty_struct *devpts_get_tty(int number); /* get tty structure */ -void devpts_pty_kill(int number); /* unlink */ +int devpts_new_index(struct inode *ptmx_inode); +void devpts_kill_index(struct inode *ptmx_inode, int idx); +/* mknod in devpts */ +int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +/* get tty structure */ +struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number); +/* unlink */ +void devpts_pty_kill(struct tty_struct *tty); #else /* Dummy stubs in the no-pty case */ -static inline int devpts_new_index(void) { return -EINVAL; } -static inline void devpts_kill_index(int idx) { } -static inline int devpts_pty_new(struct tty_struct *tty) { return -EINVAL; } -static inline struct tty_struct *devpts_get_tty(int number) { return NULL; } -static inline void devpts_pty_kill(int number) { } +static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } +static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } +static inline int devpts_pty_new(struct inode *ptmx_inode, + struct tty_struct *tty) +{ + return -EINVAL; +} +static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode, + int number) +{ + return NULL; +} +static inline void devpts_pty_kill(struct tty_struct *tty) { } #endif diff --git a/include/linux/serial.h b/include/linux/serial.h index deb714314fb..1ea8d9265bf 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -173,6 +173,22 @@ struct serial_icounter_struct { int reserved[9]; }; +/* + * Serial interface for controlling RS485 settings on chips with suitable + * support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your + * platform. The set function returns the new state, with any unsupported bits + * reverted appropriately. + */ + +struct serial_rs485 { + __u32 flags; /* RS485 feature flags */ +#define SER_RS485_ENABLED (1 << 0) +#define SER_RS485_RTS_ON_SEND (1 << 1) +#define SER_RS485_RTS_AFTER_SEND (1 << 2) + __u32 delay_rts_before_send; /* Milliseconds */ + __u32 padding[6]; /* Memory is cheap, new structs + are a royal PITA .. */ +}; #ifdef __KERNEL__ #include <linux/compiler.h> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3b2f6c04855..e27f216361f 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -241,7 +241,7 @@ typedef unsigned int __bitwise__ upf_t; struct uart_port { spinlock_t lock; /* port lock */ - unsigned int iobase; /* in/out[bwl] */ + unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ diff --git a/include/linux/termios.h b/include/linux/termios.h index 478662889f4..2acd0c1f8a2 100644 --- a/include/linux/termios.h +++ b/include/linux/termios.h @@ -4,4 +4,19 @@ #include <linux/types.h> #include <asm/termios.h> +#define NFF 5 + +struct termiox +{ + __u16 x_hflag; + __u16 x_cflag; + __u16 x_rflag[NFF]; + __u16 x_sflag; +}; + +#define RTSXOFF 0x0001 /* RTS flow control on input */ +#define CTSXON 0x0002 /* CTS flow control on output */ +#define DTRXOFF 0x0004 /* DTR flow control on input */ +#define DSRXON 0x0008 /* DCD flow control on output */ + #endif diff --git a/include/linux/tty.h b/include/linux/tty.h index 0cbec74ec08..3b8121d4e36 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -23,7 +23,7 @@ */ #define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ #define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ -#define NR_LDISCS 18 +#define NR_LDISCS 19 /* line disciplines */ #define N_TTY 0 @@ -45,6 +45,7 @@ #define N_HCI 15 /* Bluetooth HCI UART */ #define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */ #define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */ +#define N_PPS 18 /* Pulse per Second */ /* * This character is the same as _POSIX_VDISABLE: it cannot be used as @@ -181,6 +182,7 @@ struct signal_struct; struct tty_port { struct tty_struct *tty; /* Back pointer */ + spinlock_t lock; /* Lock protecting tty field */ int blocked_open; /* Waiting to open */ int count; /* Usage count */ wait_queue_head_t open_wait; /* Open waiters */ @@ -208,6 +210,7 @@ struct tty_operations; struct tty_struct { int magic; + struct kref kref; struct tty_driver *driver; const struct tty_operations *ops; int index; @@ -217,6 +220,7 @@ struct tty_struct { spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ struct ktermios *termios, *termios_locked; + struct termiox *termiox; /* May be NULL for unsupported */ char name[64]; struct pid *pgrp; /* Protected by ctrl lock */ struct pid *session; @@ -310,6 +314,25 @@ extern int kmsg_redirect; extern void console_init(void); extern int vcs_init(void); +extern struct class *tty_class; + +/** + * tty_kref_get - get a tty reference + * @tty: tty device + * + * Return a new reference to a tty object. The caller must hold + * sufficient locks/counts to ensure that their existing reference cannot + * go away + */ + +extern inline struct tty_struct *tty_kref_get(struct tty_struct *tty) +{ + if (tty) + kref_get(&tty->kref); + return tty; +} +extern void tty_kref_put(struct tty_struct *tty); + extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); extern char *tty_name(struct tty_struct *tty, char *buf); @@ -333,13 +356,15 @@ extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize *ws); - +extern void tty_shutdown(struct tty_struct *tty); +extern void tty_free_termios(struct tty_struct *tty); extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); extern void tty_hangup(struct tty_struct *tty); extern void tty_vhangup(struct tty_struct *tty); +extern void tty_vhangup_self(void); extern void tty_unhangup(struct file *filp); extern int tty_hung_up_p(struct file *filp); extern void do_SAK(struct tty_struct *tty); @@ -347,6 +372,9 @@ extern void __do_SAK(struct tty_struct *tty); extern void disassociate_ctty(int priv); extern void no_tty(void); extern void tty_flip_buffer_push(struct tty_struct *tty); +extern void tty_buffer_free_all(struct tty_struct *tty); +extern void tty_buffer_flush(struct tty_struct *tty); +extern void tty_buffer_init(struct tty_struct *tty); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); @@ -372,6 +400,15 @@ extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg); extern dev_t tty_devnum(struct tty_struct *tty); extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); +extern void tty_default_fops(struct file_operations *fops); +extern struct tty_struct *alloc_tty_struct(void); +extern void free_tty_struct(struct tty_struct *tty); +extern void initialize_tty_struct(struct tty_struct *tty, + struct tty_driver *driver, int idx); +extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok); +extern void tty_release_dev(struct file *filp); +extern int tty_init_termios(struct tty_struct *tty); extern struct mutex tty_mutex; @@ -382,6 +419,8 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); extern void tty_port_init(struct tty_port *port); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern struct tty_struct *tty_port_tty_get(struct tty_port *port); +extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); @@ -427,7 +466,7 @@ static inline void tty_audit_push_task(struct task_struct *tsk, #endif /* tty_ioctl.c */ -extern int n_tty_ioctl(struct tty_struct *tty, struct file *file, +extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); /* serial.c */ diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 16d27944c32..78416b90158 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -7,6 +7,28 @@ * defined; unless noted otherwise, they are optional, and can be * filled in with a null pointer. * + * struct tty_struct * (*lookup)(struct tty_driver *self, int idx) + * + * Return the tty device corresponding to idx, NULL if there is not + * one currently in use and an ERR_PTR value on error. Called under + * tty_mutex (for now!) + * + * Optional method. Default behaviour is to use the ttys array + * + * int (*install)(struct tty_driver *self, struct tty_struct *tty) + * + * Install a new tty into the tty driver internal tables. Used in + * conjunction with lookup and remove methods. + * + * Optional method. Default behaviour is to use the ttys array + * + * void (*remove)(struct tty_driver *self, struct tty_struct *tty) + * + * Remove a closed tty from the tty driver internal tables. Used in + * conjunction with lookup and remove methods. + * + * Optional method. Default behaviour is to use the ttys array + * * int (*open)(struct tty_struct * tty, struct file * filp); * * This routine is called when a particular tty device is opened. @@ -21,6 +43,11 @@ * * Required method. * + * void (*shutdown)(struct tty_struct * tty); + * + * This routine is called when a particular tty device is closed for + * the last time freeing up the resources. + * * int (*write)(struct tty_struct * tty, * const unsigned char *buf, int count); * @@ -180,6 +207,14 @@ * not force errors here if they are not resizable objects (eg a serial * line). See tty_do_resize() if you need to wrap the standard method * in your own logic - the usual case. + * + * void (*set_termiox)(struct tty_struct *tty, struct termiox *new); + * + * Called when the device receives a termiox based ioctl. Passes down + * the requested data from user space. This method will not be invoked + * unless the tty also has a valid tty->termiox pointer. + * + * Optional: Called under the termios lock */ #include <linux/fs.h> @@ -190,8 +225,13 @@ struct tty_struct; struct tty_driver; struct tty_operations { + struct tty_struct * (*lookup)(struct tty_driver *driver, + struct inode *inode, int idx); + int (*install)(struct tty_driver *driver, struct tty_struct *tty); + void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); + void (*shutdown)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count); int (*put_char)(struct tty_struct *tty, unsigned char ch); @@ -220,6 +260,7 @@ struct tty_operations { unsigned int set, unsigned int clear); int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize *ws); + int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); @@ -229,6 +270,7 @@ struct tty_operations { struct tty_driver { int magic; /* magic number for this structure */ + struct kref kref; /* Reference management */ struct cdev cdev; struct module *owner; const char *driver_name; @@ -242,7 +284,6 @@ struct tty_driver { short subtype; /* subtype of tty driver */ struct ktermios init_termios; /* Initial termios */ int flags; /* tty driver flags */ - int refcount; /* for loadable tty drivers */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ @@ -264,12 +305,19 @@ struct tty_driver { extern struct list_head tty_drivers; -struct tty_driver *alloc_tty_driver(int lines); -void put_tty_driver(struct tty_driver *driver); -void tty_set_operations(struct tty_driver *driver, +extern struct tty_driver *alloc_tty_driver(int lines); +extern void put_tty_driver(struct tty_driver *driver); +extern void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op); extern struct tty_driver *tty_find_polling_driver(char *name, int *line); +extern void tty_driver_kref_put(struct tty_driver *driver); +extern inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) +{ + kref_get(&d->kref); + return d; +} + /* tty driver magic number */ #define TTY_DRIVER_MAGIC 0x5402 diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 1cbd0a7db4e..2f1113467f7 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -96,7 +96,7 @@ void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); extern int unbind_con_driver(const struct consw *csw, int first, int last, int deflt); -int vty_init(void); +int vty_init(const struct file_operations *console_fops); /* * vc_screen.c shares this temporary buffer with the console write code so that diff --git a/kernel/acct.c b/kernel/acct.c index dd68b905941..f6006a60df5 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -548,7 +548,7 @@ static void do_acct_process(struct bsd_acct_struct *acct, #endif spin_lock_irq(¤t->sighand->siglock); - tty = current->signal->tty; + tty = current->signal->tty; /* Safe as we hold the siglock */ ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime))); ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime))); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 59cedfb040e..cf5bc2f5f9c 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -246,8 +246,8 @@ static int audit_match_perm(struct audit_context *ctx, int mask) unsigned n; if (unlikely(!ctx)) return 0; - n = ctx->major; + switch (audit_classify_syscall(ctx->arch, n)) { case 0: /* native */ if ((mask & AUDIT_PERM_WRITE) && @@ -1204,13 +1204,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code); - mutex_lock(&tty_mutex); - read_lock(&tasklist_lock); + spin_lock_irq(&tsk->sighand->siglock); if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) tty = tsk->signal->tty->name; else tty = "(none)"; - read_unlock(&tasklist_lock); + spin_unlock_irq(&tsk->sighand->siglock); + audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" " ppid=%d pid=%d auid=%u uid=%u gid=%u" @@ -1230,7 +1230,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->egid, context->sgid, context->fsgid, tty, tsk->sessionid); - mutex_unlock(&tty_mutex); audit_log_task_info(ab, tsk); if (context->filterkey) { diff --git a/kernel/fork.c b/kernel/fork.c index 7ce2ebe8479..30de644a40c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -802,6 +802,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->leader = 0; /* session leadership doesn't inherit */ sig->tty_old_pgrp = NULL; + sig->tty = NULL; sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero; sig->gtime = cputime_zero; @@ -838,6 +839,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) void __cleanup_signal(struct signal_struct *sig) { exit_thread_group_keys(sig); + tty_kref_put(sig->tty); kmem_cache_free(signal_cachep, sig); } @@ -1227,7 +1229,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->nsproxy->pid_ns->child_reaper = p; p->signal->leader_pid = pid; - p->signal->tty = current->signal->tty; + tty_kref_put(p->signal->tty); + p->signal->tty = tty_kref_get(current->signal->tty); set_task_pgrp(p, task_pgrp_nr(current)); set_task_session(p, task_session_nr(current)); attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); diff --git a/kernel/printk.c b/kernel/printk.c index b51b1567bb5..a430fd04008 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1291,22 +1291,6 @@ static int __init disable_boot_consoles(void) } late_initcall(disable_boot_consoles); -/** - * tty_write_message - write a message to a certain tty, not just the console. - * @tty: the destination tty_struct - * @msg: the message to write - * - * This is used for messages that need to be redirected to a specific tty. - * We don't put it into the syslog queue right now maybe in the future if - * really needed. - */ -void tty_write_message(struct tty_struct *tty, char *msg) -{ - if (tty && tty->ops->write) - tty->ops->write(tty, msg, strlen(msg)); - return; -} - #if defined CONFIG_PRINTK /* diff --git a/kernel/sys.c b/kernel/sys.c index 038a7bc0901..234d9454294 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1060,9 +1060,7 @@ asmlinkage long sys_setsid(void) group_leader->signal->leader = 1; __set_special_pids(sid); - spin_lock(&group_leader->sighand->siglock); - group_leader->signal->tty = NULL; - spin_unlock(&group_leader->sighand->siglock); + proc_clear_tty(group_leader); err = session; out: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4a7374c12d9..48881394fbd 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2121,7 +2121,6 @@ static inline void flush_unauthorized_files(struct files_struct *files) long j = -1; int drop_tty = 0; - mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { file_list_lock(); @@ -2139,8 +2138,8 @@ static inline void flush_unauthorized_files(struct files_struct *files) } } file_list_unlock(); + tty_kref_put(tty); } - mutex_unlock(&tty_mutex); /* Reset controlling tty. */ if (drop_tty) no_tty(); |