|
2
|
1 //========================================================================== |
|
|
2 // |
|
|
3 // io/serial/common/serial.c |
|
|
4 // |
|
|
5 // High level serial driver |
|
|
6 // |
|
|
7 //========================================================================== |
|
|
8 //####COPYRIGHTBEGIN#### |
|
|
9 // |
|
|
10 // ------------------------------------------- |
|
|
11 // The contents of this file are subject to the Cygnus eCos Public License |
|
|
12 // Version 1.0 (the "License"); you may not use this file except in |
|
|
13 // compliance with the License. You may obtain a copy of the License at |
|
|
14 // http://sourceware.cygnus.com/ecos |
|
|
15 // |
|
|
16 // Software distributed under the License is distributed on an "AS IS" |
|
|
17 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the |
|
|
18 // License for the specific language governing rights and limitations under |
|
|
19 // the License. |
|
|
20 // |
|
|
21 // The Original Code is eCos - Embedded Cygnus Operating System, released |
|
|
22 // September 30, 1998. |
|
|
23 // |
|
|
24 // The Initial Developer of the Original Code is Cygnus. Portions created |
|
|
25 // by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions. All Rights Reserved. |
|
|
26 // ------------------------------------------- |
|
|
27 // |
|
|
28 //####COPYRIGHTEND#### |
|
|
29 //========================================================================== |
|
|
30 //#####DESCRIPTIONBEGIN#### |
|
|
31 // |
|
|
32 // Author(s): gthomas |
|
|
33 // Contributors: gthomas |
|
|
34 // Date: 1999-02-04 |
|
|
35 // Purpose: Top level serial driver |
|
|
36 // Description: |
|
|
37 // |
|
|
38 //####DESCRIPTIONEND#### |
|
|
39 // |
|
|
40 //========================================================================== |
|
|
41 |
|
|
42 #include <pkgconf/io.h> |
|
|
43 #include <cyg/io/io.h> |
|
|
44 #include <cyg/io/devtab.h> |
|
|
45 #include <cyg/io/serial.h> |
|
|
46 #include <cyg/infra/diag.h> |
|
|
47 |
|
|
48 static Cyg_ErrNo serial_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len); |
|
|
49 static Cyg_ErrNo serial_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len); |
|
|
50 static Cyg_ErrNo serial_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len); |
|
|
51 static Cyg_ErrNo serial_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len); |
|
|
52 |
|
|
53 DEVIO_TABLE(serial_devio, |
|
|
54 serial_write, |
|
|
55 serial_read, |
|
|
56 serial_get_config, |
|
|
57 serial_set_config |
|
|
58 ); |
|
|
59 |
|
|
60 static void serial_init(serial_channel *chan); |
|
|
61 static void serial_xmt_char(serial_channel *chan); |
|
|
62 static void serial_rcv_char(serial_channel *chan, unsigned char c); |
|
|
63 SERIAL_CALLBACKS(serial_callbacks, |
|
|
64 serial_init, |
|
|
65 serial_xmt_char, |
|
|
66 serial_rcv_char); |
|
|
67 |
|
|
68 static void |
|
|
69 serial_init(serial_channel *chan) |
|
|
70 { |
|
|
71 if (chan->init) return; |
|
|
72 if (chan->out_cbuf.len != 0) { |
|
|
73 #ifdef CYGDBG_IO_INIT |
|
|
74 diag_printf("Set output buffer - buf: %x len: %d\n", chan->out_cbuf.data, chan->out_cbuf.len); |
|
|
75 #endif |
|
|
76 chan->out_cbuf.waiting = false; |
|
|
77 chan->out_cbuf.abort = false; |
|
|
78 chan->out_cbuf.pending = 0; |
|
|
79 cyg_drv_mutex_init(&chan->out_cbuf.lock); |
|
|
80 cyg_drv_cond_init(&chan->out_cbuf.wait, &chan->out_cbuf.lock); |
|
|
81 chan->out_cbuf.low_water = chan->out_cbuf.len / 4; |
|
|
82 } |
|
|
83 if (chan->in_cbuf.len != 0) { |
|
|
84 #ifdef CYGDBG_IO_INIT |
|
|
85 diag_printf("Set input buffer - buf: %x len: %d\n", chan->in_cbuf.data, chan->in_cbuf.len); |
|
|
86 #endif |
|
|
87 chan->in_cbuf.waiting = false; |
|
|
88 chan->in_cbuf.abort = false; |
|
|
89 cyg_drv_mutex_init(&chan->in_cbuf.lock); |
|
|
90 cyg_drv_cond_init(&chan->in_cbuf.wait, &chan->in_cbuf.lock); |
|
|
91 } |
|
|
92 chan->init = true; |
|
|
93 } |
|
|
94 |
|
|
95 static Cyg_ErrNo |
|
|
96 serial_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len) |
|
|
97 { |
|
|
98 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; |
|
|
99 serial_channel *chan = (serial_channel *)t->priv; |
|
|
100 serial_funs *funs = chan->funs; |
|
|
101 cyg_int32 size = *len; |
|
|
102 cyg_uint8 *buf = (cyg_uint8 *)_buf; |
|
|
103 int next; |
|
|
104 cbuf_t *cbuf = &chan->out_cbuf; |
|
|
105 Cyg_ErrNo res = ENOERR; |
|
|
106 |
|
|
107 cyg_drv_mutex_lock(&cbuf->lock); |
|
|
108 if (cbuf->len == 0) { |
|
|
109 // Non interrupt driven (i.e. polled) operation |
|
|
110 while (size-- > 0) { |
|
|
111 while ((funs->putc)(chan, *buf) == false) ; // Ignore full, keep trying |
|
|
112 buf++; |
|
|
113 } |
|
|
114 } else { |
|
|
115 cyg_drv_dsr_lock(); // Avoid race condition testing pointers |
|
|
116 while (size > 0) { |
|
|
117 next = cbuf->put + 1; |
|
|
118 if (next == cbuf->len) next = 0; |
|
|
119 if (next == cbuf->get) { |
|
|
120 cbuf->waiting = true; |
|
|
121 // Buffer full - wait for space |
|
|
122 (funs->start_xmit)(chan); // Make sure xmit is running |
|
|
123 cbuf->pending += size; // Have this much more to send [eventually] |
|
|
124 cyg_drv_cond_wait(&cbuf->wait); |
|
|
125 cbuf->pending -= size; |
|
|
126 if (cbuf->abort) { |
|
|
127 // Give up! |
|
|
128 cbuf->abort = false; |
|
|
129 cbuf->waiting = false; |
|
|
130 res = -EINTR; |
|
|
131 break; |
|
|
132 } |
|
|
133 } else { |
|
|
134 cbuf->data[cbuf->put++] = *buf++; |
|
|
135 cbuf->put = next; |
|
|
136 size--; // Only count if actually sent! |
|
|
137 } |
|
|
138 } |
|
|
139 cyg_drv_dsr_unlock(); |
|
|
140 (funs->start_xmit)(chan); // Start output as necessary |
|
|
141 } |
|
|
142 cyg_drv_mutex_unlock(&cbuf->lock); |
|
|
143 return res; |
|
|
144 } |
|
|
145 |
|
|
146 static Cyg_ErrNo |
|
|
147 serial_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len) |
|
|
148 { |
|
|
149 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; |
|
|
150 serial_channel *chan = (serial_channel *)t->priv; |
|
|
151 serial_funs *funs = chan->funs; |
|
|
152 cyg_uint8 *buf = (cyg_uint8 *)_buf; |
|
|
153 cyg_int32 size = 0; |
|
|
154 cbuf_t *cbuf = &chan->in_cbuf; |
|
|
155 Cyg_ErrNo res = ENOERR; |
|
|
156 |
|
|
157 cyg_drv_mutex_lock(&cbuf->lock); |
|
|
158 if (cbuf->len == 0) { |
|
|
159 // Non interrupt driven (i.e. polled) operation |
|
|
160 while (size++ < *len) { |
|
|
161 *buf++ = (funs->getc)(chan); |
|
|
162 } |
|
|
163 } else { |
|
|
164 cyg_drv_dsr_lock(); // Avoid races |
|
|
165 while (size < *len) { |
|
|
166 if (cbuf->get != cbuf->put) { |
|
|
167 *buf++ = cbuf->data[cbuf->get]; |
|
|
168 if (++cbuf->get == cbuf->len) cbuf->get = 0; |
|
|
169 size++; |
|
|
170 } else { |
|
|
171 cbuf->waiting = true; |
|
|
172 cyg_drv_cond_wait(&cbuf->wait); |
|
|
173 if (cbuf->abort) { |
|
|
174 // Give up! |
|
|
175 cbuf->abort = false; |
|
|
176 cbuf->waiting = false; |
|
|
177 res = -EINTR; |
|
|
178 break; |
|
|
179 } |
|
|
180 } |
|
|
181 } |
|
|
182 cyg_drv_dsr_unlock(); |
|
|
183 } |
|
|
184 cyg_drv_mutex_unlock(&cbuf->lock); |
|
|
185 return res; |
|
|
186 } |
|
|
187 |
|
|
188 static Cyg_ErrNo |
|
|
189 serial_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len) |
|
|
190 { |
|
|
191 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; |
|
|
192 serial_channel *chan = (serial_channel *)t->priv; |
|
|
193 cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf; |
|
|
194 Cyg_ErrNo res = ENOERR; |
|
|
195 cbuf_t *cbuf = &chan->out_cbuf; |
|
|
196 serial_funs *funs = chan->funs; |
|
|
197 |
|
|
198 switch (key) { |
|
|
199 case CYG_IO_GET_CONFIG_SERIAL_INFO: |
|
|
200 if (*len < sizeof(cyg_serial_info_t)) { |
|
|
201 return -EINVAL; |
|
|
202 } |
|
|
203 *buf = chan->config; |
|
|
204 *len = sizeof(chan->config); |
|
|
205 break; |
|
|
206 case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN: |
|
|
207 // Wait for any pending output to complete |
|
|
208 if (cbuf->len == 0) break; // Nothing to do if not buffered |
|
|
209 cyg_drv_mutex_lock(&cbuf->lock); // Stop any further output processing |
|
|
210 cyg_drv_dsr_lock(); |
|
|
211 while (cbuf->pending || (cbuf->get != cbuf->put)) { |
|
|
212 cbuf->waiting = true; |
|
|
213 cyg_drv_cond_wait(&cbuf->wait); |
|
|
214 } |
|
|
215 cyg_drv_dsr_unlock(); |
|
|
216 cyg_drv_mutex_unlock(&cbuf->lock); |
|
|
217 break; |
|
|
218 case CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH: |
|
|
219 // Flush any buffered input |
|
|
220 cbuf = &chan->in_cbuf; |
|
|
221 if (cbuf->len == 0) break; // Nothing to do if not buffered |
|
|
222 cyg_drv_mutex_lock(&cbuf->lock); // Stop any further input processing |
|
|
223 cyg_drv_dsr_lock(); |
|
|
224 if (cbuf->waiting) { |
|
|
225 cbuf->abort = true; |
|
|
226 cyg_drv_cond_signal(&cbuf->wait); |
|
|
227 cbuf->waiting = false; |
|
|
228 } |
|
|
229 cbuf->get = cbuf->put; // Flush buffered input |
|
|
230 cyg_drv_dsr_unlock(); |
|
|
231 cyg_drv_mutex_unlock(&cbuf->lock); |
|
|
232 break; |
|
|
233 case CYG_IO_GET_CONFIG_SERIAL_ABORT: |
|
|
234 // Abort any outstanding I/O, including blocked reads |
|
|
235 // Caution - assumed to be called from 'timeout' (i.e. DSR) code |
|
|
236 cbuf = &chan->in_cbuf; |
|
|
237 if (cbuf->len != 0) { |
|
|
238 cbuf->abort = true; |
|
|
239 cyg_drv_cond_signal(&cbuf->wait); |
|
|
240 } |
|
|
241 cbuf = &chan->out_cbuf; |
|
|
242 if (cbuf->len != 0) { |
|
|
243 cbuf->abort = true; |
|
|
244 cyg_drv_cond_signal(&cbuf->wait); |
|
|
245 } |
|
|
246 break; |
|
|
247 case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH: |
|
|
248 // Throw away any pending output |
|
|
249 if (cbuf->len == 0) break; // Nothing to do if not buffered |
|
|
250 cyg_drv_mutex_lock(&cbuf->lock); // Stop any further output processing |
|
|
251 cyg_drv_dsr_lock(); |
|
|
252 if (cbuf->get != cbuf->put) { |
|
|
253 cbuf->get = cbuf->put; // Empties queue! |
|
|
254 (funs->stop_xmit)(chan); // Done with transmit |
|
|
255 } |
|
|
256 if (cbuf->waiting) { |
|
|
257 cbuf->abort = true; |
|
|
258 cyg_drv_cond_signal(&cbuf->wait); |
|
|
259 cbuf->waiting = false; |
|
|
260 } |
|
|
261 cyg_drv_dsr_unlock(); |
|
|
262 cyg_drv_mutex_unlock(&cbuf->lock); |
|
|
263 break; |
|
|
264 default: |
|
|
265 res = -EINVAL; |
|
|
266 } |
|
|
267 return res; |
|
|
268 } |
|
|
269 |
|
|
270 static Cyg_ErrNo |
|
|
271 serial_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) |
|
|
272 { |
|
|
273 Cyg_ErrNo res = ENOERR; |
|
|
274 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; |
|
|
275 serial_channel *chan = (serial_channel *)t->priv; |
|
|
276 serial_funs *funs = chan->funs; |
|
|
277 cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf; |
|
|
278 |
|
|
279 switch (key) { |
|
|
280 case CYG_IO_SET_CONFIG_SERIAL_INFO: |
|
|
281 if (*len != sizeof(cyg_serial_info_t)) { |
|
|
282 return -EINVAL; |
|
|
283 } |
|
|
284 if ((funs->set_config)(chan, buf) != true) { |
|
|
285 // Device is not happy with paramters or they cannot be set |
|
|
286 res = -EINVAL; |
|
|
287 } |
|
|
288 break; |
|
|
289 default: |
|
|
290 res = -EINVAL; |
|
|
291 } |
|
|
292 return res; |
|
|
293 } |
|
|
294 |
|
|
295 static void |
|
|
296 serial_xmt_char(serial_channel *chan) |
|
|
297 { |
|
|
298 cbuf_t *cbuf = &chan->out_cbuf; |
|
|
299 serial_funs *funs = chan->funs; |
|
|
300 unsigned char c; |
|
|
301 int space; |
|
|
302 |
|
|
303 while (cbuf->get != cbuf->put) { |
|
|
304 c = cbuf->data[cbuf->get]; |
|
|
305 if ((funs->putc)(chan, c)) { |
|
|
306 cbuf->get++; |
|
|
307 if (cbuf->get == cbuf->len) cbuf->get = 0; |
|
|
308 if (cbuf->waiting) { |
|
|
309 // See if there is now enough room to restart writer |
|
|
310 space = (cbuf->len + cbuf->get) - cbuf->put; |
|
|
311 if (space > cbuf->len) space -= cbuf->len; |
|
|
312 if (space >= cbuf->low_water) { |
|
|
313 cbuf->waiting = false; |
|
|
314 cyg_drv_cond_broadcast(&cbuf->wait); |
|
|
315 } |
|
|
316 } |
|
|
317 } else { |
|
|
318 return; // Need to wait for more space |
|
|
319 } |
|
|
320 } |
|
|
321 (funs->stop_xmit)(chan); // Done with transmit |
|
|
322 if (cbuf->waiting) { |
|
|
323 cbuf->waiting = false; |
|
|
324 cyg_drv_cond_signal(&cbuf->wait); |
|
|
325 } |
|
|
326 } |
|
|
327 |
|
|
328 static void |
|
|
329 serial_rcv_char(serial_channel *chan, unsigned char c) |
|
|
330 { |
|
|
331 cbuf_t *cbuf = &chan->in_cbuf; |
|
|
332 |
|
|
333 cbuf->data[cbuf->put++] = c; |
|
|
334 if (cbuf->put == cbuf->len) cbuf->put = 0; |
|
|
335 if (cbuf->waiting) { |
|
|
336 cbuf->waiting = false; |
|
|
337 cyg_drv_cond_signal(&cbuf->wait); |
|
|
338 } |
|
|
339 } |
|
|
340 |