]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/serial/generic/16x5x/v2_0/src/ser_16x5x.c
Initial revision
[karo-tx-redboot.git] / packages / devs / serial / generic / 16x5x / v2_0 / src / ser_16x5x.c
1 //==========================================================================
2 //
3 //      io/serial/generic/16x5x/ser_16x5x.c
4 //
5 //      Generic 16x5x serial driver
6 //
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.
12 // Copyright (C) 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas, jlarmour, jskov
46 // Date:         1999-02-04
47 // Purpose:      16x5x generic serial driver
48 // Description: 
49 //
50 //####DESCRIPTIONEND####
51 //
52 //==========================================================================
53
54 #include <pkgconf/system.h>
55 #include <pkgconf/io_serial.h>
56 #include <pkgconf/io.h>
57
58 #include <cyg/io/io.h>
59 #include <cyg/hal/hal_intr.h>
60 #include <cyg/io/devtab.h>
61 #include <cyg/io/serial.h>
62 #include <cyg/infra/diag.h>
63 #include <cyg/infra/cyg_ass.h>
64 #include <cyg/hal/hal_io.h>
65
66 // Only compile driver if an inline file with driver details was selected.
67 #ifdef CYGDAT_IO_SERIAL_GENERIC_16X5X_INL
68
69 #ifndef CYGPRI_IO_SERIAL_GENERIC_16X5X_STEP
70 #define CYGPRI_IO_SERIAL_GENERIC_16X5X_STEP 1
71 #endif
72
73 #define SER_REG(_x_) ((_x_)*CYGPRI_IO_SERIAL_GENERIC_16X5X_STEP)
74
75 // Receive control Registers
76 #define REG_rhr SER_REG(0)    // Receive holding register
77 #define REG_isr SER_REG(2)    // Interrupt status register
78 #define REG_lsr SER_REG(5)    // Line status register
79 #define REG_msr SER_REG(6)    // Modem status register
80 #define REG_scr SER_REG(7)    // Scratch register
81
82 // Transmit control Registers
83 #define REG_thr SER_REG(0)    // Transmit holding register
84 #define REG_ier SER_REG(1)    // Interrupt enable register
85 #define REG_fcr SER_REG(2)    // FIFO control register
86 #define REG_lcr SER_REG(3)    // Line control register
87 #define REG_mcr SER_REG(4)    // Modem control register
88 #define REG_ldl SER_REG(0)    // LSB of baud rate
89 #define REG_mdl SER_REG(1)    // MSB of baud rate
90
91 // Interrupt Enable Register
92 #define IER_RCV 0x01
93 #define IER_XMT 0x02
94 #define IER_LS  0x04
95 #define IER_MS  0x08
96
97 // Line Control Register
98 #define LCR_WL5 0x00    // Word length
99 #define LCR_WL6 0x01
100 #define LCR_WL7 0x02
101 #define LCR_WL8 0x03
102 #define LCR_SB1 0x00    // Number of stop bits
103 #define LCR_SB1_5 0x04  // 1.5 -> only valid with 5 bit words
104 #define LCR_SB2 0x04
105 #define LCR_PN  0x00    // Parity mode - none
106 #define LCR_PE  0x18    // Parity mode - even
107 #define LCR_PO  0x08    // Parity mode - odd
108 #define LCR_PM  0x28    // Forced "mark" parity
109 #define LCR_PS  0x38    // Forced "space" parity
110 #define LCR_DL  0x80    // Enable baud rate latch
111
112 // Line Status Register
113 #define LSR_RSR 0x01
114 #define LSR_OE  0x02
115 #define LSR_PE  0x04
116 #define LSR_FE  0x08
117 #define LSR_BI  0x10
118 #define LSR_THE 0x20
119 #define LSR_TEMT 0x40
120 #define LSR_FIE 0x80
121
122 // Modem Control Register
123 #define MCR_DTR  0x01
124 #define MCR_RTS  0x02
125 #define MCR_INT  0x08   // Enable interrupts
126 #define MCR_LOOP 0x10   // Loopback mode
127
128 // Interrupt status Register
129 #define ISR_MS        0x00
130 #define ISR_nIP       0x01
131 #define ISR_Tx        0x02
132 #define ISR_Rx        0x04
133 #define ISR_LS        0x06
134 #define ISR_RxTO      0x0C
135 #define ISR_64BFIFO   0x20
136 #define ISR_FIFOworks 0x40
137 #define ISR_FIFOen    0x80
138
139 // Modem Status Register
140 #define MSR_DCTS 0x01
141 #define MSR_DDSR 0x02
142 #define MSR_TERI 0x04
143 #define MSR_DDCD 0x08
144 #define MSR_CTS  0x10
145 #define MSR_DSR  0x20
146 #define MSR_RI   0x40
147 #define MSR_CD   0x80
148
149 // FIFO Control Register
150 #define FCR_FE   0x01    // FIFO enable
151 #define FCR_CRF  0x02    // Clear receive FIFO
152 #define FCR_CTF  0x04    // Clear transmit FIFO
153 #define FCR_DMA  0x08    // DMA mode select
154 #define FCR_F64  0x20    // Enable 64 byte fifo (16750+)
155 #define FCR_RT14 0xC0    // Set Rx trigger at 14
156 #define FCR_RT8  0x80    // Set Rx trigger at 8
157 #define FCR_RT4  0x40    // Set Rx trigger at 4
158 #define FCR_RT1  0x00    // Set Rx trigger at 1
159
160 static unsigned char select_word_length[] = {
161     LCR_WL5,    // 5 bits / word (char)
162     LCR_WL6,
163     LCR_WL7,
164     LCR_WL8
165 };
166
167 static unsigned char select_stop_bits[] = {
168     0,
169     LCR_SB1,    // 1 stop bit
170     LCR_SB1_5,  // 1.5 stop bit
171     LCR_SB2     // 2 stop bits
172 };
173
174 static unsigned char select_parity[] = {
175     LCR_PN,     // No parity
176     LCR_PE,     // Even parity
177     LCR_PO,     // Odd parity
178     LCR_PM,     // Mark parity
179     LCR_PS,     // Space parity
180 };
181
182 // selec_baud[] must be define by the client
183
184 typedef struct pc_serial_info {
185     cyg_addrword_t base;
186     int            int_num;
187     cyg_interrupt  serial_interrupt;
188     cyg_handle_t   serial_interrupt_handle;
189 #ifdef CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO
190     enum {
191         sNone = 0,
192         s8250,
193         s16450,
194         s16550,
195         s16550a
196     } deviceType;
197 #endif
198 } pc_serial_info;
199
200 static bool pc_serial_init(struct cyg_devtab_entry *tab);
201 static bool pc_serial_putc(serial_channel *chan, unsigned char c);
202 static Cyg_ErrNo pc_serial_lookup(struct cyg_devtab_entry **tab, 
203                                   struct cyg_devtab_entry *sub_tab,
204                                   const char *name);
205 static unsigned char pc_serial_getc(serial_channel *chan);
206 static Cyg_ErrNo pc_serial_set_config(serial_channel *chan, cyg_uint32 key,
207                                       const void *xbuf, cyg_uint32 *len);
208 static void pc_serial_start_xmit(serial_channel *chan);
209 static void pc_serial_stop_xmit(serial_channel *chan);
210
211 static cyg_uint32 pc_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
212 static void       pc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count,
213                                 cyg_addrword_t data);
214
215 static SERIAL_FUNS(pc_serial_funs, 
216                    pc_serial_putc, 
217                    pc_serial_getc,
218                    pc_serial_set_config,
219                    pc_serial_start_xmit,
220                    pc_serial_stop_xmit
221     );
222
223 #include CYGDAT_IO_SERIAL_GENERIC_16X5X_INL
224
225 #ifndef CYG_IO_SERIAL_GENERIC_16X5X_INT_PRIORITY
226 # define CYG_IO_SERIAL_GENERIC_16X5X_INT_PRIORITY 4
227 #endif
228
229
230 // Internal function to actually configure the hardware to desired
231 // baud rate, etc.
232 static bool
233 serial_config_port(serial_channel *chan, 
234                    cyg_serial_info_t *new_config, bool init)
235 {
236     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
237     cyg_addrword_t base = ser_chan->base;
238     unsigned short baud_divisor = select_baud[new_config->baud];
239     unsigned char _lcr, _ier;
240     if (baud_divisor == 0) return false;  // Invalid configuration
241
242     // Disable port interrupts while changing hardware
243     HAL_READ_UINT8(base+REG_ier, _ier);
244     HAL_WRITE_UINT8(base+REG_ier, 0);
245
246     _lcr = select_word_length[new_config->word_length - CYGNUM_SERIAL_WORD_LENGTH_5] | 
247         select_stop_bits[new_config->stop] |
248         select_parity[new_config->parity];
249     HAL_WRITE_UINT8(base+REG_lcr, _lcr | LCR_DL);
250     HAL_WRITE_UINT8(base+REG_mdl, baud_divisor >> 8);
251     HAL_WRITE_UINT8(base+REG_ldl, baud_divisor & 0xFF);
252     HAL_WRITE_UINT8(base+REG_lcr, _lcr);
253     if (init) {
254 #ifdef CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO
255         unsigned char _fcr_thresh;
256         cyg_uint8 b;
257
258         /* First, find out what kind of device it is. */
259         ser_chan->deviceType = sNone;
260         HAL_WRITE_UINT8(base+REG_mcr, MCR_LOOP); // enable loopback mode
261         HAL_READ_UINT8(base+REG_msr, b);         
262         if (0 == (b & 0xF0)) {   // see if MSR had CD, RI, DSR or CTS set
263             HAL_WRITE_UINT8(base+REG_mcr, MCR_LOOP|MCR_DTR|MCR_RTS);
264             HAL_READ_UINT8(base+REG_msr, b);
265             if (0xF0 != (b & 0xF0))  // check that all of CD,RI,DSR and CTS set
266                 ser_chan->deviceType = s8250;
267         }
268         HAL_WRITE_UINT8(base+REG_mcr, 0); // disable loopback mode
269
270         if (ser_chan->deviceType == s8250) {
271             // Check for a scratch register; scratch register 
272             // indicates 16450 or above.
273             HAL_WRITE_UINT8(base+REG_scr, 0x55);
274             HAL_READ_UINT8(base+REG_scr, b);
275             if (b == 0x55) {
276                 HAL_WRITE_UINT8(base+REG_scr, 0xAA);
277                 HAL_READ_UINT8(base+REG_scr, b);
278                 if (b == 0xAA)
279                     ser_chan->deviceType = s16450;
280             }
281         }
282
283         if (ser_chan->deviceType == s16450) {
284             // Check for a FIFO
285             HAL_WRITE_UINT8(base+REG_fcr, FCR_FE);
286             HAL_READ_UINT8(base+REG_isr, b);
287             if (b & ISR_FIFOen)
288                 ser_chan->deviceType = s16550; // but FIFO doesn't 
289                                                // necessarily work
290             if (b & ISR_FIFOworks)
291                 ser_chan->deviceType = s16550a; // 16550a FIFOs work
292         }
293
294         if (ser_chan->deviceType == s16550a) {
295             switch(CYGPKG_IO_SERIAL_GENERIC_16X5X_FIFO_RX_THRESHOLD) {
296             default:
297             case 1:
298                 _fcr_thresh=FCR_RT1; break;
299             case 4:
300                 _fcr_thresh=FCR_RT4; break;
301             case 8:
302                 _fcr_thresh=FCR_RT8; break;
303             case 14:
304                 _fcr_thresh=FCR_RT14; break;
305             }
306             _fcr_thresh|=FCR_FE|FCR_CRF|FCR_CTF;
307             HAL_WRITE_UINT8(base+REG_fcr, _fcr_thresh); // Enable and clear FIFO
308         }
309         else
310             HAL_WRITE_UINT8(base+REG_fcr, 0); // make sure it's disabled
311 #endif
312         if (chan->out_cbuf.len != 0) {
313             _ier = IER_RCV;
314         } else {
315             _ier = 0;
316         }
317         // Master interrupt enable
318         HAL_WRITE_UINT8(base+REG_mcr, MCR_INT|MCR_DTR|MCR_RTS);
319     }
320 #ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
321     _ier |= (IER_LS|IER_MS);
322 #endif
323     HAL_WRITE_UINT8(base+REG_ier, _ier);
324
325     if (new_config != &chan->config) {
326         chan->config = *new_config;
327     }
328     return true;
329 }
330
331 // Function to initialize the device.  Called at bootstrap time.
332 static bool 
333 pc_serial_init(struct cyg_devtab_entry *tab)
334 {
335     serial_channel *chan = (serial_channel *)tab->priv;
336     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
337
338 #ifdef CYG_IO_SERIAL_GENERIC_16X5X_BAUD_GENERATOR
339     // Fill in baud rate table - used for platforms where this cannot
340     // be determined statically
341     int baud_idx, baud_val;
342     if (select_baud[0] == 9999) {
343         // Table not yet initialized
344         // Assumes that 'select_baud' looks like this:
345         //   static int select_baud[] = {
346         //       9999,  -- marker
347         //       50,    -- first baud rate
348         //       110,   -- second baud rate
349         // etc.
350         for (baud_idx = 1;  baud_idx < sizeof(select_baud)/sizeof(select_baud[0]);  baud_idx++) {
351             baud_val = CYG_IO_SERIAL_GENERIC_16X5X_BAUD_GENERATOR(select_baud[baud_idx]);
352             select_baud[baud_idx] = baud_val;
353         }
354         select_baud[0] = 0;
355     }
356 #endif
357
358 #ifdef CYGDBG_IO_INIT
359     diag_printf("16x5x SERIAL init - dev: %x.%d\n", 
360                 ser_chan->base, ser_chan->int_num);
361 #endif
362     // Really only required for interrupt driven devices
363     (chan->callbacks->serial_init)(chan);
364
365     if (chan->out_cbuf.len != 0) {
366         cyg_drv_interrupt_create(ser_chan->int_num,
367                                  CYG_IO_SERIAL_GENERIC_16X5X_INT_PRIORITY,
368                                  (cyg_addrword_t)chan,
369                                  pc_serial_ISR,
370                                  pc_serial_DSR,
371                                  &ser_chan->serial_interrupt_handle,
372                                  &ser_chan->serial_interrupt);
373         cyg_drv_interrupt_attach(ser_chan->serial_interrupt_handle);
374         cyg_drv_interrupt_unmask(ser_chan->int_num);
375     }
376     serial_config_port(chan, &chan->config, true);
377     return true;
378 }
379
380 // This routine is called when the device is "looked" up (i.e. attached)
381 static Cyg_ErrNo 
382 pc_serial_lookup(struct cyg_devtab_entry **tab, 
383                  struct cyg_devtab_entry *sub_tab,
384                  const char *name)
385 {
386     serial_channel *chan = (serial_channel *)(*tab)->priv;
387
388     // Really only required for interrupt driven devices
389     (chan->callbacks->serial_init)(chan);
390     return ENOERR;
391 }
392
393 // Send a character to the device output buffer.
394 // Return 'true' if character is sent to device
395 static bool
396 pc_serial_putc(serial_channel *chan, unsigned char c)
397 {
398     cyg_uint8 _lsr;
399     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
400     cyg_addrword_t base = ser_chan->base;
401
402     HAL_READ_UINT8(base+REG_lsr, _lsr);
403     if (_lsr & LSR_THE) {
404         // Transmit buffer is empty
405         HAL_WRITE_UINT8(base+REG_thr, c);
406         return true;
407     }
408     // No space
409     return false;
410 }
411
412 // Fetch a character from the device input buffer, waiting if necessary
413 static unsigned char 
414 pc_serial_getc(serial_channel *chan)
415 {
416     unsigned char c;
417     cyg_uint8 _lsr;
418     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
419     cyg_addrword_t base = ser_chan->base;
420
421     // Wait for char
422     do {
423         HAL_READ_UINT8(base+REG_lsr, _lsr);
424     } while ((_lsr & LSR_RSR) == 0);
425
426     HAL_READ_UINT8(base+REG_rhr, c);
427     return c;
428 }
429
430 // Set up the device characteristics; baud rate, etc.
431 static Cyg_ErrNo
432 pc_serial_set_config(serial_channel *chan, cyg_uint32 key, const void *xbuf,
433                      cyg_uint32 *len)
434 {
435     switch (key) {
436     case CYG_IO_SET_CONFIG_SERIAL_INFO:
437       {
438         cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
439         if ( *len < sizeof(cyg_serial_info_t) ) {
440             return -EINVAL;
441         }
442         *len = sizeof(cyg_serial_info_t);
443         if ( true != serial_config_port(chan, config, false) )
444             return -EINVAL;
445       }
446       break;
447 #ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
448     case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE:
449       {
450           cyg_uint8 _mcr;
451           pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
452           cyg_addrword_t base = ser_chan->base;
453           cyg_uint32 *f = (cyg_uint32 *)xbuf;
454           unsigned char mask=0;
455           if ( *len < sizeof(*f) )
456               return -EINVAL;
457           
458           if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX )
459               mask = MCR_RTS;
460           if ( chan->config.flags & CYGNUM_SERIAL_FLOW_DSRDTR_RX )
461               mask |= MCR_DTR;
462           HAL_READ_UINT8(base+REG_mcr, _mcr);
463           if (*f) // we should throttle
464               _mcr &= ~mask;
465           else // we should no longer throttle
466               _mcr |= mask;
467           HAL_WRITE_UINT8(base+REG_mcr, _mcr);
468       }
469       break;
470     case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG:
471         // Nothing to do because we do support both RTSCTS and DSRDTR flow
472         // control.
473         // Other targets would clear any unsupported flags here and
474         // would then return -ENOSUPP - the higher layer can then query
475         // what flags are set and decide what to do. This is optimised for
476         // the most common case - i.e. that authors know what their hardware
477         // is capable of.
478         // We just return ENOERR.
479       break;
480 #endif
481     default:
482         return -EINVAL;
483     }
484     return ENOERR;
485 }
486
487 // Enable the transmitter on the device
488 static void
489 pc_serial_start_xmit(serial_channel *chan)
490 {
491     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
492     cyg_addrword_t base = ser_chan->base;
493     cyg_uint8 _ier;
494     
495     HAL_READ_UINT8(base+REG_ier, _ier);
496     _ier |= IER_XMT;                    // Enable xmit interrupt
497     HAL_WRITE_UINT8(base+REG_ier, _ier);
498 }
499
500 // Disable the transmitter on the device
501 static void 
502 pc_serial_stop_xmit(serial_channel *chan)
503 {
504     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
505     cyg_addrword_t base = ser_chan->base;
506     cyg_uint8 _ier;
507
508     HAL_READ_UINT8(base+REG_ier, _ier);
509     _ier &= ~IER_XMT;                   // Disable xmit interrupt
510     HAL_WRITE_UINT8(base+REG_ier, _ier);
511 }
512
513 // Serial I/O - low level interrupt handler (ISR)
514 static cyg_uint32 
515 pc_serial_ISR(cyg_vector_t vector, cyg_addrword_t data)
516 {
517     serial_channel *chan = (serial_channel *)data;
518     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
519     cyg_drv_interrupt_mask(ser_chan->int_num);
520     cyg_drv_interrupt_acknowledge(ser_chan->int_num);
521     return CYG_ISR_CALL_DSR;  // Cause DSR to be run
522 }
523
524 // Serial I/O - high level interrupt handler (DSR)
525 static void       
526 pc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
527 {
528     serial_channel *chan = (serial_channel *)data;
529     pc_serial_info *ser_chan = (pc_serial_info *)chan->dev_priv;
530     cyg_addrword_t base = ser_chan->base;
531     cyg_uint8 _isr;
532
533     // Check if we have an interrupt pending - note that the interrupt
534     // is pending of the low bit of the isr is *0*, not 1.
535     HAL_READ_UINT8(base+REG_isr, _isr);
536     while ((_isr & ISR_nIP) == 0) {
537         switch (_isr&0xE) {
538         case ISR_Rx:
539         case ISR_RxTO:
540         {
541             cyg_uint8 _lsr;
542             unsigned char c;
543             HAL_READ_UINT8(base+REG_lsr, _lsr);
544             while(_lsr & LSR_RSR) {
545                 HAL_READ_UINT8(base+REG_rhr, c);
546                 (chan->callbacks->rcv_char)(chan, c);
547                 HAL_READ_UINT8(base+REG_lsr, _lsr);
548             }
549             break;
550         }
551         case ISR_Tx:
552             (chan->callbacks->xmt_char)(chan);
553             break;
554
555 #ifdef CYGOPT_IO_SERIAL_SUPPORT_LINE_STATUS
556         case ISR_LS:
557             {
558                 cyg_serial_line_status_t stat;
559                 cyg_uint8 _lsr;
560                 HAL_READ_UINT8(base+REG_lsr, _lsr);
561
562                 // this might look expensive, but it is rarely the case that
563                 // more than one of these is set
564                 stat.value = 1;
565                 if ( _lsr & LSR_OE ) {
566                     stat.which = CYGNUM_SERIAL_STATUS_OVERRUNERR;
567                     (chan->callbacks->indicate_status)(chan, &stat );
568                 }
569                 if ( _lsr & LSR_PE ) {
570                     stat.which = CYGNUM_SERIAL_STATUS_PARITYERR;
571                     (chan->callbacks->indicate_status)(chan, &stat );
572                 }
573                 if ( _lsr & LSR_FE ) {
574                     stat.which = CYGNUM_SERIAL_STATUS_FRAMEERR;
575                     (chan->callbacks->indicate_status)(chan, &stat );
576                 }
577                 if ( _lsr & LSR_BI ) {
578                     stat.which = CYGNUM_SERIAL_STATUS_BREAK;
579                     (chan->callbacks->indicate_status)(chan, &stat );
580                 }
581             }
582             break;
583
584         case ISR_MS:
585             {
586                 cyg_serial_line_status_t stat;
587                 cyg_uint8 _msr;
588                 
589                 HAL_READ_UINT8(base+REG_msr, _msr);
590 #ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
591                 if ( _msr & MSR_DDSR )
592                     if ( chan->config.flags & CYGNUM_SERIAL_FLOW_DSRDTR_TX ) {
593                         stat.which = CYGNUM_SERIAL_STATUS_FLOW;
594                         stat.value = (0 != (_msr & MSR_DSR));
595                         (chan->callbacks->indicate_status)(chan, &stat );
596                     }
597                 if ( _msr & MSR_DCTS )
598                     if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_TX ) {
599                         stat.which = CYGNUM_SERIAL_STATUS_FLOW;
600                         stat.value = (0 != (_msr & MSR_CTS));
601                         (chan->callbacks->indicate_status)(chan, &stat );
602                     }
603 #endif
604                 if ( _msr & MSR_DDCD ) {
605                     stat.which = CYGNUM_SERIAL_STATUS_CARRIERDETECT;
606                     stat.value = (0 != (_msr & MSR_CD));
607                     (chan->callbacks->indicate_status)(chan, &stat );
608                 }
609                 if ( _msr & MSR_RI ) {
610                     stat.which = CYGNUM_SERIAL_STATUS_RINGINDICATOR;
611                     stat.value = 1;
612                     (chan->callbacks->indicate_status)(chan, &stat );
613                 }
614                 if ( _msr & MSR_TERI ) {
615                     stat.which = CYGNUM_SERIAL_STATUS_RINGINDICATOR;
616                     stat.value = 0;
617                     (chan->callbacks->indicate_status)(chan, &stat );
618                 }
619             }
620             break;
621 #endif
622         default:
623             // Yes, this assertion may well not be visible. *But*
624             // if debugging, we may still successfully hit a breakpoint
625             // on cyg_assert_fail, which _is_ useful
626             CYG_FAIL("unhandled serial interrupt state");
627         }
628
629         HAL_READ_UINT8(base+REG_isr, _isr);
630     } // while
631
632     cyg_drv_interrupt_unmask(ser_chan->int_num);
633 }
634 #endif
635
636 // EOF ser_16x5x.c