1/* 2 * (Free|Open|Net)BSD USB support 3 * 4 * Derived from Linux version by Richard Tobin. 5 * 6 * $Id: bsd.c,v 1.1.1.1 2008/10/15 03:31:32 james26_jang Exp $ 7 * $Name: $ 8 * 9 * This library is covered by the LGPL, read LICENSE for details. 10 */ 11 12/* 13 * Note: I don't have a clue what I'm doing. I just looked at the 14 * man pages and source to try and find things that did the same as 15 * the Linux version. -- Richard 16 * 17 * johnjen@reynoldsnet.org - minor fixes with debug mode output. Consistent brace 18 * use as well as indenting. More error messages put in to test for failure 19 * modes with /dev/ permissions (when it happens). Note: I, like Richard, have 20 * no clue what I'm doing. Patches to increase/fix functionality happily 21 * accepted! 22 */ 23 24/* dirkx@webweaving.org - minor changes to make things actually work 25 * for both read and write. 26 */ 27 28#if defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) 29#define __FreeBSD_kernel__ __FreeBSD__ 30#endif 31 32#include <stdlib.h> 33#include <unistd.h> 34#include <string.h> 35#include <stdio.h> 36#include <fcntl.h> 37#include <errno.h> 38#include <assert.h> 39#include <sys/time.h> 40#include <sys/ioctl.h> 41 42#include <dev/usb/usb.h> 43 44#include "usbi.h" 45#ifdef HAVE_CONFIG_H 46#include "config.h" 47#endif 48 49#ifdef HAVE_OLD_DEV_USB_USB_H 50/* 51 * It appears some of the BSD's (OpenBSD atleast) have switched over to a 52 * new naming convention, so we setup some macro's for backward 53 * compability with older versions --jerdfelt 54 */ 55 56/* struct usb_ctl_request */ 57#define ucr_addr addr 58#define ucr_request request 59#define ucr_data data 60#define ucr_flags flags 61#define ucr_actlen actlen 62 63/* struct usb_alt_interface */ 64#define uai_config_index config_index 65#define uai_interface_index interface_index 66#define uai_alt_no alt_no 67 68/* struct usb_config_desc */ 69#define ucd_config_index config_index 70#define ucd_desc desc 71 72/* struct usb_interface_desc */ 73#define uid_config_index config_index 74#define uid_interface_index interface_index 75#define uid_alt_index alt_index 76#define uid_desc desc 77 78/* struct usb_endpoint_desc */ 79#define ued_config_index config_index 80#define ued_interface_index interface_index 81#define ued_alt_index alt_index 82#define ued_endpoint_index endpoint_index 83#define ued_desc desc 84 85/* struct usb_full_desc */ 86#define ufd_config_index config_index 87#define ufd_size size 88#define ufd_data data 89 90/* struct usb_string_desc */ 91#define usd_string_index string_index 92#define usd_language_id language_id 93#define usd_desc desc 94 95/* struct usb_ctl_report_desc */ 96#define ucrd_size size 97#define ucrd_data data 98 99/* struct usb_device_info */ 100#define udi_bus bus 101#define udi_addr addr 102#define udi_cookie cookie 103#define udi_product product 104#define udi_vendor vendor 105#define udi_release release 106#define udi_productNo productNo 107#define udi_vendorNo vendorNo 108#define udi_releaseNo releaseNo 109#define udi_class class 110#define udi_subclass subclass 111#define udi_protocol protocol 112#define udi_config config 113#define udi_lowspeed lowspeed 114#define udi_power power 115#define udi_nports nports 116#define udi_devnames devnames 117#define udi_ports ports 118 119/* struct usb_ctl_report */ 120#define ucr_report report 121#define ucr_data data 122 123/* struct usb_device_stats */ 124#define uds_requests requests 125#endif 126 127static int ensure_ep_open(usb_dev_handle *dev, int ep, int mode); 128 129#define MAX_CONTROLLERS 10 130 131/* This records the file descriptors for the endpoints. It doesn't seem 132 to work to re-open them for each read (as well as being inefficient). */ 133 134struct bsd_usb_dev_handle_info { 135 int ep_fd[USB_MAX_ENDPOINTS]; 136}; 137 138int usb_os_open(usb_dev_handle *dev) 139{ 140 int i; 141 struct bsd_usb_dev_handle_info *info; 142 char ctlpath[PATH_MAX + 1]; 143 144 info = malloc(sizeof(struct bsd_usb_dev_handle_info)); 145 if (!info) 146 USB_ERROR(-ENOMEM); 147 dev->impl_info = info; 148 149#ifdef __FreeBSD_kernel__ 150 snprintf(ctlpath, PATH_MAX, "%s", dev->device->filename); 151#else 152 snprintf(ctlpath, PATH_MAX, "%s.00", dev->device->filename); 153#endif 154 dev->fd = open(ctlpath, O_RDWR); 155 if (dev->fd < 0) { 156 dev->fd = open(ctlpath, O_RDONLY); 157 if (dev->fd < 0) { 158 free(info); 159 USB_ERROR_STR(-errno, "failed to open %s: %s", 160 ctlpath, strerror(errno)); 161 } 162 } 163 164 /* Mark the endpoints as not yet open */ 165 for (i = 0; i < USB_MAX_ENDPOINTS; i++) 166 info->ep_fd[i] = -1; 167 168 return 0; 169} 170 171int usb_os_close(usb_dev_handle *dev) 172{ 173 struct bsd_usb_dev_handle_info *info = dev->impl_info; 174 int i; 175 176 /* Close any open endpoints */ 177 178 for (i = 0; i < USB_MAX_ENDPOINTS; i++) 179 if (info->ep_fd[i] >= 0) { 180 if (usb_debug >= 2) 181 fprintf(stderr, "usb_os_close: closing endpoint %d\n", info->ep_fd[i]); 182 close(info->ep_fd[i]); 183 } 184 185 free(info); 186 187 if (dev->fd <= 0) 188 return 0; 189 190 if (close(dev->fd) == -1) 191 /* Failing trying to close a file really isn't an error, so return 0 */ 192 USB_ERROR_STR(0, "tried to close device fd %d: %s", dev->fd, 193 strerror(errno)); 194 195 return 0; 196} 197 198int usb_set_configuration(usb_dev_handle *dev, int configuration) 199{ 200 int ret; 201 202 ret = ioctl(dev->fd, USB_SET_CONFIG, &configuration); 203 if (ret < 0) 204 USB_ERROR_STR(-errno, "could not set config %d: %s", configuration, 205 strerror(errno)); 206 207 dev->config = configuration; 208 209 return 0; 210} 211 212int usb_claim_interface(usb_dev_handle *dev, int interface) 213{ 214 /* BSD doesn't have the corresponding ioctl. It seems to 215 be sufficient to open the relevant endpoints as needed. */ 216 217 dev->interface = interface; 218 219 return 0; 220} 221 222int usb_release_interface(usb_dev_handle *dev, int interface) 223{ 224 /* See above */ 225 return 0; 226} 227 228int usb_set_altinterface(usb_dev_handle *dev, int alternate) 229{ 230 int ret; 231 struct usb_alt_interface intf; 232 233 if (dev->interface < 0) 234 USB_ERROR(-EINVAL); 235 236 intf.uai_interface_index = dev->interface; 237 intf.uai_alt_no = alternate; 238 239 ret = ioctl(dev->fd, USB_SET_ALTINTERFACE, &intf); 240 if (ret < 0) 241 USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s", 242 dev->interface, alternate, strerror(errno)); 243 244 dev->altsetting = alternate; 245 246 return 0; 247} 248 249static int ensure_ep_open(usb_dev_handle *dev, int ep, int mode) 250{ 251 struct bsd_usb_dev_handle_info *info = dev->impl_info; 252 int fd; 253 char buf[20]; 254 255 /* Get the address for this endpoint; we could check 256 * the mode against the direction; but we've done that 257 * already in the usb_bulk_read/write. 258 */ 259 ep = UE_GET_ADDR(ep); 260 261 if (info->ep_fd[ep] < 0) { 262#ifdef __FreeBSD_kernel__ 263 snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->device->filename, ep); 264#else 265 snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->device->filename, ep); 266#endif 267 /* Try to open it O_RDWR first for those devices which have in and out 268 * endpoints with the same address (eg 0x02 and 0x82) 269 */ 270 fd = open(buf, O_RDWR); 271 if (fd < 0 && errno == ENXIO) 272 fd = open(buf, mode); 273 if (fd < 0) 274 USB_ERROR_STR(-errno, "can't open %s for bulk read: %s", 275 buf, strerror(errno)); 276 info->ep_fd[ep] = fd; 277 } 278 279 return info->ep_fd[ep]; 280} 281 282int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, 283 int timeout) 284{ 285 int fd, ret; 286 287 /* Ensure the endpoint address is correct */ 288 ep &= ~USB_ENDPOINT_IN; 289 290 fd = ensure_ep_open(dev, ep, O_WRONLY); 291 if (fd < 0) { 292 if (usb_debug >= 2) { 293#ifdef __FreeBSD_kernel__ 294 fprintf (stderr, "usb_bulk_write: got negative open file descriptor for endpoint %d\n", UE_GET_ADDR(ep)); 295#else 296 fprintf (stderr, "usb_bulk_write: got negative open file descriptor for endpoint %02d\n", UE_GET_ADDR(ep)); 297#endif 298 } 299 return fd; 300 } 301 302 ret = ioctl(fd, USB_SET_TIMEOUT, &timeout); 303 if (ret < 0) 304 USB_ERROR_STR(-errno, "error setting timeout: %s", 305 strerror(errno)); 306 307 ret = write(fd, bytes, size); 308 if (ret < 0) 309#ifdef __FreeBSD_kernel__ 310 USB_ERROR_STR(-errno, "error writing to bulk endpoint %s.%d: %s", 311 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 312#else 313 USB_ERROR_STR(-errno, "error writing to bulk endpoint %s.%02d: %s", 314 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 315#endif 316 317 return size; 318} 319 320int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, 321 int timeout) 322{ 323 int fd, ret, one = 1; 324 325 /* Ensure the endpoint address is correct */ 326 ep |= USB_ENDPOINT_IN; 327 328 fd = ensure_ep_open(dev, ep, O_RDONLY); 329 if (fd < 0) { 330 if (usb_debug >= 2) { 331#ifdef __FreeBSD_kernel__ 332 fprintf (stderr, "usb_bulk_read: got negative open file descriptor for endpoint %d\n", UE_GET_ADDR(ep)); 333#else 334 fprintf (stderr, "usb_bulk_read: got negative open file descriptor for endpoint %02d\n", UE_GET_ADDR(ep)); 335#endif 336 } 337 return fd; 338 } 339 340 ret = ioctl(fd, USB_SET_TIMEOUT, &timeout); 341 if (ret < 0) 342 USB_ERROR_STR(-errno, "error setting timeout: %s", strerror(errno)); 343 344 ret = ioctl(fd, USB_SET_SHORT_XFER, &one); 345 if (ret < 0) 346 USB_ERROR_STR(-errno, "error setting short xfer: %s", strerror(errno)); 347 348 ret = read(fd, bytes, size); 349 if (ret < 0) 350#ifdef __FreeBSD_kernel__ 351 USB_ERROR_STR(-errno, "error reading from bulk endpoint %s.%d: %s", 352 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 353#else 354 USB_ERROR_STR(-errno, "error reading from bulk endpoint %s.%02d: %s", 355 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 356#endif 357 358 return ret; 359} 360 361int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, 362 int timeout) 363{ 364 int fd, ret, sent = 0; 365 366 /* Ensure the endpoint address is correct */ 367 ep &= ~USB_ENDPOINT_IN; 368 369 fd = ensure_ep_open(dev, ep, O_WRONLY); 370 if (fd < 0) { 371 if (usb_debug >= 2) { 372#ifdef __FreeBSD_kernel__ 373 fprintf (stderr, "usb_interrupt_write: got negative open file descriptor for endpoint %d\n", UE_GET_ADDR(ep)); 374#else 375 fprintf (stderr, "usb_interrupt_write: got negative open file descriptor for endpoint %02d\n", UE_GET_ADDR(ep)); 376#endif 377 } 378 return fd; 379 } 380 381 ret = ioctl(fd, USB_SET_TIMEOUT, &timeout); 382 if (ret < 0) 383 USB_ERROR_STR(-errno, "error setting timeout: %s", 384 strerror(errno)); 385 386 do { 387 ret = write(fd, bytes+sent, size-sent); 388 if (ret < 0) 389#ifdef __FreeBSD_kernel__ 390 USB_ERROR_STR(-errno, "error writing to interrupt endpoint %s.%d: %s", 391 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 392#else 393 USB_ERROR_STR(-errno, "error writing to interrupt endpoint %s.%02d: %s", 394 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 395#endif 396 397 sent += ret; 398 } while (ret > 0 && sent < size); 399 400 return sent; 401} 402 403int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, 404 int timeout) 405{ 406 int fd, ret, retrieved = 0, one = 1; 407 408 /* Ensure the endpoint address is correct */ 409 ep |= USB_ENDPOINT_IN; 410 411 fd = ensure_ep_open(dev, ep, O_RDONLY); 412 if (fd < 0) { 413 if (usb_debug >= 2) { 414#ifdef __FreeBSD_kernel__ 415 fprintf (stderr, "usb_interrupt_read: got negative open file descriptor for endpoint %d\n", UE_GET_ADDR(ep)); 416#else 417 fprintf (stderr, "usb_interrupt_read: got negative open file descriptor for endpoint %02d\n", UE_GET_ADDR(ep)); 418#endif 419 } 420 return fd; 421 } 422 423 ret = ioctl(fd, USB_SET_TIMEOUT, &timeout); 424 if (ret < 0) 425 USB_ERROR_STR(-errno, "error setting timeout: %s", strerror(errno)); 426 427 ret = ioctl(fd, USB_SET_SHORT_XFER, &one); 428 if (ret < 0) 429 USB_ERROR_STR(-errno, "error setting short xfer: %s", strerror(errno)); 430 431 do { 432 ret = read(fd, bytes+retrieved, size-retrieved); 433 if (ret < 0) 434#ifdef __FreeBSD_kernel__ 435 USB_ERROR_STR(-errno, "error reading from interrupt endpoint %s.%d: %s", 436 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 437#else 438 USB_ERROR_STR(-errno, "error reading from interrupt endpoint %s.%02d: %s", 439 dev->device->filename, UE_GET_ADDR(ep), strerror(errno)); 440#endif 441 retrieved += ret; 442 } while (ret > 0 && retrieved < size); 443 444 return retrieved; 445} 446 447int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, 448 int value, int index, char *bytes, int size, int timeout) 449{ 450 struct usb_ctl_request req; 451 int ret; 452 453 if (usb_debug >= 3) 454 fprintf(stderr, "usb_control_msg: %d %d %d %d %p %d %d\n", 455 requesttype, request, value, index, bytes, size, timeout); 456 457 req.ucr_request.bmRequestType = requesttype; 458 req.ucr_request.bRequest = request; 459 USETW(req.ucr_request.wValue, value); 460 USETW(req.ucr_request.wIndex, index); 461 USETW(req.ucr_request.wLength, size); 462 463 req.ucr_data = bytes; 464 req.ucr_flags = USBD_SHORT_XFER_OK; 465 466 ret = ioctl(dev->fd, USB_SET_TIMEOUT, &timeout); 467#if (__NetBSD__ || __OpenBSD__) 468 if (ret < 0 && errno != EINVAL) 469#else 470 if (ret < 0) 471#endif 472 USB_ERROR_STR(-errno, "error setting timeout: %s", 473 strerror(errno)); 474 475 ret = ioctl(dev->fd, USB_DO_REQUEST, &req); 476 if (ret < 0) 477 USB_ERROR_STR(-errno, "error sending control message: %s", 478 strerror(errno)); 479 480 return UGETW(req.ucr_request.wLength); 481} 482 483int usb_os_find_busses(struct usb_bus **busses) 484{ 485 struct usb_bus *fbus = NULL; 486 int controller; 487 int fd; 488 char buf[20]; 489 490 for (controller = 0; controller < MAX_CONTROLLERS; controller++) { 491 struct usb_bus *bus; 492 493 snprintf(buf, sizeof(buf) - 1, "/dev/usb%d", controller); 494 fd = open(buf, O_RDWR); 495 if (fd < 0) { 496 if (usb_debug >= 2) 497 if (errno != ENXIO && errno != ENOENT) 498 fprintf(stderr, "usb_os_find_busses: can't open %s: %s\n", 499 buf, strerror(errno)); 500 continue; 501 } 502 close(fd); 503 504 bus = malloc(sizeof(*bus)); 505 if (!bus) 506 USB_ERROR(-ENOMEM); 507 508 memset((void *)bus, 0, sizeof(*bus)); 509 510 strncpy(bus->dirname, buf, sizeof(bus->dirname) - 1); 511 bus->dirname[sizeof(bus->dirname) - 1] = 0; 512 513 LIST_ADD(fbus, bus); 514 515 if (usb_debug >= 2) 516 fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname); 517 } 518 519 *busses = fbus; 520 521 return 0; 522} 523 524int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices) 525{ 526 struct usb_device *fdev = NULL; 527 int cfd, dfd; 528 int device; 529 530 cfd = open(bus->dirname, O_RDONLY); 531 if (cfd < 0) 532 USB_ERROR_STR(-errno, "couldn't open(%s): %s", bus->dirname, 533 strerror(errno)); 534 535 for (device = 1; device < USB_MAX_DEVICES; device++) { 536 struct usb_device_info di; 537 struct usb_device *dev; 538 unsigned char device_desc[DEVICE_DESC_LENGTH]; 539 char buf[20]; 540 541 di.udi_addr = device; 542 if (ioctl(cfd, USB_DEVICEINFO, &di) < 0) 543 continue; 544 545 /* There's a device; is it one we should mess with? */ 546 547 if (strncmp(di.udi_devnames[0], "ugen", 4) != 0) 548 /* best not to play with things we don't understand */ 549 continue; 550 551#ifdef __FreeBSD_kernel__ 552 snprintf(buf, sizeof(buf) - 1, "/dev/%s", di.udi_devnames[0]); 553#else 554 snprintf(buf, sizeof(buf) - 1, "/dev/%s.00", di.udi_devnames[0]); 555#endif 556 557 /* Open its control endpoint */ 558 dfd = open(buf, O_RDONLY); 559 if (dfd < 0) { 560 if (usb_debug >= 2) 561 fprintf(stderr, "usb_os_find_devices: couldn't open device %s: %s\n", 562 buf, strerror(errno)); 563 continue; 564 } 565 566 dev = malloc(sizeof(*dev)); 567 if (!dev) 568 USB_ERROR(-ENOMEM); 569 570 memset((void *)dev, 0, sizeof(*dev)); 571 572 dev->bus = bus; 573 574 /* we need to report the device name as /dev/ugenx NOT /dev/ugenx.00 575 * This seemed easier than having 2 variables... 576 */ 577#if (__NetBSD__ || __OpenBSD__) 578 snprintf(buf, sizeof(buf) - 1, "/dev/%s", di.udi_devnames[0]); 579#endif 580 581 strncpy(dev->filename, buf, sizeof(dev->filename) - 1); 582 dev->filename[sizeof(dev->filename) - 1] = 0; 583 584 if (ioctl(dfd, USB_GET_DEVICE_DESC, device_desc) < 0) 585 USB_ERROR_STR(-errno, "couldn't get device descriptor for %s: %s", 586 buf, strerror(errno)); 587 588 close(dfd); 589 590 usb_parse_descriptor(device_desc, "bbwbbbbwwwbbbb", &dev->descriptor); 591 592 LIST_ADD(fdev, dev); 593 594 if (usb_debug >= 2) 595 fprintf(stderr, "usb_os_find_devices: Found %s on %s\n", 596 dev->filename, bus->dirname); 597 } 598 599 close(cfd); 600 601 *devices = fdev; 602 603 return 0; 604} 605 606int usb_os_determine_children(struct usb_bus *bus) 607{ 608 /* Nothing yet */ 609 return 0; 610} 611 612void usb_os_init(void) 613{ 614 /* nothing */ 615} 616 617int usb_resetep(usb_dev_handle *dev, unsigned int ep) 618{ 619 /* Not yet done, because I haven't needed it. */ 620 621 USB_ERROR_STR(-ENOSYS, "usb_resetep called, unimplemented on BSD"); 622} 623 624int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) 625{ 626 /* Not yet done, because I haven't needed it. */ 627 628 USB_ERROR_STR(-ENOSYS, "usb_clear_halt called, unimplemented on BSD"); 629} 630 631int usb_reset(usb_dev_handle *dev) 632{ 633 /* Not yet done, because I haven't needed it. */ 634 635 USB_ERROR_STR(-ENOSYS, "usb_reset called, unimplemented on BSD"); 636} 637 638