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

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