syndilights/open-lighting-architecture/libartnet-1.1.0/artnet/transmit.c

446 lines
12 KiB
C

/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* transmit.c
* Functions to handle sending datagrams
* Copyright (C) 2004-2005 Simon Newton
*/
#include "private.h"
/*
* Send an art poll
*
* @param ip the ip address to send to
* @param ttm the talk to me value, either ARTNET_TTM_DEFAULT,
* ARTNET_TTM_PRIVATE or ARTNET_TTM_AUTO
*/
int artnet_tx_poll(node n, const char *ip, artnet_ttm_value_t ttm) {
artnet_packet_t p;
int ret;
if (n->state.mode != ARTNET_ON)
return ARTNET_EACTION;
if (n->state.node_type == ARTNET_SRV || n->state.node_type == ARTNET_RAW) {
if (ip) {
ret = artnet_net_inet_aton(ip, &p.to);
if (ret)
return ret;
} else {
p.to.s_addr = n->state.bcast_addr.s_addr;
}
memcpy(&p.data.ap.id, ARTNET_STRING, ARTNET_STRING_SIZE);
p.data.ap.opCode = htols(ARTNET_POLL);
p.data.ap.verH = 0;
p.data.ap.ver = ARTNET_VERSION;
p.data.ap.ttm = ~ttm;
p.data.ap.pad = 0;
p.length = sizeof(artnet_poll_t);
return artnet_net_send(n, &p);
} else {
artnet_error("Not sending poll, not a server or raw device");
return ARTNET_EACTION;
}
}
/*
* Send an ArtPollReply
* @param n the node
* @param response true if this reply is in response to a network packet
* false if this reply is due to the node changing it's conditions
*/
int artnet_tx_poll_reply(node n, int response) {
artnet_packet_t reply;
int i;
if (!response && n->state.mode == ARTNET_ON) {
n->state.ar_count++;
}
reply.to = n->state.reply_addr;
reply.type = ARTNET_REPLY;
reply.length = sizeof(artnet_reply_t);
// copy from a poll reply template
memcpy(&reply.data, &n->ar_temp, sizeof(artnet_reply_t));
for (i=0; i< ARTNET_MAX_PORTS; i++) {
reply.data.ar.goodinput[i] = n->ports.in[i].port_status;
reply.data.ar.goodoutput[i] = n->ports.out[i].port_status;
}
snprintf((char *) &reply.data.ar.nodereport,
sizeof(reply.data.ar.nodereport),
"%04hx [%04i] libartnet",
n->state.report_code,
n->state.ar_count);
return artnet_net_send(n, &reply);
}
/*
* Send a tod request
*/
int artnet_tx_tod_request(node n) {
int i;
artnet_packet_t todreq;
todreq.to = n->state.bcast_addr;
todreq.type = ARTNET_TODREQUEST;
todreq.length = sizeof(artnet_todrequest_t);
memset(&todreq.data,0x00, todreq.length);
// set up the data
memcpy(&todreq.data.todreq.id, ARTNET_STRING, ARTNET_STRING_SIZE);
todreq.data.todreq.opCode = htols(ARTNET_TODREQUEST);
todreq.data.todreq.verH = 0;
todreq.data.todreq.ver = ARTNET_VERSION;
todreq.data.todreq.command = ARTNET_TOD_FULL; // todfull
todreq.data.todreq.adCount = 0;
// include all enabled ports
for (i=0; i < ARTNET_MAX_PORTS; i++) {
if (n->ports.out[i].port_enabled) {
todreq.data.todreq.address[todreq.data.todreq.adCount++] = n->ports.out[i].port_addr;
}
}
return artnet_net_send(n, &todreq);
}
/*
* Send a tod data for port number id
* @param id the number of the port to send data for
*/
int artnet_tx_tod_data(node n, int id) {
artnet_packet_t tod;
int lim, remaining, bloc, offset;
int ret = ARTNET_EOK;
// ok we need to check how many uid's we have,
// may need to send more than one datagram
tod.to = n->state.bcast_addr;
tod.type = ARTNET_TODDATA;
tod.length = sizeof(artnet_toddata_t);
memset(&tod.data,0x00, tod.length);
// set up the data
memcpy(&tod.data.toddata.id, ARTNET_STRING, ARTNET_STRING_SIZE);
tod.data.toddata.opCode = htols(ARTNET_TODDATA);
tod.data.toddata.verH = 0;
tod.data.toddata.ver = ARTNET_VERSION;
tod.data.toddata.port = id;
// this is interesting, the spec mentions TOD_ADD and TOD_SUBTRACT, but the
// codes aren't given. The windows drivers don't have these either....
tod.data.toddata.cmdRes = ARTNET_TOD_FULL;
tod.data.toddata.address = n->ports.out[id].port_addr;
tod.data.toddata.uidTotalHi = short_get_high_byte(n->ports.out[id].port_tod.length);
tod.data.toddata.uidTotal = short_get_low_byte(n->ports.out[id].port_tod.length);
remaining = n->ports.out[id].port_tod.length;
bloc = 0;
while (remaining > 0) {
memset(&tod.data.toddata.tod,0x00, ARTNET_MAX_UID_COUNT);
lim = min(ARTNET_MAX_UID_COUNT, remaining);
tod.data.toddata.blockCount = bloc++;
tod.data.toddata.uidCount = lim;
offset = (n->ports.out[id].port_tod.length - remaining) * ARTNET_RDM_UID_WIDTH;
if (n->ports.out[id].port_tod.data != NULL)
memcpy(tod.data.toddata.tod,
n->ports.out[id].port_tod.data + offset,
lim * ARTNET_RDM_UID_WIDTH);
ret = ret || artnet_net_send(n, &tod);
remaining = remaining - lim;
}
return ret;
}
/*
* Send a tod data for port number id
* @param id the number of the port to send data for
*/
int artnet_tx_tod_control(node n,
uint8_t address,
artnet_tod_command_code action) {
artnet_packet_t tod;
tod.to = n->state.bcast_addr;
tod.type = ARTNET_TODCONTROL;
tod.length = sizeof(artnet_todcontrol_t);
memset(&tod.data,0x00, tod.length);
// set up the data
memcpy(&tod.data.todcontrol.id, ARTNET_STRING, ARTNET_STRING_SIZE);
tod.data.todcontrol.opCode = htols(ARTNET_TODCONTROL);
tod.data.todcontrol.verH = 0;
tod.data.todcontrol.ver = ARTNET_VERSION;
tod.data.todcontrol.cmd = action;
tod.data.todcontrol.address = address;
return artnet_net_send(n, &tod);
}
/*
* Send a RDM message
* @param address the universe to address this datagram to
* @param action the action to perform. Either ARTNET_TOD_FULL or
* ARTNET_TOD_FLUSH
*/
int artnet_tx_rdm(node n, uint8_t address, uint8_t *data, int length) {
artnet_packet_t rdm;
int len;
rdm.to = n->state.bcast_addr;
rdm.type = ARTNET_RDM;
rdm.length = sizeof(artnet_rdm_t);
memset(&rdm.data,0x00, rdm.length);
// set up the data
memcpy(&rdm.data.todcontrol.id, ARTNET_STRING, ARTNET_STRING_SIZE);
rdm.data.rdm.opCode = htols(ARTNET_RDM);
rdm.data.rdm.verH = 0;
rdm.data.rdm.ver = ARTNET_VERSION;
rdm.data.rdm.cmd = 0x00;
rdm.data.rdm.address = address;
len = min(length, ARTNET_MAX_RDM_DATA);
memcpy(&rdm.data.rdm.data, data, len);
return artnet_net_send(n, &rdm);
}
/*
* Send a firmware reply
* @param ip the ip address to send to
* @param code the response code
*/
int artnet_tx_firmware_reply(node n, in_addr_t ip,
artnet_firmware_status_code code) {
artnet_packet_t p;
memset(&p, 0x0, sizeof(p));
p.to.s_addr = ip;
p.length = sizeof(artnet_firmware_t);
p.type = ARTNET_FIRMWAREREPLY;
// now build packet
memcpy(&p.data.firmware.id, ARTNET_STRING, ARTNET_STRING_SIZE);
p.data.firmware.opCode = htols(ARTNET_FIRMWAREREPLY);
p.data.firmware.verH = 0;
p.data.firmware.ver = ARTNET_VERSION;
p.data.firmware.type = code;
return artnet_net_send(n, &p);
}
/*
* Send an firmware data datagram
*
* @param firm a pointer to the firmware structure for this transfer
*/
int artnet_tx_firmware_packet(node n, firmware_transfer_t *firm) {
artnet_packet_t p;
uint8_t type = 0;
int data_len, max_len, ret;
memset(&p, 0x0, sizeof(p));
// max value of data_len is 1024;
max_len = ARTNET_FIRMWARE_SIZE * sizeof(p.data.firmware.data[0]);
// calculate length
data_len = firm->bytes_total - firm->bytes_current;
data_len = min(data_len, max_len);
// work out type - 6 cases
if(firm->ubea) {
// ubea upload
if (firm->bytes_current == 0) {
// first
type = ARTNET_FIRMWARE_UBEAFIRST;
} else if (data_len == max_len) {
// cont
type = ARTNET_FIRMWARE_UBEACONT;
} else if (data_len < max_len) {
// last
type = ARTNET_FIRMWARE_UBEALAST;
} else {
// this should never happen, something has gone wrong
artnet_error("Attempting to send %d when the max is %d, very very bad...\n", data_len, max_len);
}
} else {
// firmware upload
if (firm->bytes_current == 0) {
// first
type = ARTNET_FIRMWARE_FIRMFIRST;
} else if (data_len == max_len) {
// cont
type = ARTNET_FIRMWARE_FIRMCONT;
} else if (data_len < max_len) {
// last
type = ARTNET_FIRMWARE_FIRMLAST;
} else {
// this should never happen, something has gone wrong
artnet_error("Attempting to send %d when the max is %d, very very bad...\n", data_len, max_len);
}
}
// set packet properties
p.to.s_addr = firm->peer.s_addr;
p.length = sizeof(artnet_firmware_t);
p.type = ARTNET_FIRMWAREMASTER;
// now build packet
memcpy(&p.data.firmware.id, ARTNET_STRING, ARTNET_STRING_SIZE);
p.data.firmware.opCode = htols(ARTNET_FIRMWAREMASTER);
p.data.firmware.verH = 0;
p.data.firmware.ver = ARTNET_VERSION;
p.data.firmware.type = type;
p.data.firmware.blockId = firm->expected_block;
artnet_misc_int_to_bytes(firm->bytes_total / sizeof(uint16_t),
p.data.firmware.length);
memcpy(&p.data.firmware.data,
firm->data + (firm->bytes_current / sizeof(uint16_t)),
data_len);
if ((ret = artnet_net_send(n, &p))) {
// send failed
return ret;
} else {
// update stats
firm->bytes_current = firm->bytes_current + data_len;
firm->last_time = time(NULL);
firm->expected_block++;
// limit between 0 and 255 (only 8 bits wide)
// we dont' actually need this cause it will be shorted when assigned above
firm->expected_block %= UINT8_MAX;
}
return ARTNET_EOK;
}
// this is called when the node's state changes to rebuild the
// artpollreply packet
int artnet_tx_build_art_poll_reply(node n) {
int i;
// shorten the amount we have to type
artnet_reply_t *ar = &n->ar_temp;
memset(ar, 0x00, sizeof(artnet_reply_t));
memcpy(&ar->id, ARTNET_STRING, ARTNET_STRING_SIZE);
ar->opCode = htols(ARTNET_REPLY);
memcpy(&ar->ip, &n->state.ip_addr.s_addr, 4);
ar->port = htols(ARTNET_PORT);
ar->verH = 0;
ar->ver = 0;
ar->subH = 0;
ar->sub = n->state.subnet;
ar->oemH = n->state.oem_hi;
ar->oem = n->state.oem_lo;
ar->ubea = 0;
// ar->status
// if(n->state
// status need to be recalc everytime
//ar->status
// ESTA Manufacturer ID
// Assigned 18/4/2006
ar->etsaman[0] = n->state.esta_hi;
ar->etsaman[1] = n->state.esta_lo;
memcpy(&ar->shortname, &n->state.short_name, sizeof(n->state.short_name));
memcpy(&ar->longname, &n->state.long_name, sizeof(n->state.long_name));
// the report is generated on every send
// port stuff here
ar->numbportsH = 0;
for (i = ARTNET_MAX_PORTS; i > 0; i--) {
if (n->ports.out[i-1].port_enabled || n->ports.in[i-1].port_enabled)
break;
}
ar->numbports = i;
for (i=0; i< ARTNET_MAX_PORTS; i++) {
ar->porttypes[i] = n->ports.types[i];
ar->goodinput[i] = n->ports.in[i].port_status;
ar->goodoutput[i] = n->ports.out[i].port_status;
ar->swin[i] = n->ports.in[i].port_addr;
ar->swout[i] = n->ports.out[i].port_addr;
}
ar->swvideo = 0;
ar->swmacro = 0;
ar->swremote = 0;
// spares
ar->sp1 = 0;
ar->sp2 = 0;
ar->sp3 = 0;
// hw address
memcpy(&ar->mac, &n->state.hw_addr, ARTNET_MAC_SIZE);
// set style
switch (n->state.node_type) {
case ARTNET_SRV:
ar->style = STSERVER;
break;
case ARTNET_NODE:
ar->style = STNODE;
break;
case ARTNET_MSRV:
ar->style = STMEDIA;
break;
// we should fix this, it'll do for now
case ARTNET_RAW:
ar->style = STNODE;
break;
default:
artnet_error("Node type not recognised!");
ar->style = STNODE;
return ARTNET_ESTATE;
}
return ARTNET_EOK;
}