]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netatm/atm_aal5.c
This commit was generated by cvs2svn to compensate for changes in r160157,
[FreeBSD/FreeBSD.git] / sys / netatm / atm_aal5.c
1 /*-
2  * ===================================
3  * HARP  |  Host ATM Research Platform
4  * ===================================
5  *
6  *
7  * This Host ATM Research Platform ("HARP") file (the "Software") is
8  * made available by Network Computing Services, Inc. ("NetworkCS")
9  * "AS IS".  NetworkCS does not provide maintenance, improvements or
10  * support of any kind.
11  *
12  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
13  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
14  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
15  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
16  * In no event shall NetworkCS be responsible for any damages, including
17  * but not limited to consequential damages, arising from or relating to
18  * any use of the Software or related support.
19  *
20  * Copyright 1994-1998 Network Computing Services, Inc.
21  *
22  * Copies of this Software may be made, however, the above copyright
23  * notice must be reproduced on all copies.
24  */
25
26 /*
27  * Core ATM Services
28  * -----------------
29  *
30  * ATM AAL5 socket protocol processing
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/lock.h>
38 #include <sys/protosw.h>
39 #include <sys/signalvar.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
42 #include <sys/stat.h>
43 #include <sys/sx.h>
44 #include <sys/systm.h>
45 #include <net/if.h>
46 #include <netatm/port.h>
47 #include <netatm/queue.h>
48 #include <netatm/atm.h>
49 #include <netatm/atm_sys.h>
50 #include <netatm/atm_sap.h>
51 #include <netatm/atm_cm.h>
52 #include <netatm/atm_if.h>
53 #include <netatm/atm_stack.h>
54 #include <netatm/atm_pcb.h>
55 #include <netatm/atm_var.h>
56
57
58 /*
59  * Global variables
60  */
61 u_long          atm_aal5_sendspace = 64 * 1024; /* XXX */
62 u_long          atm_aal5_recvspace = 64 * 1024; /* XXX */
63
64
65 /*
66  * Local functions
67  */
68 static int      atm_aal5_attach(struct socket *, int, struct thread *td);
69 static void     atm_aal5_detach(struct socket *);
70 static int      atm_aal5_bind(struct socket *, struct sockaddr *, 
71                         struct thread *td);
72 static int      atm_aal5_listen(struct socket *, int backlog,
73                         struct thread *td);
74 static int      atm_aal5_connect(struct socket *, struct sockaddr *,
75                         struct thread *td);
76 static int      atm_aal5_accept(struct socket *, struct sockaddr **);
77 static int      atm_aal5_disconnect(struct socket *);
78 static int      atm_aal5_shutdown(struct socket *);
79 static int      atm_aal5_send(struct socket *, int, KBuffer *,
80                         struct sockaddr *, KBuffer *, struct thread *td);
81 static void     atm_aal5_abort(struct socket *);
82 static int      atm_aal5_control(struct socket *, u_long, caddr_t, 
83                         struct ifnet *, struct thread *td);
84 static int      atm_aal5_sense(struct socket *, struct stat *);
85 static int      atm_aal5_sockaddr(struct socket *, struct sockaddr **);
86 static int      atm_aal5_peeraddr(struct socket *, struct sockaddr **);
87 static int      atm_aal5_incoming(void *, Atm_connection *,
88                         Atm_attributes *, void **);
89 static void     atm_aal5_cpcs_data(void *, KBuffer *);
90 static caddr_t  atm_aal5_getname(void *);
91
92
93 /*
94  * New-style socket request routines
95  */
96 struct pr_usrreqs       atm_aal5_usrreqs = {
97         .pru_abort =            atm_aal5_abort,
98         .pru_accept =           atm_aal5_accept,
99         .pru_attach =           atm_aal5_attach,
100         .pru_bind =             atm_aal5_bind,
101         .pru_connect =          atm_aal5_connect,
102         .pru_control =          atm_aal5_control,
103         .pru_detach =           atm_aal5_detach,
104         .pru_disconnect =       atm_aal5_disconnect,
105         .pru_listen =           atm_aal5_listen,
106         .pru_peeraddr =         atm_aal5_peeraddr,
107         .pru_send =             atm_aal5_send,
108         .pru_sense =            atm_aal5_sense,
109         .pru_shutdown =         atm_aal5_shutdown,
110         .pru_sockaddr =         atm_aal5_sockaddr,
111 };
112
113 /*
114  * Local variables
115  */
116 static Atm_endpoint     atm_aal5_endpt = {
117         NULL,
118         ENDPT_SOCK_AAL5,
119         NULL,
120         atm_aal5_getname,
121         atm_sock_connected,
122         atm_sock_cleared,
123         atm_aal5_incoming,
124         NULL,
125         NULL,
126         NULL,
127         atm_aal5_cpcs_data,
128         NULL,
129         NULL,
130         NULL,
131         NULL
132 };
133
134 static Atm_attributes   atm_aal5_defattr = {
135         NULL,                   /* nif */
136         CMAPI_CPCS,             /* api */
137         0,                      /* api_init */
138         0,                      /* headin */
139         0,                      /* headout */
140         {                       /* aal */
141                 T_ATM_PRESENT,
142                 ATM_AAL5
143         },
144         {                       /* traffic */
145                 T_ATM_ABSENT,
146         },
147         {                       /* bearer */
148                 T_ATM_ABSENT,
149         },
150         {                       /* bhli */
151                 T_ATM_ABSENT
152         },
153         {                       /* blli */
154                 T_ATM_ABSENT,
155                 T_ATM_ABSENT,
156         },
157         {                       /* llc */
158                 T_ATM_ABSENT,
159         },
160         {                       /* called */
161                 T_ATM_ABSENT,
162                 {
163                         T_ATM_ABSENT,
164                         0
165                 },
166                 {
167                         T_ATM_ABSENT,
168                         0
169                 }
170         },
171         {                       /* calling */
172                 T_ATM_ABSENT
173         },
174         {                       /* qos */
175                 T_ATM_ABSENT,
176         },
177         {                       /* transit */
178                 T_ATM_ABSENT
179         },
180         {                       /* cause */
181                 T_ATM_ABSENT
182         }
183 };
184
185
186 /*
187  * Handy common code macros
188  */
189 #ifdef DIAGNOSTIC
190 #define ATM_INTRO(f)                                            \
191         int             s, err = 0;                             \
192         s = splnet();                                           \
193         ATM_DEBUG2("aal5 socket %s (%p)\n", f, so);             \
194         /*                                                      \
195          * Stack queue should have been drained                 \
196          */                                                     \
197         if (atm_stackq_head != NULL)                            \
198                 panic("atm_aal5: stack queue not empty");       \
199         ;
200 #else /* !DIAGNOSTIC */
201 #define ATM_INTRO(f)                                            \
202         int             s, err = 0;                             \
203         s = splnet();                                           \
204         ;
205 #endif /* DIAGNOSTIC */
206
207 #define ATM_INTRO_NOERR(f)                                      \
208         int             s;                                      \
209         s = splnet();                                           \
210         ;
211
212 #define ATM_OUTRO()                                             \
213         /*                                                      \
214          * Drain any deferred calls                             \
215          */                                                     \
216         STACK_DRAIN();                                          \
217         (void) splx(s);                                         \
218         return (err);                                           \
219         ;
220
221 #define ATM_OUTRO_NOERR()                                       \
222         /*                                                      \
223          * Drain any deferred calls                             \
224          */                                                     \
225         STACK_DRAIN();                                          \
226         (void) splx(s);                                         \
227         ;
228
229 #define ATM_RETERR(errno) {                                     \
230         err = errno;                                            \
231         goto out;                                               \
232 }
233
234
235 /*
236  * Attach protocol to socket
237  *
238  * Arguments:
239  *      so      pointer to socket
240  *      proto   protocol identifier
241  *      p       pointer to process
242  *
243  * Returns:
244  *      0       request processed
245  *      errno   error processing request - reason indicated
246  *
247  */
248 static int
249 atm_aal5_attach(so, proto, td)
250         struct socket   *so;
251         int             proto;
252         struct thread   *td;
253 {
254         Atm_pcb         *atp;
255
256         ATM_INTRO("attach");
257
258         /*
259          * Do general attach stuff
260          */
261         err = atm_sock_attach(so, atm_aal5_sendspace, atm_aal5_recvspace);
262         if (err)
263                 ATM_RETERR(err);
264
265         /*
266          * Finish up any protocol specific stuff
267          */
268         atp = sotoatmpcb(so);
269         atp->atp_type = ATPT_AAL5;
270
271         /*
272          * Set default connection attributes
273          */
274         atp->atp_attr = atm_aal5_defattr;
275         strncpy(atp->atp_name, "(AAL5)", T_ATM_APP_NAME_LEN);
276
277 out:
278         ATM_OUTRO();
279 }
280
281
282 /*
283  * Detach protocol from socket
284  *
285  * Arguments:
286  *      so      pointer to socket
287  *
288  * Returns:
289  *      0       request processed
290  *      errno   error processing request - reason indicated
291  *
292  */
293 static void
294 atm_aal5_detach(so)
295         struct socket   *so;
296 {
297         ATM_INTRO_NOERR("detach");
298
299         atm_sock_detach(so);
300
301         ATM_OUTRO_NOERR();
302 }
303
304
305 /*
306  * Bind address to socket
307  *
308  * Arguments:
309  *      so      pointer to socket
310  *      addr    pointer to protocol address
311  *      p       pointer to process
312  *
313  * Returns:
314  *      0       request processed
315  *      errno   error processing request - reason indicated
316  *
317  */
318 static int
319 atm_aal5_bind(so, addr, td)
320         struct socket   *so;
321         struct sockaddr *addr;
322         struct thread   *td;
323 {
324         ATM_INTRO("bind");
325
326         err = atm_sock_bind(so, addr);
327
328         ATM_OUTRO();
329 }
330
331
332 /*
333  * Listen for incoming connections
334  *
335  * Arguments:
336  *      so      pointer to socket
337  *      p       pointer to process
338  *
339  * Returns:
340  *      0       request processed
341  *      errno   error processing request - reason indicated
342  *
343  */
344 static int
345 atm_aal5_listen(so, backlog, td)
346         struct socket   *so;
347         int              backlog;
348         struct thread   *td;
349 {
350         ATM_INTRO("listen");
351
352         err = atm_sock_listen(so, &atm_aal5_endpt, backlog);
353
354         ATM_OUTRO();
355 }
356
357
358 /*
359  * Connect socket to peer
360  *
361  * Arguments:
362  *      so      pointer to socket
363  *      addr    pointer to protocol address
364  *      p       pointer to process
365  *
366  * Returns:
367  *      0       request processed
368  *      errno   error processing request - reason indicated
369  *
370  */
371 static int
372 atm_aal5_connect(so, addr, td)
373         struct socket   *so;
374         struct sockaddr *addr;
375         struct thread   *td;
376 {
377         Atm_pcb         *atp;
378
379         ATM_INTRO("connect");
380
381         atp = sotoatmpcb(so);
382
383         /*
384          * Resize send socket buffer to maximum sdu size
385          */
386         if (atp->atp_attr.aal.tag == T_ATM_PRESENT) {
387                 long    size;
388
389                 size = atp->atp_attr.aal.v.aal5.forward_max_SDU_size;
390                 if (size != T_ATM_ABSENT)
391                         if (!sbreserve(&so->so_snd, size, so, td)) {
392                                 err = ENOBUFS;
393                                 ATM_OUTRO();
394                         }
395                                 
396         }
397
398         /*
399          * Now get the socket connected
400          */
401         err = atm_sock_connect(so, addr, &atm_aal5_endpt);
402
403         ATM_OUTRO();
404 }
405
406
407 /*
408  * Accept pending connection
409  *
410  * Arguments:
411  *      so      pointer to socket
412  *      addr    pointer to pointer to contain protocol address
413  *
414  * Returns:
415  *      0       request processed
416  *      errno   error processing request - reason indicated
417  *
418  */
419 static int
420 atm_aal5_accept(so, addr)
421         struct socket   *so;
422         struct sockaddr **addr;
423 {
424         ATM_INTRO("accept");
425
426         /*
427          * Everything is pretty much done already, we just need to
428          * return the caller's address to the user.
429          */
430         err = atm_sock_peeraddr(so, addr);
431
432         ATM_OUTRO();
433 }
434
435
436 /*
437  * Disconnect connected socket
438  *
439  * Arguments:
440  *      so      pointer to socket
441  *
442  * Returns:
443  *      0       request processed
444  *      errno   error processing request - reason indicated
445  *
446  */
447 static int
448 atm_aal5_disconnect(so)
449         struct socket   *so;
450 {
451         ATM_INTRO("disconnect");
452
453         err = atm_sock_disconnect(so);
454
455         ATM_OUTRO();
456 }
457
458
459 /*
460  * Shut down socket data transmission
461  *
462  * Arguments:
463  *      so      pointer to socket
464  *
465  * Returns:
466  *      0       request processed
467  *      errno   error processing request - reason indicated
468  *
469  */
470 static int
471 atm_aal5_shutdown(so)
472         struct socket   *so;
473 {
474         ATM_INTRO("shutdown");
475
476         socantsendmore(so);
477
478         ATM_OUTRO();
479 }
480
481
482 /*
483  * Send user data
484  *
485  * Arguments:
486  *      so      pointer to socket
487  *      flags   send data flags
488  *      m       pointer to buffer containing user data
489  *      addr    pointer to protocol address
490  *      control pointer to buffer containing protocol control data
491  *      p       pointer to process
492  *
493  * Returns:
494  *      0       request processed
495  *      errno   error processing request - reason indicated
496  *
497  */
498 static int
499 atm_aal5_send(so, flags, m, addr, control, td)
500         struct socket   *so;
501         int             flags;
502         KBuffer         *m;
503         struct sockaddr *addr;
504         KBuffer         *control;
505         struct thread   *td;
506 {
507         Atm_pcb         *atp;
508
509         ATM_INTRO("send");
510
511         /*
512          * We don't support any control functions
513          */
514         if (control) {
515                 int     clen;
516
517                 clen = KB_LEN(control);
518                 KB_FREEALL(control);
519                 if (clen) {
520                         KB_FREEALL(m);
521                         ATM_RETERR(EINVAL);
522                 }
523         }
524
525         /*
526          * We also don't support any flags or send-level addressing
527          */
528         if (flags || addr) {
529                 KB_FREEALL(m);
530                 ATM_RETERR(EINVAL);
531         }
532
533         /*
534          * All we've got left is the data, so push it out
535          */
536         atp = sotoatmpcb(so);
537         err = atm_cm_cpcs_data(atp->atp_conn, m);
538         if (err) {
539                 /*
540                  * Output problem, drop packet
541                  */
542                 atm_sock_stat.as_outdrop[atp->atp_type]++;
543                 KB_FREEALL(m);
544         }
545
546 out:
547         ATM_OUTRO();
548 }
549
550
551 /*
552  * Abnormally terminate service
553  *
554  * Arguments:
555  *      so      pointer to socket
556  *
557  * Returns:
558  *      0       request processed
559  *      errno   error processing request - reason indicated
560  *
561  */
562 static void
563 atm_aal5_abort(so)
564         struct socket   *so;
565 {
566         ATM_INTRO_NOERR("abort");
567
568         so->so_error = ECONNABORTED;
569         atm_sock_detach(so);
570
571         ATM_OUTRO_NOERR();
572 }
573
574
575 /*
576  * Do control operation - ioctl system call
577  *
578  * Arguments:
579  *      so      pointer to socket
580  *      cmd     ioctl code
581  *      data    pointer to code specific parameter data area
582  *      ifp     pointer to ifnet structure if it's an interface ioctl
583  *      p       pointer to process
584  *
585  * Returns:
586  *      0       request processed
587  *      errno   error processing request - reason indicated
588  *
589  */
590 static int
591 atm_aal5_control(so, cmd, data, ifp, td)
592         struct socket   *so;
593         u_long          cmd;
594         caddr_t         data;
595         struct ifnet    *ifp;
596         struct thread   *td;
597 {
598         ATM_INTRO("control");
599
600         switch (cmd) {
601
602         default:
603                 err = EOPNOTSUPP;
604         }
605
606         ATM_OUTRO();
607 }
608
609 /*
610  * Sense socket status - fstat system call
611  *
612  * Arguments:
613  *      so      pointer to socket
614  *      st      pointer to file status structure
615  *
616  * Returns:
617  *      0       request processed
618  *      errno   error processing request - reason indicated
619  *
620  */
621 static int
622 atm_aal5_sense(so, st)
623         struct socket   *so;
624         struct stat     *st;
625 {
626         ATM_INTRO("sense");
627
628         /*
629          * Just return the max sdu size for the connection
630          */
631         st->st_blksize = so->so_snd.sb_hiwat;
632
633         ATM_OUTRO();
634 }
635
636
637 /*
638  * Retrieve local socket address
639  *
640  * Arguments:
641  *      so      pointer to socket
642  *      addr    pointer to pointer to contain protocol address
643  *
644  * Returns:
645  *      0       request processed
646  *      errno   error processing request - reason indicated
647  *
648  */
649 static int
650 atm_aal5_sockaddr(so, addr)
651         struct socket   *so;
652         struct sockaddr **addr;
653 {
654         ATM_INTRO("sockaddr");
655
656         err = atm_sock_sockaddr(so, addr);
657
658         ATM_OUTRO();
659 }
660
661
662 /*
663  * Retrieve peer socket address
664  *
665  * Arguments:
666  *      so      pointer to socket
667  *      addr    pointer to pointer to contain protocol address
668  *
669  * Returns:
670  *      0       request processed
671  *      errno   error processing request - reason indicated
672  *
673  */
674 static int
675 atm_aal5_peeraddr(so, addr)
676         struct socket   *so;
677         struct sockaddr **addr;
678 {
679         ATM_INTRO("peeraddr");
680
681         err = atm_sock_peeraddr(so, addr);
682
683         ATM_OUTRO();
684 }
685
686
687 /*
688  * Process Incoming Calls
689  *
690  * This function will receive control when an incoming call has been matched
691  * to one of our registered listen parameter blocks.  Assuming the call passes
692  * acceptance criteria and all required resources are available, we will
693  * create a new protocol control block and socket association.  We must
694  * then await notification of the final SVC setup results.  If any
695  * problems are encountered, we will just tell the connection manager to
696  * reject the call.
697  *
698  * Called at splnet.
699  *
700  * Arguments:
701  *      tok     owner's matched listening token
702  *      cop     pointer to incoming call's connection block
703  *      ap      pointer to incoming call's attributes
704  *      tokp    pointer to location to store our connection token
705  *
706  * Returns:
707  *      0       call is accepted
708  *      errno   call rejected - reason indicated
709  *
710  */
711 static int
712 atm_aal5_incoming(tok, cop, ap, tokp)
713         void            *tok;
714         Atm_connection  *cop;
715         Atm_attributes  *ap;
716         void            **tokp;
717 {
718         Atm_pcb         *atp0 = tok, *atp;
719         struct socket   *so;
720         int             err = 0;
721
722         /*
723          * Allocate a new socket and pcb for this connection.
724          *
725          * Note that our attach function will be called via sonewconn
726          * and it will allocate and setup most of the pcb.
727          */
728         atm_sock_stat.as_inconn[atp0->atp_type]++;
729         so = sonewconn(atp0->atp_socket, 0);
730
731         if (so) {
732                 /*
733                  * Finish pcb setup and pass pcb back to CM
734                  */
735                 atp = sotoatmpcb(so);
736                 atp->atp_conn = cop;
737                 atp->atp_attr = *atp0->atp_conn->co_lattr;
738                 strncpy(atp->atp_name, atp0->atp_name, T_ATM_APP_NAME_LEN);
739                 *tokp = atp;
740         } else {
741                 err = ECONNABORTED;
742                 atm_sock_stat.as_connfail[atp0->atp_type]++;
743         }
744
745         return (err);
746 }
747
748
749 /*
750  * Process Socket VCC Input Data
751  *
752  * Arguments:
753  *      tok     owner's connection token (atm_pcb)
754  *      m       pointer to input packet buffer chain
755  *
756  * Returns:
757  *      none
758  *
759  */
760 static void
761 atm_aal5_cpcs_data(tok, m)
762         void            *tok;
763         KBuffer         *m;
764 {
765         Atm_pcb         *atp = tok;
766         struct socket   *so;
767         int             len;
768
769         so = atp->atp_socket;
770
771         KB_PLENGET(m, len);
772
773         /*
774          * Ensure that the socket is able to receive data and
775          * that there's room in the socket buffer
776          */
777         if (((so->so_state & SS_ISCONNECTED) == 0) ||
778             (so->so_rcv.sb_state & SBS_CANTRCVMORE) ||
779             (len > sbspace(&so->so_rcv))) {
780                 atm_sock_stat.as_indrop[atp->atp_type]++;
781                 KB_FREEALL(m);
782                 return;
783         }
784
785         /*
786          * Queue the data and notify the user
787          */
788         sbappendrecord(&so->so_rcv, m);
789         sorwakeup(so);
790
791         return;
792 }
793
794
795 /*
796  * Process getsockopt/setsockopt system calls
797  *
798  * Arguments:
799  *      so      pointer to socket
800  *      sopt    pointer to socket option info
801  *
802  * Returns:
803  *      0       request processed
804  *      errno   error processing request - reason indicated
805  *
806  */
807 int
808 atm_aal5_ctloutput(so, sopt)
809         struct socket   *so;
810         struct sockopt  *sopt;
811 {
812         Atm_pcb         *atp;
813
814         ATM_INTRO("ctloutput");
815
816         /*
817          * Make sure this is for us
818          */
819         if (sopt->sopt_level != T_ATM_SIGNALING) {
820                 ATM_RETERR(EINVAL);
821         }
822         atp = sotoatmpcb(so);
823         if (atp == NULL) {
824                 ATM_RETERR(ENOTCONN);
825         }
826
827         switch (sopt->sopt_dir) {
828
829         case SOPT_SET:
830                 /*
831                  * setsockopt()
832                  */
833
834                 /*
835                  * Validate socket state
836                  */
837                 switch (sopt->sopt_name) {
838
839                 case T_ATM_ADD_LEAF:
840                 case T_ATM_DROP_LEAF:
841                         if ((so->so_state & SS_ISCONNECTED) == 0) {
842                                 ATM_RETERR(ENOTCONN);
843                         }
844                         break;
845
846                 case T_ATM_CAUSE:
847                 case T_ATM_APP_NAME:
848                         break;
849
850                 default:
851                         if (so->so_state & SS_ISCONNECTED) {
852                                 ATM_RETERR(EISCONN);
853                         }
854                         break;
855                 }
856
857                 /*
858                  * Validate and save user-supplied option data
859                  */
860                 err = atm_sock_setopt(so, sopt, atp);
861
862                 break;
863
864         case SOPT_GET:
865                 /*
866                  * getsockopt()
867                  */
868
869                 /*
870                  * Return option data
871                  */
872                 err = atm_sock_getopt(so, sopt, atp);
873
874                 break;
875         }
876
877 out:
878         ATM_OUTRO();
879 }
880
881
882 /*
883  * Initialize AAL5 Sockets
884  *
885  * Arguments:
886  *      none
887  *
888  * Returns:
889  *      none
890  *
891  */
892 void
893 atm_aal5_init()
894 {
895         /*
896          * Register our endpoint
897          */
898         if (atm_endpoint_register(&atm_aal5_endpt))
899                 panic("atm_aal5_init: register");
900
901         /*
902          * Set default connection attributes
903          */
904         atm_aal5_defattr.aal.v.aal5.forward_max_SDU_size = T_ATM_ABSENT;
905         atm_aal5_defattr.aal.v.aal5.backward_max_SDU_size = T_ATM_ABSENT;
906         atm_aal5_defattr.aal.v.aal5.SSCS_type = T_ATM_NULL;
907 }
908
909
910 /*
911  * Get Connection's Application/Owner Name
912  *
913  * Arguments:
914  *      tok     owner's connection token (atm_pcb)
915  *
916  * Returns:
917  *      addr    pointer to string containing our name
918  *
919  */
920 static caddr_t
921 atm_aal5_getname(tok)
922         void            *tok;
923 {
924         Atm_pcb         *atp = tok;
925
926         return (atp->atp_name);
927 }
928