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

Commit fa1da242 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman
Browse files

staging/fwserial: Create loop device the 'tty' way



Register a second tty driver to create loopback devices for
each firewire node. Note that the loopback devices are numbered
from 0; the tty->index is transformed when used to index the
port table.

Remove the hack that previously enabled this.

Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a3d9ad47
Loading
Loading
Loading
Loading
+0 −13
Original line number Diff line number Diff line
@@ -12,16 +12,3 @@ TODOs prior to this driver moving out of staging
1. This driver uses the same unregistered vendor id that the firewire core does
     (0xd00d1e). Perhaps this could be exposed as a define in
     firewire.h?

-- Issues with TTY core --
  1. Hack for alternate device name scheme
     - because udev no longer allows device renaming, devices should have
       their proper names on creation. This is an issue for creating the
       fwloop<n> device with the fwtty<n> devices because although duplicating
       roughly the same operations as tty_port_register_device() isn't difficult,
       access to the tty_class & tty_fops is restricted in scope.

       This is currently being worked around in create_loop_device() by
       extracting the tty_class ptr and tty_fops ptr from the previously created
       tty devices. Perhaps an add'l api can be added -- eg.,
       tty_{port_}register_named_device().
+93 −83
Original line number Diff line number Diff line
@@ -72,6 +72,9 @@ static DEFINE_MUTEX(port_table_lock);
static bool port_table_corrupt;
#define FWTTY_INVALID_INDEX  MAX_TOTAL_PORTS

#define loop_idx(port)	(((port)->index) / num_ports)
#define table_idx(loop)	((loop) * num_ports + num_ttys)

/* total # of tty ports created per fw_card */
static int num_ports;

@@ -79,6 +82,7 @@ static int num_ports;
static struct kmem_cache *fwtty_txn_cache;

struct tty_driver *fwtty_driver;
static struct tty_driver *fwloop_driver;

struct fwtty_transaction;
typedef void (*fwtty_transaction_cb)(struct fw_card *card, int rcode,
@@ -1165,6 +1169,19 @@ static int fwtty_install(struct tty_driver *driver, struct tty_struct *tty)
	return err;
}

static int fwloop_install(struct tty_driver *driver, struct tty_struct *tty)
{
	struct fwtty_port *port = fwtty_port_get(table_idx(tty->index));
	int err;

	err = tty_standard_install(driver, tty);
	if (!err)
		tty->driver_data = port;
	else
		fwtty_port_put(port);
	return err;
}

static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{
	struct fwtty_port *port = tty->driver_data;
@@ -1601,6 +1618,26 @@ static const struct tty_operations fwtty_ops = {
	.proc_fops =		&fwtty_proc_fops,
};

static const struct tty_operations fwloop_ops = {
	.open =			fwtty_open,
	.close =		fwtty_close,
	.hangup =		fwtty_hangup,
	.cleanup =		fwtty_cleanup,
	.install =		fwloop_install,
	.write =		fwtty_write,
	.write_room =		fwtty_write_room,
	.chars_in_buffer =	fwtty_chars_in_buffer,
	.send_xchar =           fwtty_send_xchar,
	.throttle =             fwtty_throttle,
	.unthrottle =           fwtty_unthrottle,
	.ioctl =		fwtty_ioctl,
	.set_termios =		fwtty_set_termios,
	.break_ctl =		fwtty_break_ctl,
	.tiocmget =		fwtty_tiocmget,
	.tiocmset =		fwtty_tiocmset,
	.get_icount =		fwtty_get_icount,
};

static inline int mgmt_pkt_expected_len(__be16 code)
{
	static const struct fwserial_mgmt_pkt pkt;
@@ -1897,7 +1934,8 @@ free_pkt:
 * The port reference is put by fwtty_cleanup (if a reference was
 * ever taken).
 */
static void fwserial_close_port(struct fwtty_port *port)
static void fwserial_close_port(struct tty_driver *driver,
				struct fwtty_port *port)
{
	struct tty_struct *tty;

@@ -1909,7 +1947,10 @@ static void fwserial_close_port(struct fwtty_port *port)
	}
	mutex_unlock(&port->port.mutex);

	tty_unregister_device(fwtty_driver, port->index);
	if (driver == fwloop_driver)
		tty_unregister_device(driver, loop_idx(port));
	else
		tty_unregister_device(driver, port->index);
}

/**
@@ -2166,78 +2207,6 @@ static void fwserial_remove_peer(struct fwtty_peer *peer)
	kfree(peer);
}

/**
 * create_loop_device - create a loopback tty device
 * @tty_driver: tty_driver to own loopback device
 * @prototype: ptr to already-assigned 'prototype' tty port
 * @index: index to associate this device with the tty port
 * @parent: device to child to
 *
 * HACK - this is basically tty_port_register_device() with an
 * alternate naming scheme. Suggest tty_port_register_named_device()
 * helper api.
 *
 * Creates a loopback tty device named 'fwloop<n>' which is attached to
 * the local unit in fwserial_add_peer(). Note that <n> in the device
 * name advances in increments of port allocation blocks, ie., for port
 * indices 0..3, the device name will be 'fwloop0'; for 4..7, 'fwloop1',
 * and so on.
 *
 * Only one loopback device should be created per fw_card.
 */
static void release_loop_device(struct device *dev)
{
	kfree(dev);
}

static struct device *create_loop_device(struct tty_driver *driver,
					 struct fwtty_port *prototype,
					 struct fwtty_port *port,
					 struct device *parent)
{
	char name[64];
	int index = port->index;
	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
	struct device *dev = NULL;
	int err;

	if (index >= fwtty_driver->num)
		return ERR_PTR(-EINVAL);

	snprintf(name, 64, "%s%d", loop_dev_name, index / num_ports);

	tty_port_link_device(&port->port, driver, index);

	cdev_init(&driver->cdevs[index], driver->cdevs[prototype->index].ops);
	driver->cdevs[index].owner = driver->owner;
	err = cdev_add(&driver->cdevs[index], devt, 1);
	if (err)
		return ERR_PTR(err);

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		cdev_del(&driver->cdevs[index]);
		return ERR_PTR(-ENOMEM);
	}

	dev->devt = devt;
	dev->class = prototype->device->class;
	dev->parent = parent;
	dev->release = release_loop_device;
	dev_set_name(dev, "%s", name);
	dev->groups = NULL;
	dev_set_drvdata(dev, NULL);

	err = device_register(dev);
	if (err) {
		put_device(dev);
		cdev_del(&driver->cdevs[index]);
		return ERR_PTR(err);
	}

	return dev;
}

/**
 * fwserial_create - init everything to create TTYs for a specific fw_card
 * @unit: fw_unit for first 'serial' unit device probed for this fw_card
@@ -2336,17 +2305,17 @@ static int fwserial_create(struct fw_unit *unit)
	if (create_loop_dev) {
		struct device *loop_dev;

		loop_dev = create_loop_device(fwtty_driver,
					      serial->ports[0],
					      serial->ports[num_ttys],
		loop_dev = tty_port_register_device(&serial->ports[j]->port,
						    fwloop_driver,
						    loop_idx(serial->ports[j]),
						    card->device);
		if (IS_ERR(loop_dev)) {
			err = PTR_ERR(loop_dev);
			fwtty_err(&unit, "create loop device failed (%d)", err);
			goto unregister_ttys;
		}
		serial->ports[num_ttys]->device = loop_dev;
		serial->ports[num_ttys]->loopback = true;
		serial->ports[j]->device = loop_dev;
		serial->ports[j]->loopback = true;
	}

	list_add_rcu(&serial->list, &fwserial_list);
@@ -2362,6 +2331,8 @@ static int fwserial_create(struct fw_unit *unit)

	/* fall-through to error processing */
	list_del_rcu(&serial->list);
	if (create_loop_dev)
		tty_unregister_device(fwloop_driver, loop_idx(serial->ports[j]));
unregister_ttys:
	for (--j; j >= 0; --j)
		tty_unregister_device(fwtty_driver, serial->ports[j]->index);
@@ -2450,8 +2421,10 @@ static int fwserial_remove(struct device *dev)
		/* unlink from the fwserial_list here */
		list_del_rcu(&serial->list);

		for (i = 0; i < num_ports; ++i)
			fwserial_close_port(serial->ports[i]);
		for (i = 0; i < num_ttys; ++i)
			fwserial_close_port(fwtty_driver, serial->ports[i]);
		if (create_loop_dev)
			fwserial_close_port(fwloop_driver, serial->ports[i]);
		kref_put(&serial->kref, fwserial_destroy);
	}
	mutex_unlock(&fwserial_list_mutex);
@@ -2872,12 +2845,39 @@ static int __init fwserial_init(void)
		goto put_tty;
	}

	if (create_loop_dev) {
		fwloop_driver = alloc_tty_driver(MAX_TOTAL_PORTS / num_ports);
		if (!fwloop_driver) {
			err = -ENOMEM;
			goto unregister_driver;
		}

		fwloop_driver->driver_name	= KBUILD_MODNAME "_loop";
		fwloop_driver->name		= loop_dev_name;
		fwloop_driver->major		= 0;
		fwloop_driver->minor_start	= 0;
		fwloop_driver->type		= TTY_DRIVER_TYPE_SERIAL;
		fwloop_driver->subtype		= SERIAL_TYPE_NORMAL;
		fwloop_driver->flags		= TTY_DRIVER_REAL_RAW |
							TTY_DRIVER_DYNAMIC_DEV;

		fwloop_driver->init_termios	    = tty_std_termios;
		fwloop_driver->init_termios.c_cflag  |= CLOCAL;
		tty_set_operations(fwloop_driver, &fwloop_ops);

		err = tty_register_driver(fwloop_driver);
		if (err) {
			driver_err("register loop driver failed (%d)", err);
			goto put_loop;
		}
	}

	fwtty_txn_cache = kmem_cache_create("fwtty_txn_cache",
					    sizeof(struct fwtty_transaction),
					    0, 0, fwtty_txn_constructor);
	if (!fwtty_txn_cache) {
		err = -ENOMEM;
		goto unregister_driver;
		goto unregister_loop;
	}

	/*
@@ -2919,6 +2919,12 @@ remove_handler:
	fw_core_remove_address_handler(&fwserial_mgmt_addr_handler);
destroy_cache:
	kmem_cache_destroy(fwtty_txn_cache);
unregister_loop:
	if (create_loop_dev)
		tty_unregister_driver(fwloop_driver);
put_loop:
	if (create_loop_dev)
		put_tty_driver(fwloop_driver);
unregister_driver:
	tty_unregister_driver(fwtty_driver);
put_tty:
@@ -2932,6 +2938,10 @@ static void __exit fwserial_exit(void)
	fw_core_remove_descriptor(&fwserial_unit_directory);
	fw_core_remove_address_handler(&fwserial_mgmt_addr_handler);
	kmem_cache_destroy(fwtty_txn_cache);
	if (create_loop_dev) {
		tty_unregister_driver(fwloop_driver);
		put_tty_driver(fwloop_driver);
	}
	tty_unregister_driver(fwtty_driver);
	put_tty_driver(fwtty_driver);
}