source: rtems/c/src/libchip/serial/ns16550.c @ 3495c57

4.104.11
Last change on this file since 3495c57 was 3495c57, checked in by Ralf Corsepius <ralf.corsepius@…>, on Nov 30, 2009 at 3:49:08 AM

Whitespace removal.

  • Property mode set to 100644
File size: 18.0 KB
Line 
1/*
2 *  This file contains the TTY driver for the National Semiconductor NS16550.
3 *
4 *  This part is widely cloned and second sourced.  It is found in a number
5 *  of "Super IO" controllers.
6 *
7 *  COPYRIGHT (c) 1998 by Radstone Technology
8 *
9 *
10 * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
11 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
12 * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
13 * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
14 *
15 * You are hereby granted permission to use, copy, modify, and distribute
16 * this file, provided that this notice, plus the above copyright notice
17 * and disclaimer, appears in all copies. Radstone Technology will provide
18 * no support for this code.
19 *
20 *  This driver uses the termios pseudo driver.
21 */
22
23/*
24 * $Id$
25 */
26
27#include <stdlib.h>
28
29#include <rtems.h>
30#include <rtems/libio.h>
31#include <rtems/ringbuf.h>
32#include <rtems/bspIo.h>
33#include <rtems/termiostypes.h>
34
35#include <libchip/serial.h>
36#include <libchip/sersupp.h>
37
38#include <bsp.h>
39
40#include "ns16550_p.h"
41
42#ifdef BSP_FEATURE_IRQ_EXTENSION
43  #include <bsp/irq.h>
44#elif defined BSP_FEATURE_IRQ_LEGACY
45  #include <bsp/irq.h>
46#elif defined __PPC__
47  #include <bsp/irq.h>
48  #define BSP_FEATURE_IRQ_LEGACY
49  #ifdef BSP_SHARED_HANDLER_SUPPORT
50    #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
51  #endif
52#endif
53
54/*
55 * Flow control is only supported when using interrupts
56 */
57
58console_flow ns16550_flow_RTSCTS = {
59  ns16550_negate_RTS,             /* deviceStopRemoteTx */
60  ns16550_assert_RTS              /* deviceStartRemoteTx */
61};
62
63console_flow ns16550_flow_DTRCTS = {
64  ns16550_negate_DTR,             /* deviceStopRemoteTx */
65  ns16550_assert_DTR              /* deviceStartRemoteTx */
66};
67
68console_fns ns16550_fns = {
69  libchip_serial_default_probe,   /* deviceProbe */
70  ns16550_open,                   /* deviceFirstOpen */
71  NULL,                           /* deviceLastClose */
72  NULL,                           /* deviceRead */
73  ns16550_write_support_int,      /* deviceWrite */
74  ns16550_initialize_interrupts,  /* deviceInitialize */
75  ns16550_write_polled,           /* deviceWritePolled */
76  ns16550_set_attributes,         /* deviceSetAttributes */
77  true                            /* deviceOutputUsesInterrupts */
78};
79
80console_fns ns16550_fns_polled = {
81  libchip_serial_default_probe,        /* deviceProbe */
82  ns16550_open,                        /* deviceFirstOpen */
83  ns16550_close,                       /* deviceLastClose */
84  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
85  ns16550_write_support_polled,        /* deviceWrite */
86  ns16550_init,                        /* deviceInitialize */
87  ns16550_write_polled,                /* deviceWritePolled */
88  ns16550_set_attributes,              /* deviceSetAttributes */
89  false                                /* deviceOutputUsesInterrupts */
90};
91
92/*
93 *  ns16550_init
94 */
95
96NS16550_STATIC void ns16550_init(int minor)
97{
98  uint32_t                pNS16550;
99  uint8_t                 ucTrash;
100  uint8_t                 ucDataByte;
101  uint32_t                ulBaudDivisor;
102  ns16550_context        *pns16550Context;
103  setRegister_f           setReg;
104  getRegister_f           getReg;
105
106  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
107
108  if (pns16550Context == NULL) {
109    printk( "%s: Error: Not enough memory\n", __func__);
110    rtems_fatal_error_occurred( 0xdeadbeef);
111  }
112
113  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
114  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
115
116  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
117  setReg   = Console_Port_Tbl[minor].setRegister;
118  getReg   = Console_Port_Tbl[minor].getRegister;
119
120  /* Clear the divisor latch, clear all interrupt enables,
121   * and reset and
122   * disable the FIFO's.
123   */
124
125  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
126  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
127
128  /* Set the divisor latch and set the baud rate. */
129
130  ulBaudDivisor = NS16550_Baud(
131    (uint32_t) Console_Port_Tbl[minor].ulClock,
132    (uint32_t) Console_Port_Tbl[minor].pDeviceParams
133  );
134  ucDataByte = SP_LINE_DLAB;
135  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
136
137  /* XXX */
138  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, (uint8_t) (ulBaudDivisor & 0xffU));
139  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (uint8_t) ((ulBaudDivisor >> 8) & 0xffU));
140
141  /* Clear the divisor latch and set the character size to eight bits */
142  /* with one stop bit and no parity checking. */
143  ucDataByte = EIGHT_BITS;
144  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
145
146  /* Enable and reset transmit and receive FIFOs. TJA     */
147  ucDataByte = SP_FIFO_ENABLE;
148  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
149
150  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
151  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
152
153  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
154
155  /* Set data terminal ready. */
156  /* And open interrupt tristate line */
157  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
158
159  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
160  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
161}
162
163/*
164 *  ns16550_open
165 */
166
167NS16550_STATIC int ns16550_open(
168  int major,
169  int minor,
170  void *arg
171)
172{
173  rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg;
174  struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1;
175  console_tbl *c = &Console_Port_Tbl [minor];
176  console_data *d = &Console_Port_Data [minor];
177
178  d->termios_data = tty;
179
180  /* Assert DTR */
181  if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
182    ns16550_assert_DTR( minor);
183  }
184
185  /* Set initial baud */
186  rtems_termios_set_initial_baud( tty, (int32_t) c->pDeviceParams);
187
188  if (c->pDeviceFns->deviceOutputUsesInterrupts) {
189    ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
190  }
191
192  return RTEMS_SUCCESSFUL;
193}
194
195/*
196 *  ns16550_close
197 */
198
199NS16550_STATIC int ns16550_close(
200  int      major,
201  int      minor,
202  void    * arg
203)
204{
205  /*
206   * Negate DTR
207   */
208  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
209    ns16550_negate_DTR(minor);
210  }
211
212  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
213
214  return(RTEMS_SUCCESSFUL);
215}
216
217/**
218 * @brief Polled write for NS16550.
219 */
220NS16550_STATIC void ns16550_write_polled( int minor, char out)
221{
222  console_tbl *c = &Console_Port_Tbl [minor];
223  uint32_t port = c->ulCtrlPort1;
224  getRegister_f get = c->getRegister;
225  setRegister_f set = c->setRegister;
226  uint32_t status = 0;
227  rtems_interrupt_level level;
228
229  /* Save port interrupt mask */
230  uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
231
232  /* Disable port interrupts */
233  ns16550_enable_interrupts( minor, NS16550_DISABLE_ALL_INTR);
234
235  while (true) {
236    /* Try to transmit the character in a critical section */
237    rtems_interrupt_disable( level);
238
239    /* Read the transmitter holding register and check it */
240    status = get( port, NS16550_LINE_STATUS);
241    if ((status & SP_LSR_THOLD) != 0) {
242      /* Transmit character */
243      set( port, NS16550_TRANSMIT_BUFFER, out);
244
245      /* Finished */
246      rtems_interrupt_enable( level);
247      break;
248    } else {
249      rtems_interrupt_enable( level);
250    }
251
252    /* Wait for transmitter holding register to be empty */
253    do {
254      status = get( port, NS16550_LINE_STATUS);
255    } while ((status & SP_LSR_THOLD) == 0);
256  }
257
258  /* Restore port interrupt mask */
259  set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
260}
261
262/*
263 * These routines provide control of the RTS and DTR lines
264 */
265
266/*
267 *  ns16550_assert_RTS
268 */
269
270NS16550_STATIC int ns16550_assert_RTS(int minor)
271{
272  uint32_t                pNS16550;
273  uint32_t                Irql;
274  ns16550_context        *pns16550Context;
275  setRegister_f           setReg;
276
277  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
278
279  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
280  setReg   = Console_Port_Tbl[minor].setRegister;
281
282  /*
283   * Assert RTS
284   */
285  rtems_interrupt_disable(Irql);
286  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
287  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
288  rtems_interrupt_enable(Irql);
289  return 0;
290}
291
292/*
293 *  ns16550_negate_RTS
294 */
295
296NS16550_STATIC int ns16550_negate_RTS(int minor)
297{
298  uint32_t                pNS16550;
299  uint32_t                Irql;
300  ns16550_context        *pns16550Context;
301  setRegister_f           setReg;
302
303  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
304
305  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
306  setReg   = Console_Port_Tbl[minor].setRegister;
307
308  /*
309   * Negate RTS
310   */
311  rtems_interrupt_disable(Irql);
312  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
313  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
314  rtems_interrupt_enable(Irql);
315  return 0;
316}
317
318/*
319 * These flow control routines utilise a connection from the local DTR
320 * line to the remote CTS line
321 */
322
323/*
324 *  ns16550_assert_DTR
325 */
326
327NS16550_STATIC int ns16550_assert_DTR(int minor)
328{
329  uint32_t                pNS16550;
330  uint32_t                Irql;
331  ns16550_context        *pns16550Context;
332  setRegister_f           setReg;
333
334  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
335
336  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
337  setReg   = Console_Port_Tbl[minor].setRegister;
338
339  /*
340   * Assert DTR
341   */
342  rtems_interrupt_disable(Irql);
343  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
344  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
345  rtems_interrupt_enable(Irql);
346  return 0;
347}
348
349/*
350 *  ns16550_negate_DTR
351 */
352
353NS16550_STATIC int ns16550_negate_DTR(int minor)
354{
355  uint32_t                pNS16550;
356  uint32_t                Irql;
357  ns16550_context        *pns16550Context;
358  setRegister_f           setReg;
359
360  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
361
362  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
363  setReg   = Console_Port_Tbl[minor].setRegister;
364
365  /*
366   * Negate DTR
367   */
368  rtems_interrupt_disable(Irql);
369  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
370  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
371  rtems_interrupt_enable(Irql);
372  return 0;
373}
374
375/*
376 *  ns16550_set_attributes
377 *
378 *  This function sets the channel to reflect the requested termios
379 *  port settings.
380 */
381
382NS16550_STATIC int ns16550_set_attributes(
383  int                   minor,
384  const struct termios *t
385)
386{
387  uint32_t                pNS16550;
388  uint32_t                ulBaudDivisor;
389  uint8_t                 ucLineControl;
390  uint32_t                baud_requested;
391  setRegister_f           setReg;
392  getRegister_f           getReg;
393  uint32_t                Irql;
394
395  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
396  setReg   = Console_Port_Tbl[minor].setRegister;
397  getReg   = Console_Port_Tbl[minor].getRegister;
398
399  /*
400   *  Calculate the baud rate divisor
401   */
402
403  baud_requested = t->c_cflag & CBAUD;
404  if (!baud_requested)
405    baud_requested = B9600;              /* default to 9600 baud */
406
407  ulBaudDivisor = NS16550_Baud(
408    (uint32_t) Console_Port_Tbl[minor].ulClock,
409    rtems_termios_baud_to_number(baud_requested)
410  );
411
412  ucLineControl = 0;
413
414  /*
415   *  Parity
416   */
417
418  if (t->c_cflag & PARENB) {
419    ucLineControl |= SP_LINE_PAR;
420    if (!(t->c_cflag & PARODD))
421      ucLineControl |= SP_LINE_ODD;
422  }
423
424  /*
425   *  Character Size
426   */
427
428  if (t->c_cflag & CSIZE) {
429    switch (t->c_cflag & CSIZE) {
430      case CS5:  ucLineControl |= FIVE_BITS;  break;
431      case CS6:  ucLineControl |= SIX_BITS;   break;
432      case CS7:  ucLineControl |= SEVEN_BITS; break;
433      case CS8:  ucLineControl |= EIGHT_BITS; break;
434    }
435  } else {
436    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
437  }
438
439  /*
440   *  Stop Bits
441   */
442
443  if (t->c_cflag & CSTOPB) {
444    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
445  } else {
446    ;                                           /* 1 stop bit */
447  }
448
449  /*
450   *  Now actually set the chip
451   */
452
453  rtems_interrupt_disable(Irql);
454
455    /*
456     *  Set the baud rate
457     */
458
459    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
460    /* XXX are these registers right? */
461    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
462    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
463
464    /*
465     *  Now write the line control
466     */
467    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
468
469  rtems_interrupt_enable(Irql);
470
471  return 0;
472}
473
474#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
475
476/**
477 * @brief Process interrupt.
478 */
479NS16550_STATIC void ns16550_process( int minor)
480{
481  console_tbl *c = &Console_Port_Tbl [minor];
482  console_data *d = &Console_Port_Data [minor];
483  ns16550_context *ctx = d->pDeviceContext;
484  uint32_t port = c->ulCtrlPort1;
485  getRegister_f get = c->getRegister;
486  int i = 0;
487  char buf [SP_FIFO_SIZE];
488
489  /* Iterate until no more interrupts are pending */
490  do {
491    /* Fetch received characters */
492    for (i = 0; i < SP_FIFO_SIZE; ++i) {
493      if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
494        buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
495      } else {
496        break;
497      }
498    }
499
500    /* Enqueue fetched characters */
501    rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
502
503    /* Check if we can dequeue transmitted characters */
504    if (ctx->transmitFifoChars > 0
505        && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
506      unsigned chars = ctx->transmitFifoChars;
507
508      /*
509       * We finished the transmission, so clear the number of characters in the
510       * transmit FIFO.
511       */
512      ctx->transmitFifoChars = 0;
513
514      /* Dequeue transmitted characters */
515      if (rtems_termios_dequeue_characters( d->termios_data, chars) == 0) {
516        /* Nothing to do */
517        d->bActive = false;
518        ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
519      }
520    }
521  } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
522}
523#endif
524
525/**
526 * @brief Transmits up to @a len characters from @a buf.
527 *
528 * This routine is invoked either from task context with disabled interrupts to
529 * start a new transmission process with exactly one character in case of an
530 * idle output state or from the interrupt handler to refill the transmitter.
531 *
532 * Returns always zero.
533 */
534NS16550_STATIC int ns16550_write_support_int(
535  int minor,
536  const char *buf,
537  int len
538)
539{
540  console_tbl *c = &Console_Port_Tbl [minor];
541  console_data *d = &Console_Port_Data [minor];
542  ns16550_context *ctx = d->pDeviceContext;
543  uint32_t port = c->ulCtrlPort1;
544  setRegister_f set = c->setRegister;
545  int i = 0;
546  int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
547
548  for (i = 0; i < out; ++i) {
549    set( port, NS16550_TRANSMIT_BUFFER, buf [i]);
550  }
551
552  if (len > 0) {
553    ctx->transmitFifoChars = out;
554    d->bActive = true;
555    ns16550_enable_interrupts( minor, NS16550_ENABLE_ALL_INTR);
556  }
557
558  return 0;
559}
560
561/*
562 *  ns16550_enable_interrupts
563 *
564 *  This routine initializes the port to have the specified interrupts masked.
565 */
566NS16550_STATIC void ns16550_enable_interrupts(
567  int minor,
568  int mask
569)
570{
571  uint32_t       pNS16550;
572  setRegister_f  setReg;
573
574  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
575  setReg   = Console_Port_Tbl[minor].setRegister;
576
577  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
578}
579
580#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
581  NS16550_STATIC rtems_isr ns16550_isr(void *arg)
582  {
583    int minor = (int) arg;
584
585    ns16550_process( minor);
586  }
587#endif
588
589/*
590 *  ns16550_initialize_interrupts
591 *
592 *  This routine initializes the port to operate in interrupt driver mode.
593 */
594NS16550_STATIC void ns16550_initialize_interrupts( int minor)
595{
596#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
597  console_tbl *c = &Console_Port_Tbl [minor];
598#endif
599  console_data *d = &Console_Port_Data [minor];
600
601  ns16550_init( minor);
602
603  d->bActive = false;
604
605  #ifdef BSP_FEATURE_IRQ_EXTENSION
606    {
607      rtems_status_code sc = RTEMS_SUCCESSFUL;
608      sc = rtems_interrupt_handler_install(
609        c->ulIntVector,
610        "NS16550",
611        RTEMS_INTERRUPT_SHARED,
612        ns16550_isr,
613        (void *) minor
614      );
615      if (sc != RTEMS_SUCCESSFUL) {
616        /* FIXME */
617        printk( "%s: Error: Install interrupt handler\n", __func__);
618        rtems_fatal_error_occurred( 0xdeadbeef);
619      }
620    }
621  #elif defined(BSP_FEATURE_IRQ_LEGACY)
622    {
623      int rv = 0;
624      #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
625        rtems_irq_connect_data cd = {
626          c->ulIntVector,
627          ns16550_isr,
628          (void *) minor,
629          NULL,
630          NULL,
631          NULL,
632          NULL
633        };
634        rv = BSP_install_rtems_shared_irq_handler( &cd);
635      #else
636        rtems_irq_connect_data cd = {
637          c->ulIntVector,
638          ns16550_isr,
639          (void *) minor,
640          NULL,
641          NULL,
642          NULL
643        };
644        rv = BSP_install_rtems_irq_handler( &cd);
645      #endif
646      if (rv == 0) {
647        /* FIXME */
648        printk( "%s: Error: Install interrupt handler\n", __func__);
649        rtems_fatal_error_occurred( 0xdeadbeef);
650      }
651    }
652  #endif
653}
654
655/*
656 *  ns16550_write_support_polled
657 *
658 *  Console Termios output entry point.
659 *
660 */
661
662NS16550_STATIC int ns16550_write_support_polled(
663  int         minor,
664  const char *buf,
665  int         len
666)
667{
668  int nwrite = 0;
669
670  /*
671   * poll each byte in the string out of the port.
672   */
673  while (nwrite < len) {
674    /*
675     * transmit character
676     */
677    ns16550_write_polled(minor, *buf++);
678    nwrite++;
679  }
680
681  /*
682   * return the number of bytes written.
683   */
684  return nwrite;
685}
686
687/*
688 *  ns16550_inbyte_nonblocking_polled
689 *
690 *  Console Termios polling input entry point.
691 */
692
693NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
694  int minor
695)
696{
697  uint32_t             pNS16550;
698  unsigned char        ucLineStatus;
699  char                 cChar;
700  getRegister_f        getReg;
701
702  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
703  getReg   = Console_Port_Tbl[minor].getRegister;
704
705  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
706  if(ucLineStatus & SP_LSR_RDY) {
707    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
708    return (int)cChar;
709  } else {
710    return -1;
711  }
712}
Note: See TracBrowser for help on using the repository browser.