summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ruffell <sruffell@digium.com>2010-02-26 16:40:41 +0000
committerShaun Ruffell <sruffell@digium.com>2010-02-26 16:40:41 +0000
commit2e9c5b03752de6db1cb54cc6da43d648c83165b6 (patch)
tree47b1b93dd7ec3615e60fd14a64e09aa7b921ef68
parent0e66db059265bf2085413fe93f76957abf945e95 (diff)
voicebus: Add optional network debugging interface.
This interface is only used to facilitate debugging. git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@8118 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--drivers/dahdi/voicebus/Kbuild2
-rw-r--r--drivers/dahdi/voicebus/voicebus.c12
-rw-r--r--drivers/dahdi/voicebus/voicebus.h22
-rw-r--r--drivers/dahdi/voicebus/voicebus_net.c377
-rw-r--r--drivers/dahdi/voicebus/voicebus_net.h10
5 files changed, 421 insertions, 2 deletions
diff --git a/drivers/dahdi/voicebus/Kbuild b/drivers/dahdi/voicebus/Kbuild
index 7b33956..6316174 100644
--- a/drivers/dahdi/voicebus/Kbuild
+++ b/drivers/dahdi/voicebus/Kbuild
@@ -1,5 +1,5 @@
obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_VOICEBUS) += dahdi_voicebus.o
-dahdi_voicebus-objs := voicebus.o GpakCust.o GpakApi.o
+dahdi_voicebus-objs := voicebus.o GpakCust.o GpakApi.o voicebus_net.o
EXTRA_CFLAGS := -I$(src)/.. -Wno-undef
diff --git a/drivers/dahdi/voicebus/voicebus.c b/drivers/dahdi/voicebus/voicebus.c
index 869cec0..93e54a9 100644
--- a/drivers/dahdi/voicebus/voicebus.c
+++ b/drivers/dahdi/voicebus/voicebus.c
@@ -39,6 +39,7 @@
#include <dahdi/kernel.h>
#include "voicebus.h"
+#include "voicebus_net.h"
#include "vpmadtreg.h"
#include "GpakCust.h"
@@ -828,6 +829,7 @@ vb_get_completed_txb(struct voicebus *vb)
d->buffer1 = vb->idle_vbb_dma_addr;
SET_OWNED(d);
atomic_dec(&dl->count);
+ vb_net_capture_vbb(vb, vbb, 1, d->des0, d->container);
return vbb;
}
@@ -850,6 +852,9 @@ vb_get_completed_rxb(struct voicebus *vb)
dl->head = (++head) & DRING_MASK;
d->buffer1 = 0;
atomic_dec(&dl->count);
+# ifdef VOICEBUS_NET_DEBUG
+ vb_net_capture_vbb(vb, vbb, 0, d->des0, d->container);
+# endif
return vbb;
}
@@ -1041,6 +1046,10 @@ EXPORT_SYMBOL(voicebus_stop);
void
voicebus_release(struct voicebus *vb)
{
+#ifdef VOICEBUS_NET_DEBUG
+ vb_net_unregister(vb);
+#endif
+
/* quiesce the hardware */
voicebus_stop(vb);
@@ -1521,6 +1530,9 @@ voicebus_init(struct voicebus *vb, const char *board_name)
}
#endif
+#ifdef VOICEBUS_NET_DEBUG
+ vb_net_register(vb, board_name);
+#endif
return retval;
cleanup:
diff --git a/drivers/dahdi/voicebus/voicebus.h b/drivers/dahdi/voicebus/voicebus.h
index ac7e398..a58e4ca 100644
--- a/drivers/dahdi/voicebus/voicebus.h
+++ b/drivers/dahdi/voicebus/voicebus.h
@@ -31,6 +31,12 @@
#include <linux/interrupt.h>
+
+#ifdef VOICEBUS_NET_DEBUG
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#endif
+
#define VOICEBUS_DEFAULT_LATENCY 3
#define VOICEBUS_DEFAULT_MAXLATENCY 25
#define VOICEBUS_MAXLATENCY_BUMP 6
@@ -49,6 +55,9 @@
/* Do not generate interrupts on this interface, but instead just poll it */
#undef CONFIG_VOICEBUS_TIMER
+/* Define this in order to create a debugging network interface. */
+#undef VOICEBUS_NET_DEBUG
+
struct voicebus;
struct vbb {
@@ -103,6 +112,17 @@ struct voicebus {
unsigned int min_tx_buffer_count;
unsigned int max_latency;
struct list_head tx_complete;
+
+#ifdef VOICEBUS_NET_DEBUG
+ struct sk_buff_head captured_packets;
+ struct net_device *netdev;
+ struct net_device_stats net_stats;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ struct napi_struct napi;
+#endif
+ atomic_t tx_seqnum;
+ atomic_t rx_seqnum;
+#endif
};
int voicebus_init(struct voicebus *vb, const char *board_name);
@@ -116,5 +136,5 @@ int voicebus_current_latency(struct voicebus *vb);
void voicebus_lock_latency(struct voicebus *vb);
void voicebus_unlock_latency(struct voicebus *vb);
int voicebus_is_latency_locked(const struct voicebus *vb);
-
+
#endif /* __VOICEBUS_H__ */
diff --git a/drivers/dahdi/voicebus/voicebus_net.c b/drivers/dahdi/voicebus/voicebus_net.c
new file mode 100644
index 0000000..534d52d
--- /dev/null
+++ b/drivers/dahdi/voicebus/voicebus_net.c
@@ -0,0 +1,377 @@
+/*
+ * Voicebus network debug interface
+ *
+ * Written by Shaun Ruffell <sruffell@digium.com>
+ *
+ * Copyright (C) 2010 Digium, Inc.
+ *
+ * All rights reserved.
+
+ * VoiceBus is a registered trademark of Digium.
+ *
+ */
+
+/*
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "voicebus.h"
+#include "voicebus_net.h"
+
+#ifdef VOICEBUS_NET_DEBUG
+
+#ifdef HAVE_NETDEV_PRIV
+struct voicebus_netdev_priv {
+ struct voicebus *vb;
+};
+#endif
+
+static inline struct voicebus *
+voicebus_from_netdev(struct net_device *netdev)
+{
+#ifdef HAVE_NETDEV_PRIV
+ struct voicebus_netdev_priv *priv;
+ priv = netdev_priv(netdev);
+ return priv->vb;
+#else
+ return netdev->priv;
+#endif
+}
+
+static void *
+skb_to_vbb(struct voicebus *vb, struct sk_buff *skb)
+{
+ int res;
+ void *vbb;
+ const int COMMON_HEADER = 30;
+
+ if (skb->len != (VOICEBUS_SFRAME_SIZE + COMMON_HEADER)) {
+ dev_warn(&vb->pdev->dev, "Packet of length %d is not the "
+ "required %d.\n", skb->len,
+ VOICEBUS_SFRAME_SIZE + COMMON_HEADER);
+ return NULL;
+ }
+
+ vbb = voicebus_alloc(vb);
+ if (!vbb)
+ return NULL;
+ res = skb_copy_bits(skb, COMMON_HEADER, vbb, VOICEBUS_SFRAME_SIZE);
+ if (res) {
+ dev_warn(&vb->pdev->dev, "Failed call to skb_copy_bits.\n");
+ voicebus_free(vb, vbb);
+ return NULL;
+ }
+ return vbb;
+}
+
+static int
+vb_net_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ void *vbb;
+
+ vbb = skb_to_vbb(vb, skb);
+ if (vbb)
+ voicebus_transmit(vb, vbb);
+
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+static int vb_net_receive(struct voicebus *vb, int max)
+{
+ int count = 0;
+ struct sk_buff *skb;
+ WARN_ON(0 == max);
+ while ((skb = skb_dequeue(&vb->captured_packets))) {
+ netif_receive_skb(skb);
+ if (++count >= max)
+ break;
+ }
+ return count;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+static int vb_net_poll(struct net_device *netdev, int *budget)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ int count = 0;
+ int quota = min(netdev->quota, *budget);
+
+ count = vb_net_receive(vb, quota);
+
+ *budget -= count;
+ netdev->quota -= count;
+
+ if (!skb_queue_len(&vb->captured_packets)) {
+ netif_rx_complete(netdev);
+ return 0;
+ } else {
+ return -1;
+ }
+}
+#else
+static int vb_net_poll(struct napi_struct *napi, int budget)
+{
+ struct voicebus *vb = container_of(napi, struct voicebus, napi);
+ int count;
+
+ count = vb_net_receive(vb, budget);
+
+ if (!skb_queue_len(&vb->captured_packets)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
+ netif_rx_complete(vb->netdev, &vb->napi);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+ netif_rx_complete(&vb->napi);
+#else
+ napi_complete(&vb->napi);
+#endif
+ }
+ return count;
+}
+#endif
+
+static void vb_net_set_multi(struct net_device *netdev)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ dev_dbg(&vb->pdev->dev, "%s promiscuity:%d\n",
+ __func__, netdev->promiscuity);
+}
+
+static int vb_net_up(struct net_device *netdev)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ dev_dbg(&vb->pdev->dev, "%s\n", __func__);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+ netif_poll_enable(netdev);
+#else
+ napi_enable(&vb->napi);
+#endif
+ return 0;
+}
+
+static int vb_net_down(struct net_device *netdev)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ dev_dbg(&vb->pdev->dev, "%s\n", __func__);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+ netif_poll_disable(netdev);
+#else
+ napi_disable(&vb->napi);
+#endif
+ return 0;
+}
+
+static struct net_device_stats *
+vb_net_get_stats(struct net_device *netdev)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ return &vb->net_stats;
+}
+
+#ifdef HAVE_NET_DEVICE_OPS
+static const struct net_device_ops vb_netdev_ops = {
+ .ndo_set_multicast_list = &vb_net_set_multi,
+ .ndo_open = &vb_net_up,
+ .ndo_stop = &vb_net_down,
+ .ndo_start_xmit = &vb_net_hard_start_xmit,
+ .ndo_get_stats = &vb_net_get_stats,
+};
+#endif
+
+/**
+ * vb_net_register - Register a new network interface.
+ * @vb: voicebus card to register the interface for.
+ *
+ * The network interface is primarily used for debugging in order to watch the
+ * traffic between the transcoder and the host.
+ *
+ */
+int vb_net_register(struct voicebus *vb, const char *board_name)
+{
+ int res;
+ struct net_device *netdev;
+# ifdef HAVE_NETDEV_PRIV
+ struct voicebus_netdev_priv *priv;
+# endif
+ const char our_mac[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
+
+# ifdef HAVE_NETDEV_PRIV
+ netdev = alloc_netdev(sizeof(*priv), board_name, ether_setup);
+ if (!netdev)
+ return -ENOMEM;
+ priv = netdev_priv(netdev);
+ priv->vb = vb;
+# else
+ netdev = alloc_netdev(0, vb->board_name, ether_setup);
+ if (!netdev)
+ return -ENOMEM;
+ netdev->priv = vb;
+# endif
+ memcpy(netdev->dev_addr, our_mac, sizeof(our_mac));
+# ifdef HAVE_NET_DEVICE_OPS
+ netdev->netdev_ops = &vb_netdev_ops;
+# else
+ netdev->set_multicast_list = vb_net_set_multi;
+ netdev->open = vb_net_up;
+ netdev->stop = vb_net_down;
+ netdev->hard_start_xmit = vb_net_hard_start_xmit;
+ netdev->get_stats = vb_net_get_stats;
+# endif
+
+ netdev->promiscuity = 0;
+ netdev->flags |= IFF_NOARP;
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+ netdev->poll = vb_net_poll;
+ netdev->weight = 64;
+# else
+ netif_napi_add(netdev, &vb->napi, vb_net_poll, 64);
+# endif
+
+ skb_queue_head_init(&vb->captured_packets);
+ res = register_netdev(netdev);
+ if (res) {
+ dev_warn(&vb->pdev->dev,
+ "Failed to register network device %s.\n", board_name);
+ goto error_sw;
+ }
+
+ vb->netdev = netdev;
+
+ dev_dbg(&vb->pdev->dev,
+ "Created network device %s for debug.\n", board_name);
+ return 0;
+
+error_sw:
+ if (netdev)
+ free_netdev(netdev);
+ return res;
+}
+
+void vb_net_unregister(struct voicebus *wc)
+{
+ struct sk_buff *skb;
+ if (!wc->netdev)
+ return;
+
+ unregister_netdev(wc->netdev);
+
+ while ((skb = skb_dequeue(&wc->captured_packets)))
+ kfree_skb(skb);
+
+ free_netdev(wc->netdev);
+ wc->netdev = NULL;
+}
+
+/* Header format for the voicebus network interface. */
+struct voicebus_net_hdr {
+ struct ethhdr ethhdr;
+ __be16 seq_num;
+ __be32 des0;
+ __be16 tag;
+ __be16 filler[4];
+} __attribute__((packed));
+
+static struct sk_buff *
+vbb_to_skb(struct net_device *netdev, const void *vbb, const int tx,
+ const u32 des0, const u16 tag)
+{
+ struct voicebus *vb = voicebus_from_netdev(netdev);
+ struct sk_buff *skb;
+ struct voicebus_net_hdr *hdr;
+ /* 0x88B5 is the local experimental ethertype */
+ const u16 VOICEBUS_ETHTYPE = 0x88b5;
+ const u8 BOARD_MAC[6] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
+ const u8 HOST_MAC[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ skb = netdev_alloc_skb(vb->netdev,
+ VOICEBUS_SFRAME_SIZE + sizeof(*hdr) + NET_IP_ALIGN);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb->dev = netdev;
+ hdr = (struct voicebus_net_hdr *)skb_put(skb, VOICEBUS_SFRAME_SIZE +
+ sizeof(*hdr));
+ /* Fill in the source and destination mac addresses appropriately
+ * depending on whether this is a packet we are transmitting or a packet
+ * that we have received. */
+ if (tx) {
+ memcpy(hdr->ethhdr.h_dest, BOARD_MAC, sizeof(BOARD_MAC));
+ memcpy(hdr->ethhdr.h_source, HOST_MAC, sizeof(HOST_MAC));
+ hdr->seq_num = cpu_to_be16(atomic_inc_return(
+ &vb->tx_seqnum));
+ } else {
+ memcpy(hdr->ethhdr.h_dest, HOST_MAC, sizeof(HOST_MAC));
+ memcpy(hdr->ethhdr.h_source, BOARD_MAC, sizeof(BOARD_MAC));
+ hdr->seq_num = cpu_to_be16(atomic_inc_return(
+ &vb->rx_seqnum));
+ }
+ memset(hdr->filler, 0, sizeof(hdr->filler));
+ hdr->des0 = cpu_to_be32(des0);
+ hdr->tag = cpu_to_be16(tag);
+ hdr->ethhdr.h_proto = htons(VOICEBUS_ETHTYPE);
+ /* copy the rest of the packet. */
+ memcpy(skb->data + sizeof(*hdr), vbb, VOICEBUS_SFRAME_SIZE);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ return skb;
+}
+
+/**
+ * vb_net_capture_cmd - Send a vbb to the network stack.
+ * @vb: Interface card received the command.
+ * @vbb: Voicebus buffer to pass up..
+ * @tx: 1 if this is a vbb that the driver is sending to the card.
+ *
+ */
+void vb_net_capture_vbb(struct voicebus *vb, const void *vbb, const int tx,
+ const u32 des0, const u16 tag)
+{
+ struct sk_buff *skb;
+ struct net_device *netdev = vb->netdev;
+ const int MAX_CAPTURED_PACKETS = 5000;
+
+ if (!netdev)
+ return;
+
+ /* If the interface isn't up, we don't need to capture the packet. */
+ if (!(netdev->flags & IFF_UP))
+ return;
+
+ if (skb_queue_len(&vb->captured_packets) > MAX_CAPTURED_PACKETS) {
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ skb = vbb_to_skb(netdev, vbb, tx, des0, tag);
+ if (!skb)
+ return;
+
+ skb_queue_tail(&vb->captured_packets, skb);
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+ netif_rx_schedule(netdev);
+# elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
+ netif_rx_schedule(netdev, &vb->napi);
+# elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+ netif_rx_schedule(&vb->napi);
+# else
+ napi_schedule(&vb->napi);
+# endif
+ return;
+}
+
+#endif
diff --git a/drivers/dahdi/voicebus/voicebus_net.h b/drivers/dahdi/voicebus/voicebus_net.h
new file mode 100644
index 0000000..e1dc135
--- /dev/null
+++ b/drivers/dahdi/voicebus/voicebus_net.h
@@ -0,0 +1,10 @@
+#ifdef VOICEBUS_NET_DEBUG
+int vb_net_register(struct voicebus *, const char *);
+void vb_net_unregister(struct voicebus *);
+void vb_net_capture_vbb(struct voicebus *, const void *,
+ const int, const u32, const u16);
+#else
+#define vb_net_register(a, b) do { ; } while (0)
+#define vb_net_unregister(a) do { ; } while (0)
+#define vb_net_capture_vbb(a, b, c, d, e) do { ; } while (0)
+#endif