1 /*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-3-08 GuEe-GUI the first version
9 */
10
11 #include <rtthread.h>
12
13 #define DBG_TAG "rtdm.led"
14 #define DBG_LVL DBG_INFO
15 #include <rtdbg.h>
16
17 #include <drivers/led.h>
18 #include <drivers/core/dm.h>
19
20 struct blink_timer
21 {
22 rt_bool_t toggle;
23 rt_bool_t enabled;
24 struct rt_timer timer;
25 };
26
27 static struct rt_dm_ida led_ida = RT_DM_IDA_INIT(LED);
28
29 static const char * const _led_states[] =
30 {
31 [RT_LED_S_OFF] = "off",
32 [RT_LED_S_ON] = "on",
33 [RT_LED_S_TOGGLE] = "toggle",
34 [RT_LED_S_BLINK] = "blink",
35 };
36
_led_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)37 static rt_ssize_t _led_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
38 {
39 rt_ssize_t res;
40 rt_size_t state_len;
41 enum rt_led_state state;
42 struct rt_led_device *led = rt_container_of(dev, struct rt_led_device, parent);
43
44 if ((res = rt_led_get_state(led, &state)))
45 {
46 return res;
47 }
48
49 state_len = rt_strlen(_led_states[state]);
50
51 if (pos < state_len)
52 {
53 size = rt_min_t(rt_size_t, size, size - pos);
54 ((char *)buffer)[size - 1] = '\0';
55 rt_strncpy(buffer, &_led_states[state][pos], size);
56
57 return size;
58 }
59 else
60 {
61 return 0;
62 }
63 }
64
_led_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)65 static rt_ssize_t _led_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
66 {
67 rt_uint32_t brightness = 0;
68 const char *value = buffer;
69 struct rt_led_device *led = rt_container_of(dev, struct rt_led_device, parent);
70
71 for (int i = 0; i < RT_ARRAY_SIZE(_led_states); ++i)
72 {
73 if (!rt_strncpy((char *)_led_states[i], buffer, size))
74 {
75 return rt_led_set_state(led, i) ? : size;
76 }
77 }
78
79 while (*value)
80 {
81 if (*value < '0' || *value > '9')
82 {
83 return -RT_EINVAL;
84 }
85
86 brightness *= 10;
87 brightness += *value - '0';
88
89 ++value;
90 }
91
92 rt_led_set_brightness(led, brightness);
93
94 return size;
95 }
96
97 #ifdef RT_USING_DEVICE_OPS
98 const static struct rt_device_ops _led_ops =
99 {
100 .read = _led_read,
101 .write = _led_write,
102 };
103 #endif
104
_led_blink_timerout(void * param)105 static void _led_blink_timerout(void *param)
106 {
107 struct rt_led_device *led = param;
108 struct blink_timer *btimer = led->sysdata;
109
110 if (btimer->toggle)
111 {
112 led->ops->set_state(led, RT_LED_S_OFF);
113 }
114 else
115 {
116 led->ops->set_state(led, RT_LED_S_ON);
117 }
118
119 btimer->toggle = !btimer->toggle;
120 }
121
rt_led_register(struct rt_led_device * led)122 rt_err_t rt_led_register(struct rt_led_device *led)
123 {
124 rt_err_t err;
125 int device_id;
126 const char *dev_name;
127 struct blink_timer *btimer = RT_NULL;
128
129 if (!led || !led->ops)
130 {
131 return -RT_EINVAL;
132 }
133
134 if ((device_id = rt_dm_ida_alloc(&led_ida)) < 0)
135 {
136 return -RT_EFULL;
137 }
138
139 rt_dm_dev_set_name(&led->parent, "led%u", device_id);
140 dev_name = rt_dm_dev_get_name(&led->parent);
141
142 led->sysdata = RT_NULL;
143 rt_spin_lock_init(&led->spinlock);
144
145 if (!led->ops->set_period && led->ops->set_state)
146 {
147 btimer = rt_malloc(sizeof(*btimer));
148
149 if (!btimer)
150 {
151 LOG_E("%s create blink timer failed", dev_name);
152
153 err = -RT_ENOMEM;
154 goto _fail;
155 }
156
157 led->sysdata = btimer;
158
159 btimer->toggle = RT_FALSE;
160 btimer->enabled = RT_FALSE;
161 rt_timer_init(&btimer->timer, dev_name, _led_blink_timerout, led,
162 rt_tick_from_millisecond(500), RT_TIMER_FLAG_PERIODIC);
163 }
164
165 led->parent.type = RT_Device_Class_Char;
166 #ifdef RT_USING_DEVICE_OPS
167 led->parent.ops = &_led_ops;
168 #else
169 led->parent.read = _led_read;
170 led->parent.write = _led_write;
171 #endif
172 led->parent.master_id = led_ida.master_id;
173 led->parent.device_id = device_id;
174
175 if ((err = rt_device_register(&led->parent, dev_name, RT_DEVICE_FLAG_RDWR)))
176 {
177 goto _fail;
178 }
179
180 return RT_EOK;
181
182 _fail:
183 rt_dm_ida_free(&led_ida, device_id);
184
185 if (btimer)
186 {
187 rt_timer_detach(&btimer->timer);
188 rt_free(btimer);
189
190 led->sysdata = RT_NULL;
191 }
192
193 return err;
194 }
195
rt_led_unregister(struct rt_led_device * led)196 rt_err_t rt_led_unregister(struct rt_led_device *led)
197 {
198 if (!led)
199 {
200 return -RT_EINVAL;
201 }
202
203 rt_led_set_state(led, RT_LED_S_OFF);
204
205 if (led->sysdata)
206 {
207 struct blink_timer *btimer = led->sysdata;
208
209 rt_timer_detach(&btimer->timer);
210
211 rt_free(btimer);
212 }
213
214 rt_dm_ida_free(&led_ida, led->parent.device_id);
215
216 rt_device_unregister(&led->parent);
217
218 return RT_EOK;
219 }
220
rt_led_set_state(struct rt_led_device * led,enum rt_led_state state)221 rt_err_t rt_led_set_state(struct rt_led_device *led, enum rt_led_state state)
222 {
223 rt_err_t err;
224 struct blink_timer *btimer;
225
226 if (!led)
227 {
228 return -RT_EINVAL;
229 }
230
231 if (!led->ops->set_state)
232 {
233 return -RT_ENOSYS;
234 }
235
236 rt_spin_lock(&led->spinlock);
237
238 btimer = led->sysdata;
239
240 if (btimer && btimer->enabled)
241 {
242 rt_timer_stop(&btimer->timer);
243 }
244
245 err = led->ops->set_state(led, state);
246
247 if (state == RT_LED_S_BLINK)
248 {
249 if (err == -RT_ENOSYS && btimer && !btimer->enabled)
250 {
251 btimer->enabled = RT_TRUE;
252 rt_timer_start(&btimer->timer);
253 }
254 }
255 else if (btimer && btimer->enabled)
256 {
257 if (err)
258 {
259 rt_timer_start(&btimer->timer);
260 }
261 else
262 {
263 btimer->enabled = RT_FALSE;
264 }
265 }
266
267 rt_spin_unlock(&led->spinlock);
268
269 return err;
270 }
271
rt_led_get_state(struct rt_led_device * led,enum rt_led_state * out_state)272 rt_err_t rt_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state)
273 {
274 rt_err_t err;
275
276 if (!led || !out_state)
277 {
278 return -RT_EINVAL;
279 }
280
281 if (!led->ops->get_state)
282 {
283 return -RT_ENOSYS;
284 }
285
286 rt_spin_lock(&led->spinlock);
287
288 err = led->ops->get_state(led, out_state);
289
290 rt_spin_unlock(&led->spinlock);
291
292 return err;
293 }
294
rt_led_set_period(struct rt_led_device * led,rt_uint32_t period_ms)295 rt_err_t rt_led_set_period(struct rt_led_device *led, rt_uint32_t period_ms)
296 {
297 rt_err_t err;
298
299 if (!led)
300 {
301 return -RT_EINVAL;
302 }
303
304 if (!led->ops->set_period && !led->sysdata)
305 {
306 return -RT_ENOSYS;
307 }
308
309 rt_spin_lock(&led->spinlock);
310
311 if (led->ops->set_period)
312 {
313 err = led->ops->set_period(led, period_ms);
314 }
315 else
316 {
317 struct blink_timer *btimer = led->sysdata;
318 rt_tick_t tick = rt_tick_from_millisecond(period_ms);
319
320 err = rt_timer_control(&btimer->timer, RT_TIMER_CTRL_SET_TIME, &tick);
321 }
322
323 rt_spin_unlock(&led->spinlock);
324
325 return err;
326 }
327
rt_led_set_brightness(struct rt_led_device * led,rt_uint32_t brightness)328 rt_err_t rt_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness)
329 {
330 rt_err_t err;
331
332 if (!led)
333 {
334 return -RT_EINVAL;
335 }
336
337 if (!led->ops->set_brightness)
338 {
339 return -RT_ENOSYS;
340 }
341
342 rt_spin_lock(&led->spinlock);
343
344 err = led->ops->set_brightness(led, brightness);
345
346 rt_spin_unlock(&led->spinlock);
347
348 return err;
349 }
350