2023-04-01 10:00:13 +08:00

339 lines
9.7 KiB
C

/******************************************************************************
@file tty2tcp.c
@brief switch data between tcp socket and ttyUSB port.
DESCRIPTION
QLog Tool for USB and PCIE of Quectel wireless cellular modules.
INITIALIZATION AND SEQUENCING REQUIREMENTS
None.
---------------------------------------------------------------------------
Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
Quectel Wireless Solution Proprietary and Confidential.
---------------------------------------------------------------------------
******************************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <termios.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <netinet/in.h>
#include <linux/un.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
#include <linux/usb/ch9.h>
#else
#include <linux/usb_ch9.h>
#endif
//#include <asm/byteorder.h>
#include "usb_linux.h"
#include <endian.h> //for __BYTE_ORDER
char *inet_ntoa(struct in_addr in);
#define dprintf dbg_time
#define MIN(a,b) ((a) < (b)? (a) : (b))
#define MAX_USBFS_BULK_IN_SIZE (4 * 1024)
#define MAX_USBFS_BULK_OUT_SIZE (16 * 1024)
static uint32_t cpu_to_le32 (uint32_t v32) {
uint32_t tmp = v32;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#else
unsigned char *s = (unsigned char *)(&v32);
unsigned char *d = (unsigned char *)(&tmp);
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
#endif
return tmp;
}
#define le32_to_cpu(_v32) cpu_to_le32(_v32)
static int qusb_control[2];
static int noblock_full_read(int fd, void *pbuf, ssize_t size) {
ssize_t cur = 0;
while (cur < size) {
ssize_t ret = read(fd, (char *)pbuf+cur, size-cur);
if (ret > 0)
cur += ret;
else if (ret < 0 && errno == EAGAIN) {
struct pollfd pollfds[] = {{fd, POLLIN, 0}};
poll(pollfds, 1, -1);
if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
break;
} else {
dprintf("fd=%d read=%zd, errno: %d (%s)\n", fd, ret, errno, strerror(errno));
break;
}
}
if (cur != size) {
dprintf("%s fd=%d cur=%zd, size=%zd\n", __func__, fd, cur, size);
}
return cur;
}
static ssize_t noblock_full_write(int fd, const void *pbuf, ssize_t size) {
ssize_t cur = 0;
while (cur < size) {
ssize_t ret = write(fd, (char *)pbuf+cur, size-cur);
if (ret > 0)
cur += ret;
else if (ret <= 0 && errno == EAGAIN) {
struct pollfd pollfds[] = {{fd, POLLOUT, 0}};
poll(pollfds, 1, -1);
if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
break;
} else {
dprintf("fd=%d write=%zd, errno: %d (%s)\n", fd, ret, errno, strerror(errno));
break;
}
}
if (cur != size) {
dprintf("%s fd=%d cur=%zd, size=%zd\n", __func__, fd, cur, size);
}
return cur;
}
static void* usb_bulk_read_thread(void* arg) {
const void *usb_handle = arg;
void *buf = malloc(MAX_USBFS_BULK_IN_SIZE);
int fd = qusb_control[1];
if (buf == NULL)
return NULL;
while(usb_handle) {
int count = qusb_noblock_read(usb_handle, buf, MAX_USBFS_BULK_IN_SIZE, 1, 30000);
if (count > 0) {
count = write(fd, buf, count);
count = read(fd, buf, 32); //wait usb2tcp_main read
if (count <= 0) {
dprintf("read=%d\n", count);
break;
}
} else if (count <= 0) {
break;
}
}
close(fd);
free(buf);
return NULL;
}
static int qusb_open(const void *usb_handle) {
int fd = -1;
pthread_t thread_id;
pthread_attr_t usb_thread_attr;
pthread_attr_init(&usb_thread_attr);
pthread_attr_setdetachstate(&usb_thread_attr, PTHREAD_CREATE_DETACHED);
socketpair(AF_LOCAL, SOCK_STREAM, 0, qusb_control);
pthread_create(&thread_id, &usb_thread_attr, usb_bulk_read_thread, (void*)usb_handle);
fd = qusb_control[0];
return fd;
}
static ssize_t qusb_read(int fd, void* pbuf, size_t size) {
return read(fd, pbuf, size);
}
static int create_tcp_server(int socket_port) {
int sockfd = -1;
int reuse_addr = 1;
struct sockaddr_in sockaddr;
dprintf("%s tcp_port=%d\n", __func__, socket_port);
/*Create server socket*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd <= 0)
return sockfd;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockaddr.sin_port = htons(socket_port);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr));
if (bind(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
close(sockfd);
dprintf("%s bind %d errno: %d (%s)\n", __func__, socket_port, errno, strerror(errno));
return -1;
}
return sockfd;
}
static int wait_client_connect(int server_fd) {
int client_fd = -1;
unsigned char addr[128];
socklen_t alen = sizeof(addr);
dprintf("%s\n", __func__);
listen(server_fd, 1);
client_fd = accept(server_fd, (struct sockaddr *)addr, &alen);
if (client_fd <= 0)
return client_fd;
if (client_fd > 0) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
dprintf("clientfd = %d %s:%d connect\n", client_fd, inet_ntoa(addr_in->sin_addr), addr_in->sin_port);
}
return client_fd;
}
int usb2tcp_main(const void *usb_handle, int tcp_port, unsigned qusb_zlp_mode) {
void *pbuf = malloc(MAX_USBFS_BULK_OUT_SIZE);
int server_fd = -1, client_fd = -1, usb_fd = -1, size = -1;
TLV_USB tlv_usb;
if (pbuf == NULL)
return -1;
server_fd = create_tcp_server(tcp_port);
dprintf("server_fd=%d\n", server_fd);
if (server_fd <= 0) {
dprintf("Fail create_tcp_server\n");
goto _out;
}
if (client_fd <= 0) {
client_fd = wait_client_connect(server_fd);
if (client_fd < 0) {
dprintf("Fail wait_client_connect\n");
goto _out;
}
}
usb_fd = qusb_open(usb_handle);
dprintf("usb_fd = %d\n", usb_fd);
tlv_usb.tag = cpu_to_le32(Q_USB2TCP_VERSION);
tlv_usb.length = cpu_to_le32(12);
tlv_usb.idVendor = cpu_to_le32(0x05c6);
tlv_usb.idProduct = cpu_to_le32(0x9008);
tlv_usb.interfaceNum = cpu_to_le32(1);
if (write(client_fd, &tlv_usb, sizeof(tlv_usb)) == -1) {};
fcntl(usb_fd, F_SETFL, fcntl(usb_fd,F_GETFL) | O_NONBLOCK);
fcntl(client_fd, F_SETFL, fcntl(client_fd,F_GETFL) | O_NONBLOCK);
while (usb_fd > 0 && client_fd > 0) {
struct pollfd pollfds[] = {{usb_fd, POLLIN, 0}, {client_fd, POLLIN, 0}};
int ne, ret, nevents = sizeof(pollfds)/sizeof(pollfds[0]);
do {
ret = poll(pollfds, nevents, -1);
} while (ret < 0 && errno == EINTR);
if (ret <= 0) {
dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno));
goto _hangup;
}
if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
dprintf("%s poll usb_fd = %d, revents = %04x\n", __func__, usb_fd, pollfds[0].revents);
goto _hangup;
}
if (pollfds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
dprintf("%s poll client_fd = %d, revents = %04x\n", __func__, client_fd, pollfds[1].revents);
goto _hangup;
}
for (ne = 0; ne < nevents; ne++) {
int fd = pollfds[ne].fd;
TLV tlv = {Q_USB2TCP_VERSION, 0};
if ((pollfds[ne].revents & POLLIN) == 0)
continue;
if (fd == usb_fd) {
size = qusb_read(usb_fd, pbuf, MAX_USBFS_BULK_IN_SIZE);
if (size <= 0) {
dprintf("usb_fd=%d read=%d, errno: %d (%s)\n", fd, size, errno, strerror(errno));
goto _hangup;;
}
if (write(usb_fd, pbuf, 1) == -1) {}; //wakeup usb_bulk_read_thread
tlv.tag = cpu_to_le32(Q_USB2TCP_VERSION);
tlv.length = cpu_to_le32(size);
if (sizeof(tlv) != noblock_full_write(client_fd, &tlv, sizeof(tlv))) {
goto _hangup;
break;
}
if (size != noblock_full_write(client_fd, pbuf, size)) {
goto _hangup;
break;
}
}
else if (fd == client_fd) {
size = noblock_full_read(client_fd, &tlv, sizeof(tlv));
if (size != sizeof(tlv)) {
dprintf("client_fd=%d read=%d, errno: %d (%s)\n", fd, size, errno, strerror(errno));
goto _hangup;
}
if (le32_to_cpu(tlv.tag) != Q_USB2TCP_VERSION) {
break;
}
size = le32_to_cpu(tlv.length);
if (size != noblock_full_read(client_fd, pbuf, size)) {
goto _hangup;
break;
}
qusb_noblock_write(usb_handle, pbuf, size, size, 3000, qusb_zlp_mode);
}
}
}
_hangup:
if (usb_fd > 0) {
close(usb_fd);
usb_fd = -1;
}
if (client_fd > 0) {
close(client_fd);
client_fd = -1;
}
_out:
free(pbuf);
return 0;
}