diff options
author | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-02-15 02:24:18 +0000 |
---|---|---|
committer | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-02-15 02:24:18 +0000 |
commit | 41e0e5c06fead266ce179424b18134aeac2c5762 (patch) | |
tree | 246b5055fddf29d43c4d1a7c195ff3f3e1460ac4 /xpp/card_global.c | |
parent | 6aa681528c72a21a011883dcdcd35130bd066eac (diff) |
initial import of Xorcom Astribank driver (issue #6452, with minor mods)
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@949 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/card_global.c')
-rw-r--r-- | xpp/card_global.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/xpp/card_global.c b/xpp/card_global.c new file mode 100644 index 0000000..8b73f6e --- /dev/null +++ b/xpp/card_global.c @@ -0,0 +1,256 @@ +/* + * 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 "xdefs.h" +#include "xpd.h" +#include "xpp_zap.h" +#include "xproto.h" +#include <linux/module.h> + +static const char rcsid[] = "$Id$"; + +extern int print_dbg; +static bool pcm_valid(xpd_t *xpd, xpacket_t *pack); + +/*---------------- GLOBAL Protocol Commands -------------------------------*/ + +static bool global_packet_is_valid(xpacket_t *pack); +static void global_packet_dump(xpacket_t *pack); + +/*---------------- GLOBAL: HOST COMMANDS ----------------------------------*/ + +/* 0x04 */ HOSTCMD(GLOBAL, DESC_REQ, int xpd_num) +{ + int ret = 0; + xpacket_t *pack; + + if(!xbus) { + DBG("NO XBUS\n"); + return -EINVAL; + } + XPACKET_NEW(pack, xbus, GLOBAL, DESC_REQ, xpd_num); + DBG("on %s #%d\n", xbus->busname, xpd_num); + ret = packet_send(xbus, pack); + XBUS_COUNTER(xbus, DESC_REQ)++; + return ret; +} + +/* 0x11 */ HOSTCMD(GLOBAL, PCM_WRITE, xpp_line_t lines, volatile byte *buf) +{ + int ret = 0; + xpacket_t *pack; + byte *pcm; + byte *start_pcm; + int i; + extern ulong pcm_gen; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; + if(pcm_gen != 0) + return 0; +// if(lines == 0) +// return 0; + + /* + * FIXME: Workaround a bug in sync code of the Astribank. + * Send dummy PCM for sync. + */ + if(lines == 0) + lines = BIT(0); + + XPACKET_NEW(pack, xbus, GLOBAL, PCM_WRITE, xpd->id); + RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines; + start_pcm = pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm); + for(i = 0; i < CHANNELS_PERXPD; i++) { + if(IS_SET(lines, i)) { + memcpy(pcm, (byte *)buf, ZT_CHUNKSIZE); + pcm += ZT_CHUNKSIZE; + } + buf += ZT_CHUNKSIZE; + } + pack->datalen = sizeof(xpp_line_t) + (pcm - start_pcm); + packet_send(xbus, pack); + XPD_COUNTER(xpd, PCM_WRITE)++; + XBUS_COUNTER(xbus, PCM_WRITE)++; + return ret; +} + +/* 0x19 */ HOSTCMD(GLOBAL, SYNC_SOURCE, bool setit, bool is_master) +{ + xpacket_t *pack; + byte mask = 0; + + BUG_ON(!xbus); + BUG_ON(!xpd); + if(is_master) + mask |= BIT(0); + if(!setit) + mask |= BIT(1); + DBG("SYNC_SOURCE %s setit=%s is_master=%s (mask=0x%X)\n", + xpd->xpdname, (setit)?"yes":"no", (is_master)?"yes":"no", mask); + XPACKET_NEW(pack, xbus, GLOBAL, SYNC_SOURCE, xpd->id); + RPACKET_FIELD(pack, GLOBAL, SYNC_SOURCE, mask) = mask; + packet_send(xbus, pack); + return 0; +} + +/*---------------- GLOBAL: Astribank Reply Handlers -----------------------*/ + +HANDLER_DEF(GLOBAL, DEV_DESC) +{ + byte rev = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, rev); + byte type = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, type); + xpp_line_t line_status = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, line_status); + int xpd_num = XPD_NUM(pack->content.addr); + struct card_desc_struct *card_desc; + + DBG("xpd=%d type=%d rev=%d line_status=0x%04X\n", + xpd_num, type, rev, line_status); + if((card_desc = kmalloc(sizeof(struct card_desc_struct), GFP_ATOMIC)) == NULL) { + ERR("%s: Card description allocation failed.\n", __FUNCTION__); + return -ENOMEM; + } + memset(card_desc, 0, sizeof(struct card_desc_struct)); + card_desc->magic = CARD_DESC_MAGIC; + card_desc->xbus = xbus; + card_desc->type = type; + card_desc->rev = rev; + card_desc->xpd_num = xpd_num; + INIT_WORK(&card_desc->work, card_detected, card_desc); + DBG("Queueing xpp_worker for xpd %d\n", xpd_num); + if(!queue_work(xpp_worker, &card_desc->work)) { + ERR("Failed to queue card description work\n"); + return -EINVAL; + } + return 0; +} + +HANDLER_DEF(GLOBAL, PCM_READ) +{ + /* FIXME: work around temporary hardware bug */ + xpp_line_t lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines); + const byte *pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm); + volatile u_char *readchunk; + volatile u_char *r; + unsigned long flags; + int i; + + if(!xpd) { +#if 0 + int xpd_num = XPD_NUM(pack->content.addr); + NOTICE("%s: received %s for non-existing xpd: %d\n", + __FUNCTION__, cmd->name, xpd_num); +#endif + return -EPROTO; + } + // DBG("lines=0x%04X\n", lines); + + if(!pcm_valid(xpd, pack)) { + return -EPROTO; + } + spin_lock_irqsave(&xpd->lock, flags); + if (xpd->timer_count & 1) { + /* First part */ + r = readchunk = xpd->readchunk; + } else { + r = readchunk = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD; + } + + /* Copy PCM and put each channel in its index */ + for (i = 0; i < CHANNELS_PERXPD; i++) { + if(IS_SET(lines, i)) { + memcpy((u_char *)r, pcm, ZT_CHUNKSIZE); + //memset((u_char *)r, 0x5A, ZT_CHUNKSIZE); // DEBUG + pcm += ZT_CHUNKSIZE; + } + r += ZT_CHUNKSIZE; + } + + XPD_COUNTER(xpd, PCM_READ)++; + XBUS_COUNTER(xpd->xbus, PCM_READ)++; + spin_unlock_irqrestore(&xpd->lock, flags); + xpp_tick((unsigned long)xpd); + return 0; +} + +HANDLER_DEF(GLOBAL, SYNC_REPLY) +{ + if(!xpd) { + int xpd_num = XPD_NUM(pack->content.addr); + NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num); + return -EPROTO; + } + DBG("SYNC_REPLY: 0x%X\n", RPACKET_FIELD(pack, GLOBAL, SYNC_REPLY, mask)); + return 0; +} + + +xproto_table_t PROTO_TABLE(GLOBAL) = { + .entries = { + /* Card Opcode */ + XENTRY( GLOBAL, DEV_DESC ), + XENTRY( GLOBAL, PCM_READ ), + XENTRY( GLOBAL, SYNC_REPLY ), + }, + .name = "GLOBAL", + .packet_is_valid = global_packet_is_valid, + .packet_dump = global_packet_dump, +}; + +static bool global_packet_is_valid(xpacket_t *pack) +{ + const xproto_entry_t *xe; + + //DBG("\n"); + xe = xproto_global_entry(pack->content.opcode); + return xe != NULL; +} + +static void global_packet_dump(xpacket_t *pack) +{ + DBG("\n"); +} + +static bool pcm_valid(xpd_t *xpd, xpacket_t *pack) +{ + xpp_line_t lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines); + int i; + int count = 0; + + BUG_ON(!pack); + BUG_ON(pack->content.opcode != XPROTO_NAME(GLOBAL, PCM_READ)); + for (i = 0; i < CHANNELS_PERXPD; i++) + if(IS_SET(lines, i)) + count++; + if(pack->datalen != (sizeof(xpp_line_t) + count * 8)) { + static int rate_limit = 0; + + XPD_COUNTER(xpd, RECV_ERRORS)++; + if((rate_limit++ % 1000) <= 10) { + ERR("BAD PCM REPLY: pack->datalen=%d, count=%d\n", pack->datalen, count); + } + return 0; + } + return 1; +} + |