/* Copyright (c) 2013 Adafruit Author: Justin Cooper Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "c_pwm.h" #include "common.h" #ifdef BBBVERSION41 #include "c_pinmux.h" #endif #define KEYLEN 7 #define PIN_MODE_LEN 5 int pwm_initialized = 0; // pwm exports struct pwm_exp { char key[KEYLEN+1]; /* leave room for terminating NUL byte */ int period_fd; int duty_fd; int polarity_fd; #ifdef BBBVERSION41 int enable_fd; #endif float duty; unsigned long duty_ns; unsigned long period_ns; struct pwm_exp *next; }; struct pwm_exp *exported_pwms = NULL; struct pwm_exp *lookup_exported_pwm(const char *key) { struct pwm_exp *pwm = exported_pwms; while (pwm != NULL) { if (strcmp(pwm->key, key) == 0) { return pwm; } pwm = pwm->next; } syslog(LOG_DEBUG, "Adafruit_BBIO: lookup_exported_pwm: couldn't find '%s'", key); return NULL; /* standard for pointers */ } // Export PWM to the list void export_pwm(struct pwm_exp *new_pwm) { struct pwm_exp *pwm; if (exported_pwms == NULL) { // create new list exported_pwms = new_pwm; } else { // add to end of existing list pwm = exported_pwms; while (pwm->next != NULL) pwm = pwm->next; pwm->next = new_pwm; } } BBIO_err initialize_pwm(void) { #ifdef BBBVERSION41 // don't load overlay in 4.1+ if (!pwm_initialized) { strncpy(ocp_dir, "/sys/devices/platform/ocp", sizeof(ocp_dir)); #else BBIO_err err; if (!pwm_initialized && load_device_tree("am33xx_pwm")) { err = build_path("/sys/devices", "ocp", ocp_dir, sizeof(ocp_dir)); if (err != BBIO_OK) { return BBIO_SYSFS; } #endif pwm_initialized = 1; syslog(LOG_DEBUG, "Adafruit_BBIO: initialize_pwm: OK"); return BBIO_OK; } syslog(LOG_DEBUG, "Adafruit_BBIO: initialize_pwm: OK"); return BBIO_OK; } BBIO_err pwm_set_frequency(const char *key, float freq) { int len; char buffer[100]; unsigned long period_ns; struct pwm_exp *pwm; if (freq <= 0.0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s freq %f <= 0.0", key, freq); return BBIO_INVARG; } pwm = lookup_exported_pwm(key); if (pwm == NULL) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't find key", key); return BBIO_GEN; } period_ns = (unsigned long)(1e9 / freq); // If we're going to a shorter period, update the // duty cycle first, in order to avoid ever setting // the period < duty cycle (which would throw error) if (period_ns < pwm->period_ns) { pwm->period_ns = period_ns; // Update duty ns pwm->duty_ns = (unsigned long)(period_ns * (pwm->duty / 100.0)); len = snprintf(buffer, sizeof(buffer), "%lu", pwm->duty_ns); lseek(pwm->duty_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->duty_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write duty: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } // Update period ns len = snprintf(buffer, sizeof(buffer), "%lu", period_ns); lseek(pwm->period_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->period_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write period: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } } else if (period_ns > pwm->period_ns) { pwm->period_ns = period_ns; // Ordinarily update the period first, // to avoid the opposite bug - kernel won't // let us set duty greater than period // Update period ns len = snprintf(buffer, sizeof(buffer), "%lu", period_ns); lseek(pwm->period_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->period_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write period: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } // Update duty ns pwm->duty_ns = (unsigned long)(period_ns * (pwm->duty / 100.0)); len = snprintf(buffer, sizeof(buffer), "%lu", pwm->duty_ns); lseek(pwm->duty_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->duty_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_frequency: %s couldn't write duty: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } } // else do nothing syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_set_frequency: %s %f OK", key, freq); return BBIO_OK; } // Only works before chip is enabled BBIO_err pwm_set_polarity(const char *key, int polarity) { int len; char buffer[100]; /* allow room for trailing NUL byte */ struct pwm_exp *pwm; #ifdef BBBVERSION41 int enabled; /* Maintain original state */ #endif pwm = lookup_exported_pwm(key); if (pwm == NULL) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't find key", key); return BBIO_GEN; } // polarity can't be changed with enabled. #ifdef BBBVERSION41 // Read the current enabled status len = 1; memset(buffer, 0, 9); // Initialize buffer lseek(pwm->enable_fd, 0, SEEK_SET); if (read(pwm->enable_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't read enable: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } // If the PWM is enabled, disable it // Can't set the polarity with device enabled // It will be reenabled after the parameters are set if (buffer[0] == '1') { enabled = 1; lseek(pwm->enable_fd, 0, SEEK_SET); len = snprintf(buffer, sizeof(buffer), "0"); if (write(pwm->enable_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't write enable: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } } // Treating 0 as normal, 1 as inversed // See documentation of sysfs interface at // https://www.kernel.org/doc/Documentation/pwm.txt if (polarity == 0) { len = snprintf(buffer, sizeof(buffer), "normal"); } else if (polarity == 1) { len = snprintf(buffer, sizeof(buffer), "inversed"); } else { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s invalid argument value: %i", key, polarity); return BBIO_INVARG; } #else len = snprintf(buffer, sizeof(buffer), "%d", polarity); #endif lseek(pwm->polarity_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->polarity_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't write polarity: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } /* If we were enabled before, restore state */ #ifdef BBBVERSION41 if (enabled) { lseek(pwm->enable_fd, 0, SEEK_SET); len = snprintf(buffer, sizeof(buffer), "1"); if (write(pwm->enable_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_polarity: %s couldn't write enable: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } } #endif syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_set_polarity: %s %i OK", key, polarity); return BBIO_OK; } BBIO_err pwm_set_duty_cycle(const char *key, float duty) { int len; char buffer[100]; struct pwm_exp *pwm; if (duty < 0.0 || duty > 100.0) return BBIO_INVARG; pwm = lookup_exported_pwm(key); if (pwm == NULL) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_duty_cycle: %s couldn't find key", key); return BBIO_GEN; } pwm->duty = duty; pwm->duty_ns = (unsigned long)(pwm->period_ns * (duty / 100.0)); len = snprintf(buffer, sizeof(buffer), "%lu", pwm->duty_ns); lseek(pwm->duty_fd, 0, SEEK_SET); // Seek to beginning of file if (write(pwm->duty_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_set_duty_cycle: %s couldn't write duty: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_set_duty_cycle: %s %f OK", key, duty); return BBIO_OK; } BBIO_err pwm_setup(const char *key, __attribute__ ((unused)) float duty, __attribute__ ((unused)) float freq, __attribute__ ((unused)) int polarity) { BBIO_err err; struct pwm_exp *new_pwm; #ifdef BBBVERSION41 char pwm_dev_path[100]; // "/sys/devices/platform/ocp/48300000.epwmss" char pwm_addr_path[150]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm" char pwm_chip_path[200]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0" char pwm_export_path[250]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/export" char pwm_path[250]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm2" char pwm_path_udev[250]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm-0:2" char ecap_path_udev[300]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ecap/pwm/pwmchip0/pwm-0:0/" char duty_path[300]; // "/sys/devices/platform/ocp/48300000.epwmss/48300200.ehrpwm/pwm/pwmchip0/pwm2/duty_cycle" char period_path[300]; char polarity_path[300]; char enable_path[300]; char pin_mode[PIN_MODE_LEN]; // "pwm" or "pwm2" int e; int period_fd, duty_fd, polarity_fd, enable_fd; struct stat s; FILE *f = NULL; pwm_t *p; if (!pwm_initialized) { err = initialize_pwm(); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't initialize pwm: %i", key, err); return err; } } // Make sure that one of the universal capes is loaded if( !uboot_overlay_enabled() // only check kernel overlays if u-boot overlays are not being used && !beaglebone_blue() // beaglebone blue has complete dtb file and does not need overlays && !pocketbeagle() // pocketbeagle has complete dtb file and does not need overlays && !( device_tree_loaded("cape-univ-audio") // from cdsteinkuehler/beaglebone-universal-io || device_tree_loaded("cape-univ-emmc") // "" || device_tree_loaded("cape-univ-hdmi") // "" || device_tree_loaded("cape-universal") // "" || device_tree_loaded("cape-universala") // "" || device_tree_loaded("cape-universaln") // "" || device_tree_loaded("univ-all") // from latest BeagleBone Debian 8 images || device_tree_loaded("univ-bbgw") // "" || device_tree_loaded("univ-emmc") // "" || device_tree_loaded("univ-hdmi") // "" || device_tree_loaded("univ-nhdmi"))) // "" { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s no suitable cape loaded", key); return BBIO_CAPE; } // Do pinmuxing if(!strcmp(key, "P9_28")) { // ecap2 (P9_28) requires mode pwm2 // based on bonescript commit 23bf443 by Matthew West strncpy(pin_mode, "pwm2", PIN_MODE_LEN); } else { strncpy(pin_mode, "pwm", PIN_MODE_LEN); } set_pin_mode(key, pin_mode); // Get info for pwm err = get_pwm_by_key(key, &p); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't get pwm: %i", key, err); return err; } err = build_path(ocp_dir, p->chip, pwm_dev_path, sizeof(pwm_dev_path)); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_dev_path: %i", key, err); return err; } err = build_path(pwm_dev_path, p->addr, pwm_addr_path, sizeof(pwm_addr_path)); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_addr_path: %i", key, err); return err; } err = build_path(pwm_addr_path, "pwm/pwmchip", pwm_chip_path, sizeof(pwm_chip_path)); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't build pwm_chip_path: %i", key, err); return err; } snprintf(pwm_path, sizeof(pwm_path), "%s/pwm%d", pwm_chip_path, p->index); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, pwm_path: %s", key, pwm_path); //pwm with udev patch snprintf(pwm_path_udev, sizeof(pwm_path_udev), "%s/pwm-%c:%d", pwm_chip_path, pwm_path[66], p->index); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, pwm_path_udev: %s", key, pwm_path_udev); //ecap output with udev patch snprintf(ecap_path_udev, sizeof(ecap_path_udev), "%s/pwm-%c:%d", pwm_chip_path, pwm_path[66], p->index); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: key: %s, ecap_path_udev: %s", key, ecap_path_udev); // Export PWM if hasn't already been e = stat(pwm_path, &s); if (-1 == e) { if (ENOENT == errno) { // directory does not exist snprintf(pwm_export_path, sizeof(pwm_export_path), "%s/export", pwm_chip_path); f = fopen(pwm_export_path, "w"); if (f == NULL) { // Can't open the export file syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", key, pwm_export_path, errno, strerror(errno)); return BBIO_ACCESS; } fprintf(f, "%d", p->index); fclose(f); /* sleep to avoid race condition as udev needs the opportunity to set group ownership and permission */ syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: sleep 100 ms after export to avoid udev race condition"); usleep(100 * 1000); /* 100 ms */ } else { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't stat %s: %i-%s", key, pwm_path, errno, strerror(errno)); perror("stat"); return BBIO_GEN; } } else { if (S_ISDIR(s.st_mode)) { /* It is a directory. Already exported */ } else { /* It's a file. Shouldn't ever happen */ syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s %s is not a directory", key, pwm_path); return BBIO_GEN; } } e = stat(pwm_path, &s); if (-1 == e) { if (ENOENT == errno) { // Directory still doesn't exist, try the new udev pwm path format in 4.14 kernel syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: key: %s pwm_path: %s doesn't exist", key, pwm_path); e = stat(pwm_path_udev, &s); if (-1 == e) { if (ENOENT == errno) { // Directory still doesn't exist, try the new udev ecap path format in 4.14 kernel syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: key: %s pwm_path_udev: %s doesn't exist", key, pwm_path_udev); e = stat(ecap_path_udev, &s); if (-1 == e) { if (ENOENT == errno) { // Directory still doesn't exist, exit with error syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: key: %s ecap_path_udev: %s doesn't exist", key, ecap_path_udev); syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: path for %s doesn't exist", key); return BBIO_GEN; } } else { strncpy(pwm_path, ecap_path_udev, sizeof(pwm_path)); } } } else { strncpy(pwm_path, pwm_path_udev, sizeof(pwm_path)); usleep(100*1000); } } } syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: pwm_path=%s\n", pwm_path); snprintf(duty_path, sizeof(duty_path), "%s/duty_cycle", pwm_path); snprintf(enable_path, sizeof(enable_path), "%s/enable", pwm_path); #else char fragment[100]; char pwm_fragment[100]; char pwm_path[100]; char duty_path[200]; char period_path[100]; char polarity_path[100]; int period_fd, duty_fd, polarity_fd; if (!pwm_initialized) { err = initialize_pwm(); if (err != BBIO_OK) { return err; } } // load tree snprintf(fragment, sizeof(fragment), "bone_pwm_%s", key); err = load_device_tree(fragment); if (err != BBIO_OK) { //error enabling pin for pwm return err; } //creates the fragment in order to build the pwm_test_filename, such as "pwm_test_P9_13" snprintf(pwm_fragment, sizeof(pwm_fragment), "pwm_test_%s", key); // Initialize the ocp_dir err = build_path("/sys/devices", "ocp", ocp_dir, sizeof(ocp_dir)); if (err != BBIO_OK) { return err; } //finds and builds the pwm_path, as it can be variable... err = build_path(ocp_dir, pwm_fragment, pwm_path, sizeof(pwm_path)); if (err != BBIO_OK) { return err; } //create the path for duty snprintf(duty_path, sizeof(duty_path), "%s/duty", pwm_path); #endif // create the path for period and polarity snprintf(period_path, sizeof(period_path), "%s/period", pwm_path); snprintf(polarity_path, sizeof(polarity_path), "%s/polarity", pwm_path); //add period and duty fd to pwm list if ((period_fd = open(period_path, O_RDWR)) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", key, period_path, errno, strerror(errno)); return BBIO_SYSFS; } if ((duty_fd = open(duty_path, O_RDWR)) < 0) { //error, close already opened period_fd. close(period_fd); syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", key, duty_path, errno, strerror(errno)); return BBIO_SYSFS; } if ((polarity_fd = open(polarity_path, O_RDWR)) < 0) { //error, close already opened period_fd and duty_fd. close(period_fd); close(duty_fd); syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", key, polarity_path, errno, strerror(errno)); return BBIO_SYSFS; } #ifdef BBBVERSION41 if ((enable_fd = open(enable_path, O_RDWR)) < 0) { // error, close already opened files close(period_fd); close(duty_fd); close(polarity_fd); syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't open %s: %i-%s", key, enable_path, errno, strerror(errno)); return BBIO_SYSFS; } #endif // add to list new_pwm = malloc(sizeof(struct pwm_exp)); if (new_pwm == NULL) { close(period_fd); close(duty_fd); close(polarity_fd); #ifdef BBBVERSION41 close(enable_fd); #endif syslog(LOG_ERR, "Adafruit_BBIO: pwm_setup: %s couldn't malloc pwm_exp: %i-%s", key, errno, strerror(errno)); return BBIO_MEM; // out of memory } strncpy(new_pwm->key, key, KEYLEN); /* can leave string unterminated */ new_pwm->key[KEYLEN] = '\0'; /* terminate string */ new_pwm->period_fd = period_fd; new_pwm->duty_fd = duty_fd; new_pwm->polarity_fd = polarity_fd; #ifdef BBBVERSION41 new_pwm->enable_fd = enable_fd; #endif new_pwm->next = NULL; export_pwm(new_pwm); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_setup: %s OK", key); return BBIO_OK; } BBIO_err pwm_start(const char *key, float duty, float freq, int polarity) { syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: %s, %f, %f, %i", key, duty, freq, polarity); //fprintf(stderr, "Adafruit_BBIO: pwm_start: %s, %f, %f, %i\n", key, duty, freq, polarity); BBIO_err err; char buffer[100]; ssize_t len; struct pwm_exp *pwm = lookup_exported_pwm(key); if (pwm == NULL) { err = pwm_setup(key, duty, freq, polarity); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s pwm setup failed: %i", key, err); return err; } pwm = lookup_exported_pwm(key); } // If we somehow didn't start successfully if (pwm == NULL) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s pwm is NULL", key); return BBIO_GEN; } // Read out current period_ns from the file, in order for it to behave // properly memset(buffer, 0, sizeof(buffer)); // Initialize buffer lseek(pwm->period_fd, 0, SEEK_SET); len = read(pwm->period_fd, buffer, sizeof(buffer)); if (len < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't read period: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } else if (len >= (ssize_t)sizeof(buffer)) { // If this is the case, there's more in the file. // This should never happen, as it would mean that // the period is 10^8 seconds return BBIO_SYSFS; } // Set the period_ns from the file sscanf(buffer, "%lu", &(pwm->period_ns)); // Read out the current duty_ns from the file, in order for it to // behave properly memset(buffer, 0, sizeof(buffer)); lseek(pwm->duty_fd, 0, SEEK_SET); len = read(pwm->duty_fd, buffer, sizeof(buffer)); if (len < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't read duty: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } else if (len >= (ssize_t)sizeof(buffer)) { // If this is the case, there's more in the file. // This should never happen, as it would mean that // the period is 10^8 seconds return BBIO_SYSFS; } // Set the duty_ns from the file sscanf(buffer, "%lu", &(pwm->duty_ns)); // Initialize pwm->duty to avoid weirdness pwm->duty = duty; syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: call pwm_set_frequency(key=%s freq=%f)", key, freq); err = pwm_set_frequency(key, freq); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't set duty frequency: %i", key, err); return err; } syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: call pwm_set_duty_cycle(key=%s duty=%f)", key, duty); err = pwm_set_duty_cycle(key, duty); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't set duty cycle: %i", key, err); return err; } syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: call pwm_set_polarity(key=%s polarity=%d)", key, polarity); err = pwm_set_polarity(key, polarity); if (err != BBIO_OK) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't set polarity: %i", key, err); return err; } #ifdef BBBVERSION41 // Enable the PWM (don't think it's necessary before 4.1+) if (pwm == NULL) { return BBIO_GEN; } len = snprintf(buffer, sizeof(buffer), "1"); syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: write 1 to pwm->enable_fd\n"); lseek(pwm->enable_fd, 0, SEEK_SET); if (write(pwm->enable_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_start: %s couldn't write enable: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } #endif syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_start: %s OK", key); return BBIO_OK; } BBIO_err pwm_disable(const char *key) { struct pwm_exp *pwm, *temp, *prev_pwm = NULL; #ifndef BBBVERSION41 BBIO_err err; char fragment[100]; snprintf(fragment, sizeof(fragment), "bone_pwm_%s", key); err = unload_device_tree(fragment); if (err != BBIO_OK) return err; #endif // remove from list pwm = exported_pwms; while (pwm != NULL) { if (strcmp(pwm->key, key) == 0) { #ifdef BBBVERSION41 char buffer[100]; size_t len; // Disable the PWM lseek(pwm->enable_fd, 0, SEEK_SET); len = snprintf(buffer, sizeof(buffer), "0"); if (write(pwm->enable_fd, buffer, len) < 0) { syslog(LOG_ERR, "Adafruit_BBIO: pwm_disable: %s couldn't write enable: %i-%s", key, errno, strerror(errno)); return BBIO_SYSFS; } // Unexport the PWM // TODO later #endif //close the fd #ifdef BBBVERSION41 close(pwm->enable_fd); #endif close(pwm->period_fd); close(pwm->duty_fd); close(pwm->polarity_fd); if (prev_pwm == NULL) { exported_pwms = pwm->next; prev_pwm = pwm; } else { prev_pwm->next = pwm->next; } temp = pwm; pwm = pwm->next; free(temp); } else { prev_pwm = pwm; pwm = pwm->next; } } syslog(LOG_DEBUG, "Adafruit_BBIO: pwm_disable: %s OK", key); return BBIO_OK; } void pwm_cleanup(void) { while (exported_pwms != NULL) { pwm_disable(exported_pwms->key); } }