blob: f1003667c784d1207cb1d15525ee65f3d50915fa [file] [log] [blame]
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include "config.h"
#include "pfe_mod.h"
#include "pfe_pcap.h"
#include "pfe_hif_lib.h"
#ifdef CFG_PCAP
/* PFE packet capture:
- uses HIF functions to receive packets
- uses ctrl function to control packet capture
*/
static ssize_t pcap_stats_get(struct device *, struct device_attribute *, char *);
static ssize_t pcap_stats_clear(struct device *, struct device_attribute *, const char *, size_t );
static int pcap_open(struct net_device *dev)
{
printk(KERN_INFO "%s: %s\n", dev->name, __func__);
netif_stop_queue(dev);
return 0;
}
static int pcap_close(struct net_device *dev)
{
printk(KERN_INFO "%s: %s\n", dev->name, __func__);
return 0;
}
static int pcap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev )
{
netif_stop_queue(dev);
printk(KERN_INFO "%s() Dropping pkt!!!\n",__func__);
kfree_skb(skb);
return NETDEV_TX_OK;
}
static DEVICE_ATTR(pcap_stats, 0644, pcap_stats_get, pcap_stats_clear);
static int pcap_sysfs_init(struct device *dev)
{
if (device_create_file(dev, &dev_attr_pcap_stats))
{
printk(KERN_ERR "Failed to create attr capture stats\n");
goto err_stats;
}
return 0;
err_stats:
return -1;
}
/** pfe_pcap_sysfs_exit
*
*/
static void pcap_sysfs_exit(struct pfe* pfe)
{
device_remove_file(pfe->dev, &dev_attr_pcap_stats);
}
static const struct net_device_ops pcap_netdev_ops = {
.ndo_open = pcap_open,
.ndo_stop = pcap_close,
.ndo_start_xmit = pcap_hard_start_xmit,
};
static struct net_device *pcap_register_capdev(char *dev_name)
{
struct net_device *dev=NULL;
printk("%s:\n", __func__);
/* Create an ethernet device instance */
dev = (struct net_device *)alloc_etherdev(sizeof (int));
if (!dev) {
printk(KERN_ERR "%s: cap device allocation failed\n", __func__);
goto err0;
}
strcpy(dev->name, dev_name);
//dev->irq = priv->irq;
/* Fill in the dev structure */
dev->mtu = 1500;
dev->features = 0;
dev->netdev_ops = &pcap_netdev_ops;
/*TODO */
dev->dev_addr[0] = 0x0;
dev->dev_addr[1] = 0x21;
dev->dev_addr[2] = 0x32;
dev->dev_addr[3] = 0x43;
dev->dev_addr[4] = 0x54;
dev->dev_addr[5] = 0x65;
if(register_netdev(dev)) {
printk(KERN_ERR "%s: cannot register net device, aborting.\n", dev->name);
free_netdev(dev);
dev = NULL;
}
err0:
return dev;
}
static void pcap_unregister_capdev(struct net_device *dev)
{
unregister_netdev(dev);
free_netdev(dev);
}
static ssize_t pcap_stats_get(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct pfe* pfe = platform_get_drvdata(pdev);
struct pcap_priv_s *priv = &pfe->pcap;
int ii, len = 0;
//printk("rtc %d rtf %d\n", priv->rxQ.rxToCleanIndex,priv->rxQ.rxToFillIndex);
for (ii = 0; ii < NUM_GEMAC_SUPPORT; ii++)
len += sprintf(&buf[len], "GEMAC%d Rx pkts : %lu\n", ii, priv->stats[ii].rx_packets);
return len;
}
static ssize_t pcap_stats_clear(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct pfe* pfe = platform_get_drvdata(pdev);
struct pcap_priv_s *priv = &pfe->pcap;
int ii;
for (ii = 0; ii < NUM_GEMAC_SUPPORT; ii++)
priv->stats[ii].rx_packets = 0;
return count;
}
/** pfe_pcap_rx_poll
*
*/
static int pfe_pcap_rx_poll (struct napi_struct *napi, int budget)
{
struct sk_buff *skb;
void *data_addr;
int length,offset;
unsigned int idx = 0, work_done=0, qno=0;
unsigned int desc_ctrl = 0,rx_ctrl;
struct hif_pcap_hdr *pcap_hdr;
u32 tstamp;
struct pcap_priv_s *priv = container_of(napi, struct pcap_priv_s, low_napi);
do{
data_addr = hif_lib_receive_pkt(&priv->client, qno, &length, &offset, &rx_ctrl, &desc_ctrl, (void **)&pcap_hdr);
if (!data_addr)
break;
idx = pcap_hdr->ifindex;
//printk("%s: data:%x len:%d idx:%d \n", __func__, (u32)data_addr, length, idx);
if(idx < NUM_GEMAC_SUPPORT && priv->dev[idx]) {
struct net_device *dev = priv->dev[idx];
if(length > 1514) {
printk(KERN_ERR "Dropping big packet\n");
goto pkt_drop;
}
skb = alloc_skb_header(PFE_BUF_SIZE, data_addr, GFP_ATOMIC);
if (unlikely(!skb)) {
printk(KERN_ERR "Failed to allocate skb header\n");
goto pkt_drop;
}
skb_reserve(skb, offset);
skb_put(skb, length);
skb->protocol = eth_type_trans(skb, dev);
tstamp = ntohl(pcap_hdr->timestamp);
skb->tstamp = ktime_set( tstamp/USEC_PER_SEC, (tstamp%USEC_PER_SEC)*1000 );
/* Send packet to PF_PACKET socket queue */
capture_receive_skb(skb);
priv->stats[idx].rx_packets++;
priv->stats[idx].rx_bytes += length;
//dev->last_rx = jiffies;
work_done++;
}
else
{
pkt_drop:
printk(KERN_ERR "Received with wrong dev\n");
pfe_kfree(data_addr);
}
}while(work_done < budget);
if (work_done < budget) {
napi_complete(napi);
hif_lib_event_handler_start(&priv->client, EVENT_RX_PKT_IND, qno);
}
return (work_done);
}
static int pfe_pcap_event_handler(void *data, int event, int qno)
{
struct pcap_priv_s *priv = data;
switch (event) {
case EVENT_RX_PKT_IND:
/* qno is always 0 */
if (qno == 0) {
if (napi_schedule_prep(&priv->low_napi)) {
//printk(KERN_INFO "%s: schedule high prio poll\n", __func__);
__napi_schedule(&priv->low_napi);
}
}
break;
case EVENT_TXDONE_IND:
case EVENT_HIGH_RX_WM:
default:
break;
}
return 0;
}
int pfe_pcap_up( struct pcap_priv_s *priv)
{
struct hif_client_s *client;
int err = 0,rc, ii;
char ifname[IFNAMSIZ];
printk(KERN_INFO "%s\n", __func__);
priv->pfe = pfe;
for (ii = 0; ii < NUM_GEMAC_SUPPORT; ii++)
{
snprintf(ifname, IFNAMSIZ, "cap_gemac%d", ii);
priv->dev[ii] = pcap_register_capdev(ifname);
if(priv->dev[ii]==NULL) {
printk(KERN_ERR "%s: Failed to register capture device\n",__func__);
err = -EAGAIN;
goto err0;
}
}
if (pcap_sysfs_init(pfe->dev) < 0 )
{
printk(KERN_ERR "%s: Failed to register to sysfs\n",__func__);
err = -EAGAIN;
goto err0;
}
/* Register pktcap client driver to HIF */
client = &priv->client;
memset(client, 0, sizeof(*client));
client->id = PFE_CL_PCAP0;
client->tx_qn = PCAP_TXQ_CNT;
client->rx_qn = PCAP_RXQ_CNT;
client->priv = priv;
client->pfe = priv->pfe;
client->event_handler = pfe_pcap_event_handler;
/* FIXME : For now hif lib sets all tx and rx queues to same size */
client->tx_qsize = PCAP_TXQ_DEPTH;
client->rx_qsize = PCAP_RXQ_DEPTH;
printk(KERN_INFO "%s Registering client \n", __func__);
if ((rc = hif_lib_client_register(client))) {
printk(KERN_ERR"%s: hif_lib_client_register(%d) failed\n", __func__, client->id);
goto err1;
}
printk(KERN_INFO "%s Enable PCAP in pfe \n", __func__);
/* Enable Packet capture in PFE */
if ((rc = pfe_ctrl_set_pcap(1)) != 0)
{
printk("%s: Failed to send command(enable) to pfe\n",__func__);
err = -EAGAIN;
goto err2;
}
printk(KERN_INFO "%s Enable PCAP ratelimit in pfe \n", __func__);
/* Set the default values for the configurable parameters*/
priv->rate_limit = COMCERTO_CAP_DFL_RATELIMIT;
priv->pkts_per_msec = priv->rate_limit/1000;
if ((rc = pfe_ctrl_set_pcap_ratelimit(priv->pkts_per_msec)) != 0)
{
printk("%s: Failed to send ratelimit command to pfe\n",__func__);
err = -EAGAIN;
goto err2;
}
return 0;
err2:
hif_lib_client_unregister(client);
err1:
pcap_sysfs_exit(pfe);
err0:
for (ii = 0; ii < NUM_GEMAC_SUPPORT; ii++)
if(priv->dev[ii])
pcap_unregister_capdev(priv->dev[ii]);
return err;
}
static int pfe_pcap_down(struct pcap_priv_s* priv)
{
struct hif_client_s *client = &priv->client;
int ii;
printk("%s()\n", __func__);
/* Disable Packet capture module in PFE */
if(pfe_ctrl_set_pcap(0)!= 0)
printk(KERN_ERR "%s: Failed while sending command CMD_PKTCAP_ENABLE\n", __func__ );
hif_lib_client_unregister(client);
pcap_sysfs_exit(pfe);
for (ii = 0; ii < NUM_GEMAC_SUPPORT; ii++)
if(priv->dev[ii])
pcap_unregister_capdev(priv->dev[ii]);
return 0;
}
static int pcap_driver_init(struct pfe* pfe)
{
struct pcap_priv_s *priv = &pfe->pcap;
int err;
/* Initilize NAPI for Rx processing */
init_dummy_netdev(&priv->dummy_dev);
netif_napi_add(&priv->dummy_dev, &priv->low_napi, pfe_pcap_rx_poll, PCAP_RX_POLL_WEIGHT);
napi_enable(&priv->low_napi);
priv->pfe = pfe;
err = pfe_pcap_up(priv);
if (err < 0)
napi_disable(&priv->low_napi);
return err;
}
int pfe_pcap_init(struct pfe *pfe)
{
int rc ;
printk(KERN_INFO "%s\n",__func__);
rc = pcap_driver_init(pfe);
if(rc) goto err0;
return 0;
err0:
return rc;
}
void pfe_pcap_exit(struct pfe *pfe)
{
struct pcap_priv_s *priv = &pfe->pcap;
printk(KERN_INFO "%s\n", __func__);
pfe_pcap_down(priv);
}
#else /* !CFG_PCAP */
int pfe_pcap_init(struct pfe *pfe)
{
printk(KERN_INFO "%s\n", __func__);
return 0;
}
void pfe_pcap_exit(struct pfe *pfe)
{
printk(KERN_INFO "%s\n", __func__);
}
#endif /* !CFG_PCAP */