summaryrefslogtreecommitdiff
path: root/xpp/xproto.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpp/xproto.c')
-rw-r--r--xpp/xproto.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/xpp/xproto.c b/xpp/xproto.c
new file mode 100644
index 0000000..5a0d5c6
--- /dev/null
+++ b/xpp/xproto.c
@@ -0,0 +1,334 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2004-2005, Xorcom
+ *
+ * All rights reserved.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "xpd.h"
+#include "xproto.h"
+#include "xpp_zap.h"
+#include <linux/module.h>
+
+static const char rcsid[] = "$Id$";
+
+extern int print_dbg;
+static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack);
+
+static const xproto_table_t *xprotocol_tables[XPD_TYPE_NOMODULE];
+
+bool valid_xpd_addr(const xpd_addr_t *addr)
+{
+ return ((addr->bank_num & ~0x1) == 0) && ((addr->card_id & ~0x3) == 0);
+}
+
+int xpd_addr2num(const xpd_addr_t *addr)
+{
+ BUG_ON(!valid_xpd_addr(addr));
+ return addr->bank_num * 4 + addr->card_id;
+}
+
+void xpd_set_addr(xpd_addr_t *addr, int xpd_num)
+{
+ if(xpd_num < 4) {
+ addr->card_id = xpd_num;
+ addr->bank_num = 0;
+ } else {
+ addr->card_id = xpd_num % 4;
+ addr->bank_num = xpd_num / 4;
+ }
+}
+
+
+/*---------------- General Protocol Management ----------------------------*/
+
+const xproto_entry_t *xproto_card_entry(const xproto_table_t *table, byte opcode)
+{
+ const xproto_entry_t *xe;
+
+ //DBG("\n");
+ xe = &table->entries[opcode];
+ return (xe->handler != NULL) ? xe : NULL;
+}
+
+const xproto_entry_t *xproto_global_entry(byte opcode)
+{
+ const xproto_entry_t *xe;
+
+ xe = xproto_card_entry(&PROTO_TABLE(GLOBAL), opcode);
+ //DBG("opcode=0x%X xe=%p\n", opcode, xe);
+ return xe;
+}
+
+const xproto_handler_t xproto_global_handler(byte opcode)
+{
+ return xproto_card_handler(&PROTO_TABLE(GLOBAL), opcode);
+}
+
+const xproto_table_t *xproto_table(xpd_type_t cardtype)
+{
+ if(cardtype >= XPD_TYPE_NOMODULE)
+ return NULL;
+ return xprotocol_tables[cardtype];
+}
+
+const xproto_table_t *get_xproto_table(xpd_type_t cardtype)
+{
+ const xproto_table_t *xtable;
+
+ if(cardtype >= XPD_TYPE_NOMODULE)
+ return NULL;
+ xtable = xprotocol_tables[cardtype];
+ if(!xtable) { /* Try to load the relevant module */
+ int ret = request_module("xpd-type-%d", cardtype);
+ if(ret != 0) {
+ NOTICE("%s: Failed to load module for type=%d. exit status=%d.\n",
+ __FUNCTION__, cardtype, ret);
+ /* Drop through: we may be luck... */
+ }
+ xtable = xprotocol_tables[cardtype];
+ }
+ return xtable;
+}
+
+const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode)
+{
+ const xproto_entry_t *xe;
+
+ //DBG("\n");
+ xe = xproto_card_entry(table, opcode);
+ return xe->handler;
+}
+
+const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode)
+{
+ const xproto_entry_t *xe;
+
+ xe = xproto_global_entry(opcode);
+ // DBG("opcode=0x%X xe=%p\n", opcode, xe);
+ if(!xe) {
+ const xproto_table_t *xtable;
+
+ if(!xpd)
+ return NULL;
+ xtable = xproto_table(xpd->type);
+ if(!xtable)
+ return NULL;
+ xe = xproto_card_entry(xtable, opcode);
+ if(!xe)
+ return NULL;
+ }
+ return xe;
+}
+
+const xops_t *get_xops(xpd_type_t xpd_type)
+{
+ const xproto_table_t *proto_table;
+
+ if(xpd_type >= XPD_TYPE_NOMODULE)
+ return NULL;
+ proto_table = xprotocol_tables[xpd_type];
+ if(!proto_table)
+ return NULL;
+ return &proto_table->xops;
+}
+
+int packet_receive(xbus_t *xbus, xpacket_t *pack)
+{
+ int xpd_num;
+
+ if(!valid_xpd_addr(&pack->content.addr)) {
+ static int rate_limit = 0;
+
+ if((rate_limit++ % 5003) < 3)
+ dump_packet("bad address", pack, print_dbg);
+ xbus->ops->packet_free(xbus, pack);
+ return -EPROTO;
+ }
+ xpd_num = XPD_NUM(pack->content.addr);
+#if SOFT_SIMULATOR
+ if(xbus->sim[xpd_num].simulated) {
+ //dump_packet("packet_receive -> simulate", pack, print_dbg);
+ return simulate_xpd(xbus, xpd_num, pack);
+ } else
+#endif
+ {
+ //dump_packet("packet_receive -> process", pack, print_dbg);
+ return packet_process(xbus, xpd_num, pack);
+ }
+}
+
+static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack)
+{
+ byte op;
+ const xproto_entry_t *xe;
+ xproto_handler_t handler;
+ xproto_table_t *table;
+ xpd_t *xpd;
+ int ret = 0;
+
+ BUG_ON(!pack);
+ op = pack->content.opcode;
+ xpd_num = XPD_NUM(pack->content.addr);
+ xpd = xpd_of(xbus, xpd_num);
+ xe = find_xproto_entry(xpd, op);
+ /*-------- Validations -----------*/
+ if(!xe) {
+ ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op);
+ dump_packet("packet_process -- bad command", pack, print_dbg);
+ ret = -EPROTO;
+ goto out;
+ }
+ table = xe->table;
+ BUG_ON(!table);
+ if(!table->packet_is_valid(pack)) {
+ ERR("xpp: %s: wrong size %d for op=0x%02X\n",
+ __FUNCTION__, pack->datalen, op);
+ dump_packet("packet_process -- wrong size", pack, print_dbg);
+ ret = -EPROTO;
+ goto out;
+ }
+ handler = xe->handler;
+ BUG_ON(!handler);
+ XBUS_COUNTER(xbus, RX_BYTES) += pack->datalen;
+ handler(xbus, xpd, xe, pack);
+out:
+ xbus->ops->packet_free(xbus, pack);
+ return ret;
+}
+
+#define VERBOSE_DEBUG 1
+#define ERR_REPORT_LIMIT 20
+
+void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg)
+{
+ byte op = packet->content.opcode;
+
+ if(!print_dbg)
+ return;
+ DBG("%s: @0x%1X%1X OP=0x%02X LEN=%d\n",
+ msg,
+ packet->content.addr.bank_num,
+ packet->content.addr.card_id,
+ op,
+ (byte)packet->datalen);
+#if VERBOSE_DEBUG
+ {
+ int i;
+ byte *p = packet->content.data;
+
+ for(i = 0; i < packet->datalen; i++) {
+ static int limiter = 0;
+
+ if(i >= sizeof(xpacket_raw_t)) {
+ if(limiter < ERR_REPORT_LIMIT) {
+ ERR("%s: length overflow i=%d > sizeof(xpacket_raw_t)=%d\n",
+ __FUNCTION__, i+1, sizeof(xpacket_raw_t));
+ } else if(limiter == ERR_REPORT_LIMIT) {
+ ERR("%s: error packet #%d... squelsh reports.\n",
+ __FUNCTION__, limiter);
+ }
+ limiter++;
+ break;
+ }
+ DBG(" %2d> %02X\n", i+1, p[i]);
+ }
+ }
+#endif
+}
+
+const char *xproto_name(xpd_type_t xpd_type)
+{
+ const xproto_table_t *proto_table;
+
+ BUG_ON(xpd_type >= XPD_TYPE(NOMODULE));
+ proto_table = xprotocol_tables[xpd_type];
+ if(!proto_table)
+ return NULL;
+ return proto_table->name;
+}
+
+#define CHECK_XOP(f) \
+ if(!(xops)->f) { \
+ ERR("%s: missing xmethod %s [%s (%d)]\n", __FUNCTION__, #f, name, type); \
+ return -EINVAL; \
+ }
+
+int xproto_register(const xproto_table_t *proto_table)
+{
+ int type;
+ const char *name;
+ const xops_t *xops;
+
+ BUG_ON(!proto_table);
+ type = proto_table->type;
+ name = proto_table->name;
+ if(type >= XPD_TYPE(NOMODULE)) {
+ NOTICE("%s: Bad xproto type %d\n", __FUNCTION__, type);
+ return -EINVAL;
+ }
+ DBG("%s (%d)\n", name, type);
+ if(xprotocol_tables[type])
+ NOTICE("%s: overriding registration of %s (%d)\n", __FUNCTION__, name, type);
+ xops = &proto_table->xops;
+ CHECK_XOP(card_new);
+ CHECK_XOP(card_init);
+ CHECK_XOP(card_remove);
+ CHECK_XOP(card_tick);
+ CHECK_XOP(SYNC_SOURCE);
+ CHECK_XOP(PCM_WRITE);
+ CHECK_XOP(CHAN_ENABLE);
+ CHECK_XOP(CHAN_POWER);
+ CHECK_XOP(CHAN_CID);
+ CHECK_XOP(RING);
+ CHECK_XOP(SETHOOK);
+ CHECK_XOP(LED);
+ CHECK_XOP(RELAY_OUT);
+
+ xprotocol_tables[type] = proto_table;
+ return 0;
+}
+
+void xproto_unregister(const xproto_table_t *proto_table)
+{
+ int type;
+ const char *name;
+
+ BUG_ON(!proto_table);
+ type = proto_table->type;
+ name = proto_table->name;
+ DBG("%s (%d)\n", name, type);
+ if(type >= XPD_TYPE(NOMODULE)) {
+ NOTICE("%s: Bad xproto type %s (%d)\n", __FUNCTION__, name, type);
+ return;
+ }
+ if(!xprotocol_tables[type])
+ NOTICE("%s: xproto type %s (%d) is already unregistered\n", __FUNCTION__, name, type);
+ xprotocol_tables[type] = NULL;
+}
+
+EXPORT_SYMBOL(dump_packet);
+EXPORT_SYMBOL(packet_receive);
+EXPORT_SYMBOL(valid_xpd_addr);
+EXPORT_SYMBOL(xpd_addr2num);
+EXPORT_SYMBOL(xpd_set_addr);
+EXPORT_SYMBOL(xproto_global_entry);
+EXPORT_SYMBOL(xproto_card_entry);
+EXPORT_SYMBOL(xproto_name);
+EXPORT_SYMBOL(xproto_register);
+EXPORT_SYMBOL(xproto_unregister);