lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 17 Oct 2017 22:01:38 +0200
From:   "Hans-Frieder Vogt" <hfvogt@....net>
To:     linux-kernel@...r.kernel.org
Cc:     zbr@...emap.net
Subject: [PATCH] w1 driver for serial linkUSB, linkOEM, LINK devices

Hi,

attached is a driver that supports the serial OneWire masters from iButtonLink(TM) in the w1 kernel driver. In order to be usable it needs an updated userland tool (patch included in documentation file). The patch is against linux-next. Please try and comment.

Signed-off by: Hans-Frieder Vogt <hfvogt@....net>

 Documentation/w1/masters/00-INDEX   |    2 
 Documentation/w1/masters/linkserial |   44 ++
 drivers/w1/masters/Kconfig          |   10 
 drivers/w1/masters/Makefile         |    1 
 drivers/w1/masters/linkserial.c     |  540 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serio.h          |    1 
 6 files changed, 598 insertions(+)

diff --git a/Documentation/w1/masters/00-INDEX b/Documentation/w1/masters/00-INDEX
index 8330cf9325f0..33c522854126 100644
--- a/Documentation/w1/masters/00-INDEX
+++ b/Documentation/w1/masters/00-INDEX
@@ -4,6 +4,8 @@ ds2482
 	- The Maxim/Dallas Semiconductor DS2482 provides 1-wire busses.
 ds2490
 	- The Maxim/Dallas Semiconductor DS2490 builds USB <-> W1 bridges.
+linkserial
+	- Serial to W1 bridge provided by iButtonLink devices.
 mxc-w1
 	- W1 master controller driver found on Freescale MX2/MX3 SoCs
 omap-hdq
diff --git a/Documentation/w1/masters/linkserial b/Documentation/w1/masters/linkserial
new file mode 100644
index 000000000000..d8b0a209ce4a
--- /dev/null
+++ b/Documentation/w1/masters/linkserial
@@ -0,0 +1,44 @@
+Kernel driver linkserial
+========================
+
+Supported devices
+  * LinkUSB(TM)
+  * LinkOEM(TM)
+  * LINK(TM) untested
+
+Author: Hans-Frieder Vogt <hfvogt@....net>
+
+
+Description
+-----------
+
+1-wire bus master driver for iButtonLink LLC devices. These devices can operate
+in 2 modes: DS9097U Emulation and ASCII mode. The driver linkserial uses the
+ASCII command interface only.
+
+This driver needs an adapted userspace program inputattach to be used. The
+following patch modifies inputattach as needed:
+
+--- a/inputattach.c     2016-08-09 13:04:05.000000000 +0200
++++ b/inputattach.c       2017-10-14 23:49:08.594165130 +0200
+@@ -49,6 +49,9 @@
+ #ifdef SYSTEMD_SUPPORT
+ #include <systemd/sd-daemon.h>
+ #endif
++#ifndef SERIO_ILINK
++#define SERIO_ILINK 0x42
++#endif
+ 
+ static int readchar(int fd, unsigned char *c, int timeout)
+ {
+@@ -867,6 +870,9 @@ static struct input_types input_types[]
+ { "--pulse8-cec",              "-pulse8-cec",  "Pulse Eight HDMI CEC dongle",
+        B9600, CS8,
+        SERIO_PULSE8_CEC,               0x00,   0x00,   0,      NULL },
++{ "--linkserial",              "-linkserial",  "Link Onewire master",
++       B9600, CS8,
++       SERIO_ILINK,            0x00,   0x00,   0,      NULL },
+ { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL }
+ };
+ 
+
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 1708b2300c7a..eba57c2f0751 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -34,6 +34,16 @@ config W1_MASTER_DS2482
 	  This driver can also be built as a module.  If so, the module
 	  will be called ds2482.
 
+config W1_MASTER_ILINK
+	tristate "iButtonLink(TM) serial to 1-Wire bridge"
+	depends on SERIO
+	help
+	  Say Y here to get support for the iButtonLink(TM) serial to 1-Wire
+	  bridges like the LINK(TM), the LinkUSB(TM) and the LinkOEM(TM).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called linkserial.
+
 config W1_MASTER_MXC
 	tristate "Freescale MXC 1-wire busmaster"
 	depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile
index c5a3e96fcbab..c6191daa5163 100644
--- a/drivers/w1/masters/Makefile
+++ b/drivers/w1/masters/Makefile
@@ -5,6 +5,7 @@
 obj-$(CONFIG_W1_MASTER_MATROX)		+= matrox_w1.o
 obj-$(CONFIG_W1_MASTER_DS2490)		+= ds2490.o
 obj-$(CONFIG_W1_MASTER_DS2482)		+= ds2482.o
+obj-$(CONFIG_W1_MASTER_ILINK)		+= linkserial.o
 obj-$(CONFIG_W1_MASTER_MXC)		+= mxc_w1.o
 
 obj-$(CONFIG_W1_MASTER_DS1WM)		+= ds1wm.o
diff --git a/drivers/w1/masters/linkserial.c b/drivers/w1/masters/linkserial.c
new file mode 100644
index 000000000000..9cbbf0c5fa4d
--- /dev/null
+++ b/drivers/w1/masters/linkserial.c
@@ -0,0 +1,540 @@
+/*
+ * driver for W1 master based on iButtonLink(TM) Link devices
+ *
+ * Copyright (C) 2017 Hans-Frieder Vogt <hfvogt@....net>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/serio.h>
+
+#include <linux/w1.h>
+
+#define BUFFER_SIZE 64
+
+#define LINK_STATE_IDLE 0
+#define LINK_STATE_BUSY -1
+
+#define MODE_ASCII 0
+#define MODE_HEX 1
+
+#define DRV_VERSION "0.1"
+#define MAX_LINK_VERSION_LENGTH 36
+
+#define TIMEOUT 100
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+
+struct link_data {
+	struct serio 		*serio;
+	struct w1_bus_master	master;
+	spinlock_t		lock;
+
+	int state;
+	int mode;
+	unsigned char buffer[BUFFER_SIZE];
+	unsigned int pos;
+	int pull_up;
+	int expected;
+};
+
+static char val2char[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+			'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+static u8 char2val[] = {/* 0x30 */ 0, 1, 2, 3, 4, 5, 6, 7,
+			/* 0x38 */ 8, 9, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+			/* 0x40 */ 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+
+static inline int conv_hex(char c) {
+	int ret;
+	if ((c >= 0x30) && (c <= 0x46))
+		ret = char2val[c-0x30];
+	else
+		ret = 0x0f;
+	return ret;
+}
+
+static int link_read(void *data)
+{
+	struct link_data *link = data;
+
+	wait_event_interruptible_timeout(wq, link->state == LINK_STATE_IDLE,
+		msecs_to_jiffies(TIMEOUT));
+	if (link->mode == MODE_ASCII) {
+		/* remove <CR><LF> */
+		if ((link->pos >= 2) && (link->buffer[link->pos-2] == 0x0d) &&
+			(link->buffer[link->pos-1] == 0x0a)) {
+			link->pos -= 2;
+			link->buffer[link->pos] = '\0';
+		}
+	}
+	return link->pos;
+}
+
+/* change to ASCII mode
+   => send <CR>
+   <= <CR><LF>
+ */
+static int link_mode_ascii(void *data)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	unsigned long fl;
+	int len;
+
+	if (link->mode != MODE_ASCII) {
+		/* back to ASCII mode */
+		spin_lock_irqsave(&link->lock, fl);
+		serio_write(serio, 0x0d); /* cr */
+		link->pos = 0;
+		link->buffer[0] = '\0';
+		link->state = LINK_STATE_BUSY;
+		link->mode = MODE_ASCII;
+		spin_unlock_irqrestore(&link->lock, fl);
+		len = link_read(data);
+	}
+	return 0;
+}
+
+/* change to HEX mode
+   => send 'p' or 'b'
+   <= none
+ */
+static int link_mode_hex(void *data)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	unsigned long fl;
+
+	if (link->mode != MODE_HEX) {
+		spin_lock_irqsave(&link->lock, fl);
+		/* change to byte mode */
+		if (link->pull_up)
+			serio_write(serio, 'p');
+		else
+			serio_write(serio, 'b');
+		link->mode = MODE_HEX;
+		spin_unlock_irqrestore(&link->lock, fl);
+	}
+	return 0;
+}
+
+static void link_w1_write_byte(void *data, u8 byte)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	int len;
+	unsigned long fl;
+
+	/* make sure we are in hex mode */
+	link_mode_hex(data);
+
+	spin_lock_irqsave(&link->lock, fl);
+	serio_write(serio, val2char[byte >> 4]);
+	serio_write(serio, val2char[byte & 0x0f]);
+	link->pos = 0;
+	link->state = LINK_STATE_BUSY;
+	link->expected = 2;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	/* get back byte as confirmation */
+	len = link_read(data);
+	if (len != 2) {
+		dev_err(&serio->dev, "link_w1_write_byte read back %d bytes instead 2\n",
+			len);
+	}
+}
+
+static u8 link_w1_read_byte(void *data)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	u8 result = 0xff;
+	int len;
+	int ret;
+	unsigned long fl;
+
+	/* make sure we are in hex mode */
+	link_mode_hex(data);
+
+	spin_lock_irqsave(&link->lock, fl);
+	serio_write(serio, 'F');
+	serio_write(serio, 'F');
+	link->pos = 0;
+	link->state = LINK_STATE_BUSY;
+	link->expected = 2;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	len = link_read(data);
+	if (len == 2) {
+		ret = (conv_hex(link->buffer[0]) << 4) +
+			conv_hex(link->buffer[1]);
+		result = ret;
+	} else
+		dev_err(&serio->dev, "link_w1_read_byte read %d bytes instead 2\n", len);
+
+	return result;
+}
+
+static void link_w1_write_block(void *data, const u8 *buf, int len)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	int i, l;
+	const u8 *p = buf;
+	unsigned long fl;
+
+	if (len <= 0)
+		return;
+	if (len*2 > BUFFER_SIZE)
+		return;
+
+	/* make sure we are in hex mode */
+	link_mode_hex(data);
+
+	spin_lock_irqsave(&link->lock, fl);
+	for (i=0; i<len; i++, p++) {
+		serio_write(serio, val2char[*p >> 4]);
+		serio_write(serio, val2char[*p & 0x0f]);
+	}
+	link->pos = 0;
+	link->state = LINK_STATE_BUSY;
+	link->expected = 2*len;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	l = link_read(data);
+	if (l != 2*len) {
+		dev_err(&serio->dev,
+			"link_w1_write_block read back %d bytes instead of %d\n",
+			l, 2*len);
+	}
+}
+
+static u8 link_w1_read_block(void *data, u8 *buf, int len)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	u8 result;
+	int l, i;
+	u8 *p = buf;
+	unsigned long fl;
+
+	if (len <= 0)
+		return 0;
+	if (len*2 > BUFFER_SIZE)
+		return -E2BIG;
+
+	/* make sure we are in hex mode */
+	link_mode_hex(data);
+
+	spin_lock_irqsave(&link->lock, fl);
+	for (i=0; i<len; i++) {
+		serio_write(serio, 'F');
+		serio_write(serio, 'F');
+	}
+	link->pos = 0;
+	link->state = LINK_STATE_BUSY;
+	link->expected = 2*len;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	l = link_read(data);
+	if (l == 2*len) {
+		for (i=0; i<l; i+=2, p++) {
+			*p = (conv_hex(link->buffer[i]) << 4) +
+				conv_hex(link->buffer[i+1]);
+		}
+		result = len;
+	} else {
+		dev_err(&serio->dev,
+			"link_w1_read_block read %d bytes instead %d\n",
+			l, len);
+		result = 0;
+	}
+
+	return result;
+}
+
+static void link_w1_search(void *data, struct w1_master *master,
+	u8 search_type, w1_slave_found_callback callback)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	int len;
+	u64 id;
+	unsigned long fl;
+
+	link_mode_ascii(data);
+
+	spin_lock_irqsave(&link->lock, fl);
+	serio_write(serio, 'f');
+	while (1) {
+		link->pos = 0;
+		link->state = LINK_STATE_BUSY;
+		spin_unlock_irqrestore(&link->lock, fl);
+		len = link_read(data);
+		if (len <= 0)
+			break;
+		if (link->buffer[0] == '\0')
+			break;
+		/* buffer should contain now something like
+		   "+,123456789012345678" */
+		sscanf(&link->buffer[2], "%llx", &id);
+		callback(master, id);
+		if (link->buffer[0] == '+') {
+			spin_lock_irqsave(&link->lock, fl);
+			serio_write(serio, 'n');
+		} else
+			break;
+	}
+
+	master->search_id = 0;
+
+	return;
+}
+
+static u8 link_w1_reset_bus(void *data)
+{
+	struct link_data *link = data;
+	struct serio *serio = link->serio;
+	int result = 0, len;
+	int idx = 0;
+	unsigned long fl;
+
+	link_mode_ascii(data);
+
+	spin_lock_irqsave(&link->lock, fl);
+	/* send reset command */
+	serio_write(serio, 'r');
+	link->pos = 0;
+	link->buffer[0] = '\0';
+	link->state = LINK_STATE_BUSY;
+	link->mode = MODE_ASCII;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	len = link_read(data);
+	if (len <= 0) {
+		dev_err(&serio->dev, "reset zero read!\n");
+		goto err_out;
+	}
+	for (idx=0; idx<len; idx++)
+		if (link->buffer[idx] != '?')
+			break;
+	switch (link->buffer[idx]) {
+	case 'P':
+		break;
+	case 'N':
+		dev_warn(&serio->dev, "no devices on bus\n");
+		break;
+	case 'S':
+		dev_err(&serio->dev, "shortened bus!\n");
+		result = -1;
+		break;
+	default:
+		dev_err(&serio->dev, "reset_bus unknown link response '%c' (0x%02x)\n",
+			link->buffer[idx], link->buffer[idx]);
+		result = -2;
+	}
+
+err_out:
+	return result;
+}
+
+static u8 link_w1_set_pullup(void *data, int delay)
+{
+	struct link_data *link = data;
+	int result = 0;
+	unsigned long fl;
+
+	printk(KERN_INFO "pullup: %d\n", delay);
+
+	spin_lock_irqsave(&link->lock, fl);
+	link->pull_up = delay;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	return result;
+}
+
+static void linkserial_disconnect(struct serio *serio)
+{
+	struct link_data *link = serio_get_drvdata(serio);
+
+	w1_remove_master_device(&link->master);
+	serio_close(serio);
+	kfree(link);
+
+	dev_info(&serio->dev, "Disconnected from device\n");
+}
+
+static int link_version(struct link_data *link)
+{
+	struct serio *serio = link->serio;
+	int len, err;
+	unsigned long fl;
+
+	spin_lock_irqsave(&link->lock, fl);
+	/* version command */
+	serio_write(serio, ' ');
+	link->pos = 0;
+	link->buffer[0] = '\0';
+	link->state = LINK_STATE_BUSY;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	len = link_read(link);
+	if (len >= MAX_LINK_VERSION_LENGTH) {
+		err = -ENODEV;
+		dev_err(&serio->dev, "too long version string read (i=%d)\n", len);
+		goto err_close;
+	}
+	dev_info(&serio->dev, "version: \"%s\" (%d bytes)\n", link->buffer, len);
+
+	return 0;
+
+err_close:
+	return err;
+}
+
+static int link_detect(struct link_data *link)
+{
+	struct serio *serio = link->serio;
+	int len;
+	unsigned long fl;
+
+	spin_lock_irqsave(&link->lock, fl);
+
+	/* send break, reset with 9600 baud */
+	serio_write(serio, 0);
+	link->pos = 0;
+	link->buffer[0] = '\0';
+	link->mode = MODE_ASCII;
+	link->state = LINK_STATE_BUSY;
+	spin_unlock_irqrestore(&link->lock, fl);
+
+	/* slurp in initial bytes */
+	len = link_read(link);
+	dev_info(&serio->dev, "link_detect: \"%s\" (%d chars)\n",
+		link->buffer, len);
+	if (!len)
+		return -1;
+	else
+		return 0;
+}
+
+static irqreturn_t linkserial_interrupt(struct serio *serio, unsigned char data,
+				unsigned int flags)
+{
+	struct link_data *link = serio_get_drvdata(serio);
+
+	link->buffer[link->pos++] = data;
+
+	switch (link->mode) {
+	case MODE_HEX:
+		if (link->pos >= link->expected) {
+			link->buffer[link->pos] = '\0';
+			link->state = LINK_STATE_IDLE;
+			wake_up_interruptible(&wq);
+		}
+		break;
+	case MODE_ASCII:
+		if ((data == 0x0a) || (link->pos == BUFFER_SIZE-1)) {
+			link->buffer[link->pos] = '\0';
+			link->state = LINK_STATE_IDLE;
+			wake_up_interruptible(&wq);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int linkserial_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct link_data *link;
+	int err;
+
+	dev_info(&serio->dev, "linkserial_connect start\n");
+	link = kzalloc(sizeof(struct link_data), GFP_KERNEL);
+	if (!link) {
+                err = -ENOMEM;
+                goto exit;
+        }
+	spin_lock_init(&link->lock);
+	serio_set_drvdata(serio, link);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		goto exit_free;
+	}
+
+	link->serio = serio;
+
+	err = link_detect(link);
+
+	err = link_version(link);
+
+	link->master.data = link;
+	link->master.read_byte = link_w1_read_byte;
+	link->master.write_byte = link_w1_write_byte;
+	link->master.read_block = link_w1_read_block;
+	link->master.write_block = link_w1_write_block;
+	link->master.reset_bus = link_w1_reset_bus;
+	link->master.set_pullup = link_w1_set_pullup;
+	link->master.search = link_w1_search;
+
+	err = w1_add_master_device(&link->master);
+	if (err) {
+		goto exit_close;
+	}
+
+	dev_info(&serio->dev, "Connected to device\n");
+	return 0;
+
+exit_close:
+	serio_close(serio);
+exit_free:
+	kfree(link);
+exit:
+	return err;
+}
+
+static struct serio_device_id linkserial_serio_ids[] = {
+	{
+		.type   = SERIO_RS232,
+		.proto  = SERIO_ILINK,
+		.id     = SERIO_ANY,
+		.extra  = SERIO_ANY,
+	},
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(serio, linkserial_serio_ids);
+
+static struct serio_driver linkserial_drv = {
+	.driver         = {
+		.name   = "linkserial",
+	},
+	.description    = "W1 bus master driver for iButtonLink(TM) devices",
+	.id_table       = linkserial_serio_ids,
+	.connect        = linkserial_connect,
+	.disconnect     = linkserial_disconnect,
+	.interrupt      = linkserial_interrupt,
+};
+
+module_serio_driver(linkserial_drv);
+
+MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@....net>");
+MODULE_DESCRIPTION("W1 bus master driver for iButtonLink(TM) devices");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+
diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h
index ac217c6f0151..a9fa64366f59 100644
--- a/include/uapi/linux/serio.h
+++ b/include/uapi/linux/serio.h
@@ -81,5 +81,6 @@
 #define SERIO_EGALAX	0x3f
 #define SERIO_PULSE8_CEC	0x40
 #define SERIO_RAINSHADOW_CEC	0x41
+#define SERIO_ILINK	0x42
 
 #endif /* _UAPI_SERIO_H */

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ