diff --git a/apps/main.c b/apps/main.c index 6d2609b..4b74784 100644 --- a/apps/main.c +++ b/apps/main.c @@ -326,6 +326,7 @@ static void init(void) viewportmanager_init(); gui_sync_wps_init(); + usb_init(); storage_init(); settings_reset(); settings_load(SETTINGS_ALL); diff --git a/firmware/SOURCES b/firmware/SOURCES index 56ab680..2381611 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -159,7 +159,14 @@ drivers/fat.c #if (CONFIG_STORAGE & STORAGE_MMC) || (CONFIG_STORAGE & STORAGE_SD) hotswap.c #endif +#else /* SIMULATOR */ +#if (CONFIG_STORAGE & STORAGE_RAMDISK) +drivers/ramdisk.c #endif /* SIMULATOR */ +#ifdef CONFIG_STORAGE_MULTI +storage.c +#endif +#endif /* EEPROM */ #ifdef HAVE_EEPROM @@ -288,7 +295,6 @@ drivers/audio/uda1341.c #endif /* !defined(SIMULATOR) && !defined(BOOTLOADER) */ /* USB Stack */ -#if !defined(SIMULATOR) #ifdef HAVE_USBSTACK usbstack/usb_core.c #ifdef USB_ENABLE_STORAGE @@ -319,7 +325,6 @@ drivers/isp1362.c drivers/m5636.c #endif #endif /* !defined(HAVE_USBSTACK) */ -#endif /* !defined(SIMULATOR) */ /* Other Random Hardware */ #ifdef HAVE_TSC2100 diff --git a/firmware/export/as3525.h b/firmware/export/as3525.h index a8903d1..41f7e12 100644 --- a/firmware/export/as3525.h +++ b/firmware/export/as3525.h @@ -505,7 +505,9 @@ interface */ /* USB : TODO */ #define USB_NUM_ENDPOINTS 4 +#ifndef SIMULATOR #define USB_DEVBSS_ATTR IBSS_ATTR +#endif /* SIMULATOR */ /* I2SIN registers */ diff --git a/firmware/export/config.h b/firmware/export/config.h index a4940a0..db88b73 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -845,7 +845,10 @@ Lyre prototype 1 */ #if defined(HAVE_USBSTACK) /* Define the implemented USB transport classes */ -#if CONFIG_USBOTG == USBOTG_ISP1583 +#if defined(SIMULATOR) +#define USB_HAS_BULK +#define USB_HAS_INTERRUPT +#elif CONFIG_USBOTG == USBOTG_ISP1583 #define USB_HAS_BULK #elif (CONFIG_USBOTG == USBOTG_ARC) || \ (CONFIG_USBOTG == USBOTG_JZ4740) || \ @@ -880,8 +883,12 @@ Lyre prototype 1 */ #define USB_ENABLE_HID #else #define USB_ENABLE_CHARGING_ONLY -#endif -#endif +#endif /* USB_HAS_INTERRUPT */ +#else /* SIMULATOR */ +//#define USB_ENABLE_HID +//#define USB_ENABLE_SERIAL +#define USB_ENABLE_STORAGE +#endif /* SIMULATOR */ #endif /* BOOTLOADER */ diff --git a/firmware/export/config/sim.h b/firmware/export/config/sim.h index 56c3e18..da98a58 100644 --- a/firmware/export/config/sim.h +++ b/firmware/export/config/sim.h @@ -34,18 +34,27 @@ #undef HAVE_HOTSWAP #undef HAVE_HOTSWAP_STORAGE_AS_MAIN +#undef CONFIG_STORAGE_MULTI #undef CONFIG_STORAGE +#undef NUM_DRIVES + +#define CONFIG_STORAGE_MULTI +#define CONFIG_STORAGE STORAGE_RAMDISK +#define NUM_DRIVES 1 #undef CONFIG_USBOTG #undef USB_HANDLED_BY_OF -#undef HAVE_USBSTACK -#undef USE_ROCKBOX_USB -#undef USB_VENDOR_ID -#undef USB_PRODUCT_ID #undef USB_NUM_ENDPOINTS -#undef HAVE_USB_HID_MOUSE +#define USB_NUM_ENDPOINTS 8 +/* with the simulator, the previous definition can't be dangerous so keep it if already defined */ +#ifndef USB_DEVBSS_ATTR +#define USB_DEVBSS_ATTR +#endif /* USB_DEVBSS_ATTR */ +#define USB_STATUS_BY_EVENT +#define USB_DETECT_BY_DRV +#define USB_USE_RAMDISK #undef HAVE_ADJUSTABLE_CPU_FREQ diff --git a/firmware/export/dm320.h b/firmware/export/dm320.h index f78fc6c..fd3a638 100644 --- a/firmware/export/dm320.h +++ b/firmware/export/dm320.h @@ -49,7 +49,9 @@ extern unsigned long _ttbstart; /* This needs to be 2048 byte aligned, but USB_QHARRAY_ATTR should take care * of that */ #define USB_QHARRAY_ATTR __attribute__((section(".qharray"),nocommon,aligned(4))) +#ifndef SIMULATOR #define USB_DEVBSS_ATTR IBSS_ATTR +#endif /* SIMULATOR */ /* Timer 0-3 */ diff --git a/firmware/export/imx31l.h b/firmware/export/imx31l.h index dea5588..275e5cc 100644 --- a/firmware/export/imx31l.h +++ b/firmware/export/imx31l.h @@ -41,7 +41,9 @@ /* USBOTG */ #define USB_QHARRAY_ATTR __attribute__((section(".qharray"),nocommon,aligned(2048))) #define USB_NUM_ENDPOINTS 8 +#ifndef SIMULATOR #define USB_DEVBSS_ATTR DEVBSS_ATTR +#endif /* SIMULATOR */ #define USB_BASE OTG_BASE_ADDR /* diff --git a/firmware/export/jz4740.h b/firmware/export/jz4740.h index 820b43f..3b39c39 100644 --- a/firmware/export/jz4740.h +++ b/firmware/export/jz4740.h @@ -5207,7 +5207,9 @@ struct Ration2m /* Rockbox USB defines */ #define USB_NUM_ENDPOINTS 3 +#ifndef SIMULATOR #define USB_DEVBSS_ATTR IBSS_ATTR +#endif /* SIMULATOR */ /* Timer frequency */ #define TIMER_FREQ (CFG_EXTAL) /* For full precision! */ diff --git a/firmware/export/pp5020.h b/firmware/export/pp5020.h index 2d8d0e1..8261b55 100644 --- a/firmware/export/pp5020.h +++ b/firmware/export/pp5020.h @@ -31,7 +31,9 @@ /* This needs to be 2048 byte aligned, but USB_QHARRAY_ATTR should take care * of that */ #define USB_QHARRAY_ATTR __attribute__((section(".qharray"),nocommon,aligned(4))) +#ifndef SIMULATOR #define USB_DEVBSS_ATTR IBSS_ATTR +#endif /* SIMULATOR */ /* DRAM starts at 0x10000000, but in Rockbox we remap it to 0x00000000 */ #define DRAM_START 0x10000000 diff --git a/firmware/export/tcc77x.h b/firmware/export/tcc77x.h index db128b6..f8c9e9c 100644 --- a/firmware/export/tcc77x.h +++ b/firmware/export/tcc77x.h @@ -251,7 +251,9 @@ #define USB_BASE 0x90000b00 #define USB_NUM_ENDPOINTS 3 +#ifndef SIMULATOR #define USB_DEVBSS_ATTR IBSS_ATTR +#endif /* SIMULATOR */ /* Timer frequency */ /* timers are based on XIN (12Mhz) */ diff --git a/firmware/export/tcc780x.h b/firmware/export/tcc780x.h index aca3bec..810d99e 100644 --- a/firmware/export/tcc780x.h +++ b/firmware/export/tcc780x.h @@ -287,7 +287,9 @@ #define USB_BASE 0xf0010000 #define USB_NUM_ENDPOINTS 3 +#ifndef SIMULATOR #define USB_DEVBSS_ATTR IBSS_ATTR +#endif /* SIMULATOR */ /* Timer frequency */ /* Timer is based on PCK_TCT (set to 2Mhz in system.c) */ diff --git a/firmware/export/usb.h b/firmware/export/usb.h index e725f0a..a57d4bf 100644 --- a/firmware/export/usb.h +++ b/firmware/export/usb.h @@ -52,6 +52,9 @@ enum { USB_REQUEST_REBOOT, /* Event */ #endif USB_QUIT, /* Event */ +#if defined(SIMULATOR) && defined(HAVE_USBSTACK) + USB_ASK, /* Event */ +#endif }; #ifdef HAVE_USB_POWER @@ -132,6 +135,11 @@ struct usb_transfer_completion_event_data }; #endif /* HAVE_USBSTACK */ +#if defined(SIMULATOR) && defined(HAVE_USBSTACK) +#define USB_ASK_SIMULATE_INSERTION 1 +#define USB_ASK_SIMULATE_EXTRACTION 2 +void usb_ask(unsigned int what); /* Simulator: ask something (simulate insertion,extraction,...) */ +#endif void usb_init(void); void usb_enable(bool on); void usb_attach(void); diff --git a/firmware/usb.c b/firmware/usb.c index ccf12c1..302299d 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -27,7 +27,10 @@ #include "cpu.h" #include "kernel.h" #include "thread.h" -#include "system.h" +#ifdef SIMULATOR +#include "system-sdl.h" +#include "debug.h" +#endif #include "debug.h" #include "storage.h" #include "fat.h" @@ -42,6 +45,7 @@ #ifdef HAVE_USBSTACK #include "usb_core.h" #endif +/*#define LOGF_ENABLE*/ #include "logf.h" #include "screendump.h" @@ -60,7 +64,7 @@ bool do_screendump_instead_of_usb = false; #endif -#if !defined(SIMULATOR) && !defined(USB_NONE) +#if !defined(USB_NONE) static int usb_state; @@ -232,6 +236,11 @@ static void usb_thread(void) queue_wait(&usb_queue, &ev); switch(ev.id) { +#if defined(SIMULATOR) && defined(HAVE_USBSTACK) + case USB_ASK: + usb_ask((unsigned int)ev.data); + break; +#endif #ifdef USB_DRIVER_CLOSE case USB_QUIT: return; @@ -314,7 +323,7 @@ static void usb_thread(void) usb_core_enable_driver(USB_DRIVER_HID, usb_hid); #endif #ifdef USB_ENABLE_CHARGING_ONLY - usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false); + usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true); #endif /* Check any drivers enabled at this point for exclusive storage @@ -532,6 +541,7 @@ static void usb_tick(void) readings in a row */ if(countdown == 0) { + DEBUGF("usb_tick: post %d\n", current_status); queue_post(&usb_queue, current_status, 0); } } @@ -560,6 +570,7 @@ void usb_acknowledge(long id) void usb_init(void) { + DEBUGF("usb_init\n"); /* We assume that the USB cable is extracted */ usb_state = USB_EXTRACTED; #ifdef USB_DETECT_BY_DRV diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index 7b80d0b..576f758 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -263,7 +263,7 @@ static unsigned char response_data[256] USB_DEVBSS_ATTR; static short hex[16] = {'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'}; -#ifdef IPOD_ARCH +#if defined(IPOD_ARCH) && !defined(SIMULATOR) static void set_serial_descriptor(void) { #ifdef IPOD_VIDEO @@ -287,7 +287,7 @@ static void set_serial_descriptor(void) } usb_string_iSerial.bLength=52; } -#elif defined(HAVE_AS3514) +#elif defined(HAVE_AS3514) && !defined(SIMULATOR) static void set_serial_descriptor(void) { unsigned char serial[16]; @@ -321,7 +321,7 @@ static void set_serial_descriptor(void) } usb_string_iSerial.bLength=84; } -#elif (CONFIG_STORAGE & STORAGE_RAMDISK) +#elif (CONFIG_STORAGE & STORAGE_RAMDISK) || defined(SIMULATOR) /* This "serial number" isn't unique, but it should never actually appear in non-testing use */ static void set_serial_descriptor(void) diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c index f8e3aba..3ca56e4 100644 --- a/firmware/usbstack/usb_storage.c +++ b/firmware/usbstack/usb_storage.c @@ -41,7 +41,7 @@ //#define ONLY_EXPOSE_CARD_SLOT #ifdef USB_USE_RAMDISK -#define RAMDISK_SIZE 2048 +#define RAMDISK_SIZE /*2048*/(2048*10) #endif #ifndef SECTOR_SIZE @@ -342,6 +342,7 @@ static void fix_mbr(unsigned char* mbr) static bool check_disk_present(IF_MD_NONVOID(int volume)) { #ifdef USB_USE_RAMDISK + (void)volume; return true; #else unsigned char sector[SECTOR_SIZE]; @@ -1046,7 +1047,7 @@ static void handle_scsi(struct command_block_wrapper* cbw) cbw->command_block[8]); cur_cmd.orig_count = cur_cmd.count; - //logf("scsi read %d %d", cur_cmd.sector, cur_cmd.count); + logf("scsi read %d %d", cur_cmd.sector, cur_cmd.count); if((cur_cmd.sector + cur_cmd.count) > block_count) { send_csw(UMS_STATUS_FAIL); diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES index c186551..9569eb4 100644 --- a/uisimulator/common/SOURCES +++ b/uisimulator/common/SOURCES @@ -10,4 +10,7 @@ sim_tasks.c stubs.c powermgmt-sim.c backlight-sim.c - +#ifdef HAVE_USBSTACK +usb-drv.c +libusb_vhci/libusb_vhci.c +#endif diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c index d87e331..bfc78c6 100644 --- a/uisimulator/common/io.c +++ b/uisimulator/common/io.c @@ -636,6 +636,7 @@ void debugf(const char *fmt, ...) va_list ap; va_start( ap, fmt ); vfprintf( stderr, fmt, ap ); + fflush(stderr); va_end( ap ); } @@ -645,6 +646,7 @@ void ldebugf(const char* file, int line, const char *fmt, ...) va_start( ap, fmt ); fprintf( stderr, "%s:%d ", file, line ); vfprintf( stderr, fmt, ap ); + fflush(stderr); va_end( ap ); } diff --git a/uisimulator/common/libusb_vhci/libusb_vhci.c b/uisimulator/common/libusb_vhci/libusb_vhci.c new file mode 100644 index 0000000..1c749d8 --- /dev/null +++ b/uisimulator/common/libusb_vhci/libusb_vhci.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2009 Michael Singer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "vhci-hcd.h" +#include "libusb_vhci.h" + +int usb_vhci_open(uint8_t port_count, // [IN] number of ports + int32_t *id, // [OUT] controller id + int32_t *usb_busnum, // [OUT] usb bus number + char **bus_id) // [OUT] bus id (usually + // vhci_hcd.) +{ + int fd = open(USB_VHCI_DEVICE_FILE, O_RDWR); + if(fd == -1) return -1; + + struct vhci_ioc_register r; + r.port_count = port_count; + if(ioctl(fd, VHCI_HCD_IOCREGISTER, &r) == -1) + { + int err = errno; + usb_vhci_close(fd); + errno = err; + return -1; + } + + if(id) *id = r.id; + if(usb_busnum) *usb_busnum = r.usb_busnum; + if(bus_id) + { + size_t s = sizeof r.bus_id / sizeof *r.bus_id - 1; + r.bus_id[s] = 0; + size_t ss = (strlen(r.bus_id) + 1) * sizeof **bus_id; + if((*bus_id = malloc(ss))) + memcpy(*bus_id, r.bus_id, ss); + } + + return fd; +} + +int usb_vhci_close(int fd) +{ + int result; + while((result = close(fd)) == -1 && errno == EINTR); + return result; +} + +int usb_vhci_fetch_work(int fd, struct usb_vhci_work *work) +{ + struct vhci_ioc_work w; + if(ioctl(fd, VHCI_HCD_IOCFETCHWORK, &w) == -1) + return -1; + + switch(w.type) + { + case VHCI_IOC_WORK_TYPE_PORT_STAT: + work->type = USB_VHCI_WORK_TYPE_PORT_STAT; + work->work.port_stat.status = w.work.port.status; + work->work.port_stat.change = w.work.port.change; + work->work.port_stat.index = w.work.port.index; + work->work.port_stat.flags = w.work.port.flags; + return 0; + + case VHCI_IOC_WORK_TYPE_PROCESS_URB: + memset(&work->work.urb, 0, sizeof work->work.urb); + switch(w.work.urb.type) + { + case VHCI_IOC_URB_TYPE_ISO: + work->work.urb.type = USB_VHCI_URB_TYPE_ISO; + work->work.urb.packet_count = w.work.urb.packet_count; + work->work.urb.interval = w.work.urb.interval; + break; + case VHCI_IOC_URB_TYPE_INT: + work->work.urb.type = USB_VHCI_URB_TYPE_INT; + work->work.urb.interval = w.work.urb.interval; + break; + case VHCI_IOC_URB_TYPE_CONTROL: + work->work.urb.type = USB_VHCI_URB_TYPE_CONTROL; + work->work.urb.wValue = w.work.urb.setup_packet.wValue; + work->work.urb.wIndex = w.work.urb.setup_packet.wIndex; + work->work.urb.wLength = w.work.urb.setup_packet.wLength; + work->work.urb.bmRequestType = w.work.urb.setup_packet.bmRequestType; + work->work.urb.bRequest = w.work.urb.setup_packet.bRequest; + break; + case VHCI_IOC_URB_TYPE_BULK: + work->work.urb.type = USB_VHCI_URB_TYPE_BULK; + work->work.urb.flags = ((w.work.urb.flags & VHCI_IOC_URB_FLAGS_SHORT_NOT_OK) ? + USB_VHCI_URB_FLAGS_SHORT_NOT_OK : 0) | + ((w.work.urb.flags & VHCI_IOC_URB_FLAGS_ZERO_PACKET) ? + USB_VHCI_URB_FLAGS_ZERO_PACKET : 0); + break; + default: + errno = EBADMSG; + return -1; + } + work->type = USB_VHCI_WORK_TYPE_PROCESS_URB; + work->work.urb.status = -EINPROGRESS; + work->work.urb.handle = w.handle; + work->work.urb.buffer_length = w.work.urb.buffer_length; + if(usb_vhci_is_out(w.work.urb.endpoint) || usb_vhci_is_iso(work->work.urb.type)) + work->work.urb.buffer_actual = w.work.urb.buffer_length; + work->work.urb.devadr = w.work.urb.address; + work->work.urb.epadr = w.work.urb.endpoint; + // return 1 if usb_vhci_fetch_data should be called + return work->work.urb.buffer_actual || work->work.urb.packet_count; + + case VHCI_IOC_WORK_TYPE_CANCEL_URB: + work->type = USB_VHCI_WORK_TYPE_CANCEL_URB; + work->work.handle = w.handle; + return 0; + + default: + errno = EBADMSG; + return -1; + } +} + +int usb_vhci_fetch_data(int fd, const struct usb_vhci_urb *urb) +{ + struct vhci_ioc_urb_data u; + u.handle = urb->handle; + u.buffer_length = urb->buffer_length; + const int pc = urb->packet_count; + u.packet_count = pc; + u.buffer = urb->buffer; + u.iso_packets = NULL; + if(pc > 0) + u.iso_packets = malloc(sizeof *u.iso_packets * pc); + + int ret = ioctl(fd, VHCI_HCD_IOCFETCHDATA, &u); + if(ret == -1) + goto err; + ret = 0; + int i; + for(i = 0; i < pc; i++) + { + urb->iso_packets[i].offset = u.iso_packets[i].offset; + urb->iso_packets[i].packet_length = (int32_t)u.iso_packets[i].packet_length; + urb->iso_packets[i].packet_actual = 0; + urb->iso_packets[i].status = -EINPROGRESS; + } + +err: + if(u.iso_packets) + free(u.iso_packets); + return ret; +} + +int usb_vhci_giveback(int fd, const struct usb_vhci_urb *urb) +{ + struct vhci_ioc_giveback gb; + gb.handle = urb->handle; + gb.status = urb->status; + gb.buffer_actual = urb->buffer_actual; + gb.buffer = NULL; + gb.iso_packets = NULL; + gb.packet_count = 0; + gb.error_count = 0; + + if(usb_vhci_is_in(urb->epadr) && gb.buffer_actual > 0) + gb.buffer = urb->buffer; + if(usb_vhci_is_iso(urb->type)) + { + const int pc = urb->packet_count; + gb.iso_packets = malloc(sizeof *gb.iso_packets * pc); + gb.packet_count = pc; + gb.error_count = urb->error_count; + int i; + for(i = 0; i < pc; i++) + { + gb.iso_packets[i].status = urb->iso_packets[i].status; + gb.iso_packets[i].packet_actual = (uint32_t)urb->iso_packets[i].packet_actual; + } + } + + int ret = ioctl(fd, VHCI_HCD_IOCGIVEBACK, &gb); + + if(gb.iso_packets) + free(gb.iso_packets); + if(ret == -1) + return (errno == ECANCELED) ? 0 : -1; + errno = 0; + return 0; +} + +int usb_vhci_port_connect(int fd, uint8_t port, uint8_t data_rate) +{ + if(!port || + (data_rate != USB_VHCI_DATA_RATE_FULL && + data_rate != USB_VHCI_DATA_RATE_LOW && + data_rate != USB_VHCI_DATA_RATE_HIGH)) + { + errno = EINVAL; + return -1; + } + struct vhci_ioc_port_stat ps; + ps.status = USB_PORT_STAT_CONNECTION; + if(data_rate == USB_VHCI_DATA_RATE_LOW) + ps.status |= USB_PORT_STAT_LOW_SPEED; + if(data_rate == USB_VHCI_DATA_RATE_HIGH) + ps.status |= USB_PORT_STAT_HIGH_SPEED; + ps.change = USB_PORT_STAT_C_CONNECTION; + ps.index = port; + ps.flags = 0; + if(ioctl(fd, VHCI_HCD_IOCPORTSTAT, &ps) == -1) + return -1; + return 0; +} + +int usb_vhci_port_disconnect(int fd, uint8_t port) +{ + if(!port) + { + errno = EINVAL; + return -1; + } + struct vhci_ioc_port_stat ps; + ps.status = 0; + ps.change = USB_PORT_STAT_C_CONNECTION; + ps.index = port; + ps.flags = 0; + if(ioctl(fd, VHCI_HCD_IOCPORTSTAT, &ps) == -1) + return -1; + return 0; +} + +int usb_vhci_port_disable(int fd, uint8_t port) +{ + if(!port) + { + errno = EINVAL; + return -1; + } + struct vhci_ioc_port_stat ps; + ps.status = 0; + ps.change = USB_PORT_STAT_C_ENABLE; + ps.index = port; + ps.flags = 0; + if(ioctl(fd, VHCI_HCD_IOCPORTSTAT, &ps) == -1) + return -1; + return 0; +} + +int usb_vhci_port_resumed(int fd, uint8_t port) +{ + if(!port) + { + errno = EINVAL; + return -1; + } + struct vhci_ioc_port_stat ps; + ps.status = 0; + ps.change = USB_PORT_STAT_C_SUSPEND; + ps.index = port; + ps.flags = 0; + if(ioctl(fd, VHCI_HCD_IOCPORTSTAT, &ps) == -1) + return -1; + return 0; +} + +int usb_vhci_port_overcurrent(int fd, uint8_t port, uint8_t set) +{ + if(!port) + { + errno = EINVAL; + return -1; + } + struct vhci_ioc_port_stat ps; + ps.status = set ? USB_PORT_STAT_OVERCURRENT : 0; + ps.change = USB_PORT_STAT_C_OVERCURRENT; + ps.index = port; + ps.flags = 0; + if(ioctl(fd, VHCI_HCD_IOCPORTSTAT, &ps) == -1) + return -1; + return 0; +} + +int usb_vhci_port_reset_done(int fd, uint8_t port, uint8_t enable) +{ + if(!port) + { + errno = EINVAL; + return -1; + } + struct vhci_ioc_port_stat ps; + ps.status = enable ? USB_PORT_STAT_ENABLE : 0;; + ps.change = USB_PORT_STAT_C_RESET; + ps.change |= enable ? 0 : USB_PORT_STAT_C_ENABLE; + ps.index = port; + ps.flags = 0; + if(ioctl(fd, VHCI_HCD_IOCPORTSTAT, &ps) == -1) + return -1; + return 0; +} diff --git a/uisimulator/common/libusb_vhci/libusb_vhci.h b/uisimulator/common/libusb_vhci/libusb_vhci.h new file mode 100644 index 0000000..326c77e --- /dev/null +++ b/uisimulator/common/libusb_vhci/libusb_vhci.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2009 Michael Singer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _LIBUSB_VHCI_H +#define _LIBUSB_VHCI_H 1 + +#include +#include + +#define USB_VHCI_DEVICE_FILE "/dev/vhci-ctrl" + +#ifdef _LIB_USB_VHCI_NOTHROW +#undef _LIB_USB_VHCI_NOTHROW +#endif + +#define _LIB_USB_VHCI_NOTHROW + +struct usb_vhci_iso_packet +{ + uint32_t offset; + int32_t packet_length, packet_actual; + int32_t status; +}; + +struct usb_vhci_urb +{ + uint64_t handle; + uint8_t *buffer; + struct usb_vhci_iso_packet *iso_packets; + int32_t buffer_length, buffer_actual; + int32_t packet_count, error_count; + int32_t status, interval; + uint16_t flags; +#define USB_VHCI_URB_FLAGS_SHORT_NOT_OK 0x0001 +#define USB_VHCI_URB_FLAGS_ISO_ASAP 0x0002 +#define USB_VHCI_URB_FLAGS_ZERO_PACKET 0x0040 + uint16_t wValue, wIndex, wLength; + uint8_t bmRequestType, bRequest; + uint8_t devadr, epadr; + uint8_t type; +#define USB_VHCI_URB_TYPE_ISO 0 +#define USB_VHCI_URB_TYPE_INT 1 +#define USB_VHCI_URB_TYPE_CONTROL 2 +#define USB_VHCI_URB_TYPE_BULK 3 +}; + +struct usb_vhci_port_stat +{ + uint16_t status, change; +#define USB_VHCI_PORT_STAT_CONNECTION 0x0001 +#define USB_VHCI_PORT_STAT_ENABLE 0x0002 +#define USB_VHCI_PORT_STAT_SUSPEND 0x0004 +#define USB_VHCI_PORT_STAT_OVERCURRENT 0x0008 +#define USB_VHCI_PORT_STAT_RESET 0x0010 +#define USB_VHCI_PORT_STAT_POWER 0x0100 +#define USB_VHCI_PORT_STAT_LOW_SPEED 0x0200 +#define USB_VHCI_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_VHCI_PORT_STAT_C_CONNECTION 0x0001 +#define USB_VHCI_PORT_STAT_C_ENABLE 0x0002 +#define USB_VHCI_PORT_STAT_C_SUSPEND 0x0004 +#define USB_VHCI_PORT_STAT_C_OVERCURRENT 0x0008 +#define USB_VHCI_PORT_STAT_C_RESET 0x0010 + uint8_t index, flags; +#define USB_VHCI_PORT_STAT_FLAG_RESUMING 0x01 +}; +#define USB_VHCI_DATA_RATE_FULL 0 +#define USB_VHCI_DATA_RATE_LOW 1 +#define USB_VHCI_DATA_RATE_HIGH 2 + +struct usb_vhci_work +{ + union + { + uint64_t handle; + struct usb_vhci_urb urb; + struct usb_vhci_port_stat port_stat; + } work; + + int type; +#define USB_VHCI_WORK_TYPE_PORT_STAT 0 +#define USB_VHCI_WORK_TYPE_PROCESS_URB 1 +#define USB_VHCI_WORK_TYPE_CANCEL_URB 2 +}; + +int usb_vhci_open(uint8_t port_count, + int32_t *id, + int32_t *usb_busnum, + char **bus_id) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_close(int fd) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_fetch_work(int fd, struct usb_vhci_work *work) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_fetch_data(int fd, const struct usb_vhci_urb *urb) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_giveback(int fd, const struct usb_vhci_urb *urb) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_port_connect(int fd, uint8_t port, uint8_t data_rate) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_port_disconnect(int fd, uint8_t port) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_port_disable(int fd, uint8_t port) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_port_resumed(int fd, uint8_t port) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_port_overcurrent(int fd, uint8_t port, uint8_t set) _LIB_USB_VHCI_NOTHROW; +int usb_vhci_port_reset_done(int fd, uint8_t port, uint8_t enable) _LIB_USB_VHCI_NOTHROW; + +#define usb_vhci_is_out(epadr) !((epadr) & 0x80) +#define usb_vhci_is_in(epadr) !!((epadr) & 0x80) +#define usb_vhci_is_iso(type) ((type) == USB_VHCI_URB_TYPE_ISO) +#define usb_vhci_is_int(type) ((type) == USB_VHCI_URB_TYPE_INT) +#define usb_vhci_is_control(type) ((type) == USB_VHCI_URB_TYPE_CONTROL) +#define usb_vhci_is_bulk(type) ((type) == USB_VHCI_URB_TYPE_BULK) + +#endif // _LIBUSB_VHCI_H + diff --git a/uisimulator/common/libusb_vhci/vhci-hcd.h b/uisimulator/common/libusb_vhci/vhci-hcd.h new file mode 100644 index 0000000..2271d2a --- /dev/null +++ b/uisimulator/common/libusb_vhci/vhci-hcd.h @@ -0,0 +1,222 @@ +/* + * vhci_hcd.h -- VHCI USB host controller driver header. + * + * Copyright (C) 2007-2008 Conemis AG Karlsruhe Germany + * Copyright (C) 2007-2009 Michael Singer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _VHCI_HCD_H +#define _VHCI_HCD_H + +#ifdef __KERNEL__ +# include +#else +# include +# define __u8 uint8_t +# define __s8 int8_t +# define __u16 uint16_t +# define __s16 int16_t +# define __u32 uint32_t +# define __s32 int32_t +# define __u64 uint64_t +# define __s64 int64_t +#endif +#include + +#ifndef __KERNEL__ + +// wPortStatus bit field +// See USB 2.0 spec Table 11-21 +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +//#define USB_PORT_STAT_TEST 0x0800 +//#define USB_PORT_STAT_INDICATOR 0x1000 + +// wPortChange bit field +// See USB 2.0 spec Table 11-22 +#define USB_PORT_STAT_C_CONNECTION 0x0001 +#define USB_PORT_STAT_C_ENABLE 0x0002 +#define USB_PORT_STAT_C_SUSPEND 0x0004 +#define USB_PORT_STAT_C_OVERCURRENT 0x0008 +#define USB_PORT_STAT_C_RESET 0x0010 + +#endif + +// structure for the VHCI_HCD_IOCREGISTER ioctl +struct vhci_ioc_register +{ + __s32 id; // [out] identifier which was assigned by the kernel + __s32 usb_busnum; // [out] assigned USB bus number + char bus_id[20]; // [out] null-terminated bus-id of the controller + // (something similar to vhci_hcd.) + __u8 port_count; // [in] number of ports the controller should have +}; + +struct vhci_ioc_port_stat +{ + __u16 status; // state of the port + __u16 change; // indicates changed status bits + __u8 index; // index of port + __u8 flags; // additional information from kernel to user space: +#define VHCI_IOC_PORT_STAT_FLAGS_RESUMING 0 // indicates resuming + __u8 reserved1, reserved2; // size of the struct should be dividable by four +}; + +struct vhci_ioc_setup_packet +{ + __u8 bmRequestType; + __u8 bRequest; + __u16 wValue; + __u16 wIndex; + __u16 wLength; +}; + +struct vhci_ioc_urb +{ + struct vhci_ioc_setup_packet setup_packet; // only for control urbs + __s32 buffer_length; // number of bytes which were + // allocated for the buffer + __s32 interval; + __s32 packet_count; // number of iso packets + __u16 flags; // flags: +#define VHCI_IOC_URB_FLAGS_SHORT_NOT_OK 0x0001 // IN: treat incomming short + // packets as an error +#define VHCI_IOC_URB_FLAGS_ISO_ASAP 0x0002 // ISO: schedule as soon as + // possible +#define VHCI_IOC_URB_FLAGS_ZERO_PACKET 0x0040 // BULK OUT: always send a + // short packet at the end + // (send a zero length packet + // if necessary) + __u8 address; // address of the usb device + // for which this urb is for + __u8 endpoint; // endpoint incl. direction + __u8 type; +#define VHCI_IOC_URB_TYPE_ISO 0 +#define VHCI_IOC_URB_TYPE_INT 1 +#define VHCI_IOC_URB_TYPE_CONTROL 2 +#define VHCI_IOC_URB_TYPE_BULK 3 +}; + +union vhci_ioc_work_union +{ + struct vhci_ioc_urb urb; // for VHCI_IOC_WORK_TYPE_PROCESS_URB + struct vhci_ioc_port_stat port; // for VHCI_IOC_WORK_TYPE_PORT_STAT +}; + +struct vhci_ioc_work +{ + __u64 handle; // for VHCI_IOC_WORK_TYPE_PROCESS_URB + // and VHCI_IOC_WORK_TYPE_CANCEL_URB; + // handle which identifies the urb + // (it is just a pointer to the urb + // in kernel space) + union vhci_ioc_work_union work; + __u8 type; +#define VHCI_IOC_WORK_TYPE_PORT_STAT 0 // the state of a port has changed +#define VHCI_IOC_WORK_TYPE_PROCESS_URB 1 // hand an urb to the (virtual) + // hardware +#define VHCI_IOC_WORK_TYPE_CANCEL_URB 2 // cancel urb if it isn't processed + // already +}; + +struct vhci_ioc_iso_packet_data +{ + __u32 offset; + __u32 packet_length; +}; + +struct vhci_ioc_urb_data +{ + __u64 handle; // handle which identifies the urb + void *buffer; // points to the beginning of the data buffer + struct vhci_ioc_iso_packet_data *iso_packets; // points to the beginning of + // the iso packet array + __s32 buffer_length; // number of bytes which were allocated for the buffer + __s32 packet_count; // number of iso packets +}; + +struct vhci_ioc_iso_packet_giveback +{ + __u32 packet_actual; + __s32 status; +}; + +struct vhci_ioc_giveback +{ + __u64 handle; + void *buffer; // only for IN URBs: the received data (for OUT URBs + // always a null pointer) + struct vhci_ioc_iso_packet_giveback *iso_packets; // for ISO + __s32 status; // (ignored for ISO URBs) + __s32 buffer_actual; // number of bytes which were actually transfered + // (for IN-ISOs buffer_actual has to be equal to + // buffer_length; for OUT-ISOs this value will be + // ignored) + __s32 packet_count; // for ISO (has to match with the value from the urb) + __s32 error_count; // for ISO +}; + +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT +#include +struct vhci_ioc_urb_data32 +{ + __u64 handle; + compat_caddr_t buffer; + compat_caddr_t iso_packets; + __s32 buffer_length; + __s32 packet_count; +}; + +struct vhci_ioc_giveback32 +{ + __u64 handle; + compat_caddr_t buffer; + compat_caddr_t iso_packets; + __s32 status; + __s32 buffer_actual; + __s32 packet_count; + __s32 error_count; +}; +#endif +#endif + +#define VHCI_HCD_IOC_MAGIC 138 +#define VHCI_HCD_IOCREGISTER _IOWR(VHCI_HCD_IOC_MAGIC, 0, \ + struct vhci_ioc_register) +#define VHCI_HCD_IOCPORTSTAT _IOW (VHCI_HCD_IOC_MAGIC, 1, \ + struct vhci_ioc_port_stat) +#define VHCI_HCD_IOCFETCHWORK _IOR (VHCI_HCD_IOC_MAGIC, 2, \ + struct vhci_ioc_work) +#define VHCI_HCD_IOCGIVEBACK _IOW (VHCI_HCD_IOC_MAGIC, 3, \ + struct vhci_ioc_giveback) +#define VHCI_HCD_IOCGIVEBACK32 _IOW (VHCI_HCD_IOC_MAGIC, 3, \ + struct vhci_ioc_giveback32) +#define VHCI_HCD_IOCFETCHDATA _IOW (VHCI_HCD_IOC_MAGIC, 4, \ + struct vhci_ioc_urb_data) +#define VHCI_HCD_IOCFETCHDATA32 _IOW (VHCI_HCD_IOC_MAGIC, 4, \ + struct vhci_ioc_urb_data32) +#define VHCI_HCD_IOC_MAXNR 4 + +#endif + diff --git a/uisimulator/common/stubs.c b/uisimulator/common/stubs.c index d1ec4ec..5316b38 100644 --- a/uisimulator/common/stubs.c +++ b/uisimulator/common/stubs.c @@ -35,8 +35,9 @@ #include "ata.h" /* for volume definitions */ +#include "usb.h" /* for usb_detect and USB_EXTRACTED */ + extern char having_new_lcd; -static bool storage_spinning = false; #if CONFIG_CODEC != SWCODEC void audio_set_buffer_margin(int seconds) @@ -56,74 +57,17 @@ bool fat_ismounted(int volume) return true; } -int storage_init(void) -{ - return 1; -} - -int storage_write_sectors(IF_MV2(int drive,) - unsigned long start, - int count, - const void* buf) -{ - IF_MV((void)drive;) - int i; - - for (i=0; i ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * USB driver for the simulator + * + * Copyright (C) 2010 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* parts of this driver are based on the usb-arc driver */ +#define USB_VHCI_DRIVER_DEBUG_LEVEL 2 + +#include "errno.h" +#include "libusb_vhci/libusb_vhci.h" + +#include "debug.h" +#include "kernel.h" +#include "string.h" +#include "usb_ch9.h" +#include "usb_core.h" +#include "usb_drv.h" +#include "system-sdl.h" +#include "malloc.h" +#include +#include + +#if USB_VHCI_DRIVER_DEBUG_LEVEL >= 2 +#define USB_ERRORF DEBUGF +#define USB_DEBUGF DEBUGF +#elif USB_VHCI_DRIVER_DEBUG_LEVEL >= 1 +#define USB_ERRORF DEBUGF +#define USB_DEBUGF(...) ({}) +#else +#define USB_ERRORF(...) ({}) +#define USB_DEBUGF(...) ({}) +#endif + +typedef SDL_mutex *mutex_t; + +#define mutex_lock(m) SDL_LockMutex(*m) +#define mutex_unlock(m) SDL_UnlockMutex(*m) +#define mutex_init(m) *m = SDL_CreateMutex() + +struct urb_list +{ + struct usb_vhci_urb urb; + struct urb_list *next; +}; +/* If an endpoint is a control endpoint, is must be bidirectional so only the [0] + * is used regardeless of the endpoint direction + * In case of IN and OUT transfers, a buffer is allocated for the URB and stored in ->buffer */ +struct usb_endpoint_t +{ + int type[2];/* type of endpoint, must be the same on both directions */ + bool allocated[2];/* is the endpoint allocated ? */ + struct urb_list *pending_urbs[2];/* list of pending URBs (not including current URB) */ + void *buffer[2];/* is there a pending buffer ? NULL if no, address otherwise */ + int buffer_length[2];/* length of the buffer(remaining data to send/recv) */ + int real_buffer_length[2];/* length of the buffer(total transfer size) */ + struct usb_ctrlrequest setup_data;/* copy of setup data sent to core */ + mutex_t mutex[2];/* mutex for buffer/buffer_length/has_urb/urb/pending_urbs */ + struct wakeup xfer_completion[2];/* wakeup for blocking transfers (see drv_send) */ + int xfer_status[2];/* status of the transfer (used for blocking transfers) */ + bool stalled[2];/* is endpoint stalled ? */ +}; + +static int vhci_hcd_fd = -1;/* handle of the vhci control device */ +static int usb_status = USB_UNPOWERED; +static const int vhci_hcd_port = 1; + +static const char vhci_hcd_thread_name[] = "vhci-hcd"; +static unsigned int vhci_hcd_thread_entry = 0; + +static struct usb_endpoint_t endpoints[USB_NUM_ENDPOINTS]; + +#define usb_vhci_ep_num(epadr) (epadr & 0x07) + +#define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT") +#define XFER_TYPE_STR(type) \ + ((type) == USB_ENDPOINT_XFER_CONTROL ? "CTRL" : \ + ((type) == USB_ENDPOINT_XFER_ISOC ? "ISOC" : \ + ((type) == USB_ENDPOINT_XFER_BULK ? "BULK" : \ + ((type) == USB_ENDPOINT_XFER_INT ? "INTR" : "INVL")))) + +#define usb_vhci_type_str(type) \ + ((type) == USB_VHCI_URB_TYPE_CONTROL ? "CTRL" : \ + ((type) == USB_VHCI_URB_TYPE_ISO ? "ISOC" : \ + ((type) == USB_VHCI_URB_TYPE_BULK ? "BULK" : \ + ((type) == USB_VHCI_URB_TYPE_INT ? "INTR" : "INVL")))) + +#define usb_vhci_type_to_usb_xfr_type(type) \ + ((type) == USB_VHCI_URB_TYPE_CONTROL ? USB_ENDPOINT_XFER_CONTROL : \ + ((type) == USB_VHCI_URB_TYPE_ISO ? USB_ENDPOINT_XFER_ISOC : \ + ((type) == USB_VHCI_URB_TYPE_BULK ? USB_ENDPOINT_XFER_BULK : \ + ((type) == USB_VHCI_URB_TYPE_INT ? USB_ENDPOINT_XFER_INT : 4)))) + +static void bus_reset(void); + +/* general part */ +static void change_usb_status(int status) +{ + usb_status = status; + usb_status_event(status); +} + +void usb_attach(void) +{ + USB_DEBUGF("usb_attach\n"); + usb_drv_init(); +} + +int usb_detect(void) +{ + return usb_status; +} + +void usb_enable(bool on) +{ + USB_DEBUGF("usb_enable(%d)\n", on); + if(on) + { + usb_core_init(); + USB_DEBUGF("change usb status\n"); + change_usb_status(USB_INSERTED); + USB_DEBUGF("done\n"); + } + else + { + usb_core_exit(); + } +} + +static int stall_urb(struct usb_vhci_urb *urb) +{ + if(urb == NULL) + { + USB_ERRORF("vhci-hcd: hey, I can't stall a NULL urb !\n"); + return -1; + } + urb->status = -EPIPE; + + USB_DEBUGF("vhci-hcd: stall urb on EP%d %s\n", usb_vhci_ep_num(urb->epadr), + XFER_DIR_STR(usb_vhci_is_in(urb->epadr))); + int ret= usb_vhci_giveback(vhci_hcd_fd, urb); + if(ret < 0) + USB_ERRORF("vhci-hcd: giveback failed (ret=%d)!\n", ret); + return ret; +} + +static int complete_urb(struct usb_vhci_urb *urb) +{ + if(urb == NULL) + { + USB_ERRORF("vhci-hcd: hey, I can't complete a NULL urb !\n"); + return -1; + } + urb->status = 0; + + USB_DEBUGF("vhci-hcd: complete urb on EP%d %s\n", usb_vhci_ep_num(urb->epadr), + XFER_DIR_STR(usb_vhci_is_in(urb->epadr))); + int ret= usb_vhci_giveback(vhci_hcd_fd, urb); + if(ret < 0) + USB_ERRORF("vhci-hcd: giveback failed (ret=%d)!\n", ret); + return ret; +} + +static void clear_endpoint_buffer(int ep_num, bool ep_in) +{ + endpoints[ep_num].buffer[ep_in] = NULL; + endpoints[ep_num].buffer_length[ep_in] = 0; + endpoints[ep_num].real_buffer_length[ep_in] = 0; +} + +/* enqueue an URB, either in pending list or set it as current URB */ +/* NOTE: assume endpoint lock */ +static int enqueue_endpoint_urb(int ep_num, bool ep_in, struct usb_vhci_urb *urb) +{ + USB_DEBUGF("vhci-hcd: enqueue URB on EP%d %s\n", ep_num, XFER_DIR_STR(ep_in)); + + struct urb_list **cur = &endpoints[ep_num].pending_urbs[ep_in]; + struct urb_list *entry = malloc(sizeof(struct urb_list)); + memcpy(&entry->urb, urb, sizeof(struct usb_vhci_urb)); + entry->next = NULL; + + /* allocate buffer */ + if(urb->buffer_length != 0) + { + entry->urb.buffer = malloc(urb->buffer_length); + entry->urb.buffer_actual = 0; + /* retrieve data for OUT transfer */ + if(usb_vhci_is_out(entry->urb.epadr)) + { + int ret = usb_vhci_fetch_data(vhci_hcd_fd, &entry->urb); + if(ret < 0) + { + USB_ERRORF("vhci-hcd: fetch data failed(ret=%d) !\n", ret); + /* stall URB */ + stall_urb(urb); + /* free data */ + free(entry->urb.buffer); + /* don't add URB to the list */ + USB_ERRORF("vhci-hcd: don't enqueue URB on EP%d %s !\n", ep_num, XFER_DIR_STR(ep_in)); + return -1; + } + } + } + + int d = 0; + while(*cur) + { + cur = &(*cur)->next; + d++; + } + + if(d > 100) + USB_ERRORF("vhci-hcd: more than 100 pending URBs !\n"); + + *cur = entry; + + return 0; +} + +/* dequeue the first URB */ +/* NOTE: assume endpoint lock */ +static void dequeue_endpoint_urb(int ep_num, bool ep_in) +{ + if(endpoints[ep_num].pending_urbs[ep_in] == NULL) + { + USB_ERRORF("vhci-hcd: hey I can't dequeue a NULL urb on EP%d %s !\n", ep_num, XFER_DIR_STR(ep_in)); + return; + } + + USB_DEBUGF("vhci-hcd: dequeue URB on EP%d %s\n", ep_num, XFER_DIR_STR(ep_in)); + + struct urb_list *tmp = endpoints[ep_num].pending_urbs[ep_in]->next; + + /* free buffer */ + struct usb_vhci_urb *urb = &endpoints[ep_num].pending_urbs[ep_in]->urb; + if(urb->buffer_length != 0) + free(endpoints[ep_num].pending_urbs[ep_in]->urb.buffer); + + free(endpoints[ep_num].pending_urbs[ep_in]); + endpoints[ep_num].pending_urbs[ep_in] = tmp; +} + +/* cancel ALL endpoints urbs */ +static int cancel_endpoint_urbs(int ep_num, bool ep_in) +{ + if(endpoints[ep_num].pending_urbs[ep_in] == NULL) + return 0; + + USB_DEBUGF("vhci-hcd: cancel URBs on EP%d %s\n", ep_num, XFER_DIR_STR(ep_in)); + /* get lock */ + mutex_lock(&endpoints[ep_num].mutex[ep_in]); + /* stall all pending URBs */ + while(endpoints[ep_num].pending_urbs[ep_in] != NULL) + { + stall_urb(&endpoints[ep_num].pending_urbs[ep_in]->urb); + /* paranoid */ + /*memset(&endpoints[ep_num].pending_urbs[ep_in]->urb, 0, sizeof(struct usb_vhci_urb));*/ + dequeue_endpoint_urb(ep_num, ep_in); + } + + /* clear endpoint buffers */ + clear_endpoint_buffer(ep_num, ep_in); + /* set xfer status to failure */ + endpoints[ep_num].xfer_status[ep_in] = -1; + /* release lock */ + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + + /* notify the core that the transfer failed */ + usb_core_transfer_complete(ep_num, ep_in ? USB_DIR_IN : USB_DIR_OUT, 1, 0); + /* wakup waiting thread if any */ + wakeup_signal(&endpoints[ep_num].xfer_completion[ep_in]); + + return 0; +} + +static int usb_meet_up(int ep_num, bool ep_in) +{ + /* get lock */ + mutex_lock(&endpoints[ep_num].mutex[ep_in]); + + /* WARNING: for control endpoints, ep_in=false because of ^^^^ */ + /* But, the real direction is still available in the urb */ + if(endpoints[ep_num].pending_urbs[ep_in] == NULL) + { + USB_ERRORF("vhci-hcd: hey I can't meet up a NULL urb on EP%d %s !\n", ep_num, XFER_DIR_STR(ep_in)); + + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + return -1; + } + + if(endpoints[ep_num].buffer[ep_in] == NULL) + { + USB_ERRORF("vhci-hcd: hey I can't meet up a NULL buffer EP%d %s\n", ep_num, XFER_DIR_STR(ep_in)); + + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + return -1; + } + + int ret = -1; + + while(endpoints[ep_num].pending_urbs[ep_in] != NULL && + endpoints[ep_num].buffer[ep_in] != NULL) + { + struct usb_vhci_urb *urb = &endpoints[ep_num].pending_urbs[ep_in]->urb; + void *buffer = endpoints[ep_num].buffer[ep_in]; + int buffer_length = endpoints[ep_num].buffer_length[ep_in]; + int real_buffer_length = endpoints[ep_num].real_buffer_length[ep_in]; + + if(ep_num != usb_vhci_ep_num(urb->epadr)) + { + USB_ERRORF("vhci-hcd: WARNING: there is a problem, I complete on EP%d %s but the urb says it's on EP%d %s\n", + ep_num, XFER_DIR_STR(ep_in), usb_vhci_ep_num(urb->epadr), + XFER_DIR_STR(usb_vhci_is_in(urb->epadr))); + + stall_urb(urb); + dequeue_endpoint_urb(ep_num, ep_in); + continue; + } + + USB_DEBUGF("vhci-hcd: On EP%d %s, urb: buffer=%p buffer_length=%d buffer_actual=%d\n", ep_num, XFER_DIR_STR(ep_in), + urb->buffer, urb->buffer_length, urb->buffer_actual); + USB_DEBUGF("vhci-hcd: buffer: ptr=%p buffer_length=%d\n", buffer, buffer_length); + + if(usb_vhci_is_in(urb->epadr)) + { + /* NOTE: for send(aka IN) transfers, buffer_actual is used ! + * NOTE: If the driver sends more data than the urb can handle, fill urb and just advance the buffer + * in this case, clear_endpoint is not called not keep the buffer + * For IN transfers, a URB buffer is allocated by enqueue, so copy */ + + int xfer_actual = MIN(buffer_length, urb->buffer_length - urb->buffer_actual); + memcpy(urb->buffer + urb->buffer_actual, buffer, xfer_actual); + /* update ->buffer_actual (actual URB buffer size) */ + urb->buffer_actual += xfer_actual; + /* subtract transfered data */ + endpoints[ep_num].buffer_length[ep_in] -= xfer_actual; + /* advance buffer */ + endpoints[ep_num].buffer[ep_in] += xfer_actual; + + /* URB is complete when one of the following condition is met: + -URB buffer is filled + -URB buffer is not filled but smaller than maximum packet size + */ + if(urb->buffer_actual == urb->buffer_length + || urb->buffer_length < 1024 + /*|| !(urb->flags & USB_VHCI_URB_FLAGS_SHORT_NOT_OK)*/) + { + ret = complete_urb(urb); + if(ret < 0) + USB_ERRORF("vhci-hcd: meet up failed !\n"); + /* dequeue URB */ + dequeue_endpoint_urb(ep_num, ep_in); + } + else + { + USB_DEBUGF("vhci-hcd: URB partially filled: %d/%d\n", urb->buffer_actual, urb->buffer_length); + ret = 0;/* success */ + } + + /* end of buffer ? or error ? */ + if(endpoints[ep_num].buffer_length[ep_in] == 0 || ret < 0) + { + USB_DEBUGF("vhci-hcd: transfer complete on EP%d IN, %d bytes transfered\n", ep_num, real_buffer_length); + /* clear buffer, set xfer status */ + endpoints[ep_num].xfer_status[ep_in] = ret; + clear_endpoint_buffer(ep_num, ep_in); + + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + /* warn usb core that transfer is finished */ + usb_core_transfer_complete(ep_num, USB_DIR_IN, ret < 0 ? 1 : 0, real_buffer_length); + /* free (potential) waiting thread */ + wakeup_signal(&endpoints[ep_num].xfer_completion[ep_in]); + + return ret; + } + /* or just end of subtransfer ? */ + else + { + USB_DEBUGF("vhci-hcd: subtransfer complete on EP%d IN, %d bytes transfered, %d/%d bytes remaining\n", + ep_num, xfer_actual, endpoints[ep_num].buffer_length[ep_in], + endpoints[ep_num].real_buffer_length[ep_in]); + + /* continue by filling next URB (if there's one) */ + continue; + } + } + else + { + /* NOTE: for recv(aka OUT) transfers, buffer_length is used ! */ + int xfer_actual = MIN(buffer_length, urb->buffer_length - urb->buffer_actual); + + memcpy(buffer, urb->buffer + urb->buffer_actual, xfer_actual); + /* advance buffer */ + endpoints[ep_num].buffer[ep_in] += xfer_actual; + /* update lengths */ + endpoints[ep_num].buffer_length[ep_in] -= xfer_actual; + urb->buffer_actual += xfer_actual; + + /* when an URB is less than 1024 bytes, it's the end of the transfer */ + bool short_urb = (urb->buffer_length < 1024); + /* URB is complete if one of the following condition is met: + -URB buffer is empty + -URB buffer is not empty but buffer is < 1024 */ + if(urb->buffer_actual == urb->buffer_length || + short_urb) + { + ret = complete_urb(urb); + if(ret < 0) + USB_ERRORF("vhci-hcd: meet up failed !\n"); + /* dequeue URB */ + dequeue_endpoint_urb(ep_num, ep_in); + } + else + { + USB_DEBUGF("vhci-hcd: URB partially unfilled: %d/%d\n", urb->buffer_actual, urb->buffer_length); + ret = 0;/* success */ + } + + /* end of buffer ? short urb ? or error ? */ + if(endpoints[ep_num].buffer_length[ep_in] == 0 || short_urb || ret < 0) + { + endpoints[ep_num].xfer_status[ep_in] = ret; + clear_endpoint_buffer(ep_num, ep_in); + + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + + /* total transfered length can be lower than buffer length */ + int total_xfered = real_buffer_length - endpoints[ep_num].buffer_length[ep_in]; + + USB_DEBUGF("vhci-hcd: transfer complete on EP%d OUT, %d bytes transfered\n", ep_num, total_xfered); + usb_core_transfer_complete(ep_num, USB_DIR_OUT, ret < 0 ? 1 : 0, total_xfered); + wakeup_signal(&endpoints[ep_num].xfer_completion[ep_in]); + + return ret; + } + else + { + USB_DEBUGF("vhci-hcd: subtransfer complete on EP%d OUT, %d bytes transfered, %d/%d bytes remaining\n", + ep_num, xfer_actual, endpoints[ep_num].buffer_length[ep_in], + endpoints[ep_num].real_buffer_length[ep_in]); + + /* continue by unfilling next URB (if there's one) */ + continue; + } + } + } + + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + + return ret; +} + +#if USB_VHCI_DRIVER_DEBUG_LEVEL >= 2 +static void dump_urb(struct usb_vhci_urb *urb) +{ + USB_DEBUGF("[%s,EP%d %s,%d", usb_vhci_type_str(urb->type), + usb_vhci_ep_num(urb->epadr), XFER_DIR_STR(usb_vhci_is_in(urb->epadr)), + urb->buffer_length); + if(urb->flags & USB_VHCI_URB_FLAGS_ZERO_PACKET) + USB_DEBUGF(",Z"); + if(urb->flags & USB_VHCI_URB_FLAGS_SHORT_NOT_OK) + USB_DEBUGF(",NS"); + USB_DEBUGF("]"); +} +#endif + +static void dump_pending_urbs(void) +{ + #if USB_VHCI_DRIVER_DEBUG_LEVEL >= 2 + int ep_num; + int ep_in; + + USB_DEBUGF("[---------------pending URBs/buffer----------------]\n"); + for(ep_num=0;ep_num "); + else + USB_DEBUGF(" <%d/%d> ", buffer_length, real_buffer_length); + + while(cur != NULL) + { + struct usb_vhci_urb *urb = &cur->urb; + dump_urb(urb); + + if(cur->next != NULL) + USB_DEBUGF(" ---> "); + cur = cur->next; + } + + USB_DEBUGF("\n"); + } + } + USB_DEBUGF("[--------------------------------------------------]\n"); + #endif +} + +static void process_urb(struct usb_vhci_urb *urb) +{ + int ep_num = usb_vhci_ep_num(urb->epadr); + bool ep_in = usb_vhci_is_in(urb->epadr); + + dump_pending_urbs(); + + switch(urb->type) + { + case USB_VHCI_URB_TYPE_ISO: + USB_ERRORF("Rockbox doesn't handle isochronous transfers !\n"); + stall_urb(urb); + break; + case USB_VHCI_URB_TYPE_INT: + case USB_VHCI_URB_TYPE_BULK: + { + USB_DEBUGF("vhci-hcd: %s transfer on EP%d %s\n", usb_vhci_type_str(urb->type), ep_num, XFER_DIR_STR(ep_in)); + + /* check that: + * -endpoint is allocated + * -urb type match endpoint type + * -endpoint is not stalled + * -endpoint has no pending urb + */ + if(!endpoints[ep_num].allocated[ep_in] || + endpoints[ep_num].type[ep_in] != usb_vhci_type_to_usb_xfr_type(urb->type) || + endpoints[ep_num].stalled[ep_in]) + { + /* */ + if(!endpoints[ep_num].allocated[ep_in]) + USB_ERRORF("vhci-hcd: endpoint EP%d %s is not allocated\n", ep_num, XFER_DIR_STR(ep_in)); + else if(endpoints[ep_num].type[ep_in] != usb_vhci_type_to_usb_xfr_type(urb->type)) + USB_ERRORF("vhci-hcd: endpoint EP%d %s is not a(n) %s endpoint\n", ep_num, XFER_DIR_STR(ep_in), + usb_vhci_type_str(urb->type)); + else if(endpoints[ep_num].stalled[ep_in]) + USB_ERRORF("vhci-hcd: endpoint EP%d %s is stalled\n", ep_num, XFER_DIR_STR(ep_in)); + /* */ + stall_urb(urb); + return; + } + + mutex_lock(&endpoints[ep_num].mutex[ep_in]); + USB_DEBUGF("vhci-hcd: length=%d\n", urb->buffer_length); + + enqueue_endpoint_urb(ep_num, ep_in, urb); + + /* check if there is a pending buffer */ + if(endpoints[ep_num].buffer[ep_in] != NULL) + { + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + /* unlock here because usb_meet_up doesn't it own locking and nothing dangerous can happend between */ + USB_DEBUGF("vhci-hcd: %s transfer with pending buffer, immediate handling\n", + usb_vhci_type_str(urb->type)); + usb_meet_up(ep_num, ep_in); + } + else + { + USB_DEBUGF("vhci-hcd: %s transfer with no pending buffer, delayed handling\n", + usb_vhci_type_str(urb->type)); + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + } + } + break; + case USB_VHCI_URB_TYPE_CONTROL: + { + struct usb_ctrlrequest *req = &endpoints[ep_num].setup_data; + /* control transfers use [0] instead of [ep_on] for urb and buffer */ + + USB_DEBUGF("vhci-hcd: control transfer\n"); + /* check that: + * -endpoint is allocated + * -urb type match endpoint type + * -endpoint is not stalled + * -endpoint has no pending urb + */ + if(!endpoints[ep_num].allocated[0] || + endpoints[ep_num].type[0] != usb_vhci_type_to_usb_xfr_type(urb->type) || + endpoints[ep_num].stalled[ep_in]) + { + /* */ + if(!endpoints[ep_num].allocated[0]) + USB_ERRORF("vhci-hcd: endpoint EP%d is not allocated\n", ep_num); + else if(endpoints[ep_num].type[0] != usb_vhci_type_to_usb_xfr_type(urb->type)) + USB_ERRORF("vhci-hcd: endpoint EP%d is not a control endpoint\n", ep_num); + else if(endpoints[ep_num].stalled[ep_in]) + USB_ERRORF("vhci-hcd: endpoint EP%d is stalled\n", ep_num); + /* */ + stall_urb(urb); + return; + } + + mutex_lock(&endpoints[ep_num].mutex[0]); + enqueue_endpoint_urb(ep_num, 0, urb); + + USB_DEBUGF("vhci-hcd: bmRequestType=0x%02x bRequest=0x%02x wValue=0x%04x\n", urb->bmRequestType, urb->bRequest, + urb->wValue); + USB_DEBUGF("vhci-hcd: wIndex=0x%04x wLength=0x%04x epadr=0x%02x urb_length=0x%x\n", urb->wIndex, + urb->wLength, urb->epadr, urb->buffer_length); + + req->bRequestType = urb->bmRequestType; + req->bRequest = urb->bRequest; + req->wValue = urb->wValue; + req->wIndex = urb->wIndex; + req->wLength = urb->wLength; + + if(ep_num == 0) + usb_core_control_request(req); + + /* FIXME should call usb_core_transfer_complete at some point ? */ + /* FIXME should handle no data control transfers only ? */ + USB_DEBUGF("vhci-hcd: Not sending a transfer complete message for now !\n"); + + mutex_unlock(&endpoints[ep_num].mutex[0]); + } + break; + default: + break; + } + + dump_pending_urbs(); +} + +#define USB_VHCI_PORT_STAT_TRIGGER_DISABLE 0x01 +#define USB_VHCI_PORT_STAT_TRIGGER_SUSPEND 0x02 +#define USB_VHCI_PORT_STAT_TRIGGER_RESUMING 0x04 +#define USB_VHCI_PORT_STAT_TRIGGER_RESET 0x08 +#define USB_VHCI_PORT_STAT_TRIGGER_POWER_ON 0x10 +#define USB_VHCI_PORT_STAT_TRIGGER_POWER_OFF 0x20 + +static uint16_t compute_trigger(uint16_t prev, uint16_t cur) +{ + uint16_t trigger = 0; + #define CHECK_GAIN(val, trig_val) if(!(prev & val) && (cur & val)) trigger |= trig_val; + #define CHECK_LOSS(val, trig_val) if((prev & val) && !(cur & val)) trigger |= trig_val; + + CHECK_LOSS(USB_VHCI_PORT_STAT_CONNECTION, USB_VHCI_PORT_STAT_TRIGGER_DISABLE) + CHECK_GAIN(USB_VHCI_PORT_STAT_SUSPEND, USB_VHCI_PORT_STAT_TRIGGER_SUSPEND) + CHECK_LOSS(USB_VHCI_PORT_STAT_SUSPEND, USB_VHCI_PORT_STAT_TRIGGER_RESUMING) + CHECK_GAIN(USB_VHCI_PORT_STAT_RESET, USB_VHCI_PORT_STAT_TRIGGER_RESET) + CHECK_GAIN(USB_VHCI_PORT_STAT_POWER, USB_VHCI_PORT_STAT_TRIGGER_POWER_ON) + CHECK_LOSS(USB_VHCI_PORT_STAT_POWER, USB_VHCI_PORT_STAT_TRIGGER_POWER_OFF) + + return trigger; +} + +#if 0 +static void vhci_hcd_thread(void) +#else +static int vhci_hcd_thread(void *unused) +#endif +{ + struct usb_vhci_work work; + uint16_t last_status = 0; + + while(1) + { + if(usb_vhci_fetch_work(vhci_hcd_fd, &work) < 0) + { + continue; + } + + sim_enter_irq_handler(); + + if(work.type == USB_VHCI_WORK_TYPE_PORT_STAT) + { + uint16_t trigger = compute_trigger(last_status, work.work.port_stat.status); + last_status = work.work.port_stat.status; + + USB_DEBUGF("vhci-hcd: port: status:"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_CONNECTION) USB_DEBUGF(" connected"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_ENABLE) USB_DEBUGF(" enabled"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_SUSPEND) USB_DEBUGF(" suspended"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_OVERCURRENT) USB_DEBUGF(" overcurrent"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_RESET) USB_DEBUGF(" reset"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_POWER) USB_DEBUGF(" powered"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_LOW_SPEED) USB_DEBUGF(" low-speed"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_HIGH_SPEED) USB_DEBUGF(" high-speed"); + USB_DEBUGF("\n"); + USB_DEBUGF("vhci-hcd: port: change:"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_C_CONNECTION) USB_DEBUGF(" connected"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_C_ENABLE) USB_DEBUGF(" enabled"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_C_SUSPEND) USB_DEBUGF(" suspended"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_C_OVERCURRENT) USB_DEBUGF(" overcurrent"); + if(work.work.port_stat.status & USB_VHCI_PORT_STAT_C_RESET) USB_DEBUGF(" reset"); + USB_DEBUGF("\n"); + USB_DEBUGF("vhci-hcd: port: triggers:"); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_DISABLE) USB_DEBUGF(" disable"); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_SUSPEND) USB_DEBUGF(" suspend"); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_RESUMING) USB_DEBUGF(" resume"); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_RESET) USB_DEBUGF(" reset"); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_POWER_ON) USB_DEBUGF(" power-on"); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_POWER_OFF) USB_DEBUGF(" power-off"); + USB_DEBUGF("\n"); + + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_POWER_ON) + change_usb_status(USB_POWERED); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_POWER_OFF) + change_usb_status(USB_UNPOWERED); + if(trigger & USB_VHCI_PORT_STAT_TRIGGER_RESET) + bus_reset(); + } + else if(work.type == USB_VHCI_WORK_TYPE_PROCESS_URB) + { + /*USB_DEBUGF("vhci-hcd: process urb\n");*/ + process_urb(&work.work.urb); + } + else if(work.type == USB_VHCI_WORK_TYPE_CANCEL_URB) + { + USB_DEBUGF("vhci-hcd: cancel urb\n"); + int ep_num, ep_in; + struct urb_list **cur; + struct urb_list *temp; + struct usb_vhci_urb *urb; + + for(ep_num = 0; ep_num < USB_NUM_ENDPOINTS; ep_num++) + { + for(ep_in = 0; ep_in <= 1; ep_in++) + { + mutex_lock(&endpoints[ep_num].mutex[ep_in]); + + cur = &endpoints[ep_num].pending_urbs[ep_in]; + while(*cur != NULL) + { + if((*cur)->urb.handle == work.work.handle) + { + urb = &(*cur)->urb; + USB_DEBUGF("vhci-hcd: cancel urb [%s,EP%d %s,%d]\n", usb_vhci_type_str(urb->type), + usb_vhci_ep_num(urb->epadr), XFER_DIR_STR(usb_vhci_is_in(urb->epadr)), + urb->buffer_length); + + stall_urb(urb); + temp = *cur; + *cur = (*cur)->next; + free(temp); + goto Lend_cancel; + } + else + cur = &(*cur)->next; + } + + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + } + } + + USB_ERRORF("vhci-hcd: WARNING: couldn't find URB to cancel !\n"); + + Lend_cancel: + ; + } + else + { + USB_ERRORF("vhci-hcd: unknown work type\n"); + } + + sim_exit_irq_handler(); + } + +#if 1 + (void) unused; + return 0; +#endif +} + +static void usb_simulate_power() +{ + int32_t id,usb_busnum; + char *bus_id; + /* one port only */ + vhci_hcd_fd = usb_vhci_open(1, &id, &usb_busnum, &bus_id); + + if(vhci_hcd_fd >= 0) + { + USB_DEBUGF("usb-vhci: host controller at %d:%d (%s)\n", usb_busnum, id, bus_id); + //vhci_hcd_thread_entry = create_thread(vhci_hcd_thread, NULL, 0, 0, vhci_hcd_thread_name); + SDL_CreateThread(vhci_hcd_thread, NULL); + } + else + USB_ERRORF("usb-vhci: couldn't add host controller\n"); +} + +static void usb_simulate_unpower() +{ + change_usb_status(USB_UNPOWERED); +} + +void usb_ask(unsigned int what) +{ + USB_DEBUGF("usb_ask(%d)\n", what); + + switch(what) + { + case USB_ASK_SIMULATE_INSERTION: + usb_simulate_power(); + break; + case USB_ASK_SIMULATE_EXTRACTION: + usb_simulate_unpower(); + break; + default: + USB_ERRORF(" unknown usb_ask request\n"); + } +} + +/* driver part */ +void usb_init_device(void) +{ + int ep_num, ep_dir; + USB_DEBUGF("usb_init_device\n"); + /* preallocate EP0 (to avoid special cases)*/ + endpoints[0].allocated[0] = endpoints[0].allocated[1] = true, + endpoints[0].type[0] = endpoints[0].type[1] = USB_ENDPOINT_XFER_CONTROL; + + /* init mutexes and wakeups */ + for(ep_num = 0; ep_num < USB_NUM_ENDPOINTS; ep_num++) + for(ep_dir = 0; ep_dir <= 1; ep_dir++) + { + mutex_init(&endpoints[ep_num].mutex[ep_dir]); + wakeup_init(&endpoints[ep_num].xfer_completion[ep_dir]); + } +} + +void usb_drv_init(void) +{ + USB_DEBUGF("usb_drv_init\n"); + + if(usb_vhci_port_connect(vhci_hcd_fd, vhci_hcd_port, USB_VHCI_DATA_RATE_HIGH) < 0) + { + USB_ERRORF("usb: vhci-hcd: couldn't connect port !"); + return; + } +} + +void usb_drv_exit(void) +{ + USB_DEBUGF("usb_drv_exit\n"); + + usb_vhci_port_disconnect(vhci_hcd_fd, vhci_hcd_port); + usb_vhci_close(vhci_hcd_fd); +} + +static void bus_reset(void) +{ + int ep_num; + USB_DEBUGF("usb: bus reset\n"); + usb_core_bus_reset(); + usb_drv_cancel_all_transfers(); + + for(ep_num = 0; ep_num < USB_NUM_ENDPOINTS; ep_num++) + endpoints[ep_num].stalled[0] = endpoints[ep_num].stalled[1] = false; + + usb_vhci_port_reset_done(vhci_hcd_fd, vhci_hcd_port, 1); /* enable */ +} + +void usb_drv_stall(int endpoint, bool stall,bool in) +{ + USB_DEBUGF("usb: stall endpoint=%d stall=%d in=%d\n", endpoint, stall, in); + + endpoints[endpoint].stalled[in] = stall; + + if(endpoints[endpoint].type[in] == USB_ENDPOINT_XFER_CONTROL) + in = false; + + if(stall) + cancel_endpoint_urbs(endpoint,in); +} + +bool usb_drv_stalled(int endpoint,bool in) +{ + USB_DEBUGF("usb: ask_stalled endpoint=%d in=%d\n", endpoint, in); + return endpoints[endpoint].stalled[in]; +} + +/* FIXME: could probably use usb_meet_up */ +static int usb_drv_ack_send_recv(int num, bool in) +{ + mutex_lock(&endpoints[num].mutex[in]); + + if(num != 0 && !endpoints[num].allocated[in]) + { + USB_ERRORF("usb: oops, endpoint EP%d %s is not allocated !\n", num, XFER_DIR_STR(in)); + mutex_unlock(&endpoints[num].mutex[in]); + return -1; + } + if(num != 0 && endpoints[num].type[in] != USB_ENDPOINT_XFER_CONTROL) + { + USB_ERRORF("usb: oops, endpoint EP%d is not a control endpoint !\n", num); + mutex_unlock(&endpoints[num].mutex[in]); + return -1; + } + + /* ack can be used for two things: + * -ack the reception of a data packet and say that we're ready to read/write data + * -ack the reception of a nodata packet and say w've done + * + * The first kind of ack is useless here because there's no bus so we ignore it */ + + /* use [0], see previous comments */ + if(endpoints[num].pending_urbs[0] == NULL) + { + USB_ERRORF("usb: no pending urb to ack on EP%d\n", num); + mutex_unlock(&endpoints[num].mutex[in]); + return -1; + } + else + { + if(endpoints[num].pending_urbs[0]->urb.buffer_length != 0) + { + USB_DEBUGF("usb: ignore ack for data control message on EP%d\n", num); + mutex_unlock(&endpoints[num].mutex[in]); + return 0; + } + else + { + USB_DEBUGF("usb: ack urb on EP%d\n", num); + + complete_urb(&endpoints[num].pending_urbs[0]->urb); + dequeue_endpoint_urb(num, 0); + mutex_unlock(&endpoints[num].mutex[in]); + + return 0; + } + } +} + +static int usb_drv_send_recv(int epadr, bool send, void *ptr, int length, bool wait) +{ + /* check epadr */ + int ep_num = EP_NUM(epadr); + bool ep_in = (EP_DIR(epadr) == DIR_IN); + + /* EP0 is special */ + if(ep_num != 0 && ep_in != send) + { + USB_ERRORF("usb: oops, I can't %s on EP%d %s !\n", send ? "send" : "recv", ep_num, XFER_DIR_STR(ep_in)); + return -1; + } + if(ep_num !=0 && !endpoints[ep_num].allocated[ep_in]) + { + USB_ERRORF("usb: oops, EP%d %s is not allocated !\n", ep_num, XFER_DIR_STR(ep_in)); + return -1; + } + /* if control endpoint, set ep_in to false */ + if(endpoints[ep_num].type[ep_in] == USB_ENDPOINT_XFER_CONTROL) + ep_in = false; + + if(endpoints[ep_num].buffer[ep_in] != NULL) + { + USB_ERRORF("usb: oops, send/recv on EP%d %s with already set buffer, overwrite !\n", + ep_num, XFER_DIR_STR(ep_in)); + } + + mutex_lock(&endpoints[ep_num].mutex[ep_in]); + endpoints[ep_num].buffer[ep_in] = ptr; + endpoints[ep_num].buffer_length[ep_in] = length; + endpoints[ep_num].real_buffer_length[ep_in] = length; + + if(endpoints[ep_num].pending_urbs[ep_in] == NULL) + { + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + USB_DEBUGF("usb: send/recv on EP%d %s with no urb: add pending buffer\n", ep_num, XFER_DIR_STR(ep_in)); + + if(wait) + { + /* wait for transfer completion */ + USB_DEBUGF("usb: wait for transfer completion...\n"); + + wakeup_wait(&endpoints[ep_num].xfer_completion[ep_in], TIMEOUT_BLOCK); + + USB_DEBUGF("usb: Back again on EP%d %s: wait finished, result is %d !\n", + ep_num, XFER_DIR_STR(ep_in), endpoints[ep_num].xfer_status[ep_in]); + return endpoints[ep_num].xfer_status[ep_in]; + } + else + return 0; + } + else + { + mutex_unlock(&endpoints[ep_num].mutex[ep_in]); + USB_DEBUGF("usb: send/recv on EP%d %s with urb: meet up !\n", ep_num, XFER_DIR_STR(ep_in)); + return usb_meet_up(ep_num, ep_in); + } + +} + +int usb_drv_send(int endpoint, void* ptr, int length) +{ + /* check for ack */ + if(ptr == NULL && length == 0) + return usb_drv_ack_send_recv(endpoint, false); + else + return usb_drv_send_recv(endpoint, true, ptr, length, true); +} + +int usb_drv_send_nonblocking(int endpoint, void* ptr, int length) +{ + /* check for ack */ + if(ptr == NULL && length == 0) + return usb_drv_ack_send_recv(endpoint, false); + else + return usb_drv_send_recv(endpoint, true, ptr, length, false); +} + +int usb_drv_recv(int endpoint, void* ptr, int length) +{ + /* check for ack */ + if(ptr == NULL && length == 0) + return usb_drv_ack_send_recv(endpoint, true); + else + return usb_drv_send_recv(endpoint, false, ptr, length, false); +} + +void usb_drv_set_address(int address) +{ + USB_DEBUGF("usb: set_address addr=%d\n", address); + (void)address; +} + +int usb_drv_port_speed(void) +{ + USB_DEBUGF("usb: ask_port_speed\n"); + return 1; /* FIXME: 1 for high speed ? */ +} + +void usb_drv_cancel_all_transfers(void) +{ + USB_DEBUGF("usb: cancel_all_transfers\n"); + dump_pending_urbs(); + /* FIXME not sure it does the right thing */ + int ep_num, ep_in; + for(ep_num = 0; ep_num < USB_NUM_ENDPOINTS; ep_num++) + for(ep_in = 0; ep_in <= 1; ep_in++) + cancel_endpoint_urbs(ep_num, ep_in); +} + +void usb_drv_set_test_mode(int mode) +{ + USB_DEBUGF("usb: set_test_mode mode=%d, stub\n", mode); + (void)mode; +} + +bool usb_drv_connected(void) +{ + USB_DEBUGF("usb: ask_connected\n"); + return usb_status == USB_INSERTED; +} + +int usb_drv_request_endpoint(int type, int dir) +{ + int ep_num, ep_dir; + short ep_type; + + /* Safety */ + ep_dir = EP_DIR(dir); + ep_type = type & USB_ENDPOINT_XFERTYPE_MASK; + + USB_DEBUGF("usb: request %s %s\n", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type)); + + /* Find an available ep/dir pair */ + for(ep_num=1;ep_numallocated[ep_dir]) + continue; + + if(endpoint->allocated[other_dir] && + endpoint->type[other_dir] != ep_type) + /* different type */ + continue; + + endpoint->allocated[ep_dir] = true; + endpoint->type[ep_dir] = ep_type; + + USB_DEBUGF("usb: allocate EP%d %s\n", ep_num, XFER_DIR_STR(ep_dir)); + return (ep_num | (dir & USB_ENDPOINT_DIR_MASK)); + } + + USB_DEBUGF("usb: fail to allocate\n"); + return -1; +} + +void usb_drv_release_endpoint(int ep) +{ + int ep_num = EP_NUM(ep); + int ep_dir = EP_DIR(ep); + + USB_DEBUGF("usb: release EP%d %s\n", ep_num, XFER_DIR_STR(ep_dir)); + /* don't release EP0 for technical reasons */ + if(ep_num != 0) + endpoints[ep_num].allocated[ep_dir] = false; +} + + diff --git a/uisimulator/sdl/button.c b/uisimulator/sdl/button.c index 0f9770d..645442a 100644 --- a/uisimulator/sdl/button.c +++ b/uisimulator/sdl/button.c @@ -29,6 +29,7 @@ #include "misc.h" #include "sim_tasks.h" #include "button-sdl.h" +#include "usb.h" #include "backlight.h" #include "debug.h" @@ -83,8 +84,10 @@ void button_event(int key, bool pressed) { int new_btn = 0; static bool usb_connected = false; + /* if (usb_connected && key != USB_KEY) return; + */ switch (key) { @@ -162,10 +165,13 @@ void button_event(int key, bool pressed) if (!pressed) { usb_connected = !usb_connected; +#if defined(HAVE_USBSTACK) + /* Broadcast because we don't have access to usb_queue. Only usb thread will treat the message */ if (usb_connected) - queue_post(&button_queue, SYS_USB_CONNECTED, 0); + queue_broadcast(USB_ASK, USB_ASK_SIMULATE_INSERTION); else - queue_post(&button_queue, SYS_USB_DISCONNECTED, 0); + queue_broadcast(USB_ASK, USB_ASK_SIMULATE_EXTRACTION); +#endif /* defined(HAVE_USBSTACK) */ } return; diff --git a/uisimulator/sdl/system-sdl.h b/uisimulator/sdl/system-sdl.h index 04e033e..0acc509 100644 --- a/uisimulator/sdl/system-sdl.h +++ b/uisimulator/sdl/system-sdl.h @@ -39,6 +39,17 @@ int set_irq_level(int level); #define restore_irq(level) \ ((void)set_irq_level(level)) +#define cpu_idle_mode(on_off) + +#ifndef MIN +#define MIN(a, b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a)>(b))?(a):(b)) +#endif + +void system_reboot(void); void sim_enter_irq_handler(void); void sim_exit_irq_handler(void); bool sim_kernel_init(void);