From 92e6d005b1107055cdd62f41fc06bda8e98b14b3 Mon Sep 17 00:00:00 2001 From: kpfleming Date: Wed, 31 Jan 2007 17:27:30 +0000 Subject: merge support for the Digium TC400B hardware transcoder git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@2057 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- wctc4xxp/Kbuild | 15 + wctc4xxp/Makefile | 10 + wctc4xxp/base.c | 1699 ++++++++++++++++++++++++++++++++++++++++++ wctc4xxp/codec_test.c | 333 +++++++++ wctc4xxp/tc400m-firmware.bin | Bin 0 -> 1400630 bytes 5 files changed, 2057 insertions(+) create mode 100644 wctc4xxp/Kbuild create mode 100644 wctc4xxp/Makefile create mode 100644 wctc4xxp/base.c create mode 100644 wctc4xxp/codec_test.c create mode 100644 wctc4xxp/tc400m-firmware.bin (limited to 'wctc4xxp') diff --git a/wctc4xxp/Kbuild b/wctc4xxp/Kbuild new file mode 100644 index 0000000..b034819 --- /dev/null +++ b/wctc4xxp/Kbuild @@ -0,0 +1,15 @@ +obj-m += wctc4xxp.o + +EXTRA_CFLAGS := -I$(src)/.. -Wno-undef + +wctc4xxp-objs := base.o + +ifneq ($(HOTPLUG_FIRMWARE),yes) +wctc4xxp-objs += firmware_tc400m.o +endif + +$(obj)/base.o: $(src)/../zaptel.h + +$(obj)/firmware_tc400m.o: $(src)/tc400m-firmware.bin $(obj)/base.o + @echo Making firmware object file for $(notdir $<) + @cd $(src) && ../build_tools/make_firmware_object $(notdir $<) $@ $(obj)/base.o diff --git a/wctc4xxp/Makefile b/wctc4xxp/Makefile new file mode 100644 index 0000000..51770a9 --- /dev/null +++ b/wctc4xxp/Makefile @@ -0,0 +1,10 @@ +ifneq ($(KBUILD_EXTMOD),) + +include $(obj)/Kbuild + +endif + +tests: codec_test + +codec_test: codec_test.c ../zaptel.h + $(CC) -o $@ $< $(CFLAGS) diff --git a/wctc4xxp/base.c b/wctc4xxp/base.c new file mode 100644 index 0000000..df87d43 --- /dev/null +++ b/wctc4xxp/base.c @@ -0,0 +1,1699 @@ +/* + * Wildcard TC400B Interface Driver for Zapata Telephony interface + * + * Written by John Sloan + * and Kevin P. Fleming + * + * Copyright (C) 2006-2007, Digium, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + +#if defined(HOTPLUG_FIRMWARE) +static const char *tc400m_firmware = "tc400m-firmware.bin"; +#else +extern u8 _binary_tc400m_firmware_bin_start[]; +extern void _binary_tc400m_firmware_bin_size; +#endif + +static struct pci_driver driver; + +#define module_printk(fmt, args...) printk("%s: " fmt, driver.name, ## args) +#define debug_printk(test, fmt, args...) if (debug && (test)) printk("%s (%s): " fmt, driver.name, __FUNCTION__, ## args) + +/* #define USE_TEST_HW */ +#define USE_TDM_CONFIG + +#define WC_MAX_IFACES 8 + +/* NUM_CHANNELS must be checked if new firmware (dte_firm.h) is used */ +#define NUM_CHANNELS 97 + +#define DTE_FORMAT_ULAW 0x00 +#define DTE_FORMAT_G723_1 0x04 +#define DTE_FORMAT_ALAW 0x08 +#define DTE_FORMAT_G729A 0x12 +#define DTE_FORMAT_UNDEF 0xFF + +#define G729_LENGTH 20 +#define G723_LENGTH 30 + +#define G729_SAMPLES 160 +#define G723_SAMPLES 240 + +#define G729_BYTES 20 +#define G723_BYTES 20 + +/* 274 for 30ms ulaw, 194 for 20ms ulaw */ +#define OTHER_CMD_LEN 300 + +#define MAX_COMMAND_LEN OTHER_CMD_LEN + +#define ERING_SIZE (NUM_CHANNELS / 2) /* Maximum ring size */ + +#define SFRAME_SIZE MAX_COMMAND_LEN + +#define PCI_WINDOW_SIZE ((2 * 2 * ERING_SIZE * SFRAME_SIZE) + (2 * ERING_SIZE * 4)) + +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +#define RCV_CSMENCAPS 1 +#define RCV_RTP 2 +#define RCV_CSMENCAPS_ACK 3 +#define RCV_OTHER 99 + +/* TDM Commands */ +#define CMD_MSG_TDM_SELECT_BUS_MODE(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x01, 0x00,0x06,0x17,0x04, 0xFF,0xFF, \ + 0x04,0x00 } +#define CMD_MSG_TDM_ENABLE_BUS(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x02, 0x00,0x06,0x05,0x04, 0xFF,0xFF, \ + 0x04,0x00 } +#define CMD_MSG_SUPVSR_SETUP_TDM_PARMS(s,p1,p2,p3) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x10, p1, 0x00,0x06,0x07,0x04, 0xFF,0xFF, \ + p2,0x83, 0x00,0x0C, 0x00,0x00, p3,0x00 } +#define CMD_MSG_TDM_OPT(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x35,0x04, 0xFF,0xFF, \ + 0x00,0x00 } +#define CMD_MSG_DEVICE_SET_COUNTRY_CODE(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x1B,0x04, 0xFF,0xFF, \ + 0x00,0x00 } + +/* CPU Commands */ +#define CMD_MSG_SET_ARM_CLK(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x11,0x04, 0x00,0x00, \ + 0x2C,0x01, 0x00,0x00 } +#define CMD_MSG_SET_SPU_CLK(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x12,0x04, 0x00,0x00, \ + 0x2C,0x01, 0x00,0x00 } +#define CMD_MSG_SPU_FEATURES_CONTROL(s,p1) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x13,0x00, 0xFF,0xFF, \ + p1,0x00 } +#define CMD_MSG_DEVICE_STATUS_CONFIG(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x0F,0x04, 0xFF,0xFF, \ + 0x05,0x00 } + +/* General IP/RTP Commands */ +#define CMD_MSG_SET_ETH_HEADER(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x18, 0x00, 0x00,0x06,0x00,0x01, 0xFF,0xFF, \ + 0x01,0x00, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x00,0x11,0x22,0x33,0x44,0x55, 0x08,0x00 } +#define CMD_MSG_IP_SERVICE_CONFIG(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x02,0x03, 0xFF,0xFF, \ + 0x00,0x02 } +#define CMD_MSG_ARP_SERVICE_CONFIG(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x05,0x01, 0xFF,0xFF, \ + 0x01,0x00 } +#define CMD_MSG_ICMP_SERVICE_CONFIG(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x04,0x03, 0xFF,0xFF, \ + 0x01,0xFF } +#define CMD_MSG_IP_OPTIONS(s) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x06,0x03, 0xFF,0xFF, \ + 0x02,0x00 } + +/* Supervisor channel commands */ +#define CMD_MSG_CREATE_CHANNEL(s,t) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x10,0x00, 0x00,0x00, \ + 0x02,0x00, (t&0x00FF), ((t&0xFF00) >> 8) } +#define CMD_MSG_TRANS_CONNECT(s,e,c1,c2,f1,f2) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x12, 0x00, 0x00,0x06,0x22,0x93, 0x00,0x00, \ + e,0x00, (c1&0x00FF),((c1&0xFF00)>>8), f1,0x00, (c2&0x00FF),((c2&0xFF00)>>8), f2,0x00 } +#define CMD_MSG_DESTROY_CHANNEL(s,t) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x11,0x00, 0x00,0x00, \ + (t&0x00FF),((t&0xFF00)>>8), 0x00, 0x00 } + +/* Individual channel config commands */ +#define CMD_MSG_SET_IP_HDR_CHANNEL(s,c,t2,t1) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00) >> 8),(c&0x00FF), 0x26, 0x00, 0x00,0x02,0x00,0x90, 0x00,0x00, \ + 0x00,0x00, 0x45,0x00, 0x00,0x00, 0x00,0x00, 0x40,0x00, 0x80,0x11, 0x00,0x00, \ + 0xC0,0xA8,0x09,0x03, 0xC0,0xA8,0x09,0x03, \ + ((t2&0xFF00)>>8)+0x50,(t2&0x00FF), ((t1&0xFF00)>>8)+0x50,(t1&0x00FF), 0x00,0x00, 0x00,0x00 } +#define CMD_MSG_VOIP_VCEOPT(s,c,l,w) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x12, 0x00, 0x00,0x02,0x01,0x80, 0x00,0x00, \ + 0x21,l, 0x00,0x1C, 0x04,0x00, 0x00,0x00, w,0x00, 0x80,0x11 } +#define CMD_MSG_VOIP_VOPENA(s,c,f) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x16, 0x00, 0x00,0x02,0x00,0x80, 0x00,0x00, \ + 0x01,0x00, 0x80,f, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x12,0x34, 0x56,0x78, 0x00,0x00 } +#define CMD_MSG_VOIP_VOPENA_CLOSE(s,c) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x0A, 0x00, 0x00,0x02,0x00,0x80, 0x00,0x00, \ + 0x00,0x00, 0x00,0x00 } +#define CMD_MSG_VOIP_INDCTRL(s,c) {0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x0A, 0x00, 0x00,0x02,0x84,0x80, 0x00,0x00, \ + 0x07,0x00, 0x00,0x00 } + +/* CPU ACK command */ +#define CMD_MSG_ACK(s,c) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \ + 0x00,0x01, s, 0xE0, (c&0x00FF), ((c>>8)&0x00FF) } + +/* Wrapper for RTP packets */ +#define CMD_MSG_IP_UDP_RTP(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20) { \ + 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x08,0x00, \ + 0x45,0x00, p1,p2, 0x00,p3, 0x40,0x00, 0x80,0x11, p4,p5, \ + 0xC0,0xA8,0x09,0x03, 0xC0,0xA8,0x09,0x03, p6,p7, p8,p9, p10,p11, p12,p13, \ + 0x80,p14, p15,p16, p17,p18,p19,p20, 0x12,0x34,0x56,0x78} + +struct cmdq { + struct list_head list; + size_t cmdspace; + size_t cmdlen; + u8 cmd[0]; +}; + +#define MAX_PACKET_SIZE 1500 +#define MAX_TOTAL_CMDQ 40 + +struct wcdte { + struct pci_dev *dev; + const char *variety; + unsigned int intcount; + unsigned int rxints; + unsigned int txints; + unsigned int intmask; + int pos; + int freeregion; + int rdbl; + int tdbl; + spinlock_t reglock; + wait_queue_head_t regq; + int rcvflags; + + struct semaphore chansem; + struct semaphore cmdqsem; + struct list_head pending_cmdq; + struct list_head free_cmdq; + u32 total_cmdq; /* total of all cmdq entries, both pending and free */ + + unsigned int last_command_sent; + unsigned int last_rcommand; + unsigned int last_rparm2; + unsigned int seq_num; + long timeout; + + unsigned int ztsnd_rtx; + unsigned int ztsnd_0010_rtx; + + unsigned int numchannels; + unsigned char complexname[40]; + + unsigned long iobase; + dma_addr_t readdma; + dma_addr_t writedma; + dma_addr_t descripdma; + volatile u8 *writechunk; /* write buffers */ + volatile u8 *readchunk; /* read buffers */ + volatile u32 *descripchunk; /* descriptors */ + + int wqueints; + struct workqueue_struct *dte_wq; + struct work_struct dte_work; + + struct zt_transcoder *uencode; + struct zt_transcoder *udecode; +}; + +struct wcdte_desc { + const char *name; + int flags; +}; + +static const struct wcdte_desc wcdte = { "Wildcard TC400P+TC400M", 0 }; + +static struct wcdte *ifaces[WC_MAX_IFACES]; + +struct dte_state { + int encoder; + struct wcdte *wc; + + unsigned int timestamp; + unsigned int seqno; + + unsigned int timeslot_in_num; + unsigned int timeslot_out_num; + + unsigned int chan_in_num; + unsigned int chan_out_num; + + unsigned int packets_sent; + unsigned int packets_received; + + unsigned int last_dte_seqno; + unsigned int dte_seqno_rcv; +}; + +static int debug = 0; +static char *mode[2] = { "g729", "g723" }; +static int mode_count = 2; +u32 debug_packets = 0; +u32 debug_cmd_packets = 0; + +static int create_channel(struct wcdte *wc, int simple, int complicated, int part1_id, int part2_id, unsigned int *dte_chan1, unsigned int *dte_chan2); +static int destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2); + +/* Sanity check values */ +static inline int sanitycheck(struct zt_transcode_header *zth, unsigned int outbytes) +{ + if (zth->dstoffset >= sizeof(zth->dstdata)) + return 0; + if (zth->dstlen >= sizeof(zth->dstdata)) + return 0; + if (outbytes >= sizeof(zth->dstdata)) + return 0; + if ((zth->dstoffset + zth->dstlen + outbytes) >= sizeof(zth->dstdata)) + return 0; + if (zth->srcoffset >= sizeof(zth->srcdata)) + return 0; + if (zth->srclen >= sizeof(zth->srcdata)) + return 0; + if ((zth->srcoffset + zth->srclen) > sizeof(zth->srcdata)) + return 0; + return 1; +} + +static void dump_cmdq(struct wcdte *wc) +{ + struct cmdq *cmdq; + + debug_printk(1, "pending_cmdq: "); + list_for_each_entry(cmdq, &wc->pending_cmdq, list) + printk("%p(%zd) ", cmdq, cmdq->cmdspace); + printk("\n"); + + debug_printk(1, "free_cmdq: "); + list_for_each_entry(cmdq, &wc->free_cmdq, list) + printk("%p(%zd) ", cmdq, cmdq->cmdspace); + printk("\n"); +} + +static struct cmdq *get_free_cmdq(struct wcdte *wc, size_t size_needed) +{ + struct cmdq *winner = NULL; + struct cmdq *candidate = NULL; + size_t candidate_size = MAX_PACKET_SIZE; + struct cmdq *smallest_seen = NULL; + size_t smallest_seen_size = MAX_PACKET_SIZE; + struct cmdq *entry; + + size_needed = ((size_needed / 16) + 1) * 16; + + if (size_needed > MAX_PACKET_SIZE) + return NULL; + + list_for_each_entry(entry, &wc->free_cmdq, list) { + if (entry->cmdspace == size_needed) { + winner = entry; + break; + } else if ((entry->cmdspace > size_needed) && + (entry->cmdspace < candidate_size)) { + candidate = entry; + candidate_size = entry->cmdspace; + } else if (entry->cmdspace < smallest_seen_size) { + smallest_seen = entry; + smallest_seen_size = entry->cmdspace; + } + } + + /* at this point, we either have a winner, a candidate, + a potentially freeable too-small entry, or nothing... + deal with the results + */ + + if (winner) { + list_del_init(&winner->list); + return winner; + } else if (candidate) { + list_del_init(&candidate->list); + return candidate; + } else if (wc->total_cmdq < MAX_TOTAL_CMDQ) { + /* we can make a new entry */ + if (debug) + dump_cmdq(wc); + if ((winner = kmalloc(sizeof(*winner) + size_needed, GFP_KERNEL))) { + debug_printk(1, "created a '%zd' byte cmdq entry at '%p'\n", size_needed, winner); + winner->cmdspace = size_needed; + INIT_LIST_HEAD(&winner->list); + } + return winner; + } else if (smallest_seen) { + /* we can't allocate new entries, but we have a + too-small entry we can free and replace */ + if (debug) + dump_cmdq(wc); + list_del(&smallest_seen->list); + kfree(smallest_seen); + if ((winner = kmalloc(sizeof(*winner) + size_needed, GFP_KERNEL))) { + debug_printk(1, "replaced a '%zd' byte cmdq entry at '%p' with a '%zd' byte one at '%p'\n", smallest_seen_size, smallest_seen, size_needed, winner); + winner->cmdspace = size_needed; + INIT_LIST_HEAD(&winner->list); + } + return winner; + } else { + /* we failed */ + debug_printk(1, "no cmdq entries available\n"); + return NULL; + } +} + +static int queue_cmd(struct wcdte *wc, u8 *data, size_t length) +{ + struct cmdq *cmdq = get_free_cmdq(wc, length); + + if (!cmdq) + return -1; + + cmdq->cmdlen = length; + memcpy(cmdq->cmd, data, length); + list_add_tail(&cmdq->list, &wc->pending_cmdq); + + return 0; +} + +#define send_cmd(wc, command, length, hex) \ + ({ \ + int ret = 0; \ + u8 fifo[] = command; \ + do { \ + if (ret == 2) { \ + wc->ztsnd_rtx++; \ + if (hex == 0x0010) \ + wc->ztsnd_0010_rtx++; \ + } \ + down(&wc->cmdqsem); \ + queue_cmd(wc, fifo, sizeof(fifo)); \ + wc->last_command_sent = hex; \ + __transmit_demand(wc); \ + up(&wc->cmdqsem); \ + ret = waitfor_csmencaps(wc, RCV_CSMENCAPS, 0); \ + if (ret == 1) \ + return 1; \ + } while (ret == 2); \ + }) + +static void dte_init_state(struct dte_state *state_ptr, int encoder, unsigned int channel, struct wcdte *wc) +{ + memset(state_ptr, 0, sizeof(*state_ptr)); + + state_ptr->encoder = encoder; + state_ptr->wc = wc; + + state_ptr->chan_in_num = 999; + state_ptr->chan_out_num = 999; + + if (encoder) { + state_ptr->timeslot_in_num = channel * 2; + state_ptr->timeslot_out_num = channel * 2 + 1; + } else { + state_ptr->timeslot_in_num = channel * 2 + 1; + state_ptr->timeslot_out_num = channel * 2; + } +} + +static inline unsigned int zapfmt_to_dtefmt(unsigned int fmt) +{ + switch (fmt) { + case ZT_FORMAT_G723_1: + return DTE_FORMAT_G723_1; + case ZT_FORMAT_ULAW: + return DTE_FORMAT_ULAW; + case ZT_FORMAT_ALAW: + return DTE_FORMAT_ALAW; + case ZT_FORMAT_G729A: + return DTE_FORMAT_G729A; + default: + return DTE_FORMAT_UNDEF; + } +} + +static inline void __setctl(struct wcdte *wc, unsigned int addr, unsigned int val) +{ + outl(val, wc->iobase + addr); +} + +static inline unsigned int __getctl(struct wcdte *wc, unsigned int addr) +{ + return inl(wc->iobase + addr); +} + +static inline void setctl(struct wcdte *wc, unsigned int addr, unsigned int val) +{ + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + __setctl(wc, addr, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline unsigned int getctl(struct wcdte *wc, unsigned int addr) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&wc->reglock, flags); + val = __getctl(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + + return val; +} + +static inline void reinit_descriptor(struct wcdte *wc, int tx, int dbl, char *s) +{ + unsigned int o2; + + o2 = dbl * 4; + + if (!tx) + o2 += ERING_SIZE * 4; + wc->descripchunk[o2] = cpu_to_le32(0x80000000); + + setctl(wc, 0x0008, 0x00000000); +} + +static inline void __transmit_one(struct wcdte *wc, u8 *data, size_t length) +{ + u32 o2 = wc->tdbl * 4; + volatile u8 *writechunk = (volatile u8 *) wc->writechunk + (wc->tdbl * SFRAME_SIZE); + size_t xmt_length; + + /* Yes... this is a busy loop, that is not interruptible. However, it is + highly unlikely (and testing proves) that the wait for a descriptor + to become available will ever be long enough for this to be an issue. + */ + do {} while ((le32_to_cpu(wc->descripchunk[o2]) & 0x80000000)); + + xmt_length = max(length, (size_t) 64); + + wc->descripchunk[o2 + 1] = cpu_to_le32((le32_to_cpu(wc->descripchunk[o2 + 1]) & 0xFBFFF800) | xmt_length); + + memcpy((void *) writechunk, data, length); + if (length < xmt_length) + memset((void *) writechunk + length, 0, xmt_length - length); + + wc->descripchunk[o2] = cpu_to_le32(0x80000000); + setctl(wc, 0x0008, 0x00000000); /* Transmit Poll Demand */ + + wc->tdbl = (wc->tdbl + 1) % ERING_SIZE; +} + +static inline int __transmit_demand(struct wcdte *wc) +{ + int i; + unsigned int reg; + struct cmdq *cmdq; + + reg = getctl(wc, 0x0028) & 0x00700000; + + /* Nothing to transmit */ + if (list_empty(&wc->pending_cmdq)) + return 1; + + /* pop the first entry off the list */ + cmdq = list_entry(wc->pending_cmdq.next, struct cmdq, list); + list_del_init(&cmdq->list); + + debug_printk(1, "transmitting command at '%p' of '%zd' bytes\n", cmdq, cmdq->cmdlen); + + __transmit_one(wc, cmdq->cmd, cmdq->cmdlen); + + if (debug_packets) { + debug_printk(1, "TX: "); + for (i = 0; i < min((size_t) debug_packets, cmdq->cmdlen); i++) + printk("%02X ", cmdq->cmd[i]); + printk("\n"); + } + + if (debug_cmd_packets && + (cmdq->cmd[12] == 0x88) && + (cmdq->cmd[13] == 0x9B)) { + debug_printk(1, "TX: "); + for (i = 0; i < min((size_t) debug_cmd_packets, cmdq->cmdlen); i++) + printk("%02X ", cmdq->cmd[i]); + printk("\n"); + } + + list_add_tail(&cmdq->list, &wc->free_cmdq); + + return 0; +} + +static inline int transmit_demand(struct wcdte *wc) +{ + int val; + + down(&wc->cmdqsem); + val = __transmit_demand(wc); + up(&wc->cmdqsem); + + return val; +} + +static int dte_operation(struct zt_transcoder_channel *ztc, int op) +{ + struct zt_transcoder_channel *compl_ztc; + struct dte_state *st = ztc->pvt, *compl_st; + struct zt_transcode_header *zth = ztc->tch; + struct wcdte *wc = st->wc; + unsigned char *chars; + unsigned int inbytes = 0; + unsigned int timestamp_inc = 0; + int res = 0; + u32 ipchksum; + + switch (op) { + case ZT_TCOP_ALLOCATE: + if (ztc->chan_built) + break; + down(&wc->chansem); + if (st->encoder) + create_channel(wc, zapfmt_to_dtefmt(zth->srcfmt), zapfmt_to_dtefmt(zth->dstfmt), + st->timeslot_in_num, st->timeslot_out_num, &(st->chan_in_num), + &(st->chan_out_num)); + else + create_channel(wc, zapfmt_to_dtefmt(zth->dstfmt), zapfmt_to_dtefmt(zth->srcfmt), + st->timeslot_out_num, st->timeslot_in_num, &(st->chan_out_num), + &(st->chan_in_num)); + /* Mark this channel as built */ + ztc->chan_built = 1; + ztc->built_fmts = zth->dstfmt | zth->srcfmt; + + /* Mark the channel complement (other half of encoder/decoder pair) as built */ + if (st->encoder) + compl_ztc = &(wc->udecode->channels[st->timeslot_in_num >> 1]); + else + compl_ztc = &(wc->uencode->channels[st->timeslot_in_num >> 1]); + compl_ztc->chan_built = 1; + compl_ztc->built_fmts = zth->dstfmt | zth->srcfmt; + compl_st = compl_ztc->pvt; + compl_st->chan_in_num = st->chan_out_num; + compl_st->chan_out_num = st->chan_in_num; + up(&wc->chansem); + break; + case ZT_TCOP_RELEASE: + down(&wc->chansem); + + if (st->encoder) + compl_ztc = &(wc->udecode->channels[st->timeslot_in_num >> 1]); + else + compl_ztc = &(wc->uencode->channels[st->timeslot_in_num >> 1]); + + /* If the channel complement (other half of the encoder/decoder pair) is not being used... */ + if (!compl_ztc->busy) { + if (st->encoder) + destroy_channel(wc, st->chan_in_num, st->chan_out_num); + else + destroy_channel(wc, st->chan_out_num, st->chan_in_num); + + /* Mark this channel as not built */ + ztc->chan_built = 0; + ztc->built_fmts = 0; + st->chan_in_num = 999; + st->chan_out_num = 999; + + /* Mark the channel complement as not built */ + compl_ztc->chan_built = 0; + compl_ztc->built_fmts = 0; + compl_st = compl_ztc->pvt; + compl_st->chan_in_num = 999; + compl_st->chan_out_num = 999; + } + st->dte_seqno_rcv = 0; + up(&wc->chansem); + break; + case ZT_TCOP_TRANSCODE: + if ((((zth->srcfmt == ZT_FORMAT_ULAW) || (zth->srcfmt == ZT_FORMAT_ALAW)) && + ((zth->dstfmt == ZT_FORMAT_G729A && zth->srclen >= G729_SAMPLES) || + (zth->dstfmt == ZT_FORMAT_G723_1 && zth->srclen >= G723_SAMPLES))) || + ((zth->srcfmt == ZT_FORMAT_G729A) && (zth->srclen >= G729_BYTES)) || + ((zth->srcfmt == ZT_FORMAT_G723_1) && (zth->srclen >= G723_BYTES))) { + struct cmdq *cmdq; + + do { + chars = (u8 *)(zth->srcdata + zth->srcoffset); + + switch (zth->srcfmt) { + case ZT_FORMAT_ULAW: + case ZT_FORMAT_ALAW: + switch (zth->dstfmt) { + case ZT_FORMAT_G729A: + inbytes = G729_SAMPLES; + timestamp_inc = G729_SAMPLES; + break; + case ZT_FORMAT_G723_1: + inbytes = G723_SAMPLES; + timestamp_inc = G723_SAMPLES; + break; + } + break; + case ZT_FORMAT_G729A: + inbytes = G729_BYTES; + timestamp_inc = G729_SAMPLES; + break; + case ZT_FORMAT_G723_1: + inbytes = G723_BYTES; + timestamp_inc = G723_SAMPLES; + break; + } + + zth->srclen -= inbytes; + + { + u8 fifo[] = CMD_MSG_IP_UDP_RTP( + ((inbytes+40) >> 8) & 0xFF, + (inbytes+40) & 0xFF, + st->seqno & 0xFF, + 0x00, + 0x00, + (((st->timeslot_out_num) >> 8)+0x50) & 0xFF, + (st->timeslot_out_num) & 0xFF, + (((st->timeslot_in_num) >> 8)+0x50) & 0xFF, + (st->timeslot_in_num) & 0xFF, + ((inbytes+20) >> 8) & 0xFF, + (inbytes+20) & 0xFF, + 0x00, + 0x00, + zapfmt_to_dtefmt(zth->srcfmt), + ((st->seqno) >> 8) & 0xFF, + (st->seqno) & 0xFF, + ((st->timestamp) >> 24) & 0xFF, + ((st->timestamp) >> 16) & 0xFF, + ((st->timestamp) >> 8) & 0xFF, + (st->timestamp) & 0xFF); + + ipchksum = 0x9869 + (fifo[16] << 8) + fifo[17] + (fifo[18] << 8) + fifo[19]; + while (ipchksum >> 16) + ipchksum = (ipchksum & 0xFFFF) + (ipchksum >> 16); + ipchksum = (~ipchksum) & 0xFFFF; + + fifo[24] = ipchksum >> 8; + fifo[25] = ipchksum & 0xFF; + + st->seqno += 1; + st->timestamp += timestamp_inc; + + down(&wc->cmdqsem); + + if (!(cmdq = get_free_cmdq(wc, sizeof(fifo) + inbytes))) { + up(&wc->cmdqsem); + res = -EIO; + break; + } + + memcpy(cmdq->cmd, fifo, sizeof(fifo)); + memcpy(cmdq->cmd + sizeof(fifo), chars, inbytes); + cmdq->cmdlen = sizeof(fifo) + inbytes; + list_add_tail(&cmdq->list, &wc->pending_cmdq); + __transmit_demand(wc); + up(&wc->cmdqsem); + st->packets_sent++; + zth->srcoffset += inbytes; + } + } while ((((zth->srcfmt == ZT_FORMAT_ULAW) || (zth->srcfmt == ZT_FORMAT_ALAW)) && + ((zth->dstfmt == ZT_FORMAT_G729A && zth->srclen >= G729_SAMPLES) || + (zth->dstfmt == ZT_FORMAT_G723_1 && zth->srclen >= G723_SAMPLES))) || + ((zth->srcfmt == ZT_FORMAT_G729A) && (zth->srclen >= G729_BYTES)) || + ((zth->srcfmt == ZT_FORMAT_G723_1) && (zth->srclen >= G723_BYTES))); + } else { + zt_transcoder_alert(ztc); + res = -EINVAL; + } + + break; + } + + return res; +} + +static void stop_dma(struct wcdte *wc); + +static inline void receiveprep(struct wcdte *wc, int dbl) +{ + volatile u8 *readchunk = wc->readchunk + (dbl * SFRAME_SIZE); + struct zt_transcoder_channel *ztc = NULL; + struct zt_transcode_header *zth = NULL; + struct dte_state *st = NULL; + int o2, i; + unsigned char rseq, rcodec; + unsigned int rcommand, rchannel, rlen, rtp_rseq, rtp_eseq; + u8 *chars = NULL; + unsigned int ztc_ndx; + + o2 = dbl * 4; + o2 += ERING_SIZE * 4; + + if (debug_packets) { + debug_printk(1, "RX: "); + for (i = 0; i < debug_packets; i++) + printk("%02X ", readchunk[i]); + printk("\n"); + } + + if ((readchunk[12] == 0x88) && (readchunk[13] == 0x9B)) { + /* Control in packet */ + if (debug_cmd_packets) { + debug_printk(1, "RX: "); + for (i = 0; i < debug_cmd_packets; i++) + printk("%02X ", readchunk[i]); + printk("\n"); + } + /* See if message must be ACK'd */ + if ((readchunk[17] & 0xC0) == 0) { + rcommand = readchunk[24] | (readchunk[25] << 8); + rchannel = readchunk[18] | (readchunk[19] << 8); + rseq = readchunk[16]; + { + u8 fifo[] = CMD_MSG_ACK(rseq++, rchannel); + + down(&wc->cmdqsem); + queue_cmd(wc, fifo, sizeof(fifo)); + __transmit_demand(wc); + wc->rcvflags = RCV_CSMENCAPS; + wc->last_rcommand = rcommand; + wc->last_rparm2 = readchunk[30] | (readchunk[31] << 8); + wake_up_interruptible(&wc->regq); + up(&wc->cmdqsem); + } + } else { + wc->rcvflags = RCV_CSMENCAPS_ACK; + wake_up_interruptible(&wc->regq); + } + } else if ((readchunk[12] == 0x08) && (readchunk[13] == 0x00) && + (readchunk[50] == 0x12) && (readchunk[51] == 0x34) && + (readchunk[52] = 0x56) && (readchunk[53] == 0x78)) { + /* IP/UDP in packet */ + rchannel = (readchunk[37] | (readchunk[36] << 8)) - 0x5000; + rlen = (readchunk[39] | (readchunk[38] << 8)) - 20; + rtp_rseq = (readchunk[45] | (readchunk[44] << 8)); + rcodec = readchunk[43]; + + ztc_ndx = rchannel >> 1; + + if (ztc_ndx >= wc->numchannels) { + debug_printk(1, "Invalid channel number received (ztc_ndx = %d) (numchannels = %d)\n", ztc_ndx, wc->numchannels); + rcodec = DTE_FORMAT_UNDEF; + } + + switch (rcodec) { + case 0x00: /* ulaw */ + case 0x08: /* alaw */ + ztc = &(wc->udecode->channels[ztc_ndx]); + break; + case 0x04: /* g.723.1 */ + case 0x12: /* g.729 */ + ztc = &(wc->uencode->channels[ztc_ndx]); + break; + } + + zth = ztc->tch; + st = ztc->pvt; + + if (!zth) { + debug_printk(1, "Tried to put data into a freed zth header\n"); + rcodec = DTE_FORMAT_UNDEF; + } else { + chars = (u8 *)(zth->dstdata + zth->dstoffset + zth->dstlen); + st->packets_received++; + } + + if (st->dte_seqno_rcv == 0) { + st->dte_seqno_rcv = 1; + st->last_dte_seqno = rtp_rseq; + } else { + rtp_eseq = (st->last_dte_seqno + 1) & 0xFFFF; + debug_printk(rtp_rseq != rtp_eseq, "Bad seqno from module [%d][%d][%d]\n", rchannel, rtp_rseq, st->last_dte_seqno); + st->last_dte_seqno = rtp_rseq; + } + + switch (rcodec) { + case 0x00: /* ulaw */ + case 0x08: /* alaw */ + if (sanitycheck(zth, rlen) && + ((zth->srcfmt == ZT_FORMAT_G729A && rlen == G729_SAMPLES) || + (zth->srcfmt == ZT_FORMAT_G723_1 && rlen == G723_SAMPLES))) { + memcpy(chars, (void *) readchunk + 54, rlen); + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen; + } else { + ztc->errorstatus = -EOVERFLOW; + } + zt_transcoder_alert(ztc); + break; + case 0x04: /* g.723.1 */ + if (sanitycheck(zth, rlen) && (rlen == G723_BYTES)) { + memcpy(chars, (void *) readchunk + 54, rlen); + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen * 12; + } else { + ztc->errorstatus = -EOVERFLOW; + } + if ((zth->dstsamples % G723_SAMPLES) == 0) + zt_transcoder_alert(ztc); + break; + case 0x12: /* g.729 */ + if (sanitycheck(zth, rlen) && (rlen == G729_BYTES)) { + memcpy(chars, (void *) readchunk + 54, rlen); + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen * 8; + } else { + ztc->errorstatus = -EOVERFLOW; + } + + if ((zth->dstsamples % G729_SAMPLES) == 0) + zt_transcoder_alert(ztc); + } + } +} + +static int check_descriptor(struct wcdte *wc) +{ + int o2 = (ERING_SIZE * 4) + (wc->rdbl * 4); + + if (!(le32_to_cpu(wc->descripchunk[o2]) & 0x80000000)) { + wc->rxints++; + receiveprep(wc, wc->rdbl); + reinit_descriptor(wc, 0, wc->rdbl, "rxchk"); + wc->rdbl = (wc->rdbl + 1) % ERING_SIZE; + + return 1; + } + + return 0; +} + +static void init_descriptors(struct wcdte *wc) +{ + volatile u32 *descrip; + dma_addr_t descripdma; + dma_addr_t writedma; + dma_addr_t readdma; + int x; + + descrip = wc->descripchunk; + descripdma = wc->descripdma; + writedma = wc->writedma; + readdma = wc->readdma; + + for (x = 0; x < ERING_SIZE; x++) { + if (x < ERING_SIZE - 1) + descripdma += 16; + else + descripdma = wc->descripdma; + + /* Transmit descriptor */ + descrip[0] = cpu_to_le32(0x00000000); + descrip[1] = cpu_to_le32(0xe5800000 | (SFRAME_SIZE)); + descrip[2] = cpu_to_le32(writedma + x * SFRAME_SIZE); + descrip[3] = cpu_to_le32(descripdma); + + /* Receive descriptor */ + descrip[0 + ERING_SIZE * 4] = cpu_to_le32(0x80000000); + descrip[1 + ERING_SIZE * 4] = cpu_to_le32(0x01000000 | (SFRAME_SIZE)); + descrip[2 + ERING_SIZE * 4] = cpu_to_le32(readdma + (x * SFRAME_SIZE)); + descrip[3 + ERING_SIZE * 4] = cpu_to_le32(descripdma + (ERING_SIZE * 16)); + + /* Advance descriptor */ + descrip += 4; + } +} + +static void dte_wque_run(struct wcdte *wc) +{ + int res; + + if (wc->wqueints & 0x00000040) { + /* Loop descriptors is available */ + do {} while ((res = check_descriptor(wc))); + } + + /* Handle TX interrupts */ + if (wc->wqueints & 0x00000001) { + wc->txints++; + transmit_demand(wc); + wc->intcount++; + } +} + +ZAP_IRQ_HANDLER(interrupt_handler) +{ + struct wcdte *wc = dev_id; + unsigned int ints; + + if (!(ints = getctl(wc, 0x0028))) + return IRQ_NONE; + + setctl(wc, 0x0028, ints); + + ints &= wc->intmask; + + if (ints & 0x00000041) { + wc->wqueints = ints; + queue_work(wc->dte_wq, &wc->dte_work); + } + + if (!debug) + return IRQ_RETVAL(1); + + debug_printk(ints & 0x00008000, "Abnormal Interrupt\n"); + debug_printk(ints & 0x00002000, "Fatal Bus Error\n"); + debug_printk(ints & 0x00000100, "Receive Stopped\n"); + debug_printk(ints & 0x00000080, "Receive Desciptor Unavailable\n"); + debug_printk(ints & 0x00000020, "Transmit Under-flow\n"); + debug_printk(ints & 0x00000008, "Jabber Timer Time-out\n"); + debug_printk(ints & 0x00000004, "Transmit Descriptor Unavailable\n"); + debug_printk(ints & 0x00000002, "Transmit Processor Stopped\n"); + + return IRQ_RETVAL(1); +} + + +static int hardware_init(struct wcdte *wc) +{ + /* Hardware stuff */ + unsigned int reg; + unsigned long newjiffies; + + /* Initialize descriptors */ + init_descriptors(wc); + + /* Enable I/O Access */ + pci_read_config_dword(wc->dev, 0x0004, ®); + reg |= 0x00000007; + pci_write_config_dword(wc->dev, 0x0004, reg); + + setctl(wc, 0x0000, 0xFFF88001); + + newjiffies = jiffies + HZ/10; + while (((reg = getctl(wc, 0x0000)) & 0x00000001) && (newjiffies > jiffies)); + + /* Configure watchdogs, access, etc */ + setctl(wc, 0x0030, 0x00280048); + setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */); + + reg = getctl(wc, 0x00fc); + setctl(wc, 0x00fc, (reg & ~0x7) | 0x7); + + reg = getctl(wc, 0x00fc); + + return 0; +} + +static inline void setintmask(struct wcdte *wc, unsigned int intmask) +{ + wc->intmask = intmask; + setctl(wc, 0x0038, intmask); +} + +static inline void enable_interrupts(struct wcdte *wc) +{ + setintmask(wc, !debug ? 0x00010041 : 0x0001A1EB); +} + +static void start_dma(struct wcdte *wc) +{ + unsigned int reg; + + wmb(); + setctl(wc, 0x0020, wc->descripdma); + setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE)); + /* Start receiver/transmitter */ + reg = getctl(wc, 0x0030); + setctl(wc, 0x0030, reg | 0x00002002); /* Start XMT and RCD */ + setctl(wc, 0x0010, 0x00000000); /* Receive Poll Demand */ + reg = getctl(wc, 0x0028); + setctl(wc, 0x0028, reg); +} + +static void disable_interrupts(struct wcdte *wc) +{ + setintmask(wc, 0x00000000); + setctl(wc, 0x0084, 0x00000000); +} + +static void stop_dma(struct wcdte *wc) +{ + unsigned int reg; + + disable_interrupts(wc); + setctl(wc, 0x0048, 0x00000000); + /* Reset the part to be on the safe side */ + reg = getctl(wc, 0x0000); + reg |= 0x00000001; + setctl(wc, 0x0000, reg); +} + +static int waitfor_csmencaps(struct wcdte *wc, unsigned int mask, int use_mask) +{ + int ret; + + ret = wait_event_interruptible_timeout(wc->regq, + use_mask ? (wc->rcvflags == mask) : (wc->last_rcommand == wc->last_command_sent), + wc->timeout); + wc->rcvflags = 0; + wc->last_rcommand = 0; + wc->last_command_sent = 0; + + if (ret < 0) { + debug_printk(1, "Wait interrupted, need to stop boot (ret = %d)\n", ret); + return 1; + } + + if (!ret) { + debug_printk(1, "Waitfor CSMENCAPS response timed out (ret = %d)\n", ret); + return 2; + } + + return 0; +} + +static int read_phy(struct wcdte *wc, int location) +{ + int i; + long mdio_addr = 0x0048; + int read_cmd = (0xf6 << 10) | (1 << 5) | location; + int retval = 0; + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1); + getctl(wc, mdio_addr); + setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK); + getctl(wc, mdio_addr); + } + /* Shift the read command bits out. */ + for (i = 17; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + setctl(wc, mdio_addr, MDIO_ENB | dataval); + getctl(wc, mdio_addr); + setctl(wc, mdio_addr, MDIO_ENB | dataval | MDIO_SHIFT_CLK); + getctl(wc, mdio_addr); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + setctl(wc, mdio_addr, MDIO_ENB_IN); + getctl(wc, mdio_addr); + retval = (retval << 1) | ((getctl(wc, mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + setctl(wc, mdio_addr, MDIO_ENB_IN | MDIO_SHIFT_CLK); + getctl(wc, mdio_addr); + } + retval = (retval >> 1) & 0xffff; + + return retval; +} + +void write_phy(struct wcdte *wc, int location, int value) +{ + int i; + int cmd = (0x5002 << 16) | (1 << 23) | (location<<18) | value; + long mdio_addr = 0x0048; + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1); + getctl(wc, mdio_addr); + setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK); + getctl(wc, mdio_addr); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + setctl(wc, mdio_addr, MDIO_ENB | dataval); + getctl(wc, mdio_addr); + setctl(wc, mdio_addr, MDIO_ENB | dataval | MDIO_SHIFT_CLK); + getctl(wc, mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + setctl(wc, mdio_addr, MDIO_ENB_IN); + getctl(wc, mdio_addr); + setctl(wc, mdio_addr, MDIO_ENB_IN | MDIO_SHIFT_CLK); + getctl(wc, mdio_addr); + } + return; +} + +static int boot_processor(struct wcdte *wc, const struct firmware *firmware) +{ + int byteloc, last_byteloc, length, delay_count; + unsigned int reg, ret; + +#if !defined(USE_TEST_HW) + /* Turn off auto negotiation */ + write_phy(wc, 0, 0x2100); + + debug_printk(1, "PHY register 0 = %X", read_phy(wc, 0)); + + /* Set reset */ + setctl(wc, 0x00A0, 0x04000000); + + /* Wait 1000ms to ensure processor reset */ + mdelay(1000); + + /* Clear reset */ + setctl(wc, 0x00A0, 0x04080000); + + /* Wait for Ethernet link */ + for (delay_count = 0; + ((getctl(wc, 0x00fc) & 0xE0000000) != 0xE0000000) && delay_count < 100; + mdelay(100), delay_count++); + + if (delay_count == 100) { + module_printk("Failed to link to DTE processor!\n"); + return 1; + } + + /* Turn off booted LED */ + setctl(wc, 0x00A0, 0x04084000); +#endif /* !defined(USE_TEST_HW) */ + + if (debug) { + reg = getctl(wc, 0x00fc); + debug_printk(1, "LINK STATUS: reg(0xfc) = %X\n", reg); + } + + for (ret = 0, last_byteloc = byteloc = 17; byteloc < (firmware->size - 20); last_byteloc = byteloc) { + length = (firmware->data[byteloc] << 8) | firmware->data[byteloc + 1]; + byteloc += 2; + __transmit_one(wc, firmware->data + byteloc, length); + byteloc += length; + ret = waitfor_csmencaps(wc, RCV_CSMENCAPS_ACK, 1); + if (ret == 1) + return 1; + else if (ret == 2) /* Retransmit if processor times out */ + byteloc = last_byteloc; + } + + wc->timeout = 10 * HZ; + if (waitfor_csmencaps(wc, RCV_CSMENCAPS, 1)) + return 1; + + /* Turn on booted LED */ + setctl(wc, 0x00A0, 0x04080000); + + debug_printk(1, "Successfully booted DTE processor.\n"); + + return 0; +} + +static int create_channel(struct wcdte *wc, int simple, int complicated, int part1_id, + int part2_id, unsigned int *dte_chan1, unsigned int *dte_chan2) +{ + int length = 0; + unsigned char chan1, chan2; + + if (complicated == DTE_FORMAT_G729A) + length = G729_LENGTH; + else if (complicated == DTE_FORMAT_G723_1) + length = G723_LENGTH; + + /* Create complex channel */ + send_cmd(wc, CMD_MSG_CREATE_CHANNEL(wc->seq_num++, part1_id), CMD_MSG_CREATE_CHANNEL_LEN, 0x0010); + chan1 = wc->last_rparm2; + + /* Create simple channel */ + send_cmd(wc, CMD_MSG_CREATE_CHANNEL(wc->seq_num++, part2_id), CMD_MSG_CREATE_CHANNEL_LEN, 0x0010); + chan2 = wc->last_rparm2; + + /* Configure complex channel */ + send_cmd(wc, CMD_MSG_SET_IP_HDR_CHANNEL(wc->seq_num++, chan1, part2_id, part1_id), CMD_MSG_SET_IP_HDR_CHANNEL_LEN, 0x9000); + send_cmd(wc, CMD_MSG_VOIP_VCEOPT(wc->seq_num++, chan1, length, 0), CMD_MSG_VOIP_VCEOPT_LEN, 0x8001); + + /* Configure simple channel */ + send_cmd(wc, CMD_MSG_SET_IP_HDR_CHANNEL(wc->seq_num++, chan2, part1_id, part2_id), CMD_MSG_SET_IP_HDR_CHANNEL_LEN, 0x9000); + send_cmd(wc, CMD_MSG_VOIP_VCEOPT(wc->seq_num++, chan2, length, 0), CMD_MSG_VOIP_VCEOPT_LEN, 0x8001); + + send_cmd(wc, CMD_MSG_TRANS_CONNECT(wc->seq_num++, 1, chan1, chan2, complicated, simple), CMD_MSG_TRANS_CONNECT_LEN, 0x9322); + send_cmd(wc, CMD_MSG_VOIP_INDCTRL(wc->seq_num++, chan1), CMD_MSG_VOIP_INDCTRL_LEN, 0x8084); + send_cmd(wc, CMD_MSG_VOIP_INDCTRL(wc->seq_num++, chan2), CMD_MSG_VOIP_INDCTRL_LEN, 0x8084); + send_cmd(wc, CMD_MSG_VOIP_VOPENA(wc->seq_num++, chan1, complicated), CMD_MSG_VOIP_VOPENA_LEN, 0x8000); + send_cmd(wc, CMD_MSG_VOIP_VOPENA(wc->seq_num++, chan2, simple), CMD_MSG_VOIP_VOPENA_LEN, 0x8000); + + *dte_chan1 = chan1; + *dte_chan2 = chan2; + + return 1; +} + +static int destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2) +{ + /* Turn off both channels */ + send_cmd(wc, CMD_MSG_VOIP_VOPENA_CLOSE(wc->seq_num++, chan1), CMD_MSG_VOIP_VOPENA_CLOSE_LEN, 0x8000); + send_cmd(wc, CMD_MSG_VOIP_VOPENA_CLOSE(wc->seq_num++, chan2), CMD_MSG_VOIP_VOPENA_CLOSE_LEN, 0x8000); + + /* Disconnect the channels */ + send_cmd(wc, CMD_MSG_TRANS_CONNECT(wc->seq_num++, 0, chan1, chan2, 0, 0), CMD_MSG_TRANS_CONNECT_LEN, 0x9322); + + /* Remove the channels */ + send_cmd(wc, CMD_MSG_DESTROY_CHANNEL(wc->seq_num++, chan1), CMD_MSG_DESTROY_CHANNEL_LEN, 0x0011); + send_cmd(wc, CMD_MSG_DESTROY_CHANNEL(wc->seq_num++, chan2), CMD_MSG_DESTROY_CHANNEL_LEN, 0x0011); + + return 1; +} + +static int setup_channels(struct wcdte *wc) +{ +#ifndef USE_TEST_HW + send_cmd(wc, CMD_MSG_SET_ARM_CLK(wc->seq_num++), CMD_MSG_SET_ARM_CLK_LEN, 0x0411); + send_cmd(wc, CMD_MSG_SET_SPU_CLK(wc->seq_num++), CMD_MSG_SET_SPU_CLK_LEN, 0x0412); +#endif + +#ifdef USE_TDM_CONFIG + send_cmd(wc, CMD_MSG_TDM_SELECT_BUS_MODE(wc->seq_num++), CMD_MSG_TDM_SELECT_BUS_MODE_LEN, 0x0417); + send_cmd(wc, CMD_MSG_TDM_ENABLE_BUS(wc->seq_num++), CMD_MSG_TDM_ENABLE_BUS_LEN, 0x0405); + send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x03, 0x20, 0x00), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); + send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x04, 0x80, 0x04), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); + send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x05, 0x20, 0x08), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); + send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x06, 0x80, 0x0C), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); +#endif + + send_cmd(wc, CMD_MSG_SET_ETH_HEADER(wc->seq_num++), CMD_MSG_SET_ETH_HEADER_LEN, 0x0100); + send_cmd(wc, CMD_MSG_IP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_IP_SERVICE_CONFIG_LEN, 0x0302); + send_cmd(wc, CMD_MSG_ARP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_ARP_SERVICE_CONFIG_LEN, 0x0105); + send_cmd(wc, CMD_MSG_ICMP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_ICMP_SERVICE_CONFIG_LEN, 0x0304); + +#ifdef USE_TDM_CONFIG + send_cmd(wc, CMD_MSG_DEVICE_SET_COUNTRY_CODE(wc->seq_num++), CMD_MSG_DEVICE_SET_COUNTRY_CODE_LEN, 0x041B); +#endif + + send_cmd(wc, CMD_MSG_SPU_FEATURES_CONTROL(wc->seq_num++, 0x02), CMD_MSG_SPU_FEATURES_CONTROL_LEN, 0x0013); + send_cmd(wc, CMD_MSG_IP_OPTIONS(wc->seq_num++), CMD_MSG_IP_OPTIONS_LEN, 0x0306); + send_cmd(wc, CMD_MSG_SPU_FEATURES_CONTROL(wc->seq_num++, 0x04), CMD_MSG_SPU_FEATURES_CONTROL_LEN, 0x0013); + +#ifdef USE_TDM_CONFIG + send_cmd(wc, CMD_MSG_TDM_OPT(wc->seq_num++), CMD_MSG_TDM_OPT_LEN, 0x0435); +#endif + + wc->timeout = HZ/100 + 1; + + return 0; +} + +static int __devinit init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res = 0, reg; + struct wcdte *wc; + struct wcdte_desc *d = (struct wcdte_desc *) ent->driver_data; + int x; + unsigned int g729_numchannels, g723_numchannels; + u8 firmware_ver; + unsigned int complexfmts = 0; + struct firmware embedded_firmware; + const struct firmware *firmware = &embedded_firmware; + struct dte_state *encoders; + struct dte_state *decoders; + + for (x = 0; x < (sizeof(ifaces) / sizeof(ifaces[0])); x++) + if (!ifaces[x]) break; + + if (x == (sizeof(ifaces) / sizeof(ifaces[0]))) { + module_printk("Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) + return -EIO; + + if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) + return -ENOMEM; + + memset(wc, 0, sizeof(*wc)); + ifaces[x] = wc; + spin_lock_init(&wc->reglock); + sema_init(&wc->chansem, 1); + sema_init(&wc->cmdqsem, 1); + wc->iobase = pci_resource_start(pdev, 0); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + + wc->seq_num = 6; + wc->timeout = HZ; + + INIT_LIST_HEAD(&wc->pending_cmdq); + INIT_LIST_HEAD(&wc->free_cmdq); + + /* Keep track of whether we need to free the region */ + if (request_region(wc->iobase, 0xff, "wctc4xxp")) + wc->freeregion = 1; + + /* Allocate enough memory for all TX buffers, RX buffers, and descriptors */ + wc->writechunk = pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma); + if (!wc->writechunk) { + module_printk("Unable to allocate DMA-able memory\n"); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + (SFRAME_SIZE * ERING_SIZE); + wc->readdma = wc->writedma + (SFRAME_SIZE * ERING_SIZE); + + wc->descripchunk = (u32 *) (wc->readchunk + (SFRAME_SIZE * ERING_SIZE)); + wc->descripdma = wc->readdma + (SFRAME_SIZE * ERING_SIZE); + + init_waitqueue_head(&wc->regq); + + /* Initialize the work queue */ + wc->dte_wq = create_workqueue("wctc4xxp"); + + INIT_WORK(&wc->dte_work, (void (*)(void *)) dte_wque_run, wc); + +#ifdef HOTPLUG_FIRMWARE + if ((request_firmware(&firmware, tc400m_firmware, &wc->dev->dev) != 0) || !firmware) { + module_printk("Firmware %s not available from userspace\n", tc400m_firmware); + return -1; + } +#else + embedded_firmware.data = _binary_tc400m_firmware_bin_start; + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we do that and then cast that + value to the proper type. + */ + embedded_firmware.size = (size_t) &_binary_tc400m_firmware_bin_size; +#endif + + firmware_ver = firmware->data[0]; + g729_numchannels = firmware->data[1]; + g723_numchannels = firmware->data[2]; + + wc->numchannels = 2048; /* someday... */ + + for (x = 0; x < mode_count; x++) { + if (!strcmp(mode[x], "g729") || + !strcmp(mode[x], "G729")) { + if (!g729_numchannels) { + module_printk("Format '%s' not supported by the firmware for this module; ignored.\n", mode[x]); + continue; + } + strcat(wc->complexname, "G.729A / "); + complexfmts |= ZT_FORMAT_G729A; + wc->numchannels = min(wc->numchannels, g729_numchannels); + } else if (!strcmp(mode[x], "g723") || + !strcmp(mode[x], "G723")) { + if (!g723_numchannels) { + module_printk("Format '%s' not supported by the firmware for this module; ignored.\n", mode[x]); + continue; + } + strcat(wc->complexname, "G.723.1 5.3Kbps / "); + complexfmts |= ZT_FORMAT_G723_1; + wc->numchannels = min(wc->numchannels, g723_numchannels); + } else { + module_printk("Invalid transcoder format specified: %s\n", mode[x]); + } + } + + if (!complexfmts) { + module_printk("No valid transcoder formats specified; module will not be enabled.\n"); + return -1; + } + + wc->complexname[strlen(wc->complexname) - 3] = '\0'; + + wc->uencode = zt_transcoder_alloc(wc->numchannels); + wc->udecode = zt_transcoder_alloc(wc->numchannels); + encoders = kmalloc(sizeof(*encoders) * wc->numchannels, GFP_KERNEL); + decoders = kmalloc(sizeof(*decoders) * wc->numchannels, GFP_KERNEL); + if (!wc->uencode || !wc->udecode || !encoders || !decoders) { + if (wc->uencode) + zt_transcoder_free(wc->uencode); + if (wc->udecode) + zt_transcoder_free(wc->udecode); + if (encoders) + kfree(encoders); + if (decoders) + kfree(decoders); + + return -ENOMEM; + } + + strcpy(wc->udecode->name, wc->variety); + strcpy(wc->uencode->name, wc->variety); + + wc->udecode->srcfmts = wc->uencode->dstfmts = complexfmts; + wc->udecode->dstfmts = wc->uencode->srcfmts = ZT_FORMAT_ULAW | ZT_FORMAT_ALAW; + + wc->udecode->operation = wc->uencode->operation = dte_operation; + + for (x = 0;x < wc->numchannels; x++) { + dte_init_state(&encoders[x], 1, x, wc); + dte_init_state(&decoders[x], 0, x, wc); + wc->uencode->channels[x].pvt = &encoders[x]; + wc->udecode->channels[x].pvt = &decoders[x]; + } + + zt_transcoder_register(wc->uencode, THIS_MODULE); + zt_transcoder_register(wc->udecode, THIS_MODULE); + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, interrupt_handler, SA_SHIRQ, driver.name, wc)) { + module_printk("Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion) + release_region(wc->iobase, 0xFF); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + /* TODO: what about all the encoders and decoders (do this earlier)? */ + return -EIO; + } + + if (hardware_init(wc)) { + /* Set Reset Low */ + stop_dma(wc); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + /* Enable interrupts */ + enable_interrupts(wc); + + /* Start DMA */ + start_dma(wc); + + if (boot_processor(wc, firmware)) { + /* Set Reset Low */ + stop_dma(wc); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + if (setup_channels(wc)) { + /* Set Reset Low */ + stop_dma(wc); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + if (debug) { + reg = getctl(wc, 0x00fc); + debug_printk(1, "(post-boot) Reg fc is %08x\n", reg); + } + + module_printk("%s supporting '%s' with firmware version '%d'\n", wc->variety, wc->complexname, firmware_ver); + + res = 0; + + return res; +} + +static void release(struct wcdte *wc) +{ + struct cmdq *cmdq, *next; + + if (wc->freeregion) + release_region(wc->iobase, 0xff); + list_for_each_entry_safe(cmdq, next, &wc->pending_cmdq, list) { + debug_printk(1, "freeing cmdq entry at '%p'\n", cmdq); + list_del(&cmdq->list); + kfree(cmdq); + } + list_for_each_entry_safe(cmdq, next, &wc->free_cmdq, list) { + debug_printk(1, "freeing cmdq entry at '%p'\n", cmdq); + list_del(&cmdq->list); + kfree(cmdq); + } + kfree(wc); +} + +static void __devexit remove_one(struct pci_dev *pdev) +{ + int i; + struct wcdte *wc = pci_get_drvdata(pdev); + struct zt_transcoder_channel *ztc_en, *ztc_de; + struct dte_state *st_en, *st_de; + + if (!wc) + return; + + zt_transcoder_unregister(wc->udecode); + zt_transcoder_unregister(wc->uencode); + + if (debug) { + debug_printk(1, "wc->ztsnd_rtx = %d\n", wc->ztsnd_rtx); + debug_printk(1, "wc->ztsnd_0010_rtx = %d\n", wc->ztsnd_0010_rtx); + + for (i = 0; i < wc->numchannels; i++) { + ztc_en = &(wc->uencode->channels[i]); + st_en = ztc_en->pvt; + + ztc_de = &(wc->udecode->channels[i]); + st_de = ztc_de->pvt; + + debug_printk(1, "en[%d] snt = %d, rcv = %d [%d]\n", i, st_en->packets_sent, st_en->packets_received, st_en->packets_sent - st_en->packets_received); + debug_printk(1, "de[%d] snt = %d, rcv = %d [%d]\n", i, st_de->packets_sent, st_de->packets_received, st_de->packets_sent - st_de->packets_received); + } + } + + /* Stop any DMA */ + stop_dma(wc); + + /* In case hardware is still there */ + disable_interrupts(wc); + + /* Kill workqueue */ + destroy_workqueue(wc->dte_wq); + + /* Immediately free resources */ + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + kfree(wc->uencode->channels[0].pvt); + kfree(wc->udecode->channels[0].pvt); + + zt_transcoder_free(wc->uencode); + zt_transcoder_free(wc->udecode); + + /* Release span, possibly delayed */ + release(wc); +} + +static struct pci_device_id pci_tbl[] = { +#ifndef USE_TEST_HW + { 0xd161, 0x3400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcdte }, /* digium board */ +#else + { 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcdte }, /* reference board */ +#endif + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct pci_driver driver = { + name: "wctc4xxp", + probe: init_one, + remove: __devexit_p(remove_one), + suspend: NULL, + resume: NULL, + id_table: pci_tbl, +}; + +static int init(void) +{ + return pci_module_init(&driver) ? -ENODEV : 0; +} + +static void cleanup(void) +{ + pci_unregister_driver(&driver); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +module_param_array(mode, charp, &mode_count, S_IRUGO | S_IWUSR); +module_param(debug_packets, uint, S_IRUGO | S_IWUSR); +module_param(debug_cmd_packets, uint, S_IRUGO | S_IWUSR); +MODULE_DESCRIPTION("Wildcard TC400P+TC400M Transcoder"); +MODULE_AUTHOR("John Sloan "); +MODULE_LICENSE("GPL"); + +module_init(init); +module_exit(cleanup); diff --git a/wctc4xxp/codec_test.c b/wctc4xxp/codec_test.c new file mode 100644 index 0000000..5cef93e --- /dev/null +++ b/wctc4xxp/codec_test.c @@ -0,0 +1,333 @@ +/* + * Wilcard TC400B Digium Transcoder Engine Interface Driver for Zapata Telephony interface test tool. + * + * Written by Matt O'Gorman + * + * Copyright (C) 2006-2007, Digium, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef STANDALONE_ZAPATA +#include "../zaptel.h" +#else +#include +#endif + +#define MAX_CARDS_TO_TEST 6 +#define MAX_CHANNELS_PER_CARD 96 + +#define AST_FORMAT_ULAW (1 << 2) +#define AST_FORMAT_G729A (1 << 8) + +#define AST_FRIENDLY_OFFSET 64 + +static int debug = 0; + +static unsigned char ulaw_slin_ex[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char g729a_expected[] = { + 0xE9, 0x88, 0x4C, 0xA0, 0x00, 0xFA, 0xDD, 0xA2, 0x06, 0x2D, + 0x69, 0x88, 0x00, 0x60, 0x68, 0xD5, 0x9E, 0x20, 0x80, 0x50 +}; + + +struct format_map { + unsigned int map[32][32]; +}; + + +struct tcpvt { + int fd; + int fake; + int inuse; + struct zt_transcode_header *hdr; +// struct ast_frame f; +}; + +struct tctest_info { + int numcards; + int numchans[MAX_CARDS_TO_TEST]; + int total_chans; + int errors; + int overcnt_error; /* Too many cards found */ + int undercnt_error; /* Too few cards found */ + int timeout_error[MAX_CARDS_TO_TEST]; + int data_error[MAX_CARDS_TO_TEST]; + int numcards_werrors; +}; + + +static int find_transcoders(struct tctest_info *tctest_info) +{ + struct zt_transcode_info info = { 0, }; + int fd, res; + + if ((fd = open("/dev/zap/transcode", O_RDWR)) < 0) { + printf("Warning: No Zaptel transcoder support!\n"); + return 0; + } + + tctest_info->total_chans = 0; + info.op = ZT_TCOP_GETINFO; + for (info.tcnum = 0; !(res = ioctl(fd, ZT_TRANSCODE_OP, &info)); info.tcnum++) { + if (debug) + printf("Found transcoder %d, '%s' with %d channels.\n", info.tcnum, info.name, info.numchannels); + if ((info.tcnum % 2) == 0) + { + tctest_info->numchans[info.tcnum/2] = info.numchannels; + tctest_info->total_chans += info.numchannels; + } + } + tctest_info->numcards = info.tcnum / 2; + + close(fd); + if (!info.tcnum) + printf("No hardware transcoders found.\n"); + return 0; +} + + +static int open_transcoder(struct tcpvt *ztp, int dest, int source) +{ + int fd; + unsigned int x = ZT_TCOP_ALLOCATE; + struct zt_transcode_header *hdr; + int flags; + + if ((fd = open("/dev/zap/transcode", O_RDWR)) < 0) + return -1; + flags = fcntl(fd, F_GETFL); + if (flags > - 1) { + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) + printf("Could not set non-block mode!\n"); + } + + if ((hdr = mmap(NULL, sizeof(*hdr), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { + printf("Memory Map failed for transcoding (%s)\n", strerror(errno)); + close(fd); + + return -1; + } + + if (hdr->magic != ZT_TRANSCODE_MAGIC) { + printf("Transcoder header (%08x) wasn't magic. Abandoning\n", hdr->magic); + munmap(hdr, sizeof(*hdr)); + close(fd); + + return -1; + } + + hdr->srcfmt = source; + hdr->dstfmt = dest; + + if (ioctl(fd, ZT_TRANSCODE_OP, &x)) { + printf("Unable to attach transcoder: %s\n", strerror(errno)); + munmap(hdr, sizeof(*hdr)); + close(fd); + + return -1; + } + + ztp->fd = fd; + ztp->hdr = hdr; + + return 0; +} + + +static void close_transcoder(struct tcpvt *ztp) +{ + unsigned int x; + + x = ZT_TCOP_RELEASE; + if (ioctl(ztp->fd, ZT_TRANSCODE_OP, &x)) + printf("Failed to release transcoder channel: %s\n", strerror(errno)); + + munmap(ztp->hdr, sizeof(*ztp->hdr)); + close(ztp->fd); +} + + +static int encode_packet(struct tcpvt *ztp, unsigned char *packet_in, unsigned char *packet_out) +{ + struct zt_transcode_header *hdr = ztp->hdr; + unsigned int x; + + hdr->srcoffset = 0; + + memcpy(hdr->srcdata + hdr->srcoffset + hdr->srclen, packet_in, 160); + hdr->srclen += 160; + + + hdr->dstoffset = AST_FRIENDLY_OFFSET; + x = ZT_TCOP_TRANSCODE; + if (ioctl(ztp->fd, ZT_TRANSCODE_OP, &x)) + printf("Failed to transcode: %s\n", strerror(errno)); + + usleep(20000); + if (hdr->dstlen) + { + memcpy(packet_out, hdr->dstdata + hdr->dstoffset, hdr->dstlen); + return 0; + } + else + return -1; +} + + +static void print_failed() +{ + printf("______ ___ _____ _ _____ ______\n"); + printf("| ___| / _ \\ |_ _| | | | ___| | _ \\\n"); + printf("| |_ / /_\\ \\ | | | | | |__ | | | |\n"); + printf("| _| | _ | | | | | | __| | | | |\n"); + printf("| | | | | | _| |_ | |____ | |___ | |/ /\n"); + printf("\\_| \\_| |_/ \\___/ \\_____/ \\____/ |___/ \n"); +} + + +int main(int argc, char *argv[]) +{ + int arg1, arg2, i, j, card_testing, chan_testing; + struct tcpvt ztp[MAX_CHANNELS_PER_CARD * MAX_CARDS_TO_TEST]; + unsigned char packet_out[200]; + struct tctest_info tctest_info; + + memset(&tctest_info, 0, sizeof(tctest_info)); + + if ((argc < 2) || (argc > 3)) + { + printf("codec_test requires one argument.\n"); + printf(" arg1 = number of cards to test\n"); + return -1; + } + + if (argc == 2) + sscanf(argv[1], "%d", &arg1); + else if (argc == 3) + { + sscanf(argv[1], "%d %d", &arg1, &arg2); + debug = arg2; + } + + printf("Beginning test of %d TC400B cards\n", arg1); + + + /* Search for TC400Bs */ + find_transcoders(&tctest_info); + + if (tctest_info.numcards > arg1) + { + tctest_info.errors++; + tctest_info.overcnt_error = 1; + } + if (tctest_info.numcards < arg1) + { + tctest_info.errors++; + tctest_info.undercnt_error = 1; + } + + if (tctest_info.errors == 0) + { + /* Begin testing transcoder channels */ + for (card_testing = 0; card_testing < tctest_info.numcards; card_testing++) + { + tctest_info.data_error[card_testing] = 0; + tctest_info.timeout_error[card_testing] = 0; + for (chan_testing = 0; chan_testing < tctest_info.numchans[card_testing]; chan_testing++) + { + i = chan_testing; + for(j = 0; j < card_testing; j++) + i += tctest_info.numchans[j]; + + open_transcoder(&ztp[i], AST_FORMAT_G729A, AST_FORMAT_ULAW); + + if ((tctest_info.timeout_error[card_testing] = encode_packet(&ztp[i], ulaw_slin_ex, packet_out) == -1)) + tctest_info.errors++; + + if (memcmp(g729a_expected, packet_out, 20) != 0) + { + tctest_info.errors++; + tctest_info.data_error[card_testing] += 1; + } + } + if ( (tctest_info.data_error[card_testing]) || (tctest_info.timeout_error[card_testing]) ) + tctest_info.numcards_werrors++; + } + + for (i = 0; i < tctest_info.total_chans; i++) + close_transcoder(&ztp[i]); + } + + if (debug) + { + printf("\n\n"); + printf("tctest_info.errors = %d\n", tctest_info.errors); + printf("tctest_info.overcnt_error = %d\n", tctest_info.overcnt_error); + printf("tctest_info.undercnt_error = %d\n", tctest_info.undercnt_error); + printf("tctest_info.numcards_werrors = %d\n", tctest_info.numcards_werrors); + + for (i = 0; i < tctest_info.numcards; i++) + { + printf("tctest_info.data_error[%d] = %d\n", i, tctest_info.data_error[i]); + printf("tctest_info.timeout_error[%d] = %d\n", i, tctest_info.timeout_error[i]); + } + } + + if (tctest_info.errors) + { + printf("\n\n\n"); + if (tctest_info.numcards_werrors) + printf("%d of %d cards\n", tctest_info.numcards_werrors, tctest_info.numcards); + print_failed(); + if (tctest_info.overcnt_error) + printf("\n%d cards found, %d expected\n", tctest_info.numcards, arg1); + if (tctest_info.undercnt_error) + printf("\n%d cards found, %d expected\n", tctest_info.numcards, arg1); + printf("\n\n\n"); + } + else + printf("%d of %d cards PASSED\n", tctest_info.numcards - tctest_info.numcards_werrors, tctest_info.numcards); + + return 0; +} diff --git a/wctc4xxp/tc400m-firmware.bin b/wctc4xxp/tc400m-firmware.bin new file mode 100644 index 0000000..2e6c743 Binary files /dev/null and b/wctc4xxp/tc400m-firmware.bin differ -- cgit v1.2.3