Harald Welte has submitted this change and it was merged. ( https://gerrit.osmocom.org/13997 )
Change subject: Add a new (skeleton of a) USB CCID class driver ...................................................................... Add a new (skeleton of a) USB CCID class driver This tries to implement a USB CCID (Chip Card Interface Device) Class core driver in "ASF4 style". Code is currently mainly untested. Change-Id: Ia4d8a6cdc3de26fdc83dcbf89db894b513915a9a --- M sysmoOCTSIM/gcc/Makefile A sysmoOCTSIM/usb/class/ccid/device/ccid_df.c A sysmoOCTSIM/usb/class/ccid/device/ccid_df.h 3 files changed, 314 insertions(+), 3 deletions(-) diff --git a/sysmoOCTSIM/gcc/Makefile b/sysmoOCTSIM/gcc/Makefile index 2dba3e3..080aa41 100644 --- a/sysmoOCTSIM/gcc/Makefile +++ b/sysmoOCTSIM/gcc/Makefile @@ -2,6 +2,8 @@ # Automatically-generated file. Do not edit! ################################################################################ +EXTRA_CFLAGS=-I/usr/local/arm-none-eabi/include -I../../ccid + ifdef SystemRoot SHELL = cmd.exe MK_DIR = mkdir @@ -35,6 +37,7 @@ usb \ hpl/dmac \ usb/class/cdc/device \ +usb/class/ccid/device \ stdio_redirect \ hal/utils/src \ hpl/usb \ @@ -56,6 +59,7 @@ hpl/core/hpl_core_m4.o \ hal/src/hal_cache.o \ usb/class/cdc/device/cdcdf_acm.o \ +usb/class/ccid/device/ccid_df.o \ hal/utils/src/utils_syscalls.o \ stdio_redirect/gcc/read.o \ gcc/system_same54.o \ @@ -107,6 +111,7 @@ "hpl/core/hpl_core_m4.o" \ "hal/src/hal_cache.o" \ "usb/class/cdc/device/cdcdf_acm.o" \ +"usb/class/ccid/device/ccid_df.o" \ "hal/utils/src/utils_syscalls.o" \ "stdio_redirect/gcc/read.o" \ "gcc/system_same54.o" \ @@ -250,7 +255,7 @@ @echo ARM/GNU C Compiler $(QUOTE)arm-none-eabi-gcc$(QUOTE) -x c -mthumb -DDEBUG -Os -ffunction-sections -mlong-calls -g3 -Wall -c -std=gnu99 \ -D__SAME54N19A__ -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \ --I"../" -I"../config" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/sercom" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/device" -I"../" -I"../config" -I"../stdio_redirect" -I"../" -I"../dma_m2m" -I"../" -I"../CMSIS/Core/Include" -I"../include" \ +-I"../" -I"../config" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/sercom" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/class/ccid" -I"../usb/class/ccid/device" -I"../usb/device" -I"../" -I"../config" -I"../stdio_redirect" -I"../" -I"../dma_m2m" -I"../" -I"../CMSIS/Core/Include" -I"../include" \ -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<" @echo Finished building: $< @@ -259,7 +264,7 @@ @echo ARM/GNU Assembler $(QUOTE)arm-none-eabi-as$(QUOTE) -x c -mthumb -DDEBUG -Os -ffunction-sections -mlong-calls -g3 -Wall -c -std=gnu99 \ -D__SAME54N19A__ -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \ --I"../" -I"../config" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/sercom" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/device" -I"../" -I"../config" -I"../stdio_redirect" -I"../" -I"../dma_m2m" -I"../" -I"../CMSIS/Core/Include" -I"../include" \ +-I"../" -I"../config" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/sercom" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/class/ccid" -I"../usb/class/ccid/device" -I"../usb/device" -I"../" -I"../config" -I"../stdio_redirect" -I"../" -I"../dma_m2m" -I"../" -I"../CMSIS/Core/Include" -I"../include" \ -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<" @echo Finished building: $< @@ -268,7 +273,7 @@ @echo ARM/GNU Preprocessing Assembler $(QUOTE)arm-none-eabi-gcc$(QUOTE) -x c -mthumb -DDEBUG -Os -ffunction-sections -mlong-calls -g3 -Wall -c -std=gnu99 \ -D__SAME54N19A__ -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 \ --I"../" -I"../config" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/sercom" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/device" -I"../" -I"../config" -I"../stdio_redirect" -I"../" -I"../dma_m2m" -I"../" -I"../CMSIS/Core/Include" -I"../include" \ +-I"../" -I"../config" -I"../hal/include" -I"../hal/utils/include" -I"../hpl/cmcc" -I"../hpl/core" -I"../hpl/dmac" -I"../hpl/gclk" -I"../hpl/mclk" -I"../hpl/osc32kctrl" -I"../hpl/oscctrl" -I"../hpl/pm" -I"../hpl/port" -I"../hpl/ramecc" -I"../hpl/sercom" -I"../hpl/usb" -I"../hri" -I"../" -I"../config" -I"../usb" -I"../usb/class/cdc" -I"../usb/class/cdc/device" -I"../usb/class/ccid" -I"../usb/class/ccid/device" -I"../usb/device" -I"../" -I"../config" -I"../stdio_redirect" -I"../" -I"../dma_m2m" -I"../" -I"../CMSIS/Core/Include" -I"../include" \ -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)" -o "$@" "$<" @echo Finished building: $< diff --git a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c new file mode 100644 index 0000000..018208a --- /dev/null +++ b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c @@ -0,0 +1,289 @@ +/** + * \file + * + * \brief USB Device Stack CCID Function Implementation. + * + * Copyroght (c) 2019 by Harald Welte <lafo...@gnumonks.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "ccid_df.h" +#include "ccid_proto.h" +#include "usb_includes.h" + +#ifndef USB_CLASS_CCID +#define USB_CLASS_CCID 11 +#endif + +struct ccid_df_func_data { + uint8_t func_iface; /*!< interface number */ + uint8_t func_ep_in; /*!< IN endpoint number */ + uint8_t func_ep_out; /*!< OUT endpoint number */ + uint8_t func_ep_irq; /*!< IRQ endpoint number */ + bool enabled; /*!< is this driver/function enabled? */ + const struct usb_ccid_class_descriptor *ccid_cd; +}; + +static struct usbdf_driver _ccid_df; +static struct ccid_df_func_data _ccid_df_funcd; + +/* FIXME: make those configurable, ensure they're sized according to + * bNumClockSupported / bNumDataRatesSupported */ +static uint32_t ccid_clock_frequencies[] = { LE32(20000) }; +static uint32_t ccid_baud_rates[] = { LE32(9600) }; + +static int32_t ccid_df_enable(struct usbdf_driver *drv, struct usbd_descriptors *desc) +{ + struct ccid_df_func_data *func_data = (struct ccid_df_func_data *)(drv->func_data); + usb_iface_desc_t ifc_desc; + uint8_t *ifc, *ep; + + ifc = desc->sod; + /* FIXME: iterate over multiple interfaces? */ + + if (!ifc) + return ERR_NOT_FOUND; + + ifc_desc.bInterfaceNumber = ifc[2]; + ifc_desc.bInterfaceClass = ifc[5]; + + if (ifc_desc.bInterfaceClass != USB_CLASS_CCID) + return ERR_NOT_FOUND; + + if (func_data->func_iface == ifc_desc.bInterfaceNumber) + return ERR_ALREADY_INITIALIZED; + else if (func_data->func_iface != 0xff) + return ERR_NO_RESOURCE; + + func_data->func_iface = ifc_desc.bInterfaceNumber; + + ep = usb_find_desc(ifc, desc->eod, USB_DT_ENDPOINT); + while (NULL != ep) { + usb_ep_desc_t ep_desc; + ep_desc.bEndpointAddress = ep[2]; + ep_desc.bmAttributes = ep[3]; + ep_desc.wMaxPacketSize = usb_get_u16(ep + 4); + if (usb_d_ep_init(ep_desc.bEndpointAddress, ep_desc.bmAttributes, ep_desc.wMaxPacketSize)) + return ERR_NOT_INITIALIZED; + if (ep_desc.bEndpointAddress & USB_EP_DIR_IN) { + if ((ep_desc.bmAttributes & USB_EP_XTYPE_MASK) == USB_EP_XTYPE_INTERRUPT) + func_data->func_ep_irq = ep_desc.bEndpointAddress; + else + func_data->func_ep_in = ep_desc.bEndpointAddress; + } else { + func_data->func_ep_out = ep_desc.bEndpointAddress; + } + usb_d_ep_enable(ep_desc.bEndpointAddress); + desc->sod = ep; + ep = usb_find_ep_desc(usb_desc_next(desc->sod), desc->eod); + } + + ASSERT(func_data->func_ep_irq); + ASSERT(func_data->func_ep_in); + ASSERT(func_data->func_ep_out); + + _ccid_df_funcd.enabled = true; + return ERR_NONE; +} + +static int32_t ccid_df_disable(struct usbdf_driver *drv, struct usbd_descriptors *desc) +{ + struct ccid_df_func_data *func_data = (struct ccid_df_func_data *)(drv->func_data); + usb_iface_desc_t ifc_desc; + + if (desc) { + ifc_desc.bInterfaceClass = desc->sod[5]; + if (ifc_desc.bInterfaceClass != USB_CLASS_CCID) + return ERR_NOT_FOUND; + } + + func_data->func_iface = 0xff; + if (func_data->func_ep_in != 0xff) { + func_data->func_ep_in = 0xff; + usb_d_ep_deinit(func_data->func_ep_in); + } + if (func_data->func_ep_out != 0xff) { + func_data->func_ep_out = 0xff; + usb_d_ep_deinit(func_data->func_ep_out); + } + if (func_data->func_ep_irq != 0xff) { + func_data->func_ep_irq = 0xff; + usb_d_ep_deinit(func_data->func_ep_irq); + } + + _ccid_df_funcd.enabled = true; + return ERR_NONE; +} + +/*! \brief CCID Control Function (callback with USB core) */ +static int32_t ccid_df_ctrl(struct usbdf_driver *drv, enum usbdf_control ctrl, void *param) +{ + switch (ctrl) { + case USBDF_ENABLE: + return ccid_df_enable(drv, (struct usbd_descriptors *)param); + case USBDF_DISABLE: + return ccid_df_disable(drv, (struct usbd_descriptors *)param); + case USBDF_GET_IFACE: + return ERR_UNSUPPORTED_OP; + default: + return ERR_INVALID_ARG; + } +} + +/* Section 5.3.1: ABORT */ +static int32_t ccid_df_ctrl_req_ccid_abort(uint8_t ep, struct usb_req *req, + enum usb_ctrl_stage stage) +{ + const struct usb_ccid_class_descriptor *ccid_cd = _ccid_df_funcd.ccid_cd; + uint8_t slot_nr = req->wValue & 0xff; + + if (slot_nr > ccid_cd->bMaxSlotIndex) + return ERR_INVALID_ARG; + slot_nr = req->wValue; + + /* FIXME: Implement Abort handling, particularly in combination with + * the PC_to_RDR_Abort on the OUT EP */ + return ERR_NONE; +} + +/* Section 5.3.2: return array of DWORD containing clock frequencies in kHz */ +static int32_t ccid_df_ctrl_req_ccid_get_clock_freq(uint8_t ep, struct usb_req *req, + enum usb_ctrl_stage stage) +{ + const struct usb_ccid_class_descriptor *ccid_cd = _ccid_df_funcd.ccid_cd; + + if (stage != USB_DATA_STAGE) + return ERR_NONE; + + if (req->wLength != ccid_cd->bNumClockSupported * sizeof(uint32_t)) + return ERR_INVALID_DATA; + + return usbdc_xfer(ep, (uint8_t *)ccid_clock_frequencies, req->wLength, false); +} + +/* Section 5.3.3: return array of DWORD containing data rates in bps */ +static int32_t ccid_df_ctrl_req_ccid_get_data_rates(uint8_t ep, struct usb_req *req, + enum usb_ctrl_stage stage) +{ + const struct usb_ccid_class_descriptor *ccid_cd = _ccid_df_funcd.ccid_cd; + + if (stage != USB_DATA_STAGE) + return ERR_NONE; + + if (req->wLength != ccid_cd->bNumDataRatesSupported * sizeof(uint32_t)) + return ERR_INVALID_DATA; + + return usbdc_xfer(ep, (uint8_t *)ccid_baud_rates, req->wLength, false); +} + + +/* process a control endpoint request */ +static int32_t ccid_df_ctrl_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage) +{ + /* verify this is a class-specific request */ + if (0x01 != ((req->bmRequestType >> 5) & 0x03)) + return ERR_NOT_FOUND; + + /* Verify req->wIndex == interface */ + if (req->wIndex != _ccid_df_funcd.func_iface) + return ERR_NOT_FOUND; + + switch (req->bRequest) { + case CLASS_SPEC_CCID_ABORT: + if (req->bmRequestType & USB_EP_DIR_IN) + return ERR_INVALID_ARG; + return ccid_df_ctrl_req_ccid_abort(ep, req, stage); + case CLASS_SPEC_CCID_GET_CLOCK_FREQ: + if (!(req->bmRequestType & USB_EP_DIR_IN)) + return ERR_INVALID_ARG; + return ccid_df_ctrl_req_ccid_get_clock_freq(ep, req, stage); + case CLASS_SPEC_CCID_GET_DATA_RATES: + if (!(req->bmRequestType & USB_EP_DIR_IN)) + return ERR_INVALID_ARG; + return ccid_df_ctrl_req_ccid_get_data_rates(ep, req, stage); + default: + return ERR_NOT_FOUND; + } +} + +static struct usbdc_handler ccid_df_req_h = { NULL, (FUNC_PTR) ccid_df_ctrl_req }; + +int32_t ccid_df_init(void) +{ + if (usbdc_get_state() > USBD_S_POWER) + return ERR_DENIED; + + _ccid_df.ctrl = ccid_df_ctrl; + _ccid_df.func_data = &_ccid_df_funcd; + + /* register the actual USB Function */ + usbdc_register_function(&_ccid_df); + /* register the call-back for control endpoint handling */ + usbdc_register_handler(USBDC_HDL_REQ, &ccid_df_req_h); + + return ERR_NONE; +} + +void ccid_df_deinit(void) +{ + usb_d_ep_deinit(_ccid_df_funcd.func_ep_in); + usb_d_ep_deinit(_ccid_df_funcd.func_ep_out); + usb_d_ep_deinit(_ccid_df_funcd.func_ep_irq); +} + +int32_t ccid_df_read_out(uint8_t *buf, uint32_t size) +{ + if (!ccid_df_is_enabled()) + return ERR_DENIED; + return usbdc_xfer(_ccid_df_funcd.func_ep_out, buf, size, false); +} + +int32_t ccid_df_write_in(uint8_t *buf, uint32_t size) +{ + if (!ccid_df_is_enabled()) + return ERR_DENIED; + return usbdc_xfer(_ccid_df_funcd.func_ep_in, buf, size, true); +} + +int32_t ccid_df_write_irq(uint8_t *buf, uint32_t size) +{ + if (!ccid_df_is_enabled()) + return ERR_DENIED; + return usbdc_xfer(_ccid_df_funcd.func_ep_irq, buf, size, true); +} + +int32_t ccid_df_register_callback(enum ccid_df_cb_type cb_type, FUNC_PTR func) +{ + switch (cb_type) { + case CCID_DF_CB_READ_OUT: + usb_d_ep_register_callback(_ccid_df_funcd.func_ep_out, USB_D_EP_CB_XFER, func); + break; + case CCID_DF_CB_WRITE_IN: + usb_d_ep_register_callback(_ccid_df_funcd.func_ep_in, USB_D_EP_CB_XFER, func); + break; + case CCID_DF_CB_WRITE_IRQ: + usb_d_ep_register_callback(_ccid_df_funcd.func_ep_irq, USB_D_EP_CB_XFER, func); + break; + default: + return ERR_INVALID_ARG; + } + return ERR_NONE; +} + +bool ccid_df_is_enabled(void) +{ + return _ccid_df_funcd.enabled; +} diff --git a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h new file mode 100644 index 0000000..21fa99e --- /dev/null +++ b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.h @@ -0,0 +1,17 @@ +#pragma once + +#include "usbdc.h" + +enum ccid_df_cb_type { + CCID_DF_CB_READ_OUT, + CCID_DF_CB_WRITE_IN, + CCID_DF_CB_WRITE_IRQ, +}; + +int32_t ccid_df_init(void); +void ccid_df_deinit(void); +int32_t ccid_df_read_out(uint8_t *buf, uint32_t size); +int32_t ccid_df_write_in(uint8_t *buf, uint32_t size); +int32_t ccid_df_write_irq(uint8_t *buf, uint32_t size); +int32_t ccid_df_register_callback(enum ccid_df_cb_type cb_type, FUNC_PTR ptr); +bool ccid_df_is_enabled(void); -- To view, visit https://gerrit.osmocom.org/13997 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: osmo-ccid-firmware Gerrit-Branch: master Gerrit-MessageType: merged Gerrit-Change-Id: Ia4d8a6cdc3de26fdc83dcbf89db894b513915a9a Gerrit-Change-Number: 13997 Gerrit-PatchSet: 3 Gerrit-Owner: Harald Welte <lafo...@gnumonks.org> Gerrit-Reviewer: Jenkins Builder (1000002)