first pass for new make
[unix-history] / usr / src / sys / netccitt / pk_input.c
CommitLineData
7f874860
KS
1/* Copyright (c) University of British Columbia, 1984 */
2
3#include "../h/param.h"
4#include "../h/systm.h"
5#include "../h/mbuf.h"
6#include "../h/socket.h"
7#include "../h/protosw.h"
8#include "../h/socketvar.h"
9#include "../h/errno.h"
10
11#include "../net/if.h"
12
13#include "../netccitt/x25.h"
14#include "../netccitt/pk.h"
15#include "../netccitt/pk_var.h"
16
17struct pkcb *
18pk_newlink (xcp)
19struct x25config *xcp;
20{
21 register struct pkcb *pkp;
22 register struct mbuf *m;
23 register struct pklcd *lcp;
24 register struct protosw *pp;
25 register unsigned size;
26
27 if (xcp -> xc_ntnlen <= 0 || xcp -> xc_ntnlen > sizeof (xcp -> xc_ntn) * 2)
28 return ((struct pkcb *)0);
29#ifdef BSD4_3
30 pp = pffindproto (AF_CCITT, (int)xcp -> xc_lproto, 0);
31#else
32 pp = pffindproto (AF_CCITT, (int)xcp -> xc_lproto);
33#endif
34 if (pp == 0 || pp -> pr_output == 0) {
35 pk_message (0, xcp, "link level protosw error");
36 return ((struct pkcb *)0);
37 }
38
39 /*
40 * Allocate a network control block structure
41 */
42
43 size = sizeof (struct pkcb) + xcp->xc_maxlcn * sizeof (struct pklcd *);
44#ifdef sun
45 if (xcp -> xc_maxlcn < 1 || size > mclbytes) {
46#else
47 if (xcp -> xc_maxlcn < 1 || size > CLBYTES) {
48#endif
49 pk_message (0, xcp, "invalid maxlcn");
50 return ((struct pkcb *)0);
51 }
52 m = m_get (M_DONTWAIT, MT_PCB);
53 if (m == 0)
54 return ((struct pkcb *)0);
55 if (size > MLEN) {
56#ifdef sun
57 if (mclget (m) == 0) {
58 m_freem (m);
59 return ((struct pkcb *)0);
60 }
61#else
62#ifdef BSD4_3
63 MCLGET (m);
64 if (m -> m_len != CLBYTES) {
65 (void) m_free (m);
66 return ((struct pkcb *)0);
67 }
68#else
69 register struct mbuf *p;
70
71 MCLGET (p, 1);
72 if (p == 0) {
73 m_freem (m);
74 return ((struct pkcb *)0);
75 }
76 m -> m_off = (int)p - (int)m;
77#endif
78#endif
79 }
80 pkp = mtod (m, struct pkcb *);
81 bzero ((caddr_t)pkp, size);
82
83 /*
84 * Allocate a logical channel descriptor for lcn 0
85 */
86
87 m = m_getclr (M_DONTWAIT, MT_PCB);
88 if (m == 0) {
89 m_freem (dtom (pkp));
90 return ((struct pkcb *)0);
91 }
92 lcp = mtod (m, struct pklcd *);
93 lcp -> lcd_state = READY;
94 lcp -> lcd_pkp = pkp;
95 pkp -> pk_chan[0] = lcp;
96
97 pkp -> pk_output = pp -> pr_output;
98 pkp -> pk_xcp = xcp;
99 pkp -> pk_state = DTE_WAITING;
100 pkp -> pk_maxlcn = xcp -> xc_maxlcn;
101 pkp -> pk_next = pkcbhead;
102 pkcbhead = pkp;
103
104 /*
105 * set defaults
106 */
107
108 if (xcp -> xc_pwsize == 0)
109 xcp -> xc_pwsize = DEFAULT_WINDOW_SIZE;
110 if (xcp -> xc_psize == 0)
111 xcp -> xc_psize = X25_PS128;
112 return (pkp);
113}
114
115/*
116 * This procedure is called by the link level whenever the link
117 * becomes operational, is reset, or when the link goes down.
118 */
119
120pk_ctlinput (code, xcp)
121struct x25config *xcp;
122{
123 register struct pkcb *pkp;
124
125 for (pkp = pkcbhead; pkp; pkp = pkp -> pk_next)
126 if (pkp -> pk_xcp == xcp)
127 break;
128
129 if (pkp == 0 && (pkp = pk_newlink (xcp)) == 0)
130 return (EINVAL);
131
132 switch (code) {
133 case PRC_LINKUP:
134 if (pkp -> pk_state == DTE_WAITING)
135 pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
136 break;
137
138 case PRC_LINKDOWN:
139 pk_restart (pkp, -1); /* Clear all active circuits */
140 pkp -> pk_state = DTE_WAITING;
141 break;
142
143 case PRC_LINKRESET:
144 pk_restart (pkp, X25_RESTART_NETWORK_CONGESTION);
145 break;
146
147 }
148 return (0);
149}
150
151/*
152 * X.25 PACKET INPUT
153 *
154 * This procedure is called by a link level procedure whenever
155 * an information frame is received. It decodes the packet and
156 * demultiplexes based on the logical channel number.
157 *
158 */
159
160pk_input (m, xcp)
161register struct mbuf *m;
162struct x25config *xcp;
163{
164 register struct x25_packet *xp;
165 register struct pklcd *lcp;
166 register struct socket *so = 0;
167 register struct pkcb *pkp;
168 int ptype, lcn, lcdstate = LISTEN;
169 static struct x25config *lastxcp;
170 static struct pkcb *lastpkp;
171
172 if (xcp == lastxcp)
173 pkp = lastpkp;
174 else {
175 for (pkp = pkcbhead; ; pkp = pkp -> pk_next) {
176 if (pkp == 0) {
177 pk_message (0, xcp, "pk_input: unknown network");
178 m_freem (m);
179 return;
180 }
181 if (pkp -> pk_xcp == xcp)
182 break;
183 }
184 lastxcp = xcp;
185 lastpkp = pkp;
186 }
187
188 xp = mtod (m, struct x25_packet *);
189 ptype = pk_decode (xp);
190 lcn = xp -> logical_channel_number;
191 lcp = pkp -> pk_chan[lcn];
192
193 /*
194 * If the DTE is in Restart state, then it will ignore data,
195 * interrupt, call setup and clearing, flow control and reset
196 * packets.
197 */
198 if (lcn < 0 || lcn > pkp -> pk_maxlcn) {
199 pk_message (lcn, pkp -> pk_xcp, "illegal lcn");
200 m_freem (m);
201 return;
202 }
203
204 pk_trace (pkp -> pk_xcp, xp, "P-In");
205
206 if (pkp -> pk_state != DTE_READY && ptype != RESTART && ptype != RESTART_CONF) {
207 m_freem (m);
208 return;
209 }
210 if (lcp) {
211 so = lcp -> lcd_so;
212 lcdstate = lcp -> lcd_state;
213 } else {
214 if (ptype == CLEAR) { /* idle line probe (Datapac specific) */
215 /* send response on lcd 0's output queue */
216 lcp -> lcd_template = pk_template (lcn, X25_CLEAR_CONFIRM);
217 pk_output (lcp);
218 m_freem (m);
219 return;
220 }
221 if (ptype != CALL)
222 ptype = INVALID_PACKET;
223 }
224
225 if (lcn == 0 && ptype != RESTART && ptype != RESTART_CONF) {
226 pk_message (0, pkp -> pk_xcp, "illegal ptype (%s) on lcn 0",
227 pk_name[ptype / MAXSTATES]);
228 m_freem (m);
229 return;
230 }
231
232 switch (ptype + lcdstate) {
233 /*
234 * Incoming Call packet received.
235 */
236 case CALL + LISTEN:
237 incoming_call (pkp, xp, m -> m_len);
238 break;
239
240 /*
241 * Call collision: Just throw this "incoming call" away since
242 * the DCE will ignore it anyway.
243 */
244 case CALL + SENT_CALL:
245 pk_message ((int)xp -> logical_channel_number, pkp -> pk_xcp,
246 "incoming call collision");
247 break;
248
249 /*
250 * Call confirmation packet received. This usually means our
251 * previous connect request is now complete.
252 */
253 case CALL_ACCEPTED + SENT_CALL:
254 call_accepted (lcp, xp, m -> m_len);
255 break;
256
257 /*
258 * This condition can only happen if the previous state was
259 * SENT_CALL. Just ignore the packet, eventually a clear
260 * confirmation should arrive.
261 */
262 case CALL_ACCEPTED + SENT_CLEAR:
263 break;
264
265 /*
266 * Clear packet received. This requires a complete tear down
267 * of the virtual circuit. Free buffers and control blocks.
268 * and send a clear confirmation.
269 */
270 case CLEAR + READY:
271 case CLEAR + RECEIVED_CALL:
272 case CLEAR + SENT_CALL:
273 case CLEAR + DATA_TRANSFER:
274 lcp -> lcd_state = RECEIVED_CLEAR;
275 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CLEAR_CONFIRM);
276 pk_output (lcp);
277 pk_clearcause (pkp, xp);
278 pk_close (lcp);
279 break;
280
281 /*
282 * Clear collision: Treat this clear packet as a confirmation.
283 */
284 case CLEAR + SENT_CLEAR:
285 pk_close (lcp);
286 break;
287
288 /*
289 * Clear confirmation received. This usually means the virtual
290 * circuit is now completely removed.
291 */
292 case CLEAR_CONF + SENT_CLEAR:
293 pk_close (lcp);
294 break;
295
296 /*
297 * A clear confirmation on an unassigned logical channel - just
298 * ignore it. Note: All other packets on an unassigned channel
299 * results in a clear.
300 */
301 case CLEAR_CONF + READY:
302 break;
303
304 /*
305 * Data packet received. Pass on to next level. Move the Q and M
306 * bits into the data portion for the next level.
307 */
308 case DATA + DATA_TRANSFER:
309 if (lcp -> lcd_reset_condition) {
310 ptype = DELETE_PACKET;
311 break;
312 }
313
314 /*
315 * Process the P(S) flow control information in this Data packet.
316 * Check that the packets arrive in the correct sequence and that
317 * they are within the "lcd_input_window". Input window rotation is
318 * initiated by the receive interface.
319 */
320
321 if (PS(xp) != ((lcp -> lcd_rsn + 1) % MODULUS) ||
322 PS(xp) == ((lcp -> lcd_input_window + lcp->lcd_windowsize) % MODULUS)) {
323 m_freem (m);
324 pk_procerror (RESET, lcp, "p(s) flow control error");
325 break;
326 }
327 lcp -> lcd_rsn = PS(xp);
328
329 if (pk_ack (lcp, PR(xp)) != PACKET_OK) {
330 m_freem (m);
331 break;
332 }
333
334 m -> m_off += PKHEADERLN;
335 m -> m_len -= PKHEADERLN;
336 if (lcp -> lcd_flags & X25_MQBIT) {
337 octet *t;
338
339 m -> m_off -= 1;
340 m -> m_len += 1;
341 t = mtod (m, octet *);
342 *t = 0x00;
343 if (xp -> q_bit)
344 *t |= 0x80;
345 if (MBIT(xp))
346 *t |= 0x40;
347 }
348
349 /*
350 * Discard Q-BIT packets if the application
351 * doesn't want to be informed of M and Q bit status
352 */
353 if (xp -> q_bit && (lcp -> lcd_flags & X25_MQBIT) == 0) {
354 m_freem (m);
355 lcp -> lcd_rxcnt++;
356 /*
357 * NB. This is dangerous: sending a RR here can
358 * cause sequence number errors if a previous data
359 * packet has not yet been passed up to the application
360 * (RR's are normally generated via PRU_RCVD).
361 */
362 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RR);
363 pk_output (lcp);
364 } else {
365#ifdef BSD4_3
366 sbappendrecord (&so -> so_rcv, m);
367#else
368 sbappend (&so -> so_rcv, m);
369#endif
370 sorwakeup (so);
371 }
372 break;
373
374 /*
375 * Interrupt packet received.
376 */
377 case INTERRUPT + DATA_TRANSFER:
378 if (lcp -> lcd_reset_condition)
379 break;
380 lcp -> lcd_intrdata = xp -> packet_data;
381 sohasoutofband (so);
382 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_INTERRUPT_CONFIRM);
383 pk_output (lcp);
384 break;
385
386 /*
387 * Interrupt confirmation packet received.
388 */
389 case INTERRUPT_CONF + DATA_TRANSFER:
390 if (lcp -> lcd_reset_condition)
391 break;
392 if (lcp -> lcd_intrconf_pending == TRUE)
393 lcp -> lcd_intrconf_pending = FALSE;
394 else
395 pk_procerror (RESET, lcp, "unexpected packet");
396 break;
397
398 /*
399 * Receiver ready received. Rotate the output window and output
400 * any data packets waiting transmission.
401 */
402 case RR + DATA_TRANSFER:
403 if (lcp -> lcd_reset_condition)
404 break;
405 if (pk_ack (lcp, PR(xp)) != PACKET_OK)
406 break;
407 if (lcp -> lcd_rnr_condition == TRUE)
408 lcp -> lcd_rnr_condition = FALSE;
409 pk_output (lcp);
410 break;
411
412 /*
413 * Receiver Not Ready received. Packets up to the P(R) can be
414 * be sent. Condition is cleared with a RR.
415 */
416 case RNR + DATA_TRANSFER:
417 if (lcp -> lcd_reset_condition)
418 break;
419 if (pk_ack (lcp, PR(xp)) != PACKET_OK)
420 break;
421 lcp -> lcd_rnr_condition = TRUE;
422 break;
423
424 /*
425 * Reset packet received. Set state to FLOW_OPEN. The Input and
426 * Output window edges ar set to zero. Both the send and receive
427 * numbers are reset. A confirmation is returned.
428 */
429 case RESET + DATA_TRANSFER:
430 if (lcp -> lcd_reset_condition)
431 /* Reset collision. Just ignore packet. */
432 break;
433
434 pk_resetcause (pkp, xp);
435 sbflush (&so -> so_snd);
436 sbflush (&so -> so_rcv);
437
438 wakeup ((caddr_t) & so -> so_timeo);
439 sorwakeup (so);
440 sowwakeup (so);
441
442 lcp -> lcd_window_condition = lcp -> lcd_rnr_condition =
443 lcp -> lcd_intrconf_pending = FALSE;
444 lcp -> lcd_output_window = lcp -> lcd_input_window =
445 lcp -> lcd_last_transmitted_pr = 0;
446 lcp -> lcd_ssn = 0;
447 lcp -> lcd_rsn = MODULUS - 1;
448
449 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_RESET_CONFIRM);
450 pk_output (lcp);
451 break;
452
453 /*
454 * Reset confirmation received.
455 */
456 case RESET_CONF + DATA_TRANSFER:
457 if (lcp -> lcd_reset_condition) {
458 lcp -> lcd_reset_condition = FALSE;
459 pk_output (lcp);
460 }
461 else
462 pk_procerror (RESET, lcp, "unexpected packet");
463 break;
464
465 case DATA + SENT_CLEAR:
466 ptype = DELETE_PACKET;
467 case RR + SENT_CLEAR:
468 case RNR + SENT_CLEAR:
469 case INTERRUPT + SENT_CLEAR:
470 case INTERRUPT_CONF + SENT_CLEAR:
471 case RESET + SENT_CLEAR:
472 case RESET_CONF + SENT_CLEAR:
473 /* Just ignore packet if we have sent a CLEAR already.
474 */
475 break;
476
477 /*
478 * Restart sets all the permanent virtual circuits to the "Data
479 * Transfer" stae and all the switched virtual circuits to the
480 * "Ready" state.
481 */
482 case RESTART + READY:
483 switch (pkp -> pk_state) {
484 case DTE_SENT_RESTART:
485 /* Restart collision. */
486 pkp -> pk_state = DTE_READY;
487 pk_message (0, pkp -> pk_xcp,
488 "Packet level operational");
489 break;
490
491 default:
492 pk_restart (pkp, -1);
493 pk_restartcause (pkp, xp);
494 pkp -> pk_chan[0] -> lcd_template = pk_template (0,
495 X25_RESTART_CONFIRM);
496 pk_output (pkp -> pk_chan[0]);
497 }
498 break;
499
500 /*
501 * Restart confirmation received. All logical channels are set
502 * to READY.
503 */
504 case RESTART_CONF + READY:
505 switch (pkp -> pk_state) {
506 case DTE_SENT_RESTART:
507 pkp -> pk_state = DTE_READY;
508 pk_message (0, pkp -> pk_xcp,
509 "Packet level operational");
510 break;
511
512 default:
513 /* Restart local procedure error. */
514 pk_restart (pkp, X25_RESTART_LOCAL_PROCEDURE_ERROR);
515 pkp -> pk_state = DTE_SENT_RESTART;
516 }
517 break;
518
519 default:
520 if (lcp) {
521 pk_procerror (CLEAR, lcp, "unknown packet error");
522 pk_message (lcn, pkp -> pk_xcp,
523 "\"%s\" unexpected in \"%s\" state",
524 pk_name[ptype/MAXSTATES], pk_state[lcdstate]);
525 }
526 else /* Packets arrived on an unassigned channel.
527 */
528 pk_message ((int)xp->logical_channel_number, pkp -> pk_xcp,
529 "packet arrived on unassigned lcn");
530 break;
531 }
532 if (ptype != DATA)
533 m_freem (m);
534}
535
536
537/*
538 * This routine handles incoming call packets. It matches the protocol
539 * field on the Call User Data field (usually the first four bytes) with
540 * sockets awaiting connections.
541 */
542
543static
544incoming_call (pkp, xp, len)
545struct pkcb *pkp;
546struct x25_packet *xp;
547{
548 register struct pklcd *lcp, *l;
549 register struct sockaddr_x25 *sa;
550 register struct x25_calladdr *a;
551 register struct socket *so;
552 struct mbuf *m;
553 register int l1, l2;
554 char *e, *errstr = "server unavailable";
555 octet *u;
556 int lcn = xp -> logical_channel_number;
557
558 /* First, copy the data from the incoming call packet to a X25_socket
559 descriptor. */
560
561 a = (struct x25_calladdr *) &xp -> packet_data;
562 l1 = a -> calling_addrlen;
563 l2 = a -> called_addrlen;
564 if ((m = m_getclr (M_DONTWAIT, MT_HEADER)) == 0)
565 return;
566 sa = mtod (m, struct sockaddr_x25 *);
567 u = (octet *) (a -> address_field + l2 / 2);
568 e = sa -> x25_addr;
569 if (l2 & 0x01) {
570 *e++ = *u++ & 0x0f;
571 l1--;
572 }
573 from_bcd (e, &u, l1);
574 if (l1 & 0x01)
575 u++;
576
577 parse_facilities (u, sa);
578 u += *u + 1;
579 sa -> x25_udlen = min (16, ((octet *)xp) + len - u);
580 if (sa -> x25_udlen < 0)
581 sa -> x25_udlen = 0;
582 bcopy ((caddr_t)u, sa -> x25_udata, (unsigned)sa -> x25_udlen);
583
584 /*
585 * Now, loop through the listen sockets looking for a match on the
586 * PID. That is the first four octets of the user data field. This
587 * is the closest thing to a port number for X.25 packets. What it
588 * does provide is away of multiplexing services at the user level.
589 */
590
591 for (l = pk_listenhead; l; l = l -> lcd_listen) {
592 struct sockaddr_x25 *sxp = l -> lcd_ceaddr;
593
594 if (bcmp (sxp -> x25_udata, sa -> x25_udata, sxp->x25_udlen))
595 continue;
596 if (sxp -> x25_net && sxp -> x25_net != pkp->pk_xcp->xc_net)
597 continue;
598 /*
599 * don't accept incoming collect calls unless
600 * the server sets the reverse charging option.
601 */
602 if ((sxp -> x25_opts.op_flags & (X25_OLDSOCKADDR|X25_REVERSE_CHARGE)) == 0 &&
603 sa -> x25_opts.op_flags & X25_REVERSE_CHARGE) {
604 errstr = "incoming collect call refused";
605 break;
606 }
607 so = sonewconn (l -> lcd_so);
608 if (so == NULL) {
609 /*
610 * Insufficient space or too many unaccepted
611 * connections. Just throw the call away.
612 */
613 errstr = "server malfunction";
614 break;
615 }
616 lcp = (struct pklcd *) so -> so_pcb;
617 lcp -> lcd_lcn = lcn;
618 lcp -> lcd_state = RECEIVED_CALL;
619 lcp -> lcd_craddr = sa;
620 sa -> x25_opts.op_flags |= sxp -> x25_opts.op_flags &
621 ~X25_REVERSE_CHARGE;
622 pk_assoc (pkp, lcp, sa);
623 lcp -> lcd_template = pk_template (lcp -> lcd_lcn, X25_CALL_ACCEPTED);
624 pk_output (lcp);
625 soisconnected (so);
626 return;
627 }
628
629 /*
630 * If the call fails for whatever reason, we still need to build a
631 * skeleton LCD in order to be able to properly receive the CLEAR
632 * CONFIRMATION.
633 */
634#ifdef WATERLOO /* be explicit */
635 if (l == 0 && bcmp(sa->x25_udata, "ean", 3) == 0)
636 pk_message (lcn, pkp -> pk_xcp, "host=%s ean%c: %s",
637 sa->x25_addr, sa->x25_udata[3] & 0xff, errstr);
638 else if (l == 0 && bcmp(sa->x25_udata, "\1\0\0\0", 4) == 0)
639 pk_message (lcn, pkp -> pk_xcp, "host=%s x29d: %s",
640 sa->x25_addr, errstr);
641 else
642#endif
643 pk_message (lcn, pkp -> pk_xcp, "host=%s pid=%x %x %x %x: %s",
644 sa -> x25_addr, sa -> x25_udata[0] & 0xff,
645 sa -> x25_udata[1] & 0xff, sa -> x25_udata[2] & 0xff,
646 sa -> x25_udata[3] & 0xff, errstr);
647 if ((m = m_getclr (M_DONTWAIT, MT_HEADER)) == 0) {
648 (void) m_free (dtom (sa));
649 return;
650 }
651 lcp = mtod (m, struct pklcd *);
652 lcp -> lcd_lcn = lcn;
653 lcp -> lcd_state = RECEIVED_CALL;
654 pk_assoc (pkp, lcp, sa);
655 (void) m_free (dtom (sa));
656 pk_clear (lcp);
657}
658
659static
660call_accepted (lcp, xp, len)
661struct pklcd *lcp;
662struct x25_packet *xp;
663{
664 register struct x25_calladdr *ap;
665 register octet *fcp;
666
667 lcp -> lcd_state = DATA_TRANSFER;
668 soisconnected (lcp -> lcd_so);
669 if (len > 3) {
670 ap = (struct x25_calladdr *) &xp -> packet_data;
671 fcp = (octet *) ap -> address_field + (ap -> calling_addrlen +
672 ap -> called_addrlen + 1) / 2;
673 if (fcp + *fcp <= ((octet *)xp) + len)
674 parse_facilities (fcp, lcp -> lcd_ceaddr);
675 }
676 pk_assoc (lcp -> lcd_pkp, lcp, lcp -> lcd_ceaddr);
677}
678
679static
680parse_facilities (fcp, sa)
681register octet *fcp;
682register struct sockaddr_x25 *sa;
683{
684 register octet *maxfcp;
685
686 maxfcp = fcp + *fcp;
687 fcp++;
688 while (fcp < maxfcp) {
689 /*
690 * Ignore national DCE or DTE facilities
691 */
692 if (*fcp == 0 || *fcp == 0xff)
693 break;
694 switch (*fcp) {
695 case FACILITIES_WINDOWSIZE:
696 sa -> x25_opts.op_wsize = fcp[1];
697 fcp += 3;
698 break;
699
700 case FACILITIES_PACKETSIZE:
701 sa -> x25_opts.op_psize = fcp[1];
702 fcp += 3;
703 break;
704
705 case FACILITIES_THROUGHPUT:
706 sa -> x25_opts.op_speed = fcp[1];
707 fcp += 2;
708 break;
709
710 case FACILITIES_REVERSE_CHARGE:
711 if (fcp[1] & 01)
712 sa -> x25_opts.op_flags |= X25_REVERSE_CHARGE;
713 /*
714 * Datapac specific: for a X.25(1976) DTE, bit 2
715 * indicates a "hi priority" (eg. international) call.
716 */
717 if (fcp[1] & 02 && sa -> x25_opts.op_psize == 0)
718 sa -> x25_opts.op_psize = X25_PS128;
719 fcp += 2;
720 break;
721
722 default:
723/*printf("unknown facility %x, class=%d\n", *fcp, (*fcp & 0xc0) >> 6);*/
724 switch ((*fcp & 0xc0) >> 6) {
725 case 0: /* class A */
726 fcp += 2;
727 break;
728
729 case 1:
730 fcp += 3;
731 break;
732
733 case 2:
734 fcp += 4;
735 break;
736
737 case 3:
738 fcp++;
739 fcp += *fcp;
740 }
741 }
742 }
743}
744
745from_bcd (a, x, len)
746register char *a;
747register octet **x;
748register int len;
749{
750 register int posn = 0;
751
752 while (--len >= 0) {
753 if (posn++ & 0x01)
754 *a = *(*x)++ & 0x0f;
755 else
756 *a = (**x >> 4) & 0x0F;
757 *a++ |= 0x30;
758 }
759}