This is where we create, store and handle endpoint and device structures that are specific to the MA USB drivers. Each MA USB structure maps 1:1 with it's corresponding USB structure (e.g. there is one MA USB endpoint per USB endpoint). Signed-off-by: Sean O. Stalley <sean.stalley@xxxxxxxxx> Signed-off-by: Stephanie Wallick <stephanie.s.wallick@xxxxxxxxx> --- drivers/staging/mausb/drivers/mausb_const.h | 109 ++++ drivers/staging/mausb/drivers/mausb_mem-host.c | 402 ++++++++++++ drivers/staging/mausb/drivers/mausb_mem-host.h | 74 +++ drivers/staging/mausb/drivers/mausb_mem.c | 842 +++++++++++++++++++++++++ drivers/staging/mausb/drivers/mausb_mem.h | 509 +++++++++++++++ drivers/staging/mausb/drivers/mausb_state.h | 184 ++++++ 6 files changed, 2120 insertions(+) create mode 100755 drivers/staging/mausb/drivers/mausb_const.h create mode 100644 drivers/staging/mausb/drivers/mausb_mem-host.c create mode 100644 drivers/staging/mausb/drivers/mausb_mem-host.h create mode 100644 drivers/staging/mausb/drivers/mausb_mem.c create mode 100644 drivers/staging/mausb/drivers/mausb_mem.h create mode 100644 drivers/staging/mausb/drivers/mausb_state.h diff --git a/drivers/staging/mausb/drivers/mausb_const.h b/drivers/staging/mausb/drivers/mausb_const.h new file mode 100755 index 0000000..1089f81 --- /dev/null +++ b/drivers/staging/mausb/drivers/mausb_const.h @@ -0,0 +1,109 @@ +/* Name: mausb_const.h + * Description: This header describes the Media Agnostic USB constants + * that must be known by the both the host and device side drivers. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License 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. + * + * Contact Information: + * Sean Stalley, sean.stalley@xxxxxxxxx + * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx + * 2111 NE 25th Avenue + * Hillsboro, Oregon 97124 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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 __MAUSB_CONST_H +#define __MAUSB_CONST_H + +/* MA USB protocol constants per Table 68 of the MA USB Spec */ +#define MAUSB_DATA_CHANNEL_DELAY 400 /* temp value */ +#define MAUSB_MGMT_CHANNEL_DELAY ((100 * HZ)/1000 + 1) +#define MAUSB_MGMT_RESPONSE_DELAY ((5 * HZ)/1000 + 1) /* 5 msec */ +#define MAUSB_MGMT_RT_DELAY \ + ((MAUSB_MGMT_RESPONSE_DELAY + (2 * MAUSB_MGMT_CHANNEL_DELAY))) +#define MAUSB_TRANSFER_RESPONSE_TIME 10 /* 10 msec */ +#define MAUSB_TRANSFER_TIMEOUT \ + (MAUSB_TRANSFER_RESPONSE_TIME + (2 * MAUSB_DATA_CHANNEL_DELAY)) +#define MAUSB_TRANSFER_KEEP_ALIVE \ + (MAUSB_TRANSFER_RESPONSE_TIME + MAUSB_DATA_CHANNEL_DELAY) +#define MAUSB_DEFAULT_KEEP_ALIVE 0 +#define MAUSB_MAX_TRANSFER_LIFETIME 1000 /* 1 sec */ +#define MAUSB_TRANSFER_REPEAT_TIME 10 /* 10 msec */ + +#define MAUSB_MAX_REQ_ID ((1 << 8) - 1) +#define MAUSB_MAX_SEQ_NUM ((1 << 24) - 1) +#define MAUSB_MAX_DIALOG_TOKEN ((1 << 10) - 1) +#define MAUSB_MIN_CONTROL_BUF_SIZE 4104 /* Bytes */ + + +/* MA USB protocol variables per Table 69 of the MA USB Spec */ +#define MAUSB_BULK_TRANSFER_RETRIES 10 /* min value is 5 */ +#define MAUSB_CONTROL_TRANSFER_RETRIES 10 /* min value is 5 */ +#define MAUSB_INTERRUPT_TRANSFER_RETRIES 10 /* min value is 3 */ +#define MAUSB_MGMT_TRANSFER_RETRIES 4 /* min value is 4 */ +#define MAUSB_TRANSFER_SETUP_RETRIES 4 /* min value is 4 */ + + +/* MA USB constants not explicitly defined in MA USB Spec */ +#define MAUSB_HALF_REQ_ID ((MAUSB_MAX_REQ_ID + 1) >> 2) +#define MAUSB_HALF_SEQ_NUM ((MAUSB_MAX_SEQ_NUM + 1) >> 2) +#define MAUSB_MAX_OUTSTANDING_SEQ_NUM (1 << 23) + +/* Largest packet size (in bytes) that the medium can handle */ +#define MAUSB_MAX_PACKET_SIZE 300 + +/* Used to parse get_status control requests */ +#define EP_REQ (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT) +#define EP_IN_REQ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT) +#define DEV_IN_REQ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) +#define INTF_IN_REQ (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) + +/* Used for generating the mass_id for the device */ +#define MASS_ID_MIN 1 +#define MASS_ID_MAX 254 + + +#endif diff --git a/drivers/staging/mausb/drivers/mausb_mem-host.c b/drivers/staging/mausb/drivers/mausb_mem-host.c new file mode 100644 index 0000000..7ba0ec5 --- /dev/null +++ b/drivers/staging/mausb/drivers/mausb_mem-host.c @@ -0,0 +1,402 @@ +/* Name: mausb_mem-host.c + * Description: Contains hostside-specific memory functions, as well as + * wrappers for functions in mausb_mem.c. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License 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. + * + * Contact Information: + * Sean Stalley, sean.stalley@xxxxxxxxx + * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx + * 2111 NE 25th Avenue + * Hillsboro, Oregon 97124 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + */ + +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include <linux/kthread.h> + +#include "mausb_hcd.h" +#include "mausb_mem-host.h" +#include "mausb_mem.h" +#include "mausb_tx.h" +#include "mausb_mgmt.h" + +/** + * Returns a pointer to the media agnostic data for a given endpoint. + */ +struct mausb_host_ep *usb_to_ma_endpoint(struct usb_host_endpoint *ep) +{ + return (struct mausb_host_ep *) ep->hcpriv; +} + +/** + * Returns the number of urbs currently in the MA USB HCD. Will return 0 if the + * MA USB HCD is empty or a negative errno if an error occurs. + */ +int mausb_hcd_urb_count(struct mausb_hcd *mhcd) +{ + int count = 0; + struct mausb_host_ep *ma_ep; + struct mausb_dev *mausb_dev; + struct mausb_urb *maurb; + unsigned long irq_flags; + + /* for every device */ + spin_lock_irqsave(&mhcd->hcd_lock, irq_flags); + list_for_each_entry(mausb_dev, &mhcd->ma_dev.dev_list, dev_list) { + spin_unlock_irqrestore(&mhcd->hcd_lock, irq_flags); + + /* for every endpoint */ + spin_lock_irqsave(&mausb_dev->dev_lock, irq_flags); + list_for_each_entry(ma_ep, &mausb_dev->ep_list, ep_list) { + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + + /* for every urb */ + spin_lock_irqsave(&ma_ep->ep_lock, irq_flags); + list_for_each_entry(maurb, &ma_ep->urb_list, urb_list) { + ++count; + } + + spin_unlock_irqrestore(&ma_ep->ep_lock, irq_flags); + spin_lock_irqsave(&mausb_dev->dev_lock, irq_flags); + } + + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + spin_lock_irqsave(&mhcd->hcd_lock, irq_flags); + } + + spin_unlock_irqrestore(&mhcd->hcd_lock, irq_flags); + + return count; +} + +/** + * This function removes a device data from the hcd. All endpoints belonging + * to this device are also removed from the hcd and released (including EP0). + */ +void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev) +{ + struct mausb_hcd *mhcd = usb_hcd_to_mausb_hcd(hcd); + struct ma_dev *ma_dev = &mhcd->ma_dev; + struct mausb_dev *mausb_dev = mausb_find_dev_host(ma_dev, dev); + struct mausb_host_ep *ep0 = list_first_entry(&mausb_dev->ep_list, + struct mausb_host_ep, ep_list); + + if (NULL == mausb_dev) { + mausb_err(mhcd, "%s: USB device #%i not found\n", + __func__, dev->devnum); + return; + } + + ep0->ep_handle_state = MAUSB_EP_HANDLE_INACTIVE; + + /* inactivate EP0 handle */ + mausb_tx_dev_mgmt_req_ep(EPInactivateReq, &mhcd->ma_dev.mgmt, + mausb_dev, true, ep0); + + /* delete remaining EP Handles */ + mausb_tx_dev_mgmt_req(EPHandleDeleteReq, &mhcd->ma_dev.mgmt, + mausb_dev, MAUSB_HOST); + + mausb_tx_dev_mgmt_req(USBDevDisconnectReq, &ma_dev->mgmt, + mausb_dev, MAUSB_HOST); + + mausb_internal_free_dev(ma_dev, mausb_dev); + + return; +} + +/* + * Adds an endpoint to the specified device. + * + * This function allocates private endpoint data and adds the given endpoint + * to the given device. In the mausb_hcd, devices hold their endpoints in a + * linked list. + */ +int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev, + struct usb_host_endpoint *ep) +{ + int status; + char *ep_type; + struct mausb_hcd *mhcd = usb_hcd_to_mausb_hcd(hcd); + struct ma_dev *ma_dev = &mhcd->ma_dev; + struct mausb_dev *mausb_dev; + struct mausb_ms_drv *ms_driver = ma_dev->ms_driver; + struct mausb_host_ep *ma_ep; + + /* don't need to do anything for root hub */ + if ((NULL == dev->parent) && (&dev->ep0 != ep)) + return 0; + + mausb_dev = mausb_find_dev_host(ma_dev, dev); + if (NULL == mausb_dev) { + mausb_err(mhcd, "unable to add endpoint: USB device #%i not" + " found\n", dev->devnum); + return -ENODEV; + } + + status = mausb_internal_add_ep(mausb_dev, ep, NULL, &ma_ep, + &host_transfer_timeout, GFP_KERNEL); + + if (status < 0) { + mausb_err(mhcd, "could not add endpoint: error %i\n", status); + return status; + } + + /* need ms driver before adding endpoint */ + if (NULL == ms_driver) + return -ENODEV; + + /* for debug messages */ + switch (usb_endpoint_type(&ep->desc)) { + case USB_ENDPOINT_XFER_CONTROL: + ep_type = "control"; + break; + case USB_ENDPOINT_XFER_BULK: + ep_type = "bulk"; + break; + case USB_ENDPOINT_XFER_INT: + ep_type = "interrupt"; + break; + case USB_ENDPOINT_XFER_ISOC: + ep_type = "isochronous"; + break; + default: + ep_type = "unknown"; + } + + /* set up control endpoint data channel */ + if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_CONTROL) { + status = mausb_add_data_channel(ms_driver, ma_ep, + &complete_control_transferRequest); + } + /* set up bulk IN endpoint data channel */ + else if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK + && usb_endpoint_dir_in(&ep->desc)) { + status = mausb_add_data_channel(ms_driver, ma_ep, + &complete_IN_transferRequest); + } + /* set up bulk OUT endpoint data channel */ + else if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK + && usb_endpoint_dir_out(&ep->desc)) { + status = mausb_add_data_channel(ms_driver, ma_ep, + &complete_OUT_transferRequest); + } + /* set up an interrupt IN endpoint data channel */ + else if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_INT + && usb_endpoint_dir_in(&ep->desc)) { + status = mausb_add_data_channel(ms_driver, ma_ep, + &complete_IN_transferRequest); + } + /* set up an interrupt OUT endpoint data channel */ + else if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_INT + && usb_endpoint_dir_out(&ep->desc)) { + status = mausb_add_data_channel(ms_driver, ma_ep, + &complete_OUT_transferRequest); + } else { + mausb_err(mhcd, + "%s: attempted to add channel for ep %i of unsupported" + " type\n", __func__, usb_endpoint_num(&ep->desc)); + return -EOPNOTSUPP; + } + + if (status < 0) { + mausb_err(mhcd, "cannot add %s channel for ep %i: error %i\n", + ep_type, usb_endpoint_num(&ep->desc), status); + /* cleanup endpoint */ + mausb_internal_drop_ep(ma_ep); + } else { + mausb_dbg(mhcd, "%s: added %s channel for ep %i\n", __func__, + ep_type, usb_endpoint_num(&ep->desc)); + } + + return status; +} + +/* + * Removes an endpoint from the specified device. + * + * This function removes the given endpoint from the given device and releases + * private endpoint data. In the mausb_hcd, devices hold their endpoints in + * a linked list. + * + * TODO: determine & implement proper behavior when endpoint is not + * associated with given device (or is associated with a different + * device) + */ +int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev, + struct usb_host_endpoint *ep) +{ + int ret = 0; + struct mausb_hcd *mhcd = usb_hcd_to_mausb_hcd(hcd); + struct mausb_host_ep *ma_ep = usb_to_ma_endpoint(ep); + struct mausb_dev *ma_dev; + struct mausb_urb *ma_urb, *next_urb; + unsigned long irq_flags; + + if (NULL == ma_ep) { + mausb_err(mhcd, "%s: cannot find data for endpoint at %p\n", + __func__, ep); + return -ENODEV; + } + + ma_dev = ma_ep->mausb_dev; + + mausb_dbg(mhcd, "dropping endpoint at %p to USB device #%i\n", ep, + dev->devnum); + + ret = mausb_tx_dev_mgmt_req_ep(CancelTransferReq, &mhcd->ma_dev.mgmt, + ma_dev, true, ma_ep); + + ma_ep->ep_handle_state = MAUSB_EP_HANDLE_INACTIVE; + + ret = mausb_tx_dev_mgmt_req_ep(EPInactivateReq, &mhcd->ma_dev.mgmt, + ma_dev, true, ma_ep); + + spin_lock_irqsave(&ma_ep->ep_lock, irq_flags); + list_for_each_entry_safe(ma_urb, next_urb, &ma_ep->urb_list, urb_list) { + + mausb_unlink_giveback_urb(ma_urb, -ECONNRESET); + } + + if (!list_empty(&ma_ep->urb_list)) { + mausb_err(mhcd, "%s: could not drop all urbs for ma_ep at %p\n", + __func__, ma_ep->ep); + } + + spin_unlock_irqrestore(&ma_ep->ep_lock, irq_flags); + + return 0; +} + +/** + * This function allocates a new device and adds it onto the MA device's linked + * list of usb devices. It also allocates endpoint data for the control + * endpoint (EP0). + * + * Returns 1 on success or 0 on failure. + */ +int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev) +{ + int status = 0; + struct mausb_hcd *mhcd = usb_hcd_to_mausb_hcd(hcd); + struct ma_dev *ma_dev = &mhcd->ma_dev; + struct mausb_dev *mausb_dev; + + mausb_dev = mausb_internal_alloc_dev(ma_dev, dev, NULL); + if (NULL == mausb_dev) + return 0; + + /* add EP0 */ + mausb_dbg(mhcd, "%s: adding EP0 . . .\n", __func__); + status = mausb_add_endpoint(hcd, dev, &dev->ep0); + + /* + * Don't add send the EP Request yet, usbcore hasn't set + * the ep0 descriptor. Wait until a datapacket is sent. + */ + + if (status >= 0) + return 1; + + /* TODO: drop endpoint here if failure */ + mausb_free_dev(hcd, dev); + mausb_err(mhcd, "%s: could not add EP0 %i\n", __func__, status); + return 0; +} + +/** + * USB core calls the HCD's check_bandwidth function immediately after adding + * or deleting endpoints. + */ +int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev) +{ + int status; + struct mausb_hcd *mhcd = usb_hcd_to_mausb_hcd(hcd); + struct ma_dev *ma_dev = &mhcd->ma_dev; + struct mausb_dev *mausb_dev = mausb_find_dev_host(ma_dev, dev); + + /* don't do anything if roothub */ + if (NULL == dev->parent) + return 0; + + if (NULL == mausb_dev) { + mausb_err(mhcd, "%s: could not find data for USB device #%i\n", + __func__, dev->devnum); + return -ENODEV; + } + + /* delete the old endpoints (if applicable) */ + status = mausb_tx_dev_mgmt_req(EPHandleDeleteReq, &ma_dev->mgmt, + mausb_dev, MAUSB_HOST); + + /* TODO: error checking (suppose the EPHandleDelete fails...) */ + + /* add the new endpoints (if applicable) */ + status = mausb_tx_dev_mgmt_req(EPHandleReq, &ma_dev->mgmt, mausb_dev, + MAUSB_HOST); + + if (-ENODEV == status) /* no endpoints were applicable */ + status = 0; + + return status; +} + +/** + * This function will be called if mausb_add_endpoint(), mausb_drop_ep(), + * or mausb_check_bandwidth returns with an error. It is supposed to revert + * a device to a previously good schedule. We assume any schedule will work, + * so we should, in theory, never have to revert to a previous schedule. + */ +void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev) +{ + mausb_dbg(usb_hcd_to_mausb_hcd(hcd), + "resetting bandwidth for USB edvice #%i\n", dev->devnum); + return; +} + diff --git a/drivers/staging/mausb/drivers/mausb_mem-host.h b/drivers/staging/mausb/drivers/mausb_mem-host.h new file mode 100644 index 0000000..9df6c1f --- /dev/null +++ b/drivers/staging/mausb/drivers/mausb_mem-host.h @@ -0,0 +1,74 @@ +/* Name: mausb_mem-host.h + * Description: header for mausb_mem-host.c + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License 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. + * + * Contact Information: + * Sean Stalley, sean.stalley@xxxxxxxxx + * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx + * 2111 NE 25th Avenue + * Hillsboro, Oregon 97124 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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 __MAUSB_MEM_HOST_H +#define __MAUSB_MEM_HOST_H + +/* endpoint/device helper functions */ +struct mausb_host_ep *usb_to_ma_endpoint(struct usb_host_endpoint *ep); +int mausb_hcd_urb_count(struct mausb_hcd *mhcd); + +/* USB core interfaces */ +int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev); +void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev); +int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev, + struct usb_host_endpoint *ep); +int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev, + struct usb_host_endpoint *ep); +int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev); +void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev); + +#endif diff --git a/drivers/staging/mausb/drivers/mausb_mem.c b/drivers/staging/mausb/drivers/mausb_mem.c new file mode 100644 index 0000000..e378e2d --- /dev/null +++ b/drivers/staging/mausb/drivers/mausb_mem.c @@ -0,0 +1,842 @@ +/* Name: mausb_mem.c + * Description: Handles all of the dynamic data structures for the host + * controller, including any data that needs to exist + * on a per-device, per-endpoint, or per-transfer basis. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License 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. + * + * Contact Information: + * Sean Stalley, sean.stalley@xxxxxxxxx + * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx + * 2111 NE 25th Avenue + * Hillsboro, Oregon 97124 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + */ + +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/kthread.h> + +#include "mausb_mem.h" +#include "mausb_mgmt.h" +#include "mausb_tx.h" +#include "mausb_pkt.h" +#include "mausb_const.h" +#include "mausb_hcd.h" + +struct mausb_host_ep *mausb_state_to_ep(struct mausb_ep_state *state) +{ + return container_of(state, struct mausb_host_ep, state); +} +EXPORT_SYMBOL(mausb_state_to_ep); + +struct mausb_hcd *mausb_host_ep_to_mahcd(struct mausb_host_ep *ep) +{ + return ep->mausb_dev->ma_dev->mhcd; +} +EXPORT_SYMBOL(mausb_host_ep_to_mahcd); + +struct mausb_udc *mausb_host_ep_to_maudc(struct mausb_host_ep *ep) +{ + return ep->mausb_dev->ma_dev->maudc; +} +EXPORT_SYMBOL(mausb_host_ep_to_maudc); + +struct ma_dev *context_to_ma_dev(void *context) +{ + return (struct ma_dev *) context; +} + +/* + * Finds the media-agnostic data for a given device. + * + * @ma_dev: The MA USB device the USB device is located behind. + * @dev: The USB device to find. + * @handle: The MA USB device handle of USB device to find. + * @gadget: The USB gadget device to find. + * + * Returns a pointer to the media agnostic data for a USB device given one or + * more parameters. Returns NULL if the device cannot be found. + */ +struct mausb_dev *mausb_find_dev(struct ma_dev *ma_dev, + struct usb_device *dev, unsigned long *handle, + struct usb_gadget *gadget) +{ + struct mausb_dev *mausb_dev; + unsigned long irq_flags; + + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags); + list_for_each_entry(mausb_dev, &ma_dev->dev_list, dev_list) { + + /* if found */ + if ((NULL != dev && mausb_dev->dev == dev) || + (NULL != gadget && mausb_dev->gadget == gadget) || + (NULL != handle && mausb_dev->dev_handle == *handle)) { + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + return mausb_dev; + } + } + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + + /* device not found */ + return NULL; +} + +struct mausb_dev *mausb_find_dev_host(struct ma_dev *ma_dev, + struct usb_device *dev) +{ + return mausb_find_dev(ma_dev, dev, NULL, NULL); +} +EXPORT_SYMBOL(mausb_find_dev_host); + +/** + * Returns true if usb_endpoint descriptors are the same. + */ +static bool mausb_ep_desc_eq(const struct usb_endpoint_descriptor *desc1, + const struct usb_endpoint_descriptor *desc2) +{ + return ((desc1->bLength == desc2->bLength) + && (desc1->bDescriptorType == desc2->bDescriptorType) + && (desc1->bEndpointAddress == desc2->bEndpointAddress) + && (desc1->bmAttributes == desc2->bmAttributes) + && (desc1->wMaxPacketSize == desc2->wMaxPacketSize)); +} + +/** + * Finds the media agnostic data for an endpoint given one or more parameters. + * + * @ep: USB host endpoint to find MA USB endpoint for. + * @dev_ep: USB device endpoint to find MA USB endpoint for. + * @handle: Endpoint handle of MA USB endpoint. + * @mausb_dev: The MA USB device the endpoint belongs to. + * @ep_desc: endpoint descriptor for USB endpoint. + * @address: MA USB endpoint address. + * + * Returns a pointer to the media agnostic data for the endpoint or NULL if + * the device cannot be found. + */ +static struct mausb_host_ep *mausb_find_ep(struct usb_host_endpoint *ep, + struct usb_ep *dev_ep, struct mausb_ep_handle *handle, + struct mausb_dev *mausb_dev, + const struct usb_endpoint_descriptor *ep_desc, + u8 address) +{ + struct mausb_host_ep *ma_ep; + unsigned long irq_flags; + + spin_lock_irqsave(&mausb_dev->dev_lock, irq_flags); + list_for_each_entry(ma_ep, &mausb_dev->ep_list, ep_list) { + + /* if found */ + if ((NULL != dev_ep && ma_ep->dev_ep == dev_ep) || + (NULL != ep && ma_ep->ep == ep) || + (NULL != handle + && ma_ep->ep_handle_state != MAUSB_EP_HANDLE_UNASSIGNED + && ma_ep->ep_handle.handle == handle->handle) || + (NULL != ep_desc && NULL != ma_ep->ep + && mausb_ep_desc_eq(ep_desc, &ma_ep->ep->desc)) || + (NULL != ep_desc && NULL != ma_ep->dev_ep && + NULL != ma_ep->dev_ep->desc + && mausb_ep_desc_eq(ep_desc, ma_ep->dev_ep->desc)) || + (address != 0 && NULL != ma_ep->dev_ep && + address == ma_ep->dev_ep->address)) { + + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + return ma_ep; + } + } + + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + + /* endpoint not found */ + return NULL; +} + +struct mausb_host_ep *mausb_find_ep_dev(struct usb_ep *dev_ep, + struct mausb_dev *mausb_dev) +{ + return mausb_find_ep(NULL, dev_ep, NULL, mausb_dev, NULL, 0); +} +EXPORT_SYMBOL(mausb_find_ep_dev); + +struct mausb_host_ep *mausb_find_ep_host(struct usb_host_endpoint *ep, + struct mausb_dev *mausb_dev) +{ + return mausb_find_ep(ep, NULL, NULL, mausb_dev, NULL, 0); +} +EXPORT_SYMBOL(mausb_find_ep_host); + +struct mausb_host_ep *mausb_find_ep_by_desc( + const struct usb_endpoint_descriptor *ep_desc, + struct mausb_dev *mausb_dev) + +{ + return mausb_find_ep(NULL, NULL, NULL, mausb_dev, ep_desc, 0); +} +EXPORT_SYMBOL(mausb_find_ep_by_desc); + +struct mausb_host_ep *mausb_find_ep_by_handle(struct mausb_ep_handle *handle, + struct mausb_dev *mausb_dev) +{ + return mausb_find_ep(NULL, NULL, handle, mausb_dev, NULL, 0); +} +EXPORT_SYMBOL(mausb_find_ep_by_handle); + +struct mausb_host_ep *mausb_find_ep_by_address(u8 address, + struct mausb_dev *mausb_dev) + +{ + return mausb_find_ep(NULL, NULL, NULL, mausb_dev, NULL, address); +} +EXPORT_SYMBOL(mausb_find_ep_by_address); + +struct mausb_host_ep *mausb_find_ep_from_handle(struct mausb_ep_handle handle, + struct ma_dev *ma_dev) +{ + struct mausb_host_ep *ma_ep; + struct mausb_dev *mausb_dev; + unsigned long irq_flags; + + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags); + list_for_each_entry(mausb_dev, &ma_dev->dev_list, dev_list) { + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + + ma_ep = mausb_find_ep_by_handle(&handle, mausb_dev); + if (ma_ep != NULL) + return ma_ep; + + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags); + } + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + + return NULL; +} + +/** + * Wrapper function to be used by devices when they want to generate an + * ma packet. + */ +struct mausb_pkt *mausb_pkt_from_ms_pkt_ma_dev(struct ms_pkt *ms_pkt, + struct ma_dev *ma_dev, gfp_t mem_flags) +{ + return mausb_pkt_from_ms_pkt(ms_pkt, + ma_dev->ms_driver->ops->pkt_destructor, mem_flags); +} +EXPORT_SYMBOL(mausb_pkt_from_ms_pkt_ma_dev); + +/** + * Wrapper to be used by endpoints when they want to generate an ma packet. + */ +struct mausb_pkt *mausb_pkt_from_ms_pkt_ep(struct ms_pkt *ms_pkt, + struct mausb_host_ep *ep, gfp_t mem_flags) +{ + return mausb_pkt_from_ms_pkt_ma_dev(ms_pkt, ep->mausb_dev->ma_dev, + mem_flags); +} +EXPORT_SYMBOL(mausb_pkt_from_ms_pkt_ep); + +const struct usb_endpoint_descriptor *mausb_get_ep_des( + struct mausb_host_ep *ma_ep) +{ + if ((NULL != ma_ep->ep && NULL != ma_ep->dev_ep) || + (NULL == ma_ep->ep && NULL == ma_ep->dev_ep)) { + + mamem_err("%s: ERROR: ep = 0x%p, dev_ep = 0x%p " + "(only one should be set)\n", __func__, + ma_ep->ep, ma_ep->dev_ep); + + return NULL; + } + + if (NULL != ma_ep->ep) + return &ma_ep->ep->desc; + + if (NULL != ma_ep->dev_ep) + return ma_ep->dev_ep->desc; + + /* We should never get here */ + BUG(); + return NULL; +} +EXPORT_SYMBOL(mausb_get_ep_des); + +/** + * Fills an MA USB packet header for the given maurb + */ +static int fill_mausb_dph(struct mausb_dph *ma_pkt, struct mausb_urb *maurb, + bool in, bool host) +{ + const struct usb_endpoint_descriptor *ep_des; + unsigned long irq_flags; + + /* set the version number */ + ma_pkt->common.ver_flags = MAUSB_VERSION_1_0 & MAUSB_VERSION_MASK; + + /* set the host flag if from the host */ + ma_pkt->common.ver_flags |= host ? MAUSB_PKT_FLAG_HOST : 0; + + /* set the device handle field */ + spin_lock_irqsave(&maurb->ep->ep_lock, irq_flags); + ma_pkt->common.ep_handle = maurb->ep->ep_handle; + spin_unlock_irqrestore(&maurb->ep->ep_lock, irq_flags); + + ma_pkt->common.pkt_status = SUCCESS; + + ep_des = mausb_get_ep_des(maurb->ep); + + /* set transfer type in tflags field */ + switch (usb_endpoint_type(ep_des)) { + case USB_ENDPOINT_XFER_CONTROL: + ma_pkt->eps_tflags |= MAUSB_PKT_TFLAG_TYPE_CTRL; + break; + case USB_ENDPOINT_XFER_ISOC: + ma_pkt->eps_tflags |= MAUSB_PKT_TFLAG_TYPE_ISOC; + break; + case USB_ENDPOINT_XFER_BULK: + ma_pkt->eps_tflags |= MAUSB_PKT_TFLAG_TYPE_BULK; + break; + case USB_ENDPOINT_XFER_INT: + ma_pkt->eps_tflags |= MAUSB_PKT_TFLAG_TYPE_INT; + break; + default: + mamem_err("%s: invalid transfer type\n", __func__); + } + + return 0; +} + +/** + * Create a datapacket from a given URB. + */ +struct mausb_pkt *mausb_create_dp(int *status, struct mausb_urb *maurb, + bool in, bool host, gfp_t memflags) +{ + struct mausb_pkt *pkt; + int ret = 0; + + pkt = mausb_alloc_pkt(MAUSB_PKT_TYPE_DATA, status, memflags); + if (NULL == pkt) { + mamem_err("%s:failed to allocate memory for mausb packet\n", + __func__); + + if (NULL != status) + *status = -ENOMEM; + + return NULL; + } + + ret = fill_mausb_dph(pkt->data, maurb, in, host); + INIT_LIST_HEAD(&pkt->pkt_list); + if (NULL != status) + *status = ret; + + return pkt; +} +EXPORT_SYMBOL(mausb_create_dp); + +struct mausb_urb *mausb_alloc_maurb(struct mausb_host_ep *ma_ep, gfp_t memflags) +{ + struct mausb_urb *maurb; + + maurb = kzalloc(sizeof(struct mausb_urb), memflags); + if (!maurb) + return NULL; + + INIT_LIST_HEAD(&maurb->urb_list); + maurb->ep = ma_ep; + maurb->dev = ma_ep->mausb_dev; + + return maurb; +} +EXPORT_SYMBOL(mausb_alloc_maurb); + +/** + * Deletes the given maurb and removes all pointers to it from memory. + */ +int mausb_internal_drop_maurb(struct mausb_urb *maurb, struct mausb_hcd *mhcd) +{ + struct mausb_host_ep *ep = maurb->ep; + unsigned long irq_flags; + + spin_lock_irqsave(&ep->ep_lock, irq_flags); + + list_del(&maurb->urb_list); + if (maurb == ep->active_transfer) + ep->active_transfer = NULL; + + spin_unlock_irqrestore(&ep->ep_lock, irq_flags); + + spin_lock_irqsave(&mhcd->urb_list_lock, irq_flags); + list_del(&maurb->ma_hcd_urb_list); + spin_unlock_irqrestore(&mhcd->urb_list_lock, irq_flags); + + /* + * Nobody else should know this maurb exists, so it should be + * safe to free + */ + kfree(maurb); + + return 0; +} +EXPORT_SYMBOL(mausb_internal_drop_maurb); + +/** + * Sets endpoint state to default. + * + * @state: State of endpoint to be configured. + * @buf_size: The maximum number of Bytes the device-side buffer + * can receive from the host. + * + * Called whenever an MA USB endpoint is configured and needs to be set + * or reset to its initial state. + */ +void init_ep_state(struct mausb_ep_state *state, u32 buf_size) +{ + state->request_id = 0; + state->active_request_id = 0; + state->seq_number = 0; + state->tx_pending = 0; + state->earliest_request_id = 0; + state->earliest_unacked = 0; + state->occupancy = 0; + state->rx_buf_size = buf_size; + state->keep_alive_timer = MAUSB_TRANSFER_KEEP_ALIVE; + state->retry_counter = MAUSB_BULK_TRANSFER_RETRIES; + /* TODO: number of retries depends on transfer type, include + * other transfer types (control and interrupt) */ +} + +struct mausb_gadget_ep *usb_ep_to_mausb_gadget_ep(struct usb_ep *ep) +{ + return container_of(ep, struct mausb_gadget_ep, dev_ep); +} +EXPORT_SYMBOL(usb_ep_to_mausb_gadget_ep); + +/** + * Links a Device-side endpoint to an MA USB endpoint + */ +void mausb_link_ma_ep_to_usb_ep(struct mausb_host_ep *ma_ep, + struct usb_ep *dev_ep) +{ + struct mausb_gadget_ep *gadget_ep = usb_ep_to_mausb_gadget_ep(dev_ep); + + ma_ep->dev_ep = dev_ep; + ma_ep->halted = 0; + ma_ep->wedged = 0; + + ma_ep->usb_req_list = &gadget_ep->usb_req_list; +} +EXPORT_SYMBOL(mausb_link_ma_ep_to_usb_ep); + +/* + * Adds an endpoint to the specified device. + * + * @ma_dev: The usb device to add the endpoint to. + * @ep/dev_ep: The usb struct for the endpoint being added. + * Note: give only one of the endpoint pointers and + * set the other to NULL. + * @mausb_ep: The mausb_ep struct created by this function. + * @transfer_timeout: The transfer timeout function. A thread will be + * spawned with this function for handling timeouts. + * + * Adds the given endpoint to the given device. The difference between this + * function and mausb_add_endpoint (which can be called from the usb core) + * is that this uses the private ma-structs. Calling this function is + * cheaper, and therefore preferred when possible, since mausb_add_endpoint + * will have to find the internal structures. + */ +int mausb_internal_add_ep(struct mausb_dev *ma_dev, + struct usb_host_endpoint *ep, struct usb_ep *dev_ep, + struct mausb_host_ep **mausb_ep, + int (*transfer_timeout)(void *data), gfp_t memflags) +{ + struct mausb_host_ep *ma_ep; + unsigned long irq_flags; + + ma_ep = kzalloc(sizeof(struct mausb_host_ep), memflags); + if (!ma_ep) + return -ENOMEM; + + /* host operations */ + if (ep != NULL) { + ep->hcpriv = ma_ep; + ma_ep->ep = ep; + + /* device operations */ + } else if (dev_ep != NULL) + mausb_link_ma_ep_to_usb_ep(ma_ep, dev_ep); + + ma_ep->mausb_dev = ma_dev; + + /* set initial EP State */ + init_ep_state(&ma_ep->state, DEVICE_RX_BUF_SIZE); + + /* + * only used by full speed devices to determine if the max packet + * size has changed for EP0 + */ + ma_ep->max_pkt = 8; + + /* initialize mausb_ep_handle */ + ma_ep->ep_handle_state = MAUSB_EP_HANDLE_UNASSIGNED; + + INIT_LIST_HEAD(&ma_ep->urb_list); + INIT_LIST_HEAD(&ma_ep->ep_list); + INIT_LIST_HEAD(&ma_ep->req_list); + INIT_LIST_HEAD(&ma_ep->resp_list); + + spin_lock_init(&ma_ep->ep_lock); + ma_ep->tx_timed_out = false; + + /* initialize endpoint work queue */ + init_waitqueue_head(&ma_ep->host_ep_wq); + + /* initialize endpoint timer */ + setup_timer(&ma_ep->timer, wake_timeout_thread, (unsigned long) ma_ep); + + /* add the endpoint to the device */ + spin_lock_irqsave(&ma_dev->dev_lock, irq_flags); + list_add_tail(&ma_ep->ep_list, &ma_dev->ep_list); + spin_unlock_irqrestore(&ma_dev->dev_lock, irq_flags); + + /* give a pointer to the ma_ep back to the caller */ + if (NULL != mausb_ep) + *mausb_ep = ma_ep; + + return 0; +} +EXPORT_SYMBOL(mausb_internal_add_ep); + +/** + * Drops the given endpoint from the given device. The difference between this + * function and mausb_drop_endpoint (which can be called from the usb core) + * is that this uses the private ma-structs. Calling this function is + * cheaper, and therefore preferred when possible, since mausb_drop_endpoint + * will have to find the internal endpoint structure. + */ +int mausb_internal_drop_ep(struct mausb_host_ep *ma_ep) +{ + struct mausb_dev *ma_dev; + struct mausb_request *ma_request, *next_request; + struct mausb_pkt *ma_pkt, *next_pkt; + unsigned long irq_flags; + unsigned long irq_flags2; + + if (NULL == ma_ep) + return -EINVAL; + + /* drop all pending transfers */ + spin_lock_irqsave(&ma_ep->ep_lock, irq_flags); + ma_dev = ma_ep->mausb_dev; + + /* stop transfer timeout thread */ + if (NULL != ma_ep->timeout_task) + kthread_stop(ma_ep->timeout_task); + + /* drop all pending usb_requests (if there are any) */ + if (NULL != ma_ep->usb_req_list) { + list_for_each_entry_safe(ma_request, next_request, + ma_ep->usb_req_list, usb_req_list) { + list_del(&ma_request->usb_req_list); + ma_request->req.status = -ESHUTDOWN; + ma_request->req.complete(ma_ep->dev_ep, + &ma_request->req); + } + } + + /* flush out all of the stored request/response packets */ + list_for_each_entry_safe(ma_pkt, next_pkt, &ma_ep->req_list, pkt_list) { + list_del(&ma_pkt->pkt_list); + mausb_free_pkt(ma_pkt); + } + list_for_each_entry_safe(ma_pkt, next_pkt, + &ma_ep->resp_list, pkt_list) { + list_del(&ma_pkt->pkt_list); + mausb_free_pkt(ma_pkt); + } + + /* remove the endpoint from the list */ + spin_lock_irqsave(&ma_dev->dev_lock, irq_flags2); + list_del(&ma_ep->ep_list); + spin_unlock_irqrestore(&ma_dev->dev_lock, irq_flags2); + + spin_unlock_irqrestore(&ma_ep->ep_lock, irq_flags); + + kfree(ma_ep); + + return 0; +} +EXPORT_SYMBOL(mausb_internal_drop_ep); + +/** + * Updates the state of an endpoint based on the mausb_ep_des. + */ +int mausb_update_ep(struct mausb_host_ep *ma_ep, + struct mausb_ep_des *ma_ep_des) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&ma_ep->ep_lock, irq_flags); + + if (!ma_ep_des->valid) { /* Valid bit: 0 means valid */ + ma_ep->ep_handle.handle = ma_ep_des->ep_handle.handle; + ma_ep->ep_handle_state = MAUSB_EP_HANDLE_ACTIVE; + } + + if (ma_ep_des->dir == 0) { + ma_ep->state.rx_buf_size = ma_ep_des->buffer_size; + + + if (ma_ep_des->l_man) + ma_ep->lman_support = true; + else + ma_ep->lman_support = false; + } + + /*TODO: parse and use the remainder of the values in the packet */ + + spin_unlock_irqrestore(&ma_ep->ep_lock, irq_flags); + + return 0; +} + +/** + * Allocates private per-usb-device data. + * + * @ma_dev: The MA USB device to which the USB device belongs + * @usb_dev: The host-side USB device struct to add. + * @gadget: The device-side USB device struct to add. + * + * This function allocates a new device and adds it onto the MA USB device's + * linked list of usb devices. It also allocates endpoint data for the control + * endpoint (EP0). + */ +struct mausb_dev *mausb_internal_alloc_dev(struct ma_dev *ma_dev, + struct usb_device *dev, struct usb_gadget *gadget) +{ + unsigned long irq_flags; + struct mausb_dev *mausb_dev; + + mausb_dev = kzalloc(sizeof(struct mausb_dev), GFP_KERNEL); + + if (!mausb_dev) + return NULL; + + if (NULL != dev) + mausb_dev->dev = dev; + if (NULL != gadget) + mausb_dev->gadget = gadget; + mausb_dev->ma_dev = ma_dev; + INIT_LIST_HEAD(&mausb_dev->ep_list); + INIT_LIST_HEAD(&mausb_dev->dev_list); + spin_lock_init(&mausb_dev->dev_lock); + + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags); + + /* + * If this is the first device to be allocated, then it should be the + * most upstream. + */ + if (NULL == ma_dev->top_dev) + ma_dev->top_dev = mausb_dev; + + list_add_tail(&mausb_dev->dev_list, &ma_dev->dev_list); + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + + return mausb_dev; +} +EXPORT_SYMBOL(mausb_internal_alloc_dev); + +/** + * Frees private per-device data. + * + * @ma_dev: The MA USB device that the USB device beingfreed belongs to. + * @mausb_dev: The MA USB data associated with the USB device being freed. + * + * This function removes a USB device from the MA USB hcd's linked list + * and releases the device's media agnostic data structure. All endpoints + * belonging to this device are also removed from the MA USB hcd and released + * (including EP0). + */ +int mausb_internal_free_dev(struct ma_dev *ma_dev, struct mausb_dev *mausb_dev) +{ + int ret = 0; + unsigned long irq_flags, irq_flags2; + struct mausb_host_ep *ma_ep, *next; + + /* remove all the endpoints from the device */ + spin_lock_irqsave(&mausb_dev->dev_lock, irq_flags); + list_for_each_entry_safe(ma_ep, next, &mausb_dev->ep_list, ep_list) { + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + ret = mausb_internal_drop_ep(ma_ep); + + spin_lock_irqsave(&mausb_dev->dev_lock, irq_flags); + } + + if (!list_empty(&mausb_dev->ep_list)) { + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + return ret; + } + + /* remove the mausb device from the ma device */ + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags2); + list_del(&mausb_dev->dev_list); + + /* if the top device is being removed */ + if (ma_dev->top_dev == mausb_dev) + ma_dev->top_dev = NULL; + + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags2); + + spin_unlock_irqrestore(&mausb_dev->dev_lock, irq_flags); + + kfree(mausb_dev); + + return ret; + +} +EXPORT_SYMBOL(mausb_internal_free_dev); + +/** + * Resets all the data in a ma_dev struct. This includes freeing all the MA + * data for the usb devices and endpoints connected to this device. + */ +int mausb_internal_reset_ma_dev(struct ma_dev *ma_dev, __u8 ma_dev_addr, + __u8 mass_id, struct mausb_hcd *mhcd, struct mausb_udc *maudc) +{ + int ret = -EINVAL; + struct mausb_dev *mausb_dev, *mausb_next; + int (*req_switch)(struct mausb_mgmt_pkt *req, + struct mausb_mgmt_pkt *resp, + struct mausb_mgmt *mgmt) + = ma_dev->mgmt.req_switch; + int (*device_connect)(int) = ma_dev->ma_drv.device_connect; + unsigned long irq_flags; + + if (NULL != mhcd) + ma_dev->mhcd = mhcd; + + if (NULL != maudc) + ma_dev->maudc = maudc; + + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags); + list_for_each_entry_safe(mausb_dev, mausb_next, &ma_dev->dev_list, + dev_list) { + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + + ret = mausb_internal_free_dev(ma_dev, mausb_dev); + + spin_lock_irqsave(&ma_dev->ma_dev_lock, irq_flags); + } + + if (!list_empty(&ma_dev->dev_list)) { + /* could not clear all the devices */ + ret = -EAGAIN; + } + + spin_unlock_irqrestore(&ma_dev->ma_dev_lock, irq_flags); + + /* TODO: make sure this doesn't break any locking rules */ + mausb_init_ma_device(ma_dev, ma_dev_addr, mass_id, mhcd, + maudc, req_switch, device_connect); + + return ret; +} + +/** + * Initializes the media agnostic management structure. + */ +int mausb_init_mgmt(struct mausb_mgmt *mgmt, spinlock_t *ma_dev_lock, + int (*req_switch)(struct mausb_mgmt_pkt *req, + struct mausb_mgmt_pkt *resp, struct mausb_mgmt *mgmt)) +{ + INIT_LIST_HEAD(&mgmt->req_list); + mgmt->token = 0; + mgmt->tx_pair.to_ms.context = NULL; + mgmt->tx_pair.to_ms.transfer_packet = NULL; + mgmt->tx_pair.to_ma.context = NULL; + mgmt->tx_pair.to_ma.transfer_packet = NULL; + mgmt->hub_dev_handle = 0; + mgmt->ma_dev_lock = ma_dev_lock; + mgmt->req_switch = req_switch; + + return 0; +} + +static void mausb_init_pkt_dmux(struct mausb_pkt_transfer *pkt_dmux, + void *context) +{ + pkt_dmux->transfer_packet = &mausb_pkt_dmux; + pkt_dmux->context = context; +} + +/* + * Initializes the data structure for an MA USB device. + */ +int mausb_init_ma_device(struct ma_dev *ma_dev, __u8 ma_dev_addr, __u8 mass_id, + struct mausb_hcd *mhcd, struct mausb_udc *maudc, + int (*req_switch)(struct mausb_mgmt_pkt *req, + struct mausb_mgmt_pkt *resp, struct mausb_mgmt *mgmt), + int (*device_connect)(int)) +{ + INIT_LIST_HEAD(&ma_dev->dev_list); + ma_dev->top_dev = NULL; + ma_dev->ma_dev_addr = ma_dev_addr; + ma_dev->mass_id = mass_id; + if (mhcd != NULL) + ma_dev->mhcd = mhcd; + if (maudc != NULL) + ma_dev->maudc = maudc; + spin_lock_init(&ma_dev->ma_dev_lock); + ma_dev->ma_drv.device_connect = device_connect; + mausb_init_pkt_dmux(&ma_dev->ma_drv.pkt_dmux, ma_dev); + + mausb_init_mgmt(&ma_dev->mgmt, &ma_dev->ma_dev_lock, req_switch); + + return 0; +} +EXPORT_SYMBOL(mausb_init_ma_device); + diff --git a/drivers/staging/mausb/drivers/mausb_mem.h b/drivers/staging/mausb/drivers/mausb_mem.h new file mode 100644 index 0000000..870591f --- /dev/null +++ b/drivers/staging/mausb/drivers/mausb_mem.h @@ -0,0 +1,509 @@ +/* Name: mausb_mem.h + * Description: Header for mausb_mem.c. Needed do make calls to + * functions defined in mausb_mem.c. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License 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. + * + * Contact Information: + * Sean Stalley, sean.stalley@xxxxxxxxx + * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx + * 2111 NE 25th Avenue + * Hillsboro, Oregon 97124 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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 __MAUSB_MEM_H +#define __MAUSB_MEM_H + +#include "mausb_state.h" +#include "mausb_pkt.h" +#include "mausb_msapi.h" + +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/usb/gadget.h> +#include <linux/timer.h> + +#ifdef DEBUG +#define mamem_dbg(format, arg...) \ + printk(KERN_DEBUG format, ##arg) +#else +#define mamem_dbg(format, arg...) +#endif + +#define mamem_warn(format, arg...) \ + printk(KERN_WARNING format, ##arg) + +#define mamem_err(format, arg...) \ + printk(KERN_ERR format, ##arg) + +/* forward declarations */ +struct mausb_urb; +struct mausb_pkt; +struct mausb_host_ep; + +/** + * mausb_mgmt - MAUSB management datastrucure + * + * Contains all the data necessary to handle management packets. + * + * @req_list: List of all currently pending management requests for + * this device. + * @token: The value of the token field in the most recently sent + * management request. + * @tx_pair: Indicates where management packets should be sent. + * + * The following variables are to store common management packet fields: + * + * @hub_dev_handle: Device Handle of the MAUSB Dock hub. used for + * USBDevHandleReq packets. + * @ma_dev_lock: The spinlock which protects the management data in this + * struct. + * @req_switch: The host or devices request switch. It accepts an + * incoming request, makes the necessary calls, and + * returns a partially completed response (it fills the + * response type, status & any data fields specific to + * that response). the remainder of the fields are + * completed by mausb_rx_mgmt_req. + */ +struct mausb_mgmt { + struct list_head req_list; + __u16 token:10; + struct mausb_transfer_pair tx_pair; + __u16 hub_dev_handle; + spinlock_t *ma_dev_lock; + int (*req_switch)(struct mausb_mgmt_pkt *req, + struct mausb_mgmt_pkt *resp, struct mausb_mgmt *mgmt); +}; + +/** + * mausb_mgmt_req_list - MAUSB management request list entry + * + * An entry in the mausb_mgmt request linked list. + * + * @req_list: Pointers to other entries in the list. + * @mgmt_req: The request management packet. + * @mgmt_resp: The response management packet, set to NULL until a response + * is received. + * @resp_rcvd: An event triggered when the response is received. + */ +struct mausb_mgmt_req_list { + struct list_head req_list; + + struct mausb_pkt *mgmt_req; + struct mausb_pkt *mgmt_resp; + + struct completion resp_rcvd; +}; + +/** + * Media Agnostic Session State + * + * Defines the current state of the session between the host & device. + * Per MAUSB spec, sections 7.1 & 8.1.1. + */ +enum mausb_session_state { + MAUSB_SESSION_DOWN, + MAUSB_SESSION_CONNECTING, + MAUSB_SESSION_ACITVE, + MAUSB_SESSION_INACTIVE +}; + +/** + * stores information returned in a CapResp packet + */ +struct ma_capability { + __u16 num_ep; + __u8 num_dev; + __u8 num_stream:5; + __u8 dev_type:3; + __u16 max_tx_reqs; + __u16 max_mgmt_reqs:12; +}; + +/** + * MA device data + * + * This structure is used to hold MA device-specific data + * (i.e. one instance of this structure exists per MA device). + * + * @mgmt: Contains the data needed to keep track of the device's + * management packets. + * @dev_list: Stores a list of the devices connected to this HCD. + * This includes any internal mausb data that exists on + * a per-device, per-endpoint, or per-transfer basis. + * @top_dev: The most upstream mausb device. Used to determine + * where to send MA USB device management packets (such as + * MAUSBDevSleepReq and MAUSBDevWakeReq). + * @session_state: Defines the current state of the session between the + * host and device. Per MAUSB spec, sections 7.1 & 8.1.1. + * @ma_dev_addr: MA Device Address. Assigned by the host via a + * MAUSBDevResetReq. + * @mass_id: Media-Agnostic Service Set Identifier. Assigned by the + * host via a MAUSBDevResetReq. + * @ma_dev_lock: Spinlock for this device. This lock should be held + * before accessing any data in this structure. + * @ma_drv: Media agnostic driver interfaces. + * @ms_drv: Media specific driver interfaces. + * @ma_cap: Stores MA device info from CapResp packet fields. + * @speed: Stores MA device info from speed capability descriptor. + * @pout: Stores MA device info from P-managed OUT capability + * descriptor. + * @iso: Stores MA device info from isochronous capabilities + * descriptor. + * @synch: Stores MA device info from synchronization capabilities + * descriptor. + * @cont_id: Stores MA device info from container ID capability + * descriptor. + * @link_sleep: Stores MA device info from link sleep capability + * descriptor. + */ +struct ma_dev { + struct mausb_mgmt mgmt; + struct list_head dev_list; + struct mausb_dev *top_dev; + enum mausb_session_state session_state; + __u8 ma_dev_addr:7; + __u8 mass_id; + spinlock_t ma_dev_lock; + + union { + struct mausb_hcd *mhcd; + struct mausb_udc *maudc; + }; + + struct mausb_ma_drv ma_drv; + struct mausb_ms_drv *ms_driver; + + struct ma_capability ma_cap; + struct mausb_dev_cap_speed *speed; + struct mausb_dev_cap_pout *pout; + struct mausb_dev_cap_iso *iso; + struct mausb_dev_cap_sync *sync; + struct mausb_dev_cap_cont_id *cont_id; + struct mausb_dev_cap_link_sleep *link_sleep; +}; + +/** + * MA USB device data + * + * This structure is used to hold device-pecific data + * (i.e. one instance of this structure exists per usb device). + * + * @dev: A pointer to the kernel's usb device structure. Can be + * used to uniquely identify a device. + * @gadget: A pointer to the kernel's usb gadget structure. + * @ma_dev: Pointer to parent ma_dev structure. + * @ep_list: A linked list of all the endpoints for this device. + * MAUSBDevHandleResp packet. + * @dev_handle: MA USB device handle for this device + * @dev_address: The usb device address. set by the ma_dev. + * NOTE: this is not the same as the address given by + * the kernel. + * @dev_list: A linked list of all the devices in the HCD. + * @dev_lock: Spinlock for this device. This lock should be held + * before accessing any data in this structure. + */ +struct mausb_dev { + union { + struct usb_device *dev; /* Host only */ + struct usb_gadget *gadget; /* Device only */ + }; + struct ma_dev *ma_dev; + struct list_head ep_list; + __u16 dev_handle; + __u8 dev_address; + struct list_head dev_list; + spinlock_t dev_lock; +}; + +/** + * MAUSB Endpoint Handle State Variables + * + * Defines the states for an MA USB Endpoint Handle. + * + * Per MAUSB spec, seciton 7.2 + * + * Note: MAUSB_EP_HANDLE_DELETED is used to flag an endpoint for deletion + * it is not per spec; endpoints in this state should not be used and + * will be deleted in the next EPHandleDeleteReq. + */ +enum mausb_ep_handle_state { + MAUSB_EP_HANDLE_UNASSIGNED = 0, + MAUSB_EP_HANDLE_ACTIVE, + MAUSB_EP_HANDLE_INACTIVE, + MAUSB_EP_HANDLE_HALTED, + MAUSB_EP_HANDLE_DELETED +}; + +/** + * MA USB host-side endpoint data + * + * This structure is used to hold endpoint-specific data + * (i.e. one instance of this structure exists per endpoint). + * + * The following data is protected by the devices's dev_lock: + * + * @ep: Used only by the host side. A pointer to the kernel's host-side + * endpoint structure. Can be used to uniquely identify an + * endpoint. Note: the HCD probably shouldn't read or write + * anything from the usb_host_endpoint structure, since it belongs + * to the kernel. + * @dev_ep: Used only by the device side. A pointer to the kernel's + * device-side endpoint structure. + * @ep_list: A linked list of all the endpoints connected to the same + * device. + * + * The following data is protected by the endpoint's ep_lock: + * + * @state: The MA USB state variables for this endpoint. Per + * MA USB Spec, sections 5.4.1 and 5.5.2. + * @active_transfer: Urb that maps to the transfer in progress. + * @ep_handle: The endpoint handle to be used by packets for this + * endpoint per MA USB Spec, section 6.2.1.5. + * @ep_handle_state: Defines the states an Endpoint Handle can be in. + * Per MA USB Spec, section 7.2. + * @buffer: Buffer that holds data going to/from a host during + * transfer. + * @actual_length: Length of buffer. + * @lman_support: For control or non-iso OUT endpoints. Indicates if + * link-managed tranfers are supported. Not valid until + * an EPHandleResp is received for this endpoint. + * @control_dir: Control endpoints only; used to determine the direction + * of the current transfer. + * @ccu: The credit conumption unit, in bytes. Per MA USB Spec, + * table 27. + * @mausb_dev: The device the endpoint belongs to. + * @urb_list: A linked list of all URBs pending for this endpoint. + * URBs map one-to-one with MAUSB Transfers, so each entry + * also represents an MA USB Transfer. + * @usb_req_list: Only used on the device side. A linked list of all + * usb_requests submitted to this endpoint. + * @req_list: Head of list of created transferRequests and + * transferAck packets. + * @resp_list: Head of list of received transferResponses packets. + * @ep_lock: Spinlock for this endpoint. This lock should be held + * before accessing any data in this structure. + * @timer: Endpoint timer structure, used to monitor transfer + * timeouts. + * @timeout_task: Thread used to perform packet resends after transfer + * timeout. + * @host_ep_wq: Work queue for timeout thread to wait on while sleeping. + * @tx_timed_out: Used to flag when a timeout event has occured during + * a transfer. + * @tx_pair: Transfer pair for this endpoint. + * @gadget: USB gadget associated with this endpoint. + * @busy: True if this endpoint is busy, otherwise false. + * @halted: 1 if endpoint is halted, otherwise 0. + * @wedged: 1 if endpoint is wedged, otherwise 0. + * @stream_en: 1 if streams are enabled for this endpoint, otherwise 0. + * @max_pkt: Max packet size for this endpoint. + */ +struct mausb_host_ep { + + struct usb_host_endpoint *ep; /* Host only */ + struct usb_ep *dev_ep; /* Device only */ + struct list_head ep_list; + struct mausb_ep_state state; + struct mausb_urb *active_transfer; + struct mausb_ep_handle ep_handle; + enum mausb_ep_handle_state ep_handle_state; + void *buffer; + u32 actual_length; + bool lman_support; + bool control_dir; + u16 ccu; + struct mausb_dev *mausb_dev; + struct list_head urb_list; + struct list_head *usb_req_list; + struct list_head req_list; + struct list_head resp_list; + spinlock_t ep_lock; + struct timer_list timer; + struct task_struct *timeout_task; + wait_queue_head_t host_ep_wq; + bool tx_timed_out; + struct mausb_transfer_pair tx_pair; + struct usb_gadget *gadget; /* Device only */ + bool busy; + unsigned halted:1; + unsigned wedged:1; + unsigned stream_en:1; + u16 max_pkt; +}; + +/** + * endpoint request structure - describes one I/O request + * + * @usb_ep: A device-side endpoint structure. + * @queue: The list of all requests currently enqueued to this endpoint. + */ +struct mausb_gadget_ep { + struct usb_ep dev_ep; + struct list_head usb_req_list; +}; + +/** + * MA USB urb structure + * + * Stores MAUSB-specific urb data. In our implementation, one urb from usb + * core maps to exactly one MA USB transfer. When an urb is submitted, it + * queues a new transfer. When a transfer is complete (or fails), the urb + * is given back to the core. + * + * All data listed in this structure is protected by the ep_lock. + * If you want to use any of this data, you need to be in posession + * of this lock. + * + * @urb: Used only on the host side. The urb submitted to the + * HCD by usbcore. + * @usb_request_list: Used only on the device side. A pointer to the list of + * msusb_requests for this endpoint. + * @urb_list: Connects this mausb_urb with other mausb_urbs + * queued to the same endpoint. + * @ma_hcd_urb_list: Connects this urb to urbs queued to the same ma hcd. + * @state: Current state of the transfer as defined in the + * MA USB Spec. + * @ep: The endpoint that the transfer belongs to. + * @dev: The device that the transfer belongs to. + * @transfer_size: Total number of bytes for transfer associated with urb. + */ +struct mausb_urb { + struct urb *urb; /* Host Only */ + struct list_head *usb_request_list; /* Device Only */ + struct list_head urb_list; + struct list_head ma_hcd_urb_list; + struct mausb_transfer_state state; + struct mausb_host_ep *ep; + struct mausb_dev *dev; + unsigned int transfer_size; +}; + +/** + * endpoint request structure - describes one I/O request + * + * @req: The usb_request struct. Enqueuing a usb_request is + * what triggers the creation of this struct. + * @usb_req_list: List of all requests enqueued at this endpoint. + */ +struct mausb_request { + struct usb_request req; + struct list_head usb_req_list; +}; + +/* endpoint/device helper functions */ +struct mausb_host_ep *usb_to_ma_endpoint(struct usb_host_endpoint *ep); +int mausb_hcd_urb_count(struct mausb_hcd *mhcd); + +#define MAUSB_ADD true +#define MAUSB_DEL false +#define MAUSB_DEV false +#define MAUSB_HOST true +#define MAUSB_OUT false +#define MAUSB_IN true +struct mausb_pkt *mausb_create_dp(int *status, struct mausb_urb *maurb, + bool in, bool host, gfp_t memflags); + +/* endpoint/device memory management functions */ +struct mausb_host_ep *mausb_state_to_ep(struct mausb_ep_state *state); +struct ma_dev *context_to_ma_dev(void *context); +struct mausb_hcd *mausb_host_ep_to_mahcd(struct mausb_host_ep *ep); +struct mausb_udc *mausb_host_ep_to_maudc(struct mausb_host_ep *ep); +struct mausb_dev *usb_dev_to_mausb_dev(struct usb_device *udev); +struct mausb_host_ep *mausb_find_ep_dev(struct usb_ep *dev_ep, + struct mausb_dev *mausb_dev); +struct mausb_host_ep *mausb_find_ep_host(struct usb_host_endpoint *ep, + struct mausb_dev *mausb_dev); +struct mausb_host_ep *mausb_find_ep_by_handle(struct mausb_ep_handle *handle, + struct mausb_dev *mausb_dev); +struct mausb_host_ep *mausb_find_ep_from_handle(struct mausb_ep_handle handle, + struct ma_dev *ma_dev); +struct mausb_pkt *mausb_pkt_from_ms_pkt_ma_dev(struct ms_pkt *ms_pkt, + struct ma_dev *ma_dev, gfp_t mem_flags); +struct mausb_pkt *mausb_pkt_from_ms_pkt_ep(struct ms_pkt *ms_pkt, + struct mausb_host_ep *ep, gfp_t mem_flags); +struct mausb_dev *mausb_find_dev(struct ma_dev *ma_dev, struct usb_device *dev, + unsigned long *handle, struct usb_gadget *gadget); +struct mausb_dev *mausb_find_dev_host(struct ma_dev *ma_dev, + struct usb_device *dev); +struct mausb_host_ep *mausb_find_ep_by_desc( + const struct usb_endpoint_descriptor *ep_desc, + struct mausb_dev *mausb_dev); +struct mausb_host_ep *mausb_find_ep_by_address(u8 address, + struct mausb_dev *mausb_dev); +struct mausb_urb *mausb_alloc_maurb(struct mausb_host_ep *ma_ep, + gfp_t memflags); +int mausb_internal_drop_maurb(struct mausb_urb *maurb, struct mausb_hcd *mhcd); +void init_ep_state(struct mausb_ep_state *state, u32 buf_size); +int mausb_activate_endpoints(struct mausb_dev *mausb_dev); +void mausb_link_ma_ep_to_usb_ep(struct mausb_host_ep *ma_ep, + struct usb_ep *dev_ep); +struct mausb_gadget_ep *usb_ep_to_mausb_gadget_ep(struct usb_ep *ep); +int mausb_internal_add_ep(struct mausb_dev *ma_dev, + struct usb_host_endpoint *ep, struct usb_ep *dev_ep, + struct mausb_host_ep **mausb_ep, + int (*transfer_timeout)(void *data), gfp_t memflags); +int mausb_internal_drop_ep(struct mausb_host_ep *ma_ep); +int mausb_update_ep(struct mausb_host_ep *ma_ep, + struct mausb_ep_des *ma_ep_des); +struct mausb_dev *mausb_internal_alloc_dev(struct ma_dev *ma_dev, + struct usb_device *dev, struct usb_gadget *gadget); +int mausb_internal_free_dev(struct ma_dev *ma_dev, + struct mausb_dev *mausb_dev); +int mausb_internal_reset_ma_dev(struct ma_dev *ma_dev, __u8 ma_dev_addr, + __u8 mass_id, struct mausb_hcd *mhcd, struct mausb_udc *maudc); +int mausb_init_ma_device(struct ma_dev *ma_dev, __u8 ma_dev_addr, __u8 mass_id, + struct mausb_hcd *mhcd, struct mausb_udc *maudc, + int (*req_switch)(struct mausb_mgmt_pkt *req, + struct mausb_mgmt_pkt *resp, struct mausb_mgmt *mgmt), + int (*device_connect)(int)); +int mausb_init_mgmt(struct mausb_mgmt *mgmt, spinlock_t *ma_dev_lock, + int (*req_switch)(struct mausb_mgmt_pkt *req, + struct mausb_mgmt_pkt *resp, struct mausb_mgmt *mgmt)); +const struct usb_endpoint_descriptor *mausb_get_ep_des( + struct mausb_host_ep *ma_ep); + +#endif diff --git a/drivers/staging/mausb/drivers/mausb_state.h b/drivers/staging/mausb/drivers/mausb_state.h new file mode 100644 index 0000000..01f0171 --- /dev/null +++ b/drivers/staging/mausb/drivers/mausb_state.h @@ -0,0 +1,184 @@ +/* Name: mausb_state.h + * Description: Contains all of the state variables for hosts and devices as + * defined by the MAUSB spec. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License 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. + * + * Contact Information: + * Sean Stalley, sean.stalley@xxxxxxxxx + * Stephanie Wallick, stephanie.s.wallick@xxxxxxxxx + * 2111 NE 25th Avenue + * Hillsboro, Oregon 97124 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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 __MAUSB_STATE_H +#define __MAUSB_STATE_H + +/** + * MAUSB endpoint state variables, per MA USB Spec sections 5.4.1 & 5.5.2. + * + * Note: this struct contains the fields necessary to store the state on both + * the host and device side. + * + * @seq_number: For a sender, this field contains the sequence number + * that should be placed in the next outgoing packet. + * For a receiver, this field contains the sequence number + * expected in the next incoming packet. + * @keep_alive_timer: Timeout counter. Set to aMausbTransferKeepAlive + * upon a new transfer initialization. To be decremented + * every aMAUSBTransferTimeTick interval. If the timer + * reaches 0, A retry is triggered. + * @retry_counter: Indicates how many more times the + * transfer will be attempted. Decremented upon a timeout + * (see keep_alive_timer for details). If this counter + * reaches 0, the transfer fails due to a timeout. + * @request_id: Request ID number. for IN transfers, all + * transfer_request packets currently being sent or + * received should have this request ID number. for OUT + * transfers, All transfer_request and transfer_ack + * packets currently being sent or received should have + * this request ID number. + * @active_request_id: Active Request ID number. used by IN transfers only. + * All transfer_ack packets being sent/received should + * contain this number. + * @earliest_unacked: Stores the sequence number of the most recent packet + * that has been sent out but has not been acknowledged + * by the receiver. + * @rx_buf_size: Receiver Buffer Size. Used for OUT transfers only. + * Indicates the available buffer space for packets in the + * device (in bytes). + * Note: this value is signed for the host and + * unsigned for the receiver. A negative number + * indicates a buffer overflow. + * Note: defined in spec as a 25-bit signed int + * for a host and a 32-bit unsigned for a device. + * @occupancy: Size (in bytes) of the payload accepted into the receive + * buffer. Used only on the device side for OUT transfers. + * @earliest_request_id:Request ID of earliest transferReq whose state + * needs to be tracked (host IN). + * @transfer_retries: Reload value for retry_counter. Should be set to + * a ControlTransferRetries, aBulkTransferRetries, + * or aInterruptTransferRetries depending on transfer + * type. + * @delayed: Set to true if the interval between two sucessive + * transferResp packets exceeds aTransferRepeatTime. + * @tx_pending: Set to 1 when a transfer is in progress, otherwise it + * should be zero. A new transfer cannot be started until + * this flag is cleared. + */ +struct mausb_ep_state { /* HOST | DEVICE */ + + unsigned int seq_number:24; /* IN | OUT | IN | OUT */ + int keep_alive_timer; /* IN | OUT | IN | OUT */ + unsigned int retry_counter; /* IN | OUT | IN | OUT */ + __u8 request_id; /* IN | OUT | IN | OUT */ + __u8 active_request_id; /* IN | OUT | IN | */ + unsigned int earliest_unacked:24; /* | OUT | IN | OUT */ + __u32 rx_buf_size; /* | OUT | | OUT */ + unsigned int occupancy; /* | | | OUT */ + u8 earliest_request_id; /* IN | OUT | IN | OUT */ + unsigned int transfer_retries; /* IN | OUT | IN | OUT */ + bool delayed; /* | | IN | */ + + /* TODO: add response_timer (device IN and OUT/host OUT) */ + + unsigned int tx_pending:1; /* beyond spec */ +}; + +/** + * MA USB transfer state variables, per section 5.4.1 & 5.5.2. + * + * In our implementation, URBs and MAUSB transfers map one-to-one, + * so the transfer state and the urb state are one in the same. + * + * @rem_size: The remaining transfer size (in bytes). This value is + * set at the beginning of a transfer and is decremented + * as data is received. + * @payload_size: Payload size for out transfer (in bytes). For MA USB + * host, it indicates the overall transfered OUT payload + * size for the current urb. For MA USB device, It + * indicates the received OUT payload size for the + * current MA USB OUT request. + * @transfer_error: A boolean which is only set to TRUE when a + * non-recoverable error is detected in the transfer. + * @transfer_complete: A boolean which is only set to TRUE when a transfer + * is evaluated to be complete. (ie. the final ACK has + * has been sent/received). + * @eot_detected: A boolean which is only set to TRUE when a transfer + * is complete (ie: all the data has been transferred). + * Note: eot_detected & transfer_complete differ in when + * they are set. eot_detected is sent after the last data + * packet received, transfer_complete is set after the + * final ACK. + * @last_transfer_sn: Last Transfer Sequence Number. Stores the sequence + * number of the final packet of this transfer. + * @transfer_acked: Set to true when a transferReq or transferAck packet is + * released to the data channel acknowledging the last + * transferResp belonging to a transfer. + * @k: A transfer-dependent multiplicative factor used to + * reload keep_alive_timer when a transfer has a status + * of TRANSFER_PENDING. + * @ack_transfer_sn: The value of the sequence number in the last + * transferReq packet that belongs to the transfer and + * has the ARQ field set to 1. + */ +struct mausb_transfer_state { /* HOST | DEVICE */ + + __u32 rem_size; /* IN | OUT | IN | OUT */ + __u32 payload_size; /* IN | OUT | IN | OUT */ + bool transfer_error; /* IN | OUT | IN | OUT */ + bool transfer_complete; /* IN | OUT | IN | OUT */ + bool eot_detected; /* IN | | IN | OUT */ + unsigned int last_transfer_sn:24; /* IN | | IN | */ + bool transfer_acked; /* IN | OUT | | */ + unsigned int k; /* IN | | | */ + unsigned int ack_transfer_sn:24; /* | OUT | | */ + + /*TODO: add transfer_completion_timer (host IN) */ +}; + +#endif -- 1.9.1 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel