Merge commit 'v3.3.0' into upstream
[debian/gnuradio] / usrp / host / lib / fusb_win32.cc
diff --git a/usrp/host/lib/fusb_win32.cc b/usrp/host/lib/fusb_win32.cc
new file mode 100644 (file)
index 0000000..8900576
--- /dev/null
@@ -0,0 +1,266 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2003,2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio 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 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <fusb_win32.h>
+#include <usb.h>
+#include <assert.h>
+#include <stdexcept>
+#include <string.h>
+
+static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size();
+static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE;
+static const int DEFAULT_BUFFER_SIZE = 16 * (1L << 20);                // 16 MB / endpoint
+
+
+static const int USB_TIMEOUT = 1000;   // in milliseconds
+
+
+fusb_devhandle_win32::fusb_devhandle_win32 (usb_dev_handle *udh)
+  : fusb_devhandle (udh)
+{
+  // that's it
+}
+
+fusb_devhandle_win32::~fusb_devhandle_win32 ()
+{
+  // nop
+}
+
+fusb_ephandle *
+fusb_devhandle_win32::make_ephandle (int endpoint, bool input_p,
+                                      int block_size, int nblocks)
+{
+  return new fusb_ephandle_win32 (this, endpoint, input_p,
+                                   block_size, nblocks);
+}
+
+// ----------------------------------------------------------------
+
+fusb_ephandle_win32::fusb_ephandle_win32 (fusb_devhandle_win32 *dh,
+                                             int endpoint, bool input_p,
+                                             int block_size, int nblocks)
+  : fusb_ephandle (endpoint, input_p, block_size, nblocks),
+    d_devhandle (dh), d_input_leftover(0),d_output_short(0)
+{
+  if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
+    throw std::out_of_range ("fusb_ephandle_win32: block_size");
+
+  if (d_nblocks < 0)
+    throw std::out_of_range ("fusb_ephandle_win32: nblocks");
+
+  if (d_block_size == 0)
+    d_block_size = DEFAULT_BLOCK_SIZE;
+
+  if (d_nblocks == 0)
+    d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
+
+  d_buffer = new char [d_block_size*d_nblocks];
+  d_context = new void * [d_nblocks];
+
+  // allocate contexts
+
+  usb_dev_handle *dev = dh->get_usb_dev_handle ();
+  int i;
+
+  if (d_input_p)
+    endpoint |= USB_ENDPOINT_IN;
+
+  for (i=0; i<d_nblocks; i++)
+    usb_bulk_setup_async(dev, &d_context[i], endpoint);
+}
+
+fusb_ephandle_win32::~fusb_ephandle_win32 ()
+{
+  int i;
+
+  stop ();
+
+  for (i=0; i<d_nblocks; i++)
+    usb_free_async(&d_context[i]);
+
+  delete [] d_buffer;
+  delete [] d_context;
+}
+
+bool
+fusb_ephandle_win32::start ()
+{
+  if (d_started)
+    return true;       // already running
+
+  d_started = true;
+
+  d_curr = d_nblocks-1;
+  d_outstanding_write = 0;
+  d_input_leftover =0;
+  d_output_short = 0;
+
+  if (d_input_p){      // fire off all the reads
+    int i;
+
+    for (i=0; i<d_nblocks; i++) {
+      usb_submit_async(d_context[i], (char * ) d_buffer+i*d_block_size,
+                     d_block_size);
+    }
+  }
+
+  return true;
+}
+
+bool
+fusb_ephandle_win32::stop ()
+{
+  if (!d_started)
+    return true;
+
+  if (!d_input_p)
+    wait_for_completion ();
+
+  d_started = false;
+  return true;
+}
+
+int
+fusb_ephandle_win32::write (const void *buffer, int nbytes)
+{
+  int retval=0;
+  char *buf;
+
+  if (!d_started)      // doesn't matter here, but keeps semantics constant
+    return -1;
+
+  if (d_input_p)
+    return -1;
+
+  int bytes_to_write = nbytes;
+  int a=0;
+
+  if (d_output_short != 0) {
+
+       buf = &d_buffer[d_curr*d_block_size + d_block_size - d_output_short];
+       a = std::min(nbytes, d_output_short);
+       memcpy(buf, buffer, a);
+       bytes_to_write -= a;
+       d_output_short -= a;
+
+    if (d_output_short == 0)
+        usb_submit_async(d_context[d_curr],
+                        &d_buffer[d_curr*d_block_size], d_block_size);
+  }
+
+  while (bytes_to_write > 0) {
+    d_curr = (d_curr+1)%d_nblocks;
+    buf = &d_buffer[d_curr*d_block_size];
+
+    if (d_outstanding_write != d_nblocks) {
+      d_outstanding_write++;
+    } else {
+      retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT);
+      if (retval < 0) {
+                 fprintf(stderr, "%s: usb_reap_async: %s\n",
+                         __FUNCTION__, usb_strerror());
+         return retval;
+       }
+    }
+
+    int ncopy = std::min(bytes_to_write, d_block_size);
+    memcpy(buf, (void *) &(((char*)buffer)[a]), ncopy);
+    bytes_to_write -= ncopy;
+    a += ncopy;
+
+    d_output_short = d_block_size - ncopy;
+    if (d_output_short == 0)
+           usb_submit_async(d_context[d_curr], buf, d_block_size);
+  }
+
+  return retval < 0 ? retval : nbytes;
+}
+
+int
+fusb_ephandle_win32::read (void *buffer, int nbytes)
+{
+  int retval=0;
+  char *buf;
+
+  if (!d_started)      // doesn't matter here, but keeps semantics constant
+    return -1;
+
+  if (!d_input_p)
+    return -1;
+
+  int bytes_to_read = nbytes;
+
+  int a=0;
+  if (d_input_leftover != 0) {
+
+       buf = &d_buffer[d_curr*d_block_size + d_block_size - d_input_leftover];
+       a = std::min(nbytes, d_input_leftover);
+       memcpy(buffer, buf, a);
+       bytes_to_read -= a;
+       d_input_leftover -= a;
+
+    if (d_input_leftover == 0)
+        usb_submit_async(d_context[d_curr],
+                        &d_buffer[d_curr*d_block_size], d_block_size);
+  }
+
+  while (bytes_to_read > 0) {
+
+    d_curr = (d_curr+1)%d_nblocks;
+    buf = &d_buffer[d_curr*d_block_size];
+
+    retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT);
+    if (retval < 0)
+         fprintf(stderr, "%s: usb_reap_async: %s\n",
+                         __FUNCTION__, usb_strerror());
+
+    int ncopy = std::min(bytes_to_read, d_block_size);
+    memcpy((void *) &(((char*)buffer)[a]), buf, ncopy);
+    bytes_to_read -= ncopy;
+    a += ncopy;
+
+    d_input_leftover = d_block_size - ncopy;
+    if (d_input_leftover == 0)
+           usb_submit_async(d_context[d_curr], buf, d_block_size);
+  }
+
+  return retval < 0 ? retval : nbytes;
+}
+
+void
+fusb_ephandle_win32::wait_for_completion ()
+{
+  int i;
+
+  for (i=0; i<d_outstanding_write; i++) {
+    int context_num;
+
+    context_num = (d_curr+d_outstanding_write+i+1)%d_nblocks;
+    usb_reap_async(d_context[context_num], USB_TIMEOUT);
+  }
+
+  d_outstanding_write = 0;
+}