1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 ******************************************************************/ 23 24 /* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27 /* 28 * ARGO TP 29 * 30 * $Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $ 31 * $Source: /usr/argo/sys/netiso/RCS/tp_subr2.c,v $ 32 * @(#)tp_subr2.c 7.4 (Berkeley) 09/22/89 33 * 34 * Some auxiliary routines: 35 * tp_protocol_error: required by xebec- called when a combo of state, 36 * event, predicate isn't covered for by the transition file. 37 * tp_indicate: gives indications(signals) to the user process 38 * tp_getoptions: initializes variables that are affected by the options 39 * chosen. 40 */ 41 42 #ifndef lint 43 static char *rcsid = "$Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $"; 44 #endif lint 45 46 #include "argoxtwentyfive.h" 47 48 /* this def'n is to cause the expansion of this macro in the 49 * routine tp_local_credit : 50 */ 51 #define LOCAL_CREDIT_EXPAND 52 53 #include "param.h" 54 #include "mbuf.h" 55 #include "socket.h" 56 #include "socketvar.h" 57 #include "domain.h" 58 #include "protosw.h" 59 #include "errno.h" 60 #include "types.h" 61 #include "time.h" 62 #include "kernel.h" 63 #undef MNULL 64 #include "argo_debug.h" 65 #include "tp_param.h" 66 #include "tp_ip.h" 67 #include "iso.h" 68 #include "iso_errno.h" 69 #include "iso_pcb.h" 70 #include "tp_timer.h" 71 #include "tp_stat.h" 72 #include "tp_tpdu.h" 73 #include "tp_pcb.h" 74 #include "tp_seq.h" 75 #include "tp_trace.h" 76 #include "tp_user.h" 77 #include "cons.h" 78 79 /* 80 * NAME: tp_local_credit() 81 * 82 * CALLED FROM: 83 * tp_emit(), tp_usrreq() 84 * 85 * FUNCTION and ARGUMENTS: 86 * Computes the local credit and stashes it in tpcb->tp_lcredit. 87 * It's a macro in the production system rather than a procdure. 88 * 89 * RETURNS: 90 * 91 * SIDE EFFECTS: 92 * 93 * NOTES: 94 * This doesn't actually get called in a production system - 95 * the macro gets expanded instead in place of calls to this proc. 96 * But for debugging, we call this and that allows us to add 97 * debugging messages easily here. 98 */ 99 void 100 tp_local_credit(tpcb) 101 struct tp_pcb *tpcb; 102 { 103 LOCAL_CREDIT(tpcb); 104 IFDEBUG(D_CREDIT) 105 printf("ref 0x%x lcdt 0x%x l_tpdusize 0x%x decbit 0x%x\n", 106 tpcb->tp_refp - tp_ref, 107 tpcb->tp_lcredit, 108 tpcb->tp_l_tpdusize, 109 tpcb->tp_decbit, 110 tpcb->tp_cong_win 111 ); 112 ENDDEBUG 113 IFTRACE(D_CREDIT) 114 tptraceTPCB(TPPTmisc, 115 "lcdt tpdusz \n", 116 tpcb->tp_lcredit, tpcb->tp_l_tpdusize, 0, 0); 117 ENDTRACE 118 } 119 120 /* 121 * NAME: tp_protocol_error() 122 * 123 * CALLED FROM: 124 * tp_driver(), when it doesn't know what to do with 125 * a combo of event, state, predicate 126 * 127 * FUNCTION and ARGUMENTS: 128 * print error mesg 129 * 130 * RETURN VALUE: 131 * EIO - always 132 * 133 * SIDE EFFECTS: 134 * 135 * NOTES: 136 */ 137 int 138 tp_protocol_error(e,tpcb) 139 struct tp_event *e; 140 struct tp_pcb *tpcb; 141 { 142 printf("TP PROTOCOL ERROR! tpcb 0x%x event 0x%x, state 0x%x\n", 143 tpcb, e->ev_number, tpcb->tp_state); 144 IFTRACE(D_DRIVER) 145 tptraceTPCB(TPPTmisc, "PROTOCOL ERROR tpcb event state", 146 tpcb, e->ev_number, tpcb->tp_state, 0 ); 147 ENDTRACE 148 return EIO; /* for lack of anything better */ 149 } 150 151 152 /* Not used at the moment */ 153 ProtoHook 154 tp_drain() 155 { 156 return 0; 157 } 158 159 160 /* 161 * NAME: tp_indicate() 162 * 163 * CALLED FROM: 164 * tp.trans when XPD arrive, when a connection is being disconnected by 165 * the arrival of a DR or ER, and when a connection times out. 166 * 167 * FUNCTION and ARGUMENTS: 168 * (ind) is the type of indication : T_DISCONNECT, T_XPD 169 * (error) is an E* value that will be put in the socket structure 170 * to be passed along to the user later. 171 * Gives a SIGURG to the user process or group indicated by the socket 172 * attached to the tpcb. 173 * 174 * RETURNS: Rien 175 * 176 * SIDE EFFECTS: 177 * 178 * NOTES: 179 */ 180 void 181 tp_indicate(ind, tpcb, error) 182 int ind; 183 u_short error; 184 register struct tp_pcb *tpcb; 185 { 186 register struct socket *so = tpcb->tp_sock; 187 IFTRACE(D_INDICATION) 188 tptraceTPCB(TPPTindicate, ind, *(u_short *)(tpcb->tp_lsuffix), 189 *(u_short *)(tpcb->tp_fsuffix), error,so->so_pgid); 190 ENDTRACE 191 IFDEBUG(D_INDICATION) 192 char *ls, *fs; 193 ls = tpcb->tp_lsuffix, 194 fs = tpcb->tp_fsuffix, 195 196 printf( 197 "indicate 0x%x lsuf 0x%02x%02x fsuf 0x%02x%02x err 0x%x noind 0x%x ref 0x%x\n", 198 ind, 199 *ls, *(ls+1), *fs, *(fs+1), 200 error, /*so->so_pgrp,*/ 201 tpcb->tp_no_disc_indications, 202 tpcb->tp_lref); 203 ENDDEBUG 204 205 so->so_error = error; 206 207 if (ind == T_DISCONNECT) { 208 if ( tpcb->tp_no_disc_indications ) 209 return; 210 } 211 IFTRACE(D_INDICATION) 212 tptraceTPCB(TPPTmisc, "doing sohasoutofband(so)", so,0,0,0); 213 ENDTRACE 214 sohasoutofband(so); 215 } 216 217 /* 218 * NAME : tp_getoptions() 219 * 220 * CALLED FROM: 221 * tp.trans whenever we go into OPEN state 222 * 223 * FUNCTION and ARGUMENTS: 224 * sets the proper flags and values in the tpcb, to control 225 * the appropriate actions for the given class, options, 226 * sequence space, etc, etc. 227 * 228 * RETURNS: Nada 229 * 230 * SIDE EFFECTS: 231 * 232 * NOTES: 233 */ 234 void 235 tp_getoptions(tpcb) 236 struct tp_pcb *tpcb; 237 { 238 tpcb->tp_seqmask = 239 tpcb->tp_xtd_format ? TP_XTD_FMT_MASK : TP_NML_FMT_MASK ; 240 tpcb->tp_seqbit = 241 tpcb->tp_xtd_format ? TP_XTD_FMT_BIT : TP_NML_FMT_BIT ; 242 tpcb->tp_seqhalf = tpcb->tp_seqbit >> 1; 243 tpcb->tp_dt_ticks = 244 MAX(tpcb->tp_dt_ticks, (tpcb->tp_peer_acktime + 2)); 245 246 } 247 248 /* 249 * NAME: tp_recycle_tsuffix() 250 * 251 * CALLED FROM: 252 * Called when a ref is frozen. 253 * 254 * FUNCTION and ARGUMENTS: 255 * allows the suffix to be reused. 256 * 257 * RETURNS: zilch 258 * 259 * SIDE EFFECTS: 260 * 261 * NOTES: 262 */ 263 void 264 tp_recycle_tsuffix(tpcb) 265 struct tp_pcb *tpcb; 266 { 267 bzero((caddr_t)tpcb->tp_lsuffix, sizeof( tpcb->tp_lsuffix)); 268 bzero((caddr_t)tpcb->tp_fsuffix, sizeof( tpcb->tp_fsuffix)); 269 tpcb->tp_fsuffixlen = tpcb->tp_lsuffixlen = 0; 270 271 (tpcb->tp_nlproto->nlp_recycle_suffix)(tpcb->tp_npcb); 272 } 273 274 /* 275 * NAME: tp_quench() 276 * 277 * CALLED FROM: 278 * tp{af}_quench() when ICMP source quench or similar thing arrives. 279 * 280 * FUNCTION and ARGUMENTS: 281 * Drop the congestion window back to 1. 282 * Congestion window scheme: 283 * Initial value is 1. ("slow start" as Nagle, et. al. call it) 284 * For each good ack that arrives, the congestion window is increased 285 * by 1 (up to max size of logical infinity, which is to say, 286 * it doesn't wrap around). 287 * Source quench causes it to drop back to 1. 288 * tp_send() uses the smaller of (regular window, congestion window). 289 * One retransmission strategy option is to have any retransmission 290 * cause reset the congestion window back to 1. 291 * 292 * (cmd) is either PRC_QUENCH: source quench, or 293 * PRC_QUENCH2: dest. quench (dec bit) 294 * 295 * RETURNS: 296 * 297 * SIDE EFFECTS: 298 * 299 * NOTES: 300 */ 301 void 302 tp_quench( tpcb, cmd ) 303 struct tp_pcb *tpcb; 304 int cmd; 305 { 306 IFDEBUG(D_QUENCH) 307 printf("tp_quench tpcb 0x%x ref 0x%x sufx 0x%x\n", 308 tpcb, tpcb->tp_lref, *(u_short *)(tpcb->tp_lsuffix)); 309 printf("cong_win 0x%x decbit 0x%x \n", 310 tpcb->tp_cong_win, tpcb->tp_decbit); 311 ENDDEBUG 312 switch(cmd) { 313 case PRC_QUENCH: 314 tpcb->tp_cong_win = 1; 315 IncStat(ts_quench); 316 break; 317 case PRC_QUENCH2: 318 tpcb->tp_cong_win = 1; /* might as well quench source also */ 319 tpcb->tp_decbit = TP_DECBIT_CLEAR_COUNT; 320 IncStat(ts_rcvdecbit); 321 break; 322 } 323 } 324 325 326 /* 327 * NAME: tp_netcmd() 328 * 329 * CALLED FROM: 330 * 331 * FUNCTION and ARGUMENTS: 332 * 333 * RETURNS: 334 * 335 * SIDE EFFECTS: 336 * 337 * NOTES: 338 */ 339 tp_netcmd( tpcb, cmd ) 340 struct tp_pcb *tpcb; 341 int cmd; 342 { 343 #if NARGOXTWENTYFIVE > 0 344 switch (cmd) { 345 346 case CONN_CLOSE: 347 case CONN_REFUSE: 348 cons_netcmd( cmd, tpcb->tp_npcb, 0, tpcb->tp_class == TP_CLASS_4); 349 /* TODO: can this last param be replaced by 350 * tpcb->tp_netserv != ISO_CONS?) 351 */ 352 break; 353 354 default: 355 printf("tp_netcmd(0x%x, 0x%x) NOT IMPLEMENTED\n", tpcb, cmd); 356 break; 357 } 358 #else NARGOXTWENTYFIVE 359 printf("tp_netcmd(): X25 NOT CONFIGURED!!\n"); 360 #endif NARGOXTWENTYFIVE > 0 361 } 362 /* 363 * CALLED FROM: 364 * tp_ctloutput() and tp_emit() 365 * FUNCTION and ARGUMENTS: 366 * Convert a class mask to the highest numeric value it represents. 367 */ 368 369 int 370 tp_mask_to_num(x) 371 u_char x; 372 { 373 register int j; 374 375 for(j = 4; j>=0 ;j--) { 376 if(x & (1<<j)) 377 break; 378 } 379 ASSERT( (j == 4) || (j == 0) ); /* for now */ 380 if( (j != 4) && (j != 0) ) { 381 printf("ASSERTION ERROR: tp_mask_to_num: x 0x%x j %d\n", 382 x, j); 383 } 384 IFTRACE(D_TPINPUT) 385 tptrace(TPPTmisc, "tp_mask_to_num(x) returns j", x, j, 0, 0); 386 ENDTRACE 387 IFDEBUG(D_TPINPUT) 388 printf("tp_mask_to_num(0x%x) returns 0x%x\n", x, j); 389 ENDDEBUG 390 return j; 391 } 392 393 static 394 copyQOSparms(src, dst) 395 struct tp_conn_param *src, *dst; 396 { 397 /* copy all but the bits stuff at the end */ 398 #define COPYSIZE (12 * sizeof(short)) 399 400 bcopy((caddr_t)src, (caddr_t)dst, COPYSIZE); 401 dst->p_tpdusize = src->p_tpdusize; 402 dst->p_ack_strat = src->p_ack_strat; 403 dst->p_rx_strat = src->p_rx_strat; 404 #undef COPYSIZE 405 } 406 407 /* 408 * CALLED FROM: 409 * tp_usrreq on PRU_CONNECT and tp_input on receipt of CR 410 * 411 * FUNCTION and ARGUMENTS: 412 * route directly to x.25 if the address is type 37 - GROT. 413 * furthermore, let TP0 handle only type-37 addresses 414 * 415 * Since this assumes that its address argument is in a mbuf, the 416 * parameter was changed to reflect this assumtion. This also 417 * implies that an mbuf must be allocated when this is 418 * called from tp_input 419 * 420 * RETURNS: 421 * errno value : 422 * EAFNOSUPPORT if can't find an nl_protosw for x.25 (really could panic) 423 * ECONNREFUSED if trying to run TP0 with non-type 37 address 424 * possibly other E* returned from cons_netcmd() 425 * NOTE: 426 * Would like to eliminate as much of this as possible -- 427 * only one set of defaults (let the user set the parms according 428 * to parameters provided in the directory service). 429 * Left here for now 'cause we don't yet have a clean way to handle 430 * it on the passive end. 431 */ 432 int 433 tp_route_to( m, tpcb, channel) 434 struct mbuf *m; 435 register struct tp_pcb *tpcb; 436 u_int channel; 437 { 438 register struct sockaddr_iso *siso; /* NOTE: this may be a sockaddr_in */ 439 extern struct tp_conn_param tp_conn_param[]; 440 int error = 0; 441 int vc_to_kill = 0; /* kludge */ 442 443 siso = mtod(m, struct sockaddr_iso *); 444 IFTRACE(D_CONN) 445 tptraceTPCB(TPPTmisc, 446 "route_to: so afi netservice class", 447 tpcb->tp_sock, siso->siso_addr.isoa_genaddr[0], tpcb->tp_netservice, 448 tpcb->tp_class); 449 ENDTRACE 450 IFDEBUG(D_CONN) 451 printf("tp_route_to( m x%x, channel 0x%x, tpcb 0x%x netserv 0x%x)\n", 452 m, channel, tpcb, tpcb->tp_netservice); 453 printf("m->mlen x%x, m->m_data:\n", m->m_len); 454 dump_buf(mtod(m, caddr_t), m->m_len); 455 ENDDEBUG 456 if( siso->siso_family != tpcb->tp_domain ) { 457 error = EAFNOSUPPORT; 458 goto done; 459 } 460 IFDEBUG(D_CONN) 461 printf("tp_route_to calling nlp_pcbconn, netserv %d\n", 462 tpcb->tp_netservice); 463 ENDDEBUG 464 error = (tpcb->tp_nlproto->nlp_pcbconn)(tpcb->tp_sock->so_pcb, m); 465 if( error ) 466 goto done; 467 468 { 469 register int save_netservice = tpcb->tp_netservice; 470 471 switch(tpcb->tp_netservice) { 472 case ISO_COSNS: 473 case ISO_CLNS: 474 /* This is a kludge but seems necessary so the passive end 475 * can get long enough timers. sigh. 476 if( siso->siso_addr.osinet_idi[1] == (u_char)IDI_OSINET ) 477 */ 478 #define IDI_OSINET 0x0004 /* bcd of "0004" */ 479 if( siso->siso_addr.isoa_genaddr[2] == (char)IDI_OSINET ) { 480 if( tpcb->tp_dont_change_params == 0) { 481 copyQOSparms( &tp_conn_param[ISO_COSNS], 482 &tpcb->_tp_param); 483 } 484 tpcb->tp_flags |= TPF_NLQOS_PDN; 485 } 486 /* drop through to IN_CLNS*/ 487 case IN_CLNS: 488 if (iso_localifa(siso)) 489 tpcb->tp_flags |= TPF_PEER_ON_SAMENET; 490 if( (tpcb->tp_class & TP_CLASS_4)==0 ) { 491 error = EPROTOTYPE; 492 break; 493 } 494 tpcb->tp_class = TP_CLASS_4; /* IGNORE dont_change_parms */ 495 break; 496 497 case ISO_CONS: 498 #if NARGOXTWENTYFIVE > 0 499 tpcb->tp_flags |= TPF_NLQOS_PDN; 500 if( tpcb->tp_dont_change_params == 0 ) { 501 copyQOSparms( &tp_conn_param[ISO_CONS], 502 &tpcb->_tp_param); 503 } 504 /* 505 * for use over x.25 really need a small receive window, 506 * need to start slowly, need small max negotiable tpdu size, 507 * and need to use the congestion window to the max 508 * IGNORES tp_dont_change_params for these! 509 */ 510 if( tpcb->tp_sock->so_snd.sb_hiwat > 512 ) { 511 (void) soreserve(tpcb->tp_sock, 512, 512 );/* GAG */ 512 } 513 tpcb->tp_rx_strat = TPRX_USE_CW; 514 515 if( (tpcb->tp_nlproto != &nl_protosw[ISO_CONS]) ) { 516 /* if the listener was restricting us to clns, 517 * ( we never get here if the listener isn't af_iso ) 518 * refuse the connection : 519 * but we don't have a way to restrict thus - it's 520 * utterly permissive. 521 if(channel) { 522 (void) cons_netcmd(CONN_REFUSE, tpcb->tp_npcb, 523 channel, tpcb->tp_class == TP_CLASS_4); 524 error = EPFNOSUPPORT; 525 goto done; 526 } 527 */ 528 IFDEBUG(D_CONN) 529 printf( 530 "tp_route_to( CHANGING nlproto old 0x%x new 0x%x)\n", 531 tpcb->tp_nlproto , &nl_protosw[ISO_CONS]); 532 ENDDEBUG 533 tpcb->tp_nlproto = &nl_protosw[ISO_CONS]; 534 } 535 /* Now we've got the right nl_protosw. 536 * If we already have a channel (we were called from tp_input()) 537 * tell cons that we'll hang onto this channel. 538 * If we don't already have one (we were called from usrreq()) 539 * -and if it's TP0 open a net connection and wait for it to finish. 540 */ 541 if( channel ) { 542 error = cons_netcmd( CONN_CONFIRM, tpcb->tp_npcb, 543 channel, tpcb->tp_class == TP_CLASS_4); 544 vc_to_kill ++; 545 } else if( tpcb->tp_class != TP_CLASS_4 /* class 4 only */) { 546 /* better open vc if any possibility of ending up 547 * in non-multiplexing class 548 */ 549 error = cons_openvc(tpcb->tp_npcb, siso, tpcb->tp_sock); 550 vc_to_kill ++; 551 } 552 /* class 4 doesn't need to open a vc now - may use one already 553 * opened or may open one only when it sends a pkt. 554 */ 555 #else NARGOXTWENTYFIVE > 0 556 error = ECONNREFUSED; 557 #endif NARGOXTWENTYFIVE > 0 558 break; 559 default: 560 error = EPROTOTYPE; 561 } 562 563 ASSERT( save_netservice == tpcb->tp_netservice); 564 } 565 if( error && vc_to_kill ) { 566 tp_netcmd( tpcb, CONN_CLOSE); 567 goto done; 568 } 569 { /* start with the global rtt, rtv stats */ 570 register int i = 571 (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN); 572 573 tpcb->tp_rtt = tp_stat.ts_rtt[i]; 574 tpcb->tp_rtv = tp_stat.ts_rtv[i]; 575 } 576 done: 577 IFDEBUG(D_CONN) 578 printf("tp_route_to returns 0x%x\n", error); 579 ENDDEBUG 580 IFTRACE(D_CONN) 581 tptraceTPCB(TPPTmisc, "route_to: returns: error netserv class", error, 582 tpcb->tp_netservice, tpcb->tp_class, 0); 583 ENDTRACE 584 return error; 585 } 586 587 #ifdef TP_PERF_MEAS 588 /* 589 * CALLED FROM: 590 * tp_ctloutput() when the user sets TPOPT_PERF_MEAS on 591 * and tp_newsocket() when a new connection is made from 592 * a listening socket with tp_perf_on == true. 593 * FUNCTION and ARGUMENTS: 594 * (tpcb) is the usual; this procedure gets a clear cluster mbuf for 595 * a tp_pmeas structure, and makes tpcb->tp_p_meas point to it. 596 * RETURN VALUE: 597 * ENOBUFS if it cannot get a cluster mbuf. 598 */ 599 600 int 601 tp_setup_perf(tpcb) 602 register struct tp_pcb *tpcb; 603 { 604 register struct mbuf *q; 605 606 if( tpcb->tp_p_meas == 0 ) { 607 MGET(q, M_WAITOK, MT_PCB); 608 if (q == 0) 609 return ENOBUFS; 610 MCLGET(q, M_WAITOK); 611 if ((q->m_flags & M_EXT) == 0) { 612 (void) m_free(q); 613 return ENOBUFS; 614 } 615 q->m_len = sizeof (struct tp_pmeas); 616 tpcb->tp_p_mbuf = q; 617 tpcb->tp_p_meas = mtod(q, struct tp_pmeas *); 618 bzero( (caddr_t)tpcb->tp_p_meas, sizeof (struct tp_pmeas) ); 619 IFDEBUG(D_PERF_MEAS) 620 printf( 621 "tpcb 0x%x so 0x%x ref 0x%x tp_p_meas 0x%x tp_perf_on 0x%x\n", 622 tpcb, tpcb->tp_sock, tpcb->tp_lref, 623 tpcb->tp_p_meas, tpcb->tp_perf_on); 624 ENDDEBUG 625 tpcb->tp_perf_on = 1; 626 } 627 return 0; 628 } 629 #endif TP_PERF_MEAS 630 631 #ifdef ARGO_DEBUG 632 dump_addr (addr) 633 register struct sockaddr *addr; 634 { 635 switch( addr->sa_family ) { 636 case AF_INET: 637 dump_inaddr((struct sockaddr_in *)addr); 638 break; 639 #ifdef ISO 640 case AF_ISO: 641 dump_isoaddr((struct sockaddr_iso *)addr); 642 break; 643 #endif ISO 644 default: 645 printf("BAD AF: 0x%x\n", addr->sa_family); 646 break; 647 } 648 } 649 650 #define MAX_COLUMNS 8 651 /* 652 * Dump the buffer to the screen in a readable format. Format is: 653 * 654 * hex/dec where hex is the hex format, dec is the decimal format. 655 * columns of hex/dec numbers will be printed, followed by the 656 * character representations (if printable). 657 */ 658 Dump_buf(buf, len) 659 caddr_t buf; 660 int len; 661 { 662 int i,j; 663 664 printf("Dump buf 0x%x len 0x%x\n", buf, len); 665 for (i = 0; i < len; i += MAX_COLUMNS) { 666 printf("+%d:\t", i); 667 for (j = 0; j < MAX_COLUMNS; j++) { 668 if (i + j < len) { 669 printf("%x/%d\t", buf[i+j]&0xff, buf[i+j]); 670 } else { 671 printf(" "); 672 } 673 } 674 675 for (j = 0; j < MAX_COLUMNS; j++) { 676 if (i + j < len) { 677 if (((buf[i+j]) > 31) && ((buf[i+j]) < 128)) 678 printf("%c", buf[i+j]&0xff); 679 else 680 printf("."); 681 } 682 } 683 printf("\n"); 684 } 685 } 686 687 688 #endif ARGO_DEBUG 689 690