Hi Gianluca,
> This patch fixes two regressions introduced with the recent rfcomm tty
> rework.
>
> The current code uses the carrier_raised() method to wait for the
> bluetooth connection when a process opens the tty.
>
> However processes may open the port with the O_NONBLOCK flag or set the
> CLOCAL termios flag: in these cases the open() syscall returns
> immediately without waiting for the bluetooth connection to
> complete.
>
> This behaviour confuses userspace which expects an established bluetooth
> connection.
>
> The patch restores the old behaviour by waiting for the connection in
> rfcomm_dev_activate() and removes carrier_raised() from the tty_port ops.
>
> As a side effect the new code also fixes the case in which the rfcomm
> tty device is created with the flag RFCOMM_REUSE_DLC: the old code
> didn't call device_move() and ModemManager skipped the detection
> probe. Now device_move() is always called inside rfcomm_dev_activate().
>
> Signed-off-by: Gianluca Anzolin <[email protected]>
> Reported-by: Andrey Vihrov <[email protected]>
> Reported-by: Beson Chow <[email protected]>
> ---
> net/bluetooth/rfcomm/tty.c | 43 +++++++++++++++++++++++++++++++++++--------
> 1 file changed, 35 insertions(+), 8 deletions(-)
>
> diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
> index 32ef9f9..65c0699 100644
> --- a/net/bluetooth/rfcomm/tty.c
> +++ b/net/bluetooth/rfcomm/tty.c
> @@ -58,6 +58,7 @@ struct rfcomm_dev {
> uint modem_status;
>
> struct rfcomm_dlc *dlc;
> + wait_queue_head_t conn_wait;
>
> struct device *tty_dev;
>
> @@ -123,8 +124,37 @@ static struct device *rfcomm_get_device(struct
> rfcomm_dev *dev)
> static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
> {
> struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
> + DEFINE_WAIT(wait);
> + int err;
> +
> + err = rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
> + if (err)
> + return err;
> +
> + while (1) {
> + prepare_to_wait(&dev->conn_wait, &wait, TASK_INTERRUPTIBLE);
> +
> + if (dev->dlc->state == BT_CLOSED) {
> + err = -dev->err;
> + break;
> + } else if (dev->dlc->state == BT_CONNECTED)
> + break;
> + else if (signal_pending(current)) {
> + err = -ERESTARTSYS;
> + break;
> + }
don’t do an if-else-else if statement here. Just break.
if (dev->dlc->state == BT_CLOSED) {
..
break;
}
if (dev->dlc->state == BT_CONNECTED)
break;
if (signal_pending(..)) {
..
break;
}
> +
> + tty_unlock(tty);
> + schedule();
> + tty_lock(tty);
> + }
> + finish_wait(&dev->conn_wait, &wait);
>
> - return rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
> + if (!err)
> + device_move(dev->tty_dev, rfcomm_get_device(dev),
> + DPM_ORDER_DEV_AFTER_PARENT);
> +
> + return err;
> }
>
> /* we block the open until the dlc->state becomes BT_CONNECTED */
> @@ -151,7 +181,6 @@ static const struct tty_port_operations rfcomm_port_ops =
> {
> .destruct = rfcomm_dev_destruct,
> .activate = rfcomm_dev_activate,
> .shutdown = rfcomm_dev_shutdown,
> - .carrier_raised = rfcomm_dev_carrier_raised,
> };
>
> static struct rfcomm_dev *__rfcomm_dev_get(int id)
> @@ -258,6 +287,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req,
> struct rfcomm_dlc *dlc)
>
> tty_port_init(&dev->port);
> dev->port.ops = &rfcomm_port_ops;
> + init_waitqueue_head(&dev->conn_wait);
>
> skb_queue_head_init(&dev->pending);
>
> @@ -576,12 +606,9 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc
> *dlc, int err)
> BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
>
> dev->err = err;
> - if (dlc->state == BT_CONNECTED) {
> - device_move(dev->tty_dev, rfcomm_get_device(dev),
> - DPM_ORDER_DEV_AFTER_PARENT);
> + wake_up_interruptible(&dev->conn_wait);
>
> - wake_up_interruptible(&dev->port.open_wait);
> - } else if (dlc->state == BT_CLOSED)
> + if (dlc->state == BT_CLOSED)
> tty_port_tty_hangup(&dev->port, false);
> }
>
> @@ -1103,7 +1130,7 @@ int __init rfcomm_init_ttys(void)
> rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL;
> rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW |
> TTY_DRIVER_DYNAMIC_DEV;
> rfcomm_tty_driver->init_termios = tty_std_termios;
> - rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
> + rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
> CLOCAL;
Is adding CLOCAL by default intentional?
> rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
> tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
Regards
Marcel
--
To unsubscribe from this list: send the line "unsubscribe stable" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html