blob: aad88c212ae915871e003d4d48051525ccc59852 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// 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 "device_descriptors.h"
#include "server.h"
#include "smart_buffer.h"
#include "usbip.h"
#include "usbip_constants.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <memory>
#include <vector>
#include <arpa/inet.h>
#include <cups/cups.h>
#include <sys/socket.h>
#include <unistd.h>
#include <base/logging.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 = (0xFFUL << 56);
const uint64_t REQUEST_MASK = (0xFFUL << 48);
const uint64_t VALUE_0_MASK = (0xFFUL << 40);
const uint64_t VALUE_1_MASK = (0xFFUL << 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 "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;
}
// Unpacks the standard USB SETUP packet contained within |setup| into a
// UsbControlRequest struct and returns the result.
UsbControlRequest CreateUsbControlRequest(long long 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);
return request;
}
} // namespace
// explicit
UsbPrinter::UsbPrinter(
const UsbDeviceDescriptor& device_descriptor,
const UsbConfigurationDescriptor& configuration_descriptor,
const UsbDeviceQualifierDescriptor& qualifier_descriptor,
const std::vector<std::vector<char>>& strings,
const std::vector<char> ieee_device_id,
const std::vector<UsbInterfaceDescriptor>& interfaces,
const std::map<uint8_t, std::vector<UsbEndpointDescriptor>>& endpoints)
: device_descriptor_(device_descriptor),
configuration_descriptor_(configuration_descriptor),
qualifier_descriptor_(qualifier_descriptor),
strings_(strings),
ieee_device_id_(ieee_device_id),
interfaces_(interfaces),
endpoints_(endpoints) {}
void UsbPrinter::HandleUsbRequest(int sockfd,
const UsbipCmdSubmit& usb_request) const {
// Endpoint 0 is used for USB control requests.
if (usb_request.header.ep == 0) {
LOG(INFO) << "# Control Request";
HandleUsbControl(sockfd, usb_request);
} else {
LOG(INFO) << "# Data Request";
HandleUsbData(sockfd, usb_request);
}
}
void UsbPrinter::HandleUsbControl(int sockfd,
const UsbipCmdSubmit& usb_request) const {
UsbControlRequest control_request =
CreateUsbControlRequest(usb_request.setup);
PrintUsbControlRequest(control_request);
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 smart_buffer =
ReceiveBuffer(sockfd, usb_request.transfer_buffer_length);
size_t received = smart_buffer.size();
LOG(INFO) << "Received " << received << " bytes";
SendUsbDataResponse(sockfd, usb_request, received);
}
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 SET_DESCRIPTOR:
break;
case GET_CONFIGURATION:
HandleGetConfiguration(sockfd, usb_request, control_request);
break;
case SET_CONFIGURATION:
HandleSetConfiguration(sockfd, usb_request, control_request);
break;
case GET_INTERFACE:
// Support for this will be needed for interfaces with alt settings.
break;
case SET_INTERFACE:
HandleSetInterface(sockfd, usb_request, control_request);
break;
default:
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) << "Unkown printer class request " << control_request.bRequest;
}
}
void UsbPrinter::HandleGetStatus(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleGetStatus %u[%u]\n", control_request.wValue1,
control_request.wValue0);
uint16_t status = 0;
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 {
printf("HandleGetDescriptor %u[%u]\n", control_request.wValue1,
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:
break;
case USB_DESCRIPTOR_ENDPOINT:
break;
case USB_DESCRIPTOR_DEVICE_QUALIFIER:
HandleGetDeviceQualifierDescriptor(sockfd, usb_request, control_request);
break;
default:
LOG(ERROR) << "Unknown descriptor type request: "
<< control_request.wValue1;
}
}
void UsbPrinter::HandleGetDeviceDescriptor(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleGetDeviceDescriptor %u[%u]\n", control_request.wValue1,
control_request.wValue0);
SmartBuffer response(sizeof(device_descriptor_));
response.Add(&device_descriptor_, sizeof(device_descriptor_));
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetConfigurationDescriptor(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleGetConfigurationDescriptor %u[%u]\n", control_request.wValue1,
control_request.wValue0);
SmartBuffer response(control_request.wLength);
response.Add(&configuration_descriptor_, sizeof(configuration_descriptor_));
if (control_request.wLength == sizeof(configuration_descriptor_)) {
// Only the configuration descriptor itself has been requested.
printf("Only configuration descriptor requested\n");
SendUsbControlResponse(sockfd, usb_request, response.data(),
response.size());
return;
}
// 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 "
<< 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 {
printf("HandleGetDeviceQualifierDescriptor %u[%u]\n", control_request.wValue1,
control_request.wValue0);
SmartBuffer response(sizeof(qualifier_descriptor_));
response.Add(&qualifier_descriptor_, sizeof(qualifier_descriptor_));
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleGetStringDescriptor(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleGetStringDescriptor %u[%u]\n", control_request.wValue1,
control_request.wValue0);
int index = control_request.wValue0;
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 {
printf("HandleGetConfiguration %u[%u]\n", control_request.wValue1,
control_request.wValue0);
// Note: For now we only have one configuration set, so we just respond with
// with |configuration_descriptor_.bConfigurationValue|.
SmartBuffer response(sizeof(configuration_descriptor_.bConfigurationValue));
response.Add(&configuration_descriptor_.bConfigurationValue,
sizeof(configuration_descriptor_.bConfigurationValue));
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}
void UsbPrinter::HandleSetConfiguration(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleSetConfiguration %u[%u]\n", control_request.wValue1,
control_request.wValue0);
// NOTE: For now we have only one configuration to set, so we just respond
// with an empty message as a confirmation.
SendUsbControlResponse(sockfd, usb_request, 0, 0);
}
void UsbPrinter::HandleSetInterface(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleSetInterface %u[%u]\n", control_request.wValue1,
control_request.wValue0);
// NOTE: For now we have only one interface to set, so we just respond
// with an empty message as a confirmation.
SendUsbControlResponse(sockfd, usb_request, 0, 0);
}
void UsbPrinter::HandleGetDeviceId(
int sockfd, const UsbipCmdSubmit& usb_request,
const UsbControlRequest& control_request) const {
printf("HandleGetDeviceId %u[%u]\n", control_request.wValue1,
control_request.wValue0);
SmartBuffer response(ieee_device_id_.size());
response.Add(ieee_device_id_.data(), ieee_device_id_.size());
SendUsbControlResponse(sockfd, usb_request, response.data(), response.size());
}