blob: a7e84d454e92247dd0a93920d30b3ebc3ca7fcb8 [file] [log] [blame] [raw]
/*
* This is a sample kme UDP server for implementing
* remote kme. In this case, we interface to the
* linux device driver for HLC by default.
*
* 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, 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "kme.h"
char kmed_c_version[] = "@Id$";
/*
* The name of the memory device to use by default
*
* May have one %d in it, which is replaced by the "board" number.
* This allows you to select one of several different memory
* spaces remotely.
*/
#if 1
char *MemName = "/dev/kmem"; /* Kernel memory */
#else
char *MemName = "/dev/dm/ram%d"; /* Digi memory device */
#endif
/*
* The current rw_t struct leaves the endianness of rw_size and
* rw_addr undefined. This is not good. We can run this server
* in three modes to try to deal with this.
*/
#define RW_ASIS 0 /* Pray its right already */
#define RW_IN_NETWORK_ORDER 1 /* Assume its in network order */
#define RW_DETECT 2 /* Guess, based on rw_size */
int RwEndianness = RW_DETECT;
/*
* The names are all screwed up in the kme/digi.h header file, which
* we include here for reference. They are all so C/X specific.
*/
/*
* Fix the names in the above structure to indicate our actual usage
*/
#define rw_machine rw_board /* machine ID of kme daemon */
/* This # is assigned by the client */
#define rw_adapter rw_conc /* the board number in the machine */
/*
* These are really simplistic read/write mem routines
*
* A real implementation would mmap() the board memory in.
* A slicker yet implementation would mmap() the board memory in,
* and release it after a timeout of several seconds.
*/
int
open_mem(int boardnum)
{
static int fd = -1;
static int lastboard = -1;
/*
* If a new board needs to be opened, close the previous one
*/
if (fd != -1 && lastboard != boardnum)
{
close(fd);
fd = -1;
}
if (fd == -1)
{
char name[256];
sprintf(name, MemName, boardnum);
fd = open(name, O_RDWR);
if (fd < 0)
return -1;
}
lastboard = boardnum;
return (fd);
}
/*
* Reads memory.
*/
void
read_mem(int boardnum, char *buf, int addr, int len)
{
int fd = open_mem(boardnum);
if (fd == -1) return;
lseek(fd, (off_t) addr, 0);
read(fd, buf, len);
}
/*
* Write memory.
*/
void
write_mem(int boardnum, char *buf, int addr, int len)
{
int fd = open_mem(boardnum);
if (fd == -1) return;
lseek(fd, (off_t) addr, 0);
write(fd, buf, len);
}
/*
* swap routines
*/
static ushort
swapshort(ushort value)
{
value &= 0xffff;
return ((value << 8) | (value >> 8));
}
static ulong
swaplong (ulong val)
{
const ulong mask = 0x00ff00ff;
ulong d = (val << 16) | (val >> 16);
return ((d >> 8) & mask) | ((d & mask) << 8);
}
/*
* This is the request/response processing loop
*/
void
process(int fd)
{
int len;
int rc;
rw_t rw;
struct sockaddr client_addr;
int client_len;
int swap;
for (;;)
{
/*
* Get the next request from the client
*/
client_len = sizeof(client_addr);
len = recvfrom(fd, &rw, sizeof(rw), 0,
(struct sockaddr *) &client_addr, &client_len);
if (len < 0)
{
perror("recvfrom");
exit(5);
}
if (0)
printf("%s %d bytes at %x from machine=%d board=%d\n",
rw.rw_req == RW_READ ? "Read" : "Write",
rw.rw_size, rw.rw_addr, rw.rw_machine, rw.rw_adapter);
/*
* Convert request to host byte order (hopefully)
*/
switch (RwEndianness)
{
case RW_ASIS:
break;
case RW_IN_NETWORK_ORDER:
rw.rw_addr = ntohl(rw.rw_addr);
rw.rw_size = ntohs(rw.rw_size);
break;
default:
case RW_DETECT:
if (rw.rw_size & 0xff00)
{
rw.rw_size = swapshort(rw.rw_size);
rw.rw_addr = swaplong(rw.rw_addr);
swap = 1;
}
else
{
swap = 0;
}
break;
}
/*
* Process the request
*/
switch (rw.rw_req)
{
case RW_READ:
read_mem(rw.rw_adapter, rw.rw_data,
rw.rw_addr, rw.rw_size);
break;
case RW_WRITE:
write_mem(rw.rw_adapter, rw.rw_data,
rw.rw_addr, rw.rw_size);
break;
default:
continue;
}
/*
* Convert request back to proper byte order (hopefully)
*/
switch (RwEndianness)
{
case RW_ASIS:
break;
case RW_IN_NETWORK_ORDER:
rw.rw_addr = ntohl(rw.rw_addr);
rw.rw_size = ntohs(rw.rw_size);
break;
default:
case RW_DETECT:
if (swap)
{
rw.rw_size = swapshort(rw.rw_size);
rw.rw_addr = swaplong(rw.rw_addr);
}
break;
}
/*
* Send the reply
*/
rc = sendto(fd, &rw, len, 0,
(struct sockaddr *) &client_addr, client_len);
if (rc != len)
{
perror("sendto");
exit(6);
}
}
}
/*
* The usual usage message
*/
void
usage(void)
{
fprintf(stderr,
"Usage: kmed [options]\n"
"\n"
" A kme server daemon which allows a remote kme client to access\n"
" local memory and devices.\n"
"\n"
"Options:\n"
" -e endian Endianness of protocol (0,1=net,2==autodetect) [2]\n"
" -p port\t Bind to port name or number [kme][2773]\n"
" -d device Name of memory device [/dev/dm/ram%%d]\n"
"\n"
"Standard /etc/services entry:\n"
" kme 2773/udp kme # kme server\n"
);
exit(1);
}
/*
* The main program
*/
int
main(int argc, char *argv[])
{
struct sockaddr_in serv_addr;
int sockfd;
int rc;
int c;
extern char *optarg;
char *portname = "kme";
int port;
/*
* Process options
*/
while ((c = getopt(argc, argv, "?d:p:e:")) != EOF)
switch (c)
{
case 'd': MemName = optarg; break;
case 'p': portname = optarg; break;
case 'e': RwEndianness = atoi(optarg); break;
default: usage(); break;
}
/*
* Get a socket
*/
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("Can't open socket");
exit(2);
}
/*
* Figure out port number we want to use
*/
port = atoi(portname);
if (port == 0)
{
/*
* Get port number from /etc/services entry:
*
* kme 2773/udp kme # kme
*/
struct servent *sep;
sep = getservbyname(portname, "udp");
if (sep == 0)
{
fprintf(stderr, "%s/udp: unknown service\n", portname);
exit(3);
}
port = sep->s_port;
} else {
port = htons(port) ;
}
/*
* Fill in INET address structure
*/
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = port;
/*
* Bind to the socket
*/
rc = bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (rc < 0)
{
perror("bind failed");
exit(4);
}
/*
* Now run the actual server
*/
process(sockfd);
exit(0);
}