Files
bacnet_stack/bacnet-stack/ports/linux/arcnet.c
T
2005-09-06 20:28:52 +00:00

322 lines
9.2 KiB
C

#include <stdint.h>
#include "bacdef.h"
#include "arcnet.h"
#include "net.h"
// my local device data - MAC address
uint8_t ARCNET_MAC_Address = 0;
// ARCNET file handle
static int ARCNET_Sock_FD = -1;
// ARCNET socket address (has the interface name)
static struct sockaddr ARCNET_Socket_Address;
// Broadcast address
#define ARCNET_BROADCAST 0
bool arcnet_valid(void)
{
return (ARCNET_Sock_FD >= 0);
}
void arcnet_cleanup(void)
{
if (arcnet_valid())
close(ARCNET_Sock_FD);
ARCNET_Sock_FD = -1;
return;
}
static int arcnet_bind(char *interface_name)
{
int sock_fd = -1; // return value
struct ifreq ifr;
int rv; // return value - error value from df or ioctl call
fprintf(stderr,"arcnet: opening \"%s\"\n",interface_name);
// note: on some systems you may have to add or enable in
// modules.conf (or in modutils/alias on Debian with update-modules)
// alias net-pf-17 af_packet
// Then follow it by: # modprobe af_packet
if ((sock_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0)
{
/* Error occured */
fprintf(stderr,
"arcnet: Error opening socket: %s\n",
strerror(errno));
fprintf(stderr,
"You might need to add the following to modules.conf\n"
"(or in /etc/modutils/alias on Debian with update-modules):\n"
"alias net-pf-17 af_packet\n"
"Also, add af_packet to /etc/modules.\n"
"Then follow it by:\n"
"# modprobe af_packet\n");
exit(-1);
}
if (ARCNET_Sock_FD >= 0)
{
/* Bind the socket to an interface name so we only get packets from it */
ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET;
//ARCNET_Socket_Address.sa_family = PF_INET;
/* Clear the memory before copying */
memset(ARCNET_Socket_Address.sa_data, '\0',
sizeof(ARCNET_Socket_Address.sa_data));
/* Strcpy the interface name into the address */
strncpy(ARCNET_Socket_Address.sa_data, interface_name,
sizeof(ARCNET_Socket_Address.sa_data)-1);
fprintf(stderr,"arcnet: binding \"%s\"\n",ARCNET_Socket_Address.sa_data);
if (bind(sock_fd, &ARCNET_Socket_Address,
sizeof(ARCNET_Socket_Address)) != 0)
{
/* Bind problem, close socket and return */
fprintf(stderr,
"arcnet: Unable to bind socket : %s\n",
strerror(errno));
fprintf(stderr,
"You might need to add the following to modules.conf\n"
"(or in /etc/modutils/alias on Debian with update-modules):\n"
"alias net-pf-17 af_packet\n"
"Also, add af_packet to /etc/modules.\n"
"Then follow it by:\n"
"# modprobe af_packet\n");
/* Close the socket */
close(sock_fd);
exit(-1);
}
}
strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
rv = ioctl(sock_fd, SIOCGIFHWADDR, &ifr);
if (rv != -1) /* worked okay */
ARCNET_MAC_Address = ifr.ifr_hwaddr.sa_data[0];
fprintf(stderr,"arcnet: MAC=%02Xh\n",ARCNET_MAC_Address);
atexit(arcnet_cleanup);
return sock_fd;
}
bool arcnet_init(char *interface_name)
{
ARCNET_Sock_FD = arcnet_bind(interface_name);
return arcnet_valid();
}
/* function to send a packet out the socket */
/* returns number of bytes sent on success, negative on failure */
int arcnet_send(
BACNET_ADDRESS *dest, // destination address
BACNET_ADDRESS *src, // source address
uint8_t *pdu, // any data to be sent - may be null
unsigned pdu_len) // number of bytes of data
{
int bytes = 0;
uint8_t mtu[512] = { 0 };
int mtu_len = 0;
struct archdr *pkt = (struct archdr *)mtu;
// don't waste time if the socket is not valid
if (ARCNET_Sock_FD < 0)
{
fprintf(stderr, "arcnet: socket is invalid!\n");
return -1;
}
/* load destination MAC address */
if (dest->mac_len == 1)
pkt->hard.dest = dest->mac[0];
else
{
fprintf(stderr, "arcnet: invalid destination MAC address!\n");
return -2;
}
if (src->mac_len == 1)
pkt->hard.source = src->mac[0];
else
{
fprintf(stderr, "arcnet: invalid source MAC address!\n");
return -3;
}
if ((ARC_HDR_SIZE + pdu_len) > 512)
{
fprintf(stderr, "arcnet: PDU is too big to send!\n");
return -4;
}
/* Logical PDU portion */
pkt->soft.raw[0] = 0xCD; /* SC for BACnet */
pkt->soft.raw[1] = 0x82; /* DSAP for BACnet */
pkt->soft.raw[2] = 0x82; /* SSAP for BACnet */
pkt->soft.raw[3] = 0x03; /* Control byte in header */
memcpy(&pkt->soft.raw[4], pdu, pdu_len);
/* packet length */
mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC*/ + pdu_len;
/* Send the packet */
bytes =
sendto(ARCNET_Sock_FD, &mtu, mtu_len, 0,
(struct sockaddr *) &ARCNET_Socket_Address,
sizeof(ARCNET_Socket_Address));
/* did it get sent? */
if (bytes < 0)
fprintf(stderr,"arcnet: Error sending packet: %s\n",
strerror(errno));
return bytes;
}
/* function to send a PDU out the socket */
/* returns number of bytes sent on success, negative on failure */
int arcnet_send_pdu(
BACNET_ADDRESS *dest, // destination address
uint8_t *pdu, // any data to be sent - may be null
unsigned pdu_len) // number of bytes of data
{
BACNET_ADDRESS src = {0}; // source address
src.mac[0] = ARCNET_MAC_Address;
src.mac_len = 1;
return arcnet_send(dest, // destination address
&src, // source address
pdu, // any data to be sent - may be null
pdu_len); // number of bytes of data
}
// receives an framed packet
// returns the number of octets in the PDU, or zero on failure
uint16_t arcnet_receive(
BACNET_ADDRESS *src, // source address
uint8_t *pdu, // PDU data
uint16_t max_pdu, // amount of space available in the PDU
unsigned timeout) // milliseconds to wait for a packet
{
int received_bytes;
uint8_t buf[512] = {0}; // data
uint16_t pdu_len = 0; // return value
fd_set read_fds;
int max;
struct timeval select_timeout;
struct archdr *pkt = (struct archdr *)buf;
/* Make sure the socket is open */
if (ARCNET_Sock_FD <= 0)
return 0;
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000)
{
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
}
else
{
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET(ARCNET_Sock_FD, &read_fds);
max = ARCNET_Sock_FD;
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
received_bytes = read(ARCNET_Sock_FD, &buf[0], sizeof(buf));
else
return 0;
/* See if there is a problem */
if (received_bytes < 0) {
// EAGAIN Non-blocking I/O has been selected
// using O_NONBLOCK and no data
// was immediately available for reading.
if (errno != EAGAIN)
fprintf(stderr,"ethernet: Read error in receiving packet: %s\n",
strerror(errno));
return 0;
}
if (received_bytes == 0)
return 0;
printf("arcnet: received %u bytes from %02Xh (proto==%02Xh)\n",
received_bytes, pkt->hard.source, pkt->soft.raw[0]);
if (pkt->hard.source == ARCNET_MAC_Address) {
fprintf(stderr,"arcnet: self sent packet?\n");
return 0;
}
if (pkt->soft.raw[0] != 0xCD) {
fprintf(stderr,"arcnet: Non-BACnet packet.\n");
return 0;
}
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
(pkt->hard.dest != ARCNET_BROADCAST))
{
fprintf(stderr,"arcnet: This packet is not for us.\n");
return 0;
}
if ((pkt->soft.raw[1] != 0x82) || /* DSAP */
(pkt->soft.raw[2] != 0x82) || /* LSAP */
(pkt->soft.raw[3] != 0x03)) /* LLC Control */
{
fprintf(stderr,"arcnet: BACnet packet has invalid LLC.\n");
return 0;
}
/* It must be addressed to us or be a Broadcast */
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
(pkt->hard.dest != ARCNET_BROADCAST))
{
fprintf(stderr,"arcnet: This packet is not for us.\n");
return 0;
}
/* copy the source address */
src->mac_len = 1;
src->mac[0] = pkt->hard.source;
/* compute the PDU length */
pdu_len = received_bytes - ARC_HDR_SIZE;
pdu_len -= 4 /* SC, DSAP, SSAP, LLC Control */ ;
// copy the buffer into the PDU
if (pdu_len < max_pdu)
memmove(&pdu[0],&pkt->soft.raw[4],pdu_len);
// silently ignore packets that are too large
else
pdu_len = 0;
return pdu_len;
}
void arcnet_get_my_address(BACNET_ADDRESS *my_address)
{
int i = 0;
my_address->mac_len = 1;
my_address->mac[0] = ARCNET_MAC_Address;
my_address->net = 0; // local only, no routing
my_address->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++)
{
my_address->adr[i] = 0;
}
return;
}
void arcnet_get_broadcast_address(
BACNET_ADDRESS *dest) // destination address
{
int i = 0; // counter
if (dest)
{
dest->mac[0] = ARCNET_BROADCAST;
dest->mac_len = 1;
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; // len=0 denotes broadcast address
for (i = 0; i < MAX_MAC_LEN; i++)
{
dest->adr[i] = 0;
}
}
return;
}