home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / internet / tcpipsrc / h / if / Radio / c / ax25cmd < prev    next >
Encoding:
Text File  |  1994-08-29  |  16.4 KB  |  713 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include "global.h"
  6. #include "mbuf.h"
  7. #include "session.h"
  8. #include "ax25.h"
  9. #include "ax_mbx.h"
  10. #include "timer.h"
  11. #include "iface.h"
  12. #include "lapb.h"
  13. #include "cmdparse.h"
  14. #include "netuser.h"
  15. #include "misc.h"
  16. #include "vterm.h"
  17.  
  18. static int doaxreset(int, char **);
  19. static int doaxstat(int, char **);
  20. static void dumpstat(struct ax25_cb *);
  21. static int domycall(int, char **);
  22. static int dodigipeat(int, char **);
  23. static int dot1(int, char **);
  24. static int dot2(int, char **);
  25. static int dot3(int, char **);
  26. static int dot4(int, char **);
  27. static int don2(int, char **);
  28. static int domaxframe(int, char **);
  29. static int doheard(int, char **);
  30. static int dopaclen(int, char **);
  31. static int dopthresh(int, char **);
  32. static int doaxwindow(int, char **);
  33.  
  34. static int mh_compare(const void *, const void *);
  35.  
  36. char *ax25states[] = {
  37.   "Disconnected",
  38.   "Conn pending",
  39.   "Disc pending",
  40.   "Connected",
  41.   "Recovery",
  42.   "Frame Reject",
  43. };
  44.  
  45. static struct cmds axcmds[] = {
  46.   "digipeat",     dodigipeat,     0, NULLCHAR,    NULLCHAR,
  47.   "heard",        doheard,        0, NULLCHAR,    NULLCHAR,
  48.   "maxframe",     domaxframe,     0, NULLCHAR,    NULLCHAR,
  49.   "mycall",       domycall,       0, NULLCHAR,    NULLCHAR,
  50.   "paclen",       dopaclen,       0, NULLCHAR,    NULLCHAR,
  51.   "pthresh",      dopthresh,      0, NULLCHAR,    NULLCHAR,
  52.   "reset",        doaxreset,      2, "ax25 reset <axcb>", NULLCHAR,
  53.   "retry",        don2,           0, NULLCHAR,    NULLCHAR,
  54.   "status",       doaxstat,       0, NULLCHAR,    NULLCHAR,
  55.   "t1",           dot1,           0, NULLCHAR,    NULLCHAR,
  56.   "t2",           dot2,           0, NULLCHAR,    NULLCHAR,
  57.   "t3",           dot3,           0, NULLCHAR,    NULLCHAR,
  58.   "t4",           dot4,           0, NULLCHAR,    NULLCHAR,
  59.   "window",       doaxwindow,     0, NULLCHAR,    NULLCHAR,
  60.   NULLCHAR,
  61. };
  62.  
  63. /* Multiplexer for top-level ax25 command */
  64. int doax25(int argc, char **argv)
  65. {
  66.   return subcmd(axcmds,argc,argv);
  67. }
  68.  
  69. static int doaxreset(int argc, char **argv)
  70. {
  71.   struct ax25_cb *axp;
  72.   extern char notval[];
  73.  
  74.   argc = argc;
  75.  
  76.   axp = (struct ax25_cb *)htol(argv[1]);
  77.   if (!ax25val(axp))
  78.   {
  79.     cwprintf(NULL, notval);
  80.     return 1;
  81.   }
  82.   reset_ax25(axp);
  83.   return 0;
  84. }
  85.  
  86. /* Display AX.25 link level control blocks */
  87. static int doaxstat(int argc, char **argv)
  88. {
  89.   register int i;
  90.   register struct ax25_cb *axp;
  91.   char tmp[10];
  92.   extern char notval[];
  93.  
  94.   if (argc < 2)
  95.   {
  96.     cwprintf(NULL, "    &AXB IF   Snd-Q   Rcv-Q   Remote    State\r\n");
  97.     for (i=0;i<NHASH;i++)
  98.     {
  99.       for (axp = ax25_cb[i];axp != NULLAX25; axp = axp->next)
  100.       {
  101.         pax25(tmp,&axp->addr.dest);
  102.         cwprintf(NULL, "%8lx %-5s%-8d%-8d%-10s%s\r\n",
  103.         (long)axp,axp->interface->name,
  104.         len_q(axp->txq),len_mbuf(axp->rxq),
  105.         tmp,ax25states[axp->state]);
  106.       }
  107.     }
  108.     return 0;
  109.   }
  110.   axp = (struct ax25_cb *)htol(argv[1]);
  111.   if (!ax25val(axp))
  112.   {
  113.     cwprintf(NULL, notval);
  114.     return 1;
  115.   }
  116.   dumpstat(axp);
  117.   return 0;
  118. }
  119. /* Dump one control block */
  120. static void dumpstat(register struct ax25_cb *axp)
  121. {
  122.   char tmp[10];
  123.   int i;
  124.  
  125.   if (axp == NULLAX25 || axp->interface == NULLIF)
  126.     return;
  127.   cwprintf(NULL, "&AXB  IF   Remote   RB V(S) V(R) Unack P Retry State\r\n");
  128.   pax25(tmp,&axp->addr.dest);
  129.   cwprintf(NULL, "%4x %-5s%-9s",(int)axp,axp->interface->name,tmp);
  130.   cwputchar(NULL, axp->rejsent ? 'R' : ' ');
  131.   cwputchar(NULL, axp->remotebusy ? 'B' : ' ');
  132.   cwprintf(NULL, " %4d %4d",axp->vs,axp->vr);
  133.   cwprintf(NULL, " %02u/%02u %u",axp->unack,axp->maxframe,axp->proto);
  134.   cwprintf(NULL, " %02u/%02u",axp->retries,axp->n2);
  135.   cwprintf(NULL, " %s\r\n",ax25states[axp->state]);
  136.  
  137.   cwprintf(NULL, "T1: ");
  138.   if (run_timer(&axp->t1))
  139.     cwprintf(NULL, "%lu",(axp->t1.start - axp->t1.count) * MSPTICK);
  140.   else
  141.     cwprintf(NULL, "stop");
  142.   cwprintf(NULL, "/%lu ms; ",axp->t1.start * MSPTICK);
  143.  
  144.   cwprintf(NULL, "T2: ");
  145.   if (run_timer(&axp->t2))
  146.     cwprintf(NULL, "%lu",(axp->t2.start - axp->t2.count) * MSPTICK);
  147.   else
  148.     cwprintf(NULL, "stop");
  149.   cwprintf(NULL, "/%lu ms; ",axp->t2.start * MSPTICK);
  150.  
  151.   cwprintf(NULL, "T3: ");
  152.   if (run_timer(&axp->t3))
  153.     cwprintf(NULL, "%lu",(axp->t3.start - axp->t3.count) * MSPTICK);
  154.   else
  155.     cwprintf(NULL, "stop");
  156.   cwprintf(NULL, "/%lu ms; ",axp->t3.start * MSPTICK);
  157.  
  158.   cwprintf(NULL, "T4: ");
  159.   if (run_timer(&axp->t4))
  160.     cwprintf(NULL, "%lu",(axp->t4.start - axp->t4.count) * MSPTICK);
  161.   else
  162.     cwprintf(NULL, "stop");
  163.   cwprintf(NULL, "/%lu ms\r\n",axp->t4.start * MSPTICK);
  164.  
  165.   if (axp->addr.ndigis == 0)
  166.     return;
  167.   cwprintf(NULL, "Digipeaters:");
  168.   for (i=0;i<axp->addr.ndigis;i++)
  169.   {
  170.     pax25(tmp,&axp->addr.digis[i]);
  171.     cwprintf(NULL, " %s",tmp);
  172.   }
  173.   cwprintf(NULL, "\r\n");
  174. }
  175.  
  176. /* Display or change our AX.25 address */
  177. static int domycall(int argc, char **argv)
  178. {
  179.   char buf[15];
  180.  
  181.   if (argc < 2)
  182.   {
  183.     pax25(buf,&mycall);
  184.     cwprintf(NULL, "%s\r\n",buf);
  185.     return 0;
  186.   }
  187.   if (setcall(&mycall,argv[1]) == -1)
  188.     return -1;
  189.   mycall.ssid |= E;
  190.   return 0;
  191. }
  192.  
  193. /* Control AX.25 digipeating */
  194. static int dodigipeat(int argc, char **argv)
  195. {
  196.   extern int digipeat;
  197.   extern int32 digisent;
  198.  
  199.   if (argc == 1)
  200.   {
  201.     cwprintf(NULL, "digipeat %s\r\n",digipeat ? "on" : "off");
  202.     cwprintf(NULL, "digipeated packets = %lu\r\n",digisent);
  203.   }
  204.   else
  205.       {
  206.     if (strcmp(argv[1],"on") == 0)
  207.       digipeat = 1;
  208.     else
  209.         digipeat = 0;
  210.   }
  211.   return(0);
  212. }
  213.  
  214. /* Set retransmission timer */
  215. static int dot1(int argc, char **argv)
  216. {
  217.   extern int16 t1init;
  218.  
  219.   if (argc == 1)
  220.   {
  221.     cwprintf(NULL, "T1 %lu ms\r\n",(long)t1init * MSPTICK);
  222.   }
  223.   else
  224.       {
  225.     t1init = (int16)(atol(argv[1]) / MSPTICK);
  226.   }
  227.   return(0);
  228. }
  229.  
  230. /* Set acknowledgement delay timer */
  231. static int dot2(int argc, char **argv)
  232. {
  233.   extern int16 t2init;
  234.  
  235.   if (argc == 1)
  236.   {
  237.     cwprintf(NULL, "T2 %lu ms\r\n",(long)t2init * MSPTICK);
  238.   }
  239.   else
  240.   {
  241.     t2init = (int16)(atol(argv[1]) / MSPTICK);
  242.   }
  243.   return(0);
  244. }
  245.  
  246. /* Set idle timer */
  247. static int dot3(int argc, char **argv)
  248. {
  249.   extern int16 t3init;
  250.  
  251.   if (argc == 1)
  252.   {
  253.     cwprintf(NULL, "T3 %lu ms\r\n",(long)t3init * MSPTICK);
  254.   }
  255.   else
  256.   {
  257.     t3init = (int16)(atol(argv[1]) / MSPTICK);
  258.   }
  259.   return(0);
  260. }
  261.  
  262. /* Set link redundancy timer */
  263. static int dot4(int argc, char **argv)
  264. {
  265.   extern int16 t4init;
  266.  
  267.   if (argc == 1)
  268.   {
  269.     cwprintf(NULL, "T4 %lu sec\r\n",((long)t4init * MSPTICK) / 1000);
  270.   }
  271.   else
  272.   {
  273.     t4init = (int16)((atol(argv[1]) * 1000) / MSPTICK);
  274.   }
  275.   return(0);
  276. }
  277.  
  278. /* Set retry limit count */
  279. static int don2(int argc, char **argv)
  280. {
  281.   extern int16 n2;
  282.  
  283.   if (argc == 1)
  284.   {
  285.     cwprintf(NULL, "Retry %u\r\n",n2);
  286.   }
  287.   else
  288.       {
  289.     n2 = (int16)(atoi(argv[1]));
  290.   }
  291.   return(0);
  292. }
  293.  
  294. /* Set maximum number of frames that will be allowed in flight */
  295. static int domaxframe(int argc, char **argv)
  296. {
  297.   extern int16 maxframe;
  298.  
  299.   if (argc == 1)
  300.   {
  301.     cwprintf(NULL, "Maxframe %u\r\n",maxframe);
  302.   }
  303.   else
  304.       {
  305.     maxframe = (int16)(atoi(argv[1]));
  306.   }
  307.   return(0);
  308. }
  309.  
  310. /* Display the last x number of stations heard in the ether */
  311. static int doheard(int argc, char **argv)
  312. {
  313.   extern struct ax25mh mhlist[];  /* the mheard table (ax25.c) */
  314.   char mhcallsign[10];            /* field for call sign dumpout */
  315.   char *mhtime;                   /* ascii time representation */
  316.   int mhcolumn = 0;               /* page column display counter */
  317.   int mh_entry;                   /* index into mhlist */
  318.  
  319.   argv = argv;
  320.  
  321.   if (argc > 1)                    /* any second arg flushes mhlist */
  322.   {
  323.     for (mh_entry = 0; mh_entry < 40; mh_entry++)
  324.       mhlist[mh_entry].mheard_time = 0;       /* 0 time = empty */
  325.     return(0);
  326.   }
  327.  
  328.   qsort((void *)mhlist, 40, sizeof(struct ax25mh), mh_compare);
  329.  
  330.   for (mh_entry = 0; mh_entry < 40; mh_entry++)
  331.   {
  332.     if (mhlist[mh_entry].mheard_time == 0)   /* empty entry? */
  333.       break;                          /* all done now */
  334.  
  335.     if (mh_entry == 0)
  336.       cwprintf(NULL, "AX25 Stations heard.  (* = heard directly)\r\n");
  337.  
  338.     pax25(mhcallsign,&mhlist[mh_entry].mheard_call); /* dump callsign */
  339.     mhtime = ctime(&mhlist[mh_entry].mheard_time);   /* get time in ascii */
  340.     rip(mhtime);                                     /* lose the \n etc */
  341.     cwprintf(NULL, "%s %s %-11s", mhtime, mhlist[mh_entry].mheard_digi ? " " : "*", mhcallsign) ;
  342.     mhcolumn++;
  343.     if (mhcolumn == 2)
  344.     {
  345.       mhcolumn = 0;
  346.       cwprintf(NULL, "\r\n");
  347.     }
  348.   }
  349.   if (mhcolumn == 1)
  350.     cwprintf(NULL, "\r\n");
  351.   return(0);
  352. }
  353.  
  354. static int mh_compare(const void *mh1, const void *mh2)
  355. {
  356.   struct ax25mh *mheard1 = (struct ax25mh *)mh1;
  357.   struct ax25mh *mheard2 = (struct ax25mh *)mh2;
  358.  
  359.   if (mheard1->mheard_time == mheard2->mheard_time)
  360.     return(0);
  361.   if (mheard1->mheard_time > mheard2->mheard_time)
  362.     return(-1);
  363.   else
  364.       return(1);
  365. }
  366.  
  367. /* Set maximum length of I-frame data field */
  368. static int dopaclen(int argc, char **argv)
  369. {
  370.   extern int16 paclen;
  371.  
  372.   if (argc == 1)
  373.   {
  374.     cwprintf(NULL, "Paclen %u\r\n",paclen);
  375.   }
  376.   else
  377.       {
  378.     paclen = atoi(argv[1]);
  379.   }
  380.   return(0);
  381. }
  382. /* Set size of I-frame above which polls will be sent after a timeout */
  383. static int dopthresh(int argc, char **argv)
  384. {
  385.   extern int16 pthresh;
  386.  
  387.   if (argc == 1)
  388.   {
  389.     cwprintf(NULL, "Pthresh %u\r\n",pthresh);
  390.   }
  391.   else
  392.       {
  393.     pthresh = atoi(argv[1]);
  394.   }
  395.   return(0);
  396. }
  397.  
  398. /* Set high water mark on receive queue that triggers RNR */
  399. static int doaxwindow(int argc, char **argv)
  400. {
  401.   extern int16 axwindow;
  402.  
  403.   if (argc == 1)
  404.   {
  405.     cwprintf(NULL, "Axwindow %u\r\n",axwindow);
  406.   }
  407.   else
  408.       {
  409.     axwindow = atoi(argv[1]);
  410.   }
  411.   return(0);
  412. }
  413. /* End of ax25 subcommands */
  414.  
  415. /* Initiate interactive AX.25 connect to remote station */
  416. int doconnect(int argc, char **argv)
  417. {
  418.   struct ax25_addr dest;
  419.   struct ax25 addr;
  420.   struct interface *ifp;
  421.   struct session *s;
  422.   extern int16 axwindow;
  423.   int i;
  424.  
  425.   for (ifp = ifaces; ifp != NULLIF; ifp = ifp->next)
  426.     if (strcmp(argv[1],ifp->name) == 0)
  427.       break;
  428.  
  429.   if (ifp == NULLIF)
  430.   {
  431.     cwprintf(NULL, "Interface %s unknown\r\n",argv[1]);
  432.     return 1;
  433.   }
  434.  
  435.   if (strcmp(argv[1],"netrom") == 0)
  436.   {
  437.     cwprintf(NULL, "Connect on netrom interface not supported.\r\n");
  438.     return 1;
  439.   }
  440.   setcall(&dest, argv[2]);
  441.   /* See if a session already exists */
  442.   for (s = sessions; s < &sessions[nsessions]; s++)
  443.   {
  444.     if (s->type == AX25TNC && addreq(&s->cb.ax25_cb->addr.dest,&dest))
  445.     {
  446.       cwprintf(NULL, "Session %u to %s already exists\r\n", s - sessions,argv[2]);
  447.       return 1;
  448.     }
  449.   }
  450.   /* Allocate a session descriptor */
  451.   if ((s = newsession()) == NULLSESSION)
  452.   {
  453.     cwprintf(NULL, "Too many sessions\r\n");
  454.     return 1;
  455.   }
  456.   if ((s->name = malloc((unsigned)strlen(argv[2])+1)) != NULLCHAR)
  457.     strcpy(s->name,argv[2]);
  458.   s->type = AX25TNC;
  459.   s->parse = (void(*)())ax_parse;
  460.   s->echo  = TRUE;
  461.   /* use callsign dedicated to this interface */
  462.   memcpy(&addr.source, ifp->hwaddr, sizeof(struct ax25_addr));
  463.   setcall(&addr.dest, argv[2]);
  464.   for (i = 3; i < argc; i++)
  465.     setcall(&addr.digis[i - 3], argv[i]);
  466.   addr.ndigis = i - 3;
  467.   s->cb.ax25_cb = open_ax25(&addr, axwindow, (void(*)())ax_rx, (void(*)())ax_tx, (void(*)())ax_state, ifp, (char *) s);
  468.   s->window = s->cb.ax25_cb->terminal;
  469.   s->cb.ax25_cb->terminal->Session = s;
  470.  
  471.   vterm_open(s->window->vt);
  472.  
  473. /*  {
  474.     wimp_wstate window_state;
  475.  
  476.     wimp_get_wind_state(s->window->Window_Handle, &window_state);
  477.     window_state.o.behind = -1;
  478.     wimp_open_wind(&window_state.o);
  479.   } */
  480.   go(s);
  481.   return 0;
  482. }
  483.  
  484.  
  485. /* Display changes in AX.25 state */
  486. void ax_state(struct ax25_cb *axp, int old, int new)
  487. {
  488.   struct session *s;
  489.  
  490.   s = (struct session *)axp->user;
  491.  
  492.   if (current != NULLSESSION && current->type == AX25TNC && current == s)
  493.   {
  494.     /* Don't print transitions between CONNECTED and RECOVERY */
  495.     if (new != RECOVERY && !(old == RECOVERY && new == CONNECTED))
  496.       cwprintf(NULL, "%s\r\n", ax25states[new]);
  497.     if (new == DISCONNECTED)
  498.       cmdmode();
  499.   }
  500.   if (new == DISCONNECTED)
  501.   {
  502.     axp->user = NULLCHAR;
  503.     freesession(s);
  504.   }
  505. }
  506. /* Handle typed characters on AX.25 connection */
  507. void ax_parse(struct session *active, char *buf, int16 cnt)
  508. {
  509.   struct mbuf *bp;
  510.   register char *cp;
  511.   char c;
  512.  
  513.   if (active->window == NULL && (active == NULLSESSION || active->type != AX25TNC))
  514.     return; /* "can't happen" */
  515.  
  516.   /* If recording is on, record outgoing stuff too */
  517.   if (active->record != NULLFILE)
  518.     fwrite(buf,1,cnt,active->record);
  519.  
  520.   /* Allocate buffer and start it with the PID */
  521.   bp = alloc_mbuf(cnt + 1);
  522.   *bp->data = PID_NO_L3;
  523.   bp->cnt++;
  524.  
  525.   /* Copy keyboard buffer to output, stripping line feeds */
  526.   cp = bp->data + 1;
  527.   while(cnt-- != 0)
  528.   {
  529.     c = *buf++;
  530.     if (c != '\n')
  531.     {
  532.       *cp++ = c;
  533.       bp->cnt++;
  534.     }
  535.   }
  536.   send_ax25(active->cb.ax25_cb,bp);
  537. }
  538. /* Handle new incoming terminal sessions
  539.  * This is the default receive upcall function, used when
  540.  * someone else connects to us
  541.  */
  542. void ax_incom(register struct ax25_cb *axp, int16 cnt)
  543. {
  544.   if (ax25mbox)
  545.   {
  546.     mbx_incom(axp,cnt) ;
  547.   }
  548.   else
  549.       {
  550.     ax_session(axp,cnt) ;
  551.   }
  552.   return ;
  553.  
  554. }
  555.  
  556. /* This function sets up an ax25 chat session */
  557. void ax_session(register struct ax25_cb *axp, int16 cnt)
  558. {
  559.   struct session *s;
  560.   char remote[10];
  561.   extern char hostname[];
  562.   extern int attended;
  563.   char *cp;
  564.   time_t t;
  565.  
  566.   cnt = cnt;
  567.  
  568.   time(&t);
  569.   cp = ctime(&t);
  570.   rip(cp);
  571.  
  572.   pax25(remote, &axp->addr.dest);
  573.   if ((s = newsession()) == NULLSESSION)
  574.   {
  575.     /* Out of sessions */
  576.     disc_ax25(axp);
  577.     return;
  578.   }
  579.   s->type = AX25TNC;
  580.   s->name = malloc((int16) strlen(remote) + 1);
  581.   s->cb.ax25_cb = axp;
  582.   s->parse = (void(*)())ax_parse;
  583.   s->window = axp->terminal;
  584.   s->window->Session  = s;
  585.   s->echo   = TRUE;
  586.   strcpy(s->name, remote);
  587.   axp->r_upcall = (void(*)())ax_rx;
  588.   axp->s_upcall = ax_state;
  589.   axp->t_upcall = (void(*)())ax_tx;
  590.   axp->user = (char *) s;
  591.   vterm_open(s->window->vt);
  592. /* {
  593.     wimp_wstate window_state;
  594.  
  595.     wimp_get_wind_state(s->window->Window_Handle, &window_state);
  596.     window_state.o.behind = -1;
  597.     wimp_open_wind(&window_state.o);
  598.   } */
  599.   cwtitle(axp->terminal, "AX25 - From %s", remote);
  600.   cwprintf(NULL, "%s : Incoming AX25 session %u from %s\r\n", cp, s - sessions, remote);
  601.   log_event(NULL, "AX25 Chatter session requested : %s", remote);
  602.   if (attended)
  603.     aprintf(axp, "Welcome %s to the %s system's 'Chat' mode.\r", remote, hostname);
  604.   else
  605.     aprintf(axp, "Sorry %s, the %s system is UNATTENDED.\r", remote, hostname);
  606. }
  607.  
  608. /* Handle incoming terminal traffic */
  609. void ax_rx(struct ax25_cb *axp, int16 cnt)
  610. {
  611.   extern int ttyflow;
  612.   register struct mbuf *bp;
  613.   register char *s;
  614.   register char *t;
  615.   char *line;
  616.   struct session *active = (struct session *) axp->user;
  617.  
  618.   /* Hold output if we're typing */
  619.   if (axp->window == NULL && (mode != CONV_MODE || active == NULLSESSION || ttyflow == 0
  620.       || active->type != AX25TNC || active->cb.ax25_cb != axp))
  621.     return;
  622.  
  623.   if ((bp = recv_ax25(axp, cnt)) == NULLBUF)
  624.     return;
  625.  
  626.   /* Display received characters, translating CR's to CR/LF */
  627.   if ((line = s = malloc(len_mbuf(bp) + 1)) == NULLCHAR)
  628.   {
  629.     cwprintf(NULL, "Out of memory in AX25CMD\r\n");
  630.     free_p(bp);
  631.     return;
  632.   }
  633.  
  634.   while (bp != NULLBUF)
  635.   {
  636.     t = bp->data;
  637.     while(bp->cnt-- > 0)
  638.     {
  639.       if (*t == '\r')
  640.         *t = '\n';
  641.       *s++ = *t++;
  642.     }
  643.     bp = free_mbuf(bp);
  644.   }
  645.  
  646.   if (active->record)
  647.   {
  648.     fwrite(line, 1, s - line, active->record);
  649.     fflush(active->record);
  650.   }
  651.  
  652.   *s = '\0';
  653.   cwprintf(axp->terminal, line);
  654.   free(line);
  655. }
  656. /* Handle transmit upcalls. Used only for file uploading */
  657. void ax_tx(struct ax25_cb *axp, int16 cnt)
  658. {
  659.   register char *cp;
  660.   struct session *s;
  661.   register struct mbuf *bp;
  662.   int16 size;
  663.   int c;
  664.  
  665.   if ((s = (struct session *)axp->user) == NULLSESSION
  666.       || s->upload == NULLFILE)
  667.     return;
  668.   while(cnt != 0)
  669.   {
  670.     size = min(cnt,axp->paclen+1);
  671.     if ((bp = alloc_mbuf(size)) == NULLBUF)
  672.       break;
  673.     cp = bp->data;
  674.     /* Start with the PID */
  675.     *cp++ = PID_NO_L3;
  676.     bp->cnt++;
  677.  
  678.     /* Now send data characters, translating between local
  679.                      * keyboard end-of-line sequences and the (unwritten)
  680.                      * AX.25 convention, which is carriage-return only
  681.                      */
  682.     while(bp->cnt < size)
  683.     {
  684.       if ((c = getc(s->upload)) == EOF)
  685.         break;
  686.       if (c == '\n')
  687.         c = '\r';
  688.       *cp++ = c;
  689.       bp->cnt++;
  690.     }
  691.     if (bp->cnt > 1)
  692.     {
  693.       send_ax25(axp,bp);
  694.     }
  695.     else
  696.     {
  697.       /* Empty frame, don't bother sending */
  698.       free_p(bp);
  699.       break;
  700.     }
  701.     cnt -= bp->cnt;
  702.   }
  703.   if (cnt != 0)
  704.   {
  705.     /* Error or end-of-file */
  706.     fclose(s->upload);
  707.     s->upload = NULLFILE;
  708.     free(s->ufile);
  709.     s->ufile = NULLCHAR;
  710.   }
  711. }
  712.  
  713.