/* * HWA Host Controller Driver * Wire Adapter Control/Data Streaming Iface (WUSB1.0[8]) * * Copyright (C) 2005-2006 Intel Corporation * Inaky Perez-Gonzalez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * This driver implements a USB Host Controller (struct usb_hcd) for a * Wireless USB Host Controller based on the Wireless USB 1.0 * Host-Wire-Adapter specification (in layman terms, a USB-dongle that * implements a Wireless USB host). * * Check out the Design-overview.txt file in the source documentation * for other details on the implementation. * * Main blocks: * * driver glue with the driver API, workqueue daemon * * lc RC instance life cycle management (create, destroy...) * * hcd glue with the USB API Host Controller Interface API. * * nep Notification EndPoint managent: collect notifications * and queue them with the workqueue daemon. * * Handle notifications as coming from the NEP. Sends them * off others to their respective modules (eg: connect, * disconnect and reset go to devconnect). * * rpipe Remote Pipe management; rpipe is what we use to write * to an endpoint on a WUSB device that is connected to a * HWA RC. * * xfer Transfer management -- this is all the code that gets a * buffer and pushes it to a device (or viceversa). * * * Some day a lot of this code will be shared between this driver and * the drivers for DWA (xfer, rpipe). * * All starts at driver.c:hwahc_probe(), when one of this guys is * connected. hwahc_disconnect() stops it. * * During operation, the main driver is devices connecting or * disconnecting. They cause the HWA RC to send notifications into * nep.c:hwahc_nep_cb() that will dispatch them to * notif.c:wa_notif_dispatch(). From there they will fan to cause * device connects, disconnects, etc. * * Note much of the activity is difficult to follow. For example a * device connect goes to devconnect, which will cause the "fake" root * hub port to show a connect and stop there. Then khubd will notice * and call into the rh.c:hwahc_rc_port_reset() code to authenticate * the device (and this might require user intervention) and enable * the port. * * We also have a timer workqueue going from devconnect.c that * schedules in hwahc_devconnect_create(). * * The rest of the traffic is in the usual entry points of a USB HCD, * which are hooked up in driver.c:hwahc_rc_driver, and defined in * hcd.c. */ #ifndef __HWAHC_INTERNAL_H__ #define __HWAHC_INTERNAL_H__ #include #include #include #include #include #include #include struct wusbhc; struct wahc; extern void wa_urb_enqueue_run(struct work_struct *ws); extern void wa_process_errored_transfers_run(struct work_struct *ws); /** * RPipe instance * * @descr's fields are kept in LE, as we need to send it back and * forth. * * @wa is referenced when set * * @segs_available is the number of requests segments that still can * be submitted to the controller without overloading * it. It is initialized to descr->wRequests when * aiming. * * A rpipe supports a max of descr->wRequests at the same time; before * submitting seg_lock has to be taken. If segs_avail > 0, then we can * submit; if not, we have to queue them. */ struct wa_rpipe { struct kref refcnt; struct usb_rpipe_descriptor descr; struct usb_host_endpoint *ep; struct wahc *wa; spinlock_t seg_lock; struct list_head seg_list; struct list_head list_node; atomic_t segs_available; u8 buffer[1]; /* For reads/writes on USB */ }; enum wa_dti_state { WA_DTI_TRANSFER_RESULT_PENDING, WA_DTI_ISOC_PACKET_STATUS_PENDING }; /** * Instance of a HWA Host Controller * * Except where a more specific lock/mutex applies or atomic, all * fields protected by @mutex. * * @wa_descr Can be accessed without locking because it is in * the same area where the device descriptors were * read, so it is guaranteed to exist umodified while * the device exists. * * Endianess has been converted to CPU's. * * @nep_* can be accessed without locking as its processing is * serialized; we submit a NEP URB and it comes to * hwahc_nep_cb(), which won't issue another URB until it is * done processing it. * * @xfer_list: * * List of active transfers to verify existence from a xfer id * gotten from the xfer result message. Can't use urb->list because * it goes by endpoint, and we don't know the endpoint at the time * when we get the xfer result message. We can't really rely on the * pointer (will have to change for 64 bits) as the xfer id is 32 bits. * * @xfer_delayed_list: List of transfers that need to be started * (with a workqueue, because they were * submitted from an atomic context). * * FIXME: this needs to be layered up: a wusbhc layer (for sharing * comonalities with WHCI), a wa layer (for sharing * comonalities with DWA-RC). */ struct wahc { struct usb_device *usb_dev; struct usb_interface *usb_iface; /* HC to deliver notifications */ union { struct wusbhc *wusb; struct dwahc *dwa; }; const struct usb_endpoint_descriptor *dto_epd, *dti_epd; const struct usb_wa_descriptor *wa_descr; struct urb *nep_urb; /* Notification EndPoint [lockless] */ struct edc nep_edc; void *nep_buffer; size_t nep_buffer_size; atomic_t notifs_queued; u16 rpipes; unsigned long *rpipe_bm; /* rpipe usage bitmap */ struct list_head rpipe_delayed_list; /* delayed RPIPES. */ spinlock_t rpipe_lock; /* protect rpipe_bm and delayed list */ struct mutex rpipe_mutex; /* assigning resources to endpoints */ /* * dti_state is used to track the state of the dti_urb. When dti_state * is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and * dti_isoc_xfer_seg identify which xfer the incoming isoc packet status * refers to. */ enum wa_dti_state dti_state; u32 dti_isoc_xfer_in_progress; u8 dti_isoc_xfer_seg; struct urb *dti_urb; /* URB for reading xfer results */ struct urb *buf_in_urb; /* URB for reading data in */ struct edc dti_edc; /* DTI error density counter */ void *dti_buf; size_t dti_buf_size; unsigned long dto_in_use; /* protect dto endoint serialization. */ s32 status; /* For reading status */ struct list_head xfer_list; struct list_head xfer_delayed_list; struct list_head xfer_errored_list; /* * lock for the above xfer lists. Can be taken while a xfer->lock is * held but not in the reverse order. */ spinlock_t xfer_list_lock; struct work_struct xfer_enqueue_work; struct work_struct xfer_error_work; atomic_t xfer_id_count; }; extern int wa_create(struct wahc *wa, struct usb_interface *iface); extern void __wa_destroy(struct wahc *wa); void wa_reset_all(struct wahc *wa); /* Miscellaneous constants */ enum { /** Max number of EPROTO errors we tolerate on the NEP in a * period of time */ HWAHC_EPROTO_MAX = 16, /** Period of time for EPROTO errors (in jiffies) */ HWAHC_EPROTO_PERIOD = 4 * HZ, }; /* Notification endpoint handling */ extern int wa_nep_create(struct wahc *, struct usb_interface *); extern void wa_nep_destroy(struct wahc *); static inline int wa_nep_arm(struct wahc *wa, gfp_t gfp_mask) { struct urb *urb = wa->nep_urb; urb->transfer_buffer = wa->nep_buffer; urb->transfer_buffer_length = wa->nep_buffer_size; return usb_submit_urb(urb, gfp_mask); } static inline void wa_nep_disarm(struct wahc *wa) { usb_kill_urb(wa->nep_urb); } /* RPipes */ static inline void wa_rpipe_init(struct wahc *wa) { INIT_LIST_HEAD(&wa->rpipe_delayed_list); spin_lock_init(&wa->rpipe_lock); mutex_init(&wa->rpipe_mutex); } static inline void wa_init(struct wahc *wa) { edc_init(&wa->nep_edc); atomic_set(&wa->notifs_queued, 0); wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING; wa_rpipe_init(wa); edc_init(&wa->dti_edc); INIT_LIST_HEAD(&wa->xfer_list); INIT_LIST_HEAD(&wa->xfer_delayed_list); INIT_LIST_HEAD(&wa->xfer_errored_list); spin_lock_init(&wa->xfer_list_lock); INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run); INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run); wa->dto_in_use = 0; atomic_set(&wa->xfer_id_count, 1); } /** * Destroy a pipe (when refcount drops to zero) * * Assumes it has been moved to the "QUIESCING" state. */ struct wa_xfer; extern void rpipe_destroy(struct kref *_rpipe); static inline void __rpipe_get(struct wa_rpipe *rpipe) { kref_get(&rpipe->refcnt); } extern int rpipe_get_by_ep(struct wahc *, struct usb_host_endpoint *, struct urb *, gfp_t); static inline void rpipe_put(struct wa_rpipe *rpipe) { kref_put(&rpipe->refcnt, rpipe_destroy); } extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *); extern void rpipe_clear_feature_stalled(struct wahc *, struct usb_host_endpoint *); extern int wa_rpipes_create(struct wahc *); extern void wa_rpipes_destroy(struct wahc *); static inline void rpipe_avail_dec(struct wa_rpipe *rpipe) { atomic_dec(&rpipe->segs_available); } /** * Returns true if the rpipe is ready to submit more segments. */ static inline int rpipe_avail_inc(struct wa_rpipe *rpipe) { return atomic_inc_return(&rpipe->segs_available) > 0 && !list_empty(&rpipe->seg_list); } /* Transferring data */ extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *, struct urb *, gfp_t); extern int wa_urb_dequeue(struct wahc *, struct urb *); extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *); /* Misc * * FIXME: Refcounting for the actual @hwahc object is not correct; I * mean, this should be refcounting on the HCD underneath, but * it is not. In any case, the semantics for HCD refcounting * are *weird*...on refcount reaching zero it just frees * it...no RC specific function is called...unless I miss * something. * * FIXME: has to go away in favour of an 'struct' hcd based sollution */ static inline struct wahc *wa_get(struct wahc *wa) { usb_get_intf(wa->usb_iface); return wa; } static inline void wa_put(struct wahc *wa) { usb_put_intf(wa->usb_iface); } static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature) { return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, feature, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, NULL, 0, 1000 /* FIXME: arbitrary */); } static inline int __wa_set_feature(struct wahc *wa, u16 feature) { return __wa_feature(wa, 1, feature); } static inline int __wa_clear_feature(struct wahc *wa, u16 feature) { return __wa_feature(wa, 0, feature); } /** * Return the status of a Wire Adapter * * @wa: Wire Adapter instance * @returns < 0 errno code on error, or status bitmap as described * in WUSB1.0[8.3.1.6]. * * NOTE: need malloc, some arches don't take USB from the stack */ static inline s32 __wa_get_status(struct wahc *wa) { s32 result; result = usb_control_msg( wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, &wa->status, sizeof(wa->status), 1000 /* FIXME: arbitrary */); if (result >= 0) result = wa->status; return result; } /** * Waits until the Wire Adapter's status matches @mask/@value * * @wa: Wire Adapter instance. * @returns < 0 errno code on error, otherwise status. * * Loop until the WAs status matches the mask and value (status & mask * == value). Timeout if it doesn't happen. * * FIXME: is there an official specification on how long status * changes can take? */ static inline s32 __wa_wait_status(struct wahc *wa, u32 mask, u32 value) { s32 result; unsigned loops = 10; do { msleep(50); result = __wa_get_status(wa); if ((result & mask) == value) break; if (loops-- == 0) { result = -ETIMEDOUT; break; } } while (result >= 0); return result; } /** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */ static inline int __wa_stop(struct wahc *wa) { int result; struct device *dev = &wa->usb_iface->dev; result = __wa_clear_feature(wa, WA_ENABLE); if (result < 0 && result != -ENODEV) { dev_err(dev, "error commanding HC to stop: %d\n", result); goto out; } result = __wa_wait_status(wa, WA_ENABLE, 0); if (result < 0 && result != -ENODEV) dev_err(dev, "error waiting for HC to stop: %d\n", result); out: return 0; } #endif /* #ifndef __HWAHC_INTERNAL_H__ */