Upload packaging folder
[platform/upstream/iotjs.git] / tools / src / platform / iotjs_module_pwm-linux-general.inl.h
1 /* Copyright 2016-present Samsung Electronics Co., Ltd. and other contributors
2  *
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
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 #ifndef IOTJS_MODULE_PWM_LINUX_GENERAL_INL_H
17 #define IOTJS_MODULE_PWM_LINUX_GENERAL_INL_H
18
19
20 #include <math.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "iotjs_systemio-linux.h"
26 #include "module/iotjs_module_pwm.h"
27
28
29 // Generic PWM implementation for linux.
30
31
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"
40
41 #define PWM_PATH_BUFFER_SIZE 64
42 #define PWM_VALUE_BUFFER_SIZE 32
43
44
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);
55   if (prefixSize > 0) {
56     int suffixSize = fileName ? strlen(fileName) : 0;
57     devicePath = malloc(prefixSize + suffixSize + 1);
58     if (devicePath) {
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;
63     }
64   }
65   return devicePath;
66 }
67
68 // Limit period to [0..1]s
69 static double adjust_period(double period) {
70   if (period < 0) {
71     return 0.0;
72   } else if (period > 1) {
73     return 1.0;
74   } else {
75     return period;
76   }
77 }
78
79
80 void iotjs_pwm_open_worker(uv_work_t* work_req) {
81   PWM_WORKER_INIT;
82   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
83
84   char path[PWM_PATH_BUFFER_SIZE] = { 0 };
85   if (snprintf(path, PWM_PATH_BUFFER_SIZE, PWM_PIN_FORMAT, _this->chip,
86                _this->pin) < 0) {
87     req_data->result = false;
88     return;
89   }
90
91   _this->device = iotjs_string_create_with_size(path, strlen(path));
92
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);
98
99     const char* created_files[] = { PWM_PIN_DUTYCYCLE, PWM_PIN_PERIOD,
100                                     PWM_PIN_ENABlE };
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;
105       return;
106     }
107   }
108
109   // Set options.
110   if (_this->period >= 0) {
111     if (!iotjs_pwm_set_period(pwm)) {
112       req_data->result = false;
113       return;
114     }
115     if (_this->duty_cycle >= 0) {
116       if (!iotjs_pwm_set_dutycycle(pwm)) {
117         req_data->result = false;
118         return;
119       }
120     }
121   }
122
123   DDDLOG("%s - path: %s", __func__, path);
124
125   req_data->result = true;
126 }
127
128
129 bool iotjs_pwm_set_period(iotjs_pwm_t* pwm) {
130   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
131
132   bool result = false;
133   if (isfinite(_this->period) && _this->period >= 0.0) {
134     char* devicePath = generate_device_subpath(&_this->device, PWM_PIN_PERIOD);
135     if (devicePath) {
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);
142       }
143       free(devicePath);
144     }
145   }
146   return result;
147 }
148
149
150 bool iotjs_pwm_set_dutycycle(iotjs_pwm_t* pwm) {
151   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
152
153   bool result = false;
154   double dutyCycle = _this->duty_cycle;
155   if (isfinite(_this->period) && _this->period >= 0.0 && isfinite(dutyCycle) &&
156       0.0 <= dutyCycle && dutyCycle <= 1.0) {
157     char* devicePath =
158         generate_device_subpath(&_this->device, PWM_PIN_DUTYCYCLE);
159     if (devicePath) {
160       double period = adjust_period(_this->period);
161       // Linux API uses nanoseconds, thus 1E9
162       unsigned dutyCycleValue = (unsigned)(period * _this->duty_cycle * 1E9);
163
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) {
167         return false;
168       }
169
170       result = iotjs_systemio_open_write_close(devicePath, buf);
171       free(devicePath);
172     }
173   }
174   return result;
175 }
176
177
178 bool iotjs_pwm_set_enable(iotjs_pwm_t* pwm) {
179   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
180
181   bool result = false;
182
183   char* devicePath = generate_device_subpath(&_this->device, PWM_PIN_ENABlE);
184   if (devicePath) {
185     char value[4];
186     if (snprintf(value, sizeof(value), "%d", _this->enable) < 0) {
187       return false;
188     }
189
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) {
193       return false;
194     }
195
196     result = iotjs_systemio_open_write_close(devicePath, buf);
197     free(devicePath);
198   }
199   return result;
200 }
201
202
203 bool iotjs_pwm_close(iotjs_pwm_t* pwm) {
204   IOTJS_VALIDATED_STRUCT_METHOD(iotjs_pwm_t, pwm);
205
206   char path[PWM_PATH_BUFFER_SIZE] = { 0 };
207   if (snprintf(path, PWM_PATH_BUFFER_SIZE, PWM_PIN_FORMAT, _this->chip,
208                _this->pin) < 0) {
209     return false;
210   }
211
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,
216                  _this->chip) < 0) {
217       return false;
218     }
219
220     iotjs_systemio_device_close(unexport_path, _this->pin);
221   }
222
223   DDDLOG("%s- path: %s", __func__, path);
224
225   return true;
226 }
227
228
229 #endif /* IOTJS_MODULE_PWM_LINUX_GENERAL_INL_H */