diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-03 11:38:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-03 11:38:36 -0700 |
commit | 2f01ea908bcf838e815c0124b579513dbda3b8c8 (patch) | |
tree | f42db47c1695daaf369f08a21b2267d4a8ece756 /drivers/tty | |
parent | 751144271f4b63d5de9005ea4e5e6e5c7c6fd629 (diff) | |
parent | 2d1d3f3ae985ec5676fb56ff2c7acad2e1c4e6eb (diff) |
Merge tag 'tty-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver patches from Greg KH:
"Here's the big tty/serial driver pull request for 3.12-rc1.
Lots of n_tty reworks to resolve some very long-standing issues,
removing the 3-4 different locks that were taken for every character.
This code has been beaten on for a long time in linux-next with no
reported regressions.
Other than that, a range of serial and tty driver updates and
revisions. Full details in the shortlog"
* tag 'tty-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (226 commits)
hvc_xen: Remove unnecessary __GFP_ZERO from kzalloc
serial: imx: initialize the local variable
tty: ar933x_uart: add device tree support and binding documentation
tty: ar933x_uart: allow to build the driver as a module
ARM: dts: msm: Update uartdm compatible strings
devicetree: serial: Document msm_serial bindings
serial: unify serial bindings into a single dir
serial: fsl-imx-uart: Cleanup duplicate device tree binding
tty: ar933x_uart: use config_enabled() macro to clean up ifdefs
tty: ar933x_uart: remove superfluous assignment of ar933x_uart_driver.nr
tty: ar933x_uart: use the clk API to get the uart clock
tty: serial: cpm_uart: Adding proper request of GPIO used by cpm_uart driver
serial: sirf: fix the amount of serial ports
serial: sirf: define macro for some magic numbers of USP
serial: icom: move array overflow checks earlier
TTY: amiserial, remove unnecessary platform_set_drvdata()
serial: st-asc: remove unnecessary platform_set_drvdata()
msm_serial: Send more than 1 character on the console w/ UARTDM
msm_serial: Add support for non-GSBI UARTDM devices
msm_serial: Switch clock consumer strings and simplify code
...
Diffstat (limited to 'drivers/tty')
79 files changed, 6422 insertions, 2917 deletions
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 083710e0236..2b86f8e0fb5 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1785,8 +1785,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); - platform_set_drvdata(pdev, NULL); - return error; } diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index eb255e807c0..9eba119bcdd 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -361,7 +361,12 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) tty->driver_data = NULL; tty_port_put(&hp->port); printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); - } + } else + /* We are ready... raise DTR/RTS */ + if (C_BAUD(tty)) + if (hp->ops->dtr_rts) + hp->ops->dtr_rts(hp, 1); + /* Force wakeup of the polling thread */ hvc_kick(); @@ -393,6 +398,10 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) /* We are done with the tty pointer now. */ tty_port_tty_set(&hp->port, NULL); + if (C_HUPCL(tty)) + if (hp->ops->dtr_rts) + hp->ops->dtr_rts(hp, 0); + if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 674d23cb919..91310198082 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -75,6 +75,9 @@ struct hv_ops { /* tiocmget/set implementation */ int (*tiocmget)(struct hvc_struct *hp); int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); + + /* Callbacks to handle tty ports */ + void (*dtr_rts)(struct hvc_struct *hp, int raise); }; /* Register a vterm and a slot index for use as a console (console_init) */ diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index 9d47f50c275..fd17a9b804b 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -656,21 +656,64 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) } /** + * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS + * @hp: Pointer the HVC device (struct hvc_struct) + * @raise: Non-zero to raise or zero to lower DTR/RTS lines + * + * This routine notifies the HVC back-end to raise or lower DTR/RTS + * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to + * drop the IUCV connection (similar to hang up the modem). + */ +static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) +{ + struct hvc_iucv_private *priv; + struct iucv_path *path; + + /* Raising the DTR/RTS is ignored as IUCV connections can be + * established at any times. + */ + if (raise) + return; + + priv = hvc_iucv_get_private(hp->vtermno); + if (!priv) + return; + + /* Lowering the DTR/RTS lines disconnects an established IUCV + * connection. + */ + flush_sndbuf_sync(priv); + + spin_lock_bh(&priv->lock); + path = priv->path; /* save reference to IUCV path */ + priv->path = NULL; + priv->iucv_state = IUCV_DISCONN; + spin_unlock_bh(&priv->lock); + + /* Sever IUCV path outside of priv->lock due to lock ordering of: + * priv->lock <--> iucv_table_lock */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * * This routine notifies the HVC back-end that the last tty device fd has been - * closed. The function calls hvc_iucv_cleanup() to clean up the struct - * hvc_iucv_private instance. + * closed. The function cleans up tty resources. The clean-up of the IUCV + * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios + * control setting. * * Locking: struct hvc_iucv_private->lock */ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) { struct hvc_iucv_private *priv; - struct iucv_path *path; priv = hvc_iucv_get_private(id); if (!priv) @@ -679,17 +722,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) flush_sndbuf_sync(priv); spin_lock_bh(&priv->lock); - path = priv->path; /* save reference to IUCV path */ - priv->path = NULL; - hvc_iucv_cleanup(priv); + destroy_tty_buffer_list(&priv->tty_outqueue); + destroy_tty_buffer_list(&priv->tty_inqueue); + priv->tty_state = TTY_CLOSED; + priv->sndbuf_len = 0; spin_unlock_bh(&priv->lock); - - /* sever IUCV path outside of priv->lock due to lock ordering of: - * priv->lock <--> iucv_table_lock */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } } /** @@ -931,6 +968,7 @@ static const struct hv_ops hvc_iucv_ops = { .notifier_add = hvc_iucv_notifier_add, .notifier_del = hvc_iucv_notifier_del, .notifier_hangup = hvc_iucv_notifier_hangup, + .dtr_rts = hvc_iucv_dtr_rts, }; /* Suspend / resume device operations */ diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 682210d778b..e61c36cbb86 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -208,7 +208,7 @@ static int xen_hvm_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } else if (info->intf != NULL) { @@ -257,7 +257,7 @@ static int xen_pv_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } else if (info->intf != NULL) { @@ -284,7 +284,7 @@ static int xen_initial_domain_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 642239015b4..c0f76da5530 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -807,7 +807,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) int h = dlci->adaption - 1; total_size = 0; - while(1) { + while (1) { len = kfifo_len(dlci->fifo); if (len == 0) return total_size; @@ -827,8 +827,8 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) switch (dlci->adaption) { case 1: /* Unstructured */ break; - case 2: /* Unstructed with modem bits. Always one byte as we never - send inline break data */ + case 2: /* Unstructed with modem bits. + Always one byte as we never send inline break data */ *dp++ = gsm_encode_modem(dlci); break; } @@ -968,7 +968,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) unsigned long flags; int sweep; - if (dlci->constipated) + if (dlci->constipated) return; spin_lock_irqsave(&dlci->gsm->tx_lock, flags); @@ -981,7 +981,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) gsm_dlci_data_output(dlci->gsm, dlci); } if (sweep) - gsm_dlci_data_sweep(dlci->gsm); + gsm_dlci_data_sweep(dlci->gsm); spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); } @@ -1138,7 +1138,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) { struct tty_port *port; - unsigned int addr = 0 ; + unsigned int addr = 0; u8 bits; int len = clen; u8 *dp = data; @@ -1740,10 +1740,11 @@ static void gsm_queue(struct gsm_mux *gsm) if ((gsm->control & ~PF) == UI) gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - if (gsm->encoding == 0){ - /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. - In this case it contain the last piece of data - required to generate final CRC */ + if (gsm->encoding == 0) { + /* WARNING: gsm->received_fcs is used for + gsm->encoding = 0 only. + In this case it contain the last piece of data + required to generate final CRC */ gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); } if (gsm->fcs != GOOD_FCS) { @@ -2904,9 +2905,11 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) gsm = gsm_mux[mux]; if (gsm->dead) return -EL2HLT; - /* If DLCI 0 is not yet fully open return an error. This is ok from a locking - perspective as we don't have to worry about this if DLCI0 is lost */ - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) + /* If DLCI 0 is not yet fully open return an error. + This is ok from a locking + perspective as we don't have to worry about this + if DLCI0 is lost */ + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) return -EL2NSYNC; dlci = gsm->dlci[line]; if (dlci == NULL) { diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4bf0fc0843d..c9a9ddd1d0b 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -50,6 +50,7 @@ #include <linux/uaccess.h> #include <linux/module.h> #include <linux/ratelimit.h> +#include <linux/vmalloc.h> /* number of characters left in xmit buffer before select has we have room */ @@ -74,37 +75,81 @@ #define ECHO_OP_SET_CANON_COL 0x81 #define ECHO_OP_ERASE_TAB 0x82 +#define ECHO_COMMIT_WATERMARK 256 +#define ECHO_BLOCK 256 +#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32) + + +#undef N_TTY_TRACE +#ifdef N_TTY_TRACE +# define n_tty_trace(f, args...) trace_printk(f, ##args) +#else +# define n_tty_trace(f, args...) +#endif + struct n_tty_data { - unsigned int column; + /* producer-published */ + size_t read_head; + size_t canon_head; + size_t echo_head; + size_t echo_commit; + DECLARE_BITMAP(char_map, 256); + + /* private to n_tty_receive_overrun (single-threaded) */ unsigned long overrun_time; int num_overrun; + /* non-atomic */ + bool no_room; + + /* must hold exclusive termios_rwsem to reset these */ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; - unsigned char echo_overrun:1; - DECLARE_BITMAP(process_char_map, 256); + /* shared by producer and consumer */ + char read_buf[N_TTY_BUF_SIZE]; DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + unsigned char echo_buf[N_TTY_BUF_SIZE]; - char *read_buf; - int read_head; - int read_tail; - int read_cnt; int minimum_to_wake; - unsigned char *echo_buf; - unsigned int echo_pos; - unsigned int echo_cnt; + /* consumer-published */ + size_t read_tail; + size_t line_start; - int canon_data; - unsigned long canon_head; + /* protected by output lock */ + unsigned int column; unsigned int canon_column; + size_t echo_tail; struct mutex atomic_read_lock; struct mutex output_lock; - struct mutex echo_lock; - raw_spinlock_t read_lock; }; +static inline size_t read_cnt(struct n_tty_data *ldata) +{ + return ldata->read_head - ldata->read_tail; +} + +static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { @@ -114,33 +159,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set_room - receive space - * @tty: terminal - * - * Updates tty->receive_room to reflect the currently available space - * in the input buffer, and re-schedules the flip buffer work if space - * just became available. - * - * Locks: Concurrent update is protected with read_lock - */ - -static int set_room(struct tty_struct *tty) +static int receive_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; - int old_left; - unsigned long flags; - - raw_spin_lock_irqsave(&ldata->read_lock, flags); if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on * its flags, e.g. parity error). */ - left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1; + left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1; } else - left = N_TTY_BUF_SIZE |