1 /* Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #ifndef IOTJS_MODULE_PWM_LINUX_GENERAL_INL_H
17 #define IOTJS_MODULE_PWM_LINUX_GENERAL_INL_H
25 #include "iotjs_systemio-linux.h"
26 #include "module/iotjs_module_pwm.h"
29 // Generic PWM implementation for linux.
32 #define PWM_INTERFACE "/sys/class/pwm/pwmchip%d/"
33 #define PWM_PIN_INTERFACE "pwm%d/"
34 #define PWM_PIN_FORMAT PWM_INTERFACE PWM_PIN_INTERFACE
35 #define PWM_EXPORT PWM_INTERFACE "export"
36 #define PWM_UNEXPORT PWM_INTERFACE "unexport"
37 #define PWM_PIN_DUTYCYCLE "duty_cycle"
38 #define PWM_PIN_PERIOD "period"
39 #define PWM_PIN_ENABlE "enable"
41 #define PWM_PATH_BUFFER_SIZE 64
42 #define PWM_VALUE_BUFFER_SIZE 32
45 // Generate device path for specified PWM device.
46 // The path may include node suffix if passed ('enable', 'period', 'duty_cycle')
47 // Pointer to a allocated string is returned, or null in case of error.
48 // If PWM_PIN_FORMAT format results in an empty string,
49 // NULL is returned (and fileName is ignored).
50 static char* generate_device_subpath(iotjs_string_t* device,
51 const char* fileName) {
52 char* devicePath = NULL;
53 // Do not print anything, only calculate resulting string length.
54 int prefixSize = iotjs_string_size(device);
56 int suffixSize = fileName ? strlen(fileName) : 0;
57 devicePath = malloc(prefixSize + suffixSize + 1);
59 // Do not need to check bounds, the buffer is of exact required size.
60 memcpy(devicePath, iotjs_string_data(device), prefixSize);
61 memcpy(devicePath + prefixSize, fileName, suffixSize);
62 devicePath[prefixSize + suffixSize] = 0;
68 // Limit period to [0..1]s
69 static double adjust_period(double period) {
72 } else if (period > 1) {
80 void iotjs_pwm_open_worker(uv_work_t* work_req) {
82 IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
84 char path[PWM_PATH_BUFFER_SIZE] = { 0 };
85 if (snprintf(path, PWM_PATH_BUFFER_SIZE, PWM_PIN_FORMAT, _this->chip,
87 req_data->result = false;
91 _this->device = iotjs_string_create_with_size(path, strlen(path));
93 // See if the PWM is already opened.
94 if (!iotjs_systemio_check_path(path)) {
95 // Write exporting PWM path
96 char export_path[PWM_PATH_BUFFER_SIZE] = { 0 };
97 snprintf(export_path, PWM_PATH_BUFFER_SIZE, PWM_EXPORT, _this->chip);
99 const char* created_files[] = { PWM_PIN_DUTYCYCLE, PWM_PIN_PERIOD,
101 int created_files_length = sizeof(created_files) / sizeof(created_files[0]);
102 if (!iotjs_systemio_device_open(export_path, _this->pin, path,
103 created_files, created_files_length)) {
104 req_data->result = false;
110 if (_this->period >= 0) {
111 if (!iotjs_pwm_set_period(pwm)) {
112 req_data->result = false;
115 if (_this->duty_cycle >= 0) {
116 if (!iotjs_pwm_set_dutycycle(pwm)) {
117 req_data->result = false;
123 DDDLOG("%s - path: %s", __func__, path);
125 req_data->result = true;
129 bool iotjs_pwm_set_period(iotjs_pwm_t* pwm) {
130 IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
133 if (isfinite(_this->period) && _this->period >= 0.0) {
134 char* devicePath = generate_device_subpath(&_this->device, PWM_PIN_PERIOD);
136 // Linux API uses nanoseconds, thus 1E9
137 unsigned int value = (unsigned)(adjust_period(_this->period) * 1.E9);
138 DDLOG("%s - path: %s, value: %fs", __func__, devicePath, 1.E-9 * value);
139 char buf[PWM_VALUE_BUFFER_SIZE];
140 if (snprintf(buf, sizeof(buf), "%d", value) > 0) {
141 result = iotjs_systemio_open_write_close(devicePath, buf);
150 bool iotjs_pwm_set_dutycycle(iotjs_pwm_t* pwm) {
151 IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
154 double dutyCycle = _this->duty_cycle;
155 if (isfinite(_this->period) && _this->period >= 0.0 && isfinite(dutyCycle) &&
156 0.0 <= dutyCycle && dutyCycle <= 1.0) {
158 generate_device_subpath(&_this->device, PWM_PIN_DUTYCYCLE);
160 double period = adjust_period(_this->period);
161 // Linux API uses nanoseconds, thus 1E9
162 unsigned dutyCycleValue = (unsigned)(period * _this->duty_cycle * 1E9);
164 DDLOG("%s - path: %s, value: %d\n", __func__, devicePath, dutyCycleValue);
165 char buf[PWM_VALUE_BUFFER_SIZE];
166 if (snprintf(buf, sizeof(buf), "%d", dutyCycleValue) < 0) {
170 result = iotjs_systemio_open_write_close(devicePath, buf);
178 bool iotjs_pwm_set_enable(iotjs_pwm_t* pwm) {
179 IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
183 char* devicePath = generate_device_subpath(&_this->device, PWM_PIN_ENABlE);
186 if (snprintf(value, sizeof(value), "%d", _this->enable) < 0) {
190 DDLOG("%s - path: %s, set: %d\n", __func__, devicePath, _this->enable);
191 char buf[PWM_VALUE_BUFFER_SIZE];
192 if (snprintf(buf, sizeof(buf), "%d", _this->enable) < 0) {
196 result = iotjs_systemio_open_write_close(devicePath, buf);
203 bool iotjs_pwm_close(iotjs_pwm_t* pwm) {
204 IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
206 char path[PWM_PATH_BUFFER_SIZE] = { 0 };
207 if (snprintf(path, PWM_PATH_BUFFER_SIZE, PWM_PIN_FORMAT, _this->chip,
212 if (iotjs_systemio_check_path(path)) {
213 // Write exporting pin path
214 char unexport_path[PWM_PATH_BUFFER_SIZE] = { 0 };
215 if (snprintf(unexport_path, PWM_PATH_BUFFER_SIZE, PWM_UNEXPORT,
220 iotjs_systemio_device_close(unexport_path, _this->pin);
223 DDDLOG("%s- path: %s", __func__, path);
229 #endif /* IOTJS_MODULE_PWM_LINUX_GENERAL_INL_H */