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: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Peter Hurley 2013-01-28 22:34:38 -05:00 committed by Greg Kroah-Hartman
parent a3d9ad474e
commit fa1da242c3
2 changed files with 93 additions and 96 deletions

View File

@ -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 1. This driver uses the same unregistered vendor id that the firewire core does
(0xd00d1e). Perhaps this could be exposed as a define in (0xd00d1e). Perhaps this could be exposed as a define in
firewire.h? 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().

View File

@ -72,6 +72,9 @@ static DEFINE_MUTEX(port_table_lock);
static bool port_table_corrupt; static bool port_table_corrupt;
#define FWTTY_INVALID_INDEX MAX_TOTAL_PORTS #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 */ /* total # of tty ports created per fw_card */
static int num_ports; static int num_ports;
@ -79,6 +82,7 @@ static int num_ports;
static struct kmem_cache *fwtty_txn_cache; static struct kmem_cache *fwtty_txn_cache;
struct tty_driver *fwtty_driver; struct tty_driver *fwtty_driver;
static struct tty_driver *fwloop_driver;
struct fwtty_transaction; struct fwtty_transaction;
typedef void (*fwtty_transaction_cb)(struct fw_card *card, int rcode, 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; 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) static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c)
{ {
struct fwtty_port *port = tty->driver_data; struct fwtty_port *port = tty->driver_data;
@ -1601,6 +1618,26 @@ static const struct tty_operations fwtty_ops = {
.proc_fops = &fwtty_proc_fops, .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 inline int mgmt_pkt_expected_len(__be16 code)
{ {
static const struct fwserial_mgmt_pkt pkt; 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 * The port reference is put by fwtty_cleanup (if a reference was
* ever taken). * 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; struct tty_struct *tty;
@ -1909,7 +1947,10 @@ static void fwserial_close_port(struct fwtty_port *port)
} }
mutex_unlock(&port->port.mutex); 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); 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 * 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 * @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) { if (create_loop_dev) {
struct device *loop_dev; struct device *loop_dev;
loop_dev = create_loop_device(fwtty_driver, loop_dev = tty_port_register_device(&serial->ports[j]->port,
serial->ports[0], fwloop_driver,
serial->ports[num_ttys], loop_idx(serial->ports[j]),
card->device); card->device);
if (IS_ERR(loop_dev)) { if (IS_ERR(loop_dev)) {
err = PTR_ERR(loop_dev); err = PTR_ERR(loop_dev);
fwtty_err(&unit, "create loop device failed (%d)", err); fwtty_err(&unit, "create loop device failed (%d)", err);
goto unregister_ttys; goto unregister_ttys;
} }
serial->ports[num_ttys]->device = loop_dev; serial->ports[j]->device = loop_dev;
serial->ports[num_ttys]->loopback = true; serial->ports[j]->loopback = true;
} }
list_add_rcu(&serial->list, &fwserial_list); list_add_rcu(&serial->list, &fwserial_list);
@ -2362,6 +2331,8 @@ static int fwserial_create(struct fw_unit *unit)
/* fall-through to error processing */ /* fall-through to error processing */
list_del_rcu(&serial->list); list_del_rcu(&serial->list);
if (create_loop_dev)
tty_unregister_device(fwloop_driver, loop_idx(serial->ports[j]));
unregister_ttys: unregister_ttys:
for (--j; j >= 0; --j) for (--j; j >= 0; --j)
tty_unregister_device(fwtty_driver, serial->ports[j]->index); 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 */ /* unlink from the fwserial_list here */
list_del_rcu(&serial->list); list_del_rcu(&serial->list);
for (i = 0; i < num_ports; ++i) for (i = 0; i < num_ttys; ++i)
fwserial_close_port(serial->ports[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); kref_put(&serial->kref, fwserial_destroy);
} }
mutex_unlock(&fwserial_list_mutex); mutex_unlock(&fwserial_list_mutex);
@ -2872,12 +2845,39 @@ static int __init fwserial_init(void)
goto put_tty; 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", fwtty_txn_cache = kmem_cache_create("fwtty_txn_cache",
sizeof(struct fwtty_transaction), sizeof(struct fwtty_transaction),
0, 0, fwtty_txn_constructor); 0, 0, fwtty_txn_constructor);
if (!fwtty_txn_cache) { if (!fwtty_txn_cache) {
err = -ENOMEM; err = -ENOMEM;
goto unregister_driver; goto unregister_loop;
} }
/* /*
@ -2919,6 +2919,12 @@ remove_handler:
fw_core_remove_address_handler(&fwserial_mgmt_addr_handler); fw_core_remove_address_handler(&fwserial_mgmt_addr_handler);
destroy_cache: destroy_cache:
kmem_cache_destroy(fwtty_txn_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: unregister_driver:
tty_unregister_driver(fwtty_driver); tty_unregister_driver(fwtty_driver);
put_tty: put_tty:
@ -2932,6 +2938,10 @@ static void __exit fwserial_exit(void)
fw_core_remove_descriptor(&fwserial_unit_directory); fw_core_remove_descriptor(&fwserial_unit_directory);
fw_core_remove_address_handler(&fwserial_mgmt_addr_handler); fw_core_remove_address_handler(&fwserial_mgmt_addr_handler);
kmem_cache_destroy(fwtty_txn_cache); 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); tty_unregister_driver(fwtty_driver);
put_tty_driver(fwtty_driver); put_tty_driver(fwtty_driver);
} }