📄 port.c
字号:
/*
Portserver - Use RTERM protocol to serve serial ports over a network
Copyright (C) 1998 Kenn Humborg
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#include "buffer.h"
#include "connection.h"
#include "lock.h"
#include "port.h"
#include "portserver.h"
#include "status.h"
/* The list head for the port list */
struct Port port_list;
/* Add a port structure to the end of the list of ports */
void AddPort(struct Port *port)
{
port->next = &port_list;
port->prev = port_list.prev;
port_list.prev->next = port;
port_list.prev = port;
}
void DumpPortList(void)
{
struct Port *port;
fprintf(stderr, "Dumping port list:\n");
port = port_list.next;
while (port != &port_list) {
fprintf(stderr, " Name: %-16s Device: %-16s",
port->port_name,
port->device_name == NULL ? "None": port->device_name);
if (port->flags & PORT_USELOCKFILE) {
fprintf(stderr, " USELOCKFILE");
}
if (port->flags & PORT_TEMPLATE) {
fprintf(stderr, " TEMPLATE");
}
fprintf(stderr,
"\n "
"Speed: %-7d FD: %d\n",
port->speed, port->fd);
port = port->next;
}
}
struct Port *FindPort(char *port_name)
{
struct Port *port;
port = port_list.next;
while (port != &port_list) {
if (strcasecmp(port_name, port->port_name) == 0) {
return port;
}
port = port->next;
}
return NULL;
}
STATUS CreatePort(char *port_name, struct Port **new_port)
{
STATUS status;
struct Port *port;
struct Port *template;
if (FindPort(port_name) != NULL) {
return ERR_EXISTS;
}
if (strcmp(port_name, TEMPLATE_PORT_NAME) != 0) {
template = FindPort(TEMPLATE_PORT_NAME);
if (template == NULL) {
return ERR_NOTEMPLATE;
}
}
port = malloc(sizeof(struct Port));
if (port == NULL) {
return ERR_NOMEMORY;
}
if (template != NULL) {
*port = *template;
port->flags &= ~PORT_TEMPLATE;
} else {
port->flags = 0;
}
port->next = NULL;
port->prev = NULL;
port->connection = NULL;
port->fd = 0;
port->lock_file = NULL;
port->device_name = NULL;
port->description = NULL;
port->port_name = strdup(port_name);
if (port->port_name == NULL) {
free(port);
return ERR_NOMEMORY;
}
status = BufferCreate(PORT_BUFFER_SIZE, &port->read_buffer);
if (IS_FAILURE(status)) {
free(port->port_name);
free(port);
return status;
}
status = BufferCreate(PORT_BUFFER_SIZE, &port->write_buffer);
if (IS_FAILURE(status)) {
BufferDelete(port->read_buffer);
free(port->port_name);
free(port);
return status;
}
*new_port = port;
return SUCCESS;
}
STATUS SetPortDevice(struct Port *port, char *dev_name)
{
if (port->device_name != NULL) {
free(port->device_name);
}
port->device_name = strdup(dev_name);
if (port->device_name == NULL) {
return ERR_NOMEMORY;
}
return SUCCESS;
}
STATUS SetPortDescription(struct Port *port, char *description)
{
if (port->description != NULL) {
free(port->description);
}
port->description = strdup(description);
if (port->description == NULL) {
return ERR_NOMEMORY;
}
return SUCCESS;
}
STATUS OpenPort(struct Port *port)
{
int status;
int fd;
int flags;
struct termios settings;
int retval;
if (port->device_name == NULL) {
return ERR_NODEVNAME;
}
if (port->flags & PORT_USELOCKFILE) {
status = LockPort(port);
if (IS_FAILURE(status)) {
return status;
}
}
fd = open(port->device_name, O_RDWR);
if (fd == -1) {
syslog(LOG_ERR, "Cannot open port %s: %m", port->device_name);
return ERR_OPENDEV;
}
retval = tcgetattr(fd, &settings);
if (retval == -1) {
syslog(LOG_ERR, "Cannot get settings: %m");
close(fd);
UnlockPort(port);
return ERR_FAILURE;
}
port->old_settings = settings;
port->fd = fd;
/* From this point on, it's safe to call ClosePort() */
retval = tcflush(fd, TCIOFLUSH);
if (retval == -1) {
syslog(LOG_ERR, "Cannot flush port: %m");
ClosePort(port);
return ERR_FAILURE;
}
cfmakeraw(&settings);
retval = tcsetattr(fd, TCSANOW, &settings);
if (retval == -1) {
syslog(LOG_ERR, "Cannot set port to raw: %m");
ClosePort(port);
return ERR_FAILURE;
}
status = PortSetSpeed(port, port->speed);
if (IS_FAILURE(status)) {
ClosePort(port);
return status;
}
flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, flags);
/* We need to watch for incoming data from this port */
FD_SET(fd, &read_fds);
FD_SET(fd, &excep_fds);
port->fd = fd;
GetMaxFD();
return SUCCESS;
}
void ClosePort(struct Port *port)
{
int fd;
int retval;
fd = port->fd;
retval = tcsetattr(fd, TCSANOW, &port->old_settings);
if (retval == -1) {
syslog(LOG_ERR, "Cannot reset settings: %m");
}
close(fd);
FD_CLR(fd, &read_fds);
FD_CLR(fd, &write_fds);
FD_CLR(fd, &excep_fds);
port->fd = 0;
UnlockPort(port);
GetMaxFD();
}
int PortSendData(struct Port *port, char *data, int data_len)
{
int bytes_copied;
BufferAdd(port->write_buffer, data, data_len, &bytes_copied);
if (port->write_buffer->data_length != 0) {
/* signal our interest in writing to the port */
FD_SET(port->fd, &write_fds);
}
return bytes_copied;
}
void PortReady(struct Port *port)
{
int bytes_written;
struct Buffer *buf;
buf = port->write_buffer;
bytes_written = write(port->fd, buf->data,
buf->data_length);
if (bytes_written == -1) {
syslog(LOG_ERR, "Cannot write to port: %m");
}
BufferRemove(buf, bytes_written);
/* If we have more data available, signal our interest
in writing to the port */
if (buf->data_length != 0) {
FD_SET(port->fd, &write_fds);
} else {
FD_CLR(port->fd, &write_fds);
}
if (buf->data_length < buf->length) {
/* Call HandleSocketData() just in case there is data
in the socket's input buffer, waiting for space to
free up in this port's output buffer */
HandleSocketData(port->connection);
}
}
void PortDataReceived(struct Port *port)
{
struct Buffer *buf;
int space;
int bytes_read;
buf = port->read_buffer;
space = buf->length - buf->data_length;
bytes_read = read(port->fd, buf->data + buf->data_length, space);
if (bytes_read == -1) {
syslog(LOG_ERR, "Cannot read from port: %m");
return;
}
buf->data_length += bytes_read;
HandlePortData(port);
}
void HandlePortData(struct Port *port)
{
struct Buffer *buf;
int bytes_sent;
buf = port->read_buffer;
if (buf->data_length == 0) {
/* We're ready for more data */
FD_SET(port->fd, &read_fds);
} else {
bytes_sent = SocketSendData(port->connection, buf->data,
buf->data_length);
if (bytes_sent == 0) {
/* socket write buffer is full - don't read any more data from
the port */
FD_CLR(port->fd, &read_fds);
} else {
BufferRemove(buf, bytes_sent);
/* We're ready for more... */
FD_SET(port->fd, &read_fds);
}
}
}
STATUS PortSetSpeed(struct Port *port, int speed)
{
struct termios settings;
int new_speed;
int retval;
switch (speed) {
case 50: new_speed = B50; break;
case 75: new_speed = B75; break;
case 110: new_speed = B110; break;
case 134: new_speed = B134; break;
case 150: new_speed = B150; break;
case 300: new_speed = B300; break;
case 600: new_speed = B600; break;
case 1200: new_speed = B1200; break;
case 1800: new_speed = B1800; break;
case 2400: new_speed = B2400; break;
case 4800: new_speed = B4800; break;
case 9600: new_speed = B9600; break;
case 19200: new_speed = B19200; break;
case 38400: new_speed = B38400; break;
case 57600: new_speed = B57600; break;
case 115200: new_speed = B115200; break;
case 230400: new_speed = B230400; break;
default:
return ERR_FAILURE;
}
retval = tcgetattr(port->fd, &settings);
if (retval == -1) {
syslog(LOG_ERR, "Cannot get port settings: %m");
return ERR_FAILURE;
}
cfsetospeed(&settings, new_speed);
cfsetispeed(&settings, new_speed);
/* I'd prefer to use TCSADRAIN here, but that will cause the
whole server to block. That will have to wait for a
multi-threaded or multi-process server */
retval = tcsetattr(port->fd, TCSANOW, &settings);
if (retval == -1) {
syslog(LOG_ERR, "Cannot get port settings: %m");
return ERR_FAILURE;
}
syslog(LOG_DEBUG, "Speed changed to %d", speed);
return SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -