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
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
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
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>
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 */
62 unsigned char div_latch
[2]; /* mirror of 16550 R0':R1'
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
);
83 manage_int(struct com_data_struct
*cdsp
)
85 if ((cdsp
->int_enable
& IE_RCV_DATA
) && cdsp
->ids
> 0) {
87 debug(D_PORT
, "manage_int: hardint rd\n");
90 if ((cdsp
->int_enable
& IE_TRANS_HLD
) && cdsp
->emptyint
) {
92 debug(D_PORT
, "manage_int: hardint wr\n");
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;
113 input(struct com_data_struct
*cdsp
, int force_read
)
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
) : "");
129 output(struct com_data_struct
*cdsp
)
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
) : "");
140 memmove (&cdsp
->outbuf
[0],
141 &cdsp
->outbuf
[nbytes
], cdsp
->ods
);
142 if ((cdsp
->int_enable
& IE_TRANS_HLD
)
152 struct com_data_struct
*cdsp
= (struct com_data_struct
*)arg
;
158 * We postponed flush till the end of interrupt processing
162 write_char(struct com_data_struct
*cdsp
, char c
)
166 if (cdsp
->ods
>= N_BYTES
)
168 if (cdsp
->ods
< N_BYTES
) {
169 cdsp
->outbuf
[cdsp
->ods
++] = c
;
170 if (!isinhardint(cdsp
->irq
))
179 read_char(struct com_data_struct
*cdsp
)
188 memmove(&cdsp
->inbuf
[0], &cdsp
->inbuf
[1], cdsp
->ids
);
193 debug(D_PORT
, "read_char: %x\n", c
);
198 new_ii(struct com_data_struct
*cdsp
)
200 if ((cdsp
->int_enable
& IE_TRANS_HLD
) && cdsp
->ods
== 0)
206 get_status(struct com_data_struct
*cdsp
)
208 unsigned char s
= (LS_X_DATA_E
| LS_X_HOLD_E
);
213 if (cdsp
->ods
>= N_BYTES
)
216 debug(D_PORT
, "get_status: %x\n", (unsigned)s
);
221 get_int_id(struct com_data_struct
*cdsp
)
223 unsigned char s
= II_PEND_INT
;
224 if (cdsp
->fifo_ctrl
& FC_FIFO_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
;
230 s
= (s
& ~II_PEND_INT
) | II_TO
;
232 if ((cdsp
->int_enable
& IE_TRANS_HLD
) && cdsp
->emptyint
) {
234 s
= (s
& ~II_PEND_INT
) | II_TRANS_HLD
;
236 debug(D_PORT
, "get_int_id: %x\n", (unsigned)s
);
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
);
255 int14(regcontext_t
*REGS
)
257 struct com_data_struct
*cdsp
;
260 debug(D_PORT
, "int14: dl = 0x%02X, al = 0x%02X.\n", R_DL
, R_AL
);
261 if (R_DL
>= N_COMS_MAX
) {
264 fatal ("int14: illegal com port COM%d", R_DL
+ 1);
266 cdsp
= &(com_data
[R_DL
]);
269 case 0x00: /* Initialize Serial Port */
270 com_set_line(cdsp
, R_DL
+ 1, R_AL
);
271 R_AH
= get_status(cdsp
);
275 case 0x01: /* Write Character */
276 if (write_char(cdsp
, R_AL
)) {
277 R_AH
= get_status(cdsp
);
280 debug(D_PORT
, "int14: lost output character 0x%02x\n", R_AL
);
281 R_AH
= LS_SW_TIME_OUT
;
286 case 0x02: /* Read Character */
289 R_AH
= get_status(cdsp
);
292 R_AH
= LS_SW_TIME_OUT
;
297 case 0x03: /* Status Request */
298 R_AH
= get_status(cdsp
);
302 case 0x04: /* Extended Initialization */
303 R_AX
= (LS_SW_TIME_OUT
) << 8;
306 case 0x05: /* Modem Control Register operations */
308 case 0x00: /* Read Modem Control Register */
309 R_AX
= (LS_SW_TIME_OUT
) << 8;
312 case 0x01: /* Write Modem Control Register */
313 R_AX
= (LS_SW_TIME_OUT
) << 8;
317 unknown_int3(0x14, 0x05, R_AL
, REGS
);
322 unknown_int2(0x14, R_AH
, REGS
);
328 /* called when doscmd initializes a single line */
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
);
340 debug(D_PORT
, "Re-initialize serial port com%d\n", port
);
341 _RegisterIO(cdsp
->fd
, 0, 0, 0);
342 (void)close(cdsp
->fd
);
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)) {
352 "Could not initialize serial port com%d on path '%s'\n",
357 cdsp
->ids
= cdsp
->ods
= cdsp
->emptyint
= 0;
358 cdsp
->int_enable
= 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 */;
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;
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
;
385 cdsp
->tty
.c_cflag
&= ~CSTOPB
;
386 cdsp
->line_ctrl
&= ~LC_STOP_B
;
388 switch (param
& PARITY_EVEN
) {
390 cdsp
->tty
.c_cflag
|= (PARENB
| PARODD
);
391 cdsp
->line_ctrl
&= ~LC_EVEN_P
;
392 cdsp
->line_ctrl
|= LC_PAR_E
;
395 cdsp
->tty
.c_cflag
|= PARENB
;
396 cdsp
->line_ctrl
|= LC_EVEN_P
| LC_PAR_E
;
399 cdsp
->line_ctrl
&= ~LC_PAR_E
;
403 switch (param
& BITRATE_9600
) {
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
;
445 ret_val
= cfsetispeed(&cdsp
->tty
, speed
);
446 debug(D_PORT
, "com_set_line: cfsetispeed returned 0x%X.\n", ret_val
);
448 ret_val
= cfsetospeed(&cdsp
->tty
, speed
);
449 debug(D_PORT
, "com_set_line: cfsetospeed returned 0x%X.\n", ret_val
);
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
) : "");
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
);
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);
474 try_set_speed(struct com_data_struct
*cdsp
)
476 unsigned divisor
, speed
;
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
);
487 speed
= 115200 / divisor
;
492 ret_val
= cfsetispeed(&cdsp
->tty
, speed
);
493 debug(D_PORT
, "try_set_speed: cfsetispeed returned 0x%X.\n", ret_val
);
496 ret_val
= cfsetospeed(&cdsp
->tty
, speed
);
497 debug(D_PORT
, "try_set_speed: cfsetospeed returned 0x%X.\n", ret_val
);
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 */
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",
513 cdsp
= &(com_data
[port
]);
514 cdsp
->path
= path
; /* XXX DEBUG strcpy? */
518 com_set_line(cdsp
, port
+ 1, TXLEN_8BITS
| BITRATE_9600
);
520 /* update BIOS variables */
522 *(u_int16_t
*)&BIOSDATA
[0x00 + 2 * port
] = (u_int16_t
)addr
;
525 /* called when DOS wants to read directly from a physical port */
527 com_port_in(int port
)
529 struct com_data_struct
*cdsp
;
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
]);
541 if (i
== N_COMS_MAX
) {
542 debug(D_PORT
, "com port 0x%04x not found\n", port
);
546 switch (port
- cdsp
->addr
) {
547 /* 0x03F8 - (receive buffer) or (divisor latch LO) */
549 if (cdsp
->line_ctrl
& LC_DIV_ACC
)
550 rs
= cdsp
->div_latch
[DIV_LATCH_LOW
];
555 rs
= (unsigned char)r
;
559 /* 0x03F9 - (interrupt enable) or (divisor latch HI) */
561 if (cdsp
->line_ctrl
& LC_DIV_ACC
)
562 rs
= cdsp
->div_latch
[DIV_LATCH_HIGH
];
564 rs
= cdsp
->int_enable
;
567 /* 0x03FA - interrupt identification register */
569 rs
= get_int_id(cdsp
);
572 /* 0x03FB - line control register */
574 rs
= cdsp
->line_ctrl
;
577 /* 0x03FC - modem control register */
579 rs
= cdsp
->modem_ctrl
;
582 /* 0x03FD - line status register */
584 rs
= get_status(cdsp
);
587 /* 0x03FE - modem status register */
589 rs
= cdsp
->modem_stat
| MS_DCD
| MS_DSR
| MS_CTS
;
592 /* 0x03FF - spare register */
594 rs
= cdsp
->uart_spare
;
598 debug(D_PORT
, "com_port_in: illegal port index 0x%04x - 0x%04x\n",
605 /* called when DOS wants to write directly to a physical port */
607 com_port_out(int port
, unsigned char val
)
609 struct com_data_struct
*cdsp
;
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
]);
619 if (i
== N_COMS_MAX
) {
620 debug(D_PORT
, "com port 0x%04x not found\n", port
);
624 switch (port
- cdsp
->addr
) {
625 /* 0x03F8 - (transmit buffer) or (divisor latch LO) */
627 if (cdsp
->line_ctrl
& LC_DIV_ACC
) {
628 cdsp
->div_latch
[DIV_LATCH_LOW
] = val
;
631 write_char(cdsp
, val
);
635 /* 0x03F9 - (interrupt enable) or (divisor latch HI) */
637 if (cdsp
->line_ctrl
& LC_DIV_ACC
) {
638 cdsp
->div_latch
[DIV_LATCH_HIGH
] = val
;
641 cdsp
->int_enable
= val
;
646 /* 0x03FA - FIFO control register */
648 cdsp
->fifo_ctrl
= val
& (FC_FIFO_EN
| FC_FIFO_SZ_MASK
);
649 if (val
& FC_FIFO_CRV
)
651 if (val
& FC_FIFO_CTR
) {
659 /* 0x03FB - line control register */
661 cdsp
->line_ctrl
= val
;
664 /* 0x03FC - modem control register */
666 cdsp
->modem_ctrl
= val
;
669 /* 0x03FD - line status register */
674 /* 0x03FE - modem status register */
676 cdsp
->modem_stat
= val
;
679 /* 0x03FF - spare register */
681 cdsp
->uart_spare
= val
;
685 debug(D_PORT
, "com_port_out: illegal port index 0x%04x - 0x%04x\n",