Commit | Line | Data |
---|---|---|
10c3308f | 1 | /* @(#)if_ddn.c 6.3 (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 | |
277 | br = 0; cvec = br; br = cvec; | |
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; | |
602 | struct endevice *enaddr; | |
603 | ||
604 | switch (cmd) { | |
605 | ||
606 | case SIOCSIFADDR: | |
607 | if (ifa->ifa_addr.sa_family != AF_INET) | |
608 | return(EINVAL); | |
609 | ifp->if_flags |= IFF_UP; | |
610 | if ((ifp->if_flags & IFF_RUNNING) == 0) | |
611 | ddninit(ifp->if_unit); | |
612 | ddn_softc[ifp->if_unit].ddn_ipaddr = IA_SIN(ifa)->sin_addr; | |
613 | break; | |
614 | ||
615 | default: | |
616 | error = EINVAL; | |
617 | break; | |
618 | } | |
619 | splx(s); | |
620 | return (error); | |
621 | } | |
f0ecc930 MK |
622 | \f |
623 | ||
624 | /***********************************************************************\ | |
625 | * ddnintr() * | |
626 | ************************************************************************* | |
627 | * * | |
628 | * This is the interrupt handler for UNIBUS interrupts from the * | |
629 | * UMC. The interrupting HDX channel and interrupt type are * | |
630 | * obtained from the completion comm regs. If the interrupt is * | |
631 | * an I/O request acknowledge, the next I/O request is passed * | |
632 | * to the UMC. If the interrupt is an I/O completion, the * | |
633 | * completion is processed depending on whether it is for the * | |
634 | * supervisor or a data channel. * | |
635 | * * | |
636 | \***********************************************************************/ | |
637 | ||
638 | ddnintr(unit) | |
639 | int unit; | |
640 | { | |
641 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
642 | register struct hdx_chan *hc; | |
643 | register struct ddnregs *addr = (struct ddnregs *)ddninfo[unit]->ui_addr; | |
644 | int chan, type, cc, cnt; | |
645 | ||
646 | /* | |
647 | * Check for hardware errors. | |
648 | */ | |
649 | if (addr->csr & DDN_UER) | |
650 | { | |
651 | printf("ddn%d: hard error csr=%b\n", unit, addr->csr, DDN_BITS); | |
652 | addr->csr = 0; /* disable i/f */ | |
653 | return; | |
654 | } | |
655 | ||
656 | /* | |
657 | * Get logical channel info. | |
658 | */ | |
659 | if ((chan = addr->stachn) >= ((NDDNCH+1)*2)) | |
660 | { | |
661 | printf("ddn%d: unknown channel, chan=%d\n", unit, chan); | |
662 | return; | |
663 | } | |
664 | ||
665 | if (chan & 0x01) | |
666 | hc = &(ds->ddn_cb[chan/2].dc_wchan); | |
667 | else | |
668 | hc = &(ds->ddn_cb[chan/2].dc_rchan); | |
669 | ||
670 | type = addr->statyp; | |
671 | cc = addr->stacc; | |
672 | cnt = hc->hc_cnt - addr->stacnt; | |
673 | ||
674 | /* Figure out what kind of interrupt it was */ | |
675 | ||
676 | switch(type) | |
677 | { | |
678 | case DDNSACK: /* start i/o accepted */ | |
679 | if (hc != ds->ddn_sioq.sq_head) /* does ack match waiting req? */ | |
680 | { | |
681 | printf("ddn%d: STARTIO error chan=%d hc=%x sq=%x\n", | |
682 | unit, chan, hc, ds->ddn_sioq.sq_head); | |
683 | addr->csr = 0; /* disable UMC */ | |
684 | return; | |
685 | } | |
686 | ||
687 | /* dequeue old request by copying link to queue head */ | |
688 | /* and start next I/O request if queue has not gone empty */ | |
689 | ||
690 | if (ds->ddn_sioq.sq_head = ds->ddn_sioq.sq_head->hc_next) | |
691 | { | |
692 | start_chn(ds); | |
693 | } | |
694 | break; | |
695 | ||
696 | case DDNDONE: /* i/o completion */ | |
697 | switch (cc) | |
698 | { | |
699 | case DDNIOCABT: /* probably VCN flush */ | |
700 | break; | |
701 | ||
702 | case DDNIOCERR: | |
703 | printf("ddn%d: program error ", unit); | |
704 | goto daterr; | |
705 | ||
706 | case DDNIOCOVR: | |
707 | printf("ddn%d: overrun error ", unit); | |
708 | goto daterr; | |
709 | ||
710 | case DDNIOCUBE: | |
711 | printf("ddn%d: NXM timeout or UB parity error ", unit); | |
712 | ||
713 | daterr: | |
714 | printf("chan=%d func=%x\n", chan, hc->hc_func); | |
715 | if (hc->hc_func & DDNRDB) | |
716 | ds->ddn_if.if_ierrors++; | |
717 | else | |
718 | ds->ddn_if.if_oerrors++; | |
719 | } | |
720 | ||
721 | /* was it supervisor or data traffic? */ | |
722 | ||
723 | if (chan > 1) | |
724 | ddn_data(unit, chan, cc, cnt); | |
725 | else | |
726 | ddn_supr(unit, chan, cc, cnt); | |
727 | ||
728 | } | |
729 | ||
730 | /* | |
731 | * Ack the interrupt | |
732 | */ | |
733 | addr->staack = 1; | |
734 | if (!(addr->ionmi)) | |
735 | { | |
736 | addr->ionmi = 1; | |
737 | addr->csr = DDN_DMA|DDN_WRT|DDN_IEN|DDN_NMI; | |
738 | } | |
739 | } | |
740 | \f | |
741 | ||
742 | /***********************************************************************\ | |
743 | * x25_init() * | |
744 | ************************************************************************* | |
745 | * * | |
746 | * This routine builds and sends an X.25 initialization msg * | |
747 | * to the UMC. * | |
748 | * * | |
749 | \***********************************************************************/ | |
750 | ||
751 | static void x25_init(ds) | |
752 | struct ddn_softc *ds; | |
753 | { | |
754 | struct mbuf *m; | |
755 | register u_char *bp; | |
756 | ||
757 | #ifdef DDNDEBUG | |
758 | if (ddn_debug > 0) | |
759 | { | |
760 | printf("ddn%d: x25_init()\n", ds->ddn_if.if_unit); | |
761 | } | |
762 | #endif DDNDEBUG | |
763 | ||
764 | MGET(m, M_DONTWAIT, MT_DATA); /* try to get X25 init buffer */ | |
765 | if (m == 0) | |
766 | { | |
767 | printf("ddn%d: couldn't get X25 init buffer\n", ds->ddn_if.if_unit); | |
768 | return; | |
769 | } | |
770 | ||
771 | init_msg[3] = sizeof(init_msg) - 4; /* set cmnd ext length */ | |
772 | ||
773 | bcopy(init_msg, mtod(m, u_char *), sizeof(init_msg)); | |
774 | ||
775 | m->m_len = sizeof(init_msg); /* set msg length */ | |
776 | ||
777 | IF_ENQUEUE(&(ds->ddn_cb[0].dc_oq), m); | |
778 | ddn_start(ds, &(ds->ddn_cb[0])); | |
779 | } | |
780 | \f | |
781 | ||
782 | /***********************************************************************\ | |
783 | * locate_x25_lcn() * | |
784 | ************************************************************************* | |
785 | * * | |
786 | * This routine tries to locate an X25 LCN associated with a * | |
787 | * remote internet address. A linear search of the LCN table * | |
788 | * is made for a matching address. If the search succeeds, the * | |
789 | * LCN is returned. If the search fails, the LCN table is * | |
790 | * searched for an unused table entry. If an unused table entry * | |
791 | * is found, an X25 call is generated to the host specified in * | |
792 | * the destination internet address. If no LCN is available, * | |
793 | * zero is returned. * | |
794 | * * | |
795 | \***********************************************************************/ | |
796 | ||
797 | static struct ddn_cb *locate_x25_lcn(ds, ip_addr) | |
798 | struct ddn_softc *ds; | |
799 | struct in_addr ip_addr; | |
800 | { | |
801 | register int lcn; | |
802 | register struct ddn_cb *dc; | |
803 | struct mbuf *m_callbfr; | |
804 | ||
805 | #ifdef DDNDEBUG | |
806 | if (ddn_debug > 6) | |
807 | { | |
808 | printf("locate_x25_lcn()\n"); | |
809 | } | |
810 | #endif DDNDEBUG | |
811 | ||
f0ecc930 MK |
812 | dc = &(ds->ddn_cb[1]); |
813 | for(lcn = 1; lcn <= NDDNCH; lcn++) /* scan LCN table for addr match */ | |
814 | { | |
815 | if (dc->dc_inaddr.s_addr == ip_addr.s_addr) /* if found */ | |
816 | return(dc); /* return LCN */ | |
817 | dc++; | |
818 | } | |
819 | ||
820 | dc = &(ds->ddn_cb[1]); | |
821 | for(lcn = 1; lcn <= NDDNCH; lcn++) /* scan LCN table for free entry */ | |
822 | { | |
823 | if (dc->dc_state == LC_IDLE) | |
824 | break; | |
825 | dc++; | |
826 | } | |
827 | ||
828 | if (lcn > NDDNCH) /* if we didn't find a free entry */ | |
829 | return(0); /* return empty handed */ | |
830 | ||
831 | ||
832 | if (convert_ip_addr(ip_addr, cb_called_addr) && make_x25_call(ds, dc)) | |
833 | { /* addr can be converted */ | |
834 | dc->dc_inaddr.s_addr = ip_addr.s_addr; | |
835 | return(dc); /* and return the LCN */ | |
836 | } | |
837 | else | |
838 | { | |
839 | return(0); /* give up */ | |
840 | } | |
841 | } | |
842 | \f | |
843 | ||
844 | /***********************************************************************\ | |
845 | * convert_ip_addr() * | |
846 | ************************************************************************* | |
847 | * * | |
848 | * This routine accepts an internet address and attempts to * | |
849 | * translate to an equivalent X25 address. For DDN this follows * | |
850 | * the guidelines in the DDN X25 interface spec. The resultant * | |
851 | * X25 address is stored in the X25 called addr buffer. The * | |
852 | * routine returns TRUE if successfull, FALSE otherwise. * | |
853 | * * | |
854 | * NOTE: Although IF-11/X25 was designed to accept ASCII coded * | |
855 | * digits for the address fields, we only supply the binary * | |
856 | * values. The front-end only uses the low four bits to extract * | |
857 | * the binary value from the ASCII digits, so this works out. * | |
858 | * * | |
859 | \***********************************************************************/ | |
860 | ||
861 | static boolean convert_ip_addr(ip_addr, x25addr) | |
862 | struct in_addr ip_addr; | |
863 | u_char x25addr[]; | |
864 | { | |
865 | register int temp; | |
e43e0f32 MK |
866 | union { |
867 | struct in_addr ip; | |
868 | struct { /* (assumes Class A network number) */ | |
869 | u_char s_net; | |
870 | u_char s_host; | |
871 | u_char s_lh; | |
872 | u_char s_impno; | |
873 | } imp; | |
874 | } imp_addr; | |
875 | ||
876 | imp_addr.ip = ip_addr; | |
f0ecc930 MK |
877 | x25addr[0] = 14; /* set addr length */ |
878 | ||
879 | x25addr[1] = 0; /* clear DNIC */ | |
880 | x25addr[2] = 0; | |
881 | x25addr[3] = 0; | |
882 | x25addr[4] = 0; | |
883 | ||
e43e0f32 MK |
884 | if (imp_addr.imp.s_host < 64) /* Physical: 0000 0 IIIHH00 [SS] */ |
885 | { /* s_impno -> III, s_host -> HH */ | |
f0ecc930 | 886 | x25addr[5] = 0; /* set flag bit */ |
e43e0f32 MK |
887 | x25addr[6] = imp_addr.imp.s_impno / 100; |
888 | x25addr[7] = (imp_addr.imp.s_impno % 100) / 10; | |
889 | x25addr[8] = imp_addr.imp.s_impno % 10; | |
890 | x25addr[9] = imp_addr.imp.s_host / 10; | |
891 | x25addr[10] = imp_addr.imp.s_host % 10; | |
f0ecc930 MK |
892 | } |
893 | else /* Logical: 0000 1 RRRRR00 [SS] */ | |
894 | { /* s_host * 256 + s_impno -> RRRRR */ | |
e43e0f32 | 895 | temp = (imp_addr.imp.s_host << 8) + imp_addr.imp.s_impno; |
f0ecc930 MK |
896 | x25addr[5] = 1; |
897 | x25addr[6] = temp / 10000; | |
898 | x25addr[7] = (temp % 10000) / 1000; | |
899 | x25addr[8] = (temp % 1000) / 100; | |
900 | x25addr[9] = (temp % 100) / 10; | |
901 | x25addr[10] = temp % 10; | |
902 | } | |
903 | ||
904 | x25addr[11] = 0; /* clear rest of addr */ | |
905 | x25addr[12] = 0; | |
906 | x25addr[13] = 0; | |
907 | x25addr[14] = 0; | |
908 | ||
909 | #ifdef DDNDEBUG | |
910 | if (ddn_debug > 4) | |
911 | { | |
912 | printf("convert_ip_addr(): "); | |
913 | prt_addr(ip_addr); | |
914 | printf(" ==> "); | |
915 | prt_bytes(x25addr, 14); | |
916 | printf("\n"); | |
917 | } | |
918 | #endif DDNDEBUG | |
919 | ||
920 | return(1); | |
921 | } | |
922 | \f | |
923 | ||
924 | /***********************************************************************\ | |
925 | * convert_x25_addr() * | |
926 | ************************************************************************* | |
927 | * * | |
928 | * This routine accepts an X25 address and attempts to translate * | |
929 | * to an equivalent internet address. For DDN this follows the * | |
930 | * guidelines in the DDN X25 interface spec. The resultant * | |
931 | * internet address is returned to the caller. * | |
932 | * * | |
933 | \***********************************************************************/ | |
934 | ||
935 | static int convert_x25_addr(x25addr) | |
936 | u_char x25addr[]; | |
937 | { | |
938 | register int cnt, temp; | |
e43e0f32 MK |
939 | union { |
940 | struct in_addr ip; | |
941 | struct { /* (assumes Class A network number) */ | |
942 | u_char s_net; | |
943 | u_char s_host; | |
944 | u_char s_lh; | |
945 | u_char s_impno; | |
946 | } imp; | |
947 | } imp_addr; | |
f0ecc930 MK |
948 | |
949 | if (((cnt = x25addr[0]) < 12) || (cnt > 14)) | |
950 | { | |
951 | printf("DDN: illegal X25 address length!\n"); | |
952 | return(0); | |
953 | } | |
954 | ||
955 | switch(x25addr[5] & 0x0f) | |
956 | { | |
957 | case 0: /* Physical: 0000 0 IIIHH00 [SS] */ | |
e43e0f32 | 958 | imp_addr.imp.s_impno = |
f0ecc930 MK |
959 | ((int)(x25addr[6] & 0x0f) * 100) + |
960 | ((int)(x25addr[7] & 0x0f) * 10) + | |
961 | ((int)(x25addr[8] & 0x0f)); | |
962 | ||
963 | ||
e43e0f32 | 964 | imp_addr.imp.s_host = |
f0ecc930 MK |
965 | ((int)(x25addr[9] & 0x0f) * 10) + |
966 | ((int)(x25addr[10] & 0x0f)); | |
967 | break; | |
968 | case 1: /* Logical: 0000 1 RRRRR00 [SS] */ | |
969 | temp = ((int)(x25addr[6] & 0x0f) * 10000) | |
970 | + ((int)(x25addr[7] & 0x0f) * 1000) | |
971 | + ((int)(x25addr[8] & 0x0f) * 100) | |
972 | + ((int)(x25addr[9] & 0x0f) * 10) | |
973 | + ((int)(x25addr[10] & 0x0f)); | |
974 | ||
e43e0f32 MK |
975 | imp_addr.imp.s_host = temp >> 8; |
976 | imp_addr.imp.s_impno = temp & 0xff; | |
f0ecc930 MK |
977 | break; |
978 | default: | |
979 | printf("DDN: illegal X25 address format!\n"); | |
980 | return(0); | |
981 | } | |
982 | ||
e43e0f32 MK |
983 | imp_addr.imp.s_lh = 0; |
984 | imp_addr.imp.s_net = 0; | |
f0ecc930 MK |
985 | |
986 | #ifdef DDNDEBUG | |
987 | if (ddn_debug > 4) | |
988 | { | |
989 | printf("convert_x25_addr(): "); | |
990 | prt_bytes(&x25addr[1], cnt); | |
991 | printf(" ==> "); | |
e43e0f32 | 992 | prt_addr(imp_addr.ip); |
f0ecc930 MK |
993 | printf("\n"); |
994 | } | |
995 | #endif DDNDEBUG | |
996 | ||
e43e0f32 | 997 | return(imp_addr.ip.s_addr); |
f0ecc930 MK |
998 | } |
999 | \f | |
1000 | ||
1001 | /***********************************************************************\ | |
1002 | * make_x25_call() * | |
1003 | ************************************************************************* | |
1004 | * * | |
1005 | * This routine places an X25 call using the X25 Call Msg * | |
1006 | * buffer. The calling LCN is placed in the appropriate state * | |
1007 | * and a timer is started. * | |
1008 | * * | |
1009 | \***********************************************************************/ | |
1010 | ||
1011 | static boolean make_x25_call(ds, dc) | |
1012 | register struct ddn_softc *ds; | |
1013 | register struct ddn_cb *dc; | |
1014 | { | |
1015 | register struct mbuf *m_callbfr; | |
1016 | register u_char *cb; | |
f0ecc930 MK |
1017 | |
1018 | MGET(m_callbfr, M_DONTWAIT, MT_DATA); /* try to get call cmnd buffer */ | |
1019 | if (m_callbfr == 0) | |
1020 | return(0); | |
1021 | ||
1022 | cb = mtod(m_callbfr, u_char *); | |
1023 | ||
e43e0f32 | 1024 | convert_ip_addr(ds->ddn_ipaddr, cb_calling_addr); |
f0ecc930 MK |
1025 | |
1026 | cb_protocol[0] = 4; | |
1027 | cb_protocol[1] = X25_PROTO_IP; /* protocol = IP */ | |
1028 | cb_protocol[2] = 0; | |
1029 | cb_protocol[3] = 0; | |
1030 | cb_protocol[4] = 0; | |
1031 | ||
1032 | cb_facilities[0] = 4; /* number facility bytes */ | |
1033 | cb_facilities[1] = 0; /* options marker */ | |
1034 | cb_facilities[2] = 0; | |
1035 | cb_facilities[3] = X25_FACIL_DDN; /* DDN standard mode */ | |
1036 | cb_facilities[4] = FAC_DDNSTD; | |
1037 | ||
1038 | cb_user_data[0] = 0; /* no user data */ | |
1039 | ||
1040 | cb_cmnd[0] = CALL; /* set command code */ | |
1041 | cb_cmnd[1] = dc->dc_lcn << 1; /* set channel id */ | |
1042 | cb_cmnd[2] = 0; | |
1043 | cb_cmnd[3] = (cb_called_addr[0] + 1) + /* tally up cmnd ext length */ | |
1044 | (cb_calling_addr[0] + 1) + | |
1045 | (cb_protocol[0] + 1) + | |
1046 | (cb_facilities[0] + 1) + | |
1047 | (cb_user_data[0] + 1); | |
1048 | ||
1049 | m_callbfr->m_len = cb_cmnd[3] + 4; | |
1050 | ||
1051 | /* copy command header */ | |
1052 | bcopy(cb_cmnd, cb, 4); | |
1053 | cb += 4; | |
1054 | ||
1055 | /* copy called address */ | |
1056 | bcopy(cb_called_addr, cb, cb_called_addr[0] + 1); | |
1057 | cb += (cb_called_addr[0] + 1); | |
1058 | ||
1059 | /* copy calling address */ | |
1060 | bcopy(cb_calling_addr, cb, cb_calling_addr[0] + 1); | |
1061 | cb += (cb_calling_addr[0] + 1); | |
1062 | ||
1063 | /* copy protocol */ | |
1064 | bcopy(cb_protocol, cb, cb_protocol[0] + 1); | |
1065 | cb += (cb_protocol[0] + 1); | |
1066 | ||
1067 | /* copy facilities */ | |
1068 | bcopy(cb_facilities, cb, cb_facilities[0] + 1); | |
1069 | cb += (cb_facilities[0] + 1); | |
1070 | ||
1071 | /* copy user data */ | |
1072 | bcopy(cb_user_data, cb, cb_user_data[0] + 1); | |
1073 | cb += (cb_user_data[0] + 1); | |
1074 | ||
1075 | dc->dc_state = LC_CALL_PENDING; /* set state */ | |
1076 | dc->dc_timer = TMO_CALL_PENDING; /* start call timeout */ | |
1077 | ||
1078 | #ifdef DDNDEBUG | |
1079 | if (ddn_debug > 3) | |
1080 | { | |
1081 | printf("make_x25_call(): call_bfr = "); | |
1082 | prt_bytes(mtod(m_callbfr, u_char *), m_callbfr->m_len); | |
1083 | printf("\n"); | |
1084 | } | |
1085 | #endif DDNDEBUG | |
1086 | ||
1087 | IF_ENQUEUE(&(ds->ddn_cb[0].dc_oq), m_callbfr); | |
1088 | ddn_start(ds, &(ds->ddn_cb[0])); | |
1089 | ||
1090 | return(1); | |
1091 | } | |
1092 | \f | |
1093 | ||
1094 | /***********************************************************************\ | |
1095 | * ddn_start() * | |
1096 | ************************************************************************* | |
1097 | * * | |
1098 | * This routine attempts to start output of data queued on a * | |
1099 | * specific LCN. If the LCN was not already busy and data is * | |
1100 | * available for output, the data is copied into the LCN's I/O * | |
1101 | * buffer and an I/O request queued to the UMC. * | |
1102 | * * | |
1103 | \***********************************************************************/ | |
1104 | ||
1105 | static void ddn_start(ds, dc) | |
1106 | register struct ddn_softc *ds; | |
1107 | register struct ddn_cb *dc; | |
1108 | { | |
1109 | register struct mbuf *m; | |
1110 | int len; | |
1111 | ||
1112 | /* | |
1113 | * If output isn't active, attempt to | |
1114 | * start sending a new packet. | |
1115 | */ | |
1116 | ||
1117 | if ((dc->dc_flags & DC_OBUSY) || | |
1118 | (dc->dc_oq.ifq_len == 0) || | |
1119 | ((dc->dc_lcn != 0) && (dc->dc_state != LC_DATA_IDLE))) | |
1120 | { | |
1121 | return; | |
1122 | } | |
1123 | ||
1124 | IF_DEQUEUE(&dc->dc_oq, m); | |
1125 | ||
1126 | len = if_wubaput(&dc->dc_ifuba, m); /* copy data to mapped mem */ | |
1127 | dc->dc_flags |= DC_OBUSY; | |
1128 | ||
1129 | ddn_iorq(ds, dc, len, DDNWRT+DDNEOS); | |
1130 | } | |
1131 | \f | |
1132 | ||
1133 | /***********************************************************************\ | |
1134 | * ddn_iorq() * | |
1135 | ************************************************************************* | |
1136 | * * | |
1137 | * This routine builds UMC I/O requests and queues them for * | |
1138 | * delivery to the UMC. If the UMC I/O request comm regs are * | |
1139 | * not busy, the I/O request is passed to the UMC. * | |
1140 | * * | |
1141 | \***********************************************************************/ | |
1142 | ||
1143 | static void ddn_iorq(ds, dc, len, func) | |
1144 | struct ddn_softc *ds; | |
1145 | struct ddn_cb *dc; | |
1146 | int len, func; | |
1147 | { | |
1148 | register struct hdx_chan *hc; | |
1149 | register int info; | |
1150 | ||
1151 | ||
1152 | /* get appropriate UNIBUS mapping info */ | |
1153 | ||
1154 | if (func & DDNRDB) /* read or write? */ | |
1155 | { | |
1156 | hc = &dc->dc_rchan; | |
1157 | info = dc->dc_ifuba.ifu_r.ifrw_info; | |
1158 | } | |
1159 | else | |
1160 | { | |
1161 | hc = &dc->dc_wchan; | |
1162 | info = dc->dc_ifuba.ifu_w.ifrw_info; | |
1163 | } | |
1164 | ||
1165 | /* set channel info */ | |
1166 | ||
1167 | hc->hc_adx = (u_char)((info & 0x30000) >> 12); | |
1168 | hc->hc_addr = (u_short)(info & 0xffff); | |
1169 | hc->hc_cnt = len; | |
1170 | hc->hc_func = (u_char)func; | |
1171 | hc->hc_sbfc = 0; | |
1172 | ||
1173 | /* | |
1174 | * If UMC comm regs busy, queue start i/o for later. | |
1175 | */ | |
1176 | if (ds->ddn_sioq.sq_head) | |
1177 | { | |
1178 | (ds->ddn_sioq.sq_tail)->hc_next = hc; | |
1179 | ds->ddn_sioq.sq_tail = hc; | |
1180 | hc->hc_next = 0; | |
1181 | return; | |
1182 | } | |
1183 | ||
1184 | /* start i/o on channel now */ | |
1185 | ||
1186 | ds->ddn_sioq.sq_head = hc; | |
1187 | ds->ddn_sioq.sq_tail = hc; | |
1188 | hc->hc_next = 0; | |
1189 | start_chn(ds); | |
1190 | } | |
1191 | \f | |
1192 | ||
1193 | /***********************************************************************\ | |
1194 | * start_chn() * | |
1195 | ************************************************************************* | |
1196 | * * | |
1197 | * This routine copies UMC I/O requests into the UMC comm regs * | |
1198 | * and notifies the UMC. * | |
1199 | * * | |
1200 | \***********************************************************************/ | |
1201 | ||
1202 | static void start_chn(ds) | |
1203 | struct ddn_softc *ds; | |
1204 | { | |
1205 | register struct hdx_chan *hc = ds->ddn_sioq.sq_head; | |
1206 | register struct ddnregs *addr = | |
1207 | (struct ddnregs *)ddninfo[ds->ddn_if.if_unit]->ui_addr; | |
1208 | ||
1209 | /* | |
1210 | * Set up comm regs. | |
1211 | */ | |
1212 | addr->iochn = hc->hc_chan; | |
1213 | addr->ioadx = hc->hc_adx; | |
1214 | addr->ioadl = hc->hc_addr; | |
1215 | addr->iocnt = hc->hc_cnt; | |
1216 | addr->iofcn = hc->hc_func; | |
1217 | addr->iosbf = hc->hc_sbfc; | |
1218 | addr->ioini = 1; | |
1219 | ||
1220 | /* signal UMC if necessary */ | |
1221 | ||
1222 | if (!(addr->ionmi)) | |
1223 | { | |
1224 | addr->ionmi = 1; | |
1225 | addr->csr = DDN_DMA|DDN_WRT|DDN_IEN|DDN_NMI; | |
1226 | } | |
1227 | } | |
1228 | \f | |
1229 | ||
1230 | /***********************************************************************\ | |
1231 | * ddn_data() * | |
1232 | ************************************************************************* | |
1233 | * * | |
1234 | * This routine is called when a data channel I/O completes. * | |
1235 | * If the completion was for a write, an attempt is made to * | |
1236 | * start output on the next packet waiting for output on that * | |
1237 | * LCN. If the completion was for a read, the received packet * | |
1238 | * is sent to the IP input queue (if no error) and another read * | |
1239 | * is started on the LCN. * | |
1240 | * * | |
1241 | \***********************************************************************/ | |
1242 | ||
1243 | static void ddn_data(unit, chan, cc, rcnt) | |
1244 | int unit, chan, cc, rcnt; | |
1245 | { | |
1246 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
1247 | register struct ddn_cb *dc = &(ds->ddn_cb[chan/2]); | |
1248 | register struct ifqueue *inq = &ipintrq; | |
1249 | register struct mbuf *m; | |
1250 | ||
1251 | if (chan & 0x01) /* was it read or write? */ | |
1252 | { /* write, fire up next output */ | |
1253 | ds->ddn_if.if_opackets++; | |
1254 | dc->dc_flags &= ~DC_OBUSY; | |
1255 | ddn_start(ds, dc); | |
1256 | } | |
1257 | else /* read, process rcvd packet */ | |
1258 | { | |
1259 | if (cc == DDNIOCOK) | |
1260 | { /* Queue good packet for input */ | |
1261 | ds->ddn_if.if_ipackets++; | |
1262 | dc->dc_state = LC_DATA_IDLE; | |
1263 | dc->dc_timer = TMO_DATA_IDLE; | |
e43e0f32 | 1264 | m = if_rubaget(&(dc->dc_ifuba), rcnt, 0, &ds->ddn_if); |
f0ecc930 MK |
1265 | if (m) |
1266 | { | |
1267 | if (IF_QFULL(inq)) | |
1268 | { | |
1269 | IF_DROP(inq); | |
1270 | m_freem(m); | |
1271 | } | |
1272 | else | |
1273 | { | |
1274 | IF_ENQUEUE(inq, m); | |
1275 | schednetisr(NETISR_IP); | |
1276 | } | |
1277 | } | |
1278 | } | |
1279 | ||
1280 | /* hang a new data read */ | |
1281 | ||
1282 | ddn_iorq(ds, dc, DDNMTU, DDNRDB+DDNSTR); | |
1283 | ||
1284 | } | |
1285 | } | |
1286 | \f | |
1287 | ||
1288 | /***********************************************************************\ | |
1289 | * ddn_supr() * | |
1290 | ************************************************************************* | |
1291 | * * | |
1292 | * This routine is called when a supervisor I/O completes. * | |
1293 | * If the completion was for a write, an attempt is made to * | |
1294 | * start output on the next supervisor command waiting for * | |
1295 | * output. If the completion was for a read, the received * | |
1296 | * supervisor message is processed and another read is started. * | |
1297 | * * | |
1298 | \***********************************************************************/ | |
1299 | ||
1300 | static void ddn_supr(unit, chan, cc, rcnt) | |
1301 | int unit, chan, cc, rcnt; | |
1302 | { | |
1303 | register struct ddn_softc *ds = &ddn_softc[unit]; | |
1304 | u_char *p; | |
1305 | ||
1306 | /* was it read or write? */ | |
1307 | ||
1308 | if (chan & 0x01) | |
1309 | { | |
1310 | ds->ddn_cb[0].dc_flags &= ~DC_OBUSY; | |
1311 | ddn_start(ds, &(ds->ddn_cb[0])); | |
1312 | } | |
1313 | else | |
1314 | { | |
1315 | if (cc == DDNIOCOK) | |
1316 | { | |
1317 | p = (u_char *)(ds->ddn_cb[0].dc_ifuba.ifu_r.ifrw_addr); | |
1318 | ||
1319 | /* process supervisor message */ | |
1320 | ||
1321 | supr_msg(ds, p); | |
1322 | ||
1323 | } | |
1324 | ||
1325 | /* hang a new supr read */ | |
1326 | ||
1327 | ddn_iorq(ds, &(ds->ddn_cb[0]), DDNMTU, DDNRDB+DDNSTR); | |
1328 | } | |
1329 | } | |
1330 | \f | |
1331 | ||
1332 | /***********************************************************************\ | |
1333 | * supr_msg() * | |
1334 | ************************************************************************* | |
1335 | * * | |
1336 | * This routine processes received supervisor messages. * | |
1337 | * Depending on the message type, the appropriate action is * | |
1338 | * taken. | |
1339 | * * | |
1340 | \***********************************************************************/ | |
1341 | ||
1342 | static void supr_msg(ds, p) | |
1343 | struct ddn_softc *ds; | |
1344 | u_char p[]; | |
1345 | { | |
1346 | register struct ddn_cb *dc; | |
1347 | register int lcn; | |
1348 | register struct mbuf *m; | |
1349 | ||
1350 | #ifdef DDNDEBUG | |
1351 | if (ddn_debug > 5) | |
1352 | { | |
1353 | printf("supr_msg(): "); | |
1354 | prt_bytes(p, 4+p[3]); | |
1355 | printf("\n"); | |
1356 | } | |
1357 | #endif DDNDEBUG | |
1358 | ||
1359 | switch (p[0]) | |
1360 | { | |
1361 | case LINE_STATUS: /* link status msg */ | |
1362 | if (p[2] == LINK_UP) /* if link came up */ | |
1363 | { | |
1364 | send_restart(ds); /* send restart msg */ | |
1365 | } | |
1366 | else /* if link went down */ | |
1367 | { | |
1368 | ds->ddn_if.if_flags &= ~IFF_UP; | |
1369 | dc = ds->ddn_cb; | |
1370 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's */ | |
1371 | { | |
1372 | dc->dc_state = LC_DOWN; /* set state */ | |
1373 | dc->dc_timer = TMO_OFF; /* stop timer */ | |
1374 | dc++; | |
1375 | } | |
1376 | } | |
1377 | break; | |
1378 | ||
1379 | case RESTART: /* restart received */ | |
1380 | if (ds->ddn_cb[0].dc_state != LC_RESTART) /* if not restarting */ | |
1381 | send_supr(ds, RSTRT_ACK, 0, 0); /* send restart ack */ | |
1382 | /* fall thru */ | |
1383 | case RSTRT_ACK: /* restart ack */ | |
1384 | ds->ddn_if.if_flags |= IFF_UP; | |
1385 | dc = ds->ddn_cb; | |
1386 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's */ | |
1387 | { | |
1388 | dc->dc_state = LC_IDLE; /* set state */ | |
1389 | dc->dc_timer = TMO_OFF; /* stop timer */ | |
1390 | dc->dc_inaddr.s_addr = 0; /* forget address */ | |
1391 | while (dc->dc_oq.ifq_len) /* drop pending data */ | |
1392 | { | |
1393 | IF_DEQUEUE(&dc->dc_oq, m); | |
1394 | m_freem(m); | |
1395 | } | |
1396 | dc++; | |
1397 | } | |
1398 | break; | |
1399 | ||
1400 | case ANSWER: /* call answered */ | |
1401 | lcn = p[1] / 2; | |
1402 | dc = &(ds->ddn_cb[lcn]); | |
1403 | if (dc->dc_state == LC_CALL_PENDING) /* if a call pending */ | |
1404 | { | |
1405 | dc->dc_state = LC_DATA_IDLE; /* set state */ | |
1406 | dc->dc_timer = TMO_DATA_IDLE; /* start timer */ | |
1407 | ddn_start(ds, dc); /* try to send data */ | |
1408 | } | |
1409 | break; | |
1410 | ||
1411 | case RING: /* incoming call */ | |
1412 | for(lcn = NDDNCH; lcn > 0; lcn--) /* search LCN's */ | |
1413 | { | |
1414 | if (ds->ddn_cb[lcn].dc_state == LC_IDLE) /* unused? */ | |
1415 | break; | |
1416 | } | |
1417 | ||
1418 | if (lcn && decode_ring(p)) /* if a free LCN found */ | |
1419 | /* and ring looks ok */ | |
1420 | { | |
1421 | dc = &(ds->ddn_cb[lcn]); | |
1422 | dc->dc_inaddr.s_addr = convert_x25_addr(cb_calling_addr); | |
1423 | dc->dc_state = LC_DATA_IDLE; /* set state */ | |
1424 | dc->dc_timer = TMO_DATA_IDLE; /* start timer */ | |
1425 | send_supr(ds, ANSWER, lcn * 2, p[2]); /* send answer */ | |
1426 | } | |
1427 | else /* if no free LCN's */ | |
1428 | { | |
1429 | send_supr(ds, CLEARVC, p[2], 0); /* clear call */ | |
1430 | } | |
1431 | break; | |
1432 | ||
1433 | case CLEARLC: /* clear by LCN */ | |
1434 | lcn = p[1] / 2; /* get LCN */ | |
1435 | dc = &(ds->ddn_cb[lcn]); | |
1436 | if (dc->dc_state != LC_CLR_PENDING) /* if no clear pending */ | |
1437 | { | |
1438 | send_supr(ds, CLEARLC, p[1], 0); /* ack the clear */ | |
1439 | } | |
1440 | dc->dc_state = LC_IDLE; /* set state */ | |
1441 | dc->dc_timer = TMO_OFF; /* stop timer */ | |
1442 | dc->dc_inaddr.s_addr = 0; /* forget address */ | |
1443 | while (dc->dc_oq.ifq_len) /* drop pending data */ | |
1444 | { | |
1445 | IF_DEQUEUE(&dc->dc_oq, m); | |
1446 | m_freem(m); | |
1447 | } | |
1448 | break; | |
1449 | ||
1450 | case CLEARVC: /* clear by VCN */ | |
1451 | send_supr(ds, CLEARVC, p[1], 0); /* send clear ack */ | |
1452 | break; | |
1453 | ||
1454 | case RESET: /* X25 reset */ | |
1455 | send_supr(ds, RESET_ACK, p[1], 0); /* send reset ack */ | |
1456 | printf("X25 RESET on lcn = %d\n", p[1] / 2); /* log it */ | |
1457 | break; | |
1458 | ||
1459 | case INTERRUPT: /* X25 interrupt */ | |
1460 | printf("X25 INTERRUPT on lcn = %d, code = %d\n", /* log it */ | |
1461 | p[1] / 2, p[2]); | |
1462 | break; | |
1463 | ||
1464 | default: | |
1465 | printf("ddn%d: supervisor error, code=%x\n", | |
1466 | ds->ddn_if.if_unit, p[0]); | |
1467 | } | |
1468 | } | |
1469 | \f | |
1470 | ||
1471 | /***********************************************************************\ | |
1472 | * decode_ring() * | |
1473 | ************************************************************************* | |
1474 | * * | |
1475 | * This routine parses and validates the incoming call msg. * | |
1476 | * * | |
1477 | \***********************************************************************/ | |
1478 | ||
1479 | static boolean decode_ring(p) | |
1480 | register u_char *p; | |
1481 | { | |
1482 | register int cnt; | |
1483 | ||
1484 | #ifdef DDNDEBUG | |
1485 | if (ddn_debug > 3) | |
1486 | { | |
1487 | printf("decode_ring()\n"); | |
1488 | } | |
1489 | #endif DDNDEBUG | |
1490 | ||
1491 | ||
1492 | p += 3; /* skip to cmnd ext length */ | |
1493 | if (*p++ < 5) /* is count appropriate */ | |
1494 | return(0); /* return false if not */ | |
1495 | ||
1496 | /* called address */ | |
1497 | if ((cnt = *p + 1) > 16) /* is called addr len legal? */ | |
1498 | return(0); /* return false if not */ | |
1499 | bcopy(p, cb_called_addr, cnt); /* copy field */ | |
1500 | p += cnt; | |
1501 | ||
1502 | /* calling address */ | |
1503 | if ((cnt = *p + 1) > 16) /* is calling addr len legal? */ | |
1504 | return(0); /* return false if not */ | |
1505 | bcopy(p, cb_calling_addr, cnt); /* copy field */ | |
1506 | p += cnt; | |
1507 | ||
1508 | /* protocol part of user data */ | |
1509 | if ((cnt = *p + 1) > 5) /* is protocol len legal? */ | |
1510 | return(0); /* return false if not */ | |
1511 | bcopy(p, cb_protocol, cnt); /* copy field */ | |
1512 | p += cnt; | |
1513 | ||
1514 | /* facilities */ | |
1515 | if ((cnt = *p + 1) > 64) /* is facilities len legal? */ | |
1516 | return(0); /* return false if not */ | |
1517 | bcopy(p, cb_facilities, cnt); /* copy field */ | |
1518 | p += cnt; | |
1519 | ||
1520 | /* ignore rest of user data for now */ | |
1521 | ||
1522 | if ((cb_protocol[0] == 0) || (cb_protocol[1] != X25_PROTO_IP)) | |
1523 | return(0); /* bad if not IP */ | |
1524 | ||
1525 | return(1); /* looks ok */ | |
1526 | } | |
1527 | \f | |
1528 | ||
1529 | /***********************************************************************\ | |
1530 | * clear_lcn() * | |
1531 | ************************************************************************* | |
1532 | * * | |
1533 | * This routine clears an X25 circuit and releases any buffers * | |
1534 | * queued for transmission. * | |
1535 | * * | |
1536 | \***********************************************************************/ | |
1537 | ||
1538 | static void clear_lcn(ds, dc) | |
1539 | struct ddn_softc *ds; | |
1540 | struct ddn_cb *dc; | |
1541 | { | |
1542 | register struct mbuf *m; | |
1543 | ||
1544 | #ifdef DDNDEBUG | |
1545 | if (ddn_debug > 3) | |
1546 | { | |
1547 | printf("clear_lcn(%d)\n", dc->dc_lcn); | |
1548 | } | |
1549 | #endif DDNDEBUG | |
1550 | ||
1551 | dc->dc_state = LC_CLR_PENDING; /* set state */ | |
1552 | dc->dc_timer = TMO_CLR_PENDING; /* start clear timer */ | |
1553 | dc->dc_inaddr.s_addr = 0; /* clear associated address */ | |
1554 | while (dc->dc_oq.ifq_len) /* drop any pending data */ | |
1555 | { | |
1556 | IF_DEQUEUE(&dc->dc_oq, m); | |
1557 | m_freem(m); | |
1558 | } | |
1559 | send_supr(ds, CLEARLC, dc->dc_lcn * 2, 0); /* send clear msg */ | |
1560 | } | |
1561 | \f | |
1562 | ||
1563 | /***********************************************************************\ | |
1564 | * send_restart() * | |
1565 | ************************************************************************* | |
1566 | * * | |
1567 | * This routine marks all LCNs as being in a restarting state * | |
1568 | * and sends a restart command to X25. * | |
1569 | * * | |
1570 | \***********************************************************************/ | |
1571 | ||
1572 | static void send_restart(ds) | |
1573 | struct ddn_softc *ds; | |
1574 | { | |
1575 | register struct ddn_cb *dc; | |
1576 | register int lcn; | |
1577 | struct mbuf *m; | |
1578 | ||
1579 | #ifdef DDNDEBUG | |
1580 | if (ddn_debug > 1) | |
1581 | { | |
1582 | printf("send_restart()\n"); | |
1583 | } | |
1584 | #endif DDNDEBUG | |
1585 | dc = ds->ddn_cb; | |
1586 | for(lcn = 0; lcn <= NDDNCH; lcn++) /* for all LCN's */ | |
1587 | { | |
1588 | dc->dc_state = LC_RESTART; /* set state */ | |
1589 | dc->dc_timer = TMO_RESTART; /* start restart timeout */ | |
1590 | dc->dc_inaddr.s_addr = 0; /* forget address */ | |
1591 | while (dc->dc_oq.ifq_len) /* drop any pending data */ | |
1592 | { | |
1593 | IF_DEQUEUE(&dc->dc_oq, m); | |
1594 | m_freem(m); | |
1595 | } | |
1596 | dc++; | |
1597 | } | |
1598 | ||
1599 | send_supr(ds, RESTART, 0, 0); /* send restart msg */ | |
1600 | } | |
1601 | \f | |
1602 | ||
1603 | /***********************************************************************\ | |
1604 | * send_supr() * | |
1605 | ************************************************************************* | |
1606 | * * | |
1607 | * This routine is used to send short (4 bytes only) supervisor * | |
1608 | * commands. * | |
1609 | * * | |
1610 | \***********************************************************************/ | |
1611 | ||
1612 | static void send_supr(ds, cmd, p1, p2) | |
1613 | struct ddn_softc *ds; | |
1614 | int cmd, p1, p2; | |
1615 | { | |
1616 | struct mbuf *m; | |
1617 | register u_char *cp; | |
1618 | ||
1619 | #ifdef DDNDEBUG | |
1620 | if (ddn_debug > 6) | |
1621 | { | |
1622 | printf("send_supr(): %x %x %x\n", cmd, p1, p2); | |
1623 | } | |
1624 | #endif DDNDEBUG | |
1625 | ||
1626 | MGET(m, M_DONTWAIT, MT_DATA); | |
1627 | ||
1628 | if (m == 0) | |
1629 | { | |
1630 | printf("ddn%d: failed to get supr msg bfr!\n", ds->ddn_if.if_unit); | |
1631 | return; | |
1632 | } | |
1633 | ||
1634 | cp = mtod(m, u_char *); | |
1635 | ||
1636 | /* build supervisor message */ | |
1637 | ||
1638 | *cp++ = (byte)cmd; | |
1639 | *cp++ = (byte)p1; | |
1640 | *cp++ = (byte)p2; | |
1641 | *cp++ = 0; | |
1642 | ||
1643 | m->m_len = 4; | |
1644 | ||
1645 | IF_ENQUEUE(&(ds->ddn_cb[0].dc_oq), m); | |
1646 | ddn_start(ds, &(ds->ddn_cb[0])); | |
1647 | ||
1648 | } | |
1649 | \f | |
1650 | ||
1651 | #ifdef DDNDEBUG | |
1652 | ||
1653 | /***********************************************************************\ | |
1654 | * prt_addr() * | |
1655 | ************************************************************************* | |
1656 | * * | |
1657 | * This routine is used to print internet addresses in the * | |
1658 | * standard A.B.C.D format. * | |
1659 | * * | |
1660 | \***********************************************************************/ | |
1661 | ||
1662 | static void prt_addr(addr) | |
1663 | struct in_addr addr; | |
1664 | { | |
1665 | printf("%d.%d.%d.%d", addr.s_net, addr.s_host, addr.s_lh, addr.s_impno); | |
1666 | } | |
1667 | ||
1668 | /***********************************************************************\ | |
1669 | * prt_bytes() * | |
1670 | ************************************************************************* | |
1671 | * * | |
1672 | * This routine is used to print a string of bytes in hex. * | |
1673 | * * | |
1674 | \***********************************************************************/ | |
1675 | ||
1676 | static void prt_bytes(bp, cnt) | |
1677 | u_char *bp; | |
1678 | int cnt; | |
1679 | { | |
1680 | while(cnt--) | |
1681 | { | |
1682 | printf(" %x", *bp++ & 0xff); | |
1683 | } | |
1684 | } | |
1685 | ||
1686 | #endif DDNDEBUG | |
1687 | ||
1688 | #endif NDDN |