Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
3d517954cThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
*
* This file is part of HackRF.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <stdint.h>
#include <stddef.h>
#include "usb_standard_request.h"
#include "usb.h"
#include "usb_type.h"
#include "usb_queue.h"
const uint8_t* usb_endpoint_descriptor(const usb_endpoint_t* const endpoint)
{
const usb_configuration_t* const configuration = endpoint->device->configuration;
if (configuration) {
const uint8_t* descriptor = configuration->descriptor;
while (descriptor[0] != 0) {
if (descriptor[1] == USB_DESCRIPTOR_TYPE_ENDPOINT) {
if (descriptor[2] == endpoint->address) {
return descriptor;
}
}
descriptor += descriptor[0];
}
}
return 0;
}
uint_fast16_t usb_endpoint_descriptor_max_packet_size(
const uint8_t* const endpoint_descriptor)
{
return (endpoint_descriptor[5] << 8) | endpoint_descriptor[4];
}
usb_transfer_type_t usb_endpoint_descriptor_transfer_type(
const uint8_t* const endpoint_descriptor)
{
return (endpoint_descriptor[3] & 0x3);
}
void (*usb_configuration_changed_cb)(usb_device_t* const) = NULL;
void usb_set_configuration_changed_cb(void (*callback)(usb_device_t* const))
{
usb_configuration_changed_cb = callback;
}
bool usb_set_configuration(
usb_device_t* const device,
const uint_fast8_t configuration_number)
{
const usb_configuration_t* new_configuration = 0;
if (configuration_number != 0) {
// Locate requested configuration.
if (device->configurations) {
usb_configuration_t** configurations = *(device->configurations);
uint32_t i = 0;
const usb_speed_t usb_speed_current = usb_speed(device);
while (configurations[i]) {
if ((configurations[i]->speed == usb_speed_current) &&
(configurations[i]->number == configuration_number)) {
new_configuration = configurations[i];
break;
}
i++;
}
}
// Requested configuration not found: request error.
if (new_configuration == 0) {
return false;
}
}
if (new_configuration != device->configuration) {
// Configuration changed.
device->configuration = new_configuration;
}
if (usb_configuration_changed_cb) {
usb_configuration_changed_cb(device);
}
return true;
}
static usb_request_status_t usb_send_descriptor(
usb_endpoint_t* const endpoint,
const uint8_t* const descriptor_data)
{
const uint32_t setup_length = endpoint->setup.length;
uint32_t descriptor_length = descriptor_data[0];
if (descriptor_data[1] == USB_DESCRIPTOR_TYPE_CONFIGURATION) {
descriptor_length = (descriptor_data[3] << 8) | descriptor_data[2];
}
// We cast the const away but this shouldn't be a problem as this is a write transfer
usb_transfer_schedule_block(
endpoint->in,
(uint8_t* const) descriptor_data,
(setup_length > descriptor_length) ? descriptor_length : setup_length,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
return USB_REQUEST_STATUS_OK;
}
static usb_request_status_t usb_send_descriptor_string(usb_endpoint_t* const endpoint)
{
if ((endpoint->setup.value_l == 0xee) &&
(endpoint->device->wcid_string_descriptor != NULL)) { /* MS WCID string */
return usb_send_descriptor(
endpoint,
endpoint->device->wcid_string_descriptor);
} else {
uint_fast8_t index = endpoint->setup.value_l;
for (uint_fast8_t i = 0; endpoint->device->descriptor_strings[i] != 0;
i++) {
if (i == index) {
return usb_send_descriptor(
endpoint,
endpoint->device->descriptor_strings[i]);
}
}
}
return USB_REQUEST_STATUS_STALL;
}
static usb_request_status_t usb_send_descriptor_config(
usb_endpoint_t* const endpoint,
usb_speed_t speed,
const uint8_t config_num)
{
usb_configuration_t** config = *(endpoint->device->configurations);
unsigned int i = 0;
for (; *config != NULL; config++) {
if ((*config)->speed == speed) {
if (i == config_num) {
return usb_send_descriptor(
endpoint,
(*config)->descriptor);
} else {
i++;
}
}
}
return USB_REQUEST_STATUS_STALL;
}
static usb_request_status_t usb_standard_request_get_descriptor_setup(
usb_endpoint_t* const endpoint)
{
switch (endpoint->setup.value_h) {
case USB_DESCRIPTOR_TYPE_DEVICE:
return usb_send_descriptor(endpoint, endpoint->device->descriptor);
case USB_DESCRIPTOR_TYPE_CONFIGURATION:
// TODO: Duplicated code. Refactor.
if (usb_speed(endpoint->device) == USB_SPEED_HIGH) {
return usb_send_descriptor_config(
endpoint,
USB_SPEED_HIGH,
endpoint->setup.value_l);
} else {
return usb_send_descriptor_config(
endpoint,
USB_SPEED_FULL,
endpoint->setup.value_l);
}
case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
return usb_send_descriptor(
endpoint,
endpoint->device->qualifier_descriptor);
case USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION:
// TODO: Duplicated code. Refactor.
if (usb_speed(endpoint->device) == USB_SPEED_HIGH) {
return usb_send_descriptor_config(
endpoint,
USB_SPEED_FULL,
endpoint->setup.value_l);
} else {
return usb_send_descriptor_config(
endpoint,
USB_SPEED_HIGH,
endpoint->setup.value_l);
}
case USB_DESCRIPTOR_TYPE_STRING:
return usb_send_descriptor_string(endpoint);
case USB_DESCRIPTOR_TYPE_INTERFACE:
case USB_DESCRIPTOR_TYPE_ENDPOINT:
default:
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_get_descriptor(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (stage) {
case USB_TRANSFER_STAGE_SETUP:
return usb_standard_request_get_descriptor_setup(endpoint);
case USB_TRANSFER_STAGE_DATA:
case USB_TRANSFER_STAGE_STATUS:
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
usb_request_status_t usb_vendor_request_read_wcid(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
if (stage == USB_TRANSFER_STAGE_SETUP) {
if ((endpoint->setup.index == 0x04) &&
(endpoint->device->wcid_feature_descriptor != NULL)) {
usb_send_descriptor(
endpoint,
endpoint->device->wcid_feature_descriptor);
return USB_REQUEST_STATUS_OK;
}
if ((endpoint->setup.index == 0x05) &&
(endpoint->device->wcid_extended_properties_descriptor != NULL)) {
usb_send_descriptor(
endpoint,
endpoint->device->wcid_extended_properties_descriptor);
return USB_REQUEST_STATUS_OK;
}
return USB_REQUEST_STATUS_STALL;
}
return USB_REQUEST_STATUS_OK;
}
/*********************************************************************/
static usb_request_status_t usb_standard_request_set_address_setup(
usb_endpoint_t* const endpoint)
{
usb_set_address_deferred(endpoint->device, endpoint->setup.value_l);
usb_transfer_schedule_ack(endpoint->in);
return USB_REQUEST_STATUS_OK;
}
static usb_request_status_t usb_standard_request_set_address(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (stage) {
case USB_TRANSFER_STAGE_SETUP:
return usb_standard_request_set_address_setup(endpoint);
case USB_TRANSFER_STAGE_DATA:
case USB_TRANSFER_STAGE_STATUS:
/* NOTE: Not necessary to set address here, as DEVICEADR.USBADRA bit
* will cause controller to automatically perform set address
* operation on IN ACK.
*/
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
/*********************************************************************/
static usb_request_status_t usb_standard_request_set_configuration_setup(
usb_endpoint_t* const endpoint)
{
const uint8_t usb_configuration = endpoint->setup.value_l;
if (usb_set_configuration(endpoint->device, usb_configuration)) {
usb_transfer_schedule_ack(endpoint->in);
return USB_REQUEST_STATUS_OK;
} else {
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_set_configuration(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (stage) {
case USB_TRANSFER_STAGE_SETUP:
return usb_standard_request_set_configuration_setup(endpoint);
case USB_TRANSFER_STAGE_DATA:
case USB_TRANSFER_STAGE_STATUS:
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
/*********************************************************************/
static usb_request_status_t usb_standard_request_get_configuration_setup(
usb_endpoint_t* const endpoint)
{
if (endpoint->setup.length == 1) {
endpoint->buffer[0] = 0;
if (endpoint->device->configuration) {
endpoint->buffer[0] = endpoint->device->configuration->number;
}
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
1,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
return USB_REQUEST_STATUS_OK;
} else {
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_get_configuration(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (stage) {
case USB_TRANSFER_STAGE_SETUP:
return usb_standard_request_get_configuration_setup(endpoint);
case USB_TRANSFER_STAGE_DATA:
case USB_TRANSFER_STAGE_STATUS:
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_get_status_setup(
usb_endpoint_t* const endpoint)
{
if (endpoint->setup.length == 2) {
endpoint->buffer[0] = 0;
endpoint->buffer[1] = 0;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
2,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
return USB_REQUEST_STATUS_OK;
} else {
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_get_status(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (stage) {
case USB_TRANSFER_STAGE_SETUP:
return usb_standard_request_get_status_setup(endpoint);
case USB_TRANSFER_STAGE_DATA:
case USB_TRANSFER_STAGE_STATUS:
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_clear_feature_setup(
usb_endpoint_t* const endpoint)
{
switch (endpoint->setup.value) {
case USB_FEATURE_SELECTOR_ENDPOINT_HALT:
usb_endpoint_reset_data_toggle(
usb_endpoint_from_address(endpoint->setup.index));
usb_transfer_schedule_ack(endpoint->in);
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
static usb_request_status_t usb_standard_request_clear_feature(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (stage) {
case USB_TRANSFER_STAGE_SETUP:
return usb_standard_request_clear_feature_setup(endpoint);
case USB_TRANSFER_STAGE_DATA:
case USB_TRANSFER_STAGE_STATUS:
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
}
/*********************************************************************/
usb_request_status_t usb_standard_request(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
switch (endpoint->setup.request) {
case USB_STANDARD_REQUEST_GET_STATUS:
return usb_standard_request_get_status(endpoint, stage);
case USB_STANDARD_REQUEST_GET_DESCRIPTOR:
return usb_standard_request_get_descriptor(endpoint, stage);
case USB_STANDARD_REQUEST_SET_ADDRESS:
return usb_standard_request_set_address(endpoint, stage);
case USB_STANDARD_REQUEST_SET_CONFIGURATION:
return usb_standard_request_set_configuration(endpoint, stage);
case USB_STANDARD_REQUEST_GET_CONFIGURATION:
return usb_standard_request_get_configuration(endpoint, stage);
case USB_STANDARD_REQUEST_CLEAR_FEATURE:
return usb_standard_request_clear_feature(endpoint, stage);
default:
return USB_REQUEST_STATUS_STALL;
}
}