Files
luban-lite/application/baremetal/bootloader/lib/aicupg/upg_device.c

463 lines
14 KiB
C
Raw Normal View History

2023-08-28 09:48:01 +08:00
/*
* Copyright (c) 2023, Artinchip Technology Co., Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Wu Dehuang <dehuang.wu@artinchip.com>
*/
#include <string.h>
#include <usbdescriptors.h>
#include <usbdevice.h>
#include <usb_drv.h>
#include <upg_device.h>
#include <aic_core.h>
#include <aicupg.h>
#ifdef AICUPG_DEBUG
#undef pr_err
#undef pr_warn
#undef pr_info
#undef pr_debug
#define pr_err printf
#define pr_warn printf
#define pr_info printf
#define pr_debug printf
#endif
#define AIC_BULK_EP_HS_MPS 512
#if defined(AICUPG_USB_ENABLE)
struct usb_misc_descriptors {
struct usb_configuration_descriptor config;
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor ep_in;
struct usb_endpoint_descriptor ep_out;
} __attribute__((packed));
#define STR_COUNT 3
static struct usb_string_descriptor *__str_desc_table[STR_COUNT];
static struct usb_device_descriptor __device_desc = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 0x40,
.idVendor = DRIVER_VENDOR_ID,
.idProduct = DRIVER_PRODUCT_ID,
.bcdDevice = 0x0101,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 0,
.bNumConfigurations = 1,
};
static struct usb_qualifier_descriptor __qualifier_desc = {
.bLength = sizeof(struct usb_qualifier_descriptor),
.bDescriptorType = USB_DT_DEVICE_QUALIFIER,
.bcdUSB = 0x200,
.bDeviceClass = 0xff,
.bDeviceSubClass = 0xff,
.bDeviceProtocol = 0xff,
.bMaxPacketSize0 = 0x40,
.bNumConfigurations = 1,
.breserved = 0,
};
static struct usb_misc_descriptors __misc_desc = {
.config = {
.bLength = sizeof(struct usb_configuration_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof(struct usb_misc_descriptors),
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80, //not self powered
.bMaxPower = 0xFA, // Max 500mA(0xfa * 2)
},
.intf = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0x00,
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x02,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xff,
.bInterfaceProtocol = 0xff,
.iInterface = 0,
},
.ep_in = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = (0x80 | BULK_IN_EP_INDEX),
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = AIC_BULK_EP_HS_MPS,
.bInterval = 0x00,
},
.ep_out = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = BULK_OUT_EP_INDEX,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = AIC_BULK_EP_HS_MPS,
.bInterval = 0x00,
},
};
static void __string_descriptors_init(void)
{
static u8 lang[4] = { 4, USB_DT_STRING, 0x9, 0x4 };
static u8 manufacturer[22] = {
22, USB_DT_STRING, 'A', 0, 'r', 0, 't', 0, 'i', 0, 'n',
0, 'c', 0, 'h', 0, 'i', 0, 'p', 0, 0, 0
};
static u8 product[36] = { 36, USB_DT_STRING,
'A', 0,
'r', 0,
't', 0,
'i', 0,
'n', 0,
'c', 0,
'h', 0,
'i', 0,
'p', 0,
' ', 0,
'D', 0,
'e', 0,
'v', 0,
'i', 0,
'c', 0,
'e', 0,
0, 0 };
__str_desc_table[0] = (struct usb_string_descriptor *)lang;
__str_desc_table[1] = (struct usb_string_descriptor *)manufacturer;
__str_desc_table[2] = (struct usb_string_descriptor *)product;
}
static s32 __usb_get_status(struct usb_device_request *req)
{
u8 len = 0, rep;
u8 buf[2];
if (req->wLength == 0) {
/* sent zero packet */
aic_udc_ctrl_ep_send(NULL, 0);
return AIC_USB_REQ_OP_ERR;
}
len = min(req->wLength, (u16)2);
rep = (req->bmRequestType & USB_REQ_RECIPIENT_MASK);
if (rep == USB_RECIP_DEVICE) {
/* buf[0]
* Bit[0]: Self-power or bus-power
* - 0: Bus-power
* - 1: Self-power
* Bit[1]: Remote wakeup
* - 0: Disabled
* - 1: Enabled
*/
buf[0] = 1;
buf[1] = 0;
}
if (rep == USB_RECIP_INTERFACE) {
buf[0] = 0;
buf[1] = 0;
}
if (rep == USB_RECIP_ENDPOINT) {
buf[0] = 0;
buf[1] = 0;
}
aic_udc_ctrl_ep_send(buf, len);
return AIC_USB_REQ_SUCCESSED;
}
static s32 __usb_set_configuration(struct usb_device_request *req)
{
pr_debug("set configuration\n");
/* Only support 1 configuration so nak anything else */
if (req->wValue == 1) {
aic_udc_bulk_ep_reset();
} else {
pr_err("err: invalid wValue, (0, %d)\n", req->wValue);
return AIC_USB_REQ_OP_ERR;
}
aic_udc_set_configuration(req->wValue);
return AIC_USB_REQ_SUCCESSED;
}
static s32 __usb_set_address(struct usb_device_request *req)
{
u8 address;
address = req->wValue & 0x7f;
aicos_udelay(10);
pr_debug("set address 0x%x\n", address);
aic_udc_set_address(address);
return AIC_USB_REQ_SUCCESSED;
}
static s32 __usb_set_interface(struct usb_device_request *req)
{
pr_debug("set interface\n");
/* Only support interface 0, alternate 0 */
if ((req->wIndex == 0) && (req->wValue == 0)) {
/*
* Reset EP for Buik transfer
*/
aic_udc_bulk_ep_reset();
} else {
pr_err("err: invalid wIndex and wValue, (0, %d), (0, %d)\n",
req->wIndex, req->wValue);
return AIC_USB_REQ_OP_ERR;
}
return AIC_USB_REQ_SUCCESSED;
}
static s32 __usb_get_descriptor(struct usb_device_request *req)
{
u32 len = 0;
u8 str_idx;
s32 bytes_total = 0, ret = AIC_USB_REQ_DEVICE_NOT_SUPPORTED;
struct usb_device_descriptor *dev_desc;
struct usb_qualifier_descriptor *qua_desc;
switch (req->wValue >> 8) {
case USB_DT_DEVICE: {
pr_debug("get USB_DT_DEVICE\n");
dev_desc = &__device_desc;
len = min(req->wLength, (u16)sizeof(struct usb_device_descriptor));
aic_udc_ctrl_ep_send((u8 *)dev_desc, len);
ret = AIC_USB_REQ_SUCCESSED;
break;
}
case USB_DT_CONFIG: {
pr_debug("get USB_DT_CONFIG\n");
bytes_total = sizeof(struct usb_misc_descriptors);
if (bytes_total > 0) {
len = min(req->wLength, (u16)bytes_total);
aic_udc_ctrl_ep_send((u8 *)&__misc_desc, len);
ret = AIC_USB_REQ_SUCCESSED;
}
break;
}
case USB_DT_STRING: {
pr_debug("get USB_DT_STRING\n");
str_idx = req->wValue & 0xff;
/* Language ID */
if (str_idx < STR_COUNT) {
len = __str_desc_table[str_idx]->bLength;
aic_udc_ctrl_ep_send((u8 *)__str_desc_table[str_idx], len);
ret = AIC_USB_REQ_SUCCESSED;
} else {
pr_debug("string line 0x%x is not supported\n", str_idx);
}
break;
}
case USB_DT_DEVICE_QUALIFIER: {
pr_debug("get qualifier descriptor\n");
if (req->wLength < sizeof(struct usb_qualifier_descriptor))
return ret;
qua_desc = &__qualifier_desc;
aic_udc_ctrl_ep_send((u8 *)qua_desc, qua_desc->bLength);
ret = AIC_USB_REQ_SUCCESSED;
break;
}
default:
pr_err("err: unkown wValue(0x%x)\n", req->wValue);
break;
}
return ret;
}
static s32 usb_upg_init(void)
{
__string_descriptors_init();
return 0;
}
static s32 usb_upg_exit(void)
{
return 0;
}
static void usb_upg_reset(void)
{
}
static s32 usb_upg_standard_req(struct usb_device_request *req)
{
u8 dir = 0;
u8 rep = 0;
s32 ret = AIC_USB_REQ_DEVICE_NOT_SUPPORTED;
// pr_debug("%s\n", __func__);
dir = (req->bmRequestType & USB_REQ_DIRECTION_MASK);
rep = (req->bmRequestType & USB_REQ_RECIPIENT_MASK);
/* standard */
switch (req->bRequest) {
case USB_REQ_GET_STATUS: {
pr_debug("%s USB_REQ_GET_STATUS\n", __func__);
/* device-to-host */
if (USB_DIR_IN == dir)
ret = __usb_get_status(req);
break;
}
case USB_REQ_SET_ADDRESS: {
pr_debug("%s USB_REQ_SET_ADDRESS\n", __func__);
/* host-to-device */
if ((USB_DIR_OUT == dir) && (USB_RECIP_DEVICE == rep))
ret = __usb_set_address(req);
break;
}
case USB_REQ_GET_DESCRIPTOR: {
pr_debug("%s USB_REQ_GET_DESCRIPTOR\n", __func__);
/* device-to-host */
if ((USB_DIR_IN == dir) && (USB_RECIP_DEVICE == rep))
ret = __usb_get_descriptor(req);
break;
}
case USB_REQ_GET_CONFIGURATION: {
pr_debug("%s USB_REQ_GET_CONFIGURATION\n", __func__);
/* device-to-host */
if ((USB_DIR_IN == dir) && (USB_RECIP_DEVICE == rep))
ret = __usb_get_descriptor(req);
break;
}
case USB_REQ_SET_CONFIGURATION: {
pr_debug("%s USB_REQ_SET_CONFIGURATION\n", __func__);
/* host-to-device */
if ((USB_DIR_OUT == dir) && (USB_RECIP_DEVICE == rep))
ret = __usb_set_configuration(req);
break;
}
case USB_REQ_SET_INTERFACE: {
pr_debug("%s USB_REQ_SET_INTERFACE\n", __func__);
/* host-to-device */
if ((USB_DIR_OUT == dir) && (USB_RECIP_INTERFACE == rep))
ret = __usb_set_interface(req);
break;
}
default:
pr_err("aic usb err: unsupport usb out request to device\n");
break;
}
return ret;
}
static s32 usb_upg_nonstandard_req(struct usb_device_request *req)
{
s32 ret = AIC_USB_REQ_DEVICE_NOT_SUPPORTED;
pr_debug("%s\n", __func__);
return ret;
}
#define RECV_SIZE_USE_DMA 512
#define USB_DATA_PKT_SIZE (64 * 1024)
static u8 trans_pkt_buf[USB_DATA_PKT_SIZE];
static void __usb_send_csw(u32 tag, u8 status)
{
struct aic_csw csw;
csw.dCSWSignature = AIC_USB_SIGN_USBS;
csw.dCSWTag = tag;
csw.dCSWDataResidue = 0;
csw.bCSWStatus = status;
aic_udc_bulk_send((u8 *)&csw, sizeof(struct aic_csw));
pr_debug("Ack CSW tag 0x%X\n", tag);
}
/* All income should start with CBW packet */
static s32 usb_upg_trans_layer_proc(u8 *buffer, u32 len)
{
struct aic_cbw cbw;
u32 data_len;
u8 status;
if (len != sizeof(struct aic_cbw)) {
pr_err("length != 31, len = 0x%X\n", len);
/* Status error or CBW not valid, just skip pakcet here */
return -1;
}
/* To avoid alignment issue */
memcpy(&cbw, buffer, sizeof(struct aic_cbw));
if (cbw.dCBWSignature != AIC_USB_SIGN_USBC) {
pr_err("CBW signature not correct\n");
/* Status error or CBW not valid, just skip pakcet here */
return -1;
}
status = AIC_CSW_STATUS_PASSED;
pr_debug("\nCBW tag 0x%X\n", cbw.dCBWTag);
switch (cbw.bCommand) {
case TRANS_LAYER_CMD_WRITE:
data_len = cbw.dCBWDataTransferLength;
pr_debug("TRANS_LAYER_CMD_WRITE, data len %d\n", data_len);
if ((data_len > USB_DATA_PKT_SIZE) || (cbw.bmCBWFlags != 0)) {
status = AIC_CSW_STATUS_FAILED;
__usb_send_csw(cbw.dCBWTag, status);
break;
}
data_len = aic_udc_bulk_recv(trans_pkt_buf, data_len);
if (data_len == cbw.dCBWDataTransferLength) {
__usb_send_csw(cbw.dCBWTag, status); /* Ack first */
data_len = aicupg_data_packet_write(trans_pkt_buf, data_len);
} else {
status = AIC_CSW_STATUS_FAILED;
__usb_send_csw(cbw.dCBWTag, status);
}
break;
case TRANS_LAYER_CMD_READ:
data_len = cbw.dCBWDataTransferLength;
pr_debug("TRANS_LAYER_CMD_READ, data len %d\n", data_len);
if ((data_len > USB_DATA_PKT_SIZE) || (cbw.bmCBWFlags != 0x80)) {
status = AIC_CSW_STATUS_FAILED;
__usb_send_csw(cbw.dCBWTag, status);
break;
}
data_len = aicupg_data_packet_read(trans_pkt_buf, data_len);
if (data_len == cbw.dCBWDataTransferLength) {
aic_udc_bulk_send((u8 *)trans_pkt_buf, data_len);
__usb_send_csw(cbw.dCBWTag, status);
} else {
status = AIC_CSW_STATUS_FAILED;
__usb_send_csw(cbw.dCBWTag, status);
}
break;
default:
status = AIC_CSW_STATUS_PHASE_ERR;
__usb_send_csw(cbw.dCBWTag, status);
break;
}
return 0;
}
struct __usb_device usbupg_device = {
.state_init = usb_upg_init,
.state_exit = usb_upg_exit,
.state_reset = usb_upg_reset,
.standard_req_proc = usb_upg_standard_req,
.nonstandard_req_proc = usb_upg_nonstandard_req,
.state_cmd = usb_upg_trans_layer_proc,
};
#endif