Files

473 lines
18 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <rtthread.h>
#include <include/rttusb_host.h>
#include "zxinfo.h"
#include "cdc.h"
#include "rndis.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/netdb.h"
#include "lwip/sys.h"
#include "lwip/ip_addr.h"
#include "lwip/tcpip.h"
#include "netif/ethernetif.h"
#include "lib/common/sysevt.h"
#include "syscfg.h"
#ifdef RT_USBH_VENDOR_ZXINFO
// #define USB_VENDOR_ID_ZXINFO 0x3361
// #define USB_PRODUCT_ID_ZXINFO 0x7B6E // ZX800
#define ZXINFO_ATCMD_BUFF_SIZE 128
static char recv_str[ZXINFO_ATCMD_BUFF_SIZE];
static struct uclass_driver zxinfo_driver;
__weak void mifi_led_control(rt_int16_t rsrq, rt_int16_t rssi){};
static rt_bool_t send_recv_atcmd_check(void *context, const char *send_str,
const char *check_str, char *ret_str)
{
struct usb_zxinfo_at *zxinfo_at = context;
uinst_t device = zxinfo_at->device;
int recv_size;
char *recv_str = NULL;
rt_bool_t pass = RT_FALSE;
os_sprintf((char *)zxinfo_at->at_cmd_buff, send_str);
rt_usb_hcd_pipe_xfer(device->hcd, zxinfo_at->pipe_out,
zxinfo_at->at_cmd_buff, os_strlen(send_str), 0);
os_sleep_ms(10); // 需要点延迟给LTE模组反应
do {
// 每次读取会清除缓存
os_memset(zxinfo_at->at_cmd_buff, 0, ZXINFO_ATCMD_BUFF_SIZE);
recv_size = rt_usb_hcd_pipe_xfer(device->hcd, zxinfo_at->pipe_in,
zxinfo_at->at_cmd_buff, ZXINFO_ATCMD_BUFF_SIZE, 10);
if (recv_size > 0) {
recv_str = os_strstr(zxinfo_at->at_cmd_buff, check_str);
if (recv_str != NULL) {
pass = RT_TRUE;
if (ret_str != NULL && recv_size > os_strlen(check_str)) {
// ret_str存在说明需要获取返回结果做额外判断复制到函数外面
os_memcpy(ret_str, recv_str + os_strlen(check_str),
recv_size - os_strlen(check_str));
ret_str[recv_size - os_strlen(check_str)] = '\0'; // 字符串结束符
}
}
}
} while (recv_size > 0);
return pass;
}
// 计算下行频点FDD不知道上行频点号猜测直接偏固定频率
static rt_uint16_t eutra_channel_freq_mapping(rt_uint8_t band, rt_uint16_t earfcn, rt_bool_t uplink)
{
rt_uint16_t freq = 0;
switch (band) {
// FDD上下行频点有偏差
case 3:
freq = 1805 + (earfcn - 1200) / 10;
if (uplink)
freq -= 95;
break;
case 5:
freq = 869 + (earfcn - 2400) / 10;
if (uplink)
freq -= 45;
break;
case 8:
freq = 925 + (earfcn - 3450) / 10;
if (uplink)
freq -= 45;
break;
// TDD上下行使用相同频点
case 34: freq = 2010 + (earfcn - 36200) / 10; break;
case 38: freq = 2570 + (earfcn - 37750) / 10; break;
case 39: freq = 1880 + (earfcn - 38250) / 10; break;
case 40: freq = 2300 + (earfcn - 38650) / 10; break;
case 41: freq = 2496 + (earfcn - 39650) / 10; break;
default: break;
}
return freq;
}
static void zxinfo_network_info(void *context, char *recv_str)
{
struct usb_zxinfo_at *zxinfo_at = context;
//char *argv[4];
char **argv = NULL;
int argc = 0;
rt_int16_t rssi, ber;
rt_uint16_t num_dl;
rt_uint16_t freq_dl, freq_ul;
rt_uint16_t freq_wifi = 0;
rt_uint8_t band = 0, rxqual = 0;
argv = os_malloc(4 * sizeof(char *));
if (argv == NULL) {
return;
}
// 获取运营商
if (send_recv_atcmd_check(zxinfo_at, "AT+COPS?\r\n", "+COPS:", recv_str) == RT_TRUE) {
argc = os_strtok(recv_str, ",", argv, 4);
if (argc >= 3) {
// 0,0,"CHINA MOBILE",7
_os_printf("%s\r\n", argv[2]);
}
}
// 获取网络信息
if (send_recv_atcmd_check(zxinfo_at, "AT+QNWINFO\r\n", "+QNWINFO:", recv_str) == RT_TRUE) {
argc = os_strtok(recv_str, ",", argv, 4);
if (argc >= 4) {
// "FDD LTE",46001,"LTE BAND 3",1650
band = os_atoi(argv[2] + 10);
num_dl = os_atoi(argv[3]);
freq_dl = eutra_channel_freq_mapping(band, num_dl, 0);
freq_ul = eutra_channel_freq_mapping(band, num_dl, 1);
_os_printf("BAND: %d\r\n", band);
_os_printf("F_dl: %d MHz, F_ul: %d MHz\r\n", freq_dl, freq_ul);
// 检查有无和wifi频点冲突
freq_wifi = sys_status.channel * 5 + 2407;
// TDD同频ul和dl一致
if (band == 40) {
if ((freq_wifi - freq_dl < 90) && sys_status.channel != 13) {
os_printf("!!!!!!! OVERLAP at %d and %d !!!!!!!\r\n", freq_wifi, freq_dl);
SYSEVT_NEW_LTE_EVT(SYSEVT_LTE_OVERLAP_WIFI, 13); // 切去高信道
}
} else if (band == 41) {
if ((freq_dl - freq_wifi < 90) && sys_status.channel != 1) {
os_printf("!!!!!!! OVERLAP at %d and %d !!!!!!!\r\n", freq_wifi, freq_dl);
SYSEVT_NEW_LTE_EVT(SYSEVT_LTE_OVERLAP_WIFI, 1); // 切去低信道
}
}
}
}
if (send_recv_atcmd_check(zxinfo_at, "AT+CSQ\r\n", "+CSQ:", recv_str) == RT_TRUE) {
argc = os_strtok(recv_str, ",", argv, 4);
if (argc >= 2) {
// 28,99
rssi = os_atoi(argv[0]); // dBm
rxqual = os_atoi(argv[1]);
mifi_led_control(0, -113 + rssi * 2);
if (rssi == 99) {
_os_printf("RSSI: unknow\r\n");
} else if (rssi <= 0) {
_os_printf("RSSI: <= -113 dBm\r\n");
} else if (rssi >= 31) {
_os_printf("RSSI: >= -51 dBm\r\n");
} else {
_os_printf("RSSI: %d dBm\r\n", -113 + rssi * 2);
}
// RXQUAL_0 BER < 0,2 % Assumed value = 0,14 %
// RXQUAL_1 0,2 % < BER < 0,4 % Assumed value = 0,28 %
// RXQUAL_2 0,4 % < BER < 0,8 % Assumed value = 0,57 %
// RXQUAL_3 0,8 % < BER < 1,6 % Assumed value = 1,13 %
// RXQUAL_4 1,6 % < BER < 3,2 % Assumed value = 2,26 %
// RXQUAL_5 3,2 % < BER < 6,4 % Assumed value = 4,53 %
// RXQUAL_6 6,4 % < BER < 12,8 % Assumed value = 9,05 %
// RXQUAL_7 12,8 % < BER Assumed value = 18,10 %
ber = 1 << (rxqual + 1);
if (rxqual == 99) {
_os_printf("BER: unknow\r\n");
} else if (rxqual <= 0) {
_os_printf("BER: < 0.2 %%\r\n");
} else if (rxqual >= 7) {
_os_printf("BER: > 12.8 %%\r\n");
} else {
_os_printf("BER: %d.%d %% ~ %d.%d %%\r\n", (ber/2)/10, (ber/2)%10, ber/10, ber%10);
}
}
}
os_free(argv);
}
static void zxinfo_at_recv(void *context)
{
struct usb_zxinfo_at *zxinfo_at = context;
while (1) {
if (zxinfo_at->retry > 10) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_UNKNOW;
}
// os_printf("recv state:%d\r\n", zxinfo_at->state);
switch (zxinfo_at->state) {
case ZXINFO_STATE_UNKNOW:
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CHECK_AT_STATUS;
break;
case ZXINFO_STATE_CHECK_AT_STATUS:
// AT查询模块是否工作启动初始化流程
if (send_recv_atcmd_check(zxinfo_at, "AT\r\n", "OK", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CHECK_SIM_STATUS;
os_printf("AT ready\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_CHECK_SIM_STATUS:
if (send_recv_atcmd_check(zxinfo_at, "AT+CPIN?\r\n", "+CPIN: READY", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CHECK_CS_STATUS;
os_printf("SIM ready\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_CHECK_CS_STATUS:
// AT+CREG查询CS域网络注册状态
if (send_recv_atcmd_check(zxinfo_at, "AT+CREG?\r\n", "+CREG: 0,1", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CHECK_PS_STATUS;
os_printf("PS ready\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_CHECK_PS_STATUS:
// AT+CEREG查询EPS域网络注册状态
if (send_recv_atcmd_check(zxinfo_at, "AT+CEREG?\r\n", "+CEREG: 0,1", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_INITIALIZED;
SYSEVT_NEW_LTE_EVT(SYSEVT_LTE_CONNECTED, 0);
os_printf("CS ready\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_CONFIG_PDP_CONTEXT:
// 设置band优先级排除band 40/41
send_recv_atcmd_check(zxinfo_at, "AT+QCFG=\"band\",0x0,0x6200000095\r\n", "+QCFG: \"band\",", NULL);
// AT+QICSGP设置场景参数
if (send_recv_atcmd_check(zxinfo_at, "AT+QICSGP=1,1,\"UNINET\",\"\",\"\",1\r\n", "OK", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_ACTIVE_PDP_CONTEXT;
os_printf("PDP config\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_ACTIVE_PDP_CONTEXT:
// AT+QIACT激活PDP场景
if (send_recv_atcmd_check(zxinfo_at, "AT+QIACT=1\r\n", "OK", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CHECK_IP_STATUS;
os_printf("PDP ready\r\n");
} else {
zxinfo_at->retry++;
if (zxinfo_at->retry > 3) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_DEACTIVE_PDP_CONTEXT;
}
os_sleep(1);
}
break;
case ZXINFO_STATE_CHECK_IP_STATUS:
// AT+QIACT查询激活PDP场景和IP
if (send_recv_atcmd_check(zxinfo_at, "AT+QIACT?\r\n", "+QIACT: 1,1,1,", recv_str) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CONNECT_USB_ADAPTER;
os_printf("Get IP: %s", recv_str);
os_printf("PDP actived\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_CONNECT_USB_ADAPTER:
// AT+QNETDEVCTL连接USB网卡
if (send_recv_atcmd_check(zxinfo_at, "AT+QNETDEVCTL=1,1,1\r\n", "OK", recv_str) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_INITIALIZED;
SYSEVT_NEW_LTE_EVT(SYSEVT_LTE_CONNECTED, 0);
os_printf("network conneted\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_INITIALIZED:
// 定时10s不知道干什么好呢
zxinfo_network_info(zxinfo_at, recv_str);
os_sleep(10);
break;
case ZXINFO_STATE_DEACTIVE_PDP_CONTEXT:
// 失败后反激活模组
if (send_recv_atcmd_check(zxinfo_at, "AT+QIDEACT=1\r\n", "OK", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
zxinfo_at->state = ZXINFO_STATE_CHECK_SIM_STATUS;
os_printf("PDP deactived\r\n");
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
case ZXINFO_STATE_POWERDOWN:
if (send_recv_atcmd_check(zxinfo_at, "AT+QPOWD\r\n", "POWERED DOWN", NULL) == RT_TRUE) {
zxinfo_at->retry = 0;
os_printf("LTE power down\r\n");
os_sleep(2);
mcu_reset();
} else {
zxinfo_at->retry++;
os_sleep(1);
}
break;
default:
break;
}
}
os_printf("at task end\r\n");
}
static rt_err_t rt_usbh_zxinfo_enable(void *arg)
{
rt_err_t ret = RET_OK;
struct uhintf **intf = arg;
uhcd_t hcd = NULL;
uep_desc_t ep_desc = NULL;
struct ustring_descriptor str_desc __attribute__((aligned(4)));
struct usb_zxinfo_at *zxinfo_at = NULL;
struct usb_cdc_line_coding line_coding;
upipe_t pipe;
rt_uint8_t ep_index, i;
if (intf[0] == NULL) {
return -EIO;
}
hcd = intf[0]->device->hcd;
os_printf("subclass %d, protocal %d\r\n",
intf[0]->intf_desc->bInterfaceSubClass,
intf[0]->intf_desc->bInterfaceProtocol);
if (intf[0]->intf_desc->bInterfaceSubClass == 2 && intf[0]->intf_desc->bInterfaceProtocol == 1) {
// 中云信安的AT口也是走复合设备描述符的
if (intf[0]->device->dev_desc.idProduct != USB_PRODUCT_ID_ZXINFO) {
return RET_ERR;
}
// 中云信安有多个复合设备第二个复合设备是AT串口算起来接口号是5但索引是用复合设备前面的那个所以是4
if (intf[0]->intf_desc->bInterfaceNumber != 4) {
return RET_ERR;
}
// 通过字符描述符判断哪个是主调试串口
ret = rt_usbh_get_string_descriptor(intf[0]->device, intf[0]->intf_desc->iInterface,
&str_desc, sizeof(struct ustring_descriptor));
if (ret != RET_OK) {
os_printf("no string\r\n");
return ret;
}
for (i = 0; i < str_desc.bLength; i += 2) { // 暂时没支持UNICODE的打印默认ASCII可以隔一个打印
_os_printf("%c", str_desc.String[i / 2]);
}
_os_printf("\r\n");
os_printf("zxinfo_at\r\n");
zxinfo_at = (struct usb_zxinfo_at *)os_zalloc(sizeof(struct usb_zxinfo_at));
if (zxinfo_at == NULL) {
os_printf("zxinfo at alloc fail\r\n");
return -ENOMEM;
}
// 按照命令预期回复长度对齐预留长度
zxinfo_at->at_cmd_buff = os_malloc(ZXINFO_ATCMD_BUFF_SIZE);
if (zxinfo_at->at_cmd_buff == NULL) {
os_printf("at_cmd_buff alloc fail\r\n");
os_free(zxinfo_at);
return -ENOMEM;
}
zxinfo_at->device = intf[0]->device;
intf[0]->user_data = zxinfo_at;
// 顺便注册devid
dev_register(HG_USB_AT_DEVID, (struct dev_obj *)zxinfo_at);
for (ep_index = 0; ep_index < intf[1]->intf_desc->bNumEndpoints; ++ep_index) {
rt_usbh_get_endpoint_descriptor(intf[1]->intf_desc, ep_index, &ep_desc);
if (ep_desc == NULL) {
os_printf("no ep_desc\r\n");
return RET_ERR;
}
if ((ep_desc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK)
continue;
if (rt_usb_hcd_alloc_pipe(intf[0]->device->hcd, &pipe, intf[0]->device, ep_desc) != RT_EOK) {
rt_kprintf("alloc pipe failed\n");
return -RT_ERROR;
}
rt_usb_instance_add_pipe(intf[0]->device, pipe);
if ((ep_desc->bEndpointAddress & USB_DIR_MASK) == USB_DIR_IN) {
zxinfo_at->pipe_in = pipe;
} else {
zxinfo_at->pipe_out = pipe;
}
}
// 获取串口参数
os_memset(&line_coding, 0, sizeof(line_coding));
rt_usbh_cdc_get_line_coding(intf[0]->device, intf[0]->intf_desc->bInterfaceNumber, &line_coding);
os_printf("Serial: %d %d %d %d\r\n",
line_coding.dwDTERate, line_coding.bCharFormat, line_coding.bParityType, line_coding.bDataBits);
rt_thread_init(&zxinfo_at->recv_task, "at_recv", zxinfo_at_recv, zxinfo_at,
NULL, 512, OS_TASK_PRIORITY_BELOW_NORMAL, 20);
rt_thread_startup(&zxinfo_at->recv_task);
}
return ret;
}
static rt_err_t rt_usbh_zxinfo_disable(void *arg)
{
struct uhintf *intf = arg;
uhcd_t hcd = NULL;
struct usb_zxinfo_at *zxinfo_at = NULL;
if (intf == NULL) {
return -EIO;
}
zxinfo_at = intf->user_data;
if (zxinfo_at) {
hcd = intf->device->hcd;
dev_unregister((struct dev_obj *)zxinfo_at);
rt_thread_detach(&zxinfo_at->recv_task);
os_free(zxinfo_at->at_cmd_buff);
os_free(zxinfo_at);
}
return RET_OK;
}
void rt_usbh_zxinfo_at_run(void *arg)
{
rt_usbh_zxinfo_enable(arg);
}
void rt_usbh_zxinfo_at_stop(void *arg)
{
rt_usbh_zxinfo_disable(arg);
}
// 中云信安使用CDC类型接口不用注册VENDOR了从cdc.c中调用enable处理
ucd_t rt_usbh_class_driver_zxinfo(void)
{
zxinfo_driver.class_code = USB_CLASS_CDC;
zxinfo_driver.enable = rt_usbh_zxinfo_enable;
zxinfo_driver.disable = rt_usbh_zxinfo_disable;
return &zxinfo_driver;
}
#endif