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>
This commit is contained in:
commit
7cdcaef0b2
1
Makefile
1
Makefile
@ -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
|
||||
|
159
arch/arm/include/asm/arch-pxa/regs-usb.h
Normal file
159
arch/arm/include/asm/arch-pxa/regs-usb.h
Normal 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__ */
|
@ -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)
|
||||
|
@ -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
Normal file
81
common/cmd_dfu.c
Normal 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"
|
||||
);
|
@ -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
|
||||
|
44
drivers/dfu/Makefile
Normal file
44
drivers/dfu/Makefile
Normal 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
Normal file
238
drivers/dfu/dfu.c
Normal 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;
|
||||
}
|
162
drivers/dfu/dfu_mmc.c
Normal file
162
drivers/dfu/dfu_mmc.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
749
drivers/usb/gadget/f_dfu.c
Normal file
749
drivers/usb/gadget/f_dfu.c
Normal 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);
|
||||
}
|
100
drivers/usb/gadget/f_dfu.h
Normal file
100
drivers/usb/gadget/f_dfu.h
Normal 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_ */
|
202
drivers/usb/gadget/g_dnl.c
Normal file
202
drivers/usb/gadget/g_dnl.c
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
/*
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
Normal file
103
include/dfu.h
Normal 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
Normal file
33
include/g_dnl.h
Normal 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_ */
|
@ -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);
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user