sam7fc/src/telemetrie.c

255 lines
6.8 KiB
C
Raw Normal View History

2008-02-04 22:53:49 +01:00
/***************************************************************************
* Copyright (C) 02/2008 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "board.h" // ARRAY_SIZE()
2008-02-06 17:39:53 +01:00
#include "at91_pitc.h"
2008-02-04 22:53:49 +01:00
#include "telemetrie.h"
2008-02-06 14:20:47 +01:00
#include "memalloc.h"
2008-02-06 17:39:53 +01:00
#include "fifo.h"
2008-02-04 22:53:49 +01:00
/* extern symbols, defined in ldscript */
extern struct tdc_value _tdc_value_table;
extern struct tdc_value _tdc_value_table_end;
2008-02-06 17:39:53 +01:00
/* max. 8x 32 = 256 variables */
static uint32_t tdc_varmap[8];
static struct comm_device *routing_table[8];
2008-02-06 15:30:54 +01:00
/*
* returns:
2008-02-06 17:39:53 +01:00
* -1: on routing error
* 0: no space left in txfifo (caller should retry)
* >0: success
2008-02-06 15:30:54 +01:00
*/
int32_t tdc_transmit(uint32_t addr, struct tdc_pkt_header *head)
2008-02-04 22:53:49 +01:00
{
2008-02-06 17:39:53 +01:00
if (addr >= ARRAY_SIZE(routing_table) || !routing_table[addr])
return -1;
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
return fifo_put(routing_table[addr]->txfifo, (char *)head, head->size);
2008-02-04 22:53:49 +01:00
}
2008-02-06 17:39:53 +01:00
static int32_t tdc_get_vars(void)
2008-02-04 22:53:49 +01:00
{
2008-02-06 17:39:53 +01:00
/* restart point */
static uint32_t id;
struct tdc_value *value = &_tdc_value_table + id;
2008-02-04 22:53:49 +01:00
2008-02-06 15:30:54 +01:00
while (value < &_tdc_value_table_end) {
uint32_t datalen = strlen(value->name);
2008-02-04 22:53:49 +01:00
struct tdc_getvars_reply *reply = alloc(sizeof(struct tdc_getvars_reply) + datalen);
reply->cmd = TDC_REPLY | TDC_ADDR1 | TDC_GETVARS;
reply->size = sizeof(struct tdc_getvars_reply) + datalen;
2008-02-06 17:39:53 +01:00
reply->id = id;
2008-02-06 15:30:54 +01:00
reply->flags = value->flags;
memcpy(reply->name, value->name, datalen);
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
uint32_t ret = tdc_transmit(TDC_ADDR0, ((struct tdc_pkt_header *)reply));
2008-02-04 22:53:49 +01:00
free(reply);
2008-02-06 17:39:53 +01:00
/* push routing error(-1) and retry(0) */
if (ret <= 0)
return ret;
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
id++;
2008-02-06 15:30:54 +01:00
value++;
2008-02-04 22:53:49 +01:00
}
2008-02-06 17:39:53 +01:00
/* dump complete, reset restart point */
id = 0;
return 1;
2008-02-04 22:53:49 +01:00
}
2008-02-06 17:39:53 +01:00
static int32_t tdc_get_value(uint32_t id)
2008-02-04 22:53:49 +01:00
{
2008-02-06 15:30:54 +01:00
struct tdc_value *value = &_tdc_value_table + id;
2008-02-06 17:39:53 +01:00
if (value >= &_tdc_value_table_end)
return -1;
2008-02-06 15:30:54 +01:00
uint32_t datalen = value->flags & TDC_SIZEMASK;
2008-02-04 22:53:49 +01:00
2008-02-06 15:30:54 +01:00
struct tdc_getvalue_reply *reply = alloc(sizeof(struct tdc_getvalue_reply) + datalen);
reply->cmd = TDC_REPLY | TDC_ADDR1 | TDC_GETVALUE;
reply->size = sizeof(struct tdc_getvars_reply) + datalen;
reply->id = id;
memcpy(reply->data, value->data, datalen);
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
int32_t ret = tdc_transmit(TDC_ADDR0, ((struct tdc_pkt_header *)reply));
2008-02-06 15:30:54 +01:00
free(reply);
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
return ret;
2008-02-06 15:30:54 +01:00
}
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
static int32_t tdc_set_value(uint32_t id, uint8_t *data)
2008-02-06 15:30:54 +01:00
{
struct tdc_value *value = &_tdc_value_table + id;
2008-02-06 17:39:53 +01:00
if (value >= &_tdc_value_table_end)
return -1;
2008-02-06 15:30:54 +01:00
uint32_t datalen = value->flags & TDC_SIZEMASK;
2008-02-04 22:53:49 +01:00
2008-02-06 15:30:54 +01:00
// TODO: atomic?
memcpy(value->data, data, datalen);
2008-02-06 17:39:53 +01:00
return 1;
2008-02-04 22:53:49 +01:00
}
2008-02-06 17:39:53 +01:00
static uint32_t tdc_timer_cb(struct pitc_timer *timer)
2008-02-04 22:53:49 +01:00
{
2008-02-06 17:39:53 +01:00
uint32_t i, j;
for (i = 0; i < ARRAY_SIZE(tdc_varmap); i++) {
uint32_t tmp = tdc_varmap[i];
2008-02-06 15:30:54 +01:00
2008-02-06 17:39:53 +01:00
for (j = 0; j < 32; j++) {
if (!tmp)
break;
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
if (tmp & 0x01) {
if (tdc_get_value(i * 32 + j) <= 0)
return PITC_REMOVE_TIMER;
}
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
tmp >>= 1;
}
}
return PITC_RESTART_TIMER;
}
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
static struct pitc_timer tdc_timer = {
.func = tdc_timer_cb,
};
static int32_t tdc_setup_timer(uint32_t interval, uint32_t *varmap)
{
memcpy(tdc_varmap, varmap, sizeof(tdc_varmap));
if (interval > 0) {
tdc_timer.interval = interval;
// TODO: timer already running
pitc_schedule_timer(&tdc_timer);
} else {
// TODO: timer stop
}
return 1;
}
static const struct tdc_hello_reply hello_reply = {
.cmd = TDC_REPLY | TDC_ADDR1 | TDC_HELLO,
.size = sizeof(struct tdc_hello_reply),
.name = "sam7fc-v0.01",
};
/*
* returns:
* -1: on routing error
* 0: no space left in txfifo (caller should retry)
* >0: success
*/
static int32_t tdc_parse_packet(struct tdc_pkt_header *head)
{
2008-02-04 22:53:49 +01:00
/* all replys go to the HOST */
2008-02-06 17:39:53 +01:00
if (head->cmd & TDC_REPLY)
2008-02-06 15:30:54 +01:00
return tdc_transmit(TDC_ADDR0, head);
2008-02-04 22:53:49 +01:00
/* forward this packet? */
if ((head->cmd & TDC_ADDRMASK) != TDC_ADDR1) {
uint32_t addr = (head->cmd & TDC_ADDRMASK) >> 4;
2008-02-06 15:30:54 +01:00
return tdc_transmit(addr, head);
2008-02-04 22:53:49 +01:00
}
2008-02-06 17:39:53 +01:00
int32_t ret = -1;
2008-02-04 22:53:49 +01:00
/* parse the packet */
switch (head->cmd & TDC_OPCODEMASK) {
case TDC_HELLO:
2008-02-06 17:39:53 +01:00
/* answer the hello */
ret = tdc_transmit(TDC_ADDR0, (struct tdc_pkt_header *)&hello_reply);
2008-02-04 22:53:49 +01:00
break;
2008-02-06 17:39:53 +01:00
case TDC_GETVARS:
ret = tdc_get_vars();
break;
2008-02-04 22:53:49 +01:00
2008-02-06 15:30:54 +01:00
case TDC_GETVALUE: {
2008-02-06 17:39:53 +01:00
struct tdc_getvalue_request *pkt = (struct tdc_getvalue_request *)head;
ret = tdc_get_value(pkt->id);
2008-02-06 15:30:54 +01:00
} break;
case TDC_SETVALUE: {
2008-02-06 17:39:53 +01:00
struct tdc_setvalue_request *pkt = (struct tdc_setvalue_request *)head;
ret = tdc_set_value(pkt->id, pkt->data);
2008-02-06 15:30:54 +01:00
} break;
case TDC_REQVALUES: {
2008-02-06 17:39:53 +01:00
struct tdc_reqvalues_request *pkt = (struct tdc_reqvalues_request *)head;
ret = tdc_setup_timer(pkt->interval, pkt->varmap);
2008-02-06 15:30:54 +01:00
} break;
2008-02-06 17:39:53 +01:00
};
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
/*
* on succes(>0) return size of request,
* and push retry(0) and routing error(-1) up
*/
return (ret > 0) ? head->size : ret;
}
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
void tdc_register_device(uint32_t addr, struct comm_device *device)
{
if (addr < ARRAY_SIZE(routing_table))
routing_table[addr] = device;
}
2008-02-04 22:53:49 +01:00
2008-02-06 17:39:53 +01:00
void tdc_receive(struct comm_device *device)
{
while (1) {
/* peek the header */
struct tdc_pkt_header tmp_head;
uint32_t len = fifo_peek(device->rxfifo, (char *)&tmp_head, sizeof(tmp_head));
if (len != sizeof(tmp_head))
return;
/* peek the whole packet */
struct tdc_pkt_header *head = alloc(tmp_head.size);
len = fifo_peek(device->rxfifo, (char *)head, tmp_head.size);
if (len != tmp_head.size)
return;
/* if it's a hello-request, remember the device as path to the host */
if ((head->cmd & (TDC_OPCODEMASK & TDC_DIR)) == TDC_HELLO)
tdc_register_device(TDC_ADDR0, device);
/* parse packet, remove data if no restart is needed */
int32_t ret = tdc_parse_packet(head);
free(head);
/* some tx-fifo was full(0), return to caller and let it retry */
if (!ret)
return;
/* success(>0) or routing error(-1) -> remove the packet */
fifo_remove(device->rxfifo, tmp_head.size);
}
2008-02-04 22:53:49 +01:00
}