Loading...
Searching...
No Matches
transmit.c
Go to the documentation of this file.
/******************************************************************
** transmit.c **************************************************************
****************************************************************************
*
* functions that prepare IR codes for transmitting
*
* Copyright (C) 1999-2004 Christoph Bartelmus <lirc@bartelmus.de>
*
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* if the gap is lower than this value, we will concatenate the
* signals and send the signal chain at a single blow */
#define LIRCD_EXACT_GAP_THRESHOLD 10000
#include "media/lirc.h"
#include "lirc/lirc_log.h"
#include "lirc/transmit.h"
static const logchannel_t logchannel = LOG_LIB;
static struct sbuf {
lirc_t* data;
lirc_t _data[WBUF_SIZE];
int wptr;
int too_long;
int is_biphase;
lirc_t pendingp;
lirc_t pendings;
lirc_t sum;
} send_buffer;
static void send_signals(lirc_t* signals, int n);
static int init_send_or_sim(struct ir_remote* remote, struct ir_ncode* code, int sim, int repeat_preset);
/*
* sending stuff
*/
void send_buffer_init(void)
{
memset(&send_buffer, 0, sizeof(send_buffer));
}
static void clear_send_buffer(void)
{
log_trace2("clearing transmit buffer");
send_buffer.wptr = 0;
send_buffer.too_long = 0;
send_buffer.is_biphase = 0;
send_buffer.pendingp = 0;
send_buffer.pendings = 0;
send_buffer.sum = 0;
}
static void add_send_buffer(lirc_t data)
{
if (send_buffer.wptr < WBUF_SIZE) {
log_trace2("adding to transmit buffer: %u", data);
send_buffer.sum += data;
send_buffer._data[send_buffer.wptr] = data;
send_buffer.wptr++;
} else {
send_buffer.too_long = 1;
}
}
static void send_pulse(lirc_t data)
{
if (send_buffer.pendingp > 0) {
send_buffer.pendingp += data;
} else {
if (send_buffer.pendings > 0) {
add_send_buffer(send_buffer.pendings);
send_buffer.pendings = 0;
}
send_buffer.pendingp = data;
}
}
static void send_space(lirc_t data)
{
if (send_buffer.wptr == 0 && send_buffer.pendingp == 0) {
log_trace("first signal is a space!");
return;
}
if (send_buffer.pendings > 0) {
send_buffer.pendings += data;
} else {
if (send_buffer.pendingp > 0) {
add_send_buffer(send_buffer.pendingp);
send_buffer.pendingp = 0;
}
send_buffer.pendings = data;
}
}
static int bad_send_buffer(void)
{
if (send_buffer.too_long != 0)
return 1;
if (send_buffer.wptr == WBUF_SIZE && send_buffer.pendingp > 0)
return 1;
return 0;
}
static int check_send_buffer(void)
{
int i;
if (send_buffer.wptr == 0) {
log_trace("nothing to send");
return 0;
}
for (i = 0; i < send_buffer.wptr; i++) {
if (send_buffer.data[i] == 0) {
if (i % 2) {
log_trace("invalid space: %d", i);
} else {
log_trace("invalid pulse: %d", i);
}
return 0;
}
}
return 1;
}
static void flush_send_buffer(void)
{
if (send_buffer.pendingp > 0) {
add_send_buffer(send_buffer.pendingp);
send_buffer.pendingp = 0;
}
if (send_buffer.pendings > 0) {
add_send_buffer(send_buffer.pendings);
send_buffer.pendings = 0;
}
}
static void sync_send_buffer(void)
{
if (send_buffer.pendingp > 0) {
add_send_buffer(send_buffer.pendingp);
send_buffer.pendingp = 0;
}
if (send_buffer.wptr > 0 && send_buffer.wptr % 2 == 0)
send_buffer.wptr--;
}
static void send_header(struct ir_remote* remote)
{
if (has_header(remote)) {
send_pulse(remote->phead);
send_space(remote->shead);
}
}
static void send_foot(struct ir_remote* remote)
{
if (has_foot(remote)) {
send_space(remote->sfoot);
send_pulse(remote->pfoot);
}
}
static void send_lead(struct ir_remote* remote)
{
if (remote->plead != 0)
send_pulse(remote->plead);
}
static void send_trail(struct ir_remote* remote)
{
if (remote->ptrail != 0)
send_pulse(remote->ptrail);
}
static void send_data(struct ir_remote* remote, ir_code data, int bits, int done)
{
int i;
int all_bits = bit_count(remote);
int toggle_bit_mask_bits = bits_set(remote->toggle_bit_mask);
ir_code mask;
data = reverse(data, bits);
if (is_rcmm(remote)) {
mask = 1 << (all_bits - 1 - done);
if (bits % 2 || done % 2) {
log_error("invalid bit number.");
return;
}
for (i = 0; i < bits; i += 2, mask >>= 2) {
switch (data & 3) {
case 0:
send_pulse(remote->pzero);
send_space(remote->szero);
break;
/* 2 and 1 swapped due to reverse() */
case 2:
send_pulse(remote->pone);
send_space(remote->sone);
break;
case 1:
send_pulse(remote->ptwo);
send_space(remote->stwo);
break;
case 3:
send_pulse(remote->pthree);
send_space(remote->sthree);
break;
}
data = data >> 2;
}
return;
} else if (is_xmp(remote)) {
if (bits % 4 || done % 4) {
log_error("invalid bit number.");
return;
}
for (i = 0; i < bits; i += 4) {
ir_code nibble;
nibble = reverse(data & 0xf, 4);
send_pulse(remote->pzero);
send_space(remote->szero + nibble * remote->sone);
data >>= 4;
}
return;
}
mask = ((ir_code)1) << (all_bits - 1 - done);
for (i = 0; i < bits; i++, mask >>= 1) {
if (has_toggle_bit_mask(remote) && mask & remote->toggle_bit_mask) {
if (toggle_bit_mask_bits == 1) {
/* backwards compatibility */
data &= ~((ir_code)1);
if (remote->toggle_bit_mask_state & mask)
data |= (ir_code)1;
} else {
if (remote->toggle_bit_mask_state & mask)
data ^= (ir_code)1;
}
}
if (has_toggle_mask(remote) && mask & remote->toggle_mask && remote->toggle_mask_state % 2)
data ^= 1;
if (data & 1) {
if (is_biphase(remote)) {
if (mask & remote->rc6_mask) {
send_space(2 * remote->sone);
send_pulse(2 * remote->pone);
} else {
send_space(remote->sone);
send_pulse(remote->pone);
}
} else if (is_space_first(remote)) {
send_space(remote->sone);
send_pulse(remote->pone);
} else {
send_pulse(remote->pone);
send_space(remote->sone);
}
} else {
if (mask & remote->rc6_mask) {
send_pulse(2 * remote->pzero);
send_space(2 * remote->szero);
} else if (is_space_first(remote)) {
send_space(remote->szero);
send_pulse(remote->pzero);
} else {
send_pulse(remote->pzero);
send_space(remote->szero);
}
}
data = data >> 1;
}
}
static void send_pre(struct ir_remote* remote)
{
if (has_pre(remote)) {
send_data(remote, remote->pre_data, remote->pre_data_bits, 0);
if (remote->pre_p > 0 && remote->pre_s > 0) {
send_pulse(remote->pre_p);
send_space(remote->pre_s);
}
}
}
static void send_post(struct ir_remote* remote)
{
if (has_post(remote)) {
if (remote->post_p > 0 && remote->post_s > 0) {
send_pulse(remote->post_p);
send_space(remote->post_s);
}
send_data(remote, remote->post_data, remote->post_data_bits, remote->pre_data_bits + remote->bits);
}
}
static void send_repeat(struct ir_remote* remote)
{
send_lead(remote);
send_pulse(remote->prepeat);
send_space(remote->srepeat);
send_trail(remote);
}
static void send_code(struct ir_remote* remote, ir_code code, int repeat)
{
if (!repeat || !(remote->flags & NO_HEAD_REP))
send_header(remote);
send_lead(remote);
send_pre(remote);
send_data(remote, code, remote->bits, remote->pre_data_bits);
send_post(remote);
send_trail(remote);
if (!repeat || !(remote->flags & NO_FOOT_REP))
send_foot(remote);
if (!repeat && remote->flags & NO_HEAD_REP && remote->flags & CONST_LENGTH)
send_buffer.sum -= remote->phead + remote->shead;
}
static void send_signals(lirc_t* signals, int n)
{
int i;
for (i = 0; i < n; i++)
add_send_buffer(signals[i]);
}
int send_buffer_put(struct ir_remote* remote, struct ir_ncode* code)
{
return init_send_or_sim(remote, code, 0, 0);
}
int init_sim(struct ir_remote* remote, struct ir_ncode* code, int repeat_preset)
{
return init_send_or_sim(remote, code, 1, repeat_preset);
}
int send_buffer_length(void)
{
return send_buffer.wptr;
}
const lirc_t* send_buffer_data(void)
{
return send_buffer.data;
}
lirc_t send_buffer_sum(void)
{
return send_buffer.sum;
}
static int init_send_or_sim(struct ir_remote* remote, struct ir_ncode* code, int sim, int repeat_preset)
{
int i, repeat = repeat_preset;
if (is_grundig(remote) || is_serial(remote) || is_bo(remote)) {
if (!sim)
log_error("sorry, can't send this protocol yet");
return 0;
}
clear_send_buffer();
if (strcmp(remote->name, "lirc") == 0) {
send_buffer.data[send_buffer.wptr] = LIRC_EOF | 1;
send_buffer.wptr += 1;
goto final_check;
}
if (is_biphase(remote))
send_buffer.is_biphase = 1;
if (!sim) {
if (repeat_remote == NULL)
remote->repeat_countdown = remote->min_repeat;
else
repeat = 1;
}
init_send_loop:
if (repeat && has_repeat(remote)) {
if (remote->flags & REPEAT_HEADER && has_header(remote))
send_header(remote);
send_repeat(remote);
} else {
if (!is_raw(remote)) {
ir_code next_code;
if (sim || code->transmit_state == NULL)
next_code = code->code;
else
next_code = code->transmit_state->code;
if (repeat && has_repeat_mask(remote))
next_code ^= remote->repeat_mask;
send_code(remote, next_code, repeat);
if (!sim && has_toggle_mask(remote)) {
remote->toggle_mask_state++;
if (remote->toggle_mask_state == 4)
remote->toggle_mask_state = 2;
}
send_buffer.data = send_buffer._data;
} else {
if (code->signals == NULL) {
if (!sim)
log_error("no signals for raw send");
return 0;
}
if (send_buffer.wptr > 0) {
send_signals(code->signals, code->length);
} else {
send_buffer.data = code->signals;
send_buffer.wptr = code->length;
for (i = 0; i < code->length; i++)
send_buffer.sum += code->signals[i];
}
}
}
sync_send_buffer();
if (bad_send_buffer()) {
if (!sim)
log_error("buffer too small");
return 0;
}
if (sim)
goto final_check;
if (has_repeat_gap(remote) && repeat && has_repeat(remote)) {
remote->min_remaining_gap = remote->repeat_gap;
remote->max_remaining_gap = remote->repeat_gap;
} else if (is_const(remote)) {
if (min_gap(remote) > send_buffer.sum) {
remote->min_remaining_gap = min_gap(remote) - send_buffer.sum;
remote->max_remaining_gap = max_gap(remote) - send_buffer.sum;
} else {
log_error("too short gap: %u", remote->gap);
remote->min_remaining_gap = min_gap(remote);
remote->max_remaining_gap = max_gap(remote);
return 0;
}
} else {
remote->min_remaining_gap = min_gap(remote);
remote->max_remaining_gap = max_gap(remote);
}
/* update transmit state */
if (code->next != NULL) {
if (code->transmit_state == NULL) {
code->transmit_state = code->next;
} else {
code->transmit_state = code->transmit_state->next;
if (is_xmp(remote) && code->transmit_state == NULL)
code->transmit_state = code->next;
}
}
if ((remote->repeat_countdown > 0 || code->transmit_state != NULL)
&& remote->min_remaining_gap < LIRCD_EXACT_GAP_THRESHOLD) {
if (send_buffer.data != send_buffer._data) {
lirc_t* signals;
int n;
log_trace("unrolling raw signal optimisation");
signals = send_buffer.data;
n = send_buffer.wptr;
send_buffer.data = send_buffer._data;
send_buffer.wptr = 0;
send_signals(signals, n);
}
log_trace("concatenating low gap signals");
if (code->next == NULL || code->transmit_state == NULL)
remote->repeat_countdown--;
send_space(remote->min_remaining_gap);
flush_send_buffer();
send_buffer.sum = 0;
repeat = 1;
goto init_send_loop;
}
log_trace2("transmit buffer ready");
final_check:
if (!check_send_buffer()) {
if (!sim) {
log_error("invalid send buffer");
log_error("this remote configuration cannot be used to transmit");
}
return 0;
}
return 1;
}
send_buffer_put
int send_buffer_put(struct ir_remote *remote, struct ir_ncode *code)
Initializes the global send buffer for transmitting the code in the second argument,...
Definition transmit.c:351
send_buffer_sum
lirc_t send_buffer_sum(void)
Definition transmit.c:380
repeat_remote
struct ir_remote * repeat_remote
Global pointer to the remote that contains the code currently repeating.
Definition ir_remote.c:57
send_buffer_init
void send_buffer_init(void)
Initializes the global sending buffer.
Definition transmit.c:58
send_buffer_data
const lirc_t * send_buffer_data(void)
Definition transmit.c:375
send_buffer_length
int send_buffer_length(void)
Do not document this function.
Definition transmit.c:369
REPEAT_HEADER
#define REPEAT_HEADER
header is also sent before repeat code
Definition ir_remote_types.h:121
ir_code
uint64_t ir_code
Denotes an internal coded representation for an IR transmission.
Definition ir_remote_types.h:43
NO_FOOT_REP
#define NO_FOOT_REP
no foot for key repeats
Definition ir_remote_types.h:119
NO_HEAD_REP
#define NO_HEAD_REP
no header for key repeats
Definition ir_remote_types.h:118
CONST_LENGTH
#define CONST_LENGTH
signal length+gap is always constant
Definition ir_remote_types.h:120
LIRC_EOF
#define LIRC_EOF
Bit manipulator in lirc_t, see lirc.h .
Definition lirc_config.h:90
log_trace
#define log_trace(fmt,...)
Log a trace message.
Definition lirc_log.h:129
log_trace2
#define log_trace2(fmt,...)
Log a trace2 message.
Definition lirc_log.h:139
log_error
#define log_error(fmt,...)
Log an error message.
Definition lirc_log.h:104
logchannel_t
logchannel_t
Log channels used to filter messages.
Definition lirc_log.h:53
ir_ncode
IR Command, corresponding to one (command defining) line of the configuration file.
Definition ir_remote_types.h:60
ir_remote
One remote as represented in the configuration file.
Definition ir_remote_types.h:150