diff options
Diffstat (limited to 'drivers/staging/westbridge/astoria/gadget')
5 files changed, 2488 insertions, 0 deletions
diff --git a/drivers/staging/westbridge/astoria/gadget/Kconfig b/drivers/staging/westbridge/astoria/gadget/Kconfig new file mode 100644 index 0000000..6fbdf22 --- /dev/null +++ b/drivers/staging/westbridge/astoria/gadget/Kconfig @@ -0,0 +1,9 @@ +# +# West Bridge gadget driver configuration +# + +config WESTBRIDGE_GADGET_DRIVER + tristate "West Bridge Gadget Driver" + help + Include the West Bridge based gadget peripheral controller driver + diff --git a/drivers/staging/westbridge/astoria/gadget/Makefile b/drivers/staging/westbridge/astoria/gadget/Makefile new file mode 100644 index 0000000..a5eef7e --- /dev/null +++ b/drivers/staging/westbridge/astoria/gadget/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the kernel westbridge hal +# + +ifneq ($(CONFIG_WESTBRIDGE_DEBUG),y) + EXTRA_CFLAGS += -DWESTBRIDGE_NDEBUG +endif + +obj-$(CONFIG_WESTBRIDGE_GADGET_DRIVER) += cyasgadgetctrl.o +cyasgadgetctrl-y := cyasgadget.o + diff --git a/drivers/staging/westbridge/astoria/gadget/cyasgadget.c b/drivers/staging/westbridge/astoria/gadget/cyasgadget.c new file mode 100644 index 0000000..defa05c --- /dev/null +++ b/drivers/staging/westbridge/astoria/gadget/cyasgadget.c @@ -0,0 +1,2176 @@ +/* cyangadget.c - Linux USB Gadget driver file for the Cypress West Bridge +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## 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. +## =========================== +*/ + +/* + * Cypress West Bridge high/full speed usb device controller code + * Based on the Netchip 2280 device controller by David Brownell + * in the linux 2.6.10 kernel + * + * linux/drivers/usb/gadget/net2280.c + */ + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330 + * Boston, MA 02111-1307 USA + */ + +#include "cyasgadget.h" + +#define CY_AS_DRIVER_DESC "cypress west bridge usb gadget" +#define CY_AS_DRIVER_VERSION "REV B" +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +static const char cy_as_driver_name[] = "cy_astoria_gadget"; +static const char cy_as_driver_desc[] = CY_AS_DRIVER_DESC; + +static const char cy_as_ep0name[] = "EP0"; +static const char *cy_as_ep_names[] = { + cy_as_ep0name, "EP1", + "EP2", "EP3", "EP4", "EP5", "EP6", "EP7", "EP8", + "EP9", "EP10", "EP11", "EP12", "EP13", "EP14", "EP15" +}; + +/* forward declarations */ +static void +cyas_ep_reset( + struct cyasgadget_ep *an_ep); + +static int +cyasgadget_fifo_status( + struct usb_ep *_ep); + +static void +cyasgadget_stallcallback( + cy_as_device_handle h, + cy_as_return_status_t status, + uint32_t tag, + cy_as_funct_c_b_type cbtype, + void *cbdata); + +/* variables */ +static cyasgadget *cy_as_gadget_controller; + +static int append_mtp; +module_param(append_mtp, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(append_mtp, + "west bridge to append descriptors for mtp 0=no 1=yes"); + +static int msc_enum_bus_0; +module_param(msc_enum_bus_0, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(msc_enum_bus_0, + "west bridge to enumerate bus 0 as msc 0=no 1=yes"); + +static int msc_enum_bus_1; +module_param(msc_enum_bus_1, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(msc_enum_bus_1, + "west bridge to enumerate bus 1 as msc 0=no 1=yes"); + +/* all Callbacks are placed in this subsection*/ +static void cy_as_gadget_usb_event_callback( + cy_as_device_handle h, + cy_as_usb_event ev, + void *evdata + ) +{ + cyasgadget *cy_as_dev; + #ifndef WESTBRIDGE_NDEBUG + struct usb_ctrlrequest *ctrlreq; + #endif + + /* cy_as_dev = container_of(h, cyasgadget, dev_handle); */ + cy_as_dev = cy_as_gadget_controller; + switch (ev) { + case cy_as_event_usb_suspend: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_suspend received\n"); + #endif + cy_as_dev->driver->suspend(&cy_as_dev->gadget); + break; + + case cy_as_event_usb_resume: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_resume received\n"); + #endif + cy_as_dev->driver->resume(&cy_as_dev->gadget); + break; + + case cy_as_event_usb_reset: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_reset received\n"); + #endif + break; + + case cy_as_event_usb_speed_change: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_speed_change received\n"); + #endif + break; + + case cy_as_event_usb_set_config: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_set_config received\n"); + #endif + break; + + case cy_as_event_usb_setup_packet: + #ifndef WESTBRIDGE_NDEBUG + ctrlreq = (struct usb_ctrlrequest *)evdata; + + cy_as_hal_print_message("<1>_cy_as_event_usb_setup_packet " + "received" + "bRequestType=0x%x," + "bRequest=0x%x," + "wValue=x%x," + "wIndex=0x%x," + "wLength=0x%x,", + ctrlreq->bRequestType, + ctrlreq->bRequest, + ctrlreq->wValue, + ctrlreq->wIndex, + ctrlreq->wLength + ); + #endif + cy_as_dev->outsetupreq = 0; + if ((((uint8_t *)evdata)[0] & USB_DIR_IN) == USB_DIR_OUT) + cy_as_dev->outsetupreq = 1; + cy_as_dev->driver->setup(&cy_as_dev->gadget, + (struct usb_ctrlrequest *)evdata); + break; + + case cy_as_event_usb_status_packet: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_status_packet received\n"); + #endif + break; + + case cy_as_event_usb_inquiry_before: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_inquiry_before received\n"); + #endif + break; + + case cy_as_event_usb_inquiry_after: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_inquiry_after received\n"); + #endif + break; + + case cy_as_event_usb_start_stop: + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<1>_cy_as_event_usb_start_stop received\n"); + #endif + break; + + default: + break; + } +} + +static void cy_as_gadget_mtp_event_callback( + cy_as_device_handle handle, + cy_as_mtp_event evtype, + void *evdata + ) +{ + + cyasgadget *dev = cy_as_gadget_controller; + (void) handle; + + switch (evtype) { + case cy_as_mtp_send_object_complete: + { + cy_as_mtp_send_object_complete_data *send_obj_data = + (cy_as_mtp_send_object_complete_data *) evdata; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<6>MTP EVENT: send_object_complete\n"); + cy_as_hal_print_message( + "<6>_bytes sent = %d\n_send status = %d", + send_obj_data->byte_count, + send_obj_data->status); + #endif + + dev->tmtp_send_complete_data.byte_count = + send_obj_data->byte_count; + dev->tmtp_send_complete_data.status = + send_obj_data->status; + dev->tmtp_send_complete_data.transaction_id = + send_obj_data->transaction_id; + dev->tmtp_send_complete = cy_true; + break; + } + case cy_as_mtp_get_object_complete: + { + cy_as_mtp_get_object_complete_data *get_obj_data = + (cy_as_mtp_get_object_complete_data *) evdata; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<6>MTP EVENT: get_object_complete\n"); + cy_as_hal_print_message( + "<6>_bytes got = %d\n_get status = %d", + get_obj_data->byte_count, get_obj_data->status); + #endif + + dev->tmtp_get_complete_data.byte_count = + get_obj_data->byte_count; + dev->tmtp_get_complete_data.status = + get_obj_data->status; + dev->tmtp_get_complete = cy_true; + break; + } + case cy_as_mtp_block_table_needed: + { + dev->tmtp_need_new_blk_tbl = cy_true; + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<6>MTP EVENT: cy_as_mtp_block_table_needed\n"); + #endif + break; + } + default: + break; + } +} + +static void +cyasgadget_setupreadcallback( + cy_as_device_handle h, + cy_as_end_point_number_t ep, + uint32_t count, + void *buf, + cy_as_return_status_t status) +{ + cyasgadget_ep *an_ep; + cyasgadget_req *an_req; + cyasgadget *cy_as_dev; + unsigned stopped; + unsigned long flags; + (void)buf; + + cy_as_dev = cy_as_gadget_controller; + if (cy_as_dev->driver == NULL) + return; + + an_ep = &cy_as_dev->an_gadget_ep[ep]; + spin_lock_irqsave(&cy_as_dev->lock, flags); + stopped = an_ep->stopped; + +#ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: ep=%d, count=%d, " + "status=%d\n", __func__, ep, count, status); +#endif + + an_req = list_entry(an_ep->queue.next, + cyasgadget_req, queue); + list_del_init(&an_req->queue); + + if (status == CY_AS_ERROR_SUCCESS) + an_req->req.status = 0; + else + an_req->req.status = -status; + an_req->req.actual = count; + an_ep->stopped = 1; + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + an_req->req.complete(&an_ep->usb_ep_inst, &an_req->req); + + an_ep->stopped = stopped; + +} +/*called when the write of a setup packet has been completed*/ +static void cyasgadget_setupwritecallback( + cy_as_device_handle h, + cy_as_end_point_number_t ep, + uint32_t count, + void *buf, + cy_as_return_status_t status + ) +{ + cyasgadget_ep *an_ep; + cyasgadget_req *an_req; + cyasgadget *cy_as_dev; + unsigned stopped; + unsigned long flags; + + (void)buf; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called status=0x%x\n", + __func__, status); + #endif + + cy_as_dev = cy_as_gadget_controller; + + if (cy_as_dev->driver == NULL) + return; + + an_ep = &cy_as_dev->an_gadget_ep[ep]; + + spin_lock_irqsave(&cy_as_dev->lock, flags); + + stopped = an_ep->stopped; + +#ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("setup_write_callback: ep=%d, " + "count=%d, status=%d\n", ep, count, status); +#endif + + an_req = list_entry(an_ep->queue.next, cyasgadget_req, queue); + list_del_init(&an_req->queue); + + an_req->req.actual = count; + an_req->req.status = 0; + an_ep->stopped = 1; + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + an_req->req.complete(&an_ep->usb_ep_inst, &an_req->req); + + an_ep->stopped = stopped; + +} + +/* called when a read operation has completed.*/ +static void cyasgadget_readcallback( + cy_as_device_handle h, + cy_as_end_point_number_t ep, + uint32_t count, + void *buf, + cy_as_return_status_t status + ) +{ + cyasgadget_ep *an_ep; + cyasgadget_req *an_req; + cyasgadget *cy_as_dev; + unsigned stopped; + cy_as_return_status_t ret; + unsigned long flags; + + (void)h; + (void)buf; + + cy_as_dev = cy_as_gadget_controller; + + if (cy_as_dev->driver == NULL) + return; + + an_ep = &cy_as_dev->an_gadget_ep[ep]; + stopped = an_ep->stopped; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: ep=%d, count=%d, status=%d\n", + __func__, ep, count, status); + #endif + + if (status == CY_AS_ERROR_CANCELED) + return; + + spin_lock_irqsave(&cy_as_dev->lock, flags); + + an_req = list_entry(an_ep->queue.next, cyasgadget_req, queue); + list_del_init(&an_req->queue); + + if (status == CY_AS_ERROR_SUCCESS) + an_req->req.status = 0; + else + an_req->req.status = -status; + + an_req->complete = 1; + an_req->req.actual = count; + an_ep->stopped = 1; + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + an_req->req.complete(&an_ep->usb_ep_inst, &an_req->req); + + an_ep->stopped = stopped; + + /* We need to call ReadAsync on this end-point + * again, so as to not miss any data packets. */ + if (!an_ep->stopped) { + spin_lock_irqsave(&cy_as_dev->lock, flags); + an_req = 0; + if (!list_empty(&an_ep->queue)) + an_req = list_entry(an_ep->queue.next, + cyasgadget_req, queue); + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + if ((an_req) && (an_req->req.status == -EINPROGRESS)) { + ret = cy_as_usb_read_data_async(cy_as_dev->dev_handle, + an_ep->num, cy_false, an_req->req.length, + an_req->req.buf, cyasgadget_readcallback); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_read_data_async failed " + "with error code %d\n", ret); + else + an_req->req.status = -EALREADY; + } + } +} + +/* function is called when a usb write operation has completed*/ +static void cyasgadget_writecallback( + cy_as_device_handle h, + cy_as_end_point_number_t ep, + uint32_t count, + void *buf, + cy_as_return_status_t status + ) +{ + cyasgadget_ep *an_ep; + cyasgadget_req *an_req; + cyasgadget *cy_as_dev; + unsigned stopped = 0; + cy_as_return_status_t ret; + unsigned long flags; + + (void)h; + (void)buf; + + cy_as_dev = cy_as_gadget_controller; + if (cy_as_dev->driver == NULL) + return; + + an_ep = &cy_as_dev->an_gadget_ep[ep]; + + if (status == CY_AS_ERROR_CANCELED) + return; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: ep=%d, count=%d, status=%d\n", + __func__, ep, count, status); + #endif + + spin_lock_irqsave(&cy_as_dev->lock, flags); + + an_req = list_entry(an_ep->queue.next, cyasgadget_req, queue); + list_del_init(&an_req->queue); + an_req->req.actual = count; + + /* Verify the status value before setting req.status to zero */ + if (status == CY_AS_ERROR_SUCCESS) + an_req->req.status = 0; + else + an_req->req.status = -status; + + an_ep->stopped = 1; + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + an_req->req.complete(&an_ep->usb_ep_inst, &an_req->req); + an_ep->stopped = stopped; + + /* We need to call WriteAsync on this end-point again, so as to not + miss any data packets. */ + if (!an_ep->stopped) { + spin_lock_irqsave(&cy_as_dev->lock, flags); + an_req = 0; + if (!list_empty(&an_ep->queue)) + an_req = list_entry(an_ep->queue.next, + cyasgadget_req, queue); + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + if ((an_req) && (an_req->req.status == -EINPROGRESS)) { + ret = cy_as_usb_write_data_async(cy_as_dev->dev_handle, + an_ep->num, an_req->req.length, an_req->req.buf, + cy_false, cyasgadget_writecallback); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_write_data_async " + "failed with error code %d\n", ret); + else + an_req->req.status = -EALREADY; + } + } +} + +static void cyasgadget_stallcallback( + cy_as_device_handle h, + cy_as_return_status_t status, + uint32_t tag, + cy_as_funct_c_b_type cbtype, + void *cbdata + ) +{ + #ifndef WESTBRIDGE_NDEBUG + if (status != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_set/_clear stall " + "failed with status %d\n", status); + #endif +} + + +/*******************************************************************/ +/* All usb_ep_ops (cyasgadget_ep_ops) are placed in this subsection*/ +/*******************************************************************/ +static int cyasgadget_enable( + struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc + ) +{ + cyasgadget *an_dev; + cyasgadget_ep *an_ep; + u32 max, tmp; + unsigned long flags; + + an_ep = container_of(_ep, cyasgadget_ep, usb_ep_inst); + if (!_ep || !desc || an_ep->desc || _ep->name == cy_as_ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + an_dev = an_ep->dev; + if (!an_dev->driver || an_dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + + spin_lock_irqsave(&an_dev->lock, flags); + _ep->maxpacket = max & 0x7ff; + an_ep->desc = desc; + + /* ep_reset() has already been called */ + an_ep->stopped = 0; + an_ep->out_overflow = 0; + + if (an_ep->cyepconfig.enabled != cy_true) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_end_point_config EP %s mismatch " + "on enabled\n", an_ep->usb_ep_inst.name); + #endif + return -EINVAL; + } + + tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + an_ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; + + spin_unlock_irqrestore(&an_dev->lock, flags); + + switch (tmp) { + case USB_ENDPOINT_XFER_ISOC: + if (an_ep->cyepconfig.type != cy_as_usb_iso) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_end_point_config EP %s mismatch " + "on type %d %d\n", an_ep->usb_ep_inst.name, + an_ep->cyepconfig.type, cy_as_usb_iso); + #endif + return -EINVAL; + } + break; + case USB_ENDPOINT_XFER_INT: + if (an_ep->cyepconfig.type != cy_as_usb_int) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_end_point_config EP %s mismatch " + "on type %d %d\n", an_ep->usb_ep_inst.name, + an_ep->cyepconfig.type, cy_as_usb_int); + #endif + return -EINVAL; + } + break; + default: + if (an_ep->cyepconfig.type != cy_as_usb_bulk) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_end_point_config EP %s mismatch " + "on type %d %d\n", an_ep->usb_ep_inst.name, + an_ep->cyepconfig.type, cy_as_usb_bulk); + #endif + return -EINVAL; + } + break; + } + + tmp = desc->bEndpointAddress; + an_ep->is_in = (tmp & USB_DIR_IN) != 0; + + if ((an_ep->cyepconfig.dir == cy_as_usb_in) && + (!an_ep->is_in)) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_end_point_config EP %s mismatch " + "on dir %d %d\n", an_ep->usb_ep_inst.name, + an_ep->cyepconfig.dir, cy_as_usb_in); + #endif + return -EINVAL; + } else if ((an_ep->cyepconfig.dir == cy_as_usb_out) && + (an_ep->is_in)) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_end_point_config EP %s mismatch " + "on dir %d %d\n", an_ep->usb_ep_inst.name, + an_ep->cyepconfig.dir, cy_as_usb_out); + #endif + return -EINVAL; + } + + cy_as_usb_clear_stall(an_dev->dev_handle, an_ep->num, + cyasgadget_stallcallback, 0); + + cy_as_hal_print_message("%s enabled %s (ep%d-%d) max %04x\n", + __func__, _ep->name, an_ep->num, tmp, max); + + return 0; +} + +static int cyasgadget_disable( + struct usb_ep *_ep + ) +{ + cyasgadget_ep *an_ep; + unsigned long flags; + + an_ep = container_of(_ep, cyasgadget_ep, usb_ep_inst); + if (!_ep || !an_ep->desc || _ep->name == cy_as_ep0name) + return -EINVAL; + + spin_lock_irqsave(&an_ep->dev->lock, flags); + cyas_ep_reset(an_ep); + + spin_unlock_irqrestore(&an_ep->dev->lock, flags); + return 0; +} + +static struct usb_request *cyasgadget_alloc_request( + struct usb_ep *_ep, gfp_t gfp_flags + ) +{ + cyasgadget_ep *an_ep; + cyasgadget_req *an_req; + + if (!_ep) + return NULL; + + an_ep = container_of(_ep, cyasgadget_ep, usb_ep_inst); + + an_req = kzalloc(sizeof(cyasgadget_req), gfp_flags); + if (!an_req) + return NULL; + + an_req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&an_req->queue); + + return &an_req->req; +} + +static void cyasgadget_free_request( + struct usb_ep *_ep, + struct usb_request *_req + ) +{ + cyasgadget_req *an_req; + + if (!_ep || !_req) + return; + + an_req = container_of(_req, cyasgadget_req, req); + + kfree(an_req); +} + +/* Load a packet into the fifo we use for usb IN transfers. + * works for all endpoints. */ +static int cyasgadget_queue( + struct usb_ep *_ep, + struct usb_request *_req, + gfp_t gfp_flags + ) +{ + cyasgadget_req *as_req; + cyasgadget_ep *as_ep; + cyasgadget *cy_as_dev; + unsigned long flags; + cy_as_return_status_t ret = 0; + + as_req = container_of(_req, cyasgadget_req, req); + if (!_req || !_req->complete || !_req->buf + || !list_empty(&as_req->queue)) + return -EINVAL; + + as_ep = container_of(_ep, cyasgadget_ep, usb_ep_inst); + + if (!_ep || (!as_ep->desc && (as_ep->num != 0))) + return -EINVAL; + + cy_as_dev = as_ep->dev; + if (!cy_as_dev->driver || + cy_as_dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&cy_as_dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + /* Call Async functions */ + if (as_ep->is_in) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_write_data_async being called " + "on ep %d\n", as_ep->num); + #endif + + ret = cy_as_usb_write_data_async(cy_as_dev->dev_handle, + as_ep->num, _req->length, _req->buf, + cy_false, cyasgadget_writecallback); + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_write_data_async failed with " + "error code %d\n", ret); + else + _req->status = -EALREADY; + } else if (as_ep->num == 0) { + /* + ret = cy_as_usb_write_data_async(cy_as_dev->dev_handle, + as_ep->num, _req->length, _req->buf, cy_false, + cyasgadget_setupwritecallback); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_write_data_async failed with error " + "code %d\n", ret); + */ + if ((cy_as_dev->outsetupreq) && (_req->length)) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_read_data_async " + "being called on ep %d\n", + as_ep->num); + #endif + + ret = cy_as_usb_read_data_async ( + cy_as_dev->dev_handle, as_ep->num, + cy_true, _req->length, _req->buf, + cyasgadget_setupreadcallback); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_read_data_async failed with " + "error code %d\n", ret); + + } else { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_write_data_async " + "being called on ep %d\n", + as_ep->num); + #endif + + ret = cy_as_usb_write_data_async(cy_as_dev->dev_handle, + as_ep->num, _req->length, _req->buf, cy_false, + cyasgadget_setupwritecallback); + + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_write_data_async failed with " + "error code %d\n", ret); + } + + } else if (list_empty(&as_ep->queue)) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_read_data_async being called since " + "ep queue empty%d\n", ret); + #endif + + ret = cy_as_usb_read_data_async(cy_as_dev->dev_handle, + as_ep->num, cy_false, _req->length, _req->buf, + cyasgadget_readcallback); + if (ret != CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_read_data_async failed with error " + "code %d\n", ret); + else + _req->status = -EALREADY; + } + + spin_lock_irqsave(&cy_as_dev->lock, flags); + + if (as_req) + list_add_tail(&as_req->queue, &as_ep->queue); + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + return 0; +} + +/* dequeue request */ +static int cyasgadget_dequeue( + struct usb_ep *_ep, + struct usb_request *_req + ) +{ + cyasgadget_ep *an_ep; + cyasgadget *dev; + an_ep = container_of(_ep, cyasgadget_ep, usb_ep_inst); + dev = an_ep->dev; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + cy_as_usb_cancel_async(dev->dev_handle, an_ep->num); + + return 0; +} + +static int cyasgadget_set_halt( + struct usb_ep *_ep, + int value + ) +{ + cyasgadget_ep *an_ep; + int retval = 0; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + an_ep = container_of(_ep, cyasgadget_ep, usb_ep_inst); + if (!_ep || (!an_ep->desc && an_ep->num != 0)) + return -EINVAL; + + if (!an_ep->dev->driver || an_ep->dev->gadget.speed == + USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + if (an_ep->desc /* not ep0 */ && + (an_ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC) + return -EINVAL; + + if (!list_empty(&an_ep->queue)) + retval = -EAGAIN; + else if (an_ep->is_in && value && + cyasgadget_fifo_status(_ep) != 0) + retval = -EAGAIN; + else { + if (value) { + cy_as_usb_set_stall(an_ep->dev->dev_handle, + an_ep->num, cyasgadget_stallcallback, 0); + } else { + cy_as_usb_clear_stall(an_ep->dev->dev_handle, + an_ep->num, cyasgadget_stallcallback, 0); + } + } + + return retval; +} + +static int cyasgadget_fifo_status( + struct usb_ep *_ep + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + return 0; +} + +static void cyasgadget_fifo_flush( + struct usb_ep *_ep + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif +} + +static struct usb_ep_ops cyasgadget_ep_ops = { + .enable = cyasgadget_enable, + .disable = cyasgadget_disable, + .alloc_request = cyasgadget_alloc_request, + .free_request = cyasgadget_free_request, + .queue = cyasgadget_queue, + .dequeue = cyasgadget_dequeue, + .set_halt = cyasgadget_set_halt, + .fifo_status = cyasgadget_fifo_status, + .fifo_flush = cyasgadget_fifo_flush, +}; + +/*************************************************************/ +/*This subsection contains all usb_gadget_ops cyasgadget_ops */ +/*************************************************************/ +static int cyasgadget_get_frame( + struct usb_gadget *_gadget + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + return 0; +} + +static int cyasgadget_wakeup( + struct usb_gadget *_gadget + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + return 0; +} + +static int cyasgadget_set_selfpowered( + struct usb_gadget *_gadget, + int value + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + return 0; +} + +static int cyasgadget_pullup( + struct usb_gadget *_gadget, + int is_on + ) +{ + struct cyasgadget *cy_as_dev; + unsigned long flags; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + if (!_gadget) + return -ENODEV; + + cy_as_dev = container_of(_gadget, cyasgadget, gadget); + + spin_lock_irqsave(&cy_as_dev->lock, flags); + cy_as_dev->softconnect = (is_on != 0); + if (is_on) + cy_as_usb_connect(cy_as_dev->dev_handle, 0, 0); + else + cy_as_usb_disconnect(cy_as_dev->dev_handle, 0, 0); + + spin_unlock_irqrestore(&cy_as_dev->lock, flags); + + return 0; +} + +static int cyasgadget_ioctl( + struct usb_gadget *_gadget, + unsigned code, + unsigned long param + ) +{ + int err = 0; + int retval = 0; + int ret_stat = 0; + cyasgadget *dev = cy_as_gadget_controller; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called, code=%d, param=%ld\n", + __func__, code, param); + #endif + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(code) != CYASGADGET_IOC_MAGIC) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s, bad magic number = 0x%x\n", + __func__, _IOC_TYPE(code)); + #endif + return -ENOTTY; + } + + if (_IOC_NR(code) > CYASGADGET_IOC_MAXNR) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s, bad ioctl code = 0x%x\n", + __func__, _IOC_NR(code)); + #endif + return -ENOTTY; + } + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(code) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, + (void __user *)param, _IOC_SIZE(code)); + else if (_IOC_DIR(code) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, + (void __user *)param, _IOC_SIZE(code)); + + if (err) { + cy_as_hal_print_message("%s, bad ioctl dir = 0x%x\n", + __func__, _IOC_DIR(code)); + return -EFAULT; + } + + switch (code) { + case CYASGADGET_GETMTPSTATUS: + { + cy_as_gadget_ioctl_tmtp_status *usr_d = + (cy_as_gadget_ioctl_tmtp_status *)param; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: got CYASGADGET_GETMTPSTATUS\n", + __func__); + #endif + + retval = __put_user(dev->tmtp_send_complete, + (uint32_t __user *)(&(usr_d->tmtp_send_complete))); + retval = __put_user(dev->tmtp_get_complete, + (uint32_t __user *)(&(usr_d->tmtp_get_complete))); + retval = __put_user(dev->tmtp_need_new_blk_tbl, + (uint32_t __user *)(&(usr_d->tmtp_need_new_blk_tbl))); + + if (copy_to_user((&(usr_d->tmtp_send_complete_data)), + (&(dev->tmtp_send_complete_data)), + sizeof(cy_as_gadget_ioctl_send_object))) + return -EFAULT; + + if (copy_to_user((&(usr_d->tmtp_get_complete_data)), + (&(dev->tmtp_get_complete_data)), + sizeof(cy_as_gadget_ioctl_get_object))) + return -EFAULT; + break; + } + case CYASGADGET_CLEARTMTPSTATUS: + { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s got CYASGADGET_CLEARTMTPSTATUS\n", + __func__); + #endif + + dev->tmtp_send_complete = 0; + dev->tmtp_get_complete = 0; + dev->tmtp_need_new_blk_tbl = 0; + + break; + } + case CYASGADGET_INITSOJ: + { + cy_as_gadget_ioctl_i_s_o_j_d k_d; + cy_as_gadget_ioctl_i_s_o_j_d *usr_d = + (cy_as_gadget_ioctl_i_s_o_j_d *)param; + cy_as_mtp_block_table blk_table; + struct scatterlist sg; + char *alloc_filename; + struct file *file_to_allocate; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s got CYASGADGET_INITSOJ\n", + __func__); + #endif + + memset(&blk_table, 0, sizeof(blk_table)); + + /* Get user argument structure */ + if (copy_from_user(&k_d, usr_d, + sizeof(cy_as_gadget_ioctl_i_s_o_j_d))) + return -EFAULT; + + /* better use fixed size buff*/ + alloc_filename = kmalloc(k_d.name_length + 1, GFP_KERNEL); + if (alloc_filename == NULL) + return -ENOMEM; + + /* get the filename */ + if (copy_from_user(alloc_filename, k_d.file_name, + k_d.name_length + 1)) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: CYASGADGET_INITSOJ, " + "copy file name from user space failed\n", + __func__); + #endif + kfree(alloc_filename); + return -EFAULT; + } + + file_to_allocate = filp_open(alloc_filename, O_RDWR, 0); + + if (!IS_ERR(file_to_allocate)) { + + struct address_space *mapping = + file_to_allocate->f_mapping; + const struct address_space_operations *a_ops = + mapping->a_ops; + struct inode *inode = mapping->host; + struct inode *alloc_inode = + file_to_allocate->f_path.dentry->d_inode; + uint32_t num_clusters = 0; + struct buffer_head bh; + struct kstat stat; + int nr_pages = 0; + int ret_stat = 0; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: fhandle is OK, " + "calling vfs_getattr\n", __func__); + #endif + + ret_stat = vfs_getattr(file_to_allocate->f_path.mnt, + file_to_allocate->f_path.dentry, &stat); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: returned from " + "vfs_getattr() stat->blksize=0x%lx\n", + __func__, stat.blksize); + #endif + + /* TODO: get this from disk properties + * (from blockdevice)*/ + #define SECTOR_SIZE 512 + if (stat.blksize != 0) { + num_clusters = (k_d.num_bytes) / SECTOR_SIZE; + + if (((k_d.num_bytes) % SECTOR_SIZE) != 0) + num_clusters++; + } else { + goto initsoj_safe_exit; + } + + bh.b_state = 0; + bh.b_blocknr = 0; + /* block size is arbitrary , we'll use sector size*/ + bh.b_size = SECTOR_SIZE; + + + + /* clear dirty pages in page cache + * (if were any allocated) */ + nr_pages = (k_d.num_bytes) / (PAGE_CACHE_SIZE); + + if (((k_d.num_bytes) % (PAGE_CACHE_SIZE)) != 0) + nr_pages++; + + #ifndef WESTBRIDGE_NDEBUG + /*check out how many pages where actually allocated */ + if (mapping->nrpages != nr_pages) + cy_as_hal_print_message("%s mpage_cleardirty " + "mapping->nrpages %d != num_pages %d\n", + __func__, (int) mapping->nrpages, + nr_pages); + + cy_as_hal_print_message("%s: calling " + "mpage_cleardirty() " + "for %d pages\n", __func__, nr_pages); + #endif + + ret_stat = mpage_cleardirty(mapping, nr_pages); + + /*fill up the the block table from the addr mapping */ + if (a_ops->bmap) { + int8_t blk_table_idx = -1; + uint32_t file_block_idx = 0; + uint32_t last_blk_addr_map = 0, + curr_blk_addr_map = 0; + + #ifndef WESTBRIDGE_NDEBUG + if (alloc_inode->i_bytes == 0) + cy_as_hal_print_message( + "%s: alloc_inode->ibytes =0\n", + __func__); + #endif + + /* iterate through the list of + * blocks (not clusters)*/ + for (file_block_idx = 0; + file_block_idx < num_clusters + /*inode->i_bytes*/; file_block_idx++) { + + /* returns starting sector number */ + curr_blk_addr_map = + a_ops->bmap(mapping, + file_block_idx); + + /*no valid mapping*/ + if (curr_blk_addr_map == 0) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s:hit invalid " + "mapping\n", __func__); + #endif + break; + } else if (curr_blk_addr_map != + (last_blk_addr_map + 1) || + (blk_table.num_blocks + [blk_table_idx] == 65535)) { + + /* next table entry */ + blk_table_idx++; + /* starting sector of a + * scattered cluster*/ + blk_table.start_blocks + [blk_table_idx] = + curr_blk_addr_map; + /* ++ num of blocks in cur + * table entry*/ + blk_table. + num_blocks[blk_table_idx]++; + + #ifndef WESTBRIDGE_NDEBUG + if (file_block_idx != 0) + cy_as_hal_print_message( + "<*> next table " + "entry:%d required\n", + blk_table_idx); + #endif + } else { + /*add contiguous block*/ + blk_table.num_blocks + [blk_table_idx]++; + } /*if (curr_blk_addr_map == 0)*/ + + last_blk_addr_map = curr_blk_addr_map; + } /* end for (file_block_idx = 0; file_block_idx + < inode->i_bytes;) */ + + #ifndef WESTBRIDGE_NDEBUG + /*print result for verification*/ + { + int i; + cy_as_hal_print_message( + "%s: print block table " + "mapping:\n", + __func__); + for (i = 0; i <= blk_table_idx; i++) { + cy_as_hal_print_message( + "<1> %d 0x%x 0x%x\n", i, + blk_table.start_blocks[i], + blk_table.num_blocks[i]); + } + } + #endif + + /* copy the block table to user + * space (for debug purposes) */ + retval = __put_user( + blk_table.start_blocks[blk_table_idx], + (uint32_t __user *) + (&(usr_d->blk_addr_p))); + + retval = __put_user( + blk_table.num_blocks[blk_table_idx], + (uint32_t __user *) + (&(usr_d->blk_count_p))); + + blk_table_idx++; + retval = __put_user(blk_table_idx, + (uint32_t __user *) + (&(usr_d->item_count))); + + } /*end if (a_ops->bmap)*/ + + filp_close(file_to_allocate, NULL); + + dev->tmtp_send_complete = 0; + dev->tmtp_need_new_blk_tbl = 0; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: calling cy_as_mtp_init_send_object()\n", + __func__); + #endif + sg_init_one(&sg, &blk_table, sizeof(blk_table)); + ret_stat = cy_as_mtp_init_send_object(dev->dev_handle, + (cy_as_mtp_block_table *)&sg, + k_d.num_bytes, 0, 0); + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: returned from " + "cy_as_mtp_init_send_object()\n", __func__); + #endif + + } + #ifndef WESTBRIDGE_NDEBUG + else { + cy_as_hal_print_message( + "%s: failed to allocate the file %s\n", + __func__, alloc_filename); + } /* end if (file_to_allocate)*/ + #endif + kfree(alloc_filename); +initsoj_safe_exit: + ret_stat = 0; + retval = __put_user(ret_stat, + (uint32_t __user *)(&(usr_d->ret_val))); + + break; + } + case CYASGADGET_INITGOJ: + { + cy_as_gadget_ioctl_i_g_o_j_d k_d; + cy_as_gadget_ioctl_i_g_o_j_d *usr_d = + (cy_as_gadget_ioctl_i_g_o_j_d *)param; + cy_as_mtp_block_table blk_table; + struct scatterlist sg; + char *map_filename; + struct file *file_to_map; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: got CYASGADGET_INITGOJ\n", + __func__); + #endif + + memset(&blk_table, 0, sizeof(blk_table)); + + /* Get user argument sturcutre */ + if (copy_from_user(&k_d, usr_d, + sizeof(cy_as_gadget_ioctl_i_g_o_j_d))) + return -EFAULT; + + map_filename = kmalloc(k_d.name_length + 1, GFP_KERNEL); + if (map_filename == NULL) + return -ENOMEM; + if (copy_from_user(map_filename, k_d.file_name, + k_d.name_length + 1)) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: copy file name from " + "user space failed\n", __func__); + #endif + kfree(map_filename); + return -EFAULT; + } + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<*>%s: opening %s for kernel " + "mode access map\n", __func__, map_filename); + #endif + file_to_map = filp_open(map_filename, O_RDWR, 0); + if (file_to_map) { + struct address_space *mapping = file_to_map->f_mapping; + const struct address_space_operations + *a_ops = mapping->a_ops; + struct inode *inode = mapping->host; + + int8_t blk_table_idx = -1; + uint32_t file_block_idx = 0; + uint32_t last_blk_addr_map = 0, curr_blk_addr_map = 0; + + /*verify operation exists*/ + if (a_ops->bmap) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<*>%s: bmap found, i_bytes=0x%x, " + "i_size=0x%x, i_blocks=0x%x\n", + __func__, inode->i_bytes, + (unsigned int) inode->i_size, + (unsigned int) inode->i_blocks); + #endif + + k_d.num_bytes = inode->i_size; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "<*>%s: k_d.num_bytes=0x%x\n", + __func__, k_d.num_bytes); + #endif + + for (file_block_idx = 0; + file_block_idx < inode->i_size; + file_block_idx++) { + curr_blk_addr_map = + a_ops->bmap(mapping, + file_block_idx); + + if (curr_blk_addr_map == 0) { + /*no valid mapping*/ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: no valid " + "mapping\n", __func__); + #endif + break; + } else if (curr_blk_addr_map != + (last_blk_addr_map + 1)) { + /*non-contiguous break*/ + blk_table_idx++; + blk_table.start_blocks + [blk_table_idx] = + curr_blk_addr_map; + blk_table.num_blocks + [blk_table_idx]++; + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: found non-" + "contiguous break", + __func__); + #endif + } else { + /*add contiguous block*/ + blk_table.num_blocks + [blk_table_idx]++; + } + last_blk_addr_map = curr_blk_addr_map; + } + + /*print result for verification*/ + #ifndef WESTBRIDGE_NDEBUG + { + int i = 0; + + for (i = 0; i <= blk_table_idx; i++) { + cy_as_hal_print_message( + "%s %d 0x%x 0x%x\n", + __func__, i, + blk_table.start_blocks[i], + blk_table.num_blocks[i]); + } + } + #endif + } else { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: could not find " + "a_ops->bmap\n", __func__); + #endif + return -EFAULT; + } + + filp_close(file_to_map, NULL); + + dev->tmtp_get_complete = 0; + dev->tmtp_need_new_blk_tbl = 0; + + ret_stat = __put_user( + blk_table.start_blocks[blk_table_idx], + (uint32_t __user *)(&(usr_d->blk_addr_p))); + + ret_stat = __put_user( + blk_table.num_blocks[blk_table_idx], + (uint32_t __user *)(&(usr_d->blk_count_p))); + + sg_init_one(&sg, &blk_table, sizeof(blk_table)); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: calling cy_as_mtp_init_get_object() " + "start=0x%x, num =0x%x, tid=0x%x, " + "num_bytes=0x%x\n", + __func__, + blk_table.start_blocks[0], + blk_table.num_blocks[0], + k_d.tid, + k_d.num_bytes); + #endif + + ret_stat = cy_as_mtp_init_get_object( + dev->dev_handle, + (cy_as_mtp_block_table *)&sg, + k_d.num_bytes, k_d.tid, 0, 0); + if (ret_stat != CY_AS_ERROR_SUCCESS) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: cy_as_mtp_init_get_object " + "failed ret_stat=0x%x\n", + __func__, ret_stat); + #endif + } + } + #ifndef WESTBRIDGE_NDEBUG + else { + cy_as_hal_print_message( + "%s: failed to open file %s\n", + __func__, map_filename); + } + #endif + kfree(map_filename); + + ret_stat = 0; + retval = __put_user(ret_stat, (uint32_t __user *) + (&(usr_d->ret_val))); + break; + } + case CYASGADGET_CANCELSOJ: + { + cy_as_gadget_ioctl_cancel *usr_d = + (cy_as_gadget_ioctl_cancel *)param; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message( + "%s: got CYASGADGET_CANCELSOJ\n", + __func__); + #endif + + ret_stat = cy_as_mtp_cancel_send_object(dev->dev_handle, 0, 0); + + retval = __put_user(ret_stat, (uint32_t __user *) + (&(usr_d->ret_val))); + break; + } + case CYASGADGET_CANCELGOJ: + { + cy_as_gadget_ioctl_cancel *usr_d = + (cy_as_gadget_ioctl_cancel *)param; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: got CYASGADGET_CANCELGOJ\n", + __func__); + #endif + + ret_stat = cy_as_mtp_cancel_get_object(dev->dev_handle, 0, 0); + + retval = __put_user(ret_stat, + (uint32_t __user *)(&(usr_d->ret_val))); + break; + } + default: + { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: unknown ioctl received: %d\n", + __func__, code); + + cy_as_hal_print_message("%s: known codes:\n" + "CYASGADGET_GETMTPSTATUS=%d\n" + "CYASGADGET_CLEARTMTPSTATUS=%d\n" + "CYASGADGET_INITSOJ=%d\n" + "CYASGADGET_INITGOJ=%d\n" + "CYASGADGET_CANCELSOJ=%d\n" + "CYASGADGET_CANCELGOJ=%d\n", + __func__, + CYASGADGET_GETMTPSTATUS, + CYASGADGET_CLEARTMTPSTATUS, + CYASGADGET_INITSOJ, + CYASGADGET_INITGOJ, + CYASGADGET_CANCELSOJ, + CYASGADGET_CANCELGOJ); + #endif + break; + } + } + + return 0; +} + +static const struct usb_gadget_ops cyasgadget_ops = { + .get_frame = cyasgadget_get_frame, + .wakeup = cyasgadget_wakeup, + .set_selfpowered = cyasgadget_set_selfpowered, + .pullup = cyasgadget_pullup, + .ioctl = cyasgadget_ioctl, +}; + + +/* keeping it simple: + * - one bus driver, initted first; + * - one function driver, initted second + * + * most of the work to support multiple controllers would + * be to associate this gadget driver with all of them, or + * perhaps to bind specific drivers to specific devices. + */ + +static void cyas_ep_reset( + cyasgadget_ep *an_ep + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + an_ep->desc = NULL; + INIT_LIST_HEAD(&an_ep->queue); + + an_ep->stopped = 0; + an_ep->is_in = 0; + an_ep->is_iso = 0; + an_ep->usb_ep_inst.maxpacket = ~0; + an_ep->usb_ep_inst.ops = &cyasgadget_ep_ops; +} + +static void cyas_usb_reset( + cyasgadget *cy_as_dev + ) +{ + cy_as_return_status_t ret; + cy_as_usb_enum_control config; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_device *dev_p = (cy_as_device *)cy_as_dev->dev_handle; + + cy_as_hal_print_message("<1>%s called mtp_firmware=0x%x\n", + __func__, dev_p->is_mtp_firmware); + #endif + + ret = cy_as_misc_release_resource(cy_as_dev->dev_handle, + cy_as_bus_u_s_b); + if (ret != CY_AS_ERROR_SUCCESS && ret != + CY_AS_ERROR_RESOURCE_NOT_OWNED) { + cy_as_hal_print_message("<1>_cy_as_gadget: cannot " + "release usb resource: failed with error code %d\n", + ret); + return; + } + + cy_as_dev->gadget.speed = USB_SPEED_HIGH; + + ret = cy_as_usb_start(cy_as_dev->dev_handle, 0, 0); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_start failed with error code %d\n", + ret); + return; + } + /* P port will do enumeration, not West Bridge */ + config.antioch_enumeration = cy_false; + /* 1 2 : 1-BUS_NUM , 2:Storage_device number, SD - is bus 1*/ + + /* TODO: add module param to enumerate mass storage */ + config.mass_storage_interface = 0; + + if (append_mtp) { + ret = cy_as_mtp_start(cy_as_dev->dev_handle, + cy_as_gadget_mtp_event_callback, 0, 0); + if (ret == CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("MTP start passed, enumerating " + "MTP interface\n"); + config.mtp_interface = append_mtp; + /*Do not enumerate NAND storage*/ + config.devices_to_enumerate[0][0] = cy_false; + + /*enumerate SD storage as MTP*/ + config.devices_to_enumerate[1][0] = cy_true; + } + } else { + cy_as_hal_print_message("MTP start not attempted, not " + "enumerating MTP interface\n"); + config.mtp_interface = 0; + /* enumerate mass storage based on module parameters */ + config.devices_to_enumerate[0][0] = msc_enum_bus_0; + config.devices_to_enumerate[1][0] = msc_enum_bus_1; + } + + ret = cy_as_usb_set_enum_config(cy_as_dev->dev_handle, + &config, 0, 0); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_gadget: " + "cy_as_usb_set_enum_config failed with error " + "code %d\n", ret); + return; + } + + cy_as_usb_set_physical_configuration(cy_as_dev->dev_handle, 1); + +} + +static void cyas_usb_reinit( + cyasgadget *cy_as_dev + ) +{ + int index = 0; + cyasgadget_ep *an_ep_p; + cy_as_return_status_t ret; + cy_as_device *dev_p = (cy_as_device *)cy_as_dev->dev_handle; + + INIT_LIST_HEAD(&cy_as_dev->gadget.ep_list); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called, is_mtp_firmware = " + "0x%x\n", __func__, dev_p->is_mtp_firmware); + #endif + + /* Init the end points */ + for (index = 1; index <= 15; index++) { + an_ep_p = &cy_as_dev->an_gadget_ep[index]; + cyas_ep_reset(an_ep_p); + an_ep_p->usb_ep_inst.name = cy_as_ep_names[index]; + an_ep_p->dev = cy_as_dev; + an_ep_p->num = index; + memset(&an_ep_p->cyepconfig, 0, sizeof(an_ep_p->cyepconfig)); + + /* EP0, EPs 2,4,6,8 need not be added */ + if ((index <= 8) && (index % 2 == 0) && + (!dev_p->is_mtp_firmware)) { + /* EP0 is 64 and EPs 2,4,6,8 not allowed */ + cy_as_dev->an_gadget_ep[index].fifo_size = 0; + } else { + if (index == 1) + an_ep_p->fifo_size = 64; + else + an_ep_p->fifo_size = 512; + list_add_tail(&an_ep_p->usb_ep_inst.ep_list, + &cy_as_dev->gadget.ep_list); + } + } + /* need to setendpointconfig before usb connect, this is not + * quite compatible with gadget methodology (ep_enable called + * by gadget after connect), therefore need to set config in + * initialization and verify compatibility in ep_enable, + * kick up error otherwise*/ + an_ep_p = &cy_as_dev->an_gadget_ep[3]; + an_ep_p->cyepconfig.enabled = cy_true; + an_ep_p->cyepconfig.dir = cy_as_usb_out; + an_ep_p->cyepconfig.type = cy_as_usb_bulk; + an_ep_p->cyepconfig.size = 0; + an_ep_p->cyepconfig.physical = 1; + ret = cy_as_usb_set_end_point_config(an_ep_p->dev->dev_handle, + 3, &an_ep_p->cyepconfig); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("cy_as_usb_set_end_point_config " + "failed with error code %d\n", ret); + } + + cy_as_usb_set_stall(an_ep_p->dev->dev_handle, 3, 0, 0); + + an_ep_p = &cy_as_dev->an_gadget_ep[5]; + an_ep_p->cyepconfig.enabled = cy_true; + an_ep_p->cyepconfig.dir = cy_as_usb_in; + an_ep_p->cyepconfig.type = cy_as_usb_bulk; + an_ep_p->cyepconfig.size = 0; + an_ep_p->cyepconfig.physical = 2; + ret = cy_as_usb_set_end_point_config(an_ep_p->dev->dev_handle, + 5, &an_ep_p->cyepconfig); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("cy_as_usb_set_end_point_config " + "failed with error code %d\n", ret); + } + + cy_as_usb_set_stall(an_ep_p->dev->dev_handle, 5, 0, 0); + + an_ep_p = &cy_as_dev->an_gadget_ep[9]; + an_ep_p->cyepconfig.enabled = cy_true; + an_ep_p->cyepconfig.dir = cy_as_usb_in; + an_ep_p->cyepconfig.type = cy_as_usb_bulk; + an_ep_p->cyepconfig.size = 0; + an_ep_p->cyepconfig.physical = 4; + ret = cy_as_usb_set_end_point_config(an_ep_p->dev->dev_handle, + 9, &an_ep_p->cyepconfig); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("cy_as_usb_set_end_point_config " + "failed with error code %d\n", ret); + } + + cy_as_usb_set_stall(an_ep_p->dev->dev_handle, 9, 0, 0); + + if (dev_p->mtp_count != 0) { + /* these need to be set for compatibility with + * the gadget_enable logic */ + an_ep_p = &cy_as_dev->an_gadget_ep[2]; + an_ep_p->cyepconfig.enabled = cy_true; + an_ep_p->cyepconfig.dir = cy_as_usb_out; + an_ep_p->cyepconfig.type = cy_as_usb_bulk; + an_ep_p->cyepconfig.size = 0; + an_ep_p->cyepconfig.physical = 0; + cy_as_usb_set_stall(an_ep_p->dev->dev_handle, 2, 0, 0); + + an_ep_p = &cy_as_dev->an_gadget_ep[6]; + an_ep_p->cyepconfig.enabled = cy_true; + an_ep_p->cyepconfig.dir = cy_as_usb_in; + an_ep_p->cyepconfig.type = cy_as_usb_bulk; + an_ep_p->cyepconfig.size = 0; + an_ep_p->cyepconfig.physical = 0; + cy_as_usb_set_stall(an_ep_p->dev->dev_handle, 6, 0, 0); + } + + cyas_ep_reset(&cy_as_dev->an_gadget_ep[0]); + cy_as_dev->an_gadget_ep[0].usb_ep_inst.name = cy_as_ep_names[0]; + cy_as_dev->an_gadget_ep[0].dev = cy_as_dev; + cy_as_dev->an_gadget_ep[0].num = 0; + cy_as_dev->an_gadget_ep[0].fifo_size = 64; + + cy_as_dev->an_gadget_ep[0].usb_ep_inst.maxpacket = 64; + cy_as_dev->gadget.ep0 = &cy_as_dev->an_gadget_ep[0].usb_ep_inst; + cy_as_dev->an_gadget_ep[0].stopped = 0; + INIT_LIST_HEAD(&cy_as_dev->gadget.ep0->ep_list); +} + +static void cyas_ep0_start( + cyasgadget *dev + ) +{ + cy_as_return_status_t ret; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + ret = cy_as_usb_register_callback(dev->dev_handle, + cy_as_gadget_usb_event_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: cy_as_usb_register_callback " + "failed with error code %d\n", __func__, ret); + #endif + return; + } + + ret = cy_as_usb_commit_config(dev->dev_handle, 0, 0); + if (ret != CY_AS_ERROR_SUCCESS) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: cy_as_usb_commit_config " + "failed with error code %d\n", __func__, ret); + #endif + return; + } + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: cy_as_usb_commit_config " + "message sent\n", __func__); + #endif + + ret = cy_as_usb_connect(dev->dev_handle, 0, 0); + if (ret != CY_AS_ERROR_SUCCESS) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: cy_as_usb_connect failed " + "with error code %d\n", __func__, ret); + #endif + return; + } + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s: cy_as_usb_connect message " + "sent\n", __func__); + #endif +} + +/* + * When a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + cyasgadget *dev = cy_as_gadget_controller; + int retval; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called driver=0x%x\n", + __func__, (unsigned int) driver); + #endif + + /* insist on high speed support from the driver, since + * "must not be used in normal operation" + */ + if (!driver + || !bind + || !driver->unbind + || !driver->setup) + return -EINVAL; + + if (!dev) + return -ENODEV; + + if (dev->driver) + return -EBUSY; + + /* hook up the driver ... */ + dev->softconnect = 1; + driver->driver.bus = NULL; + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + /* Do the needful */ + cyas_usb_reset(dev); /* External usb */ + cyas_usb_reinit(dev); /* Internal */ + + retval = bind(&dev->gadget); + if (retval) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("%s bind to driver %s --> %d\n", + __func__, driver->driver.name, retval); + #endif + + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; + } + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + cyas_ep0_start(dev); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_probe_driver); + +static void cyasgadget_nuke( + cyasgadget_ep *an_ep + ) +{ + cyasgadget *dev = cy_as_gadget_controller; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + cy_as_usb_cancel_async(dev->dev_handle, an_ep->num); + an_ep->stopped = 1; + + while (!list_empty(&an_ep->queue)) { + cyasgadget_req *an_req = list_entry + (an_ep->queue.next, cyasgadget_req, queue); + list_del_init(&an_req->queue); + an_req->req.status = -ESHUTDOWN; + an_req->req.complete(&an_ep->usb_ep_inst, &an_req->req); + } +} + +static void cyasgadget_stop_activity( + cyasgadget *dev, + struct usb_gadget_driver *driver + ) +{ + int index; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + /* don't disconnect if it's not connected */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + if (spin_is_locked(&dev->lock)) + spin_unlock(&dev->lock); + + /* Stop hardware; prevent new request submissions; + * and kill any outstanding requests. + */ + cy_as_usb_disconnect(dev->dev_handle, 0, 0); + + for (index = 3; index <= 7; index += 2) { + cyasgadget_ep *an_ep_p = &dev->an_gadget_ep[index]; + cyasgadget_nuke(an_ep_p); + } + + for (index = 9; index <= 15; index++) { + cyasgadget_ep *an_ep_p = &dev->an_gadget_ep[index]; + cyasgadget_nuke(an_ep_p); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) + driver->disconnect(&dev->gadget); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("cy_as_usb_disconnect returned success"); + #endif + + /* Stop Usb */ + cy_as_usb_stop(dev->dev_handle, 0, 0); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("cy_as_usb_stop returned success"); + #endif +} + +int usb_gadget_unregister_driver( + struct usb_gadget_driver *driver + ) +{ + cyasgadget *dev = cy_as_gadget_controller; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + if (!dev) + return -ENODEV; + + if (!driver || driver != dev->driver) + return -EINVAL; + + cyasgadget_stop_activity(dev, driver); + + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("unregistered driver '%s'\n", + driver->driver.name); + #endif + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +static void cyas_gadget_release( + struct device *_dev + ) +{ + cyasgadget *dev = dev_get_drvdata(_dev); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>%s called\n", __func__); + #endif + + kfree(dev); +} + +/* DeInitialize gadget driver */ +static void cyasgadget_deinit( + cyasgadget *cy_as_dev + ) +{ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget deinitialize called\n"); + #endif + + if (!cy_as_dev) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget_deinit: " + "invalid cyasgadget device\n"); + #endif + return; + } + + if (cy_as_dev->driver) { + /* should have been done already by driver model core */ + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1> cy_as_gadget: '%s' " + "is still registered\n", + cy_as_dev->driver->driver.name); + #endif + usb_gadget_unregister_driver(cy_as_dev->driver); + } + + kfree(cy_as_dev); + cy_as_gadget_controller = NULL; +} + +/* Initialize gadget driver */ +static int cyasgadget_initialize(void) +{ + cyasgadget *cy_as_dev = 0; + int retval = 0; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message("<1>_cy_as_gadget [V1.1] initialize called\n"); + #endif + + if (cy_as_gadget_controller != 0) { + cy_as_hal_print_message("<1> cy_as_gadget: the device has " + "already been initilaized. ignoring\n"); + return -EBUSY; + } + + cy_as_dev = kzalloc(sizeof(cyasgadget), GFP_ATOMIC); + if (cy_as_dev == NULL) { + cy_as_hal_print_message("<1> cy_as_gadget: memory " + "allocation failed\n"); + return -ENOMEM; + } + + spin_lock_init(&cy_as_dev->lock); + cy_as_dev->gadget.ops = &cyasgadget_ops; + cy_as_dev->gadget.is_dualspeed = 1; + + /* the "gadget" abstracts/virtualizes the controller */ + /*strcpy(cy_as_dev->gadget.dev.bus_id, "cyasgadget");*/ + cy_as_dev->gadget.dev.release = cyas_gadget_release; + cy_as_dev->gadget.name = cy_as_driver_name; + + /* Get the device handle */ + cy_as_dev->dev_handle = cyasdevice_getdevhandle(); + if (0 == cy_as_dev->dev_handle) { + #ifndef NDEBUG + cy_as_hal_print_message("<1> cy_as_gadget: " + "no west bridge device\n"); + #endif + retval = -EFAULT; + goto done; + } + + /* We are done now */ + cy_as_gadget_controller = cy_as_dev; + return 0; + +/* + * in case of an error + */ +done: + if (cy_as_dev) + cyasgadget_deinit(cy_as_dev); + + return retval; +} + +static int __init cyas_init(void) +{ + int init_res = 0; + + init_res = cyasgadget_initialize(); + + if (init_res != 0) { + printk(KERN_WARNING "<1> gadget ctl instance " + "init error:%d\n", init_res); + if (init_res > 0) { + /* force -E/0 linux convention */ + init_res = init_res * -1; + } + } + + return init_res; +} +module_init(cyas_init); + +static void __exit cyas_cleanup(void) +{ + if (cy_as_gadget_controller != NULL) + cyasgadget_deinit(cy_as_gadget_controller); +} +module_exit(cyas_cleanup); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(CY_AS_DRIVER_DESC); +MODULE_AUTHOR("cypress semiconductor"); + +/*[]*/ diff --git a/drivers/staging/westbridge/astoria/gadget/cyasgadget.h b/drivers/staging/westbridge/astoria/gadget/cyasgadget.h new file mode 100644 index 0000000..e01cea7 --- /dev/null +++ b/drivers/staging/westbridge/astoria/gadget/cyasgadget.h @@ -0,0 +1,193 @@ +/* cyangadget.h - Linux USB Gadget driver file for the Cypress West Bridge +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## 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. +## =========================== +*/ + +/* + * Cypress West Bridge high/full speed USB device controller code + * Based on the Netchip 2280 device controller by David Brownell + * in the linux 2.6.10 kernel + * + * linux/drivers/usb/gadget/net2280.h + */ + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _INCLUDED_CYANGADGET_H_ +#define _INCLUDED_CYANGADGET_H_ + +#include <linux/device.h> +#include <linux/moduleparam.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/sched.h> + +#include "../include/linux/westbridge/cyastoria.h" +#include "../include/linux/westbridge/cyashal.h" +#include "../include/linux/westbridge/cyasdevice.h" +#include "cyasgadget_ioctl.h" + +#include <linux/module.h> +#include <linux/init.h> + +/*char driver defines, revisit*/ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/fs.h> /* everything... */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/proc_fs.h> +#include <linux/fcntl.h> /* O_ACCMODE */ +#include <linux/seq_file.h> +#include <linux/cdev.h> +#include <linux/scatterlist.h> +#include <linux/pagemap.h> +#include <linux/vmalloc.h> /* vmalloc(), vfree */ +#include <linux/msdos_fs.h> /*fat_alloc_cluster*/ +#include <linux/buffer_head.h> +#include <asm/system.h> /* cli(), *_flags */ +#include <linux/uaccess.h> /* copy_*_user */ + +extern int mpage_cleardirty(struct address_space *mapping, int num_pages); +extern int fat_get_block(struct inode *, sector_t , struct buffer_head *, int); +extern cy_as_device_handle *cyasdevice_getdevhandle(void); + +/* Driver data structures and utilities */ +typedef struct cyasgadget_ep { + struct usb_ep usb_ep_inst; + struct cyasgadget *dev; + + /* analogous to a host-side qh */ + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + unsigned num:8, + fifo_size:12, + in_fifo_validate:1, + out_overflow:1, + stopped:1, + is_in:1, + is_iso:1; + cy_as_usb_end_point_config cyepconfig; +} cyasgadget_ep; + +typedef struct cyasgadget_req { + struct usb_request req; + struct list_head queue; + int ep_num; + unsigned mapped:1, + valid:1, + complete:1, + ep_stopped:1; +} cyasgadget_req; + +typedef struct cyasgadget { + /* each device provides one gadget, several endpoints */ + struct usb_gadget gadget; + spinlock_t lock; + struct cyasgadget_ep an_gadget_ep[16]; + struct usb_gadget_driver *driver; + /* Handle to the West Bridge device */ + cy_as_device_handle dev_handle; + unsigned enabled:1, + protocol_stall:1, + softconnect:1, + outsetupreq:1; + struct completion thread_complete; + wait_queue_head_t thread_wq; + struct semaphore thread_sem; + struct list_head thread_queue; + + cy_bool tmtp_send_complete; + cy_bool tmtp_get_complete; + cy_bool tmtp_need_new_blk_tbl; + /* Data member used to store the SendObjectComplete event data */ + cy_as_mtp_send_object_complete_data tmtp_send_complete_data; + /* Data member used to store the GetObjectComplete event data */ + cy_as_mtp_get_object_complete_data tmtp_get_complete_data; + +} cyasgadget; + +static inline void set_halt(cyasgadget_ep *ep) +{ + return; +} + +static inline void clear_halt(cyasgadget_ep *ep) +{ + return; +} + +#define xprintk(dev, level, fmt, args...) \ + printk(level "%s %s: " fmt, driver_name, \ + pci_name(dev->pdev), ## args) + +#ifdef DEBUG +#undef DEBUG +#define DEBUG(dev, fmt, args...) \ + xprintk(dev, KERN_DEBUG, fmt, ## args) +#else +#define DEBUG(dev, fmt, args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDEBUG DEBUG +#else +#define VDEBUG(dev, fmt, args...) \ + do { } while (0) +#endif /* VERBOSE */ + +#define ERROR(dev, fmt, args...) \ + xprintk(dev, KERN_ERR, fmt, ## args) +#define GADG_WARN(dev, fmt, args...) \ + xprintk(dev, KERN_WARNING, fmt, ## args) +#define INFO(dev, fmt, args...) \ + xprintk(dev, KERN_INFO, fmt, ## args) + +/*-------------------------------------------------------------------------*/ + +static inline void start_out_naking(struct cyasgadget_ep *ep) +{ + return; +} + +static inline void stop_out_naking(struct cyasgadget_ep *ep) +{ + return; +} + +#endif /* _INCLUDED_CYANGADGET_H_ */ diff --git a/drivers/staging/westbridge/astoria/gadget/cyasgadget_ioctl.h b/drivers/staging/westbridge/astoria/gadget/cyasgadget_ioctl.h new file mode 100644 index 0000000..21dd716 --- /dev/null +++ b/drivers/staging/westbridge/astoria/gadget/cyasgadget_ioctl.h @@ -0,0 +1,99 @@ +/* cyasgadget_ioctl.h - Linux USB Gadget driver ioctl file for + * Cypress West Bridge +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## 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. +## =========================== +*/ + +#ifndef CYASGADGET_IOCTL_H +#define CYASGADGET_IOCTL_H + + +#include <linux/types.h> +#include <linux/ioctl.h> + +typedef struct cy_as_gadget_ioctl_send_object { + uint32_t status; + uint32_t byte_count; + uint32_t transaction_id; +} cy_as_gadget_ioctl_send_object; + +typedef struct cy_as_gadget_ioctl_get_object { + uint32_t status; + uint32_t byte_count; +} cy_as_gadget_ioctl_get_object; + + +typedef struct cy_as_gadget_ioctl_tmtp_status { + cy_bool tmtp_send_complete; + cy_bool tmtp_get_complete; + cy_bool tmtp_need_new_blk_tbl; + cy_as_gadget_ioctl_send_object tmtp_send_complete_data; + cy_as_gadget_ioctl_get_object tmtp_get_complete_data; + uint32_t t_usec; +} cy_as_gadget_ioctl_tmtp_status; + +/*Init send object data*/ +typedef struct cy_as_gadget_ioctl_i_s_o_j_d { + uint32_t *blk_addr_p; /* starting sector */ + uint16_t *blk_count_p; /* num of sectors in the block */ + /* number of entries in the blk table */ + uint32_t item_count; + uint32_t num_bytes; + /* in case if more prcise timestamping is done in kernel mode */ + uint32_t t_usec; + uint32_t ret_val; + char *file_name; + uint32_t name_length; + +} cy_as_gadget_ioctl_i_s_o_j_d; + + +/*Init get object data*/ +typedef struct cy_as_gadget_ioctl_i_g_o_j_d { + uint32_t *blk_addr_p; + uint16_t *blk_count_p; + uint32_t item_count; + uint32_t num_bytes; + uint32_t tid; + uint32_t ret_val; + char *file_name; + uint32_t name_length; + +} cy_as_gadget_ioctl_i_g_o_j_d; + +typedef struct cy_as_gadget_ioctl_cancel { + uint32_t ret_val; +} cy_as_gadget_ioctl_cancel; + +#define CYASGADGET_IOC_MAGIC 0xEF +#define CYASGADGET_GETMTPSTATUS \ + _IOW(CYASGADGET_IOC_MAGIC, 0, cy_as_gadget_ioctl_tmtp_status) +#define CYASGADGET_CLEARTMTPSTATUS \ + _IO(CYASGADGET_IOC_MAGIC, 1) +#define CYASGADGET_INITSOJ \ + _IOW(CYASGADGET_IOC_MAGIC, 2, cy_as_gadget_ioctl_i_s_o_j_d) +#define CYASGADGET_INITGOJ \ + _IOW(CYASGADGET_IOC_MAGIC, 3, cy_as_gadget_ioctl_i_g_o_j_d) +#define CYASGADGET_CANCELSOJ \ + _IOW(CYASGADGET_IOC_MAGIC, 4, cy_as_gadget_ioctl_cancel) +#define CYASGADGET_CANCELGOJ \ + _IOW(CYASGADGET_IOC_MAGIC, 5, cy_as_gadget_ioctl_cancel) +#define CYASGADGET_IOC_MAXNR 6 + +#endif |