xref: /netbsd-src/sys/arch/amiga/dev/msc.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: msc.c,v 1.10 1996/10/13 03:07:26 christos Exp $ */
2 
3 /*
4  * Copyright (c) 1993 Zik.
5  * Copyright (c) 1995 Jukka Marin <jmarin@teeri.jmp.fi>.
6  * Copyright (c) 1995 Timo Rossi <trossi@jyu.fi>.
7  * Copyright (c) 1995 Rob Healey <rhealey@kas.helios.mn.org>.
8  * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *   - converted from NetBSD Amiga serial driver to A2232 serial driver
40  *     by zik 931207
41  *   - added ttyflags hooks rfh 940419
42  *   - added new style config support rfh 940601
43  *   - added code to halt board during memory load so board doesn't flip
44  *     out. /dev/reload works now. Also created mschwiflow function so BSD can
45  *     attempt to use board RTS flow control now. rfh 950108
46  *   - Integrated work from Jukka Marin <jmarin@jmp.fi> and
47  *     Timo Rossi <trossi@jyu.fi> The mscmint() code is Jukka's. 950916
48  *     Integrated more bug fixes by Jukka Marin <jmarin@jmp.fi> 950918
49  *     Also added Jukka's turbo board code. 950918
50  *   - Reformatted to NetBSD style format.
51  */
52 
53 #include "msc.h"
54 
55 #if NMSC > 0
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/ioctl.h>
59 #include <sys/tty.h>
60 #include <sys/proc.h>
61 #include <sys/file.h>
62 #include <sys/malloc.h>
63 #include <sys/uio.h>
64 #include <sys/kernel.h>
65 #include <sys/syslog.h>
66 #include <sys/device.h>
67 
68 #include <amiga/amiga/device.h>
69 #include <amiga/dev/zbusvar.h>
70 #include <amiga/dev/mscreg.h>
71 #include <machine/cpu.h>
72 
73 #include <amiga/amiga/custom.h>
74 #include <amiga/amiga/cia.h>
75 #include <amiga/amiga/cc.h>
76 
77 #include <sys/conf.h>
78 #include <machine/conf.h>
79 
80 /* 6502 code for A2232 card */
81 #include "msc6502.h"
82 
83 /*
84  * Note: These are Zik's original comments:
85  * There is a bit of a "gotcha" concerning the numbering
86  * of msc devices and the specification of the number of serial
87  * lines in the kernel.
88  *
89  * Each board has seven lines, but for programming convenience
90  * and compatibility with Amiga UNIX the boards' minor device
91  * numbers are allocated in groups of sixteen:
92  *
93  *                     minor device numbers
94  * First board		0  2  4  6  8 10 12
95  * Second board        16 18 20 22 24 26 28
96  * Third board	       32 34 36 38 40 42 44
97  *
98  * The intermediate minor device numbers are dialin versions
99  * of the same devices. ie. 10 is dialout line 6 and 11 is
100  * the dialin version of line 6.
101  *
102  * On the other hand, I have made the NMSC config option refer
103  * to the total number of a2232 cards, not the maximum
104  * minor device number. So you might have NMSC=3, in which case
105  * you have three boards with minor device numbers from 0 to 45.
106  */
107 
108 int	mscparam __P((struct tty *, struct termios *));
109 void	mscstart __P((struct tty *));
110 int	mschwiflow __P((struct tty *, int));
111 int	mscinitcard __P((struct zbus_args *));
112 
113 int	mscdefaultrate = TTYDEF_SPEED;
114 
115 struct	mscdevice mscdev[MSCSLOTS];	/* device structs for all lines */
116 struct	tty *msc_tty[MSCTTYS];		/* ttys for all lines */
117 
118 struct	vbl_node msc_vbl_node[NMSC];	/* vbl interrupt node per board */
119 
120 struct speedtab mscspeedtab_normal[] = {
121 	{ 0,		0		},
122 	{ 50,		MSCPARAM_B50	},
123 	{ 75,		MSCPARAM_B75 	},
124 	{ 110,		MSCPARAM_B110 	},
125 	{ 134,		MSCPARAM_B134	},
126 	{ 150,		MSCPARAM_B150	},
127 	{ 300,		MSCPARAM_B300	},
128 	{ 600,		MSCPARAM_B600	},
129 	{ 1200,		MSCPARAM_B1200	},
130 	{ 1800,		MSCPARAM_B1800	},
131 	{ 2400,		MSCPARAM_B2400	},
132 	{ 3600,		MSCPARAM_B3600	},
133 	{ 4800,		MSCPARAM_B4800	},
134 	{ 7200,		MSCPARAM_B7200	},
135 	{ 9600,		MSCPARAM_B9600	},
136 	{ 19200,	MSCPARAM_B19200	},
137 	{ 115200,	MSCPARAM_B115200 },
138 	{ -1,		-1		}
139 };
140 
141 struct speedtab mscspeedtab_turbo[] = {
142 	{ 0,		0		},
143 	{ 100,		MSCPARAM_B50	},
144 	{ 150,		MSCPARAM_B75	},
145 	{ 220,		MSCPARAM_B110	},
146 	{ 269,		MSCPARAM_B134	},
147 	{ 300,		MSCPARAM_B150	},
148 	{ 600,		MSCPARAM_B300	},
149 	{ 1200,		MSCPARAM_B600	},
150 	{ 2400,		MSCPARAM_B1200	},
151 	{ 3600,		MSCPARAM_B1800	},
152 	{ 4800,		MSCPARAM_B2400	},
153 	{ 7200,		MSCPARAM_B3600	},
154 	{ 9600,		MSCPARAM_B4800	},
155 	{ 14400,	MSCPARAM_B7200	},
156 	{ 19200,	MSCPARAM_B9600	},
157 	{ 38400,	MSCPARAM_B19200	},
158 	{ 230400,	MSCPARAM_B115200 },
159 	{ -1,		-1		}
160 };
161 
162 struct   speedtab *mscspeedtab;
163 
164 int mscmctl __P((dev_t dev, int bits, int howto));
165 void mscmint __P((register void *data));
166 
167 int mscmatch __P((struct device *, void *, void *));
168 void mscattach __P((struct device *, struct device *, void *));
169 
170 #define	SWFLAGS(dev)	(msc->openflags | (MSCDIALIN(dev) ? 0 : TIOCFLAG_SOFTCAR))
171 #define	DEBUG_MSC	0
172 #define	DEBUG_CD	0
173 
174 struct cfattach msc_ca = {
175 	sizeof(struct device), mscmatch, mscattach
176 };
177 
178 struct cfdriver msc_cd = {
179 	NULL, "msc",DV_TTY, NULL, 0
180 };
181 
182 #if DEBUG_MSC
183 void
184 bugi(msc, string)
185 	struct mscdevice *msc;
186 	char *string;
187 {
188 	volatile struct mscstatus *ms;
189 	volatile struct mscmemory *mscmem;
190 
191 	mscmem = msc->board;
192 	ms = &mscmem->Status[msc->port];
193 
194 	printf("msc  %s u%d f%08lx F%08lx\n", string, msc->port, msc->flags,
195 		msc->openflags);
196 	printf("msc  h%d t%d H%d t%d p%02x c%02x CD%02x\n", ms->InHead,
197 		ms->InTail, ms->OutHead, ms->OutTail, ms->Param, ms->Command,
198 		ms->chCD);
199 	printf("msc  a%02x b%02x c%02x\n", ms->Pad_a, ms->Pad_b, ms->Padc);
200 
201 	return
202 }
203 
204 #endif
205 
206 int
207 mscmatch(pdp, match, auxp)
208 	struct device *pdp;
209 	void *match, *auxp;
210 {
211 	struct zbus_args *zap;
212 
213 	zap = auxp;
214 	if (zap->manid == 514 && (zap->prodid == 70 || zap->prodid == 69))
215 		return(1);
216 
217 	return (0);
218 }
219 
220 void
221 mscattach(pdp, dp, auxp)
222 	struct device *pdp, *dp;
223 	void *auxp;
224 {
225   volatile struct mscmemory *mscmem;
226   struct mscdevice *msc;
227   struct zbus_args *zap;
228   int unit;
229   int Count;
230 
231   zap = (struct zbus_args *)auxp;
232   unit = dp->dv_unit;
233 
234   if (mscinitcard(zap) != 0) {
235     printf("\nmsc%d: Board initialize failed, bad download code.\n", unit);
236     return;
237   }
238 
239   printf("\nmsc%d: Board successfully initialized.\n", unit);
240 
241   mscmem = (struct mscmemory *) zap->va;
242 
243   if (mscmem->Common.Crystal == MSC_UNKNOWN) {
244 	printf("msc%d: Unable to detect crystal frequency.\n", unit);
245 	return;
246   }
247 
248   if (mscmem->Common.Crystal == MSC_TURBO) {
249 	printf("msc%d: Turbo version detected (%02x%02x:%d)\n", unit,
250 		mscmem->Common.TimerH, mscmem->Common.TimerL,
251 		mscmem->Common.Pad_a);
252 	mscspeedtab = mscspeedtab_turbo;
253   } else {
254 	printf("msc%d: Normal version detected (%02x%02x:%d)\n", unit,
255 		mscmem->Common.TimerH, mscmem->Common.TimerL,
256 		mscmem->Common.Pad_a);
257 	mscspeedtab = mscspeedtab_normal;
258   }
259 
260   /* XXX 8 is a constant */
261   for (Count = 0; Count < 8 && MSCSLOTUL(unit, Count) < MSCSLOTS; Count++) {
262     msc = &mscdev[MSCSLOTUL(unit, Count)];
263     msc->board = mscmem;
264     msc->port = Count;
265     msc->flags = 0;
266     msc->openflags = 0;
267     msc->active = 1;
268     msc->closing = FALSE;
269     msc_tty[MSCTTYSLOT(MSCSLOTUL(unit, Count))] = NULL;
270     msc_tty[MSCTTYSLOT(MSCSLOTUL(unit, Count))+1] = NULL;
271 
272   }
273 
274   /* disable the non-existant eighth port */
275   if (MSCSLOTUL(unit, NUMLINES) < MSCSLOTS)
276     mscdev[MSCSLOTUL(unit, NUMLINES)].active = 0;
277 
278   msc_vbl_node[unit].function = (void (*) (void *)) mscmint;
279   msc_vbl_node[unit].data = (void *) unit;
280 
281   add_vbl_function (&msc_vbl_node[unit], MSC_VBL_PRIORITY, (void *)unit);
282 
283   return;
284 }
285 
286 /* ARGSUSED */
287 int
288 mscopen(dev, flag, mode, p)
289 	dev_t dev;
290 	int flag, mode;
291 	struct proc *p;
292 {
293   register struct tty *tp;
294   int error = 0;
295   int s;
296   int slot;
297   int ttyn;
298   struct mscdevice *msc;
299   volatile struct mscstatus *ms;
300 
301   /* get the device structure */
302   slot = MSCSLOT(dev);
303   ttyn = MSCTTY(dev);
304 
305   if (slot >= MSCSLOTS)
306     return ENXIO;
307 
308   if (MSCLINE(dev) >= NUMLINES)
309     return ENXIO;
310 
311   msc = &mscdev[slot];
312   ms = &msc->board->Status[msc->port];
313 
314   if (!msc->active)
315     return ENXIO;
316 
317   /*
318    * RFH: WHY here? Put down by while like other serial drivers
319    *      But if we do that it makes things bomb.
320    */
321   s = spltty();
322 
323   if (!msc_tty[ttyn]) {
324 
325       tp = ttymalloc();
326       tty_attach(tp);
327       msc_tty[ttyn] = tp;
328       msc_tty[ttyn+1] = (struct tty *)NULL;
329 
330 #if 0
331       /* default values are not optimal for this device, increase buffers. */
332       clfree(&tp->t_rawq);
333       clfree(&tp->t_canq);
334       clfree(&tp->t_outq);
335       clalloc(&tp->t_rawq, 8192, 1);
336       clalloc(&tp->t_canq, 8192, 1);
337       clalloc(&tp->t_outq, 8192, 0);
338 #endif
339 
340   }
341   else
342     tp = msc_tty[ttyn];
343 
344   tp->t_oproc = (void (*) (struct tty *)) mscstart;
345   tp->t_param = mscparam;
346   tp->t_dev = dev;
347   tp->t_hwiflow = mschwiflow;
348 
349   /* if port is still closing, just bitbucket remaining characters */
350   if (msc->closing) {
351 
352       ms->OutFlush = TRUE;
353       msc->closing = FALSE;
354   }
355 
356   /* initialize tty */
357   if ((tp->t_state & TS_ISOPEN) == 0) {
358 
359       tp->t_state |= TS_WOPEN;
360       ttychars(tp);
361       if (tp->t_ispeed == 0) {
362 
363 	  tp->t_iflag = TTYDEF_IFLAG;
364 	  tp->t_oflag = TTYDEF_OFLAG;
365 	  tp->t_cflag = TTYDEF_CFLAG;
366 	  tp->t_lflag = TTYDEF_LFLAG;
367 	  tp->t_ispeed = tp->t_ospeed = mscdefaultrate;
368       }
369 
370       /* flags changed to be private to every unit by JM */
371       if (msc->openflags & TIOCFLAG_CLOCAL)
372 		tp->t_cflag |= CLOCAL;
373       if (msc->openflags & TIOCFLAG_CRTSCTS)
374 		tp->t_cflag |= CRTSCTS;
375       if (msc->openflags & TIOCFLAG_MDMBUF)
376 		tp->t_cflag |= MDMBUF;
377 
378       mscparam(tp, &tp->t_termios);
379       ttsetwater(tp);
380 
381       (void) mscmctl(dev, TIOCM_DTR | TIOCM_RTS, DMSET);
382 
383       if ((SWFLAGS(dev) & TIOCFLAG_SOFTCAR) ||
384 	  (mscmctl(dev, 0, DMGET) & TIOCM_CD))
385             tp->t_state |= TS_CARR_ON;
386       else
387             tp->t_state &= ~TS_CARR_ON;
388 
389   }
390   else {
391         if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
392 	    splx(s);
393 	    return (EBUSY);
394        }
395   }
396 
397   /*
398    * if NONBLOCK requested, ignore carrier
399    */
400   if (flag & O_NONBLOCK)
401     goto done;
402 
403   /*
404    * s = spltty();
405    *
406    * This causes hangs when put here, like other TTY drivers do, rather than
407    * above, WHY? RFH
408    *
409    */
410 
411   while ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0) {
412 
413       tp->t_state |= TS_WOPEN;
414 
415 #if DEBUG_CD
416       printf("msc %ld waiting for CD\n", MSCLINE(dev));
417 #endif
418       error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ttopen, 0);
419 
420       if (error) {
421                splx(s);
422                return(error);
423       }
424   }
425 
426 done:
427 #if DEBUG_CD
428   printf("msc %ld waiting for CD\n", MSCLINE(dev));
429 #endif
430   /* This is a way to handle lost XON characters */
431   if ((flag & O_TRUNC) && (tp->t_state & TS_TTSTOP)) {
432           tp->t_state &= ~TS_TTSTOP;
433           ttstart (tp);
434   }
435 
436   splx(s);
437 
438   /*
439    * Reset the tty pointer, as there could have been a dialout
440    * use of the tty with a dialin open waiting.
441    */
442   tp->t_dev = dev;
443 
444   return((*linesw[tp->t_line].l_open)(dev, tp));
445 
446 }
447 
448 int
449 mscclose(dev, flag, mode, p)
450 	dev_t dev;
451 	int flag, mode;
452 	struct proc *p;
453 {
454   register struct tty *tp;
455   int slot;
456   volatile struct mscstatus *ms;
457   struct mscdevice *msc;
458 
459   /* get the device structure */
460   slot = MSCSLOT(dev);
461 
462   if (slot >= MSCSLOTS)
463     return ENXIO;
464 
465   msc = &mscdev[slot];
466 
467   if (!msc->active)
468     return ENXIO;
469 
470   ms = &msc->board->Status[msc->port];
471 
472 #if DEBUG_MSC
473   bugi(msc, "close1");
474 #endif
475 
476   tp = msc_tty[MSCTTY(dev)];
477   (*linesw[tp->t_line].l_close)(tp, flag);
478 
479   (void) mscmctl(dev, 0, DMSET);
480 
481   ttyclose(tp);
482 
483   if (msc->flags & TIOCM_DTR)
484     msc->closing = TRUE; /* flush remaining characters before dropping DTR */
485   else
486     ms->OutFlush = TRUE; /* just bitbucket remaining characters */
487 
488 #if DEBUG_MSC
489   bugi(msc, "close2");
490 #endif
491 
492   return (0);
493 
494 }
495 
496 int
497 mscread(dev, uio, flag)
498 	dev_t dev;
499 	struct uio *uio;
500 	int flag;
501 {
502 	register struct tty *tp;
503 
504 	tp = msc_tty[MSCTTY(dev)];
505 
506 	if (! tp)
507 	 return ENXIO;
508 
509 	return((*linesw[tp->t_line].l_read)(tp, uio, flag));
510 }
511 
512 int
513 mscwrite(dev, uio, flag)
514 	dev_t dev;
515 	struct uio *uio;
516 	int flag;
517 {
518   register struct tty *tp;
519 
520   tp = msc_tty[MSCTTY(dev)];
521 
522   if (! tp)
523     return ENXIO;
524 
525   return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
526 }
527 
528 /*
529  * This interrupt is periodically invoked in the vertical blank
530  * interrupt. It's used to keep track of the modem control lines
531  * and (new with the fast_int code) to move accumulated data up in
532  * to the tty layer.
533  *
534  * NOTE: MSCCDHACK is an invention of mine for dubious purposes. If you
535  *	 want to activate it add
536  *	 options MSCCDHACK
537  *	 in the kernel conf file. Basically it forces CD->Low transitions
538  *	 to ALWAYS send a signal to the process, even if the device is in
539  *	 clocal mode or an outdial device. RFH
540  */
541 void
542 mscmint (data)
543 	register void *data;
544 {
545   int unit;
546   register struct tty *tp;
547   int slot;
548   int maxslot;
549   struct mscdevice *msc;
550   volatile struct mscstatus *ms;
551   volatile u_char *ibuf, *cbuf;
552   unsigned char newhead; /* was int */
553   unsigned char bufpos;  /* was int */
554   int s;
555 
556   unit = (int) data;
557 
558   /* check each line on this board */
559   maxslot = MSCSLOTUL(unit, NUMLINES);
560   if (maxslot > MSCSLOTS)
561     maxslot = MSCSLOTS;
562 
563   for (slot = MSCSLOTUL(unit, 0); slot < maxslot; slot++)
564     {
565       msc = &mscdev[slot];
566 
567       if (!msc->active)
568         continue;
569 
570       tp = msc_tty[MSCTTYSLOT(slot)];
571       ms = &msc->board->Status[msc->port];
572 
573       newhead = ms->InHead;		/* 65c02 write pointer */
574 
575       /* yoohoo, is the port open? */
576       if (tp && (tp->t_state & (TS_ISOPEN|TS_WOPEN))) {
577 	/* port is open, handle all type of events */
578 
579 	/* set interrupt priority level */
580         s = spltty();
581 
582       /* check for input for this port */
583       if (newhead != (bufpos = ms->InTail))
584       {
585 #if DEBUG_MSC
586 	      printf("iop%d\n",slot);
587 #endif
588 	      /* buffer for input chars/events */
589 	      ibuf = &msc->board->InBuf[msc->port][0];
590 
591 	      /* data types of bytes in ibuf */
592 	      cbuf = &msc->board->InCtl[msc->port][0];
593 
594 	      /* do for all chars, if room */
595 	      while (bufpos != newhead)
596 	      {
597 		  /* which type of input data? */
598 		  switch (cbuf[bufpos])
599 		  {
600 		      /* input event (CD, BREAK, etc.) */
601 		      case MSCINCTL_EVENT:
602 			switch (ibuf[bufpos++])
603 			{
604 			    /* carrier detect change OFF -> ON */
605 			    case MSCEVENT_CarrierOn:
606 #if DEBUG_CD
607 			      printf("msc  CD ON %d\n", msc->port);
608 #endif
609 			      msc->flags |= TIOCM_CD;
610 			      if (MSCDIALIN(tp->t_dev))
611 				(*linesw[tp->t_line].l_modem)(tp, 1);
612 			      break;
613 
614 			    /*  carrier detect change ON -> OFF */
615 			    case MSCEVENT_CarrierOff:
616 #if DEBUG_CD
617 			      printf("msc  CD OFF %d\n", msc->port);
618 #endif
619 			      msc->flags &= ~TIOCM_CD;
620 #ifndef MSCCDHACK
621 			      if (MSCDIALIN(tp->t_dev))
622 #endif			    /* Note to format police: Don't merge the { below
623 			       in to the line above! */
624 			      {
625 				  if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0)
626 				  {
627 				      /* clear RTS and DTR, bitbucket output */
628 				      ms->Command = (ms->Command & ~MSCCMD_CMask) | MSCCMD_Close;
629 				      ms->Setup = TRUE;
630 				      msc->flags &= ~(TIOCM_DTR | TIOCM_RTS);
631 				      ms->OutFlush = TRUE;
632 				  }
633 			      }
634 			      break;
635 
636 			    case MSCEVENT_Break:
637 #if DEBUG_MSC
638 			      printf("Break received on msc%d\n", slot);
639 #endif
640 			      (*linesw[tp->t_line].l_rint)(TTY_FE, tp);
641 			      break;
642 
643 			    default:
644 			      printf("msc: unknown event type %d\n",
645 				      ibuf[(bufpos-1)&0xff]);
646 
647 			} /* event type switch */
648 			break;
649 
650 		      case MSCINCTL_CHAR:
651 			 if (tp->t_state & TS_TBLOCK) {
652 			   if (ms->chCD) {
653 			     /* Carrier detect ON -> OFF */
654 #if DEBUG_CD
655 			     printf("msc  CD OFF blocked %d msc->flags %08lx\n",
656 				    msc->port, msc->flags);
657 #endif
658 			     msc->flags &= ~TIOCM_CD;
659 
660 #ifndef MSCCDHACK
661 			     if (MSCDIALIN(tp->t_dev))
662 #endif
663 			     {
664 			       if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
665 				 /* Clear RTS and DTR, bitbucket output */
666 				 ms->Command = (ms->Command & ~MSCCMD_CMask) |
667 					       MSCCMD_Close;
668 				 ms->Setup = TRUE;
669 				 msc->flags &= ~(TIOCM_DTR | TIOCM_RTS);
670 				 ms->OutFlush = TRUE;
671 			       }
672 			     }
673 			   }
674 			   goto NoRoomForYa;
675 			 }
676 #if DEBUG_MSC
677 			 printf("'%c' ",ibuf[bufpos]);
678 #endif
679 			 (*linesw[tp->t_line].l_rint)((int)ibuf[bufpos++], tp);
680 			break;
681 
682 		      default:
683 			printf("msc: unknown data type %d\n", cbuf[bufpos]);
684 			bufpos++;
685 
686 		   } /* switch on input data type */
687 
688 		} /* while there's something in the buffer */
689 NoRoomForYa:
690 	      ms->InTail = bufpos;		/* tell 65C02 what we've read */
691 
692 	    } /* if there was something in the buffer */
693 
694 	  /* we get here only when the port is open */
695 	  /* send output */
696 	   if (tp->t_state & (TS_BUSY|TS_FLUSH))
697 	    {
698 
699 	      bufpos = ms->OutHead - ms->OutTail;
700 
701 	      /* busy and below low water mark? */
702 	      if (tp->t_state & TS_BUSY)
703 	        {
704 	          if (bufpos < IOBUFLOWWATER)
705 		    {
706 		      tp->t_state &= ~TS_BUSY;	/* not busy any more */
707 		      if (tp->t_line)
708 		        (*linesw[tp->t_line].l_start)(tp);
709 		      else
710 		        mscstart(tp);
711 		    }
712 		}
713 
714 	      /* waiting for flush and buffer empty? */
715 	      if (tp->t_state & TS_FLUSH)
716 	        {
717 	          if (bufpos == 0)
718 	            tp->t_state &= ~TS_FLUSH;	/* finished flushing */
719 	        }
720 	    } /* BUSY or FLUSH */
721 
722 	     splx(s);
723 
724       } else { /* End of port open */
725 	/* port is closed, don't pass on the chars from it */
726 
727       /* check for input for this port */
728       if (newhead != (bufpos = ms->InTail))
729       {
730 #if DEBUG_MSC
731 	      printf("icp%d\n",slot);
732 #endif
733 	      /* buffer for input chars/events */
734 	      ibuf = &msc->board->InBuf[msc->port][0];
735 
736 	      /* data types of bytes in ibuf */
737 	      cbuf = &msc->board->InCtl[msc->port][0];
738 
739 	      /* do for all chars, if room */
740 	      while (bufpos != newhead)
741 	      {
742 		  /* which type of input data? */
743 		  switch (cbuf[bufpos])
744 		  {
745 		      /* input event (CD, BREAK, etc.) */
746 		      case MSCINCTL_EVENT:
747 			switch (ibuf[bufpos++])
748 			{
749 			    /* carrier detect change OFF -> ON */
750 			    case MSCEVENT_CarrierOn:
751 #if DEBUG_CD
752 			      printf("msc  CD ON %d (closed)\n", msc->port);
753 #endif
754 			      msc->flags |= TIOCM_CD;
755 			      break;
756 
757 			    /*  carrier detect change ON -> OFF */
758 			    case MSCEVENT_CarrierOff:
759 #if DEBUG_CD
760 			      printf("msc  CD OFF %d (closed)\n", msc->port);
761 #endif
762 			      msc->flags &= ~TIOCM_CD;
763 #ifndef MSCCDHACK
764 			      if (tp && MSCDIALIN(tp->t_dev))
765 #else
766 			      if (tp )
767 #endif
768 			      {
769 				  if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0)
770 				  {
771 				      /* clear RTS and DTR, bitbucket output */
772 				      ms->Command = (ms->Command & ~MSCCMD_CMask) | MSCCMD_Close;
773 				      ms->Setup = TRUE;
774 				      msc->flags &= ~(TIOCM_DTR | TIOCM_RTS);
775 				      ms->OutFlush = TRUE;
776 				  }
777 			      }
778 			      break;
779 
780 			    default:
781 			      printf("msc: unknown event type %d\n",
782 				     ibuf[(bufpos-1)&0xff]);
783 
784 			} /* event type switch */
785 			break;
786 
787 		      default:
788 			bufpos++;
789 
790 		   } /* switch on input data type */
791 
792 		} /* while there's something in the buffer */
793 
794 	        ms->InTail = bufpos;		/* tell 65C02 what we've read */
795 
796 	    } /* if there was something in the buffer */
797       } /* End of port open/close */
798 
799       /* is this port closing? */
800       if (msc->closing)
801         {
802 	  /* if DTR is off, just bitbucket remaining characters */
803 	  if ( (msc->flags & TIOCM_DTR) == 0)
804 	    {
805 	      ms->OutFlush = TRUE;
806 	      msc->closing = FALSE;
807 	    }
808 	  /* if output has drained, drop DTR */
809           else if (ms->OutHead == ms->OutTail)
810 	    {
811 	      (void) mscmctl(tp->t_dev, 0, DMSET);
812 	      msc->closing = FALSE;
813             }
814         }
815     }  /* For all ports */
816 
817 }
818 
819 int
820 mscioctl(dev, cmd, data, flag, p)
821 	dev_t dev;
822 	u_long cmd;
823 	caddr_t data;
824 	int flag;
825 	struct proc *p;
826 {
827   register struct tty *tp;
828   register int slot;
829   register int error;
830   struct mscdevice *msc;
831   volatile struct mscstatus *ms;
832   int s;
833 
834   /* get the device structure */
835   slot = MSCSLOT(dev);
836 
837   if (slot >= MSCSLOTS)
838     return ENXIO;
839 
840   msc = &mscdev[slot];
841 
842   if (!msc->active)
843     return ENXIO;
844 
845   ms = &msc->board->Status[msc->port];
846   if (!(tp = msc_tty[MSCTTY(dev)]))
847     return ENXIO;
848 
849   error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
850 
851   if (error >= 0)
852     return (error);
853 
854   error = ttioctl(tp, cmd, data, flag, p);
855 
856   if (error >= 0)
857     return (error);
858 
859   switch (cmd) {
860 
861       /* send break */
862     case TIOCSBRK:
863       s = spltty();
864       ms->Command = (ms->Command & (~MSCCMD_RTSMask)) | MSCCMD_Break;
865       ms->Setup = TRUE;
866       splx(s);
867       break;
868 
869       /* clear break */
870     case TIOCCBRK:
871       s = spltty();
872       ms->Command = (ms->Command & (~MSCCMD_RTSMask)) | MSCCMD_RTSOn;
873       ms->Setup = TRUE;
874       splx(s);
875       break;
876 
877     case TIOCSDTR:
878       (void) mscmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS);
879       break;
880 
881     case TIOCCDTR:
882       if (!MSCDIALIN(dev))	/* don't let dialins drop DTR */
883         (void) mscmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC);
884       break;
885 
886     case TIOCMSET:
887       (void) mscmctl(dev, *(int *)data, DMSET);
888       break;
889 
890     case TIOCMBIS:
891       (void) mscmctl(dev, *(int *)data, DMBIS);
892       break;
893 
894     case TIOCMBIC:
895       if (MSCDIALIN(dev))	/* don't let dialins drop DTR */
896         (void) mscmctl(dev, *(int *)data & TIOCM_DTR, DMBIC);
897       else
898         (void) mscmctl(dev, *(int *)data, DMBIC);
899       break;
900 
901     case TIOCMGET:
902       *(int *)data = mscmctl(dev, 0, DMGET);
903       break;
904 
905     case TIOCGFLAGS:
906       *(int *)data = SWFLAGS(dev);
907       break;
908 
909     case TIOCSFLAGS:
910       error = suser(p->p_ucred, &p->p_acflag);
911       if (error != 0)
912               return(EPERM);
913 
914       msc->openflags = *(int *)data;
915 
916       /* only allow valid flags */
917       msc->openflags &= (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
918 
919       break;
920 
921     default:
922       return (ENOTTY);
923     }
924 
925   return (0);
926 }
927 
928 
929 int
930 mscparam(tp, t)
931 	register struct tty *tp;
932 	register struct termios *t;
933 {
934   register int cflag = t->c_cflag;
935   int slot;
936   struct mscdevice *msc;
937   volatile struct mscstatus *ms;
938   int s;
939   int ospeed = ttspeedtab(t->c_ospeed, mscspeedtab);
940 
941   /* get the device structure */
942   slot = MSCSLOT(tp->t_dev);
943 
944   if (slot >= MSCSLOTS)
945     return ENXIO;
946 
947   msc = &mscdev[slot];
948 
949   if (!msc->active)
950     return ENXIO;
951 
952   ms = &msc->board->Status[msc->port];
953 
954 #if DEBUG_MSC
955   bugi(msc, "param1");
956 #endif
957   /* check requested parameters */
958   if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
959     return (EINVAL);
960 
961   /* and copy to tty */
962   tp->t_ispeed = t->c_ispeed;
963   tp->t_ospeed = t->c_ospeed;
964   tp->t_cflag = cflag;
965 
966   /* hang up if baud is zero */
967   if (t->c_ospeed == 0) {
968 
969       if (!MSCDIALIN(tp->t_dev))  /* don't let dialins drop DTR */
970         (void) mscmctl(tp->t_dev, 0, DMSET);
971   }
972   else {
973 
974       /* set the baud rate */
975       s = spltty();
976       ms->Param = (ms->Param & ~MSCPARAM_BaudMask) | ospeed | MSCPARAM_RcvBaud;
977 
978       /* make sure any previous hangup is undone, ie.  reenable DTR.
979        * also mscmctl will cause the speed to be set
980        */
981       (void) mscmctl (tp->t_dev, TIOCM_DTR | TIOCM_RTS, DMSET);
982 
983       splx(s);
984   }
985 
986 #if DEBUG_MSC
987   bugi(msc, "param2");
988 #endif
989   return (0);
990 
991 }
992 
993 
994 /*
995  *	Jukka's code initializes alot of stuff that other drivers don't
996  *	I'm including it here so that this code is a common set of work
997  *	done by both of us. rfh
998  */
999 int
1000 mschwiflow(tp, flag)
1001 	struct tty *tp;
1002 	int flag;
1003 {
1004 
1005 /* Rob's version */
1006 #if 1
1007 #if DEBUG_MSC
1008 	printf("mschwiflow %d\n", flag);
1009 #endif
1010 
1011 	if (flag)
1012 	   mscmctl( tp->t_dev, TIOCM_RTS, DMBIC); /* Clear/Lower RTS */
1013 	else
1014 	   mscmctl( tp->t_dev, TIOCM_RTS, DMBIS); /* Set/Raise RTS */
1015 
1016 #endif
1017 
1018 /* Jukka's version */
1019 #if 0
1020   int slot;
1021   struct mscdevice *msc;
1022   volatile struct mscstatus *ms;
1023   int s;
1024 
1025   /* get the device structure */
1026   slot = MSCSLOT(tp->t_dev);
1027   if (slot >= MSCSLOTS)
1028     return ENXIO;
1029   msc = &mscdev[slot];
1030   if (!msc->active)
1031     return ENXIO;
1032   ms = &msc->board->Status[msc->port];
1033 
1034 #if DEBUG_MSC
1035   bugi(msc, "hwiflow");
1036 #endif
1037   /* Well, we should really _do_ something here, but the 65c02 code
1038    * manages the RTS signal on its own now, so...  This will probably
1039    * change in the future.
1040    */
1041 
1042 #endif
1043 	return 1;
1044 
1045 }
1046 
1047 void
1048 mscstart(tp)
1049 	register struct tty *tp;
1050 {
1051   register int cc;
1052   register char *cp;
1053   register int mhead;
1054   int s;
1055   int slot;
1056   struct mscdevice *msc;
1057   volatile struct mscstatus *ms;
1058   volatile char *mob;
1059   int hiwat = 0;
1060   int maxout;
1061 
1062   if (! (tp->t_state & TS_ISOPEN))
1063     return;
1064 
1065   slot = MSCSLOT(tp->t_dev);
1066 
1067 #if 0
1068   printf("starting msc%d\n", slot);
1069 #endif
1070 
1071   s = spltty();
1072 
1073   /* don't start if explicitly stopped */
1074   if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
1075     goto out;
1076 
1077   /* wake up if below low water */
1078   cc = tp->t_outq.c_cc;
1079 
1080   if (cc <= tp->t_lowat) {
1081       if (tp->t_state & TS_ASLEEP) {
1082 
1083 	  tp->t_state &= ~TS_ASLEEP;
1084 	  wakeup((caddr_t)&tp->t_outq);
1085 	}
1086 
1087       selwakeup(&tp->t_wsel);
1088   }
1089 
1090   /* don't bother if no characters or busy */
1091   if (cc == 0 || (tp->t_state & TS_BUSY))
1092     goto out;
1093 
1094   /*
1095    * Limit the amount of output we do in one burst
1096    */
1097   msc = &mscdev[slot];
1098   ms = &msc->board->Status[msc->port];
1099   mhead = ms->OutHead;
1100   maxout = mhead - ms->OutTail;
1101 
1102   if (maxout < 0)
1103       maxout += IOBUFLEN;
1104 
1105   maxout = IOBUFLEN - 1 - maxout;
1106 
1107   if (cc >= maxout) {
1108       hiwat++;
1109       cc = maxout;
1110   }
1111 
1112   cc = q_to_b (&tp->t_outq, msc->tmpbuf, cc);
1113 
1114   if (cc > 0) {
1115       tp->t_state |= TS_BUSY;
1116 
1117       mob = &msc->board->OutBuf[msc->port][0];
1118       cp = &msc->tmpbuf[0];
1119 
1120       /* enable output */
1121       ms->OutDisable = FALSE;
1122 
1123 #if 0
1124       msc->tmpbuf[cc] = 0;
1125       printf("sending '%s'\n", msctmpbuf);
1126 #endif
1127 
1128       /* send the first char across to reduce latency */
1129       mob[mhead++] = *cp++;
1130       mhead &= IOBUFLENMASK;
1131       ms->OutHead = mhead;
1132       cc--;
1133 
1134       /* copy the rest of the chars across quickly */
1135       while (cc > 0) {
1136 	  mob[mhead++] = *cp++;
1137 	  mhead &= IOBUFLENMASK;
1138 	  cc--;
1139       }
1140       ms->OutHead = mhead;
1141 
1142       /* leave the device busy if we've filled the buffer */
1143       if (!hiwat)
1144         tp->t_state &= ~TS_BUSY;
1145     }
1146 
1147 out:
1148   splx(s);
1149 
1150 }
1151 
1152 /* XXX */
1153 /*
1154  * Stop output on a line.
1155  */
1156 /*ARGSUSED*/
1157 void
1158 mscstop(tp, flag)
1159 	register struct tty *tp;
1160 	int flag;			/* defaulted to int anyway */
1161 {
1162 	register int s;
1163 #if 0
1164 	struct mscdevice *msc;
1165 	volatile struct mscstatus *ms;
1166 #endif
1167 
1168 	s = spltty();
1169 	if (tp->t_state & TS_BUSY) {
1170 		if ((tp->t_state & TS_TTSTOP) == 0) {
1171 			tp->t_state |= TS_FLUSH;
1172 #if 0
1173 			msc = &mscdev[MSCSLOT(tp->t_dev)];
1174 			ms = &msc->board->Status[msc->port];
1175 			printf("stopped output on msc%d\n", MSCSLOT(tp->t_dev));
1176 			ms->OutDisable = TRUE;
1177 #endif
1178 		}
1179 	}
1180 	splx(s);
1181 }
1182 
1183 /*
1184  * bits can be: TIOCM_DTR, TIOCM_RTS, TIOCM_CTS, TIOCM_CD, TIOCM_RI, TIOCM_DSR
1185  */
1186 int
1187 mscmctl(dev, bits, how)
1188 	dev_t dev;
1189 	int bits, how;
1190 {
1191   struct mscdevice *msc;
1192   volatile struct mscstatus *ms;
1193   int slot;
1194   int s;
1195   u_char newcmd;
1196   int OldFlags;
1197 
1198   /* get the device structure */
1199   slot = MSCSLOT(dev);
1200 
1201   if (slot >= MSCSLOTS)
1202     return ENXIO;
1203 
1204   msc = &mscdev[slot];
1205 
1206   if (!msc->active)
1207     return ENXIO;
1208 
1209 #if DEBUG_MSC
1210   bugi(msc, "mctl1");
1211 #endif
1212 
1213   s = spltty();		/* Jukka wants spl6() here, WHY?!! RFH */
1214 
1215   if (how != DMGET) {
1216       OldFlags = msc->flags;
1217       bits &= TIOCM_DTR | TIOCM_RTS; /* can only modify DTR and RTS */
1218 
1219       switch (how) {
1220         case DMSET:
1221 	  msc->flags = (bits | (msc->flags & ~(TIOCM_DTR | TIOCM_RTS)));
1222           break;
1223 
1224         case DMBIC:
1225           msc->flags &= ~bits;
1226           break;
1227 
1228         case DMBIS:
1229           msc->flags |= bits;
1230           break;
1231       }
1232 
1233 #if DEBUG_MSC
1234     bugi(msc, "mctl2");
1235 #endif
1236 
1237       /* modify modem control state */
1238       ms = &msc->board->Status[msc->port];
1239 
1240       if (msc->flags & TIOCM_RTS)	/* was bits & */
1241 	newcmd = MSCCMD_RTSOn;
1242       else			/* this doesn't actually work now */
1243 	newcmd = MSCCMD_RTSOff;
1244 
1245       if (msc->flags & TIOCM_DTR)	/* was bits & */
1246 	newcmd |= MSCCMD_Enable;
1247 
1248       ms->Command = (ms->Command & (~MSCCMD_RTSMask & ~MSCCMD_Enable)) | newcmd;
1249       ms->Setup = TRUE;
1250 
1251       /* if we've dropped DTR, bitbucket any pending output */
1252       if ( (OldFlags & TIOCM_DTR) && ((bits & TIOCM_DTR) == 0))
1253         ms->OutFlush = TRUE;
1254   }
1255 
1256   bits = msc->flags;
1257 
1258   (void) splx(s);
1259 
1260 #if DEBUG_MSC
1261     bugi(msc, "mctl3");
1262 #endif
1263 
1264   return(bits);
1265 
1266 }
1267 
1268 struct tty *
1269 msctty(dev)
1270 	dev_t dev;
1271 {
1272 	return(msc_tty[MSCTTY(dev)]);
1273 }
1274 
1275 /*
1276  * Load JM's freely redistributable A2232 6502c code. Let turbo detector
1277  * run for a while too.
1278  */
1279 
1280 int
1281 mscinitcard(zap)
1282 	struct zbus_args *zap;
1283 {
1284   int bcount;
1285   short start;
1286   u_char *from;
1287   volatile u_char *to;
1288   volatile struct mscmemory *mlm;
1289 
1290   mlm = (volatile struct mscmemory *)zap->va;
1291   (void)mlm->Enable6502Reset;
1292 
1293   /* copy the code across to the board */
1294   to = (u_char *)mlm;
1295   from = msc6502code; bcount = sizeof(msc6502code) - 2;
1296   start = *(short *)from; from += sizeof(start);
1297   to += start;
1298 
1299 #if DEBUG_MSC
1300   printf("\n** copying %ld bytes from %08lx to %08lx (start=%04lx)\n",
1301 	  (unsigned long)bcount, (unsigned long)from, to, start);
1302   printf("First byte to copy is %02lx\n", *from);
1303 #endif
1304 
1305   while(bcount--) *to++ = *from++;
1306 
1307   mlm->Common.Crystal = MSC_UNKNOWN;	/* use automatic speed check */
1308 
1309   /* start 6502 running */
1310   (void)mlm->ResetBoard;
1311 
1312   /* wait until speed detector has finished */
1313   for (bcount = 0; bcount < 200; bcount++) {
1314 	delay(10000);
1315 	if (mlm->Common.Crystal) break;
1316   }
1317 
1318   return(0);
1319 
1320 }
1321 
1322 #endif  /* NMSC > 0 */
1323