diff options
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/vcc.c | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 4edace1d9e4b..72e0879e332d 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -7,6 +7,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/tty.h> #include <asm/vio.h> #include <asm/ldc.h> @@ -52,6 +53,9 @@ struct vcc_port { #define VCC_MAX_PORTS 1024 #define VCC_MINOR_START 0 /* must be zero */ +#define VCC_CTL_BREAK -1 +#define VCC_CTL_HUP -2 + static const char vcc_driver_name[] = "vcc"; static const char vcc_device_node[] = "vcc"; static struct tty_driver *vcc_tty_driver; @@ -268,6 +272,77 @@ static struct vio_version vcc_versions[] = { { .major = 1, .minor = 0 }, }; +static ssize_t vcc_sysfs_domain_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct vcc_port *port; + int rv; + + port = dev_get_drvdata(dev); + if (!port) + return -ENODEV; + + rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); + + return rv; +} + +static int vcc_send_ctl(struct vcc_port *port, int ctl) +{ + struct vio_vcc pkt; + int rv; + + pkt.tag.type = VIO_TYPE_CTRL; + pkt.tag.sid = ctl; + pkt.tag.stype = 0; + + rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); + WARN_ON(!rv); + vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); + + return rv; +} + +static ssize_t vcc_sysfs_break_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vcc_port *port; + unsigned long flags; + int rv = count; + int brk; + + port = dev_get_drvdata(dev); + if (!port) + return -ENODEV; + + spin_lock_irqsave(&port->lock, flags); + + if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) + rv = -EINVAL; + else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) + pr_err("VCC: unable to send CTL_BREAK\n"); + + spin_unlock_irqrestore(&port->lock, flags); + + return rv; +} + +static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); +static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); + +static struct attribute *vcc_sysfs_entries[] = { + &dev_attr_domain.attr, + &dev_attr_break.attr, + NULL +}; + +static struct attribute_group vcc_attribute_group = { + .name = NULL, + .attrs = vcc_sysfs_entries, +}; + /** * vcc_probe() - Initialize VCC port * @vdev: Pointer to VIO device of the new VCC port @@ -349,6 +424,10 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) mdesc_release(hp); + rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); + if (rv) + goto free_domain; + dev_set_drvdata(&vdev->dev, port); /* It's possible to receive IRQs in the middle of vio_port_up. Disable @@ -360,6 +439,8 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) return 0; +free_domain: + kfree(port->domain); unreg_tty: tty_unregister_device(vcc_tty_driver, port->index); free_table: @@ -408,6 +489,7 @@ static int vcc_remove(struct vio_dev *vdev) del_timer_sync(&port->vio.timer); vio_ldc_free(&port->vio); + sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); dev_set_drvdata(&vdev->dev, NULL); if (port->tty) { |