[U-Boot] [PATCH 00/10] usb: Support for TIZEN's THOR download protocol

This patch series provide support for TIZEN's THOR download protocol.
Dedicated program for flashing TIZEN developer devices (TRATS, TRATS2) is called lthor (or thor for Windows) and can be found at:
git clone git://review.tizen.org/tools/lthor
or for git web:
https://review.tizen.org/git/?p=tools/lthor.git;a=summary
Presented composite USB function acts as a front end to perform correct USB communication with HOST PC. To store the received data on the target, the DFU (Device Firmware Update) code for flashing has been reused.
This means, that the "dfu_alt_info" environment variable is used to provide information where a received file is stored.
This also means that dfu and thor can co-exists together. Thor protocol and its implementation has one advantage - it is much faster than DFU for large files transfers (especially rootfs images).
It applies on: u-boot-denx-usb/master SHA1: 5077f96f10fe88f1f7cbe09743ac7c765f9e98c3
Test HW: Exynos4210 (TRATS)
Lukasz Majewski (10): usb:udc:s3c: Reduce dcache invalidate range for UDC receive buffer dfu:core: Find DFU alt setting number by passing its name dfu:core: Export dfu_{get|free}_buf functions usb:g_dnl: Replace static usb_configuration structure with dynamically allocated one usb:g_dnl: Add name parameter to g_dnl_bind_fixup function usb:g_dnl:f_thor: USB download function to support TIZEN's THOR protocol usb:g_dnl: Support for TIZEN's THOR function at generic download code cmd:thor: Support for TIZEN's download (thordown) command samsung:common:thor: Define common Samsung code to handle THOR usb descriptor setup trats: Update TRATS config to support TIZEN download
board/samsung/common/Makefile | 1 + board/samsung/common/thor.c | 21 + board/siemens/common/factoryset.c | 2 +- common/Makefile | 1 + common/cmd_thordown.c | 58 ++ drivers/dfu/dfu.c | 16 +- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_thor.c | 1006 +++++++++++++++++++++++++++++ drivers/usb/gadget/f_thor.h | 123 ++++ drivers/usb/gadget/g_dnl.c | 38 +- drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- include/configs/trats.h | 13 +- include/dfu.h | 3 + include/g_dnl.h | 2 +- include/thor.h | 27 + 15 files changed, 1295 insertions(+), 19 deletions(-) create mode 100644 board/samsung/common/thor.c create mode 100644 common/cmd_thordown.c create mode 100644 drivers/usb/gadget/f_thor.c create mode 100644 drivers/usb/gadget/f_thor.h create mode 100644 include/thor.h

The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num] - + DMA_BUFFER_SIZE); + + ep->ep.maxpacket);
if (length == 0) pktcnt = 1;

Dear Lukasz Majewski,
The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num]
+ DMA_BUFFER_SIZE);
+ ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num]
+ DMA_BUFFER_SIZE);
+ ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
The maxpacket value is equal to 64 B for EP0 and 512 B for EP1 and EP2 (wMaxPacketSize field of the descriptor).
Moreover this invalidation is done on already memaligned buffer (which is 16 KiB). In other words, we are copying data to specially prepared buffer (per UDC device EP, not usb_request).
In my opinion the maxpacket don't need to be rounded up.
Best regards, Marek Vasut
Since this is Samsung's UDC specific (not THOR download) - would it be possible to take this patch from this patch series (of course if my above rationale is acceptable for you)?

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long)
ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num]
+ DMA_BUFFER_SIZE);
+ ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
The maxpacket value is equal to 64 B for EP0 and 512 B for EP1 and EP2 (wMaxPacketSize field of the descriptor).
Moreover this invalidation is done on already memaligned buffer (which is 16 KiB). In other words, we are copying data to specially prepared buffer (per UDC device EP, not usb_request).
In my opinion the maxpacket don't need to be rounded up.
So it can never happen that ep maxpacket is unaligned?
Best regards, Marek Vasut
Since this is Samsung's UDC specific (not THOR download) - would it be possible to take this patch from this patch series (of course if my above rationale is acceptable for you)?
You mean for .10 ?
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long)
ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num]
+ DMA_BUFFER_SIZE);
+ ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
The maxpacket value is equal to 64 B for EP0 and 512 B for EP1 and EP2 (wMaxPacketSize field of the descriptor).
Moreover this invalidation is done on already memaligned buffer (which is 16 KiB). In other words, we are copying data to specially prepared buffer (per UDC device EP, not usb_request).
In my opinion the maxpacket don't need to be rounded up.
So it can never happen that ep maxpacket is unaligned?
maxpacket can be defined as e.g. 16 Bytes (wMaxPacketSize).
However the underlying buffer (on which we perform dcache invalidation) is already cache line aware (defined with memalign). Also we copy the usb request data there (yes I know that this is not the best possible solution). Afterwards address of this buffer is a starting point for DMA. Since we are sending in the HW one packet at a time (with maxpacket size), then it should be enough to invalidate only up to 512 B. Previously the whole buffer (DMA_BUFFER_SIZE -> 16 KiB) was invalidated each time.
Since we are invalidating cache content corresponding to buffer mapped memory, we are safe when D-cache controller invalidates 32B (CACHE_LINE_SIZE) instead of 16B.
Best regards, Marek Vasut
Since this is Samsung's UDC specific (not THOR download) - would it be possible to take this patch from this patch series (of course if my above rationale is acceptable for you)?
You mean for .10 ?
This patch hasn't introduced regressions for DFU/UMS. It can be applied to .10 or to u-boot-usb/next.
Best regards, Marek Vasut

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long)
ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num]
+ DMA_BUFFER_SIZE);
+ ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
The maxpacket value is equal to 64 B for EP0 and 512 B for EP1 and EP2 (wMaxPacketSize field of the descriptor).
Moreover this invalidation is done on already memaligned buffer (which is 16 KiB). In other words, we are copying data to specially prepared buffer (per UDC device EP, not usb_request).
In my opinion the maxpacket don't need to be rounded up.
So it can never happen that ep maxpacket is unaligned?
maxpacket can be defined as e.g. 16 Bytes (wMaxPacketSize).
However the underlying buffer (on which we perform dcache invalidation) is already cache line aware (defined with memalign). Also we copy the usb request data there (yes I know that this is not the best possible solution). Afterwards address of this buffer is a starting point for DMA. Since we are sending in the HW one packet at a time (with maxpacket size), then it should be enough to invalidate only up to 512 B. Previously the whole buffer (DMA_BUFFER_SIZE -> 16 KiB) was invalidated each time.
Since we are invalidating cache content corresponding to buffer mapped memory, we are safe when D-cache controller invalidates 32B (CACHE_LINE_SIZE) instead of 16B.
I get it, but if the size is lower than CACHE_LINE_SIZE, it will not get invalidated at all. Thus the roundup might be needed.
Best regards, Marek Vasut
Since this is Samsung's UDC specific (not THOR download) - would it be possible to take this patch from this patch series (of course if my above rationale is acceptable for you)?
You mean for .10 ?
This patch hasn't introduced regressions for DFU/UMS. It can be applied to .10 or to u-boot-usb/next.
If it's not an explicit fix, this will go to -next
Best regards,

Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..5e3ba76 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long)
ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num]
+ DMA_BUFFER_SIZE);
+ ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
The maxpacket value is equal to 64 B for EP0 and 512 B for EP1 and EP2 (wMaxPacketSize field of the descriptor).
Moreover this invalidation is done on already memaligned buffer (which is 16 KiB). In other words, we are copying data to specially prepared buffer (per UDC device EP, not usb_request).
In my opinion the maxpacket don't need to be rounded up.
So it can never happen that ep maxpacket is unaligned?
maxpacket can be defined as e.g. 16 Bytes (wMaxPacketSize).
However the underlying buffer (on which we perform dcache invalidation) is already cache line aware (defined with memalign). Also we copy the usb request data there (yes I know that this is not the best possible solution). Afterwards address of this buffer is a starting point for DMA. Since we are sending in the HW one packet at a time (with maxpacket size), then it should be enough to invalidate only up to 512 B. Previously the whole buffer (DMA_BUFFER_SIZE -> 16 KiB) was invalidated each time.
Since we are invalidating cache content corresponding to buffer mapped memory, we are safe when D-cache controller invalidates 32B (CACHE_LINE_SIZE) instead of 16B.
I get it, but if the size is lower than CACHE_LINE_SIZE, it will not get invalidated at all.
I've looked into the v7_dcache_inval_range() function at cache_v7.c and .... you are right here. Cache doesn't touch the last and fist line if they aren't properly aligned. Obviously I need ROUND(CACHE_LINE_SIZE) there.
Thanks for opening my eyes....
Thus the roundup might be needed.
Best regards, Marek Vasut
Since this is Samsung's UDC specific (not THOR download) - would it be possible to take this patch from this patch series (of course if my above rationale is acceptable for you)?
You mean for .10 ?
This patch hasn't introduced regressions for DFU/UMS. It can be applied to .10 or to u-boot-usb/next.
If it's not an explicit fix, this will go to -next
I will prepare v2 with rounding here.
Best regards,

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
> The s3c udc driver sends data in a max packet size. > Therefore the dcache invalidate range shall be equal to max > packet, not the entire DMA_BUFFER_SIZE. > > Signed-off-by: Lukasz Majewski l.majewski@samsung.com > Cc: Marek Vasut marex@denx.de > --- > > drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c > b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index > d7af5e9..5e3ba76 100644 --- > a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ > b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 > +117,7 @@ static int setdma_rx(struct s3c_ep *ep, struct > s3c_request *req) > > invalidate_dcache_range((unsigned long) > > ep->dev->dma_buf[ep_num], (unsigned long) > ep->dev->dma_buf[ep_num] > - + DMA_BUFFER_SIZE); > + + ep->ep.maxpacket);
Is this maxpacket _always_ multiple of cacheline big or will you need some ROUND_UP() call here ?
The maxpacket value is equal to 64 B for EP0 and 512 B for EP1 and EP2 (wMaxPacketSize field of the descriptor).
Moreover this invalidation is done on already memaligned buffer (which is 16 KiB). In other words, we are copying data to specially prepared buffer (per UDC device EP, not usb_request).
In my opinion the maxpacket don't need to be rounded up.
So it can never happen that ep maxpacket is unaligned?
maxpacket can be defined as e.g. 16 Bytes (wMaxPacketSize).
However the underlying buffer (on which we perform dcache invalidation) is already cache line aware (defined with memalign). Also we copy the usb request data there (yes I know that this is not the best possible solution). Afterwards address of this buffer is a starting point for DMA. Since we are sending in the HW one packet at a time (with maxpacket size), then it should be enough to invalidate only up to 512 B. Previously the whole buffer (DMA_BUFFER_SIZE -> 16 KiB) was invalidated each time.
Since we are invalidating cache content corresponding to buffer mapped memory, we are safe when D-cache controller invalidates 32B (CACHE_LINE_SIZE) instead of 16B.
I get it, but if the size is lower than CACHE_LINE_SIZE, it will not get invalidated at all.
I've looked into the v7_dcache_inval_range() function at cache_v7.c and .... you are right here. Cache doesn't touch the last and fist line if they aren't properly aligned. Obviously I need ROUND(CACHE_LINE_SIZE) there.
Thanks for opening my eyes....
Thus the roundup might be needed.
Best regards, Marek Vasut
Since this is Samsung's UDC specific (not THOR download) - would it be possible to take this patch from this patch series (of course if my above rationale is acceptable for you)?
You mean for .10 ?
This patch hasn't introduced regressions for DFU/UMS. It can be applied to .10 or to u-boot-usb/next.
If it's not an explicit fix, this will go to -next
I will prepare v2 with rounding here.
Thanks ;-)

New function - dfu_get_alt() - has been added to dfu core. If present, it returns alt setting's number corresponding to passed name.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/dfu/dfu.c | 12 ++++++++++++ include/dfu.h | 1 + 2 files changed, 13 insertions(+)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 56b21c7..cbeea3c 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -440,3 +440,15 @@ struct dfu_entity *dfu_get_entity(int alt)
return NULL; } + +int dfu_get_alt(char *name) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (!strncmp(dfu->name, name, strlen(dfu->name))) + return dfu->alt; + } + + return -ENODEV; +} diff --git a/include/dfu.h b/include/dfu.h index b2ecf1b..b144255 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -126,6 +126,7 @@ 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); void dfu_trigger_reset(void); +int dfu_get_alt(char *name); bool dfu_reset(void); int dfu_init_env_entities(char *interface, int dev);

Define the dfu_get_buf() and dfu_free_buf() as global functions. They are necessary for zero copy buffer management, when DFU backend is used for storing data.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Marek Vasut marex@denx.de --- drivers/dfu/dfu.c | 4 ++-- include/dfu.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index cbeea3c..d656f0e 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -67,14 +67,14 @@ int dfu_init_env_entities(char *interface, int dev) static unsigned char *dfu_buf; static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
-static unsigned char *dfu_free_buf(void) +unsigned char *dfu_free_buf(void) { free(dfu_buf); dfu_buf = NULL; return dfu_buf; }
-static unsigned char *dfu_get_buf(void) +unsigned char *dfu_get_buf(void) { char *s;
diff --git a/include/dfu.h b/include/dfu.h index b144255..cc14044 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -129,6 +129,8 @@ void dfu_trigger_reset(void); int dfu_get_alt(char *name); bool dfu_reset(void); int dfu_init_env_entities(char *interface, int dev); +unsigned char *dfu_get_buf(void); +unsigned char *dfu_free_buf(void);
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);

When the usb_configuration structure is declared as static, it is very hard to assure, that relevant fields (as e.g. config->interfaces[]) are cleared out before new call to g_dnl related functions.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Marek Vasut marex@denx.de --- drivers/usb/gadget/g_dnl.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 40868c0..1aaf78f 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -79,6 +79,8 @@ static int g_dnl_unbind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget;
+ free(cdev->config); + cdev->config = NULL; debug("%s: calling usb_gadget_disconnect for " "controller '%s'\n", shortname, gadget->name); usb_gadget_disconnect(gadget); @@ -105,16 +107,22 @@ static int g_dnl_do_config(struct usb_configuration *c)
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 = CONFIGURATION_NUMBER, - .iConfiguration = STRING_USBDOWN, + struct usb_configuration *config; + const char *name = "usb_dnload";
- .bind = g_dnl_do_config, - }; + config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); + if (!config) + return -ENOMEM;
- return usb_add_config(cdev, &config); + memset(config, 0, sizeof(*config)); + + config->label = name; + config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; + config->bConfigurationValue = CONFIGURATION_NUMBER; + config->iConfiguration = STRING_USBDOWN; + config->bind = g_dnl_do_config; + + return usb_add_config(cdev, config); }
__weak

New parameter, namely *name has been added to g_dnl_bind_fixup(). It is necessary (for compatibility reasons) to assign new USB idProduct and idVendor for different usb functions.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de --- board/siemens/common/factoryset.c | 2 +- drivers/usb/gadget/g_dnl.c | 4 ++-- include/g_dnl.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/board/siemens/common/factoryset.c b/board/siemens/common/factoryset.c index eda9141..fbe7997 100644 --- a/board/siemens/common/factoryset.c +++ b/board/siemens/common/factoryset.c @@ -275,7 +275,7 @@ int factoryset_setenv(void) return ret; }
-int g_dnl_bind_fixup(struct usb_device_descriptor *dev) +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) { put_unaligned(factory_dat.usb_vendor_id, &dev->idVendor); put_unaligned(factory_dat.usb_product_id, &dev->idProduct); diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 1aaf78f..98560b8 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -126,7 +126,7 @@ static int g_dnl_config_register(struct usb_composite_dev *cdev) }
__weak -int g_dnl_bind_fixup(struct usb_device_descriptor *dev) +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) { return 0; } @@ -153,7 +153,7 @@ static int g_dnl_bind(struct usb_composite_dev *cdev) g_dnl_string_defs[1].id = id; device_desc.iProduct = id;
- g_dnl_bind_fixup(&device_desc); + g_dnl_bind_fixup(&device_desc, cdev->driver->name); ret = g_dnl_config_register(cdev); if (ret) goto error; diff --git a/include/g_dnl.h b/include/g_dnl.h index 2b2f11a..f16f451 100644 --- a/include/g_dnl.h +++ b/include/g_dnl.h @@ -10,7 +10,7 @@
#include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -int g_dnl_bind_fixup(struct usb_device_descriptor *); +int g_dnl_bind_fixup(struct usb_device_descriptor *, const char *); int g_dnl_register(const char *s); void g_dnl_unregister(void);

Implementation of USB download function which supports THOR protocol.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_thor.c | 1010 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_thor.h | 123 ++++++ include/thor.h | 27 ++ 4 files changed, 1161 insertions(+) create mode 100644 drivers/usb/gadget/f_thor.c create mode 100644 drivers/usb/gadget/f_thor.h create mode 100644 include/thor.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1590c4a..afaf5ce 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -21,6 +21,7 @@ ifdef CONFIG_USB_GADGET COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +COBJS-$(CONFIG_THOR_FUNCTION) += f_thor.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c new file mode 100644 index 0000000..366194d --- /dev/null +++ b/drivers/usb/gadget/f_thor.c @@ -0,0 +1,1010 @@ +/* + * f_thor.c -- USB TIZEN THOR Downloader gadget function + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * Based on code from: + * git://review.tizen.org/kernel/u-boot + * + * Developed by: + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang mk7.kang@samsung.com + * Sanghee Kim sh0130.kim@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <errno.h> +#include <common.h> +#include <malloc.h> +#include <version.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> +#include <g_dnl.h> +#include <dfu.h> + +#include "f_thor.h" + +static void thor_tx_data(char *data, int len); +static void thor_set_dma(void *addr, int len); +static int thor_rx_data(void); + +static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{ + return container_of(f, struct f_thor, usb_function); +} + +DEFINE_CACHE_ALIGN_BUFFER(char, thor_tx_data_buf, sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(char, thor_rx_data_buf, sizeof(struct rqt_box)); + +/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE); +static unsigned long long int thor_file_size; +static int alt_setting_num; + +static void send_rsp(const struct rsp_box *rsp) +{ + /* should be copy on dma duffer */ + memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box)); + + debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data); +} + +static void send_data_rsp(s32 ack, s32 count) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp, + sizeof(struct data_rsp_box)); + + rsp->ack = ack; + rsp->count = count; + + /* should be copy on dma duffer */ + memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct data_rsp_box)); + + debug("-DATA RSP: %d, %d\n", ack, count); +} + +static int process_rqt_info(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, '\0', sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_INFO_VER_PROTOCOL: + rsp->int_data[0] = VER_PROTOCOL_MAJOR; + rsp->int_data[1] = VER_PROTOCOL_MINOR; + break; + case RQT_INIT_VER_HW: + snprintf(rsp->str_data[0], sizeof(rsp->str_data[0]), + "%x", checkboard()); + break; + case RQT_INIT_VER_BOOT: + sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION); + break; + case RQT_INIT_VER_KERNEL: + sprintf(rsp->str_data[0], "%s", "k unknown"); + break; + case RQT_INIT_VER_PLATFORM: + sprintf(rsp->str_data[0], "%s", "p unknown"); + break; + case RQT_INIT_VER_CSC: + sprintf(rsp->str_data[0], "%s", "c unknown"); + break; + default: + return -EINVAL; + } + + send_rsp(rsp); + return true; +} + +static int process_rqt_cmd(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, '\0', sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_CMD_REBOOT: + debug("TARGET RESET\n"); + send_rsp(rsp); + g_dnl_unregister(); + dfu_free_entities(); + run_command("reset", 0); + break; + case RQT_CMD_POWEROFF: + case RQT_CMD_EFSCLEAR: + send_rsp(rsp); + default: + printf("Command not supported -> cmd: %d\n", rqt->rqt_data); + return -EINVAL; + } + + return true; +} + +static long long int download_head(unsigned long long total, + unsigned int packet_size, + long long int *left, + int *cnt) +{ + long long int rcv_cnt = 0, left_to_rcv, ret_rcv; + void *transfer_buffer = dfu_get_buf(); + void *buf = transfer_buffer; + int usb_pkt_cnt = 0, ret; + + /* + * Files smaller than THOR_STORE_UNIT_SIZE (now 32 MiB) are stored on + * the medium. + * The packet response is sent on the purpose after successful data + * chunk write. There is a room for improvement when asynchronous write + * is performed. + */ + while (total - rcv_cnt >= packet_size) { + thor_set_dma(buf, packet_size); + buf += packet_size; + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + debug("%d: RCV data count: %llu cnt: %d\n", usb_pkt_cnt, + rcv_cnt, *cnt); + + if ((rcv_cnt % THOR_STORE_UNIT_SIZE) == 0) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, THOR_STORE_UNIT_SIZE, + (*cnt)++); + if (ret) { + error("DFU write failed [%d] cnt: %d", + ret, *cnt); + return ret; + } + buf = transfer_buffer; + } + send_data_rsp(0, ++usb_pkt_cnt); + } + + /* Calculate the amount of data to arrive from PC (in bytes) */ + left_to_rcv = total - rcv_cnt; + + /* + * Calculate number of data already received. but not yet stored + * on the medium (they are smaller than THOR_STORE_UNIT_SIZE) + */ + *left = left_to_rcv + buf - transfer_buffer; + debug("%s: left: %llu left_to_rcv: %llu buf: 0x%p\n", __func__, + *left, left_to_rcv, buf); + + if (left_to_rcv) { + thor_set_dma(buf, packet_size); + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + send_data_rsp(0, ++usb_pkt_cnt); + } + + debug("%s: %llu total: %llu cnt: %d\n", __func__, rcv_cnt, total, *cnt); + + return rcv_cnt; +} + +static int download_tail(long long int left, int cnt) +{ + void *transfer_buffer = dfu_get_buf(); + int ret; + + debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); + + if (left) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, left, cnt++); + if (ret) { + error("DFU write failed [%d]: left: %llu", ret, left); + return ret; + } + } + + /* + * To store last "packet" DFU storage backend requires dfu_write with + * size parameter equal to 0 + * + * This also frees memory malloc'ed by dfu_get_buf(), so no explicit + * need fo call dfu_free_buf() is needed. + */ + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, 0, cnt); + if (ret) + error("DFU write failed [%d] cnt: %d", ret, cnt); + + return ret; +} + +static long long int process_rqt_download(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + static long long int left, ret_head; + int file_type, ret = 0; + static int cnt; + + memset(rsp, '\0', sizeof(struct rsp_box)); + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_DL_INIT: + thor_file_size = rqt->int_data[0]; + debug("INIT: total %d bytes\n", rqt->int_data[0]); + break; + case RQT_DL_FILE_INFO: + file_type = rqt->int_data[0]; + if (file_type == FILE_TYPE_PIT) { + puts("PIT table file - not supported\n"); + rsp->ack = -ENOTSUPP; + ret = rsp->ack; + break; + } + + thor_file_size = rqt->int_data[1]; + memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE); + + debug("INFO: name(%s, %d), size(%llu), type(%d)\n", + f_name, 0, thor_file_size, file_type); + + rsp->int_data[0] = THOR_PACKET_SIZE; + + alt_setting_num = dfu_get_alt(f_name); + if (alt_setting_num < 0) { + error("Alt setting [%d] to write not found!", + alt_setting_num); + rsp->ack = -ENODEV; + ret = rsp->ack; + } + break; + case RQT_DL_FILE_START: + send_rsp(rsp); + ret_head = download_head(thor_file_size, THOR_PACKET_SIZE, + &left, &cnt); + if (ret_head < 0) { + left = 0; + cnt = 0; + } + return ret_head; + case RQT_DL_FILE_END: + debug("DL FILE_END\n"); + rsp->ack = download_tail(left, cnt); + ret = rsp->ack; + left = 0; + cnt = 0; + break; + case RQT_DL_EXIT: + debug("DL EXIT\n"); + break; + default: + error("Operation not supported: %d", rqt->rqt_data); + ret = -ENOTSUPP; + } + + send_rsp(rsp); + return ret; +} + +static int process_data(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rqt_box, rqt, sizeof(struct rqt_box)); + int ret = -EINVAL; + + memset(rqt, 0, sizeof(rqt)); + memcpy(rqt, thor_rx_data_buf, sizeof(struct rqt_box)); + + debug("+RQT: %d, %d\n", rqt->rqt, rqt->rqt_data); + + switch (rqt->rqt) { + case RQT_INFO: + ret = process_rqt_info(rqt); + break; + case RQT_CMD: + ret = process_rqt_cmd(rqt); + break; + case RQT_DL: + ret = (int) process_rqt_download(rqt); + break; + case RQT_UL: + puts("RQT: UPLOAD not supported!\n"); + break; + default: + error("unknown request (%d)", rqt->rqt); + } + + return ret; +} + +/* ********************************************************** */ +/* THOR USB Function */ +/* ********************************************************** */ + +static inline struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +static struct usb_interface_descriptor thor_downloader_intf_data = { + .bLength = sizeof(thor_downloader_intf_data), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, +}; + + +static struct usb_endpoint_descriptor fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* CDC configuration */ +static struct usb_interface_descriptor thor_downloader_intf_int = { + .bLength = sizeof(thor_downloader_intf_int), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + /* 0x02 Abstract Line Control Model */ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + /* 0x01 Common AT commands */ + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, +}; + +static struct usb_cdc_header_desc thor_downloader_cdc_header = { + .bLength = sizeof(thor_downloader_cdc_header), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x00, + .bcdCDC = 0x0110, +}; + + +static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = { + .bLength = sizeof(thor_downloader_cdc_call), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x01, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = { + .bLength = sizeof(thor_downloader_cdc_abstract), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x02, + .bmCapabilities = 0x00, +}; + +struct usb_cdc_union_desc thor_downloader_cdc_union = { + .bLength = sizeof(thor_downloader_cdc_union), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = USB_CDC_UNION_TYPE, +}; + +static struct usb_endpoint_descriptor fs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 3 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static struct usb_interface_assoc_descriptor +thor_iad_descriptor = { + .bLength = sizeof(thor_iad_descriptor), + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTO_NONE, +}; + +static struct usb_endpoint_descriptor hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static 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 = 2, +}; + +/* + * This attribute vendor descriptor is necessary for correct operation with + * Windows version of THOR download program + * + * It prevents windows driver from sending zero lenght packet (ZLP) after + * each THOR_PACKET_SIZE. This assures consistent behaviour with libusb + */ +static struct usb_cdc_attribute_vendor_descriptor thor_downloader_cdc_av = { + .bLength = sizeof(thor_downloader_cdc_av), + .bDescriptorType = 0x24, + .bDescriptorSubType = 0x80, + .DAUType = 0x0002, + .DAULength = 0x0001, + .DAUValue = 0x00, +}; + +static const struct usb_descriptor_header *hs_thor_downloader_function[] = { + (struct usb_descriptor_header *)&thor_iad_descriptor, + + (struct usb_descriptor_header *)&thor_downloader_intf_int, + (struct usb_descriptor_header *)&thor_downloader_cdc_header, + (struct usb_descriptor_header *)&thor_downloader_cdc_call, + (struct usb_descriptor_header *)&thor_downloader_cdc_abstract, + (struct usb_descriptor_header *)&thor_downloader_cdc_union, + (struct usb_descriptor_header *)&hs_int_desc, + + (struct usb_descriptor_header *)&thor_downloader_intf_data, + (struct usb_descriptor_header *)&thor_downloader_cdc_av, + (struct usb_descriptor_header *)&hs_in_desc, + (struct usb_descriptor_header *)&hs_out_desc, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (req) { + req->length = length; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +static int thor_rx_data(void) +{ + struct thor_dev *dev = thor_func->dev; + int data_to_rx, tmp, status; + + data_to_rx = dev->out_req->length; + tmp = data_to_rx; + do { + dev->out_req->length = data_to_rx; + debug("dev->out_req->length:%d dev->rxdata:%d\n", + dev->out_req->length, dev->rxdata); + + status = usb_ep_queue(dev->out_ep, dev->out_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->out_ep->name, dev->out_req->length, status); + usb_ep_set_halt(dev->out_ep); + return -EAGAIN; + } + + while (!dev->rxdata) { + usb_gadget_handle_interrupts(); + if (ctrlc()) + return -1; + } + dev->rxdata = 0; + data_to_rx -= dev->out_req->actual; + } while (data_to_rx); + + return tmp; +} + +static void thor_tx_data(char *data, int len) +{ + struct thor_dev *dev = thor_func->dev; + unsigned char *ptr = dev->in_req->buf; + int status; + + memset(ptr, '\0', len); + memcpy(ptr, data, len); + + dev->in_req->length = len; + + debug("%s: dev->in_req->length:%d to_cpy:%d\n", __func__, + dev->in_req->length, sizeof(data)); + + status = usb_ep_queue(dev->in_ep, dev->in_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->in_ep->name, dev->in_req->length, status); + usb_ep_set_halt(dev->in_ep); + } + + /* Wait until tx interrupt received */ + while (!dev->txdata) + usb_gadget_handle_interrupts(); + + dev->txdata = 0; +} + +static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct thor_dev *dev = thor_func->dev; + int status = req->status; + + debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req); + switch (status) { + case 0: /* normal completion? */ + if (ep == dev->out_ep) + dev->rxdata = 1; + else + dev->txdata = 1; + + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + /* Exeptional situation - print error message */ + + case -EOVERFLOW: + error("ERROR:%d", status); + default: + debug("%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + case -EREMOTEIO: /* short read */ + break; + } +} + +static struct usb_request *thor_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = alloc_ep_req(ep, ep->maxpacket); + debug("%s: ep:%p req:%p\n", __func__, ep, req); + + if (!req) + return NULL; + + memset(req->buf, 0, req->length); + req->complete = thor_rx_tx_complete; + + memset(req->buf, 0x55, req->length); + + return req; +} + +static void thor_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int +thor_func_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct thor_dev *dev = thor_func->dev; + struct usb_request *req = dev->req; + struct usb_gadget *gadget = dev->gadget; + int value = 0; + + u16 len = le16_to_cpu(ctrl->wLength); + + debug("Req_Type: 0x%x Req: 0x%x wValue: 0x%x wIndex: 0x%x wLen: 0x%x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, + ctrl->wLength); + + switch (ctrl->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + value = 0; + break; + case USB_CDC_REQ_SET_LINE_CODING: + value = len; + /* Line Coding set done = configuration done */ + thor_func->dev->configuration_done = 1; + break; + + default: + error("thor_setup: unknown request: %d", ctrl->bRequest); + } + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("%s: ep_queue: %d\n", __func__, value); + req->status = 0; + } + } + + return value; +} + +/* Specific to the THOR protocol */ +static void thor_set_dma(void *addr, int len) +{ + struct thor_dev *dev = thor_func->dev; + + debug("in_req:%p, out_req:%p\n", dev->in_req, dev->out_req); + debug("addr:%p, len:%d\n", addr, len); + + dev->out_req->buf = addr; + dev->out_req->length = len; +} + +const char *recv_key = "THOR"; +const char *tx_key = "ROHT"; + +int thor_init(void) +{ + struct thor_dev *dev = thor_func->dev; + + /* Wait for a device enumeration and configuration settings */ + debug("THOR enumeration/configuration setting....\n"); + while (!dev->configuration_done) + usb_gadget_handle_interrupts(); + + thor_set_dma(thor_rx_data_buf, strlen(recv_key)); + /* detect the download request from Host PC */ + if (thor_rx_data() < 0) { + printf("%s: Data not received!\n", __func__); + return -1; + } + + if (strncmp(thor_rx_data_buf, recv_key, strlen(recv_key)) == 0) { + puts("Download request from the Host PC\n"); + udelay(30 * 1000); /* 30 ms */ + + strncpy(thor_tx_data_buf, tx_key, strlen(tx_key)); + thor_tx_data(thor_tx_data_buf, strlen(tx_key)); + } else { + puts("Wrong reply information\n"); + return -1; + } + + return 0; +} + +int thor_handle(void) +{ + int ret; + + /* receive the data from Host PC */ + while (1) { + thor_set_dma(thor_rx_data_buf, sizeof(struct rqt_box)); + ret = thor_rx_data(); + + if (ret > 0) { + ret = process_data(); + if (ret < 0) + return ret; + } else { + printf("%s: No data received!\n", __func__); + break; + } + } + + return 0; +} + +static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev; + struct usb_ep *ep; + int status; + + thor_func = f_thor; + dev = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + dev->gadget = gadget; + f_thor->dev = dev; + + debug("%s: usb_configuration: 0x%p usb_function: 0x%p\n", + __func__, c, f); + debug("f_thor: 0x%p thor: 0x%p\n", f_thor, dev); + + /* EP0 */ + /* preallocate control response and buffer */ + dev->req = usb_ep_alloc_request(gadget->ep0, 0); + if (!dev->req) { + status = -ENOMEM; + goto fail; + } + dev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, + gadget->ep0->maxpacket); + if (!dev->req->buf) { + status = -ENOMEM; + goto fail; + } + + dev->req->complete = thor_setup_complete; + + /* DYNAMIC interface numbers assignments */ + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_int.bInterfaceNumber = status; + thor_downloader_cdc_union.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_data.bInterfaceNumber = status; + thor_downloader_cdc_union.bSlaveInterface0 = status; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(gadget, &fs_in_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + } + + dev->in_ep = ep; /* Store IN EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_out_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + dev->out_ep = ep; /* Store OUT EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_int_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + dev->int_ep = ep; + + if (gadget_is_dualspeed(gadget)) { + hs_int_desc.bEndpointAddress = + fs_int_desc.bEndpointAddress; + + f->hs_descriptors = (struct usb_descriptor_header **) + &hs_thor_downloader_function; + + if (!f->hs_descriptors) + goto fail; + } + + debug("%s: out_ep:%p out_req:%p\n", __func__, + dev->out_ep, dev->out_req); + + return 0; + + fail: + free(dev); + return status; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + free(req->buf); + usb_ep_free_request(ep, req); +} + +static void thor_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + free(dev); + memset(thor_func, 0, sizeof(*thor_func)); + thor_func = NULL; +} + +static void thor_func_disable(struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + debug("%s:\n", __func__); + + /* Avoid freeing memory when ep is still claimed */ + if (dev->in_ep->driver_data) { + free_ep_req(dev->in_ep, dev->in_req); + usb_ep_disable(dev->in_ep); + dev->in_ep->driver_data = NULL; + } + + if (dev->out_ep->driver_data) { + dev->out_req->buf = NULL; + usb_ep_free_request(dev->out_ep, dev->out_req); + usb_ep_disable(dev->out_ep); + dev->out_ep->driver_data = NULL; + } + + if (dev->int_ep->driver_data) { + usb_ep_disable(dev->int_ep); + dev->int_ep->driver_data = NULL; + } +} + +static int thor_eps_setup(struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct thor_dev *dev = thor_func->dev; + struct usb_endpoint_descriptor *d; + struct usb_request *req; + struct usb_ep *ep; + int result; + + ep = dev->in_ep; + d = ep_desc(gadget, &hs_in_desc, &fs_in_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + + if (result == 0) { + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (req != NULL) { + dev->in_req = req; + } else { + usb_ep_disable(ep); + result = -EIO; + } + } else { + goto exit; + } + ep = dev->out_ep; + d = ep_desc(gadget, &hs_out_desc, &fs_out_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + + if (result == 0) { + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (req != NULL) { + dev->out_req = req; + } else { + usb_ep_disable(ep); + result = -EIO; + } + } else { + goto exit; + } + /* ACM control EP */ + ep = dev->int_ep; + ep->driver_data = cdev; /* claim */ + + exit: + return result; +} + +static int thor_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct thor_dev *dev = thor_func->dev; + int result; + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, intf, alt); + + switch (intf) { + case 0: + debug("ACM INTR interface\n"); + break; + case 1: + debug("Communication Data interface\n"); + result = thor_eps_setup(f); + if (result) + error("%s: EPs setup failed!", __func__); + dev->configuration_done = 1; + break; + } + + return 0; +} + +static int thor_func_init(struct usb_configuration *c) +{ + struct f_thor *f_thor; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + f_thor = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_thor)); + if (!f_thor) + return -ENOMEM; + + memset(f_thor, 0, sizeof(*f_thor)); + + f_thor->usb_function.name = "f_thor"; + f_thor->usb_function.bind = thor_func_bind; + f_thor->usb_function.unbind = thor_unbind; + f_thor->usb_function.setup = thor_func_setup; + f_thor->usb_function.set_alt = thor_func_set_alt; + f_thor->usb_function.disable = thor_func_disable; + + status = usb_add_function(c, &f_thor->usb_function); + if (status) + free(f_thor); + + return status; +} + +int thor_add(struct usb_configuration *c) +{ + debug("%s:\n", __func__); + return thor_func_init(c); +} diff --git a/drivers/usb/gadget/f_thor.h b/drivers/usb/gadget/f_thor.h new file mode 100644 index 0000000..8a9e73c --- /dev/null +++ b/drivers/usb/gadget/f_thor.h @@ -0,0 +1,123 @@ +/* + * f_thor.h - USB TIZEN THOR - internal gadget definitions + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _USB_THOR_H_ +#define _USB_THOR_H_ + +#include <linux/compiler.h> + +/* THOR Composite Gadget */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +/* ********************************************************** */ +/* THOR protocol definitions */ +/* ********************************************************** */ + +/* + * Attribute Vendor descriptor - necessary to prevent ZLP transmission + * from Windows XP HOST PC + */ +struct usb_cdc_attribute_vendor_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u16 DAUType; + __u16 DAULength; + __u8 DAUValue; +} __packed; + +#define VER_PROTOCOL_MAJOR 4 +#define VER_PROTOCOL_MINOR 0 + +enum rqt { + RQT_INFO = 200, + RQT_CMD, + RQT_DL, + RQT_UL, +}; + +enum rqt_data { + /* RQT_INFO */ + RQT_INFO_VER_PROTOCOL = 1, + RQT_INIT_VER_HW, + RQT_INIT_VER_BOOT, + RQT_INIT_VER_KERNEL, + RQT_INIT_VER_PLATFORM, + RQT_INIT_VER_CSC, + + /* RQT_CMD */ + RQT_CMD_REBOOT = 1, + RQT_CMD_POWEROFF, + RQT_CMD_EFSCLEAR, + + /* RQT_DL */ + RQT_DL_INIT = 1, + RQT_DL_FILE_INFO, + RQT_DL_FILE_START, + RQT_DL_FILE_END, + RQT_DL_EXIT, + + /* RQT_UL */ + RQT_UL_INIT = 1, + RQT_UL_START, + RQT_UL_END, + RQT_UL_EXIT, +}; + +struct rqt_box { /* total: 256B */ + s32 rqt; /* request id */ + s32 rqt_data; /* request data id */ + s32 int_data[14]; /* int data */ + char str_data[5][32]; /* string data */ + char md5[32]; /* md5 checksum */ +} __packed; + +struct rsp_box { /* total: 128B */ + s32 rsp; /* response id (= request id) */ + s32 rsp_data; /* response data id */ + s32 ack; /* ack */ + s32 int_data[5]; /* int data */ + char str_data[3][32]; /* string data */ +} __packed; + +struct data_rsp_box { /* total: 8B */ + s32 ack; /* response id (= request id) */ + s32 count; /* response data id */ +} __packed; + +enum { + FILE_TYPE_NORMAL, + FILE_TYPE_PIT, +}; + +struct thor_dev { + struct usb_gadget *gadget; + struct usb_request *req; /* EP0 -> control responses */ + + /* IN/OUT EP's and correspoinding requests */ + struct usb_ep *in_ep, *out_ep, *int_ep; + struct usb_request *in_req, *out_req; + + /* Control flow variables */ + unsigned char configuration_done; + unsigned char rxdata; + unsigned char txdata; +}; + +struct f_thor { + struct usb_function usb_function; + struct thor_dev *dev; +}; + +#define F_NAME_BUF_SIZE 32 +#define THOR_PACKET_SIZE (1 << 20) /* 1 MiB */ +#define THOR_STORE_UNIT_SIZE (32 << 20) /* 32 MiB */ +#endif /* _USB_THOR_H_ */ diff --git a/include/thor.h b/include/thor.h new file mode 100644 index 0000000..afeade4 --- /dev/null +++ b/include/thor.h @@ -0,0 +1,27 @@ +/* + * thor.h -- USB THOR Downloader protocol + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef __THOR_H_ +#define __THOR_H_ + +#include <linux/usb/composite.h> + +int thor_handle(void); +int thor_init(void); + +#ifdef CONFIG_THOR_FUNCTION +int thor_add(struct usb_configuration *c); +#else +int thor_add(struct usb_configuration *c) +{ + return 0; +} +#endif +#endif /* __THOR_H_ */

Dear Lukasz Majewski,
[...]
+static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{
- return container_of(f, struct f_thor, usb_function);
+}
+DEFINE_CACHE_ALIGN_BUFFER(char, thor_tx_data_buf, sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(char, thor_rx_data_buf, sizeof(struct rqt_box));
This should either be uint8_t or unsigned char. A buffer shall not be (signed) char.
Also, I suspect you want to use DEFINE_CACHE_ALIGN_BUFFER here, no ?
+/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE);
Ditto
+static unsigned long long int thor_file_size; +static int alt_setting_num;
+static void send_rsp(const struct rsp_box *rsp) +{
- /* should be copy on dma duffer */
- memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box));
- thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box));
- debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data);
+}
+static void send_data_rsp(s32 ack, s32 count) +{
- ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp,
sizeof(struct data_rsp_box));
- rsp->ack = ack;
- rsp->count = count;
- /* should be copy on dma duffer */
This comment really makes no sense to me.
- memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box));
- thor_tx_data(thor_tx_data_buf, sizeof(struct data_rsp_box));
- debug("-DATA RSP: %d, %d\n", ack, count);
+}
+static int process_rqt_info(const struct rqt_box *rqt) +{
- ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box));
- memset(rsp, '\0', sizeof(struct rsp_box));
- rsp->rsp = rqt->rqt;
- rsp->rsp_data = rqt->rqt_data;
- switch (rqt->rqt_data) {
- case RQT_INFO_VER_PROTOCOL:
rsp->int_data[0] = VER_PROTOCOL_MAJOR;
rsp->int_data[1] = VER_PROTOCOL_MINOR;
break;
- case RQT_INIT_VER_HW:
snprintf(rsp->str_data[0], sizeof(rsp->str_data[0]),
"%x", checkboard());
break;
- case RQT_INIT_VER_BOOT:
sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION);
break;
- case RQT_INIT_VER_KERNEL:
sprintf(rsp->str_data[0], "%s", "k unknown");
break;
- case RQT_INIT_VER_PLATFORM:
sprintf(rsp->str_data[0], "%s", "p unknown");
break;
- case RQT_INIT_VER_CSC:
sprintf(rsp->str_data[0], "%s", "c unknown");
break;
- default:
return -EINVAL;
- }
- send_rsp(rsp);
- return true;
+}
+static int process_rqt_cmd(const struct rqt_box *rqt) +{
- ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box));
- memset(rsp, '\0', sizeof(struct rsp_box));
memset(rsp, 0, sizeof() ... this '\0' is unneeded, fix globally.
- rsp->rsp = rqt->rqt;
- rsp->rsp_data = rqt->rqt_data;
- switch (rqt->rqt_data) {
- case RQT_CMD_REBOOT:
debug("TARGET RESET\n");
send_rsp(rsp);
g_dnl_unregister();
dfu_free_entities();
run_command("reset", 0);
break;
- case RQT_CMD_POWEROFF:
- case RQT_CMD_EFSCLEAR:
send_rsp(rsp);
This case fallthrough is intentional here ?
- default:
printf("Command not supported -> cmd: %d\n", rqt->rqt_data);
return -EINVAL;
- }
- return true;
+}
[...] [...]
+static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = {
- .bLength = sizeof(thor_downloader_cdc_call),
- .bDescriptorType = 0x24, /* CS_INTERFACE */
- .bDescriptorSubType = 0x01,
- .bmCapabilities = 0x00,
- .bDataInterface = 0x01,
+};
+struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = {
Why is this and the rest not static ?
- .bLength = sizeof(thor_downloader_cdc_abstract),
- .bDescriptorType = 0x24, /* CS_INTERFACE */
- .bDescriptorSubType = 0x02,
- .bmCapabilities = 0x00,
+};
+struct usb_cdc_union_desc thor_downloader_cdc_union = {
- .bLength = sizeof(thor_downloader_cdc_union),
- .bDescriptorType = 0x24, /* CS_INTERFACE */
- .bDescriptorSubType = USB_CDC_UNION_TYPE,
+};
+static struct usb_endpoint_descriptor fs_int_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 3 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16(16),
- .bInterval = 0x9,
+};
[...]
+/*------------------------------------------------------------------------ -*/ +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{
- struct usb_request *req;
- req = usb_ep_alloc_request(ep, 0);
- if (req) {
if (!req) return ... ... rest of the code ...
req->length = length;
req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length);
if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
}
- }
- return req;
+}
[...]
+static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct thor_dev *dev = thor_func->dev;
- int status = req->status;
- debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req);
- switch (status) {
- case 0: /* normal completion? */
if (ep == dev->out_ep)
dev->rxdata = 1;
else
dev->txdata = 1;
break;
- /* this endpoint is normally active while we're configured */
- case -ECONNABORTED: /* hardware forced ep reset */
- case -ECONNRESET: /* request dequeued */
- case -ESHUTDOWN: /* disconnect from host */
- /* Exeptional situation - print error message */
- case -EOVERFLOW:
error("ERROR:%d", status);
- default:
debug("%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
- case -EREMOTEIO: /* short read */
break;
- }
You might want to fix the order of these cases here.
+}
+static struct usb_request *thor_start_ep(struct usb_ep *ep) +{
- struct usb_request *req;
- req = alloc_ep_req(ep, ep->maxpacket);
- debug("%s: ep:%p req:%p\n", __func__, ep, req);
- if (!req)
return NULL;
- memset(req->buf, 0, req->length);
- req->complete = thor_rx_tx_complete;
- memset(req->buf, 0x55, req->length);
- return req;
+}
[...]
+int thor_init(void) +{
- struct thor_dev *dev = thor_func->dev;
- /* Wait for a device enumeration and configuration settings */
- debug("THOR enumeration/configuration setting....\n");
- while (!dev->configuration_done)
usb_gadget_handle_interrupts();
- thor_set_dma(thor_rx_data_buf, strlen(recv_key));
- /* detect the download request from Host PC */
- if (thor_rx_data() < 0) {
printf("%s: Data not received!\n", __func__);
return -1;
- }
- if (strncmp(thor_rx_data_buf, recv_key, strlen(recv_key)) == 0) {
Use min() of upper bound on the recv_key length and this strlen()
puts("Download request from the Host PC\n");
udelay(30 * 1000); /* 30 ms */
strncpy(thor_tx_data_buf, tx_key, strlen(tx_key));
thor_tx_data(thor_tx_data_buf, strlen(tx_key));
- } else {
puts("Wrong reply information\n");
return -1;
- }
- return 0;
+}
[...]
+static int thor_eps_setup(struct usb_function *f) +{
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_gadget *gadget = cdev->gadget;
- struct thor_dev *dev = thor_func->dev;
- struct usb_endpoint_descriptor *d;
- struct usb_request *req;
- struct usb_ep *ep;
- int result;
- ep = dev->in_ep;
- d = ep_desc(gadget, &hs_in_desc, &fs_in_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
if (result) goto exit;
... the rest ...
ep->driver_data = cdev; /* claim */
req = thor_start_ep(ep);
if (req != NULL) {
dev->in_req = req;
} else {
usb_ep_disable(ep);
result = -EIO;
}
- } else {
goto exit;
- }
- ep = dev->out_ep;
- d = ep_desc(gadget, &hs_out_desc, &fs_out_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
DTTO
ep->driver_data = cdev; /* claim */
req = thor_start_ep(ep);
if (req != NULL) {
dev->out_req = req;
} else {
usb_ep_disable(ep);
result = -EIO;
}
- } else {
goto exit;
- }
- /* ACM control EP */
- ep = dev->int_ep;
- ep->driver_data = cdev; /* claim */
- exit:
- return result;
+}
[...]
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
[...]
+static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{
- return container_of(f, struct f_thor, usb_function);
+}
+DEFINE_CACHE_ALIGN_BUFFER(char, thor_tx_data_buf, sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(char, thor_rx_data_buf, sizeof(struct rqt_box));
This should either be uint8_t or unsigned char. A buffer shall not be (signed) char.
Yes. I agree. This buffer shall be unsigned char. I will correct that.
Also, I suspect you want to use DEFINE_CACHE_ALIGN_BUFFER here, no ?
I'm a bit confused.... I do use DEFINE_CACHE_ALIGN_BUFFER for those buffers.
+/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE);
Ditto
I believe that buffer for storing file name (f_name) shall be defined as char.
+static unsigned long long int thor_file_size; +static int alt_setting_num;
+static void send_rsp(const struct rsp_box *rsp) +{
- /* should be copy on dma duffer */
- memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box));
- thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box));
- debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data);
+}
+static void send_data_rsp(s32 ack, s32 count) +{
- ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp,
sizeof(struct data_rsp_box));
- rsp->ack = ack;
- rsp->count = count;
- /* should be copy on dma duffer */
This comment really makes no sense to me.
Yes. It's pretty obvious, what I intent to do in this function. I will remove it.
- memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box));
- thor_tx_data(thor_tx_data_buf, sizeof(struct
data_rsp_box)); +
- debug("-DATA RSP: %d, %d\n", ack, count);
+}
+static int process_rqt_info(const struct rqt_box *rqt) +{
- ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp,
sizeof(struct rsp_box));
- memset(rsp, '\0', sizeof(struct rsp_box));
- rsp->rsp = rqt->rqt;
- rsp->rsp_data = rqt->rqt_data;
- switch (rqt->rqt_data) {
- case RQT_INFO_VER_PROTOCOL:
rsp->int_data[0] = VER_PROTOCOL_MAJOR;
rsp->int_data[1] = VER_PROTOCOL_MINOR;
break;
- case RQT_INIT_VER_HW:
snprintf(rsp->str_data[0],
sizeof(rsp->str_data[0]),
"%x", checkboard());
break;
- case RQT_INIT_VER_BOOT:
sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION);
break;
- case RQT_INIT_VER_KERNEL:
sprintf(rsp->str_data[0], "%s", "k unknown");
break;
- case RQT_INIT_VER_PLATFORM:
sprintf(rsp->str_data[0], "%s", "p unknown");
break;
- case RQT_INIT_VER_CSC:
sprintf(rsp->str_data[0], "%s", "c unknown");
break;
- default:
return -EINVAL;
- }
- send_rsp(rsp);
- return true;
+}
+static int process_rqt_cmd(const struct rqt_box *rqt) +{
- ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp,
sizeof(struct rsp_box));
- memset(rsp, '\0', sizeof(struct rsp_box));
memset(rsp, 0, sizeof() ... this '\0' is unneeded, fix globally.
Good point. I will correct this.
- rsp->rsp = rqt->rqt;
- rsp->rsp_data = rqt->rqt_data;
- switch (rqt->rqt_data) {
- case RQT_CMD_REBOOT:
debug("TARGET RESET\n");
send_rsp(rsp);
g_dnl_unregister();
dfu_free_entities();
run_command("reset", 0);
break;
- case RQT_CMD_POWEROFF:
- case RQT_CMD_EFSCLEAR:
send_rsp(rsp);
This case fallthrough is intentional here ?
Yes. Thor protocol requires to receive response from device even when HOST PC ordered it to power off.
Also, on the target only reboot command is supported.
- default:
printf("Command not supported -> cmd: %d\n",
rqt->rqt_data);
return -EINVAL;
- }
- return true;
+}
[...] [...]
+static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = {
- .bLength = sizeof(thor_downloader_cdc_call),
- .bDescriptorType = 0x24, /* CS_INTERFACE */
- .bDescriptorSubType = 0x01,
- .bmCapabilities = 0x00,
- .bDataInterface = 0x01,
+};
+struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = {
Why is this and the rest not static ?
They shall be static. I will fix that.
- .bLength = sizeof(thor_downloader_cdc_abstract),
- .bDescriptorType = 0x24, /* CS_INTERFACE */
- .bDescriptorSubType = 0x02,
- .bmCapabilities = 0x00,
+};
+struct usb_cdc_union_desc thor_downloader_cdc_union = {
- .bLength = sizeof(thor_downloader_cdc_union),
- .bDescriptorType = 0x24, /* CS_INTERFACE */
- .bDescriptorSubType = USB_CDC_UNION_TYPE,
+};
+static struct usb_endpoint_descriptor fs_int_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = 3 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16(16),
- .bInterval = 0x9,
+};
[...]
+/*------------------------------------------------------------------------ -*/ +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{
- struct usb_request *req;
- req = usb_ep_alloc_request(ep, 0);
- if (req) {
if (!req) return ... ... rest of the code ...
Thanks for spotting. I fully agree.
req->length = length;
req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
length);
if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
}
- }
- return req;
+}
[...]
+static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct thor_dev *dev = thor_func->dev;
- int status = req->status;
- debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req);
- switch (status) {
- case 0: /* normal
completion? */
if (ep == dev->out_ep)
dev->rxdata = 1;
else
dev->txdata = 1;
break;
- /* this endpoint is normally active while we're configured
*/
- case -ECONNABORTED: /* hardware forced ep
reset */
- case -ECONNRESET: /* request dequeued */
- case -ESHUTDOWN: /* disconnect from host */
- /* Exeptional situation - print error message */
- case -EOVERFLOW:
error("ERROR:%d", status);
- default:
debug("%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
- case -EREMOTEIO: /* short read */
break;
- }
You might want to fix the order of these cases here.
Ok. I will reorder it.
+}
+static struct usb_request *thor_start_ep(struct usb_ep *ep) +{
- struct usb_request *req;
- req = alloc_ep_req(ep, ep->maxpacket);
- debug("%s: ep:%p req:%p\n", __func__, ep, req);
- if (!req)
return NULL;
- memset(req->buf, 0, req->length);
- req->complete = thor_rx_tx_complete;
- memset(req->buf, 0x55, req->length);
- return req;
+}
[...]
+int thor_init(void) +{
- struct thor_dev *dev = thor_func->dev;
- /* Wait for a device enumeration and configuration
settings */
- debug("THOR enumeration/configuration setting....\n");
- while (!dev->configuration_done)
usb_gadget_handle_interrupts();
- thor_set_dma(thor_rx_data_buf, strlen(recv_key));
- /* detect the download request from Host PC */
- if (thor_rx_data() < 0) {
printf("%s: Data not received!\n", __func__);
return -1;
- }
- if (strncmp(thor_rx_data_buf, recv_key, strlen(recv_key))
== 0) {
Use min() of upper bound on the recv_key length and this strlen()
But the recv_key is defined as const char *recv_key = "THOR";
I will rewrite this as:
if (!strcmp(thor_rx_data_buf, "THOR"))
puts("Download request from the Host PC\n");
udelay(30 * 1000); /* 30 ms */
strncpy(thor_tx_data_buf, tx_key, strlen(tx_key));
thor_tx_data(thor_tx_data_buf, strlen(tx_key));
- } else {
puts("Wrong reply information\n");
return -1;
- }
- return 0;
+}
[...]
+static int thor_eps_setup(struct usb_function *f) +{
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_gadget *gadget = cdev->gadget;
- struct thor_dev *dev = thor_func->dev;
- struct usb_endpoint_descriptor *d;
- struct usb_request *req;
- struct usb_ep *ep;
- int result;
- ep = dev->in_ep;
- d = ep_desc(gadget, &hs_in_desc, &fs_in_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
if (result) goto exit;
... the rest ...
Yes, correct. I will change this.
ep->driver_data = cdev; /* claim */
req = thor_start_ep(ep);
if (req != NULL) {
dev->in_req = req;
} else {
usb_ep_disable(ep);
result = -EIO;
}
- } else {
goto exit;
- }
- ep = dev->out_ep;
- d = ep_desc(gadget, &hs_out_desc, &fs_out_desc);
- debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
DTTO
Yes, this will be also changed.
ep->driver_data = cdev; /* claim */
req = thor_start_ep(ep);
if (req != NULL) {
dev->out_req = req;
} else {
usb_ep_disable(ep);
result = -EIO;
}
- } else {
goto exit;
- }
- /* ACM control EP */
- ep = dev->int_ep;
- ep->driver_data = cdev; /* claim */
- exit:
- return result;
+}
[...]
Best regards, Marek Vasut

Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
[...]
+static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{
- return container_of(f, struct f_thor, usb_function);
+}
+DEFINE_CACHE_ALIGN_BUFFER(char, thor_tx_data_buf, sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(char, thor_rx_data_buf, sizeof(struct rqt_box));
This should either be uint8_t or unsigned char. A buffer shall not be (signed) char.
Yes. I agree. This buffer shall be unsigned char. I will correct that.
Also, I suspect you want to use DEFINE_CACHE_ALIGN_BUFFER here, no ?
I'm a bit confused.... I do use DEFINE_CACHE_ALIGN_BUFFER for those buffers.
OOPS!
+/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE);
Ditto
I believe that buffer for storing file name (f_name) shall be defined as char.
OK, good point.
[...]
- rsp->rsp = rqt->rqt;
- rsp->rsp_data = rqt->rqt_data;
- switch (rqt->rqt_data) {
- case RQT_CMD_REBOOT:
debug("TARGET RESET\n");
send_rsp(rsp);
g_dnl_unregister();
dfu_free_entities();
run_command("reset", 0);
break;
- case RQT_CMD_POWEROFF:
- case RQT_CMD_EFSCLEAR:
send_rsp(rsp);
This case fallthrough is intentional here ?
Yes. Thor protocol requires to receive response from device even when HOST PC ordered it to power off.
Also, on the target only reboot command is supported.
But this will fall through into the default: branch here.
- default:
printf("Command not supported -> cmd: %d\n",
rqt->rqt_data);
return -EINVAL;
- }
- return true;
+}
[...]

Hi Marek,
Dear Lukasz Majewski,
Hi Marek,
Dear Lukasz Majewski,
[...]
+static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{
- return container_of(f, struct f_thor, usb_function);
+}
+DEFINE_CACHE_ALIGN_BUFFER(char, thor_tx_data_buf, sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(char, thor_rx_data_buf, sizeof(struct rqt_box));
This should either be uint8_t or unsigned char. A buffer shall not be (signed) char.
Yes. I agree. This buffer shall be unsigned char. I will correct that.
Also, I suspect you want to use DEFINE_CACHE_ALIGN_BUFFER here, no ?
I'm a bit confused.... I do use DEFINE_CACHE_ALIGN_BUFFER for those buffers.
OOPS!
+/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE);
Ditto
I believe that buffer for storing file name (f_name) shall be defined as char.
OK, good point.
[...]
- rsp->rsp = rqt->rqt;
- rsp->rsp_data = rqt->rqt_data;
- switch (rqt->rqt_data) {
- case RQT_CMD_REBOOT:
debug("TARGET RESET\n");
send_rsp(rsp);
g_dnl_unregister();
dfu_free_entities();
run_command("reset", 0);
break;
- case RQT_CMD_POWEROFF:
- case RQT_CMD_EFSCLEAR:
send_rsp(rsp);
This case fallthrough is intentional here ?
Yes. Thor protocol requires to receive response from device even when HOST PC ordered it to power off.
Also, on the target only reboot command is supported.
But this will fall through into the default: branch here.
From my perspective this looks like a proper behaviour.
We send response that we have received such request and display information that it is not supported.
- default:
printf("Command not supported -> cmd: %d\n",
rqt->rqt_data);
return -EINVAL;
- }
- return true;
+}
[...]

Support of "thor" function at generic download code (g_dnl.c).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de --- drivers/usb/gadget/g_dnl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 98560b8..43f413a 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -16,6 +16,7 @@ #include <g_dnl.h> #include <usb_mass_storage.h> #include <dfu.h> +#include <thor.h>
#include "gadget_chips.h" #include "composite.c" @@ -101,6 +102,8 @@ static int g_dnl_do_config(struct usb_configuration *c) ret = dfu_add(c); else if (!strcmp(s, "usb_dnl_ums")) ret = fsg_add(c); + else if (!strcmp(s, "usb_dnl_thor")) + ret = thor_add(c);
return ret; } @@ -191,8 +194,8 @@ static struct usb_composite_driver g_dnl_driver = {
int g_dnl_register(const char *type) { - /* We only allow "dfu" atm, so 3 should be enough */ - static char name[sizeof(shortname) + 3]; + /* The largest function name is 4 */ + static char name[sizeof(shortname) + 4]; int ret;
if (!strcmp(type, "dfu")) { @@ -201,6 +204,9 @@ int g_dnl_register(const char *type) } else if (!strcmp(type, "ums")) { strcpy(name, shortname); strcat(name, type); + } else if (!strcmp(type, "thor")) { + strcpy(name, shortname); + strcat(name, type); } else { printf("%s: unknown command: %s\n", __func__, type); return -EINVAL;

New command - thordown - has been added to support downloading data via lthor TIZEN program. It is similar to dfu command syntax and reuses its code for flashing data.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com --- common/Makefile | 1 + common/cmd_thordown.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 common/cmd_thordown.c
diff --git a/common/Makefile b/common/Makefile index 288690b..8daca5b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -168,6 +168,7 @@ COBJS-y += usb.o usb_hub.o COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o endif COBJS-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o +COBJS-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o COBJS-$(CONFIG_CMD_SPL) += cmd_spl.o diff --git a/common/cmd_thordown.c b/common/cmd_thordown.c new file mode 100644 index 0000000..fc5635b --- /dev/null +++ b/common/cmd_thordown.c @@ -0,0 +1,58 @@ +/* + * cmd_thordown.c -- USB TIZEN "THOR" Downloader gadget + * + * Copyright (C) 2013 Lukasz Majewski l.majewski@samsung.com + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <thor.h> +#include <dfu.h> +#include <g_dnl.h> + +int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *s = "thor"; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + puts("TIZEN "THOR" Downloader\n"); + + ret = dfu_init_env_entities(argv[1], simple_strtoul(argv[2], NULL, 10)); + if (ret) + return ret; + + board_usb_init(); + g_dnl_register(s); + + ret = thor_init(); + if (ret) { + error("THOR DOWNLOAD failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + ret = thor_handle(); + if (ret) { + error("THOR failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + +exit: + g_dnl_unregister(); + dfu_free_entities(); + + return ret; +} + +U_BOOT_CMD(thordown, CONFIG_SYS_MAXARGS, 1, do_thor_down, + "TIZEN "THOR" downloader", + "<interface> <dev>\n" + " - device software upgrade via LTHOR TIZEN dowload\n" + " program at <dev> attached to interface <interface>\n" +);

Special, common to Samsung, function for altering usb descriptor's idVendor and idProduct has been added. For compatibility reasons (Win vs Linux) the THOR idProduct must be different than the one for DFU/UMS.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Minkyu Kang mk7.kang@samsung.com --- board/samsung/common/Makefile | 1 + board/samsung/common/thor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 board/samsung/common/thor.c
diff --git a/board/samsung/common/Makefile b/board/samsung/common/Makefile index 9e48a7b..ad7564c 100644 --- a/board/samsung/common/Makefile +++ b/board/samsung/common/Makefile @@ -10,6 +10,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libsamsung.o
COBJS-$(CONFIG_SOFT_I2C_MULTI_BUS) += multi_i2c.o +COBJS-$(CONFIG_THOR_FUNCTION) += thor.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/board/samsung/common/thor.c b/board/samsung/common/thor.c new file mode 100644 index 0000000..1c7630d --- /dev/null +++ b/board/samsung/common/thor.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/usb/ch9.h> + +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + if (!strcmp(name, "usb_dnl_thor")) { + put_unaligned(CONFIG_G_DNL_THOR_VENDOR_NUM, &dev->idVendor); + put_unaligned(CONFIG_G_DNL_THOR_PRODUCT_NUM, &dev->idProduct); + } else { + put_unaligned(CONFIG_G_DNL_VENDOR_NUM, &dev->idVendor); + put_unaligned(CONFIG_G_DNL_PRODUCT_NUM, &dev->idProduct); + } + return 0; +}

A set of environment variables needs to be updated to provide support for TIZEN download command (tizendown).
Since DFU is used as a flashing backend, it is also necessary to extent malloc pool size for DFU buffer allocation. Moreover, for compatibility reasons (Win vs. Lin) new USB idProduct number for download gadget had to be added.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de --- include/configs/trats.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index 24ea06b..955bd20 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -50,7 +50,7 @@ #define CONFIG_MACH_TYPE MACH_TYPE_TRATS
/* Size of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (16 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 << 20))
/* select serial console configuration */ #define CONFIG_SERIAL2 /* use SERIAL 2 */ @@ -91,12 +91,20 @@
/* USB Composite download gadget - g_dnl */ #define CONFIG_USBDOWNLOAD_GADGET + +/* TIZEN THOR downloader support */ +#define CONFIG_CMD_THOR_DOWNLOAD +#define CONFIG_THOR_FUNCTION + +#define CONFIG_SYS_DFU_DATA_BUF_SIZE (32 << 20) #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_THOR_VENDOR_NUM CONFIG_G_DNL_VENDOR_NUM +#define CONFIG_G_DNL_THOR_PRODUCT_NUM 0x685D #define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 @@ -131,7 +139,8 @@ #define CONFIG_DFU_ALT \ "u-boot mmc 80 400;" \ "uImage ext4 0 2;" \ - "exynos4210-trats.dtb ext4 0 2\0" + "exynos4210-trats.dtb ext4 0 2;" \ + ""PARTS_ROOT" part 0 5\0"
#define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET

Dear Lukasz Majewski,
A set of environment variables needs to be updated to provide support for TIZEN download command (tizendown).
Since DFU is used as a flashing backend, it is also necessary to extent malloc pool size for DFU buffer allocation. Moreover, for compatibility reasons (Win vs. Lin) new USB idProduct number for download gadget had to be added.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
include/configs/trats.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index 24ea06b..955bd20 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -50,7 +50,7 @@ #define CONFIG_MACH_TYPE MACH_TYPE_TRATS
/* Size of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (16 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 << 20))
Use SZ_1M instead of that l-shift by 20.
/* select serial console configuration */ #define CONFIG_SERIAL2 /* use SERIAL 2 */ @@ -91,12 +91,20 @@
/* USB Composite download gadget - g_dnl */ #define CONFIG_USBDOWNLOAD_GADGET
+/* TIZEN THOR downloader support */ +#define CONFIG_CMD_THOR_DOWNLOAD +#define CONFIG_THOR_FUNCTION
+#define CONFIG_SYS_DFU_DATA_BUF_SIZE (32 << 20)
DTTO
#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_THOR_VENDOR_NUM CONFIG_G_DNL_VENDOR_NUM +#define CONFIG_G_DNL_THOR_PRODUCT_NUM 0x685D #define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 @@ -131,7 +139,8 @@ #define CONFIG_DFU_ALT \ "u-boot mmc 80 400;" \ "uImage ext4 0 2;" \
- "exynos4210-trats.dtb ext4 0 2\0"
- "exynos4210-trats.dtb ext4 0 2;" \
- ""PARTS_ROOT" part 0 5\0"
#define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET
btw. I'm picking this patch before this set:
[PATCH v5] usb: new board-specific USB init interface
So you might want to wait until it's applied onto usb/next and then rebase and repost.
Best regards, Marek Vasut

Hi Marek,
Dear Lukasz Majewski,
A set of environment variables needs to be updated to provide support for TIZEN download command (tizendown).
Since DFU is used as a flashing backend, it is also necessary to extent malloc pool size for DFU buffer allocation. Moreover, for compatibility reasons (Win vs. Lin) new USB idProduct number for download gadget had to be added.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
include/configs/trats.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index 24ea06b..955bd20 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -50,7 +50,7 @@ #define CONFIG_MACH_TYPE MACH_TYPE_TRATS
/* Size of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (16 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 << 20))
Use SZ_1M instead of that l-shift by 20.
Ok, I will use it.
/* select serial console configuration */ #define CONFIG_SERIAL2 /* use SERIAL 2 */ @@ -91,12 +91,20 @@
/* USB Composite download gadget - g_dnl */ #define CONFIG_USBDOWNLOAD_GADGET
+/* TIZEN THOR downloader support */ +#define CONFIG_CMD_THOR_DOWNLOAD +#define CONFIG_THOR_FUNCTION
+#define CONFIG_SYS_DFU_DATA_BUF_SIZE (32 << 20)
DTTO
#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_THOR_VENDOR_NUM CONFIG_G_DNL_VENDOR_NUM +#define CONFIG_G_DNL_THOR_PRODUCT_NUM 0x685D #define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 @@ -131,7 +139,8 @@ #define CONFIG_DFU_ALT \ "u-boot mmc 80 400;" \ "uImage ext4 0 2;" \
- "exynos4210-trats.dtb ext4 0 2\0"
- "exynos4210-trats.dtb ext4 0 2;" \
- ""PARTS_ROOT" part 0 5\0"
#define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET
btw. I'm picking this patch before this set:
[PATCH v5] usb: new board-specific USB init interface
So you might want to wait until it's applied onto usb/next and then rebase and repost.
I will do as you suggested. Thanks for review.
Best regards, Marek Vasut

This patch series provide support for TIZEN's THOR download protocol.
Dedicated program for flashing TIZEN developer devices (TRATS, TRATS2) is called lthor (or thor for Windows) and can be found at:
git clone git://review.tizen.org/tools/lthor
or for git web:
https://review.tizen.org/git/?p=tools/lthor.git;a=summary
Presented composite USB function acts as a front end to perform correct USB communication with HOST PC. To store the received data on the target, the DFU (Device Firmware Update) code for flashing has been reused.
This means, that the "dfu_alt_info" environment variable is used to provide information where a received file is stored.
This also means that dfu and thor can co-exists together. Thor protocol and its implementation has one advantage - it is much faster than DFU for large files transfers (especially rootfs images).
It applies on: u-boot-denx-usb/next SHA1: 6928d26b84a5aa4a44706362234ab435bb15a6fb
Test HW: Exynos4210 (TRATS)
Lukasz Majewski (10): usb:udc:s3c: Reduce dcache invalidate range for UDC receive buffer dfu:core: Find DFU alt setting number by passing its name dfu:core: Export dfu_{get|free}_buf functions usb:g_dnl: Replace static usb_configuration structure with dynamically allocated one usb:g_dnl: Add name parameter to g_dnl_bind_fixup function usb:g_dnl:f_thor: USB download function to support TIZEN's THOR protocol usb:g_dnl: Support for TIZEN's THOR function in generic download code cmd:thor: Support for TIZEN's download command (thordown) samsung:common:thor: Define common Samsung code to handle THOR usb descriptor setup trats: Update TRATS config to support TIZEN download
board/samsung/common/Makefile | 1 + board/samsung/common/thor.c | 21 + board/siemens/common/factoryset.c | 2 +- common/Makefile | 1 + common/cmd_thordown.c | 72 +++ drivers/dfu/dfu.c | 16 +- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_thor.c | 1003 +++++++++++++++++++++++++++++ drivers/usb/gadget/f_thor.h | 124 ++++ drivers/usb/gadget/g_dnl.c | 38 +- drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 3 +- include/configs/trats.h | 14 +- include/dfu.h | 3 + include/g_dnl.h | 2 +- include/thor.h | 27 + 15 files changed, 1309 insertions(+), 19 deletions(-) create mode 100644 board/samsung/common/thor.c create mode 100644 common/cmd_thordown.c create mode 100644 drivers/usb/gadget/f_thor.c create mode 100644 drivers/usb/gadget/f_thor.h create mode 100644 include/thor.h

The s3c udc driver sends data in a max packet size. Therefore the dcache invalidate range shall be equal to max packet, not the entire DMA_BUFFER_SIZE.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - ROUND the maxpacket value to invalidate data smaller than cache line size
drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index d7af5e9..1cbf8f6 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -117,7 +117,8 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], (unsigned long) ep->dev->dma_buf[ep_num] - + DMA_BUFFER_SIZE); + + ROUND(ep->ep.maxpacket, + CONFIG_SYS_CACHELINE_SIZE));
if (length == 0) pktcnt = 1;

New function - dfu_get_alt() - has been added to dfu core. If present, it returns alt setting's number corresponding to passed name.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com
--- Changes for v2: - None
drivers/dfu/dfu.c | 12 ++++++++++++ include/dfu.h | 1 + 2 files changed, 13 insertions(+)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index f328735..4ec330c 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -440,3 +440,15 @@ struct dfu_entity *dfu_get_entity(int alt)
return NULL; } + +int dfu_get_alt(char *name) +{ + struct dfu_entity *dfu; + + list_for_each_entry(dfu, &dfu_list, list) { + if (!strncmp(dfu->name, name, strlen(dfu->name))) + return dfu->alt; + } + + return -ENODEV; +} diff --git a/include/dfu.h b/include/dfu.h index b2ecf1b..b144255 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -126,6 +126,7 @@ 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); void dfu_trigger_reset(void); +int dfu_get_alt(char *name); bool dfu_reset(void); int dfu_init_env_entities(char *interface, int dev);

Define the dfu_get_buf() and dfu_free_buf() as global functions. They are necessary for zero copy buffer management, when DFU backend is used for storing data.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com
--- Changes for v2: - None
drivers/dfu/dfu.c | 4 ++-- include/dfu.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 4ec330c..4a8804e 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -67,14 +67,14 @@ int dfu_init_env_entities(char *interface, int dev) static unsigned char *dfu_buf; static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
-static unsigned char *dfu_free_buf(void) +unsigned char *dfu_free_buf(void) { free(dfu_buf); dfu_buf = NULL; return dfu_buf; }
-static unsigned char *dfu_get_buf(void) +unsigned char *dfu_get_buf(void) { char *s;
diff --git a/include/dfu.h b/include/dfu.h index b144255..cc14044 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -129,6 +129,8 @@ void dfu_trigger_reset(void); int dfu_get_alt(char *name); bool dfu_reset(void); int dfu_init_env_entities(char *interface, int dev); +unsigned char *dfu_get_buf(void); +unsigned char *dfu_free_buf(void);
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);

When the usb_configuration structure is declared as static, it is very hard to assure, that relevant fields (as e.g. config->interfaces[]) are cleared out before new call to g_dnl related functions.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com
--- Changes for v2: - None
drivers/usb/gadget/g_dnl.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 40868c0..1aaf78f 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -79,6 +79,8 @@ static int g_dnl_unbind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget;
+ free(cdev->config); + cdev->config = NULL; debug("%s: calling usb_gadget_disconnect for " "controller '%s'\n", shortname, gadget->name); usb_gadget_disconnect(gadget); @@ -105,16 +107,22 @@ static int g_dnl_do_config(struct usb_configuration *c)
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 = CONFIGURATION_NUMBER, - .iConfiguration = STRING_USBDOWN, + struct usb_configuration *config; + const char *name = "usb_dnload";
- .bind = g_dnl_do_config, - }; + config = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*config)); + if (!config) + return -ENOMEM;
- return usb_add_config(cdev, &config); + memset(config, 0, sizeof(*config)); + + config->label = name; + config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; + config->bConfigurationValue = CONFIGURATION_NUMBER; + config->iConfiguration = STRING_USBDOWN; + config->bind = g_dnl_do_config; + + return usb_add_config(cdev, config); }
__weak

New parameter, namely *name has been added to g_dnl_bind_fixup(). It is necessary (for compatibility reasons) to assign new USB idProduct and idVendor for different usb functions.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
board/siemens/common/factoryset.c | 2 +- drivers/usb/gadget/g_dnl.c | 4 ++-- include/g_dnl.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/board/siemens/common/factoryset.c b/board/siemens/common/factoryset.c index eda9141..fbe7997 100644 --- a/board/siemens/common/factoryset.c +++ b/board/siemens/common/factoryset.c @@ -275,7 +275,7 @@ int factoryset_setenv(void) return ret; }
-int g_dnl_bind_fixup(struct usb_device_descriptor *dev) +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) { put_unaligned(factory_dat.usb_vendor_id, &dev->idVendor); put_unaligned(factory_dat.usb_product_id, &dev->idProduct); diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 1aaf78f..98560b8 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -126,7 +126,7 @@ static int g_dnl_config_register(struct usb_composite_dev *cdev) }
__weak -int g_dnl_bind_fixup(struct usb_device_descriptor *dev) +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) { return 0; } @@ -153,7 +153,7 @@ static int g_dnl_bind(struct usb_composite_dev *cdev) g_dnl_string_defs[1].id = id; device_desc.iProduct = id;
- g_dnl_bind_fixup(&device_desc); + g_dnl_bind_fixup(&device_desc, cdev->driver->name); ret = g_dnl_config_register(cdev); if (ret) goto error; diff --git a/include/g_dnl.h b/include/g_dnl.h index b6c4dd4..de669fb 100644 --- a/include/g_dnl.h +++ b/include/g_dnl.h @@ -10,7 +10,7 @@
#include <linux/usb/ch9.h> #include <linux/usb/gadget.h> -int g_dnl_bind_fixup(struct usb_device_descriptor *); +int g_dnl_bind_fixup(struct usb_device_descriptor *, const char *); int g_dnl_register(const char *s); void g_dnl_unregister(void);

Implementation of USB download function which supports THOR protocol.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - Change thor_{rx|tx}_data_buf to unsigned char - Replace '\0' to 0 on memset calls - Refactor switch() at thor_rx_tx_complete() - Add missing static declaration - Rewrite logic at alloc_ep_req() and thor_eps_setup() - Add SZ_XM at f_thor.h thor packet size definition - Remove *recv_key = "THOR" and *tx_key = "ROHT". Use "THOR" and "ROHT" directly
drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_thor.c | 1003 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/f_thor.h | 124 ++++++ include/thor.h | 27 ++ 4 files changed, 1155 insertions(+) create mode 100644 drivers/usb/gadget/f_thor.c create mode 100644 drivers/usb/gadget/f_thor.h create mode 100644 include/thor.h
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1590c4a..afaf5ce 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -21,6 +21,7 @@ ifdef CONFIG_USB_GADGET COBJS-$(CONFIG_USB_GADGET_ATMEL_USBA) += atmel_usba_udc.o COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o +COBJS-$(CONFIG_THOR_FUNCTION) += f_thor.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c new file mode 100644 index 0000000..c4c9909 --- /dev/null +++ b/drivers/usb/gadget/f_thor.c @@ -0,0 +1,1003 @@ +/* + * f_thor.c -- USB TIZEN THOR Downloader gadget function + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * Based on code from: + * git://review.tizen.org/kernel/u-boot + * + * Developed by: + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang mk7.kang@samsung.com + * Sanghee Kim sh0130.kim@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <errno.h> +#include <common.h> +#include <malloc.h> +#include <version.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/usb/cdc.h> +#include <g_dnl.h> +#include <dfu.h> + +#include "f_thor.h" + +static void thor_tx_data(unsigned char *data, int len); +static void thor_set_dma(void *addr, int len); +static int thor_rx_data(void); + +static struct f_thor *thor_func; +static inline struct f_thor *func_to_thor(struct usb_function *f) +{ + return container_of(f, struct f_thor, usb_function); +} + +DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_tx_data_buf, + sizeof(struct rsp_box)); +DEFINE_CACHE_ALIGN_BUFFER(unsigned char, thor_rx_data_buf, + sizeof(struct rqt_box)); + +/* ********************************************************** */ +/* THOR protocol - transmission handling */ +/* ********************************************************** */ +DEFINE_CACHE_ALIGN_BUFFER(char, f_name, F_NAME_BUF_SIZE); +static unsigned long long int thor_file_size; +static int alt_setting_num; + +static void send_rsp(const struct rsp_box *rsp) +{ + memcpy(thor_tx_data_buf, rsp, sizeof(struct rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct rsp_box)); + + debug("-RSP: %d, %d\n", rsp->rsp, rsp->rsp_data); +} + +static void send_data_rsp(s32 ack, s32 count) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct data_rsp_box, rsp, + sizeof(struct data_rsp_box)); + + rsp->ack = ack; + rsp->count = count; + + memcpy(thor_tx_data_buf, rsp, sizeof(struct data_rsp_box)); + thor_tx_data(thor_tx_data_buf, sizeof(struct data_rsp_box)); + + debug("-DATA RSP: %d, %d\n", ack, count); +} + +static int process_rqt_info(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, 0, sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_INFO_VER_PROTOCOL: + rsp->int_data[0] = VER_PROTOCOL_MAJOR; + rsp->int_data[1] = VER_PROTOCOL_MINOR; + break; + case RQT_INIT_VER_HW: + snprintf(rsp->str_data[0], sizeof(rsp->str_data[0]), + "%x", checkboard()); + break; + case RQT_INIT_VER_BOOT: + sprintf(rsp->str_data[0], "%s", U_BOOT_VERSION); + break; + case RQT_INIT_VER_KERNEL: + sprintf(rsp->str_data[0], "%s", "k unknown"); + break; + case RQT_INIT_VER_PLATFORM: + sprintf(rsp->str_data[0], "%s", "p unknown"); + break; + case RQT_INIT_VER_CSC: + sprintf(rsp->str_data[0], "%s", "c unknown"); + break; + default: + return -EINVAL; + } + + send_rsp(rsp); + return true; +} + +static int process_rqt_cmd(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + memset(rsp, 0, sizeof(struct rsp_box)); + + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_CMD_REBOOT: + debug("TARGET RESET\n"); + send_rsp(rsp); + g_dnl_unregister(); + dfu_free_entities(); + run_command("reset", 0); + break; + case RQT_CMD_POWEROFF: + case RQT_CMD_EFSCLEAR: + send_rsp(rsp); + default: + printf("Command not supported -> cmd: %d\n", rqt->rqt_data); + return -EINVAL; + } + + return true; +} + +static long long int download_head(unsigned long long total, + unsigned int packet_size, + long long int *left, + int *cnt) +{ + long long int rcv_cnt = 0, left_to_rcv, ret_rcv; + void *transfer_buffer = dfu_get_buf(); + void *buf = transfer_buffer; + int usb_pkt_cnt = 0, ret; + + /* + * Files smaller than THOR_STORE_UNIT_SIZE (now 32 MiB) are stored on + * the medium. + * The packet response is sent on the purpose after successful data + * chunk write. There is a room for improvement when asynchronous write + * is performed. + */ + while (total - rcv_cnt >= packet_size) { + thor_set_dma(buf, packet_size); + buf += packet_size; + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + debug("%d: RCV data count: %llu cnt: %d\n", usb_pkt_cnt, + rcv_cnt, *cnt); + + if ((rcv_cnt % THOR_STORE_UNIT_SIZE) == 0) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, THOR_STORE_UNIT_SIZE, + (*cnt)++); + if (ret) { + error("DFU write failed [%d] cnt: %d", + ret, *cnt); + return ret; + } + buf = transfer_buffer; + } + send_data_rsp(0, ++usb_pkt_cnt); + } + + /* Calculate the amount of data to arrive from PC (in bytes) */ + left_to_rcv = total - rcv_cnt; + + /* + * Calculate number of data already received. but not yet stored + * on the medium (they are smaller than THOR_STORE_UNIT_SIZE) + */ + *left = left_to_rcv + buf - transfer_buffer; + debug("%s: left: %llu left_to_rcv: %llu buf: 0x%p\n", __func__, + *left, left_to_rcv, buf); + + if (left_to_rcv) { + thor_set_dma(buf, packet_size); + ret_rcv = thor_rx_data(); + if (ret_rcv < 0) + return ret_rcv; + rcv_cnt += ret_rcv; + send_data_rsp(0, ++usb_pkt_cnt); + } + + debug("%s: %llu total: %llu cnt: %d\n", __func__, rcv_cnt, total, *cnt); + + return rcv_cnt; +} + +static int download_tail(long long int left, int cnt) +{ + void *transfer_buffer = dfu_get_buf(); + int ret; + + debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); + + if (left) { + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, left, cnt++); + if (ret) { + error("DFU write failed [%d]: left: %llu", ret, left); + return ret; + } + } + + /* + * To store last "packet" DFU storage backend requires dfu_write with + * size parameter equal to 0 + * + * This also frees memory malloc'ed by dfu_get_buf(), so no explicit + * need fo call dfu_free_buf() is needed. + */ + ret = dfu_write(dfu_get_entity(alt_setting_num), + transfer_buffer, 0, cnt); + if (ret) + error("DFU write failed [%d] cnt: %d", ret, cnt); + + return ret; +} + +static long long int process_rqt_download(const struct rqt_box *rqt) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rsp_box, rsp, sizeof(struct rsp_box)); + static long long int left, ret_head; + int file_type, ret = 0; + static int cnt; + + memset(rsp, 0, sizeof(struct rsp_box)); + rsp->rsp = rqt->rqt; + rsp->rsp_data = rqt->rqt_data; + + switch (rqt->rqt_data) { + case RQT_DL_INIT: + thor_file_size = rqt->int_data[0]; + debug("INIT: total %d bytes\n", rqt->int_data[0]); + break; + case RQT_DL_FILE_INFO: + file_type = rqt->int_data[0]; + if (file_type == FILE_TYPE_PIT) { + puts("PIT table file - not supported\n"); + rsp->ack = -ENOTSUPP; + ret = rsp->ack; + break; + } + + thor_file_size = rqt->int_data[1]; + memcpy(f_name, rqt->str_data[0], F_NAME_BUF_SIZE); + + debug("INFO: name(%s, %d), size(%llu), type(%d)\n", + f_name, 0, thor_file_size, file_type); + + rsp->int_data[0] = THOR_PACKET_SIZE; + + alt_setting_num = dfu_get_alt(f_name); + if (alt_setting_num < 0) { + error("Alt setting [%d] to write not found!", + alt_setting_num); + rsp->ack = -ENODEV; + ret = rsp->ack; + } + break; + case RQT_DL_FILE_START: + send_rsp(rsp); + ret_head = download_head(thor_file_size, THOR_PACKET_SIZE, + &left, &cnt); + if (ret_head < 0) { + left = 0; + cnt = 0; + } + return ret_head; + case RQT_DL_FILE_END: + debug("DL FILE_END\n"); + rsp->ack = download_tail(left, cnt); + ret = rsp->ack; + left = 0; + cnt = 0; + break; + case RQT_DL_EXIT: + debug("DL EXIT\n"); + break; + default: + error("Operation not supported: %d", rqt->rqt_data); + ret = -ENOTSUPP; + } + + send_rsp(rsp); + return ret; +} + +static int process_data(void) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct rqt_box, rqt, sizeof(struct rqt_box)); + int ret = -EINVAL; + + memset(rqt, 0, sizeof(rqt)); + memcpy(rqt, thor_rx_data_buf, sizeof(struct rqt_box)); + + debug("+RQT: %d, %d\n", rqt->rqt, rqt->rqt_data); + + switch (rqt->rqt) { + case RQT_INFO: + ret = process_rqt_info(rqt); + break; + case RQT_CMD: + ret = process_rqt_cmd(rqt); + break; + case RQT_DL: + ret = (int) process_rqt_download(rqt); + break; + case RQT_UL: + puts("RQT: UPLOAD not supported!\n"); + break; + default: + error("unknown request (%d)", rqt->rqt); + } + + return ret; +} + +/* ********************************************************** */ +/* THOR USB Function */ +/* ********************************************************** */ + +static inline struct usb_endpoint_descriptor * +ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +static struct usb_interface_descriptor thor_downloader_intf_data = { + .bLength = sizeof(thor_downloader_intf_data), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, +}; + +static struct usb_endpoint_descriptor fs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor fs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* CDC configuration */ +static struct usb_interface_descriptor thor_downloader_intf_int = { + .bLength = sizeof(thor_downloader_intf_int), + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + /* 0x02 Abstract Line Control Model */ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + /* 0x01 Common AT commands */ + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, +}; + +static struct usb_cdc_header_desc thor_downloader_cdc_header = { + .bLength = sizeof(thor_downloader_cdc_header), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x00, + .bcdCDC = 0x0110, +}; + +static struct usb_cdc_call_mgmt_descriptor thor_downloader_cdc_call = { + .bLength = sizeof(thor_downloader_cdc_call), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x01, + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + +static struct usb_cdc_acm_descriptor thor_downloader_cdc_abstract = { + .bLength = sizeof(thor_downloader_cdc_abstract), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = 0x02, + .bmCapabilities = 0x00, +}; + +static struct usb_cdc_union_desc thor_downloader_cdc_union = { + .bLength = sizeof(thor_downloader_cdc_union), + .bDescriptorType = 0x24, /* CS_INTERFACE */ + .bDescriptorSubType = USB_CDC_UNION_TYPE, +}; + +static struct usb_endpoint_descriptor fs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = 3 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static struct usb_interface_assoc_descriptor +thor_iad_descriptor = { + .bLength = sizeof(thor_iad_descriptor), + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTO_NONE, +}; + +static struct usb_endpoint_descriptor hs_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_int_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(16), + + .bInterval = 0x9, +}; + +static 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 = 2, +}; + +/* + * This attribute vendor descriptor is necessary for correct operation with + * Windows version of THOR download program + * + * It prevents windows driver from sending zero lenght packet (ZLP) after + * each THOR_PACKET_SIZE. This assures consistent behaviour with libusb + */ +static struct usb_cdc_attribute_vendor_descriptor thor_downloader_cdc_av = { + .bLength = sizeof(thor_downloader_cdc_av), + .bDescriptorType = 0x24, + .bDescriptorSubType = 0x80, + .DAUType = 0x0002, + .DAULength = 0x0001, + .DAUValue = 0x00, +}; + +static const struct usb_descriptor_header *hs_thor_downloader_function[] = { + (struct usb_descriptor_header *)&thor_iad_descriptor, + + (struct usb_descriptor_header *)&thor_downloader_intf_int, + (struct usb_descriptor_header *)&thor_downloader_cdc_header, + (struct usb_descriptor_header *)&thor_downloader_cdc_call, + (struct usb_descriptor_header *)&thor_downloader_cdc_abstract, + (struct usb_descriptor_header *)&thor_downloader_cdc_union, + (struct usb_descriptor_header *)&hs_int_desc, + + (struct usb_descriptor_header *)&thor_downloader_intf_data, + (struct usb_descriptor_header *)&thor_downloader_cdc_av, + (struct usb_descriptor_header *)&hs_in_desc, + (struct usb_descriptor_header *)&hs_out_desc, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return req; + + req->length = length; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + + return req; +} + +static int thor_rx_data(void) +{ + struct thor_dev *dev = thor_func->dev; + int data_to_rx, tmp, status; + + data_to_rx = dev->out_req->length; + tmp = data_to_rx; + do { + dev->out_req->length = data_to_rx; + debug("dev->out_req->length:%d dev->rxdata:%d\n", + dev->out_req->length, dev->rxdata); + + status = usb_ep_queue(dev->out_ep, dev->out_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->out_ep->name, dev->out_req->length, status); + usb_ep_set_halt(dev->out_ep); + return -EAGAIN; + } + + while (!dev->rxdata) { + usb_gadget_handle_interrupts(); + if (ctrlc()) + return -1; + } + dev->rxdata = 0; + data_to_rx -= dev->out_req->actual; + } while (data_to_rx); + + return tmp; +} + +static void thor_tx_data(unsigned char *data, int len) +{ + struct thor_dev *dev = thor_func->dev; + unsigned char *ptr = dev->in_req->buf; + int status; + + memset(ptr, 0, len); + memcpy(ptr, data, len); + + dev->in_req->length = len; + + debug("%s: dev->in_req->length:%d to_cpy:%d\n", __func__, + dev->in_req->length, sizeof(data)); + + status = usb_ep_queue(dev->in_ep, dev->in_req, 0); + if (status) { + error("kill %s: resubmit %d bytes --> %d", + dev->in_ep->name, dev->in_req->length, status); + usb_ep_set_halt(dev->in_ep); + } + + /* Wait until tx interrupt received */ + while (!dev->txdata) + usb_gadget_handle_interrupts(); + + dev->txdata = 0; +} + +static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct thor_dev *dev = thor_func->dev; + int status = req->status; + + debug("%s: ep_ptr:%p, req_ptr:%p\n", __func__, ep, req); + switch (status) { + case 0: + if (ep == dev->out_ep) + dev->rxdata = 1; + else + dev->txdata = 1; + + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + case -EREMOTEIO: /* short read */ + case -EOVERFLOW: + error("ERROR:%d", status); + break; + } + + debug("%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); +} + +static struct usb_request *thor_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = alloc_ep_req(ep, ep->maxpacket); + debug("%s: ep:%p req:%p\n", __func__, ep, req); + + if (!req) + return NULL; + + memset(req->buf, 0, req->length); + req->complete = thor_rx_tx_complete; + + memset(req->buf, 0x55, req->length); + + return req; +} + +static void thor_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int +thor_func_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct thor_dev *dev = thor_func->dev; + struct usb_request *req = dev->req; + struct usb_gadget *gadget = dev->gadget; + int value = 0; + + u16 len = le16_to_cpu(ctrl->wLength); + + debug("Req_Type: 0x%x Req: 0x%x wValue: 0x%x wIndex: 0x%x wLen: 0x%x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, + ctrl->wLength); + + switch (ctrl->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + value = 0; + break; + case USB_CDC_REQ_SET_LINE_CODING: + value = len; + /* Line Coding set done = configuration done */ + thor_func->dev->configuration_done = 1; + break; + + default: + error("thor_setup: unknown request: %d", ctrl->bRequest); + } + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("%s: ep_queue: %d\n", __func__, value); + req->status = 0; + } + } + + return value; +} + +/* Specific to the THOR protocol */ +static void thor_set_dma(void *addr, int len) +{ + struct thor_dev *dev = thor_func->dev; + + debug("in_req:%p, out_req:%p\n", dev->in_req, dev->out_req); + debug("addr:%p, len:%d\n", addr, len); + + dev->out_req->buf = addr; + dev->out_req->length = len; +} + +int thor_init(void) +{ + struct thor_dev *dev = thor_func->dev; + + /* Wait for a device enumeration and configuration settings */ + debug("THOR enumeration/configuration setting....\n"); + while (!dev->configuration_done) + usb_gadget_handle_interrupts(); + + thor_set_dma(thor_rx_data_buf, strlen("THOR")); + /* detect the download request from Host PC */ + if (thor_rx_data() < 0) { + printf("%s: Data not received!\n", __func__); + return -1; + } + + if (!strncmp((char *)thor_rx_data_buf, "THOR", strlen("THOR"))) { + puts("Download request from the Host PC\n"); + udelay(30 * 1000); /* 30 ms */ + + strcpy((char *)thor_tx_data_buf, "ROHT"); + thor_tx_data(thor_tx_data_buf, strlen("ROHT")); + } else { + puts("Wrong reply information\n"); + return -1; + } + + return 0; +} + +int thor_handle(void) +{ + int ret; + + /* receive the data from Host PC */ + while (1) { + thor_set_dma(thor_rx_data_buf, sizeof(struct rqt_box)); + ret = thor_rx_data(); + + if (ret > 0) { + ret = process_data(); + if (ret < 0) + return ret; + } else { + printf("%s: No data received!\n", __func__); + break; + } + } + + return 0; +} + +static int thor_func_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev; + struct usb_ep *ep; + int status; + + thor_func = f_thor; + dev = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*dev)); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + dev->gadget = gadget; + f_thor->dev = dev; + + debug("%s: usb_configuration: 0x%p usb_function: 0x%p\n", + __func__, c, f); + debug("f_thor: 0x%p thor: 0x%p\n", f_thor, dev); + + /* EP0 */ + /* preallocate control response and buffer */ + dev->req = usb_ep_alloc_request(gadget->ep0, 0); + if (!dev->req) { + status = -ENOMEM; + goto fail; + } + dev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, + gadget->ep0->maxpacket); + if (!dev->req->buf) { + status = -ENOMEM; + goto fail; + } + + dev->req->complete = thor_setup_complete; + + /* DYNAMIC interface numbers assignments */ + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_int.bInterfaceNumber = status; + thor_downloader_cdc_union.bMasterInterface0 = status; + + status = usb_interface_id(c, f); + + if (status < 0) + goto fail; + + thor_downloader_intf_data.bInterfaceNumber = status; + thor_downloader_cdc_union.bSlaveInterface0 = status; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(gadget, &fs_in_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) { + hs_in_desc.bEndpointAddress = + fs_in_desc.bEndpointAddress; + } + + dev->in_ep = ep; /* Store IN EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_out_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + if (gadget_is_dualspeed(gadget)) + hs_out_desc.bEndpointAddress = + fs_out_desc.bEndpointAddress; + + dev->out_ep = ep; /* Store OUT EP for enabling @ setup */ + + ep = usb_ep_autoconfig(gadget, &fs_int_desc); + if (!ep) { + status = -ENODEV; + goto fail; + } + + dev->int_ep = ep; + + if (gadget_is_dualspeed(gadget)) { + hs_int_desc.bEndpointAddress = + fs_int_desc.bEndpointAddress; + + f->hs_descriptors = (struct usb_descriptor_header **) + &hs_thor_downloader_function; + + if (!f->hs_descriptors) + goto fail; + } + + debug("%s: out_ep:%p out_req:%p\n", __func__, + dev->out_ep, dev->out_req); + + return 0; + + fail: + free(dev); + return status; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + free(req->buf); + usb_ep_free_request(ep, req); +} + +static void thor_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + free(dev); + memset(thor_func, 0, sizeof(*thor_func)); + thor_func = NULL; +} + +static void thor_func_disable(struct usb_function *f) +{ + struct f_thor *f_thor = func_to_thor(f); + struct thor_dev *dev = f_thor->dev; + + debug("%s:\n", __func__); + + /* Avoid freeing memory when ep is still claimed */ + if (dev->in_ep->driver_data) { + free_ep_req(dev->in_ep, dev->in_req); + usb_ep_disable(dev->in_ep); + dev->in_ep->driver_data = NULL; + } + + if (dev->out_ep->driver_data) { + dev->out_req->buf = NULL; + usb_ep_free_request(dev->out_ep, dev->out_req); + usb_ep_disable(dev->out_ep); + dev->out_ep->driver_data = NULL; + } + + if (dev->int_ep->driver_data) { + usb_ep_disable(dev->int_ep); + dev->int_ep->driver_data = NULL; + } +} + +static int thor_eps_setup(struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct thor_dev *dev = thor_func->dev; + struct usb_endpoint_descriptor *d; + struct usb_request *req; + struct usb_ep *ep; + int result; + + ep = dev->in_ep; + d = ep_desc(gadget, &hs_in_desc, &fs_in_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + if (result) + goto exit; + + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (!req) { + usb_ep_disable(ep); + result = -EIO; + goto exit; + } + + dev->in_req = req; + ep = dev->out_ep; + d = ep_desc(gadget, &hs_out_desc, &fs_out_desc); + debug("(d)bEndpointAddress: 0x%x\n", d->bEndpointAddress); + + result = usb_ep_enable(ep, d); + if (result) + goto exit; + + ep->driver_data = cdev; /* claim */ + req = thor_start_ep(ep); + if (!req) { + usb_ep_disable(ep); + result = -EIO; + goto exit; + } + + dev->out_req = req; + /* ACM control EP */ + ep = dev->int_ep; + ep->driver_data = cdev; /* claim */ + + exit: + return result; +} + +static int thor_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct thor_dev *dev = thor_func->dev; + int result; + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, intf, alt); + + switch (intf) { + case 0: + debug("ACM INTR interface\n"); + break; + case 1: + debug("Communication Data interface\n"); + result = thor_eps_setup(f); + if (result) + error("%s: EPs setup failed!", __func__); + dev->configuration_done = 1; + break; + } + + return 0; +} + +static int thor_func_init(struct usb_configuration *c) +{ + struct f_thor *f_thor; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + f_thor = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_thor)); + if (!f_thor) + return -ENOMEM; + + memset(f_thor, 0, sizeof(*f_thor)); + + f_thor->usb_function.name = "f_thor"; + f_thor->usb_function.bind = thor_func_bind; + f_thor->usb_function.unbind = thor_unbind; + f_thor->usb_function.setup = thor_func_setup; + f_thor->usb_function.set_alt = thor_func_set_alt; + f_thor->usb_function.disable = thor_func_disable; + + status = usb_add_function(c, &f_thor->usb_function); + if (status) + free(f_thor); + + return status; +} + +int thor_add(struct usb_configuration *c) +{ + debug("%s:\n", __func__); + return thor_func_init(c); +} diff --git a/drivers/usb/gadget/f_thor.h b/drivers/usb/gadget/f_thor.h new file mode 100644 index 0000000..04ee9a2 --- /dev/null +++ b/drivers/usb/gadget/f_thor.h @@ -0,0 +1,124 @@ +/* + * f_thor.h - USB TIZEN THOR - internal gadget definitions + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _USB_THOR_H_ +#define _USB_THOR_H_ + +#include <linux/compiler.h> +#include <asm/sizes.h> + +/* THOR Composite Gadget */ +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +/* ********************************************************** */ +/* THOR protocol definitions */ +/* ********************************************************** */ + +/* + * Attribute Vendor descriptor - necessary to prevent ZLP transmission + * from Windows XP HOST PC + */ +struct usb_cdc_attribute_vendor_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u16 DAUType; + __u16 DAULength; + __u8 DAUValue; +} __packed; + +#define VER_PROTOCOL_MAJOR 4 +#define VER_PROTOCOL_MINOR 0 + +enum rqt { + RQT_INFO = 200, + RQT_CMD, + RQT_DL, + RQT_UL, +}; + +enum rqt_data { + /* RQT_INFO */ + RQT_INFO_VER_PROTOCOL = 1, + RQT_INIT_VER_HW, + RQT_INIT_VER_BOOT, + RQT_INIT_VER_KERNEL, + RQT_INIT_VER_PLATFORM, + RQT_INIT_VER_CSC, + + /* RQT_CMD */ + RQT_CMD_REBOOT = 1, + RQT_CMD_POWEROFF, + RQT_CMD_EFSCLEAR, + + /* RQT_DL */ + RQT_DL_INIT = 1, + RQT_DL_FILE_INFO, + RQT_DL_FILE_START, + RQT_DL_FILE_END, + RQT_DL_EXIT, + + /* RQT_UL */ + RQT_UL_INIT = 1, + RQT_UL_START, + RQT_UL_END, + RQT_UL_EXIT, +}; + +struct rqt_box { /* total: 256B */ + s32 rqt; /* request id */ + s32 rqt_data; /* request data id */ + s32 int_data[14]; /* int data */ + char str_data[5][32]; /* string data */ + char md5[32]; /* md5 checksum */ +} __packed; + +struct rsp_box { /* total: 128B */ + s32 rsp; /* response id (= request id) */ + s32 rsp_data; /* response data id */ + s32 ack; /* ack */ + s32 int_data[5]; /* int data */ + char str_data[3][32]; /* string data */ +} __packed; + +struct data_rsp_box { /* total: 8B */ + s32 ack; /* response id (= request id) */ + s32 count; /* response data id */ +} __packed; + +enum { + FILE_TYPE_NORMAL, + FILE_TYPE_PIT, +}; + +struct thor_dev { + struct usb_gadget *gadget; + struct usb_request *req; /* EP0 -> control responses */ + + /* IN/OUT EP's and correspoinding requests */ + struct usb_ep *in_ep, *out_ep, *int_ep; + struct usb_request *in_req, *out_req; + + /* Control flow variables */ + unsigned char configuration_done; + unsigned char rxdata; + unsigned char txdata; +}; + +struct f_thor { + struct usb_function usb_function; + struct thor_dev *dev; +}; + +#define F_NAME_BUF_SIZE 32 +#define THOR_PACKET_SIZE SZ_1M /* 1 MiB */ +#define THOR_STORE_UNIT_SIZE SZ_32M /* 32 MiB */ +#endif /* _USB_THOR_H_ */ diff --git a/include/thor.h b/include/thor.h new file mode 100644 index 0000000..afeade4 --- /dev/null +++ b/include/thor.h @@ -0,0 +1,27 @@ +/* + * thor.h -- USB THOR Downloader protocol + * + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef __THOR_H_ +#define __THOR_H_ + +#include <linux/usb/composite.h> + +int thor_handle(void); +int thor_init(void); + +#ifdef CONFIG_THOR_FUNCTION +int thor_add(struct usb_configuration *c); +#else +int thor_add(struct usb_configuration *c) +{ + return 0; +} +#endif +#endif /* __THOR_H_ */

Support of "thor" function in generic download code (g_dnl.c).
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - None
drivers/usb/gadget/g_dnl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 98560b8..43f413a 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -16,6 +16,7 @@ #include <g_dnl.h> #include <usb_mass_storage.h> #include <dfu.h> +#include <thor.h>
#include "gadget_chips.h" #include "composite.c" @@ -101,6 +102,8 @@ static int g_dnl_do_config(struct usb_configuration *c) ret = dfu_add(c); else if (!strcmp(s, "usb_dnl_ums")) ret = fsg_add(c); + else if (!strcmp(s, "usb_dnl_thor")) + ret = thor_add(c);
return ret; } @@ -191,8 +194,8 @@ static struct usb_composite_driver g_dnl_driver = {
int g_dnl_register(const char *type) { - /* We only allow "dfu" atm, so 3 should be enough */ - static char name[sizeof(shortname) + 3]; + /* The largest function name is 4 */ + static char name[sizeof(shortname) + 4]; int ret;
if (!strcmp(type, "dfu")) { @@ -201,6 +204,9 @@ int g_dnl_register(const char *type) } else if (!strcmp(type, "ums")) { strcpy(name, shortname); strcat(name, type); + } else if (!strcmp(type, "thor")) { + strcpy(name, shortname); + strcat(name, type); } else { printf("%s: unknown command: %s\n", __func__, type); return -EINVAL;

New command - thordown - has been added to support downloading data via lthor TIZEN program. It is similar to dfu command syntax and reuses its code for flashing data.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com
--- Changes for v2: - Adjust according to new board_usb_init() function semantics. - do_thor_down() written in a similar way to rewritten do_dfu() function - Check error condition for board_usb_init()
common/Makefile | 1 + common/cmd_thordown.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 common/cmd_thordown.c
diff --git a/common/Makefile b/common/Makefile index 288690b..8daca5b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -168,6 +168,7 @@ COBJS-y += usb.o usb_hub.o COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o endif COBJS-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o +COBJS-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o COBJS-$(CONFIG_CMD_SPL) += cmd_spl.o diff --git a/common/cmd_thordown.c b/common/cmd_thordown.c new file mode 100644 index 0000000..c4b3511 --- /dev/null +++ b/common/cmd_thordown.c @@ -0,0 +1,72 @@ +/* + * cmd_thordown.c -- USB TIZEN "THOR" Downloader gadget + * + * Copyright (C) 2013 Lukasz Majewski l.majewski@samsung.com + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <thor.h> +#include <dfu.h> +#include <g_dnl.h> +#include <usb.h> + +int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + if (argc < 4) + return CMD_RET_USAGE; + + char *usb_controller = argv[1]; + char *interface = argv[2]; + char *devstring = argv[3]; + + const char *s = "thor"; + int ret; + + puts("TIZEN "THOR" Downloader\n"); + + ret = dfu_init_env_entities(interface, simple_strtoul(devstring, + NULL, 10)); + if (ret) + return ret; + + int controller_index = simple_strtoul(usb_controller, NULL, 0); + ret = board_usb_init(controller_index, USB_INIT_DEVICE); + if (ret) { + error("USB init failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + g_dnl_register(s); + + ret = thor_init(); + if (ret) { + error("THOR DOWNLOAD failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + + ret = thor_handle(); + if (ret) { + error("THOR failed: %d", ret); + ret = CMD_RET_FAILURE; + goto exit; + } + +exit: + g_dnl_unregister(); + dfu_free_entities(); + + return ret; +} + +U_BOOT_CMD(thordown, CONFIG_SYS_MAXARGS, 1, do_thor_down, + "TIZEN "THOR" downloader", + "<USB_controller> <interface> <dev>\n" + " - device software upgrade via LTHOR TIZEN dowload\n" + " program via <USB_controller> on device <dev>,\n" + " attached to interface <interface>\n" +);

Special, common to Samsung, function for altering usb descriptor's idVendor and idProduct has been added. For compatibility reasons (Win vs Linux) the THOR idProduct must be different than the one for DFU/UMS.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com
--- Changes for v2: - None
board/samsung/common/Makefile | 1 + board/samsung/common/thor.c | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 board/samsung/common/thor.c
diff --git a/board/samsung/common/Makefile b/board/samsung/common/Makefile index 9e48a7b..ad7564c 100644 --- a/board/samsung/common/Makefile +++ b/board/samsung/common/Makefile @@ -10,6 +10,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)libsamsung.o
COBJS-$(CONFIG_SOFT_I2C_MULTI_BUS) += multi_i2c.o +COBJS-$(CONFIG_THOR_FUNCTION) += thor.o
SRCS := $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) diff --git a/board/samsung/common/thor.c b/board/samsung/common/thor.c new file mode 100644 index 0000000..1c7630d --- /dev/null +++ b/board/samsung/common/thor.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Lukasz Majewski l.majewski@samsung.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/usb/ch9.h> + +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + if (!strcmp(name, "usb_dnl_thor")) { + put_unaligned(CONFIG_G_DNL_THOR_VENDOR_NUM, &dev->idVendor); + put_unaligned(CONFIG_G_DNL_THOR_PRODUCT_NUM, &dev->idProduct); + } else { + put_unaligned(CONFIG_G_DNL_VENDOR_NUM, &dev->idVendor); + put_unaligned(CONFIG_G_DNL_PRODUCT_NUM, &dev->idProduct); + } + return 0; +}

A set of environment variables needs to be updated to provide support for TIZEN download command (tizendown).
Since DFU is used as a flashing backend, it is also necessary to extent malloc pool size for DFU buffer allocation. Moreover, for compatibility reasons (Win vs. Lin) new USB idProduct number for download gadget had to be added.
Signed-off-by: Lukasz Majewski l.majewski@samsung.com Cc: Marek Vasut marex@denx.de
--- Changes for v2: - Replace (X << 20) with SZ_XM defines
include/configs/trats.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/include/configs/trats.h b/include/configs/trats.h index 24ea06b..f5bb6aa 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -49,8 +49,9 @@ #define MACH_TYPE_TRATS 3928 #define CONFIG_MACH_TYPE MACH_TYPE_TRATS
+#include <asm/sizes.h> /* Size of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (16 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 * SZ_1M))
/* select serial console configuration */ #define CONFIG_SERIAL2 /* use SERIAL 2 */ @@ -91,12 +92,20 @@
/* USB Composite download gadget - g_dnl */ #define CONFIG_USBDOWNLOAD_GADGET + +/* TIZEN THOR downloader support */ +#define CONFIG_CMD_THOR_DOWNLOAD +#define CONFIG_THOR_FUNCTION + +#define CONFIG_SYS_DFU_DATA_BUF_SIZE SZ_32M #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_THOR_VENDOR_NUM CONFIG_G_DNL_VENDOR_NUM +#define CONFIG_G_DNL_THOR_PRODUCT_NUM 0x685D #define CONFIG_G_DNL_MANUFACTURER "Samsung"
#define CONFIG_BOOTDELAY 1 @@ -131,7 +140,8 @@ #define CONFIG_DFU_ALT \ "u-boot mmc 80 400;" \ "uImage ext4 0 2;" \ - "exynos4210-trats.dtb ext4 0 2\0" + "exynos4210-trats.dtb ext4 0 2;" \ + ""PARTS_ROOT" part 0 5\0"
#define CONFIG_ENV_OVERWRITE #define CONFIG_SYS_CONSOLE_INFO_QUIET

Dear Lukasz Majewski,
This patch series provide support for TIZEN's THOR download protocol.
Dedicated program for flashing TIZEN developer devices (TRATS, TRATS2) is called lthor (or thor for Windows) and can be found at:
git clone git://review.tizen.org/tools/lthor
or for git web:
https://review.tizen.org/git/?p=tools/lthor.git;a=summary
Presented composite USB function acts as a front end to perform correct USB communication with HOST PC. To store the received data on the target, the DFU (Device Firmware Update) code for flashing has been reused.
This means, that the "dfu_alt_info" environment variable is used to provide information where a received file is stored.
This also means that dfu and thor can co-exists together. Thor protocol and its implementation has one advantage - it is much faster than DFU for large files transfers (especially rootfs images).
It applies on: u-boot-denx-usb/next SHA1: 6928d26b84a5aa4a44706362234ab435bb15a6fb
Test HW: Exynos4210 (TRATS)
Applied all, thanks
Best regards, Marek Vasut
participants (2)
-
Lukasz Majewski
-
Marek Vasut