#include <net.h>
/* #include <malloc.h> */
#include <linux/ctype.h>
+#include <asm/unaligned.h>
#include "gadget_chips.h"
-#define USB_NET_NAME "CDC Ethernet"
+#define USB_NET_NAME "USB Ethernet"
+
#define dprintf(x, ...)
+//#define dprintf printf
#undef INFO
#define INFO(x, s...) printf(s)
#define dev_err(x, stuff...) printf(stuff)
#define dev_warn dev_err
#define DEBUG dev_err
#define VDEBUG DEBUG
-#define likely
#define atomic_read
extern struct platform_data brd;
#define spin_lock(x)
#include "rndis.h"
-/* TODO: move it to some configuration file */
-#define CONFIG_USB_ETH_RNDIS
-
#ifndef CONFIG_USB_ETH_RNDIS
#define rndis_uninit(x) do{}while(0)
#define rndis_deregister(c) do{}while(0)
#endif
}
-#define subset_active(dev) (!is_cdc(dev))
-#define cdc_active(dev) ( is_cdc(dev))
+#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev))
+#define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev))
#define DEFAULT_QLEN 2 /* double buffering by default */
static char dev_addr[18];
static char host_addr[18];
+
+void eth_connection_established(void)
+{
+ l_ethdev.network_started=1;
+ printf("connection established.\n");
+}
+
+
/*-------------------------------------------------------------------------*/
/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
int result = 0;
struct usb_gadget *gadget = dev->gadget;
+
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* status endpoint used for (optionally) CDC */
if (!subset_active(dev) && dev->status_ep) {
dev->out = NULL;
}
+ /* activate non-CDC configs right away
+ * this isn't strictly according to the RNDIS spec
+ */
+ else if (!cdc_active (dev)) {
+ /* and open the tx floodgates */
+// atomic_set (&dev->tx_qlen, 0);
+ dev->tx_qlen = 0;
+ if (rndis_active(dev)) {
+ rndis_set_param_medium (dev->rndis_config,
+ NDIS_MEDIUM_802_3,
+ BITRATE(dev->gadget)/100);
+ (void) rndis_signal_connect (dev->rndis_config);
+ }
+ }
+
/* caller is responsible for cleanup on error */
return result;
}
/* issue the second notification if host reads the first */
if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION
&& value == 0) {
- char *src, *dst = req->buf + sizeof *event;
- __le32 data;
- int i;
+ __le32 val, *data = req->buf + sizeof *event;
event->bmRequestType = 0xA1;
event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
event->wLength = __constant_cpu_to_le16 (8);
/* SPEED_CHANGE data is up/down speeds in bits/sec */
-
- data = cpu_to_le32 (BITRATE (dev->gadget));
-
- src = &data;
- for (i=0; i<4; i++)
- *dst++ = *src++;
- src = &data;
- for (i=0; i<4; i++)
- *dst++ = *src++;
+ val = cpu_to_le32 (BITRATE (dev->gadget));
+ put_unaligned_le32(val, &data[0]);
+ put_unaligned_le32(val, &data[1]);
req->length = STATUS_BYTECOUNT;
value = usb_ep_queue (ep, req, GFP_ATOMIC);
event->bNotificationType, value);
if (event->bNotificationType==
USB_CDC_NOTIFY_SPEED_CHANGE)
- {
- l_ethdev.network_started=1;
- printf("USB network up!\n");
- }
+ eth_connection_established();
}
req->context = NULL;
dprintf("done\n");
*/
dprintf("eth_setup:...\n");
- if (rndis_active(dev)) {
- rndis_set_param_medium(dev->rndis_config,
- NDIS_MEDIUM_802_3, 0);
- (void) rndis_signal_connect (dev->rndis_config);
- }
req->complete = eth_setup_complete;
switch (ctrl->bRequest) {
int length;
dprintf("%s\n", __func__);
- dprintf("rx status %d\n", req->status);
+ dprintf("rx status %d, len %d\n", req->status, req->actual);
packet_received=1;
if (!req)
return;
buf = req->buf;
- length = req->length;
+ length = req->actual;
if (rndis_active(dev)) {
- if(rndis_rm_hdr(&buf, &length))
+ if(rndis_rm_hdr(buf, &length))
dprintf("%s: incorrect RNDIS packet\n",
__func__);
/* XXX: what to do if RNDIS fails !?!? */
}
- req->buf = buf;
- req->length = length;
+ req->actual = length;
dev->rx_req=req;
}
#ifndef CONFIG_USB_ETH_RNDIS
rndis = 0;
#endif
+#if 0
/* Because most host side USB stacks handle CDC Ethernet, that
* standard protocol is _strongly_ preferred for interop purposes.
* (By everyone except Microsoft.)
*/
cdc = 0;
}
-
+#endif
gcnum = usb_gadget_controller_number (gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16 (0x0300 + gcnum);
return -ENODEV;
}
+ /* support optional vendor/distro customization */
+#if defined(CONFIG_USB_CDC_VENDOR_ID) && defined(CONFIG_USB_CDC_PRODUCT_ID)
+ device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID);
+ device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID);
+#endif
+
/* If there's an RNDIS configuration, that's what Windows wants to
* be using ... so use these product IDs here and in the "linux.inf"
* needed to install MSFT drivers. Current Linux kernels will use
__constant_cpu_to_le16(RNDIS_PRODUCT_NUM);
sprintf (product_desc, "RNDIS/%s", driver_desc);
+#if defined(CONFIG_USB_RNDIS_VENDOR_ID) && defined(CONFIG_USB_RNDIS_PRODUCT_ID)
+ device_desc.idVendor = cpu_to_le16(CONFIG_USB_RNDIS_VENDOR_ID);
+ device_desc.idProduct = cpu_to_le16(CONFIG_USB_RNDIS_PRODUCT_ID);
+#endif
+
+
/* CDC subset ... recognized by Linux since 2.4.10, but Windows
* drivers aren't widely available. (That may be improved by
* supporting one submode of the "SAFE" variant of MDLM.)
__constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
}
- /* support optional vendor/distro customization */
-#if defined(CONFIG_USB_CDC_VENDOR_ID) && defined(CONFIG_USB_CDC_PRODUCT_ID)
- device_desc.idVendor = cpu_to_le16(CONFIG_USB_CDC_VENDOR_ID);
- device_desc.idProduct = cpu_to_le16(CONFIG_USB_CDC_PRODUCT_ID);
-#endif
if (bcdDevice)
device_desc.bcdDevice = cpu_to_le16(bcdDevice);
if (iManufacturer)
dev->cdc = cdc;
dev->zlp = zlp;
- dev->rndis = rndis;
dev->in_ep = in_ep;
dev->out_ep = out_ep;
/* use PKTSIZE (or aligned... from u-boot) and set
* wMaxSegmentSize accordingly*/
dev->mtu = PKTSIZE_ALIGN; /* RNDIS does not like this, only 1514, TODO*/
+ dev->net->mtu = 1500; /* hack for RNDIS OID_GEN_MAXIMUM_FRAME_SIZE */
/* preallocate control message data and buffer */
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
goto fail;
}
- /* XXX warkaround for missing stats in eth_dev structure */
- static struct eth_device_stats dev_stats;
/* these set up a lot of the OIDs that RNDIS needs */
rndis_set_host_mac (dev->rndis_config, dev->host_mac);
+
if (rndis_set_param_dev (dev->rndis_config, dev->net,
- &dev_stats, &dev->cdc_filter))
+ NULL, &dev->cdc_filter))
goto fail0;
if (rndis_set_param_vendor(dev->rndis_config, vendorID,
manufacturer))
if (getenv("cdc_connect_timeout"))
timeout = simple_strtoul(getenv("cdc_connect_timeout"),
NULL, 10) * CONFIG_SYS_HZ;
+
+ printf("Initializing USB...");
+
ts = get_timer(0);
while (!l_ethdev.network_started)
{
printf("The remote end did not respond in time.\n");
goto fail;
}
+
irq_res = usb_gadget_handle_interrupts();
/* hack nice progress display */
if (irq_res && !l_ethdev.network_started)
- printf(".");
+ printf(".");
}
+ if (rndis_active(dev)) {
+ printf("Waiting for RNDIS link...");
+
+ /* wait for RNDIS to setup */
+ ts = get_timer(0);
+ while (get_timer(ts) < 250)
+ {
+ if (usb_gadget_handle_interrupts())
+ printf(".");
+ }
+ printf("\n");
+ }
rx_submit (dev, dev->rx_req, 0);
return 0;
fail:
if (dev->rx_req)
{
void *data = dev->rx_req->buf;
- int len = dev->rx_req->length;
+ int len = dev->rx_req->actual;
NetReceive(data, len);
packet_received=0;
rx_submit (dev, dev->rx_req, 0);
int status = 0;
struct eth_device *netdev=&l_netdev;
- sprintf(netdev->name, "USB CDC Ethernet");
+ sprintf(netdev->name, USB_NET_NAME);
netdev->init = usb_eth_init;
netdev->send = usb_eth_send;
#define ETH_ALEN 6 /* copied from ether.c */
#define netif_running(...) 1
-#define netif_carrier_on(...) do { } while(0)
+#define netif_carrier_on(...) eth_connection_established()
#define netif_wake_queue(...) do { } while(0)
#define netif_carrier_off(...) do { } while(0)
#define netif_stop_queue(...) do { } while(0)
* and will be happier if you provide the host_addr module parameter.
*/
-#if 0
-static int rndis_debug = 0;
-module_param (rndis_debug, int, 0);
-MODULE_PARM_DESC (rndis_debug, "enable debugging");
-#else
#define rndis_debug 0
-#endif
#define DBG(str,args...) do { \
if (rndis_debug) \
return -ENOMEM;
resp = (rndis_init_cmplt_type *) r->buf;
- resp->MessageType = __constant_cpu_to_le32 (
- REMOTE_NDIS_INITIALIZE_CMPLT);
+ resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_INITIALIZE_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (52);
- resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION);
resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION);
rndis_query_cmplt_type *resp;
rndis_resp_t *r;
- // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID));
+ // DBG("%s: OID = %08X\n", __func__, get_unaligned_le32(&buf->OID));
if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
/*
resp = (rndis_query_cmplt_type *) r->buf;
resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
- resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */
- if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
- le32_to_cpu(buf->InformationBufferOffset)
+ if (gen_ndis_query_resp (configNr, get_unaligned_le32(&buf->OID),
+ get_unaligned_le32(&buf->InformationBufferOffset)
+ 8 + (u8 *) buf,
- le32_to_cpu(buf->InformationBufferLength),
+ get_unaligned_le32(&buf->InformationBufferLength),
r)) {
/* OID not supported */
resp->Status = __constant_cpu_to_le32 (
return -ENOMEM;
resp = (rndis_set_cmplt_type *) r->buf;
- BufLength = le32_to_cpu (buf->InformationBufferLength);
- BufOffset = le32_to_cpu (buf->InformationBufferOffset);
+ BufLength = get_unaligned_le32(&buf->InformationBufferLength);
+ BufOffset = get_unaligned_le32(&buf->InformationBufferOffset);
#ifdef VERBOSE
DBG("%s: Length: %d\n", __func__, BufLength);
resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (16);
- resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
- if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
+ resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */
+ if (gen_ndis_set_resp (configNr, get_unaligned_le32(&buf->OID),
((u8 *) buf) + 8 + BufOffset, BufLength, r))
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
else
resp->MessageType = __constant_cpu_to_le32 (
REMOTE_NDIS_KEEPALIVE_CMPLT);
resp->MessageLength = __constant_cpu_to_le32 (16);
- resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->RequestID = get_unaligned_le32(&buf->RequestID); /* Still LE in msg buffer */
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
if (rndis_per_dev_params [configNr].ack)
u16 *cdc_filter)
{
DBG("%s:\n", __func__ );
- if (!dev || !stats) return -1;
+// if (!dev || !stats) return -1;
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
rndis_per_dev_params [configNr].dev = dev;
memcpy(rndis_buffer.data + header_size, data, len);
+ DBG("Created RNDIS packet of size %d data offset %d total %d\n", len, header_size, total_size);
rndis_buffer.len = total_size;
rndis_buffer.in_use = 1;
return &rndis_buffer;
return r;
}
-int rndis_rm_hdr(void **data, int *len)
+int rndis_rm_hdr(void *data, int *len)
{
+ int i;
+ char *dst = data;
+ char *src = data;
/* tmp points to a struct rndis_packet_msg_type */
- __le32 *tmp = (void *) *data;
+ __le32 *tmp = (void *) data;
int DataOffset, DataLength;
+ DBG("%s, %d\n", __FUNCTION__, __LINE__);
/* MessageType, MessageLength */
if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
DataOffset = get_unaligned_le32(tmp++);
DataLength = get_unaligned_le32(tmp++);
+ src += DataOffset + 8;
if (DataLength > *len)
return -EOVERFLOW;
+ for (i=0; i<DataLength; i++)
+ dst[i] = src[i];
+
*len = DataLength;
- *data = *data + DataOffset;
+
+ DBG("RNDIS pkt: offset %d size %d, src %x dst %x\n", DataOffset, DataLength, src, dst);
return 0;
}
void rndis_add_hdr (struct sk_buff *skb);
int rndis_rm_hdr (struct sk_buff *skb);
#endif
-int rndis_rm_hdr(void **data, int *len);
+int rndis_rm_hdr(void *data, int *len);
u8 *rndis_get_next_response (int configNr, u32 *length);
void rndis_free_response (int configNr, u8 *buf);
void otg_phy_off(void)
{
- writel(readl(S3C_USBOTG_PHYPWR)|(0x3<<3), S3C_USBOTG_PHYPWR);
+ /* reset controller just in case */
+ writel(0x1, S3C_USBOTG_RSTCON);
+ udelay(20);
+ writel(0x0, S3C_USBOTG_RSTCON);
+ udelay(20);
+
+ writel(readl(S3C_USBOTG_PHYPWR)|(0x3<<3)|(0x1), S3C_USBOTG_PHYPWR);
writel(readl(S5PC110_USB_PHY_CON)&~(1<<0), S5PC110_USB_PHY_CON);
// writel(readl(S5P_USB_PHY_CONTROL)&~(1<<0), S5P_USB_PHY_CONTROL);
- writel((readl(S3C_USBOTG_PHYPWR)&~(0x3<<3)&~(0x1<<0)),S3C_USBOTG_PHYPWR);
+// writel((readl(S3C_USBOTG_PHYPWR)&~(0x3<<3)&~(0x1<<0)),S3C_USBOTG_PHYPWR);
writel((readl(S3C_USBOTG_PHYCLK)&~(0x5<<2)),S3C_USBOTG_PHYCLK);
udelay(10000);
writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), S3C_UDC_OTG_DOEPCTL(EP0_CON));
writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), S3C_UDC_OTG_DIEPCTL(EP0_CON));
+ for (i = 1; i < S3C_MAX_ENDPOINTS; i++) {
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, S3C_UDC_OTG_DOEPCTL(i));
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, S3C_UDC_OTG_DIEPCTL(i));
+ }
+
/* 8. Unmask EPO interrupts*/
writel( ((1<<EP0_CON)<<DAINT_OUT_BIT)|(1<<EP0_CON), S3C_UDC_OTG_DAINTMSK);
#define GET_MAX_LUN_REQUEST 0xFE
#define BOT_RESET_REQUEST 0xFF
+#if 0
+
/* TEST MODE in set_feature request */
#define TEST_SELECTOR_MASK 0xFF
#define TEST_PKT_SIZE 53
0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E //{JKKKKKKK x 10},JK
};
+#endif
+
void s3c_udc_ep_set_stall(struct s3c_ep *ep);
static inline void s3c_udc_ep0_zlp(void)
if (!(ep_ctrl & DEPCTL_USBACTEP)) {
ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK)| (ep->bmAttributes << DEPCTL_TYPE_BIT);
ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | (ep->ep.maxpacket << DEPCTL_MPS_BIT);
- ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP);
+ ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK);
if (ep_is_in(ep)) {
writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
return 0;
}
+#if 0
+
/* Set into the test mode for Test Mode set_feature request */
static inline void set_test_mode(void)
{
}
}
+#endif
+
static int s3c_udc_set_feature(struct usb_ep *_ep)
{
struct s3c_ep *ep;
case USB_DEVICE_REMOTE_WAKEUP:
DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_REMOTE_WAKEUP\n");
break;
-
+#if 0
case USB_DEVICE_TEST_MODE:
DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_TEST_MODE\n");
set_test_mode();
break;
-
+#endif
case USB_DEVICE_B_HNP_ENABLE:
DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
break;
(usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", usb_ctrl->bRequest,
usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex);
+#ifdef DEBUG_S3C_UDC
+ {
+ int i, len = sizeof(*usb_ctrl);
+ char *p = usb_ctrl;
+
+ printf("pkt = ");
+ for (i=0; i<len; i++) {
+ printf("%02x", ((u8*)p)[i]);
+ if ((i & 7) == 7)
+ printf(" ");
+ }
+ printf("\n");
+ }
+#endif
+
+
if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && usb_ctrl->wLength != 1) {
DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid wLength = %d, setup returned\n",
__func__, usb_ctrl->wLength);