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