Initial commit
authorAndrew 'Necromant' Andrianov <andrew@ncrmnt.org>
Tue, 4 Nov 2014 17:35:47 +0000 (20:35 +0300)
committerAndrew 'Necromant' Andrianov <andrew@ncrmnt.org>
Tue, 4 Nov 2014 17:35:47 +0000 (20:35 +0300)
Signed-off-by: Andrew 'Necromant' Andrianov <andrew@ncrmnt.org>
10-pl2303userspace.rules [new file with mode: 0755]
Makefile [new file with mode: 0644]
main.c [new file with mode: 0644]
usb.c [new file with mode: 0644]

diff --git a/10-pl2303userspace.rules b/10-pl2303userspace.rules
new file mode 100755 (executable)
index 0000000..fe6af93
--- /dev/null
@@ -0,0 +1,5 @@
+# PL2303 rules to allow userspace access to the dongle
+# Copy this file to /etc/udev/rules.d, reload udev rules and replug
+
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="67b", ATTR{idProduct}=="2303", GROUP="users", MODE="0666"
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c7f3d1f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+CFLAGS  = $(shell pkg-config --cflags libusb-1.0)
+LDFLAGS = $(shell pkg-config --libs libusb-1.0)
+
+PREFIX?=/usr/local
+
+all: pl2303gpio
+
+OBJS=usb.c main.c
+pl2303gpio: $(OBJS)
+       $(CC) $(CFLAGS) -Wall -Werror -I"../include" -o $(@) $(^) $(LDFLAGS) -lusb
+
+clean:
+       -rm pl2303gpio
+
+install: pl2303gpio
+       cp pl2303gpio $(PREFIX)/bin
+       cp 10-pl2303userspace.rules /etc/udev/rules.d
\ No newline at end of file
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..1a84c2d
--- /dev/null
+++ b/main.c
@@ -0,0 +1,228 @@
+#include <stdio.h>
+#include <stdlib.h>
+/* According to POSIX.1-2001 */
+#include <sys/select.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <usb.h>
+#include <getopt.h>
+
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+
+#define I_VENDOR_NUM        0x67b
+#define I_PRODUCT_NUM       0x2303
+
+
+#define VENDOR_READ_REQUEST_TYPE       0xc0
+#define VENDOR_READ_REQUEST            0x01
+
+#define VENDOR_WRITE_REQUEST_TYPE      0x40
+#define VENDOR_WRITE_REQUEST           0x01
+
+
+void handle_error(int ret)
+{
+       if (ret<0) { 
+               perror("Failed to write to PL2303 device");
+               fprintf(stderr, "Have you installed the correct udev rules?\n");
+               exit(1);
+       }
+}
+
+
+/* Get current GPIO register from PL2303 */
+char gpio_read_reg(usb_dev_handle *h)
+{
+       char buf;
+       int bytes = usb_control_msg(
+               h,             // handle obtained with usb_open()
+               VENDOR_READ_REQUEST_TYPE, // bRequestType
+               VENDOR_READ_REQUEST,      // bRequest
+               0x0081,              // wValue
+               0,              // wIndex
+               &buf,             // pointer to destination buffer
+               1,  // wLength
+               100
+               );
+       handle_error(bytes);
+       return buf;
+}
+
+void gpio_write_reg(usb_dev_handle *h, unsigned char reg)
+{
+       int bytes = usb_control_msg(
+               h,             // handle obtained with usb_open()
+               VENDOR_WRITE_REQUEST_TYPE, // bRequestType
+               VENDOR_WRITE_REQUEST,      // bRequest
+               1,              // wValue
+               reg,              // wIndex
+               0,             // pointer to destination buffer
+               0,  // wLength
+               6000
+               );
+       handle_error(bytes);
+       
+}
+
+int gpio_dir_shift(int gpio) {
+       if (gpio == 0) 
+               return 4;
+       if (gpio == 1) 
+               return 5;
+       return 4; /* default to 0 */
+}
+
+int gpio_val_shift(int gpio) {
+       if (gpio == 0) 
+               return 6;
+       if (gpio == 1) 
+               return 7;
+       return 6; /* default to 0 */
+}
+
+
+void gpio_out(usb_dev_handle *h, int gpio, int value)
+{
+       int shift_dir = gpio_dir_shift(gpio);
+       int shift_val = gpio_val_shift(gpio);
+       unsigned char reg = gpio_read_reg(h);
+       reg |= (1 << shift_dir);
+       reg &= ~(1 << shift_val);
+       reg |= (value << shift_val);
+       gpio_write_reg(h, reg);
+}
+
+void gpio_in(usb_dev_handle *h, int gpio, int pullup)
+{
+       int shift_dir = gpio_dir_shift(gpio);
+       int shift_val = gpio_val_shift(gpio);
+
+       unsigned char reg = gpio_read_reg(h);
+       reg &= ~(1 << shift_dir);
+       reg &= ~(1 << shift_val);
+       reg |= (pullup << shift_val);
+       gpio_write_reg(h, reg);
+}
+
+int gpio_read(usb_dev_handle *h, int gpio)
+{
+       unsigned char r = gpio_read_reg(h);
+       int shift = gpio_val_shift(gpio);
+       return (r & (1<<shift));
+}
+
+static struct option long_options[] =
+{
+       /* These options set a flag. */
+       {"help",    no_argument,       0, 'h'},
+       {"gpio",    required_argument, 0, 'g'},
+       {"in",      optional_argument, 0, 'i'},
+       {"out",     required_argument, 0, 'o'},
+       {"read",    no_argument,       0, 'r'},
+       {0, 0, 0, 0}
+};
+  
+void usage(const char *self)
+{
+       printf("PL2303HXA userspace GPIO control tool\n"
+              "(c) Andrew 'Necromant' Andrianov 2014, License: GPLv3\n"
+              "Usage: %s [action1] [action2] ...\n"
+              "Options are: \n"
+              "\t -g/--gpio  n  - select GPIO, n=0, 1\n"
+              "\t -i/--in       - configure GPIO as input\n"
+              "\t -o/--out v    - configure GPIO as output with value v\n"
+              "\t -r/--read v   - Read current GPIO value\n\n"
+              "Examples: \n"
+              "\t%s --gpio=1 --out 1\n"
+              "\t%s --gpio=0 --out 0 --gpio=1 --in\n"
+              "\n", self, self, self);
+}
+
+extern usb_dev_handle *nc_usb_open(int vendor, int product, char *vendor_name, char *product_name, char *serial);
+void check_handle(usb_dev_handle **h)
+{
+       if (*h)
+               return;
+
+       /* TODO: Make a proper way to select different PL2303 devices. */
+       *h = nc_usb_open(I_VENDOR_NUM, I_PRODUCT_NUM, NULL, NULL, NULL);
+       if (!h) {
+               fprintf(stderr, "No PL2303 USB device found ;(\n");
+               exit(1);
+       }
+
+       /* We don't set config or claim interface, pl2303 kernel driver does 
+        * that for us. 
+        */
+}
+
+int main(int argc, char* argv[])
+{
+       char c;
+       usb_dev_handle *h = NULL;
+       int gpio=0; 
+       if (argc == 1) 
+       {
+               usage(argv[0]);
+               exit(1);
+       }
+       while(1) {
+               int option_index = 0;
+
+               c = getopt_long (argc, argv, "hg:i:o:r:",
+                                long_options, &option_index);
+
+               /* Detect the end of the options. */
+               if (c == -1)
+                       break;
+               
+               switch (c)
+               {
+               case 'h':
+                       usage(argv[0]);
+                       exit(1);
+                       break;
+               case 'g':
+                       gpio = atoi(optarg);
+                       break;
+               case 'i':
+               {
+                       int v=0; 
+                       check_handle(&h);
+                       if (optarg)
+                               v = atoi(optarg);
+                       gpio_in(h, gpio, v); 
+                       break;
+               }
+               case 'o':
+               {
+                       int v=0; 
+                       check_handle(&h);
+                       if (optarg)
+                               v = atoi(optarg);
+                       gpio_out(h, gpio, v); 
+                       break;
+               }
+               case 'r': 
+               {
+                       check_handle(&h);
+                       printf("%d\n", gpio_read(h, gpio) ? 1 : 0); 
+                       break;
+               }
+
+               }
+       }
+
+       return 0;
+
+       //GP0 == LDR
+       //GP1 == RST
+}
diff --git a/usb.c b/usb.c
new file mode 100644 (file)
index 0000000..92ebde9
--- /dev/null
+++ b/usb.c
@@ -0,0 +1,97 @@
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <usb.h>
+#include <libusb.h>
+
+static int did_usb_init = 0;
+
+
+static int  usb_get_string_ascii(usb_dev_handle *dev, int index, int langid, char *buf, int buflen)
+{
+       char    buffer[256];
+       int     rval, i;
+       
+       if((rval = usb_control_msg(dev, 
+                                  USB_ENDPOINT_IN, 
+                                  USB_REQ_GET_DESCRIPTOR, 
+                                  (USB_DT_STRING << 8) + index, 
+                                  langid, buffer, sizeof(buffer), 
+                                  1000)) < 0)
+               return rval;
+       if(buffer[1] != USB_DT_STRING)
+               return 0;
+       if((unsigned char)buffer[0] < rval)
+               rval = (unsigned char)buffer[0];
+       rval /= 2;
+       /* lossy conversion to ISO Latin1 */
+       for(i=1; i<rval; i++) {
+               if(i > buflen)  /* destination buffer overflow */
+                       break;
+               buf[i-1] = buffer[2 * i];
+               if(buffer[2 * i + 1] != 0)  /* outside of ISO Latin1 range */
+                       buf[i-1] = '?';
+       }
+       buf[i-1] = 0;
+       return i-1;
+}
+
+
+int usb_match_string(usb_dev_handle *handle, int index, char* string)
+{
+       char tmp[256];
+       if (string == NULL)
+               return 1; /* NULL matches anything */
+       usb_get_string_ascii(handle, index, 0x409, tmp, 256);
+       return (strcmp(string,tmp)==0);
+}
+
+usb_dev_handle *usb_check_device(struct usb_device *dev,
+                                char *vendor_name, 
+                                char *product_name, 
+                                char *serial)
+{
+       usb_dev_handle      *handle = usb_open(dev);
+       if(!handle) {
+               fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
+               return NULL;
+       }
+       if (
+               usb_match_string(handle, dev->descriptor.iManufacturer, vendor_name) &&
+               usb_match_string(handle, dev->descriptor.iProduct,      product_name) &&
+               usb_match_string(handle, dev->descriptor.iSerialNumber, serial)
+               ) {
+               return handle;
+       }
+       usb_close(handle);
+       return NULL;
+       
+}
+
+usb_dev_handle *nc_usb_open(int vendor, int product, char *vendor_name, char *product_name, char *serial)
+{
+       struct usb_bus      *bus;
+       struct usb_device   *dev;
+       usb_dev_handle      *handle = NULL;
+       
+       if(!did_usb_init++)
+               usb_init();
+
+       usb_find_busses();
+       usb_find_devices();
+
+       for(bus=usb_get_busses(); bus; bus=bus->next) {
+               for(dev=bus->devices; dev; dev=dev->next) {
+                                   if(dev->descriptor.idVendor == vendor && 
+                                      dev->descriptor.idProduct == product) {
+                                           handle = usb_check_device(dev, vendor_name, product_name, serial);
+                                           if (handle)
+                                                   return handle;
+                                   }
+               }
+       }
+       return NULL;
+}
+