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