Merge branch 'master' of git://git.denx.de/u-boot-usb
This commit is contained in:
		
						commit
						3c47f2f487
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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()) | ||||
|  |  | |||
|  | @ -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" | ||||
| ); | ||||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*-------------------------------------------------------------------------*/ | ||||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| 	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; | ||||
| 	} | ||||
| 
 | ||||
| 	exynos_usb_parse_dt(gd->fdt_blob, exynos); | ||||
| 
 | ||||
| 	reset_usb_phy(exynos->usb); | ||||
| 
 | ||||
| 	kfree(exynos); | ||||
| 	reset_usb_phy(ctx->usb); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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__ */ | ||||
|  | @ -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; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue