From 2e9c5b03752de6db1cb54cc6da43d648c83165b6 Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Fri, 26 Feb 2010 16:40:41 +0000 Subject: 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 --- drivers/dahdi/voicebus/Kbuild | 2 +- drivers/dahdi/voicebus/voicebus.c | 12 ++ drivers/dahdi/voicebus/voicebus.h | 22 +- drivers/dahdi/voicebus/voicebus_net.c | 377 ++++++++++++++++++++++++++++++++++ drivers/dahdi/voicebus/voicebus_net.h | 10 + 5 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 drivers/dahdi/voicebus/voicebus_net.c create mode 100644 drivers/dahdi/voicebus/voicebus_net.h 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 #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 + +#ifdef VOICEBUS_NET_DEBUG +#include +#include +#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 + * + * 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 +#include +#include + +#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 -- cgit v1.2.3