OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [rtos/] [rtems/] [c/] [src/] [libnetworking/] [lib/] [tftpDriver.c] - Blame information for rev 173

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 30 unneback
/*
2
 * Trivial File Transfer Protocol (RFC 1350)
3
 *
4
 * Transfer file to/from remote host
5
 *
6
 * W. Eric Norum
7
 * Saskatchewan Accelerator Laboratory
8
 * University of Saskatchewan
9
 * Saskatoon, Saskatchewan, CANADA
10
 * eric@skatter.usask.ca
11
 *
12
 *  $Id: tftpDriver.c,v 1.2 2001-09-27 12:01:52 chris Exp $
13
 */
14
 
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <errno.h>
18
#include <malloc.h>
19
#include <string.h>
20
#include <unistd.h>
21
#include <fcntl.h>
22
#include <rtems.h>
23
#include <rtems/libio.h>
24
#include <rtems/rtems_bsdnet.h>
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <netinet/in.h>
28
#include <arpa/inet.h>
29
 
30
#ifndef set_errno_and_return_minus_one
31
#define set_errno_and_return_minus_one( _error ) \
32
  do { errno = (_error); return -1; } while(0)
33
#endif
34
 
35
 
36
/*
37
 * Range of UDP ports to try
38
 */
39
#define UDP_PORT_BASE   3180
40
 
41
/*
42
 * Pathname prefix
43
 */
44
#define TFTP_PATHNAME_PREFIX    "/TFTP/"
45
 
46
/*
47
 * Default limits
48
 */
49
#define PACKET_REPLY_MILLISECONDS       6000
50
#define OPEN_RETRY_LIMIT        10
51
#define IO_RETRY_LIMIT          10
52
 
53
/*
54
 * TFTP opcodes
55
 */
56
#define TFTP_OPCODE_RRQ         1
57
#define TFTP_OPCODE_WRQ         2
58
#define TFTP_OPCODE_DATA        3
59
#define TFTP_OPCODE_ACK         4
60
#define TFTP_OPCODE_ERROR       5
61
 
62
/*
63
 * Largest data transfer
64
 */
65
#define TFTP_BUFSIZE    512
66
 
67
/*
68
 * Packets transferred between machines
69
 */
70
union tftpPacket {
71
        /*
72
         * RRQ/WRQ packet
73
         */
74
        struct tftpRWRQ {
75
                rtems_unsigned16        opcode;
76
                char                    filename_mode[TFTP_BUFSIZE];
77
        } tftpRWRQ;
78
 
79
        /*
80
         * DATA packet
81
         */
82
        struct tftpDATA {
83
                rtems_unsigned16        opcode;
84
                rtems_unsigned16        blocknum;
85
                rtems_unsigned8         data[TFTP_BUFSIZE];
86
        } tftpDATA;
87
 
88
        /*
89
         * ACK packet
90
         */
91
        struct tftpACK {
92
                rtems_unsigned16        opcode;
93
                rtems_unsigned16        blocknum;
94
        } tftpACK;
95
 
96
        /*
97
         * ERROR packet
98
         */
99
        struct tftpERROR {
100
                rtems_unsigned16        opcode;
101
                rtems_unsigned16        errorCode;
102
                char                    errorMessage[TFTP_BUFSIZE];
103
        } tftpERROR;
104
};
105
 
106
/*
107
 * State of each TFTP stream
108
 */
109
struct tftpStream {
110
        /*
111
         * Buffer for storing most recently-received packet
112
         */
113
        union tftpPacket        pkbuf;
114
 
115
        /*
116
         * Last block number received
117
         */
118
        rtems_unsigned16        blocknum;
119
 
120
        /*
121
         * Data transfer socket
122
         */
123
        int                     socket;
124
        struct sockaddr_in      myAddress;
125
        struct sockaddr_in      farAddress;
126
 
127
        /*
128
         * Indices into buffer
129
         */
130
        int     nleft;
131
        int     nused;
132
 
133
        /*
134
         * Flags
135
         */
136
        int     firstReply;
137
        int     eof;
138
};
139
 
140
/*
141
 * Number of streams open at the same time
142
 */
143
 
144
static rtems_id tftp_mutex;
145
static int nStreams;
146
static struct tftpStream ** volatile tftpStreams;
147
 
148
typedef const char *tftp_node;
149
extern rtems_filesystem_operations_table  rtems_tftp_ops;
150
extern rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
151
 
152
/*
153
 *  Direct copy from the IMFS.  Look at this.
154
 */
155
 
156
rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
157
   5,   /* link_max */
158
   6,   /* max_canon */
159
   7,   /* max_input */
160
   255, /* name_max */
161
   255, /* path_max */
162
   2,   /* pipe_buf */
163
   1,   /* posix_async_io */
164
   2,   /* posix_chown_restrictions */
165
   3,   /* posix_no_trunc */
166
   4,   /* posix_prio_io */
167
   5,   /* posix_sync_io */
168
   6    /* posix_vdisable */
169
};
170
 
171
int rtems_tftp_mount_me(
172
  rtems_filesystem_mount_table_entry_t *temp_mt_entry
173
)
174
{
175
  rtems_status_code  sc;
176
 
177
  temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers;
178
  temp_mt_entry->mt_fs_root.ops      = &rtems_tftp_ops;
179
 
180
  /*
181
   *   We have no tftp filesystem specific data to maintain.  This
182
   *   filesystem may only be mounted ONCE.
183
   *
184
   *   And we maintain no real filesystem nodes, so there is no real root.
185
   */
186
 
187
  temp_mt_entry->fs_info                = NULL;
188
  temp_mt_entry->mt_fs_root.node_access = NULL;
189
 
190
  /*
191
   *  These need to be looked at for full POSIX semantics.
192
   */
193
 
194
  temp_mt_entry->pathconf_limits_and_options = rtems_tftp_limits_and_options;
195
 
196
 
197
  /*
198
   *  Now allocate a semaphore for mutual exclusion.
199
   *
200
   *  NOTE:  This could be in an fsinfo for this filesystem type.
201
   */
202
 
203
  sc = rtems_semaphore_create (
204
    rtems_build_name('T', 'F', 'T', 'P'),
205
    1,
206
    RTEMS_FIFO |
207
    RTEMS_BINARY_SEMAPHORE |
208
    RTEMS_NO_INHERIT_PRIORITY |
209
    RTEMS_NO_PRIORITY_CEILING |
210
    RTEMS_LOCAL,
211
    0,
212
    &tftp_mutex
213
  );
214
 
215
  if (sc != RTEMS_SUCCESSFUL)
216
    set_errno_and_return_minus_one( ENOMEM );
217
 
218
  return 0;
219
}
220
 
221
/*
222
 * Initialize the TFTP driver
223
 */
224
 
225
int rtems_bsdnet_initialize_tftp_filesystem ()
226
{
227
 int                                   status;
228
 rtems_filesystem_mount_table_entry_t *entry;
229
 
230
 status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
231
 if ( status == -1 )
232
   return status;
233
 
234
  status = mount(
235
     &entry,
236
     &rtems_tftp_ops,
237
     RTEMS_FILESYSTEM_READ_ONLY,
238
     NULL,
239
     TFTP_PATHNAME_PREFIX
240
  );
241
 
242
  if ( status )
243
    perror( "TFTP mount failed" );
244
 
245
  return status;
246
}
247
 
248
/*
249
 * Set error message
250
 * This RTEMS/UNIX error mapping needs to be fixed!
251
 */
252
static void
253
tftpSetErrno (struct tftpStream *tp)
254
{
255
        unsigned int tftpError;
256
        static const int errorMap[] = {
257
                0,
258
                ENOENT,
259
                EPERM,
260
                ENOSPC,
261
                EINVAL,
262
                ENXIO,
263
                EEXIST,
264
                ESRCH,
265
                0,
266
        };
267
 
268
        tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
269
        if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
270
                errno = errorMap[tftpError];
271
        else
272
                errno = 1000 + tftpError;
273
}
274
 
275
/*
276
 * Send a message to make the other end shut up
277
 */
278
static void
279
sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
280
{
281
        int len;
282
 
283
        /*
284
         * Create the error packet (Unknown transfer ID).
285
         */
286
        tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR);
287
        tp->pkbuf.tftpERROR.errorCode = htons (5);
288
        len = sizeof tp->pkbuf.tftpERROR.opcode +
289
                                sizeof tp->pkbuf.tftpERROR.errorCode + 1;
290
        len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
291
 
292
        /*
293
         * Send it
294
         */
295
        sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
296
                                        (struct sockaddr *)to, sizeof *to);
297
}
298
 
299
/*
300
 * Wait for a data packet
301
 */
302
static int
303
getPacket (struct tftpStream *tp)
304
{
305
        int len;
306
        struct timeval tv;
307
 
308
        tv.tv_sec = PACKET_REPLY_MILLISECONDS / 1000;
309
        tv.tv_usec = (PACKET_REPLY_MILLISECONDS % 1000) * 1000;
310
        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
311
        for (;;) {
312
                union {
313
                        struct sockaddr s;
314
                        struct sockaddr_in i;
315
                } from;
316
                int fromlen = sizeof from;
317
                len = recvfrom (tp->socket, (char *)&tp->pkbuf,
318
                                                        sizeof tp->pkbuf, 0,
319
                                                        &from.s, &fromlen);
320
                if (len < 0)
321
                        break;
322
                if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
323
                        if (tp->firstReply) {
324
                                tp->firstReply = 0;
325
                                tp->farAddress.sin_port = from.i.sin_port;
326
                        }
327
                        if (tp->farAddress.sin_port == from.i.sin_port)
328
                                break;
329
                }
330
 
331
                /*
332
                 * Packet is from someone with whom we are
333
                 * not interested.  Tell them to go away.
334
                 */
335
                sendStifle (tp, &from.i);
336
        }
337
        tv.tv_sec = 0;
338
        tv.tv_usec = 0;
339
        setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
340
        return len;
341
}
342
 
343
/*
344
 * Send an acknowledgement
345
 */
346
static int
347
sendAck (struct tftpStream *tp)
348
{
349
        /*
350
         * Create the acknowledgement
351
         */
352
        tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
353
        tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
354
 
355
        /*
356
         * Send it
357
         */
358
        if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
359
                                        (struct sockaddr *)&tp->farAddress,
360
                                        sizeof tp->farAddress) < 0)
361
                return errno;
362
        return 0;
363
}
364
 
365
/*
366
 * Release a stream and clear the pointer to it
367
 */
368
static void
369
releaseStream (int s)
370
{
371
        rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
372
        free (tftpStreams[s]);
373
        tftpStreams[s] = NULL;
374
        rtems_semaphore_release (tftp_mutex);
375
}
376
 
377
int rtems_tftp_evaluate_for_make(
378
   const char                         *path,       /* IN     */
379
   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
380
   const char                        **name        /* OUT    */
381
)
382
{
383
  set_errno_and_return_minus_one( EIO );
384
}
385
 
386
/*
387
 * XXX - Fix return values.
388
 */
389
 
390
int rtems_tftp_eval_path(
391
  const char                        *pathname,     /* IN     */
392
  int                                flags,        /* IN     */
393
  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
394
)
395
{
396
 
397
  /*
398
   * Read-only for now
399
   */
400
 
401
  if ( (flags & O_WRONLY) == O_WRONLY )
402
    set_errno_and_return_minus_one( ENOENT );
403
 
404
  /*
405
   * The File system is mounted at TFTP_PATHNAME_PREFIX
406
   * the caller of this routine has striped off this part of the
407
   * name. Save the remainder of the name for use by the open routine.
408
   */
409
 
410
  pathloc->node_access = (void * ) pathname;
411
  pathloc->handlers    = &rtems_tftp_handlers;
412
 
413
  return 0;
414
}
415
 
416
 
417
int rtems_tftp_open(
418
  rtems_libio_t *iop,
419
  const char    *new_name,
420
  unsigned32     flag,
421
  unsigned32     mode
422
)
423
{
424
  struct tftpStream  *tp;
425
  int                 retryCount;
426
  rtems_unsigned32    farAddress;
427
  int                 s;
428
  int                 len;
429
  char               *cp1;
430
  char               *cp2;
431
  char               *remoteFilename;
432
  rtems_interval      now;
433
  rtems_status_code   sc;
434
  char               *hostname;
435
 
436
  /*
437
   * This came from the evaluate path.
438
   */
439
 
440
  cp2 = iop->file_info;
441
 
442
  cp1 = cp2;
443
  while (*cp2 != '/') {
444
    if (*cp2 == '\0')
445
      return ENOENT;
446
    cp2++;
447
  }
448
 
449
  len = cp2 - cp1;
450
  hostname = malloc (len + 1);
451
  if (hostname == NULL)
452
    return ENOMEM;
453
 
454
  strncpy (hostname, cp1, len);
455
  hostname[len] = '\0';
456
  farAddress = inet_addr (hostname);
457
  free (hostname);
458
 
459
  if ((farAddress == 0) || (farAddress == ~0))
460
    return ENOENT;
461
 
462
  if (*++cp2 == '\0')
463
    return ENOENT;
464
 
465
  remoteFilename = cp2;
466
  if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
467
    return ENOENT;
468
 
469
  /*
470
   * Find a free stream
471
   */
472
 
473
  sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
474
  if (sc != RTEMS_SUCCESSFUL)
475
    return EBUSY;
476
 
477
  for (s = 0 ; s < nStreams ; s++) {
478
    if (tftpStreams[s] == NULL)
479
    break;
480
  }
481
 
482
  if (s == nStreams) {
483
    /*
484
     * Reallocate stream pointers
485
     * Guard against the case where realloc() returns NULL.
486
     */
487
    struct tftpStream **np;
488
 
489
    np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
490
    if (np == NULL) {
491
      rtems_semaphore_release (tftp_mutex);
492
      return ENOMEM;
493
    }
494
    tftpStreams = np;
495
  }
496
 
497
  tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
498
  rtems_semaphore_release (tftp_mutex);
499
  if (tp == NULL)
500
    return ENOMEM;
501
  iop->data0 = s;
502
  iop->data1 = tp;
503
 
504
  /*
505
   * Create the socket
506
   */
507
 
508
  if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
509
    releaseStream (s);
510
    return ENOMEM;
511
  }
512
 
513
  /*
514
   * Bind the socket to a local address
515
   */
516
 
517
  retryCount = 0;
518
  rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
519
  for (;;) {
520
    int try = (now + retryCount) % 10;
521
 
522
    tp->myAddress.sin_family = AF_INET;
523
    tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s);
524
    tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
525
    if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
526
      break;
527
    if (++retryCount == 10) {
528
      close (tp->socket);
529
      releaseStream (s);
530
      return EBUSY;
531
    }
532
  }
533
 
534
  /*
535
   * Set the UDP destination to the TFTP server
536
   * port on the remote machine.
537
   */
538
  tp->farAddress.sin_family = AF_INET;
539
  tp->farAddress.sin_addr.s_addr = farAddress;
540
  tp->farAddress.sin_port = htons (69);
541
 
542
  /*
543
   * Start the transfer
544
   */
545
  tp->firstReply = 1;
546
  for (;;) {
547
    /*
548
     * Create the request
549
     */
550
    tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
551
    cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
552
    cp2 = (char *) remoteFilename;
553
    while ((*cp1++ = *cp2++) != '\0')
554
      continue;
555
    cp2 = "octet";
556
    while ((*cp1++ = *cp2++) != '\0')
557
      continue;
558
    len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
559
 
560
    /*
561
     * Send the request
562
     */
563
    if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
564
          (struct sockaddr *)&tp->farAddress,
565
          sizeof tp->farAddress) < 0) {
566
      close (tp->socket);
567
      releaseStream (s);
568
      return EIO;
569
    }
570
 
571
    /*
572
     * Get reply
573
     */
574
    len = getPacket (tp);
575
    if (len >= (int) sizeof tp->pkbuf.tftpACK) {
576
      int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
577
      if ((opcode == TFTP_OPCODE_DATA)
578
       && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
579
        tp->nused = 0;
580
        tp->blocknum = 1;
581
        tp->nleft = len - 2 * sizeof (rtems_unsigned16);
582
        tp->eof = (tp->nleft < TFTP_BUFSIZE);
583
        if (sendAck (tp) != 0) {
584
          close (tp->socket);
585
          releaseStream (s);
586
          return EIO;
587
        }
588
        break;
589
      }
590
      if (opcode == TFTP_OPCODE_ERROR) {
591
        tftpSetErrno (tp);
592
        close (tp->socket);
593
        releaseStream (s);
594
        return EIO;
595
      }
596
    }
597
 
598
    /*
599
     * Keep trying
600
     */
601
    if (++retryCount >= OPEN_RETRY_LIMIT) {
602
      close (tp->socket);
603
      releaseStream (s);
604
      return EIO;
605
    }
606
  }
607
 
608
  return 0;
609
}
610
 
611
/*
612
 * Read from a TFTP stream
613
 */
614
 
615
int rtems_tftp_read(
616
  rtems_libio_t *iop,
617
  void          *buffer,
618
  unsigned32     count
619
)
620
{
621
  char              *bp;
622
  struct tftpStream *tp;
623
  int                retryCount;
624
  int                nwant;
625
 
626
  tp = iop->data1;
627
 
628
  /*
629
   * Read till user request is satisfied or EOF is reached
630
   */
631
 
632
  bp = buffer;
633
  nwant = count;
634
  while (nwant) {
635
    if (tp->nleft) {
636
      int count;
637
      if (nwant < tp->nleft)
638
        count = nwant;
639
      else
640
        count = tp->nleft;
641
      memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
642
      tp->nused += count;
643
      tp->nleft -= count;
644
      bp += count;
645
      nwant -= count;
646
      if (nwant == 0)
647
        break;
648
    }
649
    if (tp->eof)
650
      break;
651
 
652
    /*
653
     * Wait for the next packet
654
     */
655
    retryCount = 0;
656
    for (;;) {
657
      int len = getPacket (tp);
658
      if (len >= (int)sizeof tp->pkbuf.tftpACK) {
659
        int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
660
        rtems_unsigned16 nextBlock = tp->blocknum + 1;
661
        if ((opcode == TFTP_OPCODE_DATA)
662
         && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
663
          tp->nused = 0;
664
          tp->nleft = len - 2 * sizeof (rtems_unsigned16);
665
          tp->eof = (tp->nleft < TFTP_BUFSIZE);
666
          tp->blocknum++;
667
          if (sendAck (tp) != 0)
668
            set_errno_and_return_minus_one( EIO );
669
          break;
670
        }
671
        if (opcode == TFTP_OPCODE_ERROR) {
672
          tftpSetErrno (tp);
673
          return RTEMS_INTERNAL_ERROR;
674
        }
675
      }
676
 
677
      /*
678
       * Keep trying?
679
       */
680
      if (++retryCount == IO_RETRY_LIMIT)
681
        set_errno_and_return_minus_one( EIO );
682
      if (sendAck (tp) != 0)
683
        set_errno_and_return_minus_one( EIO );
684
    }
685
  }
686
 
687
 /*
688
  * XXX - Eric is this right?
689
  *
690
  */
691
  return count - nwant;
692
}
693
 
694
/*
695
 * Close a TFTP stream
696
 */
697
int rtems_tftp_close(
698
  rtems_libio_t *iop
699
)
700
{
701
  struct tftpStream *tp = iop->data1;;
702
 
703
  if (!tp->eof && !tp->firstReply) {
704
    /*
705
     * Tell the other end to stop
706
     */
707
    rtems_interval ticksPerSecond;
708
    sendStifle (tp, &tp->farAddress);
709
    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
710
    rtems_task_wake_after (1 + ticksPerSecond / 10);
711
  }
712
  close (tp->socket);
713
  releaseStream (iop->data0);
714
  return RTEMS_SUCCESSFUL;
715
}
716
 
717
int rtems_tftp_write(
718
  rtems_libio_t *iop,
719
  const void    *buffer,
720
  unsigned32     count
721
)
722
{
723
  return RTEMS_NOT_CONFIGURED;
724
}
725
 
726
rtems_device_driver rtems_tftp_control(
727
  rtems_device_major_number major,
728
  rtems_device_minor_number minor,
729
  void *pargp
730
)
731
{
732
  return RTEMS_NOT_CONFIGURED;
733
}
734
 
735
rtems_filesystem_node_types_t rtems_tftp_node_type(
736
   rtems_filesystem_location_info_t    *pathloc         /* IN */
737
)
738
{
739
  return RTEMS_FILESYSTEM_MEMORY_FILE;
740
}
741
 
742
 
743
rtems_filesystem_operations_table  rtems_tftp_ops = {
744
  rtems_tftp_eval_path,            /* eval_path */
745
  rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
746
  NULL,                            /* link */
747
  NULL,                            /* unlink */
748
  rtems_tftp_node_type,            /* node_type */
749
  NULL,                            /* mknod */
750
  NULL,                            /* chown */
751
  NULL,                            /* freenodinfo */
752
  NULL,                            /* mount */
753
  rtems_tftp_mount_me,             /* initialize */
754
  NULL,                            /* unmount */
755
  NULL,                            /* fsunmount */
756
  NULL,                            /* utime, */
757
  NULL,                            /* evaluate_link */
758
  NULL,                            /* symlink */
759
  NULL,                            /* readlin */
760
};
761
 
762
rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
763
  rtems_tftp_open,
764
  rtems_tftp_close,
765
  rtems_tftp_read,
766
  rtems_tftp_write,
767
  NULL,
768
  NULL,
769
  NULL,
770
  NULL,
771
  NULL,
772
  NULL,
773
  NULL,
774
  NULL,
775
};

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.