CARPLAY版本整理

This commit is contained in:
2025-01-21 16:49:37 +08:00
commit f0fb64e4e6
26542 changed files with 13719676 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
#include "usb_os_adapter.h"
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
* @buf: Buffer to be filled
* @buflen: Size of buf
* @src: Array of descriptor pointers, terminated by null pointer.
*
* Copies descriptors into the buffer, returning the length or a
* negative error code if they can't all be copied. Useful when
* assembling descriptors for an associated set of interfaces used
* as part of configuring a composite device; or in other cases where
* sets of descriptors need to be marshaled.
*/
int
usb_descriptor_fillbuf(void *buf, unsigned buflen,
const struct usb_descriptor_header **src)
{
u8 *dest = buf;
if (!src)
return -EINVAL;
/* fill buffer from src[] until null descriptor ptr */
for (; NULL != *src; src++) {
unsigned len = (*src)->bLength;
if (len > buflen)
return -EINVAL;
memcpy(dest, *src, len);
buflen -= len;
dest += len;
}
return dest - (u8 *)buf;
}
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
* @config: Header for the descriptor, including characteristics such
* as power requirements and number of interfaces.
* @desc: Null-terminated vector of pointers to the descriptors (interface,
* endpoint, etc) defining all functions in this device configuration.
* @buf: Buffer for the resulting configuration descriptor.
* @length: Length of buffer. If this is not big enough to hold the
* entire configuration descriptor, an error code will be returned.
*
* This copies descriptors into the response buffer, building a descriptor
* for that configuration. It returns the buffer length or a negative
* status code. The config.wTotalLength field is set to match the length
* of the result, but other descriptor fields (including power usage and
* interface count) must be set by the caller.
*
* Gadget drivers could use this when constructing a config descriptor
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
*/
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void *buf,
unsigned length,
const struct usb_descriptor_header **desc
)
{
struct usb_config_descriptor *cp = buf;
int len;
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
/* config need not be aligned */
memcpy(cp, config, sizeof(*cp));
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
length - USB_DT_CONFIG_SIZE, desc);
if (len < 0)
return len;
len += USB_DT_CONFIG_SIZE;
if (len > 0xffff)
return -EINVAL;
/* patch up the config descriptor */
cp->bLength = USB_DT_CONFIG_SIZE;
cp->bDescriptorType = USB_DT_CONFIG;
put_unaligned_le16(len, &cp->wTotalLength);
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}

View File

@ -0,0 +1,236 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
*
* Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#include "usb_os_adapter.h"
#include <linux/usb/ch9.h>
#include <linux/errno.h>
#include <linux/usb/gadget.h>
#include <stdlib.h>
#define isdigit(c) ('0' <= (c) && (c) <= '9')
/* we must assign addresses for configurable endpoints (like net2280) */
static unsigned epnum;
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
*
* - ep1, ep2, ... address is fixed, not direction or type
* - ep1in, ep2out, ... address and direction are fixed, not type
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
* - ep1in-bulk, ep2out-iso, ... all three are fixed
* - ep-* ... no functionality restrictions
*
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
* Less common restrictions are implied by gadget_is_*().
*
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
static int ep_matches(
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc
)
{
u8 type;
const char *tmp;
u16 max;
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
if ('-' != ep->name[2]) {
tmp = strrchr(ep->name, '-');
if (tmp) {
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* bulk endpoints handle interrupt transfers,
* except the toggle-quirky iso-synch kind
*/
if ('s' == tmp[2]) /* == "-iso" */
return 0;
break;
case USB_ENDPOINT_XFER_BULK:
if ('b' != tmp[1]) /* != "-bulk" */
return 0;
break;
case USB_ENDPOINT_XFER_ISOC:
if ('s' != tmp[2]) /* != "-iso" */
return 0;
}
} else {
tmp = ep->name + strlen(ep->name);
}
/* direction-restriction: "..in-..", "out-.." */
tmp--;
if (!isdigit(*tmp)) {
if (desc->bEndpointAddress & USB_DIR_IN) {
if ('n' != *tmp)
return 0;
} else {
if ('t' != *tmp)
return 0;
}
}
}
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high speed */
if (!gadget->is_dualspeed && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high speed */
if (ep->maxpacket < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((get_unaligned(&desc->wMaxPacketSize) &
cpu_to_le16(3<<11))) {
if (!gadget->is_dualspeed)
return 0;
/* configure your hardware with enough buffering!! */
}
break;
}
/* MATCH!! */
/* report address */
if (isdigit(ep->name[2])) {
u8 num = strtol(&ep->name[2], NULL, 10);
desc->bEndpointAddress |= num;
} else {
if (++epnum > 15)
return 0;
desc->bEndpointAddress |= epnum;
}
/* report (variable) full speed bulk maxpacket */
if (USB_ENDPOINT_XFER_BULK == type) {
int size = ep->maxpacket;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
//put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
desc->wMaxPacketSize = (uint16_t)size;
}
return 1;
}
#if 0
static struct usb_ep *
find_ep(struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
ListItem_t *pxListItem = NULL;
list_for_each_entry(pxListItem, ep, &gadget->ep_list) {
if (0 == strcmp(ep->name, name))
return ep;
}
return NULL;
}
#endif
/**
* usb_ep_autoconfig - choose an endpoint matching the descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on success.
*
* By choosing an endpoint to use with the specified descriptor, this
* routine simplifies writing gadget drivers that work with multiple
* USB device controllers. The endpoint would be passed later to
* usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration on your
* hardware. This code may not make the best choices about how to use the
* USB controller, and it can't know all the restrictions that may apply.
* Some combinations of driver and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed. To prevent
* the endpoint from being returned by a later autoconfig call, claim it
* by assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
struct usb_ep *ep = NULL;
ListItem_t *pxListItem = NULL;
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry(pxListItem, ep, &gadget->ep_list) {
if (ep_matches(gadget, ep, desc))
return ep;
}
/* Fail */
return NULL;
}
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
* @gadget: device for which autoconfig state will be reset
*
* Use this for devices where one configuration may need to assign
* endpoint resources very differently from the next one. It clears
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
void usb_ep_autoconfig_reset(struct usb_gadget *gadget)
{
struct usb_ep *ep;
ListItem_t *pxListItem = NULL;
list_for_each_entry(pxListItem, ep, &gadget->ep_list) {
ep->driver_data = NULL;
}
epnum = 0;
}

View File

@ -0,0 +1,971 @@
#include "usb_os_adapter.h"
#include "board.h"
#include <linux/usb/cdc.h>
#include "linux/usb/composite.h"
#include "linux/usb/ether.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "trace.h"
#include "list.h"
#include "task.h"
#include "ark_dwc2.h"
#if !USE_LWIP
#include "FreeRTOS_IP.h"
#include "FreeRTOS_IP_Private.h"
#include "NetworkBufferManagement.h"
#include "NetworkInterface.h"
#else
#include "queue.h"
#include "lwip/pbuf.h"
#include "ethernet.h"
struct netif *ncm_netif = NULL;
void ncm_net_set_intf(void* intf)
{
ncm_netif = (struct netif *)intf;
}
extern void ncm_ethernetif_input(void *h, struct pbuf* p);
#endif
#define UETH__VERSION "29-May-2008"
#define WORK_RX_MEMORY 0
struct eth_dev {
spinlock_t lock;
struct gether *port_usb;
struct usb_gadget *gadget;
spinlock_t req_lock;
List_t tx_reqs;
atomic_t tx_qlen;
#if !USE_LWIP
List_t rx_frames;
#else
struct pbuf rx_frames;
#endif
struct usb_request *out_req;
unsigned header_len;
bool zlp;
u8 host_mac[ETH_ALEN];
TaskHandle_t usb_ether_task;
QueueHandle_t usb_ether_event_queue;
int start;
int tx_err_count;
};
struct eth_event
{
int type;
void* priv;
};
#define USB_ETH_EVENT_USB_CONNECT 0
#define USB_ETH_EVENT_USB_DISCONNECT 1
#define USB_ETH_EVENT_USB_DATA_RX 2 // usb recv data
#define USB_ETH_EVENT_USB_DATA_TX 3 // usb data tx ok notify
#define USB_ETH_EVENT_NET_DATA_TX 4 // data from tcpip
/*-------------------------------------------------------------------------*/
#undef atomic_read
#define atomic_read(v) ((v)->counter)
#undef atomic_set
#define atomic_set(v,i) ((v)->counter = (i))
#undef atomic_inc
#define atomic_inc(v) ((v)->counter++)
#define RX_EXTRA 20 /* bytes guarding against rx overflows */
#define DEFAULT_QLEN 4/* double buffering by default */
static unsigned qmult = 2;
static struct eth_dev *the_dev;
/* for dual-speed hardware, use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget)
{
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
gadget->speed == USB_SPEED_SUPER))
return qmult * DEFAULT_QLEN;
else
return DEFAULT_QLEN * 8;
}
/*-------------------------------------------------------------------------*/
#undef DBG
#undef VDBG
#undef ERROR
#undef INFO
#define xprintk(d, level, fmt, args...) \
printk(level "%s: " fmt , (d)->net->name , ## args)
#ifdef DEBUG
#undef DEBUG
#define DBG(dev, fmt, args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev, fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE_DEBUG
#define VDBG DBG
#else
#define VDBG(dev, fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#define ERROR(dev, fmt, args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define INFO(dev, fmt, args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
static void ether_disconnect(struct gether *link);
static int ether_connect(struct eth_dev *dev, struct gether *link);
static int rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags);
static void rx_complete(struct usb_ep *ep, struct usb_request *req);
int ncm_get_mac_address(char mac[6])
{
if (the_dev)
memcpy(mac, the_dev->host_mac, 6);
return 0;
}
static int prealloc(List_t *list, struct usb_ep *ep, unsigned n)
{
unsigned i;
struct usb_request *req;
ListItem_t *pxListItem = NULL;
if (!n)
return -ENOMEM;
i = n;
list_for_each_entry(pxListItem, req, list) {
if (i-- == 0) {
printf("prealloc out!\r\n");
goto extra;
}
}
while (i--) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (!req)
return list_empty(list) ? -ENOMEM : 0;
vListInitialiseItem(&req->list);
listSET_LIST_ITEM_OWNER(&(req->list), req);
printf("req:%x is alloc\n", req);
req->buf = NULL;
req->length = 0;
list_add_tail(&req->list, list);
}
return 0;
extra:
/* free extras */
for (;;) {
ListItem_t *next;
next = listGET_NEXT(&req->list);
uxListRemove(&req->list);
usb_ep_free_request(ep, req);
if (next == listGET_END_MARKER(list))
break;
req = listGET_LIST_ITEM_OWNER(&req->list);
}
return 0;
}
static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
{
int status;
spin_lock(&dev->req_lock);
status = prealloc(&dev->tx_reqs, link->in_ep, n);
if (status < 0)
goto fail;
/*status = prealloc(&dev->rx_reqs, link->out_ep, n);
if (status < 0)
goto fail;*/
goto done;
fail:
DBG(dev, "can't alloc requests\n");
done:
spin_unlock(&dev->req_lock);
return status;
}
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
//struct usb_request *req;
//unsigned long flags;
/* fill unused rxq slots with some skb */
/*spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) {
req = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->rx_reqs));
list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags);
rx_submit(dev, req, 0);
spin_lock_irqsave(&dev->req_lock, flags);
}
spin_unlock_irqrestore(&dev->req_lock, flags);*/
rx_submit(dev, dev->out_req, 0);
}
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct eth_dev *dev = ep->driver_data;
struct eth_event ev;
ev.type = USB_ETH_EVENT_USB_DATA_TX;
ev.priv = (void*)req;
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct eth_event ev;
struct eth_dev *dev = ep->driver_data;
if (req)
//printf("%s:%d req->actual:%d\r\n", __func__, __LINE__, req->actual);
ev.type = USB_ETH_EVENT_USB_DATA_RX;
ev.priv = (void*)req;
if (!xPortIsInInterrupt())
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
else
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
static inline int is_promisc(u16 cdc_filter)
{
return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
}
static int rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
{
int retval = -ENOMEM;
size_t size = 0;
struct usb_ep *out;
unsigned long flags;
void *buf = NULL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb)
out = dev->port_usb->out_ep;
else
out = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
if (!out)
return -ENOTCONN;
size += ETH_ALEN + ipconfigNETWORK_MTU + RX_EXTRA;
size += dev->port_usb->header_len;
if (out->maxpacket <= 0) {
printf("maxpacket err\r\n");
}
size += out->maxpacket - 1;
size -= size % out->maxpacket;
if (dev->port_usb->is_fixed)
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
buf = req->buf;
if (NULL == req->buf || req->length < size) {
if (NULL != req->buf)
vPortFree(req->buf);
buf = pvPortMalloc(size);
if (buf == NULL) {
return -ENOMEM;
}
}
//memset(req->buf, 0, req->length);
req->buf = buf;
req->length = size;
req->complete = rx_complete;
req->context = NULL;
retval = usb_ep_queue(out, req, 0);
if (retval) {
printf("rx submit failed--> %d\r\n", retval);
/*spin_lock_irqsave(&dev->req_lock, flags);
list_add_tail(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);*/
}
return retval;
}
int usb_data_rx_proc(struct eth_dev* dev, struct usb_request *req)
{
int ret = -1;
if (!dev->port_usb->connected) {
printf("%s:%d usb is disconnected\r\n", __func__, __LINE__);
return -1;
}
if (req->status != 0) {
usb_ep_queue(dev->port_usb->out_ep, req, 0);
return -1;
}
if (dev->port_usb && dev->port_usb->unwrap) {
#if !USE_LWIP
dev->port_usb->unwrap(dev->port_usb, req->buf, req->actual, &dev->rx_frames);
#else
dev->port_usb->unwrap(dev->port_usb, req->buf, req->actual, (List_t *)&dev->rx_frames);
#endif
}
if (dev->port_usb->connected) {
#if !USE_LWIP
NetworkBufferDescriptor_t* pxBufferDescriptor = NULL;
IPStackEvent_t xRxEvent;
ListItem_t *pxListItem, *pxListItem1;
list_for_each_entry_safe(pxListItem, pxListItem1, pxBufferDescriptor, &dev->rx_frames) {
list_del_init(&pxBufferDescriptor->xBufferListItem);
xRxEvent.eEventType = eNetworkRxEvent;
xRxEvent.pvData = ( void * ) pxBufferDescriptor;
if (0) {
int i, dump_len = pxBufferDescriptor->xDataLength;
printf("recv len:%d\r\n", dump_len);
for (i = 0; i < dump_len; i++) {
printf("%02x ", pxBufferDescriptor->pucEthernetBuffer[i]);
if ((i + 1) % 16 == 0)
printf("\r\n");
}printf("\r\n");
}
if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) {
vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
iptraceETHERNET_RX_EVENT_LOST();
} else {
iptraceNETWORK_INTERFACE_RECEIVE();
}
}
#else
if (dev->rx_frames.next) {
struct pbuf *header = dev->rx_frames.next;
while(header != NULL) {
struct pbuf *current = header;
header = header->next;
ncm_ethernetif_input(ncm_netif, current);
}
dev->rx_frames.next = NULL;
}
#endif
}
if (req) {
ret = rx_submit(dev, req, 0);
if (ret != 0) {
rx_fill(dev, 0);
}
}
return 0;
}
void usb_data_tx_proc(struct eth_dev* dev, struct usb_request *req)
{
if (1) {
#if !USE_LWIP
NetworkBufferDescriptor_t *pxBufferDescriptor = (NetworkBufferDescriptor_t *)req->context;
if (pxBufferDescriptor) {
if (pxBufferDescriptor->pucEthernetBuffer) {
vPortFree(pxBufferDescriptor->pucEthernetBuffer);
pxBufferDescriptor->pucEthernetBuffer = NULL;
}
vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
}
#else
struct pbuf* pxBufferDescriptor = (struct pbuf*)req->context;//the buf is alloc by ncm, it must be freed.
if (pxBufferDescriptor) {
pbuf_free(pxBufferDescriptor);
}
#endif
}
spin_lock(&dev->req_lock);
if (!dev->port_usb->connected) {
req->buf = NULL;
req->length = 0;
printf("%s #############.\n", __func__);
}
list_add_tail(&req->list, &dev->tx_reqs);
spin_unlock(&dev->req_lock);
}
static void free_net_buffer(void* desc_handle)
{
#if !USE_LWIP
NetworkBufferDescriptor_t *pxBufferDescriptor = (NetworkBufferDescriptor_t*)desc_handle;
if (NULL != pxBufferDescriptor)
vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
#else
struct pbuf* pxBufferDescriptor = (struct pbuf*)desc_handle;
if (pxBufferDescriptor) {
pbuf_free(pxBufferDescriptor);
}
#endif
}
int net_data_tx_proc(struct eth_dev* dev, void *pxBufferDescriptorHandle)
{
int length = 0;
int retval;
struct usb_request *req = NULL;
unsigned long flags;
struct usb_ep *in;
u16 cdc_filter;
#if !USE_LWIP
NetworkBufferDescriptor_t *pxBufferDescriptor = (NetworkBufferDescriptor_t*)pxBufferDescriptorHandle;
#else
struct pbuf* pxBufferDescriptor = (struct pbuf*)pxBufferDescriptorHandle;
#endif
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) {
in = dev->port_usb->in_ep;
cdc_filter = dev->port_usb->cdc_filter;
} else {
in = NULL;
cdc_filter = 0;
}
spin_unlock_irqrestore(&dev->lock, flags);
if (!in || !dev->port_usb->connected) {
free_net_buffer((void*)pxBufferDescriptor);
return 0;
}
/* apply outgoing CDC or RNDIS filters */
if (!is_promisc(cdc_filter)) {
#if !USE_LWIP
u8 *dest = pxBufferDescriptor->pucEthernetBuffer;
#else
u8 *dest = pxBufferDescriptor->payload;
#endif
if (is_multicast_ether_addr(dest)) {
u16 type;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
if (is_broadcast_ether_addr(dest))
type = USB_CDC_PACKET_TYPE_BROADCAST;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
if (!(cdc_filter & type)) {
free_net_buffer((void*)pxBufferDescriptor);
return 0;
}
}
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
}
spin_lock_irqsave(&dev->req_lock, flags);
/*
* this freelist can be empty if an interrupt triggered disconnect()
* and reconfigured the gadget (shutting down this queue) after the
* network stack decided to xmit but before we got the spinlock.
*/
if (list_empty(&dev->tx_reqs)) {
spin_unlock_irqrestore(&dev->req_lock, flags);
printf("tx reqs empty\r\n");
free_net_buffer((void*)pxBufferDescriptor);
if (dev->tx_err_count++ > 3) {
usb_dwc2_reset(0, 1);
dev->tx_err_count = 0;
}
return -1;
}
dev->tx_err_count = 0;
req = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->tx_reqs));
list_del(&req->list);
/* temporarily stop TX queue when the freelist empties */
if (list_empty(&dev->tx_reqs)) {
}
spin_unlock_irqrestore(&dev->req_lock, flags);
/* no buffer copies needed, unless the network stack did it
* or the hardware can't use skb buffers.
* or there's not enough space for extra headers we need
*/
#if !USE_LWIP
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb && dev->port_usb->wrap)
pxBufferDescriptor = dev->port_usb->wrap(dev->port_usb, pxBufferDescriptor);
spin_unlock_irqrestore(&dev->lock, flags);
if (!pxBufferDescriptor)
goto drop;
length = pxBufferDescriptor->xDataLength;
req->buf = pxBufferDescriptor->pucEthernetBuffer;
req->context = pxBufferDescriptor;
req->complete = tx_complete;
#else
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb && dev->port_usb->wrap_ext)
pxBufferDescriptor = (struct pbuf*)dev->port_usb->wrap_ext(dev->port_usb, (void*)pxBufferDescriptor);
spin_unlock_irqrestore(&dev->lock, flags);
if (!pxBufferDescriptor)
goto drop;
length = pxBufferDescriptor->len;
req->buf = pxBufferDescriptor->payload;
req->context = pxBufferDescriptor;
req->complete = tx_complete;
#endif
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
if (dev->port_usb->is_fixed &&
length == dev->port_usb->fixed_in_len &&
(length % in->maxpacket) == 0)
req->zero = 0;
else
req->zero = 1;
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding.
* and some hardware doesn't like to write zlps.
*/
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
length++;
req->length = length;
retval = usb_ep_queue(in, req, GFP_ATOMIC);
switch (retval) {
default:
DBG(dev, "tx queue err %d\n", retval);
break;
case 0:
atomic_inc(&dev->tx_qlen);
}
if (retval) {
#if !USE_LWIP
pxBufferDescriptor = (NetworkBufferDescriptor_t *)req->context;
if (pxBufferDescriptor) {
if (pxBufferDescriptor->pucEthernetBuffer) {
vPortFree(pxBufferDescriptor->pucEthernetBuffer);
pxBufferDescriptor->pucEthernetBuffer = NULL;
}
vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
}
#else
pxBufferDescriptor = (struct pbuf*)req->context;
if (pxBufferDescriptor) {
pbuf_free(pxBufferDescriptor);
}
#endif
drop:
spin_lock_irqsave(&dev->req_lock, flags);
list_add_tail(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
}
return 0;
}
static void usb_ether_task_proc(void* arg)
{
struct eth_event ev;
struct eth_dev *dev = (struct eth_dev *)arg;
while(dev->start) {
ev.type = -1;
if (xQueueReceive(dev->usb_ether_event_queue, &ev, portMAX_DELAY) != pdPASS) {
printf("%s xQueueReceive err!\r\n", __func__);
continue;
}
if (ev.type == -1)
continue;
switch(ev.type) {
case USB_ETH_EVENT_USB_DATA_RX:
if (NULL != ev.priv)
usb_data_rx_proc(dev, (struct usb_request *)ev.priv);
break;
case USB_ETH_EVENT_USB_DATA_TX:
if (NULL != ev.priv)
usb_data_tx_proc(dev, (struct usb_request *)ev.priv);
break;
case USB_ETH_EVENT_NET_DATA_TX:
if (NULL != ev.priv)
net_data_tx_proc(dev, ev.priv);
break;
case USB_ETH_EVENT_USB_DISCONNECT:
if (NULL != ev.priv)
ether_disconnect((struct gether *)ev.priv);
break;
case USB_ETH_EVENT_USB_CONNECT:
if (NULL != ev.priv)
ether_connect(dev, (struct gether *)ev.priv);
break;
default:
break;
}
}
vTaskDelete(NULL);
}
/**
* gether_setup - initialize one ethernet-over-usb link
* @g: gadget to associated with these links
* @ethaddr: NULL, or a buffer in which the ethernet address of the
* host side of the link is recorded
* Context: may sleep
*
* This sets up the single network link that may be exported by a
* gadget driver using this framework. The link layer addresses are
* set up using module parameters.
*
* Returns negative errno, or zero on success
*/
#if 0
UBaseType_t uxRand( void );
static void random_ether_addr(u8 *addr)
{
UBaseType_t val = uxRand();
if (NULL == addr)
return;
addr [0] = ((val >> 0) & 0xff);
addr [1] = ((val >> 8) & 0xff);
addr [2] = ((val >> 16) & 0xff);
addr [3] = ((val >> 24) & 0xff);
val = uxRand();
addr [4] = ((val >> 0) & 0xff);
addr [5] = ((val >> 8) & 0xff);
addr [0] &= 0xfe; /* clear multicast bit */
addr [0] |= 0x02; /* set local assignment bit (IEEE802) */
}
#endif
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
{
struct eth_dev *dev;
BaseType_t ret = pdFAIL;
if (the_dev)
return -EBUSY;
dev = pvPortMalloc(sizeof(struct eth_dev));
if (dev == NULL)
return -ENOMEM;
memset((void*)dev, 0, sizeof(struct eth_dev));
spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock);
INIT_LIST_HEAD(&dev->tx_reqs);
//INIT_LIST_HEAD(&dev->rx_reqs);
#if !USE_LWIP
INIT_LIST_HEAD(&dev->rx_frames);
#else
dev->rx_frames.next = NULL;
dev->rx_frames.len = 0;
dev->rx_frames.tot_len = 0;
#endif
#if 1
dev->host_mac[0] = 0x00;//00:0c:29:53:02:09
dev->host_mac[1] = 0x0c;
dev->host_mac[2] = 0x29;
dev->host_mac[3] = 0x53;
dev->host_mac[4] = 0x02;
dev->host_mac[5] = 0x09;
#else
random_ether_addr(dev->host_mac);
#endif
//FreeRTOS_UpdateMACAddress(dev->host_mac);
printf("\r\nncm mac: %02x %02x %02x %02x %02x %02x\r\n", dev->host_mac[0], dev->host_mac[1],
dev->host_mac[2], dev->host_mac[3], dev->host_mac[4], dev->host_mac[5]);
if (ethaddr)
memcpy(ethaddr, dev->host_mac, ETH_ALEN);
dev->usb_ether_event_queue = xQueueCreate(16, sizeof(struct eth_event));
if (NULL == dev->usb_ether_event_queue) {
goto exit;
}
dev->start = 1;
ret = xTaskCreate(usb_ether_task_proc, "usb_ether_task", 2048, (void*)dev, configMAX_PRIORITIES - 3, &dev->usb_ether_task);
if (ret != pdPASS) {
goto exit;
}
dev->gadget = g;
the_dev = dev;
return 0;
exit:
if (ret != pdPASS) {
if (dev) {
if (dev->usb_ether_event_queue) {
vQueueDelete(dev->usb_ether_event_queue);
}
vPortFree(dev);
}
}
return -1;
}
/**
* gether_cleanup - remove Ethernet-over-USB device
* Context: may sleep
*
* This is called to free all resources allocated by @gether_setup().
*/
void gether_cleanup(void)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (!dev)
return;
dev->start = 0;
if (dev->usb_ether_event_queue) {
ev.type = -1;
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
vQueueDelete(dev->usb_ether_event_queue);
}//maybe exist bug
vPortFree(dev);
the_dev = NULL;
}
static int ether_connect(struct eth_dev *dev, struct gether *link)
{
int result = 0;
if (!dev)
return -EINVAL;
dev->tx_err_count = 0;
if (link && link->connected)
return 0;
link->in_ep->driver_data = dev;
result = usb_ep_enable(link->in_ep, link->in);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->in_ep->name, result);
goto fail0;
}
link->out_ep->driver_data = dev;
result = usb_ep_enable(link->out_ep, link->out);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->out_ep->name, result);
goto fail1;
}
if (result == 0) {
result = alloc_requests(dev, link, qlen(dev->gadget));
dev->out_req = NULL;
dev->out_req = usb_ep_alloc_request(link->out_ep, GFP_ATOMIC);
if (!dev->out_req) {
printf("usb_ep_alloc_request %s --> %d\r\n",
link->out_ep->name, result);
goto fail1;
}
dev->out_req->buf = NULL;
dev->out_req->length = 0;
}
if (result == 0 && dev->out_req) {
dev->zlp = link->is_zlp_ok;
DBG(dev, "qlen %d\n", qlen(dev->gadget));
dev->header_len = link->header_len;
spin_lock(&dev->lock);
dev->port_usb = link;
link->ctx = (void *)dev;
spin_unlock(&dev->lock);
rx_fill(dev, 0);
atomic_set(&dev->tx_qlen, 0);
link->connected = true;
/* on error, disable any endpoints */
} else {
(void) usb_ep_disable(link->out_ep);
fail1:
(void) usb_ep_disable(link->in_ep);
}
fail0:
/* caller is responsible for cleanup on error */
if (result < 0)
return result;
return result;
}
/**
* gether_disconnect - notify network layer that USB link is inactive
* @link: the USB link, on which gether_connect() was called
* Context: irqs blocked
*
* This is called to deactivate endpoints and let the network layer know
* the connection went inactive ("no carrier").
*
* On return, the state is as if gether_connect() had never been called.
* The endpoints are inactive, and accordingly without active USB I/O.
* Pointers to endpoint descriptors and endpoint private data are nulled.
*/
static void ether_disconnect(struct gether *link)
{
struct eth_dev *dev = (struct eth_dev *)link->ctx;
struct usb_request *req;
if (!dev || !link->connected)
return;
dev->tx_err_count = 0;
link->connected = false;
printf("%s:%d start\r\n", __func__, __LINE__);
usb_ep_disable(link->in_ep);
spin_lock(&dev->req_lock);
while (!list_empty(&dev->tx_reqs)) {
req = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->tx_reqs));
list_del(&req->list);
spin_unlock(&dev->req_lock);
req->buf = NULL;
req->length = 0;
usb_ep_free_request(link->in_ep, req);
printf("req:%x is released at disconnect\n", req);
spin_lock(&dev->req_lock);
}
spin_unlock(&dev->req_lock);
link->in_ep->driver_data = NULL;
link->in_ep->desc = NULL;
usb_ep_disable(link->out_ep);
spin_lock(&dev->req_lock);
#if !USE_LWIP
while (!list_empty(&dev->rx_frames)) {
NetworkBufferDescriptor_t * pxNetworkBuffer = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->rx_frames));
list_del_init(&pxNetworkBuffer->xBufferListItem);
vReleaseNetworkBufferAndDescriptor(pxNetworkBuffer);
}
#else
if (dev->rx_frames.next) {
struct pbuf *header = dev->rx_frames.next;
while(header != NULL) {
struct pbuf *current = header;
header = header->next;
pbuf_free(current);
}
dev->rx_frames.next = NULL;
}
#endif
req = dev->out_req;
if (req && req->buf) {
vPortFree(req->buf);
req->buf = NULL;
req->length = 0;
}
usb_ep_free_request(link->out_ep, dev->out_req);
spin_unlock(&dev->req_lock);
link->out_ep->driver_data = NULL;
link->out_ep->desc = NULL;
/* finish forgetting about this USB link episode */
dev->header_len = 0;
spin_lock(&dev->lock);
dev->port_usb = NULL;
spin_unlock(&dev->lock);
if (link && link->disconnect_cb)
link->disconnect_cb(link);
printf("%s:%d end\r\n", __func__, __LINE__);
}
int gether_connect(struct gether *link)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_USB_CONNECT;
ev.priv = (void*)link;
if (!xPortIsInInterrupt())
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
else
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
return 0;
}
void gether_disconnect(struct gether *link)
{
struct eth_event ev;
struct eth_dev *dev = (struct eth_dev *)link->ctx;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_USB_DISCONNECT;
ev.priv = (void*)link;
if (!xPortIsInInterrupt())
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
else
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
}
void gether_send(NetworkBufferDescriptor_t * const pxDescriptor)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_NET_DATA_TX;
ev.priv = (void*)pxDescriptor;
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
} else {
#if !USE_LWIP
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
#endif
}
}
void gether_send_ext(void * const pxDescriptor)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_NET_DATA_TX;
ev.priv = (void*)pxDescriptor;
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
} else {
#if !USE_LWIP
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
#endif
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
/*
* ncm
*
*/
#include "usb_os_adapter.h"
#include <stdio.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include <linux/usb/ether.h>
#define STRING_MANUFACTURER 25
#define STRING_PRODUCT 2
#define STRING_USBDOWN 2
#define STRING_SERIAL 3
#define MAX_STRING_SERIAL 256
#define CONFIGURATION_NUMBER 1
#undef DEBUG
#define DRIVER_VERSION "usb_ncm"
#define CONFIG_USB_GADGET_MANUFACTURER "arkmicro"
#if (DEBUG)
#define debug(s, ...) printf("%s: " s "\n", "UsbNcm", ## __VA_ARGS__)
#else
#define debug(s, ...)
#endif
#define CONFIG_USB_GADGET_VENDOR_NUM 0x5001
#define CONFIG_USB_GADGET_PRODUCT_NUM 0x2003
static const char product[] = "USB ncm gadget";
static char g_ncm_serial[MAX_STRING_SERIAL];
static const char manufacturer[] = CONFIG_USB_GADGET_MANUFACTURER;
static uint8_t hostaddr[ETH_ALEN];
void g_ncm_set_serialnumber(char *s)
{
memset(g_ncm_serial, 0, MAX_STRING_SERIAL);
strncpy(g_ncm_serial, s, MAX_STRING_SERIAL - 1);
}
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/
.bDeviceProtocol = 0x00,
.idVendor = cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
.idProduct = cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
/* .iProduct = DYNAMIC */
/* .iSerialNumber = DYNAMIC */
.bNumConfigurations = 1,
};
/*
* static strings, in UTF-8
* IDs for those strings are assigned dynamically at g_ncm_bind()
*/
static struct usb_string g_ncm_string_defs[] = {
{.s = manufacturer},
{.s = product},
{.s = g_ncm_serial},
};
static struct usb_gadget_strings g_ncm_string_tab = {
.language = 0x0409, /* en-us */
.strings = g_ncm_string_defs,
};
static struct usb_gadget_strings *g_ncm_composite_strings[] = {
&g_ncm_string_tab,
NULL,
};
static int usb_gadget_controller_number(struct usb_gadget *gadget)
{
(void)gadget;
return 0x02;
}
static int g_ncm_unbind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
usb_gadget_disconnect(gadget);
return 0;
}
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
static int g_ncm_do_config(struct usb_configuration *c)
{
return ncm_bind_config(c, hostaddr);
}
static int g_ncm_config_register(struct usb_composite_dev *cdev)
{
struct usb_configuration *config;
const char *name = "usb_ncm";
config = kmalloc(sizeof(*config), 0);
if (!config)
return -ENOMEM;
memset(config, 0, sizeof(*config));
config->label = name;
config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
config->bConfigurationValue = CONFIGURATION_NUMBER;
config->iConfiguration = STRING_USBDOWN;
config->bind = g_ncm_do_config;
listSET_LIST_ITEM_OWNER(&config->list, config);
return usb_add_config(cdev, config);
}
static int g_ncm_get_bcd_device_number(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int gcnum;
gcnum = usb_gadget_controller_number(gadget);
if (gcnum > 0)
gcnum += 0x200;
return gcnum;
}
static int g_ncm_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int id, ret;
int gcnum;
ret = gether_setup(cdev->gadget, hostaddr);
debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
id = usb_string_id(cdev);
if (id < 0)
return id;
g_ncm_string_defs[0].id = id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
g_ncm_string_defs[1].id = id;
device_desc.iProduct = id;
if (strlen(g_ncm_serial)) {
id = usb_string_id(cdev);
if (id < 0)
return id;
g_ncm_string_defs[2].id = id;
device_desc.iSerialNumber = id;
}
ret = g_ncm_config_register(cdev);
if (ret)
goto error;
gcnum = g_ncm_get_bcd_device_number(cdev);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(gcnum);
else {
debug("%s: controller '%s' not recognized\n",
__func__, gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x9999);
}
debug("%s: calling usb_gadget_connect for "
"controller '%s'\n", __func__, gadget->name);
usb_gadget_connect(gadget);
return 0;
error:
g_ncm_unbind(cdev);
return -ENOMEM;
}
static struct usb_composite_driver g_ncm_driver = {
.name = NULL,
.dev = &device_desc,
.strings = g_ncm_composite_strings,
.bind = g_ncm_bind,
.unbind = g_ncm_unbind,
};
int g_ncm_register(const char *name)
{
int ret;
debug("%s: g_ncm_driver.name = %s\n", __func__, name);
g_ncm_driver.name = name;
ret = usb_composite_register(&g_ncm_driver);
if (ret) {
printf("%s: failed!, error: %d\n", __func__, ret);
return ret;
}
return 0;
}
void g_ncm_unregister(void)
{
usb_composite_unregister(&g_ncm_driver);
}

View File

@ -0,0 +1,333 @@
#include "usb_os_adapter.h"
#include "FreeRTOS_POSIX.h"
#include "pthread.h"
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "chip.h"
#include "dma-direction.h"
#include "dma-mapping.h"
/**
* struct usb_udc - describes one usb device controller
* @driver - the gadget driver pointer. For use by the class code
* @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
*/
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device dev;
ListItem_t list;
};
static List_t udc_list;
static SemaphoreHandle_t udc_lock;
static int g_udc_core_init_flag = 0;
int usb_udc_core_init()
{
if (g_udc_core_init_flag)
return 0;
g_udc_core_init_flag = 1;
udc_lock = xSemaphoreCreateMutex();
vListInitialise(&udc_list);
return 0;
}
int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return 0;
req->dma = dma_map_single(req->buf, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
return 0;
}
void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return;
dma_unmap_single((void *)req->dma, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
/* ------------------------------------------------------------------------- */
/**
* usb_gadget_giveback_request - give the request back to the gadget layer
* Context: in_interrupt()
*
* This is called by device controller drivers in order to return the
* completed request back to the gadget layer.
*/
void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req)
{
req->complete(ep, req);
}
/* ------------------------------------------------------------------------- */
void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state)
{
gadget->state = state;
}
/* ------------------------------------------------------------------------- */
/**
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which bus reset occurs
* @driver: The gadget driver we want to notify
*
* If the udc driver has bus reset handler, it needs to call this when the bus
* reset occurs, it notifies the gadget driver that the bus reset occurs as
* well as updates gadget state.
*/
void usb_gadget_udc_reset(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
driver->reset(gadget);
usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
}
/**
* usb_gadget_udc_start - tells usb device controller to start up
* @udc: The UDC to be started
*
* This call is issued by the UDC Class driver when it's about
* to register a gadget driver to the device controller, before
* calling gadget driver's bind() method.
*
* It allows the controller to be powered off until strictly
* necessary to have it powered on.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_udc_start(struct usb_udc *udc)
{
return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
}
/**
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
* @gadget: The device we want to stop activity
* @driver: The driver to unbind from @gadget
*
* This call is issued by the UDC Class driver after calling
* gadget driver's unbind() method.
*
* The details are implementation specific, but it can go as
* far as powering off UDC completely and disable its data
* line pullups.
*/
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
{
udc->gadget->ops->udc_stop(udc->gadget);
}
#if 0
/**
* usb_udc_release - release the usb_udc struct
* @dev: the dev member within usb_udc
*
* This is called by driver's core in order to free memory once the last
* reference is released.
*/
static void usb_udc_release(struct device *dev)
{
//struct usb_udc *udc;
(void)dev;
//udc = container_of(dev, struct usb_udc, dev);
//kfree(udc);
}
#endif
/**
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller driver's
* device.
* @gadget: the gadget to be added to the list.
* @release: a gadget release function.
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
struct usb_udc *udc;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
goto err1;
udc->gadget = gadget;
INIT_LIST_ITEM(&udc->list);
listSET_LIST_ITEM_OWNER(&udc->list, udc);
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_add_tail(&udc->list, &udc_list);
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
xSemaphoreGive(udc_lock);
return 0;
err1:
return ret;
}
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
* driver's device.
* @gadget: the gadget to be added to the list
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
{
usb_udc_core_init();
return usb_add_gadget_udc_release(parent, gadget, NULL);
}
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
udc->driver->function);
usb_gadget_disconnect(udc->gadget);
udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc);
udc->driver = NULL;
}
/**
* usb_del_gadget_udc - deletes @udc from udc_list
* @gadget: the gadget to be removed.
*
* This, will call usb_gadget_unregister_driver() if
* the @udc is still busy.
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
{
struct usb_udc *udc = NULL;
ListItem_t *pxListItem = NULL;
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_for_each_entry(pxListItem, udc, &udc_list)
if (udc->gadget == gadget)
goto found;
dev_err(gadget->dev.parent, "gadget not registered.\n");
xSemaphoreGive(udc_lock);
return;
found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
list_del(&udc->list);
xSemaphoreGive(udc_lock);
if (udc->driver)
usb_gadget_remove_driver(udc);
}
/* ------------------------------------------------------------------------- */
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
int ret;
dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
driver->function);
udc->driver = driver;
ret = driver->bind(udc->gadget);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc);
if (ret) {
driver->unbind(udc->gadget);
goto err1;
}
usb_gadget_connect(udc->gadget);
return 0;
err1:
if (ret != -EISNAM)
dev_err(&udc->dev, "failed to start %s: %d\n",
udc->driver->function, ret);
udc->driver = NULL;
return ret;
}
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret;
ListItem_t *pxListItem = NULL;
if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_for_each_entry(pxListItem, udc, &udc_list) {
/* For now we take the first one */
if (!udc->driver)
goto found;
}
printf("couldn't find an available UDC\n");
xSemaphoreGive(udc_lock);
return -ENODEV;
found:
ret = udc_bind_to_driver(udc, driver);
xSemaphoreGive(udc_lock);
return ret;
}
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
usb_udc_core_init();
return usb_gadget_probe_driver(driver);
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret = -ENODEV;
ListItem_t *pxListItem = NULL;
if (!driver || !driver->unbind)
return -EINVAL;
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_for_each_entry(pxListItem, udc, &udc_list)
if (udc->driver == driver) {
usb_gadget_remove_driver(udc);
usb_gadget_set_state(udc->gadget,
USB_STATE_NOTATTACHED);
ret = 0;
break;
}
xSemaphoreGive(udc_lock);
return ret;
}

View File

@ -0,0 +1,108 @@
#include "usb_os_adapter.h"
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
{
int count = 0;
u8 c;
u16 uchar;
/*
* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (u8) *s++) != 0) {
if ((c & 0x80)) {
/*
* 2-byte sequence:
* 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
*/
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
/*
* 3-byte sequence (most CJKV characters):
* zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
*/
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
/*
* 4-byte sequence (surrogate pairs, currently rare):
* 11101110wwwwzzzzyy + 110111yyyyxxxxxx
* = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
* (uuuuu = wwww + 1)
* FIXME accept the surrogate code points (only)
*/
} else
goto fail;
} else
uchar = c;
put_unaligned_le16(uchar, cp++);
count++;
len--;
}
return count;
fail:
return -1;
}
int usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf)
{
struct usb_string *s;
int len;
if (!table)
return -EINVAL;
/* descriptor 0 has the language id */
if (id == 0) {
buf[0] = 4;
buf[1] = USB_DT_STRING;
buf[2] = (u8) table->language;
buf[3] = (u8) (table->language >> 8);
return 4;
}
for (s = table->strings; s && s->s; s++)
if (s->id == id)
break;
/* unrecognized: stall. */
if (!s || !s->s)
return -EINVAL;
/* string descriptors have length, tag, then UTF16-LE text */
len = min((size_t) 126, strlen(s->s));
memset(buf + 2, 0, 2 * len); /* zero all the bytes */
len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
if (len < 0)
return -EINVAL;
buf[0] = (len + 1) * 2;
buf[1] = USB_DT_STRING;
return buf[0];
}