/* $FreeBSD$ */ /*- * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __LIBUSB10_H__ #define __LIBUSB10_H__ /* * The two following macros were taken from the original LibUSB v1.0 * for sake of compatibility: */ #define USB_LIST_INIT(entry) \ (entry)->prev = (entry)->next = entry; #define USB_LIST_EMPTY(entry) \ ((entry)->next = (entry)) #define LIST_ADD(entry, head) \ (entry)->next = (head)->next; \ (entry)->prev = (head); \ (head)->next->prev = (entry); \ (head)->next = (entry); #define LIST_ADD_TAIL(entry, head) \ (entry)->next = (head); \ (entry)->prev = (head)->prev; \ (head)->prev->next = (entry); \ (head)->prev = (entry); #define LIST_DEL(entry) \ (entry)->next->prev = (entry)->prev; \ (entry)->prev->next = (entry)->next; #define LIST_ENT(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long) (&((type*)0L)->member))) #define LIST_FOREACH_ENTRY(pos, head, member) \ for (pos = LIST_ENT((head)->next, typeof(*pos), member) ; \ &pos->member != head ; \ pos = LIST_ENT(pos->member.next, typeof(*pos), member)) #define LIST_FOREACH_ENTRY_SAFE(pos, n, head, member) \ for (pos = LIST_ENT((head)->next, typeof(*pos), member), \ n = LIST_ENT(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = LIST_ENT(n->member.next, typeof(*n), member)) /* fetch libusb20_transfer from libusb20_device */ #define GET_XFER(xfer, endpoint, pdev)\ xfer = libusb20_tr_get_pointer(pdev, \ (2 *endpoint)|(endpoint/0x80)); \ if (xfer == NULL) \ return (LIBUSB_ERROR_OTHER); static int get_next_timeout(libusb_context *ctx, struct timeval *tv, struct timeval *out); static int handle_timeouts(struct libusb_context *ctx); static int handle_events(struct libusb_context *ctx, struct timeval *tv); extern struct libusb_context *usbi_default_context; extern pthread_mutex_t libusb20_lock; /* if ctx is NULL use default context*/ #define GET_CONTEXT(ctx) \ if (ctx == NULL) ctx = usbi_default_context; #define MAX(a,b) (((a)>(b))?(a):(b)) #define USB_TIMED_OUT (1<<0) static inline void dprintf(libusb_context *ctx, int debug, char *str) { if (ctx->debug != debug) return ; switch (ctx->debug) { case LIBUSB_DEBUG_NO: break ; case LIBUSB_DEBUG_FUNCTION: printf("LIBUSB FUNCTION : %s\n", str); break ; case LIBUSB_DEBUG_TRANSFER: printf("LIBUSB TRANSFER : %s\n", str); break ; default: printf("LIBUSB UNKNOW DEBUG\n"); break ; } return ; } struct usb_pollfd { struct libusb_pollfd pollfd; struct list_head list; }; struct usb_transfer { int num_iso_packets; struct list_head list; struct timeval timeout; int transferred; uint8_t flags; }; static inline int usb_add_pollfd(libusb_context *ctx, int fd, short events) { struct usb_pollfd *pollfd; if (ctx == NULL) return (LIBUSB_ERROR_INVALID_PARAM); pollfd = malloc(sizeof(*pollfd)); if (pollfd == NULL) return (LIBUSB_ERROR_NO_MEM); pollfd->pollfd.fd = fd; pollfd->pollfd.events = events; pthread_mutex_lock(&ctx->pollfds_lock); LIST_ADD_TAIL(&pollfd->list, &ctx->pollfds); pthread_mutex_unlock(&ctx->pollfds_lock); if (ctx->fd_added_cb) ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); return (0); } static inline void usb_remove_pollfd(libusb_context *ctx, int fd) { struct usb_pollfd *pollfd; int found; found = 0; pthread_mutex_lock(&ctx->pollfds_lock); LIST_FOREACH_ENTRY(pollfd, &ctx->pollfds, list) { if (pollfd->pollfd.fd == fd) { found = 1; break ; } } if (found == 0) { pthread_mutex_unlock(&ctx->pollfds_lock); return ; } LIST_DEL(&pollfd->list); pthread_mutex_unlock(&ctx->pollfds_lock); free(pollfd); if (ctx->fd_removed_cb) ctx->fd_removed_cb(fd, ctx->fd_cb_user_data); } static inline void usb_handle_transfer_completion(struct usb_transfer *uxfer, enum libusb_transfer_status status) { libusb_transfer *xfer; libusb_context *ctx; int len; xfer = (struct libusb_transfer *) ((uint8_t *)uxfer + sizeof(struct usb_transfer)); ctx = xfer->dev_handle->dev->ctx; pthread_mutex_lock(&ctx->flying_transfers_lock); LIST_DEL(&uxfer->list); pthread_mutex_unlock(&ctx->flying_transfers_lock); if (status == LIBUSB_TRANSFER_COMPLETED && xfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { len = xfer->length; if (xfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) len -= sizeof(libusb_control_setup); if (len != uxfer->transferred) { status = LIBUSB_TRANSFER_ERROR; } } xfer->status = status; xfer->actual_length = uxfer->transferred; if (xfer->callback) xfer->callback(xfer); if (xfer->flags & LIBUSB_TRANSFER_FREE_TRANSFER) libusb_free_transfer(xfer); pthread_mutex_lock(&ctx->event_waiters_lock); pthread_cond_broadcast(&ctx->event_waiters_cond); pthread_mutex_unlock(&ctx->event_waiters_lock); } static inline void usb_handle_disconnect(struct libusb_device_handle *devh) { struct libusb_context *ctx; struct libusb_transfer *xfer; struct usb_transfer *cur; struct usb_transfer *to_cancel; ctx = devh->dev->ctx; while (1) { pthread_mutex_lock(&ctx->flying_transfers_lock); to_cancel = NULL; LIST_FOREACH_ENTRY(cur, &ctx->flying_transfers, list) { xfer = (struct libusb_transfer *) ((uint8_t *)cur + sizeof(struct usb_transfer)); if (xfer->dev_handle == devh) { to_cancel = cur; break ; } } pthread_mutex_unlock(&ctx->flying_transfers_lock); if (to_cancel == NULL) break ; usb_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE); } return ; } #endif /*__LIBUSB10_H__*/