1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2007-01-21 Bernard the first version
9 * 2010-05-04 Bernard add rt_device_init implementation
10 * 2012-10-20 Bernard add device check in register function,
11 * provided by Rob <[email protected]>
12 * 2012-12-25 Bernard return RT_EOK if the device interface not exist.
13 * 2013-07-09 Grissiom add ref_count support
14 * 2016-04-02 Bernard fix the open_flag initialization issue.
15 */
16
17 #include <rtthread.h>
18 #if defined(RT_USING_POSIX)
19 #include <rtdevice.h> /* for wqueue_init */
20 #endif
21
22 #ifdef RT_USING_DEVICE
23
24 #ifdef RT_USING_DEVICE_OPS
25 #define device_init (dev->ops->init)
26 #define device_open (dev->ops->open)
27 #define device_close (dev->ops->close)
28 #define device_read (dev->ops->read)
29 #define device_write (dev->ops->write)
30 #define device_control (dev->ops->control)
31 #else
32 #define device_init (dev->init)
33 #define device_open (dev->open)
34 #define device_close (dev->close)
35 #define device_read (dev->read)
36 #define device_write (dev->write)
37 #define device_control (dev->control)
38 #endif
39
40 /**
41 * This function registers a device driver with specified name.
42 *
43 * @param dev the pointer of device driver structure
44 * @param name the device driver's name
45 * @param flags the capabilities flag of device
46 *
47 * @return the error code, RT_EOK on initialization successfully.
48 */
rt_device_register(rt_device_t dev,const char * name,rt_uint16_t flags)49 rt_err_t rt_device_register(rt_device_t dev,
50 const char *name,
51 rt_uint16_t flags)
52 {
53 if (dev == RT_NULL)
54 return -RT_ERROR;
55
56 if (rt_device_find(name) != RT_NULL)
57 return -RT_ERROR;
58
59 rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
60 dev->flag = flags;
61 dev->ref_count = 0;
62 dev->open_flag = 0;
63
64 #if defined(RT_USING_POSIX)
65 dev->fops = RT_NULL;
66 rt_wqueue_init(&(dev->wait_queue));
67 #endif
68
69 return RT_EOK;
70 }
71 RTM_EXPORT(rt_device_register);
72
73 /**
74 * This function removes a previously registered device driver
75 *
76 * @param dev the pointer of device driver structure
77 *
78 * @return the error code, RT_EOK on successfully.
79 */
rt_device_unregister(rt_device_t dev)80 rt_err_t rt_device_unregister(rt_device_t dev)
81 {
82 RT_ASSERT(dev != RT_NULL);
83 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
84 RT_ASSERT(rt_object_is_systemobject(&dev->parent));
85
86 rt_object_detach(&(dev->parent));
87
88 return RT_EOK;
89 }
90 RTM_EXPORT(rt_device_unregister);
91
92 /**
93 * This function initializes all registered device driver
94 *
95 * @return the error code, RT_EOK on successfully.
96 *
97 * @deprecated since 1.2.x, this function is not needed because the initialization
98 * of a device is performed when applicaiton opens it.
99 */
rt_device_init_all(void)100 rt_err_t rt_device_init_all(void)
101 {
102 return RT_EOK;
103 }
104
105 /**
106 * This function finds a device driver by specified name.
107 *
108 * @param name the device driver's name
109 *
110 * @return the registered device driver on successful, or RT_NULL on failure.
111 */
rt_device_find(const char * name)112 rt_device_t rt_device_find(const char *name)
113 {
114 struct rt_object *object;
115 struct rt_list_node *node;
116 struct rt_object_information *information;
117
118 /* enter critical */
119 if (rt_thread_self() != RT_NULL)
120 rt_enter_critical();
121
122 /* try to find device object */
123 information = rt_object_get_information(RT_Object_Class_Device);
124 RT_ASSERT(information != RT_NULL);
125 for (node = information->object_list.next;
126 node != &(information->object_list);
127 node = node->next)
128 {
129 object = rt_list_entry(node, struct rt_object, list);
130 if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
131 {
132 /* leave critical */
133 if (rt_thread_self() != RT_NULL)
134 rt_exit_critical();
135
136 return (rt_device_t)object;
137 }
138 }
139
140 /* leave critical */
141 if (rt_thread_self() != RT_NULL)
142 rt_exit_critical();
143
144 /* not found */
145 return RT_NULL;
146 }
147 RTM_EXPORT(rt_device_find);
148
149 #ifdef RT_USING_HEAP
150 /**
151 * This function creates a device object with user data size.
152 *
153 * @param type, the kind type of this device object.
154 * @param attach_size, the size of user data.
155 *
156 * @return the allocated device object, or RT_NULL when failed.
157 */
rt_device_create(int type,int attach_size)158 rt_device_t rt_device_create(int type, int attach_size)
159 {
160 int size;
161 rt_device_t device;
162
163 size = RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE);
164 attach_size = RT_ALIGN(attach_size, RT_ALIGN_SIZE);
165 /* use the totoal size */
166 size += attach_size;
167
168 device = (rt_device_t)rt_malloc(size);
169 if (device)
170 {
171 rt_memset(device, 0x0, sizeof(struct rt_device));
172 device->type = (enum rt_device_class_type)type;
173 }
174
175 return device;
176 }
177 RTM_EXPORT(rt_device_create);
178
179 /**
180 * This function destroy the specific device object.
181 *
182 * @param dev, the specific device object.
183 */
rt_device_destroy(rt_device_t dev)184 void rt_device_destroy(rt_device_t dev)
185 {
186 RT_ASSERT(dev != RT_NULL);
187 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
188 RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE);
189
190 rt_object_detach(&(dev->parent));
191
192 /* release this device object */
193 rt_free(dev);
194 }
195 RTM_EXPORT(rt_device_destroy);
196 #endif
197
198 /**
199 * This function will initialize the specified device
200 *
201 * @param dev the pointer of device driver structure
202 *
203 * @return the result
204 */
rt_device_init(rt_device_t dev)205 rt_err_t rt_device_init(rt_device_t dev)
206 {
207 rt_err_t result = RT_EOK;
208
209 RT_ASSERT(dev != RT_NULL);
210
211 /* get device init handler */
212 if (device_init != RT_NULL)
213 {
214 if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
215 {
216 result = device_init(dev);
217 if (result != RT_EOK)
218 {
219 rt_kprintf("To initialize device:%s failed. The error code is %d\n",
220 dev->parent.name, result);
221 }
222 else
223 {
224 dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
225 }
226 }
227 }
228
229 return result;
230 }
231
232 /**
233 * This function will open a device
234 *
235 * @param dev the pointer of device driver structure
236 * @param oflag the flags for device open
237 *
238 * @return the result
239 */
rt_device_open(rt_device_t dev,rt_uint16_t oflag)240 rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
241 {
242 rt_err_t result = RT_EOK;
243
244 RT_ASSERT(dev != RT_NULL);
245 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
246
247 /* if device is not initialized, initialize it. */
248 if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
249 {
250 if (device_init != RT_NULL)
251 {
252 result = device_init(dev);
253 if (result != RT_EOK)
254 {
255 rt_kprintf("To initialize device:%s failed. The error code is %d\n",
256 dev->parent.name, result);
257
258 return result;
259 }
260 }
261
262 dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
263 }
264
265 /* device is a stand alone device and opened */
266 if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) &&
267 (dev->open_flag & RT_DEVICE_OFLAG_OPEN))
268 {
269 return -RT_EBUSY;
270 }
271
272 /* call device open interface */
273 if (device_open != RT_NULL)
274 {
275 result = device_open(dev, oflag);
276 }
277 else
278 {
279 /* set open flag */
280 dev->open_flag = (oflag & RT_DEVICE_OFLAG_MASK);
281 }
282
283 /* set open flag */
284 if (result == RT_EOK || result == -RT_ENOSYS)
285 {
286 dev->open_flag |= RT_DEVICE_OFLAG_OPEN;
287
288 dev->ref_count++;
289 /* don't let bad things happen silently. If you are bitten by this assert,
290 * please set the ref_count to a bigger type. */
291 RT_ASSERT(dev->ref_count != 0);
292 }
293
294 return result;
295 }
296 RTM_EXPORT(rt_device_open);
297
298 /**
299 * This function will close a device
300 *
301 * @param dev the pointer of device driver structure
302 *
303 * @return the result
304 */
rt_device_close(rt_device_t dev)305 rt_err_t rt_device_close(rt_device_t dev)
306 {
307 rt_err_t result = RT_EOK;
308
309 RT_ASSERT(dev != RT_NULL);
310 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
311
312 if (dev->ref_count == 0)
313 return -RT_ERROR;
314
315 dev->ref_count--;
316
317 if (dev->ref_count != 0)
318 return RT_EOK;
319
320 /* call device close interface */
321 if (device_close != RT_NULL)
322 {
323 result = device_close(dev);
324 }
325
326 /* set open flag */
327 if (result == RT_EOK || result == -RT_ENOSYS)
328 dev->open_flag = RT_DEVICE_OFLAG_CLOSE;
329
330 return result;
331 }
332 RTM_EXPORT(rt_device_close);
333
334 /**
335 * This function will read some data from a device.
336 *
337 * @param dev the pointer of device driver structure
338 * @param pos the position of reading
339 * @param buffer the data buffer to save read data
340 * @param size the size of buffer
341 *
342 * @return the actually read size on successful, otherwise negative returned.
343 *
344 * @note since 0.4.0, the unit of size/pos is a block for block device.
345 */
rt_device_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)346 rt_size_t rt_device_read(rt_device_t dev,
347 rt_off_t pos,
348 void *buffer,
349 rt_size_t size)
350 {
351 RT_ASSERT(dev != RT_NULL);
352 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
353
354 if (dev->ref_count == 0)
355 {
356 rt_set_errno(-RT_ERROR);
357 return 0;
358 }
359
360 /* call device read interface */
361 if (device_read != RT_NULL)
362 {
363 return device_read(dev, pos, buffer, size);
364 }
365
366 /* set error code */
367 rt_set_errno(-RT_ENOSYS);
368
369 return 0;
370 }
371 RTM_EXPORT(rt_device_read);
372
373 /**
374 * This function will write some data to a device.
375 *
376 * @param dev the pointer of device driver structure
377 * @param pos the position of written
378 * @param buffer the data buffer to be written to device
379 * @param size the size of buffer
380 *
381 * @return the actually written size on successful, otherwise negative returned.
382 *
383 * @note since 0.4.0, the unit of size/pos is a block for block device.
384 */
rt_device_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)385 rt_size_t rt_device_write(rt_device_t dev,
386 rt_off_t pos,
387 const void *buffer,
388 rt_size_t size)
389 {
390 RT_ASSERT(dev != RT_NULL);
391 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
392
393 if (dev->ref_count == 0)
394 {
395 rt_set_errno(-RT_ERROR);
396 return 0;
397 }
398
399 /* call device write interface */
400 if (device_write != RT_NULL)
401 {
402 return device_write(dev, pos, buffer, size);
403 }
404
405 /* set error code */
406 rt_set_errno(-RT_ENOSYS);
407
408 return 0;
409 }
410 RTM_EXPORT(rt_device_write);
411
412 /**
413 * This function will perform a variety of control functions on devices.
414 *
415 * @param dev the pointer of device driver structure
416 * @param cmd the command sent to device
417 * @param arg the argument of command
418 *
419 * @return the result
420 */
rt_device_control(rt_device_t dev,int cmd,void * arg)421 rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
422 {
423 RT_ASSERT(dev != RT_NULL);
424 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
425
426 /* call device write interface */
427 if (device_control != RT_NULL)
428 {
429 return device_control(dev, cmd, arg);
430 }
431
432 return -RT_ENOSYS;
433 }
434 RTM_EXPORT(rt_device_control);
435
436 /**
437 * This function will set the reception indication callback function. This callback function
438 * is invoked when this device receives data.
439 *
440 * @param dev the pointer of device driver structure
441 * @param rx_ind the indication callback function
442 *
443 * @return RT_EOK
444 */
445 rt_err_t
rt_device_set_rx_indicate(rt_device_t dev,rt_err_t (* rx_ind)(rt_device_t dev,rt_size_t size))446 rt_device_set_rx_indicate(rt_device_t dev,
447 rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))
448 {
449 RT_ASSERT(dev != RT_NULL);
450 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
451
452 dev->rx_indicate = rx_ind;
453
454 return RT_EOK;
455 }
456 RTM_EXPORT(rt_device_set_rx_indicate);
457
458 /**
459 * This function will set the indication callback function when device has
460 * written data to physical hardware.
461 *
462 * @param dev the pointer of device driver structure
463 * @param tx_done the indication callback function
464 *
465 * @return RT_EOK
466 */
467 rt_err_t
rt_device_set_tx_complete(rt_device_t dev,rt_err_t (* tx_done)(rt_device_t dev,void * buffer))468 rt_device_set_tx_complete(rt_device_t dev,
469 rt_err_t (*tx_done)(rt_device_t dev, void *buffer))
470 {
471 RT_ASSERT(dev != RT_NULL);
472 RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);
473
474 dev->tx_complete = tx_done;
475
476 return RT_EOK;
477 }
478 RTM_EXPORT(rt_device_set_tx_complete);
479
480 #endif
481