lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 21 Feb 2017 12:24:21 -0800
From:   Scott Branden <scott.branden@...adcom.com>
To:     Raviteja Garimella <raviteja.garimella@...adcom.com>,
        Rob Herring <robh+dt@...nel.org>,
        Mark Rutland <mark.rutland@....com>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Felipe Balbi <balbi@...nel.org>
Cc:     devicetree@...r.kernel.org, linux-kernel@...r.kernel.org,
        bcm-kernel-feedback-list@...adcom.com, linux-usb@...r.kernel.org
Subject: Re: [PATCH v2 6/6] UDC: Add Synopsys UDC Platform driver

Hi Raviteja,

One compatibility string to add.  Comment inline

On 17-02-21 03:43 AM, Raviteja Garimella wrote:
> This patch adds platform driver support for Synopsys UDC.
>
> A new driver file (snps_udc_plat.c) is created for this purpose
> where the platform driver registration is done based on OF
> node.
>
> Currently, UDC integrated into Broadcom's iProc SoCs (Northstar2
> and Cygnus) work with this driver.
>
> New members are added to the UDC data structure for having platform
> device support along with extcon and phy support.
>
> Kconfig and Makefiles are modified to select platform driver for
> compilation.
>
> Signed-off-by: Raviteja Garimella <raviteja.garimella@...adcom.com>
> ---
>  drivers/usb/gadget/udc/Kconfig         |  16 +-
>  drivers/usb/gadget/udc/Makefile        |   1 +
>  drivers/usb/gadget/udc/amd5536udc.h    |  14 ++
>  drivers/usb/gadget/udc/snps_udc_core.c |  54 ++++--
>  drivers/usb/gadget/udc/snps_udc_plat.c | 343 +++++++++++++++++++++++++++++++++
>  5 files changed, 408 insertions(+), 20 deletions(-)
>  create mode 100644 drivers/usb/gadget/udc/snps_udc_plat.c
>
> diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
> index 72e6234..5bc3305 100644
> --- a/drivers/usb/gadget/udc/Kconfig
> +++ b/drivers/usb/gadget/udc/Kconfig
> @@ -240,7 +240,7 @@ config USB_MV_U3D
>  	  controller, which support super speed USB peripheral.
>
>  config USB_SNP_CORE
> -	depends on USB_AMD5536UDC
> +	depends on (USB_SNP_UDC_PLAT || USB_AMD5536UDC)
>  	depends on PCI
>  	tristate
>  	help
> @@ -254,6 +254,20 @@ config USB_SNP_CORE
>  	  This IP is different to the High Speed OTG IP that can be enabled
>  	  by selecting USB_DWC2 or USB_DWC3 options.
>
> +config USB_SNP_UDC_PLAT
> +	tristate "Synopsys USB 2.0 Device controller"
> +	depends on (USB_GADGET && OF)
> +	select USB_GADGET_DUALSPEED
> +	select USB_SNP_CORE
> +	default ARCH_BCM_IPROC
> +	help
> +	  This adds Platform Device support for Synopsys Designware core
> +	  AHB subsystem USB2.0 Device Controller (UDC).
> +
> +	  This driver works with UDCs integrated into Broadcom's Northstar2
> +	  and Cygnus SoCs.
> +
> +	  If unsure, say N.
>  #
>  # Controllers available in both integrated and discrete versions
>  #
> diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
> index 4f4fd62..ea9e1c7 100644
> --- a/drivers/usb/gadget/udc/Makefile
> +++ b/drivers/usb/gadget/udc/Makefile
> @@ -37,4 +37,5 @@ obj-$(CONFIG_USB_FOTG210_UDC)	+= fotg210-udc.o
>  obj-$(CONFIG_USB_MV_U3D)	+= mv_u3d_core.o
>  obj-$(CONFIG_USB_GR_UDC)	+= gr_udc.o
>  obj-$(CONFIG_USB_GADGET_XILINX)	+= udc-xilinx.o
> +obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
>  obj-$(CONFIG_USB_BDC_UDC)	+= bdc/
> diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
> index c252457..7884281 100644
> --- a/drivers/usb/gadget/udc/amd5536udc.h
> +++ b/drivers/usb/gadget/udc/amd5536udc.h
> @@ -16,6 +16,7 @@
>  /* debug control */
>  /* #define UDC_VERBOSE */
>
> +#include <linux/extcon.h>
>  #include <linux/usb/ch9.h>
>  #include <linux/usb/gadget.h>
>
> @@ -28,6 +29,9 @@
>  #define UDC_HSA0_REV 1
>  #define UDC_HSB1_REV 2
>
> +/* Broadcom chip rev. */
> +#define UDC_BCM_REV 10
> +
>  /*
>   * SETUP usb commands
>   * needed, because some SETUP's are handled in hw, but must be passed to
> @@ -112,6 +116,7 @@
>  #define UDC_DEVCTL_BRLEN_MASK			0x00ff0000
>  #define UDC_DEVCTL_BRLEN_OFS			16
>
> +#define UDC_DEVCTL_SRX_FLUSH			14
>  #define UDC_DEVCTL_CSR_DONE			13
>  #define UDC_DEVCTL_DEVNAK			12
>  #define UDC_DEVCTL_SD				10
> @@ -564,7 +569,15 @@ struct udc {
>  	u16				cur_intf;
>  	u16				cur_alt;
>
> +	/* for platform device and extcon support */
>  	struct device			*dev;
> +	struct phy			*udc_phy;
> +	struct extcon_dev		*edev;
> +	struct extcon_specific_cable_nb	extcon_nb;
> +	struct notifier_block		nb;
> +	struct delayed_work		drd_work;
> +	struct workqueue_struct		*drd_wq;
> +	u32				conn_type;
>  };
>
>  #define to_amd5536_udc(g)	(container_of((g), struct udc, gadget))
> @@ -580,6 +593,7 @@ int udc_enable_dev_setup_interrupts(struct udc *dev);
>  int udc_mask_unused_interrupts(struct udc *dev);
>  irqreturn_t udc_irq(int irq, void *pdev);
>  void gadget_release(struct device *pdev);
> +void empty_req_queue(struct udc_ep *ep);
>  void udc_basic_init(struct udc *dev);
>  void free_dma_pools(struct udc *dev);
>  int init_dma_pools(struct udc *dev);
> diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
> index 5f95a65..98de074 100644
> --- a/drivers/usb/gadget/udc/snps_udc_core.c
> +++ b/drivers/usb/gadget/udc/snps_udc_core.c
> @@ -41,7 +41,6 @@
>  #include "amd5536udc.h"
>
>  static void udc_tasklet_disconnect(unsigned long);
> -static void empty_req_queue(struct udc_ep *);
>  static void udc_setup_endpoints(struct udc *dev);
>  static void udc_soft_reset(struct udc *dev);
>  static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);
> @@ -1248,7 +1247,7 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp)
>  }
>
>  /* Empty request queue of an endpoint; caller holds spinlock */
> -static void empty_req_queue(struct udc_ep *ep)
> +void empty_req_queue(struct udc_ep *ep)
>  {
>  	struct udc_request	*req;
>
> @@ -1260,6 +1259,7 @@ static void empty_req_queue(struct udc_ep *ep)
>  		complete_req(ep, req, -ESHUTDOWN);
>  	}
>  }
> +EXPORT_SYMBOL_GPL(empty_req_queue);
>
>  /* Dequeues a request packet, called by gadget driver */
>  static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
> @@ -1627,6 +1627,9 @@ static void udc_setup_endpoints(struct udc *dev)
>  /* Bringup after Connect event, initial bringup to be ready for ep0 events */
>  static void usb_connect(struct udc *dev)
>  {
> +	/* Return if already connected */
> +	if (dev->connected)
> +		return;
>
>  	dev_info(dev->dev, "USB Connect\n");
>
> @@ -1645,6 +1648,9 @@ static void usb_connect(struct udc *dev)
>   */
>  static void usb_disconnect(struct udc *dev)
>  {
> +	/* Return if already disconnected */
> +	if (!dev->connected)
> +		return;
>
>  	dev_info(dev->dev, "USB Disconnect\n");
>
> @@ -1719,11 +1725,15 @@ static void udc_soft_reset(struct udc *dev)
>  	/* device int. status reset */
>  	writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
>
> -	spin_lock_irqsave(&udc_irq_spinlock, flags);
> -	writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
> -	readl(&dev->regs->cfg);
> -	spin_unlock_irqrestore(&udc_irq_spinlock, flags);
> -
> +	/* Don't do this for Broadcom UDC since this is a reserved
> +	 * bit.
> +	 */
> +	if (dev->chiprev != UDC_BCM_REV) {
> +		spin_lock_irqsave(&udc_irq_spinlock, flags);
> +		writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
> +		readl(&dev->regs->cfg);
> +		spin_unlock_irqrestore(&udc_irq_spinlock, flags);
> +	}
>  }
>
>  /* RDE timer callback to set RDE bit */
> @@ -3175,21 +3185,27 @@ int udc_probe(struct udc *dev)
>  	dev_info(dev->dev, "%s\n", mod_desc);
>
>  	snprintf(tmp, sizeof(tmp), "%d", dev->irq);
> -	dev_info(dev->dev,
> -		 "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
> -		 tmp, dev->phys_addr, dev->chiprev,
> -		 (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1");
> -	strcpy(tmp, UDC_DRIVER_VERSION_STRING);
> -	if (dev->chiprev == UDC_HSA0_REV) {
> -		dev_err(dev->dev, "chip revision is A0; too old\n");
> -		retval = -ENODEV;
> -		goto finished;
> +
> +	/* Print this device info for AMD chips only*/
> +	if (dev->chiprev == UDC_HSA0_REV ||
> +	    dev->chiprev == UDC_HSB1_REV) {
> +		dev_info(dev->dev, "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n",
> +			 tmp, dev->phys_addr, dev->chiprev,
> +			 (dev->chiprev == UDC_HSA0_REV) ?
> +			 "A0" : "B1");
> +		strcpy(tmp, UDC_DRIVER_VERSION_STRING);
> +		if (dev->chiprev == UDC_HSA0_REV) {
> +			dev_err(dev->dev, "chip revision is A0; too old\n");
> +			retval = -ENODEV;
> +			goto finished;
> +		}
> +		dev_info(dev->dev,
> +			 "driver version: %s(for Geode5536 B1)\n", tmp);
>  	}
> -	dev_info(dev->dev,
> -		 "driver version: %s(for Geode5536 B1)\n", tmp);
> +
>  	udc = dev;
>
> -	retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget,
> +	retval = usb_add_gadget_udc_release(udc->dev, &dev->gadget,
>  					    gadget_release);
>  	if (retval)
>  		goto finished;
> diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c
> new file mode 100644
> index 0000000..3a4234d
> --- /dev/null
> +++ b/drivers/usb/gadget/udc/snps_udc_plat.c
> @@ -0,0 +1,343 @@
> +/*
> + * snps_udc_plat.c - Synopsys UDC Platform Driver
> + *
> + * Copyright (C) 2016 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation version 2.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/extcon.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/module.h>
> +#include <linux/dmapool.h>
> +#include <linux/interrupt.h>
> +#include <linux/moduleparam.h>
> +#include "amd5536udc.h"
> +
> +/* description */
> +#define UDC_MOD_DESCRIPTION     "Synopsys UDC platform driver"
> +
> +void start_udc(struct udc *udc)
> +{
> +	if (udc->driver) {
> +		dev_info(udc->dev, "Connecting...\n");
> +		udc_enable_dev_setup_interrupts(udc);
> +		udc_basic_init(udc);
> +		udc->connected = 1;
> +	}
> +}
> +
> +void stop_udc(struct udc *udc)
> +{
> +	int tmp;
> +	u32 reg;
> +
> +	spin_lock(&udc->lock);
> +
> +	/* Flush the receieve fifo */
> +	reg = readl(&udc->regs->ctl);
> +	reg |= AMD_BIT(UDC_DEVCTL_SRX_FLUSH);
> +	writel(reg, &udc->regs->ctl);
> +
> +	reg = readl(&udc->regs->ctl);
> +	reg &= ~(AMD_BIT(UDC_DEVCTL_SRX_FLUSH));
> +	writel(reg, &udc->regs->ctl);
> +	dev_dbg(udc->dev, "ep rx queue flushed\n");
> +
> +	/* Mask interrupts. Required more so when the
> +	 * UDC is connected to a DRD phy.
> +	 */
> +	udc_mask_unused_interrupts(udc);
> +
> +	/* Disconnect gadget driver */
> +	if (udc->driver) {
> +		spin_unlock(&udc->lock);
> +		udc->driver->disconnect(&udc->gadget);
> +		spin_lock(&udc->lock);
> +
> +		/* empty queues */
> +		for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
> +			empty_req_queue(&udc->ep[tmp]);
> +	}
> +	udc->connected = 0;
> +
> +	spin_unlock(&udc->lock);
> +	dev_info(udc->dev, "Device disconnected\n");
> +}
> +
> +void udc_drd_work(struct work_struct *work)
> +{
> +	struct udc *udc;
> +
> +	udc = container_of(to_delayed_work(work),
> +			   struct udc, drd_work);
> +
> +	if (udc->conn_type) {
> +		dev_dbg(udc->dev, "idle -> device\n");
> +		start_udc(udc);
> +	} else {
> +		dev_dbg(udc->dev, "device -> idle\n");
> +		stop_udc(udc);
> +	}
> +}
> +
> +static int usbd_connect_notify(struct notifier_block *self,
> +			       unsigned long event, void *ptr)
> +{
> +	struct udc *udc = container_of(self, struct udc, nb);
> +
> +	dev_dbg(udc->dev, "%s: event: %lu\n", __func__, event);
> +
> +	udc->conn_type = event;
> +
> +	schedule_delayed_work(&udc->drd_work, 0);
> +
> +	return NOTIFY_OK;
> +}
> +
> +static int udc_plat_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *res;
> +	struct udc *udc;
> +	int ret;
> +
> +	udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
> +	if (!udc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&udc->lock);
> +	udc->dev = dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	udc->virt_addr = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(udc->regs))
> +		return PTR_ERR(udc->regs);
> +
> +	/* udc csr registers base */
> +	udc->csr = udc->virt_addr + UDC_CSR_ADDR;
> +
> +	/* dev registers base */
> +	udc->regs = udc->virt_addr + UDC_DEVCFG_ADDR;
> +
> +	/* ep registers base */
> +	udc->ep_regs = udc->virt_addr + UDC_EPREGS_ADDR;
> +
> +	/* fifo's base */
> +	udc->rxfifo = (u32 __iomem *)(udc->virt_addr + UDC_RXFIFO_ADDR);
> +	udc->txfifo = (u32 __iomem *)(udc->virt_addr + UDC_TXFIFO_ADDR);
> +
> +	udc->phys_addr = (unsigned long)res->start;
> +
> +	udc->irq = irq_of_parse_and_map(dev->of_node, 0);
> +	if (udc->irq <= 0) {
> +		dev_err(dev, "Can't parse and map interrupt\n");
> +		return -EINVAL;
> +	}
> +
> +	udc->udc_phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
> +	if (IS_ERR(udc->udc_phy)) {
> +		dev_err(dev, "Failed to obtain phy from device tree\n");
> +		return PTR_ERR(udc->udc_phy);
> +	}
> +
> +	ret = phy_init(udc->udc_phy);
> +	if (ret) {
> +		dev_err(dev, "UDC phy init failed");
> +		return ret;
> +	}
> +
> +	ret = phy_power_on(udc->udc_phy);
> +	if (ret) {
> +		dev_err(dev, "UDC phy power on failed");
> +		phy_exit(udc->udc_phy);
> +		return ret;
> +	}
> +
> +	/* Register for extcon if supported */
> +	if (of_get_property(dev->of_node, "extcon", NULL)) {
> +		udc->edev = extcon_get_edev_by_phandle(dev, 0);
> +		if (IS_ERR(udc->edev)) {
> +			if (PTR_ERR(udc->edev) == -EPROBE_DEFER)
> +				return -EPROBE_DEFER;
> +			dev_err(dev, "Invalid or missing extcon\n");
> +			ret = PTR_ERR(udc->edev);
> +			goto exit_phy;
> +		}
> +
> +		udc->nb.notifier_call = usbd_connect_notify;
> +		ret = extcon_register_notifier(udc->edev, EXTCON_USB,
> +					       &udc->nb);
> +		if (ret < 0) {
> +			dev_err(dev, "Can't register extcon device\n");
> +			goto exit_phy;
> +		}
> +
> +		ret = extcon_get_cable_state_(udc->edev, EXTCON_USB);
> +		if (ret < 0) {
> +			dev_err(dev, "Can't get cable state\n");
> +			goto exit_extcon;
> +		} else if (ret) {
> +			udc->conn_type = ret;
> +		}
> +		INIT_DELAYED_WORK(&udc->drd_work, udc_drd_work);
> +	}
> +
> +	/* init dma pools */
> +	if (use_dma) {
> +		ret = init_dma_pools(udc);
> +		if (ret != 0)
> +			goto exit_extcon;
> +	}
> +
> +	ret = devm_request_irq(dev, udc->irq, udc_irq, IRQF_SHARED,
> +			       "snps-udc", udc);
> +	if (ret < 0) {
> +		dev_err(dev, "Request irq %d failed for UDC\n", udc->irq);
> +		goto exit_dma;
> +	}
> +
> +	platform_set_drvdata(pdev, udc);
> +	udc->chiprev = UDC_BCM_REV;
> +
> +	if (udc_probe(udc)) {
> +		ret = -ENODEV;
> +		goto exit_dma;
> +	}
> +	dev_info(dev, "Synopsys UDC platform driver probe successful\n");
> +
> +	return 0;
> +
> +exit_dma:
> +	if (use_dma)
> +		free_dma_pools(udc);
> +exit_extcon:
> +	if (udc->edev)
> +		extcon_unregister_notifier(udc->edev, EXTCON_USB, &udc->nb);
> +exit_phy:
> +	if (udc->udc_phy) {
> +		phy_power_off(udc->udc_phy);
> +		phy_exit(udc->udc_phy);
> +	}
> +	return ret;
> +}
> +
> +static int udc_plat_remove(struct platform_device *pdev)
> +{
> +	struct udc *dev;
> +
> +	dev = platform_get_drvdata(pdev);
> +
> +	usb_del_gadget_udc(&dev->gadget);
> +	/* gadget driver must not be registered */
> +	if (WARN_ON(dev->driver))
> +		return 0;
> +
> +	/* dma pool cleanup */
> +	free_dma_pools(dev);
> +
> +	udc_remove(dev);
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	if (dev->drd_wq) {
> +		flush_workqueue(dev->drd_wq);
> +		destroy_workqueue(dev->drd_wq);
> +	}
> +
> +	phy_power_off(dev->udc_phy);
> +	phy_exit(dev->udc_phy);
> +	extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb);
> +
> +	dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n");
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int udc_plat_suspend(struct device *dev)
> +{
> +	struct udc *udc;
> +
> +	udc = dev_get_drvdata(dev);
> +	stop_udc(udc);
> +
> +	if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
> +		dev_dbg(udc->dev, "device -> idle\n");
> +		stop_udc(udc);
> +	}
> +	phy_power_off(udc->udc_phy);
> +	phy_exit(udc->udc_phy);
> +
> +	return 0;
> +}
> +
> +static int udc_plat_resume(struct device *dev)
> +{
> +	struct udc *udc;
> +	int ret;
> +
> +	udc = dev_get_drvdata(dev);
> +
> +	ret = phy_init(udc->udc_phy);
> +	if (ret) {
> +		dev_err(udc->dev, "UDC phy init failure");
> +		return ret;
> +	}
> +
> +	ret = phy_power_on(udc->udc_phy);
> +	if (ret) {
> +		dev_err(udc->dev, "UDC phy power on failure");
> +		phy_exit(udc->udc_phy);
> +		return ret;
> +	}
> +
> +	if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) {
> +		dev_dbg(udc->dev, "idle -> device\n");
> +		start_udc(udc);
> +	}
> +
> +	return 0;
> +}
> +static const struct dev_pm_ops udc_plat_pm_ops = {
> +	.suspend	= udc_plat_suspend,
> +	.resume		= udc_plat_resume,
> +};
> +#endif
> +
> +#if defined(CONFIG_OF)
> +static const struct of_device_id of_udc_match[] = {
> +	{ .compatible = "brcm,ns2-udc", },
> +	{ .compatible = "brcm,cygnus-udc", },
	{ .compatible = "brcm,iproc-udc", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, of_udc_match);
> +#endif
> +
> +static struct platform_driver udc_plat_driver = {
> +	.probe		= udc_plat_probe,
> +	.remove		= udc_plat_remove,
> +	.driver		= {
> +		.name	= "snps-udc-plat",
> +		.of_match_table = of_match_ptr(of_udc_match),
> +#ifdef CONFIG_PM_SLEEP
> +		.pm	= &udc_plat_pm_ops,
> +#endif
> +	},
> +};
> +module_platform_driver(udc_plat_driver);
> +
> +MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
> +MODULE_AUTHOR("Broadcom");
> +MODULE_LICENSE("GPL v2");
>
Regards,
  Scott

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ