diff options
Diffstat (limited to 'drivers/ipack/devices/ipoctal.c')
| -rw-r--r-- | drivers/ipack/devices/ipoctal.c | 133 |
1 files changed, 80 insertions, 53 deletions
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 20fa02c81070..ba2e9e52d72b 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -33,6 +33,7 @@ struct ipoctal_channel { unsigned int pointer_read; unsigned int pointer_write; struct tty_port tty_port; + bool tty_registered; union scc2698_channel __iomem *regs; union scc2698_block __iomem *block_regs; unsigned int board_id; @@ -81,22 +82,34 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty) return 0; } -static int ipoctal_open(struct tty_struct *tty, struct file *file) +static int ipoctal_install(struct tty_driver *driver, struct tty_struct *tty) { struct ipoctal_channel *channel = dev_get_drvdata(tty->dev); struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index); - int err; - - tty->driver_data = channel; + int res; if (!ipack_get_carrier(ipoctal->dev)) return -EBUSY; - err = tty_port_open(&channel->tty_port, tty, file); - if (err) - ipack_put_carrier(ipoctal->dev); + res = tty_standard_install(driver, tty); + if (res) + goto err_put_carrier; + + tty->driver_data = channel; - return err; + return 0; + +err_put_carrier: + ipack_put_carrier(ipoctal->dev); + + return res; +} + +static int ipoctal_open(struct tty_struct *tty, struct file *file) +{ + struct ipoctal_channel *channel = tty->driver_data; + + return tty_port_open(&channel->tty_port, tty, file); } static void ipoctal_reset_stats(struct ipoctal_stats *stats) @@ -145,9 +158,7 @@ static int ipoctal_get_icount(struct tty_struct *tty, static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) { struct tty_port *port = &channel->tty_port; - unsigned char value; - unsigned char flag; - u8 isr; + u8 isr, value, flag; do { value = ioread8(&channel->regs->r.rhr); @@ -189,8 +200,8 @@ static void ipoctal_irq_rx(struct ipoctal_channel *channel, u8 sr) static void ipoctal_irq_tx(struct ipoctal_channel *channel) { - unsigned char value; unsigned int *pointer_write = &channel->pointer_write; + u8 value; if (channel->nb_bytes == 0) return; @@ -240,7 +251,7 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel) static irqreturn_t ipoctal_irq_handler(void *arg) { unsigned int i; - struct ipoctal *ipoctal = (struct ipoctal *) arg; + struct ipoctal *ipoctal = arg; /* Clear the IPack device interrupt */ readw(ipoctal->int_space + ACK_INT_REQ0); @@ -263,8 +274,7 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, { int res; int i; - struct tty_driver *tty; - char name[20]; + struct tty_driver *drv; struct ipoctal_channel *channel; struct ipack_region *region; void __iomem *addr; @@ -347,44 +357,47 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, /* Register the TTY device */ /* Each IP-OCTAL channel is a TTY port */ - tty = alloc_tty_driver(NR_CHANNELS); - - if (!tty) - return -ENOMEM; + drv = tty_alloc_driver(NR_CHANNELS, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(drv)) + return PTR_ERR(drv); /* Fill struct tty_driver with ipoctal data */ - tty->owner = THIS_MODULE; - tty->driver_name = KBUILD_MODNAME; - sprintf(name, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); - tty->name = name; - tty->major = 0; - - tty->minor_start = 0; - tty->type = TTY_DRIVER_TYPE_SERIAL; - tty->subtype = SERIAL_TYPE_NORMAL; - tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty->init_termios = tty_std_termios; - tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->init_termios.c_ispeed = 9600; - tty->init_termios.c_ospeed = 9600; - - tty_set_operations(tty, &ipoctal_fops); - res = tty_register_driver(tty); + drv->owner = THIS_MODULE; + drv->driver_name = KBUILD_MODNAME; + drv->name = kasprintf(GFP_KERNEL, KBUILD_MODNAME ".%d.%d.", bus_nr, slot); + if (!drv->name) { + res = -ENOMEM; + goto err_put_driver; + } + drv->major = 0; + + drv->minor_start = 0; + drv->type = TTY_DRIVER_TYPE_SERIAL; + drv->subtype = SERIAL_TYPE_NORMAL; + drv->init_termios = tty_std_termios; + drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + drv->init_termios.c_ispeed = 9600; + drv->init_termios.c_ospeed = 9600; + + tty_set_operations(drv, &ipoctal_fops); + res = tty_register_driver(drv); if (res) { dev_err(&ipoctal->dev->dev, "Can't register tty driver.\n"); - put_tty_driver(tty); - return res; + goto err_free_name; } /* Save struct tty_driver for use it when uninstalling the device */ - ipoctal->tty_drv = tty; + ipoctal->tty_drv = drv; for (i = 0; i < NR_CHANNELS; i++) { struct device *tty_dev; channel = &ipoctal->channel[i]; tty_port_init(&channel->tty_port); - tty_port_alloc_xmit_buf(&channel->tty_port); + res = tty_port_alloc_xmit_buf(&channel->tty_port); + if (res) + continue; channel->tty_port.ops = &ipoctal_tty_port_ops; ipoctal_reset_stats(&channel->stats); @@ -392,13 +405,15 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, spin_lock_init(&channel->lock); channel->pointer_read = 0; channel->pointer_write = 0; - tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL); + tty_dev = tty_port_register_device_attr(&channel->tty_port, drv, + i, NULL, channel, NULL); if (IS_ERR(tty_dev)) { dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n"); + tty_port_free_xmit_buf(&channel->tty_port); tty_port_destroy(&channel->tty_port); continue; } - dev_set_drvdata(tty_dev, channel); + channel->tty_registered = true; } /* @@ -410,14 +425,20 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, ipoctal_irq_handler, ipoctal); return 0; + +err_free_name: + kfree(drv->name); +err_put_driver: + tty_driver_kref_put(drv); + + return res; } -static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, - const unsigned char *buf, - int count) +static inline size_t ipoctal_copy_write_buffer(struct ipoctal_channel *channel, + const u8 *buf, size_t count) { unsigned long flags; - int i; + size_t i; unsigned int *pointer_read = &channel->pointer_read; /* Copy the bytes from the user buffer to the internal one */ @@ -435,11 +456,11 @@ static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel, return i; } -static int ipoctal_write_tty(struct tty_struct *tty, - const unsigned char *buf, int count) +static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf, + size_t count) { struct ipoctal_channel *channel = tty->driver_data; - unsigned int char_copied; + size_t char_copied; char_copied = ipoctal_copy_write_buffer(channel, buf, count); @@ -473,7 +494,7 @@ static unsigned int ipoctal_chars_in_buffer(struct tty_struct *tty) } static void ipoctal_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) + const struct ktermios *old_termios) { unsigned int cflag; unsigned char mr1 = 0; @@ -623,7 +644,7 @@ static void ipoctal_hangup(struct tty_struct *tty) tty_port_hangup(&channel->tty_port); ipoctal_reset_channel(channel); - tty_port_set_initialized(&channel->tty_port, 0); + tty_port_set_initialized(&channel->tty_port, false); wake_up_interruptible(&channel->tty_port.open_wait); } @@ -635,7 +656,7 @@ static void ipoctal_shutdown(struct tty_struct *tty) return; ipoctal_reset_channel(channel); - tty_port_set_initialized(&channel->tty_port, 0); + tty_port_set_initialized(&channel->tty_port, false); } static void ipoctal_cleanup(struct tty_struct *tty) @@ -649,6 +670,7 @@ static void ipoctal_cleanup(struct tty_struct *tty) static const struct tty_operations ipoctal_fops = { .ioctl = NULL, + .install = ipoctal_install, .open = ipoctal_open, .close = ipoctal_close, .write = ipoctal_write_tty, @@ -691,13 +713,18 @@ static void __ipoctal_remove(struct ipoctal *ipoctal) for (i = 0; i < NR_CHANNELS; i++) { struct ipoctal_channel *channel = &ipoctal->channel[i]; + + if (!channel->tty_registered) + continue; + tty_unregister_device(ipoctal->tty_drv, i); tty_port_free_xmit_buf(&channel->tty_port); tty_port_destroy(&channel->tty_port); } tty_unregister_driver(ipoctal->tty_drv); - put_tty_driver(ipoctal->tty_drv); + kfree(ipoctal->tty_drv->name); + tty_driver_kref_put(ipoctal->tty_drv); kfree(ipoctal); } |
