123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824 |
- /*
- * omap35 8250-like UART
- *
- * we ignore the first 2 uarts on the omap35 (see below) and use the
- * third one but call it 0.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- enum { /* registers */
- Rbr = 0, /* Receiver Buffer (RO) */
- Thr = 0, /* Transmitter Holding (WO) */
- Ier = 1, /* Interrupt Enable */
- Iir = 2, /* Interrupt Identification (RO) */
- Fcr = 2, /* FIFO Control (WO) */
- Lcr = 3, /* Line Control */
- Mcr = 4, /* Modem Control */
- Lsr = 5, /* Line Status */
- Msr = 6, /* Modem Status */
- Scr = 7, /* Scratch Pad */
- Mdr = 8, /* Mode Def'n (omap rw) */
- // Usr = 31, /* Uart Status Register; missing in omap? */
- Dll = 0, /* Divisor Latch LSB */
- Dlm = 1, /* Divisor Latch MSB */
- };
- enum { /* Usr */
- Busy = 0x01,
- };
- enum { /* Ier */
- Erda = 0x01, /* Enable Received Data Available */
- Ethre = 0x02, /* Enable Thr Empty */
- Erls = 0x04, /* Enable Receiver Line Status */
- Ems = 0x08, /* Enable Modem Status */
- };
- enum { /* Iir */
- Ims = 0x00, /* Ms interrupt */
- Ip = 0x01, /* Interrupt Pending (not) */
- Ithre = 0x02, /* Thr Empty */
- Irda = 0x04, /* Received Data Available */
- Irls = 0x06, /* Receiver Line Status */
- Ictoi = 0x0C, /* Character Time-out Indication */
- IirMASK = 0x3F,
- Ifena = 0xC0, /* FIFOs enabled */
- };
- enum { /* Fcr */
- FIFOena = 0x01, /* FIFO enable */
- FIFOrclr = 0x02, /* clear Rx FIFO */
- FIFOtclr = 0x04, /* clear Tx FIFO */
- // FIFOdma = 0x08,
- FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */
- FIFO4 = 0x40, /* 4 bytes */
- FIFO8 = 0x80, /* 8 bytes */
- FIFO14 = 0xC0, /* 14 bytes */
- };
- enum { /* Lcr */
- Wls5 = 0x00, /* Word Length Select 5 bits/byte */
- Wls6 = 0x01, /* 6 bits/byte */
- Wls7 = 0x02, /* 7 bits/byte */
- Wls8 = 0x03, /* 8 bits/byte */
- WlsMASK = 0x03,
- Stb = 0x04, /* 2 stop bits */
- Pen = 0x08, /* Parity Enable */
- Eps = 0x10, /* Even Parity Select */
- Stp = 0x20, /* Stick Parity */
- Brk = 0x40, /* Break */
- Dlab = 0x80, /* Divisor Latch Access Bit */
- };
- enum { /* Mcr */
- Dtr = 0x01, /* Data Terminal Ready */
- Rts = 0x02, /* Ready To Send */
- Out1 = 0x04, /* no longer in use */
- // Ie = 0x08, /* IRQ Enable (cd_sts_ch on omap) */
- Dm = 0x10, /* Diagnostic Mode loopback */
- };
- enum { /* Lsr */
- Dr = 0x01, /* Data Ready */
- Oe = 0x02, /* Overrun Error */
- Pe = 0x04, /* Parity Error */
- Fe = 0x08, /* Framing Error */
- Bi = 0x10, /* Break Interrupt */
- Thre = 0x20, /* Thr Empty */
- Temt = 0x40, /* Transmitter Empty */
- FIFOerr = 0x80, /* error in receiver FIFO */
- };
- enum { /* Msr */
- Dcts = 0x01, /* Delta Cts */
- Ddsr = 0x02, /* Delta Dsr */
- Teri = 0x04, /* Trailing Edge of Ri */
- Ddcd = 0x08, /* Delta Dcd */
- Cts = 0x10, /* Clear To Send */
- Dsr = 0x20, /* Data Set Ready */
- Ri = 0x40, /* Ring Indicator */
- Dcd = 0x80, /* Carrier Detect */
- };
- enum { /* Mdr */
- Modemask = 7,
- Modeuart = 0,
- };
- typedef struct Ctlr {
- u32int* io;
- int irq;
- int tbdf;
- int iena;
- int poll;
- uchar sticky[Scr+1];
- Lock;
- int hasfifo;
- int checkfifo;
- int fena;
- } Ctlr;
- extern PhysUart i8250physuart;
- static Ctlr i8250ctlr[] = {
- { .io = (u32int*)PHYSCONS,
- .irq = 74,
- .tbdf = -1,
- .poll = 0, },
- };
- static Uart i8250uart[] = {
- { .regs = &i8250ctlr[0], /* not [2] */
- .name = "COM3",
- .freq = 3686000, /* Not used, we use the global i8250freq */
- .phys = &i8250physuart,
- .console= 1,
- .next = nil, },
- };
- #define csr8r(c, r) ((c)->io[r])
- #define csr8w(c, r, v) ((c)->io[r] = (c)->sticky[r] | (v), coherence())
- #define csr8o(c, r, v) ((c)->io[r] = (v), coherence())
- static long
- i8250status(Uart* uart, void* buf, long n, long offset)
- {
- char *p;
- Ctlr *ctlr;
- uchar ier, lcr, mcr, msr;
- ctlr = uart->regs;
- p = malloc(READSTR);
- mcr = ctlr->sticky[Mcr];
- msr = csr8r(ctlr, Msr);
- ier = ctlr->sticky[Ier];
- lcr = ctlr->sticky[Lcr];
- snprint(p, READSTR,
- "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
- "dev(%d) type(%d) framing(%d) overruns(%d) "
- "berr(%d) serr(%d)%s%s%s%s\n",
- uart->baud,
- uart->hup_dcd,
- (msr & Dsr) != 0,
- uart->hup_dsr,
- (lcr & WlsMASK) + 5,
- (ier & Ems) != 0,
- (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
- (mcr & Rts) != 0,
- (lcr & Stb) ? 2: 1,
- ctlr->fena,
- uart->dev,
- uart->type,
- uart->ferr,
- uart->oerr,
- uart->berr,
- uart->serr,
- (msr & Cts) ? " cts": "",
- (msr & Dsr) ? " dsr": "",
- (msr & Dcd) ? " dcd": "",
- (msr & Ri) ? " ring": ""
- );
- n = readstr(offset, buf, n, p);
- free(p);
- return n;
- }
- static void
- i8250fifo(Uart* uart, int level)
- {
- Ctlr *ctlr;
- ctlr = uart->regs;
- if(ctlr->hasfifo == 0)
- return;
- /*
- * Changing the FIFOena bit in Fcr flushes data
- * from both receive and transmit FIFOs; there's
- * no easy way to guarantee not losing data on
- * the receive side, but it's possible to wait until
- * the transmitter is really empty.
- */
- ilock(ctlr);
- while(!(csr8r(ctlr, Lsr) & Temt))
- ;
- /*
- * Set the trigger level, default is the max.
- * value.
- * Some UARTs require FIFOena to be set before
- * other bits can take effect, so set it twice.
- */
- ctlr->fena = level;
- switch(level){
- case 0:
- break;
- case 1:
- level = FIFO1|FIFOena;
- break;
- case 4:
- level = FIFO4|FIFOena;
- break;
- case 8:
- level = FIFO8|FIFOena;
- break;
- default:
- level = FIFO14|FIFOena;
- break;
- }
- csr8w(ctlr, Fcr, level);
- csr8w(ctlr, Fcr, level);
- iunlock(ctlr);
- }
- static void
- i8250dtr(Uart* uart, int on)
- {
- Ctlr *ctlr;
- /*
- * Toggle DTR.
- */
- ctlr = uart->regs;
- if(on)
- ctlr->sticky[Mcr] |= Dtr;
- else
- ctlr->sticky[Mcr] &= ~Dtr;
- csr8w(ctlr, Mcr, 0);
- }
- static void
- i8250rts(Uart* uart, int on)
- {
- Ctlr *ctlr;
- /*
- * Toggle RTS.
- */
- ctlr = uart->regs;
- if(on)
- ctlr->sticky[Mcr] |= Rts;
- else
- ctlr->sticky[Mcr] &= ~Rts;
- csr8w(ctlr, Mcr, 0);
- }
- static void
- i8250modemctl(Uart* uart, int on)
- {
- Ctlr *ctlr;
- ctlr = uart->regs;
- ilock(&uart->tlock);
- if(on){
- ctlr->sticky[Ier] |= Ems;
- csr8w(ctlr, Ier, 0);
- uart->modem = 1;
- uart->cts = csr8r(ctlr, Msr) & Cts;
- }
- else{
- ctlr->sticky[Ier] &= ~Ems;
- csr8w(ctlr, Ier, 0);
- uart->modem = 0;
- uart->cts = 1;
- }
- iunlock(&uart->tlock);
- /* modem needs fifo */
- (*uart->phys->fifo)(uart, on);
- }
- static int
- i8250parity(Uart* uart, int parity)
- {
- int lcr;
- Ctlr *ctlr;
- ctlr = uart->regs;
- lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
- switch(parity){
- case 'e':
- lcr |= Eps|Pen;
- break;
- case 'o':
- lcr |= Pen;
- break;
- case 'n':
- break;
- default:
- return -1;
- }
- ctlr->sticky[Lcr] = lcr;
- csr8w(ctlr, Lcr, 0);
- uart->parity = parity;
- return 0;
- }
- static int
- i8250stop(Uart* uart, int stop)
- {
- int lcr;
- Ctlr *ctlr;
- ctlr = uart->regs;
- lcr = ctlr->sticky[Lcr] & ~Stb;
- switch(stop){
- case 1:
- break;
- case 2:
- lcr |= Stb;
- break;
- default:
- return -1;
- }
- ctlr->sticky[Lcr] = lcr;
- csr8w(ctlr, Lcr, 0);
- uart->stop = stop;
- return 0;
- }
- static int
- i8250bits(Uart* uart, int bits)
- {
- int lcr;
- Ctlr *ctlr;
- ctlr = uart->regs;
- lcr = ctlr->sticky[Lcr] & ~WlsMASK;
- switch(bits){
- case 5:
- lcr |= Wls5;
- break;
- case 6:
- lcr |= Wls6;
- break;
- case 7:
- lcr |= Wls7;
- break;
- case 8:
- lcr |= Wls8;
- break;
- default:
- return -1;
- }
- ctlr->sticky[Lcr] = lcr;
- csr8w(ctlr, Lcr, 0);
- uart->bits = bits;
- return 0;
- }
- static int
- i8250baud(Uart* uart, int baud)
- {
- #ifdef notdef /* don't change the speed */
- ulong bgc;
- Ctlr *ctlr;
- extern int i8250freq; /* In the config file */
- /*
- * Set the Baud rate by calculating and setting the Baud rate
- * Generator Constant. This will work with fairly non-standard
- * Baud rates.
- */
- if(i8250freq == 0 || baud <= 0)
- return -1;
- bgc = (i8250freq+8*baud-1)/(16*baud);
- ctlr = uart->regs;
- while(csr8r(ctlr, Usr) & Busy)
- delay(1);
- csr8w(ctlr, Lcr, Dlab); /* begin kludge */
- csr8o(ctlr, Dlm, bgc>>8);
- csr8o(ctlr, Dll, bgc);
- csr8w(ctlr, Lcr, 0);
- #endif
- uart->baud = baud;
- return 0;
- }
- static void
- i8250break(Uart* uart, int ms)
- {
- Ctlr *ctlr;
- if (up == nil)
- panic("i8250break: nil up");
- /*
- * Send a break.
- */
- if(ms <= 0)
- ms = 200;
- ctlr = uart->regs;
- csr8w(ctlr, Lcr, Brk);
- tsleep(&up->sleep, return0, 0, ms);
- csr8w(ctlr, Lcr, 0);
- }
- static void
- emptyoutstage(Uart *uart, int n)
- {
- _uartputs((char *)uart->op, n);
- uart->op = uart->oe = uart->ostage;
- }
- static void
- i8250kick(Uart* uart)
- {
- int i;
- Ctlr *ctlr;
- if(/* uart->cts == 0 || */ uart->blocked)
- return;
- if(!normalprint) { /* early */
- if (uart->op < uart->oe)
- emptyoutstage(uart, uart->oe - uart->op);
- while ((i = uartstageoutput(uart)) > 0)
- emptyoutstage(uart, i);
- return;
- }
- /* nothing more to send? then disable xmit intr */
- ctlr = uart->regs;
- if (uart->op >= uart->oe && qlen(uart->oq) == 0 &&
- csr8r(ctlr, Lsr) & Temt) {
- ctlr->sticky[Ier] &= ~Ethre;
- csr8w(ctlr, Ier, 0);
- return;
- }
- /*
- * 128 here is an arbitrary limit to make sure
- * we don't stay in this loop too long. If the
- * chip's output queue is longer than 128, too
- * bad -- presotto
- */
- for(i = 0; i < 128; i++){
- if(!(csr8r(ctlr, Lsr) & Thre))
- break;
- if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
- break;
- csr8o(ctlr, Thr, *uart->op++); /* start tx */
- ctlr->sticky[Ier] |= Ethre;
- csr8w(ctlr, Ier, 0); /* intr when done */
- }
- }
- void
- serialkick(void)
- {
- uartkick(&i8250uart[CONSOLE]);
- }
- static void
- i8250interrupt(Ureg*, void* arg)
- {
- Ctlr *ctlr;
- Uart *uart;
- int iir, lsr, old, r;
- uart = arg;
- ctlr = uart->regs;
- for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
- switch(iir & IirMASK){
- case Ims: /* Ms interrupt */
- r = csr8r(ctlr, Msr);
- if(r & Dcts){
- ilock(&uart->tlock);
- old = uart->cts;
- uart->cts = r & Cts;
- if(old == 0 && uart->cts)
- uart->ctsbackoff = 2;
- iunlock(&uart->tlock);
- }
- if(r & Ddsr){
- old = r & Dsr;
- if(uart->hup_dsr && uart->dsr && !old)
- uart->dohup = 1;
- uart->dsr = old;
- }
- if(r & Ddcd){
- old = r & Dcd;
- if(uart->hup_dcd && uart->dcd && !old)
- uart->dohup = 1;
- uart->dcd = old;
- }
- break;
- case Ithre: /* Thr Empty */
- uartkick(uart);
- break;
- case Irda: /* Received Data Available */
- case Irls: /* Receiver Line Status */
- case Ictoi: /* Character Time-out Indication */
- /*
- * Consume any received data.
- * If the received byte came in with a break,
- * parity or framing error, throw it away;
- * overrun is an indication that something has
- * already been tossed.
- */
- while((lsr = csr8r(ctlr, Lsr)) & Dr){
- if(lsr & (FIFOerr|Oe))
- uart->oerr++;
- if(lsr & Pe)
- uart->perr++;
- if(lsr & Fe)
- uart->ferr++;
- r = csr8r(ctlr, Rbr);
- if(!(lsr & (Bi|Fe|Pe)))
- uartrecv(uart, r);
- }
- break;
- default:
- iprint("weird uart interrupt type %#2.2uX\n", iir);
- break;
- }
- }
- }
- static void
- i8250disable(Uart* uart)
- {
- Ctlr *ctlr;
- /*
- * Turn off DTR and RTS, disable interrupts and fifos.
- */
- (*uart->phys->dtr)(uart, 0);
- (*uart->phys->rts)(uart, 0);
- (*uart->phys->fifo)(uart, 0);
- ctlr = uart->regs;
- ctlr->sticky[Ier] = 0;
- csr8w(ctlr, Ier, 0);
- if(ctlr->iena != 0){
- if(irqdisable(ctlr->irq, i8250interrupt, uart, uart->name) == 0)
- ctlr->iena = 0;
- }
- }
- static void
- i8250enable(Uart* uart, int ie)
- {
- int mode;
- Ctlr *ctlr;
- if (up == nil)
- return; /* too soon */
- ctlr = uart->regs;
- /* omap only: set uart/irda/cir mode to uart */
- mode = csr8r(ctlr, Mdr);
- csr8o(ctlr, Mdr, (mode & ~Modemask) | Modeuart);
- ctlr->sticky[Lcr] = Wls8; /* no parity */
- csr8w(ctlr, Lcr, 0);
- /*
- * Check if there is a FIFO.
- * Changing the FIFOena bit in Fcr flushes data
- * from both receive and transmit FIFOs; there's
- * no easy way to guarantee not losing data on
- * the receive side, but it's possible to wait until
- * the transmitter is really empty.
- * Also, reading the Iir outwith i8250interrupt()
- * can be dangerous, but this should only happen
- * once, before interrupts are enabled.
- */
- ilock(ctlr);
- if(!ctlr->checkfifo){
- /*
- * Wait until the transmitter is really empty.
- */
- while(!(csr8r(ctlr, Lsr) & Temt))
- ;
- csr8w(ctlr, Fcr, FIFOena);
- if(csr8r(ctlr, Iir) & Ifena)
- ctlr->hasfifo = 1;
- csr8w(ctlr, Fcr, 0);
- ctlr->checkfifo = 1;
- }
- iunlock(ctlr);
- /*
- * Enable interrupts and turn on DTR and RTS.
- * Be careful if this is called to set up a polled serial line
- * early on not to try to enable interrupts as interrupt-
- * -enabling mechanisms might not be set up yet.
- */
- if(ie){
- if(ctlr->iena == 0 && !ctlr->poll){
- irqenable(ctlr->irq, i8250interrupt, uart, uart->name);
- ctlr->iena = 1;
- }
- ctlr->sticky[Ier] = Erda;
- // ctlr->sticky[Mcr] |= Ie; /* not on omap */
- ctlr->sticky[Mcr] = 0;
- }
- else{
- ctlr->sticky[Ier] = 0;
- ctlr->sticky[Mcr] = 0;
- }
- csr8w(ctlr, Ier, 0);
- csr8w(ctlr, Mcr, 0);
- (*uart->phys->dtr)(uart, 1);
- (*uart->phys->rts)(uart, 1);
- /*
- * During startup, the i8259 interrupt controller is reset.
- * This may result in a lost interrupt from the i8250 uart.
- * The i8250 thinks the interrupt is still outstanding and does not
- * generate any further interrupts. The workaround is to call the
- * interrupt handler to clear any pending interrupt events.
- * Note: this must be done after setting Ier.
- */
- if(ie)
- i8250interrupt(nil, uart);
- }
- static Uart*
- i8250pnp(void)
- {
- return i8250uart;
- }
- static int
- i8250getc(Uart* uart)
- {
- Ctlr *ctlr;
- ctlr = uart->regs;
- while(!(csr8r(ctlr, Lsr) & Dr))
- delay(1);
- return csr8r(ctlr, Rbr);
- }
- static void
- i8250putc(Uart* uart, int c)
- {
- int i;
- Ctlr *ctlr;
- if (!normalprint) { /* too early; use brute force */
- int s = splhi();
- while (!(((ulong *)PHYSCONS)[Lsr] & Thre))
- ;
- ((ulong *)PHYSCONS)[Thr] = c;
- coherence();
- splx(s);
- return;
- }
- ctlr = uart->regs;
- for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++)
- delay(1);
- csr8o(ctlr, Thr, (uchar)c);
- for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++)
- delay(1);
- }
- void
- serialputc(int c)
- {
- i8250putc(&i8250uart[CONSOLE], c);
- }
- void
- serialputs(char* s, int n)
- {
- _uartputs(s, n);
- }
- #ifdef notdef
- static void
- i8250poll(Uart* uart)
- {
- Ctlr *ctlr;
- /*
- * If PhysUart has a non-nil .poll member, this
- * routine will be called from the uartclock timer.
- * If the Ctlr .poll member is non-zero, when the
- * Uart is enabled interrupts will not be enabled
- * and the result is polled input and output.
- * Not very useful here, but ports to new hardware
- * or simulators can use this to get serial I/O
- * without setting up the interrupt mechanism.
- */
- ctlr = uart->regs;
- if(ctlr->iena || !ctlr->poll)
- return;
- i8250interrupt(nil, uart);
- }
- #endif
- PhysUart i8250physuart = {
- .name = "i8250",
- .pnp = i8250pnp,
- .enable = i8250enable,
- .disable = i8250disable,
- .kick = i8250kick,
- .dobreak = i8250break,
- .baud = i8250baud,
- .bits = i8250bits,
- .stop = i8250stop,
- .parity = i8250parity,
- .modemctl = i8250modemctl,
- .rts = i8250rts,
- .dtr = i8250dtr,
- .status = i8250status,
- .fifo = i8250fifo,
- .getc = i8250getc,
- .putc = i8250putc,
- // .poll = i8250poll, /* only in 9k, not 9 */
- };
- static void
- i8250dumpregs(Ctlr* ctlr)
- {
- int dlm, dll;
- int _uartprint(char*, ...);
- csr8w(ctlr, Lcr, Dlab);
- dlm = csr8r(ctlr, Dlm);
- dll = csr8r(ctlr, Dll);
- csr8w(ctlr, Lcr, 0);
- _uartprint("dlm %#ux dll %#ux\n", dlm, dll);
- }
- Uart* uartenable(Uart *p);
- /* must call this from a process's context */
- int
- i8250console(void)
- {
- Uart *uart = &i8250uart[CONSOLE];
- if (up == nil)
- return -1; /* too early */
- if(uartenable(uart) != nil /* && uart->console */){
- // iprint("i8250console: enabling console uart\n");
- kbdq = uart->iq;
- serialoq = uart->oq;
- uart->putc = kbdcr2nl;
- uart->opens++;
- consuart = uart;
- }
- uartctl(uart, "b115200 l8 pn r1 s1 i1");
- return 0;
- }
- void
- _uartputs(char* s, int n)
- {
- char *e;
- for(e = s+n; s < e; s++){
- if(*s == '\n')
- i8250putc(&i8250uart[CONSOLE], '\r');
- i8250putc(&i8250uart[CONSOLE], *s);
- }
- }
- int
- _uartprint(char* fmt, ...)
- {
- int n;
- va_list arg;
- char buf[PRINTSIZE];
- va_start(arg, fmt);
- n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
- va_end(arg);
- _uartputs(buf, n);
- return n;
- }
|