Files

349 lines
13 KiB
C
Raw Permalink Normal View History

#include <rtthread.h>
#include <include/rttusb_host.h>
#include "quectel.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"
#ifdef RT_USBH_VENDOR_QUECTEL
#define USB_VENDOR_ID_QUECTEL 0x2C7C
#define USB_PRODUCT_ID_QUECTEL 0x0903 // EC801E-CN
#define QUECTEL_ATCMD_BUFF_SIZE 64
static char recv_str[QUECTEL_ATCMD_BUFF_SIZE];
static struct uclass_driver quectel_driver;
static rt_bool_t send_recv_atcmd_check(void *context, const char *send_str,
const char *check_str, char *ret_str)
{
struct usb_quectel_at *quectel_at = context;
uinst_t device = quectel_at->device;
int recv_size;
char *recv_str = NULL;
rt_bool_t pass = RT_FALSE;
os_sprintf((char *)quectel_at->at_cmd_buff, send_str);
rt_usb_hcd_pipe_xfer(device->hcd, quectel_at->pipe_out,
quectel_at->at_cmd_buff, os_strlen(send_str), 0);
os_sleep_ms(10); // 需要点延迟给LTE模组反应
do {
// 每次读取会清除缓存
os_memset(quectel_at->at_cmd_buff, 0, QUECTEL_ATCMD_BUFF_SIZE);
recv_size = rt_usb_hcd_pipe_xfer(device->hcd, quectel_at->pipe_in,
quectel_at->at_cmd_buff, QUECTEL_ATCMD_BUFF_SIZE, 10);
if (recv_size > 0) {
recv_str = os_strstr(quectel_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;
}
static void quectel_at_recv(void *context)
{
struct usb_quectel_at *quectel_at = context;
while (1) {
if (quectel_at->retry > 3) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_UNKNOW;
}
// os_printf("recv state:%d\r\n", quectel_at->state);
switch (quectel_at->state) {
case QUECTEL_STATE_UNKNOW:
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_AT_STATUS;
break;
case QUECTEL_STATE_CHECK_AT_STATUS:
// AT查询模块是否工作启动初始化流程
if (send_recv_atcmd_check(quectel_at, "AT\r\n", "OK", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_SIM_STATUS;
os_printf("AT ready\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CHECK_SIM_STATUS:
if (send_recv_atcmd_check(quectel_at, "AT+CPIN?\r\n", "+CPIN: READY", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_CS_STATUS;
os_printf("SIM ready\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CHECK_CS_STATUS:
// AT+CREG查询CS域网络注册状态
if (send_recv_atcmd_check(quectel_at, "AT+CREG?\r\n", "+CREG: 0,1", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_PS_STATUS;
os_printf("PS ready\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CHECK_PS_STATUS:
// AT+CEREG查询EPS域网络注册状态
if (send_recv_atcmd_check(quectel_at, "AT+CEREG?\r\n", "+CEREG: 0,1", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_USBNET_STATUS;
os_printf("CS ready\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CHECK_USBNET_STATUS:
// 查询usb网卡接口
if (send_recv_atcmd_check(quectel_at, "AT+QCFG=\"usbnet\"\r\n", "+QCFG: \"usbnet\",", recv_str) == RT_TRUE) {
if (os_atoi(recv_str) == 1) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CONFIG_USBNET_STATUS;
os_printf("USBNET is ECM\r\n");
} else {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CONFIG_PDP_CONTEXT;
os_printf("USBNET is RNDIS\r\n");
}
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CONFIG_USBNET_STATUS:
// ecm网卡发送命令切换到rndis网卡
if (send_recv_atcmd_check(quectel_at, "AT+QCFG=\"usbnet\",3\r\n", "OK", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_POWERDOWN;
os_printf("switch to RNDIS\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CONFIG_PDP_CONTEXT:
// AT+QICSGP设置场景参数
if (send_recv_atcmd_check(quectel_at, "AT+QICSGP=1,1,\"UNINET\",\"\",\"\",1\r\n", "OK", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_ACTIVE_PDP_CONTEXT;
os_printf("PDP config\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_ACTIVE_PDP_CONTEXT:
// AT+QIACT激活PDP场景
if (send_recv_atcmd_check(quectel_at, "AT+QIACT=1\r\n", "OK", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_IP_STATUS;
os_printf("PDP ready\r\n");
} else {
quectel_at->retry++;
if (quectel_at->retry > 3) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_DEACTIVE_PDP_CONTEXT;
}
os_sleep(1);
}
break;
case QUECTEL_STATE_CHECK_IP_STATUS:
// AT+QIACT查询激活PDP场景和IP
if (send_recv_atcmd_check(quectel_at, "AT+QIACT?\r\n", "+QIACT: 1,1,1,", recv_str) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CONNECT_USB_ADAPTER;
os_printf("Get IP: %s", recv_str);
os_printf("PDP actived\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_CONNECT_USB_ADAPTER:
// AT+QNETDEVCTL连接USB网卡
if (send_recv_atcmd_check(quectel_at, "AT+QNETDEVCTL=1,1,1\r\n", "OK", recv_str) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_INITIALIZED;
SYSEVT_NEW_LTE_EVT(SYSEVT_LTE_CONNECTED, 0);
os_printf("network conneted\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_INITIALIZED:
// 定时10s不知道干什么好呢
os_sleep(10);
break;
case QUECTEL_STATE_DEACTIVE_PDP_CONTEXT:
// 失败后反激活模组
if (send_recv_atcmd_check(quectel_at, "AT+QIDEACT=1\r\n", "OK", NULL) == RT_TRUE) {
quectel_at->retry = 0;
quectel_at->state = QUECTEL_STATE_CHECK_SIM_STATUS;
os_printf("PDP deactived\r\n");
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
case QUECTEL_STATE_POWERDOWN:
if (send_recv_atcmd_check(quectel_at, "AT+QPOWD\r\n", "POWERED DOWN", NULL) == RT_TRUE) {
quectel_at->retry = 0;
os_printf("LTE power down\r\n");
os_sleep(2);
mcu_reset();
} else {
quectel_at->retry++;
os_sleep(1);
}
break;
default:
break;
}
}
os_printf("at task end\r\n");
}
static rt_err_t rt_usbh_quectel_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_quectel_at *quectel_at = NULL;
struct usb_cdc_line_coding line_coding;
upipe_t pipe;
rt_uint8_t ep_index, i;
if (intf == NULL) {
return -EIO;
}
hcd = intf->device->hcd;
if (intf->device->dev_desc.idProduct == USB_PRODUCT_ID_QUECTEL) {
// 怎么有两个AT串口先直接用第3个接口判断好了
if (intf->intf_desc->bInterfaceNumber != 3) {
return RET_ERR;
}
// 通过字符描述符判断哪个是主调试串口
ret = rt_usbh_get_string_descriptor(intf->device, intf->intf_desc->iInterface,
&str_desc, sizeof(struct ustring_descriptor));
if (ret != RET_OK) {
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("quectel_at\r\n");
quectel_at = (struct usb_quectel_at *)os_zalloc(sizeof(struct usb_quectel_at));
if (quectel_at == NULL) {
os_printf("quectel at alloc fail\r\n");
return -ENOMEM;
}
// 按照命令预期回复长度对齐预留长度
quectel_at->at_cmd_buff = os_malloc(QUECTEL_ATCMD_BUFF_SIZE);
if (quectel_at->at_cmd_buff == NULL) {
os_printf("at_cmd_buff alloc fail\r\n");
os_free(quectel_at);
return -ENOMEM;
}
quectel_at->device = intf->device;
intf->user_data = quectel_at;
// 顺便注册devid
dev_register(HG_USB_AT_DEVID, (struct dev_obj *)quectel_at);
for (ep_index = 0; ep_index < intf->intf_desc->bNumEndpoints; ++ep_index) {
rt_usbh_get_endpoint_descriptor(intf->intf_desc, ep_index, &ep_desc);
if (ep_desc == NULL) {
return RET_ERR;
}
if ((ep_desc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK)
continue;
if (rt_usb_hcd_alloc_pipe(intf->device->hcd, &pipe, intf->device, ep_desc) != RT_EOK) {
rt_kprintf("alloc pipe failed\n");
return -RT_ERROR;
}
rt_usb_instance_add_pipe(intf->device, pipe);
if ((ep_desc->bEndpointAddress & USB_DIR_MASK) == USB_DIR_IN) {
quectel_at->pipe_in = pipe;
} else {
quectel_at->pipe_out = pipe;
}
}
// 获取串口参数
os_memset(&line_coding, 0, sizeof(line_coding));
rt_usbh_cdc_get_line_coding(intf->device, intf->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(&quectel_at->recv_task, "at_recv", quectel_at_recv, quectel_at,
NULL, 512, OS_TASK_PRIORITY_BELOW_NORMAL, 20);
rt_thread_startup(&quectel_at->recv_task);
}
return ret;
}
static rt_err_t rt_usbh_quectel_disable(void *arg)
{
struct uhintf *intf = arg;
uhcd_t hcd = NULL;
struct usb_quectel_at *quectel_at = NULL;
if (intf == NULL) {
return -EIO;
}
quectel_at = intf->user_data;
if (quectel_at) {
hcd = intf->device->hcd;
dev_unregister((struct dev_obj *)quectel_at);
rt_thread_detach(&quectel_at->recv_task);
os_free(quectel_at->at_cmd_buff);
os_free(quectel_at);
}
return RET_OK;
}
ucd_t rt_usbh_class_driver_quectel(void)
{
quectel_driver.class_code = USB_CLASS_VEND_SPECIFIC;
quectel_driver.vendor_id = USB_VENDOR_ID_QUECTEL;
quectel_driver.enable = rt_usbh_quectel_enable;
quectel_driver.disable = rt_usbh_quectel_disable;
return &quectel_driver;
}
#endif