* '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>master
commit
7cdcaef0b2
@ -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__ */ |
@ -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" |
||||
); |
@ -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 |
||||
|
||||
#########################################################################
|
@ -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; |
||||
} |
@ -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; |
||||
} |
@ -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); |
||||
} |
@ -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_ */ |
@ -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); |
||||
} |
@ -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_ */ |
@ -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_ */ |
Loading…
Reference in new issue