1
0
Fork 0

Merge branch 'master' of git://git.denx.de/u-boot-usb

* 'master' of git://git.denx.de/u-boot-usb:
  MUSB driver: Timeout is never detected as the while loop does not end
  usb: fix ulpi_set_vbus prototype
  pxa25x: Add UDC registers definitions
  USB: Fix strict aliasing in ohci-hcd
  usb: Optimize USB storage read/write
  ehci: Optimize qTD allocations
  usb_stor_BBB_transport: Do not delay when not required
  usb_storage: Remove EHCI constraints
  usb_storage: Restore non-EHCI support
  ehci-hcd: Boost transfer speed
  ehci: cosmetic: Define used constants
  ehci: Fail for multi-transaction interrupt transfers
  arm:trats: Enable g_dnl composite USB gadget with embedded DFU function on TRATS
  arm:trats: Support for USB UDC driver at TRATS board.
  dfu:cmd: Support for DFU u-boot command
  dfu: MMC specific routines for DFU operation
  dfu: DFU backend implementation
  dfu:usb: DFU USB function (f_dfu) support for g_dnl composite gadget
  dfu:usb: Support for g_dnl composite download gadget.
  ehci: cosmetic: Define the number of qt_buffers

Signed-off-by: Wolfgang Denk <wd@denx.de>
utp
Wolfgang Denk 2012-09-02 16:38:48 +02:00
commit 7cdcaef0b2
21 changed files with 2236 additions and 168 deletions

View File

@ -269,6 +269,7 @@ LIBS-y += drivers/pci/libpci.o
LIBS-y += drivers/pcmcia/libpcmcia.o
LIBS-y += drivers/power/libpower.o
LIBS-y += drivers/spi/libspi.o
LIBS-y += drivers/dfu/libdfu.o
ifeq ($(CPU),mpc83xx)
LIBS-y += drivers/qe/libqe.o
LIBS-y += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o

View File

@ -0,0 +1,159 @@
/*
* PXA25x UDC definitions
*
* Copyright (C) 2012 Łukasz Dałek <luk0104@gmail.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
*/
#ifndef __REGS_USB_H__
#define __REGS_USB_H__
struct pxa25x_udc_regs {
/* UDC Control Register */
uint32_t udccr; /* 0x000 */
uint32_t reserved1;
/* UDC Control Function Register */
uint32_t udccfr; /* 0x008 */
uint32_t reserved2;
/* UDC Endpoint Control/Status Registers */
uint32_t udccs[16]; /* 0x010 - 0x04c */
/* UDC Interrupt Control/Status Registers */
uint32_t uicr0; /* 0x050 */
uint32_t uicr1; /* 0x054 */
uint32_t usir0; /* 0x058 */
uint32_t usir1; /* 0x05c */
/* UDC Frame Number/Byte Count Registers */
uint32_t ufnrh; /* 0x060 */
uint32_t ufnrl; /* 0x064 */
uint32_t ubcr2; /* 0x068 */
uint32_t ubcr4; /* 0x06c */
uint32_t ubcr7; /* 0x070 */
uint32_t ubcr9; /* 0x074 */
uint32_t ubcr12; /* 0x078 */
uint32_t ubcr14; /* 0x07c */
/* UDC Endpoint Data Registers */
uint32_t uddr0; /* 0x080 */
uint32_t reserved3[7];
uint32_t uddr5; /* 0x0a0 */
uint32_t reserved4[7];
uint32_t uddr10; /* 0x0c0 */
uint32_t reserved5[7];
uint32_t uddr15; /* 0x0e0 */
uint32_t reserved6[7];
uint32_t uddr1; /* 0x100 */
uint32_t reserved7[31];
uint32_t uddr2; /* 0x180 */
uint32_t reserved8[31];
uint32_t uddr3; /* 0x200 */
uint32_t reserved9[127];
uint32_t uddr4; /* 0x400 */
uint32_t reserved10[127];
uint32_t uddr6; /* 0x600 */
uint32_t reserved11[31];
uint32_t uddr7; /* 0x680 */
uint32_t reserved12[31];
uint32_t uddr8; /* 0x700 */
uint32_t reserved13[127];
uint32_t uddr9; /* 0x900 */
uint32_t reserved14[127];
uint32_t uddr11; /* 0xb00 */
uint32_t reserved15[31];
uint32_t uddr12; /* 0xb80 */
uint32_t reserved16[31];
uint32_t uddr13; /* 0xc00 */
uint32_t reserved17[127];
uint32_t uddr14; /* 0xe00 */
};
#define PXA25X_UDC_BASE 0x40600000
#define UDCCR_UDE (1 << 0)
#define UDCCR_UDA (1 << 1)
#define UDCCR_RSM (1 << 2)
#define UDCCR_RESIR (1 << 3)
#define UDCCR_SUSIR (1 << 4)
#define UDCCR_SRM (1 << 5)
#define UDCCR_RSTIR (1 << 6)
#define UDCCR_REM (1 << 7)
/* Bulk IN endpoint 1/6/11 */
#define UDCCS_BI_TSP (1 << 7)
#define UDCCS_BI_FST (1 << 5)
#define UDCCS_BI_SST (1 << 4)
#define UDCCS_BI_TUR (1 << 3)
#define UDCCS_BI_FTF (1 << 2)
#define UDCCS_BI_TPC (1 << 1)
#define UDCCS_BI_TFS (1 << 0)
/* Bulk OUT endpoint 2/7/12 */
#define UDCCS_BO_RSP (1 << 7)
#define UDCCS_BO_RNE (1 << 6)
#define UDCCS_BO_FST (1 << 5)
#define UDCCS_BO_SST (1 << 4)
#define UDCCS_BO_DME (1 << 3)
#define UDCCS_BO_RPC (1 << 1)
#define UDCCS_BO_RFS (1 << 0)
/* Isochronous OUT endpoint 4/9/14 */
#define UDCCS_IO_RSP (1 << 7)
#define UDCCS_IO_RNE (1 << 6)
#define UDCCS_IO_DME (1 << 3)
#define UDCCS_IO_ROF (1 << 2)
#define UDCCS_IO_RPC (1 << 1)
#define UDCCS_IO_RFS (1 << 0)
/* Control endpoint 0 */
#define UDCCS0_OPR (1 << 0)
#define UDCCS0_IPR (1 << 1)
#define UDCCS0_FTF (1 << 2)
#define UDCCS0_DRWF (1 << 3)
#define UDCCS0_SST (1 << 4)
#define UDCCS0_FST (1 << 5)
#define UDCCS0_RNE (1 << 6)
#define UDCCS0_SA (1 << 7)
#define UICR0_IM0 (1 << 0)
#define USIR0_IR0 (1 << 0)
#define USIR0_IR1 (1 << 1)
#define USIR0_IR2 (1 << 2)
#define USIR0_IR3 (1 << 3)
#define USIR0_IR4 (1 << 4)
#define USIR0_IR5 (1 << 5)
#define USIR0_IR6 (1 << 6)
#define USIR0_IR7 (1 << 7)
#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */
#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */
/*
* Intel(R) PXA255 Processor Specification, September 2003 (page 31)
* define new "must be one" bits in UDCCFR (see Table 12-13.)
*/
#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN | UDCCFR_ACM))
#define UFNRH_SIR (1 << 7) /* SOF interrupt request */
#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */
#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */
#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */
#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */
#endif /* __REGS_USB_H__ */

View File

@ -59,6 +59,8 @@ static int hwrevision(int rev)
return (board_rev & 0xf) == rev;
}
struct s3c_plat_otg_data s5pc210_otg_data;
int board_init(void)
{
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100;
@ -259,6 +261,12 @@ struct s3c_plat_otg_data s5pc210_otg_data = {
.usb_phy_ctrl = EXYNOS4_USBPHY_CONTROL,
.usb_flags = PHY0_SLEEP,
};
void board_usb_init(void)
{
debug("USB_udc_probe\n");
s3c_udc_probe(&s5pc210_otg_data);
}
#endif
static void pmic_reset(void)

View File

@ -184,6 +184,7 @@ COBJS-$(CONFIG_MENU) += menu.o
COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o
COBJS-$(CONFIG_UPDATE_TFTP) += update.o
COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o
endif
ifdef CONFIG_SPL_BUILD

81
common/cmd_dfu.c 100644
View File

@ -0,0 +1,81 @@
/*
* cmd_dfu.c -- dfu command
*
* Copyright (C) 2012 Samsung Electronics
* authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
* 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 <common.h>
#include <command.h>
#include <malloc.h>
#include <dfu.h>
#include <asm/errno.h>
#include <g_dnl.h>
static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
const char *str_env;
char s[] = "dfu";
char *env_bkp;
int ret;
if (argc < 3)
return CMD_RET_USAGE;
str_env = getenv("dfu_alt_info");
if (str_env == NULL) {
printf("%s: \"dfu_alt_info\" env variable not defined!\n",
__func__);
return CMD_RET_FAILURE;
}
env_bkp = strdup(str_env);
ret = dfu_config_entities(env_bkp, argv[1],
(int)simple_strtoul(argv[2], NULL, 10));
if (ret)
return CMD_RET_FAILURE;
if (strcmp(argv[3], "list") == 0) {
dfu_show_entities();
goto done;
}
board_usb_init();
g_dnl_register(s);
while (1) {
if (ctrlc())
goto exit;
usb_gadget_handle_interrupts();
}
exit:
g_dnl_unregister();
done:
dfu_free_entities();
free(env_bkp);
return CMD_RET_SUCCESS;
}
U_BOOT_CMD(dfu, CONFIG_SYS_MAXARGS, 1, do_dfu,
"Device Firmware Upgrade",
"<interface> <dev> [list]\n"
" - device firmware upgrade on a device <dev>\n"
" attached to interface <interface>\n"
" [list] - list available alt settings"
);

View File

@ -136,6 +136,7 @@ struct us_data {
struct usb_device *pusb_dev; /* this usb_device */
unsigned int flags; /* from filter initially */
# define USB_READY (1 << 0)
unsigned char ifnum; /* interface number */
unsigned char ep_in; /* in endpoint */
unsigned char ep_out; /* out ....... */
@ -155,11 +156,16 @@ struct us_data {
trans_cmnd transport; /* transport routine */
};
#ifdef CONFIG_USB_EHCI
/*
* The U-Boot EHCI driver cannot handle more than 5 page aligned buffers
* of 4096 bytes in a transfer without running itself out of qt_buffers
* The U-Boot EHCI driver can handle any transfer length as long as there is
* enough free heap space left, but the SCSI READ(10) and WRITE(10) commands are
* limited to 65535 blocks.
*/
#define USB_MAX_XFER_BLK(start, blksz) (((4096 * 5) - (start % 4096)) / blksz)
#define USB_MAX_XFER_BLK 65535
#else
#define USB_MAX_XFER_BLK 20
#endif
static struct us_data usb_stor[USB_MAX_STOR_DEV];
@ -693,7 +699,8 @@ int usb_stor_BBB_transport(ccb *srb, struct us_data *us)
usb_stor_BBB_reset(us);
return USB_STOR_TRANSPORT_FAILED;
}
mdelay(5);
if (!(us->flags & USB_READY))
mdelay(5);
pipein = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
/* DATA phase + error handling */
@ -958,8 +965,10 @@ static int usb_test_unit_ready(ccb *srb, struct us_data *ss)
srb->cmd[1] = srb->lun << 5;
srb->datalen = 0;
srb->cmdlen = 12;
if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD)
if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD) {
ss->flags |= USB_READY;
return 0;
}
usb_request_sense(srb, ss);
mdelay(100);
} while (retries--);
@ -1046,7 +1055,7 @@ static void usb_bin_fixup(struct usb_device_descriptor descriptor,
unsigned long usb_stor_read(int device, unsigned long blknr,
unsigned long blkcnt, void *buffer)
{
unsigned long start, blks, buf_addr, max_xfer_blk;
unsigned long start, blks, buf_addr;
unsigned short smallblks;
struct usb_device *dev;
struct us_data *ss;
@ -1074,12 +1083,6 @@ unsigned long usb_stor_read(int device, unsigned long blknr,
buf_addr = (unsigned long)buffer;
start = blknr;
blks = blkcnt;
if (usb_test_unit_ready(srb, ss)) {
printf("Device NOT ready\n Request Sense returned %02X %02X"
" %02X\n", srb->sense_buf[2], srb->sense_buf[12],
srb->sense_buf[13]);
return 0;
}
USB_STOR_PRINTF("\nusb_read: dev %d startblk %lx, blccnt %lx"
" buffer %lx\n", device, start, blks, buf_addr);
@ -1088,14 +1091,12 @@ unsigned long usb_stor_read(int device, unsigned long blknr,
/* XXX need some comment here */
retry = 2;
srb->pdata = (unsigned char *)buf_addr;
max_xfer_blk = USB_MAX_XFER_BLK(buf_addr,
usb_dev_desc[device].blksz);
if (blks > max_xfer_blk)
smallblks = (unsigned short) max_xfer_blk;
if (blks > USB_MAX_XFER_BLK)
smallblks = USB_MAX_XFER_BLK;
else
smallblks = (unsigned short) blks;
retry_it:
if (smallblks == max_xfer_blk)
if (smallblks == USB_MAX_XFER_BLK)
usb_show_progress();
srb->datalen = usb_dev_desc[device].blksz * smallblks;
srb->pdata = (unsigned char *)buf_addr;
@ -1111,12 +1112,13 @@ retry_it:
blks -= smallblks;
buf_addr += srb->datalen;
} while (blks != 0);
ss->flags &= ~USB_READY;
USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\n",
start, smallblks, buf_addr);
usb_disable_asynch(0); /* asynch transfer allowed */
if (blkcnt >= max_xfer_blk)
if (blkcnt >= USB_MAX_XFER_BLK)
debug("\n");
return blkcnt;
}
@ -1124,7 +1126,7 @@ retry_it:
unsigned long usb_stor_write(int device, unsigned long blknr,
unsigned long blkcnt, const void *buffer)
{
unsigned long start, blks, buf_addr, max_xfer_blk;
unsigned long start, blks, buf_addr;
unsigned short smallblks;
struct usb_device *dev;
struct us_data *ss;
@ -1153,12 +1155,6 @@ unsigned long usb_stor_write(int device, unsigned long blknr,
buf_addr = (unsigned long)buffer;
start = blknr;
blks = blkcnt;
if (usb_test_unit_ready(srb, ss)) {
printf("Device NOT ready\n Request Sense returned %02X %02X"
" %02X\n", srb->sense_buf[2], srb->sense_buf[12],
srb->sense_buf[13]);
return 0;
}
USB_STOR_PRINTF("\nusb_write: dev %d startblk %lx, blccnt %lx"
" buffer %lx\n", device, start, blks, buf_addr);
@ -1169,14 +1165,12 @@ unsigned long usb_stor_write(int device, unsigned long blknr,
*/
retry = 2;
srb->pdata = (unsigned char *)buf_addr;
max_xfer_blk = USB_MAX_XFER_BLK(buf_addr,
usb_dev_desc[device].blksz);
if (blks > max_xfer_blk)
smallblks = (unsigned short) max_xfer_blk;
if (blks > USB_MAX_XFER_BLK)
smallblks = USB_MAX_XFER_BLK;
else
smallblks = (unsigned short) blks;
retry_it:
if (smallblks == max_xfer_blk)
if (smallblks == USB_MAX_XFER_BLK)
usb_show_progress();
srb->datalen = usb_dev_desc[device].blksz * smallblks;
srb->pdata = (unsigned char *)buf_addr;
@ -1192,12 +1186,13 @@ retry_it:
blks -= smallblks;
buf_addr += srb->datalen;
} while (blks != 0);
ss->flags &= ~USB_READY;
USB_STOR_PRINTF("usb_write: end startblk %lx, blccnt %x buffer %lx\n",
start, smallblks, buf_addr);
usb_disable_asynch(0); /* asynch transfer allowed */
if (blkcnt >= max_xfer_blk)
if (blkcnt >= USB_MAX_XFER_BLK)
debug("\n");
return blkcnt;
@ -1403,6 +1398,7 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss,
cap[0] = 2880;
cap[1] = 0x200;
}
ss->flags &= ~USB_READY;
USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\n", cap[0],
cap[1]);
#if 0

View File

@ -0,0 +1,44 @@
#
# Copyright (C) 2012 Samsung Electronics
# Lukasz Majewski <l.majewski@samsung.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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB = $(obj)libdfu.o
COBJS-$(CONFIG_DFU_FUNCTION) += dfu.o
COBJS-$(CONFIG_DFU_MMC) += dfu_mmc.o
SRCS := $(COBJS-y:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS-y))
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

238
drivers/dfu/dfu.c 100644
View File

@ -0,0 +1,238 @@
/*
* dfu.c -- DFU back-end routines
*
* Copyright (C) 2012 Samsung Electronics
* author: 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 <common.h>
#include <malloc.h>
#include <mmc.h>
#include <fat.h>
#include <dfu.h>
#include <linux/list.h>
#include <linux/compiler.h>
static LIST_HEAD(dfu_list);
static int dfu_alt_num;
static int dfu_find_alt_num(const char *s)
{
int i = 0;
for (; *s; s++)
if (*s == ';')
i++;
return ++i;
}
static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
dfu_buf[DFU_DATA_BUF_SIZE];
int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
static unsigned char *i_buf;
static int i_blk_seq_num;
long w_size = 0;
int ret = 0;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
__func__, dfu->name, buf, size, blk_seq_num, i_buf);
if (blk_seq_num == 0) {
i_buf = dfu_buf;
i_blk_seq_num = 0;
}
if (i_blk_seq_num++ != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, i_blk_seq_num, blk_seq_num);
return -1;
}
memcpy(i_buf, buf, size);
i_buf += size;
if (size == 0) {
/* Integrity check (if needed) */
debug("%s: %s %d [B] CRC32: 0x%x\n", __func__, dfu->name,
i_buf - dfu_buf, crc32(0, dfu_buf, i_buf - dfu_buf));
w_size = i_buf - dfu_buf;
ret = dfu->write_medium(dfu, dfu_buf, &w_size);
if (ret)
debug("%s: Write error!\n", __func__);
i_blk_seq_num = 0;
i_buf = NULL;
return ret;
}
return ret;
}
int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
{
static unsigned char *i_buf;
static int i_blk_seq_num;
static long r_size;
static u32 crc;
int ret = 0;
debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
__func__, dfu->name, buf, size, blk_seq_num, i_buf);
if (blk_seq_num == 0) {
i_buf = dfu_buf;
ret = dfu->read_medium(dfu, i_buf, &r_size);
debug("%s: %s %ld [B]\n", __func__, dfu->name, r_size);
i_blk_seq_num = 0;
/* Integrity check (if needed) */
crc = crc32(0, dfu_buf, r_size);
}
if (i_blk_seq_num++ != blk_seq_num) {
printf("%s: Wrong sequence number! [%d] [%d]\n",
__func__, i_blk_seq_num, blk_seq_num);
return -1;
}
if (r_size >= size) {
memcpy(buf, i_buf, size);
i_buf += size;
r_size -= size;
return size;
} else {
memcpy(buf, i_buf, r_size);
i_buf += r_size;
debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, crc);
puts("UPLOAD ... done\nCtrl+C to exit ...\n");
i_buf = NULL;
i_blk_seq_num = 0;
crc = 0;
return r_size;
}
return ret;
}
static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
char *interface, int num)
{
char *st;
debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
st = strsep(&s, " ");
strcpy(dfu->name, st);
dfu->dev_num = num;
dfu->alt = alt;
/* Specific for mmc device */
if (strcmp(interface, "mmc") == 0) {
if (dfu_fill_entity_mmc(dfu, s))
return -1;
} else {
printf("%s: Device %s not (yet) supported!\n",
__func__, interface);
return -1;
}
return 0;
}
void dfu_free_entities(void)
{
struct dfu_entity *dfu, *p, *t = NULL;
list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
list_del(&dfu->list);
t = dfu;
}
if (t)
free(t);
INIT_LIST_HEAD(&dfu_list);
}
int dfu_config_entities(char *env, char *interface, int num)
{
struct dfu_entity *dfu;
int i, ret;
char *s;
dfu_alt_num = dfu_find_alt_num(env);
debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
dfu = calloc(sizeof(*dfu), dfu_alt_num);
if (!dfu)
return -1;
for (i = 0; i < dfu_alt_num; i++) {
s = strsep(&env, ";");
ret = dfu_fill_entity(&dfu[i], s, i, interface, num);
if (ret)
return -1;
list_add_tail(&dfu[i].list, &dfu_list);
}
return 0;
}
const char *dfu_get_dev_type(enum dfu_device_type t)
{
const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND" };
return dev_t[t];
}
const char *dfu_get_layout(enum dfu_layout l)
{
const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
"EXT3", "EXT4" };
return dfu_layout[l];
}
void dfu_show_entities(void)
{
struct dfu_entity *dfu;
puts("DFU alt settings list:\n");
list_for_each_entry(dfu, &dfu_list, list) {
printf("dev: %s alt: %d name: %s layout: %s\n",
dfu_get_dev_type(dfu->dev_type), dfu->alt,
dfu->name, dfu_get_layout(dfu->layout));
}
}
int dfu_get_alt_number(void)
{
return dfu_alt_num;
}
struct dfu_entity *dfu_get_entity(int alt)
{
struct dfu_entity *dfu;
list_for_each_entry(dfu, &dfu_list, list) {
if (dfu->alt == alt)
return dfu;
}
return NULL;
}

View File

@ -0,0 +1,162 @@
/*
* dfu.c -- DFU back-end routines
*
* Copyright (C) 2012 Samsung Electronics
* author: 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 <common.h>
#include <malloc.h>
#include <dfu.h>
enum dfu_mmc_op {
DFU_OP_READ = 1,
DFU_OP_WRITE,
};
static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
{
char cmd_buf[DFU_CMD_BUF_SIZE];
sprintf(cmd_buf, "mmc %s 0x%x %x %x",
op == DFU_OP_READ ? "read" : "write",
(unsigned int) buf,
dfu->data.mmc.lba_start,
dfu->data.mmc.lba_size);
if (op == DFU_OP_READ)
*len = dfu->data.mmc.lba_blk_size * dfu->data.mmc.lba_size;
debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
return run_command(cmd_buf, 0);
}
static inline int mmc_block_write(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_block_op(DFU_OP_WRITE, dfu, buf, len);
}
static inline int mmc_block_read(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_block_op(DFU_OP_READ, dfu, buf, len);
}
static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu,
void *buf, long *len)
{
char cmd_buf[DFU_CMD_BUF_SIZE];
char *str_env;
int ret;
sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s %lx",
op == DFU_OP_READ ? "load" : "write",
dfu->data.mmc.dev, dfu->data.mmc.part,
(unsigned int) buf, dfu->name, *len);
debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
ret = run_command(cmd_buf, 0);
if (ret) {
puts("dfu: Read error!\n");
return ret;
}
if (dfu->layout != DFU_RAW_ADDR) {
str_env = getenv("filesize");
if (str_env == NULL) {
puts("dfu: Wrong file size!\n");
return -1;
}
*len = simple_strtoul(str_env, NULL, 16);
}
return ret;
}
static inline int mmc_file_write(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_file_op(DFU_OP_WRITE, dfu, buf, len);
}
static inline int mmc_file_read(struct dfu_entity *dfu, void *buf, long *len)
{
return mmc_file_op(DFU_OP_READ, dfu, buf, len);
}
int dfu_write_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mmc_block_write(dfu, buf, len);
break;
case DFU_FS_FAT:
ret = mmc_file_write(dfu, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)
{
int ret = -1;
switch (dfu->layout) {
case DFU_RAW_ADDR:
ret = mmc_block_read(dfu, buf, len);
break;
case DFU_FS_FAT:
ret = mmc_file_read(dfu, buf, len);
break;
default:
printf("%s: Layout (%s) not (yet) supported!\n", __func__,
dfu_get_layout(dfu->layout));
}
return ret;
}
int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
{
char *st;
dfu->dev_type = DFU_DEV_MMC;
st = strsep(&s, " ");
if (!strcmp(st, "mmc")) {
dfu->layout = DFU_RAW_ADDR;
dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
} else if (!strcmp(st, "fat")) {
dfu->layout = DFU_FS_FAT;
dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
} else {
printf("%s: Memory layout (%s) not supported!\n", __func__, st);
}
dfu->read_medium = dfu_read_medium_mmc;
dfu->write_medium = dfu_write_medium_mmc;
return 0;
}

View File

@ -29,6 +29,8 @@ LIB := $(obj)libusb_gadget.o
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

View File

@ -0,0 +1,749 @@
/*
* f_dfu.c -- Device Firmware Update USB function
*
* Copyright (C) 2012 Samsung Electronics
* authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
* 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 <malloc.h>
#include <linux/usb/ch9.h>
#include <usbdescriptors.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include <dfu.h>
#include "f_dfu.h"
struct f_dfu {
struct usb_function usb_function;
struct usb_descriptor_header **function;
struct usb_string *strings;
/* when configured, we have one config */
u8 config;
u8 altsetting;
enum dfu_state dfu_state;
unsigned int dfu_status;
/* Send/received block number is handy for data integrity check */
int blk_seq_num;
};
typedef int (*dfu_state_fn) (struct f_dfu *,
const struct usb_ctrlrequest *,
struct usb_gadget *,
struct usb_request *);
static inline struct f_dfu *func_to_dfu(struct usb_function *f)
{
return container_of(f, struct f_dfu, usb_function);
}
static const struct dfu_function_descriptor dfu_func = {
.bLength = sizeof dfu_func,
.bDescriptorType = DFU_DT_FUNC,
.bmAttributes = DFU_BIT_WILL_DETACH |
DFU_BIT_MANIFESTATION_TOLERANT |
DFU_BIT_CAN_UPLOAD |
DFU_BIT_CAN_DNLOAD,
.wDetachTimeOut = 0,
.wTransferSize = DFU_USB_BUFSIZ,
.bcdDFUVersion = __constant_cpu_to_le16(0x0110),
};
static struct usb_interface_descriptor dfu_intf_runtime = {
.bLength = sizeof dfu_intf_runtime,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_APP_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 1,
/* .iInterface = DYNAMIC */
};
static struct usb_descriptor_header *dfu_runtime_descs[] = {
(struct usb_descriptor_header *) &dfu_intf_runtime,
NULL,
};
static const struct usb_qualifier_descriptor dev_qualifier = {
.bLength = sizeof dev_qualifier,
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.bNumConfigurations = 1,
};
static const char dfu_name[] = "Device Firmware Upgrade";
/*
* static strings, in UTF-8
*
* dfu_generic configuration
*/
static struct usb_string strings_dfu_generic[] = {
[0].s = dfu_name,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dfu_generic = {
.language = 0x0409, /* en-us */
.strings = strings_dfu_generic,
};
static struct usb_gadget_strings *dfu_generic_strings[] = {
&stringtab_dfu_generic,
NULL,
};
/*
* usb_function specific
*/
static struct usb_gadget_strings stringtab_dfu = {
.language = 0x0409, /* en-us */
/*
* .strings
*
* assigned during initialization,
* depends on number of flash entities
*
*/
};
static struct usb_gadget_strings *dfu_strings[] = {
&stringtab_dfu,
NULL,
};
/*-------------------------------------------------------------------------*/
static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_dfu *f_dfu = req->context;
dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
req->length, f_dfu->blk_seq_num);
if (req->length == 0)
puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
}
static void handle_getstatus(struct usb_request *req)
{
struct dfu_status *dstat = (struct dfu_status *)req->buf;
struct f_dfu *f_dfu = req->context;
switch (f_dfu->dfu_state) {
case DFU_STATE_dfuDNLOAD_SYNC:
case DFU_STATE_dfuDNBUSY:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
break;
case DFU_STATE_dfuMANIFEST_SYNC:
break;
default:
break;
}
/* send status response */
dstat->bStatus = f_dfu->dfu_status;
dstat->bState = f_dfu->dfu_state;
dstat->iString = 0;
}
static void handle_getstate(struct usb_request *req)
{
struct f_dfu *f_dfu = req->context;
((u8 *)req->buf)[0] = f_dfu->dfu_state;
req->actual = sizeof(u8);
}
static inline void to_dfu_mode(struct f_dfu *f_dfu)
{
f_dfu->usb_function.strings = dfu_strings;
f_dfu->usb_function.hs_descriptors = f_dfu->function;
}
static inline void to_runtime_mode(struct f_dfu *f_dfu)
{
f_dfu->usb_function.strings = NULL;
f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
}
static int handle_upload(struct usb_request *req, u16 len)
{
struct f_dfu *f_dfu = req->context;
return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf,
req->length, f_dfu->blk_seq_num);
}
static int handle_dnload(struct usb_gadget *gadget, u16 len)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_request *req = cdev->req;
struct f_dfu *f_dfu = req->context;
if (len == 0)
f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
req->complete = dnload_request_complete;
return len;
}
/*-------------------------------------------------------------------------*/
/* DFU state machine */
static int state_app_idle(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
case USB_REQ_DFU_DETACH:
f_dfu->dfu_state = DFU_STATE_appDETACH;
to_dfu_mode(f_dfu);
f_dfu->dfu_state = DFU_STATE_dfuIDLE;
value = RET_ZLP;
break;
default:
value = RET_STALL;
break;
}
return value;
}
static int state_app_detach(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
default:
f_dfu->dfu_state = DFU_STATE_appIDLE;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_idle(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 len = le16_to_cpu(ctrl->wLength);
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_DNLOAD:
if (len == 0) {
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
f_dfu->blk_seq_num = w_value;
value = handle_dnload(gadget, len);
break;
case USB_REQ_DFU_UPLOAD:
f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
f_dfu->blk_seq_num = 0;
value = handle_upload(req, len);
break;
case USB_REQ_DFU_ABORT:
/* no zlp? */
value = RET_ZLP;
break;
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
case USB_REQ_DFU_DETACH:
/*
* Proprietary extension: 'detach' from idle mode and
* get back to runtime mode in case of USB Reset. As
* much as I dislike this, we just can't use every USB
* bus reset to switch back to runtime mode, since at
* least the Linux USB stack likes to send a number of
* resets in a row :(
*/
f_dfu->dfu_state =
DFU_STATE_dfuMANIFEST_WAIT_RST;
to_runtime_mode(f_dfu);
f_dfu->dfu_state = DFU_STATE_appIDLE;
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_dnload_sync(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_dnbusy(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_dnload_idle(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 len = le16_to_cpu(ctrl->wLength);
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_DNLOAD:
f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
f_dfu->blk_seq_num = w_value;
value = handle_dnload(gadget, len);
break;
case USB_REQ_DFU_ABORT:
f_dfu->dfu_state = DFU_STATE_dfuIDLE;
value = RET_ZLP;
break;
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_manifest_sync(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
/* We're MainfestationTolerant */
f_dfu->dfu_state = DFU_STATE_dfuIDLE;
handle_getstatus(req);
f_dfu->blk_seq_num = 0;
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_upload_idle(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 len = le16_to_cpu(ctrl->wLength);
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_UPLOAD:
/* state transition if less data then requested */
f_dfu->blk_seq_num = w_value;
value = handle_upload(req, len);
if (value >= 0 && value < len)
f_dfu->dfu_state = DFU_STATE_dfuIDLE;
break;
case USB_REQ_DFU_ABORT:
f_dfu->dfu_state = DFU_STATE_dfuIDLE;
/* no zlp? */
value = RET_ZLP;
break;
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static int state_dfu_error(struct f_dfu *f_dfu,
const struct usb_ctrlrequest *ctrl,
struct usb_gadget *gadget,
struct usb_request *req)
{
int value = 0;
switch (ctrl->bRequest) {
case USB_REQ_DFU_GETSTATUS:
handle_getstatus(req);
value = RET_STAT_LEN;
break;
case USB_REQ_DFU_GETSTATE:
handle_getstate(req);
break;
case USB_REQ_DFU_CLRSTATUS:
f_dfu->dfu_state = DFU_STATE_dfuIDLE;
f_dfu->dfu_status = DFU_STATUS_OK;
/* no zlp? */
value = RET_ZLP;
break;
default:
f_dfu->dfu_state = DFU_STATE_dfuERROR;
value = RET_STALL;
break;
}
return value;
}
static dfu_state_fn dfu_state[] = {
state_app_idle, /* DFU_STATE_appIDLE */
state_app_detach, /* DFU_STATE_appDETACH */
state_dfu_idle, /* DFU_STATE_dfuIDLE */
state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */
state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */
state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */
state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */
NULL, /* DFU_STATE_dfuMANIFEST */
NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */
state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */
state_dfu_error /* DFU_STATE_dfuERROR */
};
static int
dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct usb_gadget *gadget = f->config->cdev->gadget;
struct usb_request *req = f->config->cdev->req;
struct f_dfu *f_dfu = f->config->cdev->req->context;
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
int value = 0;
u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
debug("w_value: 0x%x len: 0x%x\n", w_value, len);
debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
req_type, ctrl->bRequest, f_dfu->dfu_state);
if (req_type == USB_TYPE_STANDARD) {
if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR &&
(w_value >> 8) == DFU_DT_FUNC) {
value = min(len, (u16) sizeof(dfu_func));
memcpy(req->buf, &dfu_func, value);
}
} else /* DFU specific request */
value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req);
if (value >= 0) {
req->length = value;
req->zero = value < len;
value = usb_ep_queue(gadget->ep0, req, 0);
if (value < 0) {
debug("ep_queue --> %d\n", value);
req->status = 0;
}
}
return value;
}
/*-------------------------------------------------------------------------*/
static int
dfu_prepare_strings(struct f_dfu *f_dfu, int n)
{
struct dfu_entity *de = NULL;
int i = 0;
f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
if (!f_dfu->strings)
goto enomem;
for (i = 0; i < n; ++i) {
de = dfu_get_entity(i);
f_dfu->strings[i].s = de->name;
}
f_dfu->strings[i].id = 0;
f_dfu->strings[i].s = NULL;
return 0;
enomem:
while (i)
f_dfu->strings[--i].s = NULL;
free(f_dfu->strings);
return -ENOMEM;
}
static int dfu_prepare_function(struct f_dfu *f_dfu, int n)
{
struct usb_interface_descriptor *d;
int i = 0;
f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n);
if (!f_dfu->function)
goto enomem;
for (i = 0; i < n; ++i) {
d = calloc(sizeof(*d), 1);
if (!d)
goto enomem;
d->bLength = sizeof(*d);
d->bDescriptorType = USB_DT_INTERFACE;
d->bAlternateSetting = i;
d->bNumEndpoints = 0;
d->bInterfaceClass = USB_CLASS_APP_SPEC;
d->bInterfaceSubClass = 1;
d->bInterfaceProtocol = 2;
f_dfu->function[i] = (struct usb_descriptor_header *)d;
}
f_dfu->function[i] = NULL;
return 0;
enomem:
while (i) {
free(f_dfu->function[--i]);
f_dfu->function[i] = NULL;
}
free(f_dfu->function);
return -ENOMEM;
}
static int dfu_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_dfu *f_dfu = func_to_dfu(f);
int alt_num = dfu_get_alt_number();
int rv, id, i;
id = usb_interface_id(c, f);
if (id < 0)
return id;
dfu_intf_runtime.bInterfaceNumber = id;
f_dfu->dfu_state = DFU_STATE_appIDLE;
f_dfu->dfu_status = DFU_STATUS_OK;
rv = dfu_prepare_function(f_dfu, alt_num);
if (rv)
goto error;
rv = dfu_prepare_strings(f_dfu, alt_num);
if (rv)
goto error;
for (i = 0; i < alt_num; i++) {
id = usb_string_id(cdev);
if (id < 0)
return id;
f_dfu->strings[i].id = id;
((struct usb_interface_descriptor *)f_dfu->function[i])
->iInterface = id;
}
stringtab_dfu.strings = f_dfu->strings;
cdev->req->context = f_dfu;
error:
return rv;
}
static void dfu_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_dfu *f_dfu = func_to_dfu(f);
int alt_num = dfu_get_alt_number();
int i;
if (f_dfu->strings) {
i = alt_num;
while (i)
f_dfu->strings[--i].s = NULL;
free(f_dfu->strings);
}
if (f_dfu->function) {
i = alt_num;
while (i) {
free(f_dfu->function[--i]);
f_dfu->function[i] = NULL;
}
free(f_dfu->function);
}
free(f_dfu);
}
static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_dfu *f_dfu = func_to_dfu(f);
debug("%s: intf:%d alt:%d\n", __func__, intf, alt);
f_dfu->altsetting = alt;
return 0;
}
/* TODO: is this really what we need here? */
static void dfu_disable(struct usb_function *f)
{
struct f_dfu *f_dfu = func_to_dfu(f);
if (f_dfu->config == 0)
return;
debug("%s: reset config\n", __func__);
f_dfu->config = 0;
}
static int dfu_bind_config(struct usb_configuration *c)
{
struct f_dfu *f_dfu;
int status;
f_dfu = calloc(sizeof(*f_dfu), 1);
if (!f_dfu)
return -ENOMEM;
f_dfu->usb_function.name = "dfu";
f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
f_dfu->usb_function.bind = dfu_bind;
f_dfu->usb_function.unbind = dfu_unbind;
f_dfu->usb_function.set_alt = dfu_set_alt;
f_dfu->usb_function.disable = dfu_disable;
f_dfu->usb_function.strings = dfu_generic_strings,
f_dfu->usb_function.setup = dfu_handle,
status = usb_add_function(c, &f_dfu->usb_function);
if (status)
free(f_dfu);
return status;
}
int dfu_add(struct usb_configuration *c)
{
int id;
id = usb_string_id(c->cdev);
if (id < 0)
return id;
strings_dfu_generic[0].id = id;
dfu_intf_runtime.iInterface = id;
debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__,
c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
return dfu_bind_config(c);
}

View File

@ -0,0 +1,100 @@
/*
* f_dfu.h -- Device Firmware Update gadget
*
* Copyright (C) 2011-2012 Samsung Electronics
* author: Andrzej Pietrasiewicz <andrzej.p@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
*/
#ifndef __F_DFU_H_
#define __F_DFU_H_
#include <linux/compiler.h>
#include <linux/usb/composite.h>
#define DFU_CONFIG_VAL 1
#define DFU_DT_FUNC 0x21
#define DFU_BIT_WILL_DETACH (0x1 << 3)
#define DFU_BIT_MANIFESTATION_TOLERANT (0x1 << 2)
#define DFU_BIT_CAN_UPLOAD (0x1 << 1)
#define DFU_BIT_CAN_DNLOAD 0x1
/* big enough to hold our biggest descriptor */
#define DFU_USB_BUFSIZ 4096
#define USB_REQ_DFU_DETACH 0x00
#define USB_REQ_DFU_DNLOAD 0x01
#define USB_REQ_DFU_UPLOAD 0x02
#define USB_REQ_DFU_GETSTATUS 0x03
#define USB_REQ_DFU_CLRSTATUS 0x04
#define USB_REQ_DFU_GETSTATE 0x05
#define USB_REQ_DFU_ABORT 0x06
#define DFU_STATUS_OK 0x00
#define DFU_STATUS_errTARGET 0x01
#define DFU_STATUS_errFILE 0x02
#define DFU_STATUS_errWRITE 0x03
#define DFU_STATUS_errERASE 0x04
#define DFU_STATUS_errCHECK_ERASED 0x05
#define DFU_STATUS_errPROG 0x06
#define DFU_STATUS_errVERIFY 0x07
#define DFU_STATUS_errADDRESS 0x08
#define DFU_STATUS_errNOTDONE 0x09
#define DFU_STATUS_errFIRMWARE 0x0a
#define DFU_STATUS_errVENDOR 0x0b
#define DFU_STATUS_errUSBR 0x0c
#define DFU_STATUS_errPOR 0x0d
#define DFU_STATUS_errUNKNOWN 0x0e
#define DFU_STATUS_errSTALLEDPKT 0x0f
#define RET_STALL -1
#define RET_ZLP 0
#define RET_STAT_LEN 6
enum dfu_state {
DFU_STATE_appIDLE = 0,
DFU_STATE_appDETACH = 1,
DFU_STATE_dfuIDLE = 2,
DFU_STATE_dfuDNLOAD_SYNC = 3,
DFU_STATE_dfuDNBUSY = 4,
DFU_STATE_dfuDNLOAD_IDLE = 5,
DFU_STATE_dfuMANIFEST_SYNC = 6,
DFU_STATE_dfuMANIFEST = 7,
DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
DFU_STATE_dfuUPLOAD_IDLE = 9,
DFU_STATE_dfuERROR = 10,
};
struct dfu_status {
__u8 bStatus;
__u8 bwPollTimeout[3];
__u8 bState;
__u8 iString;
} __packed;
struct dfu_function_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bmAttributes;
__le16 wDetachTimeOut;
__le16 wTransferSize;
__le16 bcdDFUVersion;
} __packed;
/* configuration-specific linkup */
int dfu_add(struct usb_configuration *c);
#endif /* __F_DFU_H_ */

View File

@ -0,0 +1,202 @@
/*
* g_dnl.c -- USB Downloader Gadget
*
* Copyright (C) 2012 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 <malloc.h>
#include <mmc.h>
#include <part.h>
#include <g_dnl.h>
#include "f_dfu.h"
#include "gadget_chips.h"
#include "composite.c"
/*
* One needs to define the following:
* CONFIG_G_DNL_VENDOR_NUM
* CONFIG_G_DNL_PRODUCT_NUM
* CONFIG_G_DNL_MANUFACTURER
* at e.g. ./include/configs/<board>.h
*/
#define STRING_MANUFACTURER 25
#define STRING_PRODUCT 2
#define STRING_USBDOWN 2
#define CONFIG_USBDOWNLOADER 2
#define DRIVER_VERSION "usb_dnl 2.0"
static const char shortname[] = "usb_dnl_";
static const char product[] = "USB download gadget";
static const char manufacturer[] = CONFIG_G_DNL_MANUFACTURER;
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0x02, /*0x02:CDC-modem , 0x00:CDC-serial*/
.idVendor = __constant_cpu_to_le16(CONFIG_G_DNL_VENDOR_NUM),
.idProduct = __constant_cpu_to_le16(CONFIG_G_DNL_PRODUCT_NUM),
.iProduct = STRING_PRODUCT,
.bNumConfigurations = 1,
};
/* static strings, in UTF-8 */
static struct usb_string g_dnl_string_defs[] = {
{ 0, manufacturer, },
{ 1, product, },
};
static struct usb_gadget_strings g_dnl_string_tab = {
.language = 0x0409, /* en-us */
.strings = g_dnl_string_defs,
};
static struct usb_gadget_strings *g_dnl_composite_strings[] = {
&g_dnl_string_tab,
NULL,
};
static int g_dnl_unbind(struct usb_composite_dev *cdev)
{
debug("%s\n", __func__);
return 0;
}
static int g_dnl_do_config(struct usb_configuration *c)
{
const char *s = c->cdev->driver->name;
int ret = -1;
debug("%s: configuration: 0x%p composite dev: 0x%p\n",
__func__, c, c->cdev);
printf("GADGET DRIVER: %s\n", s);
if (!strcmp(s, "usb_dnl_dfu"))
ret = dfu_add(c);
return ret;
}
static int g_dnl_config_register(struct usb_composite_dev *cdev)
{
static struct usb_configuration config = {
.label = "usb_dnload",
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bConfigurationValue = CONFIG_USBDOWNLOADER,
.iConfiguration = STRING_USBDOWN,
.bind = g_dnl_do_config,
};
return usb_add_config(cdev, &config);
}
static int g_dnl_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int id, ret;
int gcnum;
debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
id = usb_string_id(cdev);
if (id < 0)
return id;
g_dnl_string_defs[0].id = id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
g_dnl_string_defs[1].id = id;
device_desc.iProduct = id;
ret = g_dnl_config_register(cdev);
if (ret)
goto error;
gcnum = usb_gadget_controller_number(gadget);
debug("gcnum: %d\n", gcnum);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
else {
debug("%s: controller '%s' not recognized\n",
shortname, gadget->name);
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
}
return 0;
error:
g_dnl_unbind(cdev);
return -ENOMEM;
}
static struct usb_composite_driver g_dnl_driver = {
.name = NULL,
.dev = &device_desc,
.strings = g_dnl_composite_strings,
.bind = g_dnl_bind,
.unbind = g_dnl_unbind,
};
int g_dnl_register(const char *type)
{
/* We only allow "dfu" atm, so 3 should be enough */
static char name[sizeof(shortname) + 3];
int ret;
if (!strcmp(type, "dfu")) {
strcpy(name, shortname);
strcat(name, type);
} else {
printf("%s: unknown command: %s\n", __func__, type);
return -EINVAL;
}
g_dnl_driver.name = name;
debug("%s: g_dnl_driver.name: %s\n", __func__, g_dnl_driver.name);
ret = usb_composite_register(&g_dnl_driver);
if (ret) {
printf("%s: failed!, error: %d\n", __func__, ret);
return ret;
}
return 0;
}
void g_dnl_unregister(void)
{
usb_composite_unregister(&g_dnl_driver);
}

View File

@ -163,7 +163,7 @@ static int ehci_reset(void)
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
cmd = ehci_readl(&hcor->or_txfilltuning);
cmd &= ~TXFIFO_THRESH(0x3f);
cmd &= ~TXFIFO_THRESH_MASK;
cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);
ehci_writel(&hcor->or_txfilltuning, cmd);
#endif
@ -183,10 +183,10 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN));
idx = 0;
while (idx < 5) {
while (idx < QT_BUFFER_CNT) {
td->qt_buffer[idx] = cpu_to_hc32(addr);
td->qt_buffer_hi[idx] = 0;
next = (addr + 4096) & ~4095;
next = (addr + EHCI_PAGE_SIZE) & ~(EHCI_PAGE_SIZE - 1);
delta = next - addr;
if (delta >= sz)
break;
@ -195,7 +195,7 @@ static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
idx++;
}
if (idx == 5) {
if (idx == QT_BUFFER_CNT) {
printf("out of buffer pointers (%u bytes left)\n", sz);
return -1;
}
@ -208,13 +208,14 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
{
ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN);
ALLOC_ALIGN_BUFFER(struct qTD, qtd, 3, USB_DMA_MINALIGN);
struct qTD *qtd;
int qtd_count = 0;
int qtd_counter = 0;
volatile struct qTD *vtd;
unsigned long ts;
uint32_t *tdp;
uint32_t endpt, token, usbsts;
uint32_t endpt, maxpacket, token, usbsts;
uint32_t c, toggle;
uint32_t cmd;
int timeout;
@ -229,8 +230,73 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
le16_to_cpu(req->value), le16_to_cpu(req->value),
le16_to_cpu(req->index));
#define PKT_ALIGN 512
/*
* The USB transfer is split into qTD transfers. Eeach qTD transfer is
* described by a transfer descriptor (the qTD). The qTDs form a linked
* list with a queue head (QH).
*
* Each qTD transfer starts with a new USB packet, i.e. a packet cannot
* have its beginning in a qTD transfer and its end in the following
* one, so the qTD transfer lengths have to be chosen accordingly.
*
* Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to
* single pages. The first data buffer can start at any offset within a
* page (not considering the cache-line alignment issues), while the
* following buffers must be page-aligned. There is no alignment
* constraint on the size of a qTD transfer.
*/
if (req != NULL)
/* 1 qTD will be needed for SETUP, and 1 for ACK. */
qtd_count += 1 + 1;
if (length > 0 || req == NULL) {
/*
* Determine the qTD transfer size that will be used for the
* data payload (not considering the first qTD transfer, which
* may be longer or shorter, and the final one, which may be
* shorter).
*
* In order to keep each packet within a qTD transfer, the qTD
* transfer size is aligned to PKT_ALIGN, which is a multiple of
* wMaxPacketSize (except in some cases for interrupt transfers,
* see comment in submit_int_msg()).
*
* By default, i.e. if the input buffer is aligned to PKT_ALIGN,
* QT_BUFFER_CNT full pages will be used.
*/
int xfr_sz = QT_BUFFER_CNT;
/*
* However, if the input buffer is not aligned to PKT_ALIGN, the
* qTD transfer size will be one page shorter, and the first qTD
* data buffer of each transfer will be page-unaligned.
*/
if ((uint32_t)buffer & (PKT_ALIGN - 1))
xfr_sz--;
/* Convert the qTD transfer size to bytes. */
xfr_sz *= EHCI_PAGE_SIZE;
/*
* Approximate by excess the number of qTDs that will be
* required for the data payload. The exact formula is way more
* complicated and saves at most 2 qTDs, i.e. a total of 128
* bytes.
*/
qtd_count += 2 + length / xfr_sz;
}
/*
* Threshold value based on the worst-case total size of the allocated qTDs for
* a mass-storage transfer of 65535 blocks of 512 bytes.
*/
#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024
#warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI
#endif
qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD));
if (qtd == NULL) {
printf("unable to allocate TDs\n");
return -1;
}
memset(qh, 0, sizeof(struct QH));
memset(qtd, 0, 3 * sizeof(*qtd));
memset(qtd, 0, qtd_count * sizeof(*qtd));
toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
@ -245,20 +311,18 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
* - qh_overlay.qt_altnext
*/
qh->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
usb_pipeendpoint(pipe) == 0) ? 1 : 0;
endpt = (8 << 28) |
(c << 27) |
(usb_maxpacket(dev, pipe) << 16) |
(0 << 15) |
(1 << 14) |
(usb_pipespeed(pipe) << 12) |
(usb_pipeendpoint(pipe) << 8) |
(0 << 7) | (usb_pipedevice(pipe) << 0);
c = usb_pipespeed(pipe) != USB_SPEED_HIGH && !usb_pipeendpoint(pipe);
maxpacket = usb_maxpacket(dev, pipe);
endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) |
QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) |
QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) |
QH_ENDPT1_EPS(usb_pipespeed(pipe)) |
QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) |
QH_ENDPT1_DEVADDR(usb_pipedevice(pipe));
qh->qh_endpt1 = cpu_to_hc32(endpt);
endpt = (1 << 30) |
(dev->portnr << 23) |
(dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) |
QH_ENDPT2_HUBADDR(dev->parent->devnum) |
QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);
qh->qh_endpt2 = cpu_to_hc32(endpt);
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
@ -276,12 +340,13 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (0 << 31) |
(sizeof(*req) << 16) |
(0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) |
QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
QT_TOKEN_PID(QT_TOKEN_PID_SETUP) |
QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) {
printf("unable construct SETUP td\n");
if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) {
printf("unable to construct SETUP TD\n");
goto fail;
}
/* Update previous qTD! */
@ -291,31 +356,71 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
}
if (length > 0 || req == NULL) {
/*
* Setup request qTD (3.5 in ehci-r10.pdf)
*
* qt_next ................ 03-00 H
* qt_altnext ............. 07-04 H
* qt_token ............... 0B-08 H
*
* [ buffer, buffer_hi ] loaded with "buffer".
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
(length << 16) |
((req == NULL ? 1 : 0) << 15) |
(0 << 12) |
(3 << 10) |
((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) {
printf("unable construct DATA td\n");
goto fail;
}
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
tdp = &qtd[qtd_counter++].qt_next;
uint8_t *buf_ptr = buffer;
int left_length = length;
do {
/*
* Determine the size of this qTD transfer. By default,
* QT_BUFFER_CNT full pages can be used.
*/
int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE;
/*
* However, if the input buffer is not page-aligned, the
* portion of the first page before the buffer start
* offset within that page is unusable.
*/
xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1);
/*
* In order to keep each packet within a qTD transfer,
* align the qTD transfer size to PKT_ALIGN.
*/
xfr_bytes &= ~(PKT_ALIGN - 1);
/*
* This transfer may be shorter than the available qTD
* transfer size that has just been computed.
*/
xfr_bytes = min(xfr_bytes, left_length);
/*
* Setup request qTD (3.5 in ehci-r10.pdf)
*
* qt_next ................ 03-00 H
* qt_altnext ............. 07-04 H
* qt_token ............... 0B-08 H
*
* [ buffer, buffer_hi ] loaded with "buffer".
*/
qtd[qtd_counter].qt_next =
cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext =
cpu_to_hc32(QT_NEXT_TERMINATE);
token = QT_TOKEN_DT(toggle) |
QT_TOKEN_TOTALBYTES(xfr_bytes) |
QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) |
QT_TOKEN_CERR(3) |
QT_TOKEN_PID(usb_pipein(pipe) ?
QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) |
QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr,
xfr_bytes)) {
printf("unable to construct DATA TD\n");
goto fail;
}
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
tdp = &qtd[qtd_counter++].qt_next;
/*
* Data toggle has to be adjusted since the qTD transfer
* size is not always an even multiple of
* wMaxPacketSize.
*/
if ((xfr_bytes / maxpacket) & 1)
toggle ^= 1;
buf_ptr += xfr_bytes;
left_length -= xfr_bytes;
} while (left_length > 0);
}
if (req != NULL) {
@ -328,12 +433,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
(0 << 16) |
(1 << 15) |
(0 << 12) |
(3 << 10) |
((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) |
QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
QT_TOKEN_PID(usb_pipein(pipe) ?
QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) |
QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
@ -346,7 +450,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
flush_dcache_range((uint32_t)qh_list,
ALIGN_END_ADDR(struct QH, qh_list, 1));
flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1));
flush_dcache_range((uint32_t)qtd, ALIGN_END_ADDR(struct qTD, qtd, 3));
flush_dcache_range((uint32_t)qtd,
ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
/* Set async. queue head pointer. */
ehci_writel(&hcor->or_asynclistaddr, (uint32_t)qh_list);
@ -359,10 +464,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
cmd |= CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);
ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, STS_ASS,
100 * 1000);
if (ret < 0) {
printf("EHCI fail timeout STD_ASS set\n");
printf("EHCI fail timeout STS_ASS set\n");
goto fail;
}
@ -377,10 +482,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
invalidate_dcache_range((uint32_t)qh,
ALIGN_END_ADDR(struct QH, qh, 1));
invalidate_dcache_range((uint32_t)qtd,
ALIGN_END_ADDR(struct qTD, qtd, 3));
ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
token = hc32_to_cpu(vtd->qt_token);
if (!(token & 0x80))
if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE))
break;
WATCHDOG_RESET();
} while (get_timer(ts) < timeout);
@ -398,50 +503,50 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN));
/* Check that the TD processing happened */
if (token & 0x80) {
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
printf("EHCI timed out on TD - token=%#x\n", token);
}
/* Disable async schedule. */
cmd = ehci_readl(&hcor->or_usbcmd);
cmd &= ~CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);
ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
ret = handshake((uint32_t *)&hcor->or_usbsts, STS_ASS, 0,
100 * 1000);
if (ret < 0) {
printf("EHCI fail timeout STD_ASS reset\n");
printf("EHCI fail timeout STS_ASS reset\n");
goto fail;
}
token = hc32_to_cpu(qh->qh_overlay.qt_token);
if (!(token & 0x80)) {
if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {
debug("TOKEN=%#x\n", token);
switch (token & 0xfc) {
switch (QT_TOKEN_GET_STATUS(token) &
~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
case 0:
toggle = token >> 31;
toggle = QT_TOKEN_GET_DT(token);
usb_settoggle(dev, usb_pipeendpoint(pipe),
usb_pipeout(pipe), toggle);
dev->status = 0;
break;
case 0x40:
case QT_TOKEN_STATUS_HALTED:
dev->status = USB_ST_STALLED;
break;
case 0xa0:
case 0x20:
case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR:
case QT_TOKEN_STATUS_DATBUFERR:
dev->status = USB_ST_BUF_ERR;
break;
case 0x50:
case 0x10:
case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET:
case QT_TOKEN_STATUS_BABBLEDET:
dev->status = USB_ST_BABBLE_DET;
break;
default:
dev->status = USB_ST_CRC_ERR;
if ((token & 0x40) == 0x40)
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)
dev->status |= USB_ST_STALLED;
break;
}
dev->act_len = length - ((token >> 16) & 0x7fff);
dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
} else {
dev->act_len = 0;
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
@ -450,9 +555,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
ehci_readl(&hcor->or_portsc[1]));
}
free(qtd);
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
fail:
free(qtd);
return -1;
}
@ -499,12 +606,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
case USB_DT_DEVICE:
debug("USB_DT_DEVICE request\n");
srcptr = &descriptor.device;
srclen = 0x12;
srclen = descriptor.device.bLength;
break;
case USB_DT_CONFIG:
debug("USB_DT_CONFIG config\n");
srcptr = &descriptor.config;
srclen = 0x19;
srclen = descriptor.config.bLength +
descriptor.interface.bLength +
descriptor.endpoint.bLength;
break;
case USB_DT_STRING:
debug("USB_DT_STRING config\n");
@ -539,7 +648,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
case USB_DT_HUB:
debug("USB_DT_HUB config\n");
srcptr = &descriptor.hub;
srclen = 0x8;
srclen = descriptor.hub.bLength;
break;
default:
debug("unknown value %x\n", le16_to_cpu(req->value));
@ -577,13 +686,13 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (ehci_is_TDI()) {
switch ((reg >> 26) & 3) {
case 0:
switch (PORTSC_PSPD(reg)) {
case PORTSC_PSPD_FS:
break;
case 1:
case PORTSC_PSPD_LS:
tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
break;
case 2:
case PORTSC_PSPD_HS:
default:
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
break;
@ -729,26 +838,28 @@ int usb_lowlevel_init(void)
uint32_t reg;
uint32_t cmd;
if (ehci_hcd_init() != 0)
if (ehci_hcd_init())
return -1;
/* EHCI spec section 4.1 */
if (ehci_reset() != 0)
if (ehci_reset())
return -1;
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
if (ehci_hcd_init() != 0)
if (ehci_hcd_init())
return -1;
#endif
/* Set head of reclaim list */
memset(qh_list, 0, sizeof(*qh_list));
qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12));
qh_list->qh_endpt1 = cpu_to_hc32(QH_ENDPT1_H(1) |
QH_ENDPT1_EPS(USB_SPEED_HIGH));
qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40);
qh_list->qh_overlay.qt_token =
cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED));
reg = ehci_readl(&hccr->cr_hcsparams);
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
@ -808,7 +919,7 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
}
if (usb_pipedevice(pipe) == rootdev) {
if (rootdev == 0)
if (!rootdev)
dev->speed = USB_SPEED_HIGH;
return ehci_submit_root(dev, pipe, buffer, length, setup);
}
@ -819,8 +930,24 @@ int
submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, int interval)
{
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
/*
* Interrupt transfers requiring several transactions are not supported
* because bInterval is ignored.
*
* Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
* <= PKT_ALIGN if several qTDs are required, while the USB
* specification does not constrain this for interrupt transfers. That
* means that ehci_submit_async() would support interrupt transfers
* requiring several transactions only as long as the transfer size does
* 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__);
return -1;
}
return ehci_submit_async(dev, pipe, buffer, length, NULL);
}

View File

@ -68,7 +68,7 @@ struct ehci_hcor {
#define CMD_RESET (1 << 1) /* reset HC not bus */
#define CMD_RUN (1 << 0) /* start/stop HC */
uint32_t or_usbsts;
#define STD_ASS (1 << 15)
#define STS_ASS (1 << 15)
#define STS_HALT (1 << 12)
uint32_t or_usbintr;
#define INTR_UE (1 << 0) /* USB interrupt enable */
@ -83,11 +83,16 @@ struct ehci_hcor {
uint32_t _reserved_0_;
uint32_t or_burstsize;
uint32_t or_txfilltuning;
#define TXFIFO_THRESH_MASK (0x3f << 16)
#define TXFIFO_THRESH(p) ((p & 0x3f) << 16)
uint32_t _reserved_1_[6];
uint32_t or_configflag;
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
#define PORTSC_PSPD(x) (((x) >> 26) & 0x3)
#define PORTSC_PSPD_FS 0x0
#define PORTSC_PSPD_LS 0x1
#define PORTSC_PSPD_HS 0x2
uint32_t or_systune;
} __attribute__ ((packed, aligned(4)));
@ -171,16 +176,40 @@ struct usb_linux_config_descriptor {
/* Queue Element Transfer Descriptor (qTD). */
struct qTD {
/* this part defined by EHCI spec */
uint32_t qt_next; /* see EHCI 3.5.1 */
uint32_t qt_next; /* see EHCI 3.5.1 */
#define QT_NEXT_TERMINATE 1
uint32_t qt_altnext; /* see EHCI 3.5.2 */
uint32_t qt_token; /* see EHCI 3.5.3 */
uint32_t qt_buffer[5]; /* see EHCI 3.5.4 */
uint32_t qt_buffer_hi[5]; /* Appendix B */
uint32_t qt_altnext; /* see EHCI 3.5.2 */
uint32_t qt_token; /* see EHCI 3.5.3 */
#define QT_TOKEN_DT(x) (((x) & 0x1) << 31) /* Data Toggle */
#define QT_TOKEN_GET_DT(x) (((x) >> 31) & 0x1)
#define QT_TOKEN_TOTALBYTES(x) (((x) & 0x7fff) << 16) /* Total Bytes to Transfer */
#define QT_TOKEN_GET_TOTALBYTES(x) (((x) >> 16) & 0x7fff)
#define QT_TOKEN_IOC(x) (((x) & 0x1) << 15) /* Interrupt On Complete */
#define QT_TOKEN_CPAGE(x) (((x) & 0x7) << 12) /* Current Page */
#define QT_TOKEN_CERR(x) (((x) & 0x3) << 10) /* Error Counter */
#define QT_TOKEN_PID(x) (((x) & 0x3) << 8) /* PID Code */
#define QT_TOKEN_PID_OUT 0x0
#define QT_TOKEN_PID_IN 0x1
#define QT_TOKEN_PID_SETUP 0x2
#define QT_TOKEN_STATUS(x) (((x) & 0xff) << 0) /* Status */
#define QT_TOKEN_GET_STATUS(x) (((x) >> 0) & 0xff)
#define QT_TOKEN_STATUS_ACTIVE 0x80
#define QT_TOKEN_STATUS_HALTED 0x40
#define QT_TOKEN_STATUS_DATBUFERR 0x20
#define QT_TOKEN_STATUS_BABBLEDET 0x10
#define QT_TOKEN_STATUS_XACTERR 0x08
#define QT_TOKEN_STATUS_MISSEDUFRAME 0x04
#define QT_TOKEN_STATUS_SPLITXSTATE 0x02
#define QT_TOKEN_STATUS_PERR 0x01
#define QT_BUFFER_CNT 5
uint32_t qt_buffer[QT_BUFFER_CNT]; /* see EHCI 3.5.4 */
uint32_t qt_buffer_hi[QT_BUFFER_CNT]; /* Appendix B */
/* pad struct for 32 byte alignment */
uint32_t unused[3];
};
#define EHCI_PAGE_SIZE 4096
/* Queue Head (QH). */
struct QH {
uint32_t qh_link;
@ -190,7 +219,26 @@ struct QH {
#define QH_LINK_TYPE_SITD 4
#define QH_LINK_TYPE_FSTN 6
uint32_t qh_endpt1;
#define QH_ENDPT1_RL(x) (((x) & 0xf) << 28) /* NAK Count Reload */
#define QH_ENDPT1_C(x) (((x) & 0x1) << 27) /* Control Endpoint Flag */
#define QH_ENDPT1_MAXPKTLEN(x) (((x) & 0x7ff) << 16) /* Maximum Packet Length */
#define QH_ENDPT1_H(x) (((x) & 0x1) << 15) /* Head of Reclamation List Flag */
#define QH_ENDPT1_DTC(x) (((x) & 0x1) << 14) /* Data Toggle Control */
#define QH_ENDPT1_DTC_IGNORE_QTD_TD 0x0
#define QH_ENDPT1_DTC_DT_FROM_QTD 0x1
#define QH_ENDPT1_EPS(x) (((x) & 0x3) << 12) /* Endpoint Speed */
#define QH_ENDPT1_EPS_FS 0x0
#define QH_ENDPT1_EPS_LS 0x1
#define QH_ENDPT1_EPS_HS 0x2
#define QH_ENDPT1_ENDPT(x) (((x) & 0xf) << 8) /* Endpoint Number */
#define QH_ENDPT1_I(x) (((x) & 0x1) << 7) /* Inactivate on Next Transaction */
#define QH_ENDPT1_DEVADDR(x) (((x) & 0x7f) << 0) /* Device Address */
uint32_t qh_endpt2;
#define QH_ENDPT2_MULT(x) (((x) & 0x3) << 30) /* High-Bandwidth Pipe Multiplier */
#define QH_ENDPT2_PORTNUM(x) (((x) & 0x7f) << 23) /* Port Number */
#define QH_ENDPT2_HUBADDR(x) (((x) & 0x7f) << 16) /* Hub Address */
#define QH_ENDPT2_UFCMASK(x) (((x) & 0xff) << 8) /* Split Completion Mask */
#define QH_ENDPT2_UFSMASK(x) (((x) & 0xff) << 0) /* Interrupt Schedule Mask */
uint32_t qh_curtd;
struct qTD qh_overlay;
/*

View File

@ -1261,19 +1261,11 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
int leni = transfer_len;
int len = 0;
int stat = 0;
__u32 datab[4];
union {
void *ptr;
__u8 *u8;
__u16 *u16;
__u32 *u32;
} databuf;
__u16 bmRType_bReq;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
databuf.u32 = (__u32 *)datab;
ALLOC_ALIGN_BUFFER(__u8, databuf, 16, sizeof(u32));
#ifdef DEBUG
pkt_print(NULL, dev, pipe, buffer, transfer_len,
@ -1304,20 +1296,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
*/
case RH_GET_STATUS:
databuf.u16[0] = cpu_to_le16(1);
*(u16 *)databuf = cpu_to_le16(1);
OK(2);
case RH_GET_STATUS | RH_INTERFACE:
databuf.u16[0] = cpu_to_le16(0);
*(u16 *)databuf = cpu_to_le16(0);
OK(2);
case RH_GET_STATUS | RH_ENDPOINT:
databuf.u16[0] = cpu_to_le16(0);
*(u16 *)databuf = cpu_to_le16(0);
OK(2);
case RH_GET_STATUS | RH_CLASS:
databuf.u32[0] = cpu_to_le32(
*(u32 *)databuf = cpu_to_le32(
RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
OK(4);
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT);
*(u32 *)databuf = cpu_to_le32(RD_RH_PORTSTAT);
OK(4);
case RH_CLEAR_FEATURE | RH_ENDPOINT:
@ -1381,14 +1373,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
min_t(unsigned int,
sizeof(root_hub_dev_des),
wLength));
databuf.ptr = root_hub_dev_des; OK(len);
databuf = root_hub_dev_des; OK(len);
case (0x02): /* configuration descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_config_des),
wLength));
databuf.ptr = root_hub_config_des; OK(len);
databuf = root_hub_config_des; OK(len);
case (0x03): /* string descriptors */
if (wValue == 0x0300) {
len = min_t(unsigned int,
@ -1396,7 +1388,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
min_t(unsigned int,
sizeof(root_hub_str_index0),
wLength));
databuf.ptr = root_hub_str_index0;
databuf = root_hub_str_index0;
OK(len);
}
if (wValue == 0x0301) {
@ -1405,7 +1397,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
min_t(unsigned int,
sizeof(root_hub_str_index1),
wLength));
databuf.ptr = root_hub_str_index1;
databuf = root_hub_str_index1;
OK(len);
}
default:
@ -1417,40 +1409,40 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
{
__u32 temp = roothub_a(&gohci);
databuf.u8[0] = 9; /* min length; */
databuf.u8[1] = 0x29;
databuf.u8[2] = temp & RH_A_NDP;
databuf[0] = 9; /* min length; */
databuf[1] = 0x29;
databuf[2] = temp & RH_A_NDP;
#ifdef CONFIG_AT91C_PQFP_UHPBUG
databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0;
databuf[2] = (databuf[2] == 2) ? 1 : 0;
#endif
databuf.u8[3] = 0;
databuf[3] = 0;
if (temp & RH_A_PSM) /* per-port power switching? */
databuf.u8[3] |= 0x1;
databuf[3] |= 0x1;
if (temp & RH_A_NOCP) /* no overcurrent reporting? */
databuf.u8[3] |= 0x10;
databuf[3] |= 0x10;
else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */
databuf.u8[3] |= 0x8;
databuf[3] |= 0x8;
/* corresponds to databuf.u8[4-7] */
databuf.u8[1] = 0;
databuf.u8[5] = (temp & RH_A_POTPGT) >> 24;
databuf[4] = 0;
databuf[5] = (temp & RH_A_POTPGT) >> 24;
databuf[6] = 0;
temp = roothub_b(&gohci);
databuf.u8[7] = temp & RH_B_DR;
if (databuf.u8[2] < 7) {
databuf.u8[8] = 0xff;
databuf[7] = temp & RH_B_DR;
if (databuf[2] < 7) {
databuf[8] = 0xff;
} else {
databuf.u8[0] += 2;
databuf.u8[8] = (temp & RH_B_DR) >> 8;
databuf.u8[10] = databuf.u8[9] = 0xff;
databuf[0] += 2;
databuf[8] = (temp & RH_B_DR) >> 8;
databuf[10] = databuf[9] = 0xff;
}
len = min_t(unsigned int, leni,
min_t(unsigned int, databuf.u8[0], wLength));
min_t(unsigned int, databuf[0], wLength));
OK(len);
}
case RH_GET_CONFIGURATION:
databuf.u8[0] = 0x01;
databuf[0] = 0x01;
OK(1);
case RH_SET_CONFIGURATION:
@ -1469,8 +1461,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,
#endif
len = min_t(int, len, leni);
if (data != databuf.ptr)
memcpy(data, databuf.ptr, len);
if (data != databuf)
memcpy(data, databuf, len);
dev->act_len = len;
dev->status = stat;

View File

@ -1113,7 +1113,7 @@ int usb_lowlevel_init(void)
* should be a usb device connected.
*/
timeout = musb_cfg.timeout;
while (timeout--)
while (--timeout)
if (readb(&musbr->devctl) & MUSB_DEVCTL_HM)
break;

View File

@ -97,6 +97,21 @@
#undef CONFIG_CMD_ONENAND
#undef CONFIG_CMD_MTDPARTS
#define CONFIG_CMD_MMC
#define CONFIG_CMD_DFU
/* FAT */
#define CONFIG_CMD_FAT
#define CONFIG_FAT_WRITE
/* USB Composite download gadget - g_dnl */
#define CONFIG_USBDOWNLOAD_GADGET
#define CONFIG_DFU_FUNCTION
#define CONFIG_DFU_MMC
/* USB Samsung's IDs */
#define CONFIG_G_DNL_VENDOR_NUM 0x04E8
#define CONFIG_G_DNL_PRODUCT_NUM 0x6601
#define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1
#define CONFIG_ZERO_BOOTDELAY_CHECK
@ -107,6 +122,11 @@
#define CONFIG_BOOTBLOCK "10"
#define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}"
#define CONFIG_DFU_ALT \
"dfu_alt_info=" \
"u-boot mmc 80 400;" \
"uImage fat 0 2\0" \
#define CONFIG_ENV_OVERWRITE
#define CONFIG_SYS_CONSOLE_INFO_QUIET
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
@ -150,7 +170,8 @@
"mmcdev=0\0" \
"mmcbootpart=2\0" \
"mmcrootpart=3\0" \
"opts=always_resume=1"
"opts=always_resume=1\0" \
CONFIG_DFU_ALT
/* Miscellaneous configurable options */
#define CONFIG_SYS_LONGHELP /* undef to save memory */
@ -210,6 +231,7 @@
#define CONFIG_USB_GADGET
#define CONFIG_USB_GADGET_S3C_UDC_OTG
#define CONFIG_USB_GADGET_DUALSPEED
#define CONFIG_USB_GADGET_VBUS_DRAW 2
/* LCD */
#define CONFIG_EXYNOS_FB

103
include/dfu.h 100644
View File

@ -0,0 +1,103 @@
/*
* dfu.h - DFU flashable area description
*
* Copyright (C) 2012 Samsung Electronics
* authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
* 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
*/
#ifndef __DFU_ENTITY_H_
#define __DFU_ENTITY_H_
#include <common.h>
#include <linux/list.h>
#include <mmc.h>
enum dfu_device_type {
DFU_DEV_MMC = 1,
DFU_DEV_ONENAND,
DFU_DEV_NAND,
};
enum dfu_layout {
DFU_RAW_ADDR = 1,
DFU_FS_FAT,
DFU_FS_EXT2,
DFU_FS_EXT3,
DFU_FS_EXT4,
};
struct mmc_internal_data {
/* RAW programming */
unsigned int lba_start;
unsigned int lba_size;
unsigned int lba_blk_size;
/* FAT/EXT */
unsigned int dev;
unsigned int part;
};
static inline unsigned int get_mmc_blk_size(int dev)
{
return find_mmc_device(dev)->read_bl_len;
}
#define DFU_NAME_SIZE 32
#define DFU_CMD_BUF_SIZE 128
#define DFU_DATA_BUF_SIZE (1024*1024*4) /* 4 MiB */
struct dfu_entity {
char name[DFU_NAME_SIZE];
int alt;
void *dev_private;
int dev_num;
enum dfu_device_type dev_type;
enum dfu_layout layout;
union {
struct mmc_internal_data mmc;
} data;
int (*read_medium)(struct dfu_entity *dfu, void *buf, long *len);
int (*write_medium)(struct dfu_entity *dfu, void *buf, long *len);
struct list_head list;
};
int dfu_config_entities(char *s, char *interface, int num);
void dfu_free_entities(void);
void dfu_show_entities(void);
int dfu_get_alt_number(void);
const char *dfu_get_dev_type(enum dfu_device_type t);
const char *dfu_get_layout(enum dfu_layout l);
struct dfu_entity *dfu_get_entity(int alt);
char *dfu_extract_token(char** e, int *n);
int dfu_read(struct dfu_entity *de, void *buf, int size, int blk_seq_num);
int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num);
/* Device specific */
#ifdef CONFIG_DFU_MMC
extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s);
#else
static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
{
puts("MMC support not available!\n");
return -1;
}
#endif
#endif /* __DFU_ENTITY_H_ */

33
include/g_dnl.h 100644
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2012 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
*/
#ifndef __G_DOWNLOAD_H_
#define __G_DOWNLOAD_H_
#include <linux/usb/ch9.h>
#include <usbdescriptors.h>
#include <linux/usb/gadget.h>
int g_dnl_register(const char *s);
void g_dnl_unregister(void);
/* USB initialization declaration - board specific */
void board_usb_init(void);
#endif /* __G_DOWNLOAD_H_ */

View File

@ -61,7 +61,7 @@ int ulpi_select_transceiver(struct ulpi_viewport *ulpi_vp, unsigned speed);
*
* returns 0 on success, ULPI_ERROR on failure.
*/
int ulpi_enable_vbus(struct ulpi_viewport *ulpi_vp,
int ulpi_set_vbus(struct ulpi_viewport *ulpi_vp,
int on, int ext_power, int ext_ind);
/*