You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
10 KiB
C
380 lines
10 KiB
C
4 years ago
|
/**
|
||
|
* \file
|
||
|
*
|
||
|
* \brief USB Device Stack CDC ACM Function Implementation.
|
||
|
*
|
||
|
* Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
|
||
|
*
|
||
|
* \asf_license_start
|
||
|
*
|
||
|
* \page License
|
||
|
*
|
||
|
* Subject to your compliance with these terms, you may use Microchip
|
||
|
* software and any derivatives exclusively with Microchip products.
|
||
|
* It is your responsibility to comply with third party license terms applicable
|
||
|
* to your use of third party software (including open source software) that
|
||
|
* may accompany Microchip software.
|
||
|
*
|
||
|
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
|
||
|
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
|
||
|
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
|
||
|
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
|
||
|
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
|
||
|
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
|
||
|
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
|
||
|
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
|
||
|
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
|
||
|
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
|
||
|
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
|
||
|
*
|
||
|
* \asf_license_stop
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "cdcdf_acm.h"
|
||
|
|
||
|
#define CDCDF_ACM_VERSION 0x00000001u
|
||
|
#define CDCDF_ACM_COMM_EP_INDEX 0
|
||
|
#define CDCDF_ACM_DATA_EP_INDEX 1
|
||
|
|
||
|
/** USB Device CDC ACM Fucntion Specific Data */
|
||
|
struct cdcdf_acm_func_data {
|
||
|
/** CDC Device ACM Interface information */
|
||
|
uint8_t func_iface[2];
|
||
|
/** CDC Device ACM IN Endpoint */
|
||
|
uint8_t func_ep_in[2];
|
||
|
/** CDC Device ACM OUT Endpoint */
|
||
|
uint8_t func_ep_out;
|
||
|
/** CDC Device ACM Enable Flag */
|
||
|
bool enabled;
|
||
|
};
|
||
|
|
||
|
static struct usbdf_driver _cdcdf_acm;
|
||
|
static struct cdcdf_acm_func_data _cdcdf_acm_funcd;
|
||
|
static struct usb_cdc_line_coding usbd_cdc_line_coding;
|
||
|
|
||
|
static cdcdf_acm_notify_state_t cdcdf_acm_notify_state = NULL;
|
||
|
static cdcdf_acm_set_line_coding_t cdcdf_acm_set_line_coding = NULL;
|
||
|
|
||
|
/**
|
||
|
* \brief Enable CDC ACM Function
|
||
|
* \param[in] drv Pointer to USB device function driver
|
||
|
* \param[in] desc Pointer to USB interface descriptor
|
||
|
* \return Operation status.
|
||
|
*/
|
||
|
static int32_t cdcdf_acm_enable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
|
||
|
{
|
||
|
struct cdcdf_acm_func_data *func_data = (struct cdcdf_acm_func_data *)(drv->func_data);
|
||
|
|
||
|
usb_ep_desc_t ep_desc;
|
||
|
usb_iface_desc_t ifc_desc;
|
||
|
uint8_t * ifc, *ep;
|
||
|
uint8_t i;
|
||
|
|
||
|
ifc = desc->sod;
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
if (NULL == ifc) {
|
||
|
return ERR_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
ifc_desc.bInterfaceNumber = ifc[2];
|
||
|
ifc_desc.bInterfaceClass = ifc[5];
|
||
|
|
||
|
if ((CDC_CLASS_COMM == ifc_desc.bInterfaceClass) || (CDC_CLASS_DATA == ifc_desc.bInterfaceClass)) {
|
||
|
if (func_data->func_iface[i] == ifc_desc.bInterfaceNumber) { // Initialized
|
||
|
return ERR_ALREADY_INITIALIZED;
|
||
|
} else if (func_data->func_iface[i] != 0xFF) { // Occupied
|
||
|
return ERR_NO_RESOURCE;
|
||
|
} else {
|
||
|
func_data->func_iface[i] = ifc_desc.bInterfaceNumber;
|
||
|
}
|
||
|
} else { // Not supported by this function driver
|
||
|
return ERR_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
// Install endpoints
|
||
|
ep = usb_find_desc(ifc, desc->eod, USB_DT_ENDPOINT);
|
||
|
while (NULL != ep) {
|
||
|
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) {
|
||
|
func_data->func_ep_in[i] = ep_desc.bEndpointAddress;
|
||
|
usb_d_ep_enable(func_data->func_ep_in[i]);
|
||
|
} else {
|
||
|
func_data->func_ep_out = ep_desc.bEndpointAddress;
|
||
|
usb_d_ep_enable(func_data->func_ep_out);
|
||
|
}
|
||
|
desc->sod = ep;
|
||
|
ep = usb_find_ep_desc(usb_desc_next(desc->sod), desc->eod);
|
||
|
}
|
||
|
ifc = usb_find_desc(usb_desc_next(desc->sod), desc->eod, USB_DT_INTERFACE);
|
||
|
}
|
||
|
// Installed
|
||
|
_cdcdf_acm_funcd.enabled = true;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Disable CDC ACM Function
|
||
|
* \param[in] drv Pointer to USB device function driver
|
||
|
* \param[in] desc Pointer to USB device descriptor
|
||
|
* \return Operation status.
|
||
|
*/
|
||
|
static int32_t cdcdf_acm_disable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
|
||
|
{
|
||
|
struct cdcdf_acm_func_data *func_data = (struct cdcdf_acm_func_data *)(drv->func_data);
|
||
|
|
||
|
usb_iface_desc_t ifc_desc;
|
||
|
uint8_t i;
|
||
|
|
||
|
if (desc) {
|
||
|
ifc_desc.bInterfaceClass = desc->sod[5];
|
||
|
// Check interface
|
||
|
if ((ifc_desc.bInterfaceClass != CDC_CLASS_COMM) && (ifc_desc.bInterfaceClass != CDC_CLASS_DATA)) {
|
||
|
return ERR_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 2; i++) {
|
||
|
if (func_data->func_iface[i] == 0xFF) {
|
||
|
continue;
|
||
|
} else {
|
||
|
func_data->func_iface[i] = 0xFF;
|
||
|
if (func_data->func_ep_in[i] != 0xFF) {
|
||
|
usb_d_ep_deinit(func_data->func_ep_in[i]);
|
||
|
func_data->func_ep_in[i] = 0xFF;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (func_data->func_ep_out != 0xFF) {
|
||
|
usb_d_ep_deinit(func_data->func_ep_out);
|
||
|
func_data->func_ep_out = 0xFF;
|
||
|
}
|
||
|
|
||
|
_cdcdf_acm_funcd.enabled = false;
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief CDC ACM Control Function
|
||
|
* \param[in] drv Pointer to USB device function driver
|
||
|
* \param[in] ctrl USB device general function control type
|
||
|
* \param[in] param Parameter pointer
|
||
|
* \return Operation status.
|
||
|
*/
|
||
|
static int32_t cdcdf_acm_ctrl(struct usbdf_driver *drv, enum usbdf_control ctrl, void *param)
|
||
|
{
|
||
|
switch (ctrl) {
|
||
|
case USBDF_ENABLE:
|
||
|
return cdcdf_acm_enable(drv, (struct usbd_descriptors *)param);
|
||
|
|
||
|
case USBDF_DISABLE:
|
||
|
return cdcdf_acm_disable(drv, (struct usbd_descriptors *)param);
|
||
|
|
||
|
case USBDF_GET_IFACE:
|
||
|
return ERR_UNSUPPORTED_OP;
|
||
|
|
||
|
default:
|
||
|
return ERR_INVALID_ARG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Process the CDC class set request
|
||
|
* \param[in] ep Endpoint address.
|
||
|
* \param[in] req Pointer to the request.
|
||
|
* \return Operation status.
|
||
|
*/
|
||
|
static int32_t cdcdf_acm_set_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
|
||
|
{
|
||
|
struct usb_cdc_line_coding line_coding_tmp;
|
||
|
uint16_t len = req->wLength;
|
||
|
uint8_t * ctrl_buf = usbdc_get_ctrl_buffer();
|
||
|
|
||
|
switch (req->bRequest) {
|
||
|
case USB_REQ_CDC_SET_LINE_CODING:
|
||
|
if (sizeof(struct usb_cdc_line_coding) != len) {
|
||
|
return ERR_INVALID_DATA;
|
||
|
}
|
||
|
if (USB_SETUP_STAGE == stage) {
|
||
|
return usbdc_xfer(ep, ctrl_buf, len, false);
|
||
|
} else {
|
||
|
memcpy(&line_coding_tmp, ctrl_buf, sizeof(struct usb_cdc_line_coding));
|
||
|
if ((NULL == cdcdf_acm_set_line_coding) || (true == cdcdf_acm_set_line_coding(&line_coding_tmp))) {
|
||
|
usbd_cdc_line_coding = line_coding_tmp;
|
||
|
}
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
case USB_REQ_CDC_SET_CONTROL_LINE_STATE:
|
||
|
usbdc_xfer(0, NULL, 0, 0);
|
||
|
if (NULL != cdcdf_acm_notify_state) {
|
||
|
cdcdf_acm_notify_state(req->wValue);
|
||
|
}
|
||
|
return ERR_NONE;
|
||
|
default:
|
||
|
return ERR_INVALID_ARG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Process the CDC class get request
|
||
|
* \param[in] ep Endpoint address.
|
||
|
* \param[in] req Pointer to the request.
|
||
|
* \return Operation status.
|
||
|
*/
|
||
|
static int32_t cdcdf_acm_get_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
|
||
|
{
|
||
|
uint16_t len = req->wLength;
|
||
|
|
||
|
if (USB_DATA_STAGE == stage) {
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
switch (req->bRequest) {
|
||
|
case USB_REQ_CDC_GET_LINE_CODING:
|
||
|
if (sizeof(struct usb_cdc_line_coding) != len) {
|
||
|
return ERR_INVALID_DATA;
|
||
|
}
|
||
|
return usbdc_xfer(ep, (uint8_t *)&usbd_cdc_line_coding, len, false);
|
||
|
default:
|
||
|
return ERR_INVALID_ARG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Process the CDC class request
|
||
|
* \param[in] ep Endpoint address.
|
||
|
* \param[in] req Pointer to the request.
|
||
|
* \return Operation status.
|
||
|
*/
|
||
|
static int32_t cdcdf_acm_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
|
||
|
{
|
||
|
if (0x01 != ((req->bmRequestType >> 5) & 0x03)) { // class request
|
||
|
return ERR_NOT_FOUND;
|
||
|
}
|
||
|
if ((req->wIndex == _cdcdf_acm_funcd.func_iface[0]) || (req->wIndex == _cdcdf_acm_funcd.func_iface[1])) {
|
||
|
if (req->bmRequestType & USB_EP_DIR_IN) {
|
||
|
return cdcdf_acm_get_req(ep, req, stage);
|
||
|
} else {
|
||
|
return cdcdf_acm_set_req(ep, req, stage);
|
||
|
}
|
||
|
} else {
|
||
|
return ERR_NOT_FOUND;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** USB Device CDC ACM Handler Struct */
|
||
|
static struct usbdc_handler cdcdf_acm_req_h = {NULL, (FUNC_PTR)cdcdf_acm_req};
|
||
|
|
||
|
/**
|
||
|
* \brief Initialize the USB CDC ACM Function Driver
|
||
|
*/
|
||
|
int32_t cdcdf_acm_init(void)
|
||
|
{
|
||
|
if (usbdc_get_state() > USBD_S_POWER) {
|
||
|
return ERR_DENIED;
|
||
|
}
|
||
|
|
||
|
_cdcdf_acm.ctrl = cdcdf_acm_ctrl;
|
||
|
_cdcdf_acm.func_data = &_cdcdf_acm_funcd;
|
||
|
|
||
|
usbdc_register_function(&_cdcdf_acm);
|
||
|
usbdc_register_handler(USBDC_HDL_REQ, &cdcdf_acm_req_h);
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Deinitialize the USB CDC ACM Function Driver
|
||
|
*/
|
||
|
void cdcdf_acm_deinit(void)
|
||
|
{
|
||
|
usb_d_ep_deinit(_cdcdf_acm_funcd.func_ep_in[CDCDF_ACM_COMM_EP_INDEX]);
|
||
|
usb_d_ep_deinit(_cdcdf_acm_funcd.func_ep_in[CDCDF_ACM_DATA_EP_INDEX]);
|
||
|
usb_d_ep_deinit(_cdcdf_acm_funcd.func_ep_out);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief USB CDC ACM Function Read Data
|
||
|
*/
|
||
|
int32_t cdcdf_acm_read(uint8_t *buf, uint32_t size)
|
||
|
{
|
||
|
if (!cdcdf_acm_is_enabled()) {
|
||
|
return ERR_DENIED;
|
||
|
}
|
||
|
return usbdc_xfer(_cdcdf_acm_funcd.func_ep_out, buf, size, false);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief USB CDC ACM Function Write Data
|
||
|
*/
|
||
|
int32_t cdcdf_acm_write(uint8_t *buf, uint32_t size)
|
||
|
{
|
||
|
if (!cdcdf_acm_is_enabled()) {
|
||
|
return ERR_DENIED;
|
||
|
}
|
||
|
return usbdc_xfer(_cdcdf_acm_funcd.func_ep_in[CDCDF_ACM_DATA_EP_INDEX], buf, size, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief USB CDC ACM Stop the data transfer
|
||
|
*/
|
||
|
void cdcdf_acm_stop_xfer(void)
|
||
|
{
|
||
|
/* Stop transfer. */
|
||
|
usb_d_ep_abort(_cdcdf_acm_funcd.func_ep_in[CDCDF_ACM_DATA_EP_INDEX]);
|
||
|
usb_d_ep_abort(_cdcdf_acm_funcd.func_ep_out);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief USB CDC ACM Function Register Callback
|
||
|
*/
|
||
|
int32_t cdcdf_acm_register_callback(enum cdcdf_acm_cb_type cb_type, FUNC_PTR func)
|
||
|
{
|
||
|
switch (cb_type) {
|
||
|
case CDCDF_ACM_CB_READ:
|
||
|
usb_d_ep_register_callback(_cdcdf_acm_funcd.func_ep_out, USB_D_EP_CB_XFER, func);
|
||
|
break;
|
||
|
case CDCDF_ACM_CB_WRITE:
|
||
|
usb_d_ep_register_callback(_cdcdf_acm_funcd.func_ep_in[CDCDF_ACM_DATA_EP_INDEX], USB_D_EP_CB_XFER, func);
|
||
|
break;
|
||
|
case CDCDF_ACM_CB_LINE_CODING_C:
|
||
|
cdcdf_acm_set_line_coding = (cdcdf_acm_set_line_coding_t)func;
|
||
|
break;
|
||
|
case CDCDF_ACM_CB_STATE_C:
|
||
|
cdcdf_acm_notify_state = (cdcdf_acm_notify_state_t)func;
|
||
|
break;
|
||
|
default:
|
||
|
return ERR_INVALID_ARG;
|
||
|
}
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Check whether CDC ACM Function is enabled
|
||
|
*/
|
||
|
bool cdcdf_acm_is_enabled(void)
|
||
|
{
|
||
|
return _cdcdf_acm_funcd.enabled;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Return the CDC ACM line coding structure start address
|
||
|
*/
|
||
|
const struct usb_cdc_line_coding *cdcdf_acm_get_line_coding(void)
|
||
|
{
|
||
|
return (const struct usb_cdc_line_coding *)&usbd_cdc_line_coding;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Return version
|
||
|
*/
|
||
|
uint32_t cdcdf_acm_get_version(void)
|
||
|
{
|
||
|
return CDCDF_ACM_VERSION;
|
||
|
}
|