529 lines
16 KiB
C
529 lines
16 KiB
C
/*
|
|
* Copyright 2012 Jared Boone
|
|
* Copyright 2015 Benjamin Vernoux <bvernoux@airspy.com>
|
|
*
|
|
* This file is part of AirSpy (based on HackRF project).
|
|
*
|
|
* 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"
|
|
|
|
/* Shall be defined in user file usb_descriptor.c/.h */
|
|
extern uint8_t usb_descriptor_MSDescriptor[];
|
|
extern uint8_t usb_descriptor_ExtProps[];
|
|
|
|
uint16_t USB_InterfaceStatus = 0; //Always 0x0000 value reseverd for future use
|
|
uint16_t USB_DeviceStatus = 0; //Bit 0 = SelfPowerred, Bit=1 Remote Wakeup
|
|
uint8_t USB_CurrentInterface = 0; //Bit 0 = SelfPowerred, Bit=1 Remote Wakeup
|
|
uint16_t USB_EpStatus;
|
|
|
|
static uint_fast8_t usb_endpoint_number(const uint_fast8_t endpoint_address)
|
|
{
|
|
return (endpoint_address & 0xF);
|
|
}
|
|
|
|
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_data[1] == USB_DESCRIPTOR_TYPE_OTHER_SPEED_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);
|
|
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)
|
|
{
|
|
return usb_send_descriptor(endpoint, usb_descriptor_MSDescriptor);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************/
|
|
|
|
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) )
|
|
{
|
|
if( usb_configuration == 0 )
|
|
{
|
|
// TODO: Should this be done immediately?
|
|
usb_set_address_immediate(endpoint->device, 0);
|
|
}
|
|
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);
|
|
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_endpoint(usb_endpoint_t* const endpoint)
|
|
{
|
|
const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->setup.index);
|
|
USB_EpStatus = (USB0_ENDPTCTRL(endpoint_number) & (USB0_ENDPTCTRL_RXS | USB0_ENDPTCTRL_TXS)) ? 1 : 0;
|
|
usb_transfer_schedule_block(endpoint->in, &USB_EpStatus, 2);
|
|
usb_transfer_schedule_ack(endpoint->out);
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
static usb_request_status_t usb_standard_request_get_status(
|
|
usb_endpoint_t* const endpoint,
|
|
const usb_transfer_stage_t stage)
|
|
{
|
|
if (stage == USB_TRANSFER_STAGE_SETUP)
|
|
{
|
|
switch (endpoint->setup.request_type & USB_SETUP_REQUEST_TYPE_RECIPIENT_mask)
|
|
{
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_DEVICE:
|
|
usb_transfer_schedule_block(endpoint->in, &USB_DeviceStatus, 2);
|
|
usb_transfer_schedule_ack(endpoint->out);
|
|
return USB_REQUEST_STATUS_OK;
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_INTERFACE:
|
|
usb_transfer_schedule_block(endpoint->in, &USB_InterfaceStatus, 2);
|
|
usb_transfer_schedule_ack(endpoint->out);
|
|
return USB_REQUEST_STATUS_OK;
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_ENDPOINT:
|
|
return usb_standard_request_get_status_endpoint(endpoint);
|
|
break;
|
|
}
|
|
}
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
static usb_request_status_t usb_standard_request_get_interface(
|
|
usb_endpoint_t* const endpoint,
|
|
const usb_transfer_stage_t stage)
|
|
{
|
|
if (stage == USB_TRANSFER_STAGE_SETUP)
|
|
{
|
|
switch (endpoint->setup.request_type & USB_SETUP_REQUEST_TYPE_RECIPIENT_mask)
|
|
{
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_DEVICE:
|
|
break;
|
|
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_INTERFACE:
|
|
usb_transfer_schedule_block(endpoint->in, &USB_CurrentInterface, 1);
|
|
usb_transfer_schedule_ack(endpoint->out);
|
|
return USB_REQUEST_STATUS_OK;
|
|
break;
|
|
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_ENDPOINT:
|
|
break;
|
|
}
|
|
}
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
|
|
static usb_request_status_t usb_standard_request_set_interface(
|
|
usb_endpoint_t* const endpoint,
|
|
const usb_transfer_stage_t stage)
|
|
{
|
|
if (stage == USB_TRANSFER_STAGE_SETUP)
|
|
{
|
|
switch (endpoint->setup.request_type & USB_SETUP_REQUEST_TYPE_RECIPIENT_mask)
|
|
{
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_DEVICE:
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_INTERFACE:
|
|
usb_transfer_schedule_ack(endpoint->in);
|
|
return USB_REQUEST_STATUS_OK;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_ENDPOINT:
|
|
break;
|
|
}
|
|
}
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
static usb_request_status_t usb_standard_request_clear_feature_endpoint(
|
|
usb_endpoint_t* const endpoint)
|
|
{
|
|
const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->setup.index);
|
|
USB0_ENDPTCTRL(endpoint_number) &= ~(USB0_ENDPTCTRL_RXS | USB0_ENDPTCTRL_TXS);
|
|
usb_transfer_schedule_ack(endpoint->in);
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
static usb_request_status_t usb_standard_request_clear_feature(
|
|
usb_endpoint_t* const endpoint,
|
|
const usb_transfer_stage_t stage)
|
|
{
|
|
if (stage == USB_TRANSFER_STAGE_SETUP)
|
|
{
|
|
switch (endpoint->setup.request_type & USB_SETUP_REQUEST_TYPE_RECIPIENT_mask)
|
|
{
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_DEVICE:
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_INTERFACE:
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_ENDPOINT:
|
|
return usb_standard_request_clear_feature_endpoint(endpoint);
|
|
break;
|
|
}
|
|
}
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
static usb_request_status_t usb_standard_request_set_feature_endpoint(
|
|
usb_endpoint_t* const endpoint)
|
|
{
|
|
const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->setup.index);
|
|
USB0_ENDPTCTRL(endpoint_number) |= (USB0_ENDPTCTRL_RXS | USB0_ENDPTCTRL_TXS);
|
|
usb_transfer_schedule_ack(endpoint->in);
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
static usb_request_status_t usb_standard_request_set_feature(
|
|
usb_endpoint_t* const endpoint,
|
|
const usb_transfer_stage_t stage)
|
|
{
|
|
if (stage == USB_TRANSFER_STAGE_SETUP)
|
|
{
|
|
switch (endpoint->setup.request_type & USB_SETUP_REQUEST_TYPE_RECIPIENT_mask)
|
|
{
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_DEVICE:
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_INTERFACE:
|
|
break;
|
|
case USB_SETUP_REQUEST_TYPE_RECIPIENT_ENDPOINT:
|
|
return usb_standard_request_set_feature_endpoint(endpoint);
|
|
break;
|
|
}
|
|
}
|
|
return USB_REQUEST_STATUS_OK;
|
|
}
|
|
|
|
/*********************************************************************/
|
|
|
|
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_CLEAR_FEATURE:
|
|
return usb_standard_request_clear_feature(endpoint, stage);
|
|
|
|
case USB_STANDARD_REQUEST_SET_FEATURE:
|
|
return usb_standard_request_set_feature(endpoint, stage);
|
|
|
|
//USB_STANDARD_REQUEST_SET_DESCRIPTOR = 7,*/
|
|
|
|
case USB_STANDARD_REQUEST_GET_INTERFACE:
|
|
return usb_standard_request_get_interface(endpoint, stage);
|
|
|
|
case USB_STANDARD_REQUEST_SET_INTERFACE:
|
|
return usb_standard_request_set_interface(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);
|
|
|
|
default:
|
|
return USB_REQUEST_STATUS_STALL;
|
|
}
|
|
}
|