clang-format: run clang-format on C/C++ code
[contrib/mraa.git] / src / pwm / pwm.c
1 /*
2  * Author: Thomas Ingleby <thomas.c.ingleby@intel.com>
3  * Copyright (c) 2014 Intel Corporation.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <limits.h>
29
30 #include "pwm.h"
31 #include "mraa_internal.h"
32
33 #define MAX_SIZE 64
34 #define SYSFS_PWM "/sys/class/pwm"
35
36 static int
37 mraa_pwm_setup_duty_fp(mraa_pwm_context dev)
38 {
39     char bu[MAX_SIZE];
40     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/duty_cycle", dev->chipid, dev->pin);
41
42     dev->duty_fp = open(bu, O_RDWR);
43     if (dev->duty_fp == -1) {
44         return 1;
45     }
46     return 0;
47 }
48
49 static mraa_result_t
50 mraa_pwm_write_period(mraa_pwm_context dev, int period)
51 {
52     if (advance_func->pwm_period_replace != NULL) {
53         mraa_result_t result = advance_func->pwm_period_replace(dev, period);
54         if (result == MRAA_SUCCESS) {
55             dev->period = period;
56         }
57         return result;
58     }
59     char bu[MAX_SIZE];
60     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/period", dev->chipid, dev->pin);
61
62     int period_f = open(bu, O_RDWR);
63     if (period_f == -1) {
64         syslog(LOG_ERR, "pwm: Failed to open period for writing");
65         return MRAA_ERROR_INVALID_RESOURCE;
66     }
67     char out[MAX_SIZE];
68     int length = snprintf(out, MAX_SIZE, "%d", period);
69     if (write(period_f, out, length * sizeof(char)) == -1) {
70         close(period_f);
71         return MRAA_ERROR_INVALID_RESOURCE;
72     }
73
74     close(period_f);
75     dev->period = period;
76     return MRAA_SUCCESS;
77 }
78
79 static mraa_result_t
80 mraa_pwm_write_duty(mraa_pwm_context dev, int duty)
81 {
82     if (dev->duty_fp == -1) {
83         if (mraa_pwm_setup_duty_fp(dev) == 1) {
84             return MRAA_ERROR_INVALID_HANDLE;
85         }
86     }
87     char bu[64];
88     int length = sprintf(bu, "%d", duty);
89     if (write(dev->duty_fp, bu, length * sizeof(char)) == -1)
90         return MRAA_ERROR_INVALID_RESOURCE;
91     return MRAA_SUCCESS;
92 }
93
94 static int
95 mraa_pwm_read_period(mraa_pwm_context dev)
96 {
97     char bu[MAX_SIZE];
98     char output[MAX_SIZE];
99     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/period", dev->chipid, dev->pin);
100
101     int period_f = open(bu, O_RDWR);
102     if (period_f == -1) {
103         syslog(LOG_ERR, "pwm: Failed to open period for reading");
104         return 0;
105     }
106     off_t size = lseek(period_f, 0, SEEK_END);
107     lseek(period_f, 0, SEEK_SET);
108
109     ssize_t rb = read(period_f, output, size + 1);
110     close(period_f);
111
112     if (rb < 0) {
113         syslog(LOG_ERR, "pwm: Error in reading period");
114         return -1;
115     }
116
117     char* endptr;
118     long int ret = strtol(output, &endptr, 10);
119     if ('\0' != *endptr && '\n' != *endptr) {
120         syslog(LOG_ERR, "pwm: Error in string conversion");
121         return -1;
122     } else if (ret > INT_MAX || ret < INT_MIN) {
123         syslog(LOG_ERR, "pwm: Number is invalid");
124         return -1;
125     }
126     dev->period = (int) ret;
127     return (int) ret;
128 }
129
130 static int
131 mraa_pwm_read_duty(mraa_pwm_context dev)
132 {
133     if (dev->duty_fp == -1) {
134         if (mraa_pwm_setup_duty_fp(dev) == 1) {
135             return MRAA_ERROR_INVALID_HANDLE;
136         }
137     } else {
138         lseek(dev->duty_fp, 0, SEEK_SET);
139     }
140     off_t size = lseek(dev->duty_fp, 0, SEEK_END);
141     lseek(dev->duty_fp, 0, SEEK_SET);
142     char output[MAX_SIZE];
143     ssize_t rb = read(dev->duty_fp, output, size + 1);
144     if (rb < 0) {
145         syslog(LOG_ERR, "pwm: Error in reading duty");
146         return -1;
147     }
148
149     char* endptr;
150     long int ret = strtol(output, &endptr, 10);
151     if ('\0' != *endptr && '\n' != *endptr) {
152         syslog(LOG_ERR, "pwm: Error in string converstion");
153         return -1;
154     } else if (ret > INT_MAX || ret < INT_MIN) {
155         syslog(LOG_ERR, "pwm: Number is invalid");
156         return -1;
157     }
158     return (int) ret;
159 }
160
161 mraa_pwm_context
162 mraa_pwm_init(int pin)
163 {
164     if (advance_func->pwm_init_pre != NULL) {
165         if (advance_func->pwm_init_pre(pin) != MRAA_SUCCESS)
166             return NULL;
167     }
168     if (plat == NULL) {
169         syslog(LOG_ERR, "pwm: Platform Not Initialised");
170         return NULL;
171     }
172     if (plat->pins[pin].capabilites.pwm != 1) {
173         syslog(LOG_ERR, "pwm: pin not capable of pwm");
174         return NULL;
175     }
176
177     if (plat->pins[pin].capabilites.gpio == 1) {
178         // This deserves more investigation
179         mraa_gpio_context mux_i;
180         mux_i = mraa_gpio_init_raw(plat->pins[pin].gpio.pinmap);
181         if (mux_i == NULL) {
182             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
183             return NULL;
184         }
185         if (mraa_gpio_dir(mux_i, MRAA_GPIO_OUT) != MRAA_SUCCESS) {
186             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
187             return NULL;
188         }
189         if (mraa_gpio_write(mux_i, 1) != MRAA_SUCCESS) {
190             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
191             return NULL;
192         }
193         if (mraa_gpio_close(mux_i) != MRAA_SUCCESS) {
194             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
195             return NULL;
196         }
197     }
198
199     if (plat->pins[pin].pwm.mux_total > 0) {
200         if (mraa_setup_mux_mapped(plat->pins[pin].pwm) != MRAA_SUCCESS) {
201             syslog(LOG_ERR, "pwm: Failed to set-up multiplexer");
202             return NULL;
203         }
204     }
205
206     int chip = plat->pins[pin].pwm.parent_id;
207     int pinn = plat->pins[pin].pwm.pinmap;
208
209     if (advance_func->pwm_init_post != NULL) {
210         mraa_pwm_context pret = mraa_pwm_init_raw(chip, pinn);
211         mraa_result_t ret = advance_func->pwm_init_post(pret);
212         if (ret != MRAA_SUCCESS) {
213             free(pret);
214             return NULL;
215         }
216         return pret;
217     }
218     return mraa_pwm_init_raw(chip, pinn);
219 }
220
221 mraa_pwm_context
222 mraa_pwm_init_raw(int chipin, int pin)
223 {
224     mraa_pwm_context dev = (mraa_pwm_context) malloc(sizeof(struct _pwm));
225     if (dev == NULL)
226         return NULL;
227     dev->duty_fp = -1;
228     dev->chipid = chipin;
229     dev->pin = pin;
230     dev->period = -1;
231
232     char directory[MAX_SIZE];
233     snprintf(directory, MAX_SIZE, SYSFS_PWM "/pwmchip%d/pwm%d", dev->chipid, dev->pin);
234     struct stat dir;
235     if (stat(directory, &dir) == 0 && S_ISDIR(dir.st_mode)) {
236         syslog(LOG_NOTICE, "pwm: Pin already exported, continuing");
237         dev->owner = 0; // Not Owner
238     } else {
239         char buffer[MAX_SIZE];
240         snprintf(buffer, MAX_SIZE, "/sys/class/pwm/pwmchip%d/export", dev->chipid);
241         int export_f = open(buffer, O_WRONLY);
242         if (export_f == -1) {
243             syslog(LOG_ERR, "pwm: Failed to open export for writing");
244             free(dev);
245             return NULL;
246         }
247
248         char out[MAX_SIZE];
249         int size = snprintf(out, MAX_SIZE, "%d", dev->pin);
250         if (write(export_f, out, size * sizeof(char)) == -1) {
251             syslog(LOG_WARNING, "pwm: Failed to write to export! Potentially already enabled");
252             close(export_f);
253             free(dev);
254             return NULL;
255         }
256         dev->owner = 1;
257         mraa_pwm_period_us(dev, plat->pwm_default_period);
258         close(export_f);
259     }
260     mraa_pwm_setup_duty_fp(dev);
261     return dev;
262 }
263
264 mraa_result_t
265 mraa_pwm_write(mraa_pwm_context dev, float percentage)
266 {
267     if (dev->period == -1) {
268         if (mraa_pwm_read_period(dev) <= 0)
269             return MRAA_ERROR_NO_DATA_AVAILABLE;
270     }
271
272     if (percentage >= 1.0f) {
273         return mraa_pwm_write_duty(dev, dev->period);
274     }
275     return mraa_pwm_write_duty(dev, percentage * dev->period);
276 }
277
278 float
279 mraa_pwm_read(mraa_pwm_context dev)
280 {
281     int period = mraa_pwm_read_period(dev);
282     if (period > 0) {
283         return (mraa_pwm_read_duty(dev) / (float) period);
284     }
285     return 0.0f;
286 }
287
288 mraa_result_t
289 mraa_pwm_period(mraa_pwm_context dev, float seconds)
290 {
291     return mraa_pwm_period_ms(dev, seconds * 1000);
292 }
293
294 mraa_result_t
295 mraa_pwm_period_ms(mraa_pwm_context dev, int ms)
296 {
297     return mraa_pwm_period_us(dev, ms * 1000);
298 }
299
300 mraa_result_t
301 mraa_pwm_period_us(mraa_pwm_context dev, int us)
302 {
303     if (us < plat->pwm_min_period || us > plat->pwm_max_period) {
304         syslog(LOG_ERR, "pwm: period value outside platform range");
305         return MRAA_ERROR_INVALID_PARAMETER;
306     }
307     return mraa_pwm_write_period(dev, us * 1000);
308 }
309
310 mraa_result_t
311 mraa_pwm_pulsewidth(mraa_pwm_context dev, float seconds)
312 {
313     return mraa_pwm_pulsewidth_ms(dev, seconds * 1000);
314 }
315
316 mraa_result_t
317 mraa_pwm_pulsewidth_ms(mraa_pwm_context dev, int ms)
318 {
319     return mraa_pwm_pulsewidth_us(dev, ms * 1000);
320 }
321
322 mraa_result_t
323 mraa_pwm_pulsewidth_us(mraa_pwm_context dev, int us)
324 {
325     return mraa_pwm_write_duty(dev, us * 1000);
326 }
327
328 mraa_result_t
329 mraa_pwm_enable(mraa_pwm_context dev, int enable)
330 {
331     int status;
332     if (enable != 0) {
333         status = 1;
334     } else {
335         status = enable;
336     }
337     char bu[MAX_SIZE];
338     snprintf(bu, MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/enable", dev->chipid, dev->pin);
339
340     int enable_f = open(bu, O_RDWR);
341
342     if (enable_f == -1) {
343         syslog(LOG_ERR, "pwm: Failed to open enable for writing");
344         return MRAA_ERROR_INVALID_RESOURCE;
345     }
346     char out[2];
347     int size = snprintf(out, sizeof(out), "%d", enable);
348     if (write(enable_f, out, size * sizeof(char)) == -1) {
349         syslog(LOG_ERR, "pwm: Failed to write to enable");
350         close(enable_f);
351         return MRAA_ERROR_INVALID_RESOURCE;
352     }
353     close(enable_f);
354     return MRAA_SUCCESS;
355 }
356
357 mraa_result_t
358 mraa_pwm_unexport_force(mraa_pwm_context dev)
359 {
360     char filepath[MAX_SIZE];
361     snprintf(filepath, MAX_SIZE, "/sys/class/pwm/pwmchip%d/unexport", dev->chipid);
362
363     int unexport_f = open(filepath, O_WRONLY);
364     if (unexport_f == -1) {
365         syslog(LOG_ERR, "pwm: Failed to open unexport for writing");
366         return MRAA_ERROR_INVALID_RESOURCE;
367     }
368
369     char out[MAX_SIZE];
370     int size = snprintf(out, MAX_SIZE, "%d", dev->pin);
371     if (write(unexport_f, out, size * sizeof(char)) == -1) {
372         syslog(LOG_ERR, "pwm: Failed to write to unexport");
373         close(unexport_f);
374         return MRAA_ERROR_INVALID_RESOURCE;
375     }
376
377     close(unexport_f);
378     return MRAA_SUCCESS;
379 }
380
381 mraa_result_t
382 mraa_pwm_unexport(mraa_pwm_context dev)
383 {
384     mraa_pwm_enable(dev, 0);
385     if (dev->owner) {
386         return mraa_pwm_unexport_force(dev);
387     }
388     return MRAA_ERROR_INVALID_RESOURCE;
389 }
390
391 mraa_result_t
392 mraa_pwm_close(mraa_pwm_context dev)
393 {
394     mraa_pwm_unexport(dev);
395     free(dev);
396     return MRAA_SUCCESS;
397 }
398
399 mraa_result_t
400 mraa_pwm_owner(mraa_pwm_context dev, mraa_boolean_t owner_new)
401 {
402     if (dev == NULL)
403         return MRAA_ERROR_INVALID_RESOURCE;
404     dev->owner = owner_new;
405     return MRAA_SUCCESS;
406 }
407
408 mraa_result_t
409 mraa_pwm_config_ms(mraa_pwm_context dev, int ms, float ms_float)
410 {
411     int old_dutycycle, old_period, status;
412     old_dutycycle = mraa_pwm_read_duty(dev);
413     old_period = mraa_pwm_read_period(dev);
414     status = mraa_pwm_period_us(dev, ms * 1000);
415     if (status != MRAA_SUCCESS) {
416         mraa_pwm_write_duty(dev, old_dutycycle);
417         return status;
418     }
419     status = mraa_pwm_write_duty(dev, 0);
420     if (status != MRAA_SUCCESS) {
421         return status;
422     }
423     status = mraa_pwm_pulsewidth_us(dev, ms_float * 1000);
424     if (status != MRAA_SUCCESS) {
425         mraa_pwm_write_duty(dev, old_dutycycle);
426         mraa_pwm_write_period(dev, old_period);
427         return status;
428     }
429     return MRAA_SUCCESS;
430 }
431
432 mraa_result_t
433 mraa_pwm_config_percent(mraa_pwm_context dev, int ms, float percentage)
434 {
435     int old_dutycycle, old_period, status;
436     old_dutycycle = mraa_pwm_read_duty(dev);
437     old_period = mraa_pwm_read_period(dev);
438     status = mraa_pwm_period_us(dev, ms * 1000);
439     if (status != MRAA_SUCCESS) {
440         mraa_pwm_write_duty(dev, old_dutycycle);
441         return status;
442     }
443     status = mraa_pwm_write_duty(dev, 0);
444     if (status != MRAA_SUCCESS) {
445         return status;
446     }
447     status = mraa_pwm_pulsewidth_us(dev, (ms * 1000) * percentage);
448     if (status != MRAA_SUCCESS) {
449         mraa_pwm_write_duty(dev, old_dutycycle);
450         mraa_pwm_write_period(dev, old_period);
451         return status;
452     }
453     return MRAA_SUCCESS;
454 }