gnuk/src/usb_prop.c
2012-01-16 12:17:45 +09:00

451 lines
11 KiB
C

/*
* usb_prop.c - glue/interface code between Gnuk and USB-FS-Device_Lib
*
* Copyright (C) 2010, 2011 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Gnuk, a GnuPG USB Token implementation.
*
* Gnuk 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 3 of the License, or
* (at your option) any later version.
*
* Gnuk 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. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* Packet size of USB Bulk transfer for full speed */
#define GNUK_MAX_PACKET_SIZE 64
#include "config.h"
#include "usb_lib.h"
#include "usb_conf.h"
#include "usb_prop.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "hw_config.h"
#ifdef ENABLE_VIRTUAL_COM_PORT
#include "usb-cdc-vport.c"
#endif
#ifdef PINPAD_DND_SUPPORT
#include "usb_msc.h"
#endif
static void
SetEPRxCount_allocated_size (uint8_t bEpNum, uint16_t wCount)
{ /* Assume wCount is even */
uint32_t *pdwReg = _pEPRxCount (bEpNum);
uint16_t value;
if (wCount <= 62)
value = (wCount & 0x3e) << 9;
else
value = 0x8000 | (((wCount >> 5) - 1) << 10);
*pdwReg = (uint32_t)value;
}
static void
gnuk_device_init (void)
{
pInformation->Current_Configuration = 0;
/* Connect the device */
PowerOn ();
/* Perform basic device initialization operations */
_SetISTR (0);
wInterrupt_Mask = IMR_MSK;
_SetCNTR (wInterrupt_Mask);
bDeviceState = UNCONNECTED;
}
static void
gnuk_device_reset (void)
{
/* Set DEVICE as not configured */
pInformation->Current_Configuration = 0;
/* Current Feature initialization */
pInformation->Current_Feature = Config_Descriptor.Descriptor[7];
/* Set DEVICE with the default Interface*/
pInformation->Current_Interface = 0;
SetBTABLE (BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType (ENDP0, EP_CONTROL);
SetEPTxStatus (ENDP0, EP_TX_STALL);
SetEPRxAddr (ENDP0, ENDP0_RXADDR);
SetEPTxAddr (ENDP0, ENDP0_TXADDR);
Clear_Status_Out (ENDP0);
SetEPRxCount_allocated_size (ENDP0, GNUK_MAX_PACKET_SIZE);
SetEPRxValid (ENDP0);
/* Initialize Endpoint 1 */
SetEPType (ENDP1, EP_BULK);
SetEPTxAddr (ENDP1, ENDP1_TXADDR);
SetEPTxStatus (ENDP1, EP_TX_NAK);
SetEPRxStatus (ENDP1, EP_RX_DIS);
/* Initialize Endpoint 2 */
SetEPType (ENDP2, EP_BULK);
SetEPRxAddr (ENDP2, ENDP2_RXADDR);
SetEPRxCount_allocated_size (ENDP2, GNUK_MAX_PACKET_SIZE);
SetEPRxStatus (ENDP2, EP_RX_VALID);
SetEPTxStatus (ENDP2, EP_TX_DIS);
#ifdef ENABLE_VIRTUAL_COM_PORT
/* Initialize Endpoint 3 */
SetEPType (ENDP3, EP_BULK);
SetEPTxAddr (ENDP3, ENDP3_TXADDR);
SetEPTxStatus (ENDP3, EP_TX_NAK);
SetEPRxStatus (ENDP3, EP_RX_DIS);
/* Initialize Endpoint 4 */
SetEPType (ENDP4, EP_INTERRUPT);
SetEPTxAddr (ENDP4, ENDP4_TXADDR);
SetEPTxStatus (ENDP4, EP_TX_NAK);
SetEPRxStatus (ENDP4, EP_RX_DIS);
/* Initialize Endpoint 5 */
SetEPType (ENDP5, EP_BULK);
SetEPRxAddr (ENDP5, ENDP5_RXADDR);
SetEPRxCount_allocated_size (ENDP5, VIRTUAL_COM_PORT_DATA_SIZE);
SetEPRxStatus (ENDP5, EP_RX_VALID);
SetEPTxStatus (ENDP5, EP_TX_DIS);
#endif
#ifdef PINPAD_DND_SUPPORT
/* Initialize Endpoint 6 */
SetEPType (ENDP6, EP_BULK);
SetEPTxAddr (ENDP6, ENDP6_TXADDR);
SetEPTxStatus (ENDP6, EP_TX_NAK);
SetEPRxStatus (ENDP6, EP_RX_DIS);
/* Initialize Endpoint 7 */
SetEPType (ENDP7, EP_BULK);
SetEPRxAddr (ENDP7, ENDP7_RXADDR);
SetEPRxCount_allocated_size (ENDP7, 64);
SetEPRxStatus (ENDP7, EP_RX_STALL);
SetEPTxStatus (ENDP7, EP_TX_DIS);
#endif
/* Set this device to response on default address */
SetDeviceAddress (0);
bDeviceState = ATTACHED;
}
static void
gnuk_device_SetConfiguration (void)
{
DEVICE_INFO *pInfo = &Device_Info;
if (pInfo->Current_Configuration != 0)
{ /* Device configured */
extern void *main_thread;
extern void chEvtSignalI (void *, unsigned long);
#define LED_STATUS_MODE (8)
bDeviceState = CONFIGURED;
chEvtSignalI (main_thread, LED_STATUS_MODE);
}
}
static void
gnuk_device_SetInterface (void)
{
uint16_t intf = pInformation->USBwIndex0;
/* alternateSetting: pInformation->USBwValue0 should be 0 */
if (intf == 0)
{
ClearDTOG_RX (ENDP2);
ClearDTOG_TX (ENDP1);
}
#ifdef ENABLE_VIRTUAL_COM_PORT
else if (intf == 1)
{
ClearDTOG_TX (ENDP4);
}
else if (intf == 2)
{
ClearDTOG_RX (ENDP5);
ClearDTOG_TX (ENDP3);
}
#endif
#ifdef PINPAD_DND_SUPPORT
# ifdef ENABLE_VIRTUAL_COM_PORT
else if (intf == 3)
{
ClearDTOG_TX (ENDP6);
ClearDTOG_RX (ENDP7);
}
# else
else if (intf == 1)
{
ClearDTOG_TX (ENDP6);
ClearDTOG_RX (ENDP7);
}
# endif
#endif
}
static void
gnuk_device_SetDeviceAddress (void)
{
bDeviceState = ADDRESSED;
}
/* IN from port 0 */
static void
gnuk_device_Status_In (void)
{
}
/* OUT to port 0 */
static void
gnuk_device_Status_Out (void)
{
}
static uint8_t *
gnuk_device_GetDeviceDescriptor (uint16_t Length)
{
return Standard_GetDescriptorData (Length,
(PONE_DESCRIPTOR)&Device_Descriptor);
}
static uint8_t *
gnuk_device_GetConfigDescriptor (uint16_t Length)
{
return Standard_GetDescriptorData (Length,
(PONE_DESCRIPTOR)&Config_Descriptor);
}
static uint8_t *
gnuk_device_GetStringDescriptor (uint16_t Length)
{
uint8_t wValue0 = pInformation->USBwValue0;
if (wValue0 >= (sizeof (String_Descriptor) / sizeof (ONE_DESCRIPTOR)))
return NULL;
else
return
Standard_GetDescriptorData (Length,
(PONE_DESCRIPTOR)&String_Descriptor[wValue0]);
}
#ifdef PINPAD_DND_SUPPORT
# ifdef ENABLE_VIRTUAL_COM_PORT
# define NUM_INTERFACES 4 /* two for CDC, one for CCID, and MSC */
# else
# define NUM_INTERFACES 2 /* CCID and MSC */
# endif
#else
# ifdef ENABLE_VIRTUAL_COM_PORT
# define NUM_INTERFACES 3 /* two for CDC, one for CCID */
# else
# define NUM_INTERFACES 1 /* CCID only */
# endif
#endif
static RESULT
gnuk_device_Get_Interface_Setting (uint8_t Interface, uint8_t AlternateSetting)
{
if (AlternateSetting > 0) /* Any interface, we have no alternate */
return USB_UNSUPPORT;
else if (Interface > NUM_INTERFACES)
return USB_UNSUPPORT;
return USB_SUCCESS;
}
#define USB_CCID_REQ_ABORT 0x01
#define USB_CCID_REQ_GET_CLOCK_FREQUENCIES 0x02
#define USB_CCID_REQ_GET_DATA_RATES 0x03
static const uint8_t freq_table[] = { 0xf3, 0x0d, 0, 0, }; /* dwDefaultClock */
static uint8_t *
gnuk_clock_frequencies (uint16_t len)
{
if (len == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof (freq_table);
return NULL;
}
return (uint8_t *)freq_table;
}
static const uint8_t data_rate_table[] = { 0x80, 0x25, 0, 0, }; /* dwDataRate */
static uint8_t *
gnuk_data_rates (uint16_t len)
{
if (len == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof (data_rate_table);
return NULL;
}
return (uint8_t *)data_rate_table;
}
#if defined(PINPAD_DND_SUPPORT)
static const uint8_t lun_table[] = { 0, 0, 0, 0, };
static uint8_t *
msc_lun_info (uint16_t len)
{
if (len == 0)
{
pInformation->Ctrl_Info.Usb_wLength = sizeof (lun_table);
return NULL;
}
return (uint8_t *)lun_table;
}
#endif
static RESULT
gnuk_setup_with_data (uint8_t RequestNo)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) /* Interface */
if (pInformation->USBwIndex0 == 0)
{
if (RequestNo == USB_CCID_REQ_GET_CLOCK_FREQUENCIES)
{
pInformation->Ctrl_Info.CopyData = gnuk_clock_frequencies;
pInformation->Ctrl_Info.Usb_wOffset = 0;
gnuk_clock_frequencies (0);
return USB_SUCCESS;
}
else if (RequestNo == USB_CCID_REQ_GET_DATA_RATES)
{
pInformation->Ctrl_Info.CopyData = gnuk_data_rates;
pInformation->Ctrl_Info.Usb_wOffset = 0;
gnuk_data_rates (0);
return USB_SUCCESS;
}
else
return USB_UNSUPPORT;
}
#if defined(PINPAD_DND_SUPPORT)
# if defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_Data_Setup (RequestNo);
else if (pInformation->USBwIndex0 == 3)
# else
else if (pInformation->USBwIndex0 == 1)
# endif
{
if (RequestNo == MSC_GET_MAX_LUN_COMMAND)
{
pInformation->Ctrl_Info.CopyData = msc_lun_info;
pInformation->Ctrl_Info.Usb_wOffset = 0;
msc_lun_info (0);
return USB_SUCCESS;
}
else
return USB_UNSUPPORT;
}
#elif defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_Data_Setup (RequestNo);
#endif
else
return USB_UNSUPPORT;
else
return USB_UNSUPPORT;
}
static RESULT
gnuk_setup_with_nodata (uint8_t RequestNo)
{
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) /* Interface */
if (pInformation->USBwIndex0 == 0)
{
if (RequestNo == USB_CCID_REQ_ABORT)
/* wValue: bSeq, bSlot */
/* Abortion is not supported in Gnuk */
return USB_UNSUPPORT;
else
return USB_UNSUPPORT;
}
#if defined(PINPAD_DND_SUPPORT)
# if defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_NoData_Setup (RequestNo);
else if (pInformation->USBwIndex0 == 3)
# else
else if (pInformation->USBwIndex0 == 1)
# endif
{
if (RequestNo == MSC_MASS_STORAGE_RESET_COMMAND)
{
/* Should call resetting MSC thread, something like msc_reset() */
return USB_SUCCESS;
}
else
return USB_UNSUPPORT;
}
#elif defined(ENABLE_VIRTUAL_COM_PORT)
else if (pInformation->USBwIndex0 == 1)
return Virtual_Com_Port_NoData_Setup (RequestNo);
#endif
else
return USB_UNSUPPORT;
else
return USB_UNSUPPORT;
}
/*
* Interface to USB core
*/
const DEVICE_PROP Device_Property = {
gnuk_device_init,
gnuk_device_reset,
gnuk_device_Status_In,
gnuk_device_Status_Out,
gnuk_setup_with_data,
gnuk_setup_with_nodata,
gnuk_device_Get_Interface_Setting,
gnuk_device_GetDeviceDescriptor,
gnuk_device_GetConfigDescriptor,
gnuk_device_GetStringDescriptor,
0,
GNUK_MAX_PACKET_SIZE
};
const DEVICE Device_Table = {
EP_NUM,
1
};
const USER_STANDARD_REQUESTS User_Standard_Requests = {
NOP_Process, /* GetConfiguration */
gnuk_device_SetConfiguration,
NOP_Process, /* GetInterface */
gnuk_device_SetInterface,
NOP_Process, /* GetStatus */
NOP_Process, /* ClearFeature */
NOP_Process, /* SetEndPointFeature */
NOP_Process, /* SetDeviceFeature */
gnuk_device_SetDeviceAddress
};