diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index df3264cfae..a01da381e8 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -346,6 +346,7 @@ set(pcsx2USBSources USB/usb-pad/usb-pad.cpp USB/usb-pad/usb-pad-ff.cpp USB/usb-pad/lg/lg_ff.cpp + USB/usb-pad/usb-seamic.cpp USB/usb-mic/usb-mic-singstar.cpp USB/usb-mic/usb-mic-logitech.cpp USB/usb-mic/usb-headset.cpp diff --git a/pcsx2/USB/USB.cpp b/pcsx2/USB/USB.cpp index 4bfb871fb3..f3b8863b38 100644 --- a/pcsx2/USB/USB.cpp +++ b/pcsx2/USB/USB.cpp @@ -300,13 +300,13 @@ void USBclose() u8 USBread8(u32 addr) { - USB_LOG("* Invalid 8bit read at address %lx\n", addr); + USB_LOG("* Invalid 8bit read at address %08x\n", addr); return 0; } u16 USBread16(u32 addr) { - USB_LOG("* Invalid 16bit read at address %lx\n", addr); + USB_LOG("* Invalid 16bit read at address %08x\n", addr); return 0; } @@ -316,24 +316,24 @@ u32 USBread32(u32 addr) hard = ohci_mem_read(qemu_ohci, addr); - USB_LOG("* Known 32bit read at address %lx: %lx\n", addr, hard); + USB_LOG("* Known 32bit read at address %08x: %08x\n", addr, hard); return hard; } void USBwrite8(u32 addr, u8 value) { - USB_LOG("* Invalid 8bit write at address %lx value %x\n", addr, value); + USB_LOG("* Invalid 8bit write at address %08x value %x\n", addr, value); } void USBwrite16(u32 addr, u16 value) { - USB_LOG("* Invalid 16bit write at address %lx value %x\n", addr, value); + USB_LOG("* Invalid 16bit write at address %08x value %x\n", addr, value); } void USBwrite32(u32 addr, u32 value) { - USB_LOG("* Known 32bit write at address %lx value %lx\n", addr, value); + USB_LOG("* Known 32bit write at address %08x value %08x\n", addr, value); ohci_mem_write(qemu_ohci, addr, value); } @@ -354,7 +354,7 @@ s32 USBfreeze(int mode, freezeData* data) { if ((long unsigned int)data->size < sizeof(USBfreezeData)) { - Console.WriteLn(Color_Red, "USB: Unable to load freeze data! Got %d bytes, expected >= %d.\n", data->size, sizeof(USBfreezeData)); + Console.WriteLn(Color_Red, "USB: Unable to load freeze data! Got %d bytes, expected >= %zu.\n", data->size, sizeof(USBfreezeData)); return -1; } @@ -367,6 +367,11 @@ s32 USBfreeze(int mode, freezeData* data) return -1; } + s8* ptr = data->data + sizeof(USBfreezeData); + // Load the state of the attached devices + if (data->size != sizeof(USBfreezeData) + usbd.device[0].size + usbd.device[1].size + 8192) + return -1; + //TODO Subsequent save state loadings make USB "stall" for n seconds since previous load //clocks = usbd.cycles; //remaining = usbd.remaining; @@ -377,12 +382,11 @@ s32 USBfreeze(int mode, freezeData* data) usbd.t.rhport[i].port.ops = qemu_ohci->rhport[i].port.ops; usbd.t.rhport[i].port.dev = qemu_ohci->rhport[i].port.dev; } + //if (qemu_ohci->usb_packet.iov.iov) + usb_packet_cleanup(&qemu_ohci->usb_packet); *qemu_ohci = usbd.t; - - s8* ptr = data->data + sizeof(USBfreezeData); - // Load the state of the attached devices - if ((long unsigned int)data->size != sizeof(USBfreezeData) + usbd.device[0].size + usbd.device[1].size + 8192) - return -1; + // restore USBPacket for OHCIState + usb_packet_init(&qemu_ohci->usb_packet); RegisterDevice& regInst = RegisterDevice::instance(); for (int i = 0; i < 2; i++) @@ -431,6 +435,21 @@ s32 USBfreeze(int mode, freezeData* data) usb_device[i]->setup_len = tmp.setup_len; usb_device[i]->setup_index = tmp.setup_index; +#ifndef NDEBUG + std::cerr << "Loading save state:\nport: " << i + << "\naddr: " << (int)usb_device[i]->addr + << "\nattached: " << usb_device[i]->attached + << "\nauto_attach: " << usb_device[i]->auto_attach + << "\nconfig: " << usb_device[i]->configuration + << "\nninterf: " << usb_device[i]->ninterfaces + << "\nflags: " << usb_device[i]->flags + << "\nstate: " << usb_device[i]->state + << "\nremote_wakeup: " << usb_device[i]->remote_wakeup + << "\nsetup_state: " << usb_device[i]->setup_state + << "\nsetup_len: " << usb_device[i]->setup_len + << "\nsetup_index: " << usb_device[i]->setup_index + << std::endl; +#endif memcpy(usb_device[i]->data_buf, tmp.data_buf, sizeof(tmp.data_buf)); memcpy(usb_device[i]->setup_buf, tmp.setup_buf, sizeof(tmp.setup_buf)); @@ -442,9 +461,14 @@ s32 USBfreeze(int mode, freezeData* data) } proxy->Freeze(FREEZE_LOAD, usb_device[i], ptr); - //TODO reset port if save state's and configured wheel types are different - usb_detach(&qemu_ohci->rhport[i].port); - usb_attach(&qemu_ohci->rhport[i].port); + if (!usb_device[i]->attached) + { // FIXME FREEZE_SAVE fcked up + usb_device[i]->attached = true; + usb_device_reset(usb_device[i]); + //TODO reset port if save state's and configured wheel types are different + usb_detach(&qemu_ohci->rhport[i].port); + usb_attach(&qemu_ohci->rhport[i].port); + } } else if (!proxy && index != DEVTYPE_NONE) { @@ -455,10 +479,6 @@ s32 USBfreeze(int mode, freezeData* data) } int dev_index = usbd.usb_packet.dev_index; - // restore USBPacket for OHCIState - //if (qemu_ohci->usb_packet.iov.iov) - usb_packet_cleanup(&qemu_ohci->usb_packet); - usb_packet_init(&qemu_ohci->usb_packet); if (usb_device[dev_index]) { @@ -511,7 +531,7 @@ s32 USBfreeze(int mode, freezeData* data) auto proxy = regInst.Device(index); usbd.device[i].index = index; - if (proxy) + if (proxy && usb_device[i]) usbd.device[i].size = proxy->Freeze(FREEZE_SIZE, usb_device[i], nullptr); else usbd.device[i].size = 0; @@ -522,9 +542,10 @@ s32 USBfreeze(int mode, freezeData* data) strncpy(usbd.freezeID, USBfreezeID, strlen(USBfreezeID)); usbd.t = *qemu_ohci; - usbd.usb_packet.ep = qemu_ohci->usb_packet.ep ? *qemu_ohci->usb_packet.ep : USBEndpoint{0}; usbd.t.usb_packet.iov = {}; usbd.t.usb_packet.ep = nullptr; + if (qemu_ohci->usb_packet.ep) + usbd.usb_packet.ep = *qemu_ohci->usb_packet.ep; for (uint32_t i = 0; i < qemu_ohci->num_ports; i++) { @@ -542,14 +563,13 @@ s32 USBfreeze(int mode, freezeData* data) for (int i = 0; i < 2; i++) { auto proxy = regInst.Device(conf.Port[i]); - if (proxy && usbd.device[i].size) + if (usb_device[i]) { - proxy->Freeze(FREEZE_SAVE, usb_device[i], ptr); - if (usb_device[i]) - usbd.device[i].dev = *usb_device[i]; - - memset(&usbd.device[i].dev.klass, 0, sizeof(USBDeviceClass)); + usbd.device[i].dev = *usb_device[i]; + if (proxy && usbd.device[i].size) + proxy->Freeze(FREEZE_SAVE, usb_device[i], ptr); } + memset(&usbd.device[i].dev.klass, 0, sizeof(USBDeviceClass)); ptr += usbd.device[i].size; } diff --git a/pcsx2/USB/configuration.cpp b/pcsx2/USB/configuration.cpp index aa89526ff7..d87669ba47 100644 --- a/pcsx2/USB/configuration.cpp +++ b/pcsx2/USB/configuration.cpp @@ -161,3 +161,17 @@ void ClearSection(const TCHAR* section) s->RemoveAllKeys(); } } + +void RemoveSection(const char* dev_type, int port, const std::string& key) +{ + TSTDSTRING tkey; + tkey.assign(key.begin(), key.end()); + + TSTDSTRINGSTREAM section; + if (dev_type) + section << dev_type << _T(" "); + section << tkey << _T(" ") << port; + TSTDSTRING str = section.str(); + + ciniFile.RemoveSection(str_to_wstr(section.str())); +} diff --git a/pcsx2/USB/configuration.h b/pcsx2/USB/configuration.h index 8c6f43ea51..08b528e344 100644 --- a/pcsx2/USB/configuration.h +++ b/pcsx2/USB/configuration.h @@ -67,6 +67,7 @@ extern Config conf; void SaveConfig(); void LoadConfig(); void ClearSection(const TCHAR* section); +void RemoveSection(const char* dev_type, int port, const std::string& key); extern TSTDSTRING IniPath; extern TSTDSTRING LogDir; diff --git a/pcsx2/USB/device_init.cpp b/pcsx2/USB/device_init.cpp index 3960e13c80..3259056e50 100644 --- a/pcsx2/USB/device_init.cpp +++ b/pcsx2/USB/device_init.cpp @@ -24,6 +24,8 @@ void RegisterDevice::Register() { auto& inst = RegisterDevice::instance(); + if (inst.Map().size()) // FIXME Don't clear proxies, singstar keeps a copy to uninit audio + return; inst.Add(DEVTYPE_PAD, new DeviceProxy()); inst.Add(DEVTYPE_MSD, new DeviceProxy()); inst.Add(DEVTYPE_SINGSTAR, new DeviceProxy()); @@ -34,6 +36,8 @@ void RegisterDevice::Register() inst.Add(DEVTYPE_RBKIT, new DeviceProxy()); inst.Add(DEVTYPE_BUZZ, new DeviceProxy()); inst.Add(DEVTYPE_EYETOY, new DeviceProxy()); + inst.Add(DEVTYPE_BEATMANIA_DADADA, new DeviceProxy()); + inst.Add(DEVTYPE_SEGA_SEAMIC, new DeviceProxy()); RegisterAPIs(); } diff --git a/pcsx2/USB/deviceproxy.h b/pcsx2/USB/deviceproxy.h index cd63c970a0..d6a4593040 100644 --- a/pcsx2/USB/deviceproxy.h +++ b/pcsx2/USB/deviceproxy.h @@ -44,6 +44,8 @@ enum DeviceType DEVTYPE_RBKIT, DEVTYPE_BUZZ, DEVTYPE_EYETOY, + DEVTYPE_BEATMANIA_DADADA, + DEVTYPE_SEGA_SEAMIC, }; struct SelectDeviceName diff --git a/pcsx2/USB/linux/config-gtk.cpp b/pcsx2/USB/linux/config-gtk.cpp index 8788e2040a..c0e8ba8d20 100644 --- a/pcsx2/USB/linux/config-gtk.cpp +++ b/pcsx2/USB/linux/config-gtk.cpp @@ -173,7 +173,7 @@ static void configureApi(GtkWidget* widget, gpointer data) GtkWidget* new_combobox(const char* label, GtkWidget* vbox) { - GtkWidget *ro_label, *rs_hbox, *rs_label, *rs_cb; + GtkWidget *rs_hbox, *rs_label, *rs_cb; rs_hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), rs_hbox, FALSE, TRUE, 0); @@ -218,7 +218,7 @@ void USBconfigure() const char* players[] = {"Player 1:", "Player 2:"}; GtkWidget *rs_cb, *vbox; - uint32_t idx = 0, sel_idx = 0; + uint32_t sel_idx = 0; // Create the dialog window GtkWidget* dlg = gtk_dialog_new_with_buttons( @@ -246,10 +246,15 @@ void USBconfigure() gtk_combo_box_set_active(GTK_COMBO_BOX(rs_cb), 0); auto devices = RegisterDevice::instance().Names(); - int idx = 0, selected = 0; + int idx = 0; for (auto& device : devices) { auto deviceProxy = RegisterDevice::instance().Device(device); + if (!deviceProxy) + { + OSDebugOut(_T("Device '%" SFMTs "' is registered, but failed to get proxy!\n"), device.c_str()); + continue; + } auto name = deviceProxy->Name(); gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), name); idx++; diff --git a/pcsx2/USB/platcompat.h b/pcsx2/USB/platcompat.h index 43670b9810..7cd69e3405 100644 --- a/pcsx2/USB/platcompat.h +++ b/pcsx2/USB/platcompat.h @@ -27,15 +27,19 @@ #ifdef _WIN32 #define CALLBACK __stdcall -#else +#elif defined(__i386__) #define CALLBACK __attribute__((stdcall)) +#else +#define CALLBACK #endif #ifndef EXPORT_C_ #ifdef _MSC_VER #define EXPORT_C_(type) extern "C" type CALLBACK +#elif defined(__i386__) +#define EXPORT_C_(type) extern "C" __attribute__((stdcall, visibility("default"))) type #else -#define EXPORT_C_(type) extern "C" __attribute__((stdcall, externally_visible, visibility("default"))) type +#define EXPORT_C_(type) extern "C" __attribute__((visibility("default"))) type //#define EXPORT_C_(type) extern "C" __attribute__((stdcall,visibility("default"))) type #endif #endif diff --git a/pcsx2/USB/qemu-usb/hid.h b/pcsx2/USB/qemu-usb/hid.h index f119acc9ae..e349393c95 100644 --- a/pcsx2/USB/qemu-usb/hid.h +++ b/pcsx2/USB/qemu-usb/hid.h @@ -31,6 +31,8 @@ #define HID_MOUSE 1 #define HID_TABLET 2 #define HID_KEYBOARD 3 +// idk +#define HID_SUBKIND_BEATMANIA 1 /* scancode without modifiers */ #define SCANCODE_KEYMASK 0xff @@ -323,6 +325,7 @@ struct HIDState uint32_t head; /* index into circular queue */ uint32_t n; int kind; + int sub_kind; int32_t protocol; uint8_t idle; bool idle_pending; diff --git a/pcsx2/USB/qemu-usb/qusb.h b/pcsx2/USB/qemu-usb/qusb.h index cecb9e99ce..6cd46a1033 100644 --- a/pcsx2/USB/qemu-usb/qusb.h +++ b/pcsx2/USB/qemu-usb/qusb.h @@ -461,7 +461,7 @@ struct USBPort USBPortOps* ops; void* opaque; int index; /* internal port index, may be used with the opaque */ - //QTAILQ_ENTRY(USBPort) next; /* Used internally by qemu. */ + //QTAILQ_ENTRY(USBPort) next; /* Used internally by qemu. */ }; typedef void USBCallback(USBPacket* packet, void* opaque); diff --git a/pcsx2/USB/qemu-usb/usb-ohci.cpp b/pcsx2/USB/qemu-usb/usb-ohci.cpp index e56526c25a..9c092e7b9f 100644 --- a/pcsx2/USB/qemu-usb/usb-ohci.cpp +++ b/pcsx2/USB/qemu-usb/usb-ohci.cpp @@ -560,6 +560,11 @@ static int ohci_service_iso_td(OHCIState* ohci, struct ohci_ed* ed, the next ISO TD of the same ED */ //trace_usb_ohci_iso_td_relative_frame_number_big(relative_frame_number, // frame_count); + if (OHCI_CC_DATAOVERRUN == OHCI_BM(iso_td.flags, TD_CC)) + { + /* avoid infinite loop */ + return 1; + } OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN); ed->head &= ~OHCI_DPTR_MASK; ed->head |= (iso_td.next & OHCI_DPTR_MASK); @@ -603,7 +608,14 @@ static int ohci_service_iso_td(OHCIState* ohci, struct ohci_ed* ed, } start_offset = iso_td.offset[relative_frame_number]; - next_offset = iso_td.offset[relative_frame_number + 1]; + if (relative_frame_number < frame_count) + { + next_offset = iso_td.offset[relative_frame_number + 1]; + } + else + { + next_offset = iso_td.be; + } if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || ((relative_frame_number < frame_count) && @@ -647,7 +659,13 @@ static int ohci_service_iso_td(OHCIState* ohci, struct ohci_ed* ed, else { /* Last packet in the ISO TD */ - end_addr = iso_td.be; + end_addr = next_offset; + } + + if (start_addr > end_addr) + { + //trace_usb_ohci_iso_td_bad_cc_overrun(start_addr, end_addr); + return 1; } if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) @@ -658,6 +676,10 @@ static int ohci_service_iso_td(OHCIState* ohci, struct ohci_ed* ed, { len = end_addr - start_addr + 1; } + if (len > sizeof(ohci->usb_buf)) + { + len = sizeof(ohci->usb_buf); + } if (len && dir != OHCI_TD_DIR_IN) { @@ -674,6 +696,11 @@ static int ohci_service_iso_td(OHCIState* ohci, struct ohci_ed* ed, bool int_req = relative_frame_number == frame_count && OHCI_BM(iso_td.flags, TD_DI) == 0; dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) + { + //trace_usb_ohci_td_dev_error(); + return 1; + } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); @@ -851,8 +878,18 @@ static int ohci_service_td(OHCIState* ohci, struct ohci_ed* ed) } else { + if (td.cbp > td.be) + { + //trace_usb_ohci_iso_td_bad_cc_overrun(td.cbp, td.be); + ohci_die(ohci); + return 1; + } len = (td.be - td.cbp) + 1; } + if (len > sizeof(ohci->usb_buf)) + { + len = sizeof(ohci->usb_buf); + } pktlen = len; if (len && dir != OHCI_TD_DIR_IN) @@ -897,6 +934,11 @@ static int ohci_service_td(OHCIState* ohci, struct ohci_ed* ed) return 1; } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) + { + //trace_usb_ohci_td_dev_error(); + return 1; + } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, OHCI_BM(td.flags, TD_DI) == 0); @@ -1042,7 +1084,7 @@ static int ohci_service_ed_list(OHCIState* ohci, uint32_t head, int completion) if (head == 0) return 0; - for (cur = head; cur; cur = next_ed) + for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { if (!ohci_read_ed(ohci, cur, &ed)) { @@ -1053,12 +1095,6 @@ static int ohci_service_ed_list(OHCIState* ohci, uint32_t head, int completion) next_ed = ed.next & OHCI_DPTR_MASK; - if (++link_cnt > ED_LINK_LIMIT) - { - ohci_die(ohci); - return 0; - } - if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; @@ -1445,7 +1481,47 @@ static void ohci_port_set_status(OHCIState* ohci, int portnum, uint32_t val) ohci_set_interrupt(ohci, OHCI_INTR_RHSC); } +#ifdef DEBUG_OHCI +static const char* reg_names[] = { + "HcRevision", + "HcControl", + "HcCommandStatus", + "HcInterruptStatus", + "HcInterruptEnable", + "HcInterruptDisable", + "HcHCCA", + "HcPeriodCurrentED", + "HcControlHeadED", + "HcControlCurrentED", + "HcBulkHeadED", + "HcBulkCurrentED", + "HcDoneHead", + "HcFmInterval", + "HcFmRemaining", + "HcFmNumber", + "HcPeriodicStart", + "HcLSThreshold", + "HcRhDescriptorA", + "HcRhDescriptorB", + "HcRhStatus", +}; + +uint32_t ohci_mem_read_impl(OHCIState* ptr, uint32_t addr); uint32_t ohci_mem_read(OHCIState* ptr, uint32_t addr) +{ + auto val = ohci_mem_read_impl(ptr, addr); + int idx = (addr - ptr->mem_base) >> 2; + if (idx < countof(reg_names)) + { + fprintf(stderr, "ohci_mem_read %s(%d): %08x\n", reg_names[idx], idx, val); + } + return val; +} + +uint32_t ohci_mem_read_impl(OHCIState* ptr, uint32_t addr) +#else +uint32_t ohci_mem_read(OHCIState* ptr, uint32_t addr) +#endif { OHCIState* ohci = ptr; @@ -1463,9 +1539,6 @@ uint32_t ohci_mem_read(OHCIState* ptr, uint32_t addr) /* HcRhPortStatus */ return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; } -#ifdef DEBUG_OHCI - OSDebugOut(TEXT("ohci_mem_read: addr %d\n"), addr >> 2); -#endif switch (addr >> 2) { case 0: /* HcRevision */ @@ -1535,7 +1608,22 @@ uint32_t ohci_mem_read(OHCIState* ptr, uint32_t addr) } } +#ifdef DEBUG_OHCI +void ohci_mem_write_impl(OHCIState* ptr, uint32_t addr, uint32_t val); void ohci_mem_write(OHCIState* ptr, uint32_t addr, uint32_t val) +{ + int idx = (addr - ptr->mem_base) >> 2; + if (idx < countof(reg_names)) + { + fprintf(stderr, "ohci_mem_write %s(%d): %08x\n", reg_names[idx], idx, val); + } + ohci_mem_write_impl(ptr, addr, val); +} + +void ohci_mem_write_impl(OHCIState* ptr, uint32_t addr, uint32_t val) +#else +void ohci_mem_write(OHCIState* ptr, uint32_t addr, uint32_t val) +#endif { OHCIState* ohci = ptr; @@ -1555,9 +1643,6 @@ void ohci_mem_write(OHCIState* ptr, uint32_t addr, uint32_t val) ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); return; } -#ifdef DEBUG_OHCI - OSDebugOut(TEXT("ohci_mem_write: addr %d = 0x%08x\n"), addr >> 2, val); -#endif switch (addr >> 2) { case 1: /* HcControl */ @@ -1658,7 +1743,7 @@ static USBPortOps ohci_port_ops = { /*.detach =*/ohci_detach, //.child_detach = ohci_child_detach, /*.wakeup =*/ohci_wakeup, - //.complete = ohci_async_complete_packet, + /*.complete =*/ohci_async_complete_packet, }; static USBBusOps ohci_bus_ops = {}; diff --git a/pcsx2/USB/usb-eyetoy/cam-linux.cpp b/pcsx2/USB/usb-eyetoy/cam-linux.cpp index c9b071eaa7..d4be9477b4 100644 --- a/pcsx2/USB/usb-eyetoy/cam-linux.cpp +++ b/pcsx2/USB/usb-eyetoy/cam-linux.cpp @@ -44,14 +44,12 @@ namespace usb_eyetoy namespace linux_api { - static pthread_t _eyetoy_thread; - static pthread_t* eyetoy_thread = &_eyetoy_thread; + static pthread_t eyetoy_thread = 0; static unsigned char eyetoy_running = 0; static int fd = -1; buffer_t* buffers; static unsigned int n_buffers; - static int out_buf; static unsigned int pixelformat; buffer_t mpeg_buffer; @@ -384,6 +382,8 @@ namespace usb_eyetoy break; } } + eyetoy_running = 0; + fprintf(stderr, "V4L2 thread quit\n"); return NULL; } @@ -448,15 +448,15 @@ namespace usb_eyetoy if (eyetoy_running) { eyetoy_running = 0; - pthread_join(*eyetoy_thread, NULL); + pthread_join(eyetoy_thread, NULL); v4l_close(); } - eyetoy_running = 1; std::string selectedDevice; LoadSetting(EyeToyWebCamDevice::TypeName(), mPort, APINAME, N_DEVICE, selectedDevice); if (v4l_open(selectedDevice) != 0) return -1; - pthread_create(eyetoy_thread, NULL, &v4l_thread, NULL); + pthread_create(&eyetoy_thread, NULL, &v4l_thread, NULL); + eyetoy_running = 1; return 0; }; @@ -465,7 +465,9 @@ namespace usb_eyetoy if (eyetoy_running) { eyetoy_running = 0; - pthread_join(*eyetoy_thread, NULL); + if (eyetoy_thread) + pthread_join(eyetoy_thread, NULL); + eyetoy_thread = 0; v4l_close(); } return 0; @@ -489,8 +491,6 @@ namespace usb_eyetoy int GtkConfigure(int port, const char* dev_type, void* data) { - GtkWidget *ro_frame, *ro_label, *rs_hbox, *rs_label; - std::string selectedDevice; LoadSetting(dev_type, port, APINAME, N_DEVICE, selectedDevice); @@ -532,7 +532,7 @@ namespace usb_eyetoy int ret = RESULT_OK; if (result == GTK_RESPONSE_OK) { - if (sel_new != sel_idx) + if (devList.size() && sel_new != sel_idx) { if (!SaveSetting(dev_type, port, APINAME, N_DEVICE, devList.at(sel_new))) { diff --git a/pcsx2/USB/usb-eyetoy/cam-windows.cpp b/pcsx2/USB/usb-eyetoy/cam-windows.cpp index 73e79a9ce0..229b0691d8 100644 --- a/pcsx2/USB/usb-eyetoy/cam-windows.cpp +++ b/pcsx2/USB/usb-eyetoy/cam-windows.cpp @@ -13,6 +13,7 @@ * If not, see . */ +#include #include "videodev.h" #include "cam-windows.h" #include "usb-eyetoy-webcam.h" @@ -21,6 +22,104 @@ #include "../Win32/Config.h" #include "../Win32/resource.h" +#ifndef DIBSIZE +#define WIDTHBYTES(BTIS) ((DWORD)(((BTIS) + 31) & (~31)) / 8) +#define DIBWIDTHBYTES(BI) (DWORD)(BI).biBitCount) * (DWORD)WIDTHBYTES((DWORD)(BI).biWidth +#define _DIBSIZE(BI) (DIBWIDTHBYTES(BI) * (DWORD)(BI).biHeight) +#define DIBSIZE(BI) ((BI).biHeight < 0 ? (-1) * (_DIBSIZE(BI)) : _DIBSIZE(BI)) +#endif + +constexpr GUID make_guid(const char* const spec) +{ +#define nybble_from_hex(c) ((c >= '0' && c <= '9') ? (c - '0') : ((c >= 'a' && c <= 'f') ? (c - 'a' + 10) : ((c >= 'A' && c <= 'F') ? (c - 'A' + 10) : 0))) +#define byte_from_hex(c1, c2) ((nybble_from_hex(c1) << 4) | nybble_from_hex(c2)) + + return { + // Data1 + ((((((((((((( + static_cast(nybble_from_hex(spec[0])) + << 4) | + nybble_from_hex(spec[1])) + << 4) | + nybble_from_hex(spec[2])) + << 4) | + nybble_from_hex(spec[3])) + << 4) | + nybble_from_hex(spec[4])) + << 4) | + nybble_from_hex(spec[5])) + << 4) | + nybble_from_hex(spec[6])) + << 4) | + nybble_from_hex(spec[7]), + // Data2 + static_cast( + ((((( + static_cast(nybble_from_hex(spec[9])) + << 4) | + nybble_from_hex(spec[10])) + << 4) | + nybble_from_hex(spec[11])) + << 4) | + nybble_from_hex(spec[12])), + // Data 3 + static_cast( + ((((( + static_cast(nybble_from_hex(spec[14])) + << 4) | + nybble_from_hex(spec[15])) + << 4) | + nybble_from_hex(spec[16])) + << 4) | + nybble_from_hex(spec[17])), + // Data 4 + { + static_cast(byte_from_hex(spec[19], spec[20])), + static_cast(byte_from_hex(spec[21], spec[22])), + static_cast(byte_from_hex(spec[24], spec[25])), + static_cast(byte_from_hex(spec[26], spec[27])), + static_cast(byte_from_hex(spec[28], spec[29])), + static_cast(byte_from_hex(spec[30], spec[31])), + static_cast(byte_from_hex(spec[32], spec[33])), + static_cast(byte_from_hex(spec[34], spec[35]))}}; +} + +#if defined(_MSC_VER) +#define CPPX_MSVC_UUID_FOR(name, spec) \ + class __declspec(uuid(spec)) name +#else +#define CPPX_GNUC_UUID_FOR(name, spec) \ + template <> \ + inline auto __mingw_uuidof() \ + ->GUID const& \ + { \ + static constexpr GUID the_uuid = make_guid(spec); \ + \ + return the_uuid; \ + } \ + \ + template <> \ + inline auto __mingw_uuidof() \ + ->GUID const& \ + { \ + return __mingw_uuidof(); \ + } \ + \ + static_assert(true, "") +#endif + +#if !defined(CPPX_UUID_FOR) +#if defined(_MSC_VER) +#define CPPX_UUID_FOR CPPX_MSVC_UUID_FOR +#elif defined(__GNUC__) +#define CPPX_UUID_FOR CPPX_GNUC_UUID_FOR +#endif +#endif + +CPPX_UUID_FOR(ISampleGrabber, "6b652fff-11fe-4fce-92ad-0266b5d7c78f"); +CPPX_UUID_FOR(ISampleGrabberCB, "0579154a-2b53-4994-b0d0-e773148eff85"); +//CPPX_UUID_FOR(SampleGrabber, "c1f400a0-3f08-11d3-9f0b-006008039e37"); + namespace usb_eyetoy { namespace windows_api @@ -35,8 +134,8 @@ namespace usb_eyetoy if (hr != S_OK) return S_OK; - if (callback) - callback(buffer, sample->GetActualDataLength(), BITS_PER_PIXEL); + if (parent) + std::invoke(&DirectShow::dshow_callback, parent, buffer, sample->GetActualDataLength(), BITS_PER_PIXEL); return S_OK; } @@ -53,6 +152,7 @@ namespace usb_eyetoy std::vector getDevList() { std::vector devList; + devList.push_back(L"None"); ICreateDevEnum* pCreateDevEnum = 0; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum)); @@ -63,486 +163,484 @@ namespace usb_eyetoy } IEnumMoniker* pEnum = 0; - hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, NULL); - if (FAILED(hr)) + hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); + if (hr == S_FALSE || FAILED(hr)) { - fprintf(stderr, "You have no video capture hardware"); - return devList; - }; - - IMoniker* pMoniker = NULL; - while (pEnum->Next(1, &pMoniker, NULL) == S_OK) - { - IPropertyBag* pPropBag; - HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); - if (FAILED(hr)) { - pMoniker->Release(); - continue; - } + fprintf(stderr, "You have no video capture hardware"); + return devList; + }; - VARIANT var; - VariantInit(&var); - hr = pPropBag->Read(L"Description", &var, 0); - if (FAILED(hr)) + IMoniker* pMoniker = NULL; + while (pEnum->Next(1, &pMoniker, NULL) == S_OK) { - hr = pPropBag->Read(L"FriendlyName", &var, 0); - } - if (SUCCEEDED(hr)) - { - devList.push_back(var.bstrVal); - VariantClear(&var); - } - - pPropBag->Release(); - pMoniker->Release(); - } - - pEnum->Release(); - CoUninitialize(); - - return devList; - } - - int DirectShow::InitializeDevice(std::wstring selectedDevice) - { - - // Create the Capture Graph Builder. - HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraphBuilder)); - if (FAILED(hr)) - { - fprintf(stderr, "CoCreateInstance CLSID_CaptureGraphBuilder2 err : %x\n", hr); - return -1; - } - - // Create the Filter Graph Manager. - hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph)); - if (FAILED(hr)) - { - fprintf(stderr, "CoCreateInstance CLSID_FilterGraph err : %x\n", hr); - return -1; - } - - hr = pGraphBuilder->SetFiltergraph(pGraph); - if (FAILED(hr)) - { - fprintf(stderr, "SetFiltergraph err : %x\n", hr); - return -1; - } - - hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); - if (FAILED(hr)) - { - fprintf(stderr, "QueryInterface IID_IMediaControl err : %x\n", hr); - return -1; - } - - // enumerate all video capture devices - ICreateDevEnum* pCreateDevEnum = 0; - hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum)); - if (FAILED(hr)) - { - fprintf(stderr, "Error Creating Device Enumerator"); - return -1; - } - - IEnumMoniker* pEnum = 0; - hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, NULL); - if (FAILED(hr)) - { - fprintf(stderr, "You have no video capture hardware"); - return -1; - }; - - pEnum->Reset(); - - IMoniker* pMoniker; - while (pEnum->Next(1, &pMoniker, NULL) == S_OK && sourcefilter == NULL) - { - IPropertyBag* pPropBag = 0; - hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); - if (FAILED(hr)) - { - fprintf(stderr, "BindToStorage err : %x\n", hr); - goto freeMoniker; - } - - VARIANT var; - VariantInit(&var); - - hr = pPropBag->Read(L"Description", &var, 0); - if (FAILED(hr)) - { - hr = pPropBag->Read(L"FriendlyName", &var, 0); - } - if (FAILED(hr)) - { - fprintf(stderr, "Read name err : %x\n", hr); - goto freeVar; - } - fprintf(stderr, "Camera: '%ls'\n", var.bstrVal); - if (!selectedDevice.empty() && selectedDevice != var.bstrVal) - { - goto freeVar; - } - - //add a filter for the device - hr = pGraph->AddSourceFilterForMoniker(pMoniker, NULL, L"sourcefilter", &sourcefilter); - if (FAILED(hr)) - { - fprintf(stderr, "AddSourceFilterForMoniker err : %x\n", hr); - goto freeVar; - } - - hr = pGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, IID_IAMStreamConfig, (void**)&pSourceConfig); - if (SUCCEEDED(hr)) - { - int iCount = 0, iSize = 0; - hr = pSourceConfig->GetNumberOfCapabilities(&iCount, &iSize); - - // Check the size to make sure we pass in the correct structure. - if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) + IPropertyBag* pPropBag; + HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); + if (FAILED(hr)) { - // Use the video capabilities structure. - for (int iFormat = 0; iFormat < iCount; iFormat++) + pMoniker->Release(); + continue; + } + + VARIANT var; + VariantInit(&var); + hr = pPropBag->Read(L"Description", &var, 0); + if (FAILED(hr)) + { + hr = pPropBag->Read(L"FriendlyName", &var, 0); + } + if (SUCCEEDED(hr)) + { + devList.push_back(var.bstrVal); + VariantClear(&var); + } + + pPropBag->Release(); + pMoniker->Release(); + } + + pEnum->Release(); + CoUninitialize(); + + return devList; + } + + int DirectShow::InitializeDevice(std::wstring selectedDevice) + { + + // Create the Capture Graph Builder. + HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraphBuilder)); + if (FAILED(hr)) + { + fprintf(stderr, "CoCreateInstance CLSID_CaptureGraphBuilder2 err : %x\n", hr); + return -1; + } + + // Create the Filter Graph Manager. + hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph)); + if (FAILED(hr)) + { + fprintf(stderr, "CoCreateInstance CLSID_FilterGraph err : %x\n", hr); + return -1; + } + + hr = pGraphBuilder->SetFiltergraph(pGraph); + if (FAILED(hr)) + { + fprintf(stderr, "SetFiltergraph err : %x\n", hr); + return -1; + } + + hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); + if (FAILED(hr)) + { + fprintf(stderr, "QueryInterface IID_IMediaControl err : %x\n", hr); + return -1; + } + + // enumerate all video capture devices + ICreateDevEnum* pCreateDevEnum = 0; + hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum)); + if (FAILED(hr)) + { + fprintf(stderr, "Error Creating Device Enumerator"); + return -1; + } + + IEnumMoniker* pEnum = 0; + hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); + if (hr == S_FALSE || FAILED(hr)) + { + { + fprintf(stderr, "You have no video capture hardware"); + return -1; + }; + + pEnum->Reset(); + + IMoniker* pMoniker; + while (pEnum->Next(1, &pMoniker, NULL) == S_OK && sourcefilter == NULL) + { + IPropertyBag* pPropBag = 0; + hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag)); + if (FAILED(hr)) { - VIDEO_STREAM_CONFIG_CAPS scc; - AM_MEDIA_TYPE* pmtConfig; - hr = pSourceConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc); - fprintf(stderr, "GetStreamCaps min=%dx%d max=%dx%d, fmt=%x\n", - scc.MinOutputSize.cx, scc.MinOutputSize.cy, - scc.MaxOutputSize.cx, scc.MaxOutputSize.cy, - pmtConfig->subtype); + fprintf(stderr, "BindToStorage err : %x\n", hr); + goto freeMoniker; + } - if (SUCCEEDED(hr)) + VARIANT var; + VariantInit(&var); + + hr = pPropBag->Read(L"Description", &var, 0); + if (FAILED(hr)) + { + hr = pPropBag->Read(L"FriendlyName", &var, 0); + } + if (FAILED(hr)) + { + fprintf(stderr, "Read name err : %x\n", hr); + goto freeVar; + } + fprintf(stderr, "Camera: '%ls'\n", var.bstrVal); + if (!selectedDevice.empty() && selectedDevice != var.bstrVal) + { + goto freeVar; + } + + //add a filter for the device + hr = pGraph->AddSourceFilterForMoniker(pMoniker, NULL, L"sourcefilter", &sourcefilter); + if (FAILED(hr)) + { + fprintf(stderr, "AddSourceFilterForMoniker err : %x\n", hr); + goto freeVar; + } + + hr = pGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, IID_IAMStreamConfig, (void**)&pSourceConfig); + if (SUCCEEDED(hr)) + { + int iCount = 0, iSize = 0; + hr = pSourceConfig->GetNumberOfCapabilities(&iCount, &iSize); + + // Check the size to make sure we pass in the correct structure. + if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)) { - if ((pmtConfig->majortype == MEDIATYPE_Video) && - (pmtConfig->formattype == FORMAT_VideoInfo) && - (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) && - (pmtConfig->pbFormat != NULL)) + // Use the video capabilities structure. + for (int iFormat = 0; iFormat < iCount; iFormat++) { + VIDEO_STREAM_CONFIG_CAPS scc; + AM_MEDIA_TYPE* pmtConfig; + hr = pSourceConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc); + fprintf(stderr, "GetStreamCaps min=%dx%d max=%dx%d, fmt=%x\n", + scc.MinOutputSize.cx, scc.MinOutputSize.cy, + scc.MaxOutputSize.cx, scc.MaxOutputSize.cy, + pmtConfig->subtype); - VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat; - pVih->bmiHeader.biWidth = 320; - pVih->bmiHeader.biHeight = 240; - pVih->bmiHeader.biSizeImage = DIBSIZE(pVih->bmiHeader); - hr = pSourceConfig->SetFormat(pmtConfig); + if (SUCCEEDED(hr)) + { + if ((pmtConfig->majortype == MEDIATYPE_Video) && + (pmtConfig->formattype == FORMAT_VideoInfo) && + (pmtConfig->cbFormat >= sizeof(VIDEOINFOHEADER)) && + (pmtConfig->pbFormat != NULL)) + { + + VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat; + pVih->bmiHeader.biWidth = 320; + pVih->bmiHeader.biHeight = 240; + pVih->bmiHeader.biSizeImage = DIBSIZE(pVih->bmiHeader); + hr = pSourceConfig->SetFormat(pmtConfig); + } + //DeleteMediaType(pmtConfig); + } } - //DeleteMediaType(pmtConfig); } } + + // Create the Sample Grabber filter. + hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&samplegrabberfilter)); + if (FAILED(hr)) + { + fprintf(stderr, "CoCreateInstance CLSID_SampleGrabber err : %x\n", hr); + goto freeVar; + } + + hr = pGraph->AddFilter(samplegrabberfilter, L"samplegrabberfilter"); + if (FAILED(hr)) + { + fprintf(stderr, "AddFilter samplegrabberfilter err : %x\n", hr); + goto freeVar; + } + + //set mediatype on the samplegrabber + hr = samplegrabberfilter->QueryInterface(IID_PPV_ARGS(&samplegrabber)); + if (FAILED(hr)) + { + fprintf(stderr, "QueryInterface err : %x\n", hr); + goto freeVar; + } + + AM_MEDIA_TYPE mt; + ZeroMemory(&mt, sizeof(mt)); + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + hr = samplegrabber->SetMediaType(&mt); + if (FAILED(hr)) + { + fprintf(stderr, "SetMediaType err : %x\n", hr); + goto freeVar; + } + + //add the callback to the samplegrabber + hr = samplegrabber->SetCallback(callbackhandler, 0); + if (hr != S_OK) + { + fprintf(stderr, "SetCallback err : %x\n", hr); + goto freeVar; + } + + //set the null renderer + hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&nullrenderer)); + if (FAILED(hr)) + { + fprintf(stderr, "CoCreateInstance CLSID_NullRenderer err : %x\n", hr); + goto freeVar; + } + + hr = pGraph->AddFilter(nullrenderer, L"nullrenderer"); + if (FAILED(hr)) + { + fprintf(stderr, "AddFilter nullrenderer err : %x\n", hr); + goto freeVar; + } + + //set the render path + hr = pGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, sourcefilter, samplegrabberfilter, nullrenderer); + if (FAILED(hr)) + { + fprintf(stderr, "RenderStream err : %x\n", hr); + goto freeVar; + } + + // if the stream is started, start capturing immediatly + LONGLONG start, stop; + start = 0; + stop = MAXLONGLONG; + hr = pGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, &start, &stop, 1, 2); + if (FAILED(hr)) + { + fprintf(stderr, "ControlStream err : %x\n", hr); + goto freeVar; + } + + freeVar: + VariantClear(&var); + pPropBag->Release(); + + freeMoniker: + pMoniker->Release(); + } + pEnum->Release(); + if (sourcefilter == NULL) + { + return -1; + } + return 0; + } + + void DirectShow::Start() + { + HRESULT hr = nullrenderer->Run(0); + if (FAILED(hr)) + throw hr; + + hr = samplegrabberfilter->Run(0); + if (FAILED(hr)) + throw hr; + + hr = sourcefilter->Run(0); + if (FAILED(hr)) + throw hr; + } + + void DirectShow::Stop() + { + if (!sourcefilter) + return; + HRESULT hr = sourcefilter->Stop(); + if (FAILED(hr)) + throw hr; + + hr = samplegrabberfilter->Stop(); + if (FAILED(hr)) + throw hr; + + hr = nullrenderer->Stop(); + if (FAILED(hr)) + throw hr; + } + void DirectShow::store_mpeg_frame(const std::vector& data) + { + std::lock_guard lk(mpeg_mutex); + mpeg_buffer = data; + } + + void DirectShow::dshow_callback(unsigned char* data, int len, int bitsperpixel) + { + if (bitsperpixel == 24) + { + std::vector mpegData(320 * 240 * 2); + int mpegLen = jo_write_mpeg(mpegData.data(), data, 320, 240, JO_RGB24, JO_FLIP_X, JO_FLIP_Y); + //OSDebugOut(_T("MPEG: alloced: %d, got: %d\n"), mpegData.size(), mpegLen); + mpegData.resize(mpegLen); + store_mpeg_frame(mpegData); + } + else + { + fprintf(stderr, "dshow_callback: unk format: len=%d bpp=%d\n", len, bitsperpixel); } } - // Create the Sample Grabber filter. - hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&samplegrabberfilter)); - if (FAILED(hr)) + void DirectShow::create_dummy_frame() { - fprintf(stderr, "CoCreateInstance CLSID_SampleGrabber err : %x\n", hr); - goto freeVar; + const int width = 320; + const int height = 240; + const int bytesPerPixel = 3; + + std::vector rgbData(width * height * bytesPerPixel, 0); + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + unsigned char* ptr = &rgbData[(y * width + x) * bytesPerPixel]; + int c = (255 * y) / height; + ptr[0] = 255 - c; + ptr[1] = c; + ptr[2] = 255 - c; + } + } + std::vector mpegData(width * height * bytesPerPixel, 255); + int mpegLen = jo_write_mpeg(mpegData.data(), rgbData.data(), width, height, JO_RGB24, JO_NONE, JO_NONE); + mpegData.resize(mpegLen); + store_mpeg_frame(mpegData); } - hr = pGraph->AddFilter(samplegrabberfilter, L"samplegrabberfilter"); - if (FAILED(hr)) + DirectShow::DirectShow(int port) { - fprintf(stderr, "AddFilter samplegrabberfilter err : %x\n", hr); - goto freeVar; + mPort = port; + pGraphBuilder = NULL; + pGraph = NULL; + pControl = NULL; + sourcefilter = NULL; + samplegrabberfilter = NULL; + nullrenderer = NULL; + pSourceConfig = NULL; + samplegrabber = NULL; + callbackhandler = new CallbackHandler(this); + CoInitialize(NULL); } - //set mediatype on the samplegrabber - hr = samplegrabberfilter->QueryInterface(IID_PPV_ARGS(&samplegrabber)); - if (FAILED(hr)) + int DirectShow::Open() { - fprintf(stderr, "QueryInterface err : %x\n", hr); - goto freeVar; - } + mpeg_buffer.resize(320 * 240 * 2); + std::fill(mpeg_buffer.begin(), mpeg_buffer.end(), 0); - AM_MEDIA_TYPE mt; - ZeroMemory(&mt, sizeof(mt)); - mt.majortype = MEDIATYPE_Video; - mt.subtype = MEDIASUBTYPE_RGB24; - hr = samplegrabber->SetMediaType(&mt); - if (FAILED(hr)) - { - fprintf(stderr, "SetMediaType err : %x\n", hr); - goto freeVar; - } - - //add the callback to the samplegrabber - hr = samplegrabber->SetCallback(callbackhandler, 0); - if (hr != S_OK) - { - fprintf(stderr, "SetCallback err : %x\n", hr); - goto freeVar; - } - - //set the null renderer - hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&nullrenderer)); - if (FAILED(hr)) - { - fprintf(stderr, "CoCreateInstance CLSID_NullRenderer err : %x\n", hr); - goto freeVar; - } - - hr = pGraph->AddFilter(nullrenderer, L"nullrenderer"); - if (FAILED(hr)) - { - fprintf(stderr, "AddFilter nullrenderer err : %x\n", hr); - goto freeVar; - } - - //set the render path - hr = pGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, sourcefilter, samplegrabberfilter, nullrenderer); - if (FAILED(hr)) - { - fprintf(stderr, "RenderStream err : %x\n", hr); - goto freeVar; - } - - // if the stream is started, start capturing immediatly - LONGLONG start = 0, stop = MAXLONGLONG; - hr = pGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, sourcefilter, &start, &stop, 1, 2); - if (FAILED(hr)) - { - fprintf(stderr, "ControlStream err : %x\n", hr); - goto freeVar; - } - - freeVar: - VariantClear(&var); - pPropBag->Release(); - - freeMoniker: - pMoniker->Release(); - } - pEnum->Release(); - if (sourcefilter == NULL) - { - return -1; - } - return 0; - } - - void DirectShow::Start() - { - HRESULT hr = nullrenderer->Run(0); - if (FAILED(hr)) - throw hr; - - hr = samplegrabberfilter->Run(0); - if (FAILED(hr)) - throw hr; - - hr = sourcefilter->Run(0); - if (FAILED(hr)) - throw hr; - } - - void DirectShow::Stop() - { - HRESULT hr = sourcefilter->Stop(); - if (FAILED(hr)) - throw hr; - - hr = samplegrabberfilter->Stop(); - if (FAILED(hr)) - throw hr; - - hr = nullrenderer->Stop(); - if (FAILED(hr)) - throw hr; - } - - buffer_t mpeg_buffer{}; - std::mutex mpeg_mutex; - - void store_mpeg_frame(unsigned char* data, unsigned int len) - { - mpeg_mutex.lock(); - memcpy(mpeg_buffer.start, data, len); - mpeg_buffer.length = len; - mpeg_mutex.unlock(); - } - - void dshow_callback(unsigned char* data, int len, int bitsperpixel) - { - if (bitsperpixel == 24) - { - unsigned char* mpegData = (unsigned char*)calloc(1, 320 * 240 * 2); - int mpegLen = jo_write_mpeg(mpegData, data, 320, 240, JO_RGB24, JO_FLIP_X, JO_FLIP_Y); - store_mpeg_frame(mpegData, mpegLen); - free(mpegData); - } - else - { - fprintf(stderr, "dshow_callback: unk format: len=%d bpp=%d\n", len, bitsperpixel); - } - } - - void create_dummy_frame() - { - const int width = 320; - const int height = 240; - const int bytesPerPixel = 3; - - unsigned char* rgbData = (unsigned char*)calloc(1, width * height * bytesPerPixel); - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - unsigned char* ptr = rgbData + (y * width + x) * bytesPerPixel; - ptr[0] = 255 - y; - ptr[1] = y; - ptr[2] = 255 - y; - } - } - unsigned char* mpegData = (unsigned char*)calloc(1, width * height * bytesPerPixel); - int mpegLen = jo_write_mpeg(mpegData, rgbData, width, height, JO_RGB24, JO_NONE, JO_NONE); - free(rgbData); - - store_mpeg_frame(mpegData, mpegLen); - free(mpegData); - } - - DirectShow::DirectShow(int port) - { - mPort = port; - pGraphBuilder = NULL; - pGraph = NULL; - pControl = NULL; - sourcefilter = NULL; - samplegrabberfilter = NULL; - nullrenderer = NULL; - pSourceConfig = NULL; - samplegrabber = NULL; - callbackhandler = new CallbackHandler(); - CoInitialize(NULL); - } - - int DirectShow::Open() - { - mpeg_buffer.start = calloc(1, 320 * 240 * 2); - create_dummy_frame(); - - std::wstring selectedDevice; - LoadSetting(EyeToyWebCamDevice::TypeName(), Port(), APINAME, N_DEVICE, selectedDevice); - - int ret = InitializeDevice(selectedDevice); - if (ret < 0) - { - fprintf(stderr, "Camera: cannot find '%ls'\n", selectedDevice.c_str()); - return -1; - } - - pControl->Run(); - this->Stop(); - this->SetCallback(dshow_callback); - this->Start(); - - return 0; - }; - - int DirectShow::Close() - { - if (sourcefilter != NULL) - { - this->Stop(); - pControl->Stop(); - - sourcefilter->Release(); - pSourceConfig->Release(); - samplegrabberfilter->Release(); - samplegrabber->Release(); - nullrenderer->Release(); - } - - pGraphBuilder->Release(); - pGraph->Release(); - pControl->Release(); - - if (mpeg_buffer.start != NULL) - { - free(mpeg_buffer.start); - mpeg_buffer.start = NULL; - } - return 0; - }; - - int DirectShow::GetImage(uint8_t* buf, int len) - { - mpeg_mutex.lock(); - int len2 = mpeg_buffer.length; - if (len < mpeg_buffer.length) - len2 = len; - memcpy(buf, mpeg_buffer.start, len2); - mpeg_mutex.unlock(); - return len2; - }; - - BOOL CALLBACK DirectShowDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - int port; - - switch (uMsg) - { - case WM_CREATE: - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); - break; - case WM_INITDIALOG: - { - port = (int)lParam; - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); + create_dummy_frame(); std::wstring selectedDevice; - LoadSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice); - SendDlgItemMessage(hW, IDC_COMBO1, CB_RESETCONTENT, 0, 0); + LoadSetting(EyeToyWebCamDevice::TypeName(), Port(), APINAME, N_DEVICE, selectedDevice); - std::vector devList = getDevList(); - for (auto i = 0; i != devList.size(); i++) + int ret = InitializeDevice(selectedDevice); + if (ret < 0) { - SendDlgItemMessageW(hW, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)devList[i].c_str()); - if (selectedDevice == devList.at(i)) - { - SendDlgItemMessage(hW, IDC_COMBO1, CB_SETCURSEL, i, i); - } + fprintf(stderr, "Camera: cannot find '%ls'\n", selectedDevice.c_str()); + return -1; } - return TRUE; - } - case WM_COMMAND: - if (HIWORD(wParam) == BN_CLICKED) + + pControl->Run(); + this->Stop(); + this->Start(); + + return 0; + }; + + int DirectShow::Close() + { + if (!sourcefilter) + return 0; + + this->Stop(); + pControl->Stop(); + + sourcefilter->Release(); + pSourceConfig->Release(); + samplegrabberfilter->Release(); + samplegrabber->Release(); + nullrenderer->Release(); + sourcefilter = nullptr; + + pGraphBuilder->Release(); + pGraph->Release(); + pControl->Release(); + + std::lock_guard lck(mpeg_mutex); + mpeg_buffer.resize(0); + return 0; + }; + + int DirectShow::GetImage(uint8_t * buf, int len) + { + std::lock_guard lck(mpeg_mutex); + int len2 = mpeg_buffer.size(); + if (len < mpeg_buffer.size()) + len2 = len; + memcpy(buf, mpeg_buffer.data(), len2); + return len2; + }; + + BOOL CALLBACK DirectShowDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + int port; + + switch (uMsg) { - switch (LOWORD(wParam)) + case WM_CREATE: + SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); + break; + case WM_INITDIALOG: { - case IDOK: + port = (int)lParam; + SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); + + std::wstring selectedDevice; + LoadSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice); + SendDlgItemMessage(hW, IDC_COMBO1, CB_RESETCONTENT, 0, 0); + + std::vector devList = getDevList(); + for (auto i = 0; i != devList.size(); i++) { - INT_PTR res = RESULT_OK; - static wchar_t selectedDevice[500] = {0}; - GetWindowTextW(GetDlgItem(hW, IDC_COMBO1), selectedDevice, countof(selectedDevice)); - port = (int)GetWindowLongPtr(hW, GWLP_USERDATA); - if (!SaveSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice)) + SendDlgItemMessageW(hW, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)devList[i].c_str()); + if (selectedDevice == devList.at(i)) { - res = RESULT_FAILED; + SendDlgItemMessage(hW, IDC_COMBO1, CB_SETCURSEL, i, i); } - EndDialog(hW, res); - return TRUE; } - case IDCANCEL: - EndDialog(hW, RESULT_CANCELED); - return TRUE; + return TRUE; } + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + switch (LOWORD(wParam)) + { + case IDOK: + { + INT_PTR res = RESULT_OK; + static wchar_t selectedDevice[500] = {0}; + GetWindowTextW(GetDlgItem(hW, IDC_COMBO1), selectedDevice, countof(selectedDevice)); + port = (int)GetWindowLongPtr(hW, GWLP_USERDATA); + if (!SaveSetting(EyeToyWebCamDevice::TypeName(), port, APINAME, N_DEVICE, selectedDevice)) + { + res = RESULT_FAILED; + } + EndDialog(hW, res); + return TRUE; + } + case IDCANCEL: + EndDialog(hW, RESULT_CANCELED); + return TRUE; + } + } } - } - return FALSE; - } + return FALSE; + } - int DirectShow::Configure(int port, const char* dev_type, void* data) - { - Win32Handles handles = *(Win32Handles*)data; - return DialogBoxParam(handles.hInst, - MAKEINTRESOURCE(IDD_DLG_EYETOY), - handles.hWnd, - (DLGPROC)DirectShowDlgProc, port); - }; + int DirectShow::Configure(int port, const char* dev_type, void* data) + { + Win32Handles handles = *(Win32Handles*)data; + return DialogBoxParam(handles.hInst, + MAKEINTRESOURCE(IDD_DLG_EYETOY), + handles.hWnd, + (DLGPROC)DirectShowDlgProc, port); + }; - } // namespace windows_api -} // namespace usb_eyetoy + } // namespace windows_api + } // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-eyetoy/cam-windows.h b/pcsx2/USB/usb-eyetoy/cam-windows.h index b9e3b224cd..05d6c78a84 100644 --- a/pcsx2/USB/usb-eyetoy/cam-windows.h +++ b/pcsx2/USB/usb-eyetoy/cam-windows.h @@ -28,14 +28,14 @@ extern GUID CLSID_NullRenderer; } #pragma region qedit.h -struct __declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85")) +struct //__declspec(uuid("0579154a-2b53-4994-b0d0-e773148eff85")) ISampleGrabberCB : IUnknown { virtual HRESULT __stdcall SampleCB(double SampleTime, struct IMediaSample* pSample) = 0; virtual HRESULT __stdcall BufferCB(double SampleTime, unsigned char* pBuffer, long BufferLen) = 0; }; -struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f")) +struct //__declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f")) ISampleGrabber : IUnknown { virtual HRESULT __stdcall SetOneShot(long OneShot) = 0; @@ -47,8 +47,8 @@ struct __declspec(uuid("6b652fff-11fe-4fce-92ad-0266b5d7c78f")) virtual HRESULT __stdcall SetCallback(struct ISampleGrabberCB* pCallback, long WhichMethodToCallback) = 0; }; -struct __declspec(uuid("c1f400a0-3f08-11d3-9f0b-006008039e37")) - SampleGrabber; +//struct __declspec(uuid("c1f400a0-3f08-11d3-9f0b-006008039e37")) +// SampleGrabber; #pragma endregion @@ -72,12 +72,6 @@ namespace usb_eyetoy typedef void (*DShowVideoCaptureCallback)(unsigned char* data, int len, int bitsperpixel); - typedef struct - { - void* start = NULL; - size_t length = 0; - } buffer_t; - static const char* APINAME = "DirectShow"; class DirectShow : public VideoDevice @@ -100,10 +94,12 @@ namespace usb_eyetoy void Port(int port) { mPort = port; } protected: - void SetCallback(DShowVideoCaptureCallback cb) { callbackhandler->SetCallback(cb); } void Start(); void Stop(); int InitializeDevice(std::wstring selectedDevice); + void store_mpeg_frame(const std::vector& data); + void create_dummy_frame(); + void dshow_callback(unsigned char* data, int len, int bitsperpixel); private: int mPort; @@ -118,10 +114,16 @@ namespace usb_eyetoy ISampleGrabber* samplegrabber; IBaseFilter* nullrenderer; + std::vector mpeg_buffer{}; + std::mutex mpeg_mutex; + class CallbackHandler : public ISampleGrabberCB { public: - CallbackHandler() { callback = 0; } + CallbackHandler(DirectShow* parent_) + : parent(parent_) + { + } ~CallbackHandler() {} void SetCallback(DShowVideoCaptureCallback cb) { callback = cb; } @@ -133,7 +135,7 @@ namespace usb_eyetoy virtual ULONG __stdcall Release() { return 2; } private: - DShowVideoCaptureCallback callback; + DirectShow* parent; } * callbackhandler; }; diff --git a/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp b/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp index b34a3f1c03..d0262f9a1f 100644 --- a/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp +++ b/pcsx2/USB/usb-eyetoy/usb-eyetoy-webcam.cpp @@ -454,7 +454,7 @@ namespace usb_eyetoy s->frame_step++; } - else if (s->frame_step < 10) + else if (s->mpeg_frame_offset < s->mpeg_frame_size) { int data_pk = s->mpeg_frame_size - s->mpeg_frame_offset; if (data_pk > max_ep_size) @@ -464,7 +464,7 @@ namespace usb_eyetoy s->frame_step++; } - else if (s->frame_step == 10) + else { uint8_t footer[] = { 0xFF, 0xFF, 0xFF, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -518,7 +518,7 @@ namespace usb_eyetoy VideoDeviceProxyBase* proxy = RegisterVideoDevice::instance().Proxy(varApi); if (!proxy) { - SysMessage(TEXT("Invalid video device API: " SFMTs "\n"), varApi.c_str()); + SysMessage(TEXT("Invalid video device API: %" SFMTs "\n"), varApi.c_str()); return NULL; } @@ -597,7 +597,7 @@ namespace usb_eyetoy default: break; }*/ - return -1; + return 0; } } // namespace usb_eyetoy diff --git a/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp b/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp index 1796dc2aed..e71e9112f5 100644 --- a/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp +++ b/pcsx2/USB/usb-hid/evdev/evdev-gtk.cpp @@ -109,8 +109,7 @@ namespace usb_hid int GtkHidConfigure(int port, const char* dev_type, HIDType hid_type, GtkWindow* parent) { - GtkWidget *ro_frame, *ro_label, *rs_hbox, *rs_label, *rs_cb; - GtkWidget *main_hbox, *right_vbox, *left_vbox; + GtkWidget *main_hbox, *right_vbox, *rs_cb; assert((int)HIDTYPE_MOUSE == 1); //make sure there is atleast two types so we won't go beyond array length diff --git a/pcsx2/USB/usb-hid/evdev/evdev.cpp b/pcsx2/USB/usb-hid/evdev/evdev.cpp index 85d8e098e9..62bce99920 100644 --- a/pcsx2/USB/usb-hid/evdev/evdev.cpp +++ b/pcsx2/USB/usb-hid/evdev/evdev.cpp @@ -39,7 +39,6 @@ namespace usb_hid bool FindHid(const std::string& evphys, std::string& hid_dev) { int fd; - int res; char buf[256]; std::stringstream str; @@ -100,18 +99,8 @@ namespace usb_hid { // Make sure there is atleast two types so we won't go beyond array length assert((int)HIDTYPE_MOUSE == 1); - int t; std::stringstream name; - char buf[1024]; - unsigned long keybit[NBITS(KEY_MAX)] = {0}; - unsigned long absbit[NBITS(ABS_MAX)] = {0}; - - memset(mAxisMap, -1, sizeof(mAxisMap)); - memset(mBtnMap, -1, sizeof(mBtnMap)); - - mAxisCount = 0; - mButtonCount = 0; mHandle = -1; std::string path; @@ -124,39 +113,12 @@ namespace usb_hid if (path.empty() || !file_exists(path)) goto quit; - /*if (GetEvdevName(joypath, buf)) { - name << buf; - name << " (evdev)"; - }*/ - if ((mHandle = open(path.c_str(), O_RDWR | O_NONBLOCK)) < 0) { OSDebugOut("Cannot open device: %s\n", path.c_str()); goto quit; } - /* if ((ioctl(mHandle, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0) && - (ioctl(mHandle, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0)) { - // Probably isn't a evdev joystick - SysMessage(APINAME ": Getting atleast some of the bits failed: %s\n", strerror(errno)); - goto quit; - }*/ - - /*unsigned int version; - if (ioctl(mHandle, EVIOCGVERSION, &version) < 0) - { - SysMessage(APINAME ": Get version failed: %s\n", strerror(errno)); - return false; - }*/ - - /* for (int i = 0; i < KEY_MAX; ++i) { - if (test_bit(i, keybit)) { - OSDebugOut("Joystick has button: 0x%x\n", i); - mBtnMap[i] = mButtonCount; - ++mButtonCount; - } - }*/ - if (!mReaderThreadIsRunning) { if (mReaderThread.joinable()) @@ -186,9 +148,6 @@ namespace usb_hid EvDev* dev = static_cast(ptr); HIDState* hs = dev->mHIDState; - int32_t lastX = 0, lastY = 0; - bool shift = false; - bool grabbed = false; dev->mReaderThreadIsRunning = true; diff --git a/pcsx2/USB/usb-hid/evdev/evdev.h b/pcsx2/USB/usb-hid/evdev/evdev.h index 285705b218..1193cbb79c 100644 --- a/pcsx2/USB/usb-hid/evdev/evdev.h +++ b/pcsx2/USB/usb-hid/evdev/evdev.h @@ -57,10 +57,6 @@ namespace usb_hid static void ReaderThread(void* ptr); int mHandle; - uint16_t mAxisMap[ABS_MAX + 1]; - uint16_t mBtnMap[KEY_MAX + 1]; - int mAxisCount; - int mButtonCount; std::thread mReaderThread; std::atomic mReaderThreadIsRunning; diff --git a/pcsx2/USB/usb-hid/hidproxy.h b/pcsx2/USB/usb-hid/hidproxy.h index 0c3a420a88..86895fa0c9 100644 --- a/pcsx2/USB/usb-hid/hidproxy.h +++ b/pcsx2/USB/usb-hid/hidproxy.h @@ -46,6 +46,7 @@ namespace usb_hid UsbHIDProxyBase(const std::string& name); virtual UsbHID* CreateObject(int port, const char* dev_type) const = 0; // ProxyBase::Configure is ignored + virtual int Configure(int port, const char* dev_type, void* data) { return RESULT_CANCELED; } virtual int Configure(int port, const char* dev_type, HIDType hid_type, void* data) = 0; }; @@ -76,10 +77,6 @@ namespace usb_hid { return T::Name(); } - virtual int Configure(int port, const char* dev_type, void* data) - { - return RESULT_CANCELED; - } virtual int Configure(int port, const char* dev_type, HIDType hid_type, void* data) { return T::Configure(port, dev_type, hid_type, data); diff --git a/pcsx2/USB/usb-hid/usb-hid.cpp b/pcsx2/USB/usb-hid/usb-hid.cpp index 4d24c7bf81..ca8316bc73 100644 --- a/pcsx2/USB/usb-hid/usb-hid.cpp +++ b/pcsx2/USB/usb-hid/usb-hid.cpp @@ -64,6 +64,18 @@ namespace usb_hid return nullptr; } + std::list BeatManiaDevice::ListAPIs() + { + return RegisterUsbHID::instance().Names(); + } + + const TCHAR* BeatManiaDevice::LongAPIName(const std::string& name) + { + auto proxy = RegisterUsbHID::instance().Proxy(name); + if (proxy) + return proxy->Name(); + return nullptr; + } std::list HIDMouseDevice::ListAPIs() { return RegisterUsbHID::instance().Names(); @@ -100,6 +112,14 @@ namespace usb_hid "HID Keyboard", }; + + static const USBDescStrings beatmania_dadada_desc_strings = { + "", + "KONAMI CPJ1", + "USB JIS Mini Keyboard", + }; + + /* mostly the same values as the Bochs USB Keyboard device */ static const uint8_t kbd_dev_desc[] = { 0x12, /* u8 bLength; */ @@ -414,6 +434,105 @@ namespace usb_hid 0xc0, /* End Collection */ }; + static const uint8_t beatmania_dev_desc[] = { + 0x12, /* u8 bLength; */ + 0x01, /* u8 bDescriptorType; Device */ + WBVAL(0x110), /* u16 bcdUSB; v1.10 */ + + 0x00, /* u8 bDeviceClass; */ + 0x00, /* u8 bDeviceSubClass; */ + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ + + // 0x27, 0x06, /* u16 idVendor; */ + WBVAL(0x0510), + // 0x01, 0x00, /* u16 idProduct; */ + WBVAL(0x0002), + WBVAL(0x0020), /* u16 bcdDevice */ + + 1, /* u8 iManufacturer; */ + 2, /* u8 iProduct; */ + 0, /* u8 iSerialNumber; */ + 0x01 /* u8 bNumConfigurations; */ + }; + + static const uint8_t beatmania_config_desc[] = { + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x22, 0x00, // wTotalLength 34 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x02, // iConfiguration (String Index) + 0xA0, // bmAttributes Remote Wakeup + 0x14, // bMaxPower 40mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints 1 + 0x03, // bInterfaceClass + 0x01, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x21, // bDescriptorType (HID) + 0x10, 0x01, // bcdHID 1.10 + 0x0F, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType[0] (HID) + 0x44, 0x00, // wDescriptorLength[0] 68 + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x81, // bEndpointAddress (IN/D2H) + 0x03, // bmAttributes (Interrupt) + 0x08, 0x00, // wMaxPacketSize 8 + 0x0A, // bInterval 10 (unit depends on device speed) + + // 34 bytes + }; + + static const uint8_t beatmania_dadada_hid_report_descriptor[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0xE0, // Usage Minimum (0xE0) + 0x29, 0xE7, // Usage Maximum (0xE7) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x07, // Usage Page (Kbrd/Keypad) + 0x19, 0x00, // Usage Minimum (0x00) + 0x29, 0xFF, // Usage Maximum (0xFF) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (Num Lock) + 0x29, 0x05, // Usage Maximum (Kana) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x05, // Report Count (5) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x75, 0x03, // Report Size (3) + 0x95, 0x01, // Report Count (1) + 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + + // 68 bytes + }; + static void usb_hid_changed(HIDState* hs) { UsbHIDState* us = CONTAINER_OF(hs, UsbHIDState, f.hid); @@ -463,9 +582,16 @@ namespace usb_hid } else if (hs->kind == HID_KEYBOARD) { - memcpy(data, qemu_keyboard_hid_report_descriptor, - sizeof(qemu_keyboard_hid_report_descriptor)); - p->actual_length = sizeof(qemu_keyboard_hid_report_descriptor); + if (hs->sub_kind == HID_SUBKIND_BEATMANIA) + { + p->actual_length = sizeof(beatmania_dadada_hid_report_descriptor); + memcpy(data, beatmania_dadada_hid_report_descriptor, p->actual_length); + } + else + { + p->actual_length = sizeof(qemu_keyboard_hid_report_descriptor); + memcpy(data, qemu_keyboard_hid_report_descriptor, p->actual_length); + } } break; default: @@ -665,6 +791,8 @@ namespace usb_hid auto s = reinterpret_cast(dev); auto freezed = reinterpret_cast(data); + if (!s) + return 0; switch (mode) { case FREEZE_LOAD: @@ -682,7 +810,7 @@ namespace usb_hid default: break; } - return -1; + return 0; } USBDevice* HIDMouseDevice::CreateDevice(int port) @@ -754,4 +882,77 @@ namespace usb_hid return HIDKbdDevice::Freeze(mode, dev, data); } + // ---- BeatMania Da Da Da!! ---- + + USBDevice* BeatManiaDevice::CreateDevice(int port) + { + OSDebugOut(_T("%s\n"), __func__); + UsbHIDState* s; + + std::string varApi; + LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); + UsbHIDProxyBase* proxy = RegisterUsbHID::instance().Proxy(varApi); + if (!proxy) + { + SysMessage(TEXT("Invalid HID API: %" SFMTs "\n"), varApi.c_str()); + return nullptr; + } + + UsbHID* usbhid = proxy->CreateObject(port, TypeName()); + + if (!usbhid) + return nullptr; + + s = new UsbHIDState(); + + s->desc.full = &s->desc_dev; + s->desc.str = beatmania_dadada_desc_strings; + + if (usb_desc_parse_dev(beatmania_dev_desc, sizeof(beatmania_dev_desc), s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(beatmania_config_desc, sizeof(beatmania_config_desc), s->desc_dev) < 0) + goto fail; + + s->usbhid = usbhid; + s->dev.speed = USB_SPEED_FULL; + s->dev.klass.handle_attach = usb_desc_attach; + s->dev.klass.handle_reset = usb_hid_handle_reset; + s->dev.klass.handle_control = usb_hid_handle_control; + s->dev.klass.handle_data = usb_hid_handle_data; + s->dev.klass.unrealize = usb_hid_unrealize; + s->dev.klass.open = usb_hid_open; + s->dev.klass.close = usb_hid_close; + s->dev.klass.usb_desc = &s->desc; + s->dev.klass.product_desc = s->desc.str[2]; + s->port = port; + + usb_desc_init(&s->dev); + usb_ep_init(&s->dev); + s->intr = usb_ep_get(&s->dev, USB_TOKEN_IN, 1); + hid_init(&s->f.hid, HID_KEYBOARD, usb_hid_changed); + s->f.hid.sub_kind = HID_SUBKIND_BEATMANIA; + s->usbhid->SetHIDState(&s->f.hid); + s->usbhid->SetHIDType(HIDTYPE_KBD); + + usb_hid_handle_reset((USBDevice*)s); + + return (USBDevice*)s; + fail: + usb_hid_unrealize((USBDevice*)s); + return nullptr; + } + + int BeatManiaDevice::Configure(int port, const std::string& api, void* data) + { + auto proxy = RegisterUsbHID::instance().Proxy(api); + if (proxy) + return proxy->Configure(port, TypeName(), HIDTYPE_KBD, data); + return RESULT_CANCELED; + } + + int BeatManiaDevice::Freeze(int mode, USBDevice* dev, void* data) + { + return HIDKbdDevice::Freeze(mode, dev, data); + } + } // namespace usb_hid diff --git a/pcsx2/USB/usb-hid/usb-hid.h b/pcsx2/USB/usb-hid/usb-hid.h index ddaf175bcc..45ac416ad2 100644 --- a/pcsx2/USB/usb-hid/usb-hid.h +++ b/pcsx2/USB/usb-hid/usb-hid.h @@ -93,4 +93,23 @@ namespace usb_hid static int Freeze(int mode, USBDevice* dev, void* data); }; + class BeatManiaDevice + { + public: + virtual ~BeatManiaDevice() {} + static USBDevice* CreateDevice(int port); + static const TCHAR* Name() + { + return TEXT("BeatMania Da Da Da!! Keyboard"); + } + static const char* TypeName() + { + return "beatmania"; + } + static std::list ListAPIs(); + static const TCHAR* LongAPIName(const std::string& name); + static int Configure(int port, const std::string& api, void* data); + static int Freeze(int mode, USBDevice* dev, void* data); + }; + } // namespace usb_hid diff --git a/pcsx2/USB/usb-mic/audiodev-pulse.cpp b/pcsx2/USB/usb-mic/audiodev-pulse.cpp index 12380ab9a1..0cecad9b08 100644 --- a/pcsx2/USB/usb-mic/audiodev-pulse.cpp +++ b/pcsx2/USB/usb-mic/audiodev-pulse.cpp @@ -177,7 +177,7 @@ namespace usb_mic static int GtkConfigure(int port, const char* dev_type, void* data) { - GtkWidget *ro_frame, *ro_label, *rs_hbox, *rs_label, *rs_cb, *vbox; + GtkWidget* ro_frame; int dev_idxs[] = {0, 0, 0, 0}; @@ -763,7 +763,7 @@ namespace usb_mic if (ret != PA_OK) return; - auto dur = std::chrono::duration_cast(hrc::now() - padev->mLastGetBuffer).count(); + //auto dur = std::chrono::duration_cast(hrc::now() - padev->mLastGetBuffer).count(); if (padev->mPaused /*|| dur > 5000*/ || (!padata && nbytes /* hole */)) { ret = pa_stream_drop(p); @@ -836,7 +836,7 @@ namespace usb_mic void PulseAudioDevice::stream_write_cb(pa_stream* p, size_t nbytes, void* userdata) { void* pa_buffer = NULL; - size_t pa_bytes, old_size; + size_t pa_bytes; // The length of the data to write in bytes, must be in multiples of the stream's sample spec frame size ssize_t remaining_bytes = nbytes; int ret = PA_OK; diff --git a/pcsx2/USB/usb-mic/audiodev-wasapi.cpp b/pcsx2/USB/usb-mic/audiodev-wasapi.cpp index e5fa9a9c28..af9b89797a 100644 --- a/pcsx2/USB/usb-mic/audiodev-wasapi.cpp +++ b/pcsx2/USB/usb-mic/audiodev-wasapi.cpp @@ -926,12 +926,12 @@ namespace usb_mic switch (uMsg) { case WM_CREATE: - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); + SetWindowLongPtr(hW, GWLP_USERDATA, lParam); break; case WM_INITDIALOG: { s = (WASAPISettings*)lParam; - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); + SetWindowLongPtr(hW, GWLP_USERDATA, lParam); int buffering = 50; LoadSetting(s->dev_type, s->port, APINAME, N_BUFFER_LEN_SRC, buffering); diff --git a/pcsx2/USB/usb-mic/usb-headset.cpp b/pcsx2/USB/usb-mic/usb-headset.cpp index 8abb75717d..6a9c544858 100644 --- a/pcsx2/USB/usb-mic/usb-headset.cpp +++ b/pcsx2/USB/usb-mic/usb-headset.cpp @@ -1062,11 +1062,11 @@ namespace usb_mic int HeadsetDevice::Freeze(int mode, USBDevice* dev, void* data) { HeadsetState* s = (HeadsetState*)dev; + if (!s) + return 0; switch (mode) { case FREEZE_LOAD: - if (!s) - return -1; s->f = *(HeadsetState::freeze*)data; if (s->audsrc) s->audsrc->SetResampling(s->f.in.srate); @@ -1074,8 +1074,6 @@ namespace usb_mic s->audsink->SetResampling(s->f.out.srate); return sizeof(HeadsetState::freeze); case FREEZE_SAVE: - if (!s) - return -1; *(HeadsetState::freeze*)data = s->f; return sizeof(HeadsetState::freeze); case FREEZE_SIZE: @@ -1083,7 +1081,7 @@ namespace usb_mic default: break; } - return -1; + return 0; } } // namespace usb_mic diff --git a/pcsx2/USB/usb-mic/usb-mic-singstar.cpp b/pcsx2/USB/usb-mic/usb-mic-singstar.cpp index b2ea020ea9..0c08357a0a 100644 --- a/pcsx2/USB/usb-mic/usb-mic-singstar.cpp +++ b/pcsx2/USB/usb-mic/usb-mic-singstar.cpp @@ -624,7 +624,7 @@ namespace usb_mic return; } - OSDebugOut(TEXT("data len: %d bytes, src[0]: %d frames, src[1]: %d frames\n"), len, out_frames[0], out_frames[1]); + OSDebugOut(TEXT("data len: %zu bytes, src[0]: %d frames, src[1]: %d frames\n"), len, out_frames[0], out_frames[1]); //TODO well, it is 16bit interleaved, right? //Merge with MIC_MODE_SHARED case? @@ -718,7 +718,7 @@ namespace usb_mic case USB_TOKEN_OUT: printf("token out ep: %d\n", devep); OSDebugOut(TEXT("token out ep: %d len: %d\n"), devep, p->actual_length); - [[fallthrough]]; + break; default: p->status = USB_RET_STALL; break; @@ -866,11 +866,11 @@ namespace usb_mic int SingstarDevice::Freeze(int mode, USBDevice* dev, void* data) { SINGSTARMICState* s = (SINGSTARMICState*)dev; + if (!s) + return 0; switch (mode) { case FREEZE_LOAD: - if (!s) - return -1; s->f = *(SINGSTARMICState::freeze*)data; if (s->audsrc[0]) s->audsrc[0]->SetResampling(s->f.srate[0]); @@ -878,8 +878,6 @@ namespace usb_mic s->audsrc[1]->SetResampling(s->f.srate[1]); return sizeof(SINGSTARMICState::freeze); case FREEZE_SAVE: - if (!s) - return -1; *(SINGSTARMICState::freeze*)data = s->f; return sizeof(SINGSTARMICState::freeze); case FREEZE_SIZE: @@ -887,7 +885,7 @@ namespace usb_mic default: break; } - return -1; + return 0; } } // namespace usb_mic diff --git a/pcsx2/USB/usb-msd/usb-msd-gtk.cpp b/pcsx2/USB/usb-msd/usb-msd-gtk.cpp index 0e6d86a9f2..29a84fbc7b 100644 --- a/pcsx2/USB/usb-msd/usb-msd-gtk.cpp +++ b/pcsx2/USB/usb-msd/usb-msd-gtk.cpp @@ -23,8 +23,10 @@ namespace usb_msd static void entryChanged(GtkWidget* widget, gpointer data) { +#ifndef NDEBUG const gchar* text = gtk_entry_get_text(GTK_ENTRY(widget)); - //fprintf(stderr, "Entry text:%s\n", text); + fprintf(stderr, "Entry text:%s\n", text); +#endif } static void fileChooser(GtkWidget* widget, gpointer data) @@ -59,7 +61,7 @@ namespace usb_msd int MsdDevice::Configure(int port, const std::string& api, void* data) { - GtkWidget *ro_frame, *ro_label, *rs_hbox, *rs_label, *rs_cb, *vbox; + GtkWidget *ro_frame, *ro_label, *rs_hbox, *vbox; GtkWidget* dlg = gtk_dialog_new_with_buttons( "Mass Storage Settings", GTK_WINDOW(data), GTK_DIALOG_MODAL, diff --git a/pcsx2/USB/usb-msd/usb-msd.cpp b/pcsx2/USB/usb-msd/usb-msd.cpp index 046263a46c..f2f1e4d583 100644 --- a/pcsx2/USB/usb-msd/usb-msd.cpp +++ b/pcsx2/USB/usb-msd/usb-msd.cpp @@ -618,7 +618,7 @@ namespace usb_msd MSDState* s = (MSDState*)opaque; DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x\n", cbw->lun, cbw->tag, cbw->data_len, cbw->cmd[0]); - uint32_t lba; + int64_t lba; uint32_t xfer_len; s->f.last_cmd = cbw->cmd[0]; @@ -701,7 +701,7 @@ namespace usb_msd s->f.data_len = xfer_len * LBA_BLOCK_SIZE; s->f.file_op_tag = s->f.tag; - DPRINTF("read lba=0x%x, len=0x%x\n", lba, xfer_len * LBA_BLOCK_SIZE); + DPRINTF("read lba=0x%llx, len=0x%x\n", lba, xfer_len * LBA_BLOCK_SIZE); if (xfer_len == 0) // nothing to do break; @@ -1088,11 +1088,11 @@ namespace usb_msd MSDState* s = (MSDState*)dev; MSDState::freeze* tmp; + if (!s) + return 0; switch (mode) { case FREEZE_LOAD: - if (!s) - return -1; //if (s->f.req) free (s->f.req); tmp = (MSDState::freeze*)data; @@ -1107,8 +1107,6 @@ namespace usb_msd return sizeof(MSDState::freeze); // + sizeof(ReqState); case FREEZE_SAVE: - if (!s) - return -1; tmp = (MSDState::freeze*)data; *tmp = s->f; return sizeof(MSDState::freeze); @@ -1118,7 +1116,7 @@ namespace usb_msd default: break; } - return -1; + return 0; } #undef DPRINTF diff --git a/pcsx2/USB/usb-pad/dx/dinput-config.cpp b/pcsx2/USB/usb-pad/dx/dinput-config.cpp index bfee21265f..c139656b05 100644 --- a/pcsx2/USB/usb-pad/dx/dinput-config.cpp +++ b/pcsx2/USB/usb-pad/dx/dinput-config.cpp @@ -46,14 +46,6 @@ namespace usb_pad std::vector jso; // DInput joystick old state, only for config std::vector jsi; // DInput joystick initial state, only for config - int32_t AXISID[2][CID_COUNT]; - int32_t BUTTON[2][CID_COUNT]; - int32_t INVERT[2][CID_COUNT]; - int32_t HALF[2][CID_COUNT]; - int32_t LINEAR[2][CID_COUNT]; - int32_t OFFSET[2][CID_COUNT]; - int32_t DEADZONE[2][CID_COUNT]; - int32_t BYPASSCAL = 0; int32_t GAINZ[2][1]; int32_t FFMULTI[2][1]; @@ -64,7 +56,7 @@ namespace usb_pad HWND hKey; HWND hWnd; TCHAR text[1024]; - ControlID CID = CID_COUNT; + ControlID CID = CID_COUNT; //keep track of last assigned control HFONT hFont; HDC hDC; @@ -305,17 +297,18 @@ namespace usb_pad if (filtercontrol == -1) return; //slider - LINEAR[port][filtercontrol] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; - OFFSET[port][filtercontrol] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; - DEADZONE[port][filtercontrol] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; + auto& im = g_Controls[port][filtercontrol]; + im.LINEAR = SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; + im.OFFSET = SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; + im.DEADZONE = SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_GETPOS, 0, 0) - 50 * PRECMULTI; GAINZ[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_GETPOS, 0, 0); FFMULTI[port][0] = SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_GETPOS, 0, 0); - swprintf_s(text, TEXT("LINEARITY: %0.02f"), float(LINEAR[port][filtercontrol]) / PRECMULTI); + swprintf_s(text, TEXT("LINEARITY: %0.02f"), float(im.LINEAR) / PRECMULTI); SetWindowText(GetDlgItem(hWnd, IDC_LINEAR), text); - swprintf_s(text, TEXT("OFFSET: %0.02f"), float(OFFSET[port][filtercontrol]) / PRECMULTI); + swprintf_s(text, TEXT("OFFSET: %0.02f"), float(im.OFFSET) / PRECMULTI); SetWindowText(GetDlgItem(hWnd, IDC_OFFSET), text); - swprintf_s(text, TEXT("DEAD-ZONE: %0.02f"), float(DEADZONE[port][filtercontrol]) / PRECMULTI); + swprintf_s(text, TEXT("DEAD-ZONE: %0.02f"), float(im.DEADZONE) / PRECMULTI); SetWindowText(GetDlgItem(hWnd, IDC_DEADZONE), text); GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); @@ -328,12 +321,11 @@ namespace usb_pad filtercontrol = SendMessage(GetDlgItem(hWnd, IDC_COMBO1), CB_GETCURSEL, 0, 0); if (filtercontrol == -1) return; - //InputMapped im = {}; - //GetInputMap(port, (ControlID)filtercontrol, im); + auto& im = g_Controls[port][filtercontrol]; //slider - SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_SETPOS, 1, LINEAR[port][filtercontrol] + 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_SETPOS, 1, OFFSET[port][filtercontrol] + 50 * PRECMULTI); - SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_SETPOS, 1, DEADZONE[port][filtercontrol] + 50 * PRECMULTI); + SendMessage(GetDlgItem(hWnd, IDC_SLIDER1), TBM_SETPOS, 1, im.LINEAR + 50 * PRECMULTI); + SendMessage(GetDlgItem(hWnd, IDC_SLIDER2), TBM_SETPOS, 1, im.OFFSET + 50 * PRECMULTI); + SendMessage(GetDlgItem(hWnd, IDC_SLIDER3), TBM_SETPOS, 1, im.DEADZONE + 50 * PRECMULTI); SendMessage(GetDlgItem(hWnd, IDC_SLIDER4), TBM_SETPOS, 1, GAINZ[port][0]); SendMessage(GetDlgItem(hWnd, IDC_SLIDER5), TBM_SETPOS, 1, FFMULTI[port][0]); @@ -342,44 +334,47 @@ namespace usb_pad void DefaultFilters(int port, LONG id) { + auto& im_l = g_Controls[port][CID_STEERING]; + auto& im_r = g_Controls[port][CID_STEERING_R]; for (int i = 0; i < 6; i++) { - LINEAR[port][i] = 0; - OFFSET[port][i] = 0; - DEADZONE[port][i] = 0; + auto& c = g_Controls[port][i]; + c.LINEAR = 0; + c.OFFSET = 0; + c.DEADZONE = 0; } switch (id) { case 0: - LINEAR[port][0] = 0; - OFFSET[port][0] = 0; - LINEAR[port][1] = 0; - OFFSET[port][1] = 0; + im_l.LINEAR = 0; + im_l.OFFSET = 0; + im_r.LINEAR = 0; + im_r.OFFSET = 0; break; case 1: - LINEAR[port][0] = 6 * PRECMULTI; - OFFSET[port][0] = 0; - LINEAR[port][1] = 6 * PRECMULTI; - OFFSET[port][1] = 0; + im_l.LINEAR = 6 * PRECMULTI; + im_l.OFFSET = 0; + im_r.LINEAR = 6 * PRECMULTI; + im_r.OFFSET = 0; break; case 2: - LINEAR[port][0] = 12 * PRECMULTI; - OFFSET[port][0] = 0; - LINEAR[port][1] = 12 * PRECMULTI; - OFFSET[port][1] = 0; + im_l.LINEAR = 12 * PRECMULTI; + im_l.OFFSET = 0; + im_r.LINEAR = 12 * PRECMULTI; + im_r.OFFSET = 0; break; case 3: - LINEAR[port][0] = 18 * PRECMULTI; - OFFSET[port][0] = 0; - LINEAR[port][1] = 18 * PRECMULTI; - OFFSET[port][1] = 0; + im_l.LINEAR = 18 * PRECMULTI; + im_l.OFFSET = 0; + im_r.LINEAR = 18 * PRECMULTI; + im_r.OFFSET = 0; break; case 4: - LINEAR[port][0] = 25 * PRECMULTI; - OFFSET[port][0] = 0; - LINEAR[port][1] = 25 * PRECMULTI; - OFFSET[port][1] = 0; + im_l.LINEAR = 25 * PRECMULTI; + im_l.OFFSET = 0; + im_r.LINEAR = 25 * PRECMULTI; + im_r.OFFSET = 0; break; } @@ -398,7 +393,7 @@ namespace usb_pad if (GetInputMap(port, (ControlID)filtercontrol, im)) { TESTV = ReadAxis(im); - TESTVF = FilterControl(ReadAxis(im), LINEAR[port][filtercontrol], OFFSET[port][filtercontrol], DEADZONE[port][filtercontrol]); + TESTVF = FilterControl(ReadAxis(im), im.LINEAR, im.OFFSET, im.DEADZONE); GetClientRect(GetDlgItem(hWnd, IDC_PICTURE), &rect); MapWindowPoints(GetDlgItem(hWnd, IDC_PICTURE), hWnd, (POINT*)&rect, 2); InvalidateRect(hWnd, &rect, TRUE); @@ -418,13 +413,6 @@ namespace usb_pad if (FindControl(port, CID, im)) { - if (CID <= CID_BRAKE) - { - LINEAR[port][CID] = im.LINEAR; - OFFSET[port][CID] = im.OFFSET; - DEADZONE[port][CID] = im.DEADZONE; - } - AddInputMap(port, CID, im); SetControlLabel(CID, im); } @@ -501,13 +489,14 @@ namespace usb_pad filtercontrol = SendMessage(GetDlgItem(hWnd, IDC_COMBO1), CB_GETCURSEL, 0, 0); if (filtercontrol >= 0) { + auto& im = g_Controls[port][filtercontrol]; //draw nonlinear line SelectObject(hDrawingDC, bluepen); MoveToEx(hDrawingDC, (px + 8) * scale, (pheight + py - 8) * scale, 0); for (float x = 0; x < 1.0f; x += 0.001f) { - float y1 = FilterControl(x, LINEAR[port][filtercontrol], OFFSET[port][filtercontrol], DEADZONE[port][filtercontrol]); + float y1 = FilterControl(x, im.LINEAR, im.OFFSET, im.DEADZONE); LineTo(hDrawingDC, (x * (pwidth - 16) + px + 8) * scale, (-y1 * (pheight - 16) + (pheight - 8) + py) * scale); } LineTo(hDrawingDC, (1.0f * (pwidth - 16) + px + 8) * scale, (-1.0f * (pheight - 16) + (pheight - 8) + py) * scale); @@ -1340,13 +1329,6 @@ namespace usb_pad im.DEADZONE = std::stoi(value); } - //if (cid <= CID_BRAKE) - { - LINEAR[port][cid] = im.LINEAR; - OFFSET[port][cid] = im.OFFSET; - DEADZONE[port][cid] = im.DEADZONE; - } - AddInputMap(port, (ControlID)cid, im); } } diff --git a/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp b/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp index b7810a6558..4a89230a93 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp +++ b/pcsx2/USB/usb-pad/evdev/evdev-ff.cpp @@ -29,9 +29,15 @@ namespace usb_pad (((x) + 8 * sizeof(unsigned char) - 1) / (8 * sizeof(unsigned char))) #define testBit(bit, array) ((((uint8_t*)(array))[(bit) / 8] >> ((bit) % 8)) & 1) - EvdevFF::EvdevFF(int fd) + EvdevFF::EvdevFF(int fd, bool gain_enabled, int gain, bool ac_managed, int ac_strength) : mHandle(fd) , mUseRumble(false) + , mLastValue(0) + , m_gain_enabled(gain_enabled) + , m_gain(gain) + //, m_ac_managed(ac_enabled) + , m_ac_managed(true) + , m_ac_strength(ac_strength) { unsigned char features[BITS_TO_UCHAR(FF_MAX)]; if (ioctl(mHandle, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) @@ -80,17 +86,21 @@ namespace usb_pad // Logitech wheels' force vs turn direction: 255 - left, 127/128 - neutral, 0 - right // left direction mEffect.direction = 0x4000; - mEffect.u.constant.envelope.attack_length = 0; //0x100; - mEffect.u.constant.envelope.attack_level = 0; - mEffect.u.constant.envelope.fade_length = 0; //0x100; - mEffect.u.constant.envelope.fade_level = 0; mEffect.trigger.button = 0; mEffect.trigger.interval = 0; mEffect.replay.length = 0x7FFFUL; /* mseconds */ mEffect.replay.delay = 0; - SetGain(100); - SetAutoCenter(0); + if (m_gain_enabled) + SetGain(m_gain); + + m_ac_strength = std::min(100, std::max(0, m_ac_strength)); + if (ac_managed) + SetAutoCenter(0); // default to off + else + SetAutoCenter(m_ac_strength); + + m_ac_managed = ac_managed; } EvdevFF::~EvdevFF() @@ -128,6 +138,10 @@ namespace usb_pad mEffect.type = FF_CONSTANT; mEffect.id = mEffIds[EFF_CONSTANT]; mEffect.u.constant.level = /*ff.u.constant.*/ level; + // mEffect.u.constant.envelope.attack_length = 0;//0x100; + // mEffect.u.constant.envelope.attack_level = 0; + // mEffect.u.constant.envelope.fade_length = 0;//0x100; + // mEffect.u.constant.envelope.fade_level = 0; OSDebugOut("Constant force: %d\n", level); if (ioctl(mHandle, EVIOCSFF, &(mEffect)) < 0) @@ -277,7 +291,10 @@ namespace usb_pad void EvdevFF::SetAutoCenter(int value) { + if (!m_ac_managed) + return; struct input_event ie; + value = value * m_ac_strength / 100; ie.type = EV_FF; ie.code = FF_AUTOCENTER; diff --git a/pcsx2/USB/usb-pad/evdev/evdev-ff.h b/pcsx2/USB/usb-pad/evdev/evdev-ff.h index d92540e309..eb87af4058 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev-ff.h +++ b/pcsx2/USB/usb-pad/evdev/evdev-ff.h @@ -27,7 +27,7 @@ namespace usb_pad class EvdevFF : public FFDevice { public: - EvdevFF(int fd); + EvdevFF(int fd, bool gain_enabled, int gain, bool ac_managed, int ac_strength); ~EvdevFF(); void SetConstantForce(int level); @@ -45,6 +45,10 @@ namespace usb_pad bool mUseRumble; int mLastValue; + bool m_gain_enabled; + int m_gain; + bool m_ac_managed; + int m_ac_strength; }; } // namespace evdev diff --git a/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp b/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp index 56c733b8d4..535834af44 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp +++ b/pcsx2/USB/usb-pad/evdev/evdev-gtk.cpp @@ -584,7 +584,7 @@ namespace usb_pad static bool PollInput(const std::vector>& fds, std::string& dev_name, bool isaxis, int& value, bool& inverted, int& initial) { - int event_fd = -1, t; + int event_fd = -1; ssize_t len; input_event event; struct AxisValue @@ -749,9 +749,9 @@ namespace usb_pad ApiCallbacks apicbs{GetEventName, EnumerateDevices, PollInput}; int ret = 0; if (!strcmp(dev_type, BuzzDevice::TypeName())) - ret = GtkBuzzConfigure(port, dev_type, "Evdev Settings", "evdev", GTK_WINDOW(data), apicbs); + ret = GtkBuzzConfigure(port, dev_type, "Evdev Settings", evdev::APINAME, GTK_WINDOW(data), apicbs); else - ret = GtkPadConfigure(port, dev_type, "Evdev Settings", "evdev", GTK_WINDOW(data), apicbs); + ret = GtkPadConfigure(port, dev_type, "Evdev Settings", evdev::APINAME, GTK_WINDOW(data), apicbs); return ret; } diff --git a/pcsx2/USB/usb-pad/evdev/evdev.cpp b/pcsx2/USB/usb-pad/evdev/evdev.cpp index f02ce31af6..166a525dfd 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev.cpp +++ b/pcsx2/USB/usb-pad/evdev/evdev.cpp @@ -118,7 +118,7 @@ namespace usb_pad } #define EVDEV_DIR "/dev/input/by-id/" - void EnumerateDevices(vstring& list) + void EnumerateDevices(device_list& list) { int fd; int res; @@ -128,7 +128,7 @@ namespace usb_pad struct dirent* dp; //TODO do some caching? ioctl is "very" slow - static vstring list_cache; + static device_list list_cache; DIR* dirp = opendir(EVDEV_DIR); if (!dirp) @@ -140,7 +140,7 @@ namespace usb_pad // get rid of unplugged devices for (int i = 0; i < list_cache.size();) { - if (!file_exists(list_cache[i].second)) + if (!file_exists(list_cache[i].path)) list_cache.erase(list_cache.begin() + i); else i++; @@ -159,8 +159,8 @@ namespace usb_pad std::string path = str.str(); auto it = std::find_if(list_cache.begin(), list_cache.end(), - [&path](auto& pair) { - return pair.second == path; + [&path](evdev_device& dev) { + return dev.path == path; }); if (it != list_cache.end()) continue; @@ -173,16 +173,16 @@ namespace usb_pad continue; } - list_cache.push_back(std::make_pair(std::string(dp->d_name), path)); + //list_cache.push_back(std::make_pair(std::string(dp->d_name), path)); - /*res = ioctl(fd, EVIOCGNAME(sizeof(buf)), buf); - if (res < 0) - perror("EVIOCGNAME"); - else - { - OSDebugOut("Evdev device name: %s\n", buf); - list_cache.push_back(std::make_pair(std::string(buf) + " (evdev)", path)); - }*/ + res = ioctl(fd, EVIOCGNAME(sizeof(buf)), buf); + if (res < 0) + perror("EVIOCGNAME"); + else + { + OSDebugOut("Evdev device name: %s\n", buf); + list_cache.push_back({buf, dp->d_name, path}); + } close(fd); } @@ -368,6 +368,7 @@ namespace usb_pad } else { +#if 0 // Map to xbox360ish controller switch (code) { @@ -408,16 +409,17 @@ namespace usb_pad case BTN_TL: button = PAD_L1; break; - case BTN_THUMBR: + case BTN_TR2: button = PAD_R2; break; - case BTN_THUMBL: + case BTN_TL2: button = PAD_L2; break; default: OSDebugOut("Unmapped Button: %d, %d\n", code, event.value); break; } +#endif } //if (button != PAD_BUTTON_COUNT) @@ -530,9 +532,10 @@ namespace usb_pad int EvDevPad::Open() { std::stringstream name; - vstring device_list; + device_list device_list; char buf[1024]; mWheelData = {}; + int32_t b_gain, gain, b_ac, ac; unsigned long keybit[NBITS(KEY_MAX)]; unsigned long absbit[NBITS(ABS_MAX)]; @@ -563,7 +566,8 @@ namespace usb_pad case WT_DRIVING_FORCE_PRO: case WT_DRIVING_FORCE_PRO_1102: { - LoadSetting(mDevType, mPort, APINAME, N_HIDRAW_FF_PT, mUseRawFF); + if (!LoadSetting(mDevType, mPort, APINAME, N_HIDRAW_FF_PT, mUseRawFF)) + mUseRawFF = 0; } break; default: @@ -614,7 +618,7 @@ namespace usb_pad { if (mWriterThread.joinable()) mWriterThread.join(); - mWriterThread = std::thread(EvDevPad::WriterThread, this); + mWriterThread = std::thread(&EvDevPad::WriterThread, this); } } } @@ -633,11 +637,11 @@ namespace usb_pad mDevices.push_back({}); struct device_data& device = mDevices.back(); - device.name = it.first; + device.name = it.name; - if ((device.cfg.fd = open(it.second.c_str(), O_RDWR | O_NONBLOCK)) < 0) + if ((device.cfg.fd = open(it.path.c_str(), O_RDWR | O_NONBLOCK)) < 0) { - OSDebugOut("Cannot open device: %s\n", it.second.c_str()); + OSDebugOut("Cannot open device: %s\n", it.path.c_str()); continue; } @@ -664,11 +668,19 @@ namespace usb_pad switch (mType) { case WT_BUZZ_CONTROLLER: - LoadBuzzMappings(mDevType, mPort, device.name, device.cfg); + LoadBuzzMappings(mDevType, mPort, it.id, device.cfg); max_buttons = 20; break; default: - LoadMappings(mDevType, mPort, device.name, device.cfg); + LoadMappings(mDevType, mPort, it.id, device.cfg); + if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN_ENABLED, b_gain)) + b_gain = 1; + if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN, gain)) + gain = 100; + if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER_MANAGED, b_ac)) + b_ac = 1; + if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER, ac)) + ac = 100; break; } @@ -716,7 +728,9 @@ namespace usb_pad // TODO Instead of single FF instance, create for every device with X-axis??? // and then switch between them according to which device was used recently if (k == JOY_STEERING && !mFFdev && !mUseRawFF) - mFFdev = new EvdevFF(device.cfg.fd); + { + mFFdev = new EvdevFF(device.cfg.fd, b_gain, gain, b_ac, ac); + } } } } @@ -805,20 +819,19 @@ namespace usb_pad return 0; } - void EvDevPad::WriterThread(void* ptr) + void EvDevPad::WriterThread() { std::array buf; int res; - EvDevPad* pad = static_cast(ptr); - pad->mWriterThreadIsRunning = true; + mWriterThreadIsRunning = true; - while (pad->mHidHandle != -1) + while (mHidHandle != -1) { - //if (pad->mFFData.wait_dequeue_timed(buf, std::chrono::milliseconds(1000))) //FIXME SIGABORT :S - if (pad->mFFData.try_dequeue(buf)) + //if (mFFData.wait_dequeue_timed(buf, std::chrono::milliseconds(1000))) //FIXME SIGABORT :S + if (mFFData.try_dequeue(buf)) { - res = write(pad->mHidHandle, buf.data(), buf.size()); + res = write(mHidHandle, buf.data(), buf.size()); if (res < 0) { printf("Error: %d\n", errno); @@ -832,7 +845,7 @@ namespace usb_pad } OSDebugOut(TEXT("WriterThread exited.\n")); - pad->mWriterThreadIsRunning = false; + mWriterThreadIsRunning = false; } } // namespace evdev diff --git a/pcsx2/USB/usb-pad/evdev/evdev.h b/pcsx2/USB/usb-pad/evdev/evdev.h index f22e0155f0..11a9329914 100644 --- a/pcsx2/USB/usb-pad/evdev/evdev.h +++ b/pcsx2/USB/usb-pad/evdev/evdev.h @@ -32,17 +32,15 @@ namespace usb_pad (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0) #define NBITS(x) ((((x)-1) / (sizeof(long) * 8)) + 1) - void EnumerateDevices(vstring& list); + void EnumerateDevices(device_list& list); - static const char* APINAME = "evdev"; + static constexpr const char* APINAME = "evdev"; class EvDevPad : public Pad { public: EvDevPad(int port, const char* dev_type) : Pad(port, dev_type) - , mUseRawFF(0) - , mHidHandle(-1) , mWriterThreadIsRunning(false) { } @@ -64,13 +62,15 @@ namespace usb_pad protected: void PollAxesValues(const device_data& device); void SetAxis(const device_data& device, int code, int value); - static void WriterThread(void* ptr); + void WriterThread(); - int mHidHandle; - EvdevFF* mEvdevFF; - struct wheel_data_t mWheelData; + int mHidHandle = -1; + EvdevFF* mEvdevFF = nullptr; + struct wheel_data_t mWheelData + { + }; std::vector mDevices; - int32_t mUseRawFF; + int32_t mUseRawFF = 0; std::thread mWriterThread; std::atomic mWriterThreadIsRunning; moodycamel::BlockingReaderWriterQueue, 32> mFFData; diff --git a/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp b/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp index e08b49e491..3f12395a4e 100644 --- a/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp +++ b/pcsx2/USB/usb-pad/evdev/shared-gtk.cpp @@ -85,6 +85,7 @@ namespace usb_pad if (joyname.empty() || cfg.controls.size() != JOY_MAPS_COUNT) return false; + RemoveSection(dev_type, port, joyname); std::stringstream str; for (int i = 0; i < JOY_MAPS_COUNT; i++) { @@ -92,7 +93,7 @@ namespace usb_pad str.str(""); str << "map_" << JoystickMapNames[i]; const std::string& name = str.str(); - if (!SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) + if (cfg.controls[i] >= 0 && !SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) return false; } @@ -150,6 +151,7 @@ namespace usb_pad if (joyname.empty()) return false; + RemoveSection(dev_type, port, joyname); std::stringstream str; const size_t c = countof(buzz_map_names); @@ -159,7 +161,7 @@ namespace usb_pad str.clear(); str << "map_" << buzz_map_names[i % c] << "_" << (i / c); const std::string& name = str.str(); - if (!SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) + if (cfg.controls[i] >= 0 && !SaveSetting(dev_type, port, joyname, name.c_str(), static_cast(cfg.controls[i]))) return false; } return true; @@ -174,7 +176,7 @@ namespace usb_pad { for (int i = 0; /*i < JOY_MAPS_COUNT && */ i < it.second.controls.size(); i++) { - if (it.second.controls[i] == (uint16_t)-1) + if (it.second.controls[i] < 0) continue; const char* pc_name = "Unknown"; @@ -225,7 +227,7 @@ namespace usb_pad if (idx > -1) { - std::string name = (cfg->joysticks.begin() + idx)->first; + std::string name = (cfg->joysticks.begin() + idx)->name; cfg->js_iter = (cfg->joysticks.begin() + idx); OSDebugOut("Selected player %d idx: %d dev: '%s'\n", 2 - port, idx, name.c_str()); } @@ -233,7 +235,6 @@ namespace usb_pad static void button_clicked(GtkComboBox* widget, gpointer data) { - int port = reinterpret_cast(data); int type = reinterpret_cast(g_object_get_data(G_OBJECT(widget), JOYTYPE)); ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); @@ -254,7 +255,7 @@ namespace usb_pad if (cfg->cb->poll(cfg->jsconf, dev_name, is_axis, value, inverted, initial)) { auto it = std::find_if(cfg->jsconf.begin(), cfg->jsconf.end(), - [&dev_name](auto& i) -> bool { + [&dev_name](MappingPair& i) -> bool { return i.first == dev_name; }); @@ -275,7 +276,6 @@ namespace usb_pad static void button_clicked_buzz(GtkComboBox* widget, gpointer data) { - int port = reinterpret_cast(data); int type = reinterpret_cast(g_object_get_data(G_OBJECT(widget), JOYTYPE)); ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); @@ -295,7 +295,7 @@ namespace usb_pad if (cfg->cb->poll(cfg->jsconf, dev_name, false, value, inverted, initial)) { auto it = std::find_if(cfg->jsconf.begin(), cfg->jsconf.end(), - [&dev_name](auto& i) -> bool { + [&dev_name](MappingPair& i) -> bool { return i.first == dev_name; }); @@ -333,7 +333,7 @@ namespace usb_pad auto& js = cfg->jsconf; auto it = std::find_if(js.begin(), js.end(), - [&dev_name](auto i) { + [&dev_name](MappingPair i) { return i.first == dev_name; }); if (it != js.end()) @@ -394,19 +394,18 @@ namespace usb_pad refresh_store(cfg); } - static void hidraw_toggled(GtkToggleButton* widget, gpointer data) + static void checkbox_toggled(GtkToggleButton* widget, gpointer data) { - int port = reinterpret_cast(data); - ConfigData* cfg = (ConfigData*)g_object_get_data(G_OBJECT(widget), CFG); - if (cfg) + gboolean* val = reinterpret_cast(data); + if (val) { - cfg->use_hidraw_ff_pt = (bool)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + *val = (bool)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); } } int GtkPadConfigure(int port, const char* dev_type, const char* apititle, const char* apiname, GtkWindow* parent, ApiCallbacks& apicbs) { - GtkWidget *ro_frame, *ro_label, *rs_hbox, *rs_label, *rs_cb; + GtkWidget *ro_frame, *rs_cb; GtkWidget *main_hbox, *right_vbox, *left_vbox, *treeview; GtkWidget* button; @@ -424,17 +423,16 @@ namespace usb_pad for (const auto& it : cfg.joysticks) { - if ((fd = open(it.second.c_str(), O_RDONLY | O_NONBLOCK)) < 0) + if ((fd = open(it.path.c_str(), O_RDONLY | O_NONBLOCK)) < 0) { - OSDebugOut("Cannot open device: %s\n", it.second.c_str()); + OSDebugOut("Cannot open device: %s\n", it.path.c_str()); continue; } - ConfigMapping c; - c.fd = fd; - LoadMappings(cfg.dev_type, port, it.first, c); - cfg.jsconf.push_back(std::make_pair(it.first, c)); - OSDebugOut("mappings for '%s': %zu\n", it.first.c_str(), c.controls.size()); + ConfigMapping c(fd); + LoadMappings(cfg.dev_type, port, it.id, c); + cfg.jsconf.push_back(std::make_pair(it.id, c)); + OSDebugOut("mappings for '%s': %zu\n", it.name.c_str(), c.controls.size()); } refresh_store(&cfg); @@ -596,6 +594,62 @@ namespace usb_pad gtk_box_pack_start(GTK_BOX(right_vbox), cfg.label, TRUE, TRUE, 5); } + ro_frame = gtk_frame_new("Force feedback"); + gtk_box_pack_start(GTK_BOX(right_vbox), ro_frame, TRUE, FALSE, 5); + + //GtkWidget *frame_vbox = gtk_vbox_new (FALSE, 5); + //gtk_container_add (GTK_CONTAINER (ro_frame), frame_vbox); + + const char* labels_buff[][2] = {{"Set gain", "Gain"}, {"Managed by game", "Autocenter strength"}}; + const char* ff_var_name[][2] = {{N_GAIN_ENABLED, N_GAIN}, {N_AUTOCENTER_MANAGED, N_AUTOCENTER}}; + GtkWidget* ff_scales[2]; + int32_t ff_enabled[2]; + + GtkWidget* table = gtk_table_new(3, 2, true); + gtk_container_add(GTK_CONTAINER(ro_frame), table); + gtk_table_set_homogeneous(GTK_TABLE(table), FALSE); + GtkAttachOptions opt = (GtkAttachOptions)(GTK_EXPAND | GTK_FILL); // default + + for (int i = 0; i < 2; i++) + { + if (LoadSetting(dev_type, port, apiname, ff_var_name[i][0], ff_enabled[i])) + ff_enabled[i] = !!ff_enabled[i]; + else + ff_enabled[i] = 1; + + GtkWidget* chk_btn = gtk_check_button_new_with_label(labels_buff[i][0]); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_btn), (gboolean)ff_enabled[i]); + g_signal_connect(G_OBJECT(chk_btn), "toggled", G_CALLBACK(checkbox_toggled), reinterpret_cast(&ff_enabled[i])); + gtk_table_attach(GTK_TABLE(table), chk_btn, + 2, 3, + 0 + i, 1 + i, + GTK_FILL, GTK_SHRINK, 5, 1); + + GtkWidget* label = gtk_label_new(labels_buff[i][1]); + gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f); + gtk_table_attach(GTK_TABLE(table), label, + 0, 1, + 0 + i, 1 + i, + GTK_FILL, GTK_SHRINK, 5, 1); + + //ff_scales[i] = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 100, 1); + ff_scales[i] = gtk_hscale_new_with_range(0, 100, 1); + for (int v = 0; v <= 100; v += 10) + gtk_scale_add_mark(GTK_SCALE(ff_scales[i]), v, GTK_POS_BOTTOM, nullptr); + gtk_table_attach(GTK_TABLE(table), ff_scales[i], + 1, 2, + 0 + i, 1 + i, + opt, opt, 5, 1); + + int32_t var; + if (LoadSetting(dev_type, port, apiname, ff_var_name[i][1], var)) + { + var = std::min(100, std::max(0, var)); + gtk_range_set_value(GTK_RANGE(ff_scales[i]), var); + } + else + gtk_range_set_value(GTK_RANGE(ff_scales[i]), 100); + } if (is_evdev) { @@ -607,8 +661,8 @@ namespace usb_pad GtkWidget* chk_btn = gtk_check_button_new_with_label("Enable"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_btn), (gboolean)cfg.use_hidraw_ff_pt); - g_object_set_data(G_OBJECT(chk_btn), CFG, &cfg); - g_signal_connect(G_OBJECT(chk_btn), "toggled", G_CALLBACK(hidraw_toggled), reinterpret_cast(port)); + //g_object_set_data(G_OBJECT(chk_btn), CFG, &cfg); + g_signal_connect(G_OBJECT(chk_btn), "toggled", G_CALLBACK(checkbox_toggled), reinterpret_cast(&cfg.use_hidraw_ff_pt)); gtk_box_pack_start(GTK_BOX(frame_vbox), chk_btn, FALSE, FALSE, 5); rs_cb = new_combobox("Device:", frame_vbox); @@ -617,12 +671,12 @@ namespace usb_pad for (auto& it : cfg.joysticks) { std::stringstream str; - str << it.first; - if (strcmp(apiname, "evdev") && !it.second.empty()) - str << " [" << it.second << "]"; + str << it.name; + if (!strcmp(apiname, "evdev") && !it.id.empty()) + str << " [" << it.id << "]"; gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), str.str().c_str()); - if (path == it.second) + if (path == it.path) sel_idx = idx; idx++; } @@ -640,7 +694,7 @@ namespace usb_pad { if (cfg.js_iter != cfg.joysticks.end()) { - if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->second)) + if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->path)) ret = RESULT_FAILED; } @@ -651,6 +705,12 @@ namespace usb_pad { SaveSetting(dev_type, port, apiname, N_HIDRAW_FF_PT, cfg.use_hidraw_ff_pt); } + for (int i = 0; i < 2; i++) + { + SaveSetting(dev_type, port, apiname, ff_var_name[i][0], ff_enabled[i]); + int val = gtk_range_get_value(GTK_RANGE(ff_scales[i])); + SaveSetting(dev_type, port, apiname, ff_var_name[i][1], val); + } } else ret = RESULT_CANCELED; @@ -699,17 +759,17 @@ namespace usb_pad for (const auto& it : cfg.joysticks) { - if ((fd = open(it.second.c_str(), O_RDONLY | O_NONBLOCK)) < 0) + if ((fd = open(it.path.c_str(), O_RDONLY | O_NONBLOCK)) < 0) { - OSDebugOut("Cannot open device: %s\n", it.second.c_str()); + OSDebugOut("Cannot open device: %s\n", it.path.c_str()); continue; } ConfigMapping c; c.fd = fd; - LoadBuzzMappings(cfg.dev_type, port, it.first, c); - cfg.jsconf.push_back(std::make_pair(it.first, c)); - OSDebugOut("mappings for '%s': %lu\n", it.first.c_str(), c.controls.size()); + LoadBuzzMappings(cfg.dev_type, port, it.id, c); + cfg.jsconf.push_back(std::make_pair(it.id, c)); + OSDebugOut("mappings for '%s': %lu\n", it.name.c_str(), c.controls.size()); } refresh_store(&cfg); @@ -867,7 +927,7 @@ namespace usb_pad { if (cfg.js_iter != cfg.joysticks.end()) { - if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->second)) + if (!SaveSetting(dev_type, port, apiname, N_JOYSTICK, cfg.js_iter->path)) ret = RESULT_FAILED; } diff --git a/pcsx2/USB/usb-pad/evdev/shared.h b/pcsx2/USB/usb-pad/evdev/shared.h index c77f2f0bc4..090cd6f76a 100644 --- a/pcsx2/USB/usb-pad/evdev/shared.h +++ b/pcsx2/USB/usb-pad/evdev/shared.h @@ -21,8 +21,19 @@ #include "../../configuration.h" #define N_HIDRAW_FF_PT "hidraw_ff_pt" +#define N_GAIN_ENABLED "gain_enabled" +#define N_GAIN "gain" +#define N_AUTOCENTER "autocenter" +#define N_AUTOCENTER_MANAGED "ac_managed" -typedef std::vector> vstring; +struct evdev_device +{ + std::string name; + std::string id; + std::string path; +}; + +typedef std::vector device_list; GtkWidget* new_combobox(const char* label, GtkWidget* vbox); namespace usb_pad @@ -65,7 +76,7 @@ namespace usb_pad JOY_MAPS_COUNT }; - static const char* JoystickMapNames[] = { + static constexpr const char* JoystickMapNames[] = { "cross", "square", "circle", @@ -86,7 +97,7 @@ namespace usb_pad "throttle", "brake"}; - static const char* buzz_map_names[] = { + static constexpr const char* buzz_map_names[] = { "red", "yellow", "green", @@ -103,24 +114,31 @@ namespace usb_pad struct ConfigMapping { - std::vector controls; + std::vector controls; int inverted[3]; int initial[3]; int fd = -1; + + ConfigMapping() = default; + ConfigMapping(int fd_) + : fd(fd_) + { + } }; struct ApiCallbacks { bool (*get_event_name)(const char* dev_type, int map, int event, const char** name); - void (*populate)(vstring& jsdata); + void (*populate)(device_list& jsdata); bool (*poll)(const std::vector>& jsconf, std::string& dev_name, bool isaxis, int& value, bool& inverted, int& initial); }; + typedef std::pair MappingPair; struct ConfigData { - std::vector> jsconf; - vstring joysticks; - vstring::const_iterator js_iter; + std::vector jsconf; + device_list joysticks; + device_list::const_iterator js_iter; GtkWidget* label; GtkListStore* store; GtkTreeView* treeview; diff --git a/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp b/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp index 2e88b65d14..82126a0afd 100644 --- a/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp +++ b/pcsx2/USB/usb-pad/joydev/joydev-gtk.cpp @@ -173,7 +173,7 @@ namespace usb_pad return RESULT_CANCELED; evdev::ApiCallbacks apicbs{GetEventName, EnumerateDevices, PollInput}; - int ret = evdev::GtkPadConfigure(port, dev_type, "Joydev Settings", "joydev", GTK_WINDOW(data), apicbs); + int ret = evdev::GtkPadConfigure(port, dev_type, "Joydev Settings", joydev::APINAME, GTK_WINDOW(data), apicbs); return ret; } diff --git a/pcsx2/USB/usb-pad/joydev/joydev.cpp b/pcsx2/USB/usb-pad/joydev/joydev.cpp index d75a6e7296..bba5720b10 100644 --- a/pcsx2/USB/usb-pad/joydev/joydev.cpp +++ b/pcsx2/USB/usb-pad/joydev/joydev.cpp @@ -29,7 +29,7 @@ namespace usb_pad #define NORM(x, n) (((uint32_t)(32768 + x) * n) / 0xFFFF) #define NORM2(x, n) (((uint32_t)(32768 + x) * n) / 0x7FFF) - void EnumerateDevices(vstring& list) + void EnumerateDevices(device_list& list) { int fd; int res; @@ -69,7 +69,7 @@ namespace usb_pad else { OSDebugOut("Joydev device name: %s\n", buf); - list.push_back(std::make_pair(std::string(buf), path)); + list.push_back({buf, buf, path}); } close(fd); @@ -114,7 +114,6 @@ namespace usb_pad continue; } - const auto& mappings = device.cfg.controls; //Non-blocking read sets len to -1 and errno to EAGAIN if no new data while ((len = read(device.cfg.fd, &events, sizeof(events))) > -1) { @@ -325,9 +324,10 @@ namespace usb_pad int JoyDevPad::Open() { - vstring device_list; + device_list device_list; bool has_steering; int count; + int32_t b_gain, gain, b_ac, ac; memset(&mWheelData, 0, sizeof(wheel_data_t)); // Setting to unpressed @@ -350,17 +350,26 @@ namespace usb_pad EnumerateDevices(device_list); + if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN_ENABLED, b_gain)) + b_gain = 1; + if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN, gain)) + gain = 100; + if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER_MANAGED, b_ac)) + b_ac = 1; + if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER, ac)) + ac = 100; + for (const auto& it : device_list) { has_steering = false; mDevices.push_back({}); struct device_data& device = mDevices.back(); - device.name = it.first; + device.name = it.name; - if ((device.cfg.fd = open(it.second.c_str(), O_RDWR | O_NONBLOCK)) < 0) + if ((device.cfg.fd = open(it.path.c_str(), O_RDWR | O_NONBLOCK)) < 0) { - OSDebugOut("Cannot open device: %s\n", it.second.c_str()); + OSDebugOut("Cannot open device: %s\n", it.path.c_str()); continue; } @@ -441,12 +450,12 @@ namespace usb_pad std::stringstream event; int index = 0; - const char* tmp = it.second.c_str(); + const char* tmp = it.path.c_str(); while (*tmp && !isdigit(*tmp)) tmp++; sscanf(tmp, "%d", &index); - OSDebugOut("input index: %d of '%s'\n", index, it.second.c_str()); + OSDebugOut("input index: %d of '%s'\n", index, it.path.c_str()); //TODO kernel limit is 32? for (int j = 0; j <= 99; j++) @@ -472,15 +481,11 @@ namespace usb_pad OSDebugOut("%s: Cannot open '%s'\n", APINAME, event.str().c_str()); } else - mFFdev = new evdev::EvdevFF(mHandleFF); + mFFdev = new evdev::EvdevFF(mHandleFF, b_gain, gain, b_ac, ac); } } return 0; - - quit: - Close(); - return 1; } int JoyDevPad::Close() diff --git a/pcsx2/USB/usb-pad/joydev/joydev.h b/pcsx2/USB/usb-pad/joydev/joydev.h index 155ca886c5..c6b81ba786 100644 --- a/pcsx2/USB/usb-pad/joydev/joydev.h +++ b/pcsx2/USB/usb-pad/joydev/joydev.h @@ -23,9 +23,9 @@ namespace usb_pad namespace joydev { - void EnumerateDevices(vstring& list); + void EnumerateDevices(device_list& list); - static const char* APINAME = "joydev"; + static constexpr const char* APINAME = "joydev"; class JoyDevPad : public Pad { @@ -50,8 +50,10 @@ namespace usb_pad static int Configure(int port, const char* dev_type, void* data); protected: - int mHandleFF; - struct wheel_data_t mWheelData; + int mHandleFF = -1; + struct wheel_data_t mWheelData + { + }; std::vector mDevices; }; diff --git a/pcsx2/USB/usb-pad/raw/raw-config.cpp b/pcsx2/USB/usb-pad/raw/raw-config.cpp index 2a0fb2223a..1e9870c69e 100644 --- a/pcsx2/USB/usb-pad/raw/raw-config.cpp +++ b/pcsx2/USB/usb-pad/raw/raw-config.cpp @@ -695,7 +695,7 @@ namespace usb_pad if (!InitHid()) return FALSE; dgHwnd = hW; - SetWindowLongPtr(hW, GWLP_USERDATA, (LONG)lParam); + SetWindowLongPtr(hW, GWLP_USERDATA, lParam); //SendDlgItemMessage(hW, IDC_BUILD_DATE, WM_SETTEXT, 0, (LPARAM)__DATE__ " " __TIME__); ListView_SetExtendedListViewStyle(GetDlgItem(hW, IDC_LIST1), LVS_EX_FULLROWSELECT); diff --git a/pcsx2/USB/usb-pad/usb-pad-ff.cpp b/pcsx2/USB/usb-pad/usb-pad-ff.cpp index bbbafd9c76..d5c0a9ade6 100644 --- a/pcsx2/USB/usb-pad/usb-pad-ff.cpp +++ b/pcsx2/USB/usb-pad/usb-pad-ff.cpp @@ -88,6 +88,12 @@ namespace usb_pad ffdev->SetFrictionForce(ff); } + void SetAutoCenter(FFDevice* ffdev, const autocenter& effect) + { + OSDebugOut(_T("%s: k1 %d k2 %d clip %d\n"), __func__, effect.k1, effect.k2, effect.clip); + ffdev->SetAutoCenter((effect.k1 * effect.clip / 255) * 100 / 255); // FIXME + } + // Unless passing ff packets straight to a device, parse it here void Pad::ParseFFData(const ff_data* ffdata, bool isDFP) { @@ -205,6 +211,9 @@ namespace usb_pad caps |= FF_LG_CAPS_DAMPER_CLIP; SetDamperForce(mFFdev, ffdata->u.damper, caps); break; + case FTYPE_AUTO_CENTER_SPRING: + SetAutoCenter(mFFdev, ffdata->u.autocenter); + break; default: OSDebugOut(TEXT("CMD_DOWNLOAD_AND_PLAY: unhandled force type 0x%02X in slots 0x%02X\n"), ffdata->type, slots); break; @@ -230,9 +239,9 @@ namespace usb_pad case FTYPE_HIGH_RESOLUTION_SPRING: mFFdev->DisableForce(EFF_SPRING); break; - //case FTYPE_AUTO_CENTER_SPRING: - //mFFdev->DisableSpring(); - //break; + case FTYPE_AUTO_CENTER_SPRING: + mFFdev->SetAutoCenter(0); + break; case FTYPE_FRICTION: mFFdev->DisableForce(EFF_FRICTION); break; diff --git a/pcsx2/USB/usb-pad/usb-pad.cpp b/pcsx2/USB/usb-pad/usb-pad.cpp index 6fb6178e16..07b728d46f 100644 --- a/pcsx2/USB/usb-pad/usb-pad.cpp +++ b/pcsx2/USB/usb-pad/usb-pad.cpp @@ -420,6 +420,14 @@ namespace usb_pad buf[3] = (data.buttons >> 8) & 0xff; buf[4] = 0xf0 | ((data.buttons >> 16) & 0xf); break; + case WT_SEGA_SEAMIC: + buf[0] = data.steering & 0xFF; + buf[1] = data.throttle & 0xFF; + buf[2] = data.brake & 0xFF; + buf[3] = data.hatswitch & 0x0F; // 4bits? + buf[3] |= (data.buttons & 0x0F) << 4; // 4 bits // TODO Or does it start at buf[4]? + buf[4] = (data.buttons >> 4) & 0x3F; // 10 - 4 = 6 bits + break; default: break; @@ -629,17 +637,15 @@ namespace usb_pad { PADState* s = (PADState*)dev; + if (!s) + return 0; switch (mode) { case FREEZE_LOAD: - if (!s) - return -1; s->f = *(PADState::freeze*)data; s->pad->Type((PS2WheelTypes)s->f.wheel_type); return sizeof(PADState::freeze); case FREEZE_SAVE: - if (!s) - return -1; *(PADState::freeze*)data = s->f; return sizeof(PADState::freeze); case FREEZE_SIZE: @@ -647,7 +653,7 @@ namespace usb_pad default: break; } - return -1; + return 0; } // ---- Rock Band drum kit ---- diff --git a/pcsx2/USB/usb-pad/usb-pad.h b/pcsx2/USB/usb-pad/usb-pad.h index 5b0afb0729..b13b3d7bed 100644 --- a/pcsx2/USB/usb-pad/usb-pad.h +++ b/pcsx2/USB/usb-pad/usb-pad.h @@ -100,6 +100,25 @@ namespace usb_pad static void Initialize(); }; + class SeamicDevice + { + public: + virtual ~SeamicDevice() {} + static USBDevice* CreateDevice(int port); + static const TCHAR* Name() + { + return TEXT("Sega Seamic"); + } + static const char* TypeName() + { + return "seamic"; + } + static std::list ListAPIs(); + static const TCHAR* LongAPIName(const std::string& name); + static int Configure(int port, const std::string& api, void* data); + static int Freeze(int mode, USBDevice* dev, void* data); + }; + // Most likely as seen on https://github.com/matlo/GIMX #define CMD_DOWNLOAD 0x00 #define CMD_DOWNLOAD_AND_PLAY 0x01 @@ -148,12 +167,15 @@ namespace usb_pad WT_GT_FORCE, //formula gp WT_ROCKBAND1_DRUMKIT, WT_BUZZ_CONTROLLER, + WT_SEGA_SEAMIC, }; inline int range_max(PS2WheelTypes type) { if (type == WT_DRIVING_FORCE_PRO || type == WT_DRIVING_FORCE_PRO_1102) return 0x3FFF; + if (type == WT_SEGA_SEAMIC) + return 255; return 0x3FF; } @@ -296,7 +318,7 @@ namespace usb_pad virtual void SetSpringForce(const parsed_ff_data& ff) = 0; virtual void SetDamperForce(const parsed_ff_data& ff) = 0; virtual void SetFrictionForce(const parsed_ff_data& ff) = 0; - //virtual void SetAutoCenter(int value) = 0; + virtual void SetAutoCenter(int value) = 0; //virtual void SetGain(int gain) = 0; virtual void DisableForce(EffectID force) = 0; }; @@ -1083,7 +1105,7 @@ namespace usb_pad 0x03, // bmAttributes (Interrupt) 0x40, 0x00, // wMaxPacketSize 64 0x0A, // bInterval 10 (unit depends on device speed) - // 41 bytes + // 41 bytes }; //Wii Rock Band drum kit diff --git a/pcsx2/USB/usb-pad/usb-seamic.cpp b/pcsx2/USB/usb-pad/usb-seamic.cpp new file mode 100644 index 0000000000..1a58a4efd1 --- /dev/null +++ b/pcsx2/USB/usb-pad/usb-seamic.cpp @@ -0,0 +1,467 @@ +#include "padproxy.h" +#include "usb-pad.h" +#include "../qemu-usb/desc.h" +#include "../usb-mic/usb-mic-singstar.h" + +namespace usb_pad +{ + + static const USBDescStrings desc_strings = { + "", + "ASCII CORPORATION", + "ASCII Mic/Joy-stick", + }; + + static const uint8_t dev_descriptor[] = { + /* bLength */ 0x12, //(18) + /* bDescriptorType */ 0x01, //(1) + /* bcdUSB */ WBVAL(0x0110), + /* bDeviceClass */ 0x00, //(0) + /* bDeviceSubClass */ 0x00, //(0) + /* bDeviceProtocol */ 0x00, //(0) + /* bMaxPacketSize0 */ 0x08, //(8) + /* idVendor */ WBVAL(0x0B49), + /* idProduct */ WBVAL(0x0644), + /* bcdDevice */ WBVAL(0x0100), + /* iManufacturer */ 0x01, + /* iProduct */ 0x02, + /* iSerialNumber */ 0x00, + /* bNumConfigurations */ 0x01, + }; + + static const uint8_t hid_report_descriptor[] = { + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection (Physical) + 0x95, 0x03, // Report Count (3) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xFF, 0x00, // Physical Maximum (255) + 0x66, 0x00, 0x00, // Unit (None) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x01, // Report Count (1) + 0x75, 0x04, // Report Size (4) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x66, 0x14, 0x00, // Unit (System: English Rotation, Length: Centimeter) + 0x09, 0x39, // Usage (Hat switch) + 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) + 0x95, 0x0A, // Report Count (10) + 0x75, 0x01, // Report Size (1) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x66, 0x00, 0x00, // Unit (None) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (0x01) + 0x29, 0x0A, // Usage Maximum (0x0A) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x02, // Report Count (2) + 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, 0x08, // Report Count (8) + 0x75, 0x01, // Report Size (1) + 0x05, 0x08, // Usage Page (LEDs) + 0x19, 0x01, // Usage Minimum (Num Lock) + 0x29, 0x08, // Usage Maximum (Do Not Disturb) + 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, // End Collection + 0xC0, // End Collection + + // 98 bytes + }; + + static const uint8_t config_descriptor[] = { + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x86, 0x00, // wTotalLength 134 + 0x03, // bNumInterfaces 3 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0x80, // bmAttributes + 0x31, // bMaxPower 98mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x00, // bNumEndpoints 0 + 0x01, // bInterfaceClass (Audio) + 0x01, // bInterfaceSubClass (Audio Control) + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x01, // bDescriptorSubtype (CS_INTERFACE -> HEADER) + 0x00, 0x01, // bcdADC 1.00 + WBVAL(38), // wTotalLength 38 + 0x01, // binCollection 0x01 + 0x01, // baInterfaceNr 1 + + 0x0C, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x02, // bDescriptorSubtype (CS_INTERFACE -> INPUT_TERMINAL) + 0x01, // bTerminalID + 0x01, 0x02, // wTerminalType (Microphone) + 0x02, // bAssocTerminal + 0x01, // bNrChannels 1 + 0x00, 0x00, // wChannelConfig + 0x00, // iChannelNames + 0x00, // iTerminal + + 0x09, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x03, // bDescriptorSubtype (CS_INTERFACE -> OUTPUT_TERMINAL) + 0x02, // bTerminalID + 0x01, 0x01, // wTerminalType (USB Streaming) + 0x01, // bAssocTerminal + 0x03, // bSourceID + 0x00, // iTerminal + + 0x08, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x06, // bDescriptorSubtype (CS_INTERFACE -> FEATURE_UNIT) + 0x03, // bUnitID + 0x01, // bSourceID + 0x01, // bControlSize 1 + 0x03, 0x00, // bmaControls[0] (Mute,Volume) + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x01, // bInterfaceNumber 1 + 0x00, // bAlternateSetting + 0x00, // bNumEndpoints 0 + 0x01, // bInterfaceClass (Audio) + 0x02, // bInterfaceSubClass (Audio Streaming) + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x01, // bInterfaceNumber 1 + 0x01, // bAlternateSetting + 0x01, // bNumEndpoints 1 + 0x01, // bInterfaceClass (Audio) + 0x02, // bInterfaceSubClass (Audio Streaming) + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x07, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x01, // bDescriptorSubtype (CS_INTERFACE -> AS_GENERAL) + 0x02, // bTerminalLink + 0x01, // bDelay 1 + 0x01, 0x00, // wFormatTag (PCM) + + 0x0E, // bLength + 0x24, // bDescriptorType (See Next Line) + 0x02, // bDescriptorSubtype (CS_INTERFACE -> FORMAT_TYPE) + 0x01, // bFormatType 1 + 0x01, // bNrChannels (Mono) + 0x02, // bSubFrameSize 2 + 0x10, // bBitResolution 16 + 0x02, // bSamFreqType 2 + B3VAL(8000), // tSamFreq[1] 8000 Hz + B3VAL(11025), // tSamFreq[2] 11025 Hz + + 0x07, // bLength + USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType (See Next Line) + USB_ENDPOINT_IN(1), // bEndpointAddress (IN/D2H) + 0x01, // bmAttributes (Isochronous, No Sync, Data EP) + WBVAL(100), // wMaxPacketSize 100 + 0x01, // bInterval 1 (unit depends on device speed) + + 0x07, // bLength + 0x25, // bDescriptorType (See Next Line) + 0x01, // bDescriptorSubtype (CS_ENDPOINT -> EP_GENERAL) + 0x01, // bmAttributes (Sampling Freq Control) + 0x00, // bLockDelayUnits + 0x00, 0x00, // wLockDelay 0 + + USB_INTERFACE_DESC_SIZE, // bLength + USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType (Interface) + 0x02, // bInterfaceNumber 2 + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints 1 + USB_CLASS_HID, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x09, // bLength + USB_DT_HID, // bDescriptorType (HID) + WBVAL(0x0100), // bcdHID 1.00 + 0x00, // bCountryCode + 0x01, // bNumDescriptors + USB_DT_REPORT, // bDescriptorType[0] (HID) + WBVAL(98), // wDescriptorLength[0] 98 + + 0x07, // bLength + USB_ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType (Endpoint) + USB_ENDPOINT_IN(2), // bEndpointAddress (IN/D2H) + USB_ENDPOINT_TYPE_INTERRUPT, // bmAttributes (Interrupt) + WBVAL(8), // wMaxPacketSize 8 + 0x0A, // bInterval 10 (unit depends on device speed) + + // 134 bytes + }; + + std::list SeamicDevice::ListAPIs() + { + return RegisterPad::instance().Names(); + } + + const TCHAR* SeamicDevice::LongAPIName(const std::string& name) + { + auto proxy = RegisterPad::instance().Proxy(name); + if (proxy) + return proxy->Name(); + return nullptr; + } + + typedef struct SeamicState + { + USBDevice dev; + USBDesc desc; + USBDescDevice desc_dev; + USBDevice* mic; + Pad* pad; + uint8_t port; + struct freeze + { + int nothing; + } f; + } SeamicState; + + static void pad_handle_data(USBDevice* dev, USBPacket* p) + { + SeamicState* s = (SeamicState*)dev; + uint8_t data[64]; + + int ret = 0; + uint8_t devep = p->ep->nr; + + switch (p->pid) + { + case USB_TOKEN_IN: + if (devep == 1 && s->mic) + { + s->mic->klass.handle_data(s->mic, p); + } + else if (devep == 2 && s->pad) + { + ret = s->pad->TokenIn(data, p->iov.size); + if (ret > 0) + usb_packet_copy(p, data, MIN(ret, sizeof(data))); + else + p->status = ret; + } + else + { + goto fail; + } + break; + case USB_TOKEN_OUT: + usb_packet_copy(p, data, MIN(p->iov.size, sizeof(data))); + ret = s->pad->TokenOut(data, p->iov.size); + break; + default: + fail: + p->status = USB_RET_STALL; + break; + } + } + + static void pad_handle_reset(USBDevice* dev) + { + /* XXX: do it */ + SeamicState* s = (SeamicState*)dev; + s->pad->Reset(); + s->mic->klass.handle_reset(s->mic); + return; + } + + static void pad_handle_control(USBDevice* dev, USBPacket* p, int request, int value, + int index, int length, uint8_t* data) + { + int ret = 0; + + switch (request) + { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); + if (ret < 0) + goto fail; + + break; + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: + OSDebugOut(TEXT("InterfaceRequest | USB_REQ_GET_DESCRIPTOR 0x%04X\n"), value); + switch (value >> 8) + { + case USB_DT_REPORT: + OSDebugOut(TEXT("Sending hid report desc.\n")); + ret = sizeof(hid_report_descriptor); + memcpy(data, hid_report_descriptor, ret); + p->actual_length = ret; + break; + default: + goto fail; + } + break; + /* hid specific requests */ + case SET_REPORT: + if (length > 0) + { + OSDebugOut(TEXT("SET_REPORT: 0x%02X \n"), data[0]); + p->actual_length = 0; + //p->status = USB_RET_SUCCESS; + } + break; + case SET_IDLE: + OSDebugOut(TEXT("SET_IDLE\n")); + break; + default: + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); + if (ret >= 0) + { + return; + } + fail: + p->status = USB_RET_STALL; + break; + } + } + + static void pad_handle_destroy(USBDevice* dev) + { + SeamicState* s = (SeamicState*)dev; + s->mic->klass.unrealize(s->mic); + s->mic = nullptr; + delete s; + } + + static int pad_open(USBDevice* dev) + { + SeamicState* s = (SeamicState*)dev; + if (s) + { + s->mic->klass.open(s->mic); + return s->pad->Open(); + } + return 1; + } + + static void pad_close(USBDevice* dev) + { + SeamicState* s = (SeamicState*)dev; + if (s) + { + s->mic->klass.close(s->mic); + s->pad->Close(); + } + } + + USBDevice* SeamicDevice::CreateDevice(int port) + { + std::string varApi; + LoadSetting(nullptr, port, TypeName(), N_DEVICE_API, varApi); + PadProxyBase* proxy = RegisterPad::instance().Proxy(varApi); + if (!proxy) + { + SysMessage(TEXT("PAD: Invalid input API.\n")); + USB_LOG("usb-pad: %s: Invalid input API.\n", TypeName()); + return NULL; + } + + USB_LOG("usb-pad: creating device '%s' on port %d with %s\n", TypeName(), port, varApi.c_str()); + + std::string api; + if (!LoadSetting(nullptr, port, usb_mic::SingstarDevice::TypeName(), N_DEVICE_API, api)) + return nullptr; + + USBDevice* mic = usb_mic::SingstarDevice::CreateDevice(port, api); + if (!mic) + return nullptr; + + Pad* pad = proxy->CreateObject(port, TypeName()); + + if (!pad) + return NULL; + + pad->Type(WT_SEGA_SEAMIC); + SeamicState* s = new SeamicState(); + + s->mic = mic; + s->desc.full = &s->desc_dev; + s->desc.str = desc_strings; + + if (usb_desc_parse_dev(dev_descriptor, sizeof(dev_descriptor), s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(config_descriptor, sizeof(config_descriptor), s->desc_dev) < 0) + goto fail; + + s->pad = pad; + s->dev.speed = USB_SPEED_FULL; + s->dev.klass.handle_attach = usb_desc_attach; + s->dev.klass.handle_reset = pad_handle_reset; + s->dev.klass.handle_control = pad_handle_control; + s->dev.klass.handle_data = pad_handle_data; + s->dev.klass.unrealize = pad_handle_destroy; + s->dev.klass.open = pad_open; + s->dev.klass.close = pad_close; + s->dev.klass.usb_desc = &s->desc; + s->dev.klass.product_desc = s->desc.str[2]; //not really used + s->port = port; + + usb_desc_init(&s->dev); + usb_ep_init(&s->dev); + pad_handle_reset((USBDevice*)s); + + return (USBDevice*)s; + + fail: + pad_handle_destroy((USBDevice*)s); + return nullptr; + } + + int SeamicDevice::Configure(int port, const std::string& api, void* data) + { + auto proxy = RegisterPad::instance().Proxy(api); + if (proxy) + return proxy->Configure(port, TypeName(), data); + return RESULT_CANCELED; + } + + int SeamicDevice::Freeze(int mode, USBDevice* dev, void* data) + { + return 0; + // SeamicState *s = (SeamicState *)dev; + // switch (mode) + // { + // case FREEZE_LOAD: + // if (!s) return -1; + // s->f = *(SeamicState::freeze *)data; + // return sizeof(SeamicState::freeze); + // case FREEZE_SAVE: + // if (!s) return -1; + // return sizeof(SeamicState::freeze); + // case FREEZE_SIZE: + // return sizeof(SeamicState::freeze); + // default: + // break; + // } + // return -1; + } + +} // namespace usb_pad