Do not segfault when no device is present
[pub/pl2303-ft232-gpio.git] / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 /* According to POSIX.1-2001 */
4 #include <sys/select.h>
5 #include <string.h>
6 #include <sys/time.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <termios.h>
10 #include <fcntl.h>
11 #include <signal.h>
12 #include <usb.h>
13 #include <getopt.h>
14
15
16 #define _GNU_SOURCE
17 #include <getopt.h>
18
19
20 #define I_VENDOR_NUM 0x67b
21 #define I_PRODUCT_NUM 0x2303
22
23
24 #define VENDOR_READ_REQUEST_TYPE 0xc0
25 #define VENDOR_READ_REQUEST 0x01
26
27 #define VENDOR_WRITE_REQUEST_TYPE 0x40
28 #define VENDOR_WRITE_REQUEST 0x01
29
30
31 void handle_error(int ret)
32 {
33 if (ret<0) {
34 perror("Failed to write to PL2303 device");
35 fprintf(stderr, "Have you installed the correct udev rules?\n");
36 exit(1);
37 }
38 }
39
40
41 /* Get current GPIO register from PL2303 */
42 char gpio_read_reg(usb_dev_handle *h)
43 {
44 char buf;
45 int bytes = usb_control_msg(
46 h, // handle obtained with usb_open()
47 VENDOR_READ_REQUEST_TYPE, // bRequestType
48 VENDOR_READ_REQUEST, // bRequest
49 0x0081, // wValue
50 0, // wIndex
51 &buf, // pointer to destination buffer
52 1, // wLength
53 100
54 );
55 handle_error(bytes);
56 return buf;
57 }
58
59 void gpio_write_reg(usb_dev_handle *h, unsigned char reg)
60 {
61 int bytes = usb_control_msg(
62 h, // handle obtained with usb_open()
63 VENDOR_WRITE_REQUEST_TYPE, // bRequestType
64 VENDOR_WRITE_REQUEST, // bRequest
65 1, // wValue
66 reg, // wIndex
67 0, // pointer to destination buffer
68 0, // wLength
69 6000
70 );
71 handle_error(bytes);
72
73 }
74
75 int gpio_dir_shift(int gpio) {
76 if (gpio == 0)
77 return 4;
78 if (gpio == 1)
79 return 5;
80 return 4; /* default to 0 */
81 }
82
83 int gpio_val_shift(int gpio) {
84 if (gpio == 0)
85 return 6;
86 if (gpio == 1)
87 return 7;
88 return 6; /* default to 0 */
89 }
90
91
92 void gpio_out(usb_dev_handle *h, int gpio, int value)
93 {
94 int shift_dir = gpio_dir_shift(gpio);
95 int shift_val = gpio_val_shift(gpio);
96 unsigned char reg = gpio_read_reg(h);
97 reg |= (1 << shift_dir);
98 reg &= ~(1 << shift_val);
99 reg |= (value << shift_val);
100 gpio_write_reg(h, reg);
101 }
102
103 void gpio_in(usb_dev_handle *h, int gpio, int pullup)
104 {
105 int shift_dir = gpio_dir_shift(gpio);
106 int shift_val = gpio_val_shift(gpio);
107
108 unsigned char reg = gpio_read_reg(h);
109 reg &= ~(1 << shift_dir);
110 reg &= ~(1 << shift_val);
111 reg |= (pullup << shift_val);
112 gpio_write_reg(h, reg);
113 }
114
115 int gpio_read(usb_dev_handle *h, int gpio)
116 {
117 unsigned char r = gpio_read_reg(h);
118 int shift = gpio_val_shift(gpio);
119 return (r & (1<<shift));
120 }
121
122 static struct option long_options[] =
123 {
124 /* These options set a flag. */
125 {"help", no_argument, 0, 'h'},
126 {"gpio", required_argument, 0, 'g'},
127 {"in", optional_argument, 0, 'i'},
128 {"out", required_argument, 0, 'o'},
129 {"sleep", required_argument, 0, 's'},
130 {"read", no_argument, 0, 'r'},
131 {0, 0, 0, 0}
132 };
133
134 void usage(const char *self)
135 {
136 printf("PL2303HXA userspace GPIO control tool\n"
137 "(c) Andrew 'Necromant' Andrianov 2014, License: GPLv3\n"
138 "Usage: %s [action1] [action2] ...\n"
139 "Options are: \n"
140 "\t -g/--gpio n - select GPIO, n=0, 1\n"
141 "\t -i/--in - configure GPIO as input\n"
142 "\t -o/--out v - configure GPIO as output with value v\n"
143 "\t -r/--read v - Read current GPIO value\n\n"
144 "\t -s/--sleep v - Delay for v ms\n\n"
145 "Examples: \n"
146 "\t%s --gpio=1 --out 1\n"
147 "\t%s --gpio=0 --out 0 --gpio=1 --in\n\n"
148 "All arguments are executed from left to right, you can add \n"
149 "delays using --sleep v option. e.g. \n"
150 "\t%s --gpio=0 --out 0 --sleep 1000 --gpio=0 --out 1\n"
151 "\n", self, self, self, self);
152 }
153
154 extern usb_dev_handle *nc_usb_open(int vendor, int product, char *vendor_name, char *product_name, char *serial);
155 void check_handle(usb_dev_handle **h)
156 {
157 if (*h)
158 return;
159
160 /* TODO: Make a proper way to select different PL2303 devices. */
161 *h = nc_usb_open(I_VENDOR_NUM, I_PRODUCT_NUM, NULL, NULL, NULL);
162 if (!(*h)) {
163 fprintf(stderr, "No PL2303 USB device %04x:%04x found ;(\n", I_VENDOR_NUM, I_PRODUCT_NUM);
164 exit(1);
165 }
166
167 /* We don't set config or claim interface, pl2303 kernel driver does
168 * that for us.
169 */
170 }
171
172 int main(int argc, char* argv[])
173 {
174 char c;
175 usb_dev_handle *h = NULL;
176 int gpio=0;
177 if (argc == 1)
178 {
179 usage(argv[0]);
180 exit(1);
181 }
182 while(1) {
183 int option_index = 0;
184
185 c = getopt_long (argc, argv, "hg:i:o:r:s:",
186 long_options, &option_index);
187
188 /* Detect the end of the options. */
189 if (c == -1)
190 break;
191
192 switch (c)
193 {
194 case 'h':
195 usage(argv[0]);
196 exit(1);
197 break;
198 case 'g':
199 gpio = atoi(optarg);
200 break;
201 case 'i':
202 {
203 int v=0;
204 check_handle(&h);
205 if (optarg)
206 v = atoi(optarg);
207 gpio_in(h, gpio, v);
208 break;
209 }
210 case 'o':
211 {
212 int v=0;
213 check_handle(&h);
214 if (optarg)
215 v = atoi(optarg);
216 gpio_out(h, gpio, v);
217 break;
218 }
219 case 's':
220 {
221 unsigned long n = atoi(optarg);
222 usleep(n*1000);
223 break;
224 }
225 case 'r':
226 {
227 check_handle(&h);
228 printf("%d\n", gpio_read(h, gpio) ? 1 : 0);
229 break;
230 }
231
232 }
233 }
234 return 0;
235 }