Home | History | Annotate | Download | only in libupload
      1 #include <stdbool.h>
      2 #include <stdlib.h>
      3 #include <stdio.h>
      4 #include <console.h>
      5 #include <sys/io.h>
      6 #include <sys/cpu.h>
      7 #include <syslinux/config.h>
      8 
      9 #include "serial.h"
     10 
     11 enum {
     12     THR = 0,
     13     RBR = 0,
     14     DLL = 0,
     15     DLM = 1,
     16     IER = 1,
     17     IIR = 2,
     18     FCR = 2,
     19     LCR = 3,
     20     MCR = 4,
     21     LSR = 5,
     22     MSR = 6,
     23     SCR = 7,
     24 };
     25 
     26 
     27 int serial_init(struct serial_if *sif, const char *argv[])
     28 {
     29     const struct syslinux_serial_console_info *sci
     30 	= syslinux_serial_console_info();
     31     uint16_t port;
     32     unsigned int divisor;
     33     uint8_t dll, dlm, lcr;
     34 
     35     if (!argv[0]) {
     36 	if (sci->iobase) {
     37 	    port = sci->iobase;
     38 	} else {
     39 	    printf("No port number specified and not using serial console!\n");
     40 	    return -1;
     41 	}
     42     } else {
     43 	port = strtoul(argv[0], NULL, 0);
     44 	if (port <= 3) {
     45 	    uint16_t addr = ((uint16_t *)0x400)[port];
     46 	    if (!addr) {
     47 		printf("No serial port address found!\n");
     48 	    return -1;
     49 	    }
     50 	    printf("Serial port %u is at 0x%04x\n", port, addr);
     51 	    port = addr;
     52 	}
     53     }
     54 
     55     sif->port = port;
     56     sif->console = false;
     57 
     58     divisor = 1;		/* Default speed = 115200 bps */
     59 
     60     /* Check to see if this is the same as the serial console */
     61     if (port == sci->iobase) {
     62 	/* Overlaying the console... */
     63 	sif->console = true;
     64 
     65 	/* Default to already configured speed */
     66 	divisor = sci->divisor;
     67 
     68 	/* Shut down I/O to the console for the time being */
     69 	openconsole(&dev_null_r, &dev_null_w);
     70     }
     71 
     72     if (argv[0] && argv[1])
     73 	divisor = 115200/strtoul(argv[1], NULL, 0);
     74 
     75     cli();			/* Just in case... */
     76 
     77     /* Save old register settings */
     78     sif->old.lcr = inb(port + LCR);
     79     sif->old.mcr = inb(port + MCR);
     80     sif->old.iir = inb(port + IIR);
     81 
     82     /* Set speed */
     83     outb(0x83, port + LCR);	/* Enable divisor access */
     84     sif->old.dll = inb(port + DLL);
     85     sif->old.dlm = inb(port + DLM);
     86     outb(divisor, port + DLL);
     87     outb(divisor >> 8, port + DLM);
     88     (void)inb(port + IER);	/* Synchronize */
     89 
     90     dll = inb(port + DLL);
     91     dlm = inb(port + DLM);
     92     lcr = inb(port + LCR);
     93     outb(0x03, port + LCR);	/* Enable data access, n81 */
     94     (void)inb(port + IER);	/* Synchronize */
     95     sif->old.ier = inb(port + IER);
     96 
     97     /* Disable interrupts */
     98     outb(0, port + IER);
     99 
    100     sti();
    101 
    102     if (dll != (uint8_t)divisor ||
    103 	dlm != (uint8_t)(divisor >> 8) ||
    104 	lcr != 0x83) {
    105 	serial_cleanup(sif);
    106 	printf("No serial port detected!\n");
    107 	return -1;		/* This doesn't look like a serial port */
    108     }
    109 
    110     /* Enable 16550A FIFOs if available */
    111     outb(0x01, port + FCR);	/* Enable FIFO */
    112     (void)inb(port + IER);	/* Synchronize */
    113     if (inb(port + IIR) < 0xc0)
    114 	outb(0x00, port + FCR);	/* Disable FIFOs if non-functional */
    115     (void)inb(port + IER);	/* Synchronize */
    116 
    117     return 0;
    118 }
    119 
    120 void serial_write(struct serial_if *sif, const void *data, size_t n)
    121 {
    122     uint16_t port = sif->port;
    123     const char *p = data;
    124     uint8_t lsr;
    125 
    126     while (n--) {
    127 	do {
    128 	    lsr = inb(port + LSR);
    129 	} while (!(lsr & 0x20));
    130 
    131 	outb(*p++, port + THR);
    132     }
    133 }
    134 
    135 void serial_read(struct serial_if *sif, void *data, size_t n)
    136 {
    137     uint16_t port = sif->port;
    138     char *p = data;
    139     uint8_t lsr;
    140 
    141     while (n--) {
    142 	do {
    143 	    lsr = inb(port + LSR);
    144 	} while (!(lsr & 0x01));
    145 
    146 	*p++ = inb(port + RBR);
    147     }
    148 }
    149 
    150 void serial_cleanup(struct serial_if *sif)
    151 {
    152     uint16_t port = sif->port;
    153 
    154     outb(0x83, port + LCR);
    155     (void)inb(port + IER);
    156     outb(sif->old.dll, port + DLL);
    157     outb(sif->old.dlm, port + DLM);
    158     (void)inb(port + IER);
    159     outb(sif->old.lcr & 0x7f, port + LCR);
    160     (void)inb(port + IER);
    161     outb(sif->old.mcr, port + MCR);
    162     outb(sif->old.ier, port + IER);
    163     if (sif->old.iir < 0xc0)
    164 	outb(0x00, port + FCR);	/* Disable FIFOs */
    165 
    166     /* Re-enable console messages, if we shut them down */
    167     if (sif->console)
    168 	openconsole(&dev_null_r, &dev_stdcon_w);
    169 }
    170