446 lines
12 KiB
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;
|
|
}
|