/* * 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; }