USB: iuu_phoenix: add support for changing VCC
authorOlivier Bornet <Olivier.Bornet@puck.ch>
Tue, 18 Aug 2009 19:05:55 +0000 (21:05 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Sep 2009 13:46:35 +0000 (06:46 -0700)
You can now set the IUU reader to 3.3V VCC instead of 5V VCC, using the sysfs
parameter vcc_mode. Valid values are 3 and 5.

Signed-off-by: Olivier Bornet <Olivier.Bornet@puck.ch>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/iuu_phoenix.c

index 60a7d8d..5f13342 100644 (file)
@@ -79,6 +79,7 @@ struct iuu_private {
        u8 *buf;                /* used for initialize speed */
        u8 *dbgbuf;             /* debug buffer */
        u8 len;
+       int vcc;                /* vcc (either 3 or 5 V) */
 };
 
 
@@ -114,6 +115,7 @@ static int iuu_startup(struct usb_serial *serial)
                kfree(priv);
                return -ENOMEM;
        }
+       priv->vcc = 5;  /* 5 V for vcc by default */
        spin_lock_init(&priv->lock);
        init_waitqueue_head(&priv->delta_msr_wait);
        usb_set_serial_port_data(serial->port[0], priv);
@@ -1178,6 +1180,95 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
        return result;
 }
 
+/* how to change VCC */
+static int iuu_vcc_set(struct usb_serial_port *port, unsigned int vcc)
+{
+       int status;
+       u8 *buf;
+
+       buf = kmalloc(5, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       dbg("%s - enter", __func__);
+
+       buf[0] = IUU_SET_VCC;
+       buf[1] = vcc & 0xFF;
+       buf[2] = (vcc >> 8) & 0xFF;
+       buf[3] = (vcc >> 16) & 0xFF;
+       buf[4] = (vcc >> 24) & 0xFF;
+
+       status = bulk_immediate(port, buf, 5);
+       kfree(buf);
+
+       if (status != IUU_OPERATION_OK)
+               dbg("%s - vcc error status = %2x", __func__, status);
+       else
+               dbg("%s - vcc OK !", __func__);
+
+       return status;
+}
+
+/*
+ * Sysfs Attributes
+ */
+
+static ssize_t show_vcc_mode(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct usb_serial_port *port = to_usb_serial_port(dev);
+       struct iuu_private *priv = usb_get_serial_port_data(port);
+
+       return sprintf(buf, "%d\n", priv->vcc);
+}
+
+static ssize_t store_vcc_mode(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct usb_serial_port *port = to_usb_serial_port(dev);
+       struct iuu_private *priv = usb_get_serial_port_data(port);
+       unsigned long v;
+
+       if (strict_strtoul(buf, 10, &v)) {
+               dev_err(dev, "%s - vcc_mode: %s is not a unsigned long\n",
+                               __func__, buf);
+               goto fail_store_vcc_mode;
+       }
+
+       dbg("%s: setting vcc_mode = %ld", __func__, v);
+
+       if ((v != 3) && (v != 5)) {
+               dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v);
+       } else {
+               iuu_vcc_set(port, v);
+               priv->vcc = v;
+       }
+fail_store_vcc_mode:
+       return count;
+}
+
+static DEVICE_ATTR(vcc_mode, S_IRUSR | S_IWUSR, show_vcc_mode,
+       store_vcc_mode);
+
+static int iuu_create_sysfs_attrs(struct usb_serial_port *port)
+{
+       dbg("%s", __func__);
+
+       return device_create_file(&port->dev, &dev_attr_vcc_mode);
+}
+
+static int iuu_remove_sysfs_attrs(struct usb_serial_port *port)
+{
+       dbg("%s", __func__);
+
+       device_remove_file(&port->dev, &dev_attr_vcc_mode);
+       return 0;
+}
+
+/*
+ * End Sysfs Attributes
+ */
+
 static struct usb_serial_driver iuu_device = {
        .driver = {
                   .owner = THIS_MODULE,
@@ -1185,6 +1276,8 @@ static struct usb_serial_driver iuu_device = {
                   },
        .id_table = id_table,
        .num_ports = 1,
+       .port_probe = iuu_create_sysfs_attrs,
+       .port_remove = iuu_remove_sysfs_attrs,
        .open = iuu_open,
        .close = iuu_close,
        .write = iuu_uart_write,