Merge pull request #43 from jtanx/master
[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  * Export a GPIO pin and open the file descriptors
60  * @param pin The GPIO number to be exported
61  * @return true on success, false otherwise
62  */
63 bool GPIO_Export(int pin)
64 {
65         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
66         {
67                 AbortBool("Not a useable pin number: %d", pin);
68         }
69
70         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
71         if (gpio->initialised)
72         {
73                 Log(LOGNOTE, "GPIO %d already initialised.", pin);
74                 return true;
75         }
76
77         // Export the pin
78         sprintf(g_buffer, "%s/export", GPIO_DEVICE_PATH);
79         FILE * file_export = fopen(g_buffer, "w");
80         if (file_export == NULL)
81         {
82                 AbortBool("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
83         }
84         fprintf(file_export, "%d", pin);        
85         fclose(file_export);
86         
87         // Setup direction file descriptor
88         sprintf(g_buffer, "%s/gpio%d/direction", GPIO_DEVICE_PATH, pin);
89         gpio->fd_direction = open(g_buffer, O_RDWR);
90         if (gpio->fd_direction < 0)
91         {
92                 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
93         }
94
95         // Setup value file descriptor
96         sprintf(g_buffer, "%s/gpio%d/value", GPIO_DEVICE_PATH, pin);
97         gpio->fd_value = open(g_buffer, O_RDWR);
98         if (gpio->fd_value < 0)
99         {
100                 close(gpio->fd_direction);
101                 AbortBool("Couldn't open %s for GPIO pin %d - %s", g_buffer, pin, strerror(errno));
102         }
103
104         gpio->initialised = true;
105         Log(LOGDEBUG, "Exported GPIO%d", pin);
106         return true;
107 }
108
109 /**
110  * Unexport a GPIO pin and close its' file descriptors
111  * @param pin The GPIO number to be unexported
112  */
113 void GPIO_Unexport(int pin)
114 {
115         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
116         {
117                 Abort("Not a useable pin number: %d", pin);
118         }
119
120         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
121         if (!gpio->initialised)
122         {
123                 Abort("GPIO %d is already uninitialised", pin);
124         }
125
126         // Close file descriptors
127         close(gpio->fd_value);
128         close(gpio->fd_direction);
129         // Uninitialise this one
130         gpio->initialised = false;
131
132         // Unexport the pin
133         if (g_buffer[0] == '\0')
134                 sprintf(g_buffer, "%s/unexport", GPIO_DEVICE_PATH);     
135         FILE * file_export = fopen(g_buffer, "w");
136         if (file_export == NULL)
137         {
138                 Abort("Couldn't open %s to export GPIO pin %d - %s", g_buffer, pin, strerror(errno));
139         }
140
141         fprintf(file_export, "%d", pin);        
142         fclose(file_export);
143 }
144
145 /**
146  * Initialise all PWM pins and open file descriptors
147  * @param pin - The sysfs pin number
148  * @return true if exported, false otherwise
149  */
150 bool PWM_Export(int pin)
151 {
152         //goto would make this easier...
153         if (pin < 0 || pin >= PWM_NUM_PINS)
154         {
155                 AbortBool("Invalid PWM pin %d specified.", pin);
156         }
157
158         PWM_Pin *pwm = &g_pwm[pin];
159         if (pwm->initialised)
160         {
161                 Log(LOGNOTE, "PWM %d already exported.", pin);
162                 return true;
163         }
164
165         // Try export the pin, doesn't matter if it's already exported.
166         sprintf(g_buffer, "%s/export", PWM_DEVICE_PATH);
167         FILE * file_export = fopen(g_buffer, "w");
168         if (file_export == NULL)
169         {
170                 AbortBool("Couldn't open %s to export PWM pin %d - %s", 
171                                 g_buffer, pin, strerror(errno));
172         }
173         fprintf(file_export, "%d\n", pin);
174         fclose(file_export);
175
176         // Open file descriptors
177         sprintf(g_buffer, "%s/pwm%d/run", PWM_DEVICE_PATH, pin);
178         pwm->fd_run = open(g_buffer, O_WRONLY);
179         if (pwm->fd_run < 0)
180         {
181                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
182         }
183
184         sprintf(g_buffer, "%s/pwm%d/polarity", PWM_DEVICE_PATH, pin);
185         pwm->fd_polarity = open(g_buffer, O_WRONLY);
186         if (pwm->fd_polarity < 0)
187         {
188                 close(pwm->fd_run);
189                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
190         }
191
192         sprintf(g_buffer, "%s/pwm%d/period_ns", PWM_DEVICE_PATH, pin);
193         pwm->file_period = fopen(g_buffer, "w");
194         if (pwm->file_period == NULL)
195         {
196                 close(pwm->fd_run);
197                 close(pwm->fd_polarity);
198                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
199         }
200
201         sprintf(g_buffer, "%s/pwm%d/duty_ns", PWM_DEVICE_PATH, pin);
202         pwm->file_duty = fopen(g_buffer, "w");
203         if (pwm->file_duty == NULL)
204         {
205                 close(pwm->fd_run);
206                 close(pwm->fd_polarity);
207                 fclose(pwm->file_period);
208                 AbortBool("Couldn't open %s for PWM%d - %s", g_buffer, pin, strerror(errno));
209         }
210
211         // Don't buffer the streams
212         setbuf(pwm->file_period, NULL);
213         setbuf(pwm->file_duty, NULL);   
214
215         pwm->initialised = true;
216         Log(LOGDEBUG, "Exported PWM%d", pin);
217         return true;
218 }
219
220
221 /**
222  * Unexport a PWM pin and close its file descriptors
223  * @param pin - The sysfs pin number
224  */
225 void PWM_Unexport(int pin)
226 {
227         if (pin < 0 || pin >= PWM_NUM_PINS)
228         {
229                 Abort("Invalid PWM pin number %d specified.", pin);
230         }
231
232         PWM_Pin *pwm = &g_pwm[pin];
233         if (!pwm->initialised)
234         {
235                 Abort("PWM %d not initialised", pin);
236         }
237
238         // Close the file descriptors
239         close(pwm->fd_polarity);
240         //Stop it, if it's still running
241         pwrite(pwm->fd_run, "0", 1, 0);
242         close(pwm->fd_run);
243         fclose(pwm->file_period);
244         fclose(pwm->file_duty);
245
246         pwm->initialised = false;
247
248         // Try unexport the pin, doesn't matter if it's already unexported.
249         sprintf(g_buffer, "%s/unexport", PWM_DEVICE_PATH);
250         FILE * file_unexport = fopen(g_buffer, "w");
251         if (file_unexport == NULL)
252         {
253                 Abort("Couldn't open %s to unexport PWM pin %d - %s", g_buffer, pin, strerror(errno));
254         }
255         fprintf(file_unexport, "%d\n", pin);
256         fclose(file_unexport);
257 }
258
259 /**
260  * Initialise ADC structures
261  * @param pin The ADC pin number
262  */
263 bool ADC_Export(int pin)
264 {
265         if (pin < 0 || pin >= ADC_NUM_PINS)
266         {
267                 AbortBool("Invalid ADC pin %d specified.", pin);
268         }
269         else if (g_adc[pin].initialised)
270         {
271                 Log(LOGNOTE, "ADC %d already initialised", pin);
272                 return true;
273         }
274
275         sprintf(g_buffer, "%s/in_voltage%d_raw", g_options.adc_device_path, pin);
276         g_adc[pin].fd_value = open(g_buffer, O_RDONLY);
277         if (g_adc[pin].fd_value <0)
278         {
279                 AbortBool("Couldn't open ADC %d device file %s - %s", pin, g_buffer, strerror(errno));
280         }
281
282         g_adc[pin].initialised = true;
283         Log(LOGDEBUG, "Opened ADC %d", pin);
284         return true;
285 }
286
287 /**
288  * Unexport ADC pins
289  * @param pin The ADC pin number
290  */
291 void ADC_Unexport(int pin)
292 {
293         if (pin < 0 || pin >= ADC_NUM_PINS)
294         {
295                 Abort("Invalid ADC pin %d specified.", pin);
296         }
297         else if (!g_adc[pin].initialised)
298         {
299                 Abort("ADC %d already uninitialised", pin);
300         }
301
302         close(g_adc[pin].fd_value);     
303         g_adc[pin].fd_value = -1;
304         g_adc[pin].initialised = false;
305 }
306
307 /**
308  * Set a GPIO pin
309  * @param pin - The pin to set. MUST have been exported before calling this function.
310  */
311 bool GPIO_Set(int pin, bool value)
312 {
313         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
314         {
315                 AbortBool("Not a useable pin number: %d", pin);
316         }
317
318         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
319         if (!gpio->initialised)
320         {
321                 AbortBool("GPIO %d is not initialised.", pin);
322         }
323         //Set the pin direction
324         if (pwrite(gpio->fd_direction, "out", 3, 0) != 3)
325         {
326                 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
327         }
328
329         char c = value ? '1' : '0';
330         if (pwrite(gpio->fd_value, &c, 1, 0) != 1)
331         {
332                 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
333         }
334
335         return true;
336 }
337
338 /** 
339  * Read from a GPIO Pin
340  * @param pin - The pin to read
341  * @param result A pointer to store the result
342  * @return true on success, false otherwise
343  */
344 bool GPIO_Read(int pin, bool *result)
345 {
346         if (pin < 0 || pin > GPIO_MAX_NUMBER || g_pin_gpio_to_index[pin] == 128)
347         {
348                 AbortBool("Not a useable pin number: %d", pin);
349         }
350
351         GPIO_Pin *gpio = &g_gpio[g_pin_gpio_to_index[pin]];
352         if (!gpio->initialised)
353         {
354                 AbortBool("GPIO %d is not initialised.", pin);
355         }
356
357         if (pwrite(gpio->fd_direction, "in", 2, 0) != 2)
358         {
359                 AbortBool("Couldn't set GPIO %d direction - %s", pin, strerror(errno));
360         }
361         
362         char c = '0';
363         if (pread(gpio->fd_value, &c, 1, 0) != 1)
364         {
365                 AbortBool("Couldn't read GPIO %d value - %s", pin, strerror(errno));
366         }
367
368         *result = (c == '1');
369         return true;
370 }
371
372 /**
373  * Activate a PWM pin
374  * @param pin - The sysfs pin number
375  * @param polarity - if true, pin is active high, else active low
376  * @param period - The period in ns
377  * @param duty - The time the pin is active in ns
378  */
379 bool PWM_Set(int pin, bool polarity, long period, long duty)
380 {
381         Log(LOGDEBUG, "Pin %d, pol %d, period: %lu, duty: %lu", pin, polarity, period, duty);
382         
383         if (pin < 0 || pin >= PWM_NUM_PINS)
384         {
385                 AbortBool("Invalid PWM pin number %d specified.", pin);
386         }
387
388         PWM_Pin *pwm = &g_pwm[pin];
389         if (!pwm->initialised)
390         {
391                 AbortBool("PWM %d is not initialised.", pin);
392         }
393
394         // Have to stop PWM before changing it
395         if (pwrite(pwm->fd_run, "0", 1, 0) != 1)
396         {
397                 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
398         }
399
400         char c = polarity ? '1' : '0';
401         if (pwrite(pwm->fd_polarity, &c, 1, 0) != 1)
402         {
403                 AbortBool("Couldn't set PWM %d polarity - %s", pin, strerror(errno));
404         }
405
406         //This must be done first, otherwise period/duty settings can conflict
407         if (fwrite("0", 1, 1, pwm->file_duty) < 1)
408         {
409                 AbortBool("Couldn't zero the duty for PWM %d - %s", pin, strerror(errno));
410         }
411
412         if (fprintf(pwm->file_period, "%lu", period) < 0)
413         {
414                 AbortBool("Couldn't set period for PWM %d - %s", pin, strerror(errno));
415         }
416
417
418         if (fprintf(pwm->file_duty, "%lu", duty) < 0)
419         {
420                 AbortBool("Couldn't set duty cycle for PWM %d - %s", pin, strerror(errno));
421         }
422
423
424         if (pwrite(pwm->fd_run, "1", 1, 0) != 1)
425         {
426                 AbortBool("Couldn't start PWM %d - %s", pin, strerror(errno));
427         }
428
429         return true;
430 }
431
432 /**
433  * Deactivate a PWM pin
434  * @param pin - The syfs pin number
435  * @return true on success, false otherwise
436  */
437 bool PWM_Stop(int pin)
438 {
439         if (pin < 0 || pin >= PWM_NUM_PINS)
440         {
441                 AbortBool("Invalid PWM pin number %d specified.", pin);
442         }
443         else if (!g_pwm[pin].initialised)
444         {
445                 AbortBool("PWM %d is not initialised.", pin);
446         }
447
448         if (pwrite(g_pwm[pin].fd_run, "0", 1, 0) != 1)
449         {
450                 AbortBool("Couldn't stop PWM %d - %s", pin, strerror(errno));
451         }
452
453         return true;
454 }
455
456 /**
457  * Read an ADC value
458  * @param id - The ID of the ADC pin to read
459  * @param value - A pointer to store the value read from the ADC
460  * @returns - The true if succeeded, false otherwise.
461  */
462 bool ADC_Read(int id, int *value)
463 {
464         char adc_str[ADC_DIGITS] = {0};
465
466         if (id < 0 || id >= ADC_NUM_PINS)
467         {
468                 AbortBool("Invalid ADC pin %d specified.", id);
469         }
470         else if (!g_adc[id].initialised)
471         {
472                 AbortBool("ADC %d is not initialised.", id);
473         }
474
475         if (pread(g_adc[id].fd_value, adc_str, ADC_DIGITS-1, 0) == -1)
476         {
477                 AbortBool("ADC %d read failed: %s", id, strerror(errno));
478         }
479
480         *value = strtol(adc_str, NULL, 10);
481         return true;
482 }

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