1*74a4d8c2SCharles.Forsyth #include "u.h"
2*74a4d8c2SCharles.Forsyth #include "../port/lib.h"
3*74a4d8c2SCharles.Forsyth #include "mem.h"
4*74a4d8c2SCharles.Forsyth #include "dat.h"
5*74a4d8c2SCharles.Forsyth #include "fns.h"
6*74a4d8c2SCharles.Forsyth #include "io.h"
7*74a4d8c2SCharles.Forsyth #include "../port/error.h"
8*74a4d8c2SCharles.Forsyth #include "../port/netif.h"
9*74a4d8c2SCharles.Forsyth
10*74a4d8c2SCharles.Forsyth /*
11*74a4d8c2SCharles.Forsyth * currently no DMA or flow control (hardware or software)
12*74a4d8c2SCharles.Forsyth */
13*74a4d8c2SCharles.Forsyth
14*74a4d8c2SCharles.Forsyth /*
15*74a4d8c2SCharles.Forsyth * problems fixed from previous vsn:
16*74a4d8c2SCharles.Forsyth *
17*74a4d8c2SCharles.Forsyth * - no kick on queue's, so redirections weren't getting
18*74a4d8c2SCharles.Forsyth * started until the clock tick
19*74a4d8c2SCharles.Forsyth *
20*74a4d8c2SCharles.Forsyth * - lots of unnecessary overhead
21*74a4d8c2SCharles.Forsyth *
22*74a4d8c2SCharles.Forsyth * - initialization sequencing
23*74a4d8c2SCharles.Forsyth *
24*74a4d8c2SCharles.Forsyth * - uart[n] no longer indexed before calling uartinstall()
25*74a4d8c2SCharles.Forsyth */
26*74a4d8c2SCharles.Forsyth #define DEBUG if(0)iprint
27*74a4d8c2SCharles.Forsyth
28*74a4d8c2SCharles.Forsyth static void uartintr(Ureg*, void*);
29*74a4d8c2SCharles.Forsyth
30*74a4d8c2SCharles.Forsyth enum
31*74a4d8c2SCharles.Forsyth {
32*74a4d8c2SCharles.Forsyth Stagesize= 1024,
33*74a4d8c2SCharles.Forsyth Dmabufsize=Stagesize/2,
34*74a4d8c2SCharles.Forsyth Nuart=7, /* max per machine */
35*74a4d8c2SCharles.Forsyth };
36*74a4d8c2SCharles.Forsyth
37*74a4d8c2SCharles.Forsyth typedef struct Uart Uart;
38*74a4d8c2SCharles.Forsyth struct Uart
39*74a4d8c2SCharles.Forsyth {
40*74a4d8c2SCharles.Forsyth QLock;
41*74a4d8c2SCharles.Forsyth
42*74a4d8c2SCharles.Forsyth int opens;
43*74a4d8c2SCharles.Forsyth
44*74a4d8c2SCharles.Forsyth int enabled;
45*74a4d8c2SCharles.Forsyth
46*74a4d8c2SCharles.Forsyth int port; /* 0 or 1 */
47*74a4d8c2SCharles.Forsyth int kickme; /* for kick */
48*74a4d8c2SCharles.Forsyth int frame; /* framing errors */
49*74a4d8c2SCharles.Forsyth int overrun; /* rcvr overruns */
50*74a4d8c2SCharles.Forsyth int perror; /* parity error */
51*74a4d8c2SCharles.Forsyth int bps; /* baud rate */
52*74a4d8c2SCharles.Forsyth uchar bits;
53*74a4d8c2SCharles.Forsyth char parity;
54*74a4d8c2SCharles.Forsyth uchar stop;
55*74a4d8c2SCharles.Forsyth
56*74a4d8c2SCharles.Forsyth int inters; /* total interrupt count */
57*74a4d8c2SCharles.Forsyth int rinters; /* interrupts due to read */
58*74a4d8c2SCharles.Forsyth int winters; /* interrupts due to write */
59*74a4d8c2SCharles.Forsyth
60*74a4d8c2SCharles.Forsyth int rcount; /* total read count */
61*74a4d8c2SCharles.Forsyth int wcount; /* total output count */
62*74a4d8c2SCharles.Forsyth
63*74a4d8c2SCharles.Forsyth /* buffers */
64*74a4d8c2SCharles.Forsyth int (*putc)(Queue*, int);
65*74a4d8c2SCharles.Forsyth Queue *iq;
66*74a4d8c2SCharles.Forsyth Queue *oq;
67*74a4d8c2SCharles.Forsyth
68*74a4d8c2SCharles.Forsyth UartReg *reg;
69*74a4d8c2SCharles.Forsyth
70*74a4d8c2SCharles.Forsyth /* staging areas to avoid some of the per character costs */
71*74a4d8c2SCharles.Forsyth uchar *ip;
72*74a4d8c2SCharles.Forsyth uchar *ie;
73*74a4d8c2SCharles.Forsyth uchar *op;
74*74a4d8c2SCharles.Forsyth uchar *oe;
75*74a4d8c2SCharles.Forsyth
76*74a4d8c2SCharles.Forsyth /* put large buffers last to aid register-offset optimizations: */
77*74a4d8c2SCharles.Forsyth char name[KNAMELEN];
78*74a4d8c2SCharles.Forsyth uchar istage[Stagesize];
79*74a4d8c2SCharles.Forsyth uchar ostage[Stagesize];
80*74a4d8c2SCharles.Forsyth };
81*74a4d8c2SCharles.Forsyth
82*74a4d8c2SCharles.Forsyth #define UCON_ENABLEMASK (UCON_RXMDMASK | UCON_TXMDMASK | UCON_SINTMASK)
83*74a4d8c2SCharles.Forsyth #define UCON_ENABLESET (UCON_RXMDINT | UCON_TXMDINT | UCON_SINTON)
84*74a4d8c2SCharles.Forsyth #define UCON_DISABLESET (UCON_RXMDOFF | UCON_TXMDOFF | UCON_SINTOFF)
85*74a4d8c2SCharles.Forsyth
86*74a4d8c2SCharles.Forsyth static Uart *uart[Nuart];
87*74a4d8c2SCharles.Forsyth static int nuart;
88*74a4d8c2SCharles.Forsyth
89*74a4d8c2SCharles.Forsyth static uchar
readstatus(Uart * p)90*74a4d8c2SCharles.Forsyth readstatus(Uart *p)
91*74a4d8c2SCharles.Forsyth {
92*74a4d8c2SCharles.Forsyth UartReg *reg = p->reg;
93*74a4d8c2SCharles.Forsyth uchar stat = reg->stat;
94*74a4d8c2SCharles.Forsyth if (stat & USTAT_OV)
95*74a4d8c2SCharles.Forsyth p->overrun++;
96*74a4d8c2SCharles.Forsyth if (stat & USTAT_PE)
97*74a4d8c2SCharles.Forsyth p->perror++;
98*74a4d8c2SCharles.Forsyth if (stat & USTAT_FE)
99*74a4d8c2SCharles.Forsyth p->frame++;
100*74a4d8c2SCharles.Forsyth return stat;
101*74a4d8c2SCharles.Forsyth }
102*74a4d8c2SCharles.Forsyth
103*74a4d8c2SCharles.Forsyth static void
uartset(Uart * p)104*74a4d8c2SCharles.Forsyth uartset(Uart *p)
105*74a4d8c2SCharles.Forsyth {
106*74a4d8c2SCharles.Forsyth UartReg *reg = p->reg;
107*74a4d8c2SCharles.Forsyth ulong denom;
108*74a4d8c2SCharles.Forsyth ulong brdiv;
109*74a4d8c2SCharles.Forsyth int n;
110*74a4d8c2SCharles.Forsyth uchar lcon;
111*74a4d8c2SCharles.Forsyth
112*74a4d8c2SCharles.Forsyth lcon= ULCON_CLOCKMCLK | ULCON_IROFF;
113*74a4d8c2SCharles.Forsyth lcon |= ULCON_WL5 + (p->bits - 5);
114*74a4d8c2SCharles.Forsyth lcon |= p->stop == 1 ? ULCON_STOP1 : ULCON_STOP2;
115*74a4d8c2SCharles.Forsyth switch (p->parity) {
116*74a4d8c2SCharles.Forsyth default:
117*74a4d8c2SCharles.Forsyth case 'n':
118*74a4d8c2SCharles.Forsyth lcon |= ULCON_PMDNONE;
119*74a4d8c2SCharles.Forsyth break;
120*74a4d8c2SCharles.Forsyth case 'o':
121*74a4d8c2SCharles.Forsyth lcon |= ULCON_PMDODD;
122*74a4d8c2SCharles.Forsyth break;
123*74a4d8c2SCharles.Forsyth case 'e':
124*74a4d8c2SCharles.Forsyth lcon |= ULCON_PMDEVEN;
125*74a4d8c2SCharles.Forsyth break;
126*74a4d8c2SCharles.Forsyth }
127*74a4d8c2SCharles.Forsyth reg->lcon = lcon;
128*74a4d8c2SCharles.Forsyth
129*74a4d8c2SCharles.Forsyth /* clear the break and loopback bits; leave everything else alone */
130*74a4d8c2SCharles.Forsyth reg->con = (reg->con & ~(UCON_BRKMASK | UCON_LOOPMASK)) | UCON_BRKOFF | UCON_LOOPOFF;
131*74a4d8c2SCharles.Forsyth
132*74a4d8c2SCharles.Forsyth denom = 2 * 16 * p->bps;
133*74a4d8c2SCharles.Forsyth brdiv = (TIMER_HZ + denom / 2) / denom - 1;
134*74a4d8c2SCharles.Forsyth reg->brdiv = brdiv << 4;
135*74a4d8c2SCharles.Forsyth
136*74a4d8c2SCharles.Forsyth /* set buffer length according to speed, to allow
137*74a4d8c2SCharles.Forsyth * at most a 200ms delay before dumping the staging buffer
138*74a4d8c2SCharles.Forsyth * into the input queue
139*74a4d8c2SCharles.Forsyth */
140*74a4d8c2SCharles.Forsyth n = p->bps/(10*1000/200);
141*74a4d8c2SCharles.Forsyth p->ie = &p->istage[n < Stagesize ? n : Stagesize];
142*74a4d8c2SCharles.Forsyth }
143*74a4d8c2SCharles.Forsyth
144*74a4d8c2SCharles.Forsyth /*
145*74a4d8c2SCharles.Forsyth * send break
146*74a4d8c2SCharles.Forsyth */
147*74a4d8c2SCharles.Forsyth static void
uartbreak(Uart * p,int ms)148*74a4d8c2SCharles.Forsyth uartbreak(Uart *p, int ms)
149*74a4d8c2SCharles.Forsyth {
150*74a4d8c2SCharles.Forsyth UartReg *reg = p->reg;
151*74a4d8c2SCharles.Forsyth if(ms == 0)
152*74a4d8c2SCharles.Forsyth ms = 200;
153*74a4d8c2SCharles.Forsyth reg->con |= UCON_BRKON;
154*74a4d8c2SCharles.Forsyth tsleep(&up->sleep, return0, 0, ms);
155*74a4d8c2SCharles.Forsyth reg->con &= ~UCON_BRKON;
156*74a4d8c2SCharles.Forsyth }
157*74a4d8c2SCharles.Forsyth
158*74a4d8c2SCharles.Forsyth /*
159*74a4d8c2SCharles.Forsyth * turn on a port
160*74a4d8c2SCharles.Forsyth */
161*74a4d8c2SCharles.Forsyth static void
uartenable(Uart * p)162*74a4d8c2SCharles.Forsyth uartenable(Uart *p)
163*74a4d8c2SCharles.Forsyth {
164*74a4d8c2SCharles.Forsyth UartReg *reg = p->reg;
165*74a4d8c2SCharles.Forsyth
166*74a4d8c2SCharles.Forsyth if(p->enabled)
167*74a4d8c2SCharles.Forsyth return;
168*74a4d8c2SCharles.Forsyth
169*74a4d8c2SCharles.Forsyth uartset(p);
170*74a4d8c2SCharles.Forsyth // enable receive, transmit, and receive interrupt:
171*74a4d8c2SCharles.Forsyth reg->con = (reg->con & UCON_ENABLEMASK) | UCON_ENABLESET;
172*74a4d8c2SCharles.Forsyth p->enabled = 1;
173*74a4d8c2SCharles.Forsyth }
174*74a4d8c2SCharles.Forsyth
175*74a4d8c2SCharles.Forsyth /*
176*74a4d8c2SCharles.Forsyth * turn off a port
177*74a4d8c2SCharles.Forsyth */
178*74a4d8c2SCharles.Forsyth static void
uartdisable(Uart * p)179*74a4d8c2SCharles.Forsyth uartdisable(Uart *p)
180*74a4d8c2SCharles.Forsyth {
181*74a4d8c2SCharles.Forsyth p->reg->con = (p->reg->con & UCON_ENABLEMASK) | UCON_DISABLESET;
182*74a4d8c2SCharles.Forsyth p->enabled = 0;
183*74a4d8c2SCharles.Forsyth }
184*74a4d8c2SCharles.Forsyth
185*74a4d8c2SCharles.Forsyth /*
186*74a4d8c2SCharles.Forsyth * put some bytes into the local queue to avoid calling
187*74a4d8c2SCharles.Forsyth * qconsume for every character
188*74a4d8c2SCharles.Forsyth */
189*74a4d8c2SCharles.Forsyth static int
stageoutput(Uart * p)190*74a4d8c2SCharles.Forsyth stageoutput(Uart *p)
191*74a4d8c2SCharles.Forsyth {
192*74a4d8c2SCharles.Forsyth int n;
193*74a4d8c2SCharles.Forsyth Queue *q = p->oq;
194*74a4d8c2SCharles.Forsyth
195*74a4d8c2SCharles.Forsyth if(!q)
196*74a4d8c2SCharles.Forsyth return 0;
197*74a4d8c2SCharles.Forsyth n = qconsume(q, p->ostage, Stagesize);
198*74a4d8c2SCharles.Forsyth if(n <= 0)
199*74a4d8c2SCharles.Forsyth return 0;
200*74a4d8c2SCharles.Forsyth p->op = p->ostage;
201*74a4d8c2SCharles.Forsyth p->oe = p->ostage + n;
202*74a4d8c2SCharles.Forsyth return n;
203*74a4d8c2SCharles.Forsyth }
204*74a4d8c2SCharles.Forsyth
205*74a4d8c2SCharles.Forsyth static void
uartxmit(Uart * p)206*74a4d8c2SCharles.Forsyth uartxmit(Uart *p)
207*74a4d8c2SCharles.Forsyth {
208*74a4d8c2SCharles.Forsyth UartReg *reg = p->reg;
209*74a4d8c2SCharles.Forsyth ulong gag = 1;
210*74a4d8c2SCharles.Forsyth while(p->op < p->oe || stageoutput(p)) {
211*74a4d8c2SCharles.Forsyth if(readstatus(p) & USTAT_TBE) {
212*74a4d8c2SCharles.Forsyth DEBUG("T");
213*74a4d8c2SCharles.Forsyth reg->txbuf = *(p->op++);
214*74a4d8c2SCharles.Forsyth p->wcount++;
215*74a4d8c2SCharles.Forsyth } else {
216*74a4d8c2SCharles.Forsyth DEBUG("F");
217*74a4d8c2SCharles.Forsyth gag = 0;
218*74a4d8c2SCharles.Forsyth break;
219*74a4d8c2SCharles.Forsyth }
220*74a4d8c2SCharles.Forsyth }
221*74a4d8c2SCharles.Forsyth if (gag) {
222*74a4d8c2SCharles.Forsyth DEBUG("G");
223*74a4d8c2SCharles.Forsyth p->kickme = 1;
224*74a4d8c2SCharles.Forsyth intrmask(UARTTXbit(p->port), 0);
225*74a4d8c2SCharles.Forsyth }
226*74a4d8c2SCharles.Forsyth }
227*74a4d8c2SCharles.Forsyth
228*74a4d8c2SCharles.Forsyth static void
uartrecvq(Uart * p)229*74a4d8c2SCharles.Forsyth uartrecvq(Uart *p)
230*74a4d8c2SCharles.Forsyth {
231*74a4d8c2SCharles.Forsyth uchar *cp = p->istage;
232*74a4d8c2SCharles.Forsyth int n = p->ip - cp;
233*74a4d8c2SCharles.Forsyth
234*74a4d8c2SCharles.Forsyth if(n == 0)
235*74a4d8c2SCharles.Forsyth return;
236*74a4d8c2SCharles.Forsyth if(p->putc)
237*74a4d8c2SCharles.Forsyth while(n-- > 0)
238*74a4d8c2SCharles.Forsyth p->putc(p->iq, *cp++);
239*74a4d8c2SCharles.Forsyth else if(p->iq)
240*74a4d8c2SCharles.Forsyth if(qproduce(p->iq, p->istage, n) < n)
241*74a4d8c2SCharles.Forsyth print("qproduce flow control");
242*74a4d8c2SCharles.Forsyth p->ip = p->istage;
243*74a4d8c2SCharles.Forsyth }
244*74a4d8c2SCharles.Forsyth
245*74a4d8c2SCharles.Forsyth static void
uartrecv(Uart * p)246*74a4d8c2SCharles.Forsyth uartrecv(Uart *p)
247*74a4d8c2SCharles.Forsyth {
248*74a4d8c2SCharles.Forsyth UartReg *reg = p->reg;
249*74a4d8c2SCharles.Forsyth uchar stat = readstatus(p);
250*74a4d8c2SCharles.Forsyth
251*74a4d8c2SCharles.Forsyth DEBUG("R");
252*74a4d8c2SCharles.Forsyth if (stat & USTAT_RDR) {
253*74a4d8c2SCharles.Forsyth int c;
254*74a4d8c2SCharles.Forsyth c = reg->rxbuf;
255*74a4d8c2SCharles.Forsyth if (c == '?') {
256*74a4d8c2SCharles.Forsyth DEBUG("mod 0x%.8lx\n", INTREG->mod);
257*74a4d8c2SCharles.Forsyth DEBUG("msk 0x%.8lx\n", INTREG->msk);
258*74a4d8c2SCharles.Forsyth DEBUG("pnd 0x%.8lx\n", INTREG->pnd);
259*74a4d8c2SCharles.Forsyth }
260*74a4d8c2SCharles.Forsyth *p->ip++ = c;
261*74a4d8c2SCharles.Forsyth /* if(p->ip >= p->ie) */
262*74a4d8c2SCharles.Forsyth uartrecvq(p);
263*74a4d8c2SCharles.Forsyth p->rcount++;
264*74a4d8c2SCharles.Forsyth }
265*74a4d8c2SCharles.Forsyth }
266*74a4d8c2SCharles.Forsyth
267*74a4d8c2SCharles.Forsyth static void
uartkick(void * a)268*74a4d8c2SCharles.Forsyth uartkick(void *a)
269*74a4d8c2SCharles.Forsyth {
270*74a4d8c2SCharles.Forsyth Uart *p = a;
271*74a4d8c2SCharles.Forsyth int x = splhi();
272*74a4d8c2SCharles.Forsyth DEBUG("k");
273*74a4d8c2SCharles.Forsyth if (p->kickme) {
274*74a4d8c2SCharles.Forsyth p->kickme = 0;
275*74a4d8c2SCharles.Forsyth DEBUG("K");
276*74a4d8c2SCharles.Forsyth intrunmask(UARTTXbit(p->port), 0);
277*74a4d8c2SCharles.Forsyth }
278*74a4d8c2SCharles.Forsyth splx(x);
279*74a4d8c2SCharles.Forsyth }
280*74a4d8c2SCharles.Forsyth
281*74a4d8c2SCharles.Forsyth /*
282*74a4d8c2SCharles.Forsyth * UART Interrupt Handler
283*74a4d8c2SCharles.Forsyth */
284*74a4d8c2SCharles.Forsyth static void
uarttxintr(Ureg *,void * arg)285*74a4d8c2SCharles.Forsyth uarttxintr(Ureg*, void* arg)
286*74a4d8c2SCharles.Forsyth {
287*74a4d8c2SCharles.Forsyth Uart *p = arg;
288*74a4d8c2SCharles.Forsyth intrclear(UARTTXbit(p->port), 0);
289*74a4d8c2SCharles.Forsyth p->inters++;
290*74a4d8c2SCharles.Forsyth p->winters++;
291*74a4d8c2SCharles.Forsyth uartxmit(p);
292*74a4d8c2SCharles.Forsyth }
293*74a4d8c2SCharles.Forsyth
294*74a4d8c2SCharles.Forsyth static void
uartrxintr(Ureg *,void * arg)295*74a4d8c2SCharles.Forsyth uartrxintr(Ureg*, void* arg)
296*74a4d8c2SCharles.Forsyth {
297*74a4d8c2SCharles.Forsyth Uart *p = arg;
298*74a4d8c2SCharles.Forsyth intrclear(UARTRXbit(p->port), 0);
299*74a4d8c2SCharles.Forsyth p->inters++;
300*74a4d8c2SCharles.Forsyth p->rinters++;
301*74a4d8c2SCharles.Forsyth uartrecv(p);
302*74a4d8c2SCharles.Forsyth }
303*74a4d8c2SCharles.Forsyth
304*74a4d8c2SCharles.Forsyth
305*74a4d8c2SCharles.Forsyth static void
uartsetup(ulong port,char * name)306*74a4d8c2SCharles.Forsyth uartsetup(ulong port, char *name)
307*74a4d8c2SCharles.Forsyth {
308*74a4d8c2SCharles.Forsyth Uart *p;
309*74a4d8c2SCharles.Forsyth
310*74a4d8c2SCharles.Forsyth if(nuart >= Nuart)
311*74a4d8c2SCharles.Forsyth return;
312*74a4d8c2SCharles.Forsyth
313*74a4d8c2SCharles.Forsyth p = xalloc(sizeof(Uart));
314*74a4d8c2SCharles.Forsyth uart[nuart++] = p;
315*74a4d8c2SCharles.Forsyth strcpy(p->name, name);
316*74a4d8c2SCharles.Forsyth
317*74a4d8c2SCharles.Forsyth p->reg = &UARTREG[port];
318*74a4d8c2SCharles.Forsyth p->bps = 9600;
319*74a4d8c2SCharles.Forsyth p->bits = 8;
320*74a4d8c2SCharles.Forsyth p->parity = 'n';
321*74a4d8c2SCharles.Forsyth p->stop = 1;
322*74a4d8c2SCharles.Forsyth p->kickme = 0;
323*74a4d8c2SCharles.Forsyth p->port = port;
324*74a4d8c2SCharles.Forsyth
325*74a4d8c2SCharles.Forsyth p->iq = qopen(4*1024, 0, 0 , p);
326*74a4d8c2SCharles.Forsyth p->oq = qopen(4*1024, 0, uartkick, p);
327*74a4d8c2SCharles.Forsyth
328*74a4d8c2SCharles.Forsyth p->ip = p->istage;
329*74a4d8c2SCharles.Forsyth p->ie = &p->istage[Stagesize];
330*74a4d8c2SCharles.Forsyth p->op = p->ostage;
331*74a4d8c2SCharles.Forsyth p->oe = p->ostage;
332*74a4d8c2SCharles.Forsyth
333*74a4d8c2SCharles.Forsyth intrenable(UARTTXbit(port), uarttxintr, p, 0);
334*74a4d8c2SCharles.Forsyth intrenable(UARTRXbit(port), uartrxintr, p, 0);
335*74a4d8c2SCharles.Forsyth }
336*74a4d8c2SCharles.Forsyth
337*74a4d8c2SCharles.Forsyth static void
uartinstall(void)338*74a4d8c2SCharles.Forsyth uartinstall(void)
339*74a4d8c2SCharles.Forsyth {
340*74a4d8c2SCharles.Forsyth static int already;
341*74a4d8c2SCharles.Forsyth
342*74a4d8c2SCharles.Forsyth if(already)
343*74a4d8c2SCharles.Forsyth return;
344*74a4d8c2SCharles.Forsyth already = 1;
345*74a4d8c2SCharles.Forsyth
346*74a4d8c2SCharles.Forsyth uartsetup(0, "eia0");
347*74a4d8c2SCharles.Forsyth // uartsetup(1, "eia1");
348*74a4d8c2SCharles.Forsyth }
349*74a4d8c2SCharles.Forsyth
350*74a4d8c2SCharles.Forsyth /*
351*74a4d8c2SCharles.Forsyth * called by main() to configure a duart port as a console or a mouse
352*74a4d8c2SCharles.Forsyth */
353*74a4d8c2SCharles.Forsyth void
uartspecial(int port,int bps,char parity,Queue ** in,Queue ** out,int (* putc)(Queue *,int))354*74a4d8c2SCharles.Forsyth uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int))
355*74a4d8c2SCharles.Forsyth {
356*74a4d8c2SCharles.Forsyth Uart *p;
357*74a4d8c2SCharles.Forsyth
358*74a4d8c2SCharles.Forsyth uartinstall();
359*74a4d8c2SCharles.Forsyth if(port >= nuart)
360*74a4d8c2SCharles.Forsyth return;
361*74a4d8c2SCharles.Forsyth p = uart[port];
362*74a4d8c2SCharles.Forsyth if(bps)
363*74a4d8c2SCharles.Forsyth p->bps = bps;
364*74a4d8c2SCharles.Forsyth if(parity)
365*74a4d8c2SCharles.Forsyth p->parity = parity;
366*74a4d8c2SCharles.Forsyth uartenable(p);
367*74a4d8c2SCharles.Forsyth p->putc = putc;
368*74a4d8c2SCharles.Forsyth if(in)
369*74a4d8c2SCharles.Forsyth *in = p->iq;
370*74a4d8c2SCharles.Forsyth if(out)
371*74a4d8c2SCharles.Forsyth *out = p->oq;
372*74a4d8c2SCharles.Forsyth p->opens++;
373*74a4d8c2SCharles.Forsyth }
374*74a4d8c2SCharles.Forsyth
375*74a4d8c2SCharles.Forsyth Dirtab *uartdir;
376*74a4d8c2SCharles.Forsyth int ndir;
377*74a4d8c2SCharles.Forsyth
378*74a4d8c2SCharles.Forsyth static void
setlength(int i)379*74a4d8c2SCharles.Forsyth setlength(int i)
380*74a4d8c2SCharles.Forsyth {
381*74a4d8c2SCharles.Forsyth Uart *p;
382*74a4d8c2SCharles.Forsyth
383*74a4d8c2SCharles.Forsyth if(i > 0){
384*74a4d8c2SCharles.Forsyth p = uart[i];
385*74a4d8c2SCharles.Forsyth if(p && p->opens && p->iq)
386*74a4d8c2SCharles.Forsyth uartdir[1+3*i].length = qlen(p->iq);
387*74a4d8c2SCharles.Forsyth } else for(i = 0; i < nuart; i++){
388*74a4d8c2SCharles.Forsyth p = uart[i];
389*74a4d8c2SCharles.Forsyth if(p && p->opens && p->iq)
390*74a4d8c2SCharles.Forsyth uartdir[1+3*i].length = qlen(p->iq);
391*74a4d8c2SCharles.Forsyth }
392*74a4d8c2SCharles.Forsyth }
393*74a4d8c2SCharles.Forsyth
394*74a4d8c2SCharles.Forsyth /*
395*74a4d8c2SCharles.Forsyth * all uarts must be uartsetup() by this point or inside of uartinstall()
396*74a4d8c2SCharles.Forsyth */
397*74a4d8c2SCharles.Forsyth static void
uartreset(void)398*74a4d8c2SCharles.Forsyth uartreset(void)
399*74a4d8c2SCharles.Forsyth {
400*74a4d8c2SCharles.Forsyth int i;
401*74a4d8c2SCharles.Forsyth Dirtab *dp;
402*74a4d8c2SCharles.Forsyth
403*74a4d8c2SCharles.Forsyth uartinstall();
404*74a4d8c2SCharles.Forsyth
405*74a4d8c2SCharles.Forsyth ndir = 1+3*nuart;
406*74a4d8c2SCharles.Forsyth uartdir = xalloc(ndir * sizeof(Dirtab));
407*74a4d8c2SCharles.Forsyth dp = uartdir;
408*74a4d8c2SCharles.Forsyth strcpy(dp->name, ".");
409*74a4d8c2SCharles.Forsyth mkqid(&dp->qid, 0, 0, QTDIR);
410*74a4d8c2SCharles.Forsyth dp->length = 0;
411*74a4d8c2SCharles.Forsyth dp->perm = DMDIR|0555;
412*74a4d8c2SCharles.Forsyth dp++;
413*74a4d8c2SCharles.Forsyth for(i = 0; i < nuart; i++){
414*74a4d8c2SCharles.Forsyth /* 3 directory entries per port */
415*74a4d8c2SCharles.Forsyth strcpy(dp->name, uart[i]->name);
416*74a4d8c2SCharles.Forsyth dp->qid.path = NETQID(i, Ndataqid);
417*74a4d8c2SCharles.Forsyth dp->perm = 0660;
418*74a4d8c2SCharles.Forsyth dp++;
419*74a4d8c2SCharles.Forsyth sprint(dp->name, "%sctl", uart[i]->name);
420*74a4d8c2SCharles.Forsyth dp->qid.path = NETQID(i, Nctlqid);
421*74a4d8c2SCharles.Forsyth dp->perm = 0660;
422*74a4d8c2SCharles.Forsyth dp++;
423*74a4d8c2SCharles.Forsyth sprint(dp->name, "%sstatus", uart[i]->name);
424*74a4d8c2SCharles.Forsyth dp->qid.path = NETQID(i, Nstatqid);
425*74a4d8c2SCharles.Forsyth dp->perm = 0444;
426*74a4d8c2SCharles.Forsyth dp++;
427*74a4d8c2SCharles.Forsyth }
428*74a4d8c2SCharles.Forsyth }
429*74a4d8c2SCharles.Forsyth
430*74a4d8c2SCharles.Forsyth static Chan*
uartattach(char * spec)431*74a4d8c2SCharles.Forsyth uartattach(char *spec)
432*74a4d8c2SCharles.Forsyth {
433*74a4d8c2SCharles.Forsyth return devattach('t', spec);
434*74a4d8c2SCharles.Forsyth }
435*74a4d8c2SCharles.Forsyth
436*74a4d8c2SCharles.Forsyth static Walkqid*
uartwalk(Chan * c,Chan * nc,char ** name,int nname)437*74a4d8c2SCharles.Forsyth uartwalk(Chan *c, Chan *nc, char **name, int nname)
438*74a4d8c2SCharles.Forsyth {
439*74a4d8c2SCharles.Forsyth return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
440*74a4d8c2SCharles.Forsyth }
441*74a4d8c2SCharles.Forsyth
442*74a4d8c2SCharles.Forsyth static int
uartstat(Chan * c,uchar * dp,int n)443*74a4d8c2SCharles.Forsyth uartstat(Chan *c, uchar *dp, int n)
444*74a4d8c2SCharles.Forsyth {
445*74a4d8c2SCharles.Forsyth if(NETTYPE(c->qid.path) == Ndataqid)
446*74a4d8c2SCharles.Forsyth setlength(NETID(c->qid.path));
447*74a4d8c2SCharles.Forsyth return devstat(c, dp, n, uartdir, ndir, devgen);
448*74a4d8c2SCharles.Forsyth }
449*74a4d8c2SCharles.Forsyth
450*74a4d8c2SCharles.Forsyth static Chan*
uartopen(Chan * c,int omode)451*74a4d8c2SCharles.Forsyth uartopen(Chan *c, int omode)
452*74a4d8c2SCharles.Forsyth {
453*74a4d8c2SCharles.Forsyth Uart *p;
454*74a4d8c2SCharles.Forsyth
455*74a4d8c2SCharles.Forsyth c = devopen(c, omode, uartdir, ndir, devgen);
456*74a4d8c2SCharles.Forsyth
457*74a4d8c2SCharles.Forsyth switch(NETTYPE(c->qid.path)){
458*74a4d8c2SCharles.Forsyth case Nctlqid:
459*74a4d8c2SCharles.Forsyth case Ndataqid:
460*74a4d8c2SCharles.Forsyth p = uart[NETID(c->qid.path)];
461*74a4d8c2SCharles.Forsyth qlock(p);
462*74a4d8c2SCharles.Forsyth if(p->opens++ == 0){
463*74a4d8c2SCharles.Forsyth uartenable(p);
464*74a4d8c2SCharles.Forsyth qreopen(p->iq);
465*74a4d8c2SCharles.Forsyth qreopen(p->oq);
466*74a4d8c2SCharles.Forsyth }
467*74a4d8c2SCharles.Forsyth qunlock(p);
468*74a4d8c2SCharles.Forsyth break;
469*74a4d8c2SCharles.Forsyth }
470*74a4d8c2SCharles.Forsyth
471*74a4d8c2SCharles.Forsyth return c;
472*74a4d8c2SCharles.Forsyth }
473*74a4d8c2SCharles.Forsyth
474*74a4d8c2SCharles.Forsyth static void
uartclose(Chan * c)475*74a4d8c2SCharles.Forsyth uartclose(Chan *c)
476*74a4d8c2SCharles.Forsyth {
477*74a4d8c2SCharles.Forsyth Uart *p;
478*74a4d8c2SCharles.Forsyth
479*74a4d8c2SCharles.Forsyth if(c->qid.type & QTDIR)
480*74a4d8c2SCharles.Forsyth return;
481*74a4d8c2SCharles.Forsyth if((c->flag & COPEN) == 0)
482*74a4d8c2SCharles.Forsyth return;
483*74a4d8c2SCharles.Forsyth switch(NETTYPE(c->qid.path)){
484*74a4d8c2SCharles.Forsyth case Ndataqid:
485*74a4d8c2SCharles.Forsyth case Nctlqid:
486*74a4d8c2SCharles.Forsyth p = uart[NETID(c->qid.path)];
487*74a4d8c2SCharles.Forsyth qlock(p);
488*74a4d8c2SCharles.Forsyth if(--(p->opens) == 0){
489*74a4d8c2SCharles.Forsyth uartdisable(p);
490*74a4d8c2SCharles.Forsyth qclose(p->iq);
491*74a4d8c2SCharles.Forsyth qclose(p->oq);
492*74a4d8c2SCharles.Forsyth p->ip = p->istage;
493*74a4d8c2SCharles.Forsyth }
494*74a4d8c2SCharles.Forsyth qunlock(p);
495*74a4d8c2SCharles.Forsyth break;
496*74a4d8c2SCharles.Forsyth }
497*74a4d8c2SCharles.Forsyth }
498*74a4d8c2SCharles.Forsyth
499*74a4d8c2SCharles.Forsyth static long
uartstatus(Chan * c,Uart * p,void * buf,long n,long offset)500*74a4d8c2SCharles.Forsyth uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
501*74a4d8c2SCharles.Forsyth {
502*74a4d8c2SCharles.Forsyth char str[256];
503*74a4d8c2SCharles.Forsyth USED(c);
504*74a4d8c2SCharles.Forsyth
505*74a4d8c2SCharles.Forsyth str[0] = 0;
506*74a4d8c2SCharles.Forsyth sprint(str, "opens %d ferr %d oerr %d perr %d baud %d parity %c"
507*74a4d8c2SCharles.Forsyth " intr %d rintr %d wintr %d"
508*74a4d8c2SCharles.Forsyth " rcount %d wcount %d",
509*74a4d8c2SCharles.Forsyth p->opens, p->frame, p->overrun, p->perror, p->bps, p->parity,
510*74a4d8c2SCharles.Forsyth p->inters, p->rinters, p->winters,
511*74a4d8c2SCharles.Forsyth p->rcount, p->wcount);
512*74a4d8c2SCharles.Forsyth
513*74a4d8c2SCharles.Forsyth strcat(str, "\n");
514*74a4d8c2SCharles.Forsyth return readstr(offset, buf, n, str);
515*74a4d8c2SCharles.Forsyth }
516*74a4d8c2SCharles.Forsyth
517*74a4d8c2SCharles.Forsyth static long
uartread(Chan * c,void * buf,long n,vlong offset)518*74a4d8c2SCharles.Forsyth uartread(Chan *c, void *buf, long n, vlong offset)
519*74a4d8c2SCharles.Forsyth {
520*74a4d8c2SCharles.Forsyth Uart *p;
521*74a4d8c2SCharles.Forsyth
522*74a4d8c2SCharles.Forsyth if(c->qid.type & QTDIR){
523*74a4d8c2SCharles.Forsyth setlength(-1);
524*74a4d8c2SCharles.Forsyth return devdirread(c, buf, n, uartdir, ndir, devgen);
525*74a4d8c2SCharles.Forsyth }
526*74a4d8c2SCharles.Forsyth
527*74a4d8c2SCharles.Forsyth p = uart[NETID(c->qid.path)];
528*74a4d8c2SCharles.Forsyth switch(NETTYPE(c->qid.path)){
529*74a4d8c2SCharles.Forsyth case Ndataqid:
530*74a4d8c2SCharles.Forsyth return qread(p->iq, buf, n);
531*74a4d8c2SCharles.Forsyth case Nctlqid:
532*74a4d8c2SCharles.Forsyth return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
533*74a4d8c2SCharles.Forsyth case Nstatqid:
534*74a4d8c2SCharles.Forsyth return uartstatus(c, p, buf, n, offset);
535*74a4d8c2SCharles.Forsyth }
536*74a4d8c2SCharles.Forsyth
537*74a4d8c2SCharles.Forsyth return 0;
538*74a4d8c2SCharles.Forsyth }
539*74a4d8c2SCharles.Forsyth
540*74a4d8c2SCharles.Forsyth static void
uartctl(Uart * p,char * cmd)541*74a4d8c2SCharles.Forsyth uartctl(Uart *p, char *cmd)
542*74a4d8c2SCharles.Forsyth {
543*74a4d8c2SCharles.Forsyth int i, n;
544*74a4d8c2SCharles.Forsyth
545*74a4d8c2SCharles.Forsyth /* let output drain for a while (up to 4 secs) */
546*74a4d8c2SCharles.Forsyth for(i = 0; i < 200 && (qlen(p->oq) || (readstatus(p) & USTAT_TC) == 0); i++)
547*74a4d8c2SCharles.Forsyth tsleep(&up->sleep, return0, 0, 20);
548*74a4d8c2SCharles.Forsyth
549*74a4d8c2SCharles.Forsyth if(strncmp(cmd, "break", 5) == 0){
550*74a4d8c2SCharles.Forsyth uartbreak(p, 0);
551*74a4d8c2SCharles.Forsyth return;
552*74a4d8c2SCharles.Forsyth }
553*74a4d8c2SCharles.Forsyth
554*74a4d8c2SCharles.Forsyth n = atoi(cmd+1);
555*74a4d8c2SCharles.Forsyth switch(*cmd){
556*74a4d8c2SCharles.Forsyth case 'B':
557*74a4d8c2SCharles.Forsyth case 'b':
558*74a4d8c2SCharles.Forsyth if(n <= 0)
559*74a4d8c2SCharles.Forsyth error(Ebadarg);
560*74a4d8c2SCharles.Forsyth p->bps = n;
561*74a4d8c2SCharles.Forsyth uartset(p);
562*74a4d8c2SCharles.Forsyth break;
563*74a4d8c2SCharles.Forsyth case 'f':
564*74a4d8c2SCharles.Forsyth case 'F':
565*74a4d8c2SCharles.Forsyth qflush(p->oq);
566*74a4d8c2SCharles.Forsyth break;
567*74a4d8c2SCharles.Forsyth case 'H':
568*74a4d8c2SCharles.Forsyth case 'h':
569*74a4d8c2SCharles.Forsyth qhangup(p->iq, 0);
570*74a4d8c2SCharles.Forsyth qhangup(p->oq, 0);
571*74a4d8c2SCharles.Forsyth break;
572*74a4d8c2SCharles.Forsyth case 'L':
573*74a4d8c2SCharles.Forsyth case 'l':
574*74a4d8c2SCharles.Forsyth if(n < 7 || n > 8)
575*74a4d8c2SCharles.Forsyth error(Ebadarg);
576*74a4d8c2SCharles.Forsyth p->bits = n;
577*74a4d8c2SCharles.Forsyth uartset(p);
578*74a4d8c2SCharles.Forsyth break;
579*74a4d8c2SCharles.Forsyth case 'n':
580*74a4d8c2SCharles.Forsyth case 'N':
581*74a4d8c2SCharles.Forsyth qnoblock(p->oq, n);
582*74a4d8c2SCharles.Forsyth break;
583*74a4d8c2SCharles.Forsyth case 'P':
584*74a4d8c2SCharles.Forsyth case 'p':
585*74a4d8c2SCharles.Forsyth p->parity = *(cmd+1);
586*74a4d8c2SCharles.Forsyth uartset(p);
587*74a4d8c2SCharles.Forsyth break;
588*74a4d8c2SCharles.Forsyth case 'K':
589*74a4d8c2SCharles.Forsyth case 'k':
590*74a4d8c2SCharles.Forsyth uartbreak(p, n);
591*74a4d8c2SCharles.Forsyth break;
592*74a4d8c2SCharles.Forsyth case 'Q':
593*74a4d8c2SCharles.Forsyth case 'q':
594*74a4d8c2SCharles.Forsyth qsetlimit(p->iq, n);
595*74a4d8c2SCharles.Forsyth qsetlimit(p->oq, n);
596*74a4d8c2SCharles.Forsyth break;
597*74a4d8c2SCharles.Forsyth case 's':
598*74a4d8c2SCharles.Forsyth case 'S':
599*74a4d8c2SCharles.Forsyth if(n < 1 || n > 2)
600*74a4d8c2SCharles.Forsyth error(Ebadarg);
601*74a4d8c2SCharles.Forsyth p->stop = n;
602*74a4d8c2SCharles.Forsyth uartset(p);
603*74a4d8c2SCharles.Forsyth break;
604*74a4d8c2SCharles.Forsyth }
605*74a4d8c2SCharles.Forsyth }
606*74a4d8c2SCharles.Forsyth
607*74a4d8c2SCharles.Forsyth static long
uartwrite(Chan * c,void * buf,long n,vlong offset)608*74a4d8c2SCharles.Forsyth uartwrite(Chan *c, void *buf, long n, vlong offset)
609*74a4d8c2SCharles.Forsyth {
610*74a4d8c2SCharles.Forsyth Uart *p;
611*74a4d8c2SCharles.Forsyth char cmd[32];
612*74a4d8c2SCharles.Forsyth
613*74a4d8c2SCharles.Forsyth USED(offset);
614*74a4d8c2SCharles.Forsyth
615*74a4d8c2SCharles.Forsyth if(c->qid.type & QTDIR)
616*74a4d8c2SCharles.Forsyth error(Eperm);
617*74a4d8c2SCharles.Forsyth
618*74a4d8c2SCharles.Forsyth p = uart[NETID(c->qid.path)];
619*74a4d8c2SCharles.Forsyth
620*74a4d8c2SCharles.Forsyth switch(NETTYPE(c->qid.path)){
621*74a4d8c2SCharles.Forsyth case Ndataqid:
622*74a4d8c2SCharles.Forsyth return qwrite(p->oq, buf, n);
623*74a4d8c2SCharles.Forsyth case Nctlqid:
624*74a4d8c2SCharles.Forsyth
625*74a4d8c2SCharles.Forsyth if(n >= sizeof(cmd))
626*74a4d8c2SCharles.Forsyth n = sizeof(cmd)-1;
627*74a4d8c2SCharles.Forsyth memmove(cmd, buf, n);
628*74a4d8c2SCharles.Forsyth cmd[n] = 0;
629*74a4d8c2SCharles.Forsyth uartctl(p, cmd);
630*74a4d8c2SCharles.Forsyth return n;
631*74a4d8c2SCharles.Forsyth }
632*74a4d8c2SCharles.Forsyth }
633*74a4d8c2SCharles.Forsyth
634*74a4d8c2SCharles.Forsyth static int
uartwstat(Chan * c,uchar * dp,int n)635*74a4d8c2SCharles.Forsyth uartwstat(Chan *c, uchar *dp, int n)
636*74a4d8c2SCharles.Forsyth {
637*74a4d8c2SCharles.Forsyth error(Eperm);
638*74a4d8c2SCharles.Forsyth return 0;
639*74a4d8c2SCharles.Forsyth #ifdef xxx
640*74a4d8c2SCharles.Forsyth Dir d;
641*74a4d8c2SCharles.Forsyth Dirtab *dt;
642*74a4d8c2SCharles.Forsyth
643*74a4d8c2SCharles.Forsyth if(!iseve())
644*74a4d8c2SCharles.Forsyth error(Eperm);
645*74a4d8c2SCharles.Forsyth if(c->qid.type & QTDIR)
646*74a4d8c2SCharles.Forsyth error(Eperm);
647*74a4d8c2SCharles.Forsyth if(NETTYPE(c->qid.path) == Nstatqid)
648*74a4d8c2SCharles.Forsyth error(Eperm);
649*74a4d8c2SCharles.Forsyth
650*74a4d8c2SCharles.Forsyth dt = &uartdir[3 * NETID(c->qid.path)];
651*74a4d8c2SCharles.Forsyth convM2D(dp, &d);
652*74a4d8c2SCharles.Forsyth d.mode &= 0666;
653*74a4d8c2SCharles.Forsyth dt[0].perm = dt[1].perm = d.mode;
654*74a4d8c2SCharles.Forsyth #endif
655*74a4d8c2SCharles.Forsyth }
656*74a4d8c2SCharles.Forsyth
657*74a4d8c2SCharles.Forsyth Dev uartdevtab = {
658*74a4d8c2SCharles.Forsyth 't',
659*74a4d8c2SCharles.Forsyth "uart",
660*74a4d8c2SCharles.Forsyth
661*74a4d8c2SCharles.Forsyth uartreset,
662*74a4d8c2SCharles.Forsyth devinit,
663*74a4d8c2SCharles.Forsyth devshutdown,
664*74a4d8c2SCharles.Forsyth uartattach,
665*74a4d8c2SCharles.Forsyth uartwalk,
666*74a4d8c2SCharles.Forsyth uartstat,
667*74a4d8c2SCharles.Forsyth uartopen,
668*74a4d8c2SCharles.Forsyth devcreate,
669*74a4d8c2SCharles.Forsyth uartclose,
670*74a4d8c2SCharles.Forsyth uartread,
671*74a4d8c2SCharles.Forsyth devbread,
672*74a4d8c2SCharles.Forsyth uartwrite,
673*74a4d8c2SCharles.Forsyth devbwrite,
674*74a4d8c2SCharles.Forsyth devremove,
675*74a4d8c2SCharles.Forsyth uartwstat,
676*74a4d8c2SCharles.Forsyth };
677*74a4d8c2SCharles.Forsyth
678*74a4d8c2SCharles.Forsyth void
uartputc(int c)679*74a4d8c2SCharles.Forsyth uartputc(int c)
680*74a4d8c2SCharles.Forsyth {
681*74a4d8c2SCharles.Forsyth UartReg *u;
682*74a4d8c2SCharles.Forsyth
683*74a4d8c2SCharles.Forsyth if (!c)
684*74a4d8c2SCharles.Forsyth return;
685*74a4d8c2SCharles.Forsyth u = &UARTREG[1];
686*74a4d8c2SCharles.Forsyth while ((u->stat & USTAT_TBE) == 0)
687*74a4d8c2SCharles.Forsyth ;
688*74a4d8c2SCharles.Forsyth u->txbuf = c;
689*74a4d8c2SCharles.Forsyth if (c == '\n')
690*74a4d8c2SCharles.Forsyth while((u->stat & USTAT_TC) == 0) /* flush xmit fifo */
691*74a4d8c2SCharles.Forsyth ;
692*74a4d8c2SCharles.Forsyth }
693*74a4d8c2SCharles.Forsyth
694*74a4d8c2SCharles.Forsyth void
uartputs(char * data,int len)695*74a4d8c2SCharles.Forsyth uartputs(char *data, int len)
696*74a4d8c2SCharles.Forsyth {
697*74a4d8c2SCharles.Forsyth int x;
698*74a4d8c2SCharles.Forsyth
699*74a4d8c2SCharles.Forsyth clockpoll();
700*74a4d8c2SCharles.Forsyth x = splfhi();
701*74a4d8c2SCharles.Forsyth while (len--){
702*74a4d8c2SCharles.Forsyth if(*data == '\n')
703*74a4d8c2SCharles.Forsyth uartputc('\r');
704*74a4d8c2SCharles.Forsyth uartputc(*data++);
705*74a4d8c2SCharles.Forsyth }
706*74a4d8c2SCharles.Forsyth splx(x);
707*74a4d8c2SCharles.Forsyth }
708*74a4d8c2SCharles.Forsyth
709*74a4d8c2SCharles.Forsyth int
uartgetc(void)710*74a4d8c2SCharles.Forsyth uartgetc(void)
711*74a4d8c2SCharles.Forsyth {
712*74a4d8c2SCharles.Forsyth UartReg *u;
713*74a4d8c2SCharles.Forsyth
714*74a4d8c2SCharles.Forsyth clockcheck();
715*74a4d8c2SCharles.Forsyth u = &UARTREG[1];
716*74a4d8c2SCharles.Forsyth while((u->stat & USTAT_RDR) == 0)
717*74a4d8c2SCharles.Forsyth clockcheck();
718*74a4d8c2SCharles.Forsyth return u->rxbuf;
719*74a4d8c2SCharles.Forsyth }
720