1 //==========================================================================
5 // Serial device driver for TX3904 on-chip serial devices
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: nickg
46 // Purpose: TX3904 serial device driver
47 // Description: TX3904 serial device driver
49 //####DESCRIPTIONEND####
51 //==========================================================================
53 #include <pkgconf/hal.h>
54 #include <pkgconf/io_serial.h>
55 #include <cyg/hal/hal_io.h>
56 #include <cyg/hal/hal_intr.h>
58 #include <cyg/io/io.h>
59 #include <cyg/io/devtab.h>
60 #include <cyg/io/serial.h>
62 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904
64 cyg_bool cyg_hal_is_break(char *buf, int size);
65 void cyg_hal_user_break( CYG_ADDRWORD *regs );
67 //-------------------------------------------------------------------------
69 extern void diag_printf(const char *fmt, ...);
71 //-------------------------------------------------------------------------
72 // Forward definitions
74 static bool tx3904_serial_init(struct cyg_devtab_entry *tab);
75 static bool tx3904_serial_putc(serial_channel *chan, unsigned char c);
76 static Cyg_ErrNo tx3904_serial_lookup(struct cyg_devtab_entry **tab,
77 struct cyg_devtab_entry *sub_tab,
79 static unsigned char tx3904_serial_getc(serial_channel *chan);
80 static Cyg_ErrNo tx3904_serial_set_config(serial_channel *chan, cyg_uint32 key,
81 const void *xbuf, cyg_uint32 *len);
82 static void tx3904_serial_start_xmit(serial_channel *chan);
83 static void tx3904_serial_stop_xmit(serial_channel *chan);
85 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
86 static cyg_uint32 tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs);
87 static void tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
91 //-------------------------------------------------------------------------
92 // TX3904 serial line control register values:
94 // Offsets to serial control registers from base
95 #define SERIAL_CR 0x00
96 #define SERIAL_SR 0x04
97 #define SERIAL_ICR 0x08
98 #define SERIAL_ISR 0x0C
99 #define SERIAL_FCR 0x10
100 #define SERIAL_BRG 0x14
101 #define SERIAL_TXB 0x20
102 #define SERIAL_RXB 0x30
104 // Status register bits
105 #define ISR_RXRDY 0x01
106 #define ISR_TXRDY 0x02
107 #define ISR_ERROR 0x04
109 // Control register bits
110 #define LCR_SB1 0x0000
111 #define LCR_SB1_5 0x0000
112 #define LCR_SB2 0x0004
113 #define LCR_PN 0x0000 // Parity mode - none
114 #define LCR_PS 0x0000 // Forced "space" parity
115 #define LCR_PM 0x0000 // Forced "mark" parity
116 #define LCR_PE 0x0018 // Parity mode - even
117 #define LCR_PO 0x0010 // Parity mode - odd
118 #define LCR_WL5 0x0001 // not supported - use 7bit
119 #define LCR_WL6 0x0001 // not supported - use 7bit
120 #define LCR_WL7 0x0001 // 7 bit chars
121 #define LCR_WL8 0x0000 // 8 bit chars
123 #define LCR_BRG 0x0020 // Select baud rate generator
125 #define ICR_RXE 0x0001 // receive enable
126 #define ICR_TXE 0x0002 // transmit enable
128 //-------------------------------------------------------------------------
129 // Tables to map input values to hardware settings
131 static unsigned char select_word_length[] = {
132 LCR_WL5, // 5 bits / word (char)
138 static unsigned char select_stop_bits[] = {
140 LCR_SB1, // 1 stop bit
141 LCR_SB1_5, // 1.5 stop bit
142 LCR_SB2 // 2 stop bits
145 static unsigned char select_parity[] = {
147 LCR_PE, // Even parity
148 LCR_PO, // Odd parity
149 LCR_PM, // Mark parity
150 LCR_PS, // Space parity
153 // The values in this table plug straight into the BRG register
154 // in the serial driver hardware. They comprise a baud rate divisor
155 // in the bottom 8 bits and a clock selector in the top 8 bits.
156 // These figures all come from Toshiba.
158 #if (CYGHWR_HAL_MIPS_CPU_FREQ == 50)
160 static unsigned short select_baud[] = {
185 #elif (CYGHWR_HAL_MIPS_CPU_FREQ == 66)
187 static unsigned short select_baud[] = {
214 #error Unsupported CPU frequency
218 //-------------------------------------------------------------------------
219 // Info for each serial device controlled
221 typedef struct tx3904_serial_info {
224 cyg_interrupt interrupt;
225 cyg_handle_t interrupt_handle;
226 cyg_uint8 input_char;
227 cyg_bool input_char_valid;
228 cyg_bool output_ready;
230 } tx3904_serial_info;
232 //-------------------------------------------------------------------------
233 // Callback functions exported by this driver
235 static SERIAL_FUNS(tx3904_serial_funs,
238 tx3904_serial_set_config,
239 tx3904_serial_start_xmit,
240 tx3904_serial_stop_xmit
243 //-------------------------------------------------------------------------
244 // Hardware info for each serial line
246 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
247 static tx3904_serial_info tx3904_serial_info0 = {
249 CYGNUM_HAL_INTERRUPT_SIO_0
251 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE > 0
252 static unsigned char tx3904_serial_out_buf0[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE];
253 static unsigned char tx3904_serial_in_buf0[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE];
255 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
257 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
258 static tx3904_serial_info tx3904_serial_info1 = {
260 CYGNUM_HAL_INTERRUPT_SIO_1
262 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE > 0
263 static unsigned char tx3904_serial_out_buf1[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE];
264 static unsigned char tx3904_serial_in_buf1[CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE];
266 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
268 //-------------------------------------------------------------------------
269 // Channel descriptions:
271 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
272 #define SIZEOF_BUF(_x_) 0
274 #define SIZEOF_BUF(_x_) sizeof(_x_)
277 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
278 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BUFSIZE > 0
279 static SERIAL_CHANNEL_USING_INTERRUPTS(tx3904_serial_channel0,
282 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BAUD),
283 CYG_SERIAL_STOP_DEFAULT,
284 CYG_SERIAL_PARITY_DEFAULT,
285 CYG_SERIAL_WORD_LENGTH_DEFAULT,
286 CYG_SERIAL_FLAGS_DEFAULT,
287 &tx3904_serial_out_buf0[0],
288 SIZEOF_BUF(tx3904_serial_out_buf0),
289 &tx3904_serial_in_buf0[0],
290 SIZEOF_BUF(tx3904_serial_in_buf0)
293 static SERIAL_CHANNEL(tx3904_serial_channel0,
296 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL0_BAUD),
297 CYG_SERIAL_STOP_DEFAULT,
298 CYG_SERIAL_PARITY_DEFAULT,
299 CYG_SERIAL_WORD_LENGTH_DEFAULT,
300 CYG_SERIAL_FLAGS_DEFAULT
303 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
305 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
306 #if CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BUFSIZE > 0
307 static SERIAL_CHANNEL_USING_INTERRUPTS(tx3904_serial_channel1,
310 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BAUD),
311 CYG_SERIAL_STOP_DEFAULT,
312 CYG_SERIAL_PARITY_DEFAULT,
313 CYG_SERIAL_WORD_LENGTH_DEFAULT,
314 CYG_SERIAL_FLAGS_DEFAULT,
315 &tx3904_serial_out_buf1[0],
316 SIZEOF_BUF(tx3904_serial_out_buf1),
317 &tx3904_serial_in_buf1[0],
318 SIZEOF_BUF(tx3904_serial_in_buf1)
321 static SERIAL_CHANNEL(tx3904_serial_channel1,
324 CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_TX39_JMR3904_SERIAL1_BAUD),
325 CYG_SERIAL_STOP_DEFAULT,
326 CYG_SERIAL_PARITY_DEFAULT,
327 CYG_SERIAL_WORD_LENGTH_DEFAULT,
328 CYG_SERIAL_FLAGS_DEFAULT
331 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
333 //-------------------------------------------------------------------------
334 // And finally, the device table entries:
336 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
337 DEVTAB_ENTRY(tx3904_serial_io0,
338 CYGDAT_IO_SERIAL_TX39_JMR3904_SERIAL0_NAME,
339 0, // Does not depend on a lower level interface
340 &cyg_io_serial_devio,
342 tx3904_serial_lookup, // Serial driver may need initializing
343 &tx3904_serial_channel0
345 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
347 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
348 DEVTAB_ENTRY(tx3904_serial_io1,
349 CYGDAT_IO_SERIAL_TX39_JMR3904_SERIAL1_NAME,
350 0, // Does not depend on a lower level interface
351 &cyg_io_serial_devio,
353 tx3904_serial_lookup, // Serial driver may need initializing
354 &tx3904_serial_channel1
356 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL1
358 // ------------------------------------------------------------------------
359 // Delay for some number of character times. This is based on the baud
360 // rate currently set. We use the numbers that plug in to the BRG
361 // clock select and divider to control two loops. The innermost delay
362 // loop uses a count that is derived from dividing the CPU frequency
363 // by the BRG granularity (and we then add 1 to compensate for any
364 // rounding). This gives the number of cycles that the innermost loop
365 // must consume. For the sake of simplicity we assume that this loop
366 // will take 1 cycle per loop, which is roughly true in optimized
369 void delay_char_time(tx3904_serial_info *tx3904_chan, int n)
371 static cyg_uint16 clock_val[4] = { 4, 16, 64, 256 };
372 cyg_uint16 baud_val = select_baud[tx3904_chan->cur_baud];
373 cyg_count32 clock_loop = clock_val[baud_val>>8];
374 cyg_count32 div_loop = baud_val & 0xFF;
375 cyg_count32 bit_time = ((CYGHWR_HAL_MIPS_CPU_FREQ_ACTUAL)/(2457600)) + 1;
377 n *= 11; // allow for start and stop bits and 8 data bits
383 for( i = 0; i < clock_loop; i++ )
384 for( j = 0; j < div_loop; j++ )
385 for( k = 0; k < bit_time; k++ )
390 //-------------------------------------------------------------------------
393 tx3904_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
395 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
398 cyg_uint16 baud_divisor = select_baud[new_config->baud];
400 if (baud_divisor == 0)
401 return false; // Invalid baud rate selected
403 // set up other config values:
405 cr |= select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5];
406 cr |= select_stop_bits[new_config->stop];
407 cr |= select_parity[new_config->parity];
409 // Source transfer clock from BRG
412 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
413 // Enable RX interrupts only at present
414 #ifdef CYGPKG_IO_SERIAL_TX39_JMR3904_SERIAL0
415 if ((chan->out_cbuf.len != 0) || (chan == &tx3904_serial_channel0)) {
417 if (chan->out_cbuf.len != 0) {
423 // Avoid any interrupts while we are fiddling with the line parameters.
424 cyg_drv_interrupt_mask(tx3904_chan->int_num);
427 // In theory we should wait here for the transmitter to drain the
428 // FIFO so we dont change the line parameters with characters
429 // unsent. Unfortunately the TX39 serial devices do not allow us
430 // to discover when the FIFO is empty.
432 delay_char_time(tx3904_chan, 8);
434 // Disable device entirely.
435 // HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, 0);
436 // HAL_WRITE_UINT8(tx3904_chan->base+SERIAL_ICR, 0);
440 HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 7);
441 HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_FCR, 0);
445 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_BRG, baud_divisor );
447 // Write CR into hardware
448 HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_CR, cr);
450 // Write ICR into hardware
451 HAL_WRITE_UINT16(tx3904_chan->base+SERIAL_ICR, icr);
453 // Re-enable interrupts.
454 cyg_drv_interrupt_unmask(tx3904_chan->int_num);
456 // Save current baud rate
457 tx3904_chan->cur_baud = new_config->baud;
459 if (new_config != &chan->config) {
460 chan->config = *new_config;
465 //-------------------------------------------------------------------------
466 // Function to initialize the device. Called at bootstrap time.
468 bool tx3904_serial_init(struct cyg_devtab_entry *tab)
470 serial_channel *chan = (serial_channel *)tab->priv;
471 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
473 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
475 tx3904_chan->cur_baud = CYGNUM_SERIAL_BAUD_38400;
477 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
478 if (chan->out_cbuf.len != 0) {
479 // Install and enable the interrupt
480 cyg_drv_interrupt_create(tx3904_chan->int_num,
482 (cyg_addrword_t)chan, // Data item passed to interrupt handler
483 (cyg_ISR_t *)tx3904_serial_ISR,
485 &tx3904_chan->interrupt_handle,
486 &tx3904_chan->interrupt);
487 cyg_drv_interrupt_attach(tx3904_chan->interrupt_handle);
488 cyg_drv_interrupt_unmask(tx3904_chan->int_num);
492 tx3904_serial_config_port(chan, &chan->config, true);
497 //-------------------------------------------------------------------------
498 // This routine is called when the device is "looked" up (i.e. attached)
501 tx3904_serial_lookup(struct cyg_devtab_entry **tab,
502 struct cyg_devtab_entry *sub_tab,
505 serial_channel *chan = (serial_channel *)(*tab)->priv;
506 (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices
510 //-------------------------------------------------------------------------
511 // Return 'true' if character is sent to device
514 tx3904_serial_putc(serial_channel *chan, unsigned char c)
516 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
519 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
521 if( isr & ISR_TXRDY )
523 HAL_WRITE_UINT8( tx3904_chan->base+SERIAL_TXB, c );
527 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
534 //-------------------------------------------------------------------------
537 tx3904_serial_getc(serial_channel *chan)
540 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
545 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
547 // Eliminate any RX errors
548 if( isr & ISR_ERROR )
554 // HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
556 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, sr );
557 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
560 } while( (isr & ISR_RXRDY) != ISR_RXRDY );
562 HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, c );
566 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
571 //-------------------------------------------------------------------------
574 tx3904_serial_set_config(serial_channel *chan, cyg_uint32 key,
575 const void *xbuf, cyg_uint32 *len)
578 case CYG_IO_SET_CONFIG_SERIAL_INFO:
580 cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
581 if ( *len < sizeof(cyg_serial_info_t) ) {
584 *len = sizeof(cyg_serial_info_t);
585 if ( true != tx3904_serial_config_port(chan, config, false) )
595 //-------------------------------------------------------------------------
596 // Enable the transmitter on the device
599 tx3904_serial_start_xmit(serial_channel *chan)
601 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
602 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
605 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
609 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
613 //-------------------------------------------------------------------------
614 // Disable the transmitter on the device
617 tx3904_serial_stop_xmit(serial_channel *chan)
619 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
620 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
623 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
627 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ICR, icr );
631 //-------------------------------------------------------------------------
632 // Serial I/O - low level interrupt handlers (ISR)
634 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
637 tx3904_serial_ISR(cyg_vector_t vector, cyg_addrword_t data, cyg_addrword_t *regs)
639 serial_channel *chan = (serial_channel *)data;
640 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
642 cyg_uint32 result = 0;
644 cyg_drv_interrupt_mask(tx3904_chan->int_num);
645 cyg_drv_interrupt_acknowledge(tx3904_chan->int_num);
647 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
649 // Eliminate any RX errors
650 if( isr & ISR_ERROR )
656 HAL_READ_UINT16( tx3904_chan->base+SERIAL_SR, sr );
658 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_SR, 0 );
661 // Check for a TX interrupt and set the flag if so.
662 if( isr & ISR_TXRDY )
666 tx3904_chan->output_ready = true;
668 result |= CYG_ISR_CALL_DSR; // Cause DSR to be run
672 // Check here for an RX interrupt and fetch the character. If it
673 // is a ^C then call into GDB stub to handle it.
675 if( isr & ISR_RXRDY )
678 HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );
682 if( cyg_hal_is_break( &rxb , 1 ) )
683 cyg_hal_user_break( regs );
686 tx3904_chan->input_char = rxb;
687 tx3904_chan->input_char_valid = true;
688 result |= CYG_ISR_CALL_DSR; // Cause DSR to be run
693 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
701 //-------------------------------------------------------------------------
702 // Serial I/O - high level interrupt handler (DSR)
704 #ifndef CYGPKG_IO_SERIAL_TX39_JMR3904_POLLED_MODE
707 tx3904_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
709 serial_channel *chan = (serial_channel *)data;
710 tx3904_serial_info *tx3904_chan = (tx3904_serial_info *)chan->dev_priv;
713 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
715 if( tx3904_chan->input_char_valid )
717 (chan->callbacks->rcv_char)(chan, tx3904_chan->input_char);
719 tx3904_chan->input_char_valid = false;
722 // And while we are here, pull any further characters out of the
723 // FIFO. This should help to reduce the interrupt rate.
725 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
727 while( isr & ISR_RXRDY )
730 HAL_READ_UINT8( tx3904_chan->base+SERIAL_RXB, rxb );
732 (chan->callbacks->rcv_char)(chan, rxb);
736 HAL_WRITE_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
737 HAL_READ_UINT16( tx3904_chan->base+SERIAL_ISR, isr );
743 if( tx3904_chan->output_ready )
745 (chan->callbacks->xmt_char)(chan);
747 tx3904_chan->output_ready = false;
750 cyg_drv_interrupt_unmask(tx3904_chan->int_num);
754 #endif // CYGPKG_IO_SERIAL_TX39_JMR3904
756 //-------------------------------------------------------------------------
757 // EOF tx3904_serial.c