Mercurial > ecos
annotate packages/io/serial/current/src/common/serial.c @ 4:1d7f19c9e4d1 ecos-sw-1999-05-11
Merge from eCos master repository on 1999-05-11-21:11:10-BST
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 14:07:25 +0000 |
| parents | 443894e2e912 |
| children | 5f5102441818 |
| rev | line source |
|---|---|
| 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; | |
|
4
1d7f19c9e4d1
Merge from eCos master repository on 1999-05-11-21:11:10-BST
jlarmour
parents:
2
diff
changeset
|
106 cbuf->abort = false; |
| 2 | 107 |
| 108 cyg_drv_mutex_lock(&cbuf->lock); | |
| 109 if (cbuf->len == 0) { | |
| 110 // Non interrupt driven (i.e. polled) operation | |
| 111 while (size-- > 0) { | |
| 112 while ((funs->putc)(chan, *buf) == false) ; // Ignore full, keep trying | |
| 113 buf++; | |
| 114 } | |
| 115 } else { | |
| 116 cyg_drv_dsr_lock(); // Avoid race condition testing pointers | |
| 117 while (size > 0) { | |
| 118 next = cbuf->put + 1; | |
| 119 if (next == cbuf->len) next = 0; | |
| 120 if (next == cbuf->get) { | |
| 121 cbuf->waiting = true; | |
| 122 // Buffer full - wait for space | |
| 123 (funs->start_xmit)(chan); // Make sure xmit is running | |
| 124 cbuf->pending += size; // Have this much more to send [eventually] | |
| 125 cyg_drv_cond_wait(&cbuf->wait); | |
| 126 cbuf->pending -= size; | |
| 127 if (cbuf->abort) { | |
| 128 // Give up! | |
| 129 cbuf->abort = false; | |
| 130 cbuf->waiting = false; | |
| 131 res = -EINTR; | |
| 132 break; | |
| 133 } | |
| 134 } else { | |
| 135 cbuf->data[cbuf->put++] = *buf++; | |
| 136 cbuf->put = next; | |
| 137 size--; // Only count if actually sent! | |
| 138 } | |
| 139 } | |
| 140 cyg_drv_dsr_unlock(); | |
| 141 (funs->start_xmit)(chan); // Start output as necessary | |
| 142 } | |
| 143 cyg_drv_mutex_unlock(&cbuf->lock); | |
| 144 return res; | |
| 145 } | |
| 146 | |
| 147 static Cyg_ErrNo | |
| 148 serial_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len) | |
| 149 { | |
| 150 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 151 serial_channel *chan = (serial_channel *)t->priv; | |
| 152 serial_funs *funs = chan->funs; | |
| 153 cyg_uint8 *buf = (cyg_uint8 *)_buf; | |
| 154 cyg_int32 size = 0; | |
| 155 cbuf_t *cbuf = &chan->in_cbuf; | |
| 156 Cyg_ErrNo res = ENOERR; | |
|
4
1d7f19c9e4d1
Merge from eCos master repository on 1999-05-11-21:11:10-BST
jlarmour
parents:
2
diff
changeset
|
157 cbuf->abort = false; |
| 2 | 158 |
| 159 cyg_drv_mutex_lock(&cbuf->lock); | |
| 160 if (cbuf->len == 0) { | |
| 161 // Non interrupt driven (i.e. polled) operation | |
| 162 while (size++ < *len) { | |
| 163 *buf++ = (funs->getc)(chan); | |
| 164 } | |
| 165 } else { | |
| 166 cyg_drv_dsr_lock(); // Avoid races | |
| 167 while (size < *len) { | |
| 168 if (cbuf->get != cbuf->put) { | |
| 169 *buf++ = cbuf->data[cbuf->get]; | |
| 170 if (++cbuf->get == cbuf->len) cbuf->get = 0; | |
| 171 size++; | |
| 172 } else { | |
| 173 cbuf->waiting = true; | |
| 174 cyg_drv_cond_wait(&cbuf->wait); | |
| 175 if (cbuf->abort) { | |
| 176 // Give up! | |
| 177 cbuf->abort = false; | |
| 178 cbuf->waiting = false; | |
| 179 res = -EINTR; | |
| 180 break; | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 cyg_drv_dsr_unlock(); | |
| 185 } | |
| 186 cyg_drv_mutex_unlock(&cbuf->lock); | |
| 187 return res; | |
| 188 } | |
| 189 | |
| 190 static Cyg_ErrNo | |
| 191 serial_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len) | |
| 192 { | |
| 193 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 194 serial_channel *chan = (serial_channel *)t->priv; | |
| 195 cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf; | |
| 196 Cyg_ErrNo res = ENOERR; | |
| 197 cbuf_t *cbuf = &chan->out_cbuf; | |
| 198 serial_funs *funs = chan->funs; | |
| 199 | |
| 200 switch (key) { | |
| 201 case CYG_IO_GET_CONFIG_SERIAL_INFO: | |
| 202 if (*len < sizeof(cyg_serial_info_t)) { | |
| 203 return -EINVAL; | |
| 204 } | |
| 205 *buf = chan->config; | |
| 206 *len = sizeof(chan->config); | |
| 207 break; | |
| 208 case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN: | |
| 209 // Wait for any pending output to complete | |
| 210 if (cbuf->len == 0) break; // Nothing to do if not buffered | |
| 211 cyg_drv_mutex_lock(&cbuf->lock); // Stop any further output processing | |
| 212 cyg_drv_dsr_lock(); | |
| 213 while (cbuf->pending || (cbuf->get != cbuf->put)) { | |
| 214 cbuf->waiting = true; | |
| 215 cyg_drv_cond_wait(&cbuf->wait); | |
| 216 } | |
| 217 cyg_drv_dsr_unlock(); | |
| 218 cyg_drv_mutex_unlock(&cbuf->lock); | |
| 219 break; | |
| 220 case CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH: | |
| 221 // Flush any buffered input | |
| 222 cbuf = &chan->in_cbuf; | |
| 223 if (cbuf->len == 0) break; // Nothing to do if not buffered | |
| 224 cyg_drv_mutex_lock(&cbuf->lock); // Stop any further input processing | |
| 225 cyg_drv_dsr_lock(); | |
| 226 if (cbuf->waiting) { | |
| 227 cbuf->abort = true; | |
| 228 cyg_drv_cond_signal(&cbuf->wait); | |
| 229 cbuf->waiting = false; | |
| 230 } | |
| 231 cbuf->get = cbuf->put; // Flush buffered input | |
| 232 cyg_drv_dsr_unlock(); | |
| 233 cyg_drv_mutex_unlock(&cbuf->lock); | |
| 234 break; | |
| 235 case CYG_IO_GET_CONFIG_SERIAL_ABORT: | |
| 236 // Abort any outstanding I/O, including blocked reads | |
| 237 // Caution - assumed to be called from 'timeout' (i.e. DSR) code | |
| 238 cbuf = &chan->in_cbuf; | |
| 239 if (cbuf->len != 0) { | |
| 240 cbuf->abort = true; | |
| 241 cyg_drv_cond_signal(&cbuf->wait); | |
| 242 } | |
| 243 cbuf = &chan->out_cbuf; | |
| 244 if (cbuf->len != 0) { | |
| 245 cbuf->abort = true; | |
| 246 cyg_drv_cond_signal(&cbuf->wait); | |
| 247 } | |
| 248 break; | |
| 249 case CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH: | |
| 250 // Throw away any pending output | |
| 251 if (cbuf->len == 0) break; // Nothing to do if not buffered | |
| 252 cyg_drv_mutex_lock(&cbuf->lock); // Stop any further output processing | |
| 253 cyg_drv_dsr_lock(); | |
| 254 if (cbuf->get != cbuf->put) { | |
| 255 cbuf->get = cbuf->put; // Empties queue! | |
| 256 (funs->stop_xmit)(chan); // Done with transmit | |
| 257 } | |
| 258 if (cbuf->waiting) { | |
| 259 cbuf->abort = true; | |
| 260 cyg_drv_cond_signal(&cbuf->wait); | |
| 261 cbuf->waiting = false; | |
| 262 } | |
| 263 cyg_drv_dsr_unlock(); | |
| 264 cyg_drv_mutex_unlock(&cbuf->lock); | |
| 265 break; | |
| 266 default: | |
| 267 res = -EINVAL; | |
| 268 } | |
| 269 return res; | |
| 270 } | |
| 271 | |
| 272 static Cyg_ErrNo | |
| 273 serial_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) | |
| 274 { | |
| 275 Cyg_ErrNo res = ENOERR; | |
| 276 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 277 serial_channel *chan = (serial_channel *)t->priv; | |
| 278 serial_funs *funs = chan->funs; | |
| 279 cyg_serial_info_t *buf = (cyg_serial_info_t *)xbuf; | |
| 280 | |
| 281 switch (key) { | |
| 282 case CYG_IO_SET_CONFIG_SERIAL_INFO: | |
| 283 if (*len != sizeof(cyg_serial_info_t)) { | |
| 284 return -EINVAL; | |
| 285 } | |
| 286 if ((funs->set_config)(chan, buf) != true) { | |
| 287 // Device is not happy with paramters or they cannot be set | |
| 288 res = -EINVAL; | |
| 289 } | |
| 290 break; | |
| 291 default: | |
| 292 res = -EINVAL; | |
| 293 } | |
| 294 return res; | |
| 295 } | |
| 296 | |
| 297 static void | |
| 298 serial_xmt_char(serial_channel *chan) | |
| 299 { | |
| 300 cbuf_t *cbuf = &chan->out_cbuf; | |
| 301 serial_funs *funs = chan->funs; | |
| 302 unsigned char c; | |
| 303 int space; | |
| 304 | |
| 305 while (cbuf->get != cbuf->put) { | |
| 306 c = cbuf->data[cbuf->get]; | |
| 307 if ((funs->putc)(chan, c)) { | |
| 308 cbuf->get++; | |
| 309 if (cbuf->get == cbuf->len) cbuf->get = 0; | |
| 310 if (cbuf->waiting) { | |
| 311 // See if there is now enough room to restart writer | |
| 312 space = (cbuf->len + cbuf->get) - cbuf->put; | |
| 313 if (space > cbuf->len) space -= cbuf->len; | |
| 314 if (space >= cbuf->low_water) { | |
| 315 cbuf->waiting = false; | |
| 316 cyg_drv_cond_broadcast(&cbuf->wait); | |
| 317 } | |
| 318 } | |
| 319 } else { | |
| 320 return; // Need to wait for more space | |
| 321 } | |
| 322 } | |
| 323 (funs->stop_xmit)(chan); // Done with transmit | |
| 324 if (cbuf->waiting) { | |
| 325 cbuf->waiting = false; | |
| 326 cyg_drv_cond_signal(&cbuf->wait); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 static void | |
| 331 serial_rcv_char(serial_channel *chan, unsigned char c) | |
| 332 { | |
| 333 cbuf_t *cbuf = &chan->in_cbuf; | |
| 334 | |
| 335 cbuf->data[cbuf->put++] = c; | |
| 336 if (cbuf->put == cbuf->len) cbuf->put = 0; | |
| 337 if (cbuf->waiting) { | |
| 338 cbuf->waiting = false; | |
| 339 cyg_drv_cond_signal(&cbuf->wait); | |
| 340 } | |
| 341 } | |
| 342 |
