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