1 /* 2 * Author: Thomas Ingleby <thomas.c.ingleby (at) intel.com> 3 * Author: Brendan Le Foll <brendan.le.foll (at) 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 488 mraa_pwm_get_min_period() 489 { 490 if (plat == NULL) { 491 return -1; 492 } 493 return plat->pwm_min_period; 494 } 495