Files
TAIXIN/sdk/lib/bus/rttusb/usbhost/class/chinamobile.c

361 lines
13 KiB
C
Raw Normal View History

#include <rtthread.h>
#include <include/rttusb_host.h>
#include "chinamobile.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_CHINAMOBILE
#define USB_VENDOR_ID_CHINAMOBILE 0x2ECC
#define USB_PRODUCT_ID_CHINAMOBILE 0x3012 // ML307R
#define CHINAMOBILE_ATCMD_BUFF_SIZE 128
static char recv_str[CHINAMOBILE_ATCMD_BUFF_SIZE];
static struct uclass_driver chinamobile_driver;
static rt_bool_t send_recv_atcmd_check(void *context, const char *send_str,
const char *check_str, char *ret_str)
{
struct usb_chinamobile_at *chinamobile_at = context;
uinst_t device = chinamobile_at->device;
int recv_size;
char *recv_str = NULL;
rt_bool_t pass = RT_FALSE;
os_sprintf((char *)chinamobile_at->at_cmd_buff, send_str);
rt_usb_hcd_pipe_xfer(device->hcd, chinamobile_at->pipe_out,
chinamobile_at->at_cmd_buff, os_strlen(send_str), 0);
os_sleep_ms(10); // 需要点延迟给LTE模组反应
do {
// 每次读取会清除缓存
os_memset(chinamobile_at->at_cmd_buff, 0, CHINAMOBILE_ATCMD_BUFF_SIZE);
recv_size = rt_usb_hcd_pipe_xfer(device->hcd, chinamobile_at->pipe_in,
chinamobile_at->at_cmd_buff, CHINAMOBILE_ATCMD_BUFF_SIZE, 10);
if (recv_size > 0) {
recv_str = os_strstr(chinamobile_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 chinamobile_network_info(void *context, char *recv_str)
{
struct usb_chinamobile_at *chinamobile_at = context;
//char *argv[16];
char **argv = NULL;
int argc = 0;
rt_int16_t rsrp, rssi, sinr, rsrq;
rt_uint16_t num_dl;
rt_uint16_t freq_dl, freq_ul;
rt_uint8_t band = 0;
argv = os_malloc(16 * sizeof(char *));
if (argv == NULL) {
return;
}
// 获取运营商
if (send_recv_atcmd_check(chinamobile_at, "AT+COPS?\r\n", "+COPS:", recv_str) == RT_TRUE) {
argc = os_strtok(recv_str, ",", argv, 16);
if (argc >= 3) {
// 0,0,"CHINA MOBILE",7
_os_printf("%s\r\n", argv[2]);
}
}
// 获取网络信息
if (send_recv_atcmd_check(chinamobile_at, "AT+MUESTATS=\"sband\"\r\n", "+MUESTATS:", recv_str) == RT_TRUE) {
argc = os_strtok(recv_str, ",", argv, 16);
if (argc >= 2) {
// "sband",39
band = os_atoi(argv[1]);
_os_printf("BAND: %d\r\n", band);
}
}
if (send_recv_atcmd_check(chinamobile_at, "AT+MUESTATS=\"radio\"\r\n", "+MUESTATS:", recv_str) == RT_TRUE) {
argc = os_strtok(recv_str, ",", argv, 16);
if (argc >= 13) {
// "radio",4,-790,-560,-32768,0,0,0A513F03,255,200,38400,197,-30
rsrp = os_atoi(argv[2]); // 0.1dBm
rssi = os_atoi(argv[3]); // 0.1dBm
sinr = os_atoi(argv[9]); // 0.1dB
rsrq = os_atoi(argv[12]); // 0.1dB
num_dl = os_atoi(argv[10]);
freq_dl = eutra_channel_freq_mapping(band, num_dl, 0);
freq_ul = eutra_channel_freq_mapping(band, num_dl, 1);
_os_printf("RSRP: %d.%d dBm\r\n", rsrp/10, os_abs(rsrp%10));
_os_printf("RSSI: %d.%d dBm\r\n", rssi/10, os_abs(rssi%10));
_os_printf("SINR: %d.%d dB\r\n", sinr/10, os_abs(sinr%10));
_os_printf("RSRQ: %d.%d dB\r\n", rsrq/10, os_abs(rsrq%10));
_os_printf("F_dl: %d MHz, F_ul: %d MHz\r\n", freq_dl, freq_ul);
}
}
os_free(argv);
}
// 发送一条AT命令会有多条数据返回发送后多次读循环
static void chinamobile_at_recv(void *context)
{
struct usb_chinamobile_at *chinamobile_at = context;
while (1) {
if (chinamobile_at->retry > 3) {
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_UNKNOW;
}
// os_printf("recv state:%d\r\n", chinamobile_at->state);
switch (chinamobile_at->state) {
case CHINAMOBILE_STATE_UNKNOW:
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_CHECK_AT_STATUS;
break;
case CHINAMOBILE_STATE_CHECK_AT_STATUS:
// AT查询模块是否工作启动初始化流程
if (send_recv_atcmd_check(chinamobile_at, "AT\r\n", "OK", NULL) == RT_TRUE) {
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_CHECK_SIM_STATUS;
os_printf("AT ready\r\n");
} else {
chinamobile_at->retry++;
os_sleep(1);
}
break;
case CHINAMOBILE_STATE_CHECK_SIM_STATUS:
// AT+CPIN查询SIM卡状态
if (send_recv_atcmd_check(chinamobile_at, "AT+CPIN?\r\n", "+CPIN: READY", NULL) == RT_TRUE) {
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_CHECK_PS_STATUS;
os_printf("SIM ready\r\n");
} else {
chinamobile_at->retry++;
os_sleep(1);
}
break;
case CHINAMOBILE_STATE_CHECK_PS_STATUS:
// AT+CEREG查询EPS域网络注册状态
if (send_recv_atcmd_check(chinamobile_at, "AT+CEREG?\r\n", "+CEREG: 0,1", NULL) == RT_TRUE) {
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_CHECK_PDP_CONTEXT;
os_printf("CS ready\r\n");
} else {
chinamobile_at->retry++;
os_sleep(1);
}
break;
case CHINAMOBILE_STATE_CHECK_PDP_CONTEXT:
// AT+CGDCONT查询PDP场景
if (send_recv_atcmd_check(chinamobile_at, "AT+CGDCONT?\r\n", "+CGDCONT: 1", NULL) == RT_TRUE) {
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_CHECK_IP_STATUS;
os_printf("PDP ready\r\n");
} else {
chinamobile_at->retry++;
os_sleep(1);
}
break;
case CHINAMOBILE_STATE_CHECK_IP_STATUS:
// AT+MDIALUP拨号上网
if (send_recv_atcmd_check(chinamobile_at, "AT+MDIALUP=1,1\r\n", "+MDIALUP: 1,1,", recv_str) == RT_TRUE) {
chinamobile_at->retry = 0;
chinamobile_at->state = CHINAMOBILE_STATE_INITIALIZED;
os_printf("Get IP: %s", recv_str);
SYSEVT_NEW_LTE_EVT(SYSEVT_LTE_CONNECTED, 0);
os_printf("dial up\r\n");
} else {
chinamobile_at->retry++;
os_sleep(1);
}
break;
case CHINAMOBILE_STATE_INITIALIZED:
// 查询网络信息
chinamobile_network_info(chinamobile_at, recv_str);
// 定时10s检查拨号上网状态
if (send_recv_atcmd_check(chinamobile_at, "AT+MDIALUP?\r\n", "+MDIALUP: 1,1,", NULL) == RT_TRUE) {
chinamobile_at->retry = 0;
os_printf("LTE status ok\r\n");
os_sleep(10);
} else {
chinamobile_at->retry++;
os_sleep(1);
}
break;
default:
break;
}
}
os_printf("at task end\r\n");
}
static rt_err_t rt_usbh_chinamobile_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_chinamobile_at *chinamobile_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_CHINAMOBILE) {
// 怎么有两个AT串口先直接用第2个接口判断好了
if (intf->intf_desc->bInterfaceNumber != 2) {
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("chinamobile_at\r\n");
chinamobile_at = (struct usb_chinamobile_at *)os_zalloc(sizeof(struct usb_chinamobile_at));
if (chinamobile_at == NULL) {
os_printf("chinamobile at alloc fail\r\n");
return -ENOMEM;
}
// 按照命令预期回复长度对齐预留长度
chinamobile_at->at_cmd_buff = os_malloc(CHINAMOBILE_ATCMD_BUFF_SIZE);
if (chinamobile_at->at_cmd_buff == NULL) {
os_printf("at_cmd_buff alloc fail\r\n");
os_free(chinamobile_at);
return -ENOMEM;
}
chinamobile_at->device = intf->device;
intf->user_data = chinamobile_at;
// 顺便注册devid
dev_register(HG_USB_AT_DEVID, (struct dev_obj *)chinamobile_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) {
chinamobile_at->pipe_in = pipe;
} else {
chinamobile_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(&chinamobile_at->recv_task, "at_recv", chinamobile_at_recv, chinamobile_at,
NULL, 512, OS_TASK_PRIORITY_BELOW_NORMAL, 20);
rt_thread_startup(&chinamobile_at->recv_task);
}
return ret;
}
static rt_err_t rt_usbh_chinamobile_disable(void *arg)
{
struct uhintf *intf = arg;
uhcd_t hcd = NULL;
struct usb_chinamobile_at *chinamobile_at = NULL;
if (intf == NULL) {
return -EIO;
}
chinamobile_at = intf->user_data;
if (chinamobile_at) {
hcd = intf->device->hcd;
dev_unregister((struct dev_obj *)chinamobile_at);
rt_thread_detach(&chinamobile_at->recv_task);
os_free(chinamobile_at->at_cmd_buff);
os_free(chinamobile_at);
}
return RET_OK;
}
ucd_t rt_usbh_class_driver_chinamobile(void)
{
chinamobile_driver.class_code = USB_CLASS_VEND_SPECIFIC;
chinamobile_driver.vendor_id = USB_VENDOR_ID_CHINAMOBILE;
chinamobile_driver.enable = rt_usbh_chinamobile_enable;
chinamobile_driver.disable = rt_usbh_chinamobile_disable;
return &chinamobile_driver;
}
#endif