refactoring: extract common udev code
[platform/core/api/peripheral-io.git] / src / peripheral_pwm.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <libudev.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/file.h>
21 #include <unistd.h>
22
23 #include "peripheral_io.h"
24 #include "common.h"
25 #include "log.h"
26
27 FEATURE("http://tizen.org/feature/peripheral_io.pwm")
28
29 #define PWM_BUF_MAX 16
30
31 /**
32  * @brief Internal struct for pwm context
33  */
34 struct _peripheral_pwm_s {
35         int chip;
36         int pin;
37         int fd_period;
38         int fd_duty_cycle;
39         int fd_polarity;
40         int fd_enable;
41 };
42
43 #define PWMCHIP_BASE "pwmchip"
44 #define PWM_BASE "pwm"
45
46 typedef struct FilterData
47 {
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 */
50 } FilterData;
51
52 static bool __filter_device(struct udev_device *device, void *data)
53 {
54         FilterData *filterData = data;
55
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);
60                         return true;
61                 }
62         }
63         return false;
64 }
65
66 static int peripheral_pwm_export(int chip, int pin)
67 {
68         RETVM_IF(chip < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm chip");
69         RETVM_IF(pin < 0, PERIPHERAL_ERROR_INVALID_PARAMETER, "Invalid pwm pin");
70
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;
75         int ret;
76
77         ret = peripheral_create_udev_monitor(&udev, &monitor, "pwm");
78         if (ret != 0) {
79                 _E("Failed to create udev monitor");
80                 ret = PERIPHERAL_ERROR_IO_ERROR;
81                 goto out;
82         };
83
84         snprintf(path, sizeof path, EXPORT_PATH("%d"), chip);
85         ret = peripheral_write_pin_to_file(path, pin);
86         if (ret != 0) {
87                 _E("pwm: export pin %d failed with error %d", pin, ret);
88                 goto out;
89         }
90
91         FilterData filter;
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);
94
95         ret = peripheral_wait_for_udev(monitor, __filter_device, &filter);
96         if (ret != 0) {
97                 _E("device nodes are not writable");
98                 ret = PERIPHERAL_ERROR_IO_ERROR;
99                 goto out;
100         }
101
102 out:
103         udev_monitor_unref(monitor);
104         udev_unref(udev);
105
106         return ret;
107 }
108
109 static int peripheral_pwm_lock_export(int chip)
110 {
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);
113
114         return peripheral_lock(path);
115 }
116
117 static inline int cleanup_handle(peripheral_pwm_h handle)
118 {
119         if (handle == NULL)
120                 return PERIPHERAL_ERROR_NONE;
121
122         int pin = handle->pin;
123         int chip = handle->chip;
124         __attribute__ ((cleanup(peripheral_unlockp))) int lock = peripheral_pwm_lock_export(chip);
125
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() */
130         free(handle);
131
132         if (pin < 0)
133                 return PERIPHERAL_ERROR_NONE;
134
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);
138
139         int ret = peripheral_write_pin_to_file(path, pin);
140         if (ret != 0) {
141                 _E("pwm: unexport pin %d failed with error %d", pin, ret);
142                 return PERIPHERAL_ERROR_IO_ERROR;
143         }
144
145         return PERIPHERAL_ERROR_NONE;
146 }
147
148 static inline void cleanup_handlep(peripheral_pwm_h *handle)
149 {
150         cleanup_handle(*handle);
151 }
152
153 int peripheral_pwm_open(int chip, int pin, peripheral_pwm_h *pwm)
154 {
155         int ret = PERIPHERAL_ERROR_NONE;
156
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");
160
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
169         */
170         char path[sizeof (PWM_PATH_BASE(MAX_d_FMT, MAX_d_FMT, "duty_cycle"))] = {0, };
171
172         /* Initialize */
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;
177         }
178
179         handle->fd_period = -1;
180         handle->fd_duty_cycle = -1;
181         handle->fd_polarity = -1;
182         handle->fd_enable = -1;
183         handle->chip = -1;
184         handle->pin = -1;
185
186         lock = peripheral_pwm_lock_export(chip);
187         RETVM_IF(lock == -1, PERIPHERAL_ERROR_IO_ERROR, "Failed to lock 'export'");
188
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);
192                 free(handle);
193                 handle = NULL;
194         }
195
196         handle->chip = chip;
197         handle->pin = pin;
198
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);
202
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);
205
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);
209
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);
213
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);
217
218         lock = peripheral_unlock(lock);
219
220         *pwm = handle;
221         handle = NULL;
222
223         return ret;
224 }
225
226 int peripheral_pwm_close(peripheral_pwm_h pwm)
227 {
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");
230
231         return cleanup_handle(pwm);
232 }
233
234 int peripheral_pwm_set_period(peripheral_pwm_h pwm, uint32_t period_ns)
235 {
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");
238
239         int ret;
240         int length;
241         char pwm_buf[PWM_BUF_MAX] = {0};
242
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);
246
247         return PERIPHERAL_ERROR_NONE;
248 }
249
250 int peripheral_pwm_set_duty_cycle(peripheral_pwm_h pwm, uint32_t duty_cycle_ns)
251 {
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");
254
255         int ret;
256         int length;
257         char pwm_buf[PWM_BUF_MAX] = {0};
258
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);
262
263         return PERIPHERAL_ERROR_NONE;
264 }
265
266 int peripheral_pwm_set_polarity(peripheral_pwm_h pwm, peripheral_pwm_polarity_e polarity)
267 {
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");
271
272         static predefined_type_s types[2] = {
273                 {"normal",   6},
274                 {"inversed", 8}
275         };
276
277         int ret = write(pwm->fd_polarity, types[polarity].type, types[polarity].len);
278         CHECK_ERROR(ret != types[polarity].len);
279
280         return PERIPHERAL_ERROR_NONE;
281 }
282
283 int peripheral_pwm_set_enabled(peripheral_pwm_h pwm, bool enable)
284 {
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");
287
288         static predefined_type_s types[2] = {
289                 {"0", 1},
290                 {"1", 1}
291         };
292
293         int ret = write(pwm->fd_enable, types[enable].type, types[enable].len);
294         CHECK_ERROR(ret != types[enable].len);
295
296         return PERIPHERAL_ERROR_NONE;
297 }