linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Mathias Nyman <mathias.nyman@linux.intel.com>
To: Lu Baolu <baolu.lu@linux.intel.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	bhelgaas@google.com, Dan Williams <dan.j.williams@intel.com>
Subject: Re: [PATCH v7 02/10] usb: dbc: probe and setup xhci debug capability
Date: Tue, 16 Feb 2016 16:19:10 +0200	[thread overview]
Message-ID: <56C32FDE.7060609@linux.intel.com> (raw)
In-Reply-To: <1453813096-6991-3-git-send-email-baolu.lu@linux.intel.com>

Hi

Most of my concerns are due to the early printk restrictions.
As you say in the documentation:
"Keep in mind that things such as interrupt, system memory, DMA memory,
PCI configure space access, etc., are all different from a normal device
driver."

I don't know the details of these subsystems, or if what is done here is
done the "right" way.

DMA capable memory is allocated with static char xpage[PAGE_SIZE] __aligned,
and DMA address is found using  __pa()

xdbc_map_pci_mmio() does some pci magic and fixmapping that I don't know the
details of.

writing to ioport 0x80 outb(1, 0x80) is used as 1us delay, sometimes up to
5 million times to get a 5 second timeout

Adding Bjorn Helgaas and Dan Williams in case they have an opinoin about
the DMA and PCI cases.

more comments inline

On 26.01.2016 14:58, Lu Baolu wrote:
> xHCI debug capability (DbC) is an optional functionality provided
> by an xHCI host controller. Software learns this capability by
> walking through the extended capability list in mmio of the host.
>
> This patch introduces the code to probe and initialize the debug
> capability hardware during early boot. With hardware initialization
> done, the debug target (system under debug which has DbC enabled)
> will present a debug device through the debug port. The debug device
> is fully compliant with the USB framework and provides the equivalent
> of a very high performance (USB3) full-duplex serial link between the
> debug host and target.
>
> Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
> ---
>   MAINTAINERS                  |   7 +
>   arch/x86/Kconfig.debug       |  12 +
>   drivers/usb/early/Makefile   |   1 +
>   drivers/usb/early/xhci-dbc.c | 774 +++++++++++++++++++++++++++++++++++++++++++
>   include/linux/usb/xhci-dbc.h | 187 +++++++++++
>   5 files changed, 981 insertions(+)
>   create mode 100644 drivers/usb/early/xhci-dbc.c
>   create mode 100644 include/linux/usb/xhci-dbc.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 30aca4a..e6d7076 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11505,6 +11505,13 @@ S:	Supported
>   F:	drivers/usb/host/xhci*
>   F:	drivers/usb/host/pci-quirks*
>
> +USB XHCI DEBUG PORT
> +M:	Lu Baolu <baolu.lu@linux.intel.com>
> +L:	linux-usb@vger.kernel.org
> +S:	Supported
> +F:	drivers/usb/early/xhci-dbc.c
> +F:	include/linux/usb/xhci-dbc.h
> +
>   USB ZD1201 DRIVER
>   L:	linux-wireless@vger.kernel.org
>   W:	http://linux-lc100020.sourceforge.net
> diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
> index 9b18ed9..ba60cb1 100644
> --- a/arch/x86/Kconfig.debug
> +++ b/arch/x86/Kconfig.debug
> @@ -48,6 +48,18 @@ config EARLY_PRINTK_EFI
>   	  This is useful for kernel debugging when your machine crashes very
>   	  early before the console code is initialized.
>
> +config EARLY_PRINTK_XDBC
> +	bool "Early printk via xHCI debug port"
> +	depends on EARLY_PRINTK && PCI
> +	---help---
> +	  Write kernel log output directly into the xHCI debug port.
> +
> +	  This is useful for kernel debugging when your machine crashes very
> +	  early before the console code is initialized. For normal operation
> +	  it is not recommended because it looks ugly and doesn't cooperate
> +	  with klogd/syslogd or the X server. You should normally N here,
> +	  unless you want to debug such a crash.
> +
>   config X86_PTDUMP_CORE
>   	def_bool n
>
> diff --git a/drivers/usb/early/Makefile b/drivers/usb/early/Makefile
> index 24bbe51..2db5906 100644
> --- a/drivers/usb/early/Makefile
> +++ b/drivers/usb/early/Makefile
> @@ -3,3 +3,4 @@
>   #
>
>   obj-$(CONFIG_EARLY_PRINTK_DBGP) += ehci-dbgp.o
> +obj-$(CONFIG_EARLY_PRINTK_XDBC) += xhci-dbc.o
> diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c
> new file mode 100644
> index 0000000..254a0a8
> --- /dev/null
> +++ b/drivers/usb/early/xhci-dbc.c
> @@ -0,0 +1,774 @@
> +/**
> + * xhci-dbc.c - xHCI debug capability driver
> + *
> + * Copyright (C) 2015 Intel Corporation
> + *
> + * Author: Lu Baolu <baolu.lu@linux.intel.com>
> + * Some code shared with EHCI debug port and xHCI driver.
> + *
> + * 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.
> + */
> +#include <linux/pci_regs.h>
> +#include <linux/pci_ids.h>
> +#include <linux/bootmem.h>
> +#include <linux/io.h>
> +#include <asm/pci-direct.h>
> +#include <asm/fixmap.h>
> +#include <linux/bcd.h>
> +#include <linux/export.h>
> +#include <linux/version.h>
> +#include <linux/usb/xhci-dbc.h>
> +
> +#include "../host/xhci.h"
> +
> +#define	XDBC_PROTOCOL		1	/* GNU Remote Debug Command Set */
> +#define	XDBC_VENDOR_ID		0x1d6b	/* Linux Foundation 0x1d6b */
> +#define	XDBC_PRODUCT_ID		0x0004	/* __le16 idProduct; device 0004 */
> +#define	XDBC_DEVICE_REV		0x0010	/* 0.10 */
> +
> +static struct xdbc_state xdbc_stat;
> +static struct xdbc_state *xdbcp = &xdbc_stat;
> +
>

...

> +
> +#else
> +static inline void xdbc_trace(const char *fmt, ...) { }
> +static inline void xdbc_dbg_dump_regs(char *str) { }
> +static inline void xdbc_dbg_dump_data(char *str) { }
> +#endif	/* DBC_DEBUG */
> +
> +/*
> + * FIXME: kernel provided delay interfaces, like usleep, isn't ready yet
> + *        at the time DbC gets initialized. Below implementation is only
> + *        for x86 platform. Need to reconsider this when porting it onto
> + *        other architectures.
> + */
> +static inline void xdbc_udelay(int us)
> +{
> +	while (us-- > 0)
> +		outb(0x1, 0x80);
> +}

This is one of the concerns, the FIXME text describes the problem but still wondering if
there is any other solution. In many cases later in this file we will wait up to 5 seconds
for something, ending up writing 5 million times to ioport 0x80.

> +
> +static void __iomem *xdbc_map_pci_mmio(u32 bus,
> +		u32 dev, u32 func, u8 bar, size_t *length)
> +{
> +	u32 val, sz;
> +	u64 val64, sz64, mask64;
> +	u8 byte;
> +	unsigned long idx, max_idx;
> +	void __iomem *base;
> +
> +	val = read_pci_config(bus, dev, func, bar);
> +	write_pci_config(bus, dev, func, bar, ~0);
> +	sz = read_pci_config(bus, dev, func, bar);
> +	write_pci_config(bus, dev, func, bar, val);
> +	if (val == 0xffffffff || sz == 0xffffffff) {
> +		xdbc_trace("invalid mmio bar\n");
> +		return NULL;
> +	}
> +
> +	val64 = val & PCI_BASE_ADDRESS_MEM_MASK;
> +	sz64 = sz & PCI_BASE_ADDRESS_MEM_MASK;
> +	mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;
> +
> +	if ((val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
> +			PCI_BASE_ADDRESS_MEM_TYPE_64) {
> +		val = read_pci_config(bus, dev, func, bar + 4);
> +		write_pci_config(bus, dev, func, bar + 4, ~0);
> +		sz = read_pci_config(bus, dev, func, bar + 4);
> +		write_pci_config(bus, dev, func, bar + 4, val);
> +
> +		val64 |= ((u64)val << 32);
> +		sz64 |= ((u64)sz << 32);
> +		mask64 |= ((u64)~0 << 32);
> +	}
> +
> +	sz64 &= mask64;
> +
> +	if (sizeof(dma_addr_t) < 8 || !sz64) {
> +		xdbc_trace("can't handle 64bit BAR\n");
> +		return NULL;
> +	}
> +
> +	sz64 = 1ULL << __ffs64(sz64);
> +
> +	if (sz64 > (FIX_XDBC_END - FIX_XDBC_BASE + 1) * PAGE_SIZE) {
> +		xdbc_trace("mmio size beyond 64k not supported\n");
> +		return NULL;
> +	}
> +
> +	xdbc_trace("bar: base 0x%llx size 0x%llx offset %03x\n",
> +			val64, sz64, bar);
> +
> +	/* check if the mem space is enabled */
> +	byte = read_pci_config_byte(bus, dev, func, PCI_COMMAND);
> +	if (!(byte & PCI_COMMAND_MEMORY)) {
> +		byte  |= PCI_COMMAND_MEMORY;
> +		write_pci_config_byte(bus, dev, func, PCI_COMMAND, byte);
> +		xdbc_trace("mmio for xhci enabled\n");
> +	}
> +
> +	/* 64k mmio will be fix-mapped */
> +	max_idx = FIX_XDBC_END - FIX_XDBC_BASE;
> +	for (idx = 0; idx <= max_idx; idx++)
> +		set_fixmap_nocache(FIX_XDBC_BASE + idx,
> +			(val64 & PAGE_MASK) + (max_idx - idx) * PAGE_SIZE);
> +	base = (void __iomem *)__fix_to_virt(FIX_XDBC_END);
> +	base += val64 & ~PAGE_MASK;
> +
> +	/* save in the state block */
> +	xdbcp->bus = bus;
> +	xdbcp->dev = dev;
> +	xdbcp->func = func;
> +	xdbcp->bar = bar;
> +	xdbcp->xhci_base = base;
> +	xdbcp->xhci_length = sz64;
> +
> +	if (length)
> +		*length = sz64;
> +
> +	return base;
> +}
> +
> +/*
> + * FIXME: The bootmem allocator isn't ready at the time when DbC gets
> + *        initialized. Below implementation reserves DMA memory blocks
> + *        in the kernel static data segment.
> + */
> +static void *xdbc_get_page(dma_addr_t *dma_addr,
> +		enum xdbc_page_type type)
> +{
> +	void *virt;
> +	static char event_page[PAGE_SIZE] __aligned(PAGE_SIZE);
> +	static char in_ring_page[PAGE_SIZE] __aligned(PAGE_SIZE);
> +	static char out_ring_page[PAGE_SIZE] __aligned(PAGE_SIZE);
> +	static char table_page[PAGE_SIZE] __aligned(PAGE_SIZE);
> +
> +	switch (type) {
> +	case XDBC_PAGE_EVENT:
> +		virt = (void *)event_page;
> +		break;
> +	case XDBC_PAGE_TXIN:
> +		virt = (void *)in_ring_page;
> +		break;
> +	case XDBC_PAGE_TXOUT:
> +		virt = (void *)out_ring_page;
> +		break;
> +	case XDBC_PAGE_TABLE:
> +		virt = (void *)table_page;
> +		break;
> +	default:
> +		return NULL;
> +	}
> +
> +	memset(virt, 0, PAGE_SIZE);
> +
> +	if (dma_addr)
> +		*dma_addr = (dma_addr_t)__pa(virt);
> +
> +	return virt;

another concern, I can't say if this is ok:

void *virt
static char event_page[PAGE_SIZE] __aligened(PAGE_SIZE)
virt = (void *)event_page
*dma_addr = (dma_addr_t)__pa(virt)

> +}
> +
> +typedef void (*xdbc_walk_excap_cb)(int cap_offset, void *data);
> +
> +/*
> + * xdbc_walk_excap:
> + *
> + * xHCI extended capability list walker.
> + *
> + * @bus - xHC PCI bus#
> + * @dev - xHC PCI dev#
> + * @func - xHC PCI function#
> + * @cap - capability ID
> + * @oneshot - return immediately once hit match
> + * @cb - call back
> + * @data - callback private data
> + *
> + * Return the last cap offset, otherwize 0.
> + */
> +static u32 xdbc_walk_excap(u32 bus, u32 dev, u32 func, int cap,
> +		bool oneshot, xdbc_walk_excap_cb cb, void *data)
> +{
> +	void __iomem *base;
> +	int offset = 0;
> +	size_t len = 0;
> +
> +	if (xdbcp->xhci_base && xdbcp->xhci_length) {
> +		if (xdbcp->bus != bus ||
> +				xdbcp->dev != dev ||
> +				xdbcp->func != func) {
> +			xdbc_trace("only one DbC can be used\n");
> +			return 0;
> +		}
> +
> +		len = xdbcp->xhci_length;
> +		base = xdbcp->xhci_base;
> +	} else {
> +		base = xdbc_map_pci_mmio(bus, dev, func,
> +				PCI_BASE_ADDRESS_0, &len);
> +		if (!base)
> +			return 0;
> +	}
> +
> +	do {
> +		offset = xhci_find_next_ext_cap(base, offset, cap);
> +		if (!offset)
> +			break;
> +
> +		if (cb)
> +			cb(offset, data);
> +		if (oneshot)
> +			break;
> +	} while (1);
> +
> +	return offset;
> +}
> +
> +static u32 __init xdbc_find_dbgp(int xdbc_num,
> +		u32 *rbus, u32 *rdev, u32 *rfunc)
> +{
> +	u32 bus, dev, func, class;
> +	unsigned cap;
> +
> +	for (bus = 0; bus < XDBC_PCI_MAX_BUSES; bus++) {
> +		for (dev = 0; dev < XDBC_PCI_MAX_DEVICES; dev++) {
> +			for (func = 0; func < XDBC_PCI_MAX_FUNCTION; func++) {
> +				class = read_pci_config(bus, dev, func,
> +						PCI_CLASS_REVISION);
> +				if ((class >> 8) != PCI_CLASS_SERIAL_USB_XHCI)
> +					continue;
> +

Can "for_each_pci_dev()" or similar be used here?  Is it too early for that as well?

> +				if (xdbc_num-- != 0)
> +					continue;
> +
> +				cap = xdbc_walk_excap(bus, dev, func,
> +						XHCI_EXT_CAPS_DEBUG,
> +						true, NULL, NULL);
> +				*rbus = bus;
> +				*rdev = dev;
> +				*rfunc = func;
> +				return cap;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int handshake(void __iomem *ptr, u32 mask, u32 done,
> +		int wait_usec, int delay_usec)
> +{
> +	u32	result;
> +
> +	do {
> +		result = readl(ptr);
> +		result &= mask;
> +		if (result == done)
> +			return 0;
> +		xdbc_udelay(delay_usec);
> +		wait_usec -= delay_usec;
> +	} while (wait_usec > 0);
> +
> +	return -ETIMEDOUT;
> +}
> +

...

> +/*
> + * xdbc_start: start DbC
> + *
> + * Set DbC enable bit and wait until DbC run bit being set or timed out.
> + */
> +static int xdbc_start(void)
> +{
> +	u32 ctrl, status;
> +
> +	ctrl = readl(&xdbcp->xdbc_reg->control);
> +	writel(ctrl | CTRL_DCE | CTRL_LSE, &xdbcp->xdbc_reg->control);
> +
> +	if (handshake(&xdbcp->xdbc_reg->control, CTRL_DCE,
> +			CTRL_DCE, 100000, 100) < 0) {
> +		xdbc_trace("falied to initialize hardware\n");
> +		return -ENODEV;
> +	}
> +
> +	/* wait for port connection */
> +	if (handshake(&xdbcp->xdbc_reg->portsc, PORTSC_CCS,
> +			PORTSC_CCS, 5000000, 100) < 0) {
> +		xdbc_trace("waiting for connection timed out\n");
> +		return -ETIMEDOUT;
> +	}
> +	xdbc_trace("port connection detected\n");
> +
> +	/* wait for debug device to be configured */
> +	if (handshake(&xdbcp->xdbc_reg->control, CTRL_DCR,
> +			CTRL_DCR, 5000000, 100) < 0) {
> +		xdbc_trace("waiting for device configuration timed out\n");
> +		return -ETIMEDOUT;
> +	}
> +

If I follow this correcly the initialization goes like:

early_xdbc_init()
   xdbc_setup()
     write 0 to ctrl reg   // clears (disables/stops) the debug capability.
     xdbc_start()
       writel DCE to ctrl reg // this will steal the first port from xhci host driver to debug use.
       wait max 5 sec for connection (PORTSC_CCS)
       wait max 5 second for connected host machine to configure this "device" (CTRL_DCR)

Is this debug capability limited to work only when the debug device is rebooted and a separate
debug host is connected?
If a host connection is not discoved in first 5 seconds, and configured in another 5 seconds, it
gives up alltogether.

Does this work together with xhci_hcd driver? I think xhci_hcd does a HCRST when driver loads, leading to
host driver taking back the first port (DCE = 0). Looks like loading xhci_hcd will kill the xdbc early printk.

xdbc should probably give back the port if connect / configure fails (set back DCE=0)

-Mathias

Leaving some of the initialization code below for reference


> +	/* port should have a valid port# */
> +	status = readl(&xdbcp->xdbc_reg->status);
> +	if (!DCST_DPN(status)) {
> +		xdbc_trace("invalid root hub port number\n");
> +		return -ENODEV;
> +	}
> +
> +	xdbc_trace("root hub port number %d\n", DCST_DPN(status));
> +
> +	xdbc_trace("DbC is running now, control 0x%08x\n",
> +			readl(&xdbcp->xdbc_reg->control));
> +
> +	return 0;
> +}
> +
> +static int xdbc_setup(void)
> +{
> +	int ret;
> +
> +	writel(0, &xdbcp->xdbc_reg->control);
> +	if (handshake(&xdbcp->xdbc_reg->control, CTRL_DCE,
> +			0, 100000, 100) < 0) {
> +		xdbc_trace("falied to initialize hardware\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	/* allocate and initialize all memory data structures */
> +	ret = xdbc_mem_init();
> +	if (ret < 0) {
> +		xdbc_trace("failed to initialize memory\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Memory barrier to ensure hardware sees the bits
> +	 * setting above.
> +	 */
> +	mmiowb();
> +
> +	/* dump registers and data structures */
> +	xdbc_dbg_dump_regs("hardware setup completed");
> +	xdbc_dbg_dump_data("hardware setup completed");
> +
> +	ret = xdbc_start();
> +	if (ret < 0) {
> +		xdbc_trace("failed to start DbC, cable connected?\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +int __init early_xdbc_init(char *s)
> +{
> +	u32 bus = 0, dev = 0, func = 0;
> +	unsigned long dbgp_num = 0;
> +	u32 offset;
> +	int ret;
> +
> +	if (!early_pci_allowed())
> +		return -EPERM;
> +
> +	/* FIXME: early printk "keep" option will be supported later */
> +	if (strstr(s, "keep"))
> +		return -EPERM;
> +
> +	if (xdbcp->xdbc_reg)
> +		return 0;
> +
> +	if (*s && kstrtoul(s, 0, &dbgp_num))
> +		dbgp_num = 0;
> +
> +	xdbc_trace("dbgp_num: %lu\n", dbgp_num);
> +
> +	offset = xdbc_find_dbgp(dbgp_num, &bus, &dev, &func);
> +	if (!offset)
> +		return -ENODEV;
> +
> +	xdbc_trace("Found xHCI debug capability on %02x:%02x.%1x\n",
> +			bus, dev, func);
> +
> +	if (!xdbcp->xhci_base)
> +		return -EINVAL;
> +
> +	xdbcp->xdbc_reg = (struct xdbc_regs __iomem *)
> +			(xdbcp->xhci_base + offset);
> +	xdbc_dbg_dump_regs("debug capability located");
> +
> +	/* hand over the owner of host from BIOS */
> +	xdbc_bios_handoff();
> +
> +	ret = xdbc_setup();
> +	if (ret < 0) {
> +		pr_notice("failed to setup xHCI DbC connection\n");
> +		xdbcp->xhci_base = NULL;
> +		xdbcp->xdbc_reg = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> diff --git a/include/linux/usb/xhci-dbc.h b/include/linux/usb/xhci-dbc.h
> new file mode 100644
> index 0000000..153fb87
> --- /dev/null
> +++ b/include/linux/usb/xhci-dbc.h
> @@ -0,0 +1,187 @@
> +/*
> + * xHCI debug capability driver
> + *

  reply	other threads:[~2016-02-16 14:13 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-26 12:58 [PATCH v7 00/10] usb: early: add support for early printk through USB3 debug port Lu Baolu
2016-01-26 12:58 ` [PATCH v7 01/10] x86: fixmap: add permanent fixmap for xhci " Lu Baolu
2016-01-26 12:58 ` [PATCH v7 02/10] usb: dbc: probe and setup xhci debug capability Lu Baolu
2016-02-16 14:19   ` Mathias Nyman [this message]
2016-02-17  8:45     ` Lu Baolu
2016-01-26 12:58 ` [PATCH v7 03/10] usb: dbc: add support for Intel xHCI dbc quirk Lu Baolu
2016-01-26 12:58 ` [PATCH v7 04/10] usb: dbc: add debug buffer Lu Baolu
2016-02-18 11:43   ` Mathias Nyman
2016-02-19  6:35     ` Lu Baolu
2016-01-26 12:58 ` [PATCH v7 05/10] usb: dbc: add bulk out and bulk in interfaces Lu Baolu
2016-02-18 13:32   ` Mathias Nyman
2016-02-19  7:09     ` Lu Baolu
2016-01-26 12:58 ` [PATCH v7 06/10] usb: dbc: handle dbc-configured exit Lu Baolu
2016-01-26 12:58 ` [PATCH v7 07/10] usb: dbc: handle endpoint stall Lu Baolu
2016-03-02 12:58   ` Mathias Nyman
2016-03-02 12:58     ` Felipe Balbi
2016-03-03  6:12       ` Lu Baolu
2016-03-03  6:00     ` Lu Baolu
2016-01-26 12:58 ` [PATCH v7 08/10] x86: early_printk: add USB3 debug port earlyprintk support Lu Baolu
2016-01-26 12:58 ` [PATCH v7 09/10] usb: dbc: add handshake between debug target and host Lu Baolu
2016-01-26 12:58 ` [PATCH v7 10/10] usb: doc: add document for xHCI DbC driver Lu Baolu
2016-02-02 14:34 ` [PATCH v7 00/10] usb: early: add support for early printk through USB3 debug port Lu Baolu
2016-02-03 21:43   ` Greg Kroah-Hartman
2016-02-03 23:52     ` Lu Baolu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56C32FDE.7060609@linux.intel.com \
    --to=mathias.nyman@linux.intel.com \
    --cc=baolu.lu@linux.intel.com \
    --cc=bhelgaas@google.com \
    --cc=dan.j.williams@intel.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).