Add a proxy libpthread.
[dragonfly.git] / usr.bin / doscmd / int14.c
blob4a72d2aba1f176740fbcace049405c2c065e720f
1 /*
2 * Copyright (c) 1992, 1993, 1996
3 * Berkeley Software Design, Inc. All rights reserved.
5 * This code is derived from software contributed to Berkeley Software
6 * Design, Inc. by Mark Linoman.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Berkeley Software
19 * Design, Inc.
21 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
33 * BSDI int14.c,v 2.2 1996/04/08 19:32:45 bostic Exp
35 * $FreeBSD: src/usr.bin/doscmd/int14.c,v 1.2.2.1 2002/04/25 11:04:51 tg Exp $
36 * $DragonFly: src/usr.bin/doscmd/int14.c,v 1.2 2003/06/17 04:29:26 dillon Exp $
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/uio.h>
42 #include <termios.h>
43 #include <unistd.h>
45 #include "doscmd.h"
46 #include "AsyncIO.h"
47 #include "com.h"
49 #define N_BYTES 1024
51 struct com_data_struct {
52 int fd; /* BSD/386 file descriptor */
53 char *path; /* BSD/386 pathname */
54 int addr; /* ISA I/O address */
55 unsigned char irq; /* ISA IRQ */
56 unsigned char inbuf[N_BYTES]; /* input buffer */
57 unsigned char outbuf[N_BYTES];/* output buffer */
58 int ids; /* input data size */
59 int ods; /* output data size */
60 int emptyint;
61 struct termios tty;
62 unsigned char div_latch[2]; /* mirror of 16550 R0':R1'
63 read/write */
64 unsigned char int_enable; /* mirror of 16550 R1 read/write */
65 unsigned char fifo_ctrl; /* mirror of 16550 R2 write only */
66 unsigned char line_ctrl; /* mirror of 16550 R3 read/write */
67 unsigned char modem_ctrl; /* mirror of 16550 R4 read/write */
68 unsigned char modem_stat; /* mirror of 16550 R6 read/write */
69 unsigned char uart_spare; /* mirror of 16550 R7 read/write */
72 #define DIV_LATCH_LOW 0
73 #define DIV_LATCH_HIGH 1
75 struct com_data_struct com_data[N_COMS_MAX];
77 static unsigned char com_port_in(int port);
78 static void com_port_out(int port, unsigned char val);
79 static void com_set_line(struct com_data_struct *cdsp,
80 unsigned char port, unsigned char param);
82 static void
83 manage_int(struct com_data_struct *cdsp)
85 if ((cdsp->int_enable & IE_RCV_DATA) && cdsp->ids > 0) {
86 hardint(cdsp->irq);
87 debug(D_PORT, "manage_int: hardint rd\n");
88 return;
90 if ((cdsp->int_enable & IE_TRANS_HLD) && cdsp->emptyint) {
91 hardint(cdsp->irq);
92 debug(D_PORT, "manage_int: hardint wr\n");
93 return;
95 unpend (cdsp->irq);
98 static int
99 has_enough_data(struct com_data_struct *cdsp)
101 switch (cdsp->fifo_ctrl & (FC_FIFO_EN | FC_FIFO_SZ_MASK)) {
102 case FC_FIFO_EN | FC_FIFO_4B:
103 return cdsp->ids >= 4;
104 case FC_FIFO_EN | FC_FIFO_8B:
105 return cdsp->ids >= 8;
106 case FC_FIFO_EN | FC_FIFO_14B:
107 return cdsp->ids >= 14;
109 return cdsp->ids;
112 static void
113 input(struct com_data_struct *cdsp, int force_read)
115 int nbytes;
117 if (cdsp->ids < N_BYTES && (force_read || !has_enough_data(cdsp))) {
118 nbytes = read(cdsp->fd, &cdsp->inbuf[cdsp->ids],
119 N_BYTES - cdsp->ids);
120 debug(D_PORT, "read of fd %d on '%s' returned %d (%s)\n",
121 cdsp->fd, cdsp->path, nbytes,
122 nbytes == -1 ? strerror(errno) : "");
123 if (nbytes != -1)
124 cdsp->ids += nbytes;
128 static void
129 output(struct com_data_struct *cdsp)
131 int nbytes;
133 if (cdsp->ods > 0) {
134 nbytes = write(cdsp->fd, &cdsp->outbuf[0], cdsp->ods);
135 debug(D_PORT, "write of fd %d on '%s' returned %d (%s)\n",
136 cdsp->fd, cdsp->path, nbytes,
137 nbytes == -1 ? strerror(errno) : "");
138 if (nbytes != -1) {
139 cdsp->ods -= nbytes;
140 memmove (&cdsp->outbuf[0],
141 &cdsp->outbuf[nbytes], cdsp->ods);
142 if ((cdsp->int_enable & IE_TRANS_HLD)
143 && cdsp->ods == 0)
144 cdsp->emptyint = 1;
149 static void
150 flush_out(void* arg)
152 struct com_data_struct *cdsp = (struct com_data_struct*)arg;
153 output(cdsp);
154 manage_int(cdsp);
158 * We postponed flush till the end of interrupt processing
159 * (see int.c).
161 static int
162 write_char(struct com_data_struct *cdsp, char c)
164 int r = 0;
165 cdsp->emptyint = 0;
166 if (cdsp->ods >= N_BYTES)
167 output(cdsp);
168 if (cdsp->ods < N_BYTES) {
169 cdsp->outbuf[cdsp->ods ++] = c;
170 if (!isinhardint(cdsp->irq))
171 output(cdsp);
172 r = 1;
174 manage_int(cdsp);
175 return r;
178 static int
179 read_char(struct com_data_struct *cdsp)
181 int c = -1;
183 input(cdsp, 0);
185 if (cdsp->ids > 0) {
186 c = cdsp->inbuf[0];
187 cdsp->ids --;
188 memmove(&cdsp->inbuf[0], &cdsp->inbuf[1], cdsp->ids);
191 manage_int(cdsp);
193 debug(D_PORT, "read_char: %x\n", c);
194 return c;
197 static void
198 new_ii(struct com_data_struct *cdsp)
200 if ((cdsp->int_enable & IE_TRANS_HLD) && cdsp->ods == 0)
201 cdsp->emptyint = 1;
202 manage_int(cdsp);
205 static unsigned char
206 get_status(struct com_data_struct *cdsp)
208 unsigned char s = (LS_X_DATA_E | LS_X_HOLD_E);
209 if (cdsp->ids > 0)
210 s |= LS_RCV_DATA_RD;
211 if (cdsp->ods > 0) {
212 s &= ~LS_X_DATA_E;
213 if (cdsp->ods >= N_BYTES)
214 s &= ~LS_X_HOLD_E;
216 debug(D_PORT, "get_status: %x\n", (unsigned)s);
217 return s;
220 static unsigned char
221 get_int_id(struct com_data_struct *cdsp)
223 unsigned char s = II_PEND_INT;
224 if (cdsp->fifo_ctrl & FC_FIFO_EN)
225 s |= II_FIFOS_EN;
226 if ((cdsp->int_enable & IE_RCV_DATA) && cdsp->ids > 0) {
227 if (has_enough_data(cdsp))
228 s = (s & ~II_PEND_INT) | II_RCV_DATA;
229 else
230 s = (s & ~II_PEND_INT) | II_TO;
231 } else
232 if ((cdsp->int_enable & IE_TRANS_HLD) && cdsp->emptyint) {
233 cdsp->emptyint = 0;
234 s = (s & ~II_PEND_INT) | II_TRANS_HLD;
236 debug(D_PORT, "get_int_id: %x\n", (unsigned)s);
237 return s;
240 static void
241 com_async(int fd, int cond, void *arg, regcontext_t *REGS)
243 struct com_data_struct *cdsp = (struct com_data_struct*) arg;
245 debug(D_PORT, "com_async: %X.\n", cond);
247 if (cond & AS_RD)
248 input(cdsp, 1);
249 if (cond & AS_WR)
250 output(cdsp);
251 manage_int(cdsp);
254 void
255 int14(regcontext_t *REGS)
257 struct com_data_struct *cdsp;
258 int i;
260 debug(D_PORT, "int14: dl = 0x%02X, al = 0x%02X.\n", R_DL, R_AL);
261 if (R_DL >= N_COMS_MAX) {
262 if (vflag)
263 dump_regs(REGS);
264 fatal ("int14: illegal com port COM%d", R_DL + 1);
266 cdsp = &(com_data[R_DL]);
268 switch (R_AH) {
269 case 0x00: /* Initialize Serial Port */
270 com_set_line(cdsp, R_DL + 1, R_AL);
271 R_AH = get_status(cdsp);
272 R_AL = 0;
273 break;
275 case 0x01: /* Write Character */
276 if (write_char(cdsp, R_AL)) {
277 R_AH = get_status(cdsp);
278 R_AL = 0;
279 } else {
280 debug(D_PORT, "int14: lost output character 0x%02x\n", R_AL);
281 R_AH = LS_SW_TIME_OUT;
282 R_AL = 0;
284 break;
286 case 0x02: /* Read Character */
287 i = read_char(cdsp);
288 if (i != -1) {
289 R_AH = get_status(cdsp);
290 R_AL = (char)i;
291 } else {
292 R_AH = LS_SW_TIME_OUT;
293 R_AL = 0x60;
295 break;
297 case 0x03: /* Status Request */
298 R_AH = get_status(cdsp);
299 R_AL = 0;
300 break;
302 case 0x04: /* Extended Initialization */
303 R_AX = (LS_SW_TIME_OUT) << 8;
304 break;
306 case 0x05: /* Modem Control Register operations */
307 switch (R_AH) {
308 case 0x00: /* Read Modem Control Register */
309 R_AX = (LS_SW_TIME_OUT) << 8;
310 break;
312 case 0x01: /* Write Modem Control Register */
313 R_AX = (LS_SW_TIME_OUT) << 8;
314 break;
316 default:
317 unknown_int3(0x14, 0x05, R_AL, REGS);
318 break;
320 break;
321 default:
322 unknown_int2(0x14, R_AH, REGS);
323 break;
328 /* called when doscmd initializes a single line */
329 static void
330 com_set_line(struct com_data_struct *cdsp, unsigned char port, unsigned char param)
332 struct stat stat_buf;
333 int mode = 0; /* read | write */
334 int reg_num, ret_val, spd, speed;
335 u_int8_t div_hi, div_lo;
337 debug(D_PORT, "com_set_line: cdsp = %8p, port = 0x%04x,"
338 "param = 0x%04X.\n", cdsp, port, param);
339 if (cdsp->fd > 0) {
340 debug(D_PORT, "Re-initialize serial port com%d\n", port);
341 _RegisterIO(cdsp->fd, 0, 0, 0);
342 (void)close(cdsp->fd);
343 } else {
344 debug(D_PORT, "Initialize serial port com%d\n", port);
347 stat(cdsp->path, &stat_buf);
348 if (!S_ISCHR(stat_buf.st_mode) ||
349 ((cdsp->fd = open(cdsp->path, O_RDWR | O_NONBLOCK, 0666)) == -1)) {
351 debug(D_PORT,
352 "Could not initialize serial port com%d on path '%s'\n",
353 port, cdsp->path);
354 return;
357 cdsp->ids = cdsp->ods = cdsp->emptyint = 0;
358 cdsp->int_enable = 0;
359 cdsp->fifo_ctrl = 0;
360 cdsp->modem_ctrl = 0;
361 cdsp->modem_stat = 0;
362 cdsp->uart_spare = 0;
364 if ((param & PARITY_EVEN) == PARITY_NONE)
365 cdsp->tty.c_iflag = IGNBRK | IGNPAR /* | IXON | IXOFF | IXANY */;
366 else
367 cdsp->tty.c_iflag = IGNBRK /* | IXON | IXOFF | IXANY */;
368 cdsp->tty.c_oflag = 0;
369 cdsp->tty.c_lflag = 0;
370 cdsp->tty.c_cc[VTIME] = 0;
371 cdsp->tty.c_cc[VMIN] = 0;
372 cdsp->tty.c_cflag = CREAD | CLOCAL | HUPCL;
373 /* MCL WHY CLOCAL ??????; but, gets errno EIO on writes, else */
374 if ((param & TXLEN_8BITS) == TXLEN_8BITS) {
375 cdsp->tty.c_cflag |= CS8;
376 cdsp->line_ctrl |= 3;
377 } else {
378 cdsp->tty.c_cflag |= CS7;
379 cdsp->line_ctrl |= 2;
381 if ((param & STOPBIT_2) == STOPBIT_2) {
382 cdsp->tty.c_cflag |= CSTOPB;
383 cdsp->line_ctrl |= LC_STOP_B;
384 } else {
385 cdsp->tty.c_cflag &= ~CSTOPB;
386 cdsp->line_ctrl &= ~LC_STOP_B;
388 switch (param & PARITY_EVEN) {
389 case PARITY_ODD:
390 cdsp->tty.c_cflag |= (PARENB | PARODD);
391 cdsp->line_ctrl &= ~LC_EVEN_P;
392 cdsp->line_ctrl |= LC_PAR_E;
393 break;
394 case PARITY_EVEN:
395 cdsp->tty.c_cflag |= PARENB;
396 cdsp->line_ctrl |= LC_EVEN_P | LC_PAR_E;
397 break;
398 case PARITY_NONE:
399 cdsp->line_ctrl &= ~LC_PAR_E;
400 default:
401 break;
403 switch (param & BITRATE_9600) {
404 case BITRATE_110:
405 speed = B110;
406 spd = 110;
407 break;
408 case BITRATE_150:
409 speed = B150;
410 spd = 150;
411 break;
412 case BITRATE_300:
413 speed = B300;
414 spd = 300;
415 break;
416 case BITRATE_600:
417 speed = B600;
418 spd = 600;
419 break;
420 case BITRATE_1200:
421 speed = B1200;
422 spd = 1200;
423 break;
424 case BITRATE_2400:
425 speed = B2400;
426 spd = 2400;
427 break;
428 case BITRATE_4800:
429 speed = B4800;
430 spd = 4800;
431 break;
432 case BITRATE_9600:
433 speed = B9600;
434 spd = 9600;
435 break;
437 debug(D_PORT,
438 "com_set_line: going with cflag 0x%X iflag 0x%X speed %d.\n",
439 cdsp->tty.c_cflag, cdsp->tty.c_iflag, speed);
440 div_lo = (115200 / spd) & 0x00ff;
441 div_hi = (115200 / spd) & 0xff00;
442 cdsp->div_latch[DIV_LATCH_LOW] = div_lo;
443 cdsp->div_latch[DIV_LATCH_HIGH] = div_hi;
444 errno = 0;
445 ret_val = cfsetispeed(&cdsp->tty, speed);
446 debug(D_PORT, "com_set_line: cfsetispeed returned 0x%X.\n", ret_val);
447 errno = 0;
448 ret_val = cfsetospeed(&cdsp->tty, speed);
449 debug(D_PORT, "com_set_line: cfsetospeed returned 0x%X.\n", ret_val);
450 errno = 0;
451 ret_val = tcsetattr(cdsp->fd, 0, &cdsp->tty);
452 debug(D_PORT, "com_set_line: tcsetattr returned 0x%X (%s).\n",
453 ret_val, ret_val == -1 ? strerror(errno) : "");
454 errno = 0;
455 ret_val = fcntl(cdsp->fd, F_SETFL, O_NDELAY);
456 debug(D_PORT, "fcntl of 0x%X, 0x%X to fd %d returned %d errno %d\n",
457 F_SETFL, O_NDELAY, cdsp->fd, ret_val, errno);
458 errno = 0;
459 ret_val = ioctl(cdsp->fd, TIOCFLUSH, &mode);
460 debug(D_PORT, "ioctl of 0x%02lx to fd %d on 0x%X returned %d errno %d\n",
461 TIOCFLUSH, cdsp->fd, mode, ret_val, errno);
462 for (reg_num = 0; reg_num < N_OF_COM_REGS; reg_num++) {
463 define_input_port_handler(cdsp->addr + reg_num, com_port_in);
464 define_output_port_handler(cdsp->addr + reg_num, com_port_out);
466 debug(D_PORT, "com%d: attached '%s' at addr 0x%04x irq %d\n",
467 port, cdsp->path, cdsp->addr, cdsp->irq);
469 set_eoir(cdsp->irq, flush_out, cdsp);
470 _RegisterIO(cdsp->fd, com_async, cdsp, 0);
473 static void
474 try_set_speed(struct com_data_struct *cdsp)
476 unsigned divisor, speed;
477 int ret_val;
479 divisor = (unsigned) cdsp->div_latch[DIV_LATCH_HIGH] << 8
480 | (unsigned) cdsp->div_latch[DIV_LATCH_LOW];
482 debug(D_PORT, "try_set_speed: divisor = %u\n", divisor);
484 if (divisor == 0)
485 return;
487 speed = 115200 / divisor;
488 if (speed == 0)
489 return;
491 errno = 0;
492 ret_val = cfsetispeed(&cdsp->tty, speed);
493 debug(D_PORT, "try_set_speed: cfsetispeed returned 0x%X.\n", ret_val);
495 errno = 0;
496 ret_val = cfsetospeed(&cdsp->tty, speed);
497 debug(D_PORT, "try_set_speed: cfsetospeed returned 0x%X.\n", ret_val);
499 errno = 0;
500 ret_val = tcsetattr(cdsp->fd, 0, &cdsp->tty);
501 debug(D_PORT, "try_set_speed: tcsetattr returned 0x%X (%s).\n", ret_val,
502 ret_val == -1 ? strerror (errno) : "");
505 /* called when config.c initializes a single line */
506 void
507 init_com(int port, char *path, int addr, unsigned char irq)
509 struct com_data_struct *cdsp;
511 debug(D_PORT, "init_com: port = 0x%04x, addr = 0x%04X, irq = %d.\n",
512 port, addr, irq);
513 cdsp = &(com_data[port]);
514 cdsp->path = path; /* XXX DEBUG strcpy? */
515 cdsp->addr = addr;
516 cdsp->irq = irq;
517 cdsp->fd = -1;
518 com_set_line(cdsp, port + 1, TXLEN_8BITS | BITRATE_9600);
520 /* update BIOS variables */
521 nserial++;
522 *(u_int16_t *)&BIOSDATA[0x00 + 2 * port] = (u_int16_t)addr;
525 /* called when DOS wants to read directly from a physical port */
526 unsigned char
527 com_port_in(int port)
529 struct com_data_struct *cdsp;
530 unsigned char rs;
531 unsigned char i;
532 int r;
534 /* search for a valid COM ???or MOUSE??? port */
535 for (i = 0; i < N_COMS_MAX; i++) {
536 if (com_data[i].addr == ((unsigned short)port & 0xfff8)) {
537 cdsp = &(com_data[i]);
538 break;
541 if (i == N_COMS_MAX) {
542 debug(D_PORT, "com port 0x%04x not found\n", port);
543 return 0xff;
546 switch (port - cdsp->addr) {
547 /* 0x03F8 - (receive buffer) or (divisor latch LO) */
548 case 0:
549 if (cdsp->line_ctrl & LC_DIV_ACC)
550 rs = cdsp->div_latch[DIV_LATCH_LOW];
551 else {
552 rs = 0x60;
553 r = read_char(cdsp);
554 if (r != -1)
555 rs = (unsigned char)r;
557 break;
559 /* 0x03F9 - (interrupt enable) or (divisor latch HI) */
560 case 1:
561 if (cdsp->line_ctrl & LC_DIV_ACC)
562 rs = cdsp->div_latch[DIV_LATCH_HIGH];
563 else
564 rs = cdsp->int_enable;
565 break;
567 /* 0x03FA - interrupt identification register */
568 case 2:
569 rs = get_int_id(cdsp);
570 break;
572 /* 0x03FB - line control register */
573 case 3:
574 rs = cdsp->line_ctrl;
575 break;
577 /* 0x03FC - modem control register */
578 case 4:
579 rs = cdsp->modem_ctrl;
580 break;
582 /* 0x03FD - line status register */
583 case 5:
584 rs = get_status(cdsp);
585 break;
587 /* 0x03FE - modem status register */
588 case 6:
589 rs = cdsp->modem_stat | MS_DCD | MS_DSR | MS_CTS;
590 break;
592 /* 0x03FF - spare register */
593 case 7:
594 rs = cdsp->uart_spare;
595 break;
597 default:
598 debug(D_PORT, "com_port_in: illegal port index 0x%04x - 0x%04x\n",
599 port, cdsp->addr);
600 break;
602 return rs;
605 /* called when DOS wants to write directly to a physical port */
606 void
607 com_port_out(int port, unsigned char val)
609 struct com_data_struct *cdsp;
610 int i;
612 /* search for a valid COM ???or MOUSE??? port */
613 for (i = 0; i < N_COMS_MAX; i++) {
614 if (com_data[i].addr == ((unsigned short)port & 0xfff8)) {
615 cdsp = &(com_data[i]);
616 break;
619 if (i == N_COMS_MAX) {
620 debug(D_PORT, "com port 0x%04x not found\n", port);
621 return;
624 switch (port - cdsp->addr) {
625 /* 0x03F8 - (transmit buffer) or (divisor latch LO) */
626 case 0:
627 if (cdsp->line_ctrl & LC_DIV_ACC) {
628 cdsp->div_latch[DIV_LATCH_LOW] = val;
629 try_set_speed(cdsp);
630 } else {
631 write_char(cdsp, val);
633 break;
635 /* 0x03F9 - (interrupt enable) or (divisor latch HI) */
636 case 1:
637 if (cdsp->line_ctrl & LC_DIV_ACC) {
638 cdsp->div_latch[DIV_LATCH_HIGH] = val;
639 try_set_speed(cdsp);
640 } else {
641 cdsp->int_enable = val;
642 new_ii(cdsp);
644 break;
646 /* 0x03FA - FIFO control register */
647 case 2:
648 cdsp->fifo_ctrl = val & (FC_FIFO_EN | FC_FIFO_SZ_MASK);
649 if (val & FC_FIFO_CRV)
650 cdsp->ids = 0;
651 if (val & FC_FIFO_CTR) {
652 cdsp->ods = 0;
653 cdsp->emptyint = 1;
655 input(cdsp, 1);
656 manage_int(cdsp);
657 break;
659 /* 0x03FB - line control register */
660 case 3:
661 cdsp->line_ctrl = val;
662 break;
664 /* 0x03FC - modem control register */
665 case 4:
666 cdsp->modem_ctrl = val;
667 break;
669 /* 0x03FD - line status register */
670 case 5:
671 /* read-only */
672 break;
674 /* 0x03FE - modem status register */
675 case 6:
676 cdsp->modem_stat = val;
677 break;
679 /* 0x03FF - spare register */
680 case 7:
681 cdsp->uart_spare = val;
682 break;
684 default:
685 debug(D_PORT, "com_port_out: illegal port index 0x%04x - 0x%04x\n",
686 port, cdsp->addr);
687 break;