diff options
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/designware_udc.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/ether.c | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/f_thor.c | 1003 | ||||
-rw-r--r-- | drivers/usb/gadget/f_thor.h | 124 | ||||
-rw-r--r-- | drivers/usb/gadget/g_dnl.c | 38 | ||||
-rw-r--r-- | drivers/usb/gadget/mpc8xx_udc.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/mv_udc.c | 115 | ||||
-rw-r--r-- | drivers/usb/gadget/mv_udc.h | 115 | ||||
-rw-r--r-- | drivers/usb/gadget/omap1510_udc.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa27x_udc.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 3 |
12 files changed, 1347 insertions, 60 deletions
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1590c4a..afaf5ce 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -21,6 +21,7 @@ ifdef CONFIG_USB_GADGET COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +COBJS-$(CONFIG_THOR_FUNCTION) += f_thor.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o diff --git a/drivers/usb/gadget/designware_udc.c b/drivers/usb/gadget/designware_udc.c index 1aab31b..b7c1038 100644 --- a/drivers/usb/gadget/designware_udc.c +++ b/drivers/usb/gadget/designware_udc.c @@ -14,6 +14,7 @@ #include <usbdevice.h> #include "ep0.h" #include <usb/designware_udc.h> +#include <usb/udc.h> #include <asm/arch/hardware.h> #define UDC_INIT_MDELAY 80 /* Device settle delay */ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 700d5fb..cc6cc1f 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -635,6 +635,7 @@ fs_source_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(64), }; static struct usb_endpoint_descriptor @@ -644,6 +645,7 @@ fs_sink_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(64), }; static const struct usb_descriptor_header *fs_eth_function[11] = { @@ -1534,6 +1536,8 @@ static int rx_submit(struct eth_dev *dev, struct usb_request *req, */ debug("%s\n", __func__); + if (!req) + return -EINVAL; size = (ETHER_HDR_SIZE + dev->mtu + RX_EXTRA); size += dev->out_ep->maxpacket - 1; diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c new file mode 100644 index 0000000..c4c9909 --- /dev/null +++ b/drivers/usb/gadget/f_thor.c @@ -0,0 +1,1003 @@ +/* + * f_thor.c -- USB TIZEN THOR Downloader gadget function + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * Based on code from: + * git://review.tizen.org/kernel/u-boot + * + * Developed by: + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang <mk7.kang@samsung.com> + * Sanghee Kim <sh0130.kim@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <errno.h> +#include <common.h> +#include <malloc.h> +#include <version.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> +#include <g_dnl.h> +#include <dfu.h> + +#include "f_thor.h" + +static void thor_tx_data(unsigned char *data, int len); +static void thor_set_dma(void *addr, int len); +static int thor_rx_data(void); + +static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{ + return container_of(f, struct f_thor, usb_function); +} + +DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_tx_data_buf, + sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_rx_data_buf, + sizeof(struct rqt_box)); + +/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE); +static unsigned long long int thor_file_size; +static int alt_setting_num; + +static void send_rsp(const struct rsp_box *rsp) +{ + memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box)); + + debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data); +} + +static void send_data_rsp(s32 ack, s32 count) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp, + sizeof(struct data_rsp_box)); + + rsp->ack = ack; + rsp->count = count; + + memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct data_rsp_box)); + + debug("-DATA RSP: %d, %d\n", ack, count); +} + +static int process_rqt_info(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, 0, sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_INFO_VER_PROTOCOL: + rsp->int_data[0] = VER_PROTOCOL_MAJOR; + rsp->int_data[1] = VER_PROTOCOL_MINOR; + break; + case RQT_INIT_VER_HW: + snprintf(rsp->str_data[0], sizeof(rsp->str_data[0]), + "%x", checkboard()); + break; + case RQT_INIT_VER_BOOT: + sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION); + break; + case RQT_INIT_VER_KERNEL: + sprintf(rsp->str_data[0], "%s", "k unknown"); + break; + case RQT_INIT_VER_PLATFORM: + sprintf(rsp->str_data[0], "%s", "p unknown"); + break; + case RQT_INIT_VER_CSC: + sprintf(rsp->str_data[0], "%s", "c unknown"); + break; + default: + return -EINVAL; + } + + send_rsp(rsp); + return true; +} + +static int process_rqt_cmd(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, 0, sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_CMD_REBOOT: + debug("TARGET RESET\n"); + send_rsp(rsp); + g_dnl_unregister(); + dfu_free_entities(); + run_command("reset", 0); + break; + case RQT_CMD_POWEROFF: + case RQT_CMD_EFSCLEAR: + send_rsp(rsp); + default: + printf("Command not supported -> cmd: %d\n", rqt->rqt_data); + return -EINVAL; + } + + return true; +} + +static long long int download_head(unsigned long long total, + unsigned int packet_size, + long long int *left, + int *cnt) +{ + long long int rcv_cnt = 0, left_to_rcv, ret_rcv; + void *transfer_buffer = dfu_get_buf(); + void *buf = transfer_buffer; + int usb_pkt_cnt = 0, ret; + + /* + * Files smaller than THOR_STORE_UNIT_SIZE (now 32 MiB) are stored on + * the medium. + * The packet response is sent on the purpose after successful data + * chunk write. There is a room for improvement when asynchronous write + * is performed. + */ + while (total - rcv_cnt >= packet_size) { + thor_set_dma(buf, packet_size); + buf += packet_size; + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + debug("%d: RCV data count: %llu cnt: %d\n", usb_pkt_cnt, + rcv_cnt, *cnt); + + if ((rcv_cnt % THOR_STORE_UNIT_SIZE) == 0) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, THOR_STORE_UNIT_SIZE, + (*cnt)++); + if (ret) { + error("DFU write failed [%d] cnt: %d", + ret, *cnt); + return ret; + } + buf = transfer_buffer; + } + send_data_rsp(0, ++usb_pkt_cnt); + } + + /* Calculate the amount of data to arrive from PC (in bytes) */ + left_to_rcv = total - rcv_cnt; + + /* + * Calculate number of data already received. but not yet stored + * on the medium (they are smaller than THOR_STORE_UNIT_SIZE) + */ + *left = left_to_rcv + buf - transfer_buffer; + debug("%s: left: %llu left_to_rcv: %llu buf: 0x%p\n", __func__, + *left, left_to_rcv, buf); + + if (left_to_rcv) { + thor_set_dma(buf, packet_size); + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + send_data_rsp(0, ++usb_pkt_cnt); + } + + debug("%s: %llu total: %llu cnt: %d\n", __func__, rcv_cnt, total, *cnt); + + return rcv_cnt; +} + +static int download_tail(long long int left, int cnt) +{ + void *transfer_buffer = dfu_get_buf(); + int ret; + + debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); + + if (left) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, left, cnt++); + if (ret) { + error("DFU write failed [%d]: left: %llu", ret, left); + return ret; + } + } + + /* + * To store last "packet" DFU storage backend requires dfu_write with + * size parameter equal to 0 + * + * This also frees memory malloc'ed by dfu_get_buf(), so no explicit + * need fo call dfu_free_buf() is needed. + */ + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, 0, cnt); + if (ret) + error("DFU write failed [%d] cnt: %d", ret, cnt); + + return ret; +} + +static long long int process_rqt_download(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + static long long int left, ret_head; + int file_type, ret = 0; + static int cnt; + + memset(rsp, 0, sizeof(struct rsp_box)); + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_DL_INIT: + thor_file_size = rqt->int_data[0]; + debug("INIT: total %d bytes\n", rqt->int_data[0]); + break; + case RQT_DL_FILE_INFO: + file_type = rqt->int_data[0]; + if (file_type == FILE_TYPE_PIT) { + puts("PIT table file - not supported\n"); + rsp->ack = -ENOTSUPP; + ret = rsp->ack; + break; + } + + thor_file_size = rqt->int_data[1]; + memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE); + + debug("INFO: name(%s, %d), size(%llu), type(%d)\n", + f_name, 0, thor_file_size, file_type); + + rsp->int_data[0] = THOR_PACKET_SIZE; + + alt_setting_num = dfu_get_alt(f_name); + if (alt_setting_num < 0) { + error("Alt setting [%d] to write not found!", + alt_setting_num); + rsp->ack = -ENODEV; + ret = rsp->ack; + } + break; + case RQT_DL_FILE_START: + send_rsp(rsp); + ret_head = download_head(thor_file_size, THOR_PACKET_SIZE, + &left, &cnt); + if (ret_head < 0) { + left = 0; + cnt = 0; + } + return ret_head; + case RQT_DL_FILE_END: + debug("DL FILE_END\n"); + rsp->ack = download_tail(left, cnt); + ret = rsp->ack; + left = 0; + cnt = 0; + break; + case RQT_DL_EXIT: + debug("DL EXIT\n"); + break; + default: + error("Operation not supported: %d", rqt->rqt_data); + ret = -ENOTSUPP; + } + + send_rsp(rsp); + return ret; +} + +static int process_data(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rqt_box, rqt, sizeof(struct rqt_box)); + int ret = -EINVAL; + + memset(rqt, 0, sizeof(rqt)); + memcpy(rqt, thor_rx_data_buf, sizeof(struct rqt_box)); + + debug("+RQT: %d, %d\n", rqt->rqt, rqt->rqt_data); + + switch (rqt->rqt) { + case RQT_INFO: + ret = process_rqt_info(rqt); + break; + case RQT_CMD: + ret = process_rqt_cmd(rqt); + break; + case RQT_DL: + ret = (int) process_rqt_download(rqt); + break; + case RQT_UL: + puts("RQT: UPLOAD not supported!\n"); + break; + default: + error("unknown request (%d)", rqt->rqt); + } + + return ret; +} + +/* ********************************************************** */ +/* THOR USB Function */ +/* ********************************************************** */ + +static inline struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +static struct usb_interface_descriptor thor_downloader_intf_data = { + .bLength = sizeof(thor_downloader_intf_data), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, +}; + +static struct usb_endpoint_descriptor fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* CDC configuration */ +static struct usb_interface_descriptor thor_downloader_intf_int = { + .bLength = sizeof(thor_downloader_intf_int), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + /* 0x02 Abstract Line Control Model */ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + /* 0x01 Common AT commands */ + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, +}; + +static struct usb_cdc_header_desc thor_downloader_cdc_header = { + .bLength = sizeof(thor_downloader_cdc_header), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x00, + .bcdCDC = 0x0110, +}; + +static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = { + .bLength = sizeof(thor_downloader_cdc_call), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x01, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = { + .bLength = sizeof(thor_downloader_cdc_abstract), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x02, + .bmCapabilities = 0x00, +}; + +static struct usb_cdc_union_desc thor_downloader_cdc_union = { + .bLength = sizeof(thor_downloader_cdc_union), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = USB_CDC_UNION_TYPE, +}; + +static struct usb_endpoint_descriptor fs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 3 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static struct usb_interface_assoc_descriptor +thor_iad_descriptor = { + .bLength = sizeof(thor_iad_descriptor), + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTO_NONE, +}; + +static struct usb_endpoint_descriptor hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static struct usb_qualifier_descriptor dev_qualifier = { + .bLength = sizeof(dev_qualifier), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + + .bNumConfigurations = 2, +}; + +/* + * This attribute vendor descriptor is necessary for correct operation with + * Windows version of THOR download program + * + * It prevents windows driver from sending zero lenght packet (ZLP) after + * each THOR_PACKET_SIZE. This assures consistent behaviour with libusb + */ +static struct usb_cdc_attribute_vendor_descriptor thor_downloader_cdc_av = { + .bLength = sizeof(thor_downloader_cdc_av), + .bDescriptorType = 0x24, + .bDescriptorSubType = 0x80, + .DAUType = 0x0002, + .DAULength = 0x0001, + .DAUValue = 0x00, +}; + +static const struct usb_descriptor_header *hs_thor_downloader_function[] = { + (struct usb_descriptor_header *)&thor_iad_descriptor, + + (struct usb_descriptor_header *)&thor_downloader_intf_int, + (struct usb_descriptor_header *)&thor_downloader_cdc_header, + (struct usb_descriptor_header *)&thor_downloader_cdc_call, + (struct usb_descriptor_header *)&thor_downloader_cdc_abstract, + (struct usb_descriptor_header *)&thor_downloader_cdc_union, + (struct usb_descriptor_header *)&hs_int_desc, + + (struct usb_descriptor_header *)&thor_downloader_intf_data, + (struct usb_descriptor_header *)&thor_downloader_cdc_av, + (struct usb_descriptor_header *)&hs_in_desc, + (struct usb_descriptor_header *)&hs_out_desc, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return req; + + req->length = length; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + + return req; +} + +static int thor_rx_data(void) +{ + struct thor_dev *dev = thor_func->dev; + int data_to_rx, tmp, status; + + data_to_rx = dev->out_req->length; + tmp = data_to_rx; + do { + dev->out_req->length = data_to_rx; + debug("dev->out_req->length:%d dev->rxdata:%d\n", + dev->out_req->length, dev->rxdata); + + status = usb_ep_queue(dev->out_ep, dev->out_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->out_ep->name, dev->out_req->length, status); + usb_ep_set_halt(dev->out_ep); + return -EAGAIN; + } + + while (!dev->rxdata) { + usb_gadget_handle_interrupts(); + if (ctrlc()) + return -1; + } + dev->rxdata = 0; + data_to_rx -= dev->out_req->actual; + } while (data_to_rx); + + return tmp; +} + +static void thor_tx_data(unsigned char *data, int len) +{ + struct thor_dev *dev = thor_func->dev; + unsigned char *ptr = dev->in_req->buf; + int status; + + memset(ptr, 0, len); + memcpy(ptr, data, len); + + dev->in_req->length = len; + + debug("%s: dev->in_req->length:%d to_cpy:%d\n", __func__, + dev->in_req->length, sizeof(data)); + + status = usb_ep_queue(dev->in_ep, dev->in_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->in_ep->name, dev->in_req->length, status); + usb_ep_set_halt(dev->in_ep); + } + + /* Wait until tx interrupt received */ + while (!dev->txdata) + usb_gadget_handle_interrupts(); + + dev->txdata = 0; +} + +static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct thor_dev *dev = thor_func->dev; + int status = req->status; + + debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req); + switch (status) { + case 0: + if (ep == dev->out_ep) + dev->rxdata = 1; + else + dev->txdata = 1; + + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + case -EREMOTEIO: /* short read */ + case -EOVERFLOW: + error("ERROR:%d", status); + break; + } + + debug("%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); +} + +static struct usb_request *thor_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = alloc_ep_req(ep, ep->maxpacket); + debug("%s: ep:%p req:%p\n", __func__, ep, req); + + if (!req) + return NULL; + + memset(req->buf, 0, req->length); + req->complete = thor_rx_tx_complete; + + memset(req->buf, 0x55, req->length); + + return req; +} + +static void thor_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int +thor_func_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct thor_dev *dev = thor_func->dev; + struct usb_request *req = dev->req; + struct usb_gadget *gadget = dev->gadget; + int value = 0; + + u16 len = le16_to_cpu(ctrl->wLength); + + debug("Req_Type: 0x%x Req: 0x%x wValue: 0x%x wIndex: 0x%x wLen: 0x%x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, + ctrl->wLength); + + switch (ctrl->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + value = 0; + break; + case USB_CDC_REQ_SET_LINE_CODING: + value = len; + /* Line Coding set done = configuration done */ + thor_func->dev->configuration_done = 1; + break; + + default: + error("thor_setup: unknown request: %d", ctrl->bRequest); + } + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("%s: ep_queue: %d\n", __func__, value); + req->status = 0; + } + } + + return value; +} + +/* Specific to the THOR protocol */ +static void thor_set_dma(void *addr, int len) +{ + struct thor_dev *dev = thor_func->dev; + + debug("in_req:%p, out_req:%p\n", dev->in_req, dev->out_req); + debug("addr:%p, len:%d\n", addr, len); + + dev->out_req->buf = addr; + dev->out_req->length = len; +} + +int thor_init(void) +{ + struct thor_dev *dev = thor_func->dev; + + /* Wait for a device enumeration and configuration settings */ + debug("THOR enumeration/configuration setting....\n"); + while (!dev->configuration_done) + usb_gadget_handle_interrupts(); + + thor_set_dma(thor_rx_data_buf, strlen("THOR")); + /* detect the download request from Host PC */ + if (thor_rx_data() < 0) { + printf("%s: Data not received!\n", __func__); + return -1; + } + + if (!strncmp((char *)thor_rx_data_buf, "THOR", strlen("THOR"))) { + puts("Download request from the Host PC\n"); + udelay(30 * 1000); /* 30 ms */ + + strcpy((char *)thor_tx_data_buf, "ROHT"); + thor_tx_data(thor_tx_data_buf, strlen("ROHT")); + } else { + puts("Wrong reply information\n"); + return -1; + } + + return 0; +} + +int thor_handle(void) +{ + int ret; + + /* receive the data from Host PC */ + while (1) { + thor_set_dma(thor_rx_data_buf, sizeof(struct rqt_box)); + ret = thor_rx_data(); + + if (ret > 0) { + ret = process_data(); + if (ret < 0) + return ret; + } else { + printf("%s: No data received!\n", __func__); + break; + } + } + + return 0; +} + +static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev; + struct usb_ep *ep; + int status; + + thor_func = f_thor; + dev = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + dev->gadget = gadget; + f_thor->dev = dev; + + debug("%s: usb_configuration: 0x%p usb_function: 0x%p\n", + __func__, c, f); + debug("f_thor: 0x%p thor: 0x%p\n", f_thor, dev); + + /* EP0 */ + /* preallocate control response and buffer */ + dev->req = usb_ep_alloc_request(gadget->ep0, 0); + if (!dev->req) { + status = -ENOMEM; + goto fail; + } + dev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, + gadget->ep0->maxpacket); + if (!dev->req->buf) { + status = -ENOMEM; + goto fail; + } + + dev->req->complete = thor_setup_complete; + + /* DYNAMIC interface numbers assignments */ + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_int.bInterfaceNumber = status; + thor_downloader_cdc_union.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_data.bInterfaceNumber = status; + thor_downloader_cdc_union.bSlaveInterface0 = status; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(gadget, &fs_in_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + } + + dev->in_ep = ep; /* Store IN EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_out_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + dev->out_ep = ep; /* Store OUT EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_int_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + dev->int_ep = ep; + + if (gadget_is_dualspeed(gadget)) { + hs_int_desc.bEndpointAddress = + fs_int_desc.bEndpointAddress; + + f->hs_descriptors = (struct usb_descriptor_header **) + &hs_thor_downloader_function; + + if (!f->hs_descriptors) + goto fail; + } + + debug("%s: out_ep:%p out_req:%p\n", __func__, + dev->out_ep, dev->out_req); + + return 0; + + fail: + free(dev); + return status; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + free(req->buf); + usb_ep_free_request(ep, req); +} + +static void thor_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + free(dev); + memset(thor_func, 0, sizeof(*thor_func)); + thor_func = NULL; +} + +static void thor_func_disable(struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + debug("%s:\n", __func__); + + /* Avoid freeing memory when ep is still claimed */ + if (dev->in_ep->driver_data) { + free_ep_req(dev->in_ep, dev->in_req); + usb_ep_disable(dev->in_ep); + dev->in_ep->driver_data = NULL; + } + + if (dev->out_ep->driver_data) { + dev->out_req->buf = NULL; + usb_ep_free_request(dev->out_ep, dev->out_req); + usb_ep_disable(dev->out_ep); + dev->out_ep->driver_data = NULL; + } + + if (dev->int_ep->driver_data) { + usb_ep_disable(dev->int_ep); + dev->int_ep->driver_data = NULL; + } +} + +static int thor_eps_setup(struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct thor_dev *dev = thor_func->dev; + struct usb_endpoint_descriptor *d; + struct usb_request *req; + struct usb_ep *ep; + int result; + + ep = dev->in_ep; + d = ep_desc(gadget, &hs_in_desc, &fs_in_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + if (result) + goto exit; + + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (!req) { + usb_ep_disable(ep); + result = -EIO; + goto exit; + } + + dev->in_req = req; + ep = dev->out_ep; + d = ep_desc(gadget, &hs_out_desc, &fs_out_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + if (result) + goto exit; + + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (!req) { + usb_ep_disable(ep); + result = -EIO; + goto exit; + } + + dev->out_req = req; + /* ACM control EP */ + ep = dev->int_ep; + ep->driver_data = cdev; /* claim */ + + exit: + return result; +} + +static int thor_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct thor_dev *dev = thor_func->dev; + int result; + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, intf, alt); + + switch (intf) { + case 0: + debug("ACM INTR interface\n"); + break; + case 1: + debug("Communication Data interface\n"); + result = thor_eps_setup(f); + if (result) + error("%s: EPs setup failed!", __func__); + dev->configuration_done = 1; + break; + } + + return 0; +} + +static int thor_func_init(struct usb_configuration *c) +{ + struct f_thor *f_thor; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + f_thor = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_thor)); + if (!f_thor) + return -ENOMEM; + + memset(f_thor, 0, sizeof(*f_thor)); + + f_thor->usb_function.name = "f_thor"; + f_thor->usb_function.bind = thor_func_bind; + f_thor->usb_function.unbind = thor_unbind; + f_thor->usb_function.setup = thor_func_setup; + f_thor->usb_function.set_alt = thor_func_set_alt; + f_thor->usb_function.disable = thor_func_disable; + + status = usb_add_function(c, &f_thor->usb_function); + if (status) + free(f_thor); + + return status; +} + +int thor_add(struct usb_configuration *c) +{ + debug("%s:\n", __func__); + return thor_func_init(c); +} diff --git a/drivers/usb/gadget/f_thor.h b/drivers/usb/gadget/f_thor.h new file mode 100644 index 0000000..04ee9a2 --- /dev/null +++ b/drivers/usb/gadget/f_thor.h @@ -0,0 +1,124 @@ +/* + * f_thor.h - USB TIZEN THOR - internal gadget definitions + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _USB_THOR_H_ +#define _USB_THOR_H_ + +#include <linux/compiler.h> +#include <asm/sizes.h> + +/* THOR Composite Gadget */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +/* ********************************************************** */ +/* THOR protocol definitions */ +/* ********************************************************** */ + +/* + * Attribute Vendor descriptor - necessary to prevent ZLP transmission + * from Windows XP HOST PC + */ +struct usb_cdc_attribute_vendor_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u16 DAUType; + __u16 DAULength; + __u8 DAUValue; +} __packed; + +#define VER_PROTOCOL_MAJOR 4 +#define VER_PROTOCOL_MINOR 0 + +enum rqt { + RQT_INFO = 200, + RQT_CMD, + RQT_DL, + RQT_UL, +}; + +enum rqt_data { + /* RQT_INFO */ + RQT_INFO_VER_PROTOCOL = 1, + RQT_INIT_VER_HW, + RQT_INIT_VER_BOOT, + RQT_INIT_VER_KERNEL, + RQT_INIT_VER_PLATFORM, + RQT_INIT_VER_CSC, + + /* RQT_CMD */ + RQT_CMD_REBOOT = 1, + RQT_CMD_POWEROFF, + RQT_CMD_EFSCLEAR, + + /* RQT_DL */ + RQT_DL_INIT = 1, + RQT_DL_FILE_INFO, + RQT_DL_FILE_START, + RQT_DL_FILE_END, + RQT_DL_EXIT, + + /* RQT_UL */ + RQT_UL_INIT = 1, + RQT_UL_START, + RQT_UL_END, + RQT_UL_EXIT, +}; + +struct rqt_box { /* total: 256B */ + s32 rqt; /* request id */ + s32 rqt_data; /* request data id */ + s32 int_data[14]; /* int data */ + char str_data[5][32]; /* string data */ + char md5[32]; /* md5 checksum */ +} __packed; + +struct rsp_box { /* total: 128B */ + s32 rsp; /* response id (= request id) */ + s32 rsp_data; /* response data id */ + s32 ack; /* ack */ + s32 int_data[5]; /* int data */ + char str_data[3][32]; /* string data */ +} __packed; + +struct data_rsp_box { /* total: 8B */ + s32 ack; /* response id (= request id) */ + s32 count; /* response data id */ +} __packed; + +enum { + FILE_TYPE_NORMAL, + FILE_TYPE_PIT, +}; + +struct thor_dev { + struct usb_gadget *gadget; + struct usb_request *req; /* EP0 -> control responses */ + + /* IN/OUT EP's and correspoinding requests */ + struct usb_ep *in_ep, *out_ep, *int_ep; + struct usb_request *in_req, *out_req; + + /* Control flow variables */ + unsigned char configuration_done; + unsigned char rxdata; + unsigned char txdata; +}; + +struct f_thor { + struct usb_function usb_function; + struct thor_dev *dev; +}; + +#define F_NAME_BUF_SIZE 32 +#define THOR_PACKET_SIZE SZ_1M /* 1 MiB */ +#define THOR_STORE_UNIT_SIZE SZ_32M /* 32 MiB */ +#endif /* _USB_THOR_H_ */ diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 40868c0..43f413a 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -16,6 +16,7 @@ #include <g_dnl.h> #include <usb_mass_storage.h> #include <dfu.h> +#include <thor.h> #include "gadget_chips.h" #include "composite.c" @@ -79,6 +80,8 @@ static int g_dnl_unbind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; + free(cdev->config); + cdev->config = NULL; debug("%s: calling usb_gadget_disconnect for " "controller '%s'\n", shortname, gadget->name); usb_gadget_disconnect(gadget); @@ -99,26 +102,34 @@ static int g_dnl_do_config(struct usb_configuration *c) ret = dfu_add(c); else if (!strcmp(s, "usb_dnl_ums")) ret = fsg_add(c); + else if (!strcmp(s, "usb_dnl_thor")) + ret = thor_add(c); return ret; } static int g_dnl_config_register(struct usb_composite_dev *cdev) { - static struct usb_configuration config = { - .label = "usb_dnload", - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bConfigurationValue = CONFIGURATION_NUMBER, - .iConfiguration = STRING_USBDOWN, + struct usb_configuration *config; + const char *name = "usb_dnload"; - .bind = g_dnl_do_config, - }; + config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); + if (!config) + return -ENOMEM; - return usb_add_config(cdev, &config); + memset(config, 0, sizeof(*config)); + + config->label = name; + config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; + config->bConfigurationValue = CONFIGURATION_NUMBER; + config->iConfiguration = STRING_USBDOWN; + config->bind = g_dnl_do_config; + + return usb_add_config(cdev, config); } __weak -int g_dnl_bind_fixup(struct usb_device_descriptor *dev) +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) { return 0; } @@ -145,7 +156,7 @@ static int g_dnl_bind(struct usb_composite_dev *cdev) g_dnl_string_defs[1].id = id; device_desc.iProduct = id; - g_dnl_bind_fixup(&device_desc); + g_dnl_bind_fixup(&device_desc, cdev->driver->name); ret = g_dnl_config_register(cdev); if (ret) goto error; @@ -183,8 +194,8 @@ static struct usb_composite_driver g_dnl_driver = { int g_dnl_register(const char *type) { - /* We only allow "dfu" atm, so 3 should be enough */ - static char name[sizeof(shortname) + 3]; + /* The largest function name is 4 */ + static char name[sizeof(shortname) + 4]; int ret; if (!strcmp(type, "dfu")) { @@ -193,6 +204,9 @@ int g_dnl_register(const char *type) } else if (!strcmp(type, "ums")) { strcpy(name, shortname); strcat(name, type); + } else if (!strcmp(type, "thor")) { + strcpy(name, shortname); + strcat(name, type); } else { printf("%s: unknown command: %s\n", __func__, type); return -EINVAL; diff --git a/drivers/usb/gadget/mpc8xx_udc.c b/drivers/usb/gadget/mpc8xx_udc.c index 0207d39..7f72972 100644 --- a/drivers/usb/gadget/mpc8xx_udc.c +++ b/drivers/usb/gadget/mpc8xx_udc.c @@ -47,6 +47,7 @@ #include <commproc.h> #include <usbdevice.h> #include <usb/mpc8xx_udc.h> +#include <usb/udc.h> #include "ep0.h" diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c index e6700a8..da41738 100644 --- a/drivers/usb/gadget/mv_udc.c +++ b/drivers/usb/gadget/mv_udc.c @@ -13,13 +13,16 @@ #include <config.h> #include <net.h> #include <malloc.h> +#include <asm/byteorder.h> +#include <asm/errno.h> #include <asm/io.h> +#include <asm/unaligned.h> #include <linux/types.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> #include <usb/mv_udc.h> - -#if CONFIG_USB_MAX_CONTROLLER_COUNT > 1 -#error This driver only supports one single controller. -#endif +#include "../host/ehci.h" +#include "mv_udc.h" /* * Check if the system has too long cachelines. If the cachelines are @@ -107,6 +110,7 @@ static struct mv_drv controller = { .gadget = { .name = "mv_udc", .ops = &mv_udc_ops, + .is_dualspeed = 1, }, }; @@ -210,12 +214,10 @@ static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req) return; } -static void ep_enable(int num, int in) +static void ep_enable(int num, int in, int maxpacket) { - struct ept_queue_head *head; struct mv_udc *udc = (struct mv_udc *)controller.ctrl->hcor; unsigned n; - head = mv_get_qh(num, in); n = readl(&udc->epctrl[num]); if (in) @@ -224,7 +226,9 @@ static void ep_enable(int num, int in) n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK); if (num != 0) { - head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) | CONFIG_ZLT; + struct ept_queue_head *head = mv_get_qh(num, in); + + head->config = CONFIG_MAX_PKT(maxpacket) | CONFIG_ZLT; mv_flush_qh(num); } writel(n, &udc->epctrl[num]); @@ -237,17 +241,33 @@ static int mv_ep_enable(struct usb_ep *ep, int num, in; num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (desc->bEndpointAddress & USB_DIR_IN) != 0; - ep_enable(num, in); mv_ep->desc = desc; + + if (num) { + int max = get_unaligned_le16(&desc->wMaxPacketSize); + + if ((max > 64) && (controller.gadget.speed == USB_SPEED_FULL)) + max = 64; + if (ep->maxpacket != max) { + DBG("%s: from %d to %d\n", __func__, + ep->maxpacket, max); + ep->maxpacket = max; + } + } + ep_enable(num, in, ep->maxpacket); + DBG("%s: num=%d maxpacket=%d\n", __func__, num, ep->maxpacket); return 0; } static int mv_ep_disable(struct usb_ep *ep) { + struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); + + mv_ep->desc = NULL; return 0; } -static int mv_bounce(struct mv_ep *ep) +static int mv_bounce(struct mv_ep *ep, int in) { uint32_t addr = (uint32_t)ep->req.buf; uint32_t ba; @@ -276,8 +296,8 @@ align: if (!ep->b_buf) return -ENOMEM; } - - memcpy(ep->b_buf, ep->req.buf, ep->req.length); + if (in) + memcpy(ep->b_buf, ep->req.buf, ep->req.length); flush: ba = (uint32_t)ep->b_buf; @@ -286,29 +306,25 @@ flush: return 0; } -static void mv_debounce(struct mv_ep *ep) +static void mv_debounce(struct mv_ep *ep, int in) { uint32_t addr = (uint32_t)ep->req.buf; uint32_t ba = (uint32_t)ep->b_buf; + if (in) { + if (addr == ba) + return; /* not a bounce */ + goto free; + } invalidate_dcache_range(ba, ba + ep->b_len); - /* Input buffer address is not aligned. */ - if (addr & (ARCH_DMA_MINALIGN - 1)) - goto copy; + if (addr == ba) + return; /* not a bounce */ - /* Input buffer length is not aligned. */ - if (ep->req.length & (ARCH_DMA_MINALIGN - 1)) - goto copy; - - /* The buffer is well aligned, only invalidate cache. */ - return; - -copy: memcpy(ep->req.buf, ep->b_buf, ep->req.length); - +free: /* Large payloads use allocated buffer, free it. */ - if (ep->req.length > 64) + if (ep->b_buf != ep->b_fast) free(ep->b_buf); } @@ -326,7 +342,7 @@ static int mv_ep_queue(struct usb_ep *ep, head = mv_get_qh(num, in); len = req->length; - ret = mv_bounce(mv_ep); + ret = mv_bounce(mv_ep, in); if (ret) return ret; @@ -334,21 +350,20 @@ static int mv_ep_queue(struct usb_ep *ep, item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; item->page0 = (uint32_t)mv_ep->b_buf; item->page1 = ((uint32_t)mv_ep->b_buf & 0xfffff000) + 0x1000; + mv_flush_qtd(num); head->next = (unsigned) item; head->info = 0; DBG("ept%d %s queue len %x, buffer %p\n", num, in ? "in" : "out", len, mv_ep->b_buf); + mv_flush_qh(num); if (in) bit = EPT_TX(num); else bit = EPT_RX(num); - mv_flush_qh(num); - mv_flush_qtd(num); - writel(bit, &udc->epprime); return 0; @@ -366,14 +381,13 @@ static void handle_ep_complete(struct mv_ep *ep) mv_invalidate_qtd(num); if (item->info & 0xff) - printf("EP%d/%s FAIL nfo=%x pg0=%x\n", - num, in ? "in" : "out", item->info, item->page0); + printf("EP%d/%s FAIL info=%x pg0=%x\n", + num, in ? "in" : "out", item->info, item->page0); len = (item->info >> 16) & 0x7fff; - - mv_debounce(ep); - ep->req.length -= len; + mv_debounce(ep, in); + DBG("ept%d %s complete %x\n", num, in ? "in" : "out", len); ep->req.complete(&ep->ep, &ep->req); @@ -411,14 +425,16 @@ static void handle_setup(void) if ((r.wValue == 0) && (r.wLength == 0)) { req->length = 0; for (i = 0; i < NUM_ENDPOINTS; i++) { - if (!controller.ep[i].desc) + struct mv_ep *ep = &controller.ep[i]; + + if (!ep->desc) continue; - num = controller.ep[i].desc->bEndpointAddress + num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - in = (controller.ep[i].desc->bEndpointAddress + in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; if ((num == _num) && (in == _in)) { - ep_enable(num, in); + ep_enable(num, in, ep->ep.maxpacket); usb_ep_queue(controller.gadget.ep0, req, 0); break; @@ -502,15 +518,19 @@ void udc_irq(void) DBG("-- suspend --\n"); if (n & STS_PCI) { - DBG("-- portchange --\n"); + int max = 64; + int speed = USB_SPEED_FULL; + bit = (readl(&udc->portsc) >> 26) & 3; + DBG("-- portchange %x %s\n", bit, (bit == 2) ? "High" : "Full"); if (bit == 2) { - controller.gadget.speed = USB_SPEED_HIGH; - for (i = 1; i < NUM_ENDPOINTS && n; i++) - if (controller.ep[i].desc) - controller.ep[i].ep.maxpacket = 512; - } else { - controller.gadget.speed = USB_SPEED_FULL; + speed = USB_SPEED_HIGH; + max = 512; + } + controller.gadget.speed = speed; + for (i = 1; i < NUM_ENDPOINTS; i++) { + if (controller.ep[i].ep.maxpacket > max) + controller.ep[i].ep.maxpacket = max; } } @@ -626,6 +646,7 @@ static int mvudc_probe(void) free(controller.epts); return -ENOMEM; } + memset(controller.items_mem, 0, ilist_sz); for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { /* @@ -688,7 +709,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) return -EINVAL; - ret = usb_lowlevel_init(0, (void **)&controller.ctrl); + ret = usb_lowlevel_init(0, USB_INIT_DEVICE, (void **)&controller.ctrl); if (ret) return ret; diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h new file mode 100644 index 0000000..c7d8b33 --- /dev/null +++ b/drivers/usb/gadget/mv_udc.h @@ -0,0 +1,115 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * + * Licensed under the GPL-2 or later. + */ +#ifndef __GADGET__MV_UDC_H__ +#define __GADGET__MV_UDC_H__ + +#define NUM_ENDPOINTS 6 + +struct mv_udc { +#define MICRO_8FRAME 0x8 +#define USBCMD_ITC(x) ((((x) > 0xff) ? 0xff : x) << 16) +#define USBCMD_FS2 (1 << 15) +#define USBCMD_RST (1 << 1) +#define USBCMD_RUN (1) + u32 usbcmd; /* 0x140 */ +#define STS_SLI (1 << 8) +#define STS_URI (1 << 6) +#define STS_PCI (1 << 2) +#define STS_UEI (1 << 1) +#define STS_UI (1 << 0) + u32 usbsts; /* 0x144 */ + u32 pad1[3]; + u32 devaddr; /* 0x154 */ + u32 epinitaddr; /* 0x158 */ + u32 pad2[10]; +#define PTS_ENABLE 2 +#define PTS(x) (((x) & 0x3) << 30) +#define PFSC (1 << 24) + u32 portsc; /* 0x184 */ + u32 pad3[8]; +#define USBMODE_DEVICE 2 + u32 usbmode; /* 0x1a8 */ + u32 epstat; /* 0x1ac */ +#define EPT_TX(x) (1 << (((x) & 0xffff) + 16)) +#define EPT_RX(x) (1 << ((x) & 0xffff)) + u32 epprime; /* 0x1b0 */ + u32 epflush; /* 0x1b4 */ + u32 pad4; + u32 epcomp; /* 0x1bc */ +#define CTRL_TXE (1 << 23) +#define CTRL_TXR (1 << 22) +#define CTRL_RXE (1 << 7) +#define CTRL_RXR (1 << 6) +#define CTRL_TXT_BULK (2 << 18) +#define CTRL_RXT_BULK (2 << 2) + u32 epctrl[16]; /* 0x1c0 */ +}; + +struct mv_ep { + struct usb_ep ep; + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + + struct usb_request req; + uint8_t *b_buf; + uint32_t b_len; + uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN); +}; + +struct mv_drv { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct ehci_ctrl *ctrl; + struct ept_queue_head *epts; + struct ept_queue_item *items[2 * NUM_ENDPOINTS]; + uint8_t *items_mem; + struct mv_ep ep[NUM_ENDPOINTS]; +}; + +struct ept_queue_head { + unsigned config; + unsigned current; /* read-only */ + + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved_0; + + unsigned char setup_data[8]; + + unsigned reserved_1; + unsigned reserved_2; + unsigned reserved_3; + unsigned reserved_4; +}; + +#define CONFIG_MAX_PKT(n) ((n) << 16) +#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ +#define CONFIG_IOS (1 << 15) /* IRQ on setup */ + +struct ept_queue_item { + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved; +}; + +#define TERMINATE 1 +#define INFO_BYTES(n) ((n) << 16) +#define INFO_IOC (1 << 15) +#define INFO_ACTIVE (1 << 7) +#define INFO_HALTED (1 << 6) +#define INFO_BUFFER_ERROR (1 << 5) +#define INFO_TX_ERROR (1 << 3) +#endif diff --git a/drivers/usb/gadget/omap1510_udc.c b/drivers/usb/gadget/omap1510_udc.c index 8553fe5..bdc1b88 100644 --- a/drivers/usb/gadget/omap1510_udc.c +++ b/drivers/usb/gadget/omap1510_udc.c @@ -20,6 +20,7 @@ #endif #include <usbdevice.h> #include <usb/omap1510_udc.h> +#include <usb/udc.h> #include "ep0.h" diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 05d1b56..733558d 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -16,6 +16,7 @@ #include <asm/arch/hardware.h> #include <asm/io.h> #include <usb/pxa27x_udc.h> +#include <usb/udc.h> #include "ep0.h" diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..1cbf8f6 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,8 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num] - + DMA_BUFFER_SIZE); + + ROUND(ep->ep.maxpacket, + CONFIG_SYS_CACHELINE_SIZE)); if (length == 0) pktcnt = 1; |