948 lines
34 KiB
C
948 lines
34 KiB
C
#include "app_config.h"
|
||
#include "usb_host.h"
|
||
#include "usbnet.h"
|
||
#include "cdc_defs.h"
|
||
#include "device_drive.h"
|
||
#include "host/usb_ctrl_transfer.h"
|
||
|
||
#if TCFG_HOST_WIRELESS_ENABLE
|
||
|
||
#define LOG_TAG_CONST USB
|
||
#define LOG_TAG "[net]"
|
||
#define LOG_ERROR_ENABLE
|
||
#define LOG_DEBUG_ENABLE
|
||
#define LOG_INFO_ENABLE
|
||
/* #define LOG_DUMP_ENABLE */
|
||
#define LOG_CLI_ENABLE
|
||
#include "debug.h"
|
||
|
||
#define EL3RST (40) /* Level 3 reset */
|
||
#define ETH_ALEN (6)
|
||
/* #define ETIMEDOUT (238) [> Connection timed out <] */
|
||
|
||
static USBNET_RX_COMPLETE_CB usbnet_rx_complete_cb = NULL;
|
||
|
||
static void keepalive_period_task(void *priv);
|
||
static void (*usbnet_at_port_rx_handler)(u8 *buf, u32 len) = NULL;
|
||
|
||
static int set_power(struct usb_host_device *host_dev, u32 value)
|
||
{
|
||
return DEV_ERR_NONE;
|
||
}
|
||
|
||
static int get_power(struct usb_host_device *host_dev, u32 value)
|
||
{
|
||
return DEV_ERR_NONE;
|
||
}
|
||
|
||
static struct wireless_device_t wireless_dev[USB_MAX_HW_NUM];
|
||
|
||
static const struct interface_ctrl wireless_ctrl = {
|
||
.interface_class = USB_CLASS_WIRELESS_CONTROLLER,
|
||
.set_power = set_power,
|
||
.get_power = get_power,
|
||
.ioctl = NULL,
|
||
};
|
||
|
||
static const struct interface_ctrl at_port_ctrl = {
|
||
.interface_class = USB_CLASS_VENDOR_SPEC,
|
||
.set_power = set_power,
|
||
.get_power = get_power,
|
||
.ioctl = NULL,
|
||
};
|
||
|
||
static const struct usb_interface_info wireless_inf[USB_MAX_HW_NUM] = {
|
||
{
|
||
.ctrl = (struct interface_ctrl *) &wireless_ctrl,
|
||
.dev.wireless = &wireless_dev[0],
|
||
},
|
||
#if USB_MAX_HW_NUM > 1
|
||
{
|
||
.ctrl = (struct interface_ctrl *) &wireless_ctrl,
|
||
.dev.wireless = &wireless_dev[1],
|
||
}
|
||
#endif
|
||
};
|
||
|
||
static const struct usb_interface_info at_port_inf[USB_MAX_HW_NUM] = {
|
||
{
|
||
.ctrl = (struct interface_ctrl *) &at_port_ctrl,
|
||
.dev.wireless = &wireless_dev[0],
|
||
},
|
||
#if USB_MAX_HW_NUM > 1
|
||
{
|
||
.ctrl = (struct interface_ctrl *) &at_port_ctrl,
|
||
.dev.wireless = &wireless_dev[1],
|
||
}
|
||
#endif
|
||
};
|
||
|
||
|
||
void __attribute__((weak)) copy_usbnet_mac_addr(u8 *mac)
|
||
{
|
||
|
||
}
|
||
|
||
|
||
void usb_net_stop_process(usb_dev usb_id)
|
||
{
|
||
if (wireless_dev[usb_id].epin_buf) {
|
||
usb_h_free_ep_buffer(usb_id, wireless_dev[usb_id].epin_buf);
|
||
wireless_dev[usb_id].epin_buf = NULL;
|
||
}
|
||
if (wireless_dev[usb_id].epout_buf) {
|
||
usb_h_free_ep_buffer(usb_id, wireless_dev[usb_id].epout_buf);
|
||
wireless_dev[usb_id].epout_buf = NULL;
|
||
}
|
||
if (wireless_dev[usb_id].inbuf) {
|
||
free(wireless_dev[usb_id].inbuf);
|
||
wireless_dev[usb_id].inbuf = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
void usb_net_at_port_stop_process(usb_dev usb_id)
|
||
{
|
||
if (wireless_dev[usb_id].epin_buf_at) {
|
||
usb_h_free_ep_buffer(usb_id, wireless_dev[usb_id].epin_buf_at);
|
||
wireless_dev[usb_id].epin_buf_at = NULL;
|
||
}
|
||
if (wireless_dev[usb_id].epout_buf_at) {
|
||
usb_h_free_ep_buffer(usb_id, wireless_dev[usb_id].epout_buf_at);
|
||
wireless_dev[usb_id].epout_buf_at = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
int is_wireless_rndis(struct usb_interface_descriptor *desc)
|
||
{
|
||
return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER &&
|
||
desc->bInterfaceSubClass == 1 &&
|
||
desc->bInterfaceProtocol == 3);
|
||
}
|
||
|
||
|
||
int usbnet_host_bulk_only_send(usb_dev usb_id, u8 *buf, u32 len)
|
||
{
|
||
return usb_h_bulk_write(usb_id, wireless_dev[usb_id].host_epout, wireless_dev[usb_id].txmaxp, wireless_dev[usb_id].epout, buf, len);
|
||
}
|
||
|
||
|
||
int usbnet_host_at_data_send(usb_dev usb_id, u8 *buf, u32 len)
|
||
{
|
||
return usb_h_bulk_write(usb_id, wireless_dev[usb_id].host_epout_at, wireless_dev[usb_id].txmaxp_at, wireless_dev[usb_id].epout_at, buf, len);
|
||
}
|
||
|
||
|
||
void usbnet_set_rx_complete_cb(USBNET_RX_COMPLETE_CB cb)
|
||
{
|
||
usbnet_rx_complete_cb = cb;
|
||
}
|
||
|
||
int usbnet_get_rxmaxp(usb_dev usb_id)
|
||
{
|
||
return wireless_dev[usb_id].rxmaxp;
|
||
}
|
||
|
||
void usbnet_host_bulk_only_receive_int(usb_dev usb_id)
|
||
{
|
||
if (wireless_dev[usb_id].inbuf) {
|
||
//触发传输由上层决定,否则buffer会有改写的风险
|
||
usb_h_ep_read_async(usb_id, wireless_dev[usb_id].host_epin, wireless_dev[usb_id].epin, NULL, 0, USB_ENDPOINT_XFER_BULK, 1);
|
||
}
|
||
}
|
||
|
||
|
||
static void usbnet_rx_isr(struct usb_host_device *host_dev, u32 ep)
|
||
{
|
||
int rx_len = 0;
|
||
usb_dev usb_id = host_device2id(host_dev);
|
||
u32 target_ep = wireless_dev[usb_id].epin;
|
||
|
||
if (!wireless_dev[usb_id].inbuf) {
|
||
return;
|
||
}
|
||
|
||
rx_len = usb_h_ep_read_async(usb_id, ep, target_ep, wireless_dev[usb_id].inbuf, wireless_dev[usb_id].rxmaxp, USB_ENDPOINT_XFER_BULK, 0);
|
||
if (rx_len > 0 && usbnet_rx_complete_cb) {
|
||
usbnet_rx_complete_cb(usb_id, wireless_dev[usb_id].inbuf, rx_len);
|
||
}
|
||
if (rx_len == 0 || !usbnet_rx_complete_cb) {
|
||
usb_h_ep_read_async(usb_id, ep, target_ep, NULL, 0, USB_ENDPOINT_XFER_BULK, 1);
|
||
}
|
||
}
|
||
|
||
|
||
void usbnet_at_port_rx_handler_register(void (*func)(u8 *buf, u32 len))
|
||
{
|
||
usbnet_at_port_rx_handler = func;
|
||
}
|
||
|
||
|
||
static void usbnet_at_port_rx_isr(struct usb_host_device *host_dev, u32 ep)
|
||
{
|
||
int rx_len = 0;
|
||
usb_dev usb_id = host_device2id(host_dev);
|
||
u32 target_ep = wireless_dev[usb_id].epin_at;
|
||
u8 usb_recv_buf[64];
|
||
u8 usb_recv_len = sizeof(usb_recv_buf);
|
||
|
||
/* printf("usb%d lte rx hep %d %d tep %d \n", usb_id, ep, wireless_dev.host_epin_at, target_ep); */
|
||
rx_len = usb_h_ep_read_async(usb_id, ep, target_ep, usb_recv_buf, usb_recv_len, USB_ENDPOINT_XFER_BULK, 0);
|
||
if (rx_len > 0) {
|
||
if (usbnet_at_port_rx_handler) {
|
||
usbnet_at_port_rx_handler(usb_recv_buf, rx_len);
|
||
}
|
||
}
|
||
|
||
usb_h_ep_read_async(usb_id, ep, target_ep, NULL, 0, USB_ENDPOINT_XFER_BULK, 1);
|
||
}
|
||
|
||
|
||
s32 usbnet_at_port_parser(struct usb_host_device *host_dev, u8 interface_num, const u8 *pBuf)
|
||
{
|
||
s32 len = 0;
|
||
u8 rx_interval = 0;
|
||
const usb_dev usb_id = host_device2id(host_dev);
|
||
struct usb_endpoint_descriptor *end_desc = NULL;
|
||
struct usb_interface_descriptor *interface = NULL;
|
||
host_dev->interface_info[interface_num] = &at_port_inf[usb_id];
|
||
|
||
u32 cur_len;
|
||
u32 cur_type;
|
||
u32 cur_subtype;
|
||
u8 is_next_interface = 0;
|
||
u8 stage = 0;
|
||
const u8 cs_interface_1[] = {0x05, 0x24, 0x00, 0x10, 0x01};
|
||
const u8 cs_interface_2[] = {0x05, 0x24, 0x01, 0x00, 0x00};
|
||
const u8 cs_interface_3[] = {0x04, 0x24, 0x02, 0x02};
|
||
const u8 cs_interface_4[] = {0x05, 0x24, 0x06, 0x00, 0x00};
|
||
|
||
while (1) {
|
||
cur_len = pBuf[len + 0];
|
||
cur_type = pBuf[len + 1];
|
||
cur_subtype = pBuf[len + 2];
|
||
|
||
/* printf("cur_len = %x, cur_type = %x, cur_subtype = %x", cur_len, cur_type, cur_subtype); */
|
||
|
||
if (cur_type == USB_DT_INTERFACE_ASSOCIATION) {
|
||
if (is_next_interface) {
|
||
break;
|
||
}
|
||
/* put_buf(pBuf + len, cur_len); */
|
||
} else if (cur_type == USB_DT_INTERFACE) {
|
||
interface = (struct usb_interface_descriptor *)(pBuf + len);
|
||
if (interface->bLength != USB_DT_INTERFACE_SIZE) {
|
||
break;
|
||
}
|
||
if (is_next_interface) {
|
||
break;
|
||
}
|
||
is_next_interface = 1;
|
||
/* put_buf(pBuf + len, cur_len); */
|
||
} else if (cur_type == USB_DT_CS_INTERFACE) {
|
||
/* put_buf(pBuf + len, cur_len); */
|
||
switch (stage) {
|
||
case 0:
|
||
if (!memcmp(pBuf + len, cs_interface_1, sizeof(cs_interface_1))) {
|
||
stage = 1;
|
||
} else {
|
||
stage = 0;
|
||
}
|
||
break;
|
||
case 1:
|
||
if (!memcmp(pBuf + len, cs_interface_2, sizeof(cs_interface_2))) {
|
||
stage = 2;
|
||
} else {
|
||
stage = 0;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (!memcmp(pBuf + len, cs_interface_3, sizeof(cs_interface_3))) {
|
||
stage = 3;
|
||
} else {
|
||
stage = 0;
|
||
}
|
||
break;
|
||
case 3:
|
||
if (!memcmp(pBuf + len, cs_interface_4, sizeof(cs_interface_4))) {
|
||
stage = 4;
|
||
} else {
|
||
stage = 0;
|
||
}
|
||
break;
|
||
}
|
||
} else if (cur_type == USB_DT_ENDPOINT) {
|
||
/* put_buf(pBuf + len, cur_len); */
|
||
end_desc = (struct usb_endpoint_descriptor *)(pBuf + len);
|
||
if (stage != 4) { //过滤符合AT COM的CS INTERFACE
|
||
goto __parser_next;
|
||
}
|
||
|
||
if ((end_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
|
||
if (end_desc->bEndpointAddress & USB_DIR_IN) {
|
||
if (end_desc->bEndpointAddress != 0x84 && \
|
||
end_desc->bEndpointAddress != 0x86) { //4G模块有好几个接形式一样的interface-endpoint接口,固定取AT COM端点
|
||
goto __parser_next;
|
||
}
|
||
wireless_dev[usb_id].host_epin_at = usb_get_ep_num(usb_id, USB_DIR_IN, USB_ENDPOINT_XFER_BULK);
|
||
wireless_dev[usb_id].epin_at = end_desc->bEndpointAddress & 0x0f;
|
||
wireless_dev[usb_id].rxmaxp_at = end_desc->wMaxPacketSize;
|
||
rx_interval = end_desc->bInterval;
|
||
log_info("AT IN : id = %d, host_epin_at = %d, epin_at = %d, rxmaxp_at = %d", usb_id, wireless_dev[usb_id].host_epin_at, wireless_dev[usb_id].epin_at, wireless_dev[usb_id].rxmaxp_at);
|
||
if (usb_id == 0) {
|
||
wireless_dev[usb_id].rxmaxp_at = 64;
|
||
}
|
||
#if HUSB_MODE && (FUSB_MODE == 0)
|
||
if (wireless_dev[usb_id].rxmaxp_at > 512) {
|
||
wireless_dev[usb_id].rxmaxp_at = 512;
|
||
}
|
||
#endif
|
||
usb_h_set_ep_isr(host_dev, wireless_dev[usb_id].host_epin_at | USB_DIR_IN, usbnet_at_port_rx_isr, host_dev);
|
||
if (!wireless_dev[usb_id].epin_buf_at) {
|
||
wireless_dev[usb_id].epin_buf_at = usb_h_alloc_ep_buffer(usb_id, wireless_dev[usb_id].host_epin_at | USB_DIR_IN, wireless_dev[usb_id].rxmaxp_at * 2);
|
||
}
|
||
usb_write_rxfuncaddr(usb_id, wireless_dev[usb_id].host_epin_at, host_dev->private_data.devnum);
|
||
usb_h_ep_config(usb_id, wireless_dev[usb_id].host_epin_at | USB_DIR_IN, USB_ENDPOINT_XFER_BULK, 1, rx_interval, wireless_dev[usb_id].epin_buf_at, wireless_dev[usb_id].rxmaxp_at);
|
||
|
||
usb_h_ep_read_async(usb_id, wireless_dev[usb_id].host_epin_at, wireless_dev[usb_id].epin_at, NULL, 0, USB_ENDPOINT_XFER_BULK, 1);
|
||
} else {
|
||
if (end_desc->bEndpointAddress != 0x03 && \
|
||
end_desc->bEndpointAddress != 0x0F) { //4G模块有好几个接形式一样的interface-endpoint接口,固定取AT COM端点
|
||
goto __parser_next;
|
||
}
|
||
wireless_dev[usb_id].host_epout_at = usb_get_ep_num(usb_id, USB_DIR_OUT, USB_ENDPOINT_XFER_BULK);
|
||
wireless_dev[usb_id].epout_at = end_desc->bEndpointAddress & 0x0f;
|
||
wireless_dev[usb_id].txmaxp_at = end_desc->wMaxPacketSize;
|
||
log_info("AT OUT : id = %d, host_epout_at = %d, epout_at = %d, txmaxp_at = %d", usb_id, wireless_dev[usb_id].host_epout_at, wireless_dev[usb_id].epout_at, wireless_dev[usb_id].txmaxp_at);
|
||
if (usb_id == 0) {
|
||
wireless_dev[usb_id].txmaxp_at = 64;
|
||
}
|
||
#if HUSB_MODE && (FUSB_MODE == 0)
|
||
if (wireless_dev[usb_id].txmaxp_at > 512) {
|
||
wireless_dev[usb_id].txmaxp_at = 512;
|
||
}
|
||
#endif
|
||
if (!wireless_dev[usb_id].epout_buf_at) {
|
||
wireless_dev[usb_id].epout_buf_at = usb_h_alloc_ep_buffer(usb_id, wireless_dev[usb_id].host_epout_at | USB_DIR_OUT, wireless_dev[usb_id].txmaxp_at);
|
||
}
|
||
usb_write_txfuncaddr(usb_id, wireless_dev[usb_id].host_epout_at, host_dev->private_data.devnum);
|
||
usb_h_ep_config(usb_id, wireless_dev[usb_id].host_epout_at | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, 0, 0, wireless_dev[usb_id].epout_buf_at, wireless_dev[usb_id].txmaxp_at);
|
||
}
|
||
}
|
||
} else if (cur_type == USB_DT_CS_ENDPOINT) {
|
||
/* put_buf(pBuf + len, cur_len); */
|
||
} else {
|
||
break;
|
||
}
|
||
__parser_next:
|
||
len += cur_len;
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
|
||
s32 usbnet_generic_cdc_parser(struct usb_host_device *host_dev, u8 interface_num, const u8 *pBuf)
|
||
{
|
||
int len = 0;
|
||
u8 loop = 1;
|
||
u8 rx_interval = 0;
|
||
const usb_dev usb_id = host_device2id(host_dev);
|
||
host_dev->interface_info[interface_num] = &wireless_inf[usb_id];
|
||
struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)pBuf;
|
||
wireless_inf[usb_id].dev.wireless->parent = host_dev;
|
||
struct usb_endpoint_descriptor *end_desc = NULL;
|
||
const u8 *buf = pBuf;
|
||
|
||
if (interface->bInterfaceClass == USB_CLASS_CDC_DATA) {
|
||
log_info("usb_cdc_parser");
|
||
len += sizeof(struct usb_interface_descriptor);
|
||
goto __set_config;
|
||
}
|
||
|
||
len += sizeof(struct usb_interface_descriptor);
|
||
buf += sizeof(struct usb_interface_descriptor);
|
||
|
||
while (loop) {
|
||
if (buf[1] != USB_DT_CS_INTERFACE) {
|
||
if (buf[1] == USB_DT_INTERFACE_ASSOCIATION) { //下一个设备
|
||
loop = 0;
|
||
break;
|
||
}
|
||
goto next_desc;
|
||
}
|
||
/* use bDescriptorSubType to identify the CDC descriptors.
|
||
* We expect devices with CDC header and union descriptors.
|
||
* For CDC Ethernet we need the ethernet descriptor.
|
||
* For RNDIS, ignore two (pointless) CDC modem descriptors
|
||
* in favor of a complicated OID-based RPC scheme doing what
|
||
* CDC Ethernet achieves with a simple descriptor.
|
||
*/
|
||
switch (buf[2]) {
|
||
case USB_CDC_HEADER_TYPE:
|
||
break;
|
||
|
||
case USB_CDC_ACM_TYPE:
|
||
/* paranoia: disambiguate a "real" vendor-specific
|
||
* modem interface from an RNDIS non-modem.
|
||
*/
|
||
break;
|
||
|
||
case USB_CDC_UNION_TYPE:
|
||
len += buf [0]; /* bLength */
|
||
buf += buf [0];
|
||
if (buf[1] == USB_DT_ENDPOINT) {
|
||
len += USB_DT_ENDPOINT_SIZE;
|
||
len += USB_DT_INTERFACE_SIZE;
|
||
|
||
__set_config:
|
||
for (int endnum = 0; endnum < 2; endnum++) {
|
||
end_desc = (struct usb_endpoint_descriptor *)(pBuf + len);
|
||
|
||
if (end_desc->bDescriptorType != USB_DT_ENDPOINT ||
|
||
end_desc->bLength < USB_DT_ENDPOINT_SIZE) {
|
||
return -USB_DT_ENDPOINT;
|
||
}
|
||
len += USB_DT_ENDPOINT_SIZE;
|
||
|
||
if ((end_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
|
||
if (end_desc->bEndpointAddress & USB_DIR_IN) {
|
||
wireless_dev[usb_id].host_epin = usb_get_ep_num(usb_id, USB_DIR_IN, USB_ENDPOINT_XFER_BULK);
|
||
wireless_dev[usb_id].epin = end_desc->bEndpointAddress & 0x0f;
|
||
wireless_dev[usb_id].rxmaxp = end_desc->wMaxPacketSize;
|
||
if (usb_id == 0) {
|
||
wireless_dev[usb_id].rxmaxp = 64;
|
||
}
|
||
#if HUSB_MODE && (FUSB_MODE == 0)
|
||
if (wireless_dev[usb_id].rxmaxp > 512) {
|
||
wireless_dev[usb_id].rxmaxp = 512;
|
||
}
|
||
#endif
|
||
rx_interval = end_desc->bInterval;
|
||
log_info("CDC IN : id = %d, host_epin = %d, epin = %d, rxmaxp = %d", usb_id, wireless_dev[usb_id].host_epin, wireless_dev[usb_id].epin, wireless_dev[usb_id].rxmaxp);
|
||
} else {
|
||
wireless_dev[usb_id].host_epout = usb_get_ep_num(usb_id, USB_DIR_OUT, USB_ENDPOINT_XFER_BULK);
|
||
wireless_dev[usb_id].epout = end_desc->bEndpointAddress & 0x0f;
|
||
wireless_dev[usb_id].txmaxp = end_desc->wMaxPacketSize;
|
||
if (usb_id == 0) {
|
||
wireless_dev[usb_id].txmaxp = 64;
|
||
}
|
||
#if HUSB_MODE && (FUSB_MODE == 0)
|
||
if (wireless_dev[usb_id].txmaxp > 512) {
|
||
wireless_dev[usb_id].txmaxp = 512;
|
||
}
|
||
#endif
|
||
log_info("CDC OUT2 : id = %d, host_epout = %d, epout = %d, txmaxp = %d", usb_id, wireless_dev[usb_id].host_epout, wireless_dev[usb_id].epout, wireless_dev[usb_id].txmaxp);
|
||
}
|
||
}
|
||
}
|
||
|
||
usb_h_set_ep_isr(host_dev, wireless_dev[usb_id].host_epin | USB_DIR_IN, usbnet_rx_isr, host_dev);
|
||
if (!wireless_dev[usb_id].epin_buf) {
|
||
wireless_dev[usb_id].epin_buf = usb_h_alloc_ep_buffer(usb_id, wireless_dev[usb_id].host_epin | USB_DIR_IN, wireless_dev[usb_id].rxmaxp * 2);
|
||
}
|
||
if (!wireless_dev[usb_id].inbuf) {
|
||
wireless_dev[usb_id].inbuf = malloc(wireless_dev[usb_id].rxmaxp);
|
||
}
|
||
usb_write_rxfuncaddr(usb_id, wireless_dev[usb_id].host_epin, host_dev->private_data.devnum);
|
||
usb_h_ep_config(usb_id, wireless_dev[usb_id].host_epin | USB_DIR_IN, USB_ENDPOINT_XFER_BULK, 1, rx_interval, wireless_dev[usb_id].epin_buf, wireless_dev[usb_id].rxmaxp);
|
||
|
||
if (!wireless_dev[usb_id].epout_buf) {
|
||
wireless_dev[usb_id].epout_buf = usb_h_alloc_ep_buffer(usb_id, wireless_dev[usb_id].host_epout | USB_DIR_OUT, wireless_dev[usb_id].txmaxp);
|
||
}
|
||
usb_write_txfuncaddr(usb_id, wireless_dev[usb_id].host_epout, host_dev->private_data.devnum);
|
||
usb_h_ep_config(usb_id, wireless_dev[usb_id].host_epout | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK, 0, 0, wireless_dev[usb_id].epout_buf, wireless_dev[usb_id].txmaxp);
|
||
|
||
/* usb_h_ep_read_async(usb_id, wireless_dev[usb_id].host_epin, wireless_dev[usb_id].epin, NULL, 0, USB_ENDPOINT_XFER_BULK, 1); */
|
||
}
|
||
return len;
|
||
/* break; */
|
||
|
||
case USB_CDC_ETHERNET_TYPE:
|
||
break;
|
||
|
||
case USB_CDC_MDLM_TYPE:
|
||
break;
|
||
|
||
case USB_CDC_MDLM_DETAIL_TYPE:
|
||
break;
|
||
}
|
||
next_desc:
|
||
len += buf[0]; /* bLength */
|
||
buf += buf[0];
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
|
||
/*
|
||
* RPC done RNDIS-style. Caller guarantees:
|
||
* -rndis_command message is properly byteswapped
|
||
* - there's no other request pending
|
||
* - buf can hold up to 1KB response (required by RNDIS spec)
|
||
* On return, the first few entries are already byteswapped.
|
||
*
|
||
* Call context is likely probe(), before interface name is known,
|
||
* which is why we won't try to use it in the diagnostics.
|
||
*/
|
||
int rndis_command(struct usb_host_device *device, struct rndis_msg_hdr *buf, int buflen)
|
||
{
|
||
int master_ifnum;
|
||
int retval;
|
||
int partial;
|
||
unsigned count;
|
||
__le32 rsp;
|
||
static u32 xid = 0;
|
||
u32 msg_len, request_id;
|
||
|
||
/* REVISIT when this gets called from contexts other than probe() or
|
||
* disconnect(): either serialize, or dispatch responses on xid
|
||
*/
|
||
|
||
/* Issue the request; xid is unique, don't bother byteswapping it */
|
||
if (likely(buf->msg_type != RNDIS_MSG_HALT &&
|
||
buf->msg_type != RNDIS_MSG_RESET)) {
|
||
xid++;
|
||
if (!xid) {
|
||
xid++;
|
||
}
|
||
buf->request_id = (__le32) xid;
|
||
}
|
||
master_ifnum = 0;//info->control->cur_altsetting->desc.bInterfaceNumber;//控制接口描述符
|
||
retval = usb_control_msg(device,
|
||
USB_CDC_SEND_ENCAPSULATED_COMMAND,//0x00
|
||
USB_TYPE_CLASS | USB_RECIP_INTERFACE,//0x21
|
||
0, master_ifnum,
|
||
buf, le32_to_cpu(buf->msg_len)
|
||
);
|
||
if (unlikely(retval < 0 || xid == 0)) {
|
||
return retval;
|
||
}
|
||
/* Poll the control channel; the request probably completed immediately */
|
||
rsp = buf->msg_type | RNDIS_MSG_COMPLETION;
|
||
for (count = 0; count < 10; count++) {
|
||
memset(buf, 0, CONTROL_BUFFER_SIZE);
|
||
retval = usb_control_msg(device,
|
||
USB_CDC_GET_ENCAPSULATED_RESPONSE,
|
||
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||
0, master_ifnum,
|
||
buf, buflen);
|
||
|
||
if (likely(retval == 0)) {
|
||
msg_len = le32_to_cpu(buf->msg_len);
|
||
request_id = (u32) buf->request_id;
|
||
if (likely(buf->msg_type == rsp)) {
|
||
if (likely(request_id == xid)) {
|
||
/* log_info( */
|
||
/* "rndis reply status %08x\n", */
|
||
/* le32_to_cpu(buf->status)); */
|
||
if (unlikely(rsp == RNDIS_MSG_RESET_C)) {
|
||
return 0;
|
||
} else if (unlikely(rsp == RNDIS_MSG_KEEPALIVE_C)) {
|
||
log_info("RNDIS_MSG_KEEPALIVE_C");
|
||
return 0;
|
||
}
|
||
|
||
if (likely(RNDIS_STATUS_SUCCESS == buf->status)) {
|
||
return 0;
|
||
}
|
||
|
||
return -EL3RST;
|
||
}
|
||
log_info(
|
||
"rndis reply id %d expected %d",
|
||
request_id, xid);
|
||
/* then likely retry */
|
||
} else switch (buf->msg_type) {
|
||
case RNDIS_MSG_INDICATE: /* fault/event */
|
||
//rndis_msg_indicate(dev, (void *)buf, buflen);
|
||
log_info("RNDIS_MSG_INDICATE");
|
||
/* put_buf(buf, buflen); */
|
||
break;
|
||
case RNDIS_MSG_KEEPALIVE: { /* ping */
|
||
struct rndis_keepalive_c *msg = (void *)buf;
|
||
|
||
msg->msg_type = RNDIS_MSG_KEEPALIVE_C;
|
||
msg->msg_len = cpu_to_le32(sizeof * msg);
|
||
msg->status = RNDIS_STATUS_SUCCESS;
|
||
retval = usb_control_msg(device,
|
||
USB_CDC_SEND_ENCAPSULATED_COMMAND,
|
||
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||
0, master_ifnum,
|
||
msg, sizeof * msg
|
||
);
|
||
if (unlikely(retval < 0))
|
||
log_info(
|
||
"rndis keepalive err %d",
|
||
retval);
|
||
}
|
||
break;
|
||
default:
|
||
log_info(
|
||
"unexpected rndis msg %08x len %d",
|
||
le32_to_cpu(buf->msg_type), msg_len);
|
||
break;
|
||
}
|
||
} else {
|
||
/* device probably issued a protocol stall; ignore */
|
||
log_error("rndis response error, code %d\n", retval);
|
||
}
|
||
msleep(20);
|
||
}
|
||
log_error("rndis response timeout\n");
|
||
return -ETIMEDOUT;
|
||
}
|
||
|
||
|
||
/*
|
||
* rndis_query:
|
||
*
|
||
* Performs a query for @oid along with 0 or more bytes of payload as
|
||
* specified by @in_len. If @reply_len is not set to -1 then the reply
|
||
* length is checked against this value, resulting in an error if it
|
||
* doesn't match.
|
||
*
|
||
* NOTE: Adding a payload exactly or greater than the size of the expected
|
||
* response payload is an evident requirement MSFT added for ActiveSync.
|
||
*
|
||
* The only exception is for OIDs that return a variably sized response,
|
||
* in which case no payload should be added. This undocumented (and
|
||
* nonsensical!) issue was found by sniffing protocol requests from the
|
||
* ActiveSync 4.1 Windows driver.
|
||
*/
|
||
static int rndis_query(struct usb_host_device *device,
|
||
void *buf, __le32 oid, u32 in_len,
|
||
void **reply, int *reply_len)
|
||
{
|
||
int retval;
|
||
union {
|
||
void *buf;
|
||
struct rndis_msg_hdr *header;
|
||
struct rndis_query *get;
|
||
struct rndis_query_c *get_c;
|
||
} u;
|
||
u32 off, len;
|
||
|
||
u.buf = buf;
|
||
|
||
memset(u.get, 0, sizeof * u.get + in_len);
|
||
u.get->msg_type = RNDIS_MSG_QUERY;
|
||
u.get->msg_len = cpu_to_le32(sizeof * u.get + in_len);
|
||
u.get->oid = oid;
|
||
u.get->len = cpu_to_le32(in_len);
|
||
u.get->offset = cpu_to_le32(20);
|
||
|
||
/* put_buf((u8*)u.header,u.header->msg_len); */
|
||
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
if (unlikely(retval < 0)) {
|
||
log_error("RNDIS_MSG_QUERY(0x%08x) failed, %d\n",
|
||
oid, retval);
|
||
return retval;
|
||
}
|
||
|
||
off = le32_to_cpu(u.get_c->offset);
|
||
len = le32_to_cpu(u.get_c->len);
|
||
|
||
if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE)) {
|
||
goto response_error;
|
||
}
|
||
|
||
if (*reply_len != -1 && len != *reply_len) {
|
||
goto response_error;
|
||
}
|
||
|
||
*reply = (unsigned char *) &u.get_c->request_id + off;
|
||
*reply_len = len;
|
||
|
||
return retval;
|
||
|
||
response_error:
|
||
log_error("RNDIS_MSG_QUERY(0x%08x) "
|
||
"invalid response - off %d len %d\n",
|
||
oid, off, len);
|
||
return -EDOM;
|
||
}
|
||
|
||
|
||
int generic_rndis_bind(struct usb_host_device *device, int flags)
|
||
{
|
||
int retval;
|
||
|
||
union {
|
||
void *buf;
|
||
struct rndis_msg_hdr *header;
|
||
struct rndis_init *init;
|
||
struct rndis_init_c *init_c;
|
||
struct rndis_query *get;
|
||
struct rndis_query_c *get_c;
|
||
struct rndis_set *set;
|
||
struct rndis_set_c *set_c;
|
||
struct rndis_halt *halt;
|
||
struct rndis_keepalive *alive;
|
||
struct rndis_keepalive_c *alive_c;
|
||
} u;
|
||
u32 tmp;
|
||
__le32 phym_unspec, *phym, *conn_status = NULL;
|
||
int reply_len;
|
||
unsigned char *bp;
|
||
|
||
/* we can't rely on i/o from stack working, or stack allocation */
|
||
u.buf = malloc(CONTROL_BUFFER_SIZE);
|
||
if (!u.buf) {
|
||
return -ENOMEM;
|
||
}
|
||
|
||
//retval = usbnet_generic_cdc_bind(dev, intf);
|
||
|
||
u.init->msg_type = RNDIS_MSG_INIT;
|
||
u.init->msg_len = cpu_to_le32(sizeof * u.init);
|
||
u.init->major_version = cpu_to_le32(1);
|
||
u.init->minor_version = cpu_to_le32(0);
|
||
u.init->max_transfer_size = 0x4000;
|
||
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
|
||
if (unlikely(retval < 0)) {
|
||
/* it might not even be an RNDIS device!! */
|
||
log_error("RNDIS init failed, %d\n", retval);
|
||
goto fail_and_release;
|
||
}
|
||
log_info("RNDIS_MSG_INIT_C:\r\n \
|
||
status = %d\r\n \
|
||
major_version = %d\r\n \
|
||
minor_version = %d\r\n \
|
||
device_flags = %d\r\n \
|
||
medium = %d\r\n \
|
||
max_packets_per_message = %d\r\n \
|
||
max_transfer_size = %d\r\n \
|
||
packet_alignment = %d\r\n \
|
||
af_list_offset = %d\r\n \
|
||
af_list_size = %d\n", \
|
||
u.init_c->status, u.init_c->major_version, u.init_c->minor_version, u.init_c->device_flags, u.init_c->medium, \
|
||
u.init_c->max_packets_per_message, u.init_c->max_transfer_size, u.init_c->packet_alignment, \
|
||
u.init_c->af_list_offset, u.init_c->af_list_size);
|
||
|
||
tmp = le32_to_cpu(u.init_c->max_transfer_size);
|
||
#if 0
|
||
if (tmp < dev->hard_mtu) {
|
||
if (tmp <= net->hard_header_len) {
|
||
log_info(
|
||
"dev can't take %u byte packets (max %u)\n",
|
||
dev->hard_mtu, tmp);
|
||
retval = -EINVAL;
|
||
goto halt_fail_and_release;
|
||
}
|
||
dev_warn(
|
||
"dev can't take %u byte packets (max %u), "
|
||
"adjusting MTU to %u\n",
|
||
dev->hard_mtu, tmp, tmp - net->hard_header_len);
|
||
dev->hard_mtu = tmp;
|
||
net->mtu = dev->hard_mtu - net->hard_header_len;
|
||
}
|
||
|
||
/* REVISIT: peripheral "alignment" request is ignored ... */
|
||
log_info(
|
||
"hard mtu %u (%u from dev), rx buflen %Zu, align %d\n",
|
||
dev->hard_mtu, tmp, dev->rx_urb_size,
|
||
1 << le32_to_cpu(u.init_c->packet_alignment));
|
||
|
||
|
||
|
||
/* module has some device initialization code needs to be done right
|
||
* after RNDIS_INIT */
|
||
if (dev->driver_info->early_init &&
|
||
dev->driver_info->early_init(dev) != 0) {
|
||
goto halt_fail_and_release;
|
||
}
|
||
|
||
/* Check physical medium */
|
||
|
||
#endif
|
||
phym = NULL;
|
||
reply_len = sizeof * phym;
|
||
retval = rndis_query(device, u.buf, OID_GEN_PHYSICAL_MEDIUM,
|
||
0, (void **) &phym, &reply_len);
|
||
|
||
log_info("QUERY->OID_GEN_PHYSICAL_MEDIUM, retval = %d, phym = %x", retval, *phym);
|
||
|
||
if (retval != 0 || !phym) {
|
||
/* OID is optional so don't fail here. */
|
||
phym_unspec = RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED;
|
||
phym = &phym_unspec;
|
||
}
|
||
if ((flags & FLAG_RNDIS_PHYM_WIRELESS) &&
|
||
*phym != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
|
||
log_error("driver requires wireless physical medium, but device is not\n");
|
||
retval = -ENODEV;
|
||
goto halt_fail_and_release;
|
||
}
|
||
if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) &&
|
||
*phym == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) {
|
||
log_error("driver requires non-wireless physical medium, but device is wireless\n");
|
||
retval = -ENODEV;
|
||
goto halt_fail_and_release;
|
||
}
|
||
|
||
reply_len = sizeof * phym;
|
||
retval = rndis_query(device, u.buf, OID_GEN_MEDIA_CONNECT_STATUS,
|
||
0, (void **)&conn_status, &reply_len);
|
||
log_info("QUERY->OID_GEN_MEDIA_CONNECT_STATUS, retval = %d, conn_status = %x", retval, *conn_status);
|
||
|
||
|
||
reply_len = ETH_ALEN;
|
||
retval = rndis_query(device, u.buf, OID_802_3_CURRENT_ADDRESS,
|
||
48, (void **) &bp, &reply_len);
|
||
|
||
log_info("QUERY->OID_802_3_CURRENT_ADDRESS, retval = %d: \
|
||
addr = [%X:%X:%X:%X:%X:%X]", retval, bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]);
|
||
|
||
/* Get designated host ethernet address */
|
||
reply_len = ETH_ALEN;
|
||
retval = rndis_query(device, u.buf, OID_802_3_PERMANENT_ADDRESS,
|
||
48, (void **) &bp, &reply_len);
|
||
|
||
if (unlikely(retval < 0)) {
|
||
log_error("rndis get ethaddr, %d\n", retval);
|
||
goto halt_fail_and_release;
|
||
}
|
||
log_info("QUERY->OID_802_3_PERMANENT_ADDRESS: \
|
||
addr = [%X:%X:%X:%X:%X:%X]", bp[0], bp[1], bp[2], bp[3], bp[4], bp[5]);
|
||
copy_usbnet_mac_addr(bp);
|
||
|
||
#if 0
|
||
memcpy(net->dev_addr, bp, ETH_ALEN);
|
||
memcpy(net->perm_addr, bp, ETH_ALEN);
|
||
#endif
|
||
|
||
/* set a nonzero filter to enable data transfers */
|
||
#if 0
|
||
memset(u.set, 0, sizeof * u.set);
|
||
u.set->msg_type = RNDIS_MSG_SET;
|
||
u.set->msg_len = cpu_to_le32(4 + sizeof * u.set);
|
||
u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
|
||
u.set->len = cpu_to_le32(4);
|
||
u.set->offset = cpu_to_le32((sizeof * u.set) - 8);
|
||
*(__le32 *)(u.buf + sizeof * u.set) = RNDIS_DEFAULT_FILTER;
|
||
|
||
|
||
/* put_buf((u8*)u.header,u.header->msg_len); */
|
||
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("SET->RNDIS_DEFAULT_FILTER:\r\n \
|
||
status = %d\n", u.set_c->status);
|
||
|
||
if (unlikely(retval < 0)) {
|
||
log_info("rndis set packet filter, %d\n", retval);
|
||
goto halt_fail_and_release;
|
||
}
|
||
|
||
#else
|
||
////////////////////////////////////////////////////////
|
||
memset(u.set, 0, sizeof * u.set);
|
||
u.set->msg_type = RNDIS_MSG_SET;
|
||
u.set->msg_len = cpu_to_le32(4 + sizeof * u.set);
|
||
u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
|
||
u.set->len = cpu_to_le32(4);
|
||
u.set->offset = cpu_to_le32((sizeof * u.set) - 8);
|
||
*(__le32 *)(u.buf + sizeof * u.set) = cpu_to_le32(0x03);
|
||
/* put_buf(u.set, 4 + sizeof * u.set); */
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("SET->RNDIS_DEFAULT_FILTER, 0x03: retval = %d", retval);
|
||
|
||
u8 val[6] = {0x33, 0x33, 0x00, 0x00, 0x00, 0x01};
|
||
memset(u.set, 0, sizeof * u.set);
|
||
u.set->msg_type = RNDIS_MSG_SET;
|
||
u.set->msg_len = cpu_to_le32(6 + sizeof * u.set);
|
||
u.set->oid = OID_802_3_MULTICAST_LIST;
|
||
u.set->len = cpu_to_le32(6);
|
||
u.set->offset = cpu_to_le32((sizeof * u.set) - 8);
|
||
memcpy((__le32 *)(u.buf + sizeof * u.set), val, 6);
|
||
/* put_buf(u.set, 6 + sizeof * u.set); */
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("SET->OID_802_3_MULTICAST_LIST: retval = %d", retval);
|
||
|
||
memset(u.set, 0, sizeof * u.set);
|
||
u.set->msg_type = RNDIS_MSG_SET;
|
||
u.set->msg_len = cpu_to_le32(4 + sizeof * u.set);
|
||
u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
|
||
u.set->len = cpu_to_le32(4);
|
||
u.set->offset = cpu_to_le32((sizeof * u.set) - 8);
|
||
*(__le32 *)(u.buf + sizeof * u.set) = cpu_to_le32(0x07);
|
||
/* put_buf(u.set, 4 + sizeof * u.set); */
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("SET->RNDIS_DEFAULT_FILTER, 0x07: retval = %d", retval);
|
||
|
||
memset(u.set, 0, sizeof * u.set);
|
||
u.set->msg_type = RNDIS_MSG_SET;
|
||
u.set->msg_len = cpu_to_le32(4 + sizeof * u.set);
|
||
u.set->oid = OID_GEN_CURRENT_PACKET_FILTER;
|
||
u.set->len = cpu_to_le32(4);
|
||
u.set->offset = cpu_to_le32((sizeof * u.set) - 8);
|
||
*(__le32 *)(u.buf + sizeof * u.set) = cpu_to_le32(0x0f);
|
||
/* put_buf(u.set, 4 + sizeof * u.set); */
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("SET->RNDIS_DEFAULT_FILTER, 0x0f: retval = %d", retval);
|
||
#endif
|
||
|
||
memset(u.alive, 0, sizeof * u.alive);
|
||
u.alive->msg_type = RNDIS_MSG_KEEPALIVE;
|
||
u.alive->msg_len = cpu_to_le32(sizeof * u.alive);
|
||
retval = rndis_command(device, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("SET->RNDIS_MSG_KEEPALIVE, retval = %d", retval);
|
||
|
||
/* thread_fork("keepalive_period_task", 6, 512, 0, NULL, keepalive_period_task, NULL); */
|
||
////////////////////////////////////////////////////////
|
||
|
||
retval = 0;
|
||
|
||
free(u.buf);
|
||
|
||
return retval;
|
||
|
||
halt_fail_and_release:
|
||
memset(u.halt, 0, sizeof * u.halt);
|
||
u.halt->msg_type = RNDIS_MSG_HALT;
|
||
u.halt->msg_len = cpu_to_le32(sizeof * u.halt);
|
||
|
||
(void) rndis_command(device, (void *)u.halt, CONTROL_BUFFER_SIZE);
|
||
fail_and_release:
|
||
/* usb_set_intfdata(info->data, NULL); */
|
||
/* usb_driver_release_interface(driver_of(intf), info->data); */
|
||
/* info->data = NULL; */
|
||
fail:
|
||
free(u.buf);
|
||
return retval;
|
||
}
|
||
|
||
#if 0
|
||
static void keepalive_period_task(void *priv)
|
||
{
|
||
int retval = 0;
|
||
struct usb_host_device *host_dev = host_id2device(0);
|
||
|
||
union {
|
||
void *buf;
|
||
struct rndis_msg_hdr *header;
|
||
struct rndis_keepalive *alive;
|
||
struct rndis_keepalive_c *alive_c;
|
||
} u;
|
||
|
||
u.buf = malloc(CONTROL_BUFFER_SIZE);
|
||
|
||
for (;;) {
|
||
memset(u.alive, 0, sizeof * u.alive);
|
||
u.alive->msg_type = RNDIS_MSG_KEEPALIVE;
|
||
u.alive->msg_len = cpu_to_le32(sizeof * u.alive);
|
||
retval = rndis_command(host_dev, u.header, CONTROL_BUFFER_SIZE);
|
||
log_info("%s, retval = %d\n", __FUNCTION__, retval);
|
||
os_time_dly(100);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#endif
|
||
|