Merge branch 'master' of https://github.com/szmoore/MCTX3420
[matches/MCTX3420.git] / server / bbb_pin.c
1 /**
2  * @file bbb_pin.c
3  * @purpose Implementation of BBB pin control functions and structures
4  * THIS CODE IS NOT THREADSAFE
5  */
6
7 #define _BBB_PIN_SRC
8 #include "bbb_pin.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include "options.h"
14
15 /**
16  * Structure to represent a GPIO pin
17  * Note: Only accessable to this file; to use the functions pass a GPIOId
18  */
19 typedef struct
20 {
21         bool initialised;
22         int fd_value;
23         int fd_direction;
24 } GPIO_Pin;
25
26 /**
27  * Structure to represent an ADC pin
28  * Note: Only accessable to this file; to use the functions pass a ADCId
29  */
30 typedef struct
31 {
32         bool initialised;
33         int fd_value;
34 } ADC_Pin;
35
36 /**
37  * Structure to represent a PWM pin
38  * Note: Only accessable to this file; to use the functions pass a PWMId
39  */
40 typedef struct
41 {
42         bool initialised;
43         int fd_run;
44         FILE * file_duty;
45         FILE * file_period;
46         int fd_polarity;
47 } PWM_Pin;
48
49 /** Array of GPIO pins **/
50 static GPIO_Pin g_gpio[GPIO_NUM_PINS] = {{0}};
51 /** Array of ADC pins **/
52 static ADC_Pin g_adc[ADC_NUM_PINS] = {{0}};
53 /** Array of PWM pins **/
54 static PWM_Pin g_pwm[PWM_NUM_PINS] = {{0}};
55
56 static char g_buffer[BUFSIZ] = {0};
57
58 /**
59  * Maps a GPIO number to an index into g_gpio (only for use in bbb_pin.c)
60  * If there is no index for that GPIO number, 128 is returned.
61  */
62 const unsigned char g_pin_gpio_to_index[GPIO_MAX_NUMBER+1] = {
63         128, 128, 128, 128,   0,   1, 128, 128,   2,   3,   4,   5, 128, 128,
64           6,   7, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,   8,   9,
65         128, 128,  10,  11, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
66         128, 128,  12,  13,  14,  15,  16,  17, 128, 128, 128, 128, 128, 128,
67         128, 128, 128, 128,  18,  19, 128, 128, 128,  20,  21,  22,  23,  24,
68          25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36, 128, 128,
69         128, 128,  37,  38,  39,  40, 128, 128, 128, 128, 128, 128, 128, 128,
70         128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
71          41, 128, 128,  42
72 };
73
74 /**
75  * Maps an index in g_gpio to the corresponding GPIO number.
76  */
77 const unsigned char g_pin_index_to_gpio[GPIO_NUM_PINS] = {
78           4,   5,   8,   9,  10,  11,  14,  15,  26,  27,  30,  31,  44,  45,
79          46,  47,  48,  49,  60,  61,  65,  66,  67,  68,  69,  70,  71,  72,
80          73,  74,  75,  76,  77,  78,  79,  80,  81,  86,  87,  88,  89, 112,
81         115
82 };
83
84 /**
85  * Export a GPIO pin and open the file descriptors
86  * @param pin The GPIO number to be exported
87  * @return true on success, false otherwise
88  */
89 bool GPIO_Export(int pin)
90 {
91         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
92         {
93                 AbortBool("Not a useable pin number: %d", pin);
94         }
95
96         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
97         if (gpio->initialised)
98         {
99                 Log(LOGNOTE, "GPIO %d already initialised.", pin);
100                 return true;
101         }
102
103         // Export the pin
104         sprintf(g_buffer, "%s/export", GPIO_DEVICE_PATH);
105         FILE * file_export = fopen(g_buffer, "w");
106         if (file_export == NULL)
107         {
108                 AbortBool("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
109         }
110         fprintf(file_export, "%d", pin);        
111         fclose(file_export);
112         
113         // Setup direction file descriptor
114         sprintf(g_buffer, "%s/gpio%d/direction", GPIO_DEVICE_PATH, pin);
115         gpio->fd_direction = open(g_buffer, O_RDWR);
116         if (gpio->fd_direction < 0)
117         {
118                 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
119         }
120
121         // Setup value file descriptor
122         sprintf(g_buffer, "%s/gpio%d/value", GPIO_DEVICE_PATH, pin);
123         gpio->fd_value = open(g_buffer, O_RDWR);
124         if (gpio->fd_value < 0)
125         {
126                 close(gpio->fd_direction);
127                 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
128         }
129
130         gpio->initialised = true;
131         Log(LOGDEBUG, "Exported GPIO%d", pin);
132         return true;
133 }
134
135 /**
136  * Unexport a GPIO pin and close its' file descriptors
137  * @param pin The GPIO number to be unexported
138  */
139 void GPIO_Unexport(int pin)
140 {
141         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
142         {
143                 Abort("Not a useable pin number: %d", pin);
144         }
145
146         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
147         if (!gpio->initialised)
148         {
149                 Abort("GPIO %d is already uninitialised", pin);
150         }
151
152         // Close file descriptors
153         close(gpio->fd_value);
154         close(gpio->fd_direction);
155         // Uninitialise this one
156         gpio->initialised = false;
157
158         // Unexport the pin
159         if (g_buffer[0] == '\0')
160                 sprintf(g_buffer, "%s/unexport", GPIO_DEVICE_PATH);     
161         FILE * file_export = fopen(g_buffer, "w");
162         if (file_export == NULL)
163         {
164                 Abort("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
165         }
166
167         fprintf(file_export, "%d", pin);        
168         fclose(file_export);
169 }
170
171 /**
172  * Initialise all PWM pins and open file descriptors
173  * @param pin - The sysfs pin number
174  * @return true if exported, false otherwise
175  */
176 bool PWM_Export(int pin)
177 {
178         //goto would make this easier...
179         if (pin < 0 || pin >= PWM_NUM_PINS)
180         {
181                 AbortBool("Invalid PWM pin %d specified.", pin);
182         }
183
184         PWM_Pin *pwm = &g_pwm[pin];
185         if (pwm->initialised)
186         {
187                 Log(LOGNOTE, "PWM %d already exported.", pin);
188                 return true;
189         }
190
191         // Try export the pin, doesn't matter if it's already exported.
192         sprintf(g_buffer, "%s/export", PWM_DEVICE_PATH);
193         FILE * file_export = fopen(g_buffer, "w");
194         if (file_export == NULL)
195         {
196                 AbortBool("Couldn't open %s to export PWM pin %d - %s", 
197                                 g_buffer, pin, strerror(errno));
198         }
199         fprintf(file_export, "%d\n", pin);
200         fclose(file_export);
201
202         // Open file descriptors
203         sprintf(g_buffer, "%s/pwm%d/run", PWM_DEVICE_PATH, pin);
204         pwm->fd_run = open(g_buffer, O_WRONLY);
205         if (pwm->fd_run < 0)
206         {
207                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
208         }
209
210         sprintf(g_buffer, "%s/pwm%d/polarity", PWM_DEVICE_PATH, pin);
211         pwm->fd_polarity = open(g_buffer, O_WRONLY);
212         if (pwm->fd_polarity < 0)
213         {
214                 close(pwm->fd_run);
215                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
216         }
217
218         sprintf(g_buffer, "%s/pwm%d/period_ns", PWM_DEVICE_PATH, pin);
219         pwm->file_period = fopen(g_buffer, "w");
220         if (pwm->file_period == NULL)
221         {
222                 close(pwm->fd_run);
223                 close(pwm->fd_polarity);
224                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
225         }
226
227         sprintf(g_buffer, "%s/pwm%d/duty_ns", PWM_DEVICE_PATH, pin);
228         pwm->file_duty = fopen(g_buffer, "w");
229         if (pwm->file_duty == NULL)
230         {
231                 close(pwm->fd_run);
232                 close(pwm->fd_polarity);
233                 fclose(pwm->file_period);
234                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
235         }
236
237         // Don't buffer the streams
238         setbuf(pwm->file_period, NULL);
239         setbuf(pwm->file_duty, NULL);   
240
241         pwm->initialised = true;
242         Log(LOGDEBUG, "Exported PWM%d", pin);
243         return true;
244 }
245
246
247 /**
248  * Unexport a PWM pin and close its file descriptors
249  * @param pin - The sysfs pin number
250  */
251 void PWM_Unexport(int pin)
252 {
253         if (pin < 0 || pin >= PWM_NUM_PINS)
254         {
255                 Abort("Invalid PWM pin number %d specified.", pin);
256         }
257
258         PWM_Pin *pwm = &g_pwm[pin];
259         if (!pwm->initialised)
260         {
261                 Abort("PWM %d not initialised", pin);
262         }
263
264         // Close the file descriptors
265         close(pwm->fd_polarity);
266         //Stop it, if it's still running
267         pwrite(pwm->fd_run, "0", 1, 0);
268         close(pwm->fd_run);
269         fclose(pwm->file_period);
270         fclose(pwm->file_duty);
271
272         pwm->initialised = false;
273
274         // Try unexport the pin, doesn't matter if it's already unexported.
275         sprintf(g_buffer, "%s/unexport", PWM_DEVICE_PATH);
276         FILE * file_unexport = fopen(g_buffer, "w");
277         if (file_unexport == NULL)
278         {
279                 Abort("Couldn't open %s to unexport PWM pin %d - %s", g_buffer, pin, strerror(errno));
280         }
281         fprintf(file_unexport, "%d\n", pin);
282         fclose(file_unexport);
283 }
284
285 /**
286  * Initialise ADC structures
287  * @param pin The ADC pin number
288  */
289 bool ADC_Export(int pin)
290 {
291         if (pin < 0 || pin >= ADC_NUM_PINS)
292         {
293                 AbortBool("Invalid ADC pin %d specified.", pin);
294         }
295         else if (g_adc[pin].initialised)
296         {
297                 Log(LOGNOTE, "ADC %d already initialised", pin);
298                 return true;
299         }
300
301         sprintf(g_buffer, "%s/in_voltage%d_raw", ADC_DEVICE_PATH, pin);
302         g_adc[pin].fd_value = open(g_buffer, O_RDONLY);
303         if (g_adc[pin].fd_value <0)
304         {
305                 AbortBool("Couldn't open ADC %d device file %s - %s", pin, g_buffer, strerror(errno));
306         }
307
308         g_adc[pin].initialised = true;
309         Log(LOGDEBUG, "Opened ADC %d", pin);
310         return true;
311 }
312
313 /**
314  * Unexport ADC pins
315  * @param pin The ADC pin number
316  */
317 void ADC_Unexport(int pin)
318 {
319         if (pin < 0 || pin >= ADC_NUM_PINS)
320         {
321                 Abort("Invalid ADC pin %d specified.", pin);
322         }
323         else if (!g_adc[pin].initialised)
324         {
325                 Abort("ADC %d already uninitialised", pin);
326         }
327
328         close(g_adc[pin].fd_value);     
329         g_adc[pin].fd_value = -1;
330         g_adc[pin].initialised = false;
331 }
332
333 /**
334  * Set a GPIO pin
335  * @param pin - The pin to set. MUST have been exported before calling this function.
336  */
337 bool GPIO_Set(int pin, bool value)
338 {
339         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
340         {
341                 AbortBool("Not a useable pin number: %d", pin);
342         }
343
344         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
345         if (!gpio->initialised)
346         {
347                 AbortBool("GPIO %d is not initialised.", pin);
348         }
349         //Set the pin direction
350         if (pwrite(gpio->fd_direction, "out", 3, 0) != 3)
351         {
352                 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
353         }
354
355         char c = value ? '1' : '0';
356         if (pwrite(gpio->fd_value, &c, 1, 0) != 1)
357         {
358                 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
359         }
360
361         return true;
362 }
363
364 /** 
365  * Read from a GPIO Pin
366  * @param pin - The pin to read
367  * @param result A pointer to store the result
368  * @return true on success, false otherwise
369  */
370 bool GPIO_Read(int pin, bool *result)
371 {
372         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
373         {
374                 AbortBool("Not a useable pin number: %d", pin);
375         }
376
377         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
378         if (!gpio->initialised)
379         {
380                 AbortBool("GPIO %d is not initialised.", pin);
381         }
382
383         if (pwrite(gpio->fd_direction, "in", 2, 0) != 2)
384         {
385                 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
386         }
387         
388         char c = '0';
389         if (pread(gpio->fd_value, &c, 1, 0) != 1)
390         {
391                 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
392         }
393
394         *result = (c == '1');
395         return true;
396 }
397
398 /**
399  * Activate a PWM pin
400  * @param pin - The sysfs pin number
401  * @param polarity - if true, pin is active high, else active low
402  * @param period - The period in ns
403  * @param duty - The time the pin is active in ns
404  */
405 bool PWM_Set(int pin, bool polarity, long period, long duty)
406 {
407         Log(LOGDEBUG, "Pin %d, pol %d, period: %lu, duty: %lu", pin, polarity, period, duty);
408         
409         if (pin < 0 || pin >= PWM_NUM_PINS)
410         {
411                 AbortBool("Invalid PWM pin number %d specified.", pin);
412         }
413
414         PWM_Pin *pwm = &g_pwm[pin];
415         if (!pwm->initialised)
416         {
417                 AbortBool("PWM %d is not initialised.", pin);
418         }
419
420         // Have to stop PWM before changing it
421         if (pwrite(pwm->fd_run, "0", 1, 0) != 1)
422         {
423                 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
424         }
425
426         char c = polarity ? '1' : '0';
427         if (pwrite(pwm->fd_polarity, &c, 1, 0) != 1)
428         {
429                 AbortBool("Couldn't set PWM %d polarity - %s", pin, strerror(errno));
430         }
431
432         //This must be done first, otherwise period/duty settings can conflict
433         if (fwrite("0", 1, 1, pwm->file_duty) < 1)
434         {
435                 AbortBool("Couldn't zero the duty for PWM %d - %s", pin, strerror(errno));
436         }
437
438         if (fprintf(pwm->file_period, "%lu", period) < 0)
439         {
440                 AbortBool("Couldn't set period for PWM %d - %s", pin, strerror(errno));
441         }
442
443
444         if (fprintf(pwm->file_duty, "%lu", duty) < 0)
445         {
446                 AbortBool("Couldn't set duty cycle for PWM %d - %s", pin, strerror(errno));
447         }
448
449
450         if (pwrite(pwm->fd_run, "1", 1, 0) != 1)
451         {
452                 AbortBool("Couldn't start PWM %d - %s", pin, strerror(errno));
453         }
454
455         return true;
456 }
457
458 /**
459  * Deactivate a PWM pin
460  * @param pin - The syfs pin number
461  * @return true on success, false otherwise
462  */
463 bool PWM_Stop(int pin)
464 {
465         if (pin < 0 || pin >= PWM_NUM_PINS)
466         {
467                 AbortBool("Invalid PWM pin number %d specified.", pin);
468         }
469         else if (!g_pwm[pin].initialised)
470         {
471                 AbortBool("PWM %d is not initialised.", pin);
472         }
473
474         if (pwrite(g_pwm[pin].fd_run, "0", 1, 0) != 1)
475         {
476                 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
477         }
478
479         return true;
480 }
481
482 /**
483  * Read an ADC value
484  * @param id - The ID of the ADC pin to read
485  * @param value - A pointer to store the value read from the ADC
486  * @returns - The true if succeeded, false otherwise.
487  */
488 bool ADC_Read(int id, int *value)
489 {
490         char adc_str[ADC_DIGITS] = {0};
491
492         if (id < 0 || id >= ADC_NUM_PINS)
493         {
494                 AbortBool("Invalid ADC pin %d specified.", id);
495         }
496         else if (!g_adc[id].initialised)
497         {
498                 AbortBool("ADC %d is not initialised.", id);
499         }
500
501         if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1)
502         {
503                 AbortBool("ADC %d read failed: %s", id, strerror(errno));
504         }
505
506         *value = strtol(adc_str, NULL, 10);
507         return true;
508 }
509
510 #ifndef _BBB
511 //For running on systems that are not the BBB
512 bool True_Stub(void *arg, ...) { return true; }
513 bool ADC_Read_Stub(int *val, ...) { *val = 0; return true; }
514 bool GPIO_Read_Stub(bool *val, ...) { *val = false; return true; }
515 #endif

UCC git Repository :: git.ucc.asn.au