4a72d2aba1f176740fbcace049405c2c065e720f
[dragonfly.git] / usr.bin / doscmd / int14.c
1 /*
2  * Copyright (c) 1992, 1993, 1996
3  *      Berkeley Software Design, Inc.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley Software
6  * Design, Inc. by Mark Linoman.
7  *
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.
20  *
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.
32  *
33  *      BSDI int14.c,v 2.2 1996/04/08 19:32:45 bostic Exp
34  *
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 $
37  */
38
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/uio.h>
42 #include <termios.h>
43 #include <unistd.h>
44
45 #include "doscmd.h"
46 #include "AsyncIO.h"
47 #include "com.h"
48
49 #define N_BYTES 1024
50
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 */
70 };
71
72 #define DIV_LATCH_LOW   0
73 #define DIV_LATCH_HIGH  1
74
75 struct com_data_struct com_data[N_COMS_MAX];
76
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);
81  
82 static void
83 manage_int(struct com_data_struct *cdsp)
84 {
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;
89         }
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;
94         }
95         unpend (cdsp->irq);
96 }
97  
98 static int
99 has_enough_data(struct com_data_struct *cdsp)
100 {
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;
108         }
109         return cdsp->ids;
110 }
111  
112 static void
113 input(struct com_data_struct *cdsp, int force_read)
114 {
115         int nbytes;
116  
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;
125         }
126 }
127  
128 static void
129 output(struct com_data_struct *cdsp)
130 {
131         int nbytes;
132  
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;
145                 }
146         }
147 }
148  
149 static void
150 flush_out(void* arg)
151 {
152         struct com_data_struct *cdsp = (struct com_data_struct*)arg;
153         output(cdsp);
154         manage_int(cdsp);
155 }
156  
157 /*
158  *  We postponed flush till the end of interrupt processing
159  *   (see int.c).
160  */
161 static int
162 write_char(struct com_data_struct *cdsp, char c)
163 {
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;
173         }
174         manage_int(cdsp);
175         return r;
176 }
177  
178 static int
179 read_char(struct com_data_struct *cdsp)
180 {
181         int c = -1;
182  
183         input(cdsp, 0);
184  
185         if (cdsp->ids > 0) {
186                 c = cdsp->inbuf[0];
187                 cdsp->ids --;
188                 memmove(&cdsp->inbuf[0], &cdsp->inbuf[1], cdsp->ids);
189         }
190  
191         manage_int(cdsp);
192  
193         debug(D_PORT, "read_char: %x\n", c);
194         return c;
195 }
196  
197 static void
198 new_ii(struct com_data_struct *cdsp)
199 {
200         if ((cdsp->int_enable & IE_TRANS_HLD) && cdsp->ods == 0)
201                 cdsp->emptyint = 1;
202         manage_int(cdsp);
203 }
204  
205 static unsigned char
206 get_status(struct com_data_struct *cdsp)
207 {
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;
215         }
216         debug(D_PORT, "get_status: %x\n", (unsigned)s);
217         return s;
218 }
219  
220 static unsigned char
221 get_int_id(struct com_data_struct *cdsp)
222 {
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;
235         }
236         debug(D_PORT, "get_int_id: %x\n", (unsigned)s);
237         return s;
238 }
239  
240 static void
241 com_async(int fd, int cond, void *arg, regcontext_t *REGS)
242 {
243         struct com_data_struct *cdsp = (struct com_data_struct*) arg;
244  
245         debug(D_PORT, "com_async: %X.\n", cond);
246  
247         if (cond & AS_RD)
248                 input(cdsp, 1);
249         if (cond & AS_WR)
250                 output(cdsp);
251         manage_int(cdsp);
252 }
253
254 void
255 int14(regcontext_t *REGS)
256 {
257     struct com_data_struct *cdsp;
258     int i;
259
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);
265     }
266     cdsp = &(com_data[R_DL]);
267
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;
274
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;
283         }
284         break;
285
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;
294         }
295         break;
296
297     case 0x03:  /* Status Request */
298         R_AH = get_status(cdsp);
299         R_AL = 0;
300         break;
301
302     case 0x04:  /* Extended Initialization */
303         R_AX = (LS_SW_TIME_OUT) << 8;
304         break;
305
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;
311
312         case 0x01:      /* Write Modem Control Register */
313                 R_AX = (LS_SW_TIME_OUT) << 8;
314                 break;
315
316         default:
317                 unknown_int3(0x14, 0x05, R_AL, REGS);
318                 break;
319         }
320         break;
321     default:
322         unknown_int2(0x14, R_AH, REGS);
323         break;
324     }
325 }
326
327
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)
331 {
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;
336     
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);
345     }
346     
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)) {
350         
351         debug(D_PORT,
352               "Could not initialize serial port com%d on path '%s'\n",
353               port, cdsp->path);
354         return;
355     }
356     
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;
363     
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;
380     }
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;
387     }    
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;
402     }
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;
436     }
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);
465     }
466     debug(D_PORT, "com%d: attached '%s' at addr 0x%04x irq %d\n",
467         port, cdsp->path, cdsp->addr, cdsp->irq);
468
469     set_eoir(cdsp->irq, flush_out, cdsp);
470     _RegisterIO(cdsp->fd, com_async, cdsp, 0);
471 }
472
473 static void
474 try_set_speed(struct com_data_struct *cdsp)
475 {
476     unsigned divisor, speed;
477     int ret_val;
478  
479     divisor = (unsigned) cdsp->div_latch[DIV_LATCH_HIGH] << 8
480         | (unsigned) cdsp->div_latch[DIV_LATCH_LOW];
481  
482     debug(D_PORT, "try_set_speed: divisor = %u\n", divisor);
483  
484     if (divisor == 0)
485         return;
486  
487     speed = 115200 / divisor;
488     if (speed == 0)
489         return;
490  
491     errno = 0;
492     ret_val = cfsetispeed(&cdsp->tty, speed);
493     debug(D_PORT, "try_set_speed: cfsetispeed returned 0x%X.\n", ret_val);
494  
495     errno = 0;
496     ret_val = cfsetospeed(&cdsp->tty, speed);
497     debug(D_PORT, "try_set_speed: cfsetospeed returned 0x%X.\n", ret_val);
498  
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) : "");
503 }
504
505 /* called when config.c initializes a single line */
506 void
507 init_com(int port, char *path, int addr, unsigned char irq)
508 {
509     struct com_data_struct *cdsp;
510         
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);
519
520     /* update BIOS variables */
521     nserial++;
522     *(u_int16_t *)&BIOSDATA[0x00 + 2 * port] = (u_int16_t)addr;
523 }
524
525 /* called when DOS wants to read directly from a physical port */
526 unsigned char
527 com_port_in(int port)
528 {
529     struct com_data_struct *cdsp;
530     unsigned char rs;
531     unsigned char i;
532     int r;
533
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;
539         }
540     }
541     if (i == N_COMS_MAX) {
542         debug(D_PORT, "com port 0x%04x not found\n", port);
543         return 0xff;
544     }
545
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;
556         }
557         break;
558
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;
566         
567         /* 0x03FA - interrupt identification register */
568     case 2:
569         rs = get_int_id(cdsp);
570         break;
571
572         /* 0x03FB - line control register */
573     case 3:
574         rs = cdsp->line_ctrl;
575         break;
576
577         /* 0x03FC - modem control register */
578     case 4:
579         rs = cdsp->modem_ctrl;
580         break;
581
582         /* 0x03FD - line status register */
583     case 5:
584         rs = get_status(cdsp);
585         break;
586
587         /* 0x03FE - modem status register */
588     case 6:
589         rs = cdsp->modem_stat | MS_DCD | MS_DSR | MS_CTS;
590         break;
591
592         /* 0x03FF - spare register */
593     case 7:
594         rs = cdsp->uart_spare;
595         break;
596
597     default:
598         debug(D_PORT, "com_port_in: illegal port index 0x%04x - 0x%04x\n",
599               port, cdsp->addr);
600         break;
601     }
602     return rs;
603 }
604
605 /* called when DOS wants to write directly to a physical port */
606 void
607 com_port_out(int port, unsigned char val)
608 {
609     struct com_data_struct *cdsp;
610     int i;
611
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;
617         }
618     }
619     if (i == N_COMS_MAX) {
620         debug(D_PORT, "com port 0x%04x not found\n", port);
621         return;
622     }
623
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);
632         }
633         break;
634
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);
643         }
644         break;
645         
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;
654         }
655         input(cdsp, 1);
656         manage_int(cdsp);
657         break;
658         
659         /* 0x03FB - line control register */
660     case 3:
661         cdsp->line_ctrl = val;
662         break;
663
664         /* 0x03FC - modem control register */
665     case 4:
666         cdsp->modem_ctrl = val;
667         break;
668
669         /* 0x03FD - line status register */
670     case 5:
671         /* read-only */
672         break;
673
674         /* 0x03FE - modem status register */
675     case 6:
676         cdsp->modem_stat = val;
677         break;
678
679         /* 0x03FF - spare register */
680     case 7:
681         cdsp->uart_spare = val;
682         break;
683
684     default:
685         debug(D_PORT, "com_port_out: illegal port index 0x%04x - 0x%04x\n",
686               port, cdsp->addr);
687         break;
688     }
689 }