CARPLAY版本整理
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
Reference in New Issue
Block a user