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