| 30 | | |
| 31 | | static const int USB_TIMEOUT = 1000; // in milliseconds |
| 32 | | |
| 33 | | fusb_devhandle_libusb1::fusb_devhandle_libusb1 (libusb_device_handle *udh) |
| 34 | | : fusb_devhandle (udh) |
| 35 | | { |
| 36 | | // that's it |
| 37 | | } |
| 38 | | |
| 39 | | fusb_devhandle_libusb1::~fusb_devhandle_libusb1 () |
| 40 | | { |
| 41 | | // nop |
| | 30 | #include <linux/usbdevice_fs.h> // interface to kernel portion of user mode usb driver |
| | 31 | #include <sys/ioctl.h> |
| | 32 | #include <assert.h> |
| | 33 | #include <string.h> |
| | 34 | #include <algorithm> |
| | 35 | #include <errno.h> |
| | 36 | #include <string.h> |
| | 37 | #include <cstdio> |
| | 38 | #include <pthread.h> |
| | 39 | |
| | 40 | #define MINIMIZE_TX_BUFFERING true |
| | 41 | |
| | 42 | static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size(); // hard limit |
| | 43 | static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE; |
| | 44 | static const int DEFAULT_BUFFER_SIZE = 4 * (1L << 20); // 4 MB / endpoint |
| | 45 | |
| | 46 | inline static void |
| | 47 | lut_set_ephandle (libusb_transfer *lut, fusb_ephandle_libusb1 *handle) |
| | 48 | { |
| | 49 | lut->user_data = handle; |
| | 50 | } |
| | 51 | |
| | 52 | inline static fusb_ephandle_libusb1 * |
| | 53 | lut_get_ephandle (libusb_transfer *lut) |
| | 54 | { |
| | 55 | return (fusb_ephandle_libusb1 *) lut->user_data; |
| 82 | | libusb_free_transfer (lut); |
| 83 | | } |
| | 96 | //print_transfer_status(lut); |
| | 97 | lut_get_ephandle(lut)->completed_list_add(lut); |
| | 98 | } |
| | 99 | // ------------------------------------------------------------------------ |
| | 100 | // libusb_transfer allocation |
| | 101 | // ------------------------------------------------------------------------ |
| | 102 | |
| | 103 | static libusb_transfer* |
| | 104 | alloc_lut (fusb_ephandle_libusb1 *self, int buffer_length, int endpoint, |
| | 105 | bool input_p, unsigned char *write_buffer, |
| | 106 | libusb_device_handle *dev_handle) |
| | 107 | { |
| | 108 | struct libusb_transfer* lut = libusb_alloc_transfer(0); |
| | 109 | |
| | 110 | endpoint = (endpoint & 0x7f) | (input_p ? 0x80 : 0); |
| | 111 | |
| | 112 | if (input_p) |
| | 113 | write_buffer = new unsigned char [buffer_length]; |
| | 114 | |
| | 115 | libusb_fill_bulk_transfer (lut, // transfer |
| | 116 | dev_handle, // allocate later |
| | 117 | endpoint, // endpoint |
| | 118 | write_buffer, // buffer |
| | 119 | buffer_length, // length |
| | 120 | write_callback, // callback |
| | 121 | self, // user_data |
| | 122 | 0); // timeout |
| | 123 | |
| | 124 | // urb_set_ephandle(lut, self); |
| | 125 | |
| | 126 | return lut; |
| | 127 | } |
| | 128 | |
| | 129 | static void |
| | 130 | free_lut (libusb_transfer *lut) |
| | 131 | { |
| | 132 | // if this was an input lut, free the buffer |
| | 133 | if (lut->endpoint & 0x80) |
| | 134 | delete [] ((unsigned char *) lut->buffer); |
| | 135 | |
| | 136 | libusb_free_transfer(lut); |
| | 137 | } |
| | 138 | |
| | 139 | // ------------------------------------------------------------------------ |
| | 140 | // device handle |
| | 141 | // ------------------------------------------------------------------------ |
| | 142 | |
| | 143 | fusb_devhandle_libusb1::fusb_devhandle_libusb1 (libusb_device_handle *udh) |
| | 144 | : fusb_devhandle (udh) |
| | 145 | { |
| | 146 | // that's it |
| | 147 | } |
| | 148 | |
| | 149 | fusb_devhandle_libusb1::~fusb_devhandle_libusb1 () |
| | 150 | { |
| | 151 | } |
| | 152 | |
| | 153 | |
| 94 | | // libusb_transfer allocation |
| 95 | | // ------------------------------------------------------------------------ |
| 96 | | |
| 97 | | #if 0 |
| 98 | | static libusb_transfer* |
| 99 | | alloc_libusb_transfer (fusb_ephandle_libusb1 *self, int endpoint, |
| 100 | | bool input_p) |
| 101 | | { |
| 102 | | struct libusb_transfer* lut; |
| 103 | | lut = libusb_alloc_transfer(0); |
| 104 | | |
| 105 | | if (input_p) |
| 106 | | callback = read_callback; |
| 107 | | else |
| 108 | | callback = write_callback; |
| 109 | | |
| 110 | | libusb_fill_bulk_transfer (lut, // transfer |
| 111 | | self->devhandle, // dev_handle |
| 112 | | endpoint, // endpoint |
| 113 | | write_buffer, // buffer |
| 114 | | block_size, // length |
| 115 | | callback // callback |
| 116 | | NULL, // user_data |
| 117 | | 0); // timeout |
| 118 | | |
| 119 | | return lut; |
| 120 | | } |
| 121 | | #endif |
| 122 | | |
| 123 | | // ------------------------------------------------------------------------ |
| 132 | | d_devhandle (dh) |
| 133 | | { |
| 134 | | // nop |
| | 171 | d_devhandle (dh), |
| | 172 | d_write_work_in_progress (0), d_write_buffer (0), |
| | 173 | d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0) |
| | 174 | { |
| | 175 | // if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE) |
| | 176 | // throw std::out_of_range ("fusb_ephandle_linux: block_size"); |
| | 177 | |
| | 178 | // if (d_nblocks < 0) |
| | 179 | // throw std::out_of_range ("fusb_ephandle_linux: nblocks"); |
| | 180 | |
| | 181 | if (d_block_size == 0) |
| | 182 | d_block_size = DEFAULT_BLOCK_SIZE; |
| | 183 | |
| | 184 | if (d_nblocks == 0) |
| | 185 | d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size); |
| | 186 | |
| | 187 | if (!d_input_p) |
| | 188 | if (!MINIMIZE_TX_BUFFERING) |
| | 189 | d_write_buffer = new unsigned char [d_block_size]; |
| | 190 | |
| | 191 | if (1) |
| | 192 | fprintf(stderr, "fusb_ephandle_linux::ctor: d_block_size = %d d_nblocks = %d\n", |
| | 193 | d_block_size, d_nblocks); |
| | 194 | |
| | 195 | // allocate libusb_transfers |
| | 196 | |
| | 197 | for (int i = 0; i < d_nblocks; i++) |
| | 198 | d_free_list.push_back (alloc_lut (this, d_block_size, d_endpoint, |
| | 199 | d_input_p, d_write_buffer, |
| | 200 | d_devhandle->get_usb_dev_handle())); |
| 165 | | libusb_transfer *lut = libusb_alloc_transfer(0); |
| 166 | | libusb_fill_bulk_transfer (lut, // transfer |
| 167 | | d_devhandle->get_usb_dev_handle(), |
| 168 | | d_endpoint, // endpoint |
| 169 | | (unsigned char*) buffer, // buffer |
| 170 | | nbytes, // length |
| 171 | | write_callback, // callback |
| 172 | | NULL, // user_data |
| 173 | | USB_TIMEOUT); // timeout |
| 174 | | |
| 175 | | libusb_submit_transfer(lut); |
| 176 | | //libusb_handle_events; |
| | 237 | assert(nbytes % 512 == 0); |
| | 238 | |
| | 239 | unsigned char *src = (unsigned char *) buffer; |
| | 240 | |
| | 241 | int n = 0; |
| | 242 | while (n < nbytes){ |
| | 243 | |
| | 244 | struct libusb_transfer *lut = get_write_work_in_progress(); |
| | 245 | if (!lut) |
| | 246 | return -1; |
| | 247 | assert(lut->actual_length == 0); |
| | 248 | int m = std::min(nbytes - n, MAX_BLOCK_SIZE); |
| | 249 | lut->buffer = src; |
| | 250 | lut->length = m; |
| | 251 | |
| | 252 | n += m; |
| | 253 | src += m; |
| | 254 | |
| | 255 | if (libusb_submit_transfer(lut) < 0) |
| | 256 | return -1; |
| | 257 | |
| | 258 | d_write_work_in_progress = 0; |
| | 259 | } |
| | 260 | |
| | 263 | |
| | 264 | #else |
| | 265 | int |
| | 266 | fusb_ephandle_libusb1::write (const void *buffer, int nbytes) |
| | 267 | { |
| | 268 | if (!d_started) |
| | 269 | return -1; |
| | 270 | |
| | 271 | if (d_input_p) |
| | 272 | return -1; |
| | 273 | |
| | 274 | unsigned char *src = (unsigned char *) buffer; |
| | 275 | |
| | 276 | int n = 0; |
| | 277 | while (n < nbytes){ |
| | 278 | |
| | 279 | libusb_transfer *lut = get_write_work_in_progress (); |
| | 280 | if (!lut) |
| | 281 | return -1; |
| | 282 | unsigned char *dst = (unsigned char *) lut->buffer; |
| | 283 | int m = std::min (nbytes - n, lut->length - lut->actual_length); |
| | 284 | |
| | 285 | memcpy (&dst[lut->actual_length], &src[n], m); |
| | 286 | lut->actual_length += m; |
| | 287 | n += m; |
| | 288 | |
| | 289 | if (lut->actual_length == lut->length){ |
| | 290 | if (libusb_submit_transfer (lut) < 0) |
| | 291 | return -1; |
| | 292 | d_write_work_in_progress = 0; |
| | 293 | } |
| | 294 | } |
| | 295 | |
| | 296 | return n; |
| | 297 | } |
| | 298 | |
| | 299 | #endif |
| | 300 | |
| | 301 | |
| | 302 | struct libusb_transfer * |
| | 303 | fusb_ephandle_libusb1::get_write_work_in_progress () |
| | 304 | { |
| | 305 | if (d_write_work_in_progress) |
| | 306 | return d_write_work_in_progress; |
| | 307 | |
| | 308 | while (1){ |
| | 309 | |
| | 310 | reap_complete_writes (); |
| | 311 | |
| | 312 | struct libusb_transfer *lut = free_list_get (); |
| | 313 | |
| | 314 | if (lut != 0){ |
| | 315 | assert (lut->actual_length == 0); |
| | 316 | d_write_work_in_progress = lut; |
| | 317 | return lut; |
| | 318 | } |
| | 319 | |
| | 320 | libusb_handle_events(NULL); |
| | 321 | // if (!d_devhandle->_reap (true)) |
| | 322 | // return 0; |
| | 323 | // } |
| | 324 | } |
| | 325 | } |
| | 326 | |
| | 327 | void |
| | 328 | fusb_ephandle_libusb1::reap_complete_writes () |
| | 329 | { |
| | 330 | libusb_transfer *lut; |
| | 331 | |
| | 332 | while ((lut = completed_list_get ()) != 0) { |
| | 333 | if (lut->status != 0){ |
| | 334 | fprintf (stderr, "fusb: (status %d) \n", |
| | 335 | lut->status ); |
| | 336 | } |
| | 337 | else if (lut->actual_length != lut->length){ |
| | 338 | fprintf (stderr, "fusb: short write xfer: %d != %d\n", |
| | 339 | lut->actual_length, lut->length); |
| | 340 | } |
| | 341 | |
| | 342 | free_list_add (lut); |
| | 343 | } |
| | 344 | } |
| | 345 | |
| | 346 | // ---------------------------------------------------------------- |
| | 347 | // routines for reading |
| | 348 | // ---------------------------------------------------------------- |
| 188 | | |
| 189 | | libusb_transfer *lut = libusb_alloc_transfer(0); |
| 190 | | libusb_fill_bulk_transfer (lut, // transfer |
| 191 | | d_devhandle->get_usb_dev_handle(), |
| 192 | | d_endpoint, // endpoint |
| 193 | | (unsigned char*) buffer, // buffer |
| 194 | | nbytes, // length |
| 195 | | read_callback, // callback |
| 196 | | NULL, // user_data |
| 197 | | USB_TIMEOUT); // timeout |
| 198 | | |
| 199 | | libusb_submit_transfer(lut); |
| 200 | | } |
| 201 | | |
| | 358 | } |
| | 359 | |
| | 360 | // ---------------------------------------------------------------- |
| | 361 | |
| | 362 | void |
| | 363 | fusb_ephandle_libusb1::free_list_add (libusb_transfer *lut) |
| | 364 | { |
| | 365 | assert (lut_get_ephandle (lut) == this); |
| | 366 | lut->actual_length = 0; |
| | 367 | d_free_list.push_back (lut); |
| | 368 | } |
| | 369 | |
| | 370 | libusb_transfer * |
| | 371 | fusb_ephandle_libusb1::free_list_get () |
| | 372 | { |
| | 373 | if (d_free_list.empty ()) |
| | 374 | return 0; |
| | 375 | |
| | 376 | libusb_transfer *lut = d_free_list.front (); |
| | 377 | d_free_list.pop_front (); |
| | 378 | return lut; |
| | 379 | } |
| | 380 | |
| | 381 | void |
| | 382 | fusb_ephandle_libusb1::completed_list_add (libusb_transfer *lut) |
| | 383 | { |
| | 384 | assert (lut_get_ephandle (lut) == this); |
| | 385 | d_completed_list.push_back (lut); |
| | 386 | } |
| | 387 | |
| | 388 | libusb_transfer * |
| | 389 | fusb_ephandle_libusb1::completed_list_get () |
| | 390 | { |
| | 391 | if (d_completed_list.empty ()) |
| | 392 | return 0; |
| | 393 | |
| | 394 | libusb_transfer *lut = d_completed_list.front (); |
| | 395 | d_completed_list.pop_front (); |
| | 396 | return lut; |
| | 397 | } |
| | 398 | |