* **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip
@ 2005-08-02 19:20 jamey
2005-08-02 21:17 ` Michael Krufky
2005-08-02 21:18 ` Greg KH
0 siblings, 2 replies; 6+ messages in thread
From: jamey @ 2005-08-02 19:20 UTC (permalink / raw)
To: linux-kernel
This patch adds USB gadget support for the USB peripheral controller
on the MQ11xx graphics chip.
Signed-off-by: Jamey Hicks <jamey@handhelds.org>
Signed-off-by: Andrew Zabolotny <anpaza@mail.ru>
---
commit 9b2407962e0786705efeed755ab1d399deba8685
tree 7c9e5187687bec3112af0f83e9e3831b18c41fa5
parent d6631d9e21df12578ae5e7a2878b7e7132cfa28c
author <jamey@lspace.crl.dec.com> Tue, 02 Aug 2005 11:28:38 -0400
committer <jamey@lspace.crl.dec.com> Tue, 02 Aug 2005 11:28:38 -0400
drivers/usb/gadget/Kconfig | 15
drivers/usb/gadget/Makefile | 1
drivers/usb/gadget/mq11xx_udc.c | 1710 +++++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/mq11xx_udc.h | 115 +++
4 files changed, 1841 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -142,6 +142,21 @@ config USB_GOKU
select USB_GADGET_SELECTED
+config USB_GADGET_MQ11XX
+ boolean "MediaQ 11xx USB Device Controller"
+ depends on PLATFORM_MQ11XX
+ help
+ Enable this if you want to use MediaQ chip's integrated
+ USB device controller.
+
+ It has three fixed-function endpoints, as well as endpoint
+ zero (for control transfers).
+
+config USB_MQ11XX
+ tristate
+ depends on USB_GADGET_MQ11XX
+ default USB_GADGET
+
config USB_GADGET_LH7A40X
boolean "LH7A40X"
depends on ARCH_LH7A40X
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
+obj-$(CONFIG_USB_MQ11XX) += mq11xx_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/mq11xx_udc.c b/drivers/usb/gadget/mq11xx_udc.c
new file mode 100644
--- /dev/null
+++ b/drivers/usb/gadget/mq11xx_udc.c
@@ -0,0 +1,1710 @@
+/*
+ * MediaQ 11xx USB Device Controller driver
+ * Goku-S UDC driver used as a template.
+ *
+ * Copyright (C) 2004 Andrew Zabolotny <zap@homelink.ru>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/unaligned.h>
+
+#define __MQ11XX_INTERNAL
+#include "mq11xx_udc.h"
+
+/*
+ * This device has ep0 and three semi-configurable interrupt/bulk/isochronous
+ * endpoints.
+ *
+ * - Endpoints 1 and 2 are IN, 3 is OUT
+ * - Endpoint 1 is for interrupt transfers; endpoints 2 and 3 can be
+ * configured to either bulk or isochronous mode.
+ * - Endpoint 0 and 1 use FIFO, endpoints 2 and 3 use DMA transfers.
+ *
+ * Note that when using debugging output, the delay between the IN token
+ * and the actual time of filling the FIFO can exceed 18 bit times
+ * (as stated in section 7.1.19.1 of the USB specs), and the transfer
+ * times out. But when the host retries the transfer, the data is already
+ * in the FIFO so it gets transferred correctly, except that you can see
+ * "usb_control/bulk_msg: timeout" messages on the host side. Also on bulk
+ * endpoints you can eventually easily get timeouts/misbehaviour from time
+ * to time, especially if you're using small rx/tx buffers.
+ *
+ * NOTE: Isochronous mode is not fully implemented yet.
+ *
+ * NOTE: Due to crappy chip design, there are still some hardware race
+ * conditions and such. I've tried to make these dangerous time periods
+ * as short as possible, but nothing guaranteed. I've marked these places
+ * with [CRAP] marks; if you have a better solution, go ahead and fix it.
+ */
+
+/* Enable debug logs in general (The Big Red Switch) */
+//#define USB_DEBUG
+/* Enable debugging of packets */
+//#define USB_DEBUG_TRACE
+/* Enable debugging of packet contents */
+//#define USB_DEBUG_DATA
+/* Log time of every debug event */
+//#define USB_DEBUG_TIME
+
+#ifdef USB_DEBUG_TIME
+# ifdef CONFIG_ARCH_PXA
+# include <asm/arch/hardware.h>
+ /* Approximative division by 3.6864 to get microseconds */
+# define USB_DEBUG_TIMER ((OSCR*139)/512)
+# else
+# define USB_DEBUG_TIMER jiffies
+# endif
+#endif
+
+#ifdef USB_DEBUG
+# ifdef USB_DEBUG_TIMER
+# define debug(s, args...) printk (KERN_INFO "(%7u) %s: " s, USB_DEBUG_TIMER, __FUNCTION__, ##args)
+# else
+# define debug(s, args...) printk (KERN_INFO "%s: " s, __FUNCTION__, ##args)
+# endif
+#else
+# define debug(s, args...)
+#endif
+#define info(s, args...) printk (KERN_INFO "%s: " s, driver_name, ##args)
+
+/* Endpoint 0 and 1 FIFO depth in bytes */
+#define MQ_EP01_MAXPACKET (MQ_UDC_FIFO_DEPTH * sizeof (u32))
+/* Endpoint 2 and 3 packet size */
+#define MQ_EP23_MAXPACKET 64
+
+static const char driver_name [] = "mq11xx_udc";
+
+MODULE_DESCRIPTION("MediaQ 11xx USB Device Controller");
+MODULE_AUTHOR("Andrew Zabolotny <zap@homelink.ru>");
+MODULE_LICENSE("GPL");
+
+/* The size for one buffer in MediaQ internal RAM */
+static unsigned rxbs = 4096;
+MODULE_PARM (rxbs, "i");
+MODULE_PARM_DESC (rxbs, "DMA receive buffer size (two buffers)");
+
+static unsigned txbs = 4096;
+MODULE_PARM (txbs, "i");
+MODULE_PARM_DESC (txbs, "DMA transmit buffer size (two buffers)");
+
+/* Static variables are EVIL :) but for now anyways the UDC architecture
+ * allows only one UDC controller at a time.
+ */
+struct mq11xx_udc_mach_info *mach_info;
+
+static int mq_set_halt(struct usb_ep *, int);
+static void mq_ep_finish(struct mq_ep *, int status);
+static void mq_done_request (struct mq_ep *ep, struct mq_request *req,
+ int status);
+
+#ifdef USB_DEBUG_DATA
+static void debug_dump (u8 *data, int count, int base)
+{
+ char line [80];
+ u32 i, j, offs = 0;
+ while (count > 0) {
+ j = sprintf (line, "%08x | ", base + offs);
+ for (i = 0; i < 16; i++)
+ if (i < count)
+ j += sprintf (line + j, "%02x ", data [offs + i]);
+ else
+ j += sprintf (line + j, " ");
+ j += sprintf (line + j, "| ");
+ for (i = 0; i < 16; i++) {
+ u8 c = data [offs + i];
+ line [j++] = (i >= count) ? ' ' :
+ ((c < 32 || c > 126) ? '.' : c);
+ }
+ line [j] = 0;
+ printk ("%s\n", line);
+ offs += 16;
+ count -= 16;
+ }
+}
+#else
+#define debug_dump(x,y,z)
+#endif
+
+static int mq_ep_enable (struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct mqudc *mqdev;
+ struct mq_ep *ep;
+ u32 max, dmactl;
+ unsigned long flags;
+ volatile struct mediaq11xx_regs *regs;
+
+ debug ("%s type:%d addr:%x maxpkt:%d\n", _ep->name,
+ desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK,
+ desc->bEndpointAddress,
+ desc->wMaxPacketSize);
+
+ ep = container_of(_ep, struct mq_ep, ep);
+ if (!_ep || !desc || ep->desc ||
+ desc->bDescriptorType != USB_DT_ENDPOINT ||
+ (desc->bEndpointAddress & 0x0f) > 3)
+ return -EINVAL;
+ mqdev = ep->mqdev;
+ if (ep == &mqdev->ep[0])
+ return -EINVAL;
+ if (!mqdev->driver)
+ return -ESHUTDOWN;
+ if (ep->num != (desc->bEndpointAddress & 0x0f))
+ return -EINVAL;
+
+ regs = mqdev->base->regs;
+
+ /* Endpoint 1 cannot do anything but interrupt transfers
+ while endpoints 2 and 3 can do only bulk/isochronous transfers. */
+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_INT:
+ if (ep->num != 1)
+ return -EINVAL;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_ISOC:
+ if (ep->num > 1) {
+ max = (ep->num == 2) ? MQ_UDC_EP2_ISOCHRONOUS :
+ MQ_UDC_EP3_ISOCHRONOUS;
+ if ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ regs->UDC.control &= ~max;
+ ep->iso = 0;
+ ep->regs [0] &= ~MQ_UDC_FORCE_DATA0;
+ } else {
+ regs->UDC.control |= max;
+ ep->iso = 1;
+ ep->regs [0] |= MQ_UDC_FORCE_DATA0;
+ }
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (ep->regs [0] & MQ_UDC_EP_ENABLE)
+ debug ("%s: already enabled\n", ep->ep.name);
+
+ /* MediaQ UDC understands only 16 byte packets for EP0 and EP1,
+ * and 64-byte packets for EP2 & EP3.
+ */
+ max = le16_to_cpu (get_unaligned (&desc->wMaxPacketSize));
+ if (max > ((ep->num < 2) ? MQ_EP01_MAXPACKET : MQ_EP23_MAXPACKET))
+ return -EINVAL;
+
+ /* Check pipe direction - 1 & 2 are IN, 3 is OUT */
+ if (desc->bEndpointAddress & USB_DIR_IN) {
+ if (ep->num > 2)
+ return -EINVAL;
+ } else
+ if (ep->num < 3)
+ return -EINVAL;
+
+ spin_lock_irqsave (&ep->mqdev->lock, flags);
+
+ ep->regs [0] |= MQ_UDC_EP_ENABLE;
+
+ /* ep2 and ep3 can do double buffering and/or dma */
+ if (ep->dmaregs) {
+ ep->active_buffer = 0;
+ /* DMA burst transfer length - 24 bytes */
+// regs->MIU.miu_1 = (regs->MIU.miu_1 & ~MQ_MIU_UDC_BURST_MASK) |
+// MQ_MIU_UDC_BURST_COUNT_6;
+ /* For isochronous mode: burst threshold for RX is 6 free
+ * locations in the RX FIFO; for TX is when 6 locations in
+ * FIFO are received (6 locations == 24 bytes).
+ */
+// ep->regs [0] = (ep->regs [0] & ~MQ_UDC_FIFO_THRESHOLD_MASK) |
+// MQ_UDC_FIFO_THRESHOLD (6);
+ /* Switch between 2 DMA buffers on RX/TX */
+ dmactl = MQ_UDC_DMA_ENABLE | MQ_UDC_DMA_PINGPONG | \
+ MQ_UDC_DMA_NUMBUFF (2);
+ if (ep->num == 3)
+ dmactl |= MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF2_OWNER;
+ ep->dmaregs [0] = dmactl;
+ }
+
+ mq_set_halt (_ep, 0);
+ ep->ep.maxpacket = max;
+ ep->stopped = 1;
+ ep->desc = desc;
+ spin_unlock_irqrestore (&ep->mqdev->lock, flags);
+
+ return 0;
+}
+
+static void mq_ep_clear_fifo (struct mq_ep *ep)
+{
+ if (ep->num < 2) {
+ ep->quick_buff_bytes = 0;
+ ep->regs [0] |= MQ_UDC_CLEAR_FIFO;
+ ep->regs [0] &= ~MQ_UDC_CLEAR_FIFO;
+ if (ep->num == 0) {
+ ep->regs [2] |= MQ_UDC_CLEAR_FIFO;
+ ep->regs [2] &= ~MQ_UDC_CLEAR_FIFO;
+ }
+ }
+}
+
+static void mq_ep_reset (struct mq_ep *ep)
+{
+ /* Disable the endpoint (if not ep0) or clear the FIFO (if ep0) */
+ ep->regs [0] = MQ_UDC_CLEAR_FIFO;
+ ep->regs [0] = 0;
+ if (ep->num == 0) {
+ ep->regs [2] = MQ_UDC_CLEAR_FIFO;
+ ep->regs [2] = 0;
+ }
+ if (ep->dmaregs)
+ ep->dmaregs [0] = 0;
+
+ ep->ep.maxpacket = (ep->num < 2) ? MQ_EP01_MAXPACKET : MQ_EP23_MAXPACKET;
+ ep->desc = 0;
+ ep->stopped = 1;
+}
+
+/* enable bit 0 - enable primary endpoint interrupt;
+ * bit 1 - enable secondary endpoint interrupt (TX for ep0).
+ */
+static void mq_ep_ints (struct mq_ep *ep, int enable)
+{
+ volatile struct mediaq11xx_regs *regs = ep->mqdev->base->regs;
+ u32 rxmask = 0, txmask = 0;
+
+ debug ("%s: rx:%d tx:%d\n", ep->ep.name, enable & 1, (enable >> 1) & 1);
+
+ switch (ep->num) {
+ case 0:
+ rxmask = MQ_UDC_IEN_EP0_RX;
+ txmask = MQ_UDC_IEN_EP0_TX;
+ break;
+ case 1:
+ txmask = MQ_UDC_IEN_EP1_TX;
+ break;
+ case 2:
+ txmask = /*MQ_UDC_IEN_EP2_TX_EOT | */MQ_UDC_IEN_DMA_TX;
+ break;
+ case 3:
+ rxmask = /*MQ_UDC_IEN_EP3_RX_EOT | */MQ_UDC_IEN_DMA_RX | MQ_UDC_IEN_DMA_RX_EOT;
+ break;
+ default:
+ return;
+ }
+
+ if (enable & 1)
+ regs->UDC.control |= rxmask;
+ else
+ regs->UDC.control &= ~rxmask;
+
+ if (enable & 2)
+ regs->UDC.control |= txmask;
+ else
+ regs->UDC.control &= ~txmask;
+}
+
+static int mq_ep_disable (struct usb_ep *_ep)
+{
+ struct mq_ep *ep;
+ struct mqudc *mqdev;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct mq_ep, ep);
+ if (!_ep || !ep->desc)
+ return -ENODEV;
+ mqdev = ep->mqdev;
+ if (mqdev->ep0state == EP0_SUSPEND)
+ return -EBUSY;
+
+ spin_lock_irqsave(&mqdev->lock, flags);
+
+ /* Drop all pending requests */
+ mq_ep_finish(ep, -ESHUTDOWN);
+
+ /* Disable endpoint interrupts */
+ mq_ep_ints (ep, 0);
+
+ /* Set the endpoint to RESET state */
+ mq_ep_reset(ep);
+
+ spin_unlock_irqrestore(&mqdev->lock, flags);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* Send a packed through endpoint 0 or 1 using FIFO.
+ */
+static void mq_tx_fifo (struct mqudc *mqdev, struct mq_ep *ep, int rst_status)
+{
+ struct mq_request *req;
+ int bytes_left, packet_len;
+ u32 *buf;
+ volatile u32 *fifo;
+
+ /* Acknowledge all status bits, we already got the status */
+ if (rst_status)
+ ep->regs [1] = 0xff;
+
+ req = list_empty (&ep->list) ? NULL :
+ list_entry (ep->list.next, struct mq_request, queue);
+
+ /* If there's a short packet to be sent on endpoint 0, do it. */
+ if ((ep->quick_buff_bytes) &&
+ (!req || req->req.actual >= req->req.length)) {
+ bytes_left = ep->quick_buff_bytes - 1;
+ ep->quick_buff_bytes = 0;
+ buf = &ep->quick_buff;
+ req = NULL;
+ goto send_packet;
+ }
+
+ if (unlikely (!req))
+ return;
+
+ /* Since the start of request buffer is always aligned to
+ * word boundary, we can move whole words to FIFO until we
+ * get to the last (incomplete) word. Also since memory is
+ * never allocated in chunks smaller than one word, we can
+ * safely read the last incomplete 32-bit word, to avoid
+ * copying bytes by one.
+ */
+ bytes_left = req->req.length - req->req.actual;
+ if (bytes_left > ep->ep.maxpacket)
+ bytes_left = ep->ep.maxpacket;
+ buf = (u32 *)(req->req.actual + (u8 *)req->req.buf);
+ req->req.actual += bytes_left;
+
+send_packet:
+ packet_len = bytes_left;
+ fifo = (ep->num == 0) ? &ep->regs [5] : &ep->regs [2];
+ while (bytes_left > 0) {
+ *fifo = le32_to_cpu (*buf++);
+ bytes_left -= sizeof (u32);
+ }
+
+ ep->regs [0] = (ep->regs [0] & MQ_UDC_EP_ENABLE) | mqdev->toggle |
+ MQ_UDC_TX_EDT | MQ_UDC_TX_LAST_ENABLE (4 + bytes_left);
+
+ debug ("%s: Pktsize %d ctrl %08x stat %08x\n", ep->ep.name, packet_len,
+ ep->regs [0], ep->regs [1]);
+
+ /* Toggle DATA0/DATA1 */
+ mqdev->toggle ^= MQ_UDC_TX_PID_DATA1;
+
+ if (!req || (req->req.actual >= req->req.length)) {
+ /* Emmit a ZLP if the last packet is full size and
+ * req->zero is set.
+ */
+ if (unlikely (req && req->req.zero &&
+ (packet_len == ep->ep.maxpacket))) {
+ /* Send ZLP on next interrupt */
+ ep->quick_buff_bytes = 1;
+ debug ("%s: ZLP\n", ep->ep.name);
+ }
+
+ /* In any case, the request has been submitted.
+ * Note that inside done_request we can get new requests
+ * hooked on this endpoint, so be careful.
+ */
+ if (req)
+ mq_done_request (ep, req, 0);
+
+ if (!ep->stopped && list_empty (&ep->list) && !ep->quick_buff_bytes) {
+ /* If this is the last packet of a request, report
+ * successful completion to the gadget driver and
+ * disable further TX interrupts (enable them only
+ * when needed).
+ */
+ ep->stopped = 1;
+ /* Disable transmit interrupts */
+ mq_ep_ints (ep, 1);
+ if (ep->num == 0)
+ mqdev->ep0state = EP0_IDLE;
+ debug ("%s: Stop TX\n", ep->ep.name);
+ }
+ }
+}
+
+/* Send a packet through endpoint 2 using DMA.
+ */
+static void mq_tx_dma (struct mqudc *mqdev, struct mq_ep *ep)
+{
+ struct mq_request *req;
+ int transfer_size;
+ u32 dmactl, dmadesc, buffno, owner_mask, eot_mask;
+
+ debug ("dmactl:%08x status:%08x\n", ep->dmaregs [0], ep->regs [1]);
+
+ for (;;) {
+ dmactl = ep->dmaregs [0];
+
+ if (list_empty (&ep->list)) {
+ /* Disable DMA only after both DMA buffers are transferred */
+ if (dmactl & (MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF2_OWNER))
+ return;
+ mq_ep_ints (ep, 1);
+ ep->stopped = 1;
+ debug ("%s: stop TX (ctrl:%08x)\n", ep->ep.name, ep->dmaregs [0]);
+ return;
+ }
+
+ if ((buffno = ep->active_buffer) == 0) {
+ owner_mask = MQ_UDC_DMA_BUFF1_OWNER;
+ eot_mask = MQ_UDC_DMA_BUFF1_EOT;
+ } else {
+ owner_mask = MQ_UDC_DMA_BUFF2_OWNER;
+ eot_mask = MQ_UDC_DMA_BUFF2_EOT;
+ }
+
+ /* Ok, DMA buffers loaded up - return */
+ if (dmactl & owner_mask)
+ return;
+
+ req = list_entry (ep->list.next, struct mq_request, queue);
+
+ transfer_size = req->req.length - req->req.actual;
+ if (transfer_size > txbs)
+ transfer_size = txbs;
+
+ /* Zero-length transfers not supported (?) */
+ if (!transfer_size) {
+ debug ("WARNING: ZERO-LENGTH TRANSFER REQUESTED BUT NOT SUPPORTED BY HARDWARE!\n");
+ mq_done_request (ep, req, 0);
+ continue;
+ }
+
+ /* Transfer next portion of data to MediaQ RAM */
+ memcpy ((u8 *)mqdev->base->ram + ep->dmabuff + (buffno ? txbs : 0),
+ req->req.actual + (u8 *)req->req.buf, transfer_size);
+ req->req.actual += transfer_size;
+ ep->active_buffer ^= 1;
+
+ /* Now construct the buffer descriptor */
+ dmadesc = MQ_UDC_DMA_BUFFER_ADDR (ep->dmabuff + (buffno ? txbs : 0)) |
+ MQ_UDC_DMA_BUFFER_SIZE (transfer_size);
+
+ /* Find if this is last packet and multiple of maxpacket size */
+ if ((req->req.actual >= req->req.length) &&
+ !(transfer_size & 63))
+ dmadesc |= MQ_UDC_DMA_BUFFER_LAST;
+ ep->dmaregs [1 + buffno] = dmadesc;
+
+ /* Decide if we need a ZLP after last packet */
+ if (!ep->iso && req->req.zero &&
+ (req->req.actual >= req->req.length))
+ dmactl = ~0;
+ else {
+ dmactl = ~eot_mask;
+ eot_mask = 0;
+ }
+
+ /* Read register value again in the case it has been
+ * changed in the meantime (esp. during memcpy()).
+ * [CRAP] while we're holding dmaregs[0] in CPU registers,
+ * it could change in hardware. Thus this period of time
+ * must be as short as possible.
+ */
+ dmactl &= (ep->dmaregs [0] | owner_mask | eot_mask);
+ ep->dmaregs [0] = dmactl;
+
+ debug ("buff:%d size:%d dmactrl:%08x dmadesc:%08x\n",
+ buffno, transfer_size, dmactl, ep->dmaregs [1 + buffno]);
+ debug_dump ((u8 *)req->req.buf + req->req.actual - transfer_size,
+ transfer_size, ep->dmabuff + (buffno ? txbs : 0));
+
+ if (req->req.actual >= req->req.length)
+ mq_done_request (ep, req, 0);
+ }
+}
+
+/* Prepare to receive packets through endpoint 3 using DMA.
+ */
+static void mq_rx_dma_prepare (struct mq_ep *ep)
+{
+ u32 dmactl, addr, buffno;
+
+ dmactl = ep->dmaregs [0] | MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF2_OWNER;
+
+ for (buffno = 0; buffno <= 1; buffno++) {
+ if (buffno == 0)
+ dmactl &= ~(MQ_UDC_DMA_BUFF1_OWNER | MQ_UDC_DMA_BUFF1_EOT);
+ else
+ dmactl &= ~(MQ_UDC_DMA_BUFF2_OWNER | MQ_UDC_DMA_BUFF2_EOT);
+
+ addr = ep->dmabuff + (buffno ? rxbs : 0);
+ ep->dmaregs [1 + buffno] = MQ_UDC_DMA_BUFFER_ADDR (addr) |
+ MQ_UDC_DMA_BUFFER_EADDR (addr + rxbs);
+ debug ("prepare buff %d, addr %08x\n", buffno, addr);
+ }
+
+ ep->dmaregs [0] = dmactl;
+}
+
+/* Transfer all the data in DMA buffers to pending request.
+ * After this re-submit the buffers to chip DMA controller.
+ */
+static void mq_rx_dma (struct mqudc *mqdev, struct mq_ep *ep)
+{
+ struct mq_request *req = NULL;
+ int end_transf, transfer_size, space_avail;
+ u32 dmactl, buffno, owner_mask, eot_mask;
+ volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
+
+ end_transf = 0;
+try_again:
+ dmactl = ep->dmaregs [0];
+ debug ("ctrl:%08x status:%08x\n", dmactl, ep->regs [1]);
+
+ for (;;) {
+ /**
+ * [CRAP] the MQ_UDC_INT_DMA_RX_EOT is set for ANY of the
+ * two buffers being completely received. Thus we have to
+ * do a lot of guesswork in the case when two buffers are
+ * marked as "owned by CPU", e.g. contain received data.
+ */
+ if (ep->iso) {
+ // ??? todo
+ if (dmactl & MQ_UDC_ISO_TRANSFER_END)
+ end_transf |= ~ep->dmaregs [0] & (MQ_UDC_DMA_BUFF1_OWNER |
+ MQ_UDC_DMA_BUFF2_OWNER);
+ } else {
+ if (regs->UDC.intstatus & MQ_UDC_INT_DMA_RX_EOT) {
+ end_transf |= ~ep->dmaregs [0] & (MQ_UDC_DMA_BUFF1_OWNER |
+ MQ_UDC_DMA_BUFF2_OWNER);
+ regs->UDC.intstatus = MQ_UDC_INT_DMA_RX_EOT;
+ }
+ }
+
+ if ((buffno = ep->active_buffer) == 0) {
+ owner_mask = MQ_UDC_DMA_BUFF1_OWNER;
+ eot_mask = MQ_UDC_DMA_BUFF1_EOT;
+ } else {
+ owner_mask = MQ_UDC_DMA_BUFF2_OWNER;
+ eot_mask = MQ_UDC_DMA_BUFF2_EOT;
+ }
+
+ /* No more received buffers? */
+ if (!(dmactl & owner_mask)) {
+ if (dmactl & (owner_mask ^ (MQ_UDC_DMA_BUFF1_EOT | MQ_UDC_DMA_BUFF2_EOT))) {
+ debug ("Desyncronization! (buff %d, ctrl %08x)\n",
+ buffno, dmactl);
+ ep->active_buffer ^= 1;
+ continue;
+ }
+ if (end_transf) {
+ debug ("EOT received w/o data\n");
+ goto try_again;
+ }
+ return;
+ }
+
+ ep->active_buffer ^= 1;
+
+ if (list_empty (&ep->list)) {
+ ep->stopped = 1;
+ debug ("Received data, but no requests in queue!\n");
+ return;
+ }
+
+ /* Get the next pending receive request */
+ req = list_entry (ep->list.next, struct mq_request, queue);
+
+ /* Find out the size of data pending in DMA buffers */
+ transfer_size = rxbs;
+ if (!ep->iso && (dmactl & eot_mask))
+ transfer_size = ep->dmaregs [3 + buffno];
+
+ /* Find out the available space in request' buffer */
+ space_avail = req->req.length - req->req.actual;
+ if (transfer_size > space_avail) {
+ transfer_size = space_avail;
+ if (!ep->iso)
+ req->req.status = -EOVERFLOW;
+ }
+
+ /* Copy data from DMA buffers to request buffer */
+ memcpy (req->req.actual + (u8 *)req->req.buf,
+ (u8 *)mqdev->base->ram + ep->dmabuff + (buffno ? rxbs : 0),
+ transfer_size);
+
+ /* Mark immediately the DMA buffer as processed and free
+ * for more incoming data (maximize throughput).
+ *
+ * [CRAP] While we hold the old value in CPU registers,
+ * the hardware register state can change and we'll write
+ * the OLD value back.
+ */
+ dmactl = ~(owner_mask | eot_mask | MQ_UDC_ISO_TRANSFER_END);
+ dmactl &= ep->dmaregs [0];
+ ep->dmaregs [0] = dmactl;
+
+ debug ("buff:%d size:%d dmactl:%08x end:%x\n",
+ buffno, transfer_size, dmactl, end_transf);
+ debug_dump ((u8 *)req->req.buf + req->req.actual, transfer_size,
+ ep->dmabuff + (buffno ? rxbs : 0));
+
+ req->req.actual += transfer_size;
+
+ /* If we have a short buffer, we obviously have a end
+ * of transfer.
+ */
+ if (transfer_size < rxbs)
+ end_transf = owner_mask;
+ /* If we have a full buffer, and the other buffer is available,
+ * consider the EOP belonging to the next buffer, not this.
+ * This often happens when we have an incoming packet with
+ * the length equal to rxbs+a little (say, 4 bytes).
+ */
+ else if (end_transf & ~owner_mask)
+ end_transf &= ~owner_mask;
+
+ /* NOTE: we don't handle for now properly the case
+ * when a incoming ISO packet can fill several requests.
+ */
+
+ if ((ep->iso &&
+ (end_transf || (req->req.actual >= req->req.length))) ||
+ (!ep->iso &&
+ (end_transf & owner_mask))) {
+ if (ep->iso)
+ end_transf = 0;
+ else
+ end_transf &= ~owner_mask;
+ mq_done_request (ep, req, 0);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+
+static struct usb_request *mq_alloc_request (struct usb_ep *_ep,
+ int gfp_flags)
+{
+ struct mq_request *req;
+
+ req = kmalloc (sizeof (*req), gfp_flags);
+ if (!req)
+ return 0;
+
+ memset (req, 0, sizeof (*req));
+ INIT_LIST_HEAD (&req->queue);
+
+ return &req->req;
+}
+
+static void mq_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct mq_request *req;
+
+ if (!_ep || !_req)
+ return;
+
+ req = container_of (_req, struct mq_request, req);
+
+ if (!list_empty (&req->queue)) {
+ debug ("%s: request %p still processed, unlinking\n",
+ _ep->name, _req);
+ list_del (&req->queue);
+ if (likely (req->req.status == -EINPROGRESS))
+ req->req.status = -ESHUTDOWN;
+ }
+ kfree (req);
+}
+
+static void *mq_alloc_buffer (struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t *dma, int gfp_flags)
+{
+ return kmalloc (bytes, gfp_flags);
+}
+
+static void mq_free_buffer (struct usb_ep *_ep, void *buf, dma_addr_t dma,
+ unsigned bytes)
+{
+ kfree (buf);
+}
+
+static void mq_done_request (struct mq_ep *ep, struct mq_request *req,
+ int status)
+{
+ struct mqudc *mqdev;
+
+ list_del_init (&req->queue);
+
+ if (likely (req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ mqdev = ep->mqdev;
+
+#ifndef USB_DEBUG_TRACE
+ if (status && status != -ESHUTDOWN)
+#endif
+ debug ("complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ spin_unlock (&mqdev->lock);
+ req->req.complete (&ep->ep, &req->req);
+ spin_lock (&mqdev->lock);
+}
+
+static int mq_start_queue (struct mq_ep *ep)
+{
+ unsigned long flags;
+
+ debug ("%s\n", ep->ep.name);
+
+ spin_lock_irqsave (&ep->mqdev->lock, flags);
+
+ /* Enable both RX and TX interrupts from this endpoint */
+ if (ep->stopped)
+ mq_ep_ints (ep, 3);
+
+ /* For DMA-backed endpoints we must pre-fill the DMA buffers
+ * and then transmit buffers ownership to the mq chip; for FIFO
+ * endpoints we must fill the FIFO.
+ */
+ if (!ep->dmaregs) {
+ ep->stopped = 0;
+ if (ep->is_in)
+ /* Fill the transmit FIFO for this endpoint */
+ mq_tx_fifo (ep->mqdev, ep, 1);
+ } else if (ep->stopped) {
+ ep->stopped = 0;
+ if (ep->is_in)
+ mq_tx_dma (ep->mqdev, ep);
+ else
+ mq_rx_dma_prepare (ep);
+ }
+
+ spin_unlock_irqrestore (&ep->mqdev->lock, flags);
+
+ return 0;
+}
+
+static int mq_queue (struct usb_ep *_ep, struct usb_request *_req,
+ int gfp_flags)
+{
+ struct mq_ep *ep;
+ struct mqudc *mqdev;
+ unsigned long flags;
+ int status;
+ struct mq_request *req = container_of (_req, struct mq_request, req);
+
+ if (unlikely(!_req || !req->req.complete
+ || !req->req.buf || !list_empty (&req->queue))) {
+ debug ("%s: Invalid request (%p %p %p %d)\n",
+ _ep->name, req, req->req.complete, req->req.buf,
+ list_empty (&req->queue));
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct mq_ep, ep);
+ if (unlikely (!_ep ||
+ ((ep->num != 0) &&
+ (!ep->desc || !(ep->regs [0] & MQ_UDC_EP_ENABLE))))) {
+ debug ("%s: Invalid endpoint\n", _ep->name);
+ return -EINVAL;
+ }
+
+ mqdev = ep->mqdev;
+ if (unlikely(!mqdev->driver))
+ return -ESHUTDOWN;
+
+ /* can't touch registers when suspended */
+ if (mqdev->ep0state == EP0_SUSPEND)
+ return -EBUSY;
+
+#ifdef USB_DEBUG_TRACE
+ debug("%s queue req %p, len %u buf %p\n",
+ _ep->name, _req, req->req.length, req->req.buf);
+#endif
+
+ spin_lock_irqsave (&mqdev->lock, flags);
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+
+ /* for ep0 IN without premature status, zlp is required and
+ * writing EOP starts the status stage (OUT).
+ */
+ if (unlikely (ep->num == 0 && ep->is_in))
+ req->req.zero = 1;
+
+ list_add_tail (&req->queue, &ep->list);
+
+ /* kickstart this i/o queue? */
+ status = mq_start_queue (ep);
+
+ spin_unlock_irqrestore (&mqdev->lock, flags);
+
+ return status;
+}
+
+/* dequeue ALL requests */
+static void mq_ep_finish (struct mq_ep *ep, int status)
+{
+ struct mq_request *req;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ep->mqdev->lock, flags);
+
+ /* Disable tx interrupts */
+ mq_ep_ints (ep, 1);
+ ep->stopped = 1;
+ /* Clear both FIFOs */
+ mq_ep_clear_fifo (ep);
+
+ while (!list_empty(&ep->list)) {
+ req = list_entry (ep->list.next, struct mq_request, queue);
+ mq_done_request (ep, req, status);
+ }
+
+ spin_unlock_irqrestore (&ep->mqdev->lock, flags);
+}
+
+static int mq_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct mq_ep *ep;
+ struct mqudc *mqdev;
+ unsigned long flags;
+ struct mq_request *req = container_of (_req, struct mq_request, req);
+
+ if (!_req || list_empty (&req->queue))
+ return -EINVAL;
+ ep = container_of (_ep, struct mq_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ mqdev = ep->mqdev;
+ if (!mqdev->driver)
+ return -ESHUTDOWN;
+
+ /* we can't touch registers when suspended */
+ if (mqdev->ep0state == EP0_SUSPEND)
+ return -EBUSY;
+
+ spin_lock_irqsave (&mqdev->lock, flags);
+ mq_done_request (ep, req, -ECONNRESET);
+ spin_unlock_irqrestore (&mqdev->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int mq_set_halt(struct usb_ep *_ep, int value)
+{
+ struct mq_ep *ep;
+
+ if (!_ep)
+ return -ENODEV;
+
+ ep = container_of (_ep, struct mq_ep, ep);
+ debug ("%s %s halt\n", _ep->name, value ? "set" : "clear");
+
+ if (ep->num == 0) {
+ if (value)
+ ep->mqdev->ep0state = EP0_STALL;
+ else
+ return -EINVAL;
+ }
+
+ if (value) {
+ ep->regs [0] |= MQ_UDC_STALL;
+ if (ep->num == 0)
+ ep->regs [2] |= MQ_UDC_STALL;
+ ep->stopped = 1;
+ } else {
+ ep->regs [0] &= ~MQ_UDC_STALL;
+ if (ep->num == 0)
+ ep->regs [2] &= ~MQ_UDC_STALL;
+ ep->stopped = 0;
+ }
+
+ return 0;
+}
+
+static int mq_fifo_status(struct usb_ep *_ep)
+{
+ struct mq_ep *ep;
+ u32 status;
+
+ if (!_ep)
+ return -ENODEV;
+ ep = container_of (_ep, struct mq_ep, ep);
+
+ if (ep->num > 1)
+ return -EOPNOTSUPP;
+
+ if (ep->num == 0 && ep->is_in)
+ status = ep->regs [3];
+ else
+ status = ep->regs [1];
+
+ return (MQ_UDC_FIFO_DEPTH - MQ_UDC_FIFO (status)) * 4;
+}
+
+static void mq_fifo_flush(struct usb_ep *_ep)
+{
+ struct mq_ep *ep;
+
+ if (!_ep)
+ return;
+ ep = container_of (_ep, struct mq_ep, ep);
+
+ ep->regs [0] |= MQ_UDC_CLEAR_FIFO;
+ ep->regs [0] &= ~MQ_UDC_CLEAR_FIFO;
+ if (ep->num == 0) {
+ /* For endpoint 0, clear also RX FIFO */
+ ep->regs [2] |= MQ_UDC_CLEAR_FIFO;
+ ep->regs [2] &= ~MQ_UDC_CLEAR_FIFO;
+ }
+ return;
+}
+
+static struct usb_ep_ops mq_ep_ops = {
+ .enable = mq_ep_enable,
+ .disable = mq_ep_disable,
+
+ .alloc_request = mq_alloc_request,
+ .free_request = mq_free_request,
+
+ .alloc_buffer = mq_alloc_buffer,
+ .free_buffer = mq_free_buffer,
+
+ .queue = mq_queue,
+ .dequeue = mq_dequeue,
+
+ .set_halt = mq_set_halt,
+ .fifo_status = mq_fifo_status,
+ .fifo_flush = mq_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int mq_get_frame(struct usb_gadget *gadget)
+{
+ struct mqudc *mqdev = container_of (gadget, struct mqudc, gadget);
+ return mqdev->base->regs->UDC.frame_number & MQ_UDC_FRAME_MASK;
+}
+
+int mq_wakeup (struct usb_gadget *gadget)
+{
+ struct mqudc *mqdev = container_of (gadget, struct mqudc, gadget);
+ mqdev->base->regs->UDC.control |= MQ_UDC_WAKEUP_USBHOST;
+ return 0;
+}
+
+int mq_set_selfpowered (struct usb_gadget *gadget, int value)
+{
+ if (value)
+ return 0;
+ return -EOPNOTSUPP;
+}
+
+static const struct usb_gadget_ops mq_ops = {
+ .get_frame = mq_get_frame,
+ .wakeup = mq_wakeup,
+ .set_selfpowered = mq_set_selfpowered,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void mq_reinit (struct mqudc *mqdev)
+{
+ volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
+ unsigned i;
+
+ mqdev->ep0state = EP0_IDLE;
+
+ for (i = 0; i < 4; i++) {
+ struct mq_ep *ep = &mqdev->ep[i];
+
+ INIT_LIST_HEAD (&ep->list);
+ mq_ep_reset(ep);
+ }
+
+ regs->UDC.control = (regs->UDC.control & 0xffff) |
+ MQ_UDC_IEN_EP0_RX |
+ MQ_UDC_IEN_GLOBAL_SUSPEND |
+ MQ_UDC_IEN_WAKEUP | MQ_UDC_IEN_RESET |
+ MQ_UDC_SUSPEND_ENABLE |
+ MQ_UDC_REMOTEHOST_WAKEUP_ENABLE;
+
+ /* Set endpoint address to zero after reset */
+ regs->UDC.address = 0;
+ /* Drop endpoint status bits, whatever they were */
+ regs->UDC.ep0txstatus = 0xff;
+ regs->UDC.ep0rxstatus = 0xff;
+}
+
+static void mq_reset(struct mqudc *mqdev)
+{
+ volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
+
+ /* Set D+ to Hi-Z (no pullup) */
+ mach_info->udc_command (MQ11XX_UDC_CMD_DISCONNECT);
+
+ /* Initialize device registers */
+ regs->UDC.control = 0;
+ regs->UDC.address = 0;
+ regs->UDC.ep0txcontrol = MQ_UDC_CLEAR_FIFO;
+ regs->UDC.ep0txcontrol = 0;
+ regs->UDC.ep0rxcontrol = MQ_UDC_CLEAR_FIFO;
+ regs->UDC.ep0rxcontrol = 0;
+ regs->UDC.ep1control = 0;
+ regs->UDC.ep2control = 0;
+ regs->UDC.ep3control = 0;
+ regs->UDC.intstatus = 0xffffffff;
+ regs->UDC.dmatxcontrol = 0;
+ regs->UDC.dmarxcontrol = 0;
+}
+
+static void mq_enable(struct mqudc *mqdev)
+{
+ mq_reset (mqdev);
+ mq_reinit (mqdev);
+
+ /* Enable 1.5k D+ pullup resistor */
+ mach_info->udc_command (MQ11XX_UDC_CMD_CONNECT);
+}
+
+/*---------------------------------------------------------------------------*/
+/* USB Gadget exported API */
+/*---------------------------------------------------------------------------*/
+
+static struct mqudc *the_controller;
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct mqudc *mqdev = the_controller;
+ int rc;
+
+ if (!driver
+ || driver->speed != USB_SPEED_FULL
+ || !driver->bind
+ || !driver->unbind
+ || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+ if (!mqdev)
+ return -ENODEV;
+ if (mqdev->driver)
+ return -EBUSY;
+
+ /* hook up the driver */
+ driver->driver.bus = 0;
+ mqdev->driver = driver;
+ mqdev->gadget.dev.driver = &driver->driver;
+ rc = driver->bind (&mqdev->gadget);
+ if (rc) {
+ debug ("error %d while binding to driver %s\n",
+ rc, driver->driver.name);
+ mqdev->driver = 0;
+ mqdev->gadget.dev.driver = 0;
+ return rc;
+ }
+
+ /* enable host detection and ep0; and we're ready
+ * for set_configuration as well as eventual disconnect.
+ */
+ mq_enable (mqdev);
+
+ info ("Registered gadget driver '%s'\n", driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void
+stop_activity(struct mqudc *mqdev, struct usb_gadget_driver *driver)
+{
+ unsigned i;
+
+ mq_reset (mqdev);
+ for (i = 0; i < 4; i++)
+ mq_ep_finish(&mqdev->ep [i], -ESHUTDOWN);
+ if (driver) {
+ spin_unlock(&mqdev->lock);
+ driver->disconnect(&mqdev->gadget);
+ spin_lock(&mqdev->lock);
+ }
+
+ if (mqdev->driver)
+ mq_enable(mqdev);
+}
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct mqudc *mqdev = the_controller;
+ unsigned long flags;
+
+ if (!mqdev)
+ return -ENODEV;
+ if (!driver || driver != mqdev->driver)
+ return -EINVAL;
+
+ spin_lock_irqsave(&mqdev->lock, flags);
+ mqdev->driver = 0;
+ stop_activity(mqdev, driver);
+ spin_unlock_irqrestore(&mqdev->lock, flags);
+
+ driver->unbind(&mqdev->gadget);
+
+ debug ("unregistered driver '%s'\n", driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*---------------------------------------------------------------------------*/
+/* IRQ handling */
+/*---------------------------------------------------------------------------*/
+
+static void ep01_transmit (struct mqudc *mqdev, struct mq_ep *ep)
+{
+ u32 txstatus = ep->regs [1];
+
+ /* Clear the status of last packet sent */
+ ep->regs [1] = 0xff;
+
+ /* If no ACK for last sent packet, or the End Of Data flag is
+ * still set, don't disturb the transmitter - it's not ready yet.
+ */
+ if (!(txstatus & MQ_UDC_ACK) &&
+ (ep->regs [0] & MQ_UDC_TX_EDT)) {
+ debug ("No ACK and EDT is still set\n");
+ return;
+ }
+
+ if (MQ_UDC_FIFO (txstatus) < MQ_UDC_FIFO_DEPTH) {
+ debug ("TX FIFO not empty yet\n");
+ return;
+ }
+
+ if ((ep->num == 0) && (mqdev->ep0state == EP0_STALL)) {
+ ep->regs [0] |= MQ_UDC_STALL;
+ return;
+ }
+
+ mq_tx_fifo (mqdev, ep, 0);
+}
+
+static void ep0_receive (struct mqudc *mqdev)
+{
+ volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
+ struct mq_ep *ep = &mqdev->ep[0];
+ struct usb_ctrlrequest *ctrl;
+ u32 rxstatus = regs->UDC.ep0rxstatus;
+ u32 rxbuff [4];
+ int i, count;
+
+ /* Acknowledge all status bits, we already got the status */
+ regs->UDC.ep0rxstatus = 0xff;
+
+ /* If the data is broken, ignore it - the hardware already sent a NAK
+ * if the packet is not a SETUP packet, and won't generate an
+ * interrupt if the packet is a broken SETUP packet...
+ */
+ if ((rxstatus & (MQ_UDC_ERR | MQ_UDC_TIMEOUT | MQ_UDC_FIFO_OVERRUN | MQ_UDC_ACK)) != MQ_UDC_ACK) {
+ regs->UDC.ep0rxcontrol = MQ_UDC_CLEAR_FIFO;
+ regs->UDC.ep0rxcontrol = 0;
+ return;
+ }
+
+ /* Get the data from RX FIFO */
+ count = MQ_UDC_FIFO (rxstatus);
+ for (i = 0; i < count; i++)
+ /* According to docs, lowest bits are always earlier bytes */
+ rxbuff [i] = cpu_to_le32 (regs->UDC.ep0rxfifo);
+ if (count) {
+ i = MQ_UDC_RX_VALID_BYTES (rxstatus);
+ if (i == 0)
+ i = 4;
+ count = (count - 1) * 4 + i;
+ }
+
+ /* Set output DATA0/DATA1 PID from the PID of incoming packet */
+ mqdev->toggle = (rxstatus & MQ_UDC_RX_PID_DATA1) ? 0 : MQ_UDC_TX_PID_DATA1;
+
+ if (!(rxstatus & MQ_UDC_RX_TOKEN_SETUP)) {
+ struct mq_request *req;
+
+ if (unlikely (ep->is_in))
+ return;
+ if (unlikely (list_empty (&ep->list)))
+ return;
+
+ req = list_entry (ep->list.next, struct mq_request, queue);
+
+ if (req->req.actual + count > req->req.length) {
+ debug ("%s: buffer overflow, got %d, max %d\n",
+ ep->ep.name, count, req->req.length);
+ req->req.status = -EOVERFLOW;
+ count = req->req.length - req->req.actual;
+ }
+
+ memcpy (req->req.buf + req->req.actual, &rxbuff, count);
+ req->req.actual += count;
+
+ /* If packet is short, or the transfer is complete, finish */
+ if (count < ep->ep.maxpacket ||
+ req->req.actual >= req->req.length) {
+ ep->stopped = 1;
+ mqdev->ep0state = EP0_IDLE;
+ mq_done_request (ep, req, 0);
+ }
+
+ return;
+ }
+
+ ctrl = (struct usb_ctrlrequest *)&rxbuff;
+
+ le16_to_cpus (&ctrl->wValue);
+ le16_to_cpus (&ctrl->wIndex);
+ le16_to_cpus (&ctrl->wLength);
+
+ mq_ep_finish (ep, 0);
+
+ if (likely (ctrl->bRequestType & USB_DIR_IN)) {
+ ep->is_in = 1;
+ mqdev->ep0state = EP0_IN;
+ } else {
+ ep->is_in = 0;
+ mqdev->ep0state = EP0_OUT;
+ }
+
+#ifdef USB_DEBUG_TRACE
+ debug ("SETUP %02x.%02x v%04x i%04x l%04x\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+#endif
+
+ /* Handle some SETUP packets ourselves */
+ switch (ctrl->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ if (ctrl->bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+
+ debug ("SET_ADDRESS (%d)\n", ctrl->wValue);
+
+ regs->UDC.address = ctrl->wValue;
+ /* Reply with a ZLP on next IN token */
+ ep->quick_buff_bytes = 1;
+ mq_tx_fifo (mqdev, ep, 1);
+ return;
+
+ case USB_REQ_GET_STATUS: {
+ struct mq_ep *qep;
+ int epn = ctrl->wIndex & ~USB_DIR_IN;
+
+ /* hw handles device and interface status */
+ if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_ENDPOINT) ||
+ ctrl->wLength > 2 || epn > 3)
+ break;
+
+ debug ("GET_STATUS (%x)\n", ctrl->wIndex);
+
+ qep = &mqdev->ep [epn];
+ if (qep->is_in != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0))
+ break;
+
+ /* Return status on next IN token */
+ ep->quick_buff_bytes = 2;
+ ep->quick_buff = cpu_to_le32 (qep->stopped);
+ mq_tx_fifo (mqdev, ep, 1);
+ return;
+ }
+
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+ struct mq_ep *qep;
+
+ debug ("SET/CLR_FEATURE (%d)\n", ctrl->wValue);
+
+ /* Support only HALT feature */
+ if (ctrl->wValue != 0 ||
+ ctrl->wLength != 0 ||
+ ctrl->wIndex > 3)
+ goto ep0_stall;
+
+ qep = &mqdev->ep [ctrl->wIndex];
+ if (ctrl->bRequest == USB_REQ_SET_FEATURE) {
+ qep->regs [0] |= MQ_UDC_STALL;
+ if (ctrl->wIndex == 0)
+ qep->regs [2] |= MQ_UDC_STALL;
+ } else {
+ qep->regs [0] &= ~MQ_UDC_STALL;
+ if (ctrl->wIndex == 0)
+ qep->regs [2] &= ~MQ_UDC_STALL;
+ }
+
+ /* Reply with a ZLP on next IN token */
+ ep->quick_buff_bytes = 1;
+ mq_tx_fifo (mqdev, ep, 1);
+ return;
+ }
+ break;
+ }
+
+ if (!mqdev->driver)
+ return;
+
+ /* delegate everything to the gadget driver.
+ * it may respond after this irq handler returns.
+ */
+ spin_unlock (&mqdev->lock);
+ i = mqdev->driver->setup (&mqdev->gadget, ctrl);
+ spin_lock (&mqdev->lock);
+
+ if (unlikely (i < 0)) {
+ep0_stall:
+#ifdef USB_DEBUG_TRACE
+ debug("req %02x.%02x protocol STALL\n",
+ ctrl->bRequestType, ctrl->bRequest);
+#endif
+ ep->stopped = 1;
+ mqdev->ep0state = EP0_STALL;
+ regs->UDC.ep0txcontrol |= MQ_UDC_STALL;
+ }
+}
+
+static irqreturn_t mq_dev_irq(int irq, void *data, struct pt_regs *r)
+{
+ struct mqudc *mqdev = (struct mqudc *)data;
+ volatile struct mediaq11xx_regs *regs = mqdev->base->regs;
+ int irq_handled = 0;
+ u32 mask;
+
+ spin_lock(&mqdev->lock);
+
+ while ((mask = regs->UDC.intstatus) != 0) {
+ irq_handled = 1;
+
+ /* Sort in decreasing importance order */
+ if (likely (mask & MQ_UDC_INT_DMA_TX)) {
+ /* EP2 DMA end-of-transmit buffer reached */
+ regs->UDC.intstatus = MQ_UDC_INT_DMA_TX;
+ mq_tx_dma (mqdev, &mqdev->ep [2]);
+ } else if (likely (mask & (MQ_UDC_INT_DMA_RX | MQ_UDC_INT_DMA_RX_EOT))) {
+ /* EP3 DMA end-of-receive buffer reached */
+ regs->UDC.intstatus = mask & MQ_UDC_INT_DMA_RX;
+ mq_rx_dma (mqdev, &mqdev->ep [3]);
+ } else if (likely (mask & MQ_UDC_INT_EP0_RX)) {
+ /* EP0 packet received */
+ regs->UDC.intstatus = MQ_UDC_INT_EP0_RX;
+ ep0_receive (mqdev);
+ } else if (likely (mask & MQ_UDC_INT_EP0_TX)) {
+ /* EP0 packet transmitted (FIFO empty) */
+ regs->UDC.intstatus = MQ_UDC_INT_EP0_TX;
+ ep01_transmit (mqdev, &mqdev->ep [0]);
+ } else if (likely (mask & MQ_UDC_INT_EP1_TX)) {
+ /* EP1 packet transmitted (FIFO empty) */
+ regs->UDC.intstatus = MQ_UDC_INT_EP1_TX;
+ ep01_transmit (mqdev, &mqdev->ep [1]);
+ } else if (unlikely (mask & MQ_UDC_INT_RESET)) {
+ debug ("reset\n");
+ /* Device reset */
+ regs->UDC.intstatus = MQ_UDC_INT_RESET;
+ mq_reinit (mqdev);
+ } else if (unlikely (mask & MQ_UDC_INT_WAKEUP)) {
+ regs->UDC.intstatus = MQ_UDC_INT_WAKEUP;
+
+ if (mqdev->ep0state == EP0_SUSPEND) {
+ debug ("resume\n");
+ mqdev->ep0state = EP0_IDLE;
+ if (mqdev->driver && mqdev->driver->resume) {
+ spin_unlock (&mqdev->lock);
+ mqdev->driver->resume (&mqdev->gadget);
+ spin_lock (&mqdev->lock);
+ }
+ }
+ } else if (unlikely (mask & MQ_UDC_INT_GLOBAL_SUSPEND)) {
+ regs->UDC.intstatus = MQ_UDC_INT_GLOBAL_SUSPEND;
+
+ if (mqdev->ep0state != EP0_DISCONNECT &&
+ mqdev->ep0state != EP0_SUSPEND) {
+ debug ("suspend\n");
+ mqdev->ep0state = EP0_SUSPEND;
+ if (mqdev->driver && mqdev->driver->suspend) {
+ spin_unlock (&mqdev->lock);
+ mqdev->driver->suspend (&mqdev->gadget);
+ spin_lock (&mqdev->lock);
+ }
+ }
+ } else {
+ /*
+ * MQ_UDC_INT_SOF
+ * MQ_UDC_INT_EP2_TX_EOT
+ * MQ_UDC_INT_EP3_RX_EOT
+ */
+ debug ("unhandled mask: %08x\n", mask);
+ regs->UDC.intstatus = mask;
+ }
+ }
+
+ spin_unlock(&mqdev->lock);
+
+ return IRQ_RETVAL (irq_handled);
+}
+
+static irqreturn_t mq_wup_irq(int irq, void *data, struct pt_regs *r)
+{
+ debug ("WakeUP IRQ\n");
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef MQ_IRQ_MULTIPLEX
+static struct mq_irq_desc
+{
+ char *desc;
+ int delta;
+ irqreturn_t (*handler) (int, void *, struct pt_regs *);
+} mq_irq_desc [] =
+{
+ { "MediaQ UDC", IRQ_MQ_UDC, mq_dev_irq },
+ { "MediaQ UDC wake-up", IRQ_MQ_UDC_WAKE_UP, mq_wup_irq }
+};
+#endif
+
+static int mq_remove(struct device *dev);
+
+static void gadget_release(struct device *dev)
+{
+ struct mqudc *mqdev = dev_get_drvdata (dev);
+ kfree (mqdev);
+}
+
+static int mq_probe (struct device *dev)
+{
+ static char *names [] = { "ep0", "ep1in-int", "ep2in", "ep3out" };
+ struct mqudc *mqdev;
+ struct mediaq11xx_base *base = (struct mediaq11xx_base *)dev->platform_data;
+ volatile struct mediaq11xx_regs *regs = base->regs;
+ int i;
+
+ if (!base)
+ return -ENXIO;
+#ifdef MQ_IRQ_MULTIPLEX
+ if (!base->irq_base)
+ return -ENXIO;
+#endif
+
+ mqdev = kmalloc (sizeof (*mqdev), GFP_KERNEL);
+ if (!mqdev)
+ return -ENOMEM;
+
+ memset(mqdev, 0, sizeof (*mqdev));
+ mqdev->base = base;
+ spin_lock_init(&mqdev->lock);
+ mqdev->gadget.ops = &mq_ops;
+
+ strcpy(mqdev->gadget.dev.bus_id, "gadget");
+ mqdev->gadget.ep0 = &mqdev->ep [0].ep;
+ mqdev->gadget.dev.parent = dev;
+ mqdev->gadget.name = driver_name;
+ mqdev->gadget.dev.release = gadget_release;
+ mqdev->gadget.speed = USB_SPEED_FULL;
+
+ /* Initialize UDC */
+ base->set_power (base, MEDIAQ_11XX_UDC_DEVICE_ID, 1);
+ /* Enable UDC transceiver */
+ base->regs->DC.config_4 |= MQ_CONFIG_UDC_TRANSCEIVER_ENABLE;
+ mqdev->enabled = 1;
+
+ dev_set_drvdata(dev, mqdev);
+
+ /* Initialize endpoint list */
+ INIT_LIST_HEAD (&mqdev->gadget.ep_list);
+ for (i = 0; i < 4; i++) {
+ struct mq_ep *ep = &mqdev->ep[i];
+
+ ep->num = i;
+ ep->ep.name = names[i];
+ ep->mqdev = mqdev;
+ ep->ep.ops = &mq_ep_ops;
+ /* Endpoint 0 is not on the UDC's list of endpoints */
+ if (i)
+ list_add_tail (&ep->ep.ep_list, &mqdev->gadget.ep_list);
+ }
+
+ mqdev->ep[0].ep.maxpacket = MQ_EP01_MAXPACKET;
+
+ mqdev->ep[0].is_in = 0;
+ mqdev->ep[1].is_in = 1;
+ mqdev->ep[2].is_in = 1;
+ mqdev->ep[3].is_in = 0;
+
+ mqdev->ep[0].regs = ®s->UDC.ep0txcontrol;
+ mqdev->ep[1].regs = ®s->UDC.ep1control;
+ mqdev->ep[2].regs = ®s->UDC.ep2control;
+ mqdev->ep[3].regs = ®s->UDC.ep3control;
+
+ mqdev->ep[2].dmaregs = ®s->UDC.dmatxcontrol;
+ mqdev->ep[3].dmaregs = ®s->UDC.dmarxcontrol;
+
+ /* Grab all used IRQs */
+#ifdef MQ_IRQ_MULTIPLEX
+ for (i = 0; i < ARRAY_SIZE (mq_irq_desc); i++) {
+ int irqn = base->irq_base + mq_irq_desc [i].delta;
+ debug ("Requesting IRQ %d for %s\n", irqn, mq_irq_desc [i].desc);
+ if (request_irq (irqn, mq_irq_desc [i].handler, SA_INTERRUPT,
+ mq_irq_desc [i].desc, mqdev)) {
+ printk (KERN_ERR "%s: failed to request %s IRQ %d\n",
+ driver_name, mq_irq_desc [i].desc, irqn);
+ mqdev->got_irq = i - 1;
+ mq_remove (dev);
+ return -EBUSY;
+ }
+ }
+ mqdev->got_irq = ARRAY_SIZE (mq_irq_desc);
+#endif
+
+ /* Init the device to known state */
+ mq_reset (mqdev);
+ mq_reinit (mqdev);
+
+ /* Allocate two 4k buffers (used in turn) in MediaQ RAM for
+ every DMA-aware endpoint for MediaQ internal DMA transfers */
+ for (i = 2; i <= 3; i++) {
+ int buffsize = 2 * ((i == 2) ? txbs : rxbs);
+ mqdev->ep[i].dmabuff = base->alloc (base, buffsize, 0);
+ if (mqdev->ep[i].dmabuff == (u32)-1) {
+ printk (KERN_ERR "%s: cannot allocate %d bytes in onboard RAM\n",
+ driver_name, buffsize);
+ mq_remove (dev);
+ return -ENOMEM;
+ }
+ else
+ debug ("Allocated %d bytes in MediaQ RAM, addr = 0x%x\n",
+ buffsize, mqdev->ep[i].dmabuff);
+ }
+
+ device_register (&mqdev->gadget.dev);
+ mqdev->registered = 1;
+
+ the_controller = mqdev;
+
+ info ("MediaQ 11xx UDC driver successfully initialized\n");
+ return 0;
+}
+
+static int mq_remove(struct device *dev)
+{
+ struct mqudc *mqdev = dev_get_drvdata (dev);
+ int i;
+
+ debug ("\n");
+
+ if (!mqdev)
+ return 0;
+
+ /* start with the driver above us */
+ if (mqdev->driver) {
+ /* should have been done already by driver model core */
+ debug ("driver '%s' is still registered\n",
+ mqdev->driver->driver.name);
+ usb_gadget_unregister_driver(mqdev->driver);
+ }
+
+ if (mqdev->base)
+ mq_reset(mqdev);
+
+ for (i = 3; i >= 2; i--)
+ if (mqdev->ep[i].dmabuff) {
+ int buffsize = 2 * ((i == 2) ? txbs : rxbs);
+ debug ("Freeing MediaQ RAM at 0x%x, size %d\n",
+ mqdev->ep[i].dmabuff, buffsize);
+ mqdev->base->free (mqdev->base, mqdev->ep[i].dmabuff,
+ buffsize);
+ }
+
+#ifdef MQ_IRQ_MULTIPLEX
+ for (i = ((int)mqdev->got_irq) - 1; i >= 0; i--) {
+ int irqn = mqdev->base->irq_base + mq_irq_desc [i].delta;
+ debug ("Freeing IRQ %d\n", irqn);
+ free_irq(irqn, mqdev);
+ }
+#endif
+
+ if (mqdev->enabled) {
+ /* Disable UDC transceiver */
+ mqdev->base->regs->DC.config_4 &= ~MQ_CONFIG_UDC_TRANSCEIVER_ENABLE;
+ mqdev->base->set_power (mqdev->base, MEDIAQ_11XX_UDC_DEVICE_ID, 0);
+ }
+
+ if (mqdev->registered)
+ device_unregister (&mqdev->gadget.dev);
+
+ dev_set_drvdata (dev, NULL);
+ the_controller = NULL;
+
+ kfree (mqdev);
+ return 0;
+}
+
+static void mq_shutdown(struct device *dev)
+{
+ debug ("\n");
+}
+
+static int mq_suspend (struct device *dev, u32 state, u32 level)
+{
+ debug ("\n");
+
+ return 0;
+}
+
+static int mq_resume (struct device *dev, u32 level)
+{
+ debug ("\n");
+
+ return 0;
+}
+
+struct device_driver mq_platform_driver = {
+ .name = (char *) driver_name,
+ .probe = mq_probe,
+ .remove = mq_remove,
+ .shutdown = mq_shutdown,
+ .suspend = mq_suspend,
+ .resume = mq_resume
+};
+
+static int __init mq_init(void)
+{
+ return driver_register(&mq_platform_driver);
+}
+
+static void __exit mq_exit(void)
+{
+ driver_unregister (&mq_platform_driver);
+}
+
+module_init (mq_init);
+module_exit (mq_exit);
diff --git a/drivers/usb/gadget/mq11xx_udc.h b/drivers/usb/gadget/mq11xx_udc.h
new file mode 100644
--- /dev/null
+++ b/drivers/usb/gadget/mq11xx_udc.h
@@ -0,0 +1,115 @@
+/*
+ * MediaQ 11xx USB Device Controller driver
+ * Based on Goku-S UDC driver
+ *
+ * Copyright (C) 2004 Andrew Zabolotny <zap@homelink.ru>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef _MQ11XX_UDC_H
+#define _MQ11XX_UDC_H
+
+/* Public structure provided by the platform device */
+struct mq11xx_udc_mach_info {
+ int (*udc_is_connected)(void); /* do we see host? */
+ void (*udc_command)(int cmd);
+#define MQ11XX_UDC_CMD_CONNECT 0 /* let host see us */
+#define MQ11XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */
+};
+
+#ifdef __MQ11XX_INTERNAL
+
+#include "../drivers/platform/mq11xx.h"
+
+/* MediaQ UDC request structure.
+ */
+struct mq_request {
+ /* The public request structure */
+ struct usb_request req;
+ /* The chained list to link requests belonging to one endpoint */
+ struct list_head queue;
+};
+
+/* USB Endpoint structure.
+ */
+struct mq_ep {
+ /* Public endpoint structure */
+ struct usb_ep ep;
+ /* A chained list of mq_request structures */
+ struct list_head list;
+ /* Endpoint number */
+ unsigned num:8;
+ /* 1 if endpoint is INput (e.g. TX from UDC point of view) */
+ unsigned is_in:1;
+ /* 1 if endpoint is stopped */
+ unsigned stopped:1;
+ /* 1 if MediaQ RAM has been allocated for this endpoint */
+ unsigned got_mqram:1;
+ /* 1 if endpoint configured in isochronous mode */
+ unsigned iso:1;
+ /* Toggles between active buffers on DMA transfers */
+ unsigned active_buffer:1;
+ /* Number of bytes in quick_buff to send plus one */
+ unsigned quick_buff_bytes:3;
+ /* A quick buffer for small transfers
+ * (typically used during SETUP phase and for ZLP).
+ */
+ u32 quick_buff;
+ /* A pointer to MediaQ registers associated with this endpoint */
+ volatile u32 *regs;
+ /* A pointer to MediaQ registers responsible for DMA transfers */
+ volatile u32 *dmaregs;
+ /* The offset in MediaQ internal RAM of two consecutive buffers
+ * used for DMA transfers.
+ */
+ volatile u32 dmabuff;
+ /* Parent device */
+ struct mqudc *mqdev;
+ /* Endpoint descriptor */
+ const struct usb_endpoint_descriptor *desc;
+};
+
+enum ep0state {
+ EP0_DISCONNECT, /* no host */
+ EP0_SUSPEND, /* usb suspend */
+ EP0_IDLE, /* between STATUS ack and SETUP report */
+ EP0_IN, EP0_OUT, /* data stage */
+ EP0_STALL, /* something really bad happened */
+};
+
+/* The USB Device Controller structure.
+ */
+struct mqudc {
+ /* Public gadget structure */
+ struct usb_gadget gadget;
+ /* Spinlock for accessing the UDC */
+ spinlock_t lock;
+ /* All of UDC endpoints */
+ struct mq_ep ep[4];
+ /* A pointer to bound gadget driver */
+ struct usb_gadget_driver *driver;
+ /* DATA0/DATA1 toggle */
+ u32 toggle;
+
+ /* Current endpoint 0 state */
+ enum ep0state ep0state;
+
+ /* MediaQ 11xx base SoC driver public structure pointer */
+ struct mediaq11xx_base *base;
+
+ /* The following is used for device cleanup */
+
+ /* Number of IRQs requested so far */
+ unsigned got_irq:2;
+ /* 1 if the UDC is enabled */
+ unsigned enabled:1;
+ /* 1 if device has been registered with the kernel */
+ unsigned registered:1;
+};
+
+#endif
+
+#endif /* _MQ11XX_UDC_H */
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip
2005-08-02 19:20 **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip jamey
@ 2005-08-02 21:17 ` Michael Krufky
2005-08-03 21:49 ` Nick Sillik
2005-08-02 21:18 ` Greg KH
1 sibling, 1 reply; 6+ messages in thread
From: Michael Krufky @ 2005-08-02 21:17 UTC (permalink / raw)
To: jamey; +Cc: linux-kernel
jamey@crl.dec.com wrote:
>This patch adds USB gadget support for the USB peripheral controller
>on the MQ11xx graphics chip.
>
>Signed-off-by: Jamey Hicks <jamey@handhelds.org>
>Signed-off-by: Andrew Zabolotny <anpaza@mail.ru>
>
>
The email subject..... "Re: **SPAM** [PATCH 3/3] usb gadget driver for
MQ11xx graphics chip" ... Was that an accident, or did my email server
take over my headers? (i'm not running any spam filering software or
anything)
If this happened on your end, you might want to re-send that one... Some
email clients automatically filter messages containing "**SPAM**" in
subject line.
--
Michael Krufky
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip
2005-08-02 19:20 **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip jamey
2005-08-02 21:17 ` Michael Krufky
@ 2005-08-02 21:18 ` Greg KH
1 sibling, 0 replies; 6+ messages in thread
From: Greg KH @ 2005-08-02 21:18 UTC (permalink / raw)
To: jamey; +Cc: linux-kernel
3/3? I seem to be missing the other two patches...
On Tue, Aug 02, 2005 at 03:20:47PM -0400, jamey@crl.dec.com wrote:
>
> This patch adds USB gadget support for the USB peripheral controller
> on the MQ11xx graphics chip.
>
> Signed-off-by: Jamey Hicks <jamey@handhelds.org>
> Signed-off-by: Andrew Zabolotny <anpaza@mail.ru>
This should be sent to the linux-usb-devel mailing list, so the usb
gadget maintainer and other developers can review it.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip
2005-08-02 21:17 ` Michael Krufky
@ 2005-08-03 21:49 ` Nick Sillik
2005-08-04 11:48 ` Patrick McFarland
0 siblings, 1 reply; 6+ messages in thread
From: Nick Sillik @ 2005-08-03 21:49 UTC (permalink / raw)
To: mkrufky; +Cc: jamey, linux-kernel
Michael Krufky wrote:
> jamey@crl.dec.com wrote:
>
> The email subject..... "Re: **SPAM** [PATCH 3/3] usb gadget driver for
> MQ11xx graphics chip" ... Was that an accident, or did my email server
> take over my headers? (i'm not running any spam filering software or
> anything)
>
> If this happened on your end, you might want to re-send that one...
> Some email clients automatically filter messages containing "**SPAM**"
> in subject line.
>
Nope, isn't on your end. I'm seeing them too. I thought my mailserver
had gone off its rocker. I'm glad to know that others are seeing this.
Nick Sillik
n.sillik@temple.edu
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip
2005-08-03 21:49 ` Nick Sillik
@ 2005-08-04 11:48 ` Patrick McFarland
2005-08-04 12:04 ` linux-os (Dick Johnson)
0 siblings, 1 reply; 6+ messages in thread
From: Patrick McFarland @ 2005-08-04 11:48 UTC (permalink / raw)
To: Nick Sillik; +Cc: mkrufky, jamey, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1077 bytes --]
On Wednesday 03 August 2005 05:49 pm, Nick Sillik wrote:
> Michael Krufky wrote:
> > jamey@crl.dec.com wrote:
> >
> > The email subject..... "Re: **SPAM** [PATCH 3/3] usb gadget driver for
> > MQ11xx graphics chip" ... Was that an accident, or did my email server
> > take over my headers? (i'm not running any spam filering software or
> > anything)
> >
> > If this happened on your end, you might want to re-send that one...
> > Some email clients automatically filter messages containing "**SPAM**"
> > in subject line.
>
> Nope, isn't on your end. I'm seeing them too. I thought my mailserver
> had gone off its rocker. I'm glad to know that others are seeing this.
I see it too. Looks like maybe his ISP filters outgoing mail with their
anti-spam stuff?
--
Patrick "Diablo-D3" McFarland || pmcfarland@downeast.net
"Computer games don't affect kids; I mean if Pac-Man affected us as kids, we'd
all be running around in darkened rooms, munching magic pills and listening to
repetitive electronic music." -- Kristian Wilson, Nintendo, Inc, 1989
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip
2005-08-04 11:48 ` Patrick McFarland
@ 2005-08-04 12:04 ` linux-os (Dick Johnson)
0 siblings, 0 replies; 6+ messages in thread
From: linux-os (Dick Johnson) @ 2005-08-04 12:04 UTC (permalink / raw)
To: Patrick McFarland; +Cc: Nick Sillik, mkrufky, jamey, linux-kernel
On Thu, 4 Aug 2005, Patrick McFarland wrote:
> On Wednesday 03 August 2005 05:49 pm, Nick Sillik wrote:
>> Michael Krufky wrote:
>>> jamey@crl.dec.com wrote:
>>>
>>> The email subject..... "Re: **SPAM** [PATCH 3/3] usb gadget driver for
>>> MQ11xx graphics chip" ... Was that an accident, or did my email server
>>> take over my headers? (i'm not running any spam filering software or
>>> anything)
>>>
>>> If this happened on your end, you might want to re-send that one...
>>> Some email clients automatically filter messages containing "**SPAM**"
>>> in subject line.
>>
>> Nope, isn't on your end. I'm seeing them too. I thought my mailserver
>> had gone off its rocker. I'm glad to know that others are seeing this.
>
> I see it too. Looks like maybe his ISP filters outgoing mail with their
> anti-spam stuff?
>
> --
> Patrick "Diablo-D3" McFarland || pmcfarland@downeast.net
It's some idiot's "anti-porn" mail trap.
About a year ago, the aic7xxx (Adaptec SCSI driver) couldn't
be referenced without anti-porno junk dropping it on the floor.
IT idiots think that porn file-names have Xes in them. Once
they find out that they have ".jpg" in them, no more pictures
for anybody!
Cheers,
Dick Johnson
Penguin : Linux version 2.6.12 on an i686 machine (5537.79 BogoMips).
Warning : 98.36% of all statistics are fiction.
.
I apologize for the following. I tried to kill it with the above dot :
****************************************************************
The information transmitted in this message is confidential and may be privileged. Any review, retransmission, dissemination, or other use of this information by persons or entities other than the intended recipient is prohibited. If you are not the intended recipient, please notify Analogic Corporation immediately - by replying to this message or by sending an email to DeliveryErrors@analogic.com - and destroy all copies of this information, including any attachments, without reading or disclosing them.
Thank you.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2005-08-04 12:06 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-08-02 19:20 **SPAM** [PATCH 3/3] usb gadget driver for MQ11xx graphics chip jamey
2005-08-02 21:17 ` Michael Krufky
2005-08-03 21:49 ` Nick Sillik
2005-08-04 11:48 ` Patrick McFarland
2005-08-04 12:04 ` linux-os (Dick Johnson)
2005-08-02 21:18 ` Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox