Imported Upstream version 3.0
[debian/gnuradio] / usrp / host / lib / fusb_darwin.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 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 // tell mld_threads to NOT use omni_threads,
28 // but rather Darwin's pthreads
29 #undef _USE_OMNI_THREADS_
30
31 #include <usb.h>
32 #include "fusb.h"
33 #include "fusb_darwin.h"
34 #include "darwin_libusb.h"
35
36 static const int USB_TIMEOUT = 100;     // in milliseconds
37 static const UInt8 NUM_QUEUE_ITEMS = 20;
38
39 fusb_devhandle_darwin::fusb_devhandle_darwin (usb_dev_handle* udh)
40   : fusb_devhandle (udh)
41 {
42   // that's it
43 }
44
45 fusb_devhandle_darwin::~fusb_devhandle_darwin ()
46 {
47   // nop
48 }
49
50 fusb_ephandle*
51 fusb_devhandle_darwin::make_ephandle (int endpoint, bool input_p,
52                                       int block_size, int nblocks)
53 {
54   return new fusb_ephandle_darwin (this, endpoint, input_p,
55                                    block_size, nblocks);
56 }
57
58 // ----------------------------------------------------------------
59
60 fusb_ephandle_darwin::fusb_ephandle_darwin (fusb_devhandle_darwin* dh,
61                                             int endpoint, bool input_p,
62                                             int block_size, int nblocks)
63   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
64     d_devhandle (dh), d_pipeRef (0), d_transferType (0),
65     d_interfaceRef (0),  d_interface (0), d_queue (0),
66     d_buffer (0), d_bufLenBytes (0)
67 {
68   d_bufLenBytes = fusb_sysconfig::max_block_size();
69
70 // create circular buffer
71   d_buffer = new circular_buffer<char> (NUM_QUEUE_ITEMS * d_bufLenBytes,
72                                         !d_input_p, d_input_p);
73
74 // create the queue
75   d_queue = new circular_linked_list <s_buffer_ptr> (NUM_QUEUE_ITEMS);
76   d_queue->iterate_start ();
77   s_node_ptr l_node = d_queue->iterate_next ();
78   while (l_node) {
79     l_node->both (new s_both<s_buffer_ptr> (l_node, this));
80     s_buffer_ptr l_buf = new s_buffer (d_bufLenBytes);
81     l_node->object (l_buf);
82     l_node = d_queue->iterate_next ();
83     l_buf = NULL;
84   }
85
86   d_readRunning = new mld_mutex ();
87   d_runThreadRunning = new mld_mutex ();
88   d_runBlock = new mld_condition ();
89   d_readBlock = new mld_condition ();
90 }
91
92 fusb_ephandle_darwin::~fusb_ephandle_darwin ()
93 {
94   stop ();
95
96   d_queue->iterate_start ();
97   s_node_ptr l_node = d_queue->iterate_next ();
98   while (l_node) {
99     s_both_ptr l_both = l_node->both ();
100     delete l_both;
101     l_both = NULL;
102     l_node->both (NULL);
103     s_buffer_ptr l_buf = l_node->object ();
104     delete l_buf;
105     l_buf = NULL;
106     l_node->object (NULL);
107     l_node = d_queue->iterate_next ();
108   }
109   delete d_queue;
110   d_queue = NULL;
111   delete d_buffer;
112   d_buffer = NULL;
113   delete d_readRunning;
114   d_readRunning = NULL;
115   delete d_runThreadRunning;
116   d_runThreadRunning = NULL;
117   delete d_runBlock;
118   d_runBlock = NULL;
119   delete d_readBlock;
120   d_readBlock = NULL;
121 }
122
123 bool
124 fusb_ephandle_darwin::start ()
125 {
126   UInt8  direction, number, interval;
127   UInt16 maxPacketSize;
128
129 // reset circular buffer
130   d_buffer->reset ();
131
132 // reset the queue
133   d_queue->num_used (0);
134   d_queue->iterate_start ();
135   s_node_ptr l_node = d_queue->iterate_next ();
136   while (l_node) {
137     l_node->both()->set (l_node, this);
138     l_node->object()->reset ();
139     l_node->set_available ();
140     l_node = d_queue->iterate_next ();
141   }
142
143   d_pipeRef = d_transferType = 0;
144
145   usb_dev_handle* dev = d_devhandle->get_usb_dev_handle ();
146   if (! dev)
147     USB_ERROR_STR (false, -ENXIO, "fusb_ephandle_darwin::start: "
148                    "null device");
149
150   darwin_dev_handle* device = (darwin_dev_handle*) dev->impl_info;
151   if (! device)
152     USB_ERROR_STR (false, -ENOENT, "fusb_ephandle_darwin::start: "
153                    "device not initialized");
154
155   if (usb_debug)
156     fprintf (stderr, "fusb_ephandle_darwin::start: "
157              "dev = %p, device = %p\n", dev, device);
158
159   d_interfaceRef = device->interface;
160   if (! d_interfaceRef)
161     USB_ERROR_STR (false, -EACCES, "fusb_ephandle_darwin::start: "
162                    "interface used without being claimed");
163   d_interface = *d_interfaceRef;
164
165 // get read or write pipe info (depends on "d_input_p")
166
167   if (usb_debug > 3)
168     fprintf (stderr, "fusb_ephandle_darwin::start "
169              "d_endpoint = %d, d_input_p = %s\n",
170              d_endpoint, d_input_p ? "TRUE" : "FALSE");
171
172   int l_endpoint = (d_input_p ? USB_ENDPOINT_IN : USB_ENDPOINT_OUT);
173   int pipeRef = ep_to_pipeRef (device, d_endpoint | l_endpoint);
174   if (pipeRef < 0)
175     USB_ERROR_STR (false, -EINVAL, "fusb_ephandle_darwin::start "
176                    " invalid pipeRef.\n");
177
178   d_pipeRef = pipeRef;
179   d_interface->GetPipeProperties (d_interfaceRef,
180                                   d_pipeRef,
181                                   &direction,
182                                   &number,
183                                   &d_transferType,
184                                   &maxPacketSize,
185                                   &interval);
186   if (usb_debug == 3)
187     fprintf (stderr, "fusb_ephandle_darwin::start: %s: ep = 0x%02x, "
188              "pipeRef = %d, d_i = %p, d_iR = %p, if_dir = %d, if_# = %d, "
189              "if_int = %d, if_maxPS = %d\n", d_input_p ? "read" : "write",
190              d_endpoint, d_pipeRef, d_interface, d_interfaceRef, direction,
191              number, interval, maxPacketSize);
192
193 // set global start boolean
194   d_started = true;
195
196 // create the run thread, which allows OSX to process I/O separately
197   d_runThread = new mld_thread (run_thread, this);
198
199 // wait until the threads are -really- going
200   d_runBlock->wait ();
201
202   if (usb_debug)
203     fprintf (stderr, "fusb_ephandle_darwin::start: %s started.\n",
204              d_input_p ? "read" : "write");
205
206   return (true);
207 }
208
209 void
210 fusb_ephandle_darwin::run_thread (void* arg)
211 {
212   fusb_ephandle_darwin* This = static_cast<fusb_ephandle_darwin*>(arg);
213   mld_mutex_ptr l_runThreadRunning = This->d_runThreadRunning;
214   l_runThreadRunning->lock ();
215
216   mld_mutex_ptr l_readRunning = This->d_readRunning;
217   mld_condition_ptr l_readBlock = This->d_readBlock;
218
219   bool l_input_p = This->d_input_p;
220
221   if (usb_debug)
222     fprintf (stderr, "fusb_ephandle_darwin::run_thread: "
223              "starting for %s.\n",
224              l_input_p ? "read" : "write");
225
226   usb_interface_t** l_interfaceRef = This->d_interfaceRef;
227   usb_interface_t* l_interface = This->d_interface;
228   CFRunLoopSourceRef l_cfSource;
229
230 // create async run loop
231   l_interface->CreateInterfaceAsyncEventSource (l_interfaceRef, &l_cfSource);
232   CFRunLoopAddSource (CFRunLoopGetCurrent (), l_cfSource,
233                       kCFRunLoopDefaultMode);
234 // get run loop reference, to allow other threads to stop
235   This->d_CFRunLoopRef = CFRunLoopGetCurrent ();
236
237   mld_thread_ptr l_rwThread = NULL;
238
239   if (l_input_p) {
240     l_rwThread = new mld_thread (read_thread, arg);
241 // wait until the the rwThread is -really- going
242     l_readBlock->wait ();
243   }
244
245 // now signal the run condition to release and finish ::start()
246   This->d_runBlock->signal ();
247
248 // run the loop
249   CFRunLoopRun ();
250
251   if (l_input_p) {
252 // wait for read_thread () to finish
253     l_readRunning->lock ();
254     l_readRunning->unlock ();
255   }
256
257 // remove run loop stuff
258   CFRunLoopRemoveSource (CFRunLoopGetCurrent (),
259                          l_cfSource, kCFRunLoopDefaultMode);
260
261   if (usb_debug)
262     fprintf (stderr, "fusb_ephandle_darwin::run_thread: finished for %s.\n",
263              l_input_p ? "read" : "write");
264
265   l_runThreadRunning->unlock ();
266 }
267
268 void
269 fusb_ephandle_darwin::read_thread (void* arg)
270 {
271   if (usb_debug)
272     fprintf (stderr, "fusb_ephandle_darwin::read_thread: starting.\n");
273
274   fusb_ephandle_darwin* This = static_cast<fusb_ephandle_darwin*>(arg);
275
276   mld_mutex_ptr l_readRunning = This->d_readRunning;
277   l_readRunning->lock ();
278
279 // signal the read condition from run_thread() to continue
280   mld_condition_ptr l_readBlock = This->d_readBlock;
281   l_readBlock->signal ();
282
283   s_queue_ptr l_queue = This->d_queue;
284   l_queue->iterate_start ();
285   s_node_ptr l_node = l_queue->iterate_next ();
286   while (l_node) {
287     This->read_issue (l_node->both ());
288     l_node = l_queue->iterate_next ();
289   }
290
291   if (usb_debug)
292     fprintf (stderr, "fusb_ephandle_darwin::read_thread: finished.\n");
293
294   l_readRunning->unlock ();
295 }
296
297 void
298 fusb_ephandle_darwin::read_issue (s_both_ptr l_both)
299 {
300   if ((! l_both) || (! d_started))
301     return;
302
303 // set the node and buffer from the input "both"
304   s_node_ptr l_node = l_both->node ();
305   s_buffer_ptr l_buf = l_node->object ();
306   void* v_buffer = (void*) l_buf->buffer ();
307
308 // read up to d_bufLenBytes
309   UInt32 bufLen = d_bufLenBytes;
310   l_buf->n_used (bufLen);
311
312 // setup system call result
313   io_return_t result = kIOReturnSuccess;
314
315   if (d_transferType == kUSBInterrupt)
316 /* This is an interrupt pipe. We can't specify a timeout. */
317     result = d_interface->ReadPipeAsync
318       (d_interfaceRef, d_pipeRef, v_buffer, bufLen,
319        (IOAsyncCallback1) read_completed, (void*) l_both);
320   else
321     result = d_interface->ReadPipeAsyncTO
322       (d_interfaceRef, d_pipeRef, v_buffer, bufLen, 0, USB_TIMEOUT,
323        (IOAsyncCallback1) read_completed, (void*) l_both);
324
325   if (result != kIOReturnSuccess)
326     USB_ERROR_STR_NO_RET (- darwin_to_errno (result),
327                           "fusb_ephandle_darwin::read_issue "
328                           "(ReadPipeAsync%s): %s",
329                           d_transferType == kUSBInterrupt ? "" : "TO",
330                           darwin_error_str (result));
331 }
332
333 void
334 fusb_ephandle_darwin::read_completed (void* refCon,
335                                       io_return_t result,
336                                       void* io_size)
337 {
338   UInt32 l_size = (UInt32) io_size;
339   s_both_ptr l_both = static_cast<s_both_ptr>(refCon);
340   fusb_ephandle_darwin* This = static_cast<fusb_ephandle_darwin*>(l_both->This ());
341   s_node_ptr l_node = l_both->node ();
342   circular_buffer<char>* l_buffer = This->d_buffer;
343   s_buffer_ptr l_buf = l_node->object ();
344   UInt32 l_i_size = l_buf->n_used ();
345
346   if (This->d_started && (l_i_size != l_size))
347     fprintf (stderr, "fusb_ephandle_darwin::read_completed: "
348              "Expected %ld bytes; read %ld.\n",
349              l_i_size, l_size);
350
351 // add this read to the transfer buffer
352   if (l_buffer->enqueue (l_buf->buffer (), l_size) == -1) {
353     fputs ("iU", stderr);
354     fflush (stderr);
355   }
356
357 // set buffer's # data to 0
358   l_buf->n_used (0);
359
360 // issue another read for this "both"
361   This->read_issue (l_both);
362 }
363
364 int
365 fusb_ephandle_darwin::read (void* buffer, int nbytes)
366 {
367   UInt32 l_nbytes = (UInt32) nbytes;
368   d_buffer->dequeue ((char*) buffer, &l_nbytes);
369   return ((int) l_nbytes);
370 }
371
372 int
373 fusb_ephandle_darwin::write (const void* buffer, int nbytes)
374 {
375   UInt32 l_nbytes = (UInt32) nbytes;
376
377   if (! d_started) return (0);
378
379   while (l_nbytes != 0) {
380 // find out how much data to copy; limited to "d_bufLenBytes" per node
381     UInt32 t_nbytes = (l_nbytes > d_bufLenBytes) ? d_bufLenBytes : l_nbytes;
382
383 // get next available node to write into;
384 // blocks internally if none available
385     s_node_ptr l_node = d_queue->find_next_available_node ();
386
387 // copy the input into the node's buffer
388     s_buffer_ptr l_buf = l_node->object ();
389     l_buf->buffer ((char*) buffer, t_nbytes);
390     void* v_buffer = (void*) l_buf->buffer ();
391
392 // setup callback parameter & system call return
393     s_both_ptr l_both = l_node->both ();
394     io_return_t result = kIOReturnSuccess;
395
396     if (d_transferType == kUSBInterrupt)
397 /* This is an interrupt pipe ... can't specify a timeout. */
398       result = d_interface->WritePipeAsync
399         (d_interfaceRef, d_pipeRef, v_buffer, l_nbytes,
400          (IOAsyncCallback1) write_completed, (void*) l_both);
401     else
402       result = d_interface->WritePipeAsyncTO
403         (d_interfaceRef, d_pipeRef, v_buffer, l_nbytes, 0, USB_TIMEOUT,
404          (IOAsyncCallback1) write_completed, (void*) l_both);
405
406     if (result != kIOReturnSuccess)
407       USB_ERROR_STR (-1, - darwin_to_errno (result),
408                      "fusb_ephandle_darwin::write_thread "
409                      "(WritePipeAsync%s): %s",
410                      d_transferType == kUSBInterrupt ? "" : "TO",
411                      darwin_error_str (result));
412     l_nbytes -= t_nbytes;
413   }
414
415   return (nbytes);
416 }
417
418 void
419 fusb_ephandle_darwin::write_completed (void* refCon,
420                                        io_return_t result,
421                                        void* io_size)
422 {
423   s_both_ptr l_both = static_cast<s_both_ptr>(refCon);
424   fusb_ephandle_darwin* This = static_cast<fusb_ephandle_darwin*>(l_both->This ());
425   UInt32 l_size = (UInt32) io_size;
426   s_node_ptr l_node = l_both->node ();
427   s_queue_ptr l_queue = This->d_queue;
428   s_buffer_ptr l_buf = l_node->object ();
429   UInt32 l_i_size = l_buf->n_used ();
430
431   if (This->d_started && (l_i_size != l_size))
432     fprintf (stderr, "fusb_ephandle_darwin::write_completed: "
433              "Expected %ld bytes written; wrote %ld.\n",
434              l_i_size, l_size);
435
436 // set buffer's # data to 0
437   l_buf->n_used (0);
438 // make the node available for reuse
439   l_queue->make_node_available (l_node);
440 }
441
442 void
443 fusb_ephandle_darwin::abort ()
444 {
445   if (usb_debug)
446     fprintf (stderr, "fusb_ephandle_darwin::abort: starting.\n");
447
448   io_return_t result = d_interface->AbortPipe (d_interfaceRef, d_pipeRef);
449
450   if (result != kIOReturnSuccess)
451     USB_ERROR_STR_NO_RET (- darwin_to_errno (result),
452                           "fusb_ephandle_darwin::abort "
453                           "(AbortPipe): %s", darwin_error_str (result));
454   if (usb_debug)
455     fprintf (stderr, "fusb_ephandle_darwin::abort: finished.\n");
456 }
457
458 bool
459 fusb_ephandle_darwin::stop ()
460 {
461   if (! d_started)
462     return (true);
463
464   if (usb_debug)
465     fprintf (stderr, "fusb_ephandle_darwin::stop: stopping %s.\n",
466              d_input_p ? "read" : "write");
467
468   d_started = false;
469
470 // abort any pending IO transfers
471   abort ();
472
473 // wait for write transfer to finish
474   wait_for_completion ();
475
476 // tell IO buffer to abort any waiting conditions
477   d_buffer->abort ();
478
479 // stop the run loop
480   CFRunLoopStop (d_CFRunLoopRef);
481
482 // wait for the runThread to stop
483   d_runThreadRunning->lock ();
484   d_runThreadRunning->unlock ();
485
486   if (usb_debug)
487     fprintf (stderr, "fusb_ephandle_darwin::stop: %s stopped.\n",
488              d_input_p ? "read" : "write");
489
490   return (true);
491 }
492
493 void
494 fusb_ephandle_darwin::wait_for_completion ()
495 {
496   if (d_queue)
497     while (d_queue->in_use ())
498       usleep (1000);
499 }