diff mbox

net: Linn Ethernet Packet Sniffer driver

Message ID 1422007621-13567-1-git-send-email-stathis.voukelatos@linn.co.uk
State Needs Review / ACK, archived
Headers show

Checks

Context Check Description
robh/checkpatch warning total: 1 errors, 1 warnings, 0 lines checked
robh/patch-applied success

Commit Message

Stathis Voukelatos Jan. 23, 2015, 10:07 a.m. UTC
This patch adds support the Ethernet Packet Sniffer H/W module
developed by Linn Products Ltd and found in the IMG Pistachio SoC.
The module allows Ethernet packets to be parsed, matched against
a user-defined pattern and timestamped. It sits between a 100M
Ethernet MAC and PHY and is completely passive with respect to
Ethernet frames.

Matched packet bytes and timestamp values are returned through a
FIFO. Timestamps are provided to the module through an externally
generated Gray-encoded counter.

The command pattern for packet matching is stored in module RAM
and consists of a sequence of 16-bit entries. Each entry includes
an 8-bit command code and and 8-bit data value. Valid command
codes are:
0 - Don't care
1 - Match: packet data must match command string byte
2 - Copy: packet data will be copied to FIFO
3 - Match/Stamp: if packet data matches string byte, a timestamp
                 is copied into the FIFO
4 - Copy/Done: packet data will be copied into the FIFO.
               This command terminates the command string.

The driver consists of two modules:
- Core: it provides an API to user space using the Generic Netlink
        framework. Specific backend implementations, like the
        Ethernet Packet Sniffer, register one or more channels
        with the Core. For each channel a Genl family is created.
        User space can access a channel by sending Genl messages
        to the Genl family associated with the channel. Packet
        matching events are multicast.

- Ethernet Packet Sniffer backend: provides the driver for the
        Linn Ethernet Packet Sniffer H/W modules.

The split between a core and backend modules allows software-only
implementations to be added for platforms where no H/W support
is available.

Based on 3.19-rc5

Signed-off-by: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
---
 .../bindings/net/linn-ether-packet-sniffer.txt     |  27 ++
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 MAINTAINERS                                        |   7 +
 drivers/net/Kconfig                                |   2 +
 drivers/net/Makefile                               |   1 +
 drivers/net/pkt-sniffer/Kconfig                    |  23 ++
 drivers/net/pkt-sniffer/Makefile                   |   8 +
 drivers/net/pkt-sniffer/backends/ether/channel.c   | 366 ++++++++++++++++++
 drivers/net/pkt-sniffer/backends/ether/channel.h   |  76 ++++
 drivers/net/pkt-sniffer/backends/ether/hw.h        |  46 +++
 drivers/net/pkt-sniffer/backends/ether/platform.c  | 231 +++++++++++
 drivers/net/pkt-sniffer/core/dev_table.c           | 124 ++++++
 drivers/net/pkt-sniffer/core/module.c              |  37 ++
 drivers/net/pkt-sniffer/core/nl.c                  | 427 +++++++++++++++++++++
 drivers/net/pkt-sniffer/core/nl.h                  |  34 ++
 drivers/net/pkt-sniffer/core/snf_core.h            |  64 +++
 include/linux/pkt_sniffer.h                        |  89 +++++
 17 files changed, 1563 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
 create mode 100644 drivers/net/pkt-sniffer/Kconfig
 create mode 100644 drivers/net/pkt-sniffer/Makefile
 create mode 100644 drivers/net/pkt-sniffer/backends/ether/channel.c
 create mode 100644 drivers/net/pkt-sniffer/backends/ether/channel.h
 create mode 100644 drivers/net/pkt-sniffer/backends/ether/hw.h
 create mode 100644 drivers/net/pkt-sniffer/backends/ether/platform.c
 create mode 100644 drivers/net/pkt-sniffer/core/dev_table.c
 create mode 100644 drivers/net/pkt-sniffer/core/module.c
 create mode 100644 drivers/net/pkt-sniffer/core/nl.c
 create mode 100644 drivers/net/pkt-sniffer/core/nl.h
 create mode 100644 drivers/net/pkt-sniffer/core/snf_core.h
 create mode 100644 include/linux/pkt_sniffer.h

Comments

Arnd Bergmann Jan. 23, 2015, 10:21 a.m. UTC | #1
On Friday 23 January 2015 10:07:01 Stathis Voukelatos wrote:
> +- interrupts : sniffer interrupt specifier
> +- clocks : specify the system clock for the peripheral
> +- clock-names : must contain the "sys" entry
> +- fifo-block-words : number of words in one data FIFO entry
> +
> +Example:
> +
> +sniffer@1814a000 {
> +        compatible = "linn,eth-sniffer";
> +        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
> +        reg-names = "regs", "tx-ram", "rx-ram";
> +        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
> +        interrupt-names = "eth-sniffer-irq";
> +        clocks = <&system_clk>;
> +        clock-names = "sys";
> +        fifo-block-words = <4>;
> 

The example contains an interrupt-names property that is not documented.
If you want to name interrupts, the exact name strings need to
be mandated by the binding. Alternatively just drop the name.
I notice that the driver requests the first interrupt without giving
a name anyway, and the description above suggests that there can
only be one interrupt.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Rutland Jan. 23, 2015, 10:51 a.m. UTC | #2
On Fri, Jan 23, 2015 at 10:07:01AM +0000, Stathis Voukelatos wrote:
> This patch adds support the Ethernet Packet Sniffer H/W module
> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
> The module allows Ethernet packets to be parsed, matched against
> a user-defined pattern and timestamped. It sits between a 100M
> Ethernet MAC and PHY and is completely passive with respect to
> Ethernet frames.
>
> Matched packet bytes and timestamp values are returned through a
> FIFO. Timestamps are provided to the module through an externally
> generated Gray-encoded counter.
>
> The command pattern for packet matching is stored in module RAM
> and consists of a sequence of 16-bit entries. Each entry includes
> an 8-bit command code and and 8-bit data value. Valid command
> codes are:
> 0 - Don't care
> 1 - Match: packet data must match command string byte
> 2 - Copy: packet data will be copied to FIFO
> 3 - Match/Stamp: if packet data matches string byte, a timestamp
>                  is copied into the FIFO
> 4 - Copy/Done: packet data will be copied into the FIFO.
>                This command terminates the command string.
>
> The driver consists of two modules:
> - Core: it provides an API to user space using the Generic Netlink
>         framework. Specific backend implementations, like the
>         Ethernet Packet Sniffer, register one or more channels
>         with the Core. For each channel a Genl family is created.
>         User space can access a channel by sending Genl messages
>         to the Genl family associated with the channel. Packet
>         matching events are multicast.
>
> - Ethernet Packet Sniffer backend: provides the driver for the
>         Linn Ethernet Packet Sniffer H/W modules.

It sounds like the framework and particular driver need to be split into
separate patches.

Also, please split the binding patch as per
Documentation/devicetree/bindings/submitting-patches.txt.

>
> The split between a core and backend modules allows software-only
> implementations to be added for platforms where no H/W support
> is available.
>
> Based on 3.19-rc5
>
> Signed-off-by: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> ---
>  .../bindings/net/linn-ether-packet-sniffer.txt     |  27 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  MAINTAINERS                                        |   7 +
>  drivers/net/Kconfig                                |   2 +
>  drivers/net/Makefile                               |   1 +
>  drivers/net/pkt-sniffer/Kconfig                    |  23 ++
>  drivers/net/pkt-sniffer/Makefile                   |   8 +
>  drivers/net/pkt-sniffer/backends/ether/channel.c   | 366 ++++++++++++++++++
>  drivers/net/pkt-sniffer/backends/ether/channel.h   |  76 ++++
>  drivers/net/pkt-sniffer/backends/ether/hw.h        |  46 +++
>  drivers/net/pkt-sniffer/backends/ether/platform.c  | 231 +++++++++++
>  drivers/net/pkt-sniffer/core/dev_table.c           | 124 ++++++
>  drivers/net/pkt-sniffer/core/module.c              |  37 ++
>  drivers/net/pkt-sniffer/core/nl.c                  | 427 +++++++++++++++++++++
>  drivers/net/pkt-sniffer/core/nl.h                  |  34 ++
>  drivers/net/pkt-sniffer/core/snf_core.h            |  64 +++
>  include/linux/pkt_sniffer.h                        |  89 +++++
>  17 files changed, 1563 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
>  create mode 100644 drivers/net/pkt-sniffer/Kconfig
>  create mode 100644 drivers/net/pkt-sniffer/Makefile
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/channel.c
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/channel.h
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/hw.h
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/platform.c
>  create mode 100644 drivers/net/pkt-sniffer/core/dev_table.c
>  create mode 100644 drivers/net/pkt-sniffer/core/module.c
>  create mode 100644 drivers/net/pkt-sniffer/core/nl.c
>  create mode 100644 drivers/net/pkt-sniffer/core/nl.h
>  create mode 100644 drivers/net/pkt-sniffer/core/snf_core.h
>  create mode 100644 include/linux/pkt_sniffer.h
>
> diff --git a/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> new file mode 100644
> index 0000000..6b6e105
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> @@ -0,0 +1,27 @@
> +* Linn Products Ethernet Packet Sniffer
> +
> +Required properties:
> +- compatible : must be "linn,eth-sniffer"
> +- reg : physical addresses and sizes of registers. Must contain 3 entries:
> +          first entry: registers memory space
> +          second entry: TX command memory
> +          third entry: RX command memory

Just to check: are those memories are part of the packet sniffer device,
or are carveouts from other memory?

> +- reg-names : must contain the following 3 entries:
> +                  "regs", "tx-ram", "rx-ram"

If you use reg-names, please define reg in terms of reg-names. It's a
little pointless to have redundant name and index requirements.

> +- interrupts : sniffer interrupt specifier
> +- clocks : specify the system clock for the peripheral
> +- clock-names : must contain the "sys" entry

Likewise with clocks and clock-names.

> +- fifo-block-words : number of words in one data FIFO entry
> +
> +Example:
> +
> +sniffer@1814a000 {
> +        compatible = "linn,eth-sniffer";
> +        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
> +        reg-names = "regs", "tx-ram", "rx-ram";
> +        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
> +        interrupt-names = "eth-sniffer-irq";
> +        clocks = <&system_clk>;
> +        clock-names = "sys";
> +        fifo-block-words = <4>;
> +    };

Surely the relationship between the sniffer, MAC, and PHY should be
described, so we know which interface the sniffer is related to?

> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index b1df0ad..2c96f35 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -90,6 +90,7 @@ lacie LaCie
>  lantiq Lantiq Semiconductor
>  lenovo Lenovo Group Ltd.
>  lg     LG Corporation
> +linn   Linn Products Ltd.
>  linux  Linux-specific binding
>  lsi    LSI Corp. (LSI Logic)
>  lltc   Linear Technology Corporation

This addition looks fine to me.

Thanks,
Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Daniel Borkmann Jan. 23, 2015, 11:20 a.m. UTC | #3
On 01/23/2015 11:07 AM, Stathis Voukelatos wrote:
> This patch adds support the Ethernet Packet Sniffer H/W module
> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
> The module allows Ethernet packets to be parsed, matched against
> a user-defined pattern and timestamped. It sits between a 100M
> Ethernet MAC and PHY and is completely passive with respect to
> Ethernet frames.
>
> Matched packet bytes and timestamp values are returned through a
> FIFO. Timestamps are provided to the module through an externally
> generated Gray-encoded counter.
>
> The command pattern for packet matching is stored in module RAM
> and consists of a sequence of 16-bit entries. Each entry includes
> an 8-bit command code and and 8-bit data value. Valid command
> codes are:
> 0 - Don't care
> 1 - Match: packet data must match command string byte
> 2 - Copy: packet data will be copied to FIFO
> 3 - Match/Stamp: if packet data matches string byte, a timestamp
>                   is copied into the FIFO
> 4 - Copy/Done: packet data will be copied into the FIFO.
>                 This command terminates the command string.
>
> The driver consists of two modules:
> - Core: it provides an API to user space using the Generic Netlink
>          framework. Specific backend implementations, like the
>          Ethernet Packet Sniffer, register one or more channels
>          with the Core. For each channel a Genl family is created.
>          User space can access a channel by sending Genl messages
>          to the Genl family associated with the channel. Packet
>          matching events are multicast.
>
> - Ethernet Packet Sniffer backend: provides the driver for the
>          Linn Ethernet Packet Sniffer H/W modules.
>
> The split between a core and backend modules allows software-only
> implementations to be added for platforms where no H/W support
> is available.
>
> Based on 3.19-rc5
>
> Signed-off-by: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>

Please have a look at packet sockets, they offer already all the
functionality (if not more) your driver interface to the user space
resembles, are transparent to the underlying hardware, and easily
can cope with 100Mbit.

If I understand this correctly, you are effectively introducing a
parallel API *next* to packet sockets to user space that we have to
maintain forever ...

Thanks !
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
James Hogan Jan. 23, 2015, 6:12 p.m. UTC | #4
Hi,

A few general things below (I'll leave the actual networking bits for
others to comment about).

On Friday 23 January 2015 10:07:01 Stathis Voukelatos wrote:
> ---
>  .../bindings/net/linn-ether-packet-sniffer.txt     |  27 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  MAINTAINERS                                        |   7 +
>  drivers/net/Kconfig                                |   2 +
>  drivers/net/Makefile                               |   1 +
>  drivers/net/pkt-sniffer/Kconfig                    |  23 ++
>  drivers/net/pkt-sniffer/Makefile                   |   8 +
>  drivers/net/pkt-sniffer/backends/ether/channel.c   | 366 ++++++++++++++++++
>  drivers/net/pkt-sniffer/backends/ether/channel.h   |  76 ++++
>  drivers/net/pkt-sniffer/backends/ether/hw.h        |  46 +++
>  drivers/net/pkt-sniffer/backends/ether/platform.c  | 231 +++++++++++
>  drivers/net/pkt-sniffer/core/dev_table.c           | 124 ++++++
>  drivers/net/pkt-sniffer/core/module.c              |  37 ++
>  drivers/net/pkt-sniffer/core/nl.c                  | 427 +++++++++++++++++++++
>  drivers/net/pkt-sniffer/core/nl.h                  |  34 ++
>  drivers/net/pkt-sniffer/core/snf_core.h            |  64 +++
>  include/linux/pkt_sniffer.h                        |  89 +++++

Probably worth splitting this up a bit into a series of multiple
logically separate patches. E.g. the vendor prefix, the core, the ether
backend and dt bindings.

> diff --git a/drivers/net/pkt-sniffer/Kconfig b/drivers/net/pkt-sniffer/Kconfig
> new file mode 100644
> index 0000000..26b4f98
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/Kconfig
> @@ -0,0 +1,23 @@
> +menuconfig PKT_SNIFFER
> +    tristate "Linn packet sniffer support"

Should the kconfig symbol have linn in the name, or should the prompt
not have lin in the name?

> +    ---help---
> +    Say Y to add support for Linn packet sniffer drivers.
> +
> +    The core driver can also be built as a module. If so, the module
> +    will be called snf_core.
> +
> +if PKT_SNIFFER

Just make PKT_SNIFFER_ETHER depend first on PKT_SNIFFER, then it'll
appear nested within it in menuconfig.

> +
> +config PKT_SNIFFER_ETHER
> +    tristate "Ethernet packet sniffer"
> +    depends on MIPS

worth adding || COMPILE_TEST to get compile coverage on x86 allmodconfig
builds, or does it have hard dependencies on the MIPS arch?

> +    default n

No need, n is default

> +    help
> +        Say Y here if you want to use the Linn Ethernet packet sniffer
> +        module. It can be found in the upcoming Pistachio SoC by
> +        Imagination Technologies.
> +
> +        The driver can also be built as a module. If so, the module
> +        will be called snf_ether.
> +
> +endif # PKT_SNIFFER


> +/* Called when the packet sniffer device is bound with the driver */
> +static int esnf_driver_probe(struct platform_device *pdev)
> +{
> +	struct ether_snf *esnf;
> +	struct resource *res;
> +	int ret, irq;
> +	u32 fifo_blk_words;
> +	void __iomem *regs;
> +	struct device_node *ofn = pdev->dev.of_node;
> +
> +	/* Allocate the device data structure */
> +	esnf = devm_kzalloc(&pdev->dev, sizeof(*esnf), GFP_KERNEL);
> +	if (!esnf)
> +		return -ENOMEM;
> +
> +	/* Retrieve and remap register memory space */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res)
> +		return -ENODEV;

No need for this check. devm_ioremap_resource does it for you.

> +
> +	regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +


> +static const struct of_device_id esnf_of_match_table[] = {
> +	{ .compatible = "linn,eth-sniffer", .data = NULL },

Nit: not strictly necessary to initialise .data since it's static.

Cheers
James

> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, esnf_of_match_table);
> +
> +static struct platform_driver esnf_platform_driver = {
> +	.driver = {
> +		.name = esnf_driver_name,
> +		.of_match_table = esnf_of_match_table,
> +	},
> +	.probe = esnf_driver_probe,
> +	.remove = esnf_driver_remove,
> +};
> +
> +module_platform_driver(esnf_platform_driver);
> +
> +MODULE_DESCRIPTION("Linn Ethernet Packet Sniffer");
> +MODULE_AUTHOR("Linn Products Ltd");
> +MODULE_LICENSE("GPL v2");

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joe Perches Jan. 24, 2015, 9:37 p.m. UTC | #5
On Fri, 2015-01-23 at 10:07 +0000, Stathis Voukelatos wrote:
> This patch adds support the Ethernet Packet Sniffer H/W module
> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
> The module allows Ethernet packets to be parsed, matched against
> a user-defined pattern and timestamped. It sits between a 100M
> Ethernet MAC and PHY and is completely passive with respect to
> Ethernet frames.
[]
>  include/linux/pkt_sniffer.h                        |  89 +++++

Why should this file be here?
Why not in the drivers/net/pkt-sniffer directory?

> diff --git a/drivers/net/pkt-sniffer/backends/ether/channel.c b/drivers/net/pkt-sniffer/backends/ether/channel.c
[]
> +static int esnf_start(struct snf_chan *dev);

Be nice to rearrange the code to avoid the forward declarations.

> +static int esnf_stop(struct snf_chan *dev);
> +static int esnf_set_pattern(struct snf_chan *dev, const u8 *pattern, int count);
> +static int esnf_num_recs_avail(struct snf_chan *dev);
> +static int esnf_max_ptn_entries(struct snf_chan *dev);
> +static int esnf_max_match_bytes(struct snf_chan *dev);
> +static int validate_pattern(
> +			struct ether_snf_chan *ch,
> +			const u8 *buf,
> +			int count);

[]

> +static int validate_pattern(struct ether_snf_chan *ch, const u8 *buf, int count)
> +{

Maybe better as bool

> +	int i, complete, max_copy_bytes;
[]
> +	/* Check if the string was properly terminated
> +	 * and contained valid number of commands
> +	 */
> +	if (complete) {
> +		max_copy_bytes = ch->fifo_blk_words * 4;
> +		if (ts)
> +			max_copy_bytes -= 4;
> +		if ((copy_before + copy_after) > max_copy_bytes)
> +			return 0;
> +		ch->ts_present = ts;
> +		ch->nfb_before = copy_before;
> +		ch->nfb_after = copy_after;
> +		return 1;
> +	} else {
> +		return 0;
> +	}

	return complete;

[]

> +/* Interrupt thread function */
> +static irqreturn_t esnf_irq_thread(int irq, void *dev_id)
> +{
> +	struct platform_device *pdev = (struct platform_device *)dev_id;
> +	struct ether_snf *esnf = (struct ether_snf *)platform_get_drvdata(pdev);
> +	u32 irq_status;
> +
> +	if (unlikely(esnf->irq != irq))
> +		return IRQ_NONE;
> +
> +	irq_status = ioread32(esnf->regs + INTERRUPT_STATUS) &
> +				 ioread32(esnf->regs + INTERRUPT_ENABLE);
> +
> +	dev_dbg(&pdev->dev, "irq: 0x%08x\n", irq_status);
> +
> +	/* TX FIFO full */
> +	if (unlikely(irq_status & TX_FULL_IRQ_BIT))
> +		dev_notice(&pdev->dev, "TX FIFO full");

Missing terminating newlines
> +
> +	/* RX FIFO full */
> +	if (unlikely(irq_status & RX_FULL_IRQ_BIT))
> +		dev_notice(&pdev->dev, "RX FIFO full");
> +
> +	/* TX match data available */
> +	if (irq_status & TX_DATA_IRQ_BIT) {
> +		dev_dbg(&pdev->dev, "TX data");
> +		channel_data_available(&esnf->txc);
> +	}
> +
> +	/* RX match data available */
> +	if (irq_status & RX_DATA_IRQ_BIT) {
> +		dev_dbg(&pdev->dev, "RX data");
> +		channel_data_available(&esnf->rxc);
> +	}

> diff --git a/drivers/net/pkt-sniffer/core/nl.c b/drivers/net/pkt-sniffer/core/nl.c
[]
> +int snf_netlink_init(int id, struct snf_chan *dev, const char *name)
> +{
[]
> +	/* Allocate ops array and copy template data */
> +	nl->ops = kmalloc(sizeof(snf_ops_tmpl), GFP_KERNEL);
> +	if (!nl->ops) {
> +		ret = -ENOMEM;
> +		goto fail2;
> +	}
> +	memcpy(nl->ops, snf_ops_tmpl, sizeof(snf_ops_tmpl));

kmemdup



--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 26, 2015, 9:49 a.m. UTC | #6
On 23/01/15 11:20, Daniel Borkmann wrote:
> On 01/23/2015 11:07 AM, Stathis Voukelatos wrote:
>> This patch adds support the Ethernet Packet Sniffer H/W module
>> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
>> The module allows Ethernet packets to be parsed, matched against
>> a user-defined pattern and timestamped. It sits between a 100M
>> Ethernet MAC and PHY and is completely passive with respect to
>> Ethernet frames.
>>
>> Matched packet bytes and timestamp values are returned through a
>> FIFO. Timestamps are provided to the module through an externally
>> generated Gray-encoded counter.
>>
>> The command pattern for packet matching is stored in module RAM
>> and consists of a sequence of 16-bit entries. Each entry includes
>> an 8-bit command code and and 8-bit data value. Valid command
>> codes are:
>> 0 - Don't care
>> 1 - Match: packet data must match command string byte
>> 2 - Copy: packet data will be copied to FIFO
>> 3 - Match/Stamp: if packet data matches string byte, a timestamp
>>                    is copied into the FIFO
>> 4 - Copy/Done: packet data will be copied into the FIFO.
>>                  This command terminates the command string.
>>
>> The driver consists of two modules:
>> - Core: it provides an API to user space using the Generic Netlink
>>           framework. Specific backend implementations, like the
>>           Ethernet Packet Sniffer, register one or more channels
>>           with the Core. For each channel a Genl family is created.
>>           User space can access a channel by sending Genl messages
>>           to the Genl family associated with the channel. Packet
>>           matching events are multicast.
>>
>> - Ethernet Packet Sniffer backend: provides the driver for the
>>           Linn Ethernet Packet Sniffer H/W modules.
>>
>> The split between a core and backend modules allows software-only
>> implementations to be added for platforms where no H/W support
>> is available.
>>
>> Based on 3.19-rc5
>>
>> Signed-off-by: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> Please have a look at packet sockets, they offer already all the
> functionality (if not more) your driver interface to the user space
> resembles, are transparent to the underlying hardware, and easily
> can cope with 100Mbit.
>
> If I understand this correctly, you are effectively introducing a
> parallel API *next* to packet sockets to user space that we have to
> maintain forever ...
>
> Thanks !
>

Hello Daniel. Thank you for your feedback.
Packet sockets could also be used for the driver interface to
user space, however I think that both approaches would require the same
amount of maintenance. We need to maintain a protocol consisting of
a set of messages or commands that user space can use to communicate
with the driver in order to configure the H/W and retrieve results.
We could use packet sockets to send those messages  too, but I thought
netlink already provides a message exchange framework that we could
make use of.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Daniel Borkmann Jan. 26, 2015, 10:10 a.m. UTC | #7
Hi Stathis,

On 01/26/2015 10:49 AM, Stathis Voukelatos wrote:
> On 23/01/15 11:20, Daniel Borkmann wrote:
>> On 01/23/2015 11:07 AM, Stathis Voukelatos wrote:
>>> This patch adds support the Ethernet Packet Sniffer H/W module
>>> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
>>> The module allows Ethernet packets to be parsed, matched against
>>> a user-defined pattern and timestamped. It sits between a 100M
>>> Ethernet MAC and PHY and is completely passive with respect to
>>> Ethernet frames.
>>>
>>> Matched packet bytes and timestamp values are returned through a
>>> FIFO. Timestamps are provided to the module through an externally
>>> generated Gray-encoded counter.
>>>
>>> The command pattern for packet matching is stored in module RAM
>>> and consists of a sequence of 16-bit entries. Each entry includes
>>> an 8-bit command code and and 8-bit data value. Valid command
>>> codes are:
>>> 0 - Don't care
>>> 1 - Match: packet data must match command string byte
>>> 2 - Copy: packet data will be copied to FIFO
>>> 3 - Match/Stamp: if packet data matches string byte, a timestamp
>>>                    is copied into the FIFO
>>> 4 - Copy/Done: packet data will be copied into the FIFO.
>>>                  This command terminates the command string.
>>>
>>> The driver consists of two modules:
>>> - Core: it provides an API to user space using the Generic Netlink
>>>           framework. Specific backend implementations, like the
>>>           Ethernet Packet Sniffer, register one or more channels
>>>           with the Core. For each channel a Genl family is created.
>>>           User space can access a channel by sending Genl messages
>>>           to the Genl family associated with the channel. Packet
>>>           matching events are multicast.
>>>
>>> - Ethernet Packet Sniffer backend: provides the driver for the
>>>           Linn Ethernet Packet Sniffer H/W modules.
>>>
>>> The split between a core and backend modules allows software-only
>>> implementations to be added for platforms where no H/W support
>>> is available.
>>>
>>> Based on 3.19-rc5
>>>
>>> Signed-off-by: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
>> Please have a look at packet sockets, they offer already all the
>> functionality (if not more) your driver interface to the user space
>> resembles, are transparent to the underlying hardware, and easily
>> can cope with 100Mbit.
>>
>> If I understand this correctly, you are effectively introducing a
>> parallel API *next* to packet sockets to user space that we have to
>> maintain forever ...
>>
>> Thanks !
>
> Hello Daniel. Thank you for your feedback.
> Packet sockets could also be used for the driver interface to
> user space, however I think that both approaches would require the same
> amount of maintenance. We need to maintain a protocol consisting of
> a set of messages or commands that user space can use to communicate
> with the driver in order to configure the H/W and retrieve results.
> We could use packet sockets to send those messages  too, but I thought
> netlink already provides a message exchange framework that we could
> make use of.

When using packet sockets and your driver as a backend feeding them,
users can see that there's an extra capturing/monitoring netdev present,
all libpcap-based tools such as tcpdump et al would work out of the box
w/o adapting any code, and as an admin you can also see what users/tools
are making of use of the device through packet sockets. I couldn't parse
the exact motivation from the commit message of why avoiding all this is
better?

Thanks,
Daniel
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 26, 2015, 10:16 a.m. UTC | #8
On 23/01/15 10:51, Mark Rutland wrote:
>
> diff --git a/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> new file mode 100644
> index 0000000..6b6e105
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> @@ -0,0 +1,27 @@
> +* Linn Products Ethernet Packet Sniffer
> +
> +Required properties:
> +- compatible : must be "linn,eth-sniffer"
> +- reg : physical addresses and sizes of registers. Must contain 3 entries:
> +          first entry: registers memory space
> +          second entry: TX command memory
> +          third entry: RX command memory
> Just to check: are those memories are part of the packet sniffer device,
> or are carveouts from other memory?
Yes, the 3 memory areas are part of the packet sniffer module.
>> +- fifo-block-words : number of words in one data FIFO entry
>> +
>> +Example:
>> +
>> +sniffer@1814a000 {
>> +        compatible = "linn,eth-sniffer";
>> +        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
>> +        reg-names = "regs", "tx-ram", "rx-ram";
>> +        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
>> +        interrupt-names = "eth-sniffer-irq";
>> +        clocks = <&system_clk>;
>> +        clock-names = "sys";
>> +        fifo-block-words = <4>;
>> +    };
> Surely the relationship between the sniffer, MAC, and PHY should be
> described, so we know which interface the sniffer is related to?
>
The packet sniffer sits between the MAC and the PHY and monitors TX or RX
packets, or both. Will add a description in the binding doc.

Your other suggestions too, will be incorporated in the next version of
the patch set.

Thank you,
Stathis

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 26, 2015, 11:05 a.m. UTC | #9
On 23/01/15 18:12, James Hogan wrote:
>> diff --git a/drivers/net/pkt-sniffer/Kconfig 
>> b/drivers/net/pkt-sniffer/Kconfig
>> new file mode 100644
>> index 0000000..26b4f98
>> --- /dev/null
>> +++ b/drivers/net/pkt-sniffer/Kconfig
>> @@ -0,0 +1,23 @@
>> +menuconfig PKT_SNIFFER
>> +    tristate "Linn packet sniffer support"
> Should the kconfig symbol have linn in the name, or should the prompt
> not have lin in the name?

No it should not actually, as this option enables the core (framework) 
driver.
Anybody could add a backend using the framework. Will change the text.

>> +
>> +config PKT_SNIFFER_ETHER
>> +    tristate "Ethernet packet sniffer"
>> +    depends on MIPS
> worth adding || COMPILE_TEST to get compile coverage on x86 allmodconfig
> builds, or does it have hard dependencies on the MIPS arch?
>

No hard dependencies on MIPS arch. Will change as you suggest.
Will implement your other recommendations too in the next version
of the patchset.

Thank you,
Stathis

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 26, 2015, 11:11 a.m. UTC | #10
On 24/01/15 21:37, Joe Perches wrote:
> On Fri, 2015-01-23 at 10:07 +0000, Stathis Voukelatos wrote:
>> This patch adds support the Ethernet Packet Sniffer H/W module
>> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
>> The module allows Ethernet packets to be parsed, matched against
>> a user-defined pattern and timestamped. It sits between a 100M
>> Ethernet MAC and PHY and is completely passive with respect to
>> Ethernet frames.
> []
>>   include/linux/pkt_sniffer.h                        |  89 +++++
> Why should this file be here?
> Why not in the drivers/net/pkt-sniffer directory?
>
Hi Joe,
Thank you for the feedback.
This header file is the public API for the driver.
Should it not live under the 'include' directory?
Several other drivers seem to follow that convention.

I will include your other suggestions in the next version
of the patch set.
Thank you,
Stathis
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 26, 2015, 11:23 a.m. UTC | #11
On 23/01/15 10:21, Arnd Bergmann wrote:
> On Friday 23 January 2015 10:07:01 Stathis Voukelatos wrote:
>> +- interrupts : sniffer interrupt specifier
>> +- clocks : specify the system clock for the peripheral
>> +- clock-names : must contain the "sys" entry
>> +- fifo-block-words : number of words in one data FIFO entry
>> +
>> +Example:
>> +
>> +sniffer@1814a000 {
>> +        compatible = "linn,eth-sniffer";
>> +        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
>> +        reg-names = "regs", "tx-ram", "rx-ram";
>> +        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
>> +        interrupt-names = "eth-sniffer-irq";
>> +        clocks = <&system_clk>;
>> +        clock-names = "sys";
>> +        fifo-block-words = <4>;
>>
> The example contains an interrupt-names property that is not documented.
> If you want to name interrupts, the exact name strings need to
> be mandated by the binding. Alternatively just drop the name.
> I notice that the driver requests the first interrupt without giving
> a name anyway, and the description above suggests that there can
> only be one interrupt.
>
> 	Arnd
>
Hi Arnd,
Yes, we can probably just drop the interrupt name field.
It will be done in the next version of the patch set.
Thank you,
Stathis

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joe Perches Jan. 26, 2015, 7:39 p.m. UTC | #12
On Mon, 2015-01-26 at 11:11 +0000, Stathis Voukelatos wrote:
> On 24/01/15 21:37, Joe Perches wrote:
> > On Fri, 2015-01-23 at 10:07 +0000, Stathis Voukelatos wrote:
> >> This patch adds support the Ethernet Packet Sniffer H/W module
> >> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
> >> The module allows Ethernet packets to be parsed, matched against
> >> a user-defined pattern and timestamped. It sits between a 100M
> >> Ethernet MAC and PHY and is completely passive with respect to
> >> Ethernet frames.
> > []
> >>   include/linux/pkt_sniffer.h                        |  89 +++++
> > Why should this file be here?
> > Why not in the drivers/net/pkt-sniffer directory?
[]
> This header file is the public API for the driver.
> Should it not live under the 'include' directory?
> Several other drivers seem to follow that convention.

It depends on how public is public.

If it's _really_ public, it should be in uapi.
If it's kinda public, then _maybe_ it should be
in include/linux, but how likely is it another
driver will use it?


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Florian Fainelli Jan. 26, 2015, 10:30 p.m. UTC | #13
On 23/01/15 02:07, Stathis Voukelatos wrote:
> This patch adds support the Ethernet Packet Sniffer H/W module
> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
> The module allows Ethernet packets to be parsed, matched against
> a user-defined pattern and timestamped. It sits between a 100M
> Ethernet MAC and PHY and is completely passive with respect to
> Ethernet frames.

Is there any latency penalty involved in capturing (or not) packets as
opposed to having this capture HW unused?

> 
> Matched packet bytes and timestamp values are returned through a
> FIFO. Timestamps are provided to the module through an externally
> generated Gray-encoded counter.
> 
> The command pattern for packet matching is stored in module RAM
> and consists of a sequence of 16-bit entries. Each entry includes
> an 8-bit command code and and 8-bit data value. Valid command
> codes are:
> 0 - Don't care
> 1 - Match: packet data must match command string byte
> 2 - Copy: packet data will be copied to FIFO
> 3 - Match/Stamp: if packet data matches string byte, a timestamp
>                  is copied into the FIFO
> 4 - Copy/Done: packet data will be copied into the FIFO.
>                This command terminates the command string.
> 
> The driver consists of two modules:
> - Core: it provides an API to user space using the Generic Netlink
>         framework. Specific backend implementations, like the
>         Ethernet Packet Sniffer, register one or more channels
>         with the Core. For each channel a Genl family is created.
>         User space can access a channel by sending Genl messages
>         to the Genl family associated with the channel. Packet
>         matching events are multicast.

Instead of having this new generic netlink family to control sniffing,
could we imagine registering a netdevice which does not nothing but
still allows for tools like tcpdump, af_packet and other capture tools
to work transparently and just leverage the HW capture?

> 
> - Ethernet Packet Sniffer backend: provides the driver for the
>         Linn Ethernet Packet Sniffer H/W modules.
> 
> The split between a core and backend modules allows software-only
> implementations to be added for platforms where no H/W support
> is available.
> 
> Based on 3.19-rc5
> 
> Signed-off-by: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> ---
>  .../bindings/net/linn-ether-packet-sniffer.txt     |  27 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |   1 +
>  MAINTAINERS                                        |   7 +
>  drivers/net/Kconfig                                |   2 +
>  drivers/net/Makefile                               |   1 +
>  drivers/net/pkt-sniffer/Kconfig                    |  23 ++
>  drivers/net/pkt-sniffer/Makefile                   |   8 +
>  drivers/net/pkt-sniffer/backends/ether/channel.c   | 366 ++++++++++++++++++
>  drivers/net/pkt-sniffer/backends/ether/channel.h   |  76 ++++
>  drivers/net/pkt-sniffer/backends/ether/hw.h        |  46 +++
>  drivers/net/pkt-sniffer/backends/ether/platform.c  | 231 +++++++++++
>  drivers/net/pkt-sniffer/core/dev_table.c           | 124 ++++++
>  drivers/net/pkt-sniffer/core/module.c              |  37 ++
>  drivers/net/pkt-sniffer/core/nl.c                  | 427 +++++++++++++++++++++
>  drivers/net/pkt-sniffer/core/nl.h                  |  34 ++
>  drivers/net/pkt-sniffer/core/snf_core.h            |  64 +++
>  include/linux/pkt_sniffer.h                        |  89 +++++
>  17 files changed, 1563 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
>  create mode 100644 drivers/net/pkt-sniffer/Kconfig
>  create mode 100644 drivers/net/pkt-sniffer/Makefile
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/channel.c
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/channel.h
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/hw.h
>  create mode 100644 drivers/net/pkt-sniffer/backends/ether/platform.c
>  create mode 100644 drivers/net/pkt-sniffer/core/dev_table.c
>  create mode 100644 drivers/net/pkt-sniffer/core/module.c
>  create mode 100644 drivers/net/pkt-sniffer/core/nl.c
>  create mode 100644 drivers/net/pkt-sniffer/core/nl.h
>  create mode 100644 drivers/net/pkt-sniffer/core/snf_core.h
>  create mode 100644 include/linux/pkt_sniffer.h
> 
> diff --git a/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> new file mode 100644
> index 0000000..6b6e105
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> @@ -0,0 +1,27 @@
> +* Linn Products Ethernet Packet Sniffer
> +
> +Required properties:
> +- compatible : must be "linn,eth-sniffer"
> +- reg : physical addresses and sizes of registers. Must contain 3 entries:
> +          first entry: registers memory space
> +          second entry: TX command memory
> +          third entry: RX command memory
> +- reg-names : must contain the following 3 entries:
> +                  "regs", "tx-ram", "rx-ram"
> +- interrupts : sniffer interrupt specifier
> +- clocks : specify the system clock for the peripheral
> +- clock-names : must contain the "sys" entry
> +- fifo-block-words : number of words in one data FIFO entry
> +
> +Example:
> +
> +sniffer@1814a000 {
> +        compatible = "linn,eth-sniffer";
> +        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
> +        reg-names = "regs", "tx-ram", "rx-ram";
> +        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
> +        interrupt-names = "eth-sniffer-irq";
> +        clocks = <&system_clk>;
> +        clock-names = "sys";
> +        fifo-block-words = <4>;
> +    };
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index b1df0ad..2c96f35 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -90,6 +90,7 @@ lacie	LaCie
>  lantiq	Lantiq Semiconductor
>  lenovo	Lenovo Group Ltd.
>  lg	LG Corporation
> +linn	Linn Products Ltd.
>  linux	Linux-specific binding
>  lsi	LSI Corp. (LSI Logic)
>  lltc	Linear Technology Corporation
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2fa3853..7dbc6e7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5708,6 +5708,13 @@ M:	Sasha Levin <sasha.levin@oracle.com>
>  S:	Maintained
>  F:	tools/lib/lockdep/
>  
> +LINN PACKET SNIFFER DRIVER
> +M: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> +S: Maintained
> +F: include/linux/pkt_sniffer.h
> +F: drivers/net/pkt-sniffer/
> +F: Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> +
>  LINUX FOR IBM pSERIES (RS/6000)
>  M:	Paul Mackerras <paulus@au.ibm.com>
>  W:	http://www.ibm.com/linux/ltc/projects/ppc
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index d6607ee..219c786 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -380,4 +380,6 @@ config VMXNET3
>  
>  source "drivers/net/hyperv/Kconfig"
>  
> +source "drivers/net/pkt-sniffer/Kconfig"
> +
>  endif # NETDEVICES
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index e25fdd7..441111b 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -66,3 +66,4 @@ obj-$(CONFIG_USB_NET_DRIVERS) += usb/
>  
>  obj-$(CONFIG_HYPERV_NET) += hyperv/
>  obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o
> +obj-$(CONFIG_PKT_SNIFFER) += pkt-sniffer/
> diff --git a/drivers/net/pkt-sniffer/Kconfig b/drivers/net/pkt-sniffer/Kconfig
> new file mode 100644
> index 0000000..26b4f98
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/Kconfig
> @@ -0,0 +1,23 @@
> +menuconfig PKT_SNIFFER
> +    tristate "Linn packet sniffer support"
> +    ---help---
> +    Say Y to add support for Linn packet sniffer drivers.
> +
> +    The core driver can also be built as a module. If so, the module
> +    will be called snf_core.
> +
> +if PKT_SNIFFER
> +
> +config PKT_SNIFFER_ETHER
> +    tristate "Ethernet packet sniffer"
> +    depends on MIPS
> +    default n
> +    help
> +        Say Y here if you want to use the Linn Ethernet packet sniffer
> +        module. It can be found in the upcoming Pistachio SoC by
> +        Imagination Technologies.
> +
> +        The driver can also be built as a module. If so, the module
> +        will be called snf_ether.
> +
> +endif # PKT_SNIFFER
> diff --git a/drivers/net/pkt-sniffer/Makefile b/drivers/net/pkt-sniffer/Makefile
> new file mode 100644
> index 0000000..07e7339
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/Makefile
> @@ -0,0 +1,8 @@
> +snf_core-y += core/nl.o
> +snf_core-y += core/dev_table.o
> +snf_core-y += core/module.o
> +obj-$(CONFIG_PKT_SNIFFER) += snf_core.o
> +
> +snf_ether-y += backends/ether/platform.o
> +snf_ether-y += backends/ether/channel.o
> +obj-$(CONFIG_PKT_SNIFFER_ETHER) += snf_ether.o
> diff --git a/drivers/net/pkt-sniffer/backends/ether/channel.c b/drivers/net/pkt-sniffer/backends/ether/channel.c
> new file mode 100644
> index 0000000..d483b58
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/backends/ether/channel.c
> @@ -0,0 +1,366 @@
> +/*
> + * Ethernet Mii packet sniffer driver
> + *  - channel functions
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/pkt_sniffer.h>
> +#include "../../core/snf_core.h"
> +#include "hw.h"
> +#include "channel.h"
> +
> +#define to_ether_snf_chan(dev) container_of(dev, struct ether_snf_chan, chan)
> +
> +static int esnf_start(struct snf_chan *dev);
> +static int esnf_stop(struct snf_chan *dev);
> +static int esnf_set_pattern(struct snf_chan *dev, const u8 *pattern, int count);
> +static int esnf_num_recs_avail(struct snf_chan *dev);
> +static int esnf_max_ptn_entries(struct snf_chan *dev);
> +static int esnf_max_match_bytes(struct snf_chan *dev);
> +static int validate_pattern(
> +			struct ether_snf_chan *ch,
> +			const u8 *buf,
> +			int count);
> +static void read_fifo_data(struct ether_snf_chan *ch);
> +static u32 gray_decode(u32 gray);
> +
> +/* Initialises a sniffer channel */
> +int channel_init(
> +	struct ether_snf_chan *ch,
> +	struct platform_device *pdev,
> +	void *regs,
> +	int fifo_blk_words,
> +	int tx)
> +{
> +	struct resource *res;
> +	u32 *ptr;
> +	int i;
> +
> +	ch->regs = regs;
> +	ch->dev = &pdev->dev;
> +	ch->data_irq_bit = tx ? TX_DATA_IRQ_BIT : RX_DATA_IRQ_BIT;
> +	ch->full_irq_bit = tx ? TX_FULL_IRQ_BIT : RX_FULL_IRQ_BIT;
> +	ch->reg_enable = ch->regs +
> +			 (tx ? TX_SNIFFER_ENABLE : RX_SNIFFER_ENABLE);
> +	ch->reg_occ = ch->regs + (tx ? TX_FIFO_OCC : RX_FIFO_OCC);
> +	ch->reg_fifo = ch->regs + (tx ? TX_FIFO_DAT : RX_FIFO_DAT);
> +
> +	/* Retrieve and remap the address space for the command memory */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +					   tx ? "tx-ram" : "rx-ram");
> +	if (!res)
> +		return -ENOSYS;
> +	ch->cmd_ram = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(ch->cmd_ram))
> +		return PTR_ERR(ch->cmd_ram);
> +
> +	/* It is 2 bytes/command, hence divide by 2 */
> +	ch->max_cmds = resource_size(res) / 2;
> +
> +	/* Initialise the command pattern RAM */
> +	for (i = 0, ptr = ch->cmd_ram; i < resource_size(res); i += 4)
> +		iowrite32((PTN_CMD_DONTCARE << 24) | (PTN_CMD_DONTCARE << 8),
> +			  ptr++);
> +
> +	ch->fifo_blk_words = fifo_blk_words;
> +	ch->started = 0;
> +
> +	/* Register the channel methods */
> +	ch->chan.start = esnf_start;
> +	ch->chan.stop = esnf_stop;
> +	ch->chan.set_pattern = esnf_set_pattern;
> +	ch->chan.num_recs_avail = esnf_num_recs_avail;
> +	ch->chan.max_ptn_entries = esnf_max_ptn_entries;
> +	ch->chan.max_match_bytes = esnf_max_match_bytes;
> +
> +	strncpy(ch->name, tx ? "TX" : "RX", MAX_CHAN_NAME_SIZE - 1);
> +
> +	dev_dbg(ch->dev, "%s channel initialized\n", ch->name);
> +
> +	return 0;
> +}
> +
> +/* Registers the channel with the sniffer core module */
> +int channel_register(struct ether_snf_chan *ch, const char *name)
> +{
> +	int id;
> +
> +	id = snf_channel_add(&ch->chan, name);
> +	if (id < 0)
> +		return id;
> +
> +	ch->id = id;
> +	dev_info(ch->dev, "%s channel added\n", ch->name);
> +	return 0;
> +}
> +
> +/* Unregisters the channel */
> +int channel_unregister(struct ether_snf_chan *ch)
> +{
> +	int ret;
> +
> +	ch->chan.stop(&ch->chan);
> +	ret = snf_channel_remove(ch->id);
> +	if (!ret)
> +		dev_info(ch->dev, "%s channel removed\n", ch->name);
> +	return ret;
> +}
> +
> +/* Process match event data */
> +void channel_data_available(struct ether_snf_chan *ch)
> +{
> +	int ret;
> +
> +	dev_dbg(ch->dev, "%s match event\n", ch->name);
> +
> +	read_fifo_data(ch);
> +	ret = snf_channel_notify_match(&ch->chan, &ch->evt);
> +	if (ret < 0)
> +		dev_err(ch->dev, "%s: event notification failed\n", ch->name);
> +}
> +
> +/* Channel methods */
> +
> +/* Enables the channel */
> +static int esnf_start(struct snf_chan *dev)
> +{
> +	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
> +
> +	if (!ch->started) {
> +		/* Enable interrupts */
> +		iowrite32(ch->data_irq_bit | ch->full_irq_bit,
> +			  ch->regs + SET_INTERRUPT_ENABLE);
> +		/* Enable the packet matching logic */
> +		iowrite32(ENABLE_BIT, ch->reg_enable);
> +
> +		ch->started = 1;
> +		dev_info(ch->dev, "%s channel started\n", ch->name);
> +	} else {
> +		dev_dbg(ch->dev, "%s channel already running\n", ch->name);
> +	}
> +
> +	return 0;
> +}
> +
> +/* Disables the channel */
> +static int esnf_stop(struct snf_chan *dev)
> +{
> +	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
> +
> +	if (ch->started) {
> +		/* Disable interrupts */
> +		iowrite32(ch->data_irq_bit | ch->full_irq_bit,
> +			  ch->regs + CLEAR_INTERRUPT_ENABLE);
> +		/* Stop the sniffer channel */
> +		iowrite32(0, ch->reg_enable);
> +		/* Clear any pending interrupts */
> +		iowrite32(ch->data_irq_bit | ch->full_irq_bit,
> +			  ch->regs + INTERRUPT_STATUS);
> +
> +		ch->started = 0;
> +		dev_info(ch->dev, "%s channel stopped\n", ch->name);
> +	} else {
> +		dev_dbg(ch->dev, "%s channel already stopped\n", ch->name);
> +	}
> +
> +	return 0;
> +}
> +
> +/* Sets the command string (pattern) for the channel
> + * The bytes in the pattern buffer are in the following order:
> + */
> +static int esnf_set_pattern(struct snf_chan *dev, const u8 *pattern, int count)
> +{
> +	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
> +	int i, shift = 0;
> +	u32  val = 0, *ptr;
> +
> +	if (ch->started) {
> +		dev_err(ch->dev,
> +			"cannot apply cmd pattern. %s channel is active\n",
> +			ch->name);
> +		return -EBUSY;
> +	}
> +
> +	if (!validate_pattern(ch, pattern, count)) {
> +		dev_err(ch->dev,
> +			"invalid cmd pattern for %s channel\n",
> +			ch->name);
> +		return -EINVAL;
> +	}
> +
> +	for (ptr = ch->cmd_ram, i = 0, shift = 24; i < (2*count); i++) {
> +		val |= ((u32)pattern[i]) << shift;
> +		if (!shift) {
> +			iowrite32(val, ptr++);
> +			val = 0;
> +			shift = 24;
> +		} else {
> +			shift -= 8;
> +		}
> +	}
> +	if (shift)
> +		iowrite32(val, ptr);
> +
> +	return 0;
> +}
> +
> +/* Returns the number of pending match events that are
> + * available to retrieve
> + */
> +static int esnf_num_recs_avail(struct snf_chan *dev)
> +{
> +	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
> +
> +	return ioread32(ch->reg_occ);
> +}
> +
> +/* Returns max number of commands supported by the channel */
> +static int esnf_max_ptn_entries(struct snf_chan *dev)
> +{
> +	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
> +
> +	return ch->max_cmds;
> +}
> +
> +/* Returns max number of bytes that can be returned by a match */
> +static int esnf_max_match_bytes(struct snf_chan *dev)
> +{
> +	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
> +
> +	/* Subtract the word that may be used for the timestamp */
> +	return (ch->fifo_blk_words - 1) * 4;
> +}
> +
> +/* Checks if the supplied command string is compatible with the
> + * capabilities of the H/W
> + */
> +static int validate_pattern(struct ether_snf_chan *ch, const u8 *buf, int count)
> +{
> +	int i, complete, max_copy_bytes;
> +	int ts = 0;
> +	int copy_before = 0;
> +	int copy_after = 0;
> +
> +	if (count > ch->max_cmds)
> +		return 0;
> +
> +	/* Iterate through the commands in the string */
> +	for (i = 0, complete = 0; (i < count) && !complete; i++) {
> +		u8 cmd = buf[2*i];
> +
> +		switch (cmd) {
> +		case PTN_CMD_DONTCARE:
> +		case PTN_CMD_MATCH:
> +			break;
> +
> +		case PTN_CMD_MATCHSTAMP:
> +			/* The timestamp needs to be word-aligned in the FIFO
> +			 * therefore, the number of 'copy' commands before it
> +			 * needs to be a multiple of 4
> +			 */
> +			if (copy_before & 3)
> +				return 0;
> +			/* Signal that a timestamp will be present */
> +			ts = 1;
> +			break;
> +
> +		case PTN_CMD_COPY:
> +			/* Increment count of bytes that will be returned */
> +			if (ts)
> +				copy_after++;
> +			else
> +				copy_before++;
> +			break;
> +
> +		case PTN_CMD_COPYDONE:
> +			/* Increment count of bytes that will be returned
> +			 * This command terminates the string
> +			 */
> +			if (ts)
> +				copy_after++;
> +			else
> +				copy_before++;
> +			complete = 1;
> +			break;
> +
> +		default:
> +			return 0;
> +		}
> +	}
> +
> +	/* Check if the string was properly terminated
> +	 * and contained valid number of commands
> +	 */
> +	if (complete) {
> +		max_copy_bytes = ch->fifo_blk_words * 4;
> +		if (ts)
> +			max_copy_bytes -= 4;
> +		if ((copy_before + copy_after) > max_copy_bytes)
> +			return 0;
> +		ch->ts_present = ts;
> +		ch->nfb_before = copy_before;
> +		ch->nfb_after = copy_after;
> +		return 1;
> +	} else {
> +		return 0;
> +	}
> +}
> +
> +/* Read a block from the data FIFO */
> +static void read_fifo_data(struct ether_snf_chan *ch)
> +{
> +	int i;
> +	u32 val, *ptr;
> +	int ts = ch->ts_present;
> +	int dw = ch->fifo_blk_words;
> +	int bytes_before = ch->nfb_before;
> +	int bytes_after = ch->nfb_after;
> +
> +	ptr = (u32 *)ch->evt.data;
> +	for (i = 0; i < dw; i++) {
> +		val = ioread32(ch->reg_fifo);
> +		if (bytes_before > 0) {
> +			/* Bytes before the timestamp */
> +			*ptr++ = cpu_to_be32(val);
> +			bytes_before -= 4;
> +		} else if (ts) {
> +			/* Timestamp is Gray encoded */
> +			ch->evt.ts = (u64)gray_decode(val);
> +			ts = 0;
> +		} else if (bytes_after > 0) {
> +			/* Bytes after the timestamp */
> +			*ptr++ = cpu_to_be32(val);
> +			bytes_after -= 4;
> +		}
> +	}
> +
> +	ch->evt.ts_valid = ch->ts_present;
> +	ch->evt.len = ch->nfb_before + ch->nfb_after;
> +}
> +
> +/* Gray decoder */
> +static u32 gray_decode(u32 gray)
> +{
> +	gray ^= (gray >> 16);
> +	gray ^= (gray >> 8);
> +	gray ^= (gray >> 4);
> +	gray ^= (gray >> 2);
> +	gray ^= (gray >> 1);
> +	return gray;
> +}
> +
> diff --git a/drivers/net/pkt-sniffer/backends/ether/channel.h b/drivers/net/pkt-sniffer/backends/ether/channel.h
> new file mode 100644
> index 0000000..4f00b33
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/backends/ether/channel.h
> @@ -0,0 +1,76 @@
> +/*
> + * Ethernet Mii packet sniffer driver
> + *  - channel interface
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#ifndef _ETHER_SNIFFER_CHANNEL_H_
> +#define _ETHER_SNIFFER_CHANNEL_H_
> +
> +#include <linux/platform_device.h>
> +#include "../../core/snf_core.h"
> +
> +#define MAX_CHAN_NAME_SIZE 5
> +
> +struct ether_snf_chan {
> +	/* Sniffer core structure */
> +	struct snf_chan chan;
> +	/* Pointer to device struct */
> +	struct device *dev;
> +	/* Registers */
> +	u8 __iomem *regs;
> +	/* Command string memory */
> +	u32 __iomem *cmd_ram;
> +	/* Bit number for the data IRQ */
> +	int data_irq_bit;
> +	/* Bit number for the FIFO full IRQ */
> +	int full_irq_bit;
> +	/* Channel enable register */
> +	u8 __iomem *reg_enable;
> +	/* Data FIFO register */
> +	u8 __iomem *reg_fifo;
> +	/* FIFO occupancy register */
> +	u8 __iomem *reg_occ;
> +	/* Max number of commands in the string */
> +	int max_cmds;
> +	/* Max matching bytes that can fit in a FIFO block */
> +	int fifo_blk_words;
> +	/* Number of bytes in the FIFO before the timestamp */
> +	int nfb_before;
> +	/* Number of bytes in the FIFO after the timestamp */
> +	int nfb_after;
> +	/* Timestamp present flag */
> +	int ts_present;
> +	/* ID assigned to channel by the sniffer core */
> +	int id;
> +	/* Channel active flag */
> +	int started;
> +	/* Channel name (only used by debug messages) */
> +	char name[MAX_CHAN_NAME_SIZE];
> +	/* Struct to hold data from a packet match */
> +	struct snf_match_evt evt;
> +};
> +
> +int channel_init(
> +		struct ether_snf_chan *ch,
> +		struct platform_device *pdev,
> +		void *regs,
> +		int fifo_blk_words,
> +		int tx);
> +int channel_register(struct ether_snf_chan *ch, const char *name);
> +int channel_unregister(struct ether_snf_chan *ch);
> +void channel_data_available(struct ether_snf_chan *ch);
> +
> +#endif
> diff --git a/drivers/net/pkt-sniffer/backends/ether/hw.h b/drivers/net/pkt-sniffer/backends/ether/hw.h
> new file mode 100644
> index 0000000..edb1093
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/backends/ether/hw.h
> @@ -0,0 +1,46 @@
> +/*
> + * Ethernet Mii packet sniffer driver
> + *  - register map
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#ifndef _ETHER_SNIFFER_HW_H_
> +#define _ETHER_SNIFFER_HW_H_
> +
> +#include <linux/bitops.h>
> +
> +/* Registers */
> +#define INTERRUPT_ENABLE        0x00
> +#define SET_INTERRUPT_ENABLE    0x04
> +#define CLEAR_INTERRUPT_ENABLE  0x08
> +#define INTERRUPT_STATUS        0x0c
> +#define TX_FIFO_DAT             0x10
> +#define RX_FIFO_DAT             0x14
> +#define TX_FIFO_OCC             0x18
> +#define RX_FIFO_OCC             0x1c
> +#define TX_SNIFFER_ENABLE       0x20
> +#define RX_SNIFFER_ENABLE       0x24
> +
> +/* IRQ register bits */
> +#define TX_DATA_IRQ_BIT         BIT(0)
> +#define RX_DATA_IRQ_BIT         BIT(1)
> +#define TX_FULL_IRQ_BIT         BIT(2)
> +#define RX_FULL_IRQ_BIT         BIT(3)
> +
> +/* Enable register bits */
> +#define ENABLE_BIT              BIT(0)
> +
> +#endif
> +
> diff --git a/drivers/net/pkt-sniffer/backends/ether/platform.c b/drivers/net/pkt-sniffer/backends/ether/platform.c
> new file mode 100644
> index 0000000..78e7e1c
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/backends/ether/platform.c
> @@ -0,0 +1,231 @@
> +/*
> + * Ethernet Mii packet sniffer driver
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include "../../core/snf_core.h"
> +#include "hw.h"
> +#include "channel.h"
> +
> +static const char esnf_driver_name[] = "eth-sniffer";
> +
> +/* Names for the TX and RX channel.
> + * User space will used these names to access the channels
> + * through the generic netlink interface
> + */
> +static const char tx_channel_name[] = "snf_ether_tx";
> +static const char rx_channel_name[] = "snf_ether_rx";
> +
> +struct ether_snf {
> +	u8 __iomem *regs;
> +	int irq;
> +	struct clk *sys_clk;
> +	struct ether_snf_chan txc;
> +	struct ether_snf_chan rxc;
> +};
> +
> +/* Interrupt thread function */
> +static irqreturn_t esnf_irq_thread(int irq, void *dev_id)
> +{
> +	struct platform_device *pdev = (struct platform_device *)dev_id;
> +	struct ether_snf *esnf = (struct ether_snf *)platform_get_drvdata(pdev);
> +	u32 irq_status;
> +
> +	if (unlikely(esnf->irq != irq))
> +		return IRQ_NONE;
> +
> +	irq_status = ioread32(esnf->regs + INTERRUPT_STATUS) &
> +				 ioread32(esnf->regs + INTERRUPT_ENABLE);
> +
> +	dev_dbg(&pdev->dev, "irq: 0x%08x\n", irq_status);
> +
> +	/* TX FIFO full */
> +	if (unlikely(irq_status & TX_FULL_IRQ_BIT))
> +		dev_notice(&pdev->dev, "TX FIFO full");
> +
> +	/* RX FIFO full */
> +	if (unlikely(irq_status & RX_FULL_IRQ_BIT))
> +		dev_notice(&pdev->dev, "RX FIFO full");
> +
> +	/* TX match data available */
> +	if (irq_status & TX_DATA_IRQ_BIT) {
> +		dev_dbg(&pdev->dev, "TX data");
> +		channel_data_available(&esnf->txc);
> +	}
> +
> +	/* RX match data available */
> +	if (irq_status & RX_DATA_IRQ_BIT) {
> +		dev_dbg(&pdev->dev, "RX data");
> +		channel_data_available(&esnf->rxc);
> +	}
> +
> +	/* Clear interrupts */
> +	iowrite32(irq_status, esnf->regs + INTERRUPT_STATUS);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* Called when the packet sniffer device is bound with the driver */
> +static int esnf_driver_probe(struct platform_device *pdev)
> +{
> +	struct ether_snf *esnf;
> +	struct resource *res;
> +	int ret, irq;
> +	u32 fifo_blk_words;
> +	void __iomem *regs;
> +	struct device_node *ofn = pdev->dev.of_node;
> +
> +	/* Allocate the device data structure */
> +	esnf = devm_kzalloc(&pdev->dev, sizeof(*esnf), GFP_KERNEL);
> +	if (!esnf)
> +		return -ENOMEM;
> +
> +	/* Retrieve and remap register memory space */
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> +	if (!res)
> +		return -ENODEV;
> +
> +	regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	esnf->regs = regs;
> +
> +	/* Read the FIFO block size from the DT */
> +	if (!ofn)
> +		return -ENODEV;
> +
> +	ret = of_property_read_u32(
> +				ofn,
> +				"fifo-block-words",
> +				&fifo_blk_words);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (((fifo_blk_words - 1)*4) > MAX_MATCH_BYTES) {
> +		dev_err(&pdev->dev,
> +			"Invalid FIFO block size entry in device tree\n");
> +		return -EINVAL;
> +	}
> +
> +	esnf->sys_clk = devm_clk_get(&pdev->dev, "sys");
> +	if (IS_ERR(esnf->sys_clk)) {
> +		ret = PTR_ERR(esnf->sys_clk);
> +		return ret;
> +	}
> +	ret = clk_prepare_enable(esnf->sys_clk);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Initialise the TX and RX channels */
> +	ret = channel_init(&esnf->txc, pdev, regs, fifo_blk_words, 1);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to init TX channel (%d)\n", ret);
> +		goto fail1;
> +	}
> +	ret = channel_init(&esnf->rxc, pdev, regs, fifo_blk_words, 0);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to init RX channel (%d)\n", ret);
> +		goto fail1;
> +	}
> +
> +	/* Register the channels with the sniffer core module */
> +	ret = channel_register(&esnf->txc, tx_channel_name);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to register TX chan (%d)\n", ret);
> +		goto fail1;
> +	}
> +	ret = channel_register(&esnf->rxc, rx_channel_name);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to register RX chan (%d)\n", ret);
> +		goto fail2;
> +	}
> +
> +	platform_set_drvdata(pdev, esnf);
> +
> +	/* Register the interrupt handler */
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0)
> +		goto fail3;
> +	esnf->irq = irq;
> +	ret = devm_request_threaded_irq(
> +				&pdev->dev,
> +				irq,
> +				NULL,
> +				esnf_irq_thread,
> +				IRQF_ONESHOT,
> +				esnf_driver_name,
> +				pdev);
> +	if (ret < 0)
> +		goto fail3;
> +
> +	return 0;
> +
> +fail3:
> +	channel_unregister(&esnf->rxc);
> +fail2:
> +	channel_unregister(&esnf->txc);
> +fail1:
> +	clk_disable_unprepare(esnf->sys_clk);
> +	return ret;
> +}
> +
> +/* Called when the packet sniffer device unregisters with the driver */
> +static int esnf_driver_remove(struct platform_device *pdev)
> +{
> +	struct ether_snf *esnf = (struct ether_snf *)platform_get_drvdata(pdev);
> +	int ret;
> +
> +	ret = channel_unregister(&esnf->txc);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to unregister TX chan (%d)\n", ret);
> +		return ret;
> +	}
> +	ret = channel_unregister(&esnf->rxc);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to unregister RX chan (%d)\n", ret);
> +		return ret;
> +	}
> +	clk_disable_unprepare(esnf->sys_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id esnf_of_match_table[] = {
> +	{ .compatible = "linn,eth-sniffer", .data = NULL },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, esnf_of_match_table);
> +
> +static struct platform_driver esnf_platform_driver = {
> +	.driver = {
> +		.name = esnf_driver_name,
> +		.of_match_table = esnf_of_match_table,
> +	},
> +	.probe = esnf_driver_probe,
> +	.remove = esnf_driver_remove,
> +};
> +
> +module_platform_driver(esnf_platform_driver);
> +
> +MODULE_DESCRIPTION("Linn Ethernet Packet Sniffer");
> +MODULE_AUTHOR("Linn Products Ltd");
> +MODULE_LICENSE("GPL v2");
> +
> diff --git a/drivers/net/pkt-sniffer/core/dev_table.c b/drivers/net/pkt-sniffer/core/dev_table.c
> new file mode 100644
> index 0000000..3a07838
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/core/dev_table.c
> @@ -0,0 +1,124 @@
> +/*
> + * Packet sniffer core driver: channel management
> + *
> + * Copyright (C) 2014 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include "snf_core.h"
> +#include "nl.h"
> +
> +#define MAX_CHANNELS   256
> +
> +static DEFINE_MUTEX(tlock);
> +
> +static struct snf_chan *dev_tab[MAX_CHANNELS];
> +static unsigned int ref_count[MAX_CHANNELS];
> +
> +static inline int verify_args(int id)
> +{
> +	return (id < MAX_CHANNELS);
> +}
> +
> +/* Registers a sniffer channel and returns and id for it */
> +int snf_channel_add(struct snf_chan *dev, const char *name)
> +{
> +	int i;
> +	int ret = -EEXIST;
> +
> +	mutex_lock(&tlock);
> +
> +	for (i = 0; i < MAX_CHANNELS; i++) {
> +		if (!dev_tab[i]) {
> +			/* Initialise the netlink interface for the channel */
> +			ret = snf_netlink_init(i, dev, name);
> +			if (ret < 0)
> +				goto fail;
> +
> +			dev_tab[i] = dev;
> +			ref_count[i] = 0;
> +			mutex_unlock(&tlock);
> +			return i;
> +		}
> +	}
> +
> +fail:
> +	mutex_unlock(&tlock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(snf_channel_add);
> +
> +/* Removes a sniffer channel */
> +int snf_channel_remove(int id)
> +{
> +	int ret = 0;
> +	struct snf_chan *dev;
> +
> +	if (!verify_args(id))
> +		return -EINVAL;
> +
> +	mutex_lock(&tlock);
> +
> +	dev = dev_tab[id];
> +
> +	if (!dev) {
> +		ret = -ENODEV;
> +		goto fail;
> +	}
> +
> +	if (ref_count[id] > 0) {
> +		ret = -EBUSY;
> +		goto fail;
> +	}
> +
> +	dev_tab[id] = NULL;
> +
> +	/* Release netlink API resources */
> +	snf_netlink_release(dev);
> +
> +fail:
> +	mutex_unlock(&tlock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(snf_channel_remove);
> +
> +/* Returns a pointer to the specified sniffer channel
> + * and increments its reference counter
> + */
> +struct snf_chan *snf_channel_get(int id)
> +{
> +	struct snf_chan *dev;
> +
> +	if (!verify_args(id))
> +		return NULL;
> +
> +	mutex_lock(&tlock);
> +	dev = dev_tab[id];
> +	if (dev)
> +		ref_count[id]++;
> +	mutex_unlock(&tlock);
> +
> +	return dev;
> +}
> +
> +/* Decrements the reference counter for the channel */
> +void snf_channel_put(int id)
> +{
> +	mutex_lock(&tlock);
> +	if (ref_count[id] > 0)
> +		ref_count[id]--;
> +	mutex_unlock(&tlock);
> +}
> +
> diff --git a/drivers/net/pkt-sniffer/core/module.c b/drivers/net/pkt-sniffer/core/module.c
> new file mode 100644
> index 0000000..af6a1aa
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/core/module.c
> @@ -0,0 +1,37 @@
> +/*
> + * Packet sniffer core driver:
> + *  - backend channel management
> + *  - interface to userland via generic netlink
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#include <linux/module.h>
> +#include <linux/init.h>
> +
> +static int __init snf_core_init(void)
> +{
> +	return 0;
> +}
> +
> +static void __exit snf_core_cleanup(void)
> +{
> +}
> +
> +module_init(snf_core_init);
> +module_exit(snf_core_cleanup);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Core packet sniffer driver");
> +MODULE_AUTHOR("Linn Products Ltd");
> diff --git a/drivers/net/pkt-sniffer/core/nl.c b/drivers/net/pkt-sniffer/core/nl.c
> new file mode 100644
> index 0000000..6839147
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/core/nl.c
> @@ -0,0 +1,427 @@
> +/*
> + * Packet sniffer core driver: generic netlink interface
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#include <linux/version.h>
> +#include <net/netlink.h>
> +#include <net/genetlink.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/pkt_sniffer.h>
> +#include "snf_core.h"
> +#include "nl.h"
> +
> +/* Netlink API data for a sniffer channel */
> +struct snf_netlink {
> +	/* genl family */
> +	struct genl_family           fml;
> +	/* genl operations */
> +	struct genl_ops             *ops;
> +	/* genl mcast group, where sniffer match
> +	 * events will be sent
> +	 */
> +	struct genl_multicast_group  grp;
> +};
> +
> +/* Attribute policies */
> +static struct nla_policy snf_policy[SNF_ATTR_MAX + 1] = {
> +	[SNF_ATTR_PATTERN] = { .type = NLA_NESTED },
> +};
> +
> +static struct nla_policy snf_ptn_policy[SNF_ATTR_PTN_MAX + 1] = {
> +	[SNF_ATTR_PTN_ENTRY] = { .type = NLA_U16 },
> +};
> +
> +/* Generic Netlink family template definition */
> +static int pre_doit_func(const struct genl_ops *ops,
> +			 struct sk_buff *skb,
> +			 struct genl_info *info);
> +
> +static void post_doit_func(const struct genl_ops *ops,
> +			   struct sk_buff *skb,
> +			   struct genl_info *info);
> +
> +static struct genl_family snf_family_tmpl = {
> +	.id = GENL_ID_GENERATE,
> +	.hdrsize = 0,
> +	.version = SNF_GNL_VERSION,
> +	.maxattr = SNF_ATTR_MAX,
> +	.pre_doit = pre_doit_func,
> +	.post_doit = post_doit_func
> +};
> +
> +static int send_reply_uint32(
> +			struct genl_info *info,
> +			int cmd,
> +			int attr,
> +			u32 val);
> +
> +/* Generic Netlink operations template definition */
> +
> +static int do_it_start(struct sk_buff *skb, struct genl_info *info);
> +static int do_it_stop(struct sk_buff *skb, struct genl_info *info);
> +static int do_it_set_pattern(struct sk_buff *skb, struct genl_info *info);
> +static int do_it_num_rec_avail(struct sk_buff *skb, struct genl_info *info);
> +static int do_it_ptn_max_cmds(struct sk_buff *skb, struct genl_info *info);
> +static int do_it_max_match_bytes(struct sk_buff *skb, struct genl_info *info);
> +
> +#define SNF_GENL_OP(_cmd, _func)	 \
> +	{				 \
> +		.cmd	= _cmd,          \
> +		.policy = snf_policy,    \
> +		.doit   = _func,         \
> +		.dumpit = NULL,          \
> +		.flags  = 0,             \
> +		.internal_flags = 0      \
> +	}
> +
> +#define SNF_CHAN_OPS							   \
> +	{								   \
> +		SNF_GENL_OP(SNF_CMD_START,         do_it_start),	   \
> +		SNF_GENL_OP(SNF_CMD_STOP,          do_it_stop),		   \
> +		SNF_GENL_OP(SNF_CMD_SETPATTERN,    do_it_set_pattern),     \
> +		SNF_GENL_OP(SNF_CMD_NUMRECAVAIL,   do_it_num_rec_avail),   \
> +		SNF_GENL_OP(SNF_CMD_PTNMAXCMDS,    do_it_ptn_max_cmds),    \
> +		SNF_GENL_OP(SNF_CMD_MAXMATCHBYTES, do_it_max_match_bytes)  \
> +	}
> +
> +static struct genl_ops snf_ops_tmpl[] = SNF_CHAN_OPS;
> +
> +#define NUM_GENL_OPS ARRAY_SIZE(snf_ops_tmpl)
> +
> +/* Multicast a netlink event containing data from a sniffer match event.
> + * Data are included in the netlink message as a nested attribute
> + * containing the timestamp (optional) and matching packet bytes
> +*/
> +int snf_channel_notify_match(struct snf_chan *dev, struct snf_match_evt *mt)
> +{
> +	int i, ret = 0;
> +	struct sk_buff *msg = NULL;
> +	void *msg_hdr, *nest_hdr;
> +	struct snf_netlink *nl = (struct snf_netlink *)dev->cstate;
> +
> +	if (!nl) {
> +		ret = -EINVAL;
> +		goto fail;
> +	}
> +
> +	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
> +	if (!msg) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	msg_hdr = genlmsg_put(msg,
> +			      0,
> +			      0,
> +			      &nl->fml,
> +			      0,
> +			      SNF_CMD_MATCHEVENT);
> +	if (!msg_hdr) {
> +		ret = -EMSGSIZE;
> +		goto fail;
> +	}
> +
> +	/* Add the nested attribute with the data */
> +	if ((mt->ts_valid) || (mt->len > 0)) {
> +		nest_hdr = nla_nest_start(msg, SNF_ATTR_MATCHEVENT);
> +		if (!nest_hdr) {
> +			ret = -EMSGSIZE;
> +			goto fail;
> +		}
> +		if (mt->ts_valid) {
> +			ret = nla_put_u64(msg, SNF_ATTR_MATCH_TS, mt->ts);
> +			if (ret < 0)
> +				goto fail;
> +		}
> +		for (i = 0; i < mt->len; i++) {
> +			ret = nla_put_u8(msg,
> +					 SNF_ATTR_MATCH_PKTBYTE,
> +					 mt->data[i]);
> +			if (ret < 0)
> +				goto fail;
> +		}
> +		nla_nest_end(msg, nest_hdr);
> +	}
> +
> +	ret = genlmsg_end(msg, msg_hdr);
> +	if (ret < 0)
> +		goto fail;
> +	ret = genlmsg_multicast(&nl->fml, msg, 0, 0, GFP_ATOMIC);
> +
> +	/* The ESRCH code is returned when there is no socket listening on the
> +	 * multicast group, so we do not really treat is as an error
> +	 */
> +	if (ret == -ESRCH)
> +		ret = 0;
> +	return ret;
> +
> +fail:
> +	if (msg)
> +		nlmsg_free(msg);
> +	return ret;
> +}
> +EXPORT_SYMBOL(snf_channel_notify_match);
> +
> +/* Initialise the generic netlink API for a sniffer channel
> + * Registers family, ops and multicast group for events
> + */
> +int snf_netlink_init(int id, struct snf_chan *dev, const char *name)
> +{
> +	int i, ret;
> +	struct snf_netlink *nl = NULL;
> +
> +	nl = kmalloc(sizeof(*nl), GFP_KERNEL);
> +	if (!nl) {
> +		ret = -ENOMEM;
> +		goto fail1;
> +	}
> +
> +	nl->fml = snf_family_tmpl;
> +
> +	/* Set the family name */
> +	strncpy(nl->fml.name, name, GENL_NAMSIZ-1);
> +
> +	/* Allocate ops array and copy template data */
> +	nl->ops = kmalloc(sizeof(snf_ops_tmpl), GFP_KERNEL);
> +	if (!nl->ops) {
> +		ret = -ENOMEM;
> +		goto fail2;
> +	}
> +	memcpy(nl->ops, snf_ops_tmpl, sizeof(snf_ops_tmpl));
> +
> +	/* In the internal_flags field we store the id
> +	 * of the device the ops belong to
> +	 */
> +	for (i = 0; i < NUM_GENL_OPS; i++)
> +		nl->ops[i].internal_flags = id;
> +
> +	snprintf(nl->grp.name, GENL_NAMSIZ-1, SNF_EVENT_GRP);
> +
> +	ret = _genl_register_family_with_ops_grps(
> +						&nl->fml,
> +						nl->ops,
> +						NUM_GENL_OPS,
> +						&nl->grp,
> +						1);
> +	if (ret < 0) {
> +		pr_err("%s: failed to register family %s (%d)\n",
> +		       KBUILD_MODNAME, name, ret);
> +		goto fail3;
> +	}
> +
> +	pr_info("%s: registered genl family for channel %d: %s\n",
> +		KBUILD_MODNAME, id, name);
> +	dev->cstate = nl;
> +
> +	return 0;
> +
> +fail3:
> +	kfree(nl->ops);
> +fail2:
> +	kfree(nl);
> +fail1:
> +	return ret;
> +}
> +
> +/* Shuts down the netlink API for a sniffer channel
> + * and frees resources
> + */
> +void snf_netlink_release(struct snf_chan *dev)
> +{
> +	struct snf_netlink *nl = (struct snf_netlink *)dev->cstate;
> +	/* Unregister family and free ops
> +	 * NOTE: this will also unregister the ops and the mcast group
> +	 */
> +	genl_unregister_family(&nl->fml);
> +	pr_info("%s: unregistered genl family: %s\n",
> +		KBUILD_MODNAME, nl->fml.name);
> +
> +	kfree(nl->ops);
> +	kfree(nl);
> +	dev->cstate = NULL;
> +}
> +
> +/* pre_doit function that retrieves a pointer to the sniffer channel device
> + * from the instance and channel number stored in the ops internal_flag field
> + */
> +static int pre_doit_func(const struct genl_ops *ops,
> +			 struct sk_buff *skb,
> +			 struct genl_info *info)
> +{
> +	info->user_ptr[0] = snf_channel_get(ops->internal_flags);
> +	return info->user_ptr[0] ? 0 : -ENODEV;
> +}
> +
> +/* post_doit function that releases a sniffer channel device */
> +static void post_doit_func(const struct genl_ops *ops,
> +			   struct sk_buff *skb,
> +			   struct genl_info *info)
> +{
> +	snf_channel_put(ops->internal_flags);
> +}
> +
> +/* doit command handlers */
> +
> +/* Start the sniffer channel */
> +static int do_it_start(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +
> +	return dev->start(dev);
> +}
> +
> +/* Stop the sniffer channel */
> +static int do_it_stop(struct sk_buff *skb, struct genl_info *info)
> +{
> +	int ret;
> +	struct snf_match_evt mt;
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +
> +	ret = dev->stop(dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Multicast an empty match event to notify any user-space
> +	 * listeners that the sniffer is stopping
> +	 */
> +	memset(&mt, 0, sizeof(mt));
> +	return snf_channel_notify_match(dev, &mt);
> +}
> +
> +/* Set the command pattern. The string is received in a nested
> + * attribute containing a sequence of 16-bit valued.
> + * Each value consists of a command (8 bits) and data (8 bits)
> +*/
> +static int do_it_set_pattern(struct sk_buff *skb, struct genl_info *info)
> +{
> +	int ret = 0;
> +	int rem, count;
> +	struct nlattr *nla;
> +	u16 entry;
> +	u8 *buf = NULL;
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +	int max_entries = dev->max_ptn_entries(dev);
> +
> +	if (!info->attrs[SNF_ATTR_PATTERN]) {
> +		ret = -EINVAL;
> +		goto fail;
> +	}
> +
> +	ret = nla_validate_nested(info->attrs[SNF_ATTR_PATTERN],
> +				  SNF_ATTR_PTN_ENTRY, snf_ptn_policy);
> +	if (ret < 0)
> +		goto fail;
> +
> +	buf = kmalloc(max_entries*2, GFP_KERNEL);
> +	if (!buf) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	/* Iterate over the contents of the nested attribute
> +	 * and extract into the buffer
> +	 */
> +	count = 0;
> +	nla_for_each_nested(nla, info->attrs[SNF_ATTR_PATTERN], rem) {
> +		if (count >= max_entries) {
> +			ret = -EINVAL;
> +			goto fail;
> +		}
> +		entry = nla_get_u16(nla);
> +		buf[2*count] = PTNENTRY_CMD(entry);
> +		buf[2*count + 1] = PTNENTRY_DATA(entry);
> +		count++;
> +	}
> +
> +	ret = dev->set_pattern(dev, buf, count);
> +
> +fail:
> +	kfree(buf);
> +	return ret;
> +}
> +
> +/* Number of pending match events */
> +static int do_it_num_rec_avail(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +
> +	return send_reply_uint32(
> +			info,
> +			SNF_CMD_NUMRECAVAIL,
> +			SNF_ATTR_NUMRECAVAIL,
> +			dev->num_recs_avail(dev));
> +}
> +
> +/* Max number of commands that are supported in the command pattern */
> +static int do_it_ptn_max_cmds(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +
> +	return send_reply_uint32(
> +			info,
> +			SNF_CMD_PTNMAXCMDS,
> +			SNF_ATTR_PTNMAXCMDS,
> +			dev->max_ptn_entries(dev));
> +}
> +
> +/* Max bytes that can be returned by a packet match event */
> +static int do_it_max_match_bytes(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +
> +	return send_reply_uint32(
> +			info,
> +			SNF_CMD_MAXMATCHBYTES,
> +			SNF_ATTR_MAXMATCHBYTES,
> +			dev->max_match_bytes(dev));
> +}
> +
> +/* Helper function for sending a reply containing a single u32 attribute */
> +static int send_reply_uint32(struct genl_info *info, int cmd, int attr, u32 val)
> +{
> +	void *hdr;
> +	int ret = 0;
> +	struct sk_buff *msg;
> +	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
> +	struct snf_netlink *nl = (struct snf_netlink *)dev->cstate;
> +
> +	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +	if (!msg) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	hdr = genlmsg_put_reply(msg, info, &nl->fml, 0, cmd);
> +	if (!hdr) {
> +		ret = -EMSGSIZE;
> +		goto fail;
> +	}
> +	ret = nla_put_u32(msg, attr, val);
> +	if (ret < 0)
> +		goto fail;
> +	ret = genlmsg_end(msg, hdr);
> +	if (ret < 0)
> +		goto fail;
> +
> +	return genlmsg_reply(msg, info);
> +
> +fail:
> +	if (msg)
> +		nlmsg_free(msg);
> +	return ret;
> +}
> +
> diff --git a/drivers/net/pkt-sniffer/core/nl.h b/drivers/net/pkt-sniffer/core/nl.h
> new file mode 100644
> index 0000000..f7bf579
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/core/nl.h
> @@ -0,0 +1,34 @@
> +/*
> + * Packet sniffer core driver: generic netlink interface
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#ifndef __SNF_NL_H
> +#define __SNF_NL_H
> +
> +#include <net/genetlink.h>
> +#include "snf_core.h"
> +
> +/* Initialise netlink interface for a sniffer channel,
> + * register netlink families etc.
> + */
> +int snf_netlink_init(int id, struct snf_chan *dev, const char *name);
> +
> +/* Shutdown netlink interface, unregister
> + * families etc.
> + */
> +void snf_netlink_release(struct snf_chan *dev);
> +
> +#endif
> diff --git a/drivers/net/pkt-sniffer/core/snf_core.h b/drivers/net/pkt-sniffer/core/snf_core.h
> new file mode 100644
> index 0000000..062de70
> --- /dev/null
> +++ b/drivers/net/pkt-sniffer/core/snf_core.h
> @@ -0,0 +1,64 @@
> +/*
> + * Packet sniffer core driver
> + * - this header provides the interface to specific backend packet
> + *   sniffer implementations
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#ifndef __SNF_CORE_H
> +#define __SNF_CORE_H
> +
> +#include <linux/types.h>
> +
> +/* This is a global maximum. Each backend will have its own limit */
> +#define MAX_MATCH_BYTES 512
> +
> +/* Sniffer channel data structure */
> +struct snf_chan {
> +	int  (*start)(struct snf_chan *dev);
> +	int  (*stop)(struct snf_chan *dev);
> +	int  (*set_pattern)(struct snf_chan *dev, const u8 *pattern, int count);
> +	int  (*num_recs_avail)(struct snf_chan *dev);
> +	int  (*max_ptn_entries)(struct snf_chan *dev);
> +	int  (*max_match_bytes)(struct snf_chan *dev);
> +
> +	void *cstate;
> +};
> +
> +/* Data from a sniffer match event */
> +struct snf_match_evt {
> +	int ts_valid;     /* flag indicating if timestamp is present */
> +	u64 ts;           /* timestamp value */
> +	u8 data[MAX_MATCH_BYTES]; /* packet data bytes matched by sniffer */
> +	int len;          /* number of valid bytes in the 'data' buffer */
> +};
> +
> +/* Adds a sniffer channel to the registry */
> +int snf_channel_add(struct snf_chan *dev, const char *name);
> +
> +/* Removes the channel with the specified id from the registry */
> +int snf_channel_remove(int id);
> +
> +/* Returns handle to a channel and increments reference count */
> +struct snf_chan *snf_channel_get(int id);
> +
> +/* Decrements reference count for the specified channel */
> +void snf_channel_put(int id);
> +
> +/* Multicast a notification to user space for a sniffer match event */
> +int snf_channel_notify_match(struct snf_chan *dev, struct snf_match_evt *mt);
> +
> +#endif
> +
> diff --git a/include/linux/pkt_sniffer.h b/include/linux/pkt_sniffer.h
> new file mode 100644
> index 0000000..3e73d14
> --- /dev/null
> +++ b/include/linux/pkt_sniffer.h
> @@ -0,0 +1,89 @@
> +/*
> + * Linn packet sniffer driver interface
> + *
> + * Copyright (C) 2015 Linn Products Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation.
> + *
> + * 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.
> + *
> + * Written by:
> + * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
> + */
> +#ifndef __PKT_SNIFFER_H
> +#define __PKT_SNIFFER_H
> +
> +/* Commands for the pattern string */
> +#define PTN_CMD_DONTCARE    0
> +#define PTN_CMD_MATCH       1
> +#define PTN_CMD_COPY        2
> +#define PTN_CMD_MATCHSTAMP  3
> +#define PTN_CMD_COPYDONE    4
> +
> +/* Creates an entry for the pattern string.
> + * An entry consists of a command byte and a data byte
> +*/
> +#define MAKE_PTN_ENTRY(cmd, data) (((((int)cmd) & 0xff) << 8) | (data & 0xff))
> +/* Gets the cmd and data portion of a pattern string entry */
> +#define PTNENTRY_CMD(val)        (((val) >> 8) & 0xff)
> +#define PTNENTRY_DATA(val)       ((val) & 0xff)
> +
> +/* Generic Netlink commands */
> +enum {
> +	SNF_CMD_UNSPEC,
> +	SNF_CMD_START,          /* start the sniffer */
> +	SNF_CMD_STOP,           /* stop the sniffer */
> +	SNF_CMD_SETPATTERN,     /* set the command pattern */
> +	SNF_CMD_NUMRECAVAIL,    /* number of pending match events */
> +	SNF_CMD_MATCHEVENT,     /* match event notification */
> +	SNF_CMD_PTNMAXCMDS,     /* max number of commands supported */
> +	SNF_CMD_MAXMATCHBYTES,  /* max number of bytes in match event */
> +	__SNF_CMD_MAX
> +};
> +#define SNF_CMD_MAX (__SNF_CMD_MAX - 1)
> +
> +/* Generic Netlink attributes */
> +enum {
> +	SNF_ATTR_UNSPEC,
> +	SNF_ATTR_PTNMAXCMDS,    /* max number of commands supported */
> +	SNF_ATTR_PATTERN,       /* nested attribute containing commands */
> +	SNF_ATTR_NUMRECAVAIL,   /* number of pending match events */
> +	SNF_ATTR_MATCHEVENT,    /* nested attribute for a match event */
> +	SNF_ATTR_MAXMATCHBYTES, /* max bytes in a match event */
> +	__SNF_ATTR_MAX
> +};
> +#define SNF_ATTR_MAX (__SNF_ATTR_MAX - 1)
> +
> +/* Attributes that are included in the nested attribute
> + * for the command string
> + */
> +enum {
> +	SNF_ATTR_PTN_UNSPEC,
> +	SNF_ATTR_PTN_ENTRY,    /* command entry containing a command id */
> +	__SNF_ATTR_PTN_MAX     /* and data byte */
> +};
> +#define SNF_ATTR_PTN_MAX (__SNF_ATTR_PTN_MAX - 1)
> +
> +/* Attributes included in the nested attribute
> + * of a match event notification
> + */
> +enum {
> +	SNF_ATTR_MATCH_UNSPEC,
> +	SNF_ATTR_MATCH_TS,      /* timestamp (returned with a match event) */
> +	SNF_ATTR_MATCH_PKTBYTE, /* packet data (returned with a match event) */
> +	__SNF_ATTR_MATCH_MAX
> +};
> +#define SNF_ATTR_MATCH_MAX (__SNF_ATTR_MATCH_MAX - 1)
> +
> +/* Family version */
> +#define SNF_GNL_VERSION 1
> +
> +/* Multicast group name */
> +#define SNF_EVENT_GRP    "snf_evt_grp"
> +
> +#endif
>
Stathis Voukelatos Jan. 27, 2015, 9:52 a.m. UTC | #14
On 26/01/15 19:39, Joe Perches wrote:
> This header file is the public API for the driver.
> Should it not live under the 'include' directory?
> Several other drivers seem to follow that convention.
> It depends on how public is public.
>
> If it's _really_ public, it should be in uapi.
> If it's kinda public, then _maybe_ it should be
> in include/linux, but how likely is it another
> driver will use it?
>
>
It is intended for user space code that needs
to use the driver as it defines the netlink messages and
attributes that the driver understands. So I guess
uapi/linux would be the place for it.

Thanks,
Stathis
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 27, 2015, 10:51 a.m. UTC | #15
Hi Florian,

On 26/01/15 22:30, Florian Fainelli wrote:
> On 23/01/15 02:07, Stathis Voukelatos wrote:
>> This patch adds support the Ethernet Packet Sniffer H/W module
>> developed by Linn Products Ltd and found in the IMG Pistachio SoC.
>> The module allows Ethernet packets to be parsed, matched against
>> a user-defined pattern and timestamped. It sits between a 100M
>> Ethernet MAC and PHY and is completely passive with respect to
>> Ethernet frames.
> Is there any latency penalty involved in capturing (or not) packets as
> opposed to having this capture HW unused?

There is no additional latency introduced by the sniffer at the H/W level,
if that is what you mean. Only the S/W overhead for handling the
sniffer interrupt for each match event.

>> Matched packet bytes and timestamp values are returned through a
>> FIFO. Timestamps are provided to the module through an externally
>> generated Gray-encoded counter.
>>
>> The command pattern for packet matching is stored in module RAM
>> and consists of a sequence of 16-bit entries. Each entry includes
>> an 8-bit command code and and 8-bit data value. Valid command
>> codes are:
>> 0 - Don't care
>> 1 - Match: packet data must match command string byte
>> 2 - Copy: packet data will be copied to FIFO
>> 3 - Match/Stamp: if packet data matches string byte, a timestamp
>>                   is copied into the FIFO
>> 4 - Copy/Done: packet data will be copied into the FIFO.
>>                 This command terminates the command string.
>>
>> The driver consists of two modules:
>> - Core: it provides an API to user space using the Generic Netlink
>>          framework. Specific backend implementations, like the
>>          Ethernet Packet Sniffer, register one or more channels
>>          with the Core. For each channel a Genl family is created.
>>          User space can access a channel by sending Genl messages
>>          to the Genl family associated with the channel. Packet
>>          matching events are multicast.
> Instead of having this new generic netlink family to control sniffing,
> could we imagine registering a netdevice which does not nothing but
> still allows for tools like tcpdump, af_packet and other capture tools
> to work transparently and just leverage the HW capture?
Thanks, I will work on that change. It has been suggested by a previous
reviewer too and it makes sense to go down that route.

Stathis

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Rutland Jan. 27, 2015, 10:53 a.m. UTC | #16
On Mon, Jan 26, 2015 at 10:16:18AM +0000, Stathis Voukelatos wrote:
> 
> On 23/01/15 10:51, Mark Rutland wrote:
> >
> > diff --git a/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> > new file mode 100644
> > index 0000000..6b6e105
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
> > @@ -0,0 +1,27 @@
> > +* Linn Products Ethernet Packet Sniffer
> > +
> > +Required properties:
> > +- compatible : must be "linn,eth-sniffer"
> > +- reg : physical addresses and sizes of registers. Must contain 3 entries:
> > +          first entry: registers memory space
> > +          second entry: TX command memory
> > +          third entry: RX command memory
> > Just to check: are those memories are part of the packet sniffer device,
> > or are carveouts from other memory?
> Yes, the 3 memory areas are part of the packet sniffer module.
> >> +- fifo-block-words : number of words in one data FIFO entry
> >> +
> >> +Example:
> >> +
> >> +sniffer@1814a000 {
> >> +        compatible = "linn,eth-sniffer";
> >> +        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
> >> +        reg-names = "regs", "tx-ram", "rx-ram";
> >> +        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
> >> +        interrupt-names = "eth-sniffer-irq";
> >> +        clocks = <&system_clk>;
> >> +        clock-names = "sys";
> >> +        fifo-block-words = <4>;
> >> +    };
> > Surely the relationship between the sniffer, MAC, and PHY should be
> > described, so we know which interface the sniffer is related to?
> >
> The packet sniffer sits between the MAC and the PHY and monitors TX or RX
> packets, or both. Will add a description in the binding doc.

I understood that. However the binding does not explicitly refer to
either of those, so I don't see how you'd associate this sniffer
instance with the relevant PHY+MAC instances.

That should be made explicit in the binding.

Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 27, 2015, 11:15 a.m. UTC | #17
Hi Daniel,

On 26/01/15 10:10, Daniel Borkmann wrote:
>> Hello Daniel. Thank you for your feedback.
>> Packet sockets could also be used for the driver interface to
>> user space, however I think that both approaches would require the same
>> amount of maintenance. We need to maintain a protocol consisting of
>> a set of messages or commands that user space can use to communicate
>> with the driver in order to configure the H/W and retrieve results.
>> We could use packet sockets to send those messages  too, but I thought
>> netlink already provides a message exchange framework that we could
>> make use of.
>
> When using packet sockets and your driver as a backend feeding them,
> users can see that there's an extra capturing/monitoring netdev present,
> all libpcap-based tools such as tcpdump et al would work out of the box
> w/o adapting any code, and as an admin you can also see what users/tools
> are making of use of the device through packet sockets. I couldn't parse
> the exact motivation from the commit message of why avoiding all this is
> better?
>
> Thanks,
> Daniel
>
>
Just wanted to clarify some implementation details for your approach.
- The driver would need to create and register two net_device instances.
One for sniffing Ethernet TX packets and one for RX.
- Would the control interface for the sniffer in that case need to be
through private socket ioctls (ie SIOCDEVPRIVATE + x ioctl ids)?
- For each ethernet packet that matches the command string the sniffer
returns some data bytes and optionally a timestamp (depending on the
command string). Would a new protocol need to be added in
<linux/if_ether.h> in order to deliver that data to user space through
a packet socket?

Thanks,
Stathis

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Daniel Borkmann Jan. 27, 2015, 2:46 p.m. UTC | #18
Hi Stathis,

On 01/27/2015 12:15 PM, Stathis Voukelatos wrote:
> On 26/01/15 10:10, Daniel Borkmann wrote:
>>> Hello Daniel. Thank you for your feedback.
>>> Packet sockets could also be used for the driver interface to
>>> user space, however I think that both approaches would require the same
>>> amount of maintenance. We need to maintain a protocol consisting of
>>> a set of messages or commands that user space can use to communicate
>>> with the driver in order to configure the H/W and retrieve results.
>>> We could use packet sockets to send those messages  too, but I thought
>>> netlink already provides a message exchange framework that we could
>>> make use of.
>>
>> When using packet sockets and your driver as a backend feeding them,
>> users can see that there's an extra capturing/monitoring netdev present,
>> all libpcap-based tools such as tcpdump et al would work out of the box
>> w/o adapting any code, and as an admin you can also see what users/tools
>> are making of use of the device through packet sockets. I couldn't parse
>> the exact motivation from the commit message of why avoiding all this is
>> better?
>
> Just wanted to clarify some implementation details for your approach.
> - The driver would need to create and register two net_device instances.
> One for sniffing Ethernet TX packets and one for RX.

Hm, I would represent the whole device as a single monitoring-only netdev.
I'm somehow still missing the big advantage of all this as compared to
using packet sockets on the normal netdev? I couldn't parse that from your
commit message.

> - Would the control interface for the sniffer in that case need to be
> through private socket ioctls (ie SIOCDEVPRIVATE + x ioctl ids)?

Nope, please have a look at Documentation/networking/packet_mmap.txt.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stathis Voukelatos Jan. 27, 2015, 5:22 p.m. UTC | #19
Hi Daniel,

On 27/01/15 14:46, Daniel Borkmann wrote:
>> Just wanted to clarify some implementation details for your approach.
>> - The driver would need to create and register two net_device instances.
>> One for sniffing Ethernet TX packets and one for RX.
>
> Hm, I would represent the whole device as a single monitoring-only 
> netdev.
> I'm somehow still missing the big advantage of all this as compared to
> using packet sockets on the normal netdev? I couldn't parse that from 
> your
> commit message.

This H/W module was developed to allow accurate timestamping of selected
outgoing or incoming data packets. Timestamp values are provided by an
external implementation-dependent clock or timer that is of suitable
quality for the application.
Example: multiple audio receivers synchronizing their clocks to a
single transmitter, for synchronized playback.

The TX and RX blocks of the sniffer can be operated (eg. started, stopped,
configured) independently, that is why I believe two netdev instances would
be a better match to the H/W architecture.

>
>> - Would the control interface for the sniffer in that case need to be
>> through private socket ioctls (ie SIOCDEVPRIVATE + x ioctl ids)?
>
> Nope, please have a look at Documentation/networking/packet_mmap.txt.
>
>
Thanks for the link to the document. That is the way forward for retrieving
data from sniffer match events from user space very efficiently.

However, I am not sure about configuration, where we want to eg set the
command string, or query device attributes such as the size of the command
memory. That looks more suitable to an ioctl or a netlink message to me
and better use the packet socket just for data from sniffer match events.

Thanks,
Stathis


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
new file mode 100644
index 0000000..6b6e105
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
@@ -0,0 +1,27 @@ 
+* Linn Products Ethernet Packet Sniffer
+
+Required properties:
+- compatible : must be "linn,eth-sniffer"
+- reg : physical addresses and sizes of registers. Must contain 3 entries:
+          first entry: registers memory space
+          second entry: TX command memory
+          third entry: RX command memory
+- reg-names : must contain the following 3 entries:
+                  "regs", "tx-ram", "rx-ram"
+- interrupts : sniffer interrupt specifier
+- clocks : specify the system clock for the peripheral
+- clock-names : must contain the "sys" entry
+- fifo-block-words : number of words in one data FIFO entry
+
+Example:
+
+sniffer@1814a000 {
+        compatible = "linn,eth-sniffer";
+        reg = <0x1814a000 0x100>, <0x1814a400 0x400>, <0x1814a800 0x400>;
+        reg-names = "regs", "tx-ram", "rx-ram";
+        interrupts = <GIC_SHARED 58 IRQ_TYPE_LEVEL_HIGH>;
+        interrupt-names = "eth-sniffer-irq";
+        clocks = <&system_clk>;
+        clock-names = "sys";
+        fifo-block-words = <4>;
+    };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index b1df0ad..2c96f35 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -90,6 +90,7 @@  lacie	LaCie
 lantiq	Lantiq Semiconductor
 lenovo	Lenovo Group Ltd.
 lg	LG Corporation
+linn	Linn Products Ltd.
 linux	Linux-specific binding
 lsi	LSI Corp. (LSI Logic)
 lltc	Linear Technology Corporation
diff --git a/MAINTAINERS b/MAINTAINERS
index 2fa3853..7dbc6e7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5708,6 +5708,13 @@  M:	Sasha Levin <sasha.levin@oracle.com>
 S:	Maintained
 F:	tools/lib/lockdep/
 
+LINN PACKET SNIFFER DRIVER
+M: Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+S: Maintained
+F: include/linux/pkt_sniffer.h
+F: drivers/net/pkt-sniffer/
+F: Documentation/devicetree/bindings/net/linn-ether-packet-sniffer.txt
+
 LINUX FOR IBM pSERIES (RS/6000)
 M:	Paul Mackerras <paulus@au.ibm.com>
 W:	http://www.ibm.com/linux/ltc/projects/ppc
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index d6607ee..219c786 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -380,4 +380,6 @@  config VMXNET3
 
 source "drivers/net/hyperv/Kconfig"
 
+source "drivers/net/pkt-sniffer/Kconfig"
+
 endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e25fdd7..441111b 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -66,3 +66,4 @@  obj-$(CONFIG_USB_NET_DRIVERS) += usb/
 
 obj-$(CONFIG_HYPERV_NET) += hyperv/
 obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o
+obj-$(CONFIG_PKT_SNIFFER) += pkt-sniffer/
diff --git a/drivers/net/pkt-sniffer/Kconfig b/drivers/net/pkt-sniffer/Kconfig
new file mode 100644
index 0000000..26b4f98
--- /dev/null
+++ b/drivers/net/pkt-sniffer/Kconfig
@@ -0,0 +1,23 @@ 
+menuconfig PKT_SNIFFER
+    tristate "Linn packet sniffer support"
+    ---help---
+    Say Y to add support for Linn packet sniffer drivers.
+
+    The core driver can also be built as a module. If so, the module
+    will be called snf_core.
+
+if PKT_SNIFFER
+
+config PKT_SNIFFER_ETHER
+    tristate "Ethernet packet sniffer"
+    depends on MIPS
+    default n
+    help
+        Say Y here if you want to use the Linn Ethernet packet sniffer
+        module. It can be found in the upcoming Pistachio SoC by
+        Imagination Technologies.
+
+        The driver can also be built as a module. If so, the module
+        will be called snf_ether.
+
+endif # PKT_SNIFFER
diff --git a/drivers/net/pkt-sniffer/Makefile b/drivers/net/pkt-sniffer/Makefile
new file mode 100644
index 0000000..07e7339
--- /dev/null
+++ b/drivers/net/pkt-sniffer/Makefile
@@ -0,0 +1,8 @@ 
+snf_core-y += core/nl.o
+snf_core-y += core/dev_table.o
+snf_core-y += core/module.o
+obj-$(CONFIG_PKT_SNIFFER) += snf_core.o
+
+snf_ether-y += backends/ether/platform.o
+snf_ether-y += backends/ether/channel.o
+obj-$(CONFIG_PKT_SNIFFER_ETHER) += snf_ether.o
diff --git a/drivers/net/pkt-sniffer/backends/ether/channel.c b/drivers/net/pkt-sniffer/backends/ether/channel.c
new file mode 100644
index 0000000..d483b58
--- /dev/null
+++ b/drivers/net/pkt-sniffer/backends/ether/channel.c
@@ -0,0 +1,366 @@ 
+/*
+ * Ethernet Mii packet sniffer driver
+ *  - channel functions
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/pkt_sniffer.h>
+#include "../../core/snf_core.h"
+#include "hw.h"
+#include "channel.h"
+
+#define to_ether_snf_chan(dev) container_of(dev, struct ether_snf_chan, chan)
+
+static int esnf_start(struct snf_chan *dev);
+static int esnf_stop(struct snf_chan *dev);
+static int esnf_set_pattern(struct snf_chan *dev, const u8 *pattern, int count);
+static int esnf_num_recs_avail(struct snf_chan *dev);
+static int esnf_max_ptn_entries(struct snf_chan *dev);
+static int esnf_max_match_bytes(struct snf_chan *dev);
+static int validate_pattern(
+			struct ether_snf_chan *ch,
+			const u8 *buf,
+			int count);
+static void read_fifo_data(struct ether_snf_chan *ch);
+static u32 gray_decode(u32 gray);
+
+/* Initialises a sniffer channel */
+int channel_init(
+	struct ether_snf_chan *ch,
+	struct platform_device *pdev,
+	void *regs,
+	int fifo_blk_words,
+	int tx)
+{
+	struct resource *res;
+	u32 *ptr;
+	int i;
+
+	ch->regs = regs;
+	ch->dev = &pdev->dev;
+	ch->data_irq_bit = tx ? TX_DATA_IRQ_BIT : RX_DATA_IRQ_BIT;
+	ch->full_irq_bit = tx ? TX_FULL_IRQ_BIT : RX_FULL_IRQ_BIT;
+	ch->reg_enable = ch->regs +
+			 (tx ? TX_SNIFFER_ENABLE : RX_SNIFFER_ENABLE);
+	ch->reg_occ = ch->regs + (tx ? TX_FIFO_OCC : RX_FIFO_OCC);
+	ch->reg_fifo = ch->regs + (tx ? TX_FIFO_DAT : RX_FIFO_DAT);
+
+	/* Retrieve and remap the address space for the command memory */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   tx ? "tx-ram" : "rx-ram");
+	if (!res)
+		return -ENOSYS;
+	ch->cmd_ram = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ch->cmd_ram))
+		return PTR_ERR(ch->cmd_ram);
+
+	/* It is 2 bytes/command, hence divide by 2 */
+	ch->max_cmds = resource_size(res) / 2;
+
+	/* Initialise the command pattern RAM */
+	for (i = 0, ptr = ch->cmd_ram; i < resource_size(res); i += 4)
+		iowrite32((PTN_CMD_DONTCARE << 24) | (PTN_CMD_DONTCARE << 8),
+			  ptr++);
+
+	ch->fifo_blk_words = fifo_blk_words;
+	ch->started = 0;
+
+	/* Register the channel methods */
+	ch->chan.start = esnf_start;
+	ch->chan.stop = esnf_stop;
+	ch->chan.set_pattern = esnf_set_pattern;
+	ch->chan.num_recs_avail = esnf_num_recs_avail;
+	ch->chan.max_ptn_entries = esnf_max_ptn_entries;
+	ch->chan.max_match_bytes = esnf_max_match_bytes;
+
+	strncpy(ch->name, tx ? "TX" : "RX", MAX_CHAN_NAME_SIZE - 1);
+
+	dev_dbg(ch->dev, "%s channel initialized\n", ch->name);
+
+	return 0;
+}
+
+/* Registers the channel with the sniffer core module */
+int channel_register(struct ether_snf_chan *ch, const char *name)
+{
+	int id;
+
+	id = snf_channel_add(&ch->chan, name);
+	if (id < 0)
+		return id;
+
+	ch->id = id;
+	dev_info(ch->dev, "%s channel added\n", ch->name);
+	return 0;
+}
+
+/* Unregisters the channel */
+int channel_unregister(struct ether_snf_chan *ch)
+{
+	int ret;
+
+	ch->chan.stop(&ch->chan);
+	ret = snf_channel_remove(ch->id);
+	if (!ret)
+		dev_info(ch->dev, "%s channel removed\n", ch->name);
+	return ret;
+}
+
+/* Process match event data */
+void channel_data_available(struct ether_snf_chan *ch)
+{
+	int ret;
+
+	dev_dbg(ch->dev, "%s match event\n", ch->name);
+
+	read_fifo_data(ch);
+	ret = snf_channel_notify_match(&ch->chan, &ch->evt);
+	if (ret < 0)
+		dev_err(ch->dev, "%s: event notification failed\n", ch->name);
+}
+
+/* Channel methods */
+
+/* Enables the channel */
+static int esnf_start(struct snf_chan *dev)
+{
+	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
+
+	if (!ch->started) {
+		/* Enable interrupts */
+		iowrite32(ch->data_irq_bit | ch->full_irq_bit,
+			  ch->regs + SET_INTERRUPT_ENABLE);
+		/* Enable the packet matching logic */
+		iowrite32(ENABLE_BIT, ch->reg_enable);
+
+		ch->started = 1;
+		dev_info(ch->dev, "%s channel started\n", ch->name);
+	} else {
+		dev_dbg(ch->dev, "%s channel already running\n", ch->name);
+	}
+
+	return 0;
+}
+
+/* Disables the channel */
+static int esnf_stop(struct snf_chan *dev)
+{
+	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
+
+	if (ch->started) {
+		/* Disable interrupts */
+		iowrite32(ch->data_irq_bit | ch->full_irq_bit,
+			  ch->regs + CLEAR_INTERRUPT_ENABLE);
+		/* Stop the sniffer channel */
+		iowrite32(0, ch->reg_enable);
+		/* Clear any pending interrupts */
+		iowrite32(ch->data_irq_bit | ch->full_irq_bit,
+			  ch->regs + INTERRUPT_STATUS);
+
+		ch->started = 0;
+		dev_info(ch->dev, "%s channel stopped\n", ch->name);
+	} else {
+		dev_dbg(ch->dev, "%s channel already stopped\n", ch->name);
+	}
+
+	return 0;
+}
+
+/* Sets the command string (pattern) for the channel
+ * The bytes in the pattern buffer are in the following order:
+ */
+static int esnf_set_pattern(struct snf_chan *dev, const u8 *pattern, int count)
+{
+	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
+	int i, shift = 0;
+	u32  val = 0, *ptr;
+
+	if (ch->started) {
+		dev_err(ch->dev,
+			"cannot apply cmd pattern. %s channel is active\n",
+			ch->name);
+		return -EBUSY;
+	}
+
+	if (!validate_pattern(ch, pattern, count)) {
+		dev_err(ch->dev,
+			"invalid cmd pattern for %s channel\n",
+			ch->name);
+		return -EINVAL;
+	}
+
+	for (ptr = ch->cmd_ram, i = 0, shift = 24; i < (2*count); i++) {
+		val |= ((u32)pattern[i]) << shift;
+		if (!shift) {
+			iowrite32(val, ptr++);
+			val = 0;
+			shift = 24;
+		} else {
+			shift -= 8;
+		}
+	}
+	if (shift)
+		iowrite32(val, ptr);
+
+	return 0;
+}
+
+/* Returns the number of pending match events that are
+ * available to retrieve
+ */
+static int esnf_num_recs_avail(struct snf_chan *dev)
+{
+	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
+
+	return ioread32(ch->reg_occ);
+}
+
+/* Returns max number of commands supported by the channel */
+static int esnf_max_ptn_entries(struct snf_chan *dev)
+{
+	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
+
+	return ch->max_cmds;
+}
+
+/* Returns max number of bytes that can be returned by a match */
+static int esnf_max_match_bytes(struct snf_chan *dev)
+{
+	struct ether_snf_chan *ch = to_ether_snf_chan(dev);
+
+	/* Subtract the word that may be used for the timestamp */
+	return (ch->fifo_blk_words - 1) * 4;
+}
+
+/* Checks if the supplied command string is compatible with the
+ * capabilities of the H/W
+ */
+static int validate_pattern(struct ether_snf_chan *ch, const u8 *buf, int count)
+{
+	int i, complete, max_copy_bytes;
+	int ts = 0;
+	int copy_before = 0;
+	int copy_after = 0;
+
+	if (count > ch->max_cmds)
+		return 0;
+
+	/* Iterate through the commands in the string */
+	for (i = 0, complete = 0; (i < count) && !complete; i++) {
+		u8 cmd = buf[2*i];
+
+		switch (cmd) {
+		case PTN_CMD_DONTCARE:
+		case PTN_CMD_MATCH:
+			break;
+
+		case PTN_CMD_MATCHSTAMP:
+			/* The timestamp needs to be word-aligned in the FIFO
+			 * therefore, the number of 'copy' commands before it
+			 * needs to be a multiple of 4
+			 */
+			if (copy_before & 3)
+				return 0;
+			/* Signal that a timestamp will be present */
+			ts = 1;
+			break;
+
+		case PTN_CMD_COPY:
+			/* Increment count of bytes that will be returned */
+			if (ts)
+				copy_after++;
+			else
+				copy_before++;
+			break;
+
+		case PTN_CMD_COPYDONE:
+			/* Increment count of bytes that will be returned
+			 * This command terminates the string
+			 */
+			if (ts)
+				copy_after++;
+			else
+				copy_before++;
+			complete = 1;
+			break;
+
+		default:
+			return 0;
+		}
+	}
+
+	/* Check if the string was properly terminated
+	 * and contained valid number of commands
+	 */
+	if (complete) {
+		max_copy_bytes = ch->fifo_blk_words * 4;
+		if (ts)
+			max_copy_bytes -= 4;
+		if ((copy_before + copy_after) > max_copy_bytes)
+			return 0;
+		ch->ts_present = ts;
+		ch->nfb_before = copy_before;
+		ch->nfb_after = copy_after;
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/* Read a block from the data FIFO */
+static void read_fifo_data(struct ether_snf_chan *ch)
+{
+	int i;
+	u32 val, *ptr;
+	int ts = ch->ts_present;
+	int dw = ch->fifo_blk_words;
+	int bytes_before = ch->nfb_before;
+	int bytes_after = ch->nfb_after;
+
+	ptr = (u32 *)ch->evt.data;
+	for (i = 0; i < dw; i++) {
+		val = ioread32(ch->reg_fifo);
+		if (bytes_before > 0) {
+			/* Bytes before the timestamp */
+			*ptr++ = cpu_to_be32(val);
+			bytes_before -= 4;
+		} else if (ts) {
+			/* Timestamp is Gray encoded */
+			ch->evt.ts = (u64)gray_decode(val);
+			ts = 0;
+		} else if (bytes_after > 0) {
+			/* Bytes after the timestamp */
+			*ptr++ = cpu_to_be32(val);
+			bytes_after -= 4;
+		}
+	}
+
+	ch->evt.ts_valid = ch->ts_present;
+	ch->evt.len = ch->nfb_before + ch->nfb_after;
+}
+
+/* Gray decoder */
+static u32 gray_decode(u32 gray)
+{
+	gray ^= (gray >> 16);
+	gray ^= (gray >> 8);
+	gray ^= (gray >> 4);
+	gray ^= (gray >> 2);
+	gray ^= (gray >> 1);
+	return gray;
+}
+
diff --git a/drivers/net/pkt-sniffer/backends/ether/channel.h b/drivers/net/pkt-sniffer/backends/ether/channel.h
new file mode 100644
index 0000000..4f00b33
--- /dev/null
+++ b/drivers/net/pkt-sniffer/backends/ether/channel.h
@@ -0,0 +1,76 @@ 
+/*
+ * Ethernet Mii packet sniffer driver
+ *  - channel interface
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#ifndef _ETHER_SNIFFER_CHANNEL_H_
+#define _ETHER_SNIFFER_CHANNEL_H_
+
+#include <linux/platform_device.h>
+#include "../../core/snf_core.h"
+
+#define MAX_CHAN_NAME_SIZE 5
+
+struct ether_snf_chan {
+	/* Sniffer core structure */
+	struct snf_chan chan;
+	/* Pointer to device struct */
+	struct device *dev;
+	/* Registers */
+	u8 __iomem *regs;
+	/* Command string memory */
+	u32 __iomem *cmd_ram;
+	/* Bit number for the data IRQ */
+	int data_irq_bit;
+	/* Bit number for the FIFO full IRQ */
+	int full_irq_bit;
+	/* Channel enable register */
+	u8 __iomem *reg_enable;
+	/* Data FIFO register */
+	u8 __iomem *reg_fifo;
+	/* FIFO occupancy register */
+	u8 __iomem *reg_occ;
+	/* Max number of commands in the string */
+	int max_cmds;
+	/* Max matching bytes that can fit in a FIFO block */
+	int fifo_blk_words;
+	/* Number of bytes in the FIFO before the timestamp */
+	int nfb_before;
+	/* Number of bytes in the FIFO after the timestamp */
+	int nfb_after;
+	/* Timestamp present flag */
+	int ts_present;
+	/* ID assigned to channel by the sniffer core */
+	int id;
+	/* Channel active flag */
+	int started;
+	/* Channel name (only used by debug messages) */
+	char name[MAX_CHAN_NAME_SIZE];
+	/* Struct to hold data from a packet match */
+	struct snf_match_evt evt;
+};
+
+int channel_init(
+		struct ether_snf_chan *ch,
+		struct platform_device *pdev,
+		void *regs,
+		int fifo_blk_words,
+		int tx);
+int channel_register(struct ether_snf_chan *ch, const char *name);
+int channel_unregister(struct ether_snf_chan *ch);
+void channel_data_available(struct ether_snf_chan *ch);
+
+#endif
diff --git a/drivers/net/pkt-sniffer/backends/ether/hw.h b/drivers/net/pkt-sniffer/backends/ether/hw.h
new file mode 100644
index 0000000..edb1093
--- /dev/null
+++ b/drivers/net/pkt-sniffer/backends/ether/hw.h
@@ -0,0 +1,46 @@ 
+/*
+ * Ethernet Mii packet sniffer driver
+ *  - register map
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#ifndef _ETHER_SNIFFER_HW_H_
+#define _ETHER_SNIFFER_HW_H_
+
+#include <linux/bitops.h>
+
+/* Registers */
+#define INTERRUPT_ENABLE        0x00
+#define SET_INTERRUPT_ENABLE    0x04
+#define CLEAR_INTERRUPT_ENABLE  0x08
+#define INTERRUPT_STATUS        0x0c
+#define TX_FIFO_DAT             0x10
+#define RX_FIFO_DAT             0x14
+#define TX_FIFO_OCC             0x18
+#define RX_FIFO_OCC             0x1c
+#define TX_SNIFFER_ENABLE       0x20
+#define RX_SNIFFER_ENABLE       0x24
+
+/* IRQ register bits */
+#define TX_DATA_IRQ_BIT         BIT(0)
+#define RX_DATA_IRQ_BIT         BIT(1)
+#define TX_FULL_IRQ_BIT         BIT(2)
+#define RX_FULL_IRQ_BIT         BIT(3)
+
+/* Enable register bits */
+#define ENABLE_BIT              BIT(0)
+
+#endif
+
diff --git a/drivers/net/pkt-sniffer/backends/ether/platform.c b/drivers/net/pkt-sniffer/backends/ether/platform.c
new file mode 100644
index 0000000..78e7e1c
--- /dev/null
+++ b/drivers/net/pkt-sniffer/backends/ether/platform.c
@@ -0,0 +1,231 @@ 
+/*
+ * Ethernet Mii packet sniffer driver
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include "../../core/snf_core.h"
+#include "hw.h"
+#include "channel.h"
+
+static const char esnf_driver_name[] = "eth-sniffer";
+
+/* Names for the TX and RX channel.
+ * User space will used these names to access the channels
+ * through the generic netlink interface
+ */
+static const char tx_channel_name[] = "snf_ether_tx";
+static const char rx_channel_name[] = "snf_ether_rx";
+
+struct ether_snf {
+	u8 __iomem *regs;
+	int irq;
+	struct clk *sys_clk;
+	struct ether_snf_chan txc;
+	struct ether_snf_chan rxc;
+};
+
+/* Interrupt thread function */
+static irqreturn_t esnf_irq_thread(int irq, void *dev_id)
+{
+	struct platform_device *pdev = (struct platform_device *)dev_id;
+	struct ether_snf *esnf = (struct ether_snf *)platform_get_drvdata(pdev);
+	u32 irq_status;
+
+	if (unlikely(esnf->irq != irq))
+		return IRQ_NONE;
+
+	irq_status = ioread32(esnf->regs + INTERRUPT_STATUS) &
+				 ioread32(esnf->regs + INTERRUPT_ENABLE);
+
+	dev_dbg(&pdev->dev, "irq: 0x%08x\n", irq_status);
+
+	/* TX FIFO full */
+	if (unlikely(irq_status & TX_FULL_IRQ_BIT))
+		dev_notice(&pdev->dev, "TX FIFO full");
+
+	/* RX FIFO full */
+	if (unlikely(irq_status & RX_FULL_IRQ_BIT))
+		dev_notice(&pdev->dev, "RX FIFO full");
+
+	/* TX match data available */
+	if (irq_status & TX_DATA_IRQ_BIT) {
+		dev_dbg(&pdev->dev, "TX data");
+		channel_data_available(&esnf->txc);
+	}
+
+	/* RX match data available */
+	if (irq_status & RX_DATA_IRQ_BIT) {
+		dev_dbg(&pdev->dev, "RX data");
+		channel_data_available(&esnf->rxc);
+	}
+
+	/* Clear interrupts */
+	iowrite32(irq_status, esnf->regs + INTERRUPT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+/* Called when the packet sniffer device is bound with the driver */
+static int esnf_driver_probe(struct platform_device *pdev)
+{
+	struct ether_snf *esnf;
+	struct resource *res;
+	int ret, irq;
+	u32 fifo_blk_words;
+	void __iomem *regs;
+	struct device_node *ofn = pdev->dev.of_node;
+
+	/* Allocate the device data structure */
+	esnf = devm_kzalloc(&pdev->dev, sizeof(*esnf), GFP_KERNEL);
+	if (!esnf)
+		return -ENOMEM;
+
+	/* Retrieve and remap register memory space */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	if (!res)
+		return -ENODEV;
+
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	esnf->regs = regs;
+
+	/* Read the FIFO block size from the DT */
+	if (!ofn)
+		return -ENODEV;
+
+	ret = of_property_read_u32(
+				ofn,
+				"fifo-block-words",
+				&fifo_blk_words);
+	if (ret < 0)
+		return ret;
+
+	if (((fifo_blk_words - 1)*4) > MAX_MATCH_BYTES) {
+		dev_err(&pdev->dev,
+			"Invalid FIFO block size entry in device tree\n");
+		return -EINVAL;
+	}
+
+	esnf->sys_clk = devm_clk_get(&pdev->dev, "sys");
+	if (IS_ERR(esnf->sys_clk)) {
+		ret = PTR_ERR(esnf->sys_clk);
+		return ret;
+	}
+	ret = clk_prepare_enable(esnf->sys_clk);
+	if (ret < 0)
+		return ret;
+
+	/* Initialise the TX and RX channels */
+	ret = channel_init(&esnf->txc, pdev, regs, fifo_blk_words, 1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to init TX channel (%d)\n", ret);
+		goto fail1;
+	}
+	ret = channel_init(&esnf->rxc, pdev, regs, fifo_blk_words, 0);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to init RX channel (%d)\n", ret);
+		goto fail1;
+	}
+
+	/* Register the channels with the sniffer core module */
+	ret = channel_register(&esnf->txc, tx_channel_name);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register TX chan (%d)\n", ret);
+		goto fail1;
+	}
+	ret = channel_register(&esnf->rxc, rx_channel_name);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register RX chan (%d)\n", ret);
+		goto fail2;
+	}
+
+	platform_set_drvdata(pdev, esnf);
+
+	/* Register the interrupt handler */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		goto fail3;
+	esnf->irq = irq;
+	ret = devm_request_threaded_irq(
+				&pdev->dev,
+				irq,
+				NULL,
+				esnf_irq_thread,
+				IRQF_ONESHOT,
+				esnf_driver_name,
+				pdev);
+	if (ret < 0)
+		goto fail3;
+
+	return 0;
+
+fail3:
+	channel_unregister(&esnf->rxc);
+fail2:
+	channel_unregister(&esnf->txc);
+fail1:
+	clk_disable_unprepare(esnf->sys_clk);
+	return ret;
+}
+
+/* Called when the packet sniffer device unregisters with the driver */
+static int esnf_driver_remove(struct platform_device *pdev)
+{
+	struct ether_snf *esnf = (struct ether_snf *)platform_get_drvdata(pdev);
+	int ret;
+
+	ret = channel_unregister(&esnf->txc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to unregister TX chan (%d)\n", ret);
+		return ret;
+	}
+	ret = channel_unregister(&esnf->rxc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to unregister RX chan (%d)\n", ret);
+		return ret;
+	}
+	clk_disable_unprepare(esnf->sys_clk);
+	return 0;
+}
+
+static const struct of_device_id esnf_of_match_table[] = {
+	{ .compatible = "linn,eth-sniffer", .data = NULL },
+	{},
+};
+MODULE_DEVICE_TABLE(of, esnf_of_match_table);
+
+static struct platform_driver esnf_platform_driver = {
+	.driver = {
+		.name = esnf_driver_name,
+		.of_match_table = esnf_of_match_table,
+	},
+	.probe = esnf_driver_probe,
+	.remove = esnf_driver_remove,
+};
+
+module_platform_driver(esnf_platform_driver);
+
+MODULE_DESCRIPTION("Linn Ethernet Packet Sniffer");
+MODULE_AUTHOR("Linn Products Ltd");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/net/pkt-sniffer/core/dev_table.c b/drivers/net/pkt-sniffer/core/dev_table.c
new file mode 100644
index 0000000..3a07838
--- /dev/null
+++ b/drivers/net/pkt-sniffer/core/dev_table.c
@@ -0,0 +1,124 @@ 
+/*
+ * Packet sniffer core driver: channel management
+ *
+ * Copyright (C) 2014 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include "snf_core.h"
+#include "nl.h"
+
+#define MAX_CHANNELS   256
+
+static DEFINE_MUTEX(tlock);
+
+static struct snf_chan *dev_tab[MAX_CHANNELS];
+static unsigned int ref_count[MAX_CHANNELS];
+
+static inline int verify_args(int id)
+{
+	return (id < MAX_CHANNELS);
+}
+
+/* Registers a sniffer channel and returns and id for it */
+int snf_channel_add(struct snf_chan *dev, const char *name)
+{
+	int i;
+	int ret = -EEXIST;
+
+	mutex_lock(&tlock);
+
+	for (i = 0; i < MAX_CHANNELS; i++) {
+		if (!dev_tab[i]) {
+			/* Initialise the netlink interface for the channel */
+			ret = snf_netlink_init(i, dev, name);
+			if (ret < 0)
+				goto fail;
+
+			dev_tab[i] = dev;
+			ref_count[i] = 0;
+			mutex_unlock(&tlock);
+			return i;
+		}
+	}
+
+fail:
+	mutex_unlock(&tlock);
+	return ret;
+}
+EXPORT_SYMBOL(snf_channel_add);
+
+/* Removes a sniffer channel */
+int snf_channel_remove(int id)
+{
+	int ret = 0;
+	struct snf_chan *dev;
+
+	if (!verify_args(id))
+		return -EINVAL;
+
+	mutex_lock(&tlock);
+
+	dev = dev_tab[id];
+
+	if (!dev) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	if (ref_count[id] > 0) {
+		ret = -EBUSY;
+		goto fail;
+	}
+
+	dev_tab[id] = NULL;
+
+	/* Release netlink API resources */
+	snf_netlink_release(dev);
+
+fail:
+	mutex_unlock(&tlock);
+	return ret;
+}
+EXPORT_SYMBOL(snf_channel_remove);
+
+/* Returns a pointer to the specified sniffer channel
+ * and increments its reference counter
+ */
+struct snf_chan *snf_channel_get(int id)
+{
+	struct snf_chan *dev;
+
+	if (!verify_args(id))
+		return NULL;
+
+	mutex_lock(&tlock);
+	dev = dev_tab[id];
+	if (dev)
+		ref_count[id]++;
+	mutex_unlock(&tlock);
+
+	return dev;
+}
+
+/* Decrements the reference counter for the channel */
+void snf_channel_put(int id)
+{
+	mutex_lock(&tlock);
+	if (ref_count[id] > 0)
+		ref_count[id]--;
+	mutex_unlock(&tlock);
+}
+
diff --git a/drivers/net/pkt-sniffer/core/module.c b/drivers/net/pkt-sniffer/core/module.c
new file mode 100644
index 0000000..af6a1aa
--- /dev/null
+++ b/drivers/net/pkt-sniffer/core/module.c
@@ -0,0 +1,37 @@ 
+/*
+ * Packet sniffer core driver:
+ *  - backend channel management
+ *  - interface to userland via generic netlink
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+
+static int __init snf_core_init(void)
+{
+	return 0;
+}
+
+static void __exit snf_core_cleanup(void)
+{
+}
+
+module_init(snf_core_init);
+module_exit(snf_core_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Core packet sniffer driver");
+MODULE_AUTHOR("Linn Products Ltd");
diff --git a/drivers/net/pkt-sniffer/core/nl.c b/drivers/net/pkt-sniffer/core/nl.c
new file mode 100644
index 0000000..6839147
--- /dev/null
+++ b/drivers/net/pkt-sniffer/core/nl.c
@@ -0,0 +1,427 @@ 
+/*
+ * Packet sniffer core driver: generic netlink interface
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#include <linux/version.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pkt_sniffer.h>
+#include "snf_core.h"
+#include "nl.h"
+
+/* Netlink API data for a sniffer channel */
+struct snf_netlink {
+	/* genl family */
+	struct genl_family           fml;
+	/* genl operations */
+	struct genl_ops             *ops;
+	/* genl mcast group, where sniffer match
+	 * events will be sent
+	 */
+	struct genl_multicast_group  grp;
+};
+
+/* Attribute policies */
+static struct nla_policy snf_policy[SNF_ATTR_MAX + 1] = {
+	[SNF_ATTR_PATTERN] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy snf_ptn_policy[SNF_ATTR_PTN_MAX + 1] = {
+	[SNF_ATTR_PTN_ENTRY] = { .type = NLA_U16 },
+};
+
+/* Generic Netlink family template definition */
+static int pre_doit_func(const struct genl_ops *ops,
+			 struct sk_buff *skb,
+			 struct genl_info *info);
+
+static void post_doit_func(const struct genl_ops *ops,
+			   struct sk_buff *skb,
+			   struct genl_info *info);
+
+static struct genl_family snf_family_tmpl = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = 0,
+	.version = SNF_GNL_VERSION,
+	.maxattr = SNF_ATTR_MAX,
+	.pre_doit = pre_doit_func,
+	.post_doit = post_doit_func
+};
+
+static int send_reply_uint32(
+			struct genl_info *info,
+			int cmd,
+			int attr,
+			u32 val);
+
+/* Generic Netlink operations template definition */
+
+static int do_it_start(struct sk_buff *skb, struct genl_info *info);
+static int do_it_stop(struct sk_buff *skb, struct genl_info *info);
+static int do_it_set_pattern(struct sk_buff *skb, struct genl_info *info);
+static int do_it_num_rec_avail(struct sk_buff *skb, struct genl_info *info);
+static int do_it_ptn_max_cmds(struct sk_buff *skb, struct genl_info *info);
+static int do_it_max_match_bytes(struct sk_buff *skb, struct genl_info *info);
+
+#define SNF_GENL_OP(_cmd, _func)	 \
+	{				 \
+		.cmd	= _cmd,          \
+		.policy = snf_policy,    \
+		.doit   = _func,         \
+		.dumpit = NULL,          \
+		.flags  = 0,             \
+		.internal_flags = 0      \
+	}
+
+#define SNF_CHAN_OPS							   \
+	{								   \
+		SNF_GENL_OP(SNF_CMD_START,         do_it_start),	   \
+		SNF_GENL_OP(SNF_CMD_STOP,          do_it_stop),		   \
+		SNF_GENL_OP(SNF_CMD_SETPATTERN,    do_it_set_pattern),     \
+		SNF_GENL_OP(SNF_CMD_NUMRECAVAIL,   do_it_num_rec_avail),   \
+		SNF_GENL_OP(SNF_CMD_PTNMAXCMDS,    do_it_ptn_max_cmds),    \
+		SNF_GENL_OP(SNF_CMD_MAXMATCHBYTES, do_it_max_match_bytes)  \
+	}
+
+static struct genl_ops snf_ops_tmpl[] = SNF_CHAN_OPS;
+
+#define NUM_GENL_OPS ARRAY_SIZE(snf_ops_tmpl)
+
+/* Multicast a netlink event containing data from a sniffer match event.
+ * Data are included in the netlink message as a nested attribute
+ * containing the timestamp (optional) and matching packet bytes
+*/
+int snf_channel_notify_match(struct snf_chan *dev, struct snf_match_evt *mt)
+{
+	int i, ret = 0;
+	struct sk_buff *msg = NULL;
+	void *msg_hdr, *nest_hdr;
+	struct snf_netlink *nl = (struct snf_netlink *)dev->cstate;
+
+	if (!nl) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+	if (!msg) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	msg_hdr = genlmsg_put(msg,
+			      0,
+			      0,
+			      &nl->fml,
+			      0,
+			      SNF_CMD_MATCHEVENT);
+	if (!msg_hdr) {
+		ret = -EMSGSIZE;
+		goto fail;
+	}
+
+	/* Add the nested attribute with the data */
+	if ((mt->ts_valid) || (mt->len > 0)) {
+		nest_hdr = nla_nest_start(msg, SNF_ATTR_MATCHEVENT);
+		if (!nest_hdr) {
+			ret = -EMSGSIZE;
+			goto fail;
+		}
+		if (mt->ts_valid) {
+			ret = nla_put_u64(msg, SNF_ATTR_MATCH_TS, mt->ts);
+			if (ret < 0)
+				goto fail;
+		}
+		for (i = 0; i < mt->len; i++) {
+			ret = nla_put_u8(msg,
+					 SNF_ATTR_MATCH_PKTBYTE,
+					 mt->data[i]);
+			if (ret < 0)
+				goto fail;
+		}
+		nla_nest_end(msg, nest_hdr);
+	}
+
+	ret = genlmsg_end(msg, msg_hdr);
+	if (ret < 0)
+		goto fail;
+	ret = genlmsg_multicast(&nl->fml, msg, 0, 0, GFP_ATOMIC);
+
+	/* The ESRCH code is returned when there is no socket listening on the
+	 * multicast group, so we do not really treat is as an error
+	 */
+	if (ret == -ESRCH)
+		ret = 0;
+	return ret;
+
+fail:
+	if (msg)
+		nlmsg_free(msg);
+	return ret;
+}
+EXPORT_SYMBOL(snf_channel_notify_match);
+
+/* Initialise the generic netlink API for a sniffer channel
+ * Registers family, ops and multicast group for events
+ */
+int snf_netlink_init(int id, struct snf_chan *dev, const char *name)
+{
+	int i, ret;
+	struct snf_netlink *nl = NULL;
+
+	nl = kmalloc(sizeof(*nl), GFP_KERNEL);
+	if (!nl) {
+		ret = -ENOMEM;
+		goto fail1;
+	}
+
+	nl->fml = snf_family_tmpl;
+
+	/* Set the family name */
+	strncpy(nl->fml.name, name, GENL_NAMSIZ-1);
+
+	/* Allocate ops array and copy template data */
+	nl->ops = kmalloc(sizeof(snf_ops_tmpl), GFP_KERNEL);
+	if (!nl->ops) {
+		ret = -ENOMEM;
+		goto fail2;
+	}
+	memcpy(nl->ops, snf_ops_tmpl, sizeof(snf_ops_tmpl));
+
+	/* In the internal_flags field we store the id
+	 * of the device the ops belong to
+	 */
+	for (i = 0; i < NUM_GENL_OPS; i++)
+		nl->ops[i].internal_flags = id;
+
+	snprintf(nl->grp.name, GENL_NAMSIZ-1, SNF_EVENT_GRP);
+
+	ret = _genl_register_family_with_ops_grps(
+						&nl->fml,
+						nl->ops,
+						NUM_GENL_OPS,
+						&nl->grp,
+						1);
+	if (ret < 0) {
+		pr_err("%s: failed to register family %s (%d)\n",
+		       KBUILD_MODNAME, name, ret);
+		goto fail3;
+	}
+
+	pr_info("%s: registered genl family for channel %d: %s\n",
+		KBUILD_MODNAME, id, name);
+	dev->cstate = nl;
+
+	return 0;
+
+fail3:
+	kfree(nl->ops);
+fail2:
+	kfree(nl);
+fail1:
+	return ret;
+}
+
+/* Shuts down the netlink API for a sniffer channel
+ * and frees resources
+ */
+void snf_netlink_release(struct snf_chan *dev)
+{
+	struct snf_netlink *nl = (struct snf_netlink *)dev->cstate;
+	/* Unregister family and free ops
+	 * NOTE: this will also unregister the ops and the mcast group
+	 */
+	genl_unregister_family(&nl->fml);
+	pr_info("%s: unregistered genl family: %s\n",
+		KBUILD_MODNAME, nl->fml.name);
+
+	kfree(nl->ops);
+	kfree(nl);
+	dev->cstate = NULL;
+}
+
+/* pre_doit function that retrieves a pointer to the sniffer channel device
+ * from the instance and channel number stored in the ops internal_flag field
+ */
+static int pre_doit_func(const struct genl_ops *ops,
+			 struct sk_buff *skb,
+			 struct genl_info *info)
+{
+	info->user_ptr[0] = snf_channel_get(ops->internal_flags);
+	return info->user_ptr[0] ? 0 : -ENODEV;
+}
+
+/* post_doit function that releases a sniffer channel device */
+static void post_doit_func(const struct genl_ops *ops,
+			   struct sk_buff *skb,
+			   struct genl_info *info)
+{
+	snf_channel_put(ops->internal_flags);
+}
+
+/* doit command handlers */
+
+/* Start the sniffer channel */
+static int do_it_start(struct sk_buff *skb, struct genl_info *info)
+{
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+
+	return dev->start(dev);
+}
+
+/* Stop the sniffer channel */
+static int do_it_stop(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret;
+	struct snf_match_evt mt;
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+
+	ret = dev->stop(dev);
+	if (ret < 0)
+		return ret;
+
+	/* Multicast an empty match event to notify any user-space
+	 * listeners that the sniffer is stopping
+	 */
+	memset(&mt, 0, sizeof(mt));
+	return snf_channel_notify_match(dev, &mt);
+}
+
+/* Set the command pattern. The string is received in a nested
+ * attribute containing a sequence of 16-bit valued.
+ * Each value consists of a command (8 bits) and data (8 bits)
+*/
+static int do_it_set_pattern(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	int rem, count;
+	struct nlattr *nla;
+	u16 entry;
+	u8 *buf = NULL;
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+	int max_entries = dev->max_ptn_entries(dev);
+
+	if (!info->attrs[SNF_ATTR_PATTERN]) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	ret = nla_validate_nested(info->attrs[SNF_ATTR_PATTERN],
+				  SNF_ATTR_PTN_ENTRY, snf_ptn_policy);
+	if (ret < 0)
+		goto fail;
+
+	buf = kmalloc(max_entries*2, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* Iterate over the contents of the nested attribute
+	 * and extract into the buffer
+	 */
+	count = 0;
+	nla_for_each_nested(nla, info->attrs[SNF_ATTR_PATTERN], rem) {
+		if (count >= max_entries) {
+			ret = -EINVAL;
+			goto fail;
+		}
+		entry = nla_get_u16(nla);
+		buf[2*count] = PTNENTRY_CMD(entry);
+		buf[2*count + 1] = PTNENTRY_DATA(entry);
+		count++;
+	}
+
+	ret = dev->set_pattern(dev, buf, count);
+
+fail:
+	kfree(buf);
+	return ret;
+}
+
+/* Number of pending match events */
+static int do_it_num_rec_avail(struct sk_buff *skb, struct genl_info *info)
+{
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+
+	return send_reply_uint32(
+			info,
+			SNF_CMD_NUMRECAVAIL,
+			SNF_ATTR_NUMRECAVAIL,
+			dev->num_recs_avail(dev));
+}
+
+/* Max number of commands that are supported in the command pattern */
+static int do_it_ptn_max_cmds(struct sk_buff *skb, struct genl_info *info)
+{
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+
+	return send_reply_uint32(
+			info,
+			SNF_CMD_PTNMAXCMDS,
+			SNF_ATTR_PTNMAXCMDS,
+			dev->max_ptn_entries(dev));
+}
+
+/* Max bytes that can be returned by a packet match event */
+static int do_it_max_match_bytes(struct sk_buff *skb, struct genl_info *info)
+{
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+
+	return send_reply_uint32(
+			info,
+			SNF_CMD_MAXMATCHBYTES,
+			SNF_ATTR_MAXMATCHBYTES,
+			dev->max_match_bytes(dev));
+}
+
+/* Helper function for sending a reply containing a single u32 attribute */
+static int send_reply_uint32(struct genl_info *info, int cmd, int attr, u32 val)
+{
+	void *hdr;
+	int ret = 0;
+	struct sk_buff *msg;
+	struct snf_chan *dev = (struct snf_chan *)info->user_ptr[0];
+	struct snf_netlink *nl = (struct snf_netlink *)dev->cstate;
+
+	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	hdr = genlmsg_put_reply(msg, info, &nl->fml, 0, cmd);
+	if (!hdr) {
+		ret = -EMSGSIZE;
+		goto fail;
+	}
+	ret = nla_put_u32(msg, attr, val);
+	if (ret < 0)
+		goto fail;
+	ret = genlmsg_end(msg, hdr);
+	if (ret < 0)
+		goto fail;
+
+	return genlmsg_reply(msg, info);
+
+fail:
+	if (msg)
+		nlmsg_free(msg);
+	return ret;
+}
+
diff --git a/drivers/net/pkt-sniffer/core/nl.h b/drivers/net/pkt-sniffer/core/nl.h
new file mode 100644
index 0000000..f7bf579
--- /dev/null
+++ b/drivers/net/pkt-sniffer/core/nl.h
@@ -0,0 +1,34 @@ 
+/*
+ * Packet sniffer core driver: generic netlink interface
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#ifndef __SNF_NL_H
+#define __SNF_NL_H
+
+#include <net/genetlink.h>
+#include "snf_core.h"
+
+/* Initialise netlink interface for a sniffer channel,
+ * register netlink families etc.
+ */
+int snf_netlink_init(int id, struct snf_chan *dev, const char *name);
+
+/* Shutdown netlink interface, unregister
+ * families etc.
+ */
+void snf_netlink_release(struct snf_chan *dev);
+
+#endif
diff --git a/drivers/net/pkt-sniffer/core/snf_core.h b/drivers/net/pkt-sniffer/core/snf_core.h
new file mode 100644
index 0000000..062de70
--- /dev/null
+++ b/drivers/net/pkt-sniffer/core/snf_core.h
@@ -0,0 +1,64 @@ 
+/*
+ * Packet sniffer core driver
+ * - this header provides the interface to specific backend packet
+ *   sniffer implementations
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#ifndef __SNF_CORE_H
+#define __SNF_CORE_H
+
+#include <linux/types.h>
+
+/* This is a global maximum. Each backend will have its own limit */
+#define MAX_MATCH_BYTES 512
+
+/* Sniffer channel data structure */
+struct snf_chan {
+	int  (*start)(struct snf_chan *dev);
+	int  (*stop)(struct snf_chan *dev);
+	int  (*set_pattern)(struct snf_chan *dev, const u8 *pattern, int count);
+	int  (*num_recs_avail)(struct snf_chan *dev);
+	int  (*max_ptn_entries)(struct snf_chan *dev);
+	int  (*max_match_bytes)(struct snf_chan *dev);
+
+	void *cstate;
+};
+
+/* Data from a sniffer match event */
+struct snf_match_evt {
+	int ts_valid;     /* flag indicating if timestamp is present */
+	u64 ts;           /* timestamp value */
+	u8 data[MAX_MATCH_BYTES]; /* packet data bytes matched by sniffer */
+	int len;          /* number of valid bytes in the 'data' buffer */
+};
+
+/* Adds a sniffer channel to the registry */
+int snf_channel_add(struct snf_chan *dev, const char *name);
+
+/* Removes the channel with the specified id from the registry */
+int snf_channel_remove(int id);
+
+/* Returns handle to a channel and increments reference count */
+struct snf_chan *snf_channel_get(int id);
+
+/* Decrements reference count for the specified channel */
+void snf_channel_put(int id);
+
+/* Multicast a notification to user space for a sniffer match event */
+int snf_channel_notify_match(struct snf_chan *dev, struct snf_match_evt *mt);
+
+#endif
+
diff --git a/include/linux/pkt_sniffer.h b/include/linux/pkt_sniffer.h
new file mode 100644
index 0000000..3e73d14
--- /dev/null
+++ b/include/linux/pkt_sniffer.h
@@ -0,0 +1,89 @@ 
+/*
+ * Linn packet sniffer driver interface
+ *
+ * Copyright (C) 2015 Linn Products Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Written by:
+ * Stathis Voukelatos <stathis.voukelatos@linn.co.uk>
+ */
+#ifndef __PKT_SNIFFER_H
+#define __PKT_SNIFFER_H
+
+/* Commands for the pattern string */
+#define PTN_CMD_DONTCARE    0
+#define PTN_CMD_MATCH       1
+#define PTN_CMD_COPY        2
+#define PTN_CMD_MATCHSTAMP  3
+#define PTN_CMD_COPYDONE    4
+
+/* Creates an entry for the pattern string.
+ * An entry consists of a command byte and a data byte
+*/
+#define MAKE_PTN_ENTRY(cmd, data) (((((int)cmd) & 0xff) << 8) | (data & 0xff))
+/* Gets the cmd and data portion of a pattern string entry */
+#define PTNENTRY_CMD(val)        (((val) >> 8) & 0xff)
+#define PTNENTRY_DATA(val)       ((val) & 0xff)
+
+/* Generic Netlink commands */
+enum {
+	SNF_CMD_UNSPEC,
+	SNF_CMD_START,          /* start the sniffer */
+	SNF_CMD_STOP,           /* stop the sniffer */
+	SNF_CMD_SETPATTERN,     /* set the command pattern */
+	SNF_CMD_NUMRECAVAIL,    /* number of pending match events */
+	SNF_CMD_MATCHEVENT,     /* match event notification */
+	SNF_CMD_PTNMAXCMDS,     /* max number of commands supported */
+	SNF_CMD_MAXMATCHBYTES,  /* max number of bytes in match event */
+	__SNF_CMD_MAX
+};
+#define SNF_CMD_MAX (__SNF_CMD_MAX - 1)
+
+/* Generic Netlink attributes */
+enum {
+	SNF_ATTR_UNSPEC,
+	SNF_ATTR_PTNMAXCMDS,    /* max number of commands supported */
+	SNF_ATTR_PATTERN,       /* nested attribute containing commands */
+	SNF_ATTR_NUMRECAVAIL,   /* number of pending match events */
+	SNF_ATTR_MATCHEVENT,    /* nested attribute for a match event */
+	SNF_ATTR_MAXMATCHBYTES, /* max bytes in a match event */
+	__SNF_ATTR_MAX
+};
+#define SNF_ATTR_MAX (__SNF_ATTR_MAX - 1)
+
+/* Attributes that are included in the nested attribute
+ * for the command string
+ */
+enum {
+	SNF_ATTR_PTN_UNSPEC,
+	SNF_ATTR_PTN_ENTRY,    /* command entry containing a command id */
+	__SNF_ATTR_PTN_MAX     /* and data byte */
+};
+#define SNF_ATTR_PTN_MAX (__SNF_ATTR_PTN_MAX - 1)
+
+/* Attributes included in the nested attribute
+ * of a match event notification
+ */
+enum {
+	SNF_ATTR_MATCH_UNSPEC,
+	SNF_ATTR_MATCH_TS,      /* timestamp (returned with a match event) */
+	SNF_ATTR_MATCH_PKTBYTE, /* packet data (returned with a match event) */
+	__SNF_ATTR_MATCH_MAX
+};
+#define SNF_ATTR_MATCH_MAX (__SNF_ATTR_MATCH_MAX - 1)
+
+/* Family version */
+#define SNF_GNL_VERSION 1
+
+/* Multicast group name */
+#define SNF_EVENT_GRP    "snf_evt_grp"
+
+#endif