⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 port.c

📁 网络端口的服务程序
💻 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 + -