r1914 - in trunk/src/host/qemu-neo1973: . hw

andrew at sita.openmoko.org andrew at sita.openmoko.org
Sun May 6 01:51:20 CEST 2007


Author: andrew
Date: 2007-05-06 01:51:17 +0200 (Sun, 06 May 2007)
New Revision: 1914

Added:
   trunk/src/host/qemu-neo1973/hw/s3c24xx_udc.c
Modified:
   trunk/src/host/qemu-neo1973/Makefile.target
   trunk/src/host/qemu-neo1973/hw/s3c.h
   trunk/src/host/qemu-neo1973/hw/s3c2410.c
   trunk/src/host/qemu-neo1973/usb-linux-gadget.c
   trunk/src/host/qemu-neo1973/vl.h
Log:
Implement S3C2410A USB Slave controller registered as a USB gadget.
Make usb-gadget endpoints synchronous independently of each other.
Don't use always the configuration 0 - a monitor interface will be needed later for choosing the desired slave configuration.


Modified: trunk/src/host/qemu-neo1973/Makefile.target
===================================================================
--- trunk/src/host/qemu-neo1973/Makefile.target	2007-05-05 09:43:19 UTC (rev 1913)
+++ trunk/src/host/qemu-neo1973/Makefile.target	2007-05-05 23:51:17 UTC (rev 1914)
@@ -382,8 +382,8 @@
 VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o max111x.o max7310.o
 VL_OBJS+= ads7846.o sd.o ide.o serial.o nand.o $(AUDIODRV) wm8750.o wm8753.o
 VL_OBJS+= s3c2410.o s3c24xx_gpio.o s3c24xx_lcd.o s3c24xx_mmci.o s3c24xx_rtc.o
-VL_OBJS+= neo1973.o pcf5060x.o jbt6k74.o modem.o
-CPPFLAGS += -DHAS_AUDIO -DHIGH_LATENCY
+VL_OBJS+= s3c24xx_udc.o neo1973.o pcf5060x.o jbt6k74.o modem.o
+CPPFLAGS += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), sh4)
 VL_OBJS+= shix.o sh7750.o sh7750_regnames.o tc58128.o

Modified: trunk/src/host/qemu-neo1973/hw/s3c.h
===================================================================
--- trunk/src/host/qemu-neo1973/hw/s3c.h	2007-05-05 09:43:19 UTC (rev 1913)
+++ trunk/src/host/qemu-neo1973/hw/s3c.h	2007-05-05 23:51:17 UTC (rev 1914)
@@ -33,6 +33,7 @@
 # define S3C_PIC_SDI	21
 # define S3C_PIC_SPI0	22
 # define S3C_PIC_UART1	23
+# define S3C_PIC_USBD	25
 # define S3C_PIC_USBH	26
 # define S3C_PIC_IIC	27
 # define S3C_PIC_UART0	28
@@ -151,6 +152,12 @@
 struct s3c_rtc_state_s *s3c_rtc_init(target_phys_addr_t base, void *pic);
 void s3c_rtc_reset(struct s3c_rtc_state_s *s);
 
+/* s3c24xx_udc.c */
+struct s3c_udc_state_s;
+struct s3c_udc_state_s *s3c_udc_init(target_phys_addr_t base, void *pic,
+                void *dma);
+void s3c_udc_reset(struct s3c_udc_state_s *s);
+
 /* s3c2410.c */
 struct s3c_spi_state_s;
 struct s3c_spi_state_s *s3c_spi_init(target_phys_addr_t base, void *pic,
@@ -174,6 +181,7 @@
     struct s3c_i2s_state_s *i2s;
     struct s3c_rtc_state_s *rtc;
     struct s3c_spi_state_s *spi;
+    struct s3c_udc_state_s *udc;
 
     /* Memory controller */
     target_phys_addr_t mc_base;

Modified: trunk/src/host/qemu-neo1973/hw/s3c2410.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/s3c2410.c	2007-05-05 09:43:19 UTC (rev 1913)
+++ trunk/src/host/qemu-neo1973/hw/s3c2410.c	2007-05-05 23:51:17 UTC (rev 1914)
@@ -800,7 +800,7 @@
 
     elapsed = muldiv64(qemu_get_clock(vm_clock) - s->timer[tm].reload,
                     s->timer[tm].divider, ticks_per_sec);
-    if (elapsed > s->timer[tm].count)	/* unlikely() */
+    if (unlikely(elapsed > s->timer[tm].count))
         return s->timer[tm].count;
 
     return s->timer[tm].count - elapsed;
@@ -2157,6 +2157,7 @@
     s3c_i2s_reset(s->i2s);
     s3c_rtc_reset(s->rtc);
     s3c_spi_reset(s->spi);
+    s3c_udc_reset(s->udc);
     s3c_clkpwr_reset(s);
     s3c_nand_reset(s);
     for (i = 0; s3c2410_uart[i].base; i ++)
@@ -2212,7 +2213,8 @@
 
     s->timers = s3c_timers_init(0x51000000, s->pic, s->dma);
 
-    /* USBDevice at 0x52000000 */
+    s->udc = s3c_udc_init(0x52000000, s->pic, s->dma);
+
     /* Watchdog at 0x53000000 */
 
     s->i2c = s3c_i2c_init(0x54000000, s->pic);

Added: trunk/src/host/qemu-neo1973/hw/s3c24xx_udc.c
===================================================================
--- trunk/src/host/qemu-neo1973/hw/s3c24xx_udc.c	2007-05-05 09:43:19 UTC (rev 1913)
+++ trunk/src/host/qemu-neo1973/hw/s3c24xx_udc.c	2007-05-05 23:51:17 UTC (rev 1914)
@@ -0,0 +1,678 @@
+/*
+ * Samsung S3C2410A USB Device Controller emulation.
+ *
+ * Copyright (c) 2007 OpenMoko, Inc.
+ * Author: Andrzej Zaborowski <andrew at openedhand.com>
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include "vl.h"
+
+#define S3C_USB_FIFO_LEN	4096
+
+struct s3c_udc_state_s {
+    target_phys_addr_t base;
+    USBDevice dev;
+    void *pic;
+    void *dma;
+
+    /* Use FIFOs big enough to hold entire packets, just don't report
+     * lengths greater than 16 and 64 bytes for EP0 and EP1-4 respectively.  */
+    struct {
+        int start, len;
+        uint8_t fifo[S3C_USB_FIFO_LEN];
+        USBPacket *packet;
+
+        uint8_t csr;
+        uint8_t maxpacket;
+    } ep0;
+
+#define S3C_EPS 5
+    struct {
+        int start, len;
+        uint8_t fifo[S3C_USB_FIFO_LEN];
+        USBPacket *packet;
+        int packet_len;
+
+        uint8_t in_csr[2];
+        uint8_t out_csr[2];
+        uint8_t maxpacket;
+
+        uint8_t control;
+        uint8_t unit_cnt;
+        uint8_t fifo_cnt;
+        uint32_t dma_size;
+    } ep1[S3C_EPS - 1];
+
+    uint8_t index;
+    uint8_t power;
+    uint8_t ep_intr;
+    uint8_t ep_mask;
+    uint8_t usb_intr;
+    uint8_t usb_mask;
+    uint16_t frame;
+    uint8_t address;
+};
+
+void s3c_udc_reset(struct s3c_udc_state_s *s)
+{
+    int i;
+    s->address = 0x00;
+    s->power = 0x00;
+    s->ep_intr = 0x00;
+    s->ep_mask = (1 << S3C_EPS) - 1;
+    s->usb_intr = 0x00;
+    s->usb_mask = 0x06;
+    s->frame = 0x0000;
+    s->index = 0x00;
+    s->ep0.len = 0;
+    s->ep0.csr = 0x00;
+    s->ep0.maxpacket = 0x01;
+    s->ep0.packet = 0;
+    for (i = 0; i < S3C_EPS - 1; i ++) {
+        s->ep1[i].len = 0;
+        s->ep1[i].in_csr[0] = 0;
+        s->ep1[i].in_csr[1] = 0;
+        s->ep1[i].out_csr[0] = 0;
+        s->ep1[i].out_csr[1] = 0;
+        s->ep1[i].maxpacket = 0x01;
+        s->ep1[i].control = 0x00;
+        s->ep1[i].unit_cnt = 0x00;
+        s->ep1[i].fifo_cnt = 0x00;
+        s->ep1[i].dma_size = 0;
+        s->ep1[i].packet = 0;
+    }
+}
+
+static void s3c_udc_interrupt(struct s3c_udc_state_s *s, int ep)
+{
+    if (ep >= 0)
+        s->ep_intr |= 1 << ep;
+    s->ep_intr &= s->ep_mask;
+    s->usb_intr &= s->usb_mask;
+    pic_set_irq_new(s->pic, S3C_PIC_USBD, s->ep_intr | s->usb_intr);
+}
+
+static void s3c_udc_queue_packet(uint8_t *dst, uint8_t *src,
+                int fstart, int *flen, int len)
+{
+    int chunk;
+    fstart += *flen;
+    *flen += len;
+    while (len) {
+        fstart &= S3C_USB_FIFO_LEN - 1;
+        chunk = MIN(len, S3C_USB_FIFO_LEN - fstart);
+        memcpy(dst + fstart, src, chunk);
+        len -= chunk;
+        src += chunk;
+        fstart += chunk;
+    }
+}
+
+static void s3c_udc_dequeue_packet(uint8_t *dst, uint8_t *src,
+                int *fstart, int *flen)
+{
+    int chunk;
+    while (*flen) {
+        chunk = MIN(*flen, S3C_USB_FIFO_LEN - *fstart);
+        memcpy(dst, src + *fstart, chunk);
+        *flen -= chunk;
+        dst += chunk;
+        *fstart += chunk;
+        *fstart &= S3C_USB_FIFO_LEN - 1;
+    }
+}
+
+static void s3c_udc_ep0_rdy(struct s3c_udc_state_s *s, uint8_t value)
+{
+    USBPacket *packet = s->ep0.packet;
+    if (packet) {
+        if (s->ep0.len < packet->len &&
+                        !(value & (1 << 3))) {		/* DATA_END */
+            s->ep0.csr &= ~(1 << 1);			/* IN_PKT_RDY */
+            s3c_udc_interrupt(s, 0);
+            return;
+        }
+
+        if (value & (1 << 1)) {
+            packet->len = s->ep0.len;
+            s3c_udc_dequeue_packet(packet->data, s->ep0.fifo,
+                            &s->ep0.start, &s->ep0.len);
+        } else
+            packet->len = 0;
+
+        /* Signal completion of IN token */
+        s->ep0.csr &= ~(1 << 3);
+        s->ep0.packet = 0;
+        packet->complete_cb(packet, packet->complete_opaque);
+    }
+}
+
+static void s3c_udc_ep1_rdy(struct s3c_udc_state_s *s, int ep, uint8_t value)
+{
+    USBPacket *packet = s->ep1[ep].packet;
+    if (packet) {
+        /* HACK: there's no simple (timing independent) way to know when
+         * a packet ends.  We will terminate packets only when the OS
+         * writes less than 64 bytes (the FIFO size), thus the OS has to
+         * make a final zero length write if the packet is 64 multiple
+         * sized.  */
+        if (!((s->ep1[ep].len - s->ep1[ep].packet_len) & 63) &&
+                        (s->ep1[ep].len > s->ep1[ep].packet_len ||
+                         s->ep1[ep].len == 0)) {
+            s->ep1[ep].packet_len = s->ep1[ep].len;
+            s->ep1[ep].in_csr[0] &= ~(1 << 0);		/* IN_PKT_RDY */
+            s->ep1[ep].in_csr[0] &= ~(1 << 3);		/* FIFO_FLUSH */
+            s3c_udc_interrupt(s, ep + 1);
+            return;
+        }
+
+        /* TODO: check that packet->len >= s->ep1[ep].len */
+        packet->len = s->ep1[ep].len;
+        s3c_udc_dequeue_packet(packet->data, s->ep1[ep].fifo,
+                        &s->ep1[ep].start, &s->ep1[ep].len);
+
+        /* Signal completion of IN token */
+        s->ep1[ep].packet = 0;
+        s->ep1[ep].packet_len = 0;
+        packet->complete_cb(packet, packet->complete_opaque);
+    }
+}
+
+#define S3C_FUNC_ADDR	0x140	/* Function address register */
+#define S3C_PWR		0x144	/* Power management register */
+#define S3C_EP_INT	0x148	/* Endpoint interrupt register */
+#define S3C_USB_INT	0x158	/* USB interrupt register */
+#define S3C_EP_INT_EN	0x15c	/* Endpoint interrupt enable register */
+#define S3C_USB_INT_EN	0x16c	/* USB interrupt enable register */
+#define S3C_FRAME_NUM1	0x170	/* Frame number 1 register */
+#define S3C_FRAME_NUM2	0x174	/* Frame number 2 register */
+#define S3C_INDEX	0x178	/* Index register */
+#define S3C_MAXP	0x180	/* MAX packet register */
+#define S3C_IN_CSR1	0x184	/* EP In control status register 1 */
+#define S3C_IN_CSR2	0x188	/* EP In control status register 2 */
+#define S3C_OUT_CSR1	0x190	/* EP Out control status register 1 */
+#define S3C_OUT_CSR2	0x194	/* EP Out control status register 2 */
+#define S3C_FIFO_CNT1	0x198	/* EP Out write count register 1 */
+#define S3C_FIFO_CNT2	0x19c	/* EP Out write count register 2 */
+#define S3C_EP0_FIFO	0x1c0	/* Endpoint0 FIFO register */
+#define S3C_EP1_FIFO	0x1c4	/* Endpoint1 FIFO register */
+#define S3C_EP2_FIFO	0x1c8	/* Endpoint2 FIFO register */
+#define S3C_EP3_FIFO	0x1cc	/* Endpoint3 FIFO register */
+#define S3C_EP4_FIFO	0x1d0	/* Endpoint4 FIFO register */
+
+static const target_phys_addr_t s3c_udc_ep_reg[S3C_EPS] = {
+    0x000, 0x200, 0x218, 0x240, 0x258
+};
+#define S3C_EP_DMA_CON		0x00	/* DMA control register */
+#define S3C_EP_DMA_UNIT		0x04	/* DMA unit counter register */
+#define S3C_EP_DMA_FIFO		0x08	/* DMA FIFO counter register */
+#define S3C_EP_DMA_TTC_L	0x0c	/* DMA transfer counter low-byte */
+#define S3C_EP_DMA_TTC_M	0x10	/* DMA transfer counter middle-byte */
+#define S3C_EP_DMA_TTC_H	0x14	/* DMA transfer counter high-byte */
+
+static uint32_t s3c_udc_read(void *opaque, target_phys_addr_t addr)
+{
+    struct s3c_udc_state_s *s = (struct s3c_udc_state_s *) opaque;
+    int ep = S3C_EPS;
+    uint8_t ret = 0;
+    addr -= s->base;
+    while (addr < s3c_udc_ep_reg[-- ep]);
+    addr -= s3c_udc_ep_reg[ep];
+
+    switch (addr) {
+    case S3C_FUNC_ADDR:
+        return s->address;
+    case S3C_PWR:
+        return s->power;
+    case S3C_EP_INT:
+        return s->ep_intr;
+    case S3C_USB_INT:
+        return s->usb_intr;
+    case S3C_EP_INT_EN:
+        return s->ep_mask;
+    case S3C_USB_INT_EN:
+        return s->usb_mask & ~(1 << 1);
+    case S3C_FRAME_NUM1:
+        return s->frame & 0xff;
+    case S3C_FRAME_NUM2:
+        return s->frame >> 8;
+    case S3C_INDEX:
+        return s->index;
+
+    case S3C_IN_CSR1:
+        if (s->index >= S3C_EPS)
+            goto bad_reg;
+        if (s->index == 0)
+            return s->ep0.csr;
+        else
+            return s->ep1[s->index - 1].in_csr[0];
+    case S3C_IN_CSR2:
+        if (s->index >= S3C_EPS || s->index == 0)
+            goto bad_reg;
+        return s->ep1[s->index - 1].in_csr[1];
+    case S3C_OUT_CSR1:
+        if (s->index >= S3C_EPS || s->index == 0)
+            goto bad_reg;
+        return s->ep1[s->index - 1].out_csr[0];
+    case S3C_OUT_CSR2:
+        if (s->index >= S3C_EPS || s->index == 0)
+            goto bad_reg;
+        return s->ep1[s->index - 1].out_csr[1];
+    case S3C_EP0_FIFO:
+        if (unlikely(!s->ep0.len))
+            printf("%s: Endpoint0 underrun\n", __FUNCTION__);
+        else {
+            ret = s->ep0.fifo[s->ep0.start ++];
+            s->ep0.len --;
+        }
+        s->ep0.start &= S3C_USB_FIFO_LEN - 1;
+        return ret;
+    case S3C_EP4_FIFO: ep ++;
+    case S3C_EP3_FIFO: ep ++;
+    case S3C_EP2_FIFO: ep ++;
+    case S3C_EP1_FIFO:
+        if (unlikely(s->ep1[ep].in_csr[1] & (1 << 5)))	/* MODE_IN */
+            goto bad_reg;
+        if (unlikely(!s->ep1[ep].len))
+            printf("%s: Endpoint%i underrun\n", __FUNCTION__, ep + 1);
+        else {
+            ret = s->ep1[ep].fifo[s->ep1[ep].start ++];
+            s->ep1[ep].len --;
+        }
+        s->ep1[ep].start &= S3C_USB_FIFO_LEN - 1;
+        if (s->ep1[ep].out_csr[1] & (1 << 7))		/* AUTO_CLR */
+            if (s->ep1[ep].len <= 0) {
+                s->ep1[ep].out_csr[0] &= ~(1 << 0);	/* OUT_PKT_READY */
+            }
+        return ret;
+    case S3C_MAXP:
+        if (s->index >= S3C_EPS)
+            goto bad_reg;
+        if (s->index == 0)
+            return s->ep0.maxpacket;
+        else
+            return s->ep1[s->index - 1].maxpacket;
+    case S3C_FIFO_CNT1:
+        if (s->index >= S3C_EPS)
+            goto bad_reg;
+        if (s->index == 0)
+            return s->ep0.len & 0xff;
+        else
+            return s->ep1[s->index - 1].len & 0xff;
+    case S3C_FIFO_CNT2:
+        if (s->index >= S3C_EPS)
+            goto bad_reg;
+        if (s->index == 0)
+            return s->ep0.len >> 8;
+        else
+            return s->ep1[s->index - 1].len >> 8;
+
+    case S3C_EP_DMA_CON:
+        if (!ep --)
+            goto bad_reg;
+        return s->ep1[ep].control;
+    case S3C_EP_DMA_UNIT:
+        if (!ep --)
+            goto bad_reg;
+        return s->ep1[ep].unit_cnt;
+    case S3C_EP_DMA_FIFO:
+        if (!ep --)
+            goto bad_reg;
+        return s->ep1[ep].fifo_cnt;
+    case S3C_EP_DMA_TTC_L:
+        if (!ep --)
+            goto bad_reg;
+        return (s->ep1[ep].dma_size >> 0) & 0xff;
+    case S3C_EP_DMA_TTC_M:
+        if (!ep --)
+            goto bad_reg;
+        return (s->ep1[ep].dma_size >> 8) & 0xff;
+    case S3C_EP_DMA_TTC_H:
+        if (!ep --)
+            goto bad_reg;
+        return (s->ep1[ep].dma_size >> 16) & 0xf;
+    bad_reg:
+    default:
+        printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr);
+        break;
+    }
+    return 0;
+}
+
+static void s3c_udc_write(void *opaque, target_phys_addr_t addr,
+                uint32_t value)
+{
+    struct s3c_udc_state_s *s = (struct s3c_udc_state_s *) opaque;
+    int ep = S3C_EPS;
+    addr -= s->base;
+    while (addr < s3c_udc_ep_reg[-- ep]);
+    addr -= s3c_udc_ep_reg[ep];
+
+    switch (addr) {
+    case S3C_FUNC_ADDR:
+        s->address = value | (1 << 7);			/* ADDR_UPDATE */
+        s->dev.addr = value & 0x7f;
+        break;
+
+    case S3C_PWR:
+        if (!(value & (1 << 2)))			/* MCU_RESUME */
+            s->power &= ~(1 << 1);			/* SUSPEND_MODE */
+        s->power = (value & 5) | (s->power & 10);
+        /* Should send a SUSPEND interrupt every 3ms if SUSPEND is enabled.  */
+        break;
+
+    case S3C_EP_INT:
+        s->ep_intr &= ~value;
+        break;
+
+    case S3C_USB_INT:
+        s->usb_intr &= ~value;
+        break;
+
+    case S3C_EP_INT_EN:
+        s->ep_mask = value & ((1 << S3C_EPS) - 1);
+        break;
+
+    case S3C_USB_INT_EN:
+        s->usb_mask = (value & 0x5) | (1 << 1);
+        break;
+
+    case S3C_INDEX:
+        s->index = value;
+        break;
+
+    case S3C_IN_CSR1:
+        if (s->index >= S3C_EPS)
+            goto bad_reg;
+        if (s->index == 0) {
+            s->ep0.csr = (value | (s->ep0.csr & 0xa)) & (s->ep0.csr | ~0x4);
+            if (value & (1 << 7))			/* SERVICED_SETUP_EN */
+                s->ep0.csr &= ~(1 << 4);		/* SETUP_END */
+            if (value & (1 << 6))			/* SERVICED_OUT_PKT_ */
+                s->ep0.csr &= ~(1 << 0);		/* OUT_PKT_RDY */
+            if (value & (1 << 1))			/* IN_PKT_RDY */
+                s3c_udc_ep0_rdy(s, value);
+            else if (value & (1 << 3))			/* DATA_END */
+                s3c_udc_ep0_rdy(s, value);
+        } else {
+            ep = s->index - 1;
+            s->ep1[ep].in_csr[0] = (value | (s->ep1[ep].in_csr[0] & 1)) & 0x19;
+            if (value & (1 << 3))			/* FIFO_FLUSH */
+                s3c_udc_ep1_rdy(s, ep, value);
+            else if (value & (1 << 0))			/* IN_PKT_RDY */
+                s3c_udc_ep1_rdy(s, ep, value);
+        }
+        break;
+
+    case S3C_IN_CSR2:
+        if (s->index >= S3C_EPS || s->index == 0)
+            goto bad_reg;
+        s->ep1[s->index - 1].in_csr[1] = value & 0xf0;
+        break;
+
+    case S3C_OUT_CSR1:
+        if (s->index >= S3C_EPS || s->index == 0)
+            goto bad_reg;
+        if (s->ep1[s->index - 1].out_csr[0] & (1 << 0)) {	/* OUT_PKT_R */
+            if (value & (1 << 4))		/* FIFO_FLUSH */
+                s->ep1[s->index - 1].len = 0;
+            else if (!(value & (1 << 0)) &&	/* OUT_PKT_RDY */
+                            s->ep1[s->index - 1].len) {
+                value |= 1 << 0;		/* OUT_PKT_RDY */
+                s3c_udc_interrupt(s, s->index);
+            }
+        }
+        s->ep1[s->index - 1].out_csr[0] = value &
+                (0xa0 | (s->ep1[s->index - 1].out_csr[0] & 0x41));
+        break;
+
+    case S3C_OUT_CSR2:
+        if (s->index >= S3C_EPS || s->index == 0)
+            goto bad_reg;
+        s->ep1[s->index - 1].out_csr[1] = value & 0xe0;
+        break;
+
+    case S3C_EP0_FIFO:
+        if (unlikely(s->ep0.len >= S3C_USB_FIFO_LEN))
+            printf("%s: Endpoint0 overrun\n", __FUNCTION__);
+        s->ep0.fifo[(s->ep0.start + s->ep0.len ++) &
+                (S3C_USB_FIFO_LEN - 1)] = value;
+        break;
+
+    case S3C_EP4_FIFO: ep ++;
+    case S3C_EP3_FIFO: ep ++;
+    case S3C_EP2_FIFO: ep ++;
+    case S3C_EP1_FIFO:
+        if (unlikely(!(s->ep1[ep].in_csr[1] & (1 << 5))))	/* MODE_IN */
+            goto bad_reg;
+        if (unlikely(s->ep1[ep].len >= S3C_USB_FIFO_LEN))
+            printf("%s: Endpoint%i overrun\n", __FUNCTION__, ep + 1);
+        s->ep1[ep].fifo[(s->ep1[ep].start + s->ep1[ep].len ++) &
+                (S3C_USB_FIFO_LEN - 1)] = value;
+        if (s->ep1[ep].in_csr[1] & (1 << 7))		/* AUTO_SET */
+            if (s->ep1[ep].len >= (s->ep1[ep].maxpacket << 3)) {
+                s->ep1[ep].in_csr[0] |= 1 << 0;		/* IN_PKT_RDY */
+                s3c_udc_ep1_rdy(s, ep, s->ep1[ep].in_csr[0]);
+            }
+        break;
+
+    case S3C_MAXP:
+        if (s->index >= S3C_EPS)
+            goto bad_reg;
+        if (s->index == 0)
+            s->ep0.maxpacket = value & 0xf;
+        else
+            s->ep1[s->index - 1].maxpacket = value & 0xf;
+        break;
+
+    case S3C_EP_DMA_CON:
+        if (!ep --)
+            goto bad_reg;
+        s->ep1[ep].control = value;
+        break;
+
+    case S3C_EP_DMA_UNIT:
+        if (!ep --)
+            goto bad_reg;
+        s->ep1[ep].unit_cnt = value;
+        break;
+
+    case S3C_EP_DMA_FIFO:
+        if (!ep --)
+            goto bad_reg;
+        s->ep1[ep].fifo_cnt = value;
+        break;
+
+    case S3C_EP_DMA_TTC_L:
+        if (!ep --)
+            goto bad_reg;
+        s->ep1[ep].dma_size &= 0xfff00;
+        s->ep1[ep].dma_size |= (value & 0xff) << 0;
+        break;
+
+    case S3C_EP_DMA_TTC_M:
+        if (!ep --)
+            goto bad_reg;
+        s->ep1[ep].dma_size &= 0xf00ff;
+        s->ep1[ep].dma_size |= (value & 0xff) << 8;
+        break;
+
+    case S3C_EP_DMA_TTC_H:
+        if (!ep --)
+            goto bad_reg;
+        s->ep1[ep].dma_size &= 0x0ffff;
+        s->ep1[ep].dma_size |= (value & 0xf) << 16;
+        break;
+
+    bad_reg:
+    default:
+        printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr);
+    }
+}
+
+static CPUReadMemoryFunc *s3c_udc_readfn[] = {
+    s3c_udc_read,
+    s3c_udc_read,
+    s3c_udc_read,
+};
+
+static CPUWriteMemoryFunc *s3c_udc_writefn[] = {
+    s3c_udc_write,
+    s3c_udc_write,
+    s3c_udc_write,
+};
+
+static int s3c_udc_handle_packet(USBDevice *dev, USBPacket *p)
+{
+    struct s3c_udc_state_s *s = (struct s3c_udc_state_s *) dev->opaque;
+    int ep;
+    int ret = 0;
+    s->power &= ~(1 << 3);				/* USB_RESET */
+
+    switch (p->pid) {
+    case USB_MSG_ATTACH:
+        dev->state = USB_STATE_ATTACHED;
+        break;
+    case USB_MSG_DETACH:
+        dev->state = USB_STATE_NOTATTACHED;
+        break;
+    case USB_MSG_RESET:
+        s->power |= 1 << 3;				/* USB_RESET */
+#if 0
+        s->usb_intr |= 1 << 2;				/* RESET */
+#endif
+        s3c_udc_interrupt(s, -1);
+        break;
+    case USB_TOKEN_SETUP:
+        if (unlikely(s->ep0.packet))
+            printf("%s: EP0 overrun\n", __FUNCTION__);
+        if (s->ep0.csr & (1 << 5)) {			/* SEND_STALL */
+            ret = USB_RET_STALL;
+            s->ep0.csr |= 1 << 2;			/* SENT_STALL */
+            s3c_udc_interrupt(s, 0);
+            break;
+        }
+        s->frame ++;
+        s3c_udc_queue_packet(s->ep0.fifo, p->data,
+                        s->ep0.start, &s->ep0.len, p->len);
+        s->ep0.csr |= 1 << 0;				/* OUT_PKT_RDY */
+        s3c_udc_interrupt(s, 0);
+        break;
+    case USB_TOKEN_IN:
+        ep = p->devep - 1;
+        if (unlikely(ep >= 0 &&
+                !(s->ep1[ep].in_csr[1] & (1 << 5))))	/* MODE_IN */
+            goto fail;
+        if (p->devep == 0) {
+            if (unlikely(s->ep0.packet))
+                printf("%s: EP0 overrun\n", __FUNCTION__);
+            if (s->ep0.csr & (1 << 5)) {		/* SEND_STALL */
+                ret = USB_RET_STALL;
+                s->ep0.csr |= 1 << 2;			/* SENT_STALL */
+                s3c_udc_interrupt(s, 0);
+                break;
+            }
+            s->ep0.csr &= ~(1 << 1);			/* IN_PKT_RDY */
+            s3c_udc_interrupt(s, p->devep);
+            s->ep0.packet = p;
+        } else {
+            if (unlikely(s->ep1[ep].packet))
+                printf("%s: EP%i overrun\n", __FUNCTION__, ep + 1);
+            if (s->ep1[ep].in_csr[0] & (1 << 4)) {	/* SEND_STALL */
+                ret = USB_RET_STALL;
+                s->ep1[ep].in_csr[0] |= 1 << 5;		/* SENT_STALL */
+                s3c_udc_interrupt(s, 0);
+                break;
+            }
+            if (!(s->ep1[ep].in_csr[1] & (1 << 4)) ||	/* IN_DMA_INT_MASK */
+                    !(s->ep1[ep].control & (1 << 0))) {	/* DMA_MODE_EN */
+                s->ep1[ep].in_csr[0] &= ~(1 << 0);	/* IN_PKT_RDY */
+                s->ep1[ep].in_csr[0] &= ~(1 << 3);	/* FIFO_FLUSH */
+                s3c_udc_interrupt(s, p->devep);
+            }
+            /* TODO: DMA */
+            s->ep1[ep].packet = p;
+        }
+        s->frame ++;
+        ret = USB_RET_ASYNC;
+        break;
+    case USB_TOKEN_OUT:
+        ep = p->devep - 1;
+        if (unlikely(ep >= 0 &&
+                s->ep1[ep].in_csr[1] & (1 << 5)))	/* MODE_IN */
+            goto fail;
+        s->frame ++;
+        if (p->devep == 0) {
+            if (unlikely(s->ep0.packet || (s->ep0.csr &
+                                            (1 << 0))))	/* OUT_PKT_RDY */
+                printf("%s: EP0 overrun\n", __FUNCTION__);
+            if (s->ep0.csr & (1 << 5)) {		/* SEND_STALL */
+                ret = USB_RET_STALL;
+                s->ep0.csr |= 1 << 2;			/* SENT_STALL */
+                s3c_udc_interrupt(s, 0);
+                break;
+            }
+            s3c_udc_queue_packet(s->ep0.fifo, p->data,
+                            s->ep0.start, &s->ep0.len, p->len);
+            s->ep0.csr |= 1 << 0;			/* OUT_PKT_RDY */
+            s3c_udc_interrupt(s, p->devep);
+        } else {
+            if (unlikely(s->ep1[ep].packet || (s->ep1[ep].out_csr[0] &
+                                            (1 << 0))))	/* OUT_PKT_R */
+                printf("%s: EP%i overrun\n", __FUNCTION__, ep + 1);
+            if (s->ep1[ep].out_csr[0] & (1 << 5)) {	/* SEND_STALL */
+                ret = USB_RET_STALL;
+                s->ep1[ep].out_csr[0] |= 1 << 6;	/* SENT_STALL */
+                s3c_udc_interrupt(s, 0);
+                break;
+            }
+            s3c_udc_queue_packet(s->ep1[ep].fifo, p->data,
+                            s->ep1[ep].start,
+                            &s->ep1[ep].len, p->len);
+            if (!(s->ep1[ep].out_csr[1] & (1 << 5)) ||	/* OUT_DMA_INT_MASK */
+                    !(s->ep1[ep].control & (1 << 0))) {	/* DMA_MODE_EN */
+                s->ep1[ep].out_csr[0] |= 1 << 0;	/* OUT_PKT_RDY */
+                s3c_udc_interrupt(s, p->devep);
+            }
+            /* TODO: DMA */
+        }
+        break;
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void s3c_udc_handle_destroy(USBDevice *s)
+{
+}
+
+struct s3c_udc_state_s *s3c_udc_init(target_phys_addr_t base,
+                void *pic, void *dma)
+{
+    int iomemtype;
+    struct s3c_udc_state_s *s = (struct s3c_udc_state_s *)
+            qemu_mallocz(sizeof(struct s3c_udc_state_s));
+
+    s->base = base;
+    s->pic = pic;
+    s->dma = dma;
+
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = s3c_udc_handle_packet;
+    s->dev.handle_destroy = s3c_udc_handle_destroy;
+    s->dev.opaque = s;
+
+    s3c_udc_reset(s);
+
+    iomemtype = cpu_register_io_memory(0, s3c_udc_readfn,
+                    s3c_udc_writefn, s);
+    cpu_register_physical_memory(s->base, 0xffffff, iomemtype);
+
+    qemu_register_usb_gadget(&s->dev);
+
+    return s;
+}

Modified: trunk/src/host/qemu-neo1973/usb-linux-gadget.c
===================================================================
--- trunk/src/host/qemu-neo1973/usb-linux-gadget.c	2007-05-05 09:43:19 UTC (rev 1913)
+++ trunk/src/host/qemu-neo1973/usb-linux-gadget.c	2007-05-05 23:51:17 UTC (rev 1914)
@@ -32,6 +32,7 @@
 
 struct gadget_state_s {
     USBPort port;
+    uint8_t config_num;
     int connected;
     int speed;
     int hosthighspeed;
@@ -44,6 +45,10 @@
         char *path;
         int num;
         struct gadget_state_s *state;
+
+        int busy;
+        uint8_t buffer[4096];
+        USBPacket packet;
     } ep[16];
 
     /* Device descriptor */
@@ -122,6 +127,27 @@
     }
 }
 
+static void gadget_ep_run(struct ep_s *ep)
+{
+    USBDevice *dev = ep->state->port.dev;
+    int ret;
+
+    ret = dev->handle_packet(dev, &ep->packet);
+
+    if (ret >= 0) {
+        ep->packet.len = ret;
+        ep->packet.complete_cb(&ep->packet, ep->packet.complete_opaque);
+    } else if (ret == USB_RET_STALL) {
+        gadget_stall(ep->state, &ep->packet);
+    } else if (ret == USB_RET_ASYNC) {
+        ep->busy = 1;
+    } else {
+        fprintf(stderr, "%s: packet unhandled: %i\n", __FUNCTION__, ret);
+        if (ret != USB_RET_NAK)
+            gadget_detach(ep->state);
+    }
+}
+
 static void gadget_respond(USBPacket *packet, void *opaque)
 {
     struct gadget_state_s *hci = (struct gadget_state_s *) opaque;
@@ -141,8 +167,9 @@
 static void gadget_token_in(USBPacket *packet, void *opaque)
 {
     struct ep_s *ep = (struct ep_s *) opaque;
-    if (packet->len > 0)
-        write(ep->fd, packet->data, packet->len);
+    ep->busy = 0;
+    while (write(ep->fd, packet->data, packet->len) < packet->len &&
+                    errno == EINTR);
 }
 
 #if 0
@@ -160,36 +187,32 @@
     struct ep_s *ep = (struct ep_s *) opaque;
     struct gadget_state_s *hci = ep->state;
 
+    if (ep->busy)
+        return;
+
 #if 0
     if (!gadget_ep_poll(opaque))
         return;
 #endif
 
     /* write() is supposed to not block here */
-    if (write(ep->fd, hci->buffer, 0))
+    if (write(ep->fd, ep->buffer, 0))
         return;
 
-    if (hci->async_count) {
-        fprintf(stderr, "%s: overrun\n", __FUNCTION__);
-        gadget_detach(hci);
-        return;
-    }
+    ep->packet.pid = USB_TOKEN_IN;
+    ep->packet.devaddr = hci->addr;
+    ep->packet.devep = ep->num;
+    ep->packet.data = ep->buffer;
+    ep->packet.len = sizeof(ep->buffer);
+    ep->packet.complete_cb = gadget_token_in;
+    ep->packet.complete_opaque = ep;
 
-    hci->async_count = 1;
-
-    hci->packet->pid = USB_TOKEN_IN;
-    hci->packet->devaddr = hci->addr;
-    hci->packet->devep = ep->num;
-    hci->packet->data = hci->buffer;
-    hci->packet->len = sizeof(hci->buffer);
-    hci->packet->complete_cb = gadget_token_in;
-    hci->packet->complete_opaque = ep;
-
-    gadget_run(0, hci);
+    gadget_ep_run(ep);
 }
 
 static void gadget_nop(USBPacket *prev_packet, void *opaque)
 {
+    ((struct ep_s *) opaque)->busy = 0;
 }
 
 static void gadget_ep_write(void *opaque)
@@ -198,27 +221,22 @@
     struct gadget_state_s *hci = ep->state;
     int ret;
 
-    ret = read(ep->fd, hci->buffer, sizeof(hci->buffer));
-    if (ret <= 0)
+    if (ep->busy)
         return;
 
-    if (hci->async_count) {
-        fprintf(stderr, "%s: overrun\n", __FUNCTION__);
-        gadget_detach(hci);
+    ret = read(ep->fd, ep->buffer, sizeof(ep->buffer));
+    if (ret <= 0)
         return;
-    }
 
-    hci->async_count = 1;
+    ep->packet.pid = USB_TOKEN_OUT;
+    ep->packet.devaddr = hci->addr;
+    ep->packet.devep = ep->num;
+    ep->packet.data = ep->buffer;
+    ep->packet.len = ret;
+    ep->packet.complete_cb = gadget_nop;
+    ep->packet.complete_opaque = ep;
 
-    hci->packet->pid = USB_TOKEN_OUT;
-    hci->packet->devaddr = hci->addr;
-    hci->packet->devep = ep->num;
-    hci->packet->data = hci->buffer;
-    hci->packet->len = ret;
-    hci->packet->complete_cb = gadget_nop;
-    hci->packet->complete_opaque = ep;
-
-    gadget_run(0, hci);
+    gadget_ep_run(ep);
 }
 
 static int gadget_ep_open(struct gadget_state_s *hci,
@@ -268,6 +286,8 @@
         goto fail;
     }
 
+    ep->busy = 0;
+
     if (desc->bEndpointAddress & USB_DIR_IN)
         qemu_set_fd_handler(ep->fd, NULL, gadget_ep_read, ep);
     else
@@ -471,7 +491,7 @@
     req = (struct usb_ctrlrequest *) (hci->buffer + 16);
     req->bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
     req->bRequest = USB_REQ_GET_DESCRIPTOR;
-    req->wValue = (USB_DT_CONFIG << 8) | 0;
+    req->wValue = (USB_DT_CONFIG << 8) | hci->config_num;
     req->wIndex = 0x0000;
     req->wLength = sizeof(hci->buffer);
 
@@ -495,7 +515,7 @@
     struct usb_gadgetfs_event event;
     int ret, len;
 
-    if (!s->connected)
+    if (!s->addr)
         return;
 
     ret = read(s->ep0fd, &event, sizeof(event));
@@ -525,12 +545,12 @@
 
         s->async_count = 2;
 
-        memcpy(s->buffer, &event.u.setup, 8);
+        memcpy(s->buffer, &event.u.setup, sizeof(event.u.setup));
         s->packet[1].pid = USB_TOKEN_SETUP;
         s->packet[1].devaddr = s->addr;
         s->packet[1].devep = 0;
         s->packet[1].data = s->buffer;
-        s->packet[1].len = 8;
+        s->packet[1].len = sizeof(event.u.setup);
         s->packet[1].complete_cb = gadget_run;
         s->packet[1].complete_opaque = s;
 
@@ -717,6 +737,7 @@
     atexit(gadget_done);
 
     hci->addr = 0;
+    hci->config_num = 0;
 
     qemu_register_usb_port(&hci->port, hci, USB_INDEX_HOST, gadget_attach);
 

Modified: trunk/src/host/qemu-neo1973/vl.h
===================================================================
--- trunk/src/host/qemu-neo1973/vl.h	2007-05-05 09:43:19 UTC (rev 1913)
+++ trunk/src/host/qemu-neo1973/vl.h	2007-05-05 23:51:17 UTC (rev 1914)
@@ -1459,7 +1459,7 @@
 
 #include "hw/i2c.h"
 
-#define unlikely(cond)	__builtin_expect(cond, 0)
+#define unlikely(cond)	__builtin_expect(!!(cond), 0)
 
 #ifdef TARGET_ARM
 #include "hw/pxa.h"





More information about the commitlog mailing list