diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 785c8d9307..c6047a01cb 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -412,6 +412,7 @@ set(pcsx2USBSources USB/shared/shared_usb.cpp USB/shared/inifile_usb.cpp USB/shared/ringbuffer.cpp + USB/usb-printer/usb-printer.cpp ) # USB headers @@ -456,6 +457,7 @@ set(pcsx2USBHeaders USB/shared/shared_usb.h USB/shared/inifile_usb.h USB/shared/ringbuffer.h + USB/usb-printer/usb-printer.h ) if(TARGET PulseAudio::PulseAudio) diff --git a/pcsx2/USB/device_init.cpp b/pcsx2/USB/device_init.cpp index 3a4034054b..7cbf26637b 100644 --- a/pcsx2/USB/device_init.cpp +++ b/pcsx2/USB/device_init.cpp @@ -15,12 +15,13 @@ #include "PrecompiledHeader.h" #include "deviceproxy.h" -#include "usb-pad/usb-pad.h" -#include "usb-msd/usb-msd.h" -#include "usb-mic/usb-mic-singstar.h" -#include "usb-mic/usb-headset.h" -#include "usb-hid/usb-hid.h" #include "usb-eyetoy/usb-eyetoy-webcam.h" +#include "usb-hid/usb-hid.h" +#include "usb-mic/usb-headset.h" +#include "usb-mic/usb-mic-singstar.h" +#include "usb-msd/usb-msd.h" +#include "usb-pad/usb-pad.h" +#include "usb-printer/usb-printer.h" void RegisterDevice::Register() { @@ -43,6 +44,7 @@ void RegisterDevice::Register() inst.Add(DEVTYPE_EYETOY, new DeviceProxy()); inst.Add(DEVTYPE_BEATMANIA_DADADA, new DeviceProxy()); inst.Add(DEVTYPE_SEGA_SEAMIC, new DeviceProxy()); + inst.Add(DEVTYPE_PRINTER, new DeviceProxy()); inst.Add(DEVTYPE_KEYBOARDMANIA, new DeviceProxy()); RegisterAPIs(); diff --git a/pcsx2/USB/deviceproxy.h b/pcsx2/USB/deviceproxy.h index 88609d5ccc..35fd7ffd24 100644 --- a/pcsx2/USB/deviceproxy.h +++ b/pcsx2/USB/deviceproxy.h @@ -49,6 +49,7 @@ enum DeviceType DEVTYPE_EYETOY, DEVTYPE_BEATMANIA_DADADA, DEVTYPE_SEGA_SEAMIC, + DEVTYPE_PRINTER, DEVTYPE_KEYBOARDMANIA, }; diff --git a/pcsx2/USB/usb-printer/usb-printer.cpp b/pcsx2/USB/usb-printer/usb-printer.cpp new file mode 100644 index 0000000000..679ec0b266 --- /dev/null +++ b/pcsx2/USB/usb-printer/usb-printer.cpp @@ -0,0 +1,296 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" +#include "../qemu-usb/vl.h" +#include "../shared/inifile_usb.h" +#include "usb-printer.h" +#include "gui/AppConfig.h" + +#ifndef O_BINARY + #define O_BINARY 0 +#endif + +namespace usb_printer +{ + typedef struct PrinterState + { + USBDevice dev; + USBDesc desc; + USBDescDevice desc_dev; + + int selected_printer; + int cmd_state; + uint8_t last_command[65]; + int last_command_size; + int print_file; + int width; + int height; + int data_size; + } PrinterState; + + static void usb_printer_handle_reset(USBDevice* dev) + { + PrinterState* s = (PrinterState*)dev; + s->cmd_state = 0; + if (s->print_file > 0) + { + close(s->print_file); + } + s->print_file = -1; + } + + static void usb_printer_handle_control(USBDevice* dev, USBPacket* p, int request, int value, + int index, int length, uint8_t* data) + { + PrinterState* s = (PrinterState*)dev; + int ret = 0; + + ret = usb_desc_handle_control(dev, p, request, value, index, length, data); + if (ret >= 0) + { + return; + } + + switch (request) + { + case ClassInterfaceRequest | GET_DEVICE_ID: + ret = 2 + sprintf((char*)data + 2, sPrinters[s->selected_printer].device_id); + data[0] = ret >> 8; + data[1] = ret & 0xff; + p->actual_length = ret; + break; + case ClassInterfaceRequest | GET_PORT_STATUS: + data[0] = GET_PORT_STATUS_PAPER_NOT_EMPTY | GET_PORT_STATUS_SELECTED | GET_PORT_STATUS_NO_ERROR; + p->actual_length = 1; + break; + } + } + + void sony_open_file(PrinterState* s) + { + char filepath[1024]; + char cur_time_str[32]; + const time_t cur_time = time(nullptr); + strftime(cur_time_str, sizeof(cur_time_str), "%Y_%m_%d_%H_%M_%S", localtime(&cur_time)); + snprintf(filepath, sizeof(filepath), "%s/print_%s.ppm", + g_Conf->Folders.Snapshots.ToString().ToStdString().c_str(), cur_time_str); + s->print_file = open(filepath, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 666); + if (s->print_file < 0) + { + Console.WriteLn("Printer: Sony: Cannot open: %s", filepath); + return; + } + Console.WriteLn("Printer: Sony: Saving to... %s", filepath); + char header[30]; + int header_size = sprintf(header, "P6 %d %d 255\n", s->width, s->height); + write(s->print_file, header, header_size); + } + + void sony_write_data(PrinterState* s, int size, uint8_t* data) + { + if (s->print_file >= 0) + { + write(s->print_file, data, size); + } + } + + void sony_close_file(PrinterState* s) + { + Console.WriteLn("Printer: Sony: done."); + if (s->print_file >= 0) + { + close(s->print_file); + } + s->print_file = -1; + } + + static void usb_printer_handle_data_sony(USBDevice* dev, USBPacket* p) + { + struct req_reply + { + uint8_t cmd[64]; + uint8_t ret[256]; + }; + const struct req_reply commands[] = + { + { + {0x1B, 0xE0, 0x00, 0x00, 0x00, 0x0E, 0x00}, + {0x0D, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x01, 0x24, 0x38, 0x03, 0xF2, 0x02, 0x74} + }, + { + {0x1B, 0xCF, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00}, + {0x00, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + { + {0x1B, 0xCF, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00}, + {0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x07} + }, + { + {0x1B, 0xCF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00}, + {0x00, 0x00, 0x00, 0x12, 0x03, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x04} + }, + { + {0x1B, 0xCF, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00}, + {0x00, 0x00, 0x00, 0x0A, 0x04, 0x00, 0x00, 0x18, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00} + }, + { + {0x1B, 0xCF, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00}, + {0x00, 0x00, 0x00, 0x0E, 0x05, 0x00, 0x02, 0x74, 0x03, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00} + }, + { + {0x1B, 0x12, 0x01, 0x00, 0x00, 0x19, 0x00}, + {0x02, 0xC0, 0x00, 0x15, 0x03, 0xF2, 0x02, 0x74, 0x03, 0xF2, 0x02, 0x74, 0x01, 0x33, 0x00, 0x00, + 0x15, 0x01, 0x03, 0x23, 0x30, 0x31, 0x2E, 0x30, 0x30} + } + }; + const uint8_t set_size[] = {0x00, 0x00, 0x00, 0x00, 0xa7, 0x00}; + const uint8_t set_data[] = {0x1b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00}; + const uint8_t print_compl[] = {0x1b, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00}; + + PrinterState* s = (PrinterState*)dev; + const uint8_t ep_nr = p->ep->nr; + const uint8_t ep_type = p->ep->type; + + switch (p->pid) + { + case USB_TOKEN_OUT: + s->last_command_size = p->iov.size; + usb_packet_copy(p, s->last_command, s->last_command_size); + + if (s->cmd_state == 0 && s->last_command_size > 5) + { + if (memcmp(s->last_command, set_size, sizeof(set_size)) == 0) + { + s->width = s->last_command[9] << 8 | s->last_command[10]; + s->height = s->last_command[11] << 8 | s->last_command[12]; + Console.WriteLn("Printer: Sony: Size=%dx%d", s->width, s->height); + sony_open_file(s); + } + else if (memcmp(s->last_command, set_data, sizeof(set_data)) == 0) + { + s->data_size = s->last_command[8] << 8 | s->last_command[9]; + s->cmd_state = 1; + } + else if (memcmp(s->last_command, print_compl, sizeof(print_compl)) == 0) + { + sony_close_file(s); + } + } + else if (s->cmd_state == 1 && s->last_command_size > 0) + { + sony_write_data(s, s->last_command_size, s->last_command); + s->data_size -= s->last_command_size; + if (s->data_size <= 0) + { + s->cmd_state = 0; + } + } + break; + case USB_TOKEN_IN: + if (s->cmd_state == 0 && s->last_command_size > 0) + { + for (int i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) + { + if (s->last_command[1] == 0xCF) + { + if (memcmp(s->last_command, commands[i].cmd, 11) == 0) + { + int length = commands[i].cmd[9]; + usb_packet_copy(p, (void*)commands[i].ret, length); + s->last_command_size = 0; + } + } + else + { + if (memcmp(s->last_command, commands[i].cmd, 7) == 0) + { + int length = commands[i].cmd[5]; + usb_packet_copy(p, (void*)commands[i].ret, length); + s->last_command_size = 0; + } + } + } + } + break; + default: + p->status = USB_RET_STALL; + break; + } + } + + static void usb_printer_handle_destroy(USBDevice* dev) + { + PrinterState* s = (PrinterState*)dev; + delete s; + } + + USBDevice* PrinterDevice::CreateDevice(int port) + { + PrinterState* s = new PrinterState(); + std::string api = *PrinterDevice::ListAPIs().begin(); + int subtype = GetSelectedSubtype(std::make_pair(port, TypeName())); + if (subtype >= sizeof(sPrinters) / sizeof(sPrinters[0])) { + subtype = 0; + } + s->selected_printer = subtype; + + s->dev.speed = USB_SPEED_FULL; + s->desc.full = &s->desc_dev; + s->desc.str = sPrinters[subtype].usb_strings; + if (usb_desc_parse_dev(sPrinters[subtype].device_descriptor, sPrinters[subtype].device_descriptor_size, s->desc, s->desc_dev) < 0) + goto fail; + if (usb_desc_parse_config(sPrinters[subtype].config_descriptor, sPrinters[subtype].config_descriptor_size, s->desc_dev) < 0) + goto fail; + + s->dev.klass.handle_attach = usb_desc_attach; + s->dev.klass.handle_reset = usb_printer_handle_reset; + s->dev.klass.handle_control = usb_printer_handle_control; + switch (sPrinters[subtype].protocol) + { + case ProtocolSonyUPD: + s->dev.klass.handle_data = usb_printer_handle_data_sony; + break; + } + s->dev.klass.unrealize = usb_printer_handle_destroy; + s->dev.klass.usb_desc = &s->desc; + s->dev.klass.product_desc = s->desc.str[2]; + + usb_desc_init(&s->dev); + usb_ep_init(&s->dev); + usb_printer_handle_reset((USBDevice*)s); + return (USBDevice*)s; + + fail: + usb_printer_handle_destroy((USBDevice*)s); + return nullptr; + } + + int PrinterDevice::Configure(int port, const std::string& api, void* data) + { + return 0; + } + + int PrinterDevice::Freeze(FreezeAction mode, USBDevice* dev, void* data) + { + return 0; + } + +} // namespace usb_printer diff --git a/pcsx2/USB/usb-printer/usb-printer.h b/pcsx2/USB/usb-printer/usb-printer.h new file mode 100644 index 0000000000..7b9034e651 --- /dev/null +++ b/pcsx2/USB/usb-printer/usb-printer.h @@ -0,0 +1,161 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#ifndef USBPRINTER_H +#define USBPRINTER_H + +#include "../deviceproxy.h" +#include "../qemu-usb/desc.h" + +#define GET_DEVICE_ID 0 +#define GET_PORT_STATUS 1 +#define SOFT_RESET 2 + +#define GET_PORT_STATUS_PAPER_EMPTY 1<<5 +#define GET_PORT_STATUS_PAPER_NOT_EMPTY 0<<5 +#define GET_PORT_STATUS_SELECTED 1<<4 +#define GET_PORT_STATUS_NOT_SELECTED 0<<4 +#define GET_PORT_STATUS_NO_ERROR 1<<3 +#define GET_PORT_STATUS_ERROR 0<<3 + +namespace usb_printer +{ + static const uint8_t dpp_mp1_dev_desciptor[] = { + 0x12, // bLength + 0x01, // bDescriptorType (Device) + 0x10, 0x01, // bcdUSB 1.10 + 0x00, // bDeviceClass (Use class information in the Interface Descriptors) + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 0x08, // bMaxPacketSize0 8 + 0x4C, 0x05, // idVendor 0x054C + 0x65, 0x00, // idProduct 0x0065 + 0x04, 0x02, // bcdDevice 2.04 + 0x01, // iManufacturer (String Index) + 0x02, // iProduct (String Index) + 0x00, // iSerialNumber (String Index) + 0x01, // bNumConfigurations 1 + }; + static int dpp_mp1_dev_desciptor_size = sizeof(dpp_mp1_dev_desciptor); + + static const uint8_t dpp_mp1_config_descriptor[] = { + 0x09, // bLength + 0x02, // bDescriptorType (Configuration) + 0x20, 0x00, // wTotalLength 32 + 0x01, // bNumInterfaces 1 + 0x01, // bConfigurationValue + 0x00, // iConfiguration (String Index) + 0xC0, // bmAttributes Self Powered + 0x00, // bMaxPower 0mA + + 0x09, // bLength + 0x04, // bDescriptorType (Interface) + 0x00, // bInterfaceNumber 0 + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints 2 + 0x07, // bInterfaceClass + 0x01, // bInterfaceSubClass + 0x02, // bInterfaceProtocol + 0x00, // iInterface (String Index) + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x01, // bEndpointAddress (OUT/H2D) + 0x02, // bmAttributes (Bulk) + 0x40, 0x00, // wMaxPacketSize 64 + 0x00, // bInterval 0 (unit depends on device speed) + + 0x07, // bLength + 0x05, // bDescriptorType (Endpoint) + 0x82, // bEndpointAddress (IN/D2H) + 0x02, // bmAttributes (Bulk) + 0x40, 0x00, // wMaxPacketSize 64 + 0x00, // bInterval 0 (unit depends on device speed) + }; + static int dpp_mp1_config_descriptor_size = sizeof(dpp_mp1_config_descriptor); + + enum PrinterModel + { + Sony_DPP_MP1, + }; + + enum PrinterProtocol + { + ProtocolSonyUPD, + }; + + struct PrinterData + { + PrinterModel model; + char* commercial_name; + const uint8_t* device_descriptor; + const int device_descriptor_size; + const uint8_t* config_descriptor; + const int config_descriptor_size; + const USBDescStrings usb_strings; + char* device_id; + PrinterProtocol protocol; + }; + + static const PrinterData sPrinters[] = { + { + Sony_DPP_MP1, + "Sony DPP-MP1", + dpp_mp1_dev_desciptor, dpp_mp1_dev_desciptor_size, + dpp_mp1_config_descriptor, dpp_mp1_config_descriptor_size, + {"", "SONY", "USB printer"}, + "MFG:SONY;MDL:DPP-MP1;DES:SONYDPP-MP1;CMD:SONY-Original;CLS:PRINTER", + ProtocolSonyUPD, + }, + }; + + static const char* APINAME = "default"; + + class PrinterDevice + { + public: + virtual ~PrinterDevice() {} + static USBDevice* CreateDevice(int port); + static const TCHAR* Name() + { + return TEXT("Printer"); + } + static const char* TypeName() + { + return "printer"; + } + static std::list ListAPIs() + { + return std::list{APINAME}; + } + static const TCHAR* LongAPIName(const std::string& name) + { + return TEXT("Default"); + } + static int Configure(int port, const std::string& api, void* data); + static int Freeze(FreezeAction mode, USBDevice* dev, void* data); + static std::vector SubTypes() + { + std::vector ret; + for (int i = 0; i < sizeof(sPrinters) / sizeof(sPrinters[0]); i++) + { + ret.push_back(sPrinters[i].commercial_name); + } + return ret; + } + }; + +} // namespace usb_printer +#endif diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index e208251fcf..79c7103b12 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -453,6 +453,7 @@ + @@ -943,6 +944,7 @@ + diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index a840c61946..cefba9d18f 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -241,6 +241,9 @@ {8640e8ca-7d79-4221-b1bf-35bc142a9add} + + {343981c6-ca99-462d-9b20-0500a23ae4cd} + {e1cbcaf6-9f65-4f14-9e89-27dd0f10e047} @@ -1316,6 +1319,9 @@ System\Ps2\USB\usb-msd + + System\Ps2\USB\usb-printer + System\Ps2\USB\shared @@ -2386,6 +2392,9 @@ System\Ps2\USB\usb-mic + + System\Ps2\USB\usb-printer + System\Ps2\USB\shared