Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/drivers/char/serial.c 3 * 4 * Copyright 2020-2021, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/asm.h> 9 #include <fiwix/kernel.h> 10 #include <fiwix/devices.h> 11 #include <fiwix/fs.h> 12 #include <fiwix/errno.h> 13 #include <fiwix/pic.h> 14 #include <fiwix/sleep.h> 15 #include <fiwix/serial.h> 16 #include <fiwix/tty.h> 17 #include <fiwix/ctype.h> 18 #include <fiwix/stdio.h> 19 #include <fiwix/string.h> 20 21 static struct fs_operations serial_driver_fsop = { 22 0, 23 0, 24 25 tty_open, 26 tty_close, 27 tty_read, 28 tty_write, 29 tty_ioctl, 30 tty_lseek, 31 NULL, /* readdir */ 32 NULL, /* mmap */ 33 tty_select, 34 35 NULL, /* readlink */ 36 NULL, /* followlink */ 37 NULL, /* bmap */ 38 NULL, /* lockup */ 39 NULL, /* rmdir */ 40 NULL, /* link */ 41 NULL, /* unlink */ 42 NULL, /* symlink */ 43 NULL, /* mkdir */ 44 NULL, /* mknod */ 45 NULL, /* truncate */ 46 NULL, /* create */ 47 NULL, /* rename */ 48 49 NULL, /* read_block */ 50 NULL, /* write_block */ 51 52 NULL, /* read_inode */ 53 NULL, /* write_inode */ 54 NULL, /* ialloc */ 55 NULL, /* ifree */ 56 NULL, /* statfs */ 57 NULL, /* read_superblock */ 58 NULL, /* remount_fs */ 59 NULL, /* write_superblock */ 60 NULL /* release_superblock */ 61 }; 62 63 static struct device serial_device = { 64 "ttyS", 65 SERIAL_MAJOR, 66 { 0, 0, 0, 0, 0, 0, 0, 0 }, 67 0, 68 NULL, 69 &serial_driver_fsop, 70 NULL 71 }; 72 73 /* 9600,N,8,1 by default */ 74 struct serial serial_table[NR_SERIAL] = { 75 { 0x3F8, SERIAL4_IRQ, 9600, "ttyS0", UART_LCR_NP | UART_LCR_WL8 | UART_LCR_1STB, 0, NULL, NULL }, 76 { 0x2F8, SERIAL3_IRQ, 9600, "ttyS1", UART_LCR_NP | UART_LCR_WL8 | UART_LCR_1STB, 0, NULL, NULL }, 77 { 0x3E8, SERIAL4_IRQ, 9600, "ttyS2", UART_LCR_NP | UART_LCR_WL8 | UART_LCR_1STB, 0, NULL, NULL }, 78 { 0x2E8, SERIAL3_IRQ, 9600, "ttyS3", UART_LCR_NP | UART_LCR_WL8 | UART_LCR_1STB, 0, NULL, NULL } 79 }; 80 81 char *serial_chip[] = { 82 NULL, 83 "8250", 84 "16450", 85 "16550", 86 "16550A", 87 }; 88 89 static int baud_table[] = { 90 0, 91 50, 92 75, 93 110, 94 134, 95 150, 96 200, 97 300, 98 600, 99 1200, 100 1800, 101 2400, 102 4800, 103 9600, 104 19200, 105 38400, 106 57200, 107 115200, 108 0 109 }; 110 111 static struct serial *serial_ports = NULL; 112 static struct bh serial_bh = { 0, &irq_serial_bh, NULL }; 113 static struct interrupt irq_config_serial0 = { 0, "serial", &irq_serial, NULL }; 114 static struct interrupt irq_config_serial1 = { 0, "serial", &irq_serial, NULL }; 115 116 static int is_serial(__dev_t dev) 117 { 118 if(MAJOR(dev) == SERIAL_MAJOR && ((MINOR(dev) >= (1 << SERIAL_MSF) && MINOR(dev) < (1 << SERIAL_MSF) + SERIAL_MINORS))) { 119 return 1; 120 } 121 122 return 0; 123 } 124 125 static int serial_identify(struct serial *s) 126 { 127 int value; 128 129 /* set all features in FCR register to test the status of FIFO */ 130 outport_b(s->addr + UART_FCR, (UART_FCR_FIFO | 131 UART_FCR_DMA | 132 UART_FCR_FIFO64 | 133 UART_FCR_FIFO14)); 134 135 value = inport_b(s->addr + UART_IIR); 136 if(value & UART_IIR_FIFOKO) { 137 if(value & UART_IIR_FIFO) { 138 if(value & UART_IIR_FIFO64) { 139 /* 16750 chip is not supported */ 140 } else { 141 s->flags |= UART_IS_16550A | UART_HAS_FIFO; 142 return 4; 143 } 144 } else { 145 s->flags |= UART_IS_16550; 146 return 3; 147 } 148 } else { 149 /* 150 * At this point we know this device don't has FIFO, 151 * the Scratch Register will help us to know the final chip. 152 */ 153 value = inport_b(s->addr + UART_SR); /* save its value */ 154 outport_b(s->addr + UART_SR, 0xAA); /* put a random value */ 155 if(inport_b(s->addr + UART_SR) != 0xAA) { 156 s->flags |= UART_IS_8250; 157 return 1; 158 } else { 159 outport_b(s->addr + UART_SR, value); /* restore it */ 160 s->flags |= UART_IS_16450; 161 return 2; 162 } 163 } 164 return 0; 165 } 166 167 static void serial_default(struct serial *s) 168 { 169 int divisor; 170 171 outport_b(s->addr + UART_IER, 0); /* disable all interrupts */ 172 173 divisor = 115200 / s->baud; 174 outport_b(s->addr + UART_LCR, UART_LCR_DLAB); /* enable DLAB */ 175 outport_b(s->addr + UART_DLL, divisor & 0xFF); /* LSB of divisor */ 176 outport_b(s->addr + UART_DLH, divisor >> 8); /* MSB of divisor */ 177 outport_b(s->addr + UART_LCR, s->lctrl); /* line control */ 178 } 179 180 /* disable transmitter interrupts */ 181 static void serial_stop(struct tty *tty) 182 { 183 struct serial *s; 184 unsigned long int flags; 185 186 SAVE_FLAGS(flags); CLI(); 187 s = (struct serial *)tty->driver_data; 188 outport_b(s->addr + UART_IER, UART_IER_RDAI); 189 RESTORE_FLAGS(flags); 190 } 191 192 /* enable transmitter interrupts */ 193 static void serial_start(struct tty *tty) 194 { 195 struct serial *s; 196 unsigned long int flags; 197 198 SAVE_FLAGS(flags); CLI(); 199 s = (struct serial *)tty->driver_data; 200 outport_b(s->addr + UART_IER, UART_IER_RDAI | UART_IER_THREI); 201 RESTORE_FLAGS(flags); 202 } 203 204 static void serial_deltab(struct tty *tty) 205 { 206 unsigned short int col, n, count; 207 struct cblock *cb; 208 unsigned char ch; 209 210 cb = tty->cooked_q.head; 211 col = count = 0; 212 213 while(cb) { 214 for(n = 0; n < cb->end_off; n++) { 215 if(n >= cb->start_off) { 216 ch = cb->data[n]; 217 if(ch == '\t') { 218 while(!tty->tab_stop[++col]); 219 } else { 220 col++; 221 if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) { 222 col++; 223 } 224 } 225 col %= 80; 226 } 227 } 228 cb = cb->next; 229 } 230 count = tty->column - col; 231 232 while(count--) { 233 tty_queue_putchar(tty, &tty->write_q, '\b'); 234 tty->column--; 235 } 236 } 237 238 static void serial_reset(struct tty *tty) 239 { 240 termios_reset(tty); 241 tty->winsize.ws_row = 25; 242 tty->winsize.ws_col = 80; 243 tty->winsize.ws_xpixel = 0; 244 tty->winsize.ws_ypixel = 0; 245 tty->lnext = 0; 246 } 247 248 static void serial_errors(struct serial *s, int status) 249 { 250 struct tty *tty; 251 252 tty = s->tty; 253 254 if(!(tty->termios.c_iflag & IGNBRK) && tty->termios.c_iflag & BRKINT) { 255 if(status & UART_LSR_BI) { 256 printk("WARNING: break interrupt in %s.\n", s->name); 257 } 258 } 259 260 /* this includes also overrun errors */ 261 if(!(tty->termios.c_iflag & IGNPAR) && tty->termios.c_iflag & PARMRK) { 262 if(status & UART_LSR_OE) { 263 printk("WARNING: overrun error in %s.\n", s->name); 264 265 } else if(status & UART_LSR_PE) { 266 printk("WARNING: parity error in %s.\n", s->name); 267 268 } else if(status & UART_LSR_FE) { 269 printk("WARNING: framing error in %s.\n", s->name); 270 271 } else if(status & UART_LSR_EFIFO) { 272 printk("WARNING: FIFO error in %s.\n", s->name); 273 } 274 } 275 } 276 277 static void serial_send(struct tty *tty) 278 { 279 unsigned char ch; 280 struct serial *s; 281 int count; 282 283 s = (struct serial *)tty->driver_data; 284 285 if(!tty->write_q.count) { 286 outport_b(s->addr + UART_IER, UART_IER_RDAI); 287 return; 288 } 289 290 count = 0; 291 while(tty->write_q.count > 0 && count < UART_FIFO_SIZE) { 292 ch = tty_queue_getchar(&tty->write_q); 293 outport_b(s->addr + UART_TD, ch); 294 count++; 295 } 296 297 if(!tty->write_q.count) { 298 outport_b(s->addr + UART_IER, UART_IER_RDAI); 299 } 300 wakeup(&tty_write); 301 } 302 303 static int serial_receive(struct serial *s) 304 { 305 int status, errno; 306 unsigned char ch; 307 struct tty *tty; 308 309 errno = 0; 310 tty = s->tty; 311 312 do { 313 if(!tty_queue_room(&tty->read_q)) { 314 errno = -EAGAIN; 315 break; 316 } 317 ch = inport_b(s->addr + UART_RD); 318 tty_queue_putchar(tty, &tty->read_q, ch); 319 status = inport_b(s->addr + UART_LSR); 320 } while(status & UART_LSR_RDA); 321 322 serial_bh.flags |= BH_ACTIVE; 323 return errno; 324 } 325 326 void irq_serial(int num, struct sigcontext *sc) 327 { 328 struct serial *s; 329 int status; 330 331 s = serial_ports; 332 333 if(s) { 334 do { 335 if(s->irq == num) { 336 while(!(inport_b(s->addr + UART_IIR) & UART_IIR_NOINT)) { 337 status = inport_b(s->addr + UART_LSR); 338 if(status & UART_LSR_RDA) { 339 if(serial_receive(s)) { 340 break; 341 } 342 } 343 if(status & UART_LSR_THRE) { 344 serial_send(s->tty); 345 } 346 serial_errors(s, status); 347 } 348 } 349 s = s->next; 350 } while(s); 351 } 352 } 353 354 int serial_open(struct tty *tty) 355 { 356 struct serial *s; 357 int minor; 358 359 minor = MINOR(tty->dev); 360 if(!TEST_MINOR(serial_device.minors, minor)) { 361 return -ENXIO; 362 } 363 364 s = (struct serial *)tty->driver_data; 365 366 /* enable FIFO */ 367 if(s->flags & UART_HAS_FIFO) { 368 outport_b(s->addr + UART_FCR, UART_FCR_FIFO | UART_FCR_FIFO14); 369 } 370 outport_b(s->addr + UART_MCR, UART_MCR_OUT2 | UART_MCR_RTS | UART_MCR_DTR); 371 372 /* enable interrupts */ 373 outport_b(s->addr + UART_IER, UART_IER_RDAI); 374 375 /* clear all input registers */ 376 inport_b(s->addr + UART_RD); 377 inport_b(s->addr + UART_IIR); 378 inport_b(s->addr + UART_LSR); 379 inport_b(s->addr + UART_MSR); 380 381 return 0; 382 } 383 384 int serial_close(struct tty *tty) 385 { 386 struct serial *s; 387 int minor; 388 389 minor = MINOR(tty->dev); 390 if(!TEST_MINOR(serial_device.minors, minor)) { 391 return -ENXIO; 392 } 393 394 s = (struct serial *)tty->driver_data; 395 396 if(tty->count > 1) { 397 return 0; 398 } 399 400 /* disable all interrupts */ 401 outport_b(s->addr + UART_IER, 0); 402 403 /* disable FIFO */ 404 outport_b(s->addr + UART_FCR, UART_FCR_CRCVR | UART_FCR_CXMTR); 405 406 /* clear all input register */ 407 inport_b(s->addr + UART_RD); 408 409 return 0; 410 } 411 412 void serial_set_termios(struct tty *tty) 413 { 414 short int divisor; 415 int baud, size, stop; 416 int lctrl; 417 struct serial *s; 418 419 s = (struct serial *)tty->driver_data; 420 lctrl = 0; 421 422 if(!(baud = tty->termios.c_cflag & CBAUD)) { 423 return; 424 } 425 divisor = 115200 / baud_table[baud]; 426 427 outport_b(s->addr + UART_LCR, UART_LCR_DLAB); /* enable DLAB */ 428 outport_b(s->addr + UART_DLL, divisor & 0xFF); /* LSB of divisor */ 429 outport_b(s->addr + UART_DLH, divisor >> 8); /* MSB of divisor */ 430 431 size = tty->termios.c_cflag & CSIZE; 432 switch(size) { 433 case CS5: 434 lctrl = UART_LCR_WL5; 435 break; 436 case CS6: 437 lctrl = UART_LCR_WL6; 438 break; 439 case CS7: 440 lctrl = UART_LCR_WL7; 441 break; 442 case CS8: 443 lctrl = UART_LCR_WL8; 444 break; 445 default: 446 lctrl = UART_LCR_WL5; 447 break; 448 } 449 450 stop = tty->termios.c_cflag & CSTOPB; 451 if(stop) { 452 lctrl |= UART_LCR_2STB; 453 } else { 454 lctrl |= UART_LCR_1STB; 455 } 456 457 if(tty->termios.c_cflag & PARENB) { 458 lctrl |= UART_LCR_EP; 459 } else if(tty->termios.c_cflag & PARODD) { 460 lctrl |= UART_LCR_OP; 461 } else { 462 lctrl |= UART_LCR_NP; 463 } 464 465 /* FIXME: flow control RTSCTS no supported */ 466 467 outport_b(s->addr + UART_LCR, lctrl); /* line control */ 468 } 469 470 void serial_write(struct tty *tty) 471 { 472 struct serial *s; 473 unsigned long int flags; 474 475 SAVE_FLAGS(flags); CLI(); 476 s = (struct serial *)tty->driver_data; 477 outport_b(s->addr + UART_IER, UART_IER_RDAI | UART_IER_THREI); 478 RESTORE_FLAGS(flags); 479 } 480 481 void irq_serial_bh(void) 482 { 483 struct tty *tty; 484 struct serial *s; 485 486 s = serial_ports; 487 488 if(s) { 489 do { 490 tty = s->tty; 491 if(tty->read_q.count) { 492 if(!lock_area(AREA_SERIAL_READ)) { 493 tty->input(tty); 494 unlock_area(AREA_SERIAL_READ); 495 } else { 496 serial_bh.flags |= BH_ACTIVE; 497 } 498 } 499 s = s->next; 500 } while(s); 501 } 502 } 503 504 void serial_init(void) 505 { 506 int n, n2, type, found; 507 struct serial **sp, *s; 508 struct tty *tty; 509 510 for(n = 0, found = 0; n < SERIAL_MINORS; n++) { 511 s = &serial_table[n]; 512 if((type = serial_identify(s))) { 513 printk("ttyS%d 0x%04X-0x%04X %d type=%s%s\n", n, s->addr, s->addr + 7, s->irq, serial_chip[type], s->flags & UART_HAS_FIFO ? " FIFO=yes" : ""); 514 515 SET_MINOR(serial_device.minors, (1 << SERIAL_MSF) + n); 516 serial_default(s); 517 sp = &serial_ports; 518 if(*sp) { 519 do { 520 sp = &(*sp)->next; 521 } while(*sp); 522 } 523 if(!register_tty(MKDEV(SERIAL_MAJOR, (1 << SERIAL_MSF) + n))) { 524 tty = get_tty(MKDEV(SERIAL_MAJOR, (1 << SERIAL_MSF) + n)); 525 tty->driver_data = (void *)s; 526 tty->stop = serial_stop; 527 tty->start = serial_start; 528 tty->deltab = serial_deltab; 529 tty->reset = serial_reset; 530 tty->input = do_cook; 531 tty->output = serial_write; 532 tty->open = serial_open; 533 tty->close = serial_close; 534 tty->set_termios = serial_set_termios; 535 serial_reset(tty); 536 for(n2 = 0; n2 < MAX_TAB_COLS; n2++) { 537 if(!(n2 % TAB_SIZE)) { 538 tty->tab_stop[n2] = 1; 539 } else { 540 tty->tab_stop[n2] = 0; 541 } 542 } 543 tty->count = 0; 544 s->tty = tty; 545 *sp = s; 546 found++; 547 } else { 548 printk("WARNING: %s(): unable to register ttyS%d.\n", __FUNCTION__, n); 549 } 550 } 551 } 552 if(found) { 553 add_bh(&serial_bh); 554 if(register_device(CHR_DEV, &serial_device)) { 555 printk("WARNING: %s(): unable to register serial device.\n", __FUNCTION__); 556 } 557 if(!register_irq(SERIAL4_IRQ, &irq_config_serial0)) { 558 enable_irq(SERIAL4_IRQ); 559 } 560 if(found > 1) { 561 if(!register_irq(SERIAL3_IRQ, &irq_config_serial1)) { 562 enable_irq(SERIAL3_IRQ); 563 } 564 } 565 566 if(is_serial(_syscondev)) { 567 register_console(console_flush_log_buf); 568 } 569 } 570 }