Skip to contentSkip to conversation

usb:serial:pl2303: add GPIOs interface on PL2303

PATCH Superseded Wang YanQing 3 participants 4 messages Jul 21, 2014 – Jul 23, 2014

Subsystems (2)

v3 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 usb:serial:pl2303: add GPIOs interface on PL2303

PL2303HX has two GPIOs, this patch add interface for it.

---
Changes 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

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.

 drivers/usb/serial/Kconfig  |  10 ++++
 drivers/usb/serial/pl2303.c | 141 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 151 insertions(+)
drivers/usb/serial/Kconfig +10 -0
index 3ce5c74..4bc0d0f 100644
@@ -516,6 +516,16 @@ 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 the GPIOs on PL2303 USB Serial single port
+	  adapter from Prolific.
+
+	  It support 2 GPIOs on PL2303HX currently,
+	  if unsure, say N.
+
 config USB_SERIAL_OTI6858
 	tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller"
 	help
drivers/usb/serial/pl2303.c +141 -0

174 lines not on this page · raw message

Signature · 1 line
1.8.5.5.dirty
#1 lore raw

Hi,

Did some more review, sorry ;)

drivers/usb/serial/pl2303.c static int pl2303_probe(struct usb_serial *serial, · line 317

Quoted 9 lines · Wang YanQing
drivers/usb/serial/pl2303.c +9 -0
+static struct gpio_chip template_chip = {
+	.label                  = "pl2303-gpio",
+	.owner                  = THIS_MODULE,
+	.direction_input        = pl2303_gpio_direction_in,
+	.get                    = pl2303_gpio_get,
+	.direction_output       = pl2303_gpio_direction_out,
+	.set                    = pl2303_gpio_set,
+	.can_sleep              = 1,
+};

Could this be made static const? (since it's for one-time copy purposes only)

drivers/usb/serial/pl2303.c struct pl2303_type_data { · line 160

Quoted 9 lines
drivers/usb/serial/pl2303.c +9 -0
+struct pl2303_gpio {
+       /*
+        * 0..3: unknown (zero)
+        * 4: gp0 output enable (1: gp0 pin is output, 0: gp0 pin is input)
+        * 5: gp1 output enable (1: gp1 pin is output, 0: gp1 pin is input)
+        * 6: gp0 pin value
+        * 7: gp1 pin value
+        */
+       u8 index;

Most frequently accessed member at first position - good.

drivers/usb/serial/pl2303.c static int pl2303_probe(struct usb_serial *serial, · line 246

Quoted 5 lines

+static inline struct pl2303_gpio *to_pl2303_gpio(struct gpio_chip *chip)

drivers/usb/serial/pl2303.c +3 -0
+{
+       return container_of(chip, struct pl2303_gpio, gpio_chip);
+}

Nicely cast-avoiding helper :)

drivers/usb/serial/pl2303.c static int pl2303_startup(struct usb_serial *serial) · line 387

Quoted 8 lines
drivers/usb/serial/pl2303.c +8 -0
+		spriv->gpio->index = 0x00;
+		spriv->gpio->serial = serial;
+		spriv->gpio->gpio_chip = template_chip;
+		spriv->gpio->gpio_chip.ngpio = GPIO_NUM;
+		spriv->gpio->gpio_chip.base = -1;
+		spriv->gpio->gpio_chip.dev = &serial->interface->dev;
+		/* initialize GPIOs, input mode as default */
+		pl2303_vendor_write(spriv->gpio->serial, 1, spriv->gpio->index);

Perhaps the index line should better be moved
right before the pl2303_vendor_write() line,
to more obviously hint at why it's "input mode"
(but OTOH currently you're cleanly initializing things in correct member order, so it's probably better to keep it that way).

Also, it's perhaps a better idea to do this initial init call via calls of actual GPIO API rather than implementation-specific call (e.g. that way one could simple reuse the generic GPIO call for devices which happen to implement GPIO via a different mechanism...). (hmm, nope, from a layering POV it's not recommendable, since we clearly are at hardware-specific init here,
where you want to firmly guarantee
that the hardware is directly and fully initialized).

Since there are several repeated pl2303_vendor_write(...serial, 1, ...index) calls,
it's possibly a good idea to wrap these calls in a convenience pl2303_gpio_state_update(gpio)
This would also increase GPIO hardware abstraction,
by then simply having to provide an alternative for this single function if needed.
Also, it may (depending on the number of callers,
which is on the low side here unfortunately)
reduce instruction cache footprint.

So these are my additional thoughts about the implementation, which you may or may not choose to adopt.

HTH,

Andreas Mohr

Signature · 1 line
GNU/Linux. It's not the software that's free, it's you.
#2 lore raw
Quoted 5 lines · Andreas Mohr

Hi,

Did some more review, sorry ;)

On Mon, Jul 21, 2014 at 10:46:24AM +0800, Wang YanQing wrote:

Quoted 13 lines

drivers/usb/serial/pl2303.c static int pl2303_probe(struct usb_serial *serial, · line 317

Quoted 9 lines
drivers/usb/serial/pl2303.c +9 -0
+static struct gpio_chip template_chip = {
+	.label                  = "pl2303-gpio",
+	.owner                  = THIS_MODULE,
+	.direction_input        = pl2303_gpio_direction_in,
+	.get                    = pl2303_gpio_get,
+	.direction_output       = pl2303_gpio_direction_out,
+	.set                    = pl2303_gpio_set,
+	.can_sleep              = 1,
+};

Could this be made static const? (since it's for one-time copy purposes only)

OK, I will add const.

Quoted 37 lines

drivers/usb/serial/pl2303.c struct pl2303_type_data { · line 160

Quoted 9 lines
drivers/usb/serial/pl2303.c +9 -0
+struct pl2303_gpio {
+       /*
+        * 0..3: unknown (zero)
+        * 4: gp0 output enable (1: gp0 pin is output, 0: gp0 pin is input)
+        * 5: gp1 output enable (1: gp1 pin is output, 0: gp1 pin is input)
+        * 6: gp0 pin value
+        * 7: gp1 pin value
+        */
+       u8 index;

Most frequently accessed member at first position - good.

drivers/usb/serial/pl2303.c static int pl2303_probe(struct usb_serial *serial, · line 246

Quoted 5 lines

+static inline struct pl2303_gpio *to_pl2303_gpio(struct gpio_chip *chip)

drivers/usb/serial/pl2303.c +3 -0
+{
+       return container_of(chip, struct pl2303_gpio, gpio_chip);
+}

Nicely cast-avoiding helper :)

drivers/usb/serial/pl2303.c static int pl2303_startup(struct usb_serial *serial) · line 387

Quoted 8 lines
drivers/usb/serial/pl2303.c +8 -0
+		spriv->gpio->index = 0x00;
+		spriv->gpio->serial = serial;
+		spriv->gpio->gpio_chip = template_chip;
+		spriv->gpio->gpio_chip.ngpio = GPIO_NUM;
+		spriv->gpio->gpio_chip.base = -1;
+		spriv->gpio->gpio_chip.dev = &serial->interface->dev;
+		/* initialize GPIOs, input mode as default */
+		pl2303_vendor_write(spriv->gpio->serial, 1, spriv->gpio->index);

Perhaps the index line should better be moved
right before the pl2303_vendor_write() line,
to more obviously hint at why it's "input mode"
(but OTOH currently you're cleanly initializing things in correct member order, so it's probably better to keep it that way).

Quoted 10 lines

Also, it's perhaps a better idea to do this initial init call via calls of actual GPIO API rather than implementation-specific call (e.g. that way one could simple reuse the generic GPIO call for devices which happen to implement GPIO via a different mechanism...). (hmm, nope, from a layering POV it's not recommendable, since we clearly are at hardware-specific init here,
where you want to firmly guarantee
that the hardware is directly and fully initialized).

Thanks very much for review :)

Quoted 10 lines

Since there are several repeated pl2303_vendor_write(...serial, 1, ...index) calls,
it's possibly a good idea to wrap these calls in a convenience pl2303_gpio_state_update(gpio)
This would also increase GPIO hardware abstraction,
by then simply having to provide an alternative for this single function if needed.
Also, it may (depending on the number of callers,
which is on the low side here unfortunately)
reduce instruction cache footprint.

The reason I don't want to add more abstraction here are:
1: Abstraction don't reduce code lines, then reuse
pl2303_vendor_write|read is a better choice, i think.

2: pl2303_vendor_write|read is more generic than abstraction,
then someone maybe could use pl2303_vendor_write|read to support another two Auxiliary GPIOs on PL2303HXD(I don't have one) directly without play with abstraction.

Thanks.

#3 lore raw
In reply to Wang YanQing
Quoted 9 lines · Wang YanQing

PL2303HX has two GPIOs, this patch add interface for it.

---
Changes 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

Quoted 5 lines

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.

The sysfs ABI is not sound, using it is a recipe for trouble. IIRC it was merged at a time when drivers/gpio was unmaintained :-(

(...)

drivers/usb/serial/pl2303.c static int pl2303_probe(struct usb_serial *serial, · line 316

Quoted 8 lines
drivers/usb/serial/pl2303.c +8 -0
+static struct gpio_chip template_chip = {
+       .label                  = "pl2303-gpio",
+       .owner                  = THIS_MODULE,
+       .direction_input        = pl2303_gpio_direction_in,
+       .get                    = pl2303_gpio_get,
+       .direction_output       = pl2303_gpio_direction_out,
+       .set                    = pl2303_gpio_set,
+       .can_sleep              = 1,

This is a bool so use = true,

drivers/usb/serial/pl2303.c static void pl2303_release(struct usb_serial *serial) · line 409

Quoted 5 lines
drivers/usb/serial/pl2303.c +5 -0
+#ifdef CONFIG_USB_SERIAL_PL2303_GPIO
+       if (spriv && spriv->gpio) {
+               if (gpiochip_remove(&spriv->gpio->gpio_chip))
+                       dev_err(&serial->interface->dev,
+                               "unable to remove gpio_chip?\n");

I'm getting rid of the return code from gpiochip_remove() and have removed the __must_check tag in the gpio tree, so just call gpiochip_remove() unconditionally and ignore any compile error messages for now.

Yours,
Linus Walleij