Imported Upstream version 3.2.2
[debian/gnuradio] / usrp / host / lib / legacy / 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 3, 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 #include <string.h>
32
33 static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size();
34 static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE;
35 static const int DEFAULT_BUFFER_SIZE = 16 * (1L << 20);         // 16 MB / endpoint
36
37
38 static const int USB_TIMEOUT = 1000;    // in milliseconds
39
40
41 fusb_devhandle_win32::fusb_devhandle_win32 (usb_dev_handle *udh)
42   : fusb_devhandle (udh)
43 {
44   // that's it
45 }
46
47 fusb_devhandle_win32::~fusb_devhandle_win32 ()
48 {
49   // nop
50 }
51
52 fusb_ephandle *
53 fusb_devhandle_win32::make_ephandle (int endpoint, bool input_p,
54                                        int block_size, int nblocks)
55 {
56   return new fusb_ephandle_win32 (this, endpoint, input_p,
57                                     block_size, nblocks);
58 }
59
60 // ----------------------------------------------------------------
61
62 fusb_ephandle_win32::fusb_ephandle_win32 (fusb_devhandle_win32 *dh,
63                                               int endpoint, bool input_p,
64                                               int block_size, int nblocks)
65   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
66     d_devhandle (dh), d_input_leftover(0),d_output_short(0)
67 {
68   if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
69     throw std::out_of_range ("fusb_ephandle_win32: block_size");
70
71   if (d_nblocks < 0)
72     throw std::out_of_range ("fusb_ephandle_win32: nblocks");
73
74   if (d_block_size == 0)
75     d_block_size = DEFAULT_BLOCK_SIZE;
76
77   if (d_nblocks == 0)
78     d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
79
80   d_buffer = new char [d_block_size*d_nblocks];
81   d_context = new void * [d_nblocks];
82
83   // allocate contexts
84
85   usb_dev_handle *dev = dh->get_usb_dev_handle ();
86   int i;
87
88   if (d_input_p)
89     endpoint |= USB_ENDPOINT_IN;
90
91   for (i=0; i<d_nblocks; i++)
92     usb_bulk_setup_async(dev, &d_context[i], endpoint);
93 }
94
95 fusb_ephandle_win32::~fusb_ephandle_win32 ()
96 {
97   int i;
98
99   stop ();
100
101   for (i=0; i<d_nblocks; i++)
102     usb_free_async(&d_context[i]);
103
104   delete [] d_buffer;
105   delete [] d_context;
106 }
107
108 bool
109 fusb_ephandle_win32::start ()
110 {
111   if (d_started)
112     return true;        // already running
113
114   d_started = true;
115
116   d_curr = d_nblocks-1;
117   d_outstanding_write = 0;
118   d_input_leftover =0;
119   d_output_short = 0;
120
121   if (d_input_p){       // fire off all the reads
122     int i;
123
124     for (i=0; i<d_nblocks; i++) {
125       usb_submit_async(d_context[i], (char * ) d_buffer+i*d_block_size,
126                       d_block_size);
127     }
128   }
129
130   return true;
131 }
132
133 bool
134 fusb_ephandle_win32::stop ()
135 {
136   if (!d_started)
137     return true;
138
139   if (!d_input_p)
140     wait_for_completion ();
141
142   d_started = false;
143   return true;
144 }
145
146 int
147 fusb_ephandle_win32::write (const void *buffer, int nbytes)
148 {
149   int retval=0;
150   char *buf;
151
152   if (!d_started)       // doesn't matter here, but keeps semantics constant
153     return -1;
154
155   if (d_input_p)
156     return -1;
157
158   int bytes_to_write = nbytes;
159   int a=0;
160
161   if (d_output_short != 0) {
162
163         buf = &d_buffer[d_curr*d_block_size + d_block_size - d_output_short];
164         a = std::min(nbytes, d_output_short);
165         memcpy(buf, buffer, a);
166         bytes_to_write -= a;
167         d_output_short -= a;
168
169     if (d_output_short == 0)
170         usb_submit_async(d_context[d_curr],
171                          &d_buffer[d_curr*d_block_size], d_block_size);
172   }
173
174   while (bytes_to_write > 0) {
175     d_curr = (d_curr+1)%d_nblocks;
176     buf = &d_buffer[d_curr*d_block_size];
177
178     if (d_outstanding_write != d_nblocks) {
179       d_outstanding_write++;
180     } else {
181       retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT);
182       if (retval < 0) {
183                   fprintf(stderr, "%s: usb_reap_async: %s\n",
184                           __FUNCTION__, usb_strerror());
185           return retval;
186         }
187     }
188
189     int ncopy = std::min(bytes_to_write, d_block_size);
190     memcpy(buf, (void *) &(((char*)buffer)[a]), ncopy);
191     bytes_to_write -= ncopy;
192     a += ncopy;
193
194     d_output_short = d_block_size - ncopy;
195     if (d_output_short == 0)
196             usb_submit_async(d_context[d_curr], buf, d_block_size);
197   }
198
199   return retval < 0 ? retval : nbytes;
200 }
201
202 int
203 fusb_ephandle_win32::read (void *buffer, int nbytes)
204 {
205   int retval=0;
206   char *buf;
207
208   if (!d_started)       // doesn't matter here, but keeps semantics constant
209     return -1;
210
211   if (!d_input_p)
212     return -1;
213
214   int bytes_to_read = nbytes;
215
216   int a=0;
217   if (d_input_leftover != 0) {
218
219         buf = &d_buffer[d_curr*d_block_size + d_block_size - d_input_leftover];
220         a = std::min(nbytes, d_input_leftover);
221         memcpy(buffer, buf, a);
222         bytes_to_read -= a;
223         d_input_leftover -= a;
224
225     if (d_input_leftover == 0)
226         usb_submit_async(d_context[d_curr],
227                          &d_buffer[d_curr*d_block_size], d_block_size);
228   }
229
230   while (bytes_to_read > 0) {
231
232     d_curr = (d_curr+1)%d_nblocks;
233     buf = &d_buffer[d_curr*d_block_size];
234
235     retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT);
236     if (retval < 0)
237           fprintf(stderr, "%s: usb_reap_async: %s\n",
238                           __FUNCTION__, usb_strerror());
239
240     int ncopy = std::min(bytes_to_read, d_block_size);
241     memcpy((void *) &(((char*)buffer)[a]), buf, ncopy);
242     bytes_to_read -= ncopy;
243     a += ncopy;
244
245     d_input_leftover = d_block_size - ncopy;
246     if (d_input_leftover == 0)
247             usb_submit_async(d_context[d_curr], buf, d_block_size);
248   }
249
250   return retval < 0 ? retval : nbytes;
251 }
252
253 void
254 fusb_ephandle_win32::wait_for_completion ()
255 {
256   int i;
257
258   for (i=0; i<d_outstanding_write; i++) {
259     int context_num;
260
261     context_num = (d_curr+d_outstanding_write+i+1)%d_nblocks;
262     usb_reap_async(d_context[context_num], USB_TIMEOUT);
263   }
264
265   d_outstanding_write = 0;
266 }