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