[PATCH] usb: misc: Driver for Yepkit YKUSH switchable hub

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The Yepkit YKUSH <https://www.yepkit.com/products/ykush> is a three-port
switchable USB hub. In addition the to actual hub device, an HID device is
exported (VID 0x04D8 PID 0x0042). By sending specific commands to the HID
device, it is possible to power up/down the three ports separately or all ports
together. Commands 0x01-0x03 is used to power down ports 1-3, while 0x0A is used
to power down all ports. In order to power up a port (or all ports), the command
is OR'd with 0x10.

Signed-off-by: Kristian Evensen <kristian.evensen@xxxxxxxxx>
---
 drivers/hid/hid-core.c    |   1 +
 drivers/hid/hid-ids.h     |   1 +
 drivers/usb/misc/Kconfig  |  11 +++
 drivers/usb/misc/Makefile |   1 +
 drivers/usb/misc/ykush.c  | 176 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 190 insertions(+)
 create mode 100644 drivers/usb/misc/ykush.c

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 8b63879..77afffc 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2328,6 +2328,7 @@ static const struct hid_device_id hid_ignore_list[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICK16F1454) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9243359..8df4744 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -638,6 +638,7 @@
 #define USB_DEVICE_ID_PICKIT2		0x0033
 #define USB_DEVICE_ID_PICOLCD		0xc002
 #define USB_DEVICE_ID_PICOLCD_BOOTLOADER	0xf002
+#define USB_DEVICE_ID_PICK16F1454	0x0042
 
 #define USB_VENDOR_ID_MICROSOFT		0x045e
 #define USB_DEVICE_ID_SIDEWINDER_GV	0x003b
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 76d7720..4476d1d 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -255,3 +255,14 @@ config USB_LINK_LAYER_TEST
 	  This driver is for generating specific traffic for Super Speed Link
 	  Layer Test Device. Say Y only when you want to conduct USB Super Speed
 	  Link Layer Test for host controllers.
+
+config USB_YKUSH
+	tristate "USB Yepkit YKUSH driver"
+	help
+	  Yepkit YKUSH is a three-port switchable USB hub (see
+	  <https://www.yepkit.com/products/ykush>).  Say Y here if you want to
+	  add support for the switching part of the hub.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ykush.
+
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 65b0402..1dc17fe 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_USB_HSIC_USB3503)		+= usb3503.o
 
 obj-$(CONFIG_USB_SISUSBVGA)		+= sisusbvga/
 obj-$(CONFIG_USB_LINK_LAYER_TEST)	+= lvstest.o
+obj-$(CONFIG_USB_YKUSH)			+= ykush.o
diff --git a/drivers/usb/misc/ykush.c b/drivers/usb/misc/ykush.c
new file mode 100644
index 0000000..0278796
--- /dev/null
+++ b/drivers/usb/misc/ykush.c
@@ -0,0 +1,176 @@
+/*
+ * Yepkit YKUSH driver
+ *
+ * Copyright (C) 2015 Kristian Evensen (kristian.evensen@xxxxxxxxx)
+ *
+ *	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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+
+#define DRIVER_AUTHOR "Kristian Evensen, kristian.evensen@xxxxxxxxx"
+#define DRIVER_DESC "Yepkit YKUSH driver"
+
+#define CMD_PORT_1	0x01
+#define CMD_PORT_2	0x02
+#define CMD_PORT_3	0x03
+#define CMD_PORT_ALL	0x0A
+
+static const struct usb_device_id id_table[] = {
+	{ USB_DEVICE(0x04d8, 0x0042) },
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct ykush_hub {
+	struct usb_device *udev;
+	u8 port1;
+	u8 port2;
+	u8 port3;
+	u8 all;
+};
+
+static void send_port_cmd(struct ykush_hub *hub, u8 cmd)
+{
+	int retval = 0, actlength;
+	u8 *buffer = kmalloc(6, GFP_KERNEL);
+
+	if (!buffer) {
+		dev_err(&hub->udev->dev, "out of memory\n");
+		return;
+	}
+
+	buffer[0] = cmd;
+	buffer[1] = cmd;
+
+	retval = usb_interrupt_msg(hub->udev,
+				   usb_sndctrlpipe(hub->udev, 1),
+				   buffer,
+				   6,
+				   &actlength,
+				   5000);
+
+	if (retval)
+		pr_info("retval = %d\n", retval);
+
+	kfree(buffer);
+}
+
+#define show_set(value, cmd) \
+static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
+			    char *buf)					\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct ykush_hub *hub = usb_get_intfdata(intf);			\
+									\
+	return sprintf(buf, "%u\n", hub->value);			\
+}									\
+static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
+			   const char *buf, size_t count)		\
+{									\
+	struct usb_interface *intf = to_usb_interface(dev);		\
+	struct ykush_hub *hub = usb_get_intfdata(intf);			\
+	u8 enable = 0, cmd = CMD_##cmd;					\
+									\
+	if (kstrtou8(buf, 0, &enable) || (enable > 1))			\
+		return -EINVAL;						\
+									\
+	hub->value = enable;						\
+									\
+	if (enable)							\
+		cmd |= 0x10;						\
+									\
+	send_port_cmd(hub, cmd);					\
+	return count;							\
+}									\
+static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value)
+show_set(port1, PORT_1);
+show_set(port2, PORT_2);
+show_set(port3, PORT_3);
+show_set(all, PORT_ALL);
+
+static int ykush_probe(struct usb_interface *interface,
+		       const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct ykush_hub *dev = NULL;
+	int retval = -ENOMEM;
+
+	dev = kzalloc(sizeof(struct ykush_hub), GFP_KERNEL);
+
+	if (dev == NULL) {
+		dev_err(&interface->dev, "out of memory\n");
+		goto error_mem;
+	}
+
+	dev->udev = usb_get_dev(udev);
+	usb_set_intfdata(interface, dev);
+
+	retval = device_create_file(&interface->dev, &dev_attr_port1);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_port2);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_port3);
+	if (retval)
+		goto error;
+	retval = device_create_file(&interface->dev, &dev_attr_all);
+	if (retval)
+		goto error;
+
+	dev_info(&interface->dev, "Yepkit YKUSH hub now attached\n");
+	return 0;
+
+error:
+	device_remove_file(&interface->dev, &dev_attr_port1);
+	device_remove_file(&interface->dev, &dev_attr_port2);
+	device_remove_file(&interface->dev, &dev_attr_port3);
+	device_remove_file(&interface->dev, &dev_attr_all);
+	usb_set_intfdata(interface, NULL);
+	usb_put_dev(dev->udev);
+	kfree(dev);
+error_mem:
+	return retval;
+}
+
+static void ykush_disconnect(struct usb_interface *interface)
+{
+	struct ykush_hub *dev;
+
+	dev = usb_get_intfdata(interface);
+
+	device_remove_file(&interface->dev, &dev_attr_port1);
+	device_remove_file(&interface->dev, &dev_attr_port2);
+	device_remove_file(&interface->dev, &dev_attr_port3);
+	device_remove_file(&interface->dev, &dev_attr_all);
+
+	usb_set_intfdata(interface, NULL);
+
+	usb_put_dev(dev->udev);
+
+	kfree(dev);
+
+	dev_info(&interface->dev, "Yepkit YKUSH now disconnected\n");
+}
+
+static struct usb_driver ykush_driver = {
+	.name =		"ykush",
+	.probe =	ykush_probe,
+	.disconnect =	ykush_disconnect,
+	.id_table =	id_table,
+};
+
+module_usb_driver(ykush_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux