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