Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54b926a1 authored by Gianluca Anzolin's avatar Gianluca Anzolin Committed by Gustavo Padovan
Browse files

Bluetooth: Move the tty initialization and cleanup out of open/close



Move the tty_struct initialization from rfcomm_tty_open() to
rfcomm_tty_install() and do the same for the cleanup moving the code from
rfcomm_tty_close() to rfcomm_tty_cleanup().

Add also extra error handling in rfcomm_tty_install() because, unlike
.open()/.close(), .cleanup() is not called if .install() fails.

Signed-off-by: default avatarGianluca Anzolin <gianluca@sottospazio.it>
Reviewed-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGustavo Padovan <gustavo.padovan@collabora.co.uk>
parent ebe937f7
Loading
Loading
Loading
Loading
+72 −42
Original line number Diff line number Diff line
@@ -633,49 +633,61 @@ static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev)
		tty_flip_buffer_push(&dev->port);
}

static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
/* do the reverse of install, clearing the tty fields and releasing the
 * reference to tty_port
 */
static void rfcomm_tty_cleanup(struct tty_struct *tty)
{
	DECLARE_WAITQUEUE(wait, current);
	struct rfcomm_dev *dev;
	struct rfcomm_dlc *dlc;
	unsigned long flags;
	int err, id;
	struct rfcomm_dev *dev = tty->driver_data;

	id = tty->index;
	if (dev->tty_dev->parent)
		device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);

	BT_DBG("tty %p id %d", tty, id);
	/* Close DLC and dettach TTY */
	rfcomm_dlc_close(dev->dlc, 0);

	/* We don't leak this refcount. For reasons which are not entirely
	   clear, the TTY layer will call our ->close() method even if the
	   open fails. We decrease the refcount there, and decreasing it
	   here too would cause breakage. */
	dev = rfcomm_dev_get(id);
	if (!dev)
		return -ENODEV;
	clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);

	BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
	       dev->channel, dev->port.count);
	rfcomm_dlc_lock(dev->dlc);
	tty->driver_data = NULL;
	dev->port.tty = NULL;
	rfcomm_dlc_unlock(dev->dlc);

	spin_lock_irqsave(&dev->port.lock, flags);
	if (++dev->port.count > 1) {
		spin_unlock_irqrestore(&dev->port.lock, flags);
		return 0;
	tty_port_put(&dev->port);
}
	spin_unlock_irqrestore(&dev->port.lock, flags);

/* we acquire the tty_port reference since it's here the tty is first used
 * by setting the termios. We also populate the driver_data field and install
 * the tty port
 */
static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
{
	DECLARE_WAITQUEUE(wait, current);
	struct rfcomm_dev *dev;
	struct rfcomm_dlc *dlc;
	int err;

	dev = rfcomm_dev_get(tty->index);
	if (!dev)
		return -ENODEV;

	dlc = dev->dlc;

	/* Attach TTY and open DLC */

	rfcomm_dlc_lock(dlc);
	tty->driver_data = dev;
	dev->port.tty = tty;
	rfcomm_dlc_unlock(dlc);
	set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);

	/* install the tty_port */
	err = tty_port_install(&dev->port, driver, tty);
	if (err < 0)
		goto error_no_dlc;

	err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
	if (err < 0)
		return err;
		goto error_no_dlc;

	/* Wait for DLC to connect */
	add_wait_queue(&dev->wait, &wait);
@@ -702,15 +714,45 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&dev->wait, &wait);

	if (err == 0)
	if (err < 0)
		goto error_no_connection;

	device_move(dev->tty_dev, rfcomm_get_device(dev),
		    DPM_ORDER_DEV_AFTER_PARENT);
	return 0;

error_no_connection:
	rfcomm_dlc_close(dlc, err);
error_no_dlc:
	clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
	tty_port_put(&dev->port);
	return err;
}

static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
{
	struct rfcomm_dev *dev = tty->driver_data;
	unsigned long flags;

	BT_DBG("tty %p id %d", tty, tty->index);

	BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
	       dev->channel, dev->port.count);

	spin_lock_irqsave(&dev->port.lock, flags);
	dev->port.count++;
	spin_unlock_irqrestore(&dev->port.lock, flags);

	/*
	 * FIXME: rfcomm should use proper flow control for
	 * received data. This hack will be unnecessary and can
	 * be removed when that's implemented
	 */
	rfcomm_tty_copy_pending(dev);

	rfcomm_dlc_unthrottle(dev->dlc);

	return err;
	return 0;
}

static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
@@ -727,25 +769,11 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
	spin_lock_irqsave(&dev->port.lock, flags);
	if (!--dev->port.count) {
		spin_unlock_irqrestore(&dev->port.lock, flags);
		if (dev->tty_dev->parent)
			device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);

		/* Close DLC and dettach TTY */
		rfcomm_dlc_close(dev->dlc, 0);

		clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);

		rfcomm_dlc_lock(dev->dlc);
		tty->driver_data = NULL;
		dev->port.tty = NULL;
		rfcomm_dlc_unlock(dev->dlc);

		if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
			tty_port_put(&dev->port);
	} else
		spin_unlock_irqrestore(&dev->port.lock, flags);

	tty_port_put(&dev->port);
}

static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
@@ -1118,6 +1146,8 @@ static const struct tty_operations rfcomm_ops = {
	.wait_until_sent	= rfcomm_tty_wait_until_sent,
	.tiocmget		= rfcomm_tty_tiocmget,
	.tiocmset		= rfcomm_tty_tiocmset,
	.install                = rfcomm_tty_install,
	.cleanup                = rfcomm_tty_cleanup,
};

int __init rfcomm_init_ttys(void)