Fork me on GitHub

root/drivers/char/serial.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_serial
  2. serial_identify
  3. serial_default
  4. serial_stop
  5. serial_start
  6. serial_deltab
  7. serial_reset
  8. serial_errors
  9. serial_send
  10. serial_receive
  11. irq_serial
  12. serial_open
  13. serial_close
  14. serial_set_termios
  15. serial_write
  16. irq_serial_bh
  17. serial_init

   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 }

/* [previous][next][first][last][top][bottom][index][help] */