Files

473 lines
18 KiB
C
Raw Permalink Normal View History

#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