/* * Copyright 2001-2010 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Originally written based on Parallel port addon by Michael Pfeiffer, * changes by Andreas Benzler, Philippe Houdoin * Rewritten to use the USBKit by Ithamar R. Adema. * (Using code from usb_printer.cpp by Michael Lotz) * * Authors: * Ithamar R. Adema, * Michael Pfeiffer, * Andreas Benzler, * Philippe Houdoin, */ #include "PrintTransportAddOn.h" #include #include #include #include #define PRINTER_INTERFACE_CLASS 0x07 #define PRINTER_INTERFACE_SUBCLASS 0x01 // printer interface types #define PIT_UNIDIRECTIONAL 0x01 #define PIT_BIDIRECTIONAL 0x02 #define PIT_1284_4_COMPATIBLE 0x03 #define PIT_VENDOR_SPECIFIC 0xff // TODO handle disconnection of printer during printing // currently the USBPrinter will be deleted and USBTransport will still // access the memory class USBPrinter { public: USBPrinter(const BString& id, const BString& name, const BUSBInterface *interface, const BUSBEndpoint *in, const BUSBEndpoint *out); ssize_t Write(const void *buf, size_t size); ssize_t Read(void *buf, size_t size); const BUSBInterface *fInterface; const BUSBEndpoint *fOut; const BUSBEndpoint *fIn; BString fName; BString fID; }; class USBPrinterRoster : public BUSBRoster { public: USBPrinterRoster(); status_t DeviceAdded(BUSBDevice *dev); void DeviceRemoved(BUSBDevice *dev); USBPrinter *Printer(const BString& key); status_t ListPrinters(BMessage *msg); private: typedef HashMap PrinterMap; PrinterMap fPrinters; }; class USBTransport : public BDataIO { public: USBTransport(BDirectory *printer, BMessage *msg); ~USBTransport(); status_t InitCheck() { return fPrinter ? B_OK : B_ERROR; }; ssize_t Read(void *buffer, size_t size); ssize_t Write(const void *buffer, size_t size); private: USBPrinter *fPrinter; USBPrinterRoster *fRoster; }; // Set transport_features so we stay loaded uint32 transport_features = B_TRANSPORT_IS_HOTPLUG; USBPrinterRoster::USBPrinterRoster() { Start(); } USBPrinter * USBPrinterRoster::Printer(const BString& key) { if (fPrinters.ContainsKey(key.String())) return fPrinters.Get(key.String()); return NULL; } status_t USBPrinterRoster::DeviceAdded(BUSBDevice *dev) { const BUSBConfiguration *config = dev->ActiveConfiguration(); const BUSBEndpoint *in = NULL, *out = NULL; const BUSBInterface *printer = NULL; // Try to find a working printer interface in this device if (config) { for (uint32 idx = 0; printer == NULL && idx < config->CountInterfaces(); idx++) { const BUSBInterface *interface = config->InterfaceAt(idx); for (uint32 alt = 0; alt < interface->CountAlternates(); alt++) { const BUSBInterface *alternate = interface->AlternateAt(alt); if (alternate->Class() == PRINTER_INTERFACE_CLASS && alternate->Subclass() == PRINTER_INTERFACE_SUBCLASS && (alternate->Protocol() == PIT_UNIDIRECTIONAL || alternate->Protocol() == PIT_BIDIRECTIONAL || alternate->Protocol() == PIT_1284_4_COMPATIBLE)) { // Found a usable Printer interface! for (uint32 endpointIdx = 0; endpointIdx < alternate->CountEndpoints(); endpointIdx++) { const BUSBEndpoint *endpoint = alternate->EndpointAt(endpointIdx); if (!endpoint->IsBulk()) continue; if (endpoint->IsInput()) in = endpoint; else if (endpoint->IsOutput()) out = endpoint; if (!in || !out) continue; printer = alternate; ((BUSBInterface*)interface)->SetAlternate(alt); break; } } } } } if (printer != NULL) { // We found a working printer interface, lets determine a unique ID // for it now, and a user identification for display in the Printers // preference. BString portId = dev->SerialNumberString(); if (!portId.Length()) { // No persistent unique ID available, use the vendor/product // ID for now. This will be unique as long as no two similar // devices are attached. portId << dev->VendorID() << "/" << dev->ProductID(); } BString portName = dev->ManufacturerString(); if (portName.Length()) portName << " "; portName << dev->ProductString(); //TODO: Do we want to use usb.ids to find proper name if strings // are not supplied by USB? fPrinters.Put(portId.String(), new USBPrinter(portId, portName, printer, in, out)); } return B_OK; } void USBPrinterRoster::DeviceRemoved(BUSBDevice *dev) { PrinterMap::Iterator iterator = fPrinters.GetIterator(); while (iterator.HasNext()) { const PrinterMap::Entry& entry = iterator.Next(); // If the device is in the list, remove it if (entry.value->fInterface->Device() == dev) { fPrinters.Remove(entry.key); delete entry.value; break; } } } status_t USBPrinterRoster::ListPrinters(BMessage *msg) { PrinterMap::Iterator iterator = fPrinters.GetIterator(); while (iterator.HasNext()) { const PrinterMap::Entry& entry = iterator.Next(); msg->AddString("port_id", entry.value->fID); msg->AddString("port_name", entry.value->fName); } return B_OK; } USBPrinter::USBPrinter(const BString& id, const BString& name, const BUSBInterface *intf, const BUSBEndpoint *in, const BUSBEndpoint *out) : fInterface(intf), fOut(out), fIn(in), fName(name), fID(id) { } //TODO: see usb_printer.cpp for error handling during read/write! ssize_t USBPrinter::Write(const void *buf, size_t size) { if (!buf || size <= 0) return B_BAD_VALUE; // NOTE: we can safely cast below as we're sending data _out_ return fOut->BulkTransfer((void*)buf, size); } ssize_t USBPrinter::Read(void *buf, size_t size) { if (!buf || size <= 0) return B_BAD_VALUE; return fIn->BulkTransfer(buf, size); } // Implementation of transport add-on interface BDataIO * instantiate_transport(BDirectory *printer, BMessage *msg) { USBTransport *transport = new(std::nothrow) USBTransport(printer, msg); if (transport != NULL && transport->InitCheck() == B_OK) return transport; delete transport; return NULL; } // List detected printers status_t list_transport_ports(BMessage *msg) { USBPrinterRoster roster; status_t status = roster.ListPrinters(msg); roster.Stop(); return status; } // Implementation of USBTransport USBTransport::USBTransport(BDirectory *printer, BMessage *msg) : fPrinter(NULL) { BString key; if (printer->ReadAttrString("transport_address", &key) != B_OK) return; fRoster = new(std::nothrow) USBPrinterRoster; if (fRoster == NULL) return; fPrinter = fRoster->Printer(key.String()); if (fPrinter == NULL) return; // If caller doesn't care... if (msg == NULL) return; // Fill up the message msg->what = 'okok'; } USBTransport::~USBTransport() { if (fRoster != NULL) { fRoster->Stop(); delete fRoster; } } ssize_t USBTransport::Read(void *buffer, size_t size) { return fPrinter ? fPrinter->Read(buffer, size) : B_ERROR; } ssize_t USBTransport::Write(const void *buffer, size_t size) { return fPrinter ? fPrinter->Write(buffer, size) : B_ERROR; }