mirror of
https://gitee.com/Vancouver2017/luban-lite.git
synced 2025-12-17 17:48:55 +00:00
463 lines
14 KiB
C
463 lines
14 KiB
C
|
|
/*
|
||
|
|
* 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
|