On Sun, Aug 31, 2014 at 11:24:56PM +0800, Wang YanQing wrote:
> PL2303 USB Serial devices may has GPIOs, this patch add
> basic PL2303 gpio support.
> 
> Known issue:
> If gpios are in use(export to userspace through sysfs interface, etc),
> then call pl2303_release(unplug usb-serial convertor, modprobe -r, etc),
> will cause trouble, so we need to make sure there is no gpio user before
> call pl2303_release.
> 
> Signed-off-by: Wang YanQing <udkni...@gmail.com>
> ---
>  Note: I sniffed office HXD gpio test program to get 
>        gpios control protocol with PL2303 RA, so I only
>        test it with PL2303 RA and HXA, I don't have HXD,
>        but because it is *office* HXD gpio test program,
>        so if it doesn't work with HXD, I will feel surprise.
> 
>  Changes
>  v7-v8:
>  1: add support for four dedicated gpios on HXD and RA
>  2: fix problem reported by Johan Hovold in v7
>  3: fix checkpatch.pl's warning
> 
>  v6-v7:
>  1: add generic gpio support interfaces for pl2303 USB Serial devices
>     in pl2303_type_data and pl2303_serial_private suggested by Andreas Mohr.
>  2: drop different label names for different pl2303 instance suggested by 
> Johan Hovold.
>  3: fix missing lock corrected by Johan Hovold.
>  4: use prefix pl2303hx_gpio instead pl2303_gpio, pl2303_gpio is over generic 
> for current code,
>     and now we move gpio_startup|gpio_release into type-specified data 
> structure, so pl2303hx_gpio
>     is a better prefix.
>  5: make words in Kconfig a little more useful and clarified.
>  6: many misc code quality enhancement suggested by Johan Hovold.
> 
>  v5-v6:
>  1: fix typo error in Kconfig reported by Andreas Mohr 
>  2: add const qulification to gpio_dir_mask and gpio_value_mask suggested by 
> Andreas Mohr
> 
>  v4-v5:
>  1: fix gpio_chip.lable been overwrited by template_chip. 
>  2: use idr to manage minor instead of crude monotonous atomic increment. 
> 
>  v3-v4:
>  1: fix missing static for gpio_dir_mask and gpio_value_mask 
>  2: reduce unneeded compile macro defined suggested by 
> gno...@lxorguk.ukuu.org.uk 
>  3: use true instead of 1 corrected by Linus Walleij
>  4: ignore return value of gpiochip_remove suggested by Linus Walleij
>  5: fix multi gpio_chips registered by pl2303 can't be distinguished in 
> kernel space. 
> 
>  v2-v3:
>  1: fix errors and warnings reported by Daniele Forsi checked with 
> checkpatch.pl 
>  2: fix missing GPIOLIB dependence in Kconfig 
>  3: fix pl2303_gpio_get can't work 
> 
>  v1-v2:  
>  1:drop gpio-pl2303.c and relation stuff 
>  2:hang gpio stuff off of pl2303.c  
> 
>  drivers/usb/serial/Kconfig  |  11 ++
>  drivers/usb/serial/pl2303.c | 250 
> ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 261 insertions(+)
> 
> diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
> index 3ce5c74..b4e0cf9 100644
> --- a/drivers/usb/serial/Kconfig
> +++ b/drivers/usb/serial/Kconfig
> @@ -516,6 +516,17 @@ config USB_SERIAL_PL2303
>         To compile this driver as a module, choose M here: the
>         module will be called pl2303.
>  
> +config USB_SERIAL_PL2303_GPIO
> +     bool "USB Prolific 2303 Single Port GPIOs support"
> +     depends on USB_SERIAL_PL2303 && GPIOLIB
> +     ---help---
> +       Say Y here if you want to use GPIOs on PL2303 USB Serial single port
> +       adapter from Prolific.
> +
> +       It supports 2 dedicated GPIOs on PL2303HXA, 4 dedicated GPIOs on
> +       PL2303HXD and PL2303RA currently.
> +       If unsure, say N.
> +
>  config USB_SERIAL_OTI6858
>       tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge 
> Controller"
>       help
> diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
> index e9bad92..a3f8411 100644
> --- a/drivers/usb/serial/pl2303.c
> +++ b/drivers/usb/serial/pl2303.c
> @@ -28,6 +28,8 @@
>  #include <linux/usb.h>
>  #include <linux/usb/serial.h>
>  #include <asm/unaligned.h>
> +#include <linux/gpio.h>
> +#include <linux/mutex.h>
>  #include "pl2303.h"
>  
>  
> @@ -132,6 +134,22 @@ MODULE_DEVICE_TABLE(usb, id_table);
>  #define UART_OVERRUN_ERROR           0x40
>  #define UART_CTS                     0x80
>  
> +#define HXD_GPIO_01_CTRL             0x0001
> +#define HXD_GPIO_01_VALUE            0x0081
> +#define HXD_GPIO_23_DIR_CTRL         0x0c0c
> +#define HXD_GPIO_23_VALUE_CTRL               0x0d0d
> +#define HXD_GPIO_23_VALUE            0x8d8d
> +
> +#define HXD_GPIO0_DIR_MASK           0x10
> +#define HXD_GPIO1_DIR_MASK           0x20
> +#define HXD_GPIO2_DIR_MASK           0x03
> +#define HXD_GPIO3_DIR_MASK           0x0c
> +
> +#define HXD_GPIO0_VALUE_MASK         0x40
> +#define HXD_GPIO1_VALUE_MASK         0x80
> +#define HXD_GPIO2_VALUE_MASK         0x01
> +#define HXD_GPIO3_VALUE_MASK         0x02
> +
>  
>  enum pl2303_type {
>       TYPE_01,        /* Type 0 and 1 (difference unknown) */
> @@ -144,9 +162,12 @@ struct pl2303_type_data {
>       unsigned long quirks;
>  };
>  
> +struct pl2303_gpio;
>  struct pl2303_serial_private {
>       const struct pl2303_type_data *type;
>       unsigned long quirks;
> +     u8 ngpio;
> +     struct pl2303_gpio *gpio;
>  };
>  
>  struct pl2303_private {
> @@ -157,6 +178,14 @@ struct pl2303_private {
>       u8 line_settings[7];
>  };
>  
> +#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
> +static int pl2303_gpio_startup(struct usb_serial *serial);
> +static void pl2303_gpio_release(struct usb_serial *serial);
> +#else
> +static inline int pl2303_gpio_startup(struct usb_serial *serial) { return 0; 
> }
> +static inline void pl2303_gpio_release(struct usb_serial *serial) {}
> +#endif
> +
>  static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
>       [TYPE_01] = {
>               .max_baud_rate =        1228800,
> @@ -214,11 +243,213 @@ static int pl2303_probe(struct usb_serial *serial,
>       return 0;
>  }
>  
> +#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
> +struct pl2303_gpio_desc {
> +     u8 dir_offset;
> +     u8 dir_mask;
> +     u16 dir_ctrl;
> +     u8 value_offset;
> +     u8 value_mask;
> +     u16 value_ctrl;
> +     u16 read_ctrl;
> +};
> +
> +struct pl2303_gpio {
> +     struct pl2303_gpio_desc *descs;
> +     u8 data[3];
> +     struct mutex lock;
> +     struct usb_serial *serial;
> +     struct gpio_chip gpio_chip;
> +};
> +
> +static inline struct pl2303_gpio *to_pl2303_gpio(struct gpio_chip *chip)
> +{
> +     return container_of(chip, struct pl2303_gpio, gpio_chip);
> +}
> +
> +static void pl2303_gpio_add(struct usb_serial *serial, u8 num, u16 read_ctrl,
> +                     u8 dir_offset, u8 dir_mask, u16 dir_ctrl,
> +                     u8 value_offset, u8 value_mask, u16 value_ctrl)
> +{
> +     struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
> +     struct pl2303_gpio *gpio = spriv->gpio;
> +
> +     BUG_ON(num >= spriv->ngpio);
> +     gpio->descs[num].dir_offset = dir_offset;
> +     gpio->descs[num].dir_mask = dir_mask;
> +     gpio->descs[num].dir_ctrl = dir_ctrl;
> +
> +     gpio->descs[num].value_offset = value_offset;
> +     gpio->descs[num].value_mask = value_mask;
> +     gpio->descs[num].value_ctrl = value_ctrl;
> +
> +     gpio->descs[num].read_ctrl = read_ctrl;
> +}
> +
> +static int pl2303_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
> +{
> +     struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
> +     struct pl2303_gpio_desc *desc;
> +     int ret;
> +
> +     mutex_lock(&gpio->lock);
> +     desc = gpio->descs+offset;
> +     gpio->data[desc->dir_offset] &= ~desc->dir_mask;
> +     ret = pl2303_vendor_write(gpio->serial, desc->dir_ctrl,
> +                             gpio->data[desc->dir_offset]);
> +     mutex_unlock(&gpio->lock);
> +
> +     return ret;
> +}
> +
> +static int pl2303_gpio_direction_out(struct gpio_chip *chip,
> +                             unsigned offset, int value)
> +{
> +     struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
> +     struct pl2303_gpio_desc *desc;
> +     int ret;
> +
> +     mutex_lock(&gpio->lock);
> +     desc = gpio->descs+offset;
> +     gpio->data[desc->dir_offset] |= desc->dir_mask;
> +     ret = pl2303_vendor_write(gpio->serial, desc->dir_ctrl,
> +                             gpio->data[desc->dir_offset]);
> +     if (ret)
> +             goto error;
> +
> +     if (value)
> +             gpio->data[desc->value_offset] |= desc->value_mask;
> +     else
> +             gpio->data[desc->value_offset] &= ~desc->value_mask;
> +
> +     ret = pl2303_vendor_write(gpio->serial, desc->value_ctrl,
> +                             gpio->data[desc->value_offset]);
> +error:
> +     mutex_unlock(&gpio->lock);
> +
> +     return ret;
> +}
> +
> +static void pl2303_gpio_set(struct gpio_chip *chip, unsigned offset, int 
> value)
> +{
> +     struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
> +     struct pl2303_gpio_desc *desc;
> +
> +     mutex_lock(&gpio->lock);
> +     desc = gpio->descs+offset;
> +     if (value)
> +             gpio->data[desc->value_offset] |= desc->value_mask;
> +     else
> +             gpio->data[desc->value_offset] &= ~desc->value_mask;
> +
> +     pl2303_vendor_write(gpio->serial, desc->value_ctrl,
> +                     gpio->data[desc->value_offset]);
> +     mutex_unlock(&gpio->lock);
> +}
> +
> +static int pl2303_gpio_get(struct gpio_chip *chip, unsigned offset)
> +{
> +     struct pl2303_gpio *gpio = to_pl2303_gpio(chip);
> +     struct pl2303_gpio_desc *desc;
> +     unsigned char *buf;
> +     int value;
> +
> +     buf = kzalloc(1, GFP_KERNEL);
> +     if (!buf)
> +             return -ENOMEM;
> +
> +     mutex_lock(&gpio->lock);
> +     desc = gpio->descs+offset;
> +
> +     if (pl2303_vendor_read(gpio->serial, desc->read_ctrl, buf)) {
> +             mutex_unlock(&gpio->lock);
> +             return -EIO;
> +     }
> +
> +     value = buf[0] & desc->value_mask;
> +     mutex_unlock(&gpio->lock);
> +     kfree(buf);
> +
> +     return value;
> +}
> +
> +static int pl2303_gpio_startup(struct usb_serial *serial)
> +{
> +     struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
> +     struct pl2303_gpio *gpio;
> +     int ret;
> +
> +     gpio = kzalloc(sizeof(struct pl2303_gpio), GFP_KERNEL);
> +     if (!gpio)
> +             return -ENOMEM;
> +
> +     gpio->descs = kcalloc(spriv->ngpio, sizeof(struct pl2303_gpio_desc),
> +                     GFP_KERNEL);
> +     if (!gpio->descs) {
> +             kfree(gpio);
> +             return -ENOMEM;
> +     }
> +     spriv->gpio = gpio;
> +
> +     pl2303_gpio_add(serial, 0, HXD_GPIO_01_VALUE,
> +                     0, HXD_GPIO0_DIR_MASK, HXD_GPIO_01_CTRL,
> +                     0, HXD_GPIO0_VALUE_MASK, HXD_GPIO_01_CTRL);
> +     pl2303_gpio_add(serial, 1, HXD_GPIO_01_VALUE,
> +                     0, HXD_GPIO1_DIR_MASK, HXD_GPIO_01_CTRL,
> +                     0, HXD_GPIO1_VALUE_MASK, HXD_GPIO_01_CTRL);
> +
> +     if (spriv->ngpio == 4) {
> +             pl2303_gpio_add(serial, 2, HXD_GPIO_23_VALUE,
> +                             1, HXD_GPIO2_DIR_MASK, HXD_GPIO_23_DIR_CTRL,
> +                             2, HXD_GPIO2_VALUE_MASK,
> +                             HXD_GPIO_23_VALUE_CTRL);
> +             pl2303_gpio_add(serial, 3, HXD_GPIO_23_VALUE,
> +                             1, HXD_GPIO3_DIR_MASK, HXD_GPIO_23_DIR_CTRL,
> +                             2, HXD_GPIO3_VALUE_MASK,
> +                             HXD_GPIO_23_VALUE_CTRL);
> +     }
> +
> +     gpio->serial = serial;
> +     mutex_init(&gpio->lock);
> +
> +     gpio->gpio_chip.label = "pl2303";
> +     gpio->gpio_chip.owner = THIS_MODULE;
> +     gpio->gpio_chip.direction_input = pl2303_gpio_direction_in;
> +     gpio->gpio_chip.get = pl2303_gpio_get;
> +     gpio->gpio_chip.direction_output = pl2303_gpio_direction_out;
> +     gpio->gpio_chip.set = pl2303_gpio_set;
> +     gpio->gpio_chip.can_sleep  = true;
> +     gpio->gpio_chip.ngpio = spriv->ngpio;
> +     gpio->gpio_chip.base = -1;
> +     gpio->gpio_chip.dev = &serial->interface->dev;
> +
> +     ret = gpiochip_add(&gpio->gpio_chip);
> +     if (ret < 0) {
> +             kfree(gpio);
> +             return ret;
> +     }
> +     return 0;
> +}
> +
> +static void pl2303_gpio_release(struct usb_serial *serial)
> +{
> +     struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
> +     struct pl2303_gpio *gpio = (struct pl2303_gpio *)spriv->gpio;
> +
> +     gpiochip_remove(&gpio->gpio_chip);
> +     kfree(gpio->descs);
> +     kfree(gpio);
> +}
> +#endif
> +
>  static int pl2303_startup(struct usb_serial *serial)
>  {
>       struct pl2303_serial_private *spriv;
>       enum pl2303_type type = TYPE_01;
>       unsigned char *buf;
> +     u16 bcdDevice;
> +     u8 major_revision;
> +     int ret;
>  
>       spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
>       if (!spriv)
> @@ -244,6 +475,16 @@ static int pl2303_startup(struct usb_serial *serial)
>       spriv->quirks = (unsigned long)usb_get_serial_data(serial);
>       spriv->quirks |= spriv->type->quirks;
>  
> +     spriv->ngpio = 0;
> +     if (type == TYPE_HX) {
> +             bcdDevice = le16_to_cpu(serial->dev->descriptor.bcdDevice);
> +             major_revision = bcdDevice = bcdDevice >> 8;
> +             if (major_revision == 3)
> +                     spriv->ngpio = 2;
> +             else if (major_revision == 4)
> +                     spriv->ngpio = 4;
> +     }
> +
>       usb_set_serial_data(serial, spriv);
>  
>       pl2303_vendor_read(serial, 0x8484, buf);
> @@ -263,6 +504,13 @@ static int pl2303_startup(struct usb_serial *serial)
>  
>       kfree(buf);
>  
> +     if (spriv->ngpio > 0) {
> +             ret = pl2303_gpio_startup(serial);
> +             if (ret) {
> +                     kfree(spriv);
> +                     return ret;
> +             }
> +     }
>       return 0;
>  }
>  
> @@ -270,6 +518,8 @@ static void pl2303_release(struct usb_serial *serial)
>  {
>       struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
>  
> +     if (spriv->ngpio > 0)
> +             pl2303_gpio_release(serial);
>       kfree(spriv);
>  }
>  
> -- 
> 1.8.5.5.dirty

Add Benjamin Henrion <zoo...@gmail.com> to CC list, thanks very much for his 
test work with this patch.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to