Imported Upstream version 3.0
[debian/gnuradio] / usrp / host / lib / fusb_ra_wb.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2003,2006 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_ra_wb.h>
28 #include <usb.h>
29
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33
34 #include <sys/event.h>
35 #include <dev/usb/usb.h>
36
37 static const int USB_TIMEOUT = 1000;    // in milliseconds
38
39 // the following comment and function is from fusb_linux.cc
40 #if 0
41 // Totally evil and fragile extraction of file descriptor from
42 // guts of libusb.  They don't install usbi.h, which is what we'd need
43 // to do this nicely.
44 //
45 // FIXME if everything breaks someday in the future, look here...
46
47 static int
48 fd_from_usb_dev_handle (usb_dev_handle *udh)
49 {
50   return *((int *) udh);
51 }
52 #endif
53
54 // the control endpoint doesn't actually do us any good so here is a
55 // new "fragile extraction"
56 static int
57 ep_fd_from_usb_dev_handle (usb_dev_handle *udh, int endpoint)
58 {
59   struct usb_dev_handle_kludge2 { // see also usrp_prims.cc
60     int                  fd;
61     struct usb_bus      *bus;
62     struct usb_device   *device;
63     int                  config;
64     int                  interface;
65     int                  altsetting;
66     void                *impl_info;
67   };
68   struct bsd_usb_dev_handle_info_kludge {
69     int                  ep_fd[USB_MAX_ENDPOINTS];
70   };
71   struct bsd_usb_dev_handle_info_kludge *info
72       = (struct bsd_usb_dev_handle_info_kludge *)
73            ((struct usb_dev_handle_kludge2 *)udh)->impl_info;
74   return info->ep_fd[UE_GET_ADDR(endpoint)];
75 }
76
77
78 fusb_devhandle_ra_wb::fusb_devhandle_ra_wb (usb_dev_handle *udh)
79   : fusb_devhandle (udh)
80 {
81   // that's it
82 }
83
84 fusb_devhandle_ra_wb::~fusb_devhandle_ra_wb ()
85 {
86   // nop
87 }
88
89 fusb_ephandle *
90 fusb_devhandle_ra_wb::make_ephandle (int endpoint, bool input_p,
91                                      int block_size, int nblocks)
92 {
93   return new fusb_ephandle_ra_wb (this, endpoint, input_p,
94                                   block_size, nblocks);
95 }
96
97 // ----------------------------------------------------------------
98
99 fusb_ephandle_ra_wb::fusb_ephandle_ra_wb (fusb_devhandle_ra_wb *dh,
100                                           int endpoint, bool input_p,
101                                           int block_size, int nblocks)
102   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
103     d_devhandle (dh), d_ra_wb_on (false)
104 {
105   // that's it 
106 }
107
108 fusb_ephandle_ra_wb::~fusb_ephandle_ra_wb ()
109 {
110   // nop
111 }
112
113 bool
114 fusb_ephandle_ra_wb::start ()
115 {
116   d_started = true;
117
118   char buf = '\0';
119   int fd;
120
121   // this is to cause libusb to open the endpoint
122   if (!d_input_p) {
123     write(&buf, 0);
124     fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(),
125                                     d_endpoint);
126   }
127   else {
128     read(&buf, 0);
129     fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(),
130                                     d_endpoint|USB_ENDPOINT_IN);
131   }
132
133   // enable read ahead/write behind
134   int ret;
135   struct usb_bulk_ra_wb_opt opts;
136   int enable = 1;
137
138   opts.ra_wb_buffer_size = d_block_size*d_nblocks;
139   opts.ra_wb_request_size = d_block_size;
140 //  fprintf (stderr, "setting buffer size to %d, request size to %d\n",
141 //         opts.ra_wb_buffer_size, opts.ra_wb_request_size);
142   if (!d_input_p) {
143     ret = ioctl (fd, USB_SET_BULK_WB_OPT, &opts);
144     if (ret < 0)
145       fprintf (stderr, "USB_SET_BULK_WB_OPT: %s\n", strerror(errno));
146     else {
147       ret = ioctl (fd, USB_SET_BULK_WB, &enable);
148       if (ret < 0)
149         fprintf (stderr, "USB_SET_BULK_WB: %s\n", strerror(errno));
150       else
151         d_ra_wb_on = true;
152     }
153   }
154   else {
155     ret = ioctl (fd, USB_SET_BULK_RA_OPT, &opts);
156     if (ret < 0)
157       fprintf (stderr, "USB_SET_BULK_RA_OPT: %s\n", strerror(errno));
158     else {
159       ret = ioctl (fd, USB_SET_BULK_RA, &enable);
160       if (ret < 0)
161         fprintf (stderr, "USB_SET_BULK_RA: %s\n", strerror(errno));
162       else
163         d_ra_wb_on = true;
164     }
165   }
166
167   return true;
168 }
169
170 bool
171 fusb_ephandle_ra_wb::stop ()
172 {
173   int fd;
174   int ret;
175   int enable = 0;
176   if (d_ra_wb_on) {
177     if (!d_input_p) {
178       fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(),
179                                       d_endpoint);
180       ret = ioctl (fd, USB_SET_BULK_WB, &enable);
181       if (ret < 0)
182         fprintf (stderr, "USB_SET_BULK_WB: %s\n", strerror(errno));
183       else
184         d_ra_wb_on = false;
185     }
186     else {
187       fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(),
188                                       d_endpoint|USB_ENDPOINT_IN);
189       ret = ioctl (fd, USB_SET_BULK_RA, &enable);
190       if (ret < 0)
191         fprintf (stderr, "USB_SET_BULK_RA: %s\n", strerror(errno));
192       else
193         d_ra_wb_on = false;
194     }
195   }
196
197   d_started = false;
198   return true;
199 }
200
201 int
202 fusb_ephandle_ra_wb::write (const void *buffer, int nbytes)
203 {
204   if (!d_started)
205     return -1;
206   
207   if (d_input_p)
208     return -1;
209   
210   return usb_bulk_write (d_devhandle->get_usb_dev_handle (),
211                          d_endpoint, (char *) buffer, nbytes, USB_TIMEOUT);
212 }
213
214 int
215 fusb_ephandle_ra_wb::read (void *buffer, int nbytes)
216 {
217   if (!d_started)
218     return -1;
219
220   if (!d_input_p)
221     return -1;
222
223   return usb_bulk_read (d_devhandle->get_usb_dev_handle (),
224                         d_endpoint|USB_ENDPOINT_IN, (char *) buffer, nbytes,
225                         USB_TIMEOUT);
226 }
227
228 void
229 fusb_ephandle_ra_wb::wait_for_completion ()
230 {
231   // as the driver is implemented this only makes sense for write 
232   if (d_ra_wb_on && !d_input_p) {
233     int fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(),
234                                         d_endpoint);
235     int kq = kqueue();
236     if (kq < 0)
237       return;
238     struct kevent evt;
239     int nevents;
240     EV_SET (&evt, fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, 0/*NULL*/);
241     nevents = kevent (kq, &evt, 1, &evt, 1, NULL);
242     if (nevents < 1) {
243       close(kq);
244       return;
245     }
246     while (!(evt.flags & EV_ERROR) && evt.data < (d_block_size*d_nblocks)) {
247       // it's a busy loop, but that's all I can do at the moment
248       nevents = kevent (kq, NULL, 0, &evt, 1, NULL);
249       // let's see if this improves the test_usrp_standard_tx throughput &
250       // "CPU usage" by looping less frequently
251       struct timeval timeout;
252       timeout.tv_sec = 0;
253       timeout.tv_usec = 1000; // 1 ms
254       select (0, NULL, NULL, NULL, &timeout);
255     }
256     close (kq);
257   }
258 }