Imported Upstream version 3.0
[debian/gnuradio] / usrp / host / lib / fusb_win32.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2003,2005 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <fusb_win32.h>
28 #include <usb.h>
29 #include <assert.h>
30 #include <stdexcept>
31
32 static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size();
33 static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE;
34 static const int DEFAULT_BUFFER_SIZE = 16 * (1L << 20);         // 16 MB / endpoint
35
36
37 static const int USB_TIMEOUT = 1000;    // in milliseconds
38
39
40 fusb_devhandle_win32::fusb_devhandle_win32 (usb_dev_handle *udh)
41   : fusb_devhandle (udh)
42 {
43   // that's it
44 }
45
46 fusb_devhandle_win32::~fusb_devhandle_win32 ()
47 {
48   // nop
49 }
50
51 fusb_ephandle *
52 fusb_devhandle_win32::make_ephandle (int endpoint, bool input_p,
53                                        int block_size, int nblocks)
54 {
55   return new fusb_ephandle_win32 (this, endpoint, input_p,
56                                     block_size, nblocks);
57 }
58
59 // ----------------------------------------------------------------
60
61 fusb_ephandle_win32::fusb_ephandle_win32 (fusb_devhandle_win32 *dh,
62                                               int endpoint, bool input_p,
63                                               int block_size, int nblocks)
64   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
65     d_devhandle (dh), d_input_leftover(0),d_output_short(0)
66 {
67   if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
68     throw std::out_of_range ("fusb_ephandle_win32: block_size");
69
70   if (d_nblocks < 0)
71     throw std::out_of_range ("fusb_ephandle_win32: nblocks");
72
73   if (d_block_size == 0)
74     d_block_size = DEFAULT_BLOCK_SIZE;
75
76   if (d_nblocks == 0)
77     d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
78
79   d_buffer = new char [d_block_size*d_nblocks];
80   d_context = new void * [d_nblocks];
81
82   // allocate contexts
83
84   usb_dev_handle *dev = dh->get_usb_dev_handle ();
85   int i;
86
87   if (d_input_p)
88     endpoint |= USB_ENDPOINT_IN;
89
90   for (i=0; i<d_nblocks; i++)
91     usb_bulk_setup_async(dev, &d_context[i], endpoint);
92 }
93
94 fusb_ephandle_win32::~fusb_ephandle_win32 ()
95 {
96   int i;
97
98   stop ();
99
100   for (i=0; i<d_nblocks; i++)
101     usb_free_async(&d_context[i]);
102
103   delete [] d_buffer;
104   delete [] d_context;
105 }
106
107 bool
108 fusb_ephandle_win32::start ()
109 {
110   if (d_started)
111     return true;        // already running
112
113   d_started = true;
114
115   d_curr = d_nblocks-1;
116   d_outstanding_write = 0;
117   d_input_leftover =0;
118   d_output_short = 0;
119
120   if (d_input_p){       // fire off all the reads
121     int i;
122
123     for (i=0; i<d_nblocks; i++) {
124       usb_submit_async(d_context[i], (char * ) d_buffer+i*d_block_size,
125                       d_block_size);
126     }
127   }
128
129   return true;
130 }
131
132 bool
133 fusb_ephandle_win32::stop ()
134 {
135   if (!d_started)
136     return true;
137
138   if (!d_input_p)
139     wait_for_completion ();
140
141   d_started = false;
142   return true;
143 }
144
145 int
146 fusb_ephandle_win32::write (const void *buffer, int nbytes)
147 {
148   int retval=0;
149   char *buf;
150
151   if (!d_started)       // doesn't matter here, but keeps semantics constant
152     return -1;
153
154   if (d_input_p)
155     return -1;
156
157   int bytes_to_write = nbytes;
158   int a=0;
159
160   if (d_output_short != 0) {
161
162         buf = &d_buffer[d_curr*d_block_size + d_block_size - d_output_short];
163         a = std::min(nbytes, d_output_short);
164         memcpy(buf, buffer, a);
165         bytes_to_write -= a;
166         d_output_short -= a;
167
168     if (d_output_short == 0)
169         usb_submit_async(d_context[d_curr],
170                          &d_buffer[d_curr*d_block_size], d_block_size);
171
172         if (bytes_to_write == 0)
173                 return nbytes;
174
175         assert(d_output_short == 0);
176   }
177
178   d_curr = (d_curr+1)%d_nblocks;
179   buf = &d_buffer[d_curr*d_block_size];
180
181   if (d_outstanding_write != d_nblocks) {
182     d_outstanding_write++;
183   } else {
184     retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT);
185     if (retval < 0) {
186                 fprintf(stderr, "%s: usb_reap_async: %s\n",
187                         __FUNCTION__, usb_strerror());
188         return retval;
189       }
190   }
191
192   memcpy(buf, (void *) &(((char*)buffer)[a]), bytes_to_write);
193
194   d_output_short = d_block_size - bytes_to_write;
195   if (d_output_short == 0)
196           usb_submit_async(d_context[d_curr], buf, d_block_size);
197
198   return retval < 0 ? retval : nbytes;
199 }
200
201 int
202 fusb_ephandle_win32::read (void *buffer, int nbytes)
203 {
204   int retval=0;
205   char *buf;
206
207   if (!d_started)       // doesn't matter here, but keeps semantics constant
208     return -1;
209
210   if (!d_input_p)
211     return -1;
212
213   int bytes_to_read = nbytes;
214
215   int a=0;
216   if (d_input_leftover != 0) {
217
218         buf = &d_buffer[d_curr*d_block_size + d_block_size - d_input_leftover];
219         a = std::min(nbytes, d_input_leftover);
220         memcpy(buffer, buf, a);
221         bytes_to_read -= a;
222         d_input_leftover -= a;
223
224     if (d_input_leftover == 0)
225         usb_submit_async(d_context[d_curr],
226                          &d_buffer[d_curr*d_block_size], d_block_size);
227
228         if (bytes_to_read == 0)
229                 return nbytes;
230
231         assert(d_input_leftover == 0);
232   }
233
234
235   d_curr = (d_curr+1)%d_nblocks;
236   buf = &d_buffer[d_curr*d_block_size];
237
238   retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT);
239   if (retval < 0)
240         fprintf(stderr, "%s: usb_reap_async: %s\n",
241                         __FUNCTION__, usb_strerror());
242
243   memcpy((void *) &(((char*)buffer)[a]), buf, bytes_to_read);
244
245   d_input_leftover = d_block_size - bytes_to_read;
246   if (d_input_leftover == 0)
247           usb_submit_async(d_context[d_curr], buf, d_block_size);
248
249   return retval < 0 ? retval : nbytes;
250 }
251
252 void
253 fusb_ephandle_win32::wait_for_completion ()
254 {
255   int i;
256
257   for (i=0; i<d_outstanding_write; i++) {
258     int context_num;
259
260     context_num = (d_curr+d_outstanding_write+i+1)%d_nblocks;
261     usb_reap_async(d_context[context_num], USB_TIMEOUT);
262   }
263
264   d_outstanding_write = 0;
265 }