sim/usb: add sim usb host
signed-off-by: zhangyuan21 <zhangyuan21@xiaomi.com>
This commit is contained in:
parent
c61c694a77
commit
0af63cfc48
@ -635,4 +635,39 @@ config SIM_USB_RAW_GADGET
|
||||
|
||||
endif
|
||||
|
||||
config SIM_USB_HOST
|
||||
bool "Linux USB Host"
|
||||
select USBHOST
|
||||
select USBHOST_HAVE_ASYNCH
|
||||
select USBHOST_ASYNCH
|
||||
---help---
|
||||
Build in support for simulated usb host
|
||||
|
||||
if SIM_USB_HOST
|
||||
|
||||
config SIM_LIBUSB
|
||||
bool "Simulated USB Host use libusb"
|
||||
default n
|
||||
depends on HOST_LINUX
|
||||
---help---
|
||||
Use libusb to set up virtual USB Host controller.
|
||||
|
||||
config SIM_USB_VID
|
||||
hex "Simulated USB Dev VID"
|
||||
default 0x18d1
|
||||
|
||||
config SIM_USB_PID
|
||||
hex "Simulated USB Dev PID"
|
||||
default 0x4e11
|
||||
|
||||
config SIM_USB_STACKSIZE
|
||||
int "Simulated USB waiter stack size"
|
||||
default 1024
|
||||
|
||||
config SIM_USB_PRIO
|
||||
int "Simulated USB waiter task priority"
|
||||
default 100
|
||||
|
||||
endif
|
||||
|
||||
endif # ARCH_SIM
|
||||
|
@ -205,6 +205,14 @@ ifeq ($(CONFIG_SIM_USB_RAW_GADGET),y)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SIM_USB_HOST),y)
|
||||
CSRCS += sim_usbhost.c
|
||||
ifeq ($(CONFIG_SIM_LIBUSB),y)
|
||||
HOSTSRCS += sim_libusb.c
|
||||
STDLIBS += -lusb-1.0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_RPTUN),y)
|
||||
CSRCS += sim_rptun.c
|
||||
endif
|
||||
|
700
arch/sim/src/sim/posix/sim_libusb.c
Normal file
700
arch/sim/src/sim/posix/sim_libusb.c
Normal file
@ -0,0 +1,700 @@
|
||||
/****************************************************************************
|
||||
* arch/sim/src/sim/posix/sim_libusb.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include "sim_usbhost.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define ERROR(fmt, ...) \
|
||||
syslog(LOG_ERR, "sim_libusb: " fmt "\n", ##__VA_ARGS__)
|
||||
#define INFO(fmt, ...) \
|
||||
syslog(LOG_INFO, "sim_libusb: " fmt "\n", ##__VA_ARGS__)
|
||||
#define DEBUG(fmt, ...)
|
||||
|
||||
#define HOST_LIBUSB_EP_NUM(addr) ((addr) & USB_ENDPOINT_NUMBER_MASK)
|
||||
#define HOST_LIBUSB_EP_DIR(addr) ((addr) & USB_ENDPOINT_DIR_MASK)
|
||||
|
||||
#ifdef CONFIG_SIM_USB_VID
|
||||
# define USB_VID CONFIG_SIM_USB_VID
|
||||
#else
|
||||
# define USB_VID 0x18d1
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SIM_USB_PID
|
||||
# define USB_PID CONFIG_SIM_USB_PID
|
||||
#else
|
||||
# define USB_PID 0x4e11
|
||||
#endif
|
||||
|
||||
#define HOST_LIBUSB_FIFO_NUM 8
|
||||
|
||||
#define HOST_LIBUSB_FIFO_USED(fifo) \
|
||||
((fifo)->write - (fifo)->read)
|
||||
#define HOST_LIBUSB_FIFO_PUSH(fifo) \
|
||||
((fifo)->write++)
|
||||
#define HOST_LIBUSB_FIFO_POP(fifo) \
|
||||
((fifo)->read++)
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct host_libusb_fifo_s
|
||||
{
|
||||
uint16_t read;
|
||||
uint16_t write;
|
||||
struct host_usb_datareq_s *datareq[HOST_LIBUSB_FIFO_NUM];
|
||||
};
|
||||
|
||||
struct host_libusb_hostdev_s
|
||||
{
|
||||
libusb_device *priv;
|
||||
libusb_device_handle *handle;
|
||||
struct libusb_device_descriptor dev_desc;
|
||||
struct libusb_config_descriptor **config_desc;
|
||||
libusb_hotplug_callback_handle callback_handle;
|
||||
struct host_libusb_fifo_s completed;
|
||||
pthread_t handle_thread;
|
||||
bool connected;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static libusb_context *g_libusb_context;
|
||||
static struct host_libusb_hostdev_s g_libusb_dev;
|
||||
static libusb_device **g_libusb_dev_list;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void host_libusb_fifoinit(struct host_libusb_fifo_s *fifo)
|
||||
{
|
||||
fifo->write = 0;
|
||||
fifo->read = 0;
|
||||
}
|
||||
|
||||
static struct host_usb_datareq_s *
|
||||
host_libusb_fifopop(struct host_libusb_fifo_s *fifo)
|
||||
{
|
||||
struct host_usb_datareq_s *datareq;
|
||||
uint16_t r_idx;
|
||||
|
||||
if (HOST_LIBUSB_FIFO_USED(fifo) == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r_idx = fifo->read & (HOST_LIBUSB_FIFO_NUM - 1);
|
||||
|
||||
datareq = fifo->datareq[r_idx];
|
||||
|
||||
fifo->read++;
|
||||
|
||||
return datareq;
|
||||
}
|
||||
|
||||
static bool host_libusb_fifopush(struct host_libusb_fifo_s *fifo,
|
||||
struct host_usb_datareq_s *datareq)
|
||||
{
|
||||
uint16_t w_idx;
|
||||
|
||||
if (HOST_LIBUSB_FIFO_USED(fifo) == HOST_LIBUSB_FIFO_NUM)
|
||||
{
|
||||
ERROR("USB get fifo fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
w_idx = fifo->write & (HOST_LIBUSB_FIFO_NUM - 1);
|
||||
|
||||
fifo->datareq[w_idx] = datareq;
|
||||
|
||||
fifo->write++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
host_libusb_getctrlreq(struct usb_ctrlrequest *libusb_req,
|
||||
const struct host_usb_ctrlreq_s *host_req)
|
||||
{
|
||||
libusb_req->bRequestType = host_req->type;
|
||||
libusb_req->bRequest = host_req->req;
|
||||
libusb_req->wValue = host_req->value;
|
||||
libusb_req->wIndex = host_req->index;
|
||||
libusb_req->wLength = host_req->len;
|
||||
}
|
||||
|
||||
static void host_libusb_ep0transfer_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
struct host_usb_datareq_s *datareq;
|
||||
uint8_t *buffer;
|
||||
|
||||
if (!transfer)
|
||||
{
|
||||
ERROR("host_libusb_ep0transfer_cb() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_INVALID_PARAM));
|
||||
return;
|
||||
}
|
||||
|
||||
datareq = (struct host_usb_datareq_s *)transfer->user_data;
|
||||
|
||||
buffer = libusb_control_transfer_get_data(transfer);
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
|
||||
{
|
||||
datareq->success = true;
|
||||
datareq->xfer += transfer->actual_length;
|
||||
memcpy(datareq->data, buffer, transfer->actual_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
datareq->success = false;
|
||||
}
|
||||
|
||||
free(buffer - LIBUSB_CONTROL_SETUP_SIZE);
|
||||
|
||||
host_libusb_fifopush(&dev->completed, datareq);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void host_libusb_bulktransfer_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
struct host_usb_datareq_s *datareq;
|
||||
|
||||
if (!transfer)
|
||||
{
|
||||
ERROR("host_libusb_bulktransfer_cb() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_INVALID_PARAM));
|
||||
return;
|
||||
}
|
||||
|
||||
datareq = (struct host_usb_datareq_s *)transfer->user_data;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
||||
{
|
||||
datareq->success = false;
|
||||
goto transfer_end;
|
||||
}
|
||||
|
||||
datareq->xfer += transfer->actual_length;
|
||||
if ((datareq->xfer >= datareq->len) ||
|
||||
(datareq->addr & USB_DIR_IN) != 0)
|
||||
{
|
||||
datareq->success = true;
|
||||
goto transfer_end;
|
||||
}
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, dev->handle, datareq->addr,
|
||||
datareq->data + datareq->xfer,
|
||||
datareq->len - datareq->xfer,
|
||||
host_libusb_bulktransfer_cb,
|
||||
datareq, 0);
|
||||
if (libusb_submit_transfer(transfer) != LIBUSB_SUCCESS)
|
||||
{
|
||||
datareq->success = false;
|
||||
goto transfer_end;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
transfer_end:
|
||||
host_libusb_fifopush(&dev->completed, datareq);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void host_libusb_inttransfer_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
struct host_usb_datareq_s *datareq;
|
||||
|
||||
if (!transfer)
|
||||
{
|
||||
ERROR("host_libusb_inttransfer_cb() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_INVALID_PARAM));
|
||||
return;
|
||||
}
|
||||
|
||||
datareq = (struct host_usb_datareq_s *)transfer->user_data;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
|
||||
{
|
||||
datareq->success = true;
|
||||
datareq->xfer += transfer->actual_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
datareq->success = false;
|
||||
}
|
||||
|
||||
host_libusb_fifopush(&dev->completed, datareq);
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
static int host_libusb_ep0inhandle(struct host_libusb_hostdev_s *dev,
|
||||
struct usb_ctrlrequest *ctrlreq,
|
||||
struct host_usb_datareq_s *datareq,
|
||||
int timeout)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
uint8_t *buffer;
|
||||
int ret = LIBUSB_SUCCESS;
|
||||
|
||||
if (!dev->handle)
|
||||
{
|
||||
ERROR("host_libusb_ep0inhandle() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_DEVICE));
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + ctrlreq->wLength);
|
||||
if (!buffer)
|
||||
{
|
||||
ERROR("control data buffer malloc() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_MEM));
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
if (!transfer)
|
||||
{
|
||||
ERROR("libusb_alloc_transfer() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_MEM));
|
||||
ret = LIBUSB_ERROR_NO_MEM;
|
||||
goto err_with_buffer;
|
||||
}
|
||||
|
||||
libusb_fill_control_setup(buffer, ctrlreq->bRequestType,
|
||||
ctrlreq->bRequest, ctrlreq->wValue,
|
||||
ctrlreq->wIndex, ctrlreq->wLength);
|
||||
libusb_fill_control_transfer(transfer, dev->handle, buffer,
|
||||
host_libusb_ep0transfer_cb,
|
||||
datareq, timeout);
|
||||
|
||||
ret = libusb_submit_transfer(transfer);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
goto err_with_transfer;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
|
||||
err_with_transfer:
|
||||
libusb_free_transfer(transfer);
|
||||
|
||||
err_with_buffer:
|
||||
free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int host_libusb_ep0outhandle(struct host_libusb_hostdev_s *dev,
|
||||
struct usb_ctrlrequest *ctrlreq)
|
||||
{
|
||||
int ret = LIBUSB_SUCCESS;
|
||||
|
||||
if (!dev->handle)
|
||||
{
|
||||
ERROR("host_libusb_control_request() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_DEVICE));
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
if ((ctrlreq->bRequestType & USB_TYPE_MASK) !=
|
||||
USB_TYPE_STANDARD)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (ctrlreq->bRequest)
|
||||
{
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
ret = libusb_detach_kernel_driver(dev->handle, 0);
|
||||
if (ret == LIBUSB_SUCCESS)
|
||||
{
|
||||
ret = libusb_set_configuration(dev->handle,
|
||||
ctrlreq->wValue);
|
||||
ret |= libusb_claim_interface(dev->handle, 0);
|
||||
}
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE: /* TODO */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
host_libusb_bulktransfer(struct host_libusb_hostdev_s *dev, uint8_t addr,
|
||||
struct host_usb_datareq_s *datareq)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
int ret = LIBUSB_SUCCESS;
|
||||
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
if (!transfer)
|
||||
{
|
||||
ERROR("libusb_alloc_transfer() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_MEM));
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
libusb_fill_bulk_transfer(transfer, dev->handle, addr,
|
||||
datareq->data, datareq->len,
|
||||
host_libusb_bulktransfer_cb,
|
||||
datareq, 0);
|
||||
ret = libusb_submit_transfer(transfer);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
host_libusb_inttransfer(struct host_libusb_hostdev_s *dev, uint8_t addr,
|
||||
struct host_usb_datareq_s *datareq)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
int ret = LIBUSB_SUCCESS;
|
||||
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
if (!transfer)
|
||||
{
|
||||
ERROR("libusb_alloc_transfer() fail: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_MEM));
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
libusb_fill_interrupt_transfer(transfer, dev->handle, addr,
|
||||
datareq->data, datareq->len,
|
||||
host_libusb_inttransfer_cb,
|
||||
datareq, 0);
|
||||
ret = libusb_submit_transfer(transfer);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *host_libusb_event_handle(void *arg)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
libusb_handle_events(g_libusb_context);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool host_libusb_connectdevice(void)
|
||||
{
|
||||
int dev_cnt;
|
||||
int i;
|
||||
|
||||
dev_cnt = libusb_get_device_list(g_libusb_context,
|
||||
&g_libusb_dev_list);
|
||||
if (dev_cnt < 0)
|
||||
{
|
||||
ERROR("libusb_get_device_list() failed: %s\n",
|
||||
libusb_strerror(dev_cnt));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < dev_cnt; i++)
|
||||
{
|
||||
libusb_device *dev = g_libusb_dev_list[i];
|
||||
struct libusb_device_descriptor dev_desc;
|
||||
int ret = libusb_get_device_descriptor(dev, &dev_desc);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
ERROR("libusb_get_device_descriptor() failed: %s\n",
|
||||
libusb_strerror(ret));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dev_desc.bDeviceClass == LIBUSB_CLASS_HUB)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dev_desc.idVendor == USB_VID ||
|
||||
dev_desc.idProduct == USB_PID)
|
||||
{
|
||||
g_libusb_dev.priv = dev;
|
||||
memcpy(&g_libusb_dev.dev_desc, &dev_desc,
|
||||
sizeof(struct libusb_device_descriptor));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(g_libusb_dev_list, 1);
|
||||
g_libusb_dev_list = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int host_libusb_hotplug_callback(libusb_context *ctx,
|
||||
libusb_device *device,
|
||||
libusb_hotplug_event event,
|
||||
void *user_data)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
|
||||
INFO("Hotplug event: %d\n", event);
|
||||
|
||||
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)
|
||||
{
|
||||
dev->connected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->connected = false;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int host_usbhost_ep0trans(struct host_usb_ctrlreq_s *ctrlreq,
|
||||
struct host_usb_datareq_s *datareq)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
struct usb_ctrlrequest libusb_ctrlreq;
|
||||
int ret;
|
||||
|
||||
host_libusb_getctrlreq(&libusb_ctrlreq, ctrlreq);
|
||||
|
||||
if (!(libusb_ctrlreq.bRequestType & USB_DIR_IN))
|
||||
{
|
||||
ret = host_libusb_ep0outhandle(dev, &libusb_ctrlreq);
|
||||
datareq->success = (ret != LIBUSB_SUCCESS) ? false : true;
|
||||
host_libusb_fifopush(&dev->completed, datareq);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = host_libusb_ep0inhandle(dev, &libusb_ctrlreq,
|
||||
datareq, 0);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
datareq->success = false;
|
||||
host_libusb_fifopush(&dev->completed, datareq);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int host_usbhost_eptrans(struct host_usb_datareq_s *datareq)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
int ret = LIBUSB_SUCCESS;
|
||||
|
||||
switch (datareq->xfrtype & USB_ENDPOINT_XFERTYPE_MASK)
|
||||
{
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
ret = host_libusb_bulktransfer(dev, datareq->addr, datareq);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
ret = host_libusb_inttransfer(dev, datareq->addr, datareq);
|
||||
break;
|
||||
default:
|
||||
ERROR("Unsupported transfer type");
|
||||
ret = LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
datareq->success = false;
|
||||
host_libusb_fifopush(&dev->completed, datareq);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int host_usbhost_open(void)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
uint8_t cnt;
|
||||
int ret;
|
||||
|
||||
if (!dev->priv)
|
||||
{
|
||||
if (!host_libusb_connectdevice())
|
||||
{
|
||||
ERROR("host_libusb_connectdevice() failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = libusb_open(dev->priv, &dev->handle);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
ERROR("libusb_open() failed: %s\n", libusb_strerror(ret));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = libusb_set_auto_detach_kernel_driver(dev->handle, 1);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
ERROR("libusb_set_auto_detach_kernel_driver() failed: %s\n",
|
||||
libusb_strerror(ret));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
dev->config_desc = (struct libusb_config_descriptor **)
|
||||
malloc(dev->dev_desc.bNumConfigurations
|
||||
*(sizeof(struct libusb_config_descriptor *)));
|
||||
if (!dev->config_desc)
|
||||
{
|
||||
ERROR("host_libusb_devinit() malloc failed: %s\n",
|
||||
libusb_strerror(LIBUSB_ERROR_NO_MEM));
|
||||
ret = LIBUSB_ERROR_NO_MEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < dev->dev_desc.bNumConfigurations; cnt++)
|
||||
{
|
||||
ret = libusb_get_config_descriptor(dev->priv, cnt,
|
||||
&dev->config_desc[cnt]);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
ERROR("libusb_get_config_descriptor() failed: %s\n",
|
||||
libusb_strerror(ret));
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
host_libusb_fifoinit(&dev->completed);
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
|
||||
err_out:
|
||||
host_usbhost_close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void host_usbhost_close(void)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
|
||||
dev->priv = NULL;
|
||||
|
||||
if (dev->config_desc)
|
||||
{
|
||||
free(dev->config_desc);
|
||||
dev->config_desc = NULL;
|
||||
}
|
||||
|
||||
if (dev->handle)
|
||||
{
|
||||
libusb_close(dev->handle);
|
||||
dev->handle = NULL;
|
||||
}
|
||||
|
||||
if (g_libusb_dev_list)
|
||||
{
|
||||
libusb_free_device_list(g_libusb_dev_list, 1);
|
||||
g_libusb_dev_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct host_usb_datareq_s *host_usbhost_getcomplete(void)
|
||||
{
|
||||
struct host_libusb_hostdev_s *dev = &g_libusb_dev;
|
||||
return host_libusb_fifopop(&dev->completed);
|
||||
}
|
||||
|
||||
bool host_usbhost_getconnstate(void)
|
||||
{
|
||||
return g_libusb_dev.connected;
|
||||
}
|
||||
|
||||
int host_usbhost_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = libusb_init(&g_libusb_context);
|
||||
if (ret < 0)
|
||||
{
|
||||
ERROR("libusb_init() failed: %s\n", libusb_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = libusb_hotplug_register_callback(g_libusb_context,
|
||||
(libusb_hotplug_event) (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT |
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED),
|
||||
(libusb_hotplug_flag) 0, USB_VID, USB_PID,
|
||||
LIBUSB_HOTPLUG_MATCH_ANY, host_libusb_hotplug_callback,
|
||||
NULL, NULL);
|
||||
if (ret != LIBUSB_SUCCESS)
|
||||
{
|
||||
ERROR("libusb_hotplug_register_callback() failed: %s\n",
|
||||
libusb_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
g_libusb_dev.connected = host_libusb_connectdevice();
|
||||
|
||||
ret = pthread_create(&g_libusb_dev.handle_thread, NULL,
|
||||
host_libusb_event_handle, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
ERROR("pthread_create() failed\n");
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
@ -216,6 +216,10 @@ static int sim_loop_task(int argc, char **argv)
|
||||
sched_unlock();
|
||||
up_irq_restore(flags);
|
||||
|
||||
#ifdef CONFIG_SIM_USB_HOST
|
||||
sim_usbhost_loop();
|
||||
#endif
|
||||
|
||||
/* Sleep minimal time, let the idle run */
|
||||
|
||||
usleep(CONFIG_SIM_LOOPTASK_INTERVAL);
|
||||
@ -297,6 +301,10 @@ void up_initialize(void)
|
||||
sim_usbdev_initialize();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SIM_USB_HOST
|
||||
sim_usbhost_initialize();
|
||||
#endif
|
||||
|
||||
kthread_create("loop_task", SCHED_PRIORITY_MAX,
|
||||
CONFIG_DEFAULT_TASK_STACKSIZE,
|
||||
sim_loop_task, NULL);
|
||||
|
@ -390,6 +390,13 @@ void sim_usbdev_initialize(void);
|
||||
int sim_usbdev_loop(void);
|
||||
#endif
|
||||
|
||||
/* sim_usbhost.c ************************************************************/
|
||||
|
||||
#ifdef CONFIG_SIM_USB_HOST
|
||||
int sim_usbhost_initialize(void);
|
||||
int sim_usbhost_loop(void);
|
||||
#endif
|
||||
|
||||
/* Debug ********************************************************************/
|
||||
|
||||
#ifdef CONFIG_STACK_COLORATION
|
||||
|
803
arch/sim/src/sim/sim_usbhost.c
Normal file
803
arch/sim/src/sim/sim_usbhost.c
Normal file
@ -0,0 +1,803 @@
|
||||
/****************************************************************************
|
||||
* arch/sim/src/sim/sim_usbhost.c
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/kthread.h>
|
||||
#include <nuttx/usb/usb.h>
|
||||
#include <nuttx/usb/usbhost.h>
|
||||
#include <nuttx/usb/usbhost_trace.h>
|
||||
|
||||
#include "sim_usbhost.h"
|
||||
#include "sim_internal.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define SIM_USBHOST_BUFSIZE 256
|
||||
|
||||
#define RHPNDX(rh) ((rh)->hport.hport.port)
|
||||
#define RHPORT(rh) (RHPNDX(rh)+1)
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(a,b) ((a) > (b) ? (b) : (a))
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
enum sim_hoststate_e
|
||||
{
|
||||
USB_HOST_DETACHED = 0, /* Not attached to a device */
|
||||
USB_HOST_ATTACHED, /* Attached to a device */
|
||||
USB_HOST_ENUM, /* Attached, enumerating */
|
||||
USB_HOST_CLASS_BOUND, /* Enumeration complete, class bound */
|
||||
};
|
||||
|
||||
struct sim_epinfo_s
|
||||
{
|
||||
uint8_t epno:7; /* Endpoint number */
|
||||
uint8_t dirin:1; /* 1:IN endpoint 0:OUT endpoint */
|
||||
uint8_t devaddr:7; /* Device address */
|
||||
uint8_t toggle:1; /* Next data toggle */
|
||||
uint8_t interval; /* Polling interval */
|
||||
uint8_t status; /* Retained token status bits (for debug purposes) */
|
||||
uint16_t maxpacket:11; /* Maximum packet size */
|
||||
uint16_t xfrtype:2; /* See USB_EP_ATTR_XFER_* definitions in usb.h */
|
||||
uint16_t speed:2; /* See USB_*_SPEED definitions */
|
||||
int result; /* The result of the transfer */
|
||||
uint32_t xfrd; /* On completion, will hold the number of bytes transferred */
|
||||
sem_t iocsem; /* Semaphore used to wait for transfer completion */
|
||||
};
|
||||
|
||||
struct sim_usbhost_s
|
||||
{
|
||||
/* Common device fields. This must be the first thing defined in the
|
||||
* structure so that it is possible to simply cast from struct usbhost_s
|
||||
* to struct sim_usbhost_s.
|
||||
*/
|
||||
|
||||
struct usbhost_driver_s drvr;
|
||||
|
||||
/* This is the hub port description understood by class drivers */
|
||||
|
||||
struct usbhost_roothubport_s hport;
|
||||
|
||||
/* Root hub port status */
|
||||
|
||||
volatile bool connected; /* Connected to device */
|
||||
struct sim_epinfo_s ep0; /* EP0 endpoint info */
|
||||
|
||||
uint8_t state;
|
||||
|
||||
volatile bool pscwait; /* TRUE: Thread is waiting for port status change event */
|
||||
|
||||
mutex_t lock; /* Support mutually exclusive access */
|
||||
sem_t pscsem; /* Semaphore to wait for port status change events */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* USB host connection operations *******************************************/
|
||||
|
||||
static int sim_usbhost_wait(struct usbhost_connection_s *conn,
|
||||
struct usbhost_hubport_s **hport);
|
||||
static int sim_usbhost_enumerate(struct usbhost_connection_s *conn,
|
||||
struct usbhost_hubport_s *hport);
|
||||
|
||||
/* USB host driver operations ***********************************************/
|
||||
|
||||
static int sim_usbhost_ep0configure(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep0, uint8_t funcaddr,
|
||||
uint8_t speed, uint16_t maxpacketsize);
|
||||
static int sim_usbhost_epalloc(FAR struct usbhost_driver_s *drvr,
|
||||
FAR const struct usbhost_epdesc_s *epdesc,
|
||||
FAR usbhost_ep_t *ep);
|
||||
static int sim_usbhost_epfree(FAR struct usbhost_driver_s *drvr,
|
||||
FAR usbhost_ep_t ep);
|
||||
static int sim_usbhost_alloc(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t **buffer, FAR size_t *maxlen);
|
||||
static int sim_usbhost_free(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t *buffer);
|
||||
static int sim_usbhost_ioalloc(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t **buffer, size_t buflen);
|
||||
static int sim_usbhost_iofree(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t *buffer);
|
||||
static int sim_usbhost_ctrlin(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep0,
|
||||
FAR const struct usb_ctrlreq_s *req,
|
||||
FAR uint8_t *buffer);
|
||||
static int sim_usbhost_ctrlout(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep0,
|
||||
FAR const struct usb_ctrlreq_s *req,
|
||||
FAR const uint8_t *buffer);
|
||||
static ssize_t sim_usbhost_transfer(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep, FAR uint8_t *buffer,
|
||||
size_t buflen);
|
||||
static int sim_usbhost_cancel(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep);
|
||||
static void sim_usbhost_disconnect(FAR struct usbhost_driver_s *drvr,
|
||||
FAR struct usbhost_hubport_s *hport);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static struct sim_usbhost_s g_sim_usbhost =
|
||||
{
|
||||
.lock = NXMUTEX_INITIALIZER,
|
||||
.pscsem = SEM_INITIALIZER(0),
|
||||
.ep0.iocsem = SEM_INITIALIZER(1),
|
||||
};
|
||||
|
||||
static struct usbhost_connection_s g_sim_usbconn =
|
||||
{
|
||||
.wait = sim_usbhost_wait,
|
||||
.enumerate = sim_usbhost_enumerate,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_allocrq
|
||||
****************************************************************************/
|
||||
|
||||
static struct host_usb_datareq_s *sim_usbhost_allocrq(void)
|
||||
{
|
||||
struct host_usb_datareq_s *req;
|
||||
|
||||
req = zalloc(sizeof(struct host_usb_datareq_s));
|
||||
if (req)
|
||||
{
|
||||
req->success = false;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_freerq
|
||||
****************************************************************************/
|
||||
|
||||
static void sim_usbhost_freerq(struct host_usb_datareq_s *req)
|
||||
{
|
||||
DEBUGASSERT(req);
|
||||
free(req);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_getctrlreq
|
||||
****************************************************************************/
|
||||
|
||||
static void
|
||||
sim_usbhost_getctrlreq(struct host_usb_ctrlreq_s *host_req,
|
||||
const struct usb_ctrlreq_s *ctr_req)
|
||||
{
|
||||
host_req->type = ctr_req->type;
|
||||
host_req->req = ctr_req->req;
|
||||
host_req->value = GETUINT16(ctr_req->value);
|
||||
host_req->index = GETUINT16(ctr_req->index);
|
||||
host_req->len = GETUINT16(ctr_req->len);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_waittask
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_waittask(int argc, char *argv[])
|
||||
{
|
||||
struct usbhost_connection_s *conn = &g_sim_usbconn;
|
||||
struct usbhost_hubport_s *hport;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
/* Wait for the device to change state */
|
||||
|
||||
CONN_WAIT(conn, &hport);
|
||||
uinfo("%s\n", hport->connected ? "connected" : "disconnected");
|
||||
|
||||
/* Did we just become connected? */
|
||||
|
||||
if (hport->connected)
|
||||
{
|
||||
/* Yes.. enumerate the newly connected device */
|
||||
|
||||
CONN_ENUMERATE(conn, hport);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_wait
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_wait(struct usbhost_connection_s *conn,
|
||||
struct usbhost_hubport_s **hport)
|
||||
{
|
||||
struct sim_usbhost_s *priv = &g_sim_usbhost;
|
||||
irqstate_t flags;
|
||||
int ret;
|
||||
|
||||
/* Loop until the connection state changes on one of the root hub ports or
|
||||
* until an error occurs.
|
||||
*/
|
||||
|
||||
flags = enter_critical_section();
|
||||
for (; ; )
|
||||
{
|
||||
/* Check for a change in the connection state on any root hub port */
|
||||
|
||||
struct usbhost_hubport_s *connport;
|
||||
|
||||
/* Has the connection state changed on the RH port? */
|
||||
|
||||
connport = &priv->hport.hport;
|
||||
if (priv->connected != connport->connected)
|
||||
{
|
||||
/* Yes.. Return the RH port to inform the caller which
|
||||
* port has the connection change.
|
||||
*/
|
||||
|
||||
connport->connected = priv->connected;
|
||||
*hport = connport;
|
||||
leave_critical_section(flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* No changes on any port. Wait for a connection/disconnection event
|
||||
* and check again
|
||||
*/
|
||||
|
||||
priv->pscwait = true;
|
||||
ret = nxsem_wait_uninterruptible(&priv->pscsem);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_enumerate
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_enumerate(struct usbhost_connection_s *conn,
|
||||
struct usbhost_hubport_s *hport)
|
||||
{
|
||||
struct sim_usbhost_s *priv = &g_sim_usbhost;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(hport);
|
||||
|
||||
/* Then let the common usbhost_enumerate do the real enumeration. */
|
||||
|
||||
uinfo("Enumerate the device\n");
|
||||
priv->state = USB_HOST_ENUM;
|
||||
ret = usbhost_enumerate(hport, &hport->devclass);
|
||||
|
||||
/* The enumeration may fail either because of some HCD interfaces failure
|
||||
* or because the device class is not supported. In either case, we just
|
||||
* need to perform the disconnection operation and make ready for a new
|
||||
* enumeration.
|
||||
*/
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Return to the disconnected state */
|
||||
|
||||
uerr("ERROR: Enumeration failed: %d\n", ret);
|
||||
hport->connected = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_ep0configure
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_ep0configure(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep0, uint8_t funcaddr,
|
||||
uint8_t speed, uint16_t maxpacketsize)
|
||||
{
|
||||
struct sim_usbhost_s *priv = &g_sim_usbhost;
|
||||
struct sim_epinfo_s *epinfo = (struct sim_epinfo_s *)ep0;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(drvr != NULL && epinfo != NULL);
|
||||
|
||||
ret = nxmutex_lock(&priv->lock);
|
||||
if (ret >= 0)
|
||||
{
|
||||
/* Remember the new device address and max packet size */
|
||||
|
||||
epinfo->devaddr = funcaddr;
|
||||
epinfo->speed = speed;
|
||||
epinfo->maxpacket = maxpacketsize;
|
||||
|
||||
nxmutex_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_epalloc
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_epalloc(FAR struct usbhost_driver_s *drvr,
|
||||
FAR const struct usbhost_epdesc_s *epdesc,
|
||||
FAR usbhost_ep_t *ep)
|
||||
{
|
||||
struct sim_epinfo_s *epinfo;
|
||||
struct usbhost_hubport_s *hport;
|
||||
|
||||
/* Sanity check. NOTE that this method should only be called if a device
|
||||
* is connected (because we need a valid low speed indication).
|
||||
*/
|
||||
|
||||
DEBUGASSERT(drvr != 0 && epdesc != NULL && epdesc->hport != NULL &&
|
||||
ep != NULL);
|
||||
hport = epdesc->hport;
|
||||
|
||||
uinfo("EP%d DIR=%s FA=%08x TYPE=%d Interval=%d MaxPacket=%d\n",
|
||||
epdesc->addr, epdesc->in ? "IN" : "OUT", hport->funcaddr,
|
||||
epdesc->xfrtype, epdesc->interval, epdesc->mxpacketsize);
|
||||
|
||||
/* Allocate a endpoint information structure */
|
||||
|
||||
epinfo = (struct sim_epinfo_s *)kmm_zalloc(sizeof(struct sim_epinfo_s));
|
||||
if (!epinfo)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Initialize the endpoint container (which is really just another form of
|
||||
* 'struct usbhost_epdesc_s', packed differently and with additional
|
||||
* information. A cleaner design might just embed struct usbhost_epdesc_s
|
||||
* inside of struct sim_epinfo_s and just memcpy here.
|
||||
*/
|
||||
|
||||
epinfo->epno = epdesc->addr;
|
||||
epinfo->dirin = epdesc->in;
|
||||
epinfo->devaddr = hport->funcaddr;
|
||||
epinfo->interval = epdesc->interval;
|
||||
epinfo->maxpacket = epdesc->mxpacketsize;
|
||||
epinfo->xfrtype = epdesc->xfrtype;
|
||||
epinfo->speed = hport->speed;
|
||||
nxsem_init(&epinfo->iocsem, 0, 0);
|
||||
|
||||
/* Success.. return an opaque reference to the endpoint information
|
||||
* structure instance
|
||||
*/
|
||||
|
||||
*ep = (usbhost_ep_t)epinfo;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_epfree
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_epfree(FAR struct usbhost_driver_s *drvr,
|
||||
FAR usbhost_ep_t ep)
|
||||
{
|
||||
struct sim_epinfo_s *epinfo = (struct sim_epinfo_s *)ep;
|
||||
|
||||
/* There should not be any pending, transfers */
|
||||
|
||||
DEBUGASSERT(drvr && epinfo);
|
||||
|
||||
/* Free the container */
|
||||
|
||||
kmm_free(epinfo);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_alloc
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_alloc(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t **buffer, FAR size_t *maxlen)
|
||||
{
|
||||
DEBUGASSERT(drvr && buffer && maxlen);
|
||||
|
||||
*buffer = (uint8_t *)kmm_malloc(SIM_USBHOST_BUFSIZE);
|
||||
if (*buffer)
|
||||
{
|
||||
*maxlen = SIM_USBHOST_BUFSIZE;
|
||||
return OK;
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_free
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_free(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t *buffer)
|
||||
{
|
||||
DEBUGASSERT(drvr && buffer);
|
||||
|
||||
kmm_free(buffer);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_ioalloc
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_ioalloc(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t **buffer, size_t buflen)
|
||||
{
|
||||
DEBUGASSERT(drvr && buffer && buflen > 0);
|
||||
|
||||
*buffer = (uint8_t *)kumm_malloc(buflen);
|
||||
return *buffer ? OK : -ENOMEM;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_iofree
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_iofree(FAR struct usbhost_driver_s *drvr,
|
||||
FAR uint8_t *buffer)
|
||||
{
|
||||
DEBUGASSERT(drvr && buffer);
|
||||
|
||||
kumm_free(buffer);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_ctrlin
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_ctrlin(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep0,
|
||||
FAR const struct usb_ctrlreq_s *req,
|
||||
FAR uint8_t *buffer)
|
||||
{
|
||||
struct sim_usbhost_s *priv = (struct sim_usbhost_s *)drvr;
|
||||
struct sim_epinfo_s *ep0info = (struct sim_epinfo_s *)ep0;
|
||||
struct host_usb_ctrlreq_s hostreq;
|
||||
struct host_usb_datareq_s *datareq;
|
||||
uint16_t len;
|
||||
ssize_t nbytes;
|
||||
sem_t iocsem;
|
||||
|
||||
DEBUGASSERT(ep0info != NULL && req != NULL);
|
||||
|
||||
datareq = sim_usbhost_allocrq();
|
||||
if (!datareq)
|
||||
{
|
||||
uerr("sim_usbhost_allocrq fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
len = GETUINT16(req->len);
|
||||
|
||||
/* Terse output only if we are tracing */
|
||||
|
||||
uinfo("type: %02x req: %02x value: %02x%02x index: %02x%02x "
|
||||
"len: %04x\n",
|
||||
req->type, req->req, req->value[1], req->value[0],
|
||||
req->index[1], req->index[0], len);
|
||||
|
||||
sim_usbhost_getctrlreq(&hostreq, req);
|
||||
|
||||
nxsem_init(&iocsem, 0, 0);
|
||||
|
||||
datareq->addr = 0;
|
||||
datareq->data = buffer;
|
||||
datareq->len = len;
|
||||
datareq->priv = &iocsem;
|
||||
|
||||
/* We must have exclusive access to the data structures. */
|
||||
|
||||
nxmutex_lock(&priv->lock);
|
||||
|
||||
nbytes = host_usbhost_ep0trans(&hostreq, datareq);
|
||||
|
||||
nxmutex_unlock(&priv->lock);
|
||||
|
||||
/* Wait for transfer completion */
|
||||
|
||||
nxsem_wait(&iocsem);
|
||||
|
||||
nbytes = datareq->xfer;
|
||||
|
||||
sim_usbhost_freerq(datareq);
|
||||
|
||||
return (int)nbytes;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_ctrlout
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_ctrlout(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep0,
|
||||
FAR const struct usb_ctrlreq_s *req,
|
||||
FAR const uint8_t *buffer)
|
||||
{
|
||||
return sim_usbhost_ctrlin(drvr, ep0, req, (uint8_t *)buffer);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_transfer
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t sim_usbhost_transfer(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep, FAR uint8_t *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
struct sim_usbhost_s *priv = (struct sim_usbhost_s *)drvr;
|
||||
struct sim_epinfo_s *epinfo = (struct sim_epinfo_s *)ep;
|
||||
struct host_usb_datareq_s *datareq;
|
||||
sem_t iocsem;
|
||||
int nbytes;
|
||||
|
||||
DEBUGASSERT(epinfo && buffer && buflen > 0);
|
||||
|
||||
datareq = sim_usbhost_allocrq();
|
||||
if (!datareq)
|
||||
{
|
||||
uerr("sim_usbhost_allocrq fail\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nbytes = MIN(buflen, epinfo->maxpacket);
|
||||
|
||||
nxsem_init(&iocsem, 0, 0);
|
||||
|
||||
datareq->addr = (epinfo->dirin << 7) + epinfo->epno;
|
||||
datareq->xfrtype = epinfo->xfrtype;
|
||||
datareq->data = buffer;
|
||||
datareq->len = nbytes;
|
||||
datareq->priv = &iocsem;
|
||||
|
||||
/* We must have exclusive access to and data structures. */
|
||||
|
||||
nxmutex_lock(&priv->lock);
|
||||
|
||||
nbytes = host_usbhost_eptrans(datareq);
|
||||
|
||||
nxmutex_unlock(&priv->lock);
|
||||
|
||||
/* Wait for transfer completion */
|
||||
|
||||
nxsem_wait(&iocsem);
|
||||
|
||||
nbytes = datareq->xfer;
|
||||
|
||||
sim_usbhost_freerq(datareq);
|
||||
|
||||
return (ssize_t)nbytes;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_cancel
|
||||
****************************************************************************/
|
||||
|
||||
static int sim_usbhost_cancel(FAR struct usbhost_driver_s *drvr,
|
||||
usbhost_ep_t ep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_disconnect
|
||||
****************************************************************************/
|
||||
|
||||
static void sim_usbhost_disconnect(FAR struct usbhost_driver_s *drvr,
|
||||
FAR struct usbhost_hubport_s *hport)
|
||||
{
|
||||
DEBUGASSERT(hport != NULL);
|
||||
hport->devclass = NULL;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_rqcomplete
|
||||
****************************************************************************/
|
||||
|
||||
static void sim_usbhost_rqcomplete(FAR struct sim_usbhost_s *drvr)
|
||||
{
|
||||
struct host_usb_datareq_s *datareq;
|
||||
|
||||
while ((datareq = host_usbhost_getcomplete()) != NULL)
|
||||
{
|
||||
sem_t *iocsem = datareq->priv;
|
||||
nxsem_post(iocsem);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_initialize
|
||||
*
|
||||
* Description:
|
||||
* Initialize USB host device controller hardware.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int sim_usbhost_initialize(void)
|
||||
{
|
||||
struct sim_usbhost_s *priv = &g_sim_usbhost;
|
||||
struct usbhost_hubport_s *hport;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_USBHOST_CDCACM
|
||||
ret = usbhost_cdcacm_initialize();
|
||||
#endif
|
||||
|
||||
/* Initialize the device operations */
|
||||
|
||||
priv->drvr.ep0configure = sim_usbhost_ep0configure;
|
||||
priv->drvr.epalloc = sim_usbhost_epalloc;
|
||||
priv->drvr.epfree = sim_usbhost_epfree;
|
||||
priv->drvr.alloc = sim_usbhost_alloc;
|
||||
priv->drvr.free = sim_usbhost_free;
|
||||
priv->drvr.ioalloc = sim_usbhost_ioalloc;
|
||||
priv->drvr.iofree = sim_usbhost_iofree;
|
||||
priv->drvr.ctrlin = sim_usbhost_ctrlin;
|
||||
priv->drvr.ctrlout = sim_usbhost_ctrlout;
|
||||
priv->drvr.transfer = sim_usbhost_transfer;
|
||||
priv->drvr.cancel = sim_usbhost_cancel;
|
||||
priv->drvr.disconnect = sim_usbhost_disconnect;
|
||||
|
||||
/* Initialize the public port representation */
|
||||
|
||||
hport = &priv->hport.hport;
|
||||
hport->drvr = &priv->drvr;
|
||||
hport->ep0 = &priv->ep0;
|
||||
hport->port = 0;
|
||||
hport->speed = USB_SPEED_HIGH;
|
||||
|
||||
/* Initialize function address generation logic */
|
||||
|
||||
usbhost_devaddr_initialize(&priv->hport);
|
||||
|
||||
/* Initialize host usb controller */
|
||||
|
||||
host_usbhost_init();
|
||||
|
||||
/* Initialize the driver state data */
|
||||
|
||||
priv->state = USB_HOST_DETACHED;
|
||||
|
||||
ret = kthread_create("usbhost monitor", CONFIG_SIM_USB_PRIO,
|
||||
CONFIG_SIM_USB_STACKSIZE,
|
||||
sim_usbhost_waittask, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
uerr("ERROR: Failed to create sim_usbhost_waittask: %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: sim_usbhost_loop
|
||||
*
|
||||
* Description:
|
||||
* USB host loop process.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int sim_usbhost_loop(void)
|
||||
{
|
||||
struct sim_usbhost_s *priv = &g_sim_usbhost;
|
||||
struct usbhost_hubport_s *hport;
|
||||
bool connect;
|
||||
|
||||
/* Handle root hub status change on each root port */
|
||||
|
||||
connect = host_usbhost_getconnstate();
|
||||
|
||||
/* Check current connect status */
|
||||
|
||||
if (connect)
|
||||
{
|
||||
/* Connected ... Did we just become connected? */
|
||||
|
||||
if (!priv->connected)
|
||||
{
|
||||
host_usbhost_open();
|
||||
|
||||
/* Yes.. connected. */
|
||||
|
||||
priv->connected = true;
|
||||
|
||||
/* Notify any waiters */
|
||||
|
||||
nxsem_post(&priv->pscsem);
|
||||
priv->pscwait = false;
|
||||
}
|
||||
|
||||
sim_usbhost_rqcomplete(priv);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Disconnected... Did we just become disconnected? */
|
||||
|
||||
if (priv->connected)
|
||||
{
|
||||
sim_usbhost_rqcomplete(priv);
|
||||
|
||||
host_usbhost_close();
|
||||
|
||||
/* Yes.. disconnect the device */
|
||||
|
||||
priv->connected = false;
|
||||
|
||||
/* Are we bound to a class instance? */
|
||||
|
||||
hport = &priv->hport.hport;
|
||||
if (hport->devclass)
|
||||
{
|
||||
/* Yes.. Disconnect the class */
|
||||
|
||||
CLASS_DISCONNECTED(hport->devclass);
|
||||
hport->devclass = NULL;
|
||||
}
|
||||
|
||||
/* Notify any waiters for the Root Hub Status change
|
||||
* event.
|
||||
*/
|
||||
|
||||
nxsem_post(&priv->pscsem);
|
||||
priv->pscwait = false;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
93
arch/sim/src/sim/sim_usbhost.h
Normal file
93
arch/sim/src/sim/sim_usbhost.h
Normal file
@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
* arch/sim/src/sim/sim_usbhost.h
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ARCH_SIM_SRC_SIM_USB_HOST_H
|
||||
#define __ARCH_SIM_SRC_SIM_USB_HOST_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __SIM__
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/* NuttX Endpoint descriptor */
|
||||
|
||||
struct host_usb_epdesc_s
|
||||
{
|
||||
uint8_t len; /* Descriptor length */
|
||||
uint8_t type; /* Descriptor type */
|
||||
uint8_t addr; /* Endpoint address */
|
||||
uint8_t attr; /* Endpoint attributes */
|
||||
uint16_t mxpacketsize; /* Maximum packet size */
|
||||
uint8_t interval; /* Interval */
|
||||
};
|
||||
|
||||
/* This structure is used to send control requests to a USB device. */
|
||||
|
||||
struct host_usb_ctrlreq_s
|
||||
{
|
||||
uint8_t type; /* Matches request type */
|
||||
uint8_t req; /* Matches request field */
|
||||
uint16_t value;
|
||||
uint16_t index;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct host_usb_datareq_s
|
||||
{
|
||||
struct host_usb_datareq_s *flink;
|
||||
uint8_t addr;
|
||||
uint8_t xfrtype;
|
||||
uint8_t *data;
|
||||
uint16_t len;
|
||||
uint16_t xfer;
|
||||
bool success;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
/* Host USB host Interface */
|
||||
|
||||
int host_usbhost_init(void);
|
||||
bool host_usbhost_getconnstate(void);
|
||||
int host_usbhost_open(void);
|
||||
void host_usbhost_close(void);
|
||||
int host_usbhost_ep0trans(struct host_usb_ctrlreq_s *ctrlreq,
|
||||
struct host_usb_datareq_s *datareq);
|
||||
int host_usbhost_eptrans(struct host_usb_datareq_s *datareq);
|
||||
struct host_usb_datareq_s *host_usbhost_getcomplete(void);
|
||||
|
||||
#endif /* __ARCH_SIM_SRC_SIM_USB_HOST_H */
|
@ -1549,3 +1549,29 @@ usbdev
|
||||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||
|
||||
Then you can test the network connection using the ping command or telnet.
|
||||
|
||||
usbhost
|
||||
|
||||
This is a configuration with sim usbhost support.
|
||||
|
||||
1. Libusb1.0 setup
|
||||
|
||||
$ sudo apt-get -y install libusb-1.0-0-dev
|
||||
$ sudo apt-get -y install libusb-1.0-0-dev:i386
|
||||
|
||||
2. Configuration
|
||||
|
||||
sim:usbhost support cdcacm.
|
||||
|
||||
You can use the sim:usbdev configuration:
|
||||
$ ./tools/configure.sh sim:usbhost
|
||||
|
||||
Configure the device you want to connet:
|
||||
|
||||
CONFIG_SIM_USB_PID=0x0042
|
||||
CONFIG_SIM_USB_VID=0x1630
|
||||
|
||||
3. How to run
|
||||
|
||||
Run sim usbhost with root mode, run sim usbdev or plug-in cdcacm usb device.
|
||||
Then you can use /dev/ttyACM to transfer data.
|
57
boards/sim/sim/sim/configs/usbhost/defconfig
Normal file
57
boards/sim/sim/sim/configs/usbhost/defconfig
Normal file
@ -0,0 +1,57 @@
|
||||
#
|
||||
# This file is autogenerated: PLEASE DO NOT EDIT IT.
|
||||
#
|
||||
# You can use "make menuconfig" to make any modifications to the installed .config file.
|
||||
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
|
||||
# modifications.
|
||||
#
|
||||
# CONFIG_SIM_UART_DMA is not set
|
||||
CONFIG_ARCH="sim"
|
||||
CONFIG_ARCH_BOARD="sim"
|
||||
CONFIG_ARCH_BOARD_SIM=y
|
||||
CONFIG_ARCH_CHIP="sim"
|
||||
CONFIG_ARCH_SIM=y
|
||||
CONFIG_BOARDCTL_POWEROFF=y
|
||||
CONFIG_BUILTIN=y
|
||||
CONFIG_DEBUG_ERROR=y
|
||||
CONFIG_DEBUG_FEATURES=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_SCHED=y
|
||||
CONFIG_DEBUG_SCHED_ERROR=y
|
||||
CONFIG_DEBUG_SCHED_WARN=y
|
||||
CONFIG_DEBUG_SYMBOLS=y
|
||||
CONFIG_DEBUG_USB=y
|
||||
CONFIG_DEBUG_USB_ERROR=y
|
||||
CONFIG_DEBUG_USB_INFO=y
|
||||
CONFIG_DEBUG_USB_WARN=y
|
||||
CONFIG_DEBUG_WARN=y
|
||||
CONFIG_FS_PROCFS=y
|
||||
CONFIG_FS_TMPFS=y
|
||||
CONFIG_INIT_ENTRYPOINT="nsh_main"
|
||||
CONFIG_LIBC_DLFCN=y
|
||||
CONFIG_LIBC_EXECFUNCS=y
|
||||
CONFIG_NSH_ARCHINIT=y
|
||||
CONFIG_NSH_BUILTIN_APPS=y
|
||||
CONFIG_NSH_READLINE=y
|
||||
CONFIG_PSEUDOFS_SOFTLINKS=y
|
||||
CONFIG_PSEUDOTERM=y
|
||||
CONFIG_READLINE_CMD_HISTORY=y
|
||||
CONFIG_READLINE_TABCOMPLETION=y
|
||||
CONFIG_SCHED_CHILD_STATUS=y
|
||||
CONFIG_SCHED_HAVE_PARENT=y
|
||||
CONFIG_SCHED_LPNTHREADS=2
|
||||
CONFIG_SCHED_LPWORK=y
|
||||
CONFIG_SCHED_WAITPID=y
|
||||
CONFIG_SIM_LIBUSB=y
|
||||
CONFIG_SIM_USB_HOST=y
|
||||
CONFIG_SIM_USB_PID=0x0042
|
||||
CONFIG_SIM_USB_STACKSIZE=2048
|
||||
CONFIG_SIM_USB_VID=0x1630
|
||||
CONFIG_SYSLOG_CHARDEV=y
|
||||
CONFIG_SYSLOG_MAX_CHANNELS=2
|
||||
CONFIG_SYSTEM_CLE=y
|
||||
CONFIG_SYSTEM_NSH=y
|
||||
CONFIG_TESTING_OSTEST=y
|
||||
CONFIG_TLS_TASK_NELEM=4
|
||||
CONFIG_USBHOST_CDCACM=y
|
||||
CONFIG_USBHOST_COMPOSITE=y
|
@ -521,6 +521,15 @@ static const char *g_white_list[] =
|
||||
"wMaxPacketSize",
|
||||
"bInterval",
|
||||
|
||||
/* Ref:
|
||||
* sim/posix/sim_libusb.c
|
||||
*/
|
||||
|
||||
"bNumConfigurations",
|
||||
"bDeviceClass",
|
||||
"idVendor",
|
||||
"idProduct",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user