summaryrefslogtreecommitdiffstats
path: root/c/src/exec/libnetworking/lib
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/exec/libnetworking/lib')
-rw-r--r--c/src/exec/libnetworking/lib/README1
-rw-r--r--c/src/exec/libnetworking/lib/getprotoby.c42
-rw-r--r--c/src/exec/libnetworking/lib/syslog.c187
-rw-r--r--c/src/exec/libnetworking/lib/tftpDriver.c609
4 files changed, 839 insertions, 0 deletions
diff --git a/c/src/exec/libnetworking/lib/README b/c/src/exec/libnetworking/lib/README
new file mode 100644
index 0000000000..787c24c012
--- /dev/null
+++ b/c/src/exec/libnetworking/lib/README
@@ -0,0 +1 @@
+Sources from application-level (as opposed to kernel-level) libraries.
diff --git a/c/src/exec/libnetworking/lib/getprotoby.c b/c/src/exec/libnetworking/lib/getprotoby.c
new file mode 100644
index 0000000000..b90c247a54
--- /dev/null
+++ b/c/src/exec/libnetworking/lib/getprotoby.c
@@ -0,0 +1,42 @@
+#include <netdb.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+static const struct protoent prototab[] = {
+ { "ip", NULL, IPPROTO_IP },
+ { "icmp", NULL, IPPROTO_ICMP },
+ { "tcp", NULL, IPPROTO_TCP },
+ { "udp", NULL, IPPROTO_UDP },
+ };
+
+/*
+ * Dummy version of BSD getprotobyname()
+ */
+struct protoent *
+getprotobyname (const char *name)
+{
+ int i;
+
+ for (i = 0 ; i < (sizeof prototab / sizeof prototab[0]) ; i++) {
+ if (strcmp (name, prototab[i].p_name) == 0)
+ return &prototab[i];
+ }
+ return NULL;
+}
+
+/*
+ * Dummy version of BSD getprotobynumber()
+ */
+struct protoent *
+getprotobynumber (int proto)
+{
+ int i;
+
+ for (i = 0 ; i < (sizeof prototab / sizeof prototab[0]) ; i++) {
+ if (proto == prototab[i].p_proto)
+ return &prototab[i];
+ }
+ return NULL;
+}
diff --git a/c/src/exec/libnetworking/lib/syslog.c b/c/src/exec/libnetworking/lib/syslog.c
new file mode 100644
index 0000000000..7f468c81e6
--- /dev/null
+++ b/c/src/exec/libnetworking/lib/syslog.c
@@ -0,0 +1,187 @@
+/*
+ * RTEMS version of syslog and associated routines
+ *
+ * $Id$
+ */
+
+#include <rtems.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+static int LogStatus = LOG_CONS;
+static const char *LogTag = "syslog";
+static int LogFacility = LOG_USER;
+static int LogMask = 0xff;
+
+static int LogFd = -1;
+static rtems_id LogSemaphore;
+extern struct in_addr rtems_bsdnet_log_host_address;
+
+#define SYSLOG_PORT 514
+
+void
+syslog (int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vsyslog (pri, fmt, ap);
+ va_end (ap);
+}
+
+/*
+ * FIXME: Should cbuf be static? It could be if we put the mutex
+ * around the entire body of this routine. Then we wouldn't
+ * have to worry about blowing stacks with a local variable
+ * that large. Could make cbuf bigger, too.
+ */
+void
+vsyslog (int pri, const char *fmt, va_list ap)
+{
+ int cnt;
+ char *cp;
+ char *msgp, cbuf[200];
+ int sent;
+
+ if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
+ syslog (LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID,
+ "syslog: unknown facility/priority: %#x", pri);
+ pri &= LOG_PRIMASK|LOG_FACMASK;
+ }
+
+ if (!LOG_MASK(LOG_PRI(pri)) & LogMask)
+ return;
+
+ if ((pri & LOG_FACMASK) == 0)
+ pri |= LogFacility;
+
+ cnt = sprintf (cbuf, "<%d>", pri);
+ cp = msgp = cbuf + cnt;
+ if (LogTag) {
+ const char *lp = LogTag;
+ while ((*cp = *lp++) != '\0')
+ cp++;
+ }
+ if (LogStatus & LOG_PID) {
+ rtems_id tid;
+ rtems_task_ident (RTEMS_SELF, 0, &tid);
+ cnt = sprintf (cp, "[%#lx]", (unsigned long)tid);
+ cp += cnt;
+ }
+ if (LogTag) {
+ *cp++ = ':';
+ *cp++ = ' ';
+ }
+ cnt = vsprintf (cp, fmt, ap);
+ cnt += cp - cbuf;
+ if (cbuf[cnt-1] == '\n')
+ cbuf[--cnt] = '\0';
+
+ if (LogStatus & LOG_PERROR)
+ printf ("%s\n", cbuf);
+
+ /*
+ * Grab the mutex
+ */
+ sent = 0;
+ if ((rtems_bsdnet_log_host_address.s_addr != INADDR_ANY)
+ && (LogFd >= 0)
+ && (rtems_semaphore_obtain (LogSemaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL)) {
+ /*
+ * Set the destination address/port
+ */
+ struct sockaddr_in farAddress;
+ farAddress.sin_family = AF_INET;
+ farAddress.sin_port = htons (SYSLOG_PORT);
+ farAddress.sin_addr = rtems_bsdnet_log_host_address;
+ memset (farAddress.sin_zero, '\0', sizeof farAddress.sin_zero);
+
+ /*
+ * Send the message
+ */
+ if (sendto (LogFd, cbuf, cnt, 0, (struct sockaddr *)&farAddress, sizeof farAddress) >= 0)
+ sent = 1;
+ rtems_semaphore_release (LogSemaphore);
+ }
+ if (!sent && (LogStatus & LOG_CONS) && !(LogStatus & LOG_PERROR))
+ printf ("%s\n", msgp);
+}
+
+void
+openlog (const char *ident, int logstat, int logfac)
+{
+ rtems_status_code sc;
+ struct sockaddr_in myAddress;
+
+ if (ident != NULL)
+ LogTag = ident;
+ LogStatus = logstat;
+ if (logfac != 0 && (logfac & ~LOG_FACMASK) == 0)
+ LogFacility = logfac;
+
+ /*
+ * Create the socket
+ */
+ if ((LogFd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
+ printf ("Can't create syslog socket: %d\n", errno);
+ return;
+ }
+
+ /*
+ * Bind socket to name
+ */
+ myAddress.sin_family = AF_INET;
+ myAddress.sin_addr.s_addr = INADDR_ANY;
+ myAddress.sin_port = 0;
+ memset (myAddress.sin_zero, '\0', sizeof myAddress.sin_zero);
+ if (bind (LogFd, (struct sockaddr *)&myAddress, sizeof (myAddress)) < 0) {
+ close (LogFd);
+ LogFd = -1;
+ printf ("Can't bind syslog socket: %d\n", errno);
+ return;
+ }
+
+ /*
+ * Create the mutex
+ */
+ sc = rtems_semaphore_create (rtems_build_name('s', 'L', 'o', 'g'),
+ 1,
+ RTEMS_PRIORITY |
+ RTEMS_BINARY_SEMAPHORE |
+ RTEMS_INHERIT_PRIORITY |
+ RTEMS_NO_PRIORITY_CEILING |
+ RTEMS_LOCAL,
+ 0,
+ &LogSemaphore);
+ if (sc != RTEMS_SUCCESSFUL) {
+ printf ("Can't create syslog seamphore: %d\n", sc);
+ close (LogFd);
+ LogFd = -1;
+ }
+}
+
+void
+closelog(void)
+{
+ if (LogFd >= 0) {
+ close (LogFd);
+ LogFd = -1;
+ rtems_semaphore_delete (LogSemaphore);
+ }
+}
+
+int
+setlogmask (int pmask)
+{
+ int omask;
+
+ omask = LogMask;
+ if (pmask != 0)
+ LogMask = pmask;
+ return (omask);
+}
diff --git a/c/src/exec/libnetworking/lib/tftpDriver.c b/c/src/exec/libnetworking/lib/tftpDriver.c
new file mode 100644
index 0000000000..cf75623580
--- /dev/null
+++ b/c/src/exec/libnetworking/lib/tftpDriver.c
@@ -0,0 +1,609 @@
+/*
+ * Trivial File Transfer Protocol (RFC 1350)
+ *
+ * Transfer file to/from remote host
+ *
+ * W. Eric Norum
+ * Saskatchewan Accelerator Laboratory
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric@skatter.usask.ca
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <unistd.h>
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/rtems_bsdnet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*
+ * Range of UDP ports to try
+ */
+#define UDP_PORT_BASE 3180
+
+/*
+ * Pathname prefix
+ */
+#define TFTP_PATHNAME_PREFIX "/TFTP/"
+
+/*
+ * Default limits
+ */
+#define PACKET_REPLY_MILLISECONDS 6000
+#define OPEN_RETRY_LIMIT 10
+#define IO_RETRY_LIMIT 10
+
+/*
+ * TFTP opcodes
+ */
+#define TFTP_OPCODE_RRQ 1
+#define TFTP_OPCODE_WRQ 2
+#define TFTP_OPCODE_DATA 3
+#define TFTP_OPCODE_ACK 4
+#define TFTP_OPCODE_ERROR 5
+
+/*
+ * Largest data transfer
+ */
+#define TFTP_BUFSIZE 512
+
+/*
+ * Packets transferred between machines
+ */
+union tftpPacket {
+ /*
+ * RRQ/WRQ packet
+ */
+ struct tftpRWRQ {
+ rtems_unsigned16 opcode;
+ char filename_mode[TFTP_BUFSIZE];
+ } tftpRWRQ;
+
+ /*
+ * DATA packet
+ */
+ struct tftpDATA {
+ rtems_unsigned16 opcode;
+ rtems_unsigned16 blocknum;
+ rtems_unsigned8 data[TFTP_BUFSIZE];
+ } tftpDATA;
+
+ /*
+ * ACK packet
+ */
+ struct tftpACK {
+ rtems_unsigned16 opcode;
+ rtems_unsigned16 blocknum;
+ } tftpACK;
+
+ /*
+ * ERROR packet
+ */
+ struct tftpERROR {
+ rtems_unsigned16 opcode;
+ rtems_unsigned16 errorCode;
+ char errorMessage[TFTP_BUFSIZE];
+ } tftpERROR;
+};
+
+/*
+ * State of each TFTP stream
+ */
+struct tftpStream {
+ /*
+ * Buffer for storing most recently-received packet
+ */
+ union tftpPacket pkbuf;
+
+ /*
+ * Last block number received
+ */
+ rtems_unsigned16 blocknum;
+
+ /*
+ * Data transfer socket
+ */
+ int socket;
+ struct sockaddr_in myAddress;
+ struct sockaddr_in farAddress;
+
+ /*
+ * Indices into buffer
+ */
+ int nleft;
+ int nused;
+
+ /*
+ * Flags
+ */
+ int firstReply;
+ int eof;
+};
+
+/*
+ * Number of streams open at the same time
+ */
+static rtems_id tftp_mutex;
+static int nStreams;
+static struct tftpStream ** volatile tftpStreams;
+
+/*
+ * Initialize the TFTP driver
+ */
+rtems_device_driver rtems_tftp_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ rtems_status_code sc;
+
+ sc = rtems_semaphore_create (rtems_build_name('T', 'F', 'T', 'P'),
+ 1,
+ RTEMS_FIFO |
+ RTEMS_BINARY_SEMAPHORE |
+ RTEMS_NO_INHERIT_PRIORITY |
+ RTEMS_NO_PRIORITY_CEILING |
+ RTEMS_LOCAL,
+ 0,
+ &tftp_mutex);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+ rtems_io_register_name (TFTP_PATHNAME_PREFIX, major, minor);
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * Set error message
+ * This RTEMS/UNIX error mapping needs to be fixed!
+ */
+static void
+tftpSetErrno (struct tftpStream *tp)
+{
+ unsigned int tftpError;
+ static const int errorMap[] = {
+ 0,
+ ENOENT,
+ EPERM,
+ ENOSPC,
+ EINVAL,
+ ENXIO,
+ EEXIST,
+ ESRCH,
+ 0,
+ };
+
+ tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
+ if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
+ errno = errorMap[tftpError];
+ else
+ errno = 1000 + tftpError;
+}
+
+/*
+ * Send a message to make the other end shut up
+ */
+static void
+sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
+{
+ int len;
+
+ /*
+ * Create the error packet (Unknown transfer ID).
+ */
+ tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR);
+ tp->pkbuf.tftpERROR.errorCode = htons (5);
+ len = sizeof tp->pkbuf.tftpERROR.opcode +
+ sizeof tp->pkbuf.tftpERROR.errorCode + 1;
+ len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
+
+ /*
+ * Send it
+ */
+ sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
+ (struct sockaddr *)to, sizeof *to);
+}
+
+/*
+ * Wait for a data packet
+ */
+static int
+getPacket (struct tftpStream *tp)
+{
+ int len;
+ struct timeval tv;
+
+ tv.tv_sec = 6;
+ tv.tv_usec = 0;
+ setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
+ for (;;) {
+ union {
+ struct sockaddr s;
+ struct sockaddr_in i;
+ } from;
+ int fromlen = sizeof from;
+ len = recvfrom (tp->socket, (char *)&tp->pkbuf,
+ sizeof tp->pkbuf, 0,
+ &from.s, &fromlen);
+ if (len < 0)
+ break;
+ if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
+ if (tp->firstReply) {
+ tp->firstReply = 0;
+ tp->farAddress.sin_port = from.i.sin_port;
+ }
+ if (tp->farAddress.sin_port == from.i.sin_port)
+ break;
+ }
+
+ /*
+ * Packet is from someone with whom we are
+ * not interested. Tell them to go away.
+ */
+ sendStifle (tp, &from.i);
+ }
+ tv.tv_sec = 0;
+ setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
+ return len;
+}
+
+/*
+ * Send an acknowledgement
+ */
+static int
+sendAck (struct tftpStream *tp)
+{
+ /*
+ * Create the acknowledgement
+ */
+ tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
+ tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
+
+ /*
+ * Send it
+ */
+ if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
+ (struct sockaddr *)&tp->farAddress,
+ sizeof tp->farAddress) < 0)
+ return errno;
+ return 0;
+}
+
+/*
+ * Release a stream and clear the pointer to it
+ */
+static void
+releaseStream (int s)
+{
+ rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ free (tftpStreams[s]);
+ tftpStreams[s] = NULL;
+ rtems_semaphore_release (tftp_mutex);
+}
+
+/*
+ * Open a TFTP stream
+ */
+rtems_device_driver rtems_tftp_open(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ rtems_libio_open_close_args_t *ap = pargp;
+ struct tftpStream *tp;
+ int retryCount;
+ rtems_unsigned32 farAddress;
+ int s;
+ int len;
+ char *cp1, *cp2;
+ char *remoteFilename;
+ rtems_interval now;
+ rtems_status_code sc;
+
+ /*
+ * Read-only for now
+ */
+ if (ap->flags & LIBIO_FLAGS_WRITE)
+ return RTEMS_NOT_IMPLEMENTED;
+
+ /*
+ * Pick apart the name into a host:pathname pair
+ */
+ if (strlen (ap->iop->pathname) <= strlen (TFTP_PATHNAME_PREFIX))
+ return RTEMS_INVALID_NAME;
+ cp2 = ap->iop->pathname + strlen (TFTP_PATHNAME_PREFIX);
+ if (*cp2 == '/') {
+ farAddress = rtems_bsdnet_bootp_server_address.s_addr;
+ }
+ else {
+ char *hostname;
+
+ cp1 = cp2;
+ while (*cp2 != '/') {
+ if (*cp2 == '\0')
+ return RTEMS_INVALID_NAME;
+ cp2++;
+ }
+ len = cp2 - cp1;
+ hostname = malloc (len + 1);
+ if (hostname == NULL)
+ return RTEMS_NO_MEMORY;
+ strncpy (hostname, cp1, len);
+ hostname[len] = '\0';
+ farAddress = inet_addr (hostname);
+ free (hostname);
+ }
+ if ((farAddress == 0) || (farAddress == ~0))
+ return RTEMS_INVALID_NAME;
+ if (*++cp2 == '\0')
+ return RTEMS_INVALID_NAME;
+ remoteFilename = cp2;
+ if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
+ return RTEMS_INVALID_NAME;
+
+ /*
+ * Find a free stream
+ */
+ sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ return sc;
+ for (s = 0 ; s < nStreams ; s++) {
+ if (tftpStreams[s] == NULL)
+ break;
+ }
+ if (s == nStreams) {
+ /*
+ * Reallocate stream pointers
+ * Guard against the case where realloc() returns NULL.
+ */
+ struct tftpStream **np;
+
+ np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
+ if (np == NULL) {
+ rtems_semaphore_release (tftp_mutex);
+ return RTEMS_NO_MEMORY;
+ }
+ tftpStreams = np;
+ }
+ tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
+ rtems_semaphore_release (tftp_mutex);
+ if (tp == NULL)
+ return RTEMS_NO_MEMORY;
+ ap->iop->data0 = s;
+ ap->iop->data1 = tp;
+
+ /*
+ * Create the socket
+ */
+ if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
+ releaseStream (s);
+ return RTEMS_TOO_MANY;
+ }
+
+ /*
+ * Bind the socket to a local address
+ */
+ retryCount = 0;
+ rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
+ for (;;) {
+ int try = (now + retryCount) % 10;
+
+ tp->myAddress.sin_family = AF_INET;
+ tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + minor);
+ tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
+ if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
+ break;
+ if (++retryCount == 10) {
+ close (tp->socket);
+ releaseStream (minor);
+ return RTEMS_RESOURCE_IN_USE;
+ }
+ }
+
+ /*
+ * Set the UDP destination to the TFTP server
+ * port on the remote machine.
+ */
+ tp->farAddress.sin_family = AF_INET;
+ tp->farAddress.sin_addr.s_addr = farAddress;
+ tp->farAddress.sin_port = htons (69);
+
+ /*
+ * Start the transfer
+ */
+ tp->firstReply = 1;
+ for (;;) {
+ /*
+ * Create the request
+ */
+ tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
+ cp1 = tp->pkbuf.tftpRWRQ.filename_mode;
+ cp2 = remoteFilename;
+ while ((*cp1++ = *cp2++) != '\0')
+ continue;
+ cp2 = "octet";
+ while ((*cp1++ = *cp2++) != '\0')
+ continue;
+ len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
+
+ /*
+ * Send the request
+ */
+ if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
+ (struct sockaddr *)&tp->farAddress,
+ sizeof tp->farAddress) < 0) {
+ close (tp->socket);
+ releaseStream (minor);
+ return RTEMS_UNSATISFIED;
+ }
+
+ /*
+ * Get reply
+ */
+ len = getPacket (tp);
+ if (len >= (int) sizeof tp->pkbuf.tftpACK) {
+ int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
+ if ((opcode == TFTP_OPCODE_DATA)
+ && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
+ tp->nused = 0;
+ tp->blocknum = 1;
+ tp->nleft = len - 2 * sizeof (rtems_unsigned16);
+ tp->eof = (tp->nleft < TFTP_BUFSIZE);
+ if (sendAck (tp) != 0) {
+ close (tp->socket);
+ releaseStream (minor);
+ return RTEMS_UNSATISFIED;
+ }
+ break;
+ }
+ if (opcode == TFTP_OPCODE_ERROR) {
+ tftpSetErrno (tp);
+ close (tp->socket);
+ releaseStream (ap->iop->data0);
+ return RTEMS_INTERNAL_ERROR;
+ }
+ }
+
+ /*
+ * Keep trying
+ */
+ if (++retryCount >= OPEN_RETRY_LIMIT) {
+ close (tp->socket);
+ releaseStream (minor);
+ return RTEMS_UNSATISFIED;
+ }
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * Read from a TFTP stream
+ */
+rtems_device_driver rtems_tftp_read(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ rtems_libio_rw_args_t *ap = pargp;
+ char *bp;
+ struct tftpStream *tp;
+ int retryCount;
+ int nwant;
+
+ tp = ap->iop->data1;
+
+ /*
+ * Read till user request is satisfied or EOF is reached
+ */
+ bp = ap->buffer;
+ nwant = ap->count;
+ while (nwant) {
+ if (tp->nleft) {
+ int count;
+ if (nwant < tp->nleft)
+ count = nwant;
+ else
+ count = tp->nleft;
+ memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count);
+ tp->nused += count;
+ tp->nleft -= count;
+ bp += count;
+ nwant -= count;
+ if (nwant == 0)
+ break;
+ }
+ if (tp->eof)
+ break;
+
+ /*
+ * Wait for the next packet
+ */
+ retryCount = 0;
+ for (;;) {
+ int len = getPacket (tp);
+ if (len >= (int)sizeof tp->pkbuf.tftpACK) {
+ int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
+ rtems_unsigned16 nextBlock = tp->blocknum + 1;
+ if ((opcode == TFTP_OPCODE_DATA)
+ && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
+ tp->nused = 0;
+ tp->nleft = len - 2 * sizeof (rtems_unsigned16);
+ tp->eof = (tp->nleft < TFTP_BUFSIZE);
+ tp->blocknum++;
+ if (sendAck (tp) != 0)
+ return RTEMS_IO_ERROR;
+ break;
+ }
+ if (opcode == TFTP_OPCODE_ERROR) {
+ tftpSetErrno (tp);
+ return RTEMS_INTERNAL_ERROR;
+ }
+ }
+
+ /*
+ * Keep trying?
+ */
+ if (++retryCount == IO_RETRY_LIMIT)
+ return RTEMS_IO_ERROR;
+ if (sendAck (tp) != 0)
+ return RTEMS_IO_ERROR;
+ }
+ }
+ ap->bytes_moved = ap->count - nwant;
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * Close a TFTP stream
+ */
+rtems_device_driver rtems_tftp_close(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ rtems_libio_open_close_args_t *ap = pargp;
+ struct tftpStream *tp = ap->iop->data1;;
+
+ if (!tp->eof && !tp->firstReply) {
+ /*
+ * Tell the other end to stop
+ */
+ rtems_interval ticksPerSecond;
+ sendStifle (tp, &tp->farAddress);
+ rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
+ rtems_task_wake_after (1 + ticksPerSecond / 10);
+ }
+ close (tp->socket);
+ releaseStream (ap->iop->data0);
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_device_driver rtems_tftp_write(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ return RTEMS_NOT_CONFIGURED;
+}
+
+rtems_device_driver rtems_tftp_control(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ return RTEMS_NOT_CONFIGURED;
+}