/[dynamips]/trunk/atm.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/atm.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (hide annotations)
Sat Oct 6 16:23:47 2007 UTC (16 years, 5 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.7-RC1/atm.c
File MIME type: text/plain
File size: 17450 byte(s)
dynamips-0.2.7-RC1

1 dpavlin 1 /*
2 dpavlin 7 * Cisco router simulation platform.
3 dpavlin 1 * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4     *
5     * ATM utility functions and Virtual ATM switch.
6     *
7     * HEC and AAL5 CRC computation functions are from Charles Michael Heard
8     * and can be found at (no licence specified, this is to check!):
9     *
10     * http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/
11     */
12    
13     #include <stdio.h>
14     #include <stdlib.h>
15     #include <string.h>
16     #include <unistd.h>
17     #include <pthread.h>
18     #include <errno.h>
19     #include <sys/select.h>
20     #include <sys/time.h>
21     #include <sys/types.h>
22    
23    
24     #include "utils.h"
25     #include "registry.h"
26     #include "atm.h"
27     #include "net_io.h"
28    
29     /********************************************************************/
30     #define HEC_GENERATOR 0x107 /* x^8 + x^2 + x + 1 */
31     #define COSET_LEADER 0x055 /* x^6 + x^4 + x^2 + 1 */
32    
33     m_uint8_t hec_syndrome_table[256];
34    
35     /* Generate a table of CRC-8 syndromes for all possible input bytes */
36     static void gen_syndrome_table(void)
37     {
38     int i,j,syndrome;
39    
40     for(i=0;i<256;i++) {
41     syndrome = i;
42    
43     for(j=0;j<8;j++) {
44     if (syndrome & 0x80)
45     syndrome = (syndrome << 1) ^ HEC_GENERATOR;
46     else
47     syndrome = (syndrome << 1);
48     }
49     hec_syndrome_table[i] = (unsigned char)syndrome;
50     }
51     }
52    
53     /* Compute HEC field for ATM header */
54     m_uint8_t atm_compute_hec(m_uint8_t *cell_header)
55     {
56     register m_uint8_t hec_accum = 0;
57     register int i;
58    
59     /*
60     * calculate CRC-8 remainder over first four bytes of cell header.
61     * exclusive-or with coset leader & insert into fifth header byte.
62     */
63     for(i=0;i<4;i++)
64     hec_accum = hec_syndrome_table[hec_accum ^ cell_header[i]];
65    
66     return(hec_accum ^ COSET_LEADER);
67     }
68    
69     /* Insert HEC field into an ATM header */
70     void atm_insert_hec(m_uint8_t *cell_header)
71     {
72     cell_header[4] = atm_compute_hec(cell_header);
73     }
74    
75     /* Initialize ATM code (for HEC checksums) */
76     void atm_init(void)
77     {
78     gen_syndrome_table();
79     }
80    
81     /* VPC hash function */
82     static inline u_int atmsw_vpc_hash(u_int vpi)
83     {
84     return((vpi ^ (vpi >> 8)) & (ATMSW_VP_HASH_SIZE-1));
85     }
86    
87     /* VCC hash function */
88     static inline u_int atmsw_vcc_hash(u_int vpi,u_int vci)
89     {
90     return((vpi ^ vci) & (ATMSW_VC_HASH_SIZE-1));
91     }
92    
93     /* VP lookup */
94     atmsw_vp_conn_t *atmsw_vp_lookup(atmsw_table_t *t,netio_desc_t *input,
95     u_int vpi)
96     {
97     atmsw_vp_conn_t *swc;
98    
99     for(swc=t->vp_table[atmsw_vpc_hash(vpi)];swc;swc=swc->next)
100     if ((swc->input == input) && (swc->vpi_in == vpi))
101     return swc;
102    
103     return NULL;
104     }
105    
106     /* VC lookup */
107     atmsw_vc_conn_t *atmsw_vc_lookup(atmsw_table_t *t,netio_desc_t *input,
108     u_int vpi,u_int vci)
109     {
110     atmsw_vc_conn_t *swc;
111    
112     for(swc=t->vc_table[atmsw_vcc_hash(vpi,vci)];swc;swc=swc->next)
113     if ((swc->input == input) && (swc->vpi_in == vpi) &&
114     (swc->vci_in == vci))
115     return swc;
116    
117     return NULL;
118     }
119    
120     /* VP switching */
121     void atmsw_vp_switch(atmsw_vp_conn_t *vpc,m_uint8_t *cell)
122     {
123     m_uint32_t atm_hdr;
124    
125     /* rewrite the atm header with new vpi */
126     atm_hdr = ntohl(*(m_uint32_t *)cell);
127     atm_hdr = atm_hdr & ~ATM_HDR_VPI_MASK;
128     atm_hdr |= vpc->vpi_out << ATM_HDR_VPI_SHIFT;
129     *(m_uint32_t *)cell = htonl(atm_hdr);
130    
131     /* recompute HEC field */
132     atm_insert_hec(cell);
133    
134     /* update the statistics counter */
135     vpc->cell_cnt++;
136     }
137    
138     /* VC switching */
139     void atmsw_vc_switch(atmsw_vc_conn_t *vcc,m_uint8_t *cell)
140     {
141     m_uint32_t atm_hdr;
142    
143     /* rewrite the atm header with new vpi/vci */
144     atm_hdr = ntohl(*(m_uint32_t *)cell);
145    
146     atm_hdr = atm_hdr & ~(ATM_HDR_VPI_MASK|ATM_HDR_VCI_MASK);
147     atm_hdr |= vcc->vpi_out << ATM_HDR_VPI_SHIFT;
148     atm_hdr |= vcc->vci_out << ATM_HDR_VCI_SHIFT;
149     *(m_uint32_t *)cell = htonl(atm_hdr);
150    
151     /* recompute HEC field */
152     atm_insert_hec(cell);
153    
154     /* update the statistics counter */
155     vcc->cell_cnt++;
156     }
157    
158     /* Handle an ATM cell */
159     ssize_t atmsw_handle_cell(atmsw_table_t *t,netio_desc_t *input,
160     m_uint8_t *cell)
161     {
162     m_uint32_t atm_hdr,vpi,vci;
163     netio_desc_t *output = NULL;
164     atmsw_vp_conn_t *vpc;
165     atmsw_vc_conn_t *vcc;
166     ssize_t len;
167    
168     /* Extract VPI/VCI information */
169     atm_hdr = ntohl(*(m_uint32_t *)cell);
170    
171     vpi = (atm_hdr & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT;
172     vci = (atm_hdr & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT;
173    
174     /* VP switching */
175     if ((vpc = atmsw_vp_lookup(t,input,vpi)) != NULL) {
176     atmsw_vp_switch(vpc,cell);
177     output = vpc->output;
178     } else {
179     /* VC switching */
180     if ((vcc = atmsw_vc_lookup(t,input,vpi,vci)) != NULL) {
181     atmsw_vc_switch(vcc,cell);
182     output = vcc->output;
183     }
184     }
185    
186     len = netio_send(output,cell,ATM_CELL_SIZE);
187    
188     if (len != ATM_CELL_SIZE) {
189     t->cell_drop++;
190     return(-1);
191     }
192    
193     return(0);
194     }
195    
196     /* Receive an ATM cell */
197     static int atmsw_recv_cell(netio_desc_t *nio,u_char *atm_cell,ssize_t cell_len,
198     atmsw_table_t *t)
199     {
200     int res;
201    
202     if (cell_len != ATM_CELL_SIZE)
203     return(-1);
204    
205     ATMSW_LOCK(t);
206     res = atmsw_handle_cell(t,nio,atm_cell);
207     ATMSW_UNLOCK(t);
208     return(res);
209     }
210    
211     /* Acquire a reference to an ATM switch (increment reference count) */
212     atmsw_table_t *atmsw_acquire(char *name)
213     {
214     return(registry_find(name,OBJ_TYPE_ATMSW));
215     }
216    
217     /* Release an ATM switch (decrement reference count) */
218     int atmsw_release(char *name)
219     {
220     return(registry_unref(name,OBJ_TYPE_ATMSW));
221     }
222    
223     /* Create a virtual switch table */
224     atmsw_table_t *atmsw_create_table(char *name)
225     {
226     atmsw_table_t *t;
227    
228     /* Allocate a new switch structure */
229     if (!(t = malloc(sizeof(*t))))
230     return NULL;
231    
232     memset(t,0,sizeof(*t));
233     pthread_mutex_init(&t->lock,NULL);
234     mp_create_fixed_pool(&t->mp,"ATM Switch");
235    
236     if (!(t->name = mp_strdup(&t->mp,name)))
237     goto err_name;
238    
239     /* Record this object in registry */
240     if (registry_add(t->name,OBJ_TYPE_ATMSW,t) == -1) {
241     fprintf(stderr,"atmsw_create_table: unable to create switch '%s'\n",
242     name);
243     goto err_reg;
244     }
245    
246     return t;
247    
248     err_reg:
249     err_name:
250     mp_free_pool(&t->mp);
251     free(t);
252     return NULL;
253     }
254    
255     /* Free resources used by a VPC */
256     static void atmsw_release_vpc(atmsw_vp_conn_t *swc)
257     {
258     if (swc) {
259     /* release input NIO */
260     if (swc->input) {
261     netio_rxl_remove(swc->input);
262     netio_release(swc->input->name);
263     }
264    
265     /* release output NIO */
266     if (swc->output)
267     netio_release(swc->output->name);
268     }
269     }
270    
271     /* Free resources used by a VCC */
272     static void atmsw_release_vcc(atmsw_vc_conn_t *swc)
273     {
274     if (swc) {
275     /* release input NIO */
276     if (swc->input) {
277     netio_rxl_remove(swc->input);
278     netio_release(swc->input->name);
279     }
280    
281     /* release output NIO */
282     if (swc->output)
283     netio_release(swc->output->name);
284     }
285     }
286    
287     /* Create a VP switch connection */
288     int atmsw_create_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in,
289     char *nio_output,u_int vpi_out)
290     {
291     atmsw_vp_conn_t *swc;
292     u_int hbucket;
293    
294     ATMSW_LOCK(t);
295    
296     /* Allocate a new switch connection */
297     if (!(swc = mp_alloc(&t->mp,sizeof(*swc)))) {
298     ATMSW_UNLOCK(t);
299     return(-1);
300     }
301    
302     swc->input = netio_acquire(nio_input);
303     swc->output = netio_acquire(nio_output);
304     swc->vpi_in = vpi_in;
305     swc->vpi_out = vpi_out;
306    
307     /* Check these NIOs are valid and the input VPI does not exists */
308     if (!swc->input || !swc->output || atmsw_vp_lookup(t,swc->input,vpi_in))
309     goto error;
310    
311     /* Add as a RX listener */
312     if (netio_rxl_add(swc->input,(netio_rx_handler_t)atmsw_recv_cell,
313     t,NULL) == -1)
314     goto error;
315    
316     hbucket = atmsw_vpc_hash(vpi_in);
317     swc->next = t->vp_table[hbucket];
318     t->vp_table[hbucket] = swc;
319     ATMSW_UNLOCK(t);
320     return(0);
321    
322     error:
323     ATMSW_UNLOCK(t);
324     atmsw_release_vpc(swc);
325     mp_free(swc);
326     return(-1);
327     }
328    
329     /* Delete a VP switch connection */
330     int atmsw_delete_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in,
331     char *nio_output,u_int vpi_out)
332     {
333     netio_desc_t *input,*output;
334     atmsw_vp_conn_t **swc,*p;
335     u_int hbucket;
336    
337     ATMSW_LOCK(t);
338    
339     input = registry_exists(nio_input,OBJ_TYPE_NIO);
340     output = registry_exists(nio_output,OBJ_TYPE_NIO);
341    
342     if (!input || !output) {
343     ATMSW_UNLOCK(t);
344     return(-1);
345     }
346    
347     hbucket = atmsw_vpc_hash(vpi_in);
348     for(swc=&t->vp_table[hbucket];*swc;swc=&(*swc)->next)
349     {
350     p = *swc;
351    
352     if ((p->input == input) && (p->output == output) &&
353     (p->vpi_in == vpi_in) && (p->vpi_out == vpi_out))
354     {
355     /* found a matching VP, remove it */
356     *swc = (*swc)->next;
357     ATMSW_UNLOCK(t);
358    
359     atmsw_release_vpc(p);
360     mp_free(p);
361     return(0);
362     }
363     }
364    
365     ATMSW_UNLOCK(t);
366     return(-1);
367     }
368    
369     /* Create a VC switch connection */
370     int atmsw_create_vcc(atmsw_table_t *t,
371     char *input,u_int vpi_in,u_int vci_in,
372     char *output,u_int vpi_out,u_int vci_out)
373     {
374     atmsw_vc_conn_t *swc;
375     u_int hbucket;
376    
377     ATMSW_LOCK(t);
378    
379     /* Allocate a new switch connection */
380     if (!(swc = mp_alloc(&t->mp,sizeof(*swc)))) {
381     ATMSW_UNLOCK(t);
382     return(-1);
383     }
384    
385     swc->input = netio_acquire(input);
386     swc->output = netio_acquire(output);
387     swc->vpi_in = vpi_in;
388     swc->vci_in = vci_in;
389     swc->vpi_out = vpi_out;
390     swc->vci_out = vci_out;
391    
392     /* Ensure that there is not already VP switching */
393     if (atmsw_vp_lookup(t,swc->input,vpi_in) != NULL) {
394     fprintf(stderr,"atmsw_create_vcc: VP switching already exists for "
395     "VPI=%u\n",vpi_in);
396     goto error;
397     }
398    
399     /* Check these NIOs are valid and the input VPI does not exists */
400     if (!swc->input || !swc->output ||
401     atmsw_vc_lookup(t,swc->input,vpi_in,vci_in))
402     goto error;
403    
404     /* Add as a RX listener */
405     if (netio_rxl_add(swc->input,(netio_rx_handler_t)atmsw_recv_cell,
406     t,NULL) == -1)
407     goto error;
408    
409     hbucket = atmsw_vcc_hash(vpi_in,vci_in);
410     swc->next = t->vc_table[hbucket];
411     t->vc_table[hbucket] = swc;
412     ATMSW_UNLOCK(t);
413     return(0);
414    
415     error:
416     ATMSW_UNLOCK(t);
417     atmsw_release_vcc(swc);
418     mp_free(swc);
419     return(-1);
420     }
421    
422     /* Delete a VC switch connection */
423     int atmsw_delete_vcc(atmsw_table_t *t,
424     char *nio_input,u_int vpi_in,u_int vci_in,
425     char *nio_output,u_int vpi_out,u_int vci_out)
426     {
427     netio_desc_t *input,*output;
428     atmsw_vc_conn_t **swc,*p;
429     u_int hbucket;
430    
431     ATMSW_LOCK(t);
432    
433     input = registry_exists(nio_input,OBJ_TYPE_NIO);
434     output = registry_exists(nio_output,OBJ_TYPE_NIO);
435    
436     hbucket = atmsw_vcc_hash(vpi_in,vci_in);
437     for(swc=&t->vc_table[hbucket];*swc;swc=&(*swc)->next)
438     {
439     p = *swc;
440    
441     if ((p->input == input) && (p->output == output) &&
442     (p->vpi_in == vpi_in) && (p->vci_in == vci_in) &&
443     (p->vpi_out == vpi_out) && (p->vci_out == vci_out))
444     {
445     /* found a matching VP, remove it */
446     *swc = (*swc)->next;
447     ATMSW_UNLOCK(t);
448    
449     atmsw_release_vcc(p);
450     mp_free(p);
451     return(0);
452     }
453     }
454    
455     ATMSW_UNLOCK(t);
456     return(-1);
457     }
458    
459     /* Free resources used by an ATM switch */
460     static int atmsw_free(void *data,void *arg)
461     {
462     atmsw_table_t *t = data;
463     atmsw_vp_conn_t *vp;
464     atmsw_vc_conn_t *vc;
465     int i;
466    
467     /* Remove all VPs */
468     for(i=0;i<ATMSW_VP_HASH_SIZE;i++)
469     for(vp=t->vp_table[i];vp;vp=vp->next)
470     atmsw_release_vpc(vp);
471    
472     /* Remove all VCs */
473     for(i=0;i<ATMSW_VC_HASH_SIZE;i++)
474     for(vc=t->vc_table[i];vc;vc=vc->next)
475     atmsw_release_vcc(vc);
476    
477     mp_free_pool(&t->mp);
478     free(t);
479     return(TRUE);
480     }
481    
482     /* Delete an ATM switch */
483     int atmsw_delete(char *name)
484     {
485     return(registry_delete_if_unused(name,OBJ_TYPE_ATMSW,atmsw_free,NULL));
486     }
487    
488     /* Delete all ATM switches */
489     int atmsw_delete_all(void)
490     {
491     return(registry_delete_type(OBJ_TYPE_ATMSW,atmsw_free,NULL));
492     }
493    
494     /* Save the configuration of an ATM switch */
495     void atmsw_save_config(atmsw_table_t *t,FILE *fd)
496     {
497     atmsw_vp_conn_t *vp;
498     atmsw_vc_conn_t *vc;
499     int i;
500    
501     fprintf(fd,"atmsw create %s\n",t->name);
502    
503     ATMSW_LOCK(t);
504    
505     for(i=0;i<ATMSW_VP_HASH_SIZE;i++) {
506     for(vp=t->vp_table[i];vp;vp=vp->next) {
507     fprintf(fd,"atmsw create_vpc %s %s %u %s %u\n",
508     t->name,vp->input->name,vp->vpi_in,
509     vp->output->name,vp->vpi_out);
510     }
511     }
512    
513     for(i=0;i<ATMSW_VC_HASH_SIZE;i++) {
514     for(vc=t->vc_table[i];vc;vc=vc->next) {
515     fprintf(fd,"atmsw create_vcc %s %s %u %u %s %u %u\n",
516     t->name,vc->input->name,vc->vpi_in,vc->vci_in,
517     vc->output->name,vc->vpi_out,vc->vci_out);
518     }
519     }
520    
521     ATMSW_UNLOCK(t);
522    
523     fprintf(fd,"\n");
524     }
525    
526     /* Save configurations of all ATM switches */
527     static void atmsw_reg_save_config(registry_entry_t *entry,void *opt,int *err)
528     {
529     atmsw_save_config((atmsw_table_t *)entry->data,(FILE *)opt);
530     }
531    
532     void atmsw_save_config_all(FILE *fd)
533     {
534     registry_foreach_type(OBJ_TYPE_ATMSW,atmsw_reg_save_config,fd,NULL);
535     }
536    
537     /* Create a new interface */
538     int atmsw_cfg_create_if(atmsw_table_t *t,char **tokens,int count)
539     {
540     netio_desc_t *nio = NULL;
541     int nio_type;
542    
543     /* at least: IF, interface name, NetIO type */
544     if (count < 3) {
545     fprintf(stderr,"atmsw_cfg_create_if: invalid interface description\n");
546     return(-1);
547     }
548    
549     nio_type = netio_get_type(tokens[2]);
550     switch(nio_type) {
551     case NETIO_TYPE_UNIX:
552     if (count != 5) {
553     fprintf(stderr,"ATMSW: invalid number of arguments "
554     "for UNIX NIO '%s'\n",tokens[1]);
555     break;
556     }
557    
558     nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]);
559     break;
560    
561     case NETIO_TYPE_UDP:
562     if (count != 6) {
563     fprintf(stderr,"ATMSW: invalid number of arguments "
564     "for UDP NIO '%s'\n",tokens[1]);
565     break;
566     }
567    
568     nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]),
569     tokens[4],atoi(tokens[5]));
570     break;
571    
572     case NETIO_TYPE_TCP_CLI:
573     if (count != 5) {
574     fprintf(stderr,"ATMSW: invalid number of arguments "
575     "for TCP CLI NIO '%s'\n",tokens[1]);
576     break;
577     }
578    
579     nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]);
580     break;
581    
582     case NETIO_TYPE_TCP_SER:
583     if (count != 4) {
584     fprintf(stderr,"ATMSW: invalid number of arguments "
585     "for TCP SER NIO '%s'\n",tokens[1]);
586     break;
587     }
588    
589     nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]);
590     break;
591    
592     default:
593     fprintf(stderr,"ATMSW: unknown/invalid NETIO type '%s'\n",
594     tokens[2]);
595     }
596    
597     if (!nio) {
598     fprintf(stderr,"ATMSW: unable to create NETIO descriptor of "
599     "interface %s\n",tokens[1]);
600     return(-1);
601     }
602    
603     netio_release(nio->name);
604     return(0);
605     }
606    
607     /* Create a new Virtual Path Connection */
608     int atmsw_cfg_create_vpc(atmsw_table_t *t,char **tokens,int count)
609     {
610     /* 5 parameters: "VP", InputIF, InVPI, OutputIF, OutVPI */
611     if (count != 5) {
612     fprintf(stderr,"ATMSW: invalid VPC descriptor.\n");
613     return(-1);
614     }
615    
616     return(atmsw_create_vpc(t,tokens[1],atoi(tokens[2]),
617     tokens[3],atoi(tokens[4])));
618     }
619    
620     /* Create a new Virtual Channel Connection */
621     int atmsw_cfg_create_vcc(atmsw_table_t *t,char **tokens,int count)
622     {
623     /* 7 parameters: "VP", InputIF, InVPI/VCI, OutputIF, OutVPI/VCI */
624     if (count != 7) {
625     fprintf(stderr,"ATMSW: invalid VCC descriptor.\n");
626     return(-1);
627     }
628    
629     return(atmsw_create_vcc(t,tokens[1],atoi(tokens[2]),atoi(tokens[3]),
630     tokens[4],atoi(tokens[5]),atoi(tokens[6])));
631     }
632    
633     #define ATMSW_MAX_TOKENS 16
634    
635     /* Handle an ATMSW configuration line */
636     int atmsw_handle_cfg_line(atmsw_table_t *t,char *str)
637     {
638     char *tokens[ATMSW_MAX_TOKENS];
639     int count;
640    
641     if ((count = m_strsplit(str,':',tokens,ATMSW_MAX_TOKENS)) <= 1)
642     return(-1);
643    
644     if (!strcmp(tokens[0],"IF"))
645     return(atmsw_cfg_create_if(t,tokens,count));
646     else if (!strcmp(tokens[0],"VP"))
647     return(atmsw_cfg_create_vpc(t,tokens,count));
648     else if (!strcmp(tokens[0],"VC"))
649     return(atmsw_cfg_create_vcc(t,tokens,count));
650    
651     fprintf(stderr,"ATMSW: Unknown statement \"%s\" (allowed: IF,VP,VC)\n",
652     tokens[0]);
653     return(-1);
654     }
655    
656     /* Read an ATMSW configuration file */
657     int atmsw_read_cfg_file(atmsw_table_t *t,char *filename)
658     {
659     char buffer[1024],*ptr;
660     FILE *fd;
661    
662     if (!(fd = fopen(filename,"r"))) {
663     perror("fopen");
664     return(-1);
665     }
666    
667     while(!feof(fd)) {
668     if (!fgets(buffer,sizeof(buffer),fd))
669     break;
670    
671     /* skip comments and end of line */
672     if ((ptr = strpbrk(buffer,"#\r\n")) != NULL)
673     *ptr = 0;
674    
675     /* analyze non-empty lines */
676     if (strchr(buffer,':'))
677     atmsw_handle_cfg_line(t,buffer);
678     }
679    
680     fclose(fd);
681     return(0);
682     }
683    
684     /* Start a virtual ATM switch */
685     int atmsw_start(char *filename)
686     {
687     atmsw_table_t *t;
688    
689     if (!(t = atmsw_create_table("default"))) {
690     fprintf(stderr,"ATMSW: unable to create virtual fabric table.\n");
691     return(-1);
692     }
693    
694     if (atmsw_read_cfg_file(t,filename) == -1) {
695     fprintf(stderr,"ATMSW: unable to parse configuration file.\n");
696     return(-1);
697     }
698    
699     atmsw_release("default");
700     return(0);
701     }

  ViewVC Help
Powered by ViewVC 1.1.26