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