diff options
| author | Tom Rini <trini@ti.com> | 2013-03-18 15:33:47 -0400 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2013-03-18 15:33:47 -0400 | 
| commit | 3c47f2f4871c345c20b9d986b11fec550ef6cc9f (patch) | |
| tree | 267c6eedacaa02cc8c7096f67bedb9d17fe3e21d | |
| parent | 0ce033d2582129243aca10d3072a221386bbba44 (diff) | |
| parent | ae003d057077d792c1f2131753a7596c94e0bba4 (diff) | |
| download | olio-uboot-2014.01-3c47f2f4871c345c20b9d986b11fec550ef6cc9f.tar.xz olio-uboot-2014.01-3c47f2f4871c345c20b9d986b11fec550ef6cc9f.zip | |
Merge branch 'master' of git://git.denx.de/u-boot-usb
| -rw-r--r-- | board/samsung/trats/trats.c | 63 | ||||
| -rw-r--r-- | common/Makefile | 1 | ||||
| -rw-r--r-- | common/cmd_dfu.c | 5 | ||||
| -rw-r--r-- | common/cmd_usb_mass_storage.c | 86 | ||||
| -rw-r--r-- | drivers/dfu/dfu_mmc.c | 31 | ||||
| -rw-r--r-- | drivers/serial/usbtty.c | 2 | ||||
| -rw-r--r-- | drivers/usb/eth/smsc95xx.c | 4 | ||||
| -rw-r--r-- | drivers/usb/gadget/Makefile | 10 | ||||
| -rw-r--r-- | drivers/usb/gadget/composite.c | 19 | ||||
| -rw-r--r-- | drivers/usb/gadget/f_dfu.c | 3 | ||||
| -rw-r--r-- | drivers/usb/gadget/f_mass_storage.c | 2793 | ||||
| -rw-r--r-- | drivers/usb/gadget/g_dnl.c | 6 | ||||
| -rw-r--r-- | drivers/usb/gadget/storage_common.c | 653 | ||||
| -rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-exynos.c | 51 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 318 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-pci.c | 60 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-spear.c | 59 | ||||
| -rw-r--r-- | drivers/usb/host/ehci.h | 6 | ||||
| -rw-r--r-- | include/configs/coreboot.h | 17 | ||||
| -rw-r--r-- | include/configs/trats.h | 5 | ||||
| -rw-r--r-- | include/usb_mass_storage.h | 55 | ||||
| -rw-r--r-- | include/usbdevice.h | 4 | 
23 files changed, 4203 insertions, 49 deletions
| diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index 88d193de2..920764664 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -42,6 +42,7 @@  #include <power/max8997_muic.h>  #include <power/battery.h>  #include <power/max17042_fg.h> +#include <usb_mass_storage.h>  #include "setup.h" @@ -791,3 +792,65 @@ void init_panel_info(vidinfo_t *vid)  	setenv("lcdinfo", "lcd=s6e8ax0");  } + +#ifdef CONFIG_USB_GADGET_MASS_STORAGE +static int ums_read_sector(struct ums_device *ums_dev, +			   ulong start, lbaint_t blkcnt, void *buf) +{ +	if (ums_dev->mmc->block_dev.block_read(ums_dev->dev_num, +			start + ums_dev->offset, blkcnt, buf) != blkcnt) +		return -1; + +	return 0; +} + +static int ums_write_sector(struct ums_device *ums_dev, +			    ulong start, lbaint_t blkcnt, const void *buf) +{ +	if (ums_dev->mmc->block_dev.block_write(ums_dev->dev_num, +			start + ums_dev->offset, blkcnt, buf) != blkcnt) +		return -1; + +	return 0; +} + +static void ums_get_capacity(struct ums_device *ums_dev, +			     long long int *capacity) +{ +	long long int tmp_capacity; + +	tmp_capacity = (long long int) ((ums_dev->offset + ums_dev->part_size) +					* SECTOR_SIZE); +	*capacity = ums_dev->mmc->capacity - tmp_capacity; +} + +static struct ums_board_info ums_board = { +	.read_sector = ums_read_sector, +	.write_sector = ums_write_sector, +	.get_capacity = ums_get_capacity, +	.name = "TRATS UMS disk", +	.ums_dev = { +		.mmc = NULL, +		.dev_num = 0, +		.offset = 0, +		.part_size = 0. +	}, +}; + +struct ums_board_info *board_ums_init(unsigned int dev_num, unsigned int offset, +				      unsigned int part_size) +{ +	struct mmc *mmc; + +	mmc = find_mmc_device(dev_num); +	if (!mmc) +		return NULL; + +	ums_board.ums_dev.mmc = mmc; +	ums_board.ums_dev.dev_num = dev_num; +	ums_board.ums_dev.offset = offset; +	ums_board.ums_dev.part_size = part_size; + +	return &ums_board; +} +#endif diff --git a/common/Makefile b/common/Makefile index 08af1a8ac..1abf4ea79 100644 --- a/common/Makefile +++ b/common/Makefile @@ -179,6 +179,7 @@ COBJS-y += cmd_usb.o  COBJS-y += usb.o usb_hub.o  COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o  endif +COBJS-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o  COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o  COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o  COBJS-$(CONFIG_CMD_SPL) += cmd_spl.o diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c index 01d6b3a2d..83ef32497 100644 --- a/common/cmd_dfu.c +++ b/common/cmd_dfu.c @@ -50,12 +50,15 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  	if (ret)  		return CMD_RET_FAILURE; -	if (strcmp(argv[3], "list") == 0) { +	if (argc > 3 && strcmp(argv[3], "list") == 0) {  		dfu_show_entities();  		goto done;  	} +#ifdef CONFIG_TRATS  	board_usb_init(); +#endif +  	g_dnl_register(s);  	while (1) {  		if (ctrlc()) diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c new file mode 100644 index 000000000..87a5f2f3a --- /dev/null +++ b/common/cmd_usb_mass_storage.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <errno.h> +#include <common.h> +#include <command.h> +#include <g_dnl.h> +#include <usb_mass_storage.h> + +int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, +			       int argc, char * const argv[]) +{ +	char *ep; +	unsigned int dev_num = 0, offset = 0, part_size = 0; +	int rc; + +	struct ums_board_info *ums_info; +	static char *s = "ums"; + +	if (argc < 2) { +		printf("usage: ums <dev> - e.g. ums 0\n"); +		return 0; +	} + +	dev_num = (int)simple_strtoul(argv[1], &ep, 16); + +	if (dev_num) { +		puts("\nSet eMMC device to 0! - e.g. ums 0\n"); +		goto fail; +	} + +	board_usb_init(); +	ums_info = board_ums_init(dev_num, offset, part_size); + +	if (!ums_info) { +		printf("MMC: %d -> NOT available\n", dev_num); +		goto fail; +	} +	rc = fsg_init(ums_info); +	if (rc) { +		printf("cmd ums: fsg_init failed\n"); +		goto fail; +	} + +	g_dnl_register(s); + +	while (1) { +		/* Handle control-c and timeouts */ +		if (ctrlc()) { +			printf("The remote end did not respond in time.\n"); +			goto exit; +		} +		usb_gadget_handle_interrupts(); +		/* Check if USB cable has been detached */ +		if (fsg_main_thread(NULL) == EIO) +			goto exit; +	} +exit: +	g_dnl_unregister(); +	return 0; + +fail: +	return -1; +} + +U_BOOT_CMD(ums, CONFIG_SYS_MAXARGS, 1, do_usb_mass_storage, +	"Use the UMS [User Mass Storage]", +	"ums - User Mass Storage Gadget" +); diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 5d504dffd..083d74591 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -21,6 +21,7 @@  #include <common.h>  #include <malloc.h> +#include <errno.h>  #include <dfu.h>  enum dfu_mmc_op { @@ -153,6 +154,10 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)  int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)  { +	int dev, part; +	struct mmc *mmc; +	block_dev_desc_t *blk_dev; +	disk_partition_t partinfo;  	char *st;  	dfu->dev_type = DFU_DEV_MMC; @@ -166,8 +171,34 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)  		dfu->layout = DFU_FS_FAT;  	} else if (!strcmp(st, "ext4")) {  		dfu->layout = DFU_FS_EXT4; +	} else if (!strcmp(st, "part")) { + +		dfu->layout = DFU_RAW_ADDR; + +		dev = simple_strtoul(s, &s, 10); +		s++; +		part = simple_strtoul(s, &s, 10); + +		mmc = find_mmc_device(dev); +		if (mmc == NULL || mmc_init(mmc)) { +			printf("%s: could not find mmc device #%d!\n", __func__, dev); +			return -ENODEV; +		} + +		blk_dev = &mmc->block_dev; +		if (get_partition_info(blk_dev, part, &partinfo) != 0) { +			printf("%s: could not find partition #%d on mmc device #%d!\n", +					__func__, part, dev); +			return -ENODEV; +		} + +		dfu->data.mmc.lba_start = partinfo.start; +		dfu->data.mmc.lba_size = partinfo.size; +		dfu->data.mmc.lba_blk_size = partinfo.blksz; +  	} else {  		printf("%s: Memory layout (%s) not supported!\n", __func__, st); +		return -ENODEV;  	}  	if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index e47cb9a9e..148d1a6dd 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -63,7 +63,7 @@  /*   * Buffers to hold input and output data   */ -#define USBTTY_BUFFER_SIZE 256 +#define USBTTY_BUFFER_SIZE 2048  static circbuf_t usbtty_input;  static circbuf_t usbtty_output; diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index dc5ca6546..fd8f8a760 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -265,10 +265,6 @@ static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)  	do {  		smsc95xx_read_reg(dev, E2P_CMD, &val); -		if (!(val & E2P_CMD_LOADED_)) { -			debug("No EEPROM present\n"); -			return -1; -		}  		if (!(val & E2P_CMD_BUSY_))  			return 0;  		udelay(40); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 040eaba3b..e545b6be6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -25,15 +25,21 @@ 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 +endif +  # new USB gadget layer dependencies  ifdef CONFIG_USB_GADGET -COBJS-y += epautoconf.o config.o usbstring.o  COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o  COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o  COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o  endif  ifdef CONFIG_USB_ETHER -COBJS-y += ether.o epautoconf.o config.o usbstring.o +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 diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index ebb5131a9..2c5600ed5 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -859,6 +859,25 @@ unknown:  			if (&f->list == &cdev->config->functions)  				f = NULL;  			break; +		/* +		 * dfu-util (version 0.5) sets bmRequestType.Receipent = Device +		 * for non-standard request (w_value = 0x21, +		 * bRequest = GET_DESCRIPTOR in this case). +		 * When only one interface is registered (as it is done now), +		 * then this request shall be handled as it was requested for +		 * interface. +		 * +		 * In the below code it is checked if only one interface is +		 * present and proper function for it is extracted. Due to that +		 * function's setup (f->setup) is called to handle this +		 * special non-standard request. +		 */ +		case USB_RECIP_DEVICE: +			debug("cdev->config->next_interface_id: %d intf: %d\n", +			       cdev->config->next_interface_id, intf); +			if (cdev->config->next_interface_id == 1) +				f = cdev->config->interface[intf]; +			break;  		}  		if (f && f->setup) diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 10547e327..a322ae5eb 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -164,6 +164,9 @@ static void handle_getstatus(struct usb_request *req)  	/* send status response */  	dstat->bStatus = f_dfu->dfu_status; +	dstat->bwPollTimeout[0] = 0; +	dstat->bwPollTimeout[1] = 0; +	dstat->bwPollTimeout[2] = 0;  	dstat->bState = f_dfu->dfu_state;  	dstat->iString = 0;  } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c new file mode 100644 index 000000000..c28866f7d --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.c @@ -0,0 +1,2793 @@ +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + *                    Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions, and the following disclaimer, + *    without modification. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + *    to endorse or promote products derived from this software without + *    specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive.  In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs).  Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only.  Moreover, the + * function can indicate that LUN is removable and/or CD-ROM.  (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure.  It has the + * following fields: + * + *	nluns		Number of LUNs function have (anywhere from 1 + *				to FSG_MAX_LUNS which is 8). + *	luns		An array of LUN configuration values.  This + *				should be filled for each LUN that + *				function will include (ie. for "nluns" + *				LUNs).  Each element of the array has + *				the following fields: + *	->filename	The path to the backing file for the LUN. + *				Required if LUN is not marked as + *				removable. + *	->ro		Flag specifying access to the LUN shall be + *				read-only.  This is implied if CD-ROM + *				emulation is enabled as well as when + *				it was impossible to open "filename" + *				in R/W mode. + *	->removable	Flag specifying that LUN shall be indicated as + *				being removable. + *	->cdrom		Flag specifying that LUN shall be reported as + *				being a CD-ROM. + * + *	lun_name_format	A printf-like format for names of the LUN + *				devices.  This determines how the + *				directory in sysfs will be named. + *				Unless you are using several MSFs in + *				a single gadget (as opposed to single + *				MSF in many configurations) you may + *				leave it as NULL (in which case + *				"lun%d" will be used).  In the format + *				you can use "%d" to index LUNs for + *				MSF's with more than one LUN.  (Beware + *				that there is only one integer given + *				as an argument for the format and + *				specifying invalid format may cause + *				unspecified behaviour.) + *	thread_name	Name of the kernel thread process used by the + *				MSF.  You can safely set it to NULL + *				(in which case default "file-storage" + *				will be used). + * + *	vendor_name + *	product_name + *	release		Information used as a reply to INQUIRY + *				request.  To use default set to NULL, + *				NULL, 0xffff respectively.  The first + *				field should be 8 and the second 16 + *				characters or less. + * + *	can_stall	Set to permit function to halt bulk endpoints. + *				Disabled on some USB devices known not + *				to work correctly.  You should set it + *				to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified.  If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error).  The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN.  Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters.  If gadget using it + * decides to use it, the following module parameters will be + * available: + * + *	file=filename[,filename...] + *			Names of the files or block devices used for + *				backing storage. + *	ro=b[,b...]	Default false, boolean for read-only access. + *	removable=b[,b...] + *			Default true, boolean for removable media. + *	cdrom=b[,b...]	Default false, boolean for whether to emulate + *				a CD-ROM drive. + *	luns=N		Default N = number of filenames, number of + *				LUNs to support. + *	stall		Default determined according to the type of + *				USB device controller (usually true), + *				boolean to permit the driver to halt + *				bulk endpoints. + * + * The module parameters may be prefixed with some string.  You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed.  The memory requirement amounts to two 16K buffers, size + * configurable by a parameter.  Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints.  With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * + * The pathnames of the backing files and the ro settings are + * available in the attribute files "file" and "ro" in the lun<n> (or + * to be more precise in a directory which name comes from + * "lun_name_format" option!) subdirectory of the gadget's sysfs + * directory.  If the "removable" option is set, writing to these + * files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab.  Changes to the + * ro setting are not allowed when the medium is loaded or if CD-ROM + * emulation is being used. + * + * When a LUN receive an "eject" SCSI request (Start/Stop Unit), + * if the LUN is removable, the backing file is released to simulate + * ejection. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell.  The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + + +/* + *				Driver Design + * + * The MSF is fairly straightforward.  There is a main kernel + * thread that handles most of the work.  Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls.  Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction.  It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file.  This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example.  To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind().  But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead.  At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd).  In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering).  But it helps to think of the + * pipeline as being a long one.  Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol.  There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete.  Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows.  This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint.  However, under certain circumstances the + * Bulk-only specification requires a stall.  In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset.  Hopefully this + * will permit everything to work correctly.  Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests.  Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time.  During that delay the host might give up on + * the original ep0 request and issue a new one.  When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it.  So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change).  When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag.  Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long.  It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#include <config.h> +#include <malloc.h> +#include <common.h> + +#include <linux/err.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <usb_mass_storage.h> + +#include <asm/unaligned.h> +#include <linux/usb/gadget.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <usb/lin_gadget_compat.h> + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC	"Mass Storage Function" +#define FSG_DRIVER_VERSION	"2012/06/5" + +static const char fsg_string_interface[] = "Mass Storage"; + + +#define FSG_NO_INTR_EP 1 +#define FSG_NO_DEVICE_STRINGS    1 +#define FSG_NO_OTG               1 +#define FSG_NO_INTR_EP           1 + +#include "storage_common.c" + +/*-------------------------------------------------------------------------*/ + +#define GFP_ATOMIC ((gfp_t) 0) +#define PAGE_CACHE_SHIFT	12 +#define PAGE_CACHE_SIZE		(1 << PAGE_CACHE_SHIFT) +#define kthread_create(...)	__builtin_return_address(0) +#define wait_for_completion(...) do {} while (0) + +struct kref {int x; }; +struct completion {int x; }; + +inline void set_bit(int nr, volatile void *addr) +{ +	int	mask; +	unsigned int *a = (unsigned int *) addr; + +	a += nr >> 5; +	mask = 1 << (nr & 0x1f); +	*a |= mask; +} + +inline void clear_bit(int nr, volatile void *addr) +{ +	int	mask; +	unsigned int *a = (unsigned int *) addr; + +	a += nr >> 5; +	mask = 1 << (nr & 0x1f); +	*a &= ~mask; +} + +struct fsg_dev; +struct fsg_common; + +/* Data shared by all the FSG instances. */ +struct fsg_common { +	struct usb_gadget	*gadget; +	struct fsg_dev		*fsg, *new_fsg; + +	struct usb_ep		*ep0;		/* Copy of gadget->ep0 */ +	struct usb_request	*ep0req;	/* Copy of cdev->req */ +	unsigned int		ep0_req_tag; + +	struct fsg_buffhd	*next_buffhd_to_fill; +	struct fsg_buffhd	*next_buffhd_to_drain; +	struct fsg_buffhd	buffhds[FSG_NUM_BUFFERS]; + +	int			cmnd_size; +	u8			cmnd[MAX_COMMAND_SIZE]; + +	unsigned int		nluns; +	unsigned int		lun; +	struct fsg_lun          luns[FSG_MAX_LUNS]; + +	unsigned int		bulk_out_maxpacket; +	enum fsg_state		state;		/* For exception handling */ +	unsigned int		exception_req_tag; + +	enum data_direction	data_dir; +	u32			data_size; +	u32			data_size_from_cmnd; +	u32			tag; +	u32			residue; +	u32			usb_amount_left; + +	unsigned int		can_stall:1; +	unsigned int		free_storage_on_release:1; +	unsigned int		phase_error:1; +	unsigned int		short_packet_received:1; +	unsigned int		bad_lun_okay:1; +	unsigned int		running:1; + +	int			thread_wakeup_needed; +	struct completion	thread_notifier; +	struct task_struct	*thread_task; + +	/* Callback functions. */ +	const struct fsg_operations	*ops; +	/* Gadget's private data. */ +	void			*private_data; + +	const char *vendor_name;		/*  8 characters or less */ +	const char *product_name;		/* 16 characters or less */ +	u16 release; + +	/* Vendor (8 chars), product (16 chars), release (4 +	 * hexadecimal digits) and NUL byte */ +	char inquiry_string[8 + 16 + 4 + 1]; + +	struct kref		ref; +}; + +struct fsg_config { +	unsigned nluns; +	struct fsg_lun_config { +		const char *filename; +		char ro; +		char removable; +		char cdrom; +		char nofua; +	} luns[FSG_MAX_LUNS]; + +	/* Callback functions. */ +	const struct fsg_operations     *ops; +	/* Gadget's private data. */ +	void			*private_data; + +	const char *vendor_name;		/*  8 characters or less */ +	const char *product_name;		/* 16 characters or less */ + +	char			can_stall; +}; + +struct fsg_dev { +	struct usb_function	function; +	struct usb_gadget	*gadget;	/* Copy of cdev->gadget */ +	struct fsg_common	*common; + +	u16			interface_number; + +	unsigned int		bulk_in_enabled:1; +	unsigned int		bulk_out_enabled:1; + +	unsigned long		atomic_bitflags; +#define IGNORE_BULK_OUT		0 + +	struct usb_ep		*bulk_in; +	struct usb_ep		*bulk_out; +}; + + +static inline int __fsg_is_set(struct fsg_common *common, +			       const char *func, unsigned line) +{ +	if (common->fsg) +		return 1; +	ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); +	WARN_ON(1); +	return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ +	return container_of(f, struct fsg_dev, function); +} + + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ +	return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, +		struct fsg_buffhd *bh, unsigned int length) +{ +	unsigned int	rem; + +	bh->bulk_out_intended_length = length; +	rem = length % common->bulk_out_maxpacket; +	if (rem > 0) +		length += common->bulk_out_maxpacket - rem; +	bh->outreq->length = length; +} + +/*-------------------------------------------------------------------------*/ + +struct ums_board_info			*ums_info; +struct fsg_common *the_fsg_common; + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ +	const char	*name; + +	if (ep == fsg->bulk_in) +		name = "bulk-in"; +	else if (ep == fsg->bulk_out) +		name = "bulk-out"; +	else +		name = ep->name; +	DBG(fsg, "%s set halt\n", name); +	return usb_ep_set_halt(ep); +} + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_common *common) +{ +	common->thread_wakeup_needed = 1; +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ +	/* Do nothing if a higher-priority exception is already in progress. +	 * If a lower-or-equal priority exception is in progress, preempt it +	 * and notify the main thread by sending it a signal. */ +	if (common->state <= new_state) { +		common->exception_req_tag = common->ep0_req_tag; +		common->state = new_state; +		common->thread_wakeup_needed = 1; +	} +} + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ +	int	rc; + +	rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); +	common->ep0->driver_data = common; +	if (rc != 0 && rc != -ESHUTDOWN) { +		/* We can't do much more than wait for a reset */ +		WARNING(common, "error in submission: %s --> %d\n", +			common->ep0->name, rc); +	} +	return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ +	struct fsg_common	*common = ep->driver_data; +	struct fsg_buffhd	*bh = req->context; + +	if (req->status || req->actual != req->length) +		DBG(common, "%s --> %d, %u/%u\n", __func__, +				req->status, req->actual, req->length); +	if (req->status == -ECONNRESET)		/* Request was cancelled */ +		usb_ep_fifo_flush(ep); + +	/* Hold the lock while we update the request and buffer states */ +	bh->inreq_busy = 0; +	bh->state = BUF_STATE_EMPTY; +	wakeup_thread(common); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ +	struct fsg_common	*common = ep->driver_data; +	struct fsg_buffhd	*bh = req->context; + +	dump_msg(common, "bulk-out", req->buf, req->actual); +	if (req->status || req->actual != bh->bulk_out_intended_length) +		DBG(common, "%s --> %d, %u/%u\n", __func__, +				req->status, req->actual, +				bh->bulk_out_intended_length); +	if (req->status == -ECONNRESET)		/* Request was cancelled */ +		usb_ep_fifo_flush(ep); + +	/* Hold the lock while we update the request and buffer states */ +	bh->outreq_busy = 0; +	bh->state = BUF_STATE_FULL; +	wakeup_thread(common); +} + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers.  These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, +		const struct usb_ctrlrequest *ctrl) +{ +	struct fsg_dev		*fsg = fsg_from_func(f); +	struct usb_request	*req = fsg->common->ep0req; +	u16			w_index = le16_to_cpu(ctrl->wIndex); +	u16			w_value = le16_to_cpu(ctrl->wValue); +	u16			w_length = le16_to_cpu(ctrl->wLength); + +	if (!fsg_is_set(fsg->common)) +		return -EOPNOTSUPP; + +	switch (ctrl->bRequest) { + +	case USB_BULK_RESET_REQUEST: +		if (ctrl->bRequestType != +		    (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) +			break; +		if (w_index != fsg->interface_number || w_value != 0) +			return -EDOM; + +		/* Raise an exception to stop the current operation +		 * and reinitialize our state. */ +		DBG(fsg, "bulk reset request\n"); +		raise_exception(fsg->common, FSG_STATE_RESET); +		return DELAYED_STATUS; + +	case USB_BULK_GET_MAX_LUN_REQUEST: +		if (ctrl->bRequestType != +		    (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) +			break; +		if (w_index != fsg->interface_number || w_value != 0) +			return -EDOM; +		VDBG(fsg, "get max LUN\n"); +		*(u8 *) req->buf = fsg->common->nluns - 1; + +		/* Respond with data/status */ +		req->length = min((u16)1, w_length); +		return ep0_queue(fsg->common); +	} + +	VDBG(fsg, +	     "unknown class-specific control req " +	     "%02x.%02x v%04x i%04x l%u\n", +	     ctrl->bRequestType, ctrl->bRequest, +	     le16_to_cpu(ctrl->wValue), w_index, w_length); +	return -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, +		struct usb_request *req, int *pbusy, +		enum fsg_buffer_state *state) +{ +	int	rc; + +	if (ep == fsg->bulk_in) +		dump_msg(fsg, "bulk-in", req->buf, req->length); + +	*pbusy = 1; +	*state = BUF_STATE_BUSY; +	rc = usb_ep_queue(ep, req, GFP_KERNEL); +	if (rc != 0) { +		*pbusy = 0; +		*state = BUF_STATE_EMPTY; + +		/* We can't do much more than wait for a reset */ + +		/* Note: currently the net2280 driver fails zero-length +		 * submissions if DMA is enabled. */ +		if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && +						req->length == 0)) +			WARNING(fsg, "error in submission: %s --> %d\n", +					ep->name, rc); +	} +} + +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state)		\ +	if (fsg_is_set(common))						\ +		start_transfer((common)->fsg, (common)->fsg->ep_name,	\ +			       req, pbusy, state);			\ +	else + +#define START_TRANSFER(common, ep_name, req, pbusy, state)		\ +	START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + +static void busy_indicator(void) +{ +	static int state; + +	switch (state) { +	case 0: +		puts("\r|"); break; +	case 1: +		puts("\r/"); break; +	case 2: +		puts("\r-"); break; +	case 3: +		puts("\r\\"); break; +	case 4: +		puts("\r|"); break; +	case 5: +		puts("\r/"); break; +	case 6: +		puts("\r-"); break; +	case 7: +		puts("\r\\"); break; +	default: +		state = 0; +	} +	if (state++ == 8) +		state = 0; +} + +static int sleep_thread(struct fsg_common *common) +{ +	int	rc = 0; +	int i = 0, k = 0; + +	/* Wait until a signal arrives or we are woken up */ +	for (;;) { +		if (common->thread_wakeup_needed) +			break; + +		if (++i == 50000) { +			busy_indicator(); +			i = 0; +			k++; +		} + +		usb_gadget_handle_interrupts(); +	} +	common->thread_wakeup_needed = 0; +	return rc; +} + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	u32			lba; +	struct fsg_buffhd	*bh; +	int			rc; +	u32			amount_left; +	loff_t			file_offset; +	unsigned int		amount; +	unsigned int		partial_page; +	ssize_t			nread; + +	/* Get the starting Logical Block Address and check that it's +	 * not too big */ +	if (common->cmnd[0] == SC_READ_6) +		lba = get_unaligned_be24(&common->cmnd[1]); +	else { +		lba = get_unaligned_be32(&common->cmnd[2]); + +		/* We allow DPO (Disable Page Out = don't save data in the +		 * cache) and FUA (Force Unit Access = don't read from the +		 * cache), but we don't implement them. */ +		if ((common->cmnd[1] & ~0x18) != 0) { +			curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +			return -EINVAL; +		} +	} +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} +	file_offset = ((loff_t) lba) << 9; + +	/* Carry out the file reads */ +	amount_left = common->data_size_from_cmnd; +	if (unlikely(amount_left == 0)) +		return -EIO;		/* No default reply */ + +	for (;;) { + +		/* Figure out how much we need to read: +		 * Try to read the remaining amount. +		 * But don't read more than the buffer size. +		 * And don't try to read past the end of the file. +		 * Finally, if we're not at a page boundary, don't read past +		 *	the next page. +		 * If this means reading 0 then we were asked to read past +		 *	the end of file. */ +		amount = min(amount_left, FSG_BUFLEN); +		partial_page = file_offset & (PAGE_CACHE_SIZE - 1); +		if (partial_page > 0) +			amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - +					partial_page); + +		/* Wait for the next buffer to become available */ +		bh = common->next_buffhd_to_fill; +		while (bh->state != BUF_STATE_EMPTY) { +			rc = sleep_thread(common); +			if (rc) +				return rc; +		} + +		/* If we were asked to read past the end of file, +		 * end with an empty buffer. */ +		if (amount == 0) { +			curlun->sense_data = +					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +			curlun->info_valid = 1; +			bh->inreq->length = 0; +			bh->state = BUF_STATE_FULL; +			break; +		} + +		/* 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) +			return -EIO; +		nread = amount; + +		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, +				(unsigned long long) file_offset, +				(int) nread); + +		if (nread < 0) { +			LDBG(curlun, "error in file read: %d\n", +					(int) nread); +			nread = 0; +		} else if (nread < amount) { +			LDBG(curlun, "partial file read: %d/%u\n", +					(int) nread, amount); +			nread -= (nread & 511);	/* Round down to a block */ +		} +		file_offset  += nread; +		amount_left  -= nread; +		common->residue -= nread; +		bh->inreq->length = nread; +		bh->state = BUF_STATE_FULL; + +		/* If an error occurred, report it and its position */ +		if (nread < amount) { +			curlun->sense_data = SS_UNRECOVERED_READ_ERROR; +			curlun->info_valid = 1; +			break; +		} + +		if (amount_left == 0) +			break;		/* No more left to read */ + +		/* Send this buffer and go read some more */ +		bh->inreq->zero = 0; +		START_TRANSFER_OR(common, bulk_in, bh->inreq, +			       &bh->inreq_busy, &bh->state) +			/* Don't know what to do if +			 * common->fsg is NULL */ +			return -EIO; +		common->next_buffhd_to_fill = bh->next; +	} + +	return -EIO;		/* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	u32			lba; +	struct fsg_buffhd	*bh; +	int			get_some_more; +	u32			amount_left_to_req, amount_left_to_write; +	loff_t			usb_offset, file_offset; +	unsigned int		amount; +	unsigned int		partial_page; +	ssize_t			nwritten; +	int			rc; + +	if (curlun->ro) { +		curlun->sense_data = SS_WRITE_PROTECTED; +		return -EINVAL; +	} + +	/* Get the starting Logical Block Address and check that it's +	 * not too big */ +	if (common->cmnd[0] == SC_WRITE_6) +		lba = get_unaligned_be24(&common->cmnd[1]); +	else { +		lba = get_unaligned_be32(&common->cmnd[2]); + +		/* We allow DPO (Disable Page Out = don't save data in the +		 * cache) and FUA (Force Unit Access = write directly to the +		 * medium).  We don't implement DPO; we implement FUA by +		 * performing synchronous output. */ +		if (common->cmnd[1] & ~0x18) { +			curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +			return -EINVAL; +		} +	} +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} + +	/* Carry out the file writes */ +	get_some_more = 1; +	file_offset = usb_offset = ((loff_t) lba) << 9; +	amount_left_to_req = common->data_size_from_cmnd; +	amount_left_to_write = common->data_size_from_cmnd; + +	while (amount_left_to_write > 0) { + +		/* Queue a request for more data from the host */ +		bh = common->next_buffhd_to_fill; +		if (bh->state == BUF_STATE_EMPTY && get_some_more) { + +			/* Figure out how much we want to get: +			 * Try to get the remaining amount. +			 * But don't get more than the buffer size. +			 * And don't try to go past the end of the file. +			 * If we're not at a page boundary, +			 *	don't go past the next page. +			 * If this means getting 0, then we were asked +			 *	to write past the end of file. +			 * Finally, round down to a block boundary. */ +			amount = min(amount_left_to_req, FSG_BUFLEN); +			partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); +			if (partial_page > 0) +				amount = min(amount, +	(unsigned int) PAGE_CACHE_SIZE - partial_page); + +			if (amount == 0) { +				get_some_more = 0; +				curlun->sense_data = +					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +				curlun->info_valid = 1; +				continue; +			} +			amount -= (amount & 511); +			if (amount == 0) { + +				/* Why were we were asked to transfer a +				 * partial block? */ +				get_some_more = 0; +				continue; +			} + +			/* Get the next buffer */ +			usb_offset += amount; +			common->usb_amount_left -= amount; +			amount_left_to_req -= amount; +			if (amount_left_to_req == 0) +				get_some_more = 0; + +			/* amount is always divisible by 512, hence by +			 * the bulk-out maxpacket size */ +			bh->outreq->length = amount; +			bh->bulk_out_intended_length = amount; +			bh->outreq->short_not_ok = 1; +			START_TRANSFER_OR(common, bulk_out, bh->outreq, +					  &bh->outreq_busy, &bh->state) +				/* Don't know what to do if +				 * common->fsg is NULL */ +				return -EIO; +			common->next_buffhd_to_fill = bh->next; +			continue; +		} + +		/* Write the received data to the backing file */ +		bh = common->next_buffhd_to_drain; +		if (bh->state == BUF_STATE_EMPTY && !get_some_more) +			break;			/* We stopped early */ +		if (bh->state == BUF_STATE_FULL) { +			common->next_buffhd_to_drain = bh->next; +			bh->state = BUF_STATE_EMPTY; + +			/* Did something go wrong with the transfer? */ +			if (bh->outreq->status != 0) { +				curlun->sense_data = SS_COMMUNICATION_FAILURE; +				curlun->info_valid = 1; +				break; +			} + +			amount = bh->outreq->actual; + +			/* Perform the write */ +			rc = ums_info->write_sector(&(ums_info->ums_dev), +					       file_offset / SECTOR_SIZE, +					       amount / SECTOR_SIZE, +					       (char __user *)bh->buf); +			if (rc) +				return -EIO; +			nwritten = amount; + +			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, +					(unsigned long long) file_offset, +					(int) nwritten); + +			if (nwritten < 0) { +				LDBG(curlun, "error in file write: %d\n", +						(int) nwritten); +				nwritten = 0; +			} else if (nwritten < amount) { +				LDBG(curlun, "partial file write: %d/%u\n", +						(int) nwritten, amount); +				nwritten -= (nwritten & 511); +				/* Round down to a block */ +			} +			file_offset += nwritten; +			amount_left_to_write -= nwritten; +			common->residue -= nwritten; + +			/* If an error occurred, report it and its position */ +			if (nwritten < amount) { +				curlun->sense_data = SS_WRITE_ERROR; +				curlun->info_valid = 1; +				break; +			} + +			/* Did the host decide to stop early? */ +			if (bh->outreq->actual != bh->outreq->length) { +				common->short_packet_received = 1; +				break; +			} +			continue; +		} + +		/* Wait for something to happen */ +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	return -EIO;		/* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_verify(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	u32			lba; +	u32			verification_length; +	struct fsg_buffhd	*bh = common->next_buffhd_to_fill; +	loff_t			file_offset; +	u32			amount_left; +	unsigned int		amount; +	ssize_t			nread; +	int			rc; + +	/* Get the starting Logical Block Address and check that it's +	 * not too big */ +	lba = get_unaligned_be32(&common->cmnd[2]); +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} + +	/* We allow DPO (Disable Page Out = don't save data in the +	 * cache) but we don't implement it. */ +	if (common->cmnd[1] & ~0x10) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	verification_length = get_unaligned_be16(&common->cmnd[7]); +	if (unlikely(verification_length == 0)) +		return -EIO;		/* No default reply */ + +	/* Prepare to carry out the file verify */ +	amount_left = verification_length << 9; +	file_offset = ((loff_t) lba) << 9; + +	/* Write out all the dirty buffers before invalidating them */ + +	/* Just try to read the requested blocks */ +	while (amount_left > 0) { + +		/* Figure out how much we need to read: +		 * Try to read the remaining amount, but not more than +		 * the buffer size. +		 * And don't try to read past the end of the file. +		 * If this means reading 0 then we were asked to read +		 * past the end of file. */ +		amount = min(amount_left, FSG_BUFLEN); +		if (amount == 0) { +			curlun->sense_data = +					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +			curlun->info_valid = 1; +			break; +		} + +		/* 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) +			return -EIO; +		nread = amount; + +		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, +				(unsigned long long) file_offset, +				(int) nread); +		if (nread < 0) { +			LDBG(curlun, "error in file verify: %d\n", +					(int) nread); +			nread = 0; +		} else if (nread < amount) { +			LDBG(curlun, "partial file verify: %d/%u\n", +					(int) nread, amount); +			nread -= (nread & 511);	/* Round down to a sector */ +		} +		if (nread == 0) { +			curlun->sense_data = SS_UNRECOVERED_READ_ERROR; +			curlun->info_valid = 1; +			break; +		} +		file_offset += nread; +		amount_left -= nread; +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun *curlun = &common->luns[common->lun]; +	static const char vendor_id[] = "Linux   "; +	u8	*buf = (u8 *) bh->buf; + +	if (!curlun) {		/* Unsupported LUNs are okay */ +		common->bad_lun_okay = 1; +		memset(buf, 0, 36); +		buf[0] = 0x7f;		/* Unsupported, no device-type */ +		buf[4] = 31;		/* Additional length */ +		return 36; +	} + +	memset(buf, 0, 8); +	buf[0] = TYPE_DISK; +	buf[2] = 2;		/* ANSI SCSI level 2 */ +	buf[3] = 2;		/* SCSI-2 INQUIRY data format */ +	buf[4] = 31;		/* Additional length */ +				/* No special options */ +	sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id , +			ums_info->name, (u16) 0xffff); + +	return 36; +} + + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	u8		*buf = (u8 *) bh->buf; +	u32		sd, sdinfo; +	int		valid; + +	/* +	 * From the SCSI-2 spec., section 7.9 (Unit attention condition): +	 * +	 * If a REQUEST SENSE command is received from an initiator +	 * with a pending unit attention condition (before the target +	 * generates the contingent allegiance condition), then the +	 * target shall either: +	 *   a) report any pending sense data and preserve the unit +	 *	attention condition on the logical unit, or, +	 *   b) report the unit attention condition, may discard any +	 *	pending sense data, and clear the unit attention +	 *	condition on the logical unit for that initiator. +	 * +	 * FSG normally uses option a); enable this code to use option b). +	 */ +#if 0 +	if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { +		curlun->sense_data = curlun->unit_attention_data; +		curlun->unit_attention_data = SS_NO_SENSE; +	} +#endif + +	if (!curlun) {		/* Unsupported LUNs are okay */ +		common->bad_lun_okay = 1; +		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; +		sdinfo = 0; +		valid = 0; +	} else { +		sd = curlun->sense_data; +		valid = curlun->info_valid << 7; +		curlun->sense_data = SS_NO_SENSE; +		curlun->info_valid = 0; +	} + +	memset(buf, 0, 18); +	buf[0] = valid | 0x70;			/* Valid, current error */ +	buf[2] = SK(sd); +	put_unaligned_be32(sdinfo, &buf[3]);	/* Sense information */ +	buf[7] = 18 - 8;			/* Additional sense length */ +	buf[12] = ASC(sd); +	buf[13] = ASCQ(sd); +	return 18; +} + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	u32		lba = get_unaligned_be32(&common->cmnd[2]); +	int		pmi = common->cmnd[8]; +	u8		*buf = (u8 *) bh->buf; + +	/* Check the PMI and LBA fields */ +	if (pmi > 1 || (pmi == 0 && lba != 0)) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); +						/* Max logical block */ +	put_unaligned_be32(512, &buf[4]);	/* Block length */ +	return 8; +} + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		msf = common->cmnd[1] & 0x02; +	u32		lba = get_unaligned_be32(&common->cmnd[2]); +	u8		*buf = (u8 *) bh->buf; + +	if (common->cmnd[1] & ~0x02) {		/* Mask away MSF */ +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} + +	memset(buf, 0, 8); +	buf[0] = 0x01;		/* 2048 bytes of user data, rest is EC */ +	store_cdrom_address(&buf[4], msf, lba); +	return 8; +} + + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		msf = common->cmnd[1] & 0x02; +	int		start_track = common->cmnd[6]; +	u8		*buf = (u8 *) bh->buf; + +	if ((common->cmnd[1] & ~0x02) != 0 ||	/* Mask away MSF */ +			start_track > 1) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	memset(buf, 0, 20); +	buf[1] = (20-2);		/* TOC data length */ +	buf[2] = 1;			/* First track number */ +	buf[3] = 1;			/* Last track number */ +	buf[5] = 0x16;			/* Data track, copying allowed */ +	buf[6] = 0x01;			/* Only track is number 1 */ +	store_cdrom_address(&buf[8], msf, 0); + +	buf[13] = 0x16;			/* Lead-out track is data */ +	buf[14] = 0xAA;			/* Lead-out track number */ +	store_cdrom_address(&buf[16], msf, curlun->num_sectors); + +	return 20; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		mscmnd = common->cmnd[0]; +	u8		*buf = (u8 *) bh->buf; +	u8		*buf0 = buf; +	int		pc, page_code; +	int		changeable_values, all_pages; +	int		valid_page = 0; +	int		len, limit; + +	if ((common->cmnd[1] & ~0x08) != 0) {	/* Mask away DBD */ +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} +	pc = common->cmnd[2] >> 6; +	page_code = common->cmnd[2] & 0x3f; +	if (pc == 3) { +		curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; +		return -EINVAL; +	} +	changeable_values = (pc == 1); +	all_pages = (page_code == 0x3f); + +	/* Write the mode parameter header.  Fixed values are: default +	 * medium type, no cache control (DPOFUA), and no block descriptors. +	 * The only variable value is the WriteProtect bit.  We will fill in +	 * the mode data length later. */ +	memset(buf, 0, 8); +	if (mscmnd == SC_MODE_SENSE_6) { +		buf[2] = (curlun->ro ? 0x80 : 0x00);		/* WP, DPOFUA */ +		buf += 4; +		limit = 255; +	} else {			/* SC_MODE_SENSE_10 */ +		buf[3] = (curlun->ro ? 0x80 : 0x00);		/* WP, DPOFUA */ +		buf += 8; +		limit = 65535;		/* Should really be FSG_BUFLEN */ +	} + +	/* No block descriptors */ + +	/* The mode pages, in numerical order.  The only page we support +	 * is the Caching page. */ +	if (page_code == 0x08 || all_pages) { +		valid_page = 1; +		buf[0] = 0x08;		/* Page code */ +		buf[1] = 10;		/* Page length */ +		memset(buf+2, 0, 10);	/* None of the fields are changeable */ + +		if (!changeable_values) { +			buf[2] = 0x04;	/* Write cache enable, */ +					/* Read cache not disabled */ +					/* No cache retention priorities */ +			put_unaligned_be16(0xffff, &buf[4]); +					/* Don't disable prefetch */ +					/* Minimum prefetch = 0 */ +			put_unaligned_be16(0xffff, &buf[8]); +					/* Maximum prefetch */ +			put_unaligned_be16(0xffff, &buf[10]); +					/* Maximum prefetch ceiling */ +		} +		buf += 12; +	} + +	/* Check that a valid page was requested and the mode data length +	 * isn't too long. */ +	len = buf - buf0; +	if (!valid_page || len > limit) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	/*  Store the mode data length */ +	if (mscmnd == SC_MODE_SENSE_6) +		buf0[0] = len - 1; +	else +		put_unaligned_be16(len - 2, buf0); +	return len; +} + + +static int do_start_stop(struct fsg_common *common) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; + +	if (!curlun) { +		return -EINVAL; +	} else if (!curlun->removable) { +		curlun->sense_data = SS_INVALID_COMMAND; +		return -EINVAL; +	} + +	return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		prevent; + +	if (!curlun->removable) { +		curlun->sense_data = SS_INVALID_COMMAND; +		return -EINVAL; +	} + +	prevent = common->cmnd[4] & 0x01; +	if ((common->cmnd[4] & ~0x01) != 0) {	/* Mask away Prevent */ +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	if (curlun->prevent_medium_removal && !prevent) +		fsg_lun_fsync_sub(curlun); +	curlun->prevent_medium_removal = prevent; +	return 0; +} + + +static int do_read_format_capacities(struct fsg_common *common, +			struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	u8		*buf = (u8 *) bh->buf; + +	buf[0] = buf[1] = buf[2] = 0; +	buf[3] = 8;	/* Only the Current/Maximum Capacity Descriptor */ +	buf += 4; + +	put_unaligned_be32(curlun->num_sectors, &buf[0]); +						/* Number of blocks */ +	put_unaligned_be32(512, &buf[4]);	/* Block length */ +	buf[4] = 0x02;				/* Current capacity */ +	return 12; +} + + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; + +	/* We don't support MODE SELECT */ +	if (curlun) +		curlun->sense_data = SS_INVALID_COMMAND; +	return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ +	int	rc; + +	rc = fsg_set_halt(fsg, fsg->bulk_in); +	if (rc == -EAGAIN) +		VDBG(fsg, "delayed bulk-in endpoint halt\n"); +	while (rc != 0) { +		if (rc != -EAGAIN) { +			WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); +			rc = 0; +			break; +		} + +		rc = usb_ep_set_halt(fsg->bulk_in); +	} +	return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ +	int	rc; + +	DBG(fsg, "bulk-in set wedge\n"); +	rc = 0; /* usb_ep_set_wedge(fsg->bulk_in); */ +	if (rc == -EAGAIN) +		VDBG(fsg, "delayed bulk-in endpoint wedge\n"); +	while (rc != 0) { +		if (rc != -EAGAIN) { +			WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); +			rc = 0; +			break; +		} +	} +	return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ +	struct fsg_buffhd	*bh = fsg->common->next_buffhd_to_fill; +	u32			nkeep = bh->inreq->length; +	u32			nsend; +	int			rc; + +	bh->state = BUF_STATE_EMPTY;		/* For the first iteration */ +	fsg->common->usb_amount_left = nkeep + fsg->common->residue; +	while (fsg->common->usb_amount_left > 0) { + +		/* Wait for the next buffer to be free */ +		while (bh->state != BUF_STATE_EMPTY) { +			rc = sleep_thread(fsg->common); +			if (rc) +				return rc; +		} + +		nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); +		memset(bh->buf + nkeep, 0, nsend - nkeep); +		bh->inreq->length = nsend; +		bh->inreq->zero = 0; +		start_transfer(fsg, fsg->bulk_in, bh->inreq, +				&bh->inreq_busy, &bh->state); +		bh = fsg->common->next_buffhd_to_fill = bh->next; +		fsg->common->usb_amount_left -= nsend; +		nkeep = 0; +	} +	return 0; +} + +static int throw_away_data(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh; +	u32			amount; +	int			rc; + +	for (bh = common->next_buffhd_to_drain; +	     bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; +	     bh = common->next_buffhd_to_drain) { + +		/* Throw away the data in a filled buffer */ +		if (bh->state == BUF_STATE_FULL) { +			bh->state = BUF_STATE_EMPTY; +			common->next_buffhd_to_drain = bh->next; + +			/* A short packet or an error ends everything */ +			if (bh->outreq->actual != bh->outreq->length || +					bh->outreq->status != 0) { +				raise_exception(common, +						FSG_STATE_ABORT_BULK_OUT); +				return -EINTR; +			} +			continue; +		} + +		/* Try to submit another request if we need one */ +		bh = common->next_buffhd_to_fill; +		if (bh->state == BUF_STATE_EMPTY +		 && common->usb_amount_left > 0) { +			amount = min(common->usb_amount_left, FSG_BUFLEN); + +			/* amount is always divisible by 512, hence by +			 * the bulk-out maxpacket size */ +			bh->outreq->length = amount; +			bh->bulk_out_intended_length = amount; +			bh->outreq->short_not_ok = 1; +			START_TRANSFER_OR(common, bulk_out, bh->outreq, +					  &bh->outreq_busy, &bh->state) +				/* Don't know what to do if +				 * common->fsg is NULL */ +				return -EIO; +			common->next_buffhd_to_fill = bh->next; +			common->usb_amount_left -= amount; +			continue; +		} + +		/* Otherwise wait for something to happen */ +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} +	return 0; +} + + +static int finish_reply(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh = common->next_buffhd_to_fill; +	int			rc = 0; + +	switch (common->data_dir) { +	case DATA_DIR_NONE: +		break;			/* Nothing to send */ + +	/* If we don't know whether the host wants to read or write, +	 * this must be CB or CBI with an unknown command.  We mustn't +	 * try to send or receive any data.  So stall both bulk pipes +	 * if we can and wait for a reset. */ +	case DATA_DIR_UNKNOWN: +		if (!common->can_stall) { +			/* Nothing */ +		} else if (fsg_is_set(common)) { +			fsg_set_halt(common->fsg, common->fsg->bulk_out); +			rc = halt_bulk_in_endpoint(common->fsg); +		} else { +			/* Don't know what to do if common->fsg is NULL */ +			rc = -EIO; +		} +		break; + +	/* All but the last buffer of data must have already been sent */ +	case DATA_DIR_TO_HOST: +		if (common->data_size == 0) { +			/* Nothing to send */ + +		/* If there's no residue, simply send the last buffer */ +		} else if (common->residue == 0) { +			bh->inreq->zero = 0; +			START_TRANSFER_OR(common, bulk_in, bh->inreq, +					  &bh->inreq_busy, &bh->state) +				return -EIO; +			common->next_buffhd_to_fill = bh->next; + +		/* For Bulk-only, if we're allowed to stall then send the +		 * short packet and halt the bulk-in endpoint.  If we can't +		 * stall, pad out the remaining data with 0's. */ +		} else if (common->can_stall) { +			bh->inreq->zero = 1; +			START_TRANSFER_OR(common, bulk_in, bh->inreq, +					  &bh->inreq_busy, &bh->state) +				/* Don't know what to do if +				 * common->fsg is NULL */ +				rc = -EIO; +			common->next_buffhd_to_fill = bh->next; +			if (common->fsg) +				rc = halt_bulk_in_endpoint(common->fsg); +		} else if (fsg_is_set(common)) { +			rc = pad_with_zeros(common->fsg); +		} else { +			/* Don't know what to do if common->fsg is NULL */ +			rc = -EIO; +		} +		break; + +	/* We have processed all we want from the data the host has sent. +	 * There may still be outstanding bulk-out requests. */ +	case DATA_DIR_FROM_HOST: +		if (common->residue == 0) { +			/* Nothing to receive */ + +		/* Did the host stop sending unexpectedly early? */ +		} else if (common->short_packet_received) { +			raise_exception(common, FSG_STATE_ABORT_BULK_OUT); +			rc = -EINTR; + +		/* We haven't processed all the incoming data.  Even though +		 * we may be allowed to stall, doing so would cause a race. +		 * The controller may already have ACK'ed all the remaining +		 * bulk-out packets, in which case the host wouldn't see a +		 * STALL.  Not realizing the endpoint was halted, it wouldn't +		 * clear the halt -- leading to problems later on. */ +#if 0 +		} else if (common->can_stall) { +			if (fsg_is_set(common)) +				fsg_set_halt(common->fsg, +					     common->fsg->bulk_out); +			raise_exception(common, FSG_STATE_ABORT_BULK_OUT); +			rc = -EINTR; +#endif + +		/* We can't stall.  Read in the excess data and throw it +		 * all away. */ +		} else { +			rc = throw_away_data(common); +		} +		break; +	} +	return rc; +} + + +static int send_status(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	struct fsg_buffhd	*bh; +	struct bulk_cs_wrap	*csw; +	int			rc; +	u8			status = USB_STATUS_PASS; +	u32			sd, sdinfo = 0; + +	/* Wait for the next buffer to become available */ +	bh = common->next_buffhd_to_fill; +	while (bh->state != BUF_STATE_EMPTY) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	if (curlun) +		sd = curlun->sense_data; +	else if (common->bad_lun_okay) +		sd = SS_NO_SENSE; +	else +		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + +	if (common->phase_error) { +		DBG(common, "sending phase-error status\n"); +		status = USB_STATUS_PHASE_ERROR; +		sd = SS_INVALID_COMMAND; +	} else if (sd != SS_NO_SENSE) { +		DBG(common, "sending command-failure status\n"); +		status = USB_STATUS_FAIL; +		VDBG(common, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" +			"  info x%x\n", +			SK(sd), ASC(sd), ASCQ(sd), sdinfo); +	} + +	/* Store and send the Bulk-only CSW */ +	csw = (void *)bh->buf; + +	csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); +	csw->Tag = common->tag; +	csw->Residue = cpu_to_le32(common->residue); +	csw->Status = status; + +	bh->inreq->length = USB_BULK_CS_WRAP_LEN; +	bh->inreq->zero = 0; +	START_TRANSFER_OR(common, bulk_in, bh->inreq, +			  &bh->inreq_busy, &bh->state) +		/* Don't know what to do if common->fsg is NULL */ +		return -EIO; + +	common->next_buffhd_to_fill = bh->next; +	return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_common *common, int cmnd_size, +		enum data_direction data_dir, unsigned int mask, +		int needs_medium, const char *name) +{ +	int			i; +	int			lun = common->cmnd[1] >> 5; +	static const char	dirletter[4] = {'u', 'o', 'i', 'n'}; +	char			hdlen[20]; +	struct fsg_lun		*curlun; + +	hdlen[0] = 0; +	if (common->data_dir != DATA_DIR_UNKNOWN) +		sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], +				common->data_size); +	VDBG(common, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n", +	     name, cmnd_size, dirletter[(int) data_dir], +	     common->data_size_from_cmnd, common->cmnd_size, hdlen); + +	/* We can't reply at all until we know the correct data direction +	 * and size. */ +	if (common->data_size_from_cmnd == 0) +		data_dir = DATA_DIR_NONE; +	if (common->data_size < common->data_size_from_cmnd) { +		/* Host data size < Device data size is a phase error. +		 * Carry out the command, but only transfer as much as +		 * we are allowed. */ +		common->data_size_from_cmnd = common->data_size; +		common->phase_error = 1; +	} +	common->residue = common->data_size; +	common->usb_amount_left = common->data_size; + +	/* Conflicting data directions is a phase error */ +	if (common->data_dir != data_dir +	 && common->data_size_from_cmnd > 0) { +		common->phase_error = 1; +		return -EINVAL; +	} + +	/* Verify the length of the command itself */ +	if (cmnd_size != common->cmnd_size) { + +		/* Special case workaround: There are plenty of buggy SCSI +		 * implementations. Many have issues with cbw->Length +		 * field passing a wrong command size. For those cases we +		 * always try to work around the problem by using the length +		 * sent by the host side provided it is at least as large +		 * as the correct command length. +		 * Examples of such cases would be MS-Windows, which issues +		 * REQUEST SENSE with cbw->Length == 12 where it should +		 * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and +		 * REQUEST SENSE with cbw->Length == 10 where it should +		 * be 6 as well. +		 */ +		if (cmnd_size <= common->cmnd_size) { +			DBG(common, "%s is buggy! Expected length %d " +			    "but we got %d\n", name, +			    cmnd_size, common->cmnd_size); +			cmnd_size = common->cmnd_size; +		} else { +			common->phase_error = 1; +			return -EINVAL; +		} +	} + +	/* Check that the LUN values are consistent */ +	if (common->lun != lun) +		DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", +		    common->lun, lun); + +	/* Check the LUN */ +	if (common->lun >= 0 && common->lun < common->nluns) { +		curlun = &common->luns[common->lun]; +		if (common->cmnd[0] != SC_REQUEST_SENSE) { +			curlun->sense_data = SS_NO_SENSE; +			curlun->info_valid = 0; +		} +	} else { +		curlun = NULL; +		common->bad_lun_okay = 0; + +		/* INQUIRY and REQUEST SENSE commands are explicitly allowed +		 * to use unsupported LUNs; all others may not. */ +		if (common->cmnd[0] != SC_INQUIRY && +		    common->cmnd[0] != SC_REQUEST_SENSE) { +			DBG(common, "unsupported LUN %d\n", common->lun); +			return -EINVAL; +		} +	} +#if 0 +	/* If a unit attention condition exists, only INQUIRY and +	 * REQUEST SENSE commands are allowed; anything else must fail. */ +	if (curlun && curlun->unit_attention_data != SS_NO_SENSE && +			common->cmnd[0] != SC_INQUIRY && +			common->cmnd[0] != SC_REQUEST_SENSE) { +		curlun->sense_data = curlun->unit_attention_data; +		curlun->unit_attention_data = SS_NO_SENSE; +		return -EINVAL; +	} +#endif +	/* Check that only command bytes listed in the mask are non-zero */ +	common->cmnd[1] &= 0x1f;			/* Mask away the LUN */ +	for (i = 1; i < cmnd_size; ++i) { +		if (common->cmnd[i] && !(mask & (1 << i))) { +			if (curlun) +				curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +			return -EINVAL; +		} +	} + +	return 0; +} + + +static int do_scsi_command(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh; +	int			rc; +	int			reply = -EINVAL; +	int			i; +	static char		unknown[16]; +	struct fsg_lun		*curlun = &common->luns[common->lun]; + +	dump_cdb(common); + +	/* Wait for the next buffer to become available for data or status */ +	bh = common->next_buffhd_to_fill; +	common->next_buffhd_to_drain = bh; +	while (bh->state != BUF_STATE_EMPTY) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} +	common->phase_error = 0; +	common->short_packet_received = 0; + +	down_read(&common->filesem);	/* We're using the backing file */ +	switch (common->cmnd[0]) { + +	case SC_INQUIRY: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (1<<4), 0, +				      "INQUIRY"); +		if (reply == 0) +			reply = do_inquiry(common, bh); +		break; + +	case SC_MODE_SELECT_6: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_FROM_HOST, +				      (1<<1) | (1<<4), 0, +				      "MODE SELECT(6)"); +		if (reply == 0) +			reply = do_mode_select(common, bh); +		break; + +	case SC_MODE_SELECT_10: +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_FROM_HOST, +				      (1<<1) | (3<<7), 0, +				      "MODE SELECT(10)"); +		if (reply == 0) +			reply = do_mode_select(common, bh); +		break; + +	case SC_MODE_SENSE_6: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (1<<1) | (1<<2) | (1<<4), 0, +				      "MODE SENSE(6)"); +		if (reply == 0) +			reply = do_mode_sense(common, bh); +		break; + +	case SC_MODE_SENSE_10: +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (1<<1) | (1<<2) | (3<<7), 0, +				      "MODE SENSE(10)"); +		if (reply == 0) +			reply = do_mode_sense(common, bh); +		break; + +	case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 6, DATA_DIR_NONE, +				      (1<<4), 0, +				      "PREVENT-ALLOW MEDIUM REMOVAL"); +		if (reply == 0) +			reply = do_prevent_allow(common); +		break; + +	case SC_READ_6: +		i = common->cmnd[4]; +		common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (7<<1) | (1<<4), 1, +				      "READ(6)"); +		if (reply == 0) +			reply = do_read(common); +		break; + +	case SC_READ_10: +		common->data_size_from_cmnd = +				get_unaligned_be16(&common->cmnd[7]) << 9; +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (1<<1) | (0xf<<2) | (3<<7), 1, +				      "READ(10)"); +		if (reply == 0) +			reply = do_read(common); +		break; + +	case SC_READ_12: +		common->data_size_from_cmnd = +				get_unaligned_be32(&common->cmnd[6]) << 9; +		reply = check_command(common, 12, DATA_DIR_TO_HOST, +				      (1<<1) | (0xf<<2) | (0xf<<6), 1, +				      "READ(12)"); +		if (reply == 0) +			reply = do_read(common); +		break; + +	case SC_READ_CAPACITY: +		common->data_size_from_cmnd = 8; +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (0xf<<2) | (1<<8), 1, +				      "READ CAPACITY"); +		if (reply == 0) +			reply = do_read_capacity(common, bh); +		break; + +	case SC_READ_HEADER: +		if (!common->luns[common->lun].cdrom) +			goto unknown_cmnd; +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (3<<7) | (0x1f<<1), 1, +				      "READ HEADER"); +		if (reply == 0) +			reply = do_read_header(common, bh); +		break; + +	case SC_READ_TOC: +		if (!common->luns[common->lun].cdrom) +			goto unknown_cmnd; +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (7<<6) | (1<<1), 1, +				      "READ TOC"); +		if (reply == 0) +			reply = do_read_toc(common, bh); +		break; + +	case SC_READ_FORMAT_CAPACITIES: +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (3<<7), 1, +				      "READ FORMAT CAPACITIES"); +		if (reply == 0) +			reply = do_read_format_capacities(common, bh); +		break; + +	case SC_REQUEST_SENSE: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (1<<4), 0, +				      "REQUEST SENSE"); +		if (reply == 0) +			reply = do_request_sense(common, bh); +		break; + +	case SC_START_STOP_UNIT: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 6, DATA_DIR_NONE, +				      (1<<1) | (1<<4), 0, +				      "START-STOP UNIT"); +		if (reply == 0) +			reply = do_start_stop(common); +		break; + +	case SC_SYNCHRONIZE_CACHE: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 10, DATA_DIR_NONE, +				      (0xf<<2) | (3<<7), 1, +				      "SYNCHRONIZE CACHE"); +		if (reply == 0) +			reply = do_synchronize_cache(common); +		break; + +	case SC_TEST_UNIT_READY: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 6, DATA_DIR_NONE, +				0, 1, +				"TEST UNIT READY"); +		break; + +	/* Although optional, this command is used by MS-Windows.  We +	 * support a minimal version: BytChk must be 0. */ +	case SC_VERIFY: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 10, DATA_DIR_NONE, +				      (1<<1) | (0xf<<2) | (3<<7), 1, +				      "VERIFY"); +		if (reply == 0) +			reply = do_verify(common); +		break; + +	case SC_WRITE_6: +		i = common->cmnd[4]; +		common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +		reply = check_command(common, 6, DATA_DIR_FROM_HOST, +				      (7<<1) | (1<<4), 1, +				      "WRITE(6)"); +		if (reply == 0) +			reply = do_write(common); +		break; + +	case SC_WRITE_10: +		common->data_size_from_cmnd = +				get_unaligned_be16(&common->cmnd[7]) << 9; +		reply = check_command(common, 10, DATA_DIR_FROM_HOST, +				      (1<<1) | (0xf<<2) | (3<<7), 1, +				      "WRITE(10)"); +		if (reply == 0) +			reply = do_write(common); +		break; + +	case SC_WRITE_12: +		common->data_size_from_cmnd = +				get_unaligned_be32(&common->cmnd[6]) << 9; +		reply = check_command(common, 12, DATA_DIR_FROM_HOST, +				      (1<<1) | (0xf<<2) | (0xf<<6), 1, +				      "WRITE(12)"); +		if (reply == 0) +			reply = do_write(common); +		break; + +	/* Some mandatory commands that we recognize but don't implement. +	 * They don't mean much in this setting.  It's left as an exercise +	 * for anyone interested to implement RESERVE and RELEASE in terms +	 * of Posix locks. */ +	case SC_FORMAT_UNIT: +	case SC_RELEASE: +	case SC_RESERVE: +	case SC_SEND_DIAGNOSTIC: +		/* Fall through */ + +	default: +unknown_cmnd: +		common->data_size_from_cmnd = 0; +		sprintf(unknown, "Unknown x%02x", common->cmnd[0]); +		reply = check_command(common, common->cmnd_size, +				      DATA_DIR_UNKNOWN, 0xff, 0, unknown); +		if (reply == 0) { +			curlun->sense_data = SS_INVALID_COMMAND; +			reply = -EINVAL; +		} +		break; +	} +	up_read(&common->filesem); + +	if (reply == -EINTR) +		return -EINTR; + +	/* Set up the single reply buffer for finish_reply() */ +	if (reply == -EINVAL) +		reply = 0;		/* Error reply length */ +	if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { +		reply = min((u32) reply, common->data_size_from_cmnd); +		bh->inreq->length = reply; +		bh->state = BUF_STATE_FULL; +		common->residue -= reply; +	}				/* Otherwise it's already set */ + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ +	struct usb_request	*req = bh->outreq; +	struct fsg_bulk_cb_wrap	*cbw = req->buf; +	struct fsg_common	*common = fsg->common; + +	/* Was this a real packet?  Should it be ignored? */ +	if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) +		return -EINVAL; + +	/* Is the CBW valid? */ +	if (req->actual != USB_BULK_CB_WRAP_LEN || +			cbw->Signature != cpu_to_le32( +				USB_BULK_CB_SIG)) { +		DBG(fsg, "invalid CBW: len %u sig 0x%x\n", +				req->actual, +				le32_to_cpu(cbw->Signature)); + +		/* The Bulk-only spec says we MUST stall the IN endpoint +		 * (6.6.1), so it's unavoidable.  It also says we must +		 * retain this state until the next reset, but there's +		 * no way to tell the controller driver it should ignore +		 * Clear-Feature(HALT) requests. +		 * +		 * We aren't required to halt the OUT endpoint; instead +		 * we can simply accept and discard any data received +		 * until the next reset. */ +		wedge_bulk_in_endpoint(fsg); +		set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); +		return -EINVAL; +	} + +	/* Is the CBW meaningful? */ +	if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || +			cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { +		DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " +				"cmdlen %u\n", +				cbw->Lun, cbw->Flags, cbw->Length); + +		/* We can do anything we want here, so let's stall the +		 * bulk pipes if we are allowed to. */ +		if (common->can_stall) { +			fsg_set_halt(fsg, fsg->bulk_out); +			halt_bulk_in_endpoint(fsg); +		} +		return -EINVAL; +	} + +	/* Save the command for later */ +	common->cmnd_size = cbw->Length; +	memcpy(common->cmnd, cbw->CDB, common->cmnd_size); +	if (cbw->Flags & USB_BULK_IN_FLAG) +		common->data_dir = DATA_DIR_TO_HOST; +	else +		common->data_dir = DATA_DIR_FROM_HOST; +	common->data_size = le32_to_cpu(cbw->DataTransferLength); +	if (common->data_size == 0) +		common->data_dir = DATA_DIR_NONE; +	common->lun = cbw->Lun; +	common->tag = cbw->Tag; +	return 0; +} + + +static int get_next_command(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh; +	int			rc = 0; + +	/* Wait for the next buffer to become available */ +	bh = common->next_buffhd_to_fill; +	while (bh->state != BUF_STATE_EMPTY) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	/* Queue a request to read a Bulk-only CBW */ +	set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); +	bh->outreq->short_not_ok = 1; +	START_TRANSFER_OR(common, bulk_out, bh->outreq, +			  &bh->outreq_busy, &bh->state) +		/* Don't know what to do if common->fsg is NULL */ +		return -EIO; + +	/* We will drain the buffer in software, which means we +	 * can reuse it for the next filling.  No need to advance +	 * next_buffhd_to_fill. */ + +	/* Wait for the CBW to arrive */ +	while (bh->state != BUF_STATE_FULL) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; +	bh->state = BUF_STATE_EMPTY; + +	return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, +		const struct usb_endpoint_descriptor *d) +{ +	int	rc; + +	ep->driver_data = common; +	rc = usb_ep_enable(ep, d); +	if (rc) +		ERROR(common, "can't enable %s, result %d\n", ep->name, rc); +	return rc; +} + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, +		struct usb_request **preq) +{ +	*preq = usb_ep_alloc_request(ep, GFP_ATOMIC); +	if (*preq) +		return 0; +	ERROR(common, "can't allocate request for %s\n", ep->name); +	return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ +	const struct usb_endpoint_descriptor *d; +	struct fsg_dev *fsg; +	int i, rc = 0; + +	if (common->running) +		DBG(common, "reset interface\n"); + +reset: +	/* Deallocate the requests */ +	if (common->fsg) { +		fsg = common->fsg; + +		for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +			struct fsg_buffhd *bh = &common->buffhds[i]; + +			if (bh->inreq) { +				usb_ep_free_request(fsg->bulk_in, bh->inreq); +				bh->inreq = NULL; +			} +			if (bh->outreq) { +				usb_ep_free_request(fsg->bulk_out, bh->outreq); +				bh->outreq = NULL; +			} +		} + +		/* Disable the endpoints */ +		if (fsg->bulk_in_enabled) { +			usb_ep_disable(fsg->bulk_in); +			fsg->bulk_in_enabled = 0; +		} +		if (fsg->bulk_out_enabled) { +			usb_ep_disable(fsg->bulk_out); +			fsg->bulk_out_enabled = 0; +		} + +		common->fsg = NULL; +		/* wake_up(&common->fsg_wait); */ +	} + +	common->running = 0; +	if (!new_fsg || rc) +		return rc; + +	common->fsg = new_fsg; +	fsg = common->fsg; + +	/* Enable the endpoints */ +	d = fsg_ep_desc(common->gadget, +			&fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); +	rc = enable_endpoint(common, fsg->bulk_in, d); +	if (rc) +		goto reset; +	fsg->bulk_in_enabled = 1; + +	d = fsg_ep_desc(common->gadget, +			&fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); +	rc = enable_endpoint(common, fsg->bulk_out, d); +	if (rc) +		goto reset; +	fsg->bulk_out_enabled = 1; +	common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); +	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + +	/* Allocate the requests */ +	for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +		struct fsg_buffhd	*bh = &common->buffhds[i]; + +		rc = alloc_request(common, fsg->bulk_in, &bh->inreq); +		if (rc) +			goto reset; +		rc = alloc_request(common, fsg->bulk_out, &bh->outreq); +		if (rc) +			goto reset; +		bh->inreq->buf = bh->outreq->buf = bh->buf; +		bh->inreq->context = bh->outreq->context = bh; +		bh->inreq->complete = bulk_in_complete; +		bh->outreq->complete = bulk_out_complete; +	} + +	common->running = 1; + +	return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ +	struct fsg_dev *fsg = fsg_from_func(f); +	fsg->common->new_fsg = fsg; +	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +	return 0; +} + +static void fsg_disable(struct usb_function *f) +{ +	struct fsg_dev *fsg = fsg_from_func(f); +	fsg->common->new_fsg = NULL; +	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ +	int			i; +	struct fsg_buffhd	*bh; +	enum fsg_state		old_state; +	struct fsg_lun		*curlun; +	unsigned int		exception_req_tag; + +	/* Cancel all the pending transfers */ +	if (common->fsg) { +		for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +			bh = &common->buffhds[i]; +			if (bh->inreq_busy) +				usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); +			if (bh->outreq_busy) +				usb_ep_dequeue(common->fsg->bulk_out, +					       bh->outreq); +		} + +		/* Wait until everything is idle */ +		for (;;) { +			int num_active = 0; +			for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +				bh = &common->buffhds[i]; +				num_active += bh->inreq_busy + bh->outreq_busy; +			} +			if (num_active == 0) +				break; +			if (sleep_thread(common)) +				return; +		} + +		/* Clear out the controller's fifos */ +		if (common->fsg->bulk_in_enabled) +			usb_ep_fifo_flush(common->fsg->bulk_in); +		if (common->fsg->bulk_out_enabled) +			usb_ep_fifo_flush(common->fsg->bulk_out); +	} + +	/* Reset the I/O buffer states and pointers, the SCSI +	 * state, and the exception.  Then invoke the handler. */ + +	for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +		bh = &common->buffhds[i]; +		bh->state = BUF_STATE_EMPTY; +	} +	common->next_buffhd_to_fill = &common->buffhds[0]; +	common->next_buffhd_to_drain = &common->buffhds[0]; +	exception_req_tag = common->exception_req_tag; +	old_state = common->state; + +	if (old_state == FSG_STATE_ABORT_BULK_OUT) +		common->state = FSG_STATE_STATUS_PHASE; +	else { +		for (i = 0; i < common->nluns; ++i) { +			curlun = &common->luns[i]; +			curlun->sense_data = SS_NO_SENSE; +			curlun->info_valid = 0; +		} +		common->state = FSG_STATE_IDLE; +	} + +	/* Carry out any extra actions required for the exception */ +	switch (old_state) { +	case FSG_STATE_ABORT_BULK_OUT: +		send_status(common); + +		if (common->state == FSG_STATE_STATUS_PHASE) +			common->state = FSG_STATE_IDLE; +		break; + +	case FSG_STATE_RESET: +		/* In case we were forced against our will to halt a +		 * bulk endpoint, clear the halt now.  (The SuperH UDC +		 * requires this.) */ +		if (!fsg_is_set(common)) +			break; +		if (test_and_clear_bit(IGNORE_BULK_OUT, +				       &common->fsg->atomic_bitflags)) +			usb_ep_clear_halt(common->fsg->bulk_in); + +		if (common->ep0_req_tag == exception_req_tag) +			ep0_queue(common);	/* Complete the status stage */ + +		break; + +	case FSG_STATE_CONFIG_CHANGE: +		do_set_interface(common, common->new_fsg); +		break; + +	case FSG_STATE_EXIT: +	case FSG_STATE_TERMINATED: +		do_set_interface(common, NULL);		/* Free resources */ +		common->state = FSG_STATE_TERMINATED;	/* Stop the thread */ +		break; + +	case FSG_STATE_INTERFACE_CHANGE: +	case FSG_STATE_DISCONNECT: +	case FSG_STATE_COMMAND_PHASE: +	case FSG_STATE_DATA_PHASE: +	case FSG_STATE_STATUS_PHASE: +	case FSG_STATE_IDLE: +		break; +	} +} + +/*-------------------------------------------------------------------------*/ + +int fsg_main_thread(void *common_) +{ +	struct fsg_common	*common = the_fsg_common; +	/* The main loop */ +	do { +		if (exception_in_progress(common)) { +			handle_exception(common); +			continue; +		} + +		if (!common->running) { +			sleep_thread(common); +			continue; +		} + +		if (get_next_command(common)) +			continue; + +		if (!exception_in_progress(common)) +			common->state = FSG_STATE_DATA_PHASE; + +		if (do_scsi_command(common) || finish_reply(common)) +			continue; + +		if (!exception_in_progress(common)) +			common->state = FSG_STATE_STATUS_PHASE; + +		if (send_status(common)) +			continue; + +		if (!exception_in_progress(common)) +			common->state = FSG_STATE_IDLE; +	} while (0); + +	common->thread_task = NULL; + +	return 0; +} + +static void fsg_common_release(struct kref *ref); + +static struct fsg_common *fsg_common_init(struct fsg_common *common, +					  struct usb_composite_dev *cdev) +{ +	struct usb_gadget *gadget = cdev->gadget; +	struct fsg_buffhd *bh; +	struct fsg_lun *curlun; +	int nluns, i, rc; + +	/* Find out how many LUNs there should be */ +	nluns = 1; +	if (nluns < 1 || nluns > FSG_MAX_LUNS) { +		printf("invalid number of LUNs: %u\n", nluns); +		return ERR_PTR(-EINVAL); +	} + +	/* Allocate? */ +	if (!common) { +		common = calloc(sizeof *common, 1); +		if (!common) +			return ERR_PTR(-ENOMEM); +		common->free_storage_on_release = 1; +	} else { +		memset(common, 0, sizeof common); +		common->free_storage_on_release = 0; +	} + +	common->ops = NULL; +	common->private_data = NULL; + +	common->gadget = gadget; +	common->ep0 = gadget->ep0; +	common->ep0req = cdev->req; + +	/* Maybe allocate device-global string IDs, and patch descriptors */ +	if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { +		rc = usb_string_id(cdev); +		if (unlikely(rc < 0)) +			goto error_release; +		fsg_strings[FSG_STRING_INTERFACE].id = rc; +		fsg_intf_desc.iInterface = rc; +	} + +	/* Create the LUNs, open their backing files, and register the +	 * LUN devices in sysfs. */ +	curlun = calloc(nluns, sizeof *curlun); +	if (!curlun) { +		rc = -ENOMEM; +		goto error_release; +	} +	common->nluns = nluns; + +	for (i = 0; i < nluns; i++) { +		common->luns[i].removable = 1; + +		rc = fsg_lun_open(&common->luns[i], ""); +		if (rc) +			goto error_luns; +	} +	common->lun = 0; + +	/* Data buffers cyclic list */ +	bh = common->buffhds; + +	i = FSG_NUM_BUFFERS; +	goto buffhds_first_it; +	do { +		bh->next = bh + 1; +		++bh; +buffhds_first_it: +		bh->inreq_busy = 0; +		bh->outreq_busy = 0; +		bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); +		if (unlikely(!bh->buf)) { +			rc = -ENOMEM; +			goto error_release; +		} +	} while (--i); +	bh->next = common->buffhds; + +	snprintf(common->inquiry_string, sizeof common->inquiry_string, +		 "%-8s%-16s%04x", +		 "Linux   ", +		 "File-Store Gadget", +		 0xffff); + +	/* Some peripheral controllers are known not to be able to +	 * halt bulk endpoints correctly.  If one of them is present, +	 * disable stalls. +	 */ + +	/* Tell the thread to start working */ +	common->thread_task = +		kthread_create(fsg_main_thread, common, +			       OR(cfg->thread_name, "file-storage")); +	if (IS_ERR(common->thread_task)) { +		rc = PTR_ERR(common->thread_task); +		goto error_release; +	} + +#undef OR +	/* Information */ +	INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); +	INFO(common, "Number of LUNs=%d\n", common->nluns); + +	return common; + +error_luns: +	common->nluns = i + 1; +error_release: +	common->state = FSG_STATE_TERMINATED;	/* The thread is dead */ +	/* Call fsg_common_release() directly, ref might be not +	 * initialised */ +	fsg_common_release(&common->ref); +	return ERR_PTR(rc); +} + +static void fsg_common_release(struct kref *ref) +{ +	struct fsg_common *common = container_of(ref, struct fsg_common, ref); + +	/* If the thread isn't already dead, tell it to exit now */ +	if (common->state != FSG_STATE_TERMINATED) { +		raise_exception(common, FSG_STATE_EXIT); +		wait_for_completion(&common->thread_notifier); +	} + +	if (likely(common->luns)) { +		struct fsg_lun *lun = common->luns; +		unsigned i = common->nluns; + +		/* In error recovery common->nluns may be zero. */ +		for (; i; --i, ++lun) +			fsg_lun_close(lun); + +		kfree(common->luns); +	} + +	{ +		struct fsg_buffhd *bh = common->buffhds; +		unsigned i = FSG_NUM_BUFFERS; +		do { +			kfree(bh->buf); +		} while (++bh, --i); +	} + +	if (common->free_storage_on_release) +		kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors.  Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors.  Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header ** +usb_copy_descriptors(struct usb_descriptor_header **src) +{ +	struct usb_descriptor_header **tmp; +	unsigned bytes; +	unsigned n_desc; +	void *mem; +	struct usb_descriptor_header **ret; + +	/* count descriptors and their sizes; then add vector size */ +	for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) +		bytes += (*tmp)->bLength; +	bytes += (n_desc + 1) * sizeof(*tmp); + +	mem = kmalloc(bytes, GFP_KERNEL); +	if (!mem) +		return NULL; + +	/* fill in pointers starting at "tmp", +	 * to descriptors copied starting at "mem"; +	 * and return "ret" +	 */ +	tmp = mem; +	ret = mem; +	mem += (n_desc + 1) * sizeof(*tmp); +	while (*src) { +		memcpy(mem, *src, (*src)->bLength); +		*tmp = mem; +		tmp++; +		mem += (*src)->bLength; +		src++; +	} +	*tmp = NULL; + +	return ret; +} + + + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ +	struct fsg_dev		*fsg = fsg_from_func(f); + +	DBG(fsg, "unbind\n"); +	if (fsg->common->fsg == fsg) { +		fsg->common->new_fsg = NULL; +		raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +	} + +	free(fsg->function.descriptors); +	free(fsg->function.hs_descriptors); +	kfree(fsg); +} + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ +	struct fsg_dev		*fsg = fsg_from_func(f); +	struct usb_gadget	*gadget = c->cdev->gadget; +	int			i; +	struct usb_ep		*ep; +	fsg->gadget = gadget; + +	/* New interface */ +	i = usb_interface_id(c, f); +	if (i < 0) +		return i; +	fsg_intf_desc.bInterfaceNumber = i; +	fsg->interface_number = i; + +	/* Find all the endpoints we will use */ +	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); +	if (!ep) +		goto autoconf_fail; +	ep->driver_data = fsg->common;	/* claim the endpoint */ +	fsg->bulk_in = ep; + +	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); +	if (!ep) +		goto autoconf_fail; +	ep->driver_data = fsg->common;	/* claim the endpoint */ +	fsg->bulk_out = ep; + +	/* Copy descriptors */ +	f->descriptors = usb_copy_descriptors(fsg_fs_function); +	if (unlikely(!f->descriptors)) +		return -ENOMEM; + +	if (gadget_is_dualspeed(gadget)) { +		/* Assume endpoint addresses are the same for both speeds */ +		fsg_hs_bulk_in_desc.bEndpointAddress = +			fsg_fs_bulk_in_desc.bEndpointAddress; +		fsg_hs_bulk_out_desc.bEndpointAddress = +			fsg_fs_bulk_out_desc.bEndpointAddress; +		f->hs_descriptors = usb_copy_descriptors(fsg_hs_function); +		if (unlikely(!f->hs_descriptors)) { +			free(f->descriptors); +			return -ENOMEM; +		} +	} +	return 0; + +autoconf_fail: +	ERROR(fsg, "unable to autoconfigure all endpoints\n"); +	return -ENOTSUPP; +} + + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { +	&fsg_stringtab, +	NULL, +}; + +static int fsg_bind_config(struct usb_composite_dev *cdev, +			   struct usb_configuration *c, +			   struct fsg_common *common) +{ +	struct fsg_dev *fsg; +	int rc; + +	fsg = calloc(1, sizeof *fsg); +	if (!fsg) +		return -ENOMEM; +	fsg->function.name        = FSG_DRIVER_DESC; +	fsg->function.strings     = fsg_strings_array; +	fsg->function.bind        = fsg_bind; +	fsg->function.unbind      = fsg_unbind; +	fsg->function.setup       = fsg_setup; +	fsg->function.set_alt     = fsg_set_alt; +	fsg->function.disable     = fsg_disable; + +	fsg->common               = common; +	common->fsg               = fsg; +	/* Our caller holds a reference to common structure so we +	 * don't have to be worry about it being freed until we return +	 * from this function.  So instead of incrementing counter now +	 * and decrement in error recovery we increment it only when +	 * call to usb_add_function() was successful. */ + +	rc = usb_add_function(c, &fsg->function); + +	if (rc) +		kfree(fsg); + +	return rc; +} + +int fsg_add(struct usb_configuration *c) +{ +	struct fsg_common *fsg_common; + +	fsg_common = fsg_common_init(NULL, c->cdev); + +	fsg_common->vendor_name = 0; +	fsg_common->product_name = 0; +	fsg_common->release = 0xffff; + +	fsg_common->ops = NULL; +	fsg_common->private_data = NULL; + +	the_fsg_common = fsg_common; + +	return fsg_bind_config(c->cdev, c, fsg_common); +} + +int fsg_init(struct ums_board_info *ums) +{ +	ums_info = ums; + +	return 0; +} diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index a5a4c1fe6..cc3f3449c 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -31,6 +31,7 @@  #include "gadget_chips.h"  #include "composite.c" +#include "f_mass_storage.c"  /*   * One needs to define the following: @@ -104,6 +105,8 @@ static int g_dnl_do_config(struct usb_configuration *c)  	printf("GADGET DRIVER: %s\n", s);  	if (!strcmp(s, "usb_dnl_dfu"))  		ret = dfu_add(c); +	else if (!strcmp(s, "usb_dnl_ums")) +		ret = fsg_add(c);  	return ret;  } @@ -188,6 +191,9 @@ int g_dnl_register(const char *type)  	if (!strcmp(type, "dfu")) {  		strcpy(name, shortname);  		strcat(name, type); +	} else if (!strcmp(type, "ums")) { +		strcpy(name, shortname); +		strcat(name, type);  	} else {  		printf("%s: unknown command: %s\n", __func__, type);  		return -EINVAL; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c new file mode 100644 index 000000000..594dc10af --- /dev/null +++ b/drivers/usb/gadget/storage_common.c @@ -0,0 +1,653 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * Ported to u-boot: + * Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * Code refactoring & cleanup: + * Łukasz Majewski <l.majewski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + *  - fsg_string_manufacturer -- name of the manufacturer + *  - fsg_string_product      -- name of the product + *  - fsg_string_serial       -- product's serial + *  - fsg_string_config       -- name of the configuration + *  - fsg_string_interface    -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* + * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and + * fsg_hs_intr_in_desc objects as well as + * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES + * macros are not defined. + * + * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER, + * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not + * defined (as well as corresponding entries in string tables are + * missing) and FSG_STRING_INTERFACE has value of zero. + * + * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. + */ + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + + +/* #include <asm/unaligned.h> */ + + +/* + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!!  Ever!! + * Instead:  allocate your own, using normal USB-IF procedures. + */ +#define FSG_VENDOR_ID	0x0525	/* NetChip */ +#define FSG_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */ + +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG	LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +/* +#define LDBG(lun, fmt, args...)   dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...)  dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...)  dev_info(&(lun)->dev, fmt, ## args) +*/ + +#define LDBG(lun, fmt, args...) do { } while (0) +#define LERROR(lun, fmt, args...) do { } while (0) +#define LWARN(lun, fmt, args...) do { } while (0) +#define LINFO(lun, fmt, args...) do { } while (0) + +/* + * Keep those macros in sync with those in + * include/linux/usb/composite.h or else GCC will complain.  If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. + * + * Those macros are needed here because File Storage Gadget does not + * include the composite.h header.  For composite gadgets those macros + * are redundant since composite.h is included any way. + * + * One could check whether those macros are already defined (which + * would indicate composite.h had been included) or not (which would + * indicate we were in FSG) but this is not done because a warning is + * desired if definitions here differ from the ones in composite.h. + * + * We want the definitions to match and be the same in File Storage + * Gadget as well as Mass Storage Function (and so composite gadgets + * using MSF).  If someone changes them in composite.h it will produce + * a warning in this file when building MSF. + */ + +#define DBG(d, fmt, args...)     debug(fmt , ## args) +#define VDBG(d, fmt, args...)    debug(fmt , ## args) +/* #define ERROR(d, fmt, args...)   printf(fmt , ## args) */ +/* #define WARNING(d, fmt, args...) printf(fmt , ## args) */ +/* #define INFO(d, fmt, args...)    printf(fmt , ## args) */ + +/* #define DBG(d, fmt, args...)     do { } while (0) */ +/* #define VDBG(d, fmt, args...)    do { } while (0) */ +#define ERROR(d, fmt, args...)   do { } while (0) +#define WARNING(d, fmt, args...) do { } while (0) +#define INFO(d, fmt, args...)    do { } while (0) + +#ifdef DUMP_MSGS + +/* dump_msg(fsg, const char * label, const u8 * buf, unsigned length); */ +# define dump_msg(fsg, label, buf, length) do {                         \ +	if (length < 512) {						\ +		DBG(fsg, "%s, length %u:\n", label, length);		\ +		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,	\ +			       16, 1, buf, length, 0);			\ +	}								\ +} while (0) + +#  define dump_cdb(fsg) do { } while (0) + +#else + +#  define dump_msg(fsg, /* const char * */ label, \ +		   /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +#  ifdef VERBOSE_DEBUG + +#    define dump_cdb(fsg)						\ +	print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,	\ +		       16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)		\ + +#  else + +#    define dump_cdb(fsg) do { } while (0) + +#  endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/*-------------------------------------------------------------------------*/ + +/* SCSI device types */ +#define TYPE_DISK	0x00 +#define TYPE_CDROM	0x05 + +/* USB protocol value = the transport method */ +#define USB_PR_CBI	0x00		/* Control/Bulk/Interrupt */ +#define USB_PR_CB	0x01		/* Control/Bulk w/o interrupt */ +#define USB_PR_BULK	0x50		/* Bulk-only */ + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_RBC	0x01		/* Reduced Block Commands (flash) */ +#define USB_SC_8020	0x02		/* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ +#define USB_SC_QIC	0x03		/* QIC-157 (tape) */ +#define USB_SC_UFI	0x04		/* UFI (floppy) */ +#define USB_SC_8070	0x05		/* SFF-8070i (removable) */ +#define USB_SC_SCSI	0x06		/* Transparent SCSI */ + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct fsg_bulk_cb_wrap { +	__le32	Signature;		/* Contains 'USBC' */ +	u32	Tag;			/* Unique per command id */ +	__le32	DataTransferLength;	/* Size of the data */ +	u8	Flags;			/* Direction in bit 7 */ +	u8	Lun;			/* LUN (normally 0) */ +	u8	Length;			/* Of the CDB, <= MAX_COMMAND_SIZE */ +	u8	CDB[16];		/* Command Data Block */ +}; + +#define USB_BULK_CB_WRAP_LEN	31 +#define USB_BULK_CB_SIG		0x43425355	/* Spells out USBC */ +#define USB_BULK_IN_FLAG	0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { +	__le32	Signature;		/* Should = 'USBS' */ +	u32	Tag;			/* Same as original command */ +	__le32	Residue;		/* Amount not transferred */ +	u8	Status;			/* See below */ +}; + +#define USB_BULK_CS_WRAP_LEN	13 +#define USB_BULK_CS_SIG		0x53425355	/* Spells out 'USBS' */ +#define USB_STATUS_PASS		0 +#define USB_STATUS_FAIL		1 +#define USB_STATUS_PHASE_ERROR	2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST		0xff +#define USB_BULK_GET_MAX_LUN_REQUEST	0xfe + +/* CBI Interrupt data structure */ +struct interrupt_data { +	u8	bType; +	u8	bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN		2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST		0x00 + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE	16 + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT			0x04 +#define SC_INQUIRY			0x12 +#define SC_MODE_SELECT_6		0x15 +#define SC_MODE_SELECT_10		0x55 +#define SC_MODE_SENSE_6			0x1a +#define SC_MODE_SENSE_10		0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL	0x1e +#define SC_READ_6			0x08 +#define SC_READ_10			0x28 +#define SC_READ_12			0xa8 +#define SC_READ_CAPACITY		0x25 +#define SC_READ_FORMAT_CAPACITIES	0x23 +#define SC_READ_HEADER			0x44 +#define SC_READ_TOC			0x43 +#define SC_RELEASE			0x17 +#define SC_REQUEST_SENSE		0x03 +#define SC_RESERVE			0x16 +#define SC_SEND_DIAGNOSTIC		0x1d +#define SC_START_STOP_UNIT		0x1b +#define SC_SYNCHRONIZE_CACHE		0x35 +#define SC_TEST_UNIT_READY		0x00 +#define SC_VERIFY			0x2f +#define SC_WRITE_6			0x0a +#define SC_WRITE_10			0x2a +#define SC_WRITE_12			0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE				0 +#define SS_COMMUNICATION_FAILURE		0x040800 +#define SS_INVALID_COMMAND			0x052000 +#define SS_INVALID_FIELD_IN_CDB			0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500 +#define SS_MEDIUM_NOT_PRESENT			0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED		0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION	0x062800 +#define SS_RESET_OCCURRED			0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900 +#define SS_UNRECOVERED_READ_ERROR		0x031100 +#define SS_WRITE_ERROR				0x030c02 +#define SS_WRITE_PROTECTED			0x072700 + +#define SK(x)		((u8) ((x) >> 16))	/* Sense Key byte, etc. */ +#define ASC(x)		((u8) ((x) >> 8)) +#define ASCQ(x)		((u8) (x)) + +struct device_attribute { int i; }; +struct rw_semaphore { int i; }; +#define down_write(...)			do { } while (0) +#define up_write(...)			do { } while (0) +#define down_read(...)			do { } while (0) +#define up_read(...)			do { } while (0) +#define ETOOSMALL	525 + +#include <usb_mass_storage.h> +extern struct ums_board_info		*ums_info; + +/*-------------------------------------------------------------------------*/ + +struct fsg_lun { +	loff_t		file_length; +	loff_t		num_sectors; + +	unsigned int	initially_ro:1; +	unsigned int	ro:1; +	unsigned int	removable:1; +	unsigned int	cdrom:1; +	unsigned int	prevent_medium_removal:1; +	unsigned int	registered:1; +	unsigned int	info_valid:1; +	unsigned int	nofua:1; + +	u32		sense_data; +	u32		sense_data_info; +	u32		unit_attention_data; + +	struct device	dev; +}; + +#define fsg_lun_is_open(curlun)	((curlun)->filp != NULL) +#if 0 +static struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ +	return container_of(dev, struct fsg_lun, dev); +} +#endif + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE	256 +#define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */ + +/* Number of buffers we will use.  2 is enough for double-buffering */ +#define FSG_NUM_BUFFERS	2 + +/* Default size of buffer length. */ +#define FSG_BUFLEN	((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS	8 + +enum fsg_buffer_state { +	BUF_STATE_EMPTY = 0, +	BUF_STATE_FULL, +	BUF_STATE_BUSY +}; + +struct fsg_buffhd { +#ifdef FSG_BUFFHD_STATIC_BUFFER +	char				buf[FSG_BUFLEN]; +#else +	void				*buf; +#endif +	enum fsg_buffer_state		state; +	struct fsg_buffhd		*next; + +	/* +	 * The NetChip 2280 is faster, and handles some protocol faults +	 * better, if we don't submit any short bulk-out read requests. +	 * So we will record the intended request length here. +	 */ +	unsigned int			bulk_out_intended_length; + +	struct usb_request		*inreq; +	int				inreq_busy; +	struct usb_request		*outreq; +	int				outreq_busy; +}; + +enum fsg_state { +	/* This one isn't used anywhere */ +	FSG_STATE_COMMAND_PHASE = -10, +	FSG_STATE_DATA_PHASE, +	FSG_STATE_STATUS_PHASE, + +	FSG_STATE_IDLE = 0, +	FSG_STATE_ABORT_BULK_OUT, +	FSG_STATE_RESET, +	FSG_STATE_INTERFACE_CHANGE, +	FSG_STATE_CONFIG_CHANGE, +	FSG_STATE_DISCONNECT, +	FSG_STATE_EXIT, +	FSG_STATE_TERMINATED +}; + +enum data_direction { +	DATA_DIR_UNKNOWN = 0, +	DATA_DIR_FROM_HOST, +	DATA_DIR_TO_HOST, +	DATA_DIR_NONE +}; + +/*-------------------------------------------------------------------------*/ + +static inline u32 get_unaligned_be24(u8 *buf) +{ +	return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +/*-------------------------------------------------------------------------*/ + +enum { +#ifndef FSG_NO_DEVICE_STRINGS +	FSG_STRING_MANUFACTURER	= 1, +	FSG_STRING_PRODUCT, +	FSG_STRING_SERIAL, +	FSG_STRING_CONFIG, +#endif +	FSG_STRING_INTERFACE +}; + +#ifndef FSG_NO_OTG +static struct usb_otg_descriptor +fsg_otg_desc = { +	.bLength =		sizeof fsg_otg_desc, +	.bDescriptorType =	USB_DT_OTG, + +	.bmAttributes =		USB_OTG_SRP, +}; +#endif + +/* There is only one interface. */ + +static struct usb_interface_descriptor +fsg_intf_desc = { +	.bLength =		sizeof fsg_intf_desc, +	.bDescriptorType =	USB_DT_INTERFACE, + +	.bNumEndpoints =	2,		/* Adjusted during fsg_bind() */ +	.bInterfaceClass =	USB_CLASS_MASS_STORAGE, +	.bInterfaceSubClass =	USB_SC_SCSI,	/* Adjusted during fsg_bind() */ +	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */ +	.iInterface =		FSG_STRING_INTERFACE, +}; + +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ + +static struct usb_endpoint_descriptor +fsg_fs_bulk_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	USB_DIR_IN, +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	/* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fsg_fs_bulk_out_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	USB_DIR_OUT, +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	/* wMaxPacketSize set by autoconfiguration */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_fs_intr_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	USB_DIR_IN, +	.bmAttributes =		USB_ENDPOINT_XFER_INT, +	.wMaxPacketSize =	cpu_to_le16(2), +	.bInterval =		32,	/* frames -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +#  define FSG_FS_FUNCTION_PRE_EP_ENTRIES	2 +#else +#  define FSG_FS_FUNCTION_PRE_EP_ENTRIES	1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_fs_function[] = { +#ifndef FSG_NO_OTG +	(struct usb_descriptor_header *) &fsg_otg_desc, +#endif +	(struct usb_descriptor_header *) &fsg_intf_desc, +	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, +	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP +	(struct usb_descriptor_header *) &fsg_fs_intr_in_desc, +#endif +	NULL, +}; + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the configuration descriptor. + */ +static struct usb_endpoint_descriptor +fsg_hs_bulk_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	.wMaxPacketSize =	cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +fsg_hs_bulk_out_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	.wMaxPacketSize =	cpu_to_le16(512), +	.bInterval =		1,	/* NAK every 1 uframe */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_hs_intr_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ +	.bmAttributes =		USB_ENDPOINT_XFER_INT, +	.wMaxPacketSize =	cpu_to_le16(2), +	.bInterval =		9,	/* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +#  define FSG_HS_FUNCTION_PRE_EP_ENTRIES	2 +#else +#  define FSG_HS_FUNCTION_PRE_EP_ENTRIES	1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_hs_function[] = { +#ifndef FSG_NO_OTG +	(struct usb_descriptor_header *) &fsg_otg_desc, +#endif +	(struct usb_descriptor_header *) &fsg_intf_desc, +	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, +	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP +	(struct usb_descriptor_header *) &fsg_hs_intr_in_desc, +#endif +	NULL, +}; + +/* Maxpacket and other transfer characteristics vary by speed. */ +static struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, +		struct usb_endpoint_descriptor *hs) +{ +	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +		return hs; +	return fs; +} + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string		fsg_strings[] = { +#ifndef FSG_NO_DEVICE_STRINGS +	{FSG_STRING_MANUFACTURER,	fsg_string_manufacturer}, +	{FSG_STRING_PRODUCT,		fsg_string_product}, +	{FSG_STRING_SERIAL,		fsg_string_serial}, +	{FSG_STRING_CONFIG,		fsg_string_config}, +#endif +	{FSG_STRING_INTERFACE,		fsg_string_interface}, +	{} +}; + +static struct usb_gadget_strings	fsg_stringtab = { +	.language	= 0x0409,		/* en-us */ +	.strings	= fsg_strings, +}; + +/*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +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; +	debug("open backing file: %s\n", filename); +	rc = 0; + +out: +	return rc; +} + +static void fsg_lun_close(struct fsg_lun *curlun) +{ +} + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ +	return 0; +} + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ +	if (msf) { +		/* Convert to Minutes-Seconds-Frames */ +		addr >>= 2;		/* Convert to 2048-byte frames */ +		addr += 2*75;		/* Lead-in occupies 2 seconds */ +		dest[3] = addr % 75;	/* Frames */ +		addr /= 75; +		dest[2] = addr % 60;	/* Seconds */ +		addr /= 60; +		dest[1] = addr;		/* Minutes */ +		dest[0] = 0;		/* Reserved */ +	} else { +		/* Absolute sector */ +		put_unaligned_be32(addr, dest); +	} +} + +/*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6c9479492..9a6f98208 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -54,6 +54,7 @@ COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o  COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o  COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o  COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +COBJS-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o  COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o  COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 3ca4c5c33..0c797aa04 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -42,11 +42,15 @@ DECLARE_GLOBAL_DATA_PTR;   */  struct exynos_ehci {  	struct exynos_usb_phy *usb; -	unsigned int *hcd; +	struct ehci_hccr *hcd;  }; +static struct exynos_ehci exynos; + +#ifdef CONFIG_OF_CONTROL  static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)  { +	fdt_addr_t addr;  	unsigned int node;  	int depth; @@ -59,12 +63,14 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)  	/*  	 * Get the base address for EHCI controller from the device node  	 */ -	exynos->hcd = (unsigned int *)fdtdec_get_addr(blob, node, "reg"); -	if (exynos->hcd == NULL) { +	addr = fdtdec_get_addr(blob, node, "reg"); +	if (addr == FDT_ADDR_T_NONE) {  		debug("Can't get the EHCI register address\n");  		return -ENXIO;  	} +	exynos->hcd = (struct ehci_hccr *)addr; +  	depth = 0;  	node = fdtdec_next_compatible_subnode(blob, node,  					COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); @@ -85,6 +91,7 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)  	return 0;  } +#endif  /* Setup the EHCI host controller. */  static void setup_usb_phy(struct exynos_usb_phy *usb) @@ -144,20 +151,21 @@ static void reset_usb_phy(struct exynos_usb_phy *usb)   */  int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)  { -	struct exynos_ehci *exynos = NULL; +	struct exynos_ehci *ctx = &exynos; -	exynos = (struct exynos_ehci *) -			kzalloc(sizeof(struct exynos_ehci), GFP_KERNEL); -	if (!exynos) { -		debug("failed to allocate exynos ehci context\n"); -		return -ENOMEM; +#ifdef CONFIG_OF_CONTROL +	if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { +		debug("Unable to parse device tree for ehci-exynos\n"); +		return -ENODEV;  	} +#else +	ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); +	ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); +#endif -	exynos_usb_parse_dt(gd->fdt_blob, exynos); +	setup_usb_phy(ctx->usb); -	setup_usb_phy(exynos->usb); - -	*hccr = (struct ehci_hccr *)(exynos->hcd); +	*hccr = ctx->hcd;  	*hcor = (struct ehci_hcor *)((uint32_t) *hccr  				+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); @@ -165,8 +173,6 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)  		(uint32_t)*hccr, (uint32_t)*hcor,  		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); -	kfree(exynos); -  	return 0;  } @@ -176,20 +182,9 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)   */  int ehci_hcd_stop(int index)  { -	struct exynos_ehci *exynos = NULL; - -	exynos = (struct exynos_ehci *) -			kzalloc(sizeof(struct exynos_ehci), GFP_KERNEL); -	if (!exynos) { -		debug("failed to allocate exynos ehci context\n"); -		return -ENOMEM; -	} - -	exynos_usb_parse_dt(gd->fdt_blob, exynos); - -	reset_usb_phy(exynos->usb); +	struct exynos_ehci *ctx = &exynos; -	kfree(exynos); +	reset_usb_phy(ctx->usb);  	return 0;  } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7f98a6354..c81687820 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -21,12 +21,14 @@   * MA 02111-1307 USA   */  #include <common.h> +#include <errno.h>  #include <asm/byteorder.h>  #include <asm/unaligned.h>  #include <usb.h>  #include <asm/io.h>  #include <malloc.h>  #include <watchdog.h> +#include <linux/compiler.h>  #include "ehci.h" @@ -39,7 +41,10 @@ static struct ehci_ctrl {  	struct ehci_hcor *hcor;  	int rootdev;  	uint16_t portreset; -	struct QH qh_list __attribute__((aligned(USB_DMA_MINALIGN))); +	struct QH qh_list __aligned(USB_DMA_MINALIGN); +	struct QH periodic_queue __aligned(USB_DMA_MINALIGN); +	uint32_t *periodic_list; +	int ntds;  } ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];  #define ALIGN_END_ADDR(type, ptr, size)			\ @@ -858,6 +863,8 @@ int usb_lowlevel_init(int index, void **controller)  	uint32_t reg;  	uint32_t cmd;  	struct QH *qh_list; +	struct QH *periodic; +	int i;  	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))  		return -1; @@ -870,6 +877,9 @@ int usb_lowlevel_init(int index, void **controller)  	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))  		return -1;  #endif +	/* Set the high address word (aka segment) for 64-bit controller */ +	if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1) +		ehci_writel(ehcic[index].hcor->or_ctrldssegment, 0);  	qh_list = &ehcic[index].qh_list; @@ -884,6 +894,40 @@ int usb_lowlevel_init(int index, void **controller)  	qh_list->qh_overlay.qt_token =  			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); +	/* Set async. queue head pointer. */ +	ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); + +	/* +	 * Set up periodic list +	 * Step 1: Parent QH for all periodic transfers. +	 */ +	periodic = &ehcic[index].periodic_queue; +	memset(periodic, 0, sizeof(*periodic)); +	periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); +	periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +	periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + +	/* +	 * Step 2: Setup frame-list: Every microframe, USB tries the same list. +	 *         In particular, device specifications on polling frequency +	 *         are disregarded. Keyboards seem to send NAK/NYet reliably +	 *         when polled with an empty buffer. +	 * +	 *         Split Transactions will be spread across microframes using +	 *         S-mask and C-mask. +	 */ +	ehcic[index].periodic_list = memalign(4096, 1024*4); +	if (!ehcic[index].periodic_list) +		return -ENOMEM; +	for (i = 0; i < 1024; i++) { +		ehcic[index].periodic_list[i] = (uint32_t)periodic +						| QH_LINK_TYPE_QH; +	} + +	/* Set periodic list base address */ +	ehci_writel(&ehcic[index].hcor->or_periodiclistbase, +		(uint32_t)ehcic[index].periodic_list); +  	reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams);  	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);  	debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); @@ -953,10 +997,254 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	return ehci_submit_async(dev, pipe, buffer, length, setup);  } +struct int_queue { +	struct QH *first; +	struct QH *current; +	struct QH *last; +	struct qTD *tds; +}; + +#define NEXT_QH(qh) (struct QH *)((qh)->qh_link & ~0x1f) + +static int +enable_periodic(struct ehci_ctrl *ctrl) +{ +	uint32_t cmd; +	struct ehci_hcor *hcor = ctrl->hcor; +	int ret; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd |= CMD_PSE; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&hcor->or_usbsts, +			STS_PSS, STS_PSS, 100 * 1000); +	if (ret < 0) { +		printf("EHCI failed: timeout when enabling periodic list\n"); +		return -ETIMEDOUT; +	} +	udelay(1000); +	return 0; +} + +static int +disable_periodic(struct ehci_ctrl *ctrl) +{ +	uint32_t cmd; +	struct ehci_hcor *hcor = ctrl->hcor; +	int ret; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd &= ~CMD_PSE; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&hcor->or_usbsts, +			STS_PSS, 0, 100 * 1000); +	if (ret < 0) { +		printf("EHCI failed: timeout when disabling periodic list\n"); +		return -ETIMEDOUT; +	} +	return 0; +} + +static int periodic_schedules; + +struct int_queue * +create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, +		 int elementsize, void *buffer) +{ +	struct ehci_ctrl *ctrl = dev->controller; +	struct int_queue *result = NULL; +	int i; + +	debug("Enter create_int_queue\n"); +	if (usb_pipetype(pipe) != PIPE_INTERRUPT) { +		debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); +		return NULL; +	} + +	/* limit to 4 full pages worth of data - +	 * we can safely fit them in a single TD, +	 * no matter the alignment +	 */ +	if (elementsize >= 16384) { +		debug("too large elements for interrupt transfers\n"); +		return NULL; +	} + +	result = malloc(sizeof(*result)); +	if (!result) { +		debug("ehci intr queue: out of memory\n"); +		goto fail1; +	} +	result->first = memalign(32, sizeof(struct QH) * queuesize); +	if (!result->first) { +		debug("ehci intr queue: out of memory\n"); +		goto fail2; +	} +	result->current = result->first; +	result->last = result->first + queuesize - 1; +	result->tds = memalign(32, sizeof(struct qTD) * queuesize); +	if (!result->tds) { +		debug("ehci intr queue: out of memory\n"); +		goto fail3; +	} +	memset(result->first, 0, sizeof(struct QH) * queuesize); +	memset(result->tds, 0, sizeof(struct qTD) * queuesize); + +	for (i = 0; i < queuesize; i++) { +		struct QH *qh = result->first + i; +		struct qTD *td = result->tds + i; +		void **buf = &qh->buffer; + +		qh->qh_link = (uint32_t)(qh+1) | QH_LINK_TYPE_QH; +		if (i == queuesize - 1) +			qh->qh_link = QH_LINK_TERMINATE; + +		qh->qh_overlay.qt_next = (uint32_t)td; +		qh->qh_endpt1 = (0 << 28) | /* No NAK reload (ehci 4.9) */ +			(usb_maxpacket(dev, pipe) << 16) | /* MPS */ +			(1 << 14) | +			QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | +			(usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */ +			(usb_pipedevice(pipe) << 0); +		qh->qh_endpt2 = (1 << 30) | /* 1 Tx per mframe */ +			(1 << 0); /* S-mask: microframe 0 */ +		if (dev->speed == USB_SPEED_LOW || +				dev->speed == USB_SPEED_FULL) { +			debug("TT: port: %d, hub address: %d\n", +				dev->portnr, dev->parent->devnum); +			qh->qh_endpt2 |= (dev->portnr << 23) | +				(dev->parent->devnum << 16) | +				(0x1c << 8); /* C-mask: microframes 2-4 */ +		} + +		td->qt_next = QT_NEXT_TERMINATE; +		td->qt_altnext = QT_NEXT_TERMINATE; +		debug("communication direction is '%s'\n", +		      usb_pipein(pipe) ? "in" : "out"); +		td->qt_token = (elementsize << 16) | +			((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ +			0x80; /* active */ +		td->qt_buffer[0] = (uint32_t)buffer + i * elementsize; +		td->qt_buffer[1] = (td->qt_buffer[0] + 0x1000) & ~0xfff; +		td->qt_buffer[2] = (td->qt_buffer[0] + 0x2000) & ~0xfff; +		td->qt_buffer[3] = (td->qt_buffer[0] + 0x3000) & ~0xfff; +		td->qt_buffer[4] = (td->qt_buffer[0] + 0x4000) & ~0xfff; + +		*buf = buffer + i * elementsize; +	} + +	if (disable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto fail3; +	} + +	/* hook up to periodic list */ +	struct QH *list = &ctrl->periodic_queue; +	result->last->qh_link = list->qh_link; +	list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH; + +	if (enable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto fail3; +	} +	periodic_schedules++; + +	debug("Exit create_int_queue\n"); +	return result; +fail3: +	if (result->tds) +		free(result->tds); +fail2: +	if (result->first) +		free(result->first); +	if (result) +		free(result); +fail1: +	return NULL; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ +	struct QH *cur = queue->current; + +	/* depleted queue */ +	if (cur == NULL) { +		debug("Exit poll_int_queue with completed queue\n"); +		return NULL; +	} +	/* still active */ +	if (cur->qh_overlay.qt_token & 0x80) { +		debug("Exit poll_int_queue with no completed intr transfer. " +		      "token is %x\n", cur->qh_overlay.qt_token); +		return NULL; +	} +	if (!(cur->qh_link & QH_LINK_TERMINATE)) +		queue->current++; +	else +		queue->current = NULL; +	debug("Exit poll_int_queue with completed intr transfer. " +	      "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, +	      &cur->qh_overlay.qt_token, queue->first); +	return cur->buffer; +} + +/* Do not free buffers associated with QHs, they're owned by someone else */ +int +destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ +	struct ehci_ctrl *ctrl = dev->controller; +	int result = -1; +	unsigned long timeout; + +	if (disable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto out; +	} +	periodic_schedules--; + +	struct QH *cur = &ctrl->periodic_queue; +	timeout = get_timer(0) + 500; /* abort after 500ms */ +	while (!(cur->qh_link & QH_LINK_TERMINATE)) { +		debug("considering %p, with qh_link %x\n", cur, cur->qh_link); +		if (NEXT_QH(cur) == queue->first) { +			debug("found candidate. removing from chain\n"); +			cur->qh_link = queue->last->qh_link; +			result = 0; +			break; +		} +		cur = NEXT_QH(cur); +		if (get_timer(0) > timeout) { +			printf("Timeout destroying interrupt endpoint queue\n"); +			result = -1; +			goto out; +		} +	} + +	if (periodic_schedules > 0) { +		result = enable_periodic(ctrl); +		if (result < 0) +			debug("FATAL: periodic should never fail, but did"); +	} + +out: +	free(queue->tds); +	free(queue->first); +	free(queue); + +	return result; +} +  int  submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	       int length, int interval)  { +	void *backbuffer; +	struct int_queue *queue; +	unsigned long timeout; +	int result = 0, ret; +  	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",  	      dev, pipe, buffer, length, interval); @@ -972,9 +1260,31 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	 * not require more than a single qTD.  	 */  	if (length > usb_maxpacket(dev, pipe)) { -		printf("%s: Interrupt transfers requiring several transactions " -			"are not supported.\n", __func__); +		printf("%s: Interrupt transfers requiring several " +			"transactions are not supported.\n", __func__);  		return -1;  	} -	return ehci_submit_async(dev, pipe, buffer, length, NULL); + +	queue = create_int_queue(dev, pipe, 1, length, buffer); + +	timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); +	while ((backbuffer = poll_int_queue(dev, queue)) == NULL) +		if (get_timer(0) > timeout) { +			printf("Timeout poll on interrupt endpoint\n"); +			result = -ETIMEDOUT; +			break; +		} + +	if (backbuffer != buffer) { +		debug("got wrong buffer back (%x instead of %x)\n", +		      (uint32_t)backbuffer, (uint32_t)buffer); +		return -EINVAL; +	} + +	ret = destroy_int_queue(dev, queue); +	if (ret < 0) +		return ret; + +	/* everything worked out fine */ +	return result;  } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 29af02dc5..90d7a6feb 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -19,6 +19,7 @@   */  #include <common.h> +#include <errno.h>  #include <pci.h>  #include <usb.h> @@ -32,31 +33,76 @@ static struct pci_device_id ehci_pci_ids[] = {  	{0x12D8, 0x400F},	/* Pericom */  	{0, 0}  }; +#else +static pci_dev_t ehci_find_class(int index) +{ +	int bus; +	int devnum; +	pci_dev_t bdf; +	uint32_t class; + +	for (bus = 0; bus <= pci_last_busno(); bus++) { +		for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) { +			pci_read_config_dword(PCI_BDF(bus, devnum, 0), +					      PCI_CLASS_REVISION, &class); +			if (class >> 16 == 0xffff) +				continue; + +			for (bdf = PCI_BDF(bus, devnum, 0); +					bdf <= PCI_BDF(bus, devnum, +						PCI_MAX_PCI_FUNCTIONS - 1); +					bdf += PCI_BDF(0, 0, 1)) { +				pci_read_config_dword(bdf, PCI_CLASS_REVISION, +						      &class); +				if ((class >> 8 == PCI_CLASS_SERIAL_USB_EHCI) +						&& !index--) +					return bdf; +			} +		} +	} + +	return -ENODEV; +}  #endif  /*   * Create the appropriate control structures to manage   * a new EHCI host controller.   */ -int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr, +		struct ehci_hcor **ret_hcor)  {  	pci_dev_t pdev; +	uint32_t cmd; +	struct ehci_hccr *hccr; +	struct ehci_hcor *hcor; +#ifdef CONFIG_PCI_EHCI_DEVICE  	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); -	if (pdev == -1) { +#else +	pdev = ehci_find_class(index); +#endif +	if (pdev < 0) {  		printf("EHCI host controller not found\n");  		return -1;  	} -	*hccr = (struct ehci_hccr *)pci_map_bar(pdev, +	hccr = (struct ehci_hccr *)pci_map_bar(pdev,  			PCI_BASE_ADDRESS_0, PCI_REGION_MEM); -	*hcor = (struct ehci_hcor *)((uint32_t) *hccr + -			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +	hcor = (struct ehci_hcor *)((uint32_t) hccr + +			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));  	debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n", -			(uint32_t)*hccr, (uint32_t)*hcor, -			(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +			(uint32_t)hccr, (uint32_t)hcor, +			(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + +	*ret_hccr = hccr; +	*ret_hcor = hcor; +	/* enable busmaster */ +	pci_read_config_dword(pdev, PCI_COMMAND, &cmd); +	cmd |= PCI_COMMAND_MASTER; +	pci_write_config_dword(pdev, PCI_COMMAND, cmd);  	return 0;  } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c new file mode 100644 index 000000000..f99bd1f69 --- /dev/null +++ b/drivers/usb/host/ehci-spear.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2010 + * Armando Visconti, ST Micoelectronics, <armando.visconti@st.com>. + * + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include "ehci.h" +#include <asm/arch/hardware.h> + + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	*hccr = (struct ehci_hccr *)(CONFIG_SYS_UHC0_EHCI_BASE + 0x100); +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr +			+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	debug("SPEAr-ehci: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 1e3cd793b..d090f0a53 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -69,6 +69,7 @@ struct ehci_hcor {  #define CMD_RUN		(1 << 0)		/* start/stop HC */  	uint32_t or_usbsts;  #define STS_ASS		(1 << 15) +#define	STS_PSS		(1 << 14)  #define STS_HALT	(1 << 12)  	uint32_t or_usbintr;  #define INTR_UE         (1 << 0)                /* USB interrupt enable */ @@ -245,7 +246,10 @@ struct QH {  	 * Add dummy fill value to make the size of this struct  	 * aligned to 32 bytes  	 */ -	uint8_t fill[16]; +	union { +		uint32_t fill[4]; +		void *buffer; +	};  };  /* Low level init functions */ diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h index 49f05decc..87daf6268 100644 --- a/include/configs/coreboot.h +++ b/include/configs/coreboot.h @@ -273,6 +273,23 @@   */  #define CONFIG_PCI +/*----------------------------------------------------------------------- + * USB configuration + */ +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_PCI +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS     12 +#define CONFIG_USB_MAX_CONTROLLER_COUNT        2 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_KEYBOARD +#define CONFIG_SYS_USB_EVENT_POLL + +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_USB_ETHER_SMSC95XX + +#define CONFIG_CMD_USB +  #define CONFIG_EXTRA_ENV_SETTINGS \  	CONFIG_STD_DEVICES_SETTINGS diff --git a/include/configs/trats.h b/include/configs/trats.h index 63745ac8f..31d81901b 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -316,4 +316,9 @@  #define CONFIG_VIDEO_BMP_GZIP  #define CONFIG_SYS_VIDEO_LOGO_MAX_SIZE ((500 * 120 * 4) + (1 << 12)) +#define CONFIG_CMD_USB_MASS_STORAGE +#if defined(CONFIG_CMD_USB_MASS_STORAGE) +#define CONFIG_USB_GADGET_MASS_STORAGE +#endif +  #endif	/* __CONFIG_H */ diff --git a/include/usb_mass_storage.h b/include/usb_mass_storage.h new file mode 100644 index 000000000..ffc3a13c2 --- /dev/null +++ b/include/usb_mass_storage.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 Samsung Electrnoics + * Lukasz Majewski <l.majewski@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * aloong with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __USB_MASS_STORAGE_H__ +#define __USB_MASS_STORAGE_H__ + +#define SECTOR_SIZE		0x200 + +#include <mmc.h> + +struct ums_device { +	struct mmc *mmc; +	int dev_num; +	int offset; +	int part_size; +}; + +struct ums_board_info { +	int (*read_sector)(struct ums_device *ums_dev, +			   ulong start, lbaint_t blkcnt, void *buf); +	int (*write_sector)(struct ums_device *ums_dev, +			    ulong start, lbaint_t blkcnt, const void *buf); +	void (*get_capacity)(struct ums_device *ums_dev, +			     long long int *capacity); +	const char *name; +	struct ums_device ums_dev; +}; + +extern void board_usb_init(void); + +extern int fsg_init(struct ums_board_info *); +extern void fsg_cleanup(void); +extern struct ums_board_info *board_ums_init(unsigned int, +					     unsigned int, unsigned int); +extern int usb_gadget_handle_interrupts(void); +extern int fsg_main_thread(void *); + +#endif /* __USB_MASS_STORAGE_H__ */ diff --git a/include/usbdevice.h b/include/usbdevice.h index 3edaf8bcc..7037efd33 100644 --- a/include/usbdevice.h +++ b/include/usbdevice.h @@ -475,7 +475,9 @@ typedef struct urb_link {   * function driver to inform it that data has arrived.   */ -#define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */ +/* in linux we'd malloc this, but in u-boot we prefer static data */ +#define URB_BUF_SIZE 512 +  struct urb {  	struct usb_endpoint_instance *endpoint; |