1 /*
2 * COPYRIGHT (C) 2018, Real-Thread Information Technology Ltd
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2013-11-04 Grissiom add comment
9 */
10
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <rtdevice.h>
14
15 #include "vbus.h"
16
_rx_indicate(void * ctx)17 static void _rx_indicate(void *ctx)
18 {
19 rt_device_t dev = ctx;
20
21 if (dev->rx_indicate)
22 dev->rx_indicate(dev, 0);
23 }
24
_tx_complete(void * ctx)25 static void _tx_complete(void *ctx)
26 {
27 rt_device_t dev = ctx;
28
29 if (dev->tx_complete)
30 dev->tx_complete(dev, 0);
31 }
32
_open(rt_device_t dev,rt_uint16_t oflag)33 static rt_err_t _open(rt_device_t dev, rt_uint16_t oflag)
34 {
35 int chnr;
36 struct rt_vbus_dev *vdev = dev->user_data;
37
38 if (vdev->chnr)
39 return RT_EOK;
40
41 /* FIXME: request the same name for twice will crash */
42 chnr = rt_vbus_request_chn(&vdev->req, RT_WAITING_FOREVER);
43 if (chnr < 0)
44 return chnr;
45
46 vdev->chnr = chnr;
47 rt_vbus_register_listener(chnr, RT_VBUS_EVENT_ID_RX, _rx_indicate, dev);
48 rt_vbus_register_listener(chnr, RT_VBUS_EVENT_ID_TX, _tx_complete, dev);
49
50 return RT_EOK;
51 }
52
_close(rt_device_t dev)53 static rt_err_t _close(rt_device_t dev)
54 {
55 struct rt_vbus_dev *vdev = dev->user_data;
56
57 RT_ASSERT(vdev->chnr != 0);
58
59 rt_vbus_close_chn(vdev->chnr);
60 vdev->chnr = 0;
61
62 return RT_EOK;
63 }
64
_read(rt_device_t dev,rt_off_t pos,void * buffer,rt_size_t size)65 static rt_size_t _read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
66 {
67 rt_size_t outsz = 0;
68 struct rt_vbus_dev *vdev = dev->user_data;
69
70 RT_ASSERT(vdev->chnr != 0);
71
72 if (vdev->act == RT_NULL)
73 {
74 vdev->act = rt_vbus_data_pop(vdev->chnr);
75 vdev->pos = 0;
76 }
77
78 while (1)
79 {
80 rt_err_t err;
81
82 while (vdev->act)
83 {
84 rt_size_t cpysz;
85
86 if (size - outsz > vdev->act->size - vdev->pos)
87 cpysz = vdev->act->size - vdev->pos;
88 else
89 cpysz = size - outsz;
90
91 rt_memcpy((char*)buffer + outsz, ((char*)(vdev->act+1)) + vdev->pos, cpysz);
92 vdev->pos += cpysz;
93
94 outsz += cpysz;
95 if (outsz == size)
96 {
97 return outsz;
98 }
99 else if (outsz > size)
100 RT_ASSERT(0);
101
102 /* free old and get new */
103 rt_free(vdev->act);
104 vdev->act = rt_vbus_data_pop(vdev->chnr);
105 vdev->pos = 0;
106 }
107
108 /* TODO: We don't want to touch the rx_indicate here. But this lead to
109 * some duplication. Maybe we should find a better way to handle this.
110 */
111 if (rt_interrupt_get_nest() == 0)
112 {
113 err = rt_vbus_listen_on(vdev->chnr, RT_WAITING_FOREVER);
114 }
115 else
116 {
117 err = rt_vbus_listen_on(vdev->chnr, 0);
118 }
119 if (err != RT_EOK)
120 {
121 rt_set_errno(err);
122 return outsz;
123 }
124 vdev->act = rt_vbus_data_pop(vdev->chnr);
125 vdev->pos = 0;
126 }
127 }
128
_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)129 static rt_size_t _write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
130 {
131 rt_err_t err;
132 struct rt_vbus_dev *vdev = dev->user_data;
133
134 RT_ASSERT(vdev->chnr != 0);
135
136 if (rt_interrupt_get_nest() == 0)
137 {
138 /* Thread context. */
139 err = rt_vbus_post(vdev->chnr, vdev->req.prio,
140 buffer, size, RT_WAITING_FOREVER);
141 }
142 else
143 {
144 /* Interrupt context. */
145 err = rt_vbus_post(vdev->chnr, vdev->req.prio,
146 buffer, size, 0);
147 }
148
149 if (err)
150 {
151 rt_set_errno(err);
152 return 0;
153 }
154
155 return size;
156 }
157
_control(rt_device_t dev,int cmd,void * args)158 rt_err_t _control(rt_device_t dev, int cmd, void *args)
159 {
160 RT_ASSERT(dev);
161
162 switch (cmd) {
163 case VBUS_IOC_LISCFG: {
164 struct rt_vbus_dev *vdev = dev->user_data;
165 struct rt_vbus_dev_liscfg *liscfg = args;
166
167 RT_ASSERT(vdev->chnr != 0);
168 if (!liscfg)
169 return -RT_ERROR;
170
171 rt_vbus_register_listener(vdev->chnr, liscfg->event,
172 liscfg->listener, liscfg->ctx);
173 return RT_EOK;
174 }
175 break;
176 #ifdef RT_VBUS_USING_FLOW_CONTROL
177 case VBUS_IOCRECV_WM: {
178 struct rt_vbus_dev *vdev = dev->user_data;
179 struct rt_vbus_wm_cfg *cfg;
180
181 RT_ASSERT(vdev->chnr != 0);
182
183 if (!args)
184 return -RT_ERROR;
185
186 cfg = (struct rt_vbus_wm_cfg*)args;
187 if (cfg->low > cfg->high)
188 return -RT_ERROR;
189
190 rt_vbus_set_recv_wm(vdev->chnr, cfg->low, cfg->high);
191 return RT_EOK;
192 }
193 break;
194 case VBUS_IOCPOST_WM: {
195 struct rt_vbus_dev *vdev = dev->user_data;
196 struct rt_vbus_wm_cfg *cfg;
197
198 RT_ASSERT(vdev->chnr != 0);
199
200 if (!args)
201 return -RT_ERROR;
202
203 cfg = (struct rt_vbus_wm_cfg*)args;
204 if (cfg->low > cfg->high)
205 return -RT_ERROR;
206
207 rt_vbus_set_post_wm(vdev->chnr, cfg->low, cfg->high);
208 return RT_EOK;
209 }
210 break;
211 #endif
212 default:
213 break;
214 };
215
216 return -RT_ENOSYS;
217 }
218
rt_vbus_get_chnnr(rt_device_t dev)219 rt_uint8_t rt_vbus_get_chnnr(rt_device_t dev)
220 {
221 struct rt_vbus_dev *vdev;
222
223 RT_ASSERT(dev);
224
225 vdev = dev->user_data;
226
227 return vdev->chnr;
228 }
229
rt_vbus_chnx_register_disconn(rt_device_t dev,rt_vbus_event_listener indi,void * ctx)230 void rt_vbus_chnx_register_disconn(rt_device_t dev,
231 rt_vbus_event_listener indi,
232 void *ctx)
233 {
234 struct rt_vbus_dev *vdev = dev->user_data;
235
236 RT_ASSERT(vdev->chnr != 0);
237
238 if (vdev)
239 rt_vbus_register_listener(vdev->chnr, RT_VBUS_EVENT_ID_DISCONN,
240 indi, ctx);
241 }
242
243 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
244
245 extern struct rt_vbus_dev rt_vbus_chn_devx[];
246 static struct rt_device _devx[32];
247
rt_vbus_chnx_init(void)248 rt_err_t rt_vbus_chnx_init(void)
249 {
250 int i;
251 struct rt_vbus_dev *p;
252
253 for (i = 0, p = rt_vbus_chn_devx;
254 i < ARRAY_SIZE(_devx) && p->req.name;
255 i++, p++)
256 {
257 _devx[i].type = RT_Device_Class_Char;
258 _devx[i].open = _open;
259 _devx[i].close = _close;
260 _devx[i].read = _read;
261 _devx[i].write = _write;
262 _devx[i].control = _control;
263 _devx[i].user_data = p;
264 rt_device_register(&_devx[i], p->req.name, RT_DEVICE_FLAG_RDWR);
265 }
266
267 return RT_EOK;
268 }
269