358 lines
11 KiB
C++
358 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2009-2016 Benjamin D Lunt (fys [at] fysnet [dot] net)
|
|
// 2009-2021 The Bochs Project
|
|
//
|
|
// This library 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 Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library 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
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
|
|
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
|
|
// is used to know when we are exporting symbols and when we are importing.
|
|
#define BX_PLUGGABLE
|
|
|
|
#include "iodev.h"
|
|
|
|
#if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
|
|
#include "usb_common.h"
|
|
#include "usb_printer.h"
|
|
|
|
#define LOG_THIS
|
|
|
|
// USB device plugin entry point
|
|
|
|
PLUGIN_ENTRY_FOR_MODULE(usb_printer)
|
|
{
|
|
if (mode == PLUGIN_PROBE) {
|
|
return (int)PLUGTYPE_USB;
|
|
}
|
|
return 0; // Success
|
|
}
|
|
|
|
//
|
|
// Define the static class that registers the derived USB device class,
|
|
// and allocates one on request.
|
|
//
|
|
class bx_usb_printer_locator_c : public usbdev_locator_c {
|
|
public:
|
|
bx_usb_printer_locator_c(void) : usbdev_locator_c("usb_printer") {}
|
|
protected:
|
|
usb_device_c *allocate(const char *devname) {
|
|
return (new usb_printer_device_c());
|
|
}
|
|
} bx_usb_printer_match;
|
|
|
|
static const Bit8u bx_printer_dev_descriptor[] = {
|
|
0x12, /* u8 bLength; */
|
|
0x01, /* u8 bDescriptorType; Device */
|
|
0x10, 0x01, /* u16 bcdUSB; v1.10 */
|
|
|
|
0x00, /* u8 bDeviceClass; */
|
|
0x00, /* u8 bDeviceSubClass; */
|
|
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
|
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
|
|
|
0xF0, 0x03, /* u16 idVendor; */
|
|
0x04, 0x15, /* u16 idProduct; */
|
|
0x00, 0x01, /* u16 bcdDevice */
|
|
|
|
0x01, /* u8 iManufacturer; */
|
|
0x02, /* u8 iProduct; */
|
|
0x03, /* u8 iSerialNumber; */
|
|
0x01 /* u8 bNumConfigurations; */
|
|
};
|
|
|
|
static const Bit8u bx_printer_config_descriptor[] = {
|
|
/* one configuration */
|
|
0x09, /* u8 bLength; */
|
|
0x02, /* u8 bDescriptorType; Configuration */
|
|
0x20, 0x00, /* u16 wTotalLength; */
|
|
0x01, /* u8 bNumInterfaces; (1) */
|
|
0x01, /* u8 bConfigurationValue; */
|
|
0x00, /* u8 iConfiguration string desc.; */
|
|
0xC0, /* u8 bmAttributes;
|
|
Bit 7: must be set,
|
|
6: Self-powered,
|
|
5: Remote wakeup,
|
|
4..0: resvd */
|
|
0x02, /* u8 MaxPower; */
|
|
|
|
/* one interface */
|
|
0x09, /* u8 if_bLength; */
|
|
0x04, /* u8 if_bDescriptorType; Interface */
|
|
0x00, /* u8 if_bInterfaceNumber; */
|
|
0x00, /* u8 if_bAlternateSetting; */
|
|
0x02, /* u8 if_bNumEndpoints; */
|
|
0x07, /* u8 if_bInterfaceClass; */
|
|
0x01, /* u8 if_bInterfaceSubClass; */
|
|
0x02, /* u8 if_bInterfaceProtocol; */
|
|
0x00, /* u8 if_iInterface; string desc. */
|
|
|
|
/* first endpoint */
|
|
0x07, /* u8 ep_bLength; */
|
|
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
|
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
|
0x02, /* u8 ep_bmAttributes; Bulk */
|
|
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
0x00, /* u8 ep_bInterval; */
|
|
|
|
/* second endpoint */
|
|
0x07, /* u8 ep_bLength; */
|
|
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
|
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
|
|
0x02, /* u8 ep_bmAttributes; Bulk */
|
|
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
0x00, /* u8 ep_bInterval; */
|
|
};
|
|
|
|
// Length word calculated at run time
|
|
static const Bit8u bx_device_id_string[] =
|
|
"\0\0" // len field is calculated at run-time
|
|
"MFG:HEWLETT-PACKARD;"
|
|
"MDL:DESKJET 920C;"
|
|
"CMD:MLC,PCL,PML;"
|
|
"CLASS:PRINTER;"
|
|
"DESCRIPTION:Hewlett-Packard DeskJet 920C;"
|
|
"SERN:CN21R1C0BPIS;"
|
|
"VSTATUS:$HBO,$NCO,ff,DN,IDLE,CUT,K0,C0,SM,NR,KP093,CP097;"
|
|
"VP:0800,FL,B0;"
|
|
"VJ: ;";
|
|
|
|
static Bit8u usb_printer_count = 0;
|
|
|
|
usb_printer_device_c::usb_printer_device_c()
|
|
{
|
|
char pname[12];
|
|
char label[32];
|
|
bx_param_string_c *fname;
|
|
|
|
d.speed = d.minspeed = d.maxspeed = USB_SPEED_FULL;
|
|
memset((void*)&s, 0, sizeof(s));
|
|
strcpy(d.devname, "USB Printer");
|
|
d.dev_descriptor = bx_printer_dev_descriptor;
|
|
d.config_descriptor = bx_printer_config_descriptor;
|
|
d.device_desc_size = sizeof(bx_printer_dev_descriptor);
|
|
d.config_desc_size = sizeof(bx_printer_config_descriptor);
|
|
d.vendor_desc = "Hewlett-Packard";
|
|
d.product_desc = "Deskjet 920C";
|
|
d.serial_num = "HU18L6P2DNBI";
|
|
s.fname[0] = 0;
|
|
s.fp = NULL;
|
|
// config options
|
|
bx_list_c *usb_rt = (bx_list_c*)SIM->get_param(BXPN_MENU_RUNTIME_USB);
|
|
sprintf(pname, "printer%u", ++usb_printer_count);
|
|
sprintf(label, "USB Printer #%u Configuration", usb_printer_count);
|
|
s.config = new bx_list_c(usb_rt, pname, label);
|
|
s.config->set_options(bx_list_c::SHOW_PARENT | bx_list_c::USE_BOX_TITLE);
|
|
s.config->set_device_param(this);
|
|
fname = new bx_param_filename_c(s.config, "file", "File", "", "", BX_PATHNAME_LEN);
|
|
fname->set_handler(printfile_handler);
|
|
if (SIM->is_wx_selected()) {
|
|
bx_list_c *usb = (bx_list_c*)SIM->get_param("ports.usb");
|
|
usb->add(s.config);
|
|
}
|
|
put("usb_printer", "USBPRN");
|
|
}
|
|
|
|
usb_printer_device_c::~usb_printer_device_c(void)
|
|
{
|
|
if (s.fp != NULL) {
|
|
fclose(s.fp);
|
|
}
|
|
if (SIM->is_wx_selected()) {
|
|
bx_list_c *usb = (bx_list_c*)SIM->get_param("ports.usb");
|
|
usb->remove(s.config->get_name());
|
|
}
|
|
bx_list_c *usb_rt = (bx_list_c*)SIM->get_param(BXPN_MENU_RUNTIME_USB);
|
|
usb_rt->remove(s.config->get_name());
|
|
}
|
|
|
|
bool usb_printer_device_c::set_option(const char *option)
|
|
{
|
|
if (!strncmp(option, "file:", 5)) {
|
|
strcpy(s.fname, option+5);
|
|
SIM->get_param_string("file", s.config)->set(s.fname);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool usb_printer_device_c::init()
|
|
{
|
|
if (strlen(s.fname) > 0) {
|
|
s.fp = fopen(s.fname, "w+b");
|
|
if (s.fp == NULL) {
|
|
BX_ERROR(("Could not create/open '%s'", s.fname));
|
|
} else {
|
|
sprintf(s.info_txt, "USB printer: file='%s'", s.fname);
|
|
d.connected = 1;
|
|
return 1;
|
|
}
|
|
} else {
|
|
BX_ERROR(("USB printer: missing output file"));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char* usb_printer_device_c::get_info()
|
|
{
|
|
return s.info_txt;
|
|
}
|
|
|
|
void usb_printer_device_c::register_state_specific(bx_list_c *parent)
|
|
{
|
|
bx_list_c *list = new bx_list_c(parent, "s", "USB PRINTER Device State");
|
|
BXRS_HEX_PARAM_FIELD(list, printer_status, s.printer_status);
|
|
}
|
|
|
|
void usb_printer_device_c::handle_reset()
|
|
{
|
|
BX_DEBUG(("Reset"));
|
|
}
|
|
|
|
int usb_printer_device_c::handle_control(int request, int value, int index, int length, Bit8u *data)
|
|
{
|
|
int ret = 0;
|
|
|
|
BX_DEBUG(("Printer: request: 0x%04X value: 0x%04X index: 0x%04X len: %i", request, value, index, length));
|
|
|
|
ret = handle_control_common(request, value, index, length, data);
|
|
if (ret >= 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = 0;
|
|
switch(request) {
|
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
goto fail;
|
|
break;
|
|
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
goto fail;
|
|
break;
|
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
switch(value >> 8) {
|
|
case USB_DT_STRING:
|
|
BX_ERROR(("USB Printer handle_control: unknown string descriptor 0x%02x", value & 0xff));
|
|
goto fail;
|
|
break;
|
|
default:
|
|
BX_ERROR(("USB Printer handle_control: unknown descriptor type 0x%02x", value >> 8));
|
|
goto fail;
|
|
}
|
|
break;
|
|
|
|
/* printer specific requests */
|
|
case InterfaceInClassRequest | 0x00: // 1284 get device id string
|
|
memcpy(data, bx_device_id_string, sizeof(bx_device_id_string));
|
|
ret = sizeof(bx_device_id_string);
|
|
data[0] = (Bit8u) (ret >> 8); // len word is big endian
|
|
data[1] = (Bit8u) (ret & 0xFF); //
|
|
break;
|
|
case InterfaceInClassRequest | 0x01: // Get Port Status
|
|
s.printer_status = (0<<5) | (1<<4) | (1<<3);
|
|
memcpy(data, &s.printer_status, 1);
|
|
ret = 1;
|
|
break;
|
|
case InterfaceOutClassRequest | 0x02: // soft reset
|
|
// for now, just return
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
BX_ERROR(("USB PRINTER handle_control: unknown request 0x%04x", request));
|
|
fail:
|
|
d.stall = 1;
|
|
ret = USB_RET_STALL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int usb_printer_device_c::handle_data(USBPacket *p)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch(p->pid) {
|
|
case USB_TOKEN_IN:
|
|
if (p->devep == 1) {
|
|
BX_INFO(("Printer: handle_data: IN: len = %i", p->len));
|
|
BX_INFO(("Printer: Ben: We need to find out what this is and send valid status back"));
|
|
ret = p->len;
|
|
} else {
|
|
goto fail;
|
|
}
|
|
break;
|
|
case USB_TOKEN_OUT:
|
|
if (p->devep == 2) {
|
|
BX_DEBUG(("Sent %i bytes to the 'usb printer': %s", p->len, s.fname));
|
|
usb_dump_packet(p->data, p->len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_CONTROL, false, true);
|
|
if (s.fp != NULL) {
|
|
fwrite(p->data, 1, p->len, s.fp);
|
|
}
|
|
ret = p->len;
|
|
} else {
|
|
goto fail;
|
|
}
|
|
break;
|
|
default:
|
|
fail:
|
|
d.stall = 1;
|
|
ret = USB_RET_STALL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#undef LOG_THIS
|
|
#define LOG_THIS printer->
|
|
|
|
// USB printer runtime parameter handlers
|
|
const char *usb_printer_device_c::printfile_handler(bx_param_string_c *param, bool set,
|
|
const char *oldval, const char *val,
|
|
int maxlen)
|
|
{
|
|
usb_printer_device_c *printer;
|
|
|
|
if (set) {
|
|
if (strlen(val) < 1) {
|
|
val = "none";
|
|
}
|
|
printer = (usb_printer_device_c*) param->get_parent()->get_device_param();
|
|
if (printer != NULL) {
|
|
if (printer->s.fp != NULL) {
|
|
fclose(printer->s.fp);
|
|
}
|
|
if (strcmp(val, "none")) {
|
|
printer->s.fp = fopen(val, "w+b");
|
|
if (printer->s.fp != NULL) {
|
|
strcpy(printer->s.fname, val);
|
|
sprintf(printer->s.info_txt, "USB printer: file='%s'", printer->s.fname);
|
|
} else {
|
|
BX_ERROR(("Could not create/open '%s'", val));
|
|
printer->s.fname[0] = 0;
|
|
}
|
|
} else {
|
|
printer->s.fname[0] = 0;
|
|
}
|
|
} else {
|
|
BX_PANIC(("printfile_handler: printer not found"));
|
|
}
|
|
}
|
|
return val;
|
|
}
|
|
#endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
|