Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/* composite.c - USB composite device driver relay */

/*
 * Copyright (c) 2017 PHYTEC Messtechnik GmbH
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <kernel.h>
#include <init.h>
#include <misc/byteorder.h>
#include <string.h>
#include <usb/usb_common.h>
#include <usb/usb_device.h>
#include <usb/usbstruct.h>
#include "composite.h"
#include "usb_descriptor.h"

#define SYS_LOG_LEVEL CONFIG_SYS_LOG_USB_LEVEL
#include <logging/sys_log.h>

static struct usb_interface_cfg_data function_cfg[NUMOF_IFACES];

usb_status_callback cb_usb_status[NUMOF_IFACES];

static u8_t iface_data_buf[CONFIG_USB_COMPOSITE_BUFFER_SIZE];

static void composite_status_cb(enum usb_dc_status_code status, u8_t *param)
{
	for (u8_t i = 0; i < NUMOF_IFACES; i++) {
		if (cb_usb_status[i]) {
			cb_usb_status[i](status, param);
		}
	}
}

static int composite_class_handler(struct usb_setup_packet *pSetup,
		s32_t *len, u8_t **data)
{
	if (pSetup->wIndex >= NUMOF_IFACES) {
		SYS_LOG_DBG("unknown request 0x%x, value 0x%x",
			pSetup->bRequest, pSetup->wValue);
		return -EINVAL;
	}

	if (function_cfg[pSetup->wIndex].class_handler != NULL) {
		return function_cfg[pSetup->wIndex].class_handler(
			pSetup, len, data);
	}

	return -EINVAL;
}

static int composite_custom_handler(struct usb_setup_packet *pSetup,
		s32_t *len, u8_t **data)
{
	if (pSetup->wIndex >= NUMOF_IFACES) {
		SYS_LOG_DBG("unknown request 0x%x, value 0x%x",
			pSetup->bRequest, pSetup->wValue);
		return -ENOTSUP;
	}

	if (function_cfg[pSetup->wIndex].custom_handler != NULL) {
		return function_cfg[pSetup->wIndex].custom_handler(
			pSetup, len, data);
	}

	return -ENOTSUP;
}

static struct usb_ep_cfg_data ep_data[NUMOF_ENDPOINTS] = {};

static struct usb_cfg_data composite_cfg = {
	.usb_device_description = NULL,
	.cb_usb_status = composite_status_cb,
	.interface = {
		.class_handler = composite_class_handler,
		.custom_handler = composite_custom_handler,
		.payload_data = iface_data_buf,
	},
	.num_endpoints = NUMOF_ENDPOINTS,
	.endpoint = ep_data
};

int composite_add_function(struct usb_cfg_data *cfg_data, u8_t if_num)
{
	int numof_free_ep = 0;
	int ep_idx = 0;

	for (u32_t ep = 0; ep < NUMOF_ENDPOINTS; ep++) {
		if (ep_data[ep].ep_cb == NULL) {
			numof_free_ep++;
		}
	}

	if (numof_free_ep < cfg_data->num_endpoints) {
		SYS_LOG_ERR("Not enough free endpoints (free %d, requested %d)",
			    numof_free_ep, cfg_data->num_endpoints);
		return -ENOMEM;
	}

	for (u32_t ep = 0; ep < NUMOF_ENDPOINTS; ep++) {
		if (ep_data[ep].ep_cb == NULL) {
			ep_data[ep].ep_cb =
				cfg_data->endpoint[ep_idx].ep_cb;
			ep_data[ep].ep_addr =
				cfg_data->endpoint[ep_idx].ep_addr;
			ep_idx++;
		}

		if (ep_idx >= cfg_data->num_endpoints) {
			break;
		}
	}

	if (if_num >= NUMOF_IFACES) {
		SYS_LOG_ERR("Interface number out of range");
		return -ENOMEM;
	}

	cfg_data->interface.payload_data = iface_data_buf;

	memcpy(&function_cfg[if_num], &cfg_data->interface,
	       sizeof(struct usb_interface_cfg_data));
	cb_usb_status[if_num] = cfg_data->cb_usb_status;

	return 0;
}

static int composite_init(struct device *dev)
{
	int ret;

	SYS_LOG_DBG("");

	composite_cfg.usb_device_description = usb_get_device_descriptor();

	ret = usb_set_config(&composite_cfg);
	if (ret < 0) {
		SYS_LOG_ERR("Failed to config USB");
		return ret;
	}

	/* Enable USB driver */
	ret = usb_enable(&composite_cfg);
	if (ret < 0) {
		SYS_LOG_ERR("Failed to enable USB");
		return ret;
	}

	return 0;
}

SYS_INIT(composite_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);