2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
23 #include "peripheral_io.h"
27 FEATURE("http://tizen.org/feature/peripheral_io.pwm")
29 #define PWM_BUF_MAX 16
32 * @brief Internal struct for pwm context
34 struct _peripheral_pwm_s {
43 #define PWMCHIP_BASE "pwmchip"
44 #define PWM_BASE "pwm"
46 typedef struct FilterData
48 char pwmchip_name[sizeof(PWMCHIP_BASE MAX_d_FMT)]; /* space for pwmchip%d */
49 char pwm_name[sizeof(PWM_BASE MAX_d_FMT)]; /* space for pwm%d */
52 static bool __filter_device(struct udev_device *device, void *data)
54 FilterData *filterData = data;
56 if (strcmp(udev_device_get_sysname(device), filterData->pwmchip_name) == 0) {
57 const char *prop = udev_device_get_property_value(device, "EXPORT");
58 if (prop && strcmp(prop, filterData->pwm_name) == 0) {
59 _D("udev for %s is initialized", filterData->pwm_name);
66 static int peripheral_pwm_export(int chip, int pin)
68 RETVM_IF(chip < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm chip");
69 RETVM_IF(pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm pin");
71 #define EXPORT_PATH(chip) ("/sys/class/pwm/pwmchip" chip "/export")
72 char path[sizeof EXPORT_PATH(MAX_d_FMT)] = {0, }; /* space for /sys/class/pwm/pwmchip%d/export */
73 struct udev *udev = NULL;
74 struct udev_monitor *monitor = NULL;
77 ret = peripheral_create_udev_monitor(&udev, &monitor, "pwm");
79 _E("Failed to create udev monitor");
80 ret = PERIPHERAL_ERROR_IO_ERROR;
84 snprintf(path, sizeof path, EXPORT_PATH("%d"), chip);
85 ret = peripheral_write_pin_to_file(path, pin);
87 _E("pwm: export pin %d failed with error %d", pin, ret);
92 snprintf(filter.pwmchip_name, sizeof filter.pwmchip_name, PWMCHIP_BASE "%d", chip);
93 snprintf(filter.pwm_name, sizeof filter.pwm_name, PWM_BASE "%d", pin);
95 ret = peripheral_wait_for_udev(monitor, __filter_device, &filter);
97 _E("device nodes are not writable");
98 ret = PERIPHERAL_ERROR_IO_ERROR;
103 udev_monitor_unref(monitor);
109 static int peripheral_pwm_lock_export(int chip)
111 char path[sizeof EXPORT_PATH(MAX_d_FMT)] = {0, }; /* space for /sys/class/pwm/pwmchip%d/export */
112 snprintf(path, sizeof path, EXPORT_PATH("%d"), chip);
114 return peripheral_lock(path);
117 static inline int cleanup_handle(peripheral_pwm_h handle)
120 return PERIPHERAL_ERROR_NONE;
122 int pin = handle->pin;
123 int chip = handle->chip;
124 __attribute__ ((cleanup(peripheral_unlockp))) int lock = peripheral_pwm_lock_export(chip);
126 close_fd(handle->fd_duty_cycle);
127 close_fd(handle->fd_polarity);
128 close_fd(handle->fd_enable);
129 close_fd(handle->fd_period); /* this releases chip+pin flock() */
133 return PERIPHERAL_ERROR_NONE;
135 #define UNEXPORT_PATH(chip) ("/sys/class/pwm/pwmchip" chip "/unexport")
136 char path[sizeof UNEXPORT_PATH(MAX_d_FMT)] = {0, }; /* space for /sys/class/pwm/pwmchip%d/unexport */
137 snprintf(path, sizeof path, UNEXPORT_PATH("%d"), chip);
139 int ret = peripheral_write_pin_to_file(path, pin);
141 _E("pwm: unexport pin %d failed with error %d", pin, ret);
142 return PERIPHERAL_ERROR_IO_ERROR;
145 return PERIPHERAL_ERROR_NONE;
148 static inline void cleanup_handlep(peripheral_pwm_h *handle)
150 cleanup_handle(*handle);
153 int peripheral_pwm_open(int chip, int pin, peripheral_pwm_h *pwm)
155 int ret = PERIPHERAL_ERROR_NONE;
157 RETVM_IF(!__is_feature_supported(), PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported");
158 RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm handle");
159 RETVM_IF(chip < 0 || pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid parameter");
161 __attribute__ ((cleanup(peripheral_unlockp))) int lock = -1;
162 __attribute__ ((cleanup(cleanup_handlep))) peripheral_pwm_h handle = NULL;
163 #define PWM_PATH_BASE(chip, pin, file) ("/sys/class/pwm/pwmchip" chip "/pwm" pin "/" file)
164 /* space for /sys/class/pwm/pwmchip%d/pwm%d/duty_cycle
165 which is longer than:
166 /sys/class/pwm/pwmchip%d/pwm%d/period
167 /sys/class/pwm/pwmchip%d/pwm%d/polarity
168 /sys/class/pwm/pwmchip%d/pwm%d/enable
170 char path[sizeof (PWM_PATH_BASE(MAX_d_FMT, MAX_d_FMT, "duty_cycle"))] = {0, };
173 handle = (peripheral_pwm_h)calloc(1, sizeof(struct _peripheral_pwm_s));
174 if (handle == NULL) {
175 _E("Failed to allocate peripheral_pwm_h");
176 return PERIPHERAL_ERROR_OUT_OF_MEMORY;
179 handle->fd_period = -1;
180 handle->fd_duty_cycle = -1;
181 handle->fd_polarity = -1;
182 handle->fd_enable = -1;
186 lock = peripheral_pwm_lock_export(chip);
187 RETVM_IF(lock == -1, PERIPHERAL_ERROR_IO_ERROR, "Failed to lock 'export'");
189 ret = peripheral_pwm_export(chip, pin);
190 if (ret != PERIPHERAL_ERROR_NONE) {
191 _E("Failed to open PWM chip : %d, pin : %d", chip, pin);
199 snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "period"), chip, pin);
200 handle->fd_period = open(path, O_RDWR | O_CLOEXEC);
201 CHECK_ERROR(handle->fd_period < 0);
203 TRY_FLOCK(ret, handle->fd_period, LOCK_EX | LOCK_NB, "pwm: chip %d, pin %d", chip, pin);
204 CHECK_ERROR(ret != PERIPHERAL_ERROR_NONE);
206 snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "duty_cycle"), chip, pin);
207 handle->fd_duty_cycle = open(path, O_RDWR | O_CLOEXEC);
208 CHECK_ERROR(handle->fd_duty_cycle < 0);
210 snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "polarity"), chip, pin);
211 handle->fd_polarity = open(path, O_RDWR | O_CLOEXEC);
212 CHECK_ERROR(handle->fd_polarity < 0);
214 snprintf(path, sizeof path, PWM_PATH_BASE("%d", "%d", "enable"), chip, pin);
215 handle->fd_enable = open(path, O_RDWR | O_CLOEXEC);
216 CHECK_ERROR(handle->fd_enable < 0);
218 lock = peripheral_unlock(lock);
226 int peripheral_pwm_close(peripheral_pwm_h pwm)
228 RETVM_IF(!__is_feature_supported(), PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported");
229 RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL");
231 return cleanup_handle(pwm);
234 int peripheral_pwm_set_period(peripheral_pwm_h pwm, uint32_t period_ns)
236 RETVM_IF(!__is_feature_supported(), PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported");
237 RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL");
241 char pwm_buf[PWM_BUF_MAX] = {0};
243 length = snprintf(pwm_buf, sizeof(pwm_buf), "%d", period_ns);
244 ret = write(pwm->fd_period, pwm_buf, length);
245 CHECK_ERROR(ret != length);
247 return PERIPHERAL_ERROR_NONE;
250 int peripheral_pwm_set_duty_cycle(peripheral_pwm_h pwm, uint32_t duty_cycle_ns)
252 RETVM_IF(!__is_feature_supported(), PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported");
253 RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL");
257 char pwm_buf[PWM_BUF_MAX] = {0};
259 length = snprintf(pwm_buf, sizeof(pwm_buf), "%d", duty_cycle_ns);
260 ret = write(pwm->fd_duty_cycle, pwm_buf, length);
261 CHECK_ERROR(ret != length);
263 return PERIPHERAL_ERROR_NONE;
266 int peripheral_pwm_set_polarity(peripheral_pwm_h pwm, peripheral_pwm_polarity_e polarity)
268 RETVM_IF(!__is_feature_supported(), PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported");
269 RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL");
270 RETVM_IF((polarity < PERIPHERAL_PWM_POLARITY_ACTIVE_HIGH) || (polarity > PERIPHERAL_PWM_POLARITY_ACTIVE_LOW), PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid polarity parameter");
272 static predefined_type_s types[2] = {
277 int ret = write(pwm->fd_polarity, types[polarity].type, types[polarity].len);
278 CHECK_ERROR(ret != types[polarity].len);
280 return PERIPHERAL_ERROR_NONE;
283 int peripheral_pwm_set_enabled(peripheral_pwm_h pwm, bool enable)
285 RETVM_IF(!__is_feature_supported(), PERIPHERAL_ERROR_NOT_SUPPORTED, "PWM feature is not supported");
286 RETVM_IF(pwm == NULL, PERIPHERAL_ERROR_INVALID_PARAMETER, "pwm handle is NULL");
288 static predefined_type_s types[2] = {
293 int ret = write(pwm->fd_enable, types[enable].type, types[enable].len);
294 CHECK_ERROR(ret != types[enable].len);
296 return PERIPHERAL_ERROR_NONE;