diff options
Diffstat (limited to 'drivers/usb/gadget')
| -rw-r--r-- | drivers/usb/gadget/Makefile | 59 | ||||
| -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_mass_storage.c | 67 | ||||
| -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 | 58 | ||||
| -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 | ||||
| -rw-r--r-- | drivers/usb/gadget/storage_common.c | 27 | 
14 files changed, 1431 insertions, 148 deletions
| diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1590c4a75..f52d3f450 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -5,58 +5,37 @@  # SPDX-License-Identifier:	GPL-2.0+  # -include $(TOPDIR)/config.mk - -LIB	:= $(obj)libusb_gadget.o -  # if defined(CONFIG_USB_GADGET) || defined(CONFIG_USB_ETHER)  #   Everytime you forget how crufty makefiles can get things like  #   this remind you...  ifneq (,$(CONFIG_USB_GADGET)$(CONFIG_USB_ETHER)) -COBJS-y += epautoconf.o config.o usbstring.o +obj-y += epautoconf.o config.o usbstring.o  endif  # new USB gadget layer dependencies  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_USBDOWNLOAD_GADGET) += g_dnl.o -COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o -COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o +obj-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o +obj-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +obj-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +obj-$(CONFIG_THOR_FUNCTION) += f_thor.o +obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o +obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o +obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o  endif  ifdef CONFIG_USB_ETHER -COBJS-y += ether.o -COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o -COBJS-$(CONFIG_MV_UDC)	+= mv_udc.o -COBJS-$(CONFIG_CPU_PXA25X) += pxa25x_udc.o +obj-y += ether.o +obj-$(CONFIG_USB_ETH_RNDIS) += rndis.o +obj-$(CONFIG_MV_UDC)	+= mv_udc.o +obj-$(CONFIG_CPU_PXA25X) += pxa25x_udc.o  else  # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE  ifdef CONFIG_USB_DEVICE -COBJS-y += core.o -COBJS-y += ep0.o -COBJS-$(CONFIG_DW_UDC) += designware_udc.o -COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o -COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o -COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o -COBJS-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o +obj-y += core.o +obj-y += ep0.o +obj-$(CONFIG_DW_UDC) += designware_udc.o +obj-$(CONFIG_OMAP1510) += omap1510_udc.o +obj-$(CONFIG_OMAP1610) += omap1510_udc.o +obj-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o +obj-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o  endif  endif - -COBJS	:= $(COBJS-y) -SRCS	:= $(COBJS:.o=.c) -OBJS	:= $(addprefix $(obj),$(COBJS)) - -all:	$(LIB) - -$(LIB):	$(obj).depend $(OBJS) -	$(call cmd_link_o_target, $(OBJS)) - -######################################################################### - -# defines $(obj).depend target -include $(SRCTREE)/rules.mk - -sinclude $(obj).depend - -######################################################################### diff --git a/drivers/usb/gadget/designware_udc.c b/drivers/usb/gadget/designware_udc.c index 1aab31bba..b7c10384a 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 700d5fbfb..cc6cc1f32 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_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 6ecdea3e1..b1fe8bd3a 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -243,6 +243,7 @@  #include <config.h>  #include <malloc.h>  #include <common.h> +#include <usb.h>  #include <linux/err.h>  #include <linux/usb/ch9.h> @@ -441,7 +442,7 @@ static void set_bulk_out_req_length(struct fsg_common *common,  /*-------------------------------------------------------------------------*/ -struct ums_board_info			*ums_info; +struct ums *ums;  struct fsg_common *the_fsg_common;  static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) @@ -675,6 +676,18 @@ static int sleep_thread(struct fsg_common *common)  			k++;  		} +		if (k == 10) { +			/* Handle CTRL+C */ +			if (ctrlc()) +				return -EPIPE; +#ifdef CONFIG_USB_CABLE_CHECK +			/* Check cable connection */ +			if (!usb_cable_connected()) +				return -EIO; +#endif +			k = 0; +		} +  		usb_gadget_handle_interrupts();  	}  	common->thread_wakeup_needed = 0; @@ -757,14 +770,14 @@ static int do_read(struct fsg_common *common)  		}  		/* Perform the read */ -		nread = 0; -		rc = ums_info->read_sector(&(ums_info->ums_dev), -					   file_offset / SECTOR_SIZE, -					   amount / SECTOR_SIZE, -					   (char __user *)bh->buf); -		if (rc) +		rc = ums->read_sector(ums, +				      file_offset / SECTOR_SIZE, +				      amount / SECTOR_SIZE, +				      (char __user *)bh->buf); +		if (!rc)  			return -EIO; -		nread = amount; + +		nread = rc * SECTOR_SIZE;  		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,  				(unsigned long long) file_offset, @@ -931,13 +944,13 @@ static int do_write(struct fsg_common *common)  			amount = bh->outreq->actual;  			/* Perform the write */ -			rc = ums_info->write_sector(&(ums_info->ums_dev), +			rc = ums->write_sector(ums,  					       file_offset / SECTOR_SIZE,  					       amount / SECTOR_SIZE,  					       (char __user *)bh->buf); -			if (rc) +			if (!rc)  				return -EIO; -			nwritten = amount; +			nwritten = rc * SECTOR_SIZE;  			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,  					(unsigned long long) file_offset, @@ -959,6 +972,8 @@ static int do_write(struct fsg_common *common)  			/* If an error occurred, report it and its position */  			if (nwritten < amount) { +				printf("nwritten:%d amount:%d\n", nwritten, +				       amount);  				curlun->sense_data = SS_WRITE_ERROR;  				curlun->info_valid = 1;  				break; @@ -1045,14 +1060,13 @@ static int do_verify(struct fsg_common *common)  		}  		/* Perform the read */ -		nread = 0; -		rc = ums_info->read_sector(&(ums_info->ums_dev), -					   file_offset / SECTOR_SIZE, -					   amount / SECTOR_SIZE, -					   (char __user *)bh->buf); -		if (rc) +		rc = ums->read_sector(ums, +				      file_offset / SECTOR_SIZE, +				      amount / SECTOR_SIZE, +				      (char __user *)bh->buf); +		if (!rc)  			return -EIO; -		nread = amount; +		nread = rc * SECTOR_SIZE;  		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,  				(unsigned long long) file_offset, @@ -1100,7 +1114,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)  	buf[4] = 31;		/* Additional length */  				/* No special options */  	sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id , -			ums_info->name, (u16) 0xffff); +			ums->name, (u16) 0xffff);  	return 36;  } @@ -2386,6 +2400,7 @@ static void handle_exception(struct fsg_common *common)  int fsg_main_thread(void *common_)  { +	int ret;  	struct fsg_common	*common = the_fsg_common;  	/* The main loop */  	do { @@ -2395,12 +2410,16 @@ int fsg_main_thread(void *common_)  		}  		if (!common->running) { -			sleep_thread(common); +			ret = sleep_thread(common); +			if (ret) +				return ret; +  			continue;  		} -		if (get_next_command(common)) -			continue; +		ret = get_next_command(common); +		if (ret) +			return ret;  		if (!exception_in_progress(common))  			common->state = FSG_STATE_DATA_PHASE; @@ -2753,9 +2772,9 @@ int fsg_add(struct usb_configuration *c)  	return fsg_bind_config(c->cdev, c, fsg_common);  } -int fsg_init(struct ums_board_info *ums) +int fsg_init(struct ums *ums_dev)  { -	ums_info = ums; +	ums = ums_dev;  	return 0;  } diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c new file mode 100644 index 000000000..c4c990915 --- /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 000000000..04ee9a243 --- /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 40868c034..8dc3d9f8a 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" @@ -32,6 +33,9 @@  #define STRING_PRODUCT 2  /* Index of String Descriptor describing this configuration */  #define STRING_USBDOWN 2 +/* Index of String serial */ +#define STRING_SERIAL  3 +#define MAX_STRING_SERIAL	32  /* Number of supported configurations */  #define CONFIGURATION_NUMBER 1 @@ -39,8 +43,16 @@  static const char shortname[] = "usb_dnl_";  static const char product[] = "USB download gadget"; +static char g_dnl_serial[MAX_STRING_SERIAL];  static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER; +void g_dnl_set_serialnumber(char *s) +{ +	memset(g_dnl_serial, 0, MAX_STRING_SERIAL); +	if (strlen(s) < MAX_STRING_SERIAL) +		strncpy(g_dnl_serial, s, strlen(s)); +} +  static struct usb_device_descriptor device_desc = {  	.bLength = sizeof device_desc,  	.bDescriptorType = USB_DT_DEVICE, @@ -52,6 +64,7 @@ static struct usb_device_descriptor device_desc = {  	.idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM),  	.idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM),  	.iProduct = STRING_PRODUCT, +	.iSerialNumber = STRING_SERIAL,  	.bNumConfigurations = 1,  }; @@ -62,6 +75,7 @@ static struct usb_device_descriptor device_desc = {  static struct usb_string g_dnl_string_defs[] = {  	{.s = manufacturer},  	{.s = product}, +	{.s = g_dnl_serial},  	{ }		/* end of list */  }; @@ -79,6 +93,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 +115,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 +169,14 @@ 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); +	id = usb_string_id(cdev); +	if (id < 0) +		return id; + +	g_dnl_string_defs[2].id = id; +	device_desc.iSerialNumber = id; + +	g_dnl_bind_fixup(&device_desc, cdev->driver->name);  	ret = g_dnl_config_register(cdev);  	if (ret)  		goto error; @@ -183,8 +214,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 +224,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 0207d391a..7f72972dc 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 e6700a80f..da4173865 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 000000000..c7d8b3398 --- /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 8553fe539..bdc1b886f 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 05d1b5666..733558def 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 d7af5e903..1cbf8f60a 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; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 866e7c7b8..02803df23 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -275,7 +275,6 @@ struct rw_semaphore { int i; };  #define ETOOSMALL	525  #include <usb_mass_storage.h> -extern struct ums_board_info		*ums_info;  /*-------------------------------------------------------------------------*/ @@ -573,36 +572,16 @@ static struct usb_gadget_strings	fsg_stringtab = {  static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)  {  	int				ro; -	int				rc = -EINVAL; -	loff_t				size; -	loff_t				num_sectors; -	loff_t				min_sectors;  	/* R/W if we can, R/O if we must */  	ro = curlun->initially_ro; -	ums_info->get_capacity(&(ums_info->ums_dev), &size); -	if (size < 0) { -		printf("unable to find file size: %s\n", filename); -		rc = (int) size; -		goto out; -	} -	num_sectors = size >> 9;	/* File size in 512-byte blocks */ -	min_sectors = 1; -	if (num_sectors < min_sectors) { -		printf("file too small: %s\n", filename); -		rc = -ETOOSMALL; -		goto out; -	} -  	curlun->ro = ro; -	curlun->file_length = size; -	curlun->num_sectors = num_sectors; +	curlun->file_length = ums->num_sectors << 9; +	curlun->num_sectors = ums->num_sectors;  	debug("open backing file: %s\n", filename); -	rc = 0; -out: -	return rc; +	return 0;  }  static void fsg_lun_close(struct fsg_lun *curlun) |