From 8fceae15a15edd918ef9c72bdf575ad3fec81e14 Mon Sep 17 00:00:00 2001 From: "steve.jeong" Date: Thu, 17 Nov 2022 12:37:24 +0900 Subject: [PATCH] debian: update package version 3.14.1 ODROID-COMMON: support hardware PWM with sysnode. Support pwm with gpiomem mode. not sys mode. ANDROID: Add Android.mk for android. Signed-off-by: steve.jeong Change-Id: Ic6005c0cb81586c9f945b9b6c75f904fa79e3915 --- Android.mk | 8 + debian/changelog | 7 + debian/odroid-wiringpi.install | 1 + debian/odroid-wiringpi.postrm | 2 + etc/odroid-wpi-pwm.sh | 18 ++ etc/sudoers.d/odroid-wpi-sudoers | 2 + udev/rules.d/99-odroid-wiringpi-pwm.rules | 1 + wiringPi/odroidc4.c | 278 +++++++++++++++++++++- wiringPi/odroidc4.h | 2 + wiringPi/wiringPi.c | 14 ++ wiringPi/wiringPi.h | 3 + 11 files changed, 334 insertions(+), 2 deletions(-) create mode 100644 Android.mk create mode 100644 etc/odroid-wpi-pwm.sh create mode 100644 etc/sudoers.d/odroid-wpi-sudoers create mode 100644 udev/rules.d/99-odroid-wiringpi-pwm.rules diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..6f3ad98 --- /dev/null +++ b/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := odroid-wpi-pwm.sh +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +LOCAL_SRC_FILES := etc/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) diff --git a/debian/changelog b/debian/changelog index 33a08a1..b0832a8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +odroid-wiringpi (3.14.1) stable; urgency=medium + + * ODROID-COMMON: allow permission pwm sys nodes and use with gpiomem mode. + * ANDROID: Add Android.mk for android. + + -- steve Wed, 23 Nov 2022 10:41:05 +0900 + odroid-wiringpi (3.14.0) stable; urgency=medium * ODROID-N2L: new support ODROID-N2L diff --git a/debian/odroid-wiringpi.install b/debian/odroid-wiringpi.install index b842a38..b142a5e 100644 --- a/debian/odroid-wiringpi.install +++ b/debian/odroid-wiringpi.install @@ -1 +1,2 @@ udev/rules.d/* usr/lib/udev/rules.d/ +etc/* etc/ diff --git a/debian/odroid-wiringpi.postrm b/debian/odroid-wiringpi.postrm index 3284f91..e6d3158 100644 --- a/debian/odroid-wiringpi.postrm +++ b/debian/odroid-wiringpi.postrm @@ -5,6 +5,8 @@ set -e case "$1" in purge) rm -f /etc/udev/rules.d/99-odroid-wiringpi-* + rm -f /etc/odroid-wpi-pwm.sh + rm -f /etc/sudoers.d/odroid-wpi-sudoers ;; remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) diff --git a/etc/odroid-wpi-pwm.sh b/etc/odroid-wpi-pwm.sh new file mode 100644 index 0000000..a2f8d74 --- /dev/null +++ b/etc/odroid-wpi-pwm.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# /etc/odroid-wpi-pwm.sh +# written by steve.jeong +# +# allow access pwm sys node (with odroid-wiringpi gpiomem). +# udev rules: 99-odroid-wiringpi-pwm.rules +# param: "/sys/class/pwm/pwmchip*" default. + +cutoff=0 + +while [ ! -d "$1" ]; do + cutoff=$(expr ${cutoff} + 1) + [ ${cutoff} -gt 5 ] && break + sleep .1 +done + +chmod -R ugo+rw $1 diff --git a/etc/sudoers.d/odroid-wpi-sudoers b/etc/sudoers.d/odroid-wpi-sudoers new file mode 100644 index 0000000..d37bca4 --- /dev/null +++ b/etc/sudoers.d/odroid-wpi-sudoers @@ -0,0 +1,2 @@ +# Allow members of the odroid group change permission of wpi +%odroid ALL = (ALL) NOPASSWD: /usr/bin/sh /etc/odroid-wpi-pwm.sh* diff --git a/udev/rules.d/99-odroid-wiringpi-pwm.rules b/udev/rules.d/99-odroid-wiringpi-pwm.rules new file mode 100644 index 0000000..da2d183 --- /dev/null +++ b/udev/rules.d/99-odroid-wiringpi-pwm.rules @@ -0,0 +1 @@ +ACTION=="add", KERNEL=="pwmchip*", SUBSYSTEM=="pwm", RUN+="/bin/sh /etc/odroid-wpi-pwm.sh /sys/class/pwm/pwmchip*" diff --git a/wiringPi/odroidc4.c b/wiringPi/odroidc4.c index 347162d..0a7b42d 100644 --- a/wiringPi/odroidc4.c +++ b/wiringPi/odroidc4.c @@ -5,6 +5,7 @@ // // /*----------------------------------------------------------------------------*/ +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include /*----------------------------------------------------------------------------*/ #include "softPwm.h" @@ -47,7 +49,7 @@ static const int pinToGpio[64] = { 474, 475, // 30 | 31 : GPIOA.14(I2C-3_SDA), GPIOA.15(I2C-3_SCL) // Padding: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32...47 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 48...63 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 48...63 }; static const int phyToGpio[64] = { @@ -79,6 +81,62 @@ static const int phyToGpio[64] = { -1, -1, -1, -1, -1, -1, -1 // 57...63 }; +static const char *pinToPwm[64] = { + // wiringPi number to pwm group number + "ffd1a000", "ffd19000", // 0 | 1 : GPIOX.3(PWM_CD), GPIOX.16(PWM_EF) + "None", "ffd19000", // 2 | 3 : GPIOX.4, GPIOX.7(PWM_EF) + "None", "None", // 4 | 5 : GPIOX.0, GPIOX.1 + "None", "ffd1a000", // 6 | 7 : GPIOX.2, GPIOX.5(PWM_CD) + "None", "None", // 8 | 9 : GPIOX.17(I2C-2_SDA), GPIOX.18(I2C-2_SCL) + "None", "None", // 10 | 11 : GPIOX.10(SPI_SS), GPIOH.6 + "None", "None", // 12 | 13 : GPIOX.8(SPI_MOSI), GPIOX.9(SPI_MISO) + "None", "None", // 14 | 15 : GPIOX.11(SPI_CLK), GPIOX.12(UART_TX_B) + "None", "None", // 16 | 17 : GPIOX.13(UART_RX_B), + "None", "None", // 18 | 19 : + "None", "None", // 20 | 21 : , GPIOX.14 + "None", "ffd1b000", // 22 | 23 : GPIOX.15, GPIOX.6(PWM_AB) + "ffd1b000", "None", // 24 | 25 : GPIOX.19(PWM_AB), ADC.AIN3 + "None", "None", // 26 | 27 : GPIOH.7, GPIOH.5 + "None", "None", // 28 | 29 : REF1.8V OUT, ADC.AIC4 + "None", "None", // 30 | 31 : GPIOA.14(I2C-3_SDA), GPIOA.15(I2C-3_SCL) + // Padding: + "None","None","None","None","None","None","None","None","None","None","None","None","None","None","None","None", // 32...47 + "None","None","None","None","None","None","None","None","None","None","None","None","None","None","None","None" // 48...63 +}; + +static const int pinToPwmNum[64] = { + // wiringPi number to pwm pin number + 3, 4, // 0 | 1 : GPIOX.3(PWM_D), GPIOX.16(PWM_E) + -1, 5, // 2 | 3 : GPIOX.4, GPIOX.7(PWM_F) + -1, -1, // 4 | 5 : GPIOX.0, GPIOX.1 + -1, 2, // 6 | 7 : GPIOX.2, GPIOX.5(PWM_C) + -1, -1, // 8 | 9 : GPIOX.17(I2C-2_SDA), GPIOX.18(I2C-2_SCL) + -1, -1, // 10 | 11 : GPIOX.10(SPI_SS), GPIOH.6 + -1, -1, // 12 | 13 : GPIOX.8(SPI_MOSI), GPIOX.9(SPI_MISO) + -1, -1, // 14 | 15 : GPIOX.11(SPI_CLK), GPIOX.12(UART_TX_B) + -1, -1, // 16 | 17 : GPIOX.13(UART_RX_B), + -1, -1, // 18 | 19 : + -1, -1, // 20 | 21 : , GPIOX.14 + -1, 0, // 22 | 23 : GPIOX.15, GPIOX.6(PWM_A) + 1, -1, // 24 | 25 : GPIOX.19(PWM_B), ADC.AIN3 + -1, -1, // 26 | 27 : GPIOH.7, GPIOH.5 + -1, -1, // 28 | 29 : REF1.8V OUT, ADC.AIC4 + -1, -1, // 30 | 31 : GPIOA.14(I2C-3_SDA), GPIOA.15(I2C-3_SCL) + // Padding: + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32...47 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 48...63 +}; + +static char pwmPinPath[10][(BLOCK_SIZE)] = { + "","", + "","", + "","", + // Padding: + "None","None","None","None" +}; + +static char setupedPwmPinPath[BLOCK_SIZE]; + /*----------------------------------------------------------------------------*/ // // Global variable define @@ -95,6 +153,18 @@ static volatile uint32_t *gpio; /* wiringPi Global library */ static struct libodroid *lib = NULL; +/* pwm sysnode */ +static DIR *pwm; +static struct dirent *pwmchip; +/* pwm params */ +static char sysPwmPath[(BLOCK_SIZE / 4)]; +static char pwmExport[(BLOCK_SIZE / 16)]; +static char pwmUnexport[(BLOCK_SIZE / 16)]; +static char pwmPeriod[(BLOCK_SIZE / 16)]; +static char pwmDuty[(BLOCK_SIZE / 16)]; +static unsigned int pwmClock; +static unsigned int pwmRange; + /*----------------------------------------------------------------------------*/ // Function prototype define /*----------------------------------------------------------------------------*/ @@ -106,7 +176,12 @@ static int gpioToShiftReg (int pin); static int gpioToGPFSELReg (int pin); static int gpioToDSReg (int pin); static int gpioToMuxReg (int pin); - +/*----------------------------------------------------------------------------*/ +// Function of pwm define +/*----------------------------------------------------------------------------*/ +static int pinToSysPwmPath (int pin); +static int pwmSetup (int pin); +static int pwmRelease (int pin); /*----------------------------------------------------------------------------*/ // wiringPi core function /*----------------------------------------------------------------------------*/ @@ -119,9 +194,12 @@ static int _getPUPD (int pin); static int _pullUpDnControl (int pin, int pud); static int _digitalRead (int pin); static int _digitalWrite (int pin, int value); +static int _pwmWrite (int pin, int value); static int _analogRead (int pin); static int _digitalWriteByte (const unsigned int value); static unsigned int _digitalReadByte (void); +static void _pwmSetRange (unsigned int range); +static void _pwmSetClock (int divisor); /*----------------------------------------------------------------------------*/ // board init function @@ -271,6 +349,114 @@ static int gpioToMuxReg (int pin) } } +/*----------------------------------------------------------------------------*/ +// +// config pwm sys path. "/sys/class/pwm/pwmchip?" +// +/*----------------------------------------------------------------------------*/ +static int pinToSysPwmPath (int pin) +{ + const char *pwmGroup; + char pwmLinkSrc[(BLOCK_SIZE / 8)]; + char pwmPath[(BLOCK_SIZE / 8)]; + int sz_link; + + memset(pwmLinkSrc, 0, sizeof(pwmLinkSrc)); + memset(pwmPath, 0, sizeof(pwmPath)); + + pwmGroup = pinToPwm[pin]; + pwm = opendir("/sys/class/pwm"); + if (pwm == NULL) { + printf("need to set device: pwm\n"); + return -1; + } + + while (1) { + pwmchip = readdir(pwm); + + if (pwmchip == NULL) { + break; + } + + if (strlen(pwmchip->d_name) <= 2) + continue; + + sprintf(pwmPath, "%s/%s", "/sys/class/pwm", pwmchip->d_name); + sz_link = readlink(pwmPath, pwmLinkSrc, sizeof(pwmLinkSrc)); + if (sz_link < 0) { + perror("Read symbolic link fail"); + return sz_link; + } + + if (strstr(pwmLinkSrc, pwmGroup) != NULL) { + strncpy(sysPwmPath, pwmPath, (sizeof(sysPwmPath) - 1)); + break; + } + } + closedir(pwm); + + return 0; +} + +static int pwmSetup (int pin) { + char cmd[(BLOCK_SIZE * 2)]; + int pwmPin, ret; + + memset(cmd, 0, sizeof(cmd)); + memset(pwmExport, 0, sizeof(pwmExport)); + + if ((ret = pinToSysPwmPath(pin)) < 0) { + perror("set pwm dtb overlays"); + return ret; + } + + if (strstr(sysPwmPath, "pwmchip") == NULL) { + printf("config pwm dtb overlays\n"); + return -1; + } + + pwmPin = pinToPwmNum[pin]; + sprintf(pwmExport, "%d", (pwmPin % 2)); + sprintf(pwmPinPath[pwmPin], "%s/pwm%d", sysPwmPath, (pwmPin % 2)); + strncpy(setupedPwmPinPath, pwmPinPath[pwmPin], (sizeof(setupedPwmPinPath) - 1)); +#ifdef ANDROID + sprintf(cmd, "su -s sh -c %s %s", PWM_ACCESS_SCRIPT, pwmPinPath[pwmPin]); +#else + sprintf(cmd, "sudo sh %s %s", PWM_ACCESS_SCRIPT, pwmPinPath[pwmPin]); +#endif + inputToSysNode(sysPwmPath, "export", pwmExport); + system(cmd); + inputToSysNode(pwmPinPath[pwmPin], "polarity", "normal"); + inputToSysNode(pwmPinPath[pwmPin], "enable", "1"); + + return 0; +} + +static int pwmRelease (int pin) { + int pwmPin, ret; + + if ((ret = pinToSysPwmPath(pin)) < 0) { + perror("set pwm dtb overlays"); + return ret; + } + + if (strstr(sysPwmPath, "pwmchip") == NULL) { + printf("config pwm dtb overlays\n"); + return -1; + } + + pwmPin = pinToPwmNum[pin]; + sprintf(pwmUnexport, "%d", (pwmPin % 2)); + sprintf(pwmPinPath[pwmPin], "%s/pwm%d", sysPwmPath, (pwmPin % 2)); + if ((pwm = opendir(pwmPinPath[pwmPin])) != NULL) { + inputToSysNode(pwmPinPath[pwmPin], "enable", "0"); + inputToSysNode(sysPwmPath, "unexport", pwmUnexport); + closedir(pwm); + } + + return 0; +} + /*----------------------------------------------------------------------------*/ static int _getModeToGpio (int mode, int pin) { @@ -356,6 +542,7 @@ static int _pinMode (int pin, int mode) if ((pin = _getModeToGpio(lib->mode, pin)) < 0) return -1; + pwmRelease (origPin); softPwmStop (origPin); softToneStop (origPin); @@ -375,6 +562,9 @@ static int _pinMode (int pin, int mode) case SOFT_TONE_OUTPUT: softToneCreate (pin); break; + case PWM_OUTPUT: + pwmSetup(origPin); + break; default: msg(MSG_WARN, "%s : Unknown Mode %d\n", __func__, mode); return -1; @@ -512,6 +702,36 @@ static int _digitalWrite (int pin, int value) return 0; } +/*----------------------------------------------------------------------------*/ +// PWM signal ___-----------___________---------------_______-----_ +// <--value--> <----value----> +// <-------range--------><-------range--------> +/*----------------------------------------------------------------------------*/ +static int _pwmWrite (int pin, int value) +{ + unsigned int duty; + int pwmPin; + + memset(pwmDuty, 0, sizeof(pwmDuty)); + + if (lib->mode == MODE_GPIO_SYS) + return -1; + + if (((unsigned int)value > pwmRange) || (pwmRange <= 0)) { + printf("Set \'pwmWrite\' valied value. ( < pwm range)\n"); + printf("pwmSetRange value is greater than or equal pwmWrite's\n"); + return -1; + } + + pwmPin = pinToPwmNum[pin]; + duty = ((value * 100) / pwmRange); + sprintf(pwmDuty, "%d", ((atoi(pwmPeriod) * duty) / 100)); + + inputToSysNode(pwmPinPath[pwmPin], "duty_cycle", pwmDuty); + + return 0; +} + /*----------------------------------------------------------------------------*/ static int _analogRead (int pin) { @@ -624,6 +844,57 @@ static unsigned int _digitalReadByte (void) return value; } +/*----------------------------------------------------------------------------*/ +// PWM signal ___-----------___________---------------_______-----_ +// <--value--> <----value----> +// <-------range--------><-------range--------> +// PWM frequency == (PWM clock) / range +/*----------------------------------------------------------------------------*/ + +static void _pwmSetRange (unsigned int range) +{ + unsigned int freq, period; + + memset(pwmPeriod, 0, sizeof(pwmPeriod)); + + if (lib->mode == MODE_GPIO_SYS) + return; + + if (pwmClock < 2) { + printf("Set \'pwmSetClock\' valied value.\n"); + printf("pwm freq: %dMHz / (pwmSetClock's value) >= 2\n", (C4_PWM_INTERNAL_CLK / 1000000)); + return; + } + + pwmRange = range; + if ((pwmRange < 1) || (pwmRange >= pwmClock)) { + printf("Set \'pwmSetRange\' valied value. ( < pwm freq)\n"); + return; + } + + freq = (pwmClock / pwmRange); + period = (1000000000 / freq); // period: s to ns. + sprintf(pwmPeriod, "%d", period); + if (strstr(setupedPwmPinPath, "pwm") == NULL) { + printf("Not setuped pwm target.\n"); + return; + } + + inputToSysNode(setupedPwmPinPath, "period", pwmPeriod); +} + + +/*----------------------------------------------------------------------------*/ +// Internal clock == 24MHz +// PWM clock == (Internal clock) / divisor +// PWM frequency == (PWM clock) / range +/*----------------------------------------------------------------------------*/ + +static void _pwmSetClock (int divisor) +{ + pwmClock = (C4_PWM_INTERNAL_CLK / divisor); +} + /*----------------------------------------------------------------------------*/ static void init_gpio_mmap (void) { @@ -706,9 +977,12 @@ void init_odroidc4 (struct libodroid *libwiring) libwiring->pullUpDnControl = _pullUpDnControl; libwiring->digitalRead = _digitalRead; libwiring->digitalWrite = _digitalWrite; + libwiring->pwmWrite = _pwmWrite; libwiring->analogRead = _analogRead; libwiring->digitalWriteByte = _digitalWriteByte; libwiring->digitalReadByte = _digitalReadByte; + libwiring->pwmSetRange = _pwmSetRange; + libwiring->pwmSetClock = _pwmSetClock; /* specify pin base number */ libwiring->pinBase = C4_GPIO_PIN_BASE; diff --git a/wiringPi/odroidc4.h b/wiringPi/odroidc4.h index 77ee6c5..94cb989 100644 --- a/wiringPi/odroidc4.h +++ b/wiringPi/odroidc4.h @@ -49,6 +49,8 @@ #define C4_GPIOX_MUX_4_REG_OFFSET 0x1B4 #define C4_GPIOX_MUX_5_REG_OFFSET 0x1B5 +#define C4_PWM_INTERNAL_CLK 24000000 // 24MHz + #ifdef __cplusplus extern "C" { #endif diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index c631dac..268fe52 100644 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -1311,6 +1311,20 @@ int wiringPiSetupPhys (void) * Initialisation (again), however this time we are using the /sys/class/gpio * interface to the GPIO systems - slightly slower, but always usable as * a non-root user, assuming the devices are already exported and setup correctly. + * + * ----------------------------------------------------------------------------- + * Applies from "odroid-wiringpi" 3.14.1 and later. + * + * [The odroid hardware pwm uses sysnode, but it is separate from it.] + * I decided to use the wiringpi service as gpiomem base for odroid, + * but the pwm is using sysnode. + * + * In addition, i designed the API in common so that devices such as gpio as well as pwm + * can be directly accessed and used by sysnode. + * + * "inputToSysNode" is what it is. + * + * Authored by steve.jeong , */ /*----------------------------------------------------------------------------*/ int wiringPiSetupSys (void) diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h index bb8721c..585ec98 100644 --- a/wiringPi/wiringPi.h +++ b/wiringPi/wiringPi.h @@ -102,6 +102,9 @@ // Module names #define AML_MODULE_I2C "aml_i2c" +// syspwm +#define PWM_ACCESS_SCRIPT "/etc/odroid-wpi-pwm.sh" + // Threads #define PI_THREAD(X) void *X (UNU void *dummy)