blob: d263c55023d39677c325b45569fa853b33db2a62 [file] [log] [blame] [edit]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "usb_printer.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <optional>
#include <utility>
#include <base/check.h>
#include <base/check_op.h>
#include <base/logging.h>
#include <base/notreached.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "ipp_util.h"
#include "server.h"
#include "usbip_constants.h"
#include "value_util.h"
#include "load_config.h"
namespace {
// Bitmask constants used for extracting individual values out of
// UsbControlRequest which is packed inside of a uint64_t.
const uint64_t REQUEST_TYPE_MASK{0xFFULL << 56};
const uint64_t REQUEST_MASK{0xFFULL << 48};
const uint64_t VALUE_0_MASK{0xFFULL << 40};
const uint64_t VALUE_1_MASK{0xFFULL << 32};
const uint64_t INDEX_0_MASK{0xFFUL << 24};
const uint64_t INDEX_1_MASK{0xFFUL << 16};
const uint64_t LENGTH_MASK{0xFFFFUL};
// Returns the numeric value of the "direction" bit within |bmRequestType|.
int GetControlDirection(uint8_t bmRequestType) {
// Data transfer direction is bit 7.
return (bmRequestType >> 7) & 0x01;
}
// Returns the numeric value of the "type" stored within the |bmRequestType|
// bitmap.
int GetControlType(uint8_t bmRequestType) {
// The "type" of the request is stored within bits 5 and 6 of |bmRequestType|.
// So we shift these bits down to the least significant bits and perform a
// bitwise AND operation in order to clear any other bits and return strictly
// the number value of the type bits.
return (bmRequestType >> 5) & 3;
}
// Returns the numeric value of the "recipient" stored within |bmRequestType|.
int GetControlRecipient(uint8_t bmRequestType) {
// The "recipient" of the request is stored in bits 0..4, so we just need to
// mask it off.
return bmRequestType & 0x1f;
}
// Bit 7 indicates the data transfer direction:
// 0 = Host to Device
// 1 = Device to Host
std::string RequestDirectionString(uint8_t bmRequestType) {
return GetControlDirection(bmRequestType) ? "To host" : "To device";
}
std::string RequestTypeString(uint8_t bmRequestType) {
switch (GetControlType(bmRequestType)) {
case STANDARD_TYPE:
return "Standard";
case CLASS_TYPE:
return "Class";
case VENDOR_TYPE:
return "Vendor";
case RESERVED_TYPE:
return "Reserved";
default:
NOTREACHED();
}
return "Invalid";
}
std::string RequestRecipientString(uint8_t bmRequestType) {
switch (GetControlRecipient(bmRequestType)) {
case RECIPIENT_DEVICE:
return "Device";
case RECIPIENT_INTERFACE:
return "Interface";
case RECIPIENT_ENDPOINT:
return "Endpoint";
case RECIPIENT_OTHER:
return "Other";
default:
return "Reserved";
}
}
std::string StandardDeviceRequestString(uint8_t bRequest) {
switch (bRequest) {
case GET_STATUS:
return "GET_STATUS";
case CLEAR_FEATURE:
return "CLEAR_FEATURE";
case SET_FEATURE:
return "SET_FEATURE";
case SET_ADDRESS:
return "SET_ADDRESS";
case GET_DESCRIPTOR:
return "GET_DESCRIPTOR";
case SET_DESCRIPTOR:
return "SET_DESCRIPTOR";
case GET_CONFIGURATION:
return "GET_CONFIGURATION";
case SET_CONFIGURATION:
return "SET_CONFIGURATION";
case GET_INTERFACE:
return "GET_INTERFACE";
case SET_INTERFACE:
return "SET_INTERFACE";
case SET_FRAME:
return "SET_FRAME";
default:
LOG(ERROR) << "Unknown standard device bRequest value "
<< base::StringPrintf("0x%02X", bRequest);
return base::StringPrintf("BREQUEST_RAW_%02X", bRequest);
}
}
std::string RequestNameString(uint8_t bmRequestType, uint8_t bRequest) {
if (GetControlType(bmRequestType) == STANDARD_TYPE) {
switch (GetControlRecipient(bmRequestType)) {
case RECIPIENT_DEVICE:
return StandardDeviceRequestString(bRequest);
// TODO(b/238353195): Add mappings for known device class requests.
}
}
LOG(ERROR) << "No mapping defined for bmRequestType "
<< base::StringPrintf("0x%02X", bmRequestType);
return base::StringPrintf("%02X", bRequest);
}
std::string DescriptorTypeString(uint8_t wValue) {
switch (wValue) {
case USB_DESCRIPTOR_DEVICE:
return "USB_DESCRIPTOR_DEVICE";
case USB_DESCRIPTOR_CONFIGURATION:
return "USB_DESCRIPTOR_CONFIGURATION";
case USB_DESCRIPTOR_STRING:
return "USB_DESCRIPTOR_STRING";
case USB_DESCRIPTOR_INTERFACE:
return "USB_DESCRIPTOR_INTERFACE";
case USB_DESCRIPTOR_ENDPOINT:
return "USB_DESCRIPTOR_ENDPOINT";
case USB_DESCRIPTOR_DEVICE_QUALIFIER:
return "USB_DESCRIPTOR_DEVICE_QUALIFIER";
case USB_DESCRIPTOR_OTHER_SPEED:
return "USB_DESCRIPTOR_OTHER_SPEED";
case USB_DESCRIPTOR_INTERFACE_POWER:
return "USB_DESCRIPTOR_INTERFACE_POWER";
case USB_DESCRIPTOR_OTG:
return "USB_DESCRIPTOR_OTG";
case USB_DESCRIPTOR_DEBUG:
return "USB_DESCRIPTOR_DEBUG";
default:
LOG(ERROR) << "Unknown descriptor type request: "
<< base::StringPrintf("0x%02X", wValue);
return base::StringPrintf("USB_DESCRIPTOR_RAW_%02X", wValue);
}
}
// Unpacks the standard USB SETUP packet contained within |setup| into a
// UsbControlRequest struct and returns the result.
UsbControlRequest CreateUsbControlRequest(int64_t setup) {
UsbControlRequest request;
request.bmRequestType = (setup & REQUEST_TYPE_MASK) >> 56;
request.bRequest = (setup & REQUEST_MASK) >> 48;
request.wValue0 = (setup & VALUE_0_MASK) >> 40;
request.wValue1 = (setup & VALUE_1_MASK) >> 32;
request.wIndex0 = (setup & INDEX_0_MASK) >> 24;
request.wIndex1 = (setup & INDEX_1_MASK) >> 16;
request.wLength = ntohs(setup & LENGTH_MASK);
VLOG(2) << "bmRequestType "
<< base::StringPrintf("0x%02X", request.bmRequestType);
VLOG(2) << " direction " << GetControlDirection(request.bmRequestType)
<< " " << RequestDirectionString(request.bmRequestType);
VLOG(2) << " type " << GetControlType(request.bmRequestType) << " "
<< RequestTypeString(request.bmRequestType);
VLOG(2) << " recipient " << GetControlRecipient(request.bmRequestType)
<< " " << RequestRecipientString(request.bmRequestType);
VLOG(2) << "bRequest " << base::StringPrintf("0x%02X", request.bRequest)
<< " " << RequestNameString(request.bmRequestType, request.bRequest);
VLOG(2) << "wValue0 " << base::StringPrintf("0x%02X", request.wValue0);
VLOG(2) << "wValue1 " << base::StringPrintf("0x%02X", request.wValue1);
VLOG(2) << "wIndex0 " << base::StringPrintf("0x%02X", request.wIndex0);
VLOG(2) << "wIndex1 " << base::StringPrintf("0x%02X", request.wIndex1);
VLOG(2) << "wLength " << unsigned{request.wLength};
return request;
}
// Appends |buf| to the file at |path|. If no file exists at |path|, it is
// created.
void AppendToFile(base::FilePath path, SmartBuffer buf) {
base::File file(path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_APPEND);
if (!file.IsValid()) {
LOG(ERROR) << "Failed to open/create file at " << path;
return;
}
int written = file.WriteAtCurrentPos(
reinterpret_cast<const char*>(buf.data()), buf.size());
if (written != buf.size()) {
PLOG(ERROR) << "Failed to write buf to file at " << path;
}
}
} // namespace
void InterfaceManager::QueueMessage(const SmartBuffer& message) {
queue_.push(message);
}
bool InterfaceManager::QueueEmpty() const {
return queue_.empty();
}
std::optional<SmartBuffer> InterfaceManager::AddMessageAndReturnIfComplete(
const SmartBuffer& message) {
message_.Add(message);
const bool complete =
receiving_chunked_ ? ContainsFinalChunk(message)
: message_.size() == request_header_.ContentLength();
if (!complete) {
return std::nullopt;
}
SmartBuffer payload;
if (receiving_chunked_) {
// Assemble the chunks into the HTTP response body.
payload = MergeDocument(&message_);
} else {
payload = message_;
message_.Erase(0, message_.size());
}
receiving_message_ = false;
return payload;
}
SmartBuffer InterfaceManager::PopMessage() {
CHECK(!QueueEmpty()) << "Can't pop message from empty queue.";
auto message = queue_.front();
queue_.pop();
return message;
}
// explicit
UsbDescriptors::UsbDescriptors(
const UsbDeviceDescriptor& device_descriptor,
const UsbConfigurationDescriptor& configuration_descriptor,
const UsbDeviceQualifierDescriptor& qualifier_descriptor,
const std::vector<std::vector<char>>& string_descriptors,
const std::vector<char>& ieee_device_id,
const std::vector<UsbInterfaceDescriptor>& interface_descriptors,
const std::map<uint8_t, std::vector<UsbEndpointDescriptor>>&
endpoint_descriptors)
: device_descriptor_(device_descriptor),
configuration_descriptor_(configuration_descriptor),
qualifier_descriptor_(qualifier_descriptor),
string_descriptors_(string_descriptors),
ieee_device_id_(ieee_device_id),
interface_descriptors_(interface_descriptors),
endpoint_descriptors_(endpoint_descriptors) {}
std::optional<UsbDescriptors> UsbDescriptors::Create(
const std::string& descriptor_file) {
std::optional<std::string> descriptors_contents =
GetJSONContents(descriptor_file);
if (!descriptors_contents.has_value()) {
LOG(ERROR) << "Failed to load file contents for " << descriptor_file;
return std::nullopt;
}
std::optional<base::Value> descriptors =
base::JSONReader::Read(*descriptors_contents);
if (!descriptors) {
LOG(ERROR) << "Failed to parse " << descriptor_file;
return std::nullopt;
}
if (!descriptors->is_dict()) {
LOG(ERROR) << "Failed to extract printer configuration as dictionary";
return std::nullopt;
}
UsbDeviceDescriptor device = GetDeviceDescriptor(descriptors->GetDict());
UsbConfigurationDescriptor configuration =
GetConfigurationDescriptor(descriptors->GetDict());
UsbDeviceQualifierDescriptor qualifier =
GetDeviceQualifierDescriptor(descriptors->GetDict());
std::vector<UsbInterfaceDescriptor> interfaces =
GetInterfaceDescriptors(descriptors->GetDict());
std::map<uint8_t, std::vector<UsbEndpointDescriptor>> endpoint_map =
GetEndpointDescriptorMap(descriptors->GetDict());
std::vector<std::vector<char>> strings =
GetStringDescriptors(descriptors->GetDict());
std::vector<char> ieee_device_id = GetIEEEDeviceId(descriptors->GetDict());
return UsbDescriptors(device, configuration, qualifier, strings,
ieee_device_id, interfaces, endpoint_map);
}
UsbPrinter::UsbPrinter(const UsbDescriptors& usb_descriptors,
const base::FilePath& document_output_path,
const base::FilePath& http_output_dir,
IppManager ipp_manager,
EsclManager escl_manager,
std::unique_ptr<mock_printer::MockPrinter> mock_printer)
: usb_descriptors_(usb_descriptors),
document_output_path_(document_output_path),
http_output_dir_(http_output_dir),
ipp_manager_(std::move(ipp_manager)),
escl_manager_(std::move(escl_manager)),
mock_printer_(std::move(mock_printer)),
interface_managers_(usb_descriptors.interface_descriptors().size()) {}
bool UsbPrinter::IsIppUsb() const {
int count = 0;
const std::vector<UsbInterfaceDescriptor> interfaces =
interface_descriptors();
for (const auto& interface : interfaces) {
if (interface.bInterfaceClass == 7 && interface.bInterfaceSubClass == 1 &&
interface.bInterfaceProtocol == 4) {
++count;
}
}
// An ipp-over-usb printer must have at least 2 ipp-over-usb interfaces.
return count >= 2;
}
void UsbPrinter::HandleUsbRequest(int sockfd,
const UsbipCmdSubmit& usb_request) {
// Endpoint 0 is used for USB control requests.
if (usb_request.header.ep == 0) {
HandleUsbControl(sockfd, usb_request);
} else {
if (usb_request.header.direction == 1) {
HandleBulkInRequest(sockfd, usb_request);
} else {
if (IsIppUsb()) {
HandleIppUsbData(sockfd, usb_request);
} else {
HandleUsbData(sockfd, usb_request);
}
}
}
}
void UsbPrinter::HandleUsbControl(int sockfd,
const UsbipCmdSubmit& usb_request) const {
UsbControlRequest control_request =
CreateUsbControlRequest(usb_request.setup);
int request_type = GetControlType(control_request.bmRequestType);
switch (request_type) {
case STANDARD_TYPE:
HandleStandardControl(sockfd, usb_request, control_request);
break;
case CLASS_TYPE:
HandlePrinterControl(sockfd, usb_request, control_request);
break;
case VENDOR_TYPE:
case RESERVED_TYPE:
default:
LOG(ERROR) << "Unable to handle request of type: " << request_type;
break;
}
}
void UsbPrinter::HandleUsbData(int sockfd,
const UsbipCmdSubmit& usb_request) const {
SmartBuffer data = ReceiveBuffer(sockfd, usb_request.transfer_buffer_length);
size_t received = data.size();
LOG(INFO) << "Received " << received << " bytes";
// Acknowledge receipt of BULK transfer.
SendUsbDataResponse(sockfd, usb_request, received);
if (!document_output_path_.empty()) {
LOG(INFO) << "Recording document...";
AppendToFile(document_output_path_, data);
}
}
void UsbPrinter::HandleIppUsbData(int sockfd,
const UsbipCmdSubmit& usb_request) {
SmartBuffer message =
ReceiveBuffer(sockfd, usb_request.transfer_buffer_length);
size_t received = message.size();
LOG(INFO) << "Received " << received << " bytes";
// Acknowledge receipt of BULK transfer.
SendUsbDataResponse(sockfd, usb_request, received);
HandleHttpData(usb_request, &message);
}
void UsbPrinter::HandleHttpData(const UsbipCmdSubmit& usb_request,
SmartBuffer* message) {
InterfaceManager* im = GetInterfaceManager(usb_request.header.ep);
if (!im->receiving_message()) {
// If we're not currently receiving, |message| must be the start of a new
// HTTP message. Parse the header and setup some fields to track state.
std::optional<HttpRequest> opt_request = HttpRequest::Deserialize(message);
if (!opt_request.has_value()) {
LOG(ERROR) << "Incoming message is not valid HTTP; ignoring";
return;
}
HttpRequest request = opt_request.value();
im->set_receiving_message(true);
im->set_request_header(request);
im->set_receiving_chunked(request.IsChunkedMessage());
}
std::optional<SmartBuffer> payload =
im->AddMessageAndReturnIfComplete(*message);
if (!payload.has_value()) {
return;
}
LogHttpHeaders(im->request_header());
HttpResponse response =
GenerateHttpResponse(im->request_header(), &payload.value());
QueueHttpResponse(usb_request, response);
}
void UsbPrinter::HandleStandardControl(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
switch (control_request.bRequest) {
case GET_STATUS:
HandleGetStatus(sockfd, usb_request, control_request);
break;
case GET_DESCRIPTOR:
HandleGetDescriptor(sockfd, usb_request, control_request);
break;
case GET_CONFIGURATION:
HandleGetConfiguration(sockfd, usb_request, control_request);
break;
case CLEAR_FEATURE:
case SET_FEATURE:
case SET_ADDRESS:
case SET_DESCRIPTOR:
case SET_CONFIGURATION:
case GET_INTERFACE:
case SET_INTERFACE:
case SET_FRAME:
HandleUnsupportedRequest(sockfd, usb_request, control_request);
break;
default:
LOG(ERROR) << "Received unknown control request "
<< unsigned{control_request.bRequest};
break;
}
}
void UsbPrinter::HandlePrinterControl(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
switch (control_request.bRequest) {
case GET_DEVICE_ID:
HandleGetDeviceId(sockfd, usb_request, control_request);
break;
case GET_PORT_STATUS:
break;
case SOFT_RESET:
break;
default:
LOG(ERROR) << "Unknown printer class request "
<< unsigned{control_request.bRequest};
}
}
InterfaceManager* UsbPrinter::GetInterfaceManager(int endpoint) {
CHECK_GT(endpoint, 0) << "Received request on an invalid endpoint";
// Since each interface contains a pair of in/out endpoints, we perform this
// conversion in order to retrieve the corresponding interface number.
// Examples:
// endpoints 1 and 2 both map to interface 0.
// endpoints 3 and 4 both map to interface 1.
int index = (endpoint - 1) / 2;
CHECK_LT(index, interface_managers_.size())
<< "Received request on an invalid endpoint";
return &interface_managers_[index];
}
void UsbPrinter::LogHttpHeaders(const HttpRequest& request) {
if (http_output_dir_.empty()) {
return;
}
static int counter = 0;
const std::string filename =
base::StringPrintf("http-header-%04d.txt", ++counter);
const base::FilePath filePath = http_output_dir_.Append(filename);
base::File file(filePath,
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
LOG(ERROR) << "Failed to open/create file at " << filePath;
return;
}
const std::string msg =
base::StringPrintf("%s\n", request.request_line.c_str());
const int written = file.WriteAtCurrentPos(msg.c_str(), msg.length());
if (written != msg.length()) {
PLOG(ERROR) << "Failed to write HTTP request line to file at " << filePath;
return;
}
const HttpHeaders& headers = request.headers;
for (auto it = headers.begin(); it != headers.end(); ++it) {
const std::string msg =
base::StringPrintf("%s: %s\n", it->first.c_str(), it->second.c_str());
const int written = file.WriteAtCurrentPos(msg.c_str(), msg.length());
if (written != msg.length()) {
PLOG(ERROR) << "Failed to write http headers to file at " << filePath;
return;
}
}
}
HttpResponse UsbPrinter::GenerateHttpResponse(const HttpRequest& request,
SmartBuffer* body) {
LOG(INFO) << request.request_line;
if (request.method == "POST" && request.uri == "/ipp/print") {
if (!mock_printer_) {
return GenerateHttpResponseFromIppManager(body);
}
HttpResponse retval = mock_printer_->GenerateHttpResponse(body);
if (retval.default_response) {
return GenerateHttpResponseFromIppManager(body);
}
return retval;
} else if (base::StartsWith(request.uri, "/eSCL",
base::CompareCase::SENSITIVE)) {
return escl_manager_.HandleEsclRequest(request, *body);
}
HttpResponse response;
LOG(ERROR) << "Invalid method '" << request.method << "' and/or endpoint '"
<< request.uri << "'";
response.status = "404 Not Found";
return response;
}
HttpResponse UsbPrinter::GenerateHttpResponseFromIppManager(SmartBuffer* body) {
HttpResponse response;
std::optional<IppHeader> ipp_header = IppHeader::Deserialize(body);
if (!ipp_header) {
LOG(ERROR) << "Request does not contain a valid IPP header.";
response.status = "415 Unsupported Media Type";
return response;
}
if (!RemoveIppAttributes(body)) {
LOG(ERROR) << "IPP request has malformed attributes section.";
response.status = "415 Unsupported Media Type";
return response;
}
response.status = "200 OK";
response.headers["Content-Type"] = "application/ipp";
response.body = ipp_manager_.HandleIppRequest(ipp_header.value(), *body);
return response;
}
void UsbPrinter::HandleGetStatus(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetStatus " << unsigned{control_request.wValue1} << "["
<< unsigned{control_request.wValue0} << "]";
uint16_t status = 0x1; // Self-powered.
SmartBuffer response(sizeof(status));
response.Add(&status, sizeof(status));
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetDescriptor(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetDescriptor "
<< DescriptorTypeString(control_request.wValue1) << "["
<< unsigned{control_request.wValue0} << "]";
switch (control_request.wValue1) {
case USB_DESCRIPTOR_DEVICE:
HandleGetDeviceDescriptor(sockfd, usb_request, control_request);
break;
case USB_DESCRIPTOR_CONFIGURATION:
HandleGetConfigurationDescriptor(sockfd, usb_request, control_request);
break;
case USB_DESCRIPTOR_STRING:
HandleGetStringDescriptor(sockfd, usb_request, control_request);
break;
case USB_DESCRIPTOR_INTERFACE:
case USB_DESCRIPTOR_ENDPOINT:
case USB_DESCRIPTOR_DEBUG:
// Types that are known and not handled.
HandleUnsupportedRequest(sockfd, usb_request, control_request);
break;
case USB_DESCRIPTOR_DEVICE_QUALIFIER:
HandleGetDeviceQualifierDescriptor(sockfd, usb_request, control_request);
break;
default:
LOG(ERROR) << "Unknown descriptor type request: "
<< unsigned{control_request.wValue1};
HandleUnsupportedRequest(sockfd, usb_request, control_request);
}
}
void UsbPrinter::HandleGetDeviceDescriptor(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetDeviceDescriptor "
<< DescriptorTypeString(control_request.wValue1) << "["
<< unsigned{control_request.wValue0} << "]";
SmartBuffer response(sizeof(device_descriptor()));
const UsbDeviceDescriptor& dev = device_descriptor();
// If the requested number of bytes is smaller than the size of the device
// descriptor then only send a portion of the descriptor.
if (control_request.wLength < sizeof(dev)) {
response.Add(&dev, control_request.wLength);
} else {
response.Add(dev);
}
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetConfigurationDescriptor(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetConfigurationDescriptor "
<< DescriptorTypeString(control_request.wValue1) << "["
<< unsigned{control_request.wValue0} << "]";
SmartBuffer response(control_request.wLength);
response.Add(configuration_descriptor());
if (control_request.wLength == sizeof(configuration_descriptor())) {
// Only the configuration descriptor itself has been requested.
VLOG(1) << "Only configuration descriptor requested";
SendUsbControlResponse(sockfd, usb_request, response.data(),
response.size());
return;
}
const auto& interfaces = interface_descriptors();
const auto& endpoints = endpoint_descriptors();
// Place each interface and their corresponding endnpoint descriptors into the
// response buffer.
for (int i = 0; i < configuration_descriptor().bNumInterfaces; ++i) {
const auto& interface = interfaces[i];
response.Add(&interface, sizeof(interface));
auto iter = endpoints.find(interface.bInterfaceNumber);
if (iter == endpoints.end()) {
LOG(ERROR) << "Unable to find endpoints for interface "
<< unsigned{interface.bInterfaceNumber};
exit(1);
}
for (const auto& endpoint : iter->second) {
response.Add(&endpoint, sizeof(endpoint));
}
}
CHECK_EQ(control_request.wLength, response.size())
<< "Response length does not match requested number of bytes";
// After filling the buffer with all of the necessary descriptors we can send
// a response.
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetDeviceQualifierDescriptor(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetDeviceQualifierDescriptor "
<< DescriptorTypeString(control_request.wValue1) << "["
<< unsigned{control_request.wValue0} << "]";
SmartBuffer response(sizeof(qualifier_descriptor()));
response.Add(qualifier_descriptor());
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetStringDescriptor(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetStringDescriptor "
<< DescriptorTypeString(control_request.wValue1) << "["
<< unsigned{control_request.wValue0} << "]";
int index = control_request.wValue0;
const auto& strings = string_descriptors();
CHECK(index < strings.size());
SmartBuffer response(strings[index][0]);
response.Add(strings[index].data(), strings[index][0]);
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetConfiguration(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetConfiguration " << unsigned{control_request.wValue1}
<< "[" << unsigned{control_request.wValue0} << "]";
// Note: For now we only have one configuration set, so we just respond with
// with |configuration_descriptor_.bConfigurationValue|.
const auto& configuration = configuration_descriptor();
SmartBuffer response(sizeof(configuration.bConfigurationValue));
response.Add(&configuration.bConfigurationValue,
sizeof(configuration.bConfigurationValue));
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleUnsupportedRequest(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleUnsupportedRequest "
<< StandardDeviceRequestString(control_request.bRequest) << ": "
<< unsigned{control_request.wValue1} << "["
<< unsigned{control_request.wValue0} << "]";
SendUsbControlResponse(sockfd, usb_request, 0, 0);
}
void UsbPrinter::HandleGetDeviceId(
int sockfd,
const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
VLOG(1) << "HandleGetDeviceId " << unsigned{control_request.wValue1} << "["
<< unsigned{control_request.wValue0} << "]";
SmartBuffer response(ieee_device_id().size());
response.Add(ieee_device_id());
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::QueueHttpResponse(const UsbipCmdSubmit& usb_request,
const HttpResponse& response) {
SmartBuffer http_message;
response.Serialize(&http_message);
LOG(INFO) << "Queueing HTTP response...";
InterfaceManager* im = GetInterfaceManager(usb_request.header.ep);
im->QueueMessage(http_message);
}
void UsbPrinter::HandleBulkInRequest(int sockfd,
const UsbipCmdSubmit& usb_request) {
InterfaceManager* im = GetInterfaceManager(usb_request.header.ep);
if (im->QueueEmpty()) {
LOG(ERROR) << "No queued messages, sending empty response.";
SendUsbControlResponse(sockfd, usb_request, 0, 0);
return;
}
SmartBuffer http_message = im->PopMessage();
size_t max_size = usb_request.transfer_buffer_length;
UsbipRetSubmit response = CreateUsbipRetSubmit(usb_request);
response.header.direction = 1;
response.actual_length = std::min(max_size, http_message.size());
LOG(INFO) << "Sending " << response.actual_length << " byte response.";
SmartBuffer response_buffer = PackUsbipRetSubmit(response);
if (http_message.size() > max_size) {
size_t leftover_size = http_message.size() - max_size;
SmartBuffer leftover(leftover_size);
leftover.Add(http_message, max_size);
http_message.Shrink(max_size);
im->QueueMessage(leftover);
}
response_buffer.Add(http_message);
SendBuffer(sockfd, response_buffer);
}