Oops, forgot a file.
authorJoshua Oreman <oremanj@xenon.get-linux.org>
Mon, 13 Apr 2009 04:20:10 +0000 (21:20 -0700)
committerJoshua Oreman <oremanj@xenon.get-linux.org>
Mon, 13 Apr 2009 04:39:04 +0000 (21:39 -0700)
src/core/fwload.c [new file with mode: 0644]

diff --git a/src/core/fwload.c b/src/core/fwload.c
new file mode 100644 (file)
index 0000000..e9e5087
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Implements an ad-hoc protocol (host end util/fireserve.c) for
+ * chainloading gPXE over FireWire, to make testing faster.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/uri.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/xfer.h>
+#include <gpxe/process.h>
+#include <gpxe/open.h>
+#include <gpxe/malloc.h>
+#include <gpxe/fwtrans.h>
+#include <gpxe/uaccess.h>
+
+#define FWLOAD_BUFSIZE 1024
+
+static struct fwtrans_connection fwload_link;
+
+/* We only support one FW connection at a time */
+static int fwload_in_use;
+
+struct fwload_state {
+       /* Reference count */
+       struct refcnt refcnt;
+
+       /* xfer_interface we're using */
+       struct xfer_interface xfer;
+
+       /* Link-pumping process */
+       struct process process;
+
+       /* Length we're supposed to get in total */
+       size_t file_length;
+
+       /* Length we got so far */
+       size_t rx_length;
+};
+
+static void fwload_done(struct fwload_state *state, int rc)
+{
+       struct fwtrans_connection *l = &fwload_link;
+       l->response = l->idx_get = l->idx_put = 0;
+       l->request = FWTRANS_NAK;
+
+       fwload_in_use--;
+
+       if (rc == 0 && state->rx_length != state->file_length) {
+               DBG("FW %p incorrect length %zd, should be %zd\n",
+                   state, state->rx_length, state->file_length);
+               rc = -EIO;
+       }
+
+
+       process_del(&state->process);
+       xfer_nullify(&state->xfer);
+       xfer_close(&state->xfer, rc);
+}
+
+static void fwload_close(struct xfer_interface *xfer, int rc)
+{
+       struct fwload_state *state = container_of(xfer, struct fwload_state, xfer);
+       fwload_done(state, rc);
+}
+
+static void fwload_free(struct refcnt *refcnt) 
+{
+       struct fwload_state *state = container_of(refcnt, struct fwload_state, refcnt);
+       free(state);
+}
+
+/*
+ * State: If (request & SYN) we're waiting for an open.
+ * If (request & FIN) we're waiting for a close.
+ * Otherwise, read.
+ */
+
+static void fwload_pump(struct process *proc) 
+{
+       struct fwload_state *state = container_of(proc, struct fwload_state, process);
+       struct fwtrans_connection *l = &fwload_link;
+
+       if (l->response & FWTRANS_NAK) {
+               DBG("FW %p host sent NAK\n", state);
+               fwload_done(state, -EIO);
+               return;
+       }
+
+       if (l->request & FWTRANS_SYN) {
+               if (l->response & FWTRANS_ACK) {
+                       state->file_length = FWTRANS_SIZE(l->response);
+                       l->request &= ~FWTRANS_SYN;
+               }
+               return;
+       }
+
+       if (l->request & FWTRANS_FIN) {
+               if (l->response == 0)
+                       fwload_done(state, 0);
+               return;
+       }
+
+       /* Normal read loop */
+
+       /* Save `put', which is volatile, so as not to get confused if
+          it gets changed while we work. */
+       u32 put = l->idx_put;
+       if (l->idx_get > put) {
+               /* The buffer is wrapped; send here-to-end
+                  first. */
+               xfer_deliver_raw(&state->xfer, l->buffer + l->idx_get,
+                                l->buffer_size - l->idx_get);
+               state->rx_length += l->buffer_size - l->idx_get;
+               l->idx_get = 0;
+       }
+       if (l->idx_get < put) {
+               /* Send either the second part of a wrapped buffer or
+                  the entirety of an unwrapped. */
+               xfer_deliver_raw(&state->xfer, l->buffer + l->idx_get,
+                                put - l->idx_get);
+               state->rx_length += put - l->idx_get;
+               l->idx_get = put;
+       }
+
+       if (l->response & FWTRANS_FIN)
+               l->request |= FWTRANS_FIN;
+}
+
+static struct xfer_interface_operations fwload_xfer_ops = {
+       .close          = fwload_close,
+       .vredirect      = ignore_xfer_vredirect,
+       .window         = unlimited_xfer_window,
+       .alloc_iob      = default_xfer_alloc_iob,
+       .deliver_iob    = xfer_deliver_as_raw,
+       .deliver_raw    = ignore_xfer_deliver_raw,
+};
+
+static int fwload_open(struct xfer_interface *xfer, struct uri *uri) 
+{
+       struct fwload_state *state;
+       struct fwtrans_connection *l = &fwload_link;
+       const char *reqfile;
+
+       if (fwload_in_use)
+               return -EBUSY;
+
+       /* Set up structures */
+       state = zalloc(sizeof(*state));
+       if (!state)
+               return -ENOMEM;
+
+       /* In case we've done this before... */
+       free_dma(l->buffer, l->buffer_size);
+
+       l->buffer_size = FWLOAD_BUFSIZE;
+       l->buffer = malloc_dma(l->buffer_size, 16);
+       if (!l->buffer) {
+               free(state);
+               return -ENOMEM;
+       }
+       
+       l->buffer_addr = virt_to_phys(l->buffer);
+       l->response = 0;
+       l->idx_get = l->idx_put = 0;
+       l->magic = FWTRANS_MAGIC;
+
+       state->rx_length = 0;
+
+       printf("fwload: link structure at %08lX\n", virt_to_phys(l));
+
+       if (uri->path)
+               reqfile = uri->path;
+       else if (uri->host)
+               reqfile = uri->host;
+       else
+               reqfile = NULL;
+
+       if (!reqfile) {
+               l->request = 0;
+       } else {
+               strcpy((char *)l->buffer, reqfile);
+               l->request = strlen(reqfile);
+       }
+
+       state->refcnt.free = fwload_free;
+       xfer_init(&state->xfer, &fwload_xfer_ops, &state->refcnt);
+       process_init(&state->process, fwload_pump, &state->refcnt);
+
+       xfer_plug_plug(&state->xfer, xfer);
+       ref_put(&state->refcnt);
+
+       l->request |= FWTRANS_SYN;
+       fwload_in_use++;
+
+       return 0;
+}
+
+struct uri_opener fwload_uri_opener __uri_opener = {
+       .scheme = "fwload",
+       .open   = fwload_open,
+};