mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-02-23 08:13:56 -04:00
HAL_Linux: support direct UDP output from UART drivers
this allows safe operation over WiFi links without MAVProxy
This commit is contained in:
parent
4e27dbdc42
commit
e0e534628b
@ -1,3 +1,5 @@
|
|||||||
|
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: -*- nil -*-
|
||||||
|
|
||||||
#include <AP_HAL.h>
|
#include <AP_HAL.h>
|
||||||
|
|
||||||
#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
|
#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
|
||||||
@ -25,14 +27,11 @@ extern const AP_HAL::HAL& hal;
|
|||||||
|
|
||||||
using namespace Linux;
|
using namespace Linux;
|
||||||
|
|
||||||
#define DEVICE_TCP 0
|
|
||||||
#define DEVICE_SERIAL 1
|
|
||||||
#define DEVICE_UNKNOWN 99
|
|
||||||
|
|
||||||
LinuxUARTDriver::LinuxUARTDriver(bool default_console) :
|
LinuxUARTDriver::LinuxUARTDriver(bool default_console) :
|
||||||
device_path(NULL),
|
device_path(NULL),
|
||||||
_rd_fd(-1),
|
_rd_fd(-1),
|
||||||
_wr_fd(-1)
|
_wr_fd(-1),
|
||||||
|
_packetise(false)
|
||||||
{
|
{
|
||||||
if (default_console) {
|
if (default_console) {
|
||||||
_rd_fd = 0;
|
_rd_fd = 0;
|
||||||
@ -62,8 +61,6 @@ void LinuxUARTDriver::begin(uint32_t b, uint16_t rxS, uint16_t txS)
|
|||||||
if (device_path == NULL && _console) {
|
if (device_path == NULL && _console) {
|
||||||
_rd_fd = 0;
|
_rd_fd = 0;
|
||||||
_wr_fd = 1;
|
_wr_fd = 1;
|
||||||
rxS = 512;
|
|
||||||
txS = 512;
|
|
||||||
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
fcntl(_wr_fd, F_SETFL, fcntl(_wr_fd, F_GETFL, 0) | O_NONBLOCK);
|
fcntl(_wr_fd, F_SETFL, fcntl(_wr_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
} else if (!_initialised) {
|
} else if (!_initialised) {
|
||||||
@ -73,73 +70,70 @@ void LinuxUARTDriver::begin(uint32_t b, uint16_t rxS, uint16_t txS)
|
|||||||
|
|
||||||
switch (_parseDevicePath(device_path)){
|
switch (_parseDevicePath(device_path)){
|
||||||
case DEVICE_TCP:
|
case DEVICE_TCP:
|
||||||
{
|
{
|
||||||
_connected = false;
|
_connected = false;
|
||||||
if (_flag != NULL){
|
if (_flag != NULL){
|
||||||
if (!strcmp(_flag, "wait")){
|
if (!strcmp(_flag, "wait")){
|
||||||
_tcp_start_connection(true);
|
_tcp_start_connection(true);
|
||||||
} else {
|
|
||||||
_tcp_start_connection(false);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
_tcp_start_connection(false);
|
_tcp_start_connection(false);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (_connected) {
|
_tcp_start_connection(false);
|
||||||
if (rxS < 1024) {
|
|
||||||
rxS = 1024;
|
|
||||||
}
|
|
||||||
if (txS < 1024) {
|
|
||||||
txS = 1024;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
printf("LinuxUARTDriver TCP connection not stablished\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case DEVICE_SERIAL:
|
|
||||||
{
|
|
||||||
_rd_fd = open(device_path, O_RDWR);
|
|
||||||
_wr_fd = _rd_fd;
|
|
||||||
if (_rd_fd == -1) {
|
|
||||||
fprintf(stdout, "Failed to open UART device %s - %s\n",
|
|
||||||
device_path, strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// always run the file descriptor non-blocking, and deal with
|
|
||||||
// blocking IO in the higher level calls
|
|
||||||
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
|
||||||
|
|
||||||
if (rxS < 1024) {
|
|
||||||
rxS = 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have enough memory to have a larger transmit buffer for
|
|
||||||
// all ports. This means we don't get delays while waiting to
|
|
||||||
// write GPS config packets
|
|
||||||
if (txS < 1024) {
|
|
||||||
txS = 1024;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
{
|
if (!_connected) {
|
||||||
// Notify that the option is not valid and select standart input and output
|
printf("LinuxUARTDriver TCP connection not stablished\n");
|
||||||
printf("LinuxUARTDriver parsing failed, using default\n");
|
exit(1);
|
||||||
|
|
||||||
_rd_fd = 0;
|
|
||||||
_wr_fd = 1;
|
|
||||||
rxS = 512;
|
|
||||||
txS = 512;
|
|
||||||
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
|
||||||
fcntl(_wr_fd, F_SETFL, fcntl(_wr_fd, F_GETFL, 0) | O_NONBLOCK);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEVICE_UDP:
|
||||||
|
{
|
||||||
|
_udp_start_connection();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEVICE_SERIAL:
|
||||||
|
{
|
||||||
|
_rd_fd = open(device_path, O_RDWR);
|
||||||
|
_wr_fd = _rd_fd;
|
||||||
|
if (_rd_fd == -1) {
|
||||||
|
fprintf(stdout, "Failed to open UART device %s - %s\n",
|
||||||
|
device_path, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// always run the file descriptor non-blocking, and deal with
|
||||||
|
// blocking IO in the higher level calls
|
||||||
|
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Notify that the option is not valid and select standart input and output
|
||||||
|
printf("LinuxUARTDriver parsing failed, using default\n");
|
||||||
|
|
||||||
|
_rd_fd = 0;
|
||||||
|
_wr_fd = 1;
|
||||||
|
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
fcntl(_wr_fd, F_SETFL, fcntl(_wr_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we have enough memory to have a larger transmit buffer for
|
||||||
|
// all ports. This means we don't get delays while waiting to
|
||||||
|
// write GPS config packets
|
||||||
|
if (rxS < 1024) {
|
||||||
|
rxS = 8192;
|
||||||
|
}
|
||||||
|
if (txS < 8192) {
|
||||||
|
txS = 8192;
|
||||||
|
}
|
||||||
|
|
||||||
_initialised = false;
|
_initialised = false;
|
||||||
while (_in_timer) hal.scheduler->delay(1);
|
while (_in_timer) hal.scheduler->delay(1);
|
||||||
|
|
||||||
@ -190,31 +184,53 @@ void LinuxUARTDriver::begin(uint32_t b, uint16_t rxS, uint16_t txS)
|
|||||||
/*
|
/*
|
||||||
Device path accepts the following syntaxes:
|
Device path accepts the following syntaxes:
|
||||||
- /dev/ttyO1
|
- /dev/ttyO1
|
||||||
- tcp:192.168.2.15:1243:wait
|
- tcp:*:1243:wait
|
||||||
|
- udp:192.168.2.15:1243
|
||||||
*/
|
*/
|
||||||
int LinuxUARTDriver::_parseDevicePath(char* arg)
|
LinuxUARTDriver::device_type LinuxUARTDriver::_parseDevicePath(const char *arg)
|
||||||
{
|
{
|
||||||
const char *serial_string = "/dev/tty";
|
struct stat st;
|
||||||
const char *tcp_string = "tcp";
|
|
||||||
_flag = NULL; // init flag
|
_flag = NULL; // init flag
|
||||||
|
|
||||||
if(strstr(arg, tcp_string) != NULL){
|
|
||||||
// Parse the TCP string
|
char *devstr = strdup(arg);
|
||||||
|
if (devstr == NULL) {
|
||||||
|
return DEVICE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(devstr, &st) == 0 && S_ISCHR(st.st_mode)) {
|
||||||
|
free(devstr);
|
||||||
|
return DEVICE_SERIAL;
|
||||||
|
} else if (strncmp(devstr, "tcp:", 4) == 0 ||
|
||||||
|
strncmp(devstr, "udp:", 4) == 0) {
|
||||||
|
char *saveptr = NULL;
|
||||||
|
// Parse the string
|
||||||
char *protocol, *ip, *port, *flag;
|
char *protocol, *ip, *port, *flag;
|
||||||
protocol = strtok ( arg, ":" );
|
protocol = strtok_r(devstr, ":", &saveptr);
|
||||||
ip = strtok ( NULL, ":" );
|
ip = strtok_r(NULL, ":", &saveptr);
|
||||||
port = strtok ( NULL, ":" );
|
port = strtok_r(NULL, ":", &saveptr);
|
||||||
flag = strtok ( NULL, ":" );
|
flag = strtok_r(NULL, ":", &saveptr);
|
||||||
|
|
||||||
_base_port = (uint16_t) atoi(port);
|
_base_port = (uint16_t) atoi(port);
|
||||||
_ip = ip;
|
if (_ip) free(_ip);
|
||||||
_flag = flag;
|
_ip = NULL;
|
||||||
|
if (ip) {
|
||||||
|
_ip = strdup(ip);
|
||||||
|
}
|
||||||
|
if (_flag) free(_flag);
|
||||||
|
_flag = NULL;
|
||||||
|
if (flag) {
|
||||||
|
_flag = strdup(flag);
|
||||||
|
}
|
||||||
|
if (strcmp(protocol, "udp") == 0) {
|
||||||
|
free(devstr);
|
||||||
|
return DEVICE_UDP;
|
||||||
|
}
|
||||||
|
free(devstr);
|
||||||
return DEVICE_TCP;
|
return DEVICE_TCP;
|
||||||
} else if (strstr(arg, serial_string) != NULL){
|
}
|
||||||
return DEVICE_SERIAL;
|
free(devstr);
|
||||||
} else {
|
return DEVICE_UNKNOWN;
|
||||||
return DEVICE_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -230,14 +246,6 @@ void LinuxUARTDriver::_tcp_start_connection(bool wait_for_connection)
|
|||||||
int net_fd = -1; // network file descriptor, will be linked to wr_fd and rd_fd
|
int net_fd = -1; // network file descriptor, will be linked to wr_fd and rd_fd
|
||||||
uint8_t portNumber = 0; // connecto to _base_port + portNumber
|
uint8_t portNumber = 0; // connecto to _base_port + portNumber
|
||||||
|
|
||||||
// if (_console) {
|
|
||||||
// // hack for console access
|
|
||||||
// connected = true;
|
|
||||||
// listen_fd = -1;
|
|
||||||
// fd = 1;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (net_fd != -1) {
|
if (net_fd != -1) {
|
||||||
close(net_fd);
|
close(net_fd);
|
||||||
}
|
}
|
||||||
@ -250,10 +258,12 @@ void LinuxUARTDriver::_tcp_start_connection(bool wait_for_connection)
|
|||||||
#endif
|
#endif
|
||||||
sockaddr.sin_port = htons(_base_port + portNumber);
|
sockaddr.sin_port = htons(_base_port + portNumber);
|
||||||
sockaddr.sin_family = AF_INET;
|
sockaddr.sin_family = AF_INET;
|
||||||
// sockaddr.sin_addr.s_addr = inet_addr(_base_ip);
|
if (strcmp(_ip, "*") == 0) {
|
||||||
|
// Bind to all interfaces
|
||||||
// Bind to all interfaces
|
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
} else {
|
||||||
|
sockaddr.sin_addr.s_addr = inet_addr(_ip);
|
||||||
|
}
|
||||||
|
|
||||||
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (listen_fd == -1) {
|
if (listen_fd == -1) {
|
||||||
@ -308,6 +318,47 @@ void LinuxUARTDriver::_tcp_start_connection(bool wait_for_connection)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
start a UDP connection for the serial port
|
||||||
|
*/
|
||||||
|
void LinuxUARTDriver::_udp_start_connection(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sockaddr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&sockaddr,0,sizeof(sockaddr));
|
||||||
|
|
||||||
|
#ifdef HAVE_SOCK_SIN_LEN
|
||||||
|
sockaddr.sin_len = sizeof(sockaddr);
|
||||||
|
#endif
|
||||||
|
sockaddr.sin_port = htons(_base_port);
|
||||||
|
sockaddr.sin_family = AF_INET;
|
||||||
|
sockaddr.sin_addr.s_addr = inet_addr(_ip);
|
||||||
|
|
||||||
|
_rd_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (_rd_fd == -1) {
|
||||||
|
printf("socket failed - %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = connect(_rd_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
|
||||||
|
if (ret == -1) {
|
||||||
|
printf("connect failed to %s:%u - %s\n",
|
||||||
|
_ip, (unsigned)_base_port,
|
||||||
|
strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// always run the file descriptor non-blocking, and deal with |
|
||||||
|
// blocking IO in the higher level calls
|
||||||
|
fcntl(_rd_fd, F_SETFL, fcntl(_rd_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
_wr_fd = _rd_fd;
|
||||||
|
|
||||||
|
// try to write on MAVLink packet boundaries if possible
|
||||||
|
_packetise = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
shutdown a UART
|
shutdown a UART
|
||||||
*/
|
*/
|
||||||
@ -541,6 +592,22 @@ void LinuxUARTDriver::_timer_tick(void)
|
|||||||
// write any pending bytes
|
// write any pending bytes
|
||||||
uint16_t _tail;
|
uint16_t _tail;
|
||||||
n = BUF_AVAILABLE(_writebuf);
|
n = BUF_AVAILABLE(_writebuf);
|
||||||
|
if (_packetise && n > 0 && _writebuf[_writebuf_head] == 254) {
|
||||||
|
// this looks like a MAVLink packet - try to write on
|
||||||
|
// packet boundaries when possible
|
||||||
|
if (n < 8) {
|
||||||
|
n = 0;
|
||||||
|
} else {
|
||||||
|
uint16_t ofs = (_writebuf_head + 1) % _writebuf_size;
|
||||||
|
uint8_t len = _writebuf[ofs];
|
||||||
|
if (n < len+8) {
|
||||||
|
n = 0;
|
||||||
|
} else if (n > len+8) {
|
||||||
|
n = len+8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
if (_tail > _writebuf_head) {
|
if (_tail > _writebuf_head) {
|
||||||
// do as a single write
|
// do as a single write
|
||||||
|
@ -41,6 +41,7 @@ private:
|
|||||||
char *_ip;
|
char *_ip;
|
||||||
char *_flag;
|
char *_flag;
|
||||||
bool _connected; // true if a client has connected
|
bool _connected; // true if a client has connected
|
||||||
|
bool _packetise; // true if writes should try to be on mavlink boundaries
|
||||||
|
|
||||||
// we use in-task ring buffers to reduce the system call cost
|
// we use in-task ring buffers to reduce the system call cost
|
||||||
// of ::read() and ::write() in the main loop
|
// of ::read() and ::write() in the main loop
|
||||||
@ -60,7 +61,16 @@ private:
|
|||||||
int _write_fd(const uint8_t *buf, uint16_t n);
|
int _write_fd(const uint8_t *buf, uint16_t n);
|
||||||
int _read_fd(uint8_t *buf, uint16_t n);
|
int _read_fd(uint8_t *buf, uint16_t n);
|
||||||
void _tcp_start_connection(bool wait_for_connection);
|
void _tcp_start_connection(bool wait_for_connection);
|
||||||
int _parseDevicePath(char* arg);
|
void _udp_start_connection(void);
|
||||||
|
|
||||||
|
enum device_type {
|
||||||
|
DEVICE_TCP,
|
||||||
|
DEVICE_UDP,
|
||||||
|
DEVICE_SERIAL,
|
||||||
|
DEVICE_UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
enum device_type _parseDevicePath(const char *arg);
|
||||||
uint64_t _last_write_time;
|
uint64_t _last_write_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user