From fa1da242c3d24801ae946f4664406f679865388f Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 28 Jan 2013 22:34:38 -0500 Subject: [PATCH 10/22] 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 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fwserial/TODO | 13 --- drivers/staging/fwserial/fwserial.c | 176 +++++++++++++++++++----------------- 2 files changed, 93 insertions(+), 96 deletions(-) diff --git a/drivers/staging/fwserial/TODO b/drivers/staging/fwserial/TODO index dc61d97..382a795 100644 --- a/drivers/staging/fwserial/TODO +++ b/drivers/staging/fwserial/TODO @@ -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 device with the fwtty 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(). diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index a9e814e..3f2d26e 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -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); } /** @@ -2167,78 +2208,6 @@ static void fwserial_remove_peer(struct fwtty_peer *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' which is attached to - * the local unit in fwserial_add_peer(). Note that 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], - card->device); + 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); } -- 1.7.12.4