Commit | Line | Data |
---|---|---|
4f02060d | 1 | /* @(#)if_ddn.c 7.2 (Berkeley) %G% */ |
f0ecc930 MK |
2 | |
3 | ||
4 | /************************************************************************\ | |
5 | ||
6 | ________________________________________________________ | |
7 | / \ | |
8 | | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | | |
9 | | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | | |
10 | | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | | |
11 | | AAAA AAAA CCCC CCCC | | |
12 | | AAAA AAAA CCCC CCCC | | |
13 | | AAAA AAAA CCCC CCCC | | |
14 | | AAAA AAAA CCCC CCCC | | |
15 | | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | | |
16 | | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | | |
17 | | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | | |
18 | \________________________________________________________/ | |
19 | ||
20 | Copyright (c) 1985 by Advanced Computer Communications | |
21 | 720 Santa Barbara Street, Santa Barbara, California 93101 | |
22 | (805) 963-9431 | |
23 | ||
24 | This software may be duplicated and used on systems | |
25 | which are licensed to run U.C. Berkeley versions of | |
26 | the UNIX operating system. Any duplication of any | |
27 | part of this software must include a copy of ACC's | |
28 | copyright notice. | |
29 | ||
30 | ||
31 | File: | |
32 | if_ddn.c | |
33 | ||
34 | Author: | |
35 | Art Berggreen | |
36 | ||
37 | Project: | |
38 | 4.2 DDN X.25 network driver | |
39 | ||
40 | Function: | |
41 | This is a network device driver for BSD 4.2 UNIX which | |
42 | provides an interface between IP and ACC's ACP625 | |
43 | (IF-11/X25) for connecting to the Defense Data Network. | |
44 | ||
45 | Components: | |
46 | ||
47 | Revision History: | |
48 | 16-May-1985: V1.0 - First release. | |
49 | Art Berggreen. | |
50 | ||
51 | \************************************************************************/ | |
52 | \f | |
53 | ||
54 | /* if_ddn.c V1.0 5/16/85 */ | |
55 | ||
56 | /* | |
57 | * ACC ACP625 DDN/X.25 Network device driver | |
58 | */ | |
59 | ||
60 | /* #define DDNDEBUG 1 /* Enable definition for Debug code */ | |
61 | ||
62 | #include "ddn.h" | |
63 | #if NDDN > 0 | |
64 | #include "../machine/pte.h" | |
65 | ||
e43e0f32 MK |
66 | #include "param.h" |
67 | #include "systm.h" | |
68 | #include "mbuf.h" | |
69 | #include "buf.h" | |
70 | #include "protosw.h" | |
71 | #include "socket.h" | |
72 | #include "vmmac.h" | |
73 | #include "errno.h" | |
74 | #include "time.h" | |
75 | #include "kernel.h" | |
76 | #include "ioctl.h" | |
f0ecc930 MK |
77 | |
78 | #include "../net/if.h" | |
79 | #include "../net/netisr.h" | |
80 | #include "../net/route.h" | |
e43e0f32 | 81 | |
e43e0f32 | 82 | #ifdef INET |
f0ecc930 MK |
83 | #include "../netinet/in.h" |
84 | #include "../netinet/in_systm.h" | |
e43e0f32 | 85 | #include "../netinet/in_var.h" |
f0ecc930 | 86 | #include "../netinet/ip.h" |
e43e0f32 | 87 | #endif |
f0ecc930 MK |
88 | |
89 | #include "../vax/cpu.h" | |
90 | #include "../vax/mtpr.h" | |
e43e0f32 MK |
91 | #include "if_ddnreg.h" |
92 | #include "if_ddnvar.h" | |
93 | #include "if_uba.h" | |
f0ecc930 MK |
94 | #include "../vaxuba/ubareg.h" |
95 | #include "../vaxuba/ubavar.h" | |
96 | ||
97 | \f | |
98 | ||
99 | /* declare global functions */ | |
100 | ||
101 | int ddnprobe(); | |
102 | int ddnattach(); | |
103 | int ddnreset(); | |
104 | int ddninit(); | |
105 | int ddnoutput(); | |
106 | int ddntimer(); | |
107 | int ddnioctl(); | |
108 | int ddnintr(); | |
109 | ||
110 | /* declare local functions */ | |
111 | ||
112 | static void x25_init(); | |
113 | static struct ddn_cb *locate_x25_lcn(); | |
114 | static boolean convert_ip_addr(); | |
115 | static int convert_x25_addr(); | |
116 | static boolean make_x25_call(); | |
117 | static void ddn_start(); | |
118 | static void ddn_iorq(); | |
119 | static void start_chn(); | |
120 | static void ddn_data(); | |
121 | static void ddn_supr(); | |
122 | static void supr_msg(); | |
123 | static boolean decode_ring(); | |
124 | static void clear_lcn(); | |
125 | static void send_restart(); | |
126 | static void send_supr(); | |
127 | #ifdef DDNDEBUG | |
128 | static void prt_addr(); | |
129 | static void prt_bytes(); | |
130 | #endif DDNDEBUG | |
131 | \f | |
132 | ||
133 | struct uba_device *ddninfo[NDDN]; /* ptrs to device info */ | |
134 | u_short ddnstd[] = { 0766740, 0 }; /* standard addresses */ | |
135 | struct uba_driver ddndriver = /* device driver info */ | |
136 | { | |
137 | ddnprobe, /* device probe routine */ | |
138 | 0, /* slave probe routine */ | |
139 | ddnattach, /* device attach routine */ | |
140 | 0, /* "dmago" routine */ | |
141 | ddnstd, /* device address */ | |
142 | "ddn", /* device name */ | |
143 | ddninfo /* ptr to device info ptrs */ | |
144 | }; | |
145 | ||
146 | static u_char init_msg[] = | |
147 | { | |
148 | LINE_CNTL, /* set command code */ | |
149 | 0x00, /* not used */ | |
150 | 0x00, /* not used */ | |
151 | 0x00, /* extension length (set at runtime) */ | |
152 | LINK_DISABLE, /* link disable */ | |
153 | /* LINK_LOOPBACK, /* loopback mode */ | |
154 | /* LOOP_INTERNAL, /* = internal loopback */ | |
155 | PKT_SIZE, /* packet size */ | |
156 | 0x80, /* 128 - LSB */ | |
157 | 0x00, /* 128 - MSB */ | |
158 | PKT_WINDOW, /* packet window */ | |
159 | 0x02, /* = 2 */ | |
160 | LINK_ENABLE /* link enable */ | |
161 | }; | |
162 | ||
163 | u_char cb_cmnd[4] = | |
164 | { | |
165 | CALL, | |
166 | 0, | |
167 | 0, | |
168 | 0 | |
169 | }; | |
170 | ||
171 | u_char cb_called_addr[16] = {0}; | |
172 | ||
173 | u_char cb_calling_addr[16] = {0}; | |
174 | ||
175 | u_char cb_facilities[64] = {0}; | |
176 | ||
177 | u_char cb_protocol[5] = {0}; | |
178 | ||
179 | u_char cb_user_data[1] = {0}; | |
180 | ||
181 | #ifdef DDNDEBUG | |
182 | int ddn_debug = 1; /* values 0-8 cause increasing verbosity */ | |
183 | #endif DDNDEBUG | |
184 | \f | |
185 | ||
186 | /***********************************************************************\ | |
187 | * * | |
188 | * Information for each device unit is maintained in an array * | |
189 | * of structures named ddn_softc[]. The array is indexed by * | |
190 | * unit number. Each entry includes the network interface * | |
191 | * structure (ddn_if) used by the routing code to locate the * | |
192 | * interface, an array of Logical Channel control blocks which * | |
193 | * maintain information about each of the Logical Channels (LCNs) * | |
194 | * through which X.25 virtual calls are established, a queue of * | |
195 | * I/O requests pending for the UMC, the UNIBUS interrupt vector * | |
196 | * for the unit and misc flags. The Logical Channel Control * | |
197 | * blocks maintain information about the state of each LCN, * | |
198 | * a queue of outbound data, Half Duplex Channel (HDX) blocks * | |
199 | * used for queuing I/O requests to the UMC and an ifuba * | |
200 | * structure which records the UNIBUS resources being held by * | |
201 | * the LCN. * | |
202 | * * | |
203 | \***********************************************************************/ | |
204 | ||
205 | struct sioq /* Start I/O queue head */ | |
206 | { | |
207 | struct hdx_chan *sq_head; /* queue head */ | |
208 | struct hdx_chan *sq_tail; /* queue tail */ | |
209 | }; | |
210 | ||
211 | struct hdx_chan /* HDX channel block */ | |
212 | { | |
213 | struct hdx_chan *hc_next; /* link to next HDX channel */ | |
214 | u_char hc_chan; /* HDX channel number */ | |
215 | u_char hc_adx; /* address bits 17-16 */ | |
216 | u_short hc_addr; /* address bits 15-00 */ | |
217 | u_short hc_cnt; /* byte count */ | |
218 | u_char hc_func; /* I/O function */ | |
219 | u_char hc_sbfc; /* I/O subfunction */ | |
220 | }; | |
221 | ||
222 | struct ddn_cb /* Logical Channel control block */ | |
223 | { | |
224 | struct in_addr dc_inaddr; /* remote Internet address */ | |
225 | u_char dc_lcn; /* LCN number */ | |
226 | u_char dc_state; /* LCN state */ | |
227 | u_short dc_timer; /* LCN timer */ | |
228 | struct ifqueue dc_oq; /* LCN output queue */ | |
229 | struct hdx_chan dc_rchan; /* LCN read HDX channel */ | |
230 | struct hdx_chan dc_wchan; /* LCN write HDX channel */ | |
231 | struct ifuba dc_ifuba; /* UNIBUS resources */ | |
232 | u_short dc_flags; /* misc flags */ | |
233 | }; | |
234 | ||
235 | struct ddn_softc /* device control structure */ | |
236 | { | |
237 | struct ifnet ddn_if; /* network-visible interface */ | |
238 | struct ddn_cb ddn_cb[NDDNCH+1]; /* Logical Channel cntl blks */ | |
239 | struct sioq ddn_sioq; /* start I/O queue */ | |
240 | int ddn_vector; /* UNIBUS interrupt vector */ | |
241 | u_short ddn_flags; /* misc flags */ | |
e43e0f32 | 242 | struct in_addr ddn_ipaddr; /* local IP address */ |
f0ecc930 MK |
243 | } ddn_softc[NDDN]; |
244 | \f | |
245 | ||
246 | /***********************************************************************\ | |
247 | * ddnprobe() * | |
248 | ************************************************************************* | |
249 | * * | |
250 | * This routine probes the device to obtain the UNIBUS interrupt * | |
251 | * vector. Since the UMC is a soft vector device, we obtain * | |
252 | * an unused vector from the uba structure and return that. * | |
253 | * The UMC is given the vector and the board is reset. * | |
254 | * In order to save the vector in the device info structure, we * | |
255 | * place it in a static temporary where the attach routine can * | |
256 | * find it and save it in the device info structure. This is * | |
257 | * necessary because probe only provides a pointer to the device * | |
258 | * and we have no idea which unit is being referenced. This * | |
259 | * works in 4.2 because the attach routine is called immediately * | |
260 | * after a successful probe. * | |
261 | * * | |
262 | \***********************************************************************/ | |
263 | ||
264 | #define INIT_DELAY (100 * 2) /* time for board initialization */ | |
265 | /* ( in 10 millisecond ticks) */ | |
266 | ||
267 | static int savevec; /* static variable for vector */ | |
268 | ||
269 | ddnprobe(reg) | |
270 | caddr_t reg; | |
271 | { | |
272 | register int br, cvec; /* r11, r10 value-result */ | |
273 | register struct ddnregs *addr = (struct ddnregs *)reg; | |
274 | register int delay_time; | |
275 | ||
276 | #ifdef lint | |
58bca61d | 277 | br = 0; cvec = br; br = cvec; ddnintr(0); |
f0ecc930 MK |
278 | #endif |
279 | ||
280 | cvec = savevec = (uba_hd[numuba].uh_lastiv -= 4); /* return vector */ | |
281 | br = 0x15; /* return bus level */ | |
282 | ||
283 | addr->ioini = 0; /* clear handshake flags */ | |
284 | addr->ionmi = 0; | |
285 | addr->staack = 0; | |
286 | addr->xfrgnt = 0; | |
287 | addr->iovect = cvec >> 2; /* pass vector to UMC */ | |
288 | addr->csr = DDN_RST; /* reset the board */ | |
289 | delay_time = mfpr(TODR) + INIT_DELAY; | |
290 | while(delay_time > mfpr(TODR)) /* wait */ ; | |
291 | ||
292 | return (sizeof(struct ddnregs)); | |
293 | } | |
294 | \f | |
295 | ||
296 | /***********************************************************************\ | |
297 | * ddnattach * | |
298 | ************************************************************************* | |
299 | * * | |
300 | * This routine attaches the device to the network software. * | |
301 | * The network interface structure is filled in. The device * | |
302 | * will be initialized when the system is ready to accept packets. * | |
303 | * * | |
304 | \***********************************************************************/ | |
305 | ||
306 | ddnattach(ui) | |
307 | struct uba_device *ui; | |
308 | { | |
309 | register struct ddn_softc *ds = &ddn_softc[ui->ui_unit]; | |
310 | ||
311 | ds->ddn_vector = savevec; /* save vector from probe() */ | |
312 | ds->ddn_if.if_unit = ui->ui_unit; /* set unit number */ | |
313 | ds->ddn_if.if_name = "ddn"; /* set device name */ | |
314 | ds->ddn_if.if_mtu = DDNMTU; /* set max msg size */ | |
315 | ds->ddn_if.if_init = ddninit; /* set init routine addr */ | |
316 | ds->ddn_if.if_ioctl = ddnioctl; /* set ioctl routine addr */ | |
317 | ds->ddn_if.if_output = ddnoutput; /* set output routine addr */ | |
318 | ds->ddn_if.if_reset = ddnreset; /* set reset routine addr */ | |
319 | ds->ddn_if.if_watchdog = ddntimer; /* set timer routine addr */ | |
e43e0f32 | 320 | if_attach(&ds->ddn_if); |
f0ecc930 MK |
321 | } |
322 | \f | |
323 | ||
324 | /***********************************************************************\ | |
325 | * ddnreset() * | |
326 | ************************************************************************* | |
327 | * * | |
328 | * Reset of interface after UNIBUS reset. * | |
329 | * If interface is on specified uba, reset its state. * | |
330 | * * | |
331 | \***********************************************************************/ | |
332 | ||
333 | ddnreset(unit, uban) | |
334 | int unit, uban; | |
335 | { | |
336 | register struct uba_device *ui; | |
337 | register struct ddnregs *addr; | |
338 | register int delay_time; | |
339 | ||
340 | if (unit >= NDDN || (ui = ddninfo[unit]) == 0 || ui->ui_alive == 0 || | |
341 | ui->ui_ubanum != uban) | |
342 | return; | |
343 | ||
344 | printf(" ddn%d", unit); | |
345 | ||
346 | addr = (struct ddnregs *)ui->ui_addr; | |
347 | addr->ioini = 0; /* clear handshake flags */ | |
348 | addr->ionmi = 0; | |
349 | addr->staack = 0; | |
350 | addr->xfrgnt = 0; | |
351 | addr->iovect = ddn_softc[unit].ddn_vector >> 2; /* pass vector to UMC */ | |
352 | addr->csr = DDN_RST; /* reset the board */ | |
353 | delay_time = mfpr(TODR) + INIT_DELAY; | |
354 | while(delay_time > mfpr(TODR)) /* wait */ ; | |
355 | ||
356 | ddninit(unit); | |
357 | } | |
358 | \f | |
359 | ||
360 | /***********************************************************************\ | |
361 | * ddninit() * | |
362 | ************************************************************************* | |
363 | * * | |
364 | * This routine initializes the interface for operation. The * | |
365 | * device control blocks are initialized, UNIBUS resources are * | |
366 | * allocated and an X.25 initialization message is sent to the * | |
367 | * UMC. * | |
368 | * * | |
369 | \***********************************************************************/ | |
370 | ||
371 | ddninit(unit) | |
372 | int unit; | |
373 | { | |
374 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
375 | register struct ddn_cb *dc; | |
376 | register struct uba_device *ui = ddninfo[unit]; | |
f0ecc930 MK |
377 | int lcn, s; |
378 | ||
379 | #ifdef DDNDEBUG | |
380 | if (ddn_debug > 0) | |
381 | { | |
382 | printf("ddn%d: ddninit()\n", unit); | |
383 | } | |
384 | #endif DDNDEBUG | |
385 | ||
e43e0f32 | 386 | if (ds->ddn_if.if_addrlist == 0) /* if we have no internet addr */ |
f0ecc930 MK |
387 | return; /* don't init yet */ |
388 | ||
389 | dc = ds->ddn_cb; /* setup ptr to first LCN cntl block */ | |
390 | ||
391 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's ... */ | |
392 | { | |
393 | dc->dc_lcn = lcn; /* record LCN */ | |
394 | dc->dc_inaddr.s_addr = 0; /* clear remote internet addr */ | |
395 | dc->dc_state = LC_DOWN; /* init LCN state */ | |
396 | dc->dc_timer = TMO_OFF; /* turn LCN timer off */ | |
397 | ||
398 | /* init LCN output queue */ | |
399 | ||
400 | dc->dc_oq.ifq_head = (struct mbuf *)0; | |
401 | dc->dc_oq.ifq_tail = (struct mbuf *)0; | |
402 | dc->dc_oq.ifq_len = 0; | |
403 | dc->dc_oq.ifq_maxlen = DDN_OQMAX; | |
404 | dc->dc_oq.ifq_drops = 0; | |
405 | ||
406 | /* init HDX channels */ | |
407 | ||
408 | dc->dc_rchan.hc_next = (struct hdx_chan *)0; | |
409 | dc->dc_rchan.hc_chan = lcn * 2; | |
410 | dc->dc_wchan.hc_next = (struct hdx_chan *)0; | |
411 | dc->dc_wchan.hc_chan = (lcn * 2) + 1; | |
412 | ||
413 | /* init UNIBUS resources */ | |
414 | ||
415 | if (if_ubainit(&dc->dc_ifuba, ui->ui_ubanum, | |
416 | 0, (int)btoc(DDNMTU)) == 0) | |
417 | { | |
418 | printf("ddn%d: failed getting UBA resources for lcn %d\n", | |
419 | unit, lcn); | |
420 | ds->ddn_if.if_flags &= ~(IFF_RUNNING | IFF_UP); | |
421 | return; | |
422 | } | |
423 | ||
424 | dc->dc_flags = 0; /* initialize flags */ | |
425 | ||
426 | dc++; /* point at next cntl blk */ | |
427 | } | |
428 | ||
429 | ds->ddn_sioq.sq_head = (struct hdx_chan *)0; | |
430 | ds->ddn_sioq.sq_tail = (struct hdx_chan *)0; | |
431 | ds->ddn_if.if_flags |= IFF_RUNNING; | |
432 | ||
433 | s = splimp(); | |
434 | ||
435 | dc = ds->ddn_cb; /* setup ptr to first LCN cntl block */ | |
436 | ||
437 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* issue reads on all LCNs */ | |
438 | { | |
439 | ddn_iorq(ds, dc, DDNMTU, DDNRDB+DDNSTR); | |
440 | dc++; | |
441 | } | |
442 | ||
443 | x25_init(ds); /* init the X.25 board */ | |
444 | ||
445 | splx(s); | |
446 | ||
447 | ddntimer(unit); /* start timers */ | |
f0ecc930 MK |
448 | } |
449 | \f | |
450 | ||
451 | /***********************************************************************\ | |
452 | * ddnoutput() * | |
453 | ************************************************************************* | |
454 | * * | |
455 | * This routine is called by the network software when it has * | |
456 | * an IP datagram to send out this interface. An attempt is * | |
457 | * made to find a LCN which has a virtual circuit open to the * | |
458 | * indicated host. If an LCN is found the packet is queued for * | |
459 | * output on that LCN. * | |
460 | * * | |
461 | \***********************************************************************/ | |
462 | ||
463 | ddnoutput(ifp, m0, dst) | |
464 | struct ifnet *ifp; | |
465 | struct mbuf *m0; | |
466 | struct sockaddr_in *dst; | |
467 | { | |
468 | register struct mbuf *m = m0; | |
469 | register struct ddn_softc *ds = &ddn_softc[ifp->if_unit]; | |
470 | register struct ddn_cb *dc; | |
471 | register struct ifqueue *oq; | |
472 | int s; | |
473 | ||
474 | if ((ds->ddn_if.if_flags & IFF_UP) == 0) | |
475 | return (ENETDOWN); | |
476 | ||
477 | switch (dst->sin_family) | |
478 | { | |
479 | ||
480 | #ifdef INET | |
481 | case AF_INET: | |
482 | break; | |
483 | #endif INET | |
484 | ||
485 | default: | |
486 | printf("ddn%d: can't handle af%d\n", ifp->if_unit, | |
487 | dst->sin_family); | |
488 | m_freem(m0); | |
489 | return (EAFNOSUPPORT); | |
490 | } | |
491 | ||
492 | ||
493 | #ifdef DDNDEBUG | |
494 | if (ddn_debug > 6) | |
495 | { | |
496 | printf("ddnoutput(): dst = "); | |
497 | prt_addr(dst->sin_addr.s_addr); | |
498 | printf("\n"); | |
499 | } | |
500 | #endif DDNDEBUG | |
501 | ||
502 | s = splimp(); | |
503 | ||
504 | /* try to find an LCN */ | |
505 | ||
506 | if (dc = locate_x25_lcn(ds, dst->sin_addr)) | |
507 | { /* if found */ | |
508 | oq = &(dc->dc_oq); /* point to output queue */ | |
509 | dc->dc_state = LC_DATA_IDLE; | |
510 | dc->dc_timer = TMO_DATA_IDLE; | |
511 | if (IF_QFULL(oq)) /* if q full */ | |
512 | { | |
513 | IF_DROP(oq); /* drop the data */ | |
514 | m_freem(m); | |
515 | splx(s); | |
516 | return (ENOBUFS); | |
517 | } | |
518 | IF_ENQUEUE(oq, m); /* otherwise queue it */ | |
519 | ddn_start(ds, dc); /* and try to output */ | |
520 | splx(s); | |
521 | return (0); | |
522 | } | |
523 | else /* if no circuit available */ | |
524 | { | |
525 | IF_DROP(&ifp->if_snd); /* drop the data */ | |
526 | m_freem(m); | |
527 | splx(s); | |
528 | return (EHOSTUNREACH); | |
529 | } | |
530 | ||
531 | } | |
532 | \f | |
533 | ||
534 | /***********************************************************************\ | |
535 | * ddntimer() * | |
536 | ************************************************************************* | |
537 | * * | |
538 | * This routine is entered once a second to perform timer * | |
539 | * managment. The LCN table is scanned for active timers, * | |
540 | * (nonzero) which are decremented. If a timer expires * | |
541 | * (becomes zero), the proper action is taken. * | |
542 | * * | |
543 | \***********************************************************************/ | |
544 | ||
545 | int ddntimer(unit) | |
546 | int unit; | |
547 | { | |
548 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
549 | register struct ddn_cb *dc; | |
550 | register int s, lcn; | |
551 | ||
552 | #ifdef DDNDEBUG | |
553 | if (ddn_debug > 7) | |
554 | { | |
555 | printf("ddntimer()\n"); | |
556 | } | |
557 | #endif DDNDEBUG | |
558 | ||
559 | ds->ddn_if.if_timer = DDN_TIMEOUT; /* restart timer */ | |
560 | ||
561 | dc = ds->ddn_cb; | |
562 | ||
563 | s = splimp(); | |
564 | ||
565 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* scan all LCN's */ | |
566 | { | |
567 | if (dc->dc_timer && (--(dc->dc_timer) == 0)) | |
568 | { /* if a timer expired */ | |
569 | if (dc->dc_state == LC_RESTART) | |
570 | { /* if a restart was out */ | |
571 | send_restart(ds); /* send another one */ | |
572 | break; | |
573 | } | |
574 | else /* otherwise */ | |
575 | { | |
576 | clear_lcn(ds, dc); /* clear the LCN */ | |
577 | } | |
578 | } | |
579 | dc++; | |
580 | } | |
581 | splx(s); | |
582 | } | |
583 | \f | |
584 | ||
585 | /***********************************************************************\ | |
586 | * ddnioctl() * | |
587 | ************************************************************************* | |
588 | * * | |
589 | * This routine processes device dependent ioctl's. Currently, * | |
590 | * the only ioctl supported is used to set the host's internet * | |
591 | * address for this network interface. * | |
592 | * * | |
593 | \***********************************************************************/ | |
594 | ||
595 | ddnioctl(ifp, cmd, data) | |
e43e0f32 MK |
596 | register struct ifnet *ifp; |
597 | int cmd; | |
598 | caddr_t data; | |
599 | { | |
600 | struct ifaddr *ifa = (struct ifaddr *) data; | |
601 | int s = splimp(), error = 0; | |
e43e0f32 MK |
602 | |
603 | switch (cmd) { | |
604 | ||
605 | case SIOCSIFADDR: | |
4f02060d | 606 | if (ifa->ifa_addr->sa_family != AF_INET) |
e43e0f32 MK |
607 | return(EINVAL); |
608 | ifp->if_flags |= IFF_UP; | |
609 | if ((ifp->if_flags & IFF_RUNNING) == 0) | |
610 | ddninit(ifp->if_unit); | |
611 | ddn_softc[ifp->if_unit].ddn_ipaddr = IA_SIN(ifa)->sin_addr; | |
612 | break; | |
613 | ||
614 | default: | |
615 | error = EINVAL; | |
616 | break; | |
617 | } | |
618 | splx(s); | |
619 | return (error); | |
620 | } | |
f0ecc930 MK |
621 | \f |
622 | ||
623 | /***********************************************************************\ | |
624 | * ddnintr() * | |
625 | ************************************************************************* | |
626 | * * | |
627 | * This is the interrupt handler for UNIBUS interrupts from the * | |
628 | * UMC. The interrupting HDX channel and interrupt type are * | |
629 | * obtained from the completion comm regs. If the interrupt is * | |
630 | * an I/O request acknowledge, the next I/O request is passed * | |
631 | * to the UMC. If the interrupt is an I/O completion, the * | |
632 | * completion is processed depending on whether it is for the * | |
633 | * supervisor or a data channel. * | |
634 | * * | |
635 | \***********************************************************************/ | |
636 | ||
637 | ddnintr(unit) | |
638 | int unit; | |
639 | { | |
640 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
641 | register struct hdx_chan *hc; | |
642 | register struct ddnregs *addr = (struct ddnregs *)ddninfo[unit]->ui_addr; | |
643 | int chan, type, cc, cnt; | |
644 | ||
645 | /* | |
646 | * Check for hardware errors. | |
647 | */ | |
648 | if (addr->csr & DDN_UER) | |
649 | { | |
650 | printf("ddn%d: hard error csr=%b\n", unit, addr->csr, DDN_BITS); | |
651 | addr->csr = 0; /* disable i/f */ | |
652 | return; | |
653 | } | |
654 | ||
655 | /* | |
656 | * Get logical channel info. | |
657 | */ | |
658 | if ((chan = addr->stachn) >= ((NDDNCH+1)*2)) | |
659 | { | |
660 | printf("ddn%d: unknown channel, chan=%d\n", unit, chan); | |
661 | return; | |
662 | } | |
663 | ||
664 | if (chan & 0x01) | |
665 | hc = &(ds->ddn_cb[chan/2].dc_wchan); | |
666 | else | |
667 | hc = &(ds->ddn_cb[chan/2].dc_rchan); | |
668 | ||
669 | type = addr->statyp; | |
670 | cc = addr->stacc; | |
671 | cnt = hc->hc_cnt - addr->stacnt; | |
672 | ||
673 | /* Figure out what kind of interrupt it was */ | |
674 | ||
675 | switch(type) | |
676 | { | |
677 | case DDNSACK: /* start i/o accepted */ | |
678 | if (hc != ds->ddn_sioq.sq_head) /* does ack match waiting req? */ | |
679 | { | |
680 | printf("ddn%d: STARTIO error chan=%d hc=%x sq=%x\n", | |
681 | unit, chan, hc, ds->ddn_sioq.sq_head); | |
682 | addr->csr = 0; /* disable UMC */ | |
683 | return; | |
684 | } | |
685 | ||
686 | /* dequeue old request by copying link to queue head */ | |
687 | /* and start next I/O request if queue has not gone empty */ | |
688 | ||
689 | if (ds->ddn_sioq.sq_head = ds->ddn_sioq.sq_head->hc_next) | |
690 | { | |
691 | start_chn(ds); | |
692 | } | |
693 | break; | |
694 | ||
695 | case DDNDONE: /* i/o completion */ | |
696 | switch (cc) | |
697 | { | |
698 | case DDNIOCABT: /* probably VCN flush */ | |
699 | break; | |
700 | ||
701 | case DDNIOCERR: | |
702 | printf("ddn%d: program error ", unit); | |
703 | goto daterr; | |
704 | ||
705 | case DDNIOCOVR: | |
706 | printf("ddn%d: overrun error ", unit); | |
707 | goto daterr; | |
708 | ||
709 | case DDNIOCUBE: | |
710 | printf("ddn%d: NXM timeout or UB parity error ", unit); | |
711 | ||
712 | daterr: | |
713 | printf("chan=%d func=%x\n", chan, hc->hc_func); | |
714 | if (hc->hc_func & DDNRDB) | |
715 | ds->ddn_if.if_ierrors++; | |
716 | else | |
717 | ds->ddn_if.if_oerrors++; | |
718 | } | |
719 | ||
720 | /* was it supervisor or data traffic? */ | |
721 | ||
722 | if (chan > 1) | |
723 | ddn_data(unit, chan, cc, cnt); | |
724 | else | |
9205905c | 725 | ddn_supr(unit, chan, cc); |
f0ecc930 MK |
726 | |
727 | } | |
728 | ||
729 | /* | |
730 | * Ack the interrupt | |
731 | */ | |
732 | addr->staack = 1; | |
733 | if (!(addr->ionmi)) | |
734 | { | |
735 | addr->ionmi = 1; | |
736 | addr->csr = DDN_DMA|DDN_WRT|DDN_IEN|DDN_NMI; | |
737 | } | |
738 | } | |
739 | \f | |
740 | ||
741 | /***********************************************************************\ | |
742 | * x25_init() * | |
743 | ************************************************************************* | |
744 | * * | |
745 | * This routine builds and sends an X.25 initialization msg * | |
746 | * to the UMC. * | |
747 | * * | |
748 | \***********************************************************************/ | |
749 | ||
750 | static void x25_init(ds) | |
751 | struct ddn_softc *ds; | |
752 | { | |
753 | struct mbuf *m; | |
f0ecc930 MK |
754 | |
755 | #ifdef DDNDEBUG | |
756 | if (ddn_debug > 0) | |
757 | { | |
758 | printf("ddn%d: x25_init()\n", ds->ddn_if.if_unit); | |
759 | } | |
760 | #endif DDNDEBUG | |
761 | ||
762 | MGET(m, M_DONTWAIT, MT_DATA); /* try to get X25 init buffer */ | |
763 | if (m == 0) | |
764 | { | |
765 | printf("ddn%d: couldn't get X25 init buffer\n", ds->ddn_if.if_unit); | |
766 | return; | |
767 | } | |
768 | ||
769 | init_msg[3] = sizeof(init_msg) - 4; /* set cmnd ext length */ | |
770 | ||
8011f5df | 771 | bcopy((caddr_t)init_msg, mtod(m, caddr_t), sizeof(init_msg)); |
f0ecc930 MK |
772 | |
773 | m->m_len = sizeof(init_msg); /* set msg length */ | |
774 | ||
775 | IF_ENQUEUE(&(ds->ddn_cb[0].dc_oq), m); | |
776 | ddn_start(ds, &(ds->ddn_cb[0])); | |
777 | } | |
778 | \f | |
779 | ||
780 | /***********************************************************************\ | |
781 | * locate_x25_lcn() * | |
782 | ************************************************************************* | |
783 | * * | |
784 | * This routine tries to locate an X25 LCN associated with a * | |
785 | * remote internet address. A linear search of the LCN table * | |
786 | * is made for a matching address. If the search succeeds, the * | |
787 | * LCN is returned. If the search fails, the LCN table is * | |
788 | * searched for an unused table entry. If an unused table entry * | |
789 | * is found, an X25 call is generated to the host specified in * | |
790 | * the destination internet address. If no LCN is available, * | |
791 | * zero is returned. * | |
792 | * * | |
793 | \***********************************************************************/ | |
794 | ||
795 | static struct ddn_cb *locate_x25_lcn(ds, ip_addr) | |
796 | struct ddn_softc *ds; | |
797 | struct in_addr ip_addr; | |
798 | { | |
799 | register int lcn; | |
800 | register struct ddn_cb *dc; | |
f0ecc930 MK |
801 | |
802 | #ifdef DDNDEBUG | |
803 | if (ddn_debug > 6) | |
804 | { | |
805 | printf("locate_x25_lcn()\n"); | |
806 | } | |
807 | #endif DDNDEBUG | |
808 | ||
f0ecc930 MK |
809 | dc = &(ds->ddn_cb[1]); |
810 | for(lcn = 1; lcn <= NDDNCH; lcn++) /* scan LCN table for addr match */ | |
811 | { | |
812 | if (dc->dc_inaddr.s_addr == ip_addr.s_addr) /* if found */ | |
813 | return(dc); /* return LCN */ | |
814 | dc++; | |
815 | } | |
816 | ||
817 | dc = &(ds->ddn_cb[1]); | |
818 | for(lcn = 1; lcn <= NDDNCH; lcn++) /* scan LCN table for free entry */ | |
819 | { | |
820 | if (dc->dc_state == LC_IDLE) | |
821 | break; | |
822 | dc++; | |
823 | } | |
824 | ||
825 | if (lcn > NDDNCH) /* if we didn't find a free entry */ | |
826 | return(0); /* return empty handed */ | |
827 | ||
828 | ||
829 | if (convert_ip_addr(ip_addr, cb_called_addr) && make_x25_call(ds, dc)) | |
830 | { /* addr can be converted */ | |
831 | dc->dc_inaddr.s_addr = ip_addr.s_addr; | |
832 | return(dc); /* and return the LCN */ | |
833 | } | |
834 | else | |
835 | { | |
836 | return(0); /* give up */ | |
837 | } | |
838 | } | |
839 | \f | |
840 | ||
841 | /***********************************************************************\ | |
842 | * convert_ip_addr() * | |
843 | ************************************************************************* | |
844 | * * | |
845 | * This routine accepts an internet address and attempts to * | |
846 | * translate to an equivalent X25 address. For DDN this follows * | |
847 | * the guidelines in the DDN X25 interface spec. The resultant * | |
848 | * X25 address is stored in the X25 called addr buffer. The * | |
849 | * routine returns TRUE if successfull, FALSE otherwise. * | |
850 | * * | |
851 | * NOTE: Although IF-11/X25 was designed to accept ASCII coded * | |
852 | * digits for the address fields, we only supply the binary * | |
853 | * values. The front-end only uses the low four bits to extract * | |
854 | * the binary value from the ASCII digits, so this works out. * | |
855 | * * | |
856 | \***********************************************************************/ | |
857 | ||
858 | static boolean convert_ip_addr(ip_addr, x25addr) | |
859 | struct in_addr ip_addr; | |
860 | u_char x25addr[]; | |
861 | { | |
862 | register int temp; | |
e43e0f32 MK |
863 | union { |
864 | struct in_addr ip; | |
865 | struct { /* (assumes Class A network number) */ | |
866 | u_char s_net; | |
867 | u_char s_host; | |
868 | u_char s_lh; | |
869 | u_char s_impno; | |
870 | } imp; | |
871 | } imp_addr; | |
872 | ||
873 | imp_addr.ip = ip_addr; | |
f0ecc930 MK |
874 | x25addr[0] = 14; /* set addr length */ |
875 | ||
876 | x25addr[1] = 0; /* clear DNIC */ | |
877 | x25addr[2] = 0; | |
878 | x25addr[3] = 0; | |
879 | x25addr[4] = 0; | |
880 | ||
e43e0f32 MK |
881 | if (imp_addr.imp.s_host < 64) /* Physical: 0000 0 IIIHH00 [SS] */ |
882 | { /* s_impno -> III, s_host -> HH */ | |
f0ecc930 | 883 | x25addr[5] = 0; /* set flag bit */ |
e43e0f32 MK |
884 | x25addr[6] = imp_addr.imp.s_impno / 100; |
885 | x25addr[7] = (imp_addr.imp.s_impno % 100) / 10; | |
886 | x25addr[8] = imp_addr.imp.s_impno % 10; | |
887 | x25addr[9] = imp_addr.imp.s_host / 10; | |
888 | x25addr[10] = imp_addr.imp.s_host % 10; | |
f0ecc930 MK |
889 | } |
890 | else /* Logical: 0000 1 RRRRR00 [SS] */ | |
891 | { /* s_host * 256 + s_impno -> RRRRR */ | |
e43e0f32 | 892 | temp = (imp_addr.imp.s_host << 8) + imp_addr.imp.s_impno; |
f0ecc930 MK |
893 | x25addr[5] = 1; |
894 | x25addr[6] = temp / 10000; | |
895 | x25addr[7] = (temp % 10000) / 1000; | |
896 | x25addr[8] = (temp % 1000) / 100; | |
897 | x25addr[9] = (temp % 100) / 10; | |
898 | x25addr[10] = temp % 10; | |
899 | } | |
900 | ||
901 | x25addr[11] = 0; /* clear rest of addr */ | |
902 | x25addr[12] = 0; | |
903 | x25addr[13] = 0; | |
904 | x25addr[14] = 0; | |
905 | ||
906 | #ifdef DDNDEBUG | |
907 | if (ddn_debug > 4) | |
908 | { | |
909 | printf("convert_ip_addr(): "); | |
910 | prt_addr(ip_addr); | |
911 | printf(" ==> "); | |
912 | prt_bytes(x25addr, 14); | |
913 | printf("\n"); | |
914 | } | |
915 | #endif DDNDEBUG | |
916 | ||
917 | return(1); | |
918 | } | |
919 | \f | |
920 | ||
921 | /***********************************************************************\ | |
922 | * convert_x25_addr() * | |
923 | ************************************************************************* | |
924 | * * | |
925 | * This routine accepts an X25 address and attempts to translate * | |
926 | * to an equivalent internet address. For DDN this follows the * | |
927 | * guidelines in the DDN X25 interface spec. The resultant * | |
928 | * internet address is returned to the caller. * | |
929 | * * | |
930 | \***********************************************************************/ | |
931 | ||
932 | static int convert_x25_addr(x25addr) | |
933 | u_char x25addr[]; | |
934 | { | |
935 | register int cnt, temp; | |
e43e0f32 MK |
936 | union { |
937 | struct in_addr ip; | |
938 | struct { /* (assumes Class A network number) */ | |
939 | u_char s_net; | |
940 | u_char s_host; | |
941 | u_char s_lh; | |
942 | u_char s_impno; | |
943 | } imp; | |
944 | } imp_addr; | |
f0ecc930 MK |
945 | |
946 | if (((cnt = x25addr[0]) < 12) || (cnt > 14)) | |
947 | { | |
948 | printf("DDN: illegal X25 address length!\n"); | |
949 | return(0); | |
950 | } | |
951 | ||
952 | switch(x25addr[5] & 0x0f) | |
953 | { | |
954 | case 0: /* Physical: 0000 0 IIIHH00 [SS] */ | |
e43e0f32 | 955 | imp_addr.imp.s_impno = |
f0ecc930 MK |
956 | ((int)(x25addr[6] & 0x0f) * 100) + |
957 | ((int)(x25addr[7] & 0x0f) * 10) + | |
958 | ((int)(x25addr[8] & 0x0f)); | |
959 | ||
960 | ||
e43e0f32 | 961 | imp_addr.imp.s_host = |
f0ecc930 MK |
962 | ((int)(x25addr[9] & 0x0f) * 10) + |
963 | ((int)(x25addr[10] & 0x0f)); | |
964 | break; | |
965 | case 1: /* Logical: 0000 1 RRRRR00 [SS] */ | |
966 | temp = ((int)(x25addr[6] & 0x0f) * 10000) | |
967 | + ((int)(x25addr[7] & 0x0f) * 1000) | |
968 | + ((int)(x25addr[8] & 0x0f) * 100) | |
969 | + ((int)(x25addr[9] & 0x0f) * 10) | |
970 | + ((int)(x25addr[10] & 0x0f)); | |
971 | ||
e43e0f32 MK |
972 | imp_addr.imp.s_host = temp >> 8; |
973 | imp_addr.imp.s_impno = temp & 0xff; | |
f0ecc930 MK |
974 | break; |
975 | default: | |
976 | printf("DDN: illegal X25 address format!\n"); | |
977 | return(0); | |
978 | } | |
979 | ||
e43e0f32 MK |
980 | imp_addr.imp.s_lh = 0; |
981 | imp_addr.imp.s_net = 0; | |
f0ecc930 MK |
982 | |
983 | #ifdef DDNDEBUG | |
984 | if (ddn_debug > 4) | |
985 | { | |
986 | printf("convert_x25_addr(): "); | |
987 | prt_bytes(&x25addr[1], cnt); | |
988 | printf(" ==> "); | |
e43e0f32 | 989 | prt_addr(imp_addr.ip); |
f0ecc930 MK |
990 | printf("\n"); |
991 | } | |
992 | #endif DDNDEBUG | |
993 | ||
e43e0f32 | 994 | return(imp_addr.ip.s_addr); |
f0ecc930 MK |
995 | } |
996 | \f | |
997 | ||
998 | /***********************************************************************\ | |
999 | * make_x25_call() * | |
1000 | ************************************************************************* | |
1001 | * * | |
1002 | * This routine places an X25 call using the X25 Call Msg * | |
1003 | * buffer. The calling LCN is placed in the appropriate state * | |
1004 | * and a timer is started. * | |
1005 | * * | |
1006 | \***********************************************************************/ | |
1007 | ||
1008 | static boolean make_x25_call(ds, dc) | |
1009 | register struct ddn_softc *ds; | |
1010 | register struct ddn_cb *dc; | |
1011 | { | |
1012 | register struct mbuf *m_callbfr; | |
8011f5df | 1013 | register caddr_t cb; |
f0ecc930 MK |
1014 | |
1015 | MGET(m_callbfr, M_DONTWAIT, MT_DATA); /* try to get call cmnd buffer */ | |
1016 | if (m_callbfr == 0) | |
1017 | return(0); | |
1018 | ||
8011f5df | 1019 | cb = mtod(m_callbfr, caddr_t); |
f0ecc930 | 1020 | |
58bca61d | 1021 | (void)convert_ip_addr(ds->ddn_ipaddr, cb_calling_addr); |
f0ecc930 MK |
1022 | |
1023 | cb_protocol[0] = 4; | |
1024 | cb_protocol[1] = X25_PROTO_IP; /* protocol = IP */ | |
1025 | cb_protocol[2] = 0; | |
1026 | cb_protocol[3] = 0; | |
1027 | cb_protocol[4] = 0; | |
1028 | ||
1029 | cb_facilities[0] = 4; /* number facility bytes */ | |
1030 | cb_facilities[1] = 0; /* options marker */ | |
1031 | cb_facilities[2] = 0; | |
1032 | cb_facilities[3] = X25_FACIL_DDN; /* DDN standard mode */ | |
1033 | cb_facilities[4] = FAC_DDNSTD; | |
1034 | ||
1035 | cb_user_data[0] = 0; /* no user data */ | |
1036 | ||
1037 | cb_cmnd[0] = CALL; /* set command code */ | |
1038 | cb_cmnd[1] = dc->dc_lcn << 1; /* set channel id */ | |
1039 | cb_cmnd[2] = 0; | |
1040 | cb_cmnd[3] = (cb_called_addr[0] + 1) + /* tally up cmnd ext length */ | |
1041 | (cb_calling_addr[0] + 1) + | |
1042 | (cb_protocol[0] + 1) + | |
1043 | (cb_facilities[0] + 1) + | |
1044 | (cb_user_data[0] + 1); | |
1045 | ||
1046 | m_callbfr->m_len = cb_cmnd[3] + 4; | |
1047 | ||
1048 | /* copy command header */ | |
8011f5df | 1049 | bcopy((caddr_t)cb_cmnd, cb, 4); |
f0ecc930 MK |
1050 | cb += 4; |
1051 | ||
1052 | /* copy called address */ | |
8011f5df | 1053 | bcopy((caddr_t)cb_called_addr, cb, cb_called_addr[0] + 1); |
f0ecc930 MK |
1054 | cb += (cb_called_addr[0] + 1); |
1055 | ||
1056 | /* copy calling address */ | |
8011f5df | 1057 | bcopy((caddr_t)cb_calling_addr, cb, cb_calling_addr[0] + 1); |
f0ecc930 MK |
1058 | cb += (cb_calling_addr[0] + 1); |
1059 | ||
1060 | /* copy protocol */ | |
8011f5df | 1061 | bcopy((caddr_t)cb_protocol, cb, cb_protocol[0] + 1); |
f0ecc930 MK |
1062 | cb += (cb_protocol[0] + 1); |
1063 | ||
1064 | /* copy facilities */ | |
8011f5df | 1065 | bcopy((caddr_t)cb_facilities, cb, cb_facilities[0] + 1); |
f0ecc930 MK |
1066 | cb += (cb_facilities[0] + 1); |
1067 | ||
1068 | /* copy user data */ | |
8011f5df | 1069 | bcopy((caddr_t)cb_user_data, cb, cb_user_data[0] + 1); |
f0ecc930 MK |
1070 | cb += (cb_user_data[0] + 1); |
1071 | ||
1072 | dc->dc_state = LC_CALL_PENDING; /* set state */ | |
1073 | dc->dc_timer = TMO_CALL_PENDING; /* start call timeout */ | |
1074 | ||
1075 | #ifdef DDNDEBUG | |
1076 | if (ddn_debug > 3) | |
1077 | { | |
1078 | printf("make_x25_call(): call_bfr = "); | |
1079 | prt_bytes(mtod(m_callbfr, u_char *), m_callbfr->m_len); | |
1080 | printf("\n"); | |
1081 | } | |
1082 | #endif DDNDEBUG | |
1083 | ||
1084 | IF_ENQUEUE(&(ds->ddn_cb[0].dc_oq), m_callbfr); | |
1085 | ddn_start(ds, &(ds->ddn_cb[0])); | |
1086 | ||
1087 | return(1); | |
1088 | } | |
1089 | \f | |
1090 | ||
1091 | /***********************************************************************\ | |
1092 | * ddn_start() * | |
1093 | ************************************************************************* | |
1094 | * * | |
1095 | * This routine attempts to start output of data queued on a * | |
1096 | * specific LCN. If the LCN was not already busy and data is * | |
1097 | * available for output, the data is copied into the LCN's I/O * | |
1098 | * buffer and an I/O request queued to the UMC. * | |
1099 | * * | |
1100 | \***********************************************************************/ | |
1101 | ||
1102 | static void ddn_start(ds, dc) | |
1103 | register struct ddn_softc *ds; | |
1104 | register struct ddn_cb *dc; | |
1105 | { | |
1106 | register struct mbuf *m; | |
1107 | int len; | |
1108 | ||
1109 | /* | |
1110 | * If output isn't active, attempt to | |
1111 | * start sending a new packet. | |
1112 | */ | |
1113 | ||
1114 | if ((dc->dc_flags & DC_OBUSY) || | |
1115 | (dc->dc_oq.ifq_len == 0) || | |
1116 | ((dc->dc_lcn != 0) && (dc->dc_state != LC_DATA_IDLE))) | |
1117 | { | |
1118 | return; | |
1119 | } | |
1120 | ||
1121 | IF_DEQUEUE(&dc->dc_oq, m); | |
1122 | ||
1123 | len = if_wubaput(&dc->dc_ifuba, m); /* copy data to mapped mem */ | |
1124 | dc->dc_flags |= DC_OBUSY; | |
1125 | ||
1126 | ddn_iorq(ds, dc, len, DDNWRT+DDNEOS); | |
1127 | } | |
1128 | \f | |
1129 | ||
1130 | /***********************************************************************\ | |
1131 | * ddn_iorq() * | |
1132 | ************************************************************************* | |
1133 | * * | |
1134 | * This routine builds UMC I/O requests and queues them for * | |
1135 | * delivery to the UMC. If the UMC I/O request comm regs are * | |
1136 | * not busy, the I/O request is passed to the UMC. * | |
1137 | * * | |
1138 | \***********************************************************************/ | |
1139 | ||
1140 | static void ddn_iorq(ds, dc, len, func) | |
1141 | struct ddn_softc *ds; | |
1142 | struct ddn_cb *dc; | |
1143 | int len, func; | |
1144 | { | |
1145 | register struct hdx_chan *hc; | |
1146 | register int info; | |
1147 | ||
1148 | ||
1149 | /* get appropriate UNIBUS mapping info */ | |
1150 | ||
1151 | if (func & DDNRDB) /* read or write? */ | |
1152 | { | |
1153 | hc = &dc->dc_rchan; | |
1154 | info = dc->dc_ifuba.ifu_r.ifrw_info; | |
1155 | } | |
1156 | else | |
1157 | { | |
1158 | hc = &dc->dc_wchan; | |
1159 | info = dc->dc_ifuba.ifu_w.ifrw_info; | |
1160 | } | |
1161 | ||
1162 | /* set channel info */ | |
1163 | ||
1164 | hc->hc_adx = (u_char)((info & 0x30000) >> 12); | |
1165 | hc->hc_addr = (u_short)(info & 0xffff); | |
1166 | hc->hc_cnt = len; | |
1167 | hc->hc_func = (u_char)func; | |
1168 | hc->hc_sbfc = 0; | |
1169 | ||
1170 | /* | |
1171 | * If UMC comm regs busy, queue start i/o for later. | |
1172 | */ | |
1173 | if (ds->ddn_sioq.sq_head) | |
1174 | { | |
1175 | (ds->ddn_sioq.sq_tail)->hc_next = hc; | |
1176 | ds->ddn_sioq.sq_tail = hc; | |
1177 | hc->hc_next = 0; | |
1178 | return; | |
1179 | } | |
1180 | ||
1181 | /* start i/o on channel now */ | |
1182 | ||
1183 | ds->ddn_sioq.sq_head = hc; | |
1184 | ds->ddn_sioq.sq_tail = hc; | |
1185 | hc->hc_next = 0; | |
1186 | start_chn(ds); | |
1187 | } | |
1188 | \f | |
1189 | ||
1190 | /***********************************************************************\ | |
1191 | * start_chn() * | |
1192 | ************************************************************************* | |
1193 | * * | |
1194 | * This routine copies UMC I/O requests into the UMC comm regs * | |
1195 | * and notifies the UMC. * | |
1196 | * * | |
1197 | \***********************************************************************/ | |
1198 | ||
1199 | static void start_chn(ds) | |
1200 | struct ddn_softc *ds; | |
1201 | { | |
1202 | register struct hdx_chan *hc = ds->ddn_sioq.sq_head; | |
1203 | register struct ddnregs *addr = | |
1204 | (struct ddnregs *)ddninfo[ds->ddn_if.if_unit]->ui_addr; | |
1205 | ||
1206 | /* | |
1207 | * Set up comm regs. | |
1208 | */ | |
1209 | addr->iochn = hc->hc_chan; | |
1210 | addr->ioadx = hc->hc_adx; | |
1211 | addr->ioadl = hc->hc_addr; | |
1212 | addr->iocnt = hc->hc_cnt; | |
1213 | addr->iofcn = hc->hc_func; | |
1214 | addr->iosbf = hc->hc_sbfc; | |
1215 | addr->ioini = 1; | |
1216 | ||
1217 | /* signal UMC if necessary */ | |
1218 | ||
1219 | if (!(addr->ionmi)) | |
1220 | { | |
1221 | addr->ionmi = 1; | |
1222 | addr->csr = DDN_DMA|DDN_WRT|DDN_IEN|DDN_NMI; | |
1223 | } | |
1224 | } | |
1225 | \f | |
1226 | ||
1227 | /***********************************************************************\ | |
1228 | * ddn_data() * | |
1229 | ************************************************************************* | |
1230 | * * | |
1231 | * This routine is called when a data channel I/O completes. * | |
1232 | * If the completion was for a write, an attempt is made to * | |
1233 | * start output on the next packet waiting for output on that * | |
1234 | * LCN. If the completion was for a read, the received packet * | |
1235 | * is sent to the IP input queue (if no error) and another read * | |
1236 | * is started on the LCN. * | |
1237 | * * | |
1238 | \***********************************************************************/ | |
1239 | ||
1240 | static void ddn_data(unit, chan, cc, rcnt) | |
1241 | int unit, chan, cc, rcnt; | |
1242 | { | |
1243 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
1244 | register struct ddn_cb *dc = &(ds->ddn_cb[chan/2]); | |
1245 | register struct ifqueue *inq = &ipintrq; | |
1246 | register struct mbuf *m; | |
1247 | ||
1248 | if (chan & 0x01) /* was it read or write? */ | |
1249 | { /* write, fire up next output */ | |
1250 | ds->ddn_if.if_opackets++; | |
1251 | dc->dc_flags &= ~DC_OBUSY; | |
1252 | ddn_start(ds, dc); | |
1253 | } | |
1254 | else /* read, process rcvd packet */ | |
1255 | { | |
1256 | if (cc == DDNIOCOK) | |
1257 | { /* Queue good packet for input */ | |
1258 | ds->ddn_if.if_ipackets++; | |
1259 | dc->dc_state = LC_DATA_IDLE; | |
1260 | dc->dc_timer = TMO_DATA_IDLE; | |
e43e0f32 | 1261 | m = if_rubaget(&(dc->dc_ifuba), rcnt, 0, &ds->ddn_if); |
f0ecc930 MK |
1262 | if (m) |
1263 | { | |
1264 | if (IF_QFULL(inq)) | |
1265 | { | |
1266 | IF_DROP(inq); | |
1267 | m_freem(m); | |
1268 | } | |
1269 | else | |
1270 | { | |
1271 | IF_ENQUEUE(inq, m); | |
1272 | schednetisr(NETISR_IP); | |
1273 | } | |
1274 | } | |
1275 | } | |
1276 | ||
1277 | /* hang a new data read */ | |
1278 | ||
1279 | ddn_iorq(ds, dc, DDNMTU, DDNRDB+DDNSTR); | |
1280 | ||
1281 | } | |
1282 | } | |
1283 | \f | |
1284 | ||
1285 | /***********************************************************************\ | |
1286 | * ddn_supr() * | |
1287 | ************************************************************************* | |
1288 | * * | |
1289 | * This routine is called when a supervisor I/O completes. * | |
1290 | * If the completion was for a write, an attempt is made to * | |
1291 | * start output on the next supervisor command waiting for * | |
1292 | * output. If the completion was for a read, the received * | |
1293 | * supervisor message is processed and another read is started. * | |
1294 | * * | |
1295 | \***********************************************************************/ | |
1296 | ||
9205905c MK |
1297 | static void ddn_supr(unit, chan, cc) |
1298 | int unit, chan, cc; | |
f0ecc930 MK |
1299 | { |
1300 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
1301 | u_char *p; | |
1302 | ||
1303 | /* was it read or write? */ | |
1304 | ||
1305 | if (chan & 0x01) | |
1306 | { | |
1307 | ds->ddn_cb[0].dc_flags &= ~DC_OBUSY; | |
1308 | ddn_start(ds, &(ds->ddn_cb[0])); | |
1309 | } | |
1310 | else | |
1311 | { | |
1312 | if (cc == DDNIOCOK) | |
1313 | { | |
1314 | p = (u_char *)(ds->ddn_cb[0].dc_ifuba.ifu_r.ifrw_addr); | |
1315 | ||
1316 | /* process supervisor message */ | |
1317 | ||
1318 | supr_msg(ds, p); | |
1319 | ||
1320 | } | |
1321 | ||
1322 | /* hang a new supr read */ | |
1323 | ||
1324 | ddn_iorq(ds, &(ds->ddn_cb[0]), DDNMTU, DDNRDB+DDNSTR); | |
1325 | } | |
1326 | } | |
1327 | \f | |
1328 | ||
1329 | /***********************************************************************\ | |
1330 | * supr_msg() * | |
1331 | ************************************************************************* | |
1332 | * * | |
1333 | * This routine processes received supervisor messages. * | |
1334 | * Depending on the message type, the appropriate action is * | |
1335 | * taken. | |
1336 | * * | |
1337 | \***********************************************************************/ | |
1338 | ||
1339 | static void supr_msg(ds, p) | |
1340 | struct ddn_softc *ds; | |
1341 | u_char p[]; | |
1342 | { | |
1343 | register struct ddn_cb *dc; | |
1344 | register int lcn; | |
1345 | register struct mbuf *m; | |
1346 | ||
1347 | #ifdef DDNDEBUG | |
1348 | if (ddn_debug > 5) | |
1349 | { | |
1350 | printf("supr_msg(): "); | |
1351 | prt_bytes(p, 4+p[3]); | |
1352 | printf("\n"); | |
1353 | } | |
1354 | #endif DDNDEBUG | |
1355 | ||
1356 | switch (p[0]) | |
1357 | { | |
1358 | case LINE_STATUS: /* link status msg */ | |
1359 | if (p[2] == LINK_UP) /* if link came up */ | |
1360 | { | |
1361 | send_restart(ds); /* send restart msg */ | |
1362 | } | |
1363 | else /* if link went down */ | |
1364 | { | |
1365 | ds->ddn_if.if_flags &= ~IFF_UP; | |
1366 | dc = ds->ddn_cb; | |
1367 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's */ | |
1368 | { | |
1369 | dc->dc_state = LC_DOWN; /* set state */ | |
1370 | dc->dc_timer = TMO_OFF; /* stop timer */ | |
1371 | dc++; | |
1372 | } | |
1373 | } | |
1374 | break; | |
1375 | ||
1376 | case RESTART: /* restart received */ | |
1377 | if (ds->ddn_cb[0].dc_state != LC_RESTART) /* if not restarting */ | |
1378 | send_supr(ds, RSTRT_ACK, 0, 0); /* send restart ack */ | |
1379 | /* fall thru */ | |
1380 | case RSTRT_ACK: /* restart ack */ | |
1381 | ds->ddn_if.if_flags |= IFF_UP; | |
1382 | dc = ds->ddn_cb; | |
1383 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's */ | |
1384 | { | |
1385 | dc->dc_state = LC_IDLE; /* set state */ | |
1386 | dc->dc_timer = TMO_OFF; /* stop timer */ | |
1387 | dc->dc_inaddr.s_addr = 0; /* forget address */ | |
1388 | while (dc->dc_oq.ifq_len) /* drop pending data */ | |
1389 | { | |
1390 | IF_DEQUEUE(&dc->dc_oq, m); | |
1391 | m_freem(m); | |
1392 | } | |
1393 | dc++; | |
1394 | } | |
1395 | break; | |
1396 | ||
1397 | case ANSWER: /* call answered */ | |
1398 | lcn = p[1] / 2; | |
1399 | dc = &(ds->ddn_cb[lcn]); | |
1400 | if (dc->dc_state == LC_CALL_PENDING) /* if a call pending */ | |
1401 | { | |
1402 | dc->dc_state = LC_DATA_IDLE; /* set state */ | |
1403 | dc->dc_timer = TMO_DATA_IDLE; /* start timer */ | |
1404 | ddn_start(ds, dc); /* try to send data */ | |
1405 | } | |
1406 | break; | |
1407 | ||
1408 | case RING: /* incoming call */ | |
1409 | for(lcn = NDDNCH; lcn > 0; lcn--) /* search LCN's */ | |
1410 | { | |
1411 | if (ds->ddn_cb[lcn].dc_state == LC_IDLE) /* unused? */ | |
1412 | break; | |
1413 | } | |
1414 | ||
1415 | if (lcn && decode_ring(p)) /* if a free LCN found */ | |
1416 | /* and ring looks ok */ | |
1417 | { | |
1418 | dc = &(ds->ddn_cb[lcn]); | |
1419 | dc->dc_inaddr.s_addr = convert_x25_addr(cb_calling_addr); | |
1420 | dc->dc_state = LC_DATA_IDLE; /* set state */ | |
1421 | dc->dc_timer = TMO_DATA_IDLE; /* start timer */ | |
58bca61d | 1422 | send_supr(ds, ANSWER, lcn * 2, (int)p[2]); /* send answer */ |
f0ecc930 MK |
1423 | } |
1424 | else /* if no free LCN's */ | |
1425 | { | |
58bca61d | 1426 | send_supr(ds, CLEARVC, (int)p[2], 0); /* clear call */ |
f0ecc930 MK |
1427 | } |
1428 | break; | |
1429 | ||
1430 | case CLEARLC: /* clear by LCN */ | |
1431 | lcn = p[1] / 2; /* get LCN */ | |
1432 | dc = &(ds->ddn_cb[lcn]); | |
1433 | if (dc->dc_state != LC_CLR_PENDING) /* if no clear pending */ | |
1434 | { | |
58bca61d | 1435 | send_supr(ds, CLEARLC, (int)p[1], 0); /* ack the clear */ |
f0ecc930 MK |
1436 | } |
1437 | dc->dc_state = LC_IDLE; /* set state */ | |
1438 | dc->dc_timer = TMO_OFF; /* stop timer */ | |
1439 | dc->dc_inaddr.s_addr = 0; /* forget address */ | |
1440 | while (dc->dc_oq.ifq_len) /* drop pending data */ | |
1441 | { | |
1442 | IF_DEQUEUE(&dc->dc_oq, m); | |
1443 | m_freem(m); | |
1444 | } | |
1445 | break; | |
1446 | ||
1447 | case CLEARVC: /* clear by VCN */ | |
58bca61d | 1448 | send_supr(ds, CLEARVC, (int)p[1], 0); /* send clear ack */ |
f0ecc930 MK |
1449 | break; |
1450 | ||
1451 | case RESET: /* X25 reset */ | |
58bca61d | 1452 | send_supr(ds, RESET_ACK, (int)p[1], 0); /* send reset ack */ |
f0ecc930 MK |
1453 | printf("X25 RESET on lcn = %d\n", p[1] / 2); /* log it */ |
1454 | break; | |
1455 | ||
1456 | case INTERRUPT: /* X25 interrupt */ | |
1457 | printf("X25 INTERRUPT on lcn = %d, code = %d\n", /* log it */ | |
1458 | p[1] / 2, p[2]); | |
1459 | break; | |
1460 | ||
1461 | default: | |
1462 | printf("ddn%d: supervisor error, code=%x\n", | |
1463 | ds->ddn_if.if_unit, p[0]); | |
1464 | } | |
1465 | } | |
1466 | \f | |
1467 | ||
1468 | /***********************************************************************\ | |
1469 | * decode_ring() * | |
1470 | ************************************************************************* | |
1471 | * * | |
1472 | * This routine parses and validates the incoming call msg. * | |
1473 | * * | |
1474 | \***********************************************************************/ | |
1475 | ||
1476 | static boolean decode_ring(p) | |
1477 | register u_char *p; | |
1478 | { | |
1479 | register int cnt; | |
1480 | ||
1481 | #ifdef DDNDEBUG | |
1482 | if (ddn_debug > 3) | |
1483 | { | |
1484 | printf("decode_ring()\n"); | |
1485 | } | |
1486 | #endif DDNDEBUG | |
1487 | ||
1488 | ||
1489 | p += 3; /* skip to cmnd ext length */ | |
1490 | if (*p++ < 5) /* is count appropriate */ | |
1491 | return(0); /* return false if not */ | |
1492 | ||
1493 | /* called address */ | |
1494 | if ((cnt = *p + 1) > 16) /* is called addr len legal? */ | |
1495 | return(0); /* return false if not */ | |
58bca61d | 1496 | bcopy((caddr_t)p, (caddr_t)cb_called_addr, (unsigned)cnt); /* copy field */ |
f0ecc930 MK |
1497 | p += cnt; |
1498 | ||
1499 | /* calling address */ | |
1500 | if ((cnt = *p + 1) > 16) /* is calling addr len legal? */ | |
1501 | return(0); /* return false if not */ | |
58bca61d | 1502 | bcopy((caddr_t)p, (caddr_t)cb_calling_addr, (unsigned)cnt); /* copy field */ |
f0ecc930 MK |
1503 | p += cnt; |
1504 | ||
1505 | /* protocol part of user data */ | |
1506 | if ((cnt = *p + 1) > 5) /* is protocol len legal? */ | |
1507 | return(0); /* return false if not */ | |
58bca61d | 1508 | bcopy((caddr_t)p, (caddr_t)cb_protocol, (unsigned)cnt); /* copy field */ |
f0ecc930 MK |
1509 | p += cnt; |
1510 | ||
1511 | /* facilities */ | |
1512 | if ((cnt = *p + 1) > 64) /* is facilities len legal? */ | |
1513 | return(0); /* return false if not */ | |
58bca61d | 1514 | bcopy((caddr_t)p, (caddr_t)cb_facilities, (unsigned)cnt); /* copy field */ |
f0ecc930 MK |
1515 | p += cnt; |
1516 | ||
1517 | /* ignore rest of user data for now */ | |
1518 | ||
1519 | if ((cb_protocol[0] == 0) || (cb_protocol[1] != X25_PROTO_IP)) | |
1520 | return(0); /* bad if not IP */ | |
1521 | ||
1522 | return(1); /* looks ok */ | |
1523 | } | |
1524 | \f | |
1525 | ||
1526 | /***********************************************************************\ | |
1527 | * clear_lcn() * | |
1528 | ************************************************************************* | |
1529 | * * | |
1530 | * This routine clears an X25 circuit and releases any buffers * | |
1531 | * queued for transmission. * | |
1532 | * * | |
1533 | \***********************************************************************/ | |
1534 | ||
1535 | static void clear_lcn(ds, dc) | |
1536 | struct ddn_softc *ds; | |
1537 | struct ddn_cb *dc; | |
1538 | { | |
1539 | register struct mbuf *m; | |
1540 | ||
1541 | #ifdef DDNDEBUG | |
1542 | if (ddn_debug > 3) | |
1543 | { | |
1544 | printf("clear_lcn(%d)\n", dc->dc_lcn); | |
1545 | } | |
1546 | #endif DDNDEBUG | |
1547 | ||
1548 | dc->dc_state = LC_CLR_PENDING; /* set state */ | |
1549 | dc->dc_timer = TMO_CLR_PENDING; /* start clear timer */ | |
1550 | dc->dc_inaddr.s_addr = 0; /* clear associated address */ | |
1551 | while (dc->dc_oq.ifq_len) /* drop any pending data */ | |
1552 | { | |
1553 | IF_DEQUEUE(&dc->dc_oq, m); | |
1554 | m_freem(m); | |
1555 | } | |
58bca61d | 1556 | send_supr(ds, CLEARLC, (int)dc->dc_lcn * 2, 0); /* send clear msg */ |
f0ecc930 MK |
1557 | } |
1558 | \f | |
1559 | ||
1560 | /***********************************************************************\ | |
1561 | * send_restart() * | |
1562 | ************************************************************************* | |
1563 | * * | |
1564 | * This routine marks all LCNs as being in a restarting state * | |
1565 | * and sends a restart command to X25. * | |
1566 | * * | |
1567 | \***********************************************************************/ | |
1568 | ||
1569 | static void send_restart(ds) | |
1570 | struct ddn_softc *ds; | |
1571 | { | |
1572 | register struct ddn_cb *dc; | |
1573 | register int lcn; | |
1574 | struct mbuf *m; | |
1575 | ||
1576 | #ifdef DDNDEBUG | |
1577 | if (ddn_debug > 1) | |
1578 | { | |
1579 | printf("send_restart()\n"); | |
1580 | } | |
1581 | #endif DDNDEBUG | |
1582 | dc = ds->ddn_cb; | |
1583 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's */ | |
1584 | { | |
1585 | dc->dc_state = LC_RESTART; /* set state */ | |
1586 | dc->dc_timer = TMO_RESTART; /* start restart timeout */ | |
1587 | dc->dc_inaddr.s_addr = 0; /* forget address */ | |
1588 | while (dc->dc_oq.ifq_len) /* drop any pending data */ | |
1589 | { | |
1590 | IF_DEQUEUE(&dc->dc_oq, m); | |
1591 | m_freem(m); | |
1592 | } | |
1593 | dc++; | |
1594 | } | |
1595 | ||
1596 | send_supr(ds, RESTART, 0, 0); /* send restart msg */ | |
1597 | } | |
1598 | \f | |
1599 | ||
1600 | /***********************************************************************\ | |
1601 | * send_supr() * | |
1602 | ************************************************************************* | |
1603 | * * | |
1604 | * This routine is used to send short (4 bytes only) supervisor * | |
1605 | * commands. * | |
1606 | * * | |
1607 | \***********************************************************************/ | |
1608 | ||
1609 | static void send_supr(ds, cmd, p1, p2) | |
1610 | struct ddn_softc *ds; | |
1611 | int cmd, p1, p2; | |
1612 | { | |
1613 | struct mbuf *m; | |
1614 | register u_char *cp; | |
1615 | ||
1616 | #ifdef DDNDEBUG | |
1617 | if (ddn_debug > 6) | |
1618 | { | |
1619 | printf("send_supr(): %x %x %x\n", cmd, p1, p2); | |
1620 | } | |
1621 | #endif DDNDEBUG | |
1622 | ||
1623 | MGET(m, M_DONTWAIT, MT_DATA); | |
1624 | ||
1625 | if (m == 0) | |
1626 | { | |
1627 | printf("ddn%d: failed to get supr msg bfr!\n", ds->ddn_if.if_unit); | |
1628 | return; | |
1629 | } | |
1630 | ||
1631 | cp = mtod(m, u_char *); | |
1632 | ||
1633 | /* build supervisor message */ | |
1634 | ||
1635 | *cp++ = (byte)cmd; | |
1636 | *cp++ = (byte)p1; | |
1637 | *cp++ = (byte)p2; | |
1638 | *cp++ = 0; | |
1639 | ||
1640 | m->m_len = 4; | |
1641 | ||
1642 | IF_ENQUEUE(&(ds->ddn_cb[0].dc_oq), m); | |
1643 | ddn_start(ds, &(ds->ddn_cb[0])); | |
1644 | ||
1645 | } | |
1646 | \f | |
1647 | ||
1648 | #ifdef DDNDEBUG | |
1649 | ||
1650 | /***********************************************************************\ | |
1651 | * prt_addr() * | |
1652 | ************************************************************************* | |
1653 | * * | |
1654 | * This routine is used to print internet addresses in the * | |
1655 | * standard A.B.C.D format. * | |
1656 | * * | |
1657 | \***********************************************************************/ | |
1658 | ||
1659 | static void prt_addr(addr) | |
1660 | struct in_addr addr; | |
1661 | { | |
1662 | printf("%d.%d.%d.%d", addr.s_net, addr.s_host, addr.s_lh, addr.s_impno); | |
1663 | } | |
1664 | ||
1665 | /***********************************************************************\ | |
1666 | * prt_bytes() * | |
1667 | ************************************************************************* | |
1668 | * * | |
1669 | * This routine is used to print a string of bytes in hex. * | |
1670 | * * | |
1671 | \***********************************************************************/ | |
1672 | ||
1673 | static void prt_bytes(bp, cnt) | |
1674 | u_char *bp; | |
1675 | int cnt; | |
1676 | { | |
1677 | while(cnt--) | |
1678 | { | |
1679 | printf(" %x", *bp++ & 0xff); | |
1680 | } | |
1681 | } | |
1682 | ||
1683 | #endif DDNDEBUG | |
1684 | ||
1685 | #endif NDDN |