pwm.c: Fix error in mraa_pwm_read_duty
[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         return advance_func->pwm_period_replace(dev,period);
54
55     char bu[MAX_SIZE];
56     snprintf(bu,MAX_SIZE ,"/sys/class/pwm/pwmchip%d/pwm%d/period", dev->chipid, dev->pin);
57
58     int period_f = open(bu, O_RDWR);
59     if (period_f == -1) {
60         syslog(LOG_ERR, "pwm: Failed to open period for writing");
61         return MRAA_ERROR_INVALID_RESOURCE;
62     }
63     char out[MAX_SIZE];
64     int length = snprintf(out, MAX_SIZE, "%d", period);
65     if (write(period_f, out, length*sizeof(char)) == -1) {
66         close(period_f);
67         return MRAA_ERROR_INVALID_RESOURCE;
68     }
69
70     close(period_f);
71     return MRAA_SUCCESS;
72 }
73
74 static mraa_result_t
75 mraa_pwm_write_duty(mraa_pwm_context dev, int duty)
76 {
77     if (dev->duty_fp == -1) {
78         if (mraa_pwm_setup_duty_fp(dev) == 1) {
79             return MRAA_ERROR_INVALID_HANDLE;
80         }
81     }
82     char bu[64];
83     int length = sprintf(bu, "%d", duty);
84     if (write(dev->duty_fp, bu, length * sizeof(char)) == -1)
85         return MRAA_ERROR_INVALID_RESOURCE;
86     return MRAA_SUCCESS;
87 }
88
89 static int
90 mraa_pwm_read_period(mraa_pwm_context dev)
91 {
92     char bu[MAX_SIZE];
93     char output[MAX_SIZE];
94     snprintf(bu,MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/period", dev->chipid, dev->pin);
95
96     int period_f = open(bu, O_RDWR);
97     if (period_f == -1) {
98         syslog(LOG_ERR, "pwm: Failed to open period for reading");
99         return 0;
100     }
101     off_t size = lseek(period_f, 0, SEEK_END);
102     lseek(period_f, 0, SEEK_SET);
103
104     ssize_t rb = read(period_f, output, size + 1);
105     close(period_f);
106
107     if (rb < 0) {
108         syslog(LOG_ERR, "pwm: Error in reading period");
109         return -1;
110     }
111
112     char *endptr;
113     long int ret = strtol(output, &endptr, 10);
114     if ('\0' != *endptr && '\n' != *endptr) {
115         syslog(LOG_ERR, "pwm: Error in string conversion");
116         return -1;
117     }
118     else if (ret > INT_MAX || ret < INT_MIN) {
119         syslog(LOG_ERR, "pwm: Number is invalid");
120         return -1;
121     }
122     return (int) ret;
123 }
124
125 static int
126 mraa_pwm_read_duty(mraa_pwm_context dev)
127 {
128     if (dev->duty_fp == -1) {
129         if (mraa_pwm_setup_duty_fp(dev) == 1) {
130             return MRAA_ERROR_INVALID_HANDLE;
131         }
132     } else {
133         lseek(dev->duty_fp, 0, SEEK_SET);
134     }
135     off_t size = lseek(dev->duty_fp, 0, SEEK_END);
136     lseek(dev->duty_fp, 0, SEEK_SET);
137     char output[MAX_SIZE];
138     ssize_t rb = read(dev->duty_fp, output, size+1);
139     if (rb < 0) {
140         syslog(LOG_ERR, "pwm: Error in reading duty");
141         return -1;
142     }
143
144     char *endptr;
145     long int ret = strtol(output, &endptr, 10);
146     if ('\0' != *endptr && '\n' != *endptr) {
147         syslog(LOG_ERR, "pwm: Error in string converstion");
148         return -1;
149     }
150     else if (ret > INT_MAX || ret < INT_MIN) {
151         syslog(LOG_ERR, "pwm: Number is invalid");
152         return -1;
153     }
154     return (int) ret;
155 }
156
157 mraa_pwm_context
158 mraa_pwm_init(int pin)
159 {
160     if (advance_func->pwm_init_pre != NULL) {
161         if (advance_func->pwm_init_pre(pin) != MRAA_SUCCESS)
162             return NULL;
163     }
164     if (plat == NULL) {
165         syslog(LOG_ERR, "pwm: Platform Not Initialised");
166         return NULL;
167     }
168     if (plat->pins[pin].capabilites.pwm != 1) {
169         syslog(LOG_ERR, "pwm: pin not capable of pwm");
170         return NULL;
171     }
172
173     if (plat->pins[pin].capabilites.gpio == 1) {
174         // This deserves more investigation
175         mraa_gpio_context mux_i;
176         mux_i = mraa_gpio_init_raw(plat->pins[pin].gpio.pinmap);
177         if (mux_i == NULL) {
178             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
179             return NULL;
180         }
181         if (mraa_gpio_dir(mux_i, MRAA_GPIO_OUT) != MRAA_SUCCESS) {
182             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
183             return NULL;
184         }
185         if (mraa_gpio_write(mux_i, 1) != MRAA_SUCCESS) {
186             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
187             return NULL;
188         }
189         if (mraa_gpio_close(mux_i) != MRAA_SUCCESS) {
190             syslog(LOG_ERR, "pwm: error in gpio->pwm transition");
191             return NULL;
192         }
193     }
194
195     if (plat->pins[pin].pwm.mux_total > 0) {
196         if (mraa_setup_mux_mapped(plat->pins[pin].pwm) != MRAA_SUCCESS) {
197             syslog(LOG_ERR, "pwm: Failed to set-up multiplexer");
198             return NULL;
199         }
200     }
201
202     int chip = plat->pins[pin].pwm.parent_id;
203     int pinn = plat->pins[pin].pwm.pinmap;
204
205     if (advance_func->pwm_init_post != NULL) {
206         mraa_pwm_context pret = mraa_pwm_init_raw(chip,pinn);
207         mraa_result_t ret = advance_func->pwm_init_post(pret);
208         if (ret != MRAA_SUCCESS) {
209             free(pret);
210             return NULL;
211         }
212         return pret;
213     }
214     return mraa_pwm_init_raw(chip,pinn);
215 }
216
217 mraa_pwm_context
218 mraa_pwm_init_raw(int chipin, int pin)
219 {
220     mraa_pwm_context dev = (mraa_pwm_context) malloc(sizeof(struct _pwm));
221     if (dev == NULL)
222         return NULL;
223     dev->duty_fp = -1;
224     dev->chipid = chipin;
225     dev->pin = pin;
226
227     char directory[MAX_SIZE];
228     snprintf(directory, MAX_SIZE, SYSFS_PWM "/pwmchip%d/pwm%d", dev->chipid, dev->pin);
229     struct stat dir;
230     if (stat(directory, &dir) == 0 && S_ISDIR(dir.st_mode)) {
231         syslog(LOG_NOTICE, "pwm: Pin already exported, continuing");
232         dev->owner = 0; // Not Owner
233     } else {
234         char buffer[MAX_SIZE];
235         snprintf(buffer, MAX_SIZE, "/sys/class/pwm/pwmchip%d/export", dev->chipid);
236         int export_f = open(buffer, O_WRONLY);
237         if (export_f == -1) {
238             syslog(LOG_ERR, "pwm: Failed to open export for writing");
239             free(dev);
240             return NULL;
241         }
242
243         char out[MAX_SIZE];
244         int size = snprintf(out, MAX_SIZE, "%d", dev->pin);
245         if (write(export_f, out, size*sizeof(char)) == -1) {
246             syslog(LOG_WARNING, "pwm: Failed to write to export! Potentially already enabled");
247             close(export_f);
248             free(dev);
249             return NULL;
250         }
251         dev->owner = 1;
252         mraa_pwm_period_us(dev, plat->pwm_default_period);
253         close(export_f);
254     }
255     mraa_pwm_setup_duty_fp(dev);
256     return dev;
257 }
258
259 mraa_result_t
260 mraa_pwm_write(mraa_pwm_context dev, float percentage)
261 {
262     if (percentage >= 1.0f) {
263         return mraa_pwm_write_duty(dev, mraa_pwm_read_period(dev));
264     }
265     return mraa_pwm_write_duty(dev, percentage * mraa_pwm_read_period(dev));
266 }
267
268 float
269 mraa_pwm_read(mraa_pwm_context dev)
270 {
271     int period = mraa_pwm_read_period(dev);
272     if (period > 0) {
273          return (mraa_pwm_read_duty(dev) / (float) period);
274     }
275     return 0.0f;
276 }
277
278 mraa_result_t
279 mraa_pwm_period(mraa_pwm_context dev, float seconds)
280 {
281     return mraa_pwm_period_ms(dev, seconds*1000);
282 }
283
284 mraa_result_t
285 mraa_pwm_period_ms(mraa_pwm_context dev, int ms)
286 {
287     return mraa_pwm_period_us(dev, ms*1000);
288 }
289
290 mraa_result_t
291 mraa_pwm_period_us(mraa_pwm_context dev, int us)
292 {
293     if (us < plat->pwm_min_period ||
294         us > plat->pwm_max_period) {
295         syslog(LOG_ERR, "pwm: period value outside platform range");
296         return MRAA_ERROR_INVALID_PARAMETER;
297     }
298     return mraa_pwm_write_period(dev, us*1000);
299 }
300
301 mraa_result_t
302 mraa_pwm_pulsewidth(mraa_pwm_context dev, float seconds)
303 {
304     return mraa_pwm_pulsewidth_ms(dev, seconds*1000);
305 }
306
307 mraa_result_t
308 mraa_pwm_pulsewidth_ms(mraa_pwm_context dev, int ms)
309 {
310     return mraa_pwm_pulsewidth_us(dev, ms*1000);
311 }
312
313 mraa_result_t
314 mraa_pwm_pulsewidth_us(mraa_pwm_context dev, int us)
315 {
316     return mraa_pwm_write_duty(dev, us*1000);
317 }
318
319 mraa_result_t
320 mraa_pwm_enable(mraa_pwm_context dev, int enable)
321 {
322     int status;
323     if (enable != 0) {
324         status = 1;
325     } else {
326         status = enable;
327     }
328     char bu[MAX_SIZE];
329     snprintf(bu,MAX_SIZE, "/sys/class/pwm/pwmchip%d/pwm%d/enable", dev->chipid, dev->pin);
330
331     int enable_f = open(bu, O_RDWR);
332
333     if (enable_f == -1) {
334         syslog(LOG_ERR, "pwm: Failed to open enable for writing");
335         return MRAA_ERROR_INVALID_RESOURCE;
336     }
337     char out[2];
338     int size = snprintf(out, sizeof(out), "%d", enable);
339     if (write(enable_f, out, size * sizeof(char)) == -1) {
340         syslog(LOG_ERR, "pwm: Failed to write to enable");
341         close(enable_f);
342         return MRAA_ERROR_INVALID_RESOURCE;
343     }
344     close(enable_f);
345     return MRAA_SUCCESS;
346 }
347
348 mraa_result_t
349 mraa_pwm_unexport_force(mraa_pwm_context dev)
350 {
351     char filepath[MAX_SIZE];
352     snprintf(filepath, MAX_SIZE, "/sys/class/pwm/pwmchip%d/unexport", dev->chipid);
353
354     int unexport_f = open(filepath, O_WRONLY);
355     if (unexport_f == -1) {
356         syslog(LOG_ERR, "pwm: Failed to open unexport for writing");
357         return MRAA_ERROR_INVALID_RESOURCE;
358     }
359
360     char out[MAX_SIZE];
361     int size = snprintf(out, MAX_SIZE, "%d", dev->pin);
362     if (write(unexport_f, out, size*sizeof(char)) == -1) {
363         syslog(LOG_ERR, "pwm: Failed to write to unexport");
364         close(unexport_f);
365         return MRAA_ERROR_INVALID_RESOURCE;
366     }
367
368     close(unexport_f);
369     return MRAA_SUCCESS;
370 }
371
372 mraa_result_t
373 mraa_pwm_unexport(mraa_pwm_context dev)
374 {
375     mraa_pwm_enable(dev, 0);
376     if (dev->owner) {
377         return mraa_pwm_unexport_force(dev);
378     }
379     return MRAA_ERROR_INVALID_RESOURCE;
380 }
381
382 mraa_result_t
383 mraa_pwm_close(mraa_pwm_context dev)
384 {
385     mraa_pwm_unexport(dev);
386     free(dev);
387     return MRAA_SUCCESS;
388 }
389
390 mraa_result_t
391 mraa_pwm_owner(mraa_pwm_context dev, mraa_boolean_t owner_new)
392 {
393     if (dev == NULL)
394         return MRAA_ERROR_INVALID_RESOURCE;
395     dev->owner = owner_new;
396     return MRAA_SUCCESS;
397 }
398
399 mraa_result_t
400 mraa_pwm_config_ms(mraa_pwm_context dev, int ms ,float ms_float)
401 {
402     int old_dutycycle, old_period, status;
403     old_dutycycle = mraa_pwm_read_duty(dev);
404     old_period = mraa_pwm_read_period(dev);
405     status = mraa_pwm_period_us(dev, ms*1000);
406     if (status != MRAA_SUCCESS) {
407         mraa_pwm_write_duty(dev, old_dutycycle);
408         return status;
409     }
410     status = mraa_pwm_write_duty(dev, 0);
411     if (status != MRAA_SUCCESS) {
412         return status;
413     }
414     status = mraa_pwm_pulsewidth_us(dev, ms_float*1000);
415     if (status != MRAA_SUCCESS) {
416         mraa_pwm_write_duty(dev, old_dutycycle);
417         mraa_pwm_write_period(dev, old_period);
418         return status;
419     }
420     return MRAA_SUCCESS;
421 }
422
423 mraa_result_t
424 mraa_pwm_config_percent(mraa_pwm_context dev, int ms ,float percentage)
425 {
426     int old_dutycycle, old_period, status;
427     old_dutycycle = mraa_pwm_read_duty(dev);
428     old_period = mraa_pwm_read_period(dev);
429     status = mraa_pwm_period_us(dev, ms*1000);
430     if (status != MRAA_SUCCESS) {
431         mraa_pwm_write_duty(dev, old_dutycycle);
432         return status;
433     }
434     status = mraa_pwm_write_duty(dev, 0);
435     if (status != MRAA_SUCCESS) {
436         return status;
437     }
438     status = mraa_pwm_pulsewidth_us(dev, (ms*1000)*percentage);
439     if (status != MRAA_SUCCESS) {
440         mraa_pwm_write_duty(dev, old_dutycycle);
441         mraa_pwm_write_period(dev, old_period);
442         return status;
443     }
444     return MRAA_SUCCESS;
445 }