/* * Written by Oron Peled * Copyright (C) 2004-2006, 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 "zap_debug.h" #include 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 | ~xpd->no_pcm); 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_each_line(xpd, 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_each_line(xpd, 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) { byte mask = RPACKET_FIELD(pack, GLOBAL, SYNC_REPLY, mask); bool setit = mask & 0x01; 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("%s/%s: SYNC_REPLY: 0x%X %s\n", xpd->xbus->busname, xpd->xpdname, mask, (setit) ? "SET SYNC MASTER" : ""); if(setit) sync_master_is(xpd); 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_each_line(xpd, 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; }