summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-01-28 09:02:24 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-28 09:02:24 -0800
commite770d73ceb93c235525cb9bbbf1374c9b61a0895 (patch)
treeac72b64d1978fece984c8b5a27442acbef7f172c /drivers/tty
parentbe86497152220677e3e5cb326519e19d727f8674 (diff)
parent07be0382097027cde68d9268cc628741069b5762 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 patches from Martin Schwidefsky: "A new binary interface to be able to query and modify the LPAR scheduler weight and cap settings. Some improvements for the hvc terminal over iucv and a couple of bux fixes" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: s390/hypfs: add interface for diagnose 0x304 s390: wire up sys_sched_setattr/sys_sched_getattr s390/uapi: fix struct statfs64 definition s390/uaccess: remove dead extern declarations, make functions static s390/uaccess: test if current->mm is set before walking page tables s390/zfcpdump: make zfcpdump depend on 64BIT s390/32bit: fix cmpxchg64 s390/xpram: don't modify module parameters s390/zcrypt: remove zcrypt kmsg documentation again s390/hvc_iucv: Automatically assign free HVC terminal devices s390/hvc_iucv: Display connection details through device attributes s390/hvc_iucv: fix sparse warning s390/vmur: Link parent CCW device during UR device creation
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/hvc/hvc_iucv.c103
1 files changed, 95 insertions, 8 deletions
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c
index db19a38c8c6..ea74460f363 100644
--- a/drivers/tty/hvc/hvc_iucv.c
+++ b/drivers/tty/hvc/hvc_iucv.c
@@ -77,6 +77,7 @@ struct hvc_iucv_private {
struct list_head tty_outqueue; /* outgoing IUCV messages */
struct list_head tty_inqueue; /* incoming IUCV messages */
struct device *dev; /* device structure */
+ u8 info_path[16]; /* IUCV path info (dev attr) */
};
struct iucv_tty_buffer {
@@ -126,7 +127,7 @@ static struct iucv_handler hvc_iucv_handler = {
* This function returns the struct hvc_iucv_private instance that corresponds
* to the HVC virtual terminal number specified as parameter @num.
*/
-struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
+static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
{
if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices))
return NULL;
@@ -772,18 +773,37 @@ static int hvc_iucv_filter_connreq(u8 ipvmid[8])
static int hvc_iucv_path_pending(struct iucv_path *path,
u8 ipvmid[8], u8 ipuser[16])
{
- struct hvc_iucv_private *priv;
+ struct hvc_iucv_private *priv, *tmp;
+ u8 wildcard[9] = "lnxhvc ";
+ int i, rc, find_unused;
u8 nuser_data[16];
u8 vm_user_id[9];
- int i, rc;
+ ASCEBC(wildcard, sizeof(wildcard));
+ find_unused = !memcmp(wildcard, ipuser, 8);
+
+ /* First, check if the pending path request is managed by this
+ * IUCV handler:
+ * - find a disconnected device if ipuser contains the wildcard
+ * - find the device that matches the terminal ID in ipuser
+ */
priv = NULL;
- for (i = 0; i < hvc_iucv_devices; i++)
- if (hvc_iucv_table[i] &&
- (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) {
- priv = hvc_iucv_table[i];
+ for (i = 0; i < hvc_iucv_devices; i++) {
+ tmp = hvc_iucv_table[i];
+ if (!tmp)
+ continue;
+
+ if (find_unused) {
+ spin_lock(&tmp->lock);
+ if (tmp->iucv_state == IUCV_DISCONN)
+ priv = tmp;
+ spin_unlock(&tmp->lock);
+
+ } else if (!memcmp(tmp->srv_name, ipuser, 8))
+ priv = tmp;
+ if (priv)
break;
- }
+ }
if (!priv)
return -ENODEV;
@@ -826,6 +846,10 @@ static int hvc_iucv_path_pending(struct iucv_path *path,
priv->path = path;
priv->iucv_state = IUCV_CONNECTED;
+ /* store path information */
+ memcpy(priv->info_path, ipvmid, 8);
+ memcpy(priv->info_path + 8, ipuser + 8, 8);
+
/* flush buffered output data... */
schedule_delayed_work(&priv->sndbuf_work, 5);
@@ -960,6 +984,49 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev)
return 0;
}
+static ssize_t hvc_iucv_dev_termid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+ size_t len;
+
+ len = sizeof(priv->srv_name);
+ memcpy(buf, priv->srv_name, len);
+ EBCASC(buf, len);
+ buf[len++] = '\n';
+ return len;
+}
+
+static ssize_t hvc_iucv_dev_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+ return sprintf(buf, "%u:%u\n", priv->iucv_state, priv->tty_state);
+}
+
+static ssize_t hvc_iucv_dev_peer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hvc_iucv_private *priv = dev_get_drvdata(dev);
+ char vmid[9], ipuser[9];
+
+ memset(vmid, 0, sizeof(vmid));
+ memset(ipuser, 0, sizeof(ipuser));
+
+ spin_lock_bh(&priv->lock);
+ if (priv->iucv_state == IUCV_CONNECTED) {
+ memcpy(vmid, priv->info_path, 8);
+ memcpy(ipuser, priv->info_path + 8, 8);
+ }
+ spin_unlock_bh(&priv->lock);
+ EBCASC(ipuser, 8);
+
+ return sprintf(buf, "%s:%s\n", vmid, ipuser);
+}
+
/* HVC operations */
static const struct hv_ops hvc_iucv_ops = {
@@ -985,6 +1052,25 @@ static struct device_driver hvc_iucv_driver = {
.pm = &hvc_iucv_pm_ops,
};
+/* IUCV HVC device attributes */
+static DEVICE_ATTR(termid, 0640, hvc_iucv_dev_termid_show, NULL);
+static DEVICE_ATTR(state, 0640, hvc_iucv_dev_state_show, NULL);
+static DEVICE_ATTR(peer, 0640, hvc_iucv_dev_peer_show, NULL);
+static struct attribute *hvc_iucv_dev_attrs[] = {
+ &dev_attr_termid.attr,
+ &dev_attr_state.attr,
+ &dev_attr_peer.attr,
+ NULL,
+};
+static struct attribute_group hvc_iucv_dev_attr_group = {
+ .attrs = hvc_iucv_dev_attrs,
+};
+static const struct attribute_group *hvc_iucv_dev_attr_groups[] = {
+ &hvc_iucv_dev_attr_group,
+ NULL,
+};
+
+
/**
* hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance
* @id: hvc_iucv_table index
@@ -1046,6 +1132,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
priv->dev->bus = &iucv_bus;
priv->dev->parent = iucv_root;
priv->dev->driver = &hvc_iucv_driver;
+ priv->dev->groups = hvc_iucv_dev_attr_groups;
priv->dev->release = (void (*)(struct device *)) kfree;
rc = device_register(priv->dev);
if (rc) {