Commit 920f873a authored by David Brownell's avatar David Brownell Committed by Linus Torvalds

[PATCH] USB: rndis gadget driver updates

Various build fixes:  64bit (Andrew Morton), static linking,
broken on big-endian, etc.

Tighten up the integration with the main "ether" driver, so
state transitions and host ethernet addresses are shared too.
Add missing spinlock calls around RNDIS command outcall,
fix GET_INTERFACE issue, host mustn't clobber netdev flags.

Minor code cleanups.
parent 836115bf
......@@ -120,6 +120,7 @@ struct eth_dev {
unsigned long todo;
#define WORK_RX_MEMORY 0
int rndis_config;
u8 host_mac [ETH_ALEN];
};
/* This version autoconfigures as much as possible at run-time.
......@@ -159,9 +160,8 @@ static const char *EP_STATUS_NAME;
/* For hardware that can talk RNDIS and either of the above protocols,
* use this ID ... the windows INF files will know it. Unless it's
* used with CDC Ethernet, Linux hosts will need updates to choose the
* non-MSFT configuration, either in the kernel (2.4) or else from a
* hotplug script (2.6).
* used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
* the non-RNDIS configuration.
*/
#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
......@@ -1334,8 +1334,10 @@ static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
struct eth_dev *dev = ep->driver_data;
/* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */
spin_lock(&dev->lock);
if (rndis_msg_parser (dev->rndis_config, (u8 *) req->buf))
ERROR(dev, "%s: rndis parse error\n", __FUNCTION__ );
spin_unlock(&dev->lock);
}
#endif /* RNDIS */
......@@ -1486,14 +1488,14 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|| !dev->config
|| ctrl->wIndex > 1)
break;
if (!dev->cdc && ctrl->wIndex != 0)
if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0)
break;
/* if carrier is on, data interface is active. */
*(u8 *)req->buf =
((ctrl->wIndex == 1) && netif_carrier_ok (dev->net))
? 1
: 0,
/* for CDC, iff carrier is on, data interface is active. */
if (dev->rndis || ctrl->wIndex != 1)
*(u8 *)req->buf = 0;
else
*(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
value = min (ctrl->wLength, (u16) 1);
break;
......@@ -1552,6 +1554,7 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
memcpy (req->buf, buf, value);
req->complete = rndis_response_complete;
}
/* else stalls ... spec says to avoid that */
}
break;
#endif /* RNDIS */
......@@ -1590,6 +1593,8 @@ eth_disconnect (struct usb_gadget *gadget)
eth_reset_config (dev);
spin_unlock_irqrestore (&dev->lock, flags);
/* FIXME RNDIS should enter RNDIS_UNINITIALIZED */
/* next we may get setup() calls to enumerate new connections;
* or an unbind() during shutdown (including removing module).
*/
......@@ -2376,19 +2381,19 @@ eth_bind (struct usb_gadget *gadget)
*/
random_ether_addr(net->dev_addr);
#ifdef DEV_CONFIG_CDC
/* ... another address for the host, on the other end of the
* link, gets exported through CDC (see CDC spec table 41)
* and RNDIS.
*/
if (cdc) {
u8 node_id [ETH_ALEN];
random_ether_addr(node_id);
if (cdc || rndis) {
random_ether_addr(dev->host_mac);
#ifdef DEV_CONFIG_CDC
snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
node_id [0], node_id [1], node_id [2],
node_id [3], node_id [4], node_id [5]);
}
dev->host_mac [0], dev->host_mac [1],
dev->host_mac [2], dev->host_mac [3],
dev->host_mac [4], dev->host_mac [5]);
#endif
}
if (rndis) {
status = rndis_init();
......@@ -2448,10 +2453,11 @@ eth_bind (struct usb_gadget *gadget)
net->dev_addr [2], net->dev_addr [3],
net->dev_addr [4], net->dev_addr [5]);
#ifdef DEV_CONFIG_CDC
if (cdc)
INFO (dev, "CDC host enet %s\n", ethaddr);
#endif
if (cdc || rndis)
INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\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]);
#ifdef CONFIG_USB_ETH_RNDIS
if (rndis) {
......@@ -2468,6 +2474,7 @@ eth_bind (struct usb_gadget *gadget)
}
/* 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))
goto fail0;
......
......@@ -37,6 +37,16 @@
#include "rndis.h"
/* The driver for your USB chip needs to support ep0 OUT to work with
* RNDIS, plus the same three descriptors as CDC Ethernet.
*
* Windows hosts need an INF file like Documentation/usb/linux.inf
*/
#ifndef __LITTLE_ENDIAN
#warning this code is missing all cpu_to_leXX() calls ...
#endif
#if 0
#define DEBUG if (rndis_debug) printk
static int rndis_debug = 0;
......@@ -89,8 +99,12 @@ static u32 devFlags2currentFilter (struct net_device *dev)
static void currentFilter2devFlags (u32 currentFilter, struct net_device *dev)
{
/* FIXME the filter is supposed to control what gets
* forwarded from gadget to host; but dev->flags controls
* reporting from host to gadget ...
*/
#if 0
if (!dev) return;
if (currentFilter & NDIS_PACKET_TYPE_MULTICAST)
dev->flags |= IFF_MULTICAST;
if (currentFilter & NDIS_PACKET_TYPE_BROADCAST)
......@@ -99,8 +113,13 @@ static void currentFilter2devFlags (u32 currentFilter, struct net_device *dev)
dev->flags |= IFF_ALLMULTI;
if (currentFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
dev->flags |= IFF_PROMISC;
#endif
}
/* FIXME OMITTED OIDs, that RNDIS-on-USB "must" support, include
* - power management (OID_PNP_CAPABILITIES, ...)
* - network wakeup (OID_PNP_ENABLE_WAKE_UP, ...)
*/
/* NDIS Functions */
static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
......@@ -114,8 +133,6 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
if (!resp) return -ENOMEM;
if (!resp) return -ENOMEM;
switch (OID) {
/* mandatory */
case OID_GEN_SUPPORTED_LIST:
......@@ -178,7 +195,8 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
case OID_GEN_LINK_SPEED:
DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__);
length = 4;
if (rndis_per_dev_params [configNr].media_state)
if (rndis_per_dev_params [configNr].media_state
== NDIS_MEDIA_STATE_DISCONNECTED)
*((u32 *) resp + 6) = 0;
else
*((u32 *) resp + 6) = rndis_per_dev_params [configNr].speed;
......@@ -611,15 +629,10 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
case OID_802_3_PERMANENT_ADDRESS:
DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].dev) {
length = 6;
length = ETH_ALEN;
memcpy ((u8 *) resp + 24,
rndis_per_dev_params [configNr].dev->dev_addr,
rndis_per_dev_params [configNr].host_mac,
length);
/*
* we need a MAC address and hope that
* (our MAC + 1) is not in use
*/
*((u8 *) resp + 29) += 1;
retval = 0;
} else {
*((u32 *) resp + 6) = 0;
......@@ -631,15 +644,10 @@ static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r)
case OID_802_3_CURRENT_ADDRESS:
DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__);
if (rndis_per_dev_params [configNr].dev) {
length = 6;
length = ETH_ALEN;
memcpy ((u8 *) resp + 24,
rndis_per_dev_params [configNr].dev->dev_addr,
rndis_per_dev_params [configNr].host_mac,
length);
/*
* we need a MAC address and hope that
* (our MAC + 1) is not in use
*/
*((u8 *) resp + 29) += 1;
retval = 0;
}
break;
......@@ -746,22 +754,38 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
rndis_set_cmplt_type *resp;
int i, retval = -ENOTSUPP;
struct rndis_config_parameter *param;
struct rndis_params *params;
u8 *cp;
if (!r) return -ENOMEM;
if (!r)
return -ENOMEM;
resp = (rndis_set_cmplt_type *) r->buf;
if (!resp)
return -ENOMEM;
if (!resp) return -ENOMEM;
cp = (u8 *)resp;
switch (OID) {
case OID_GEN_CURRENT_PACKET_FILTER:
DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__);
currentFilter2devFlags ((u32) ((u8 *) resp + 28),
rndis_per_dev_params [configNr].dev);
params = &rndis_per_dev_params [configNr];
currentFilter2devFlags(cp[28], params->dev);
retval = 0;
if ((u32) ((u8 *) resp + 28))
rndis_per_dev_params [configNr].state = RNDIS_INITIALIZED;
else
rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
/* this call has a significant side effect: it's
* what makes the packet flow start and stop, like
* activating the CDC Ethernet altsetting.
*/
if (cp[28]) {
params->state = RNDIS_DATA_INITIALIZED;
netif_carrier_on(params->dev);
if (netif_running(params->dev))
netif_wake_queue (params->dev);
} else {
params->state = RNDIS_INITIALIZED;
netif_carrier_off (params->dev);
netif_stop_queue (params->dev);
}
break;
case OID_802_3_MULTICAST_LIST:
......@@ -938,9 +962,8 @@ static int rndis_keepalive_response (int configNr,
rndis_keepalive_cmplt_type *resp;
rndis_resp_t *r;
/* respond only in RNDIS_INITIALIZED state */
if (rndis_per_dev_params [configNr].state != RNDIS_INITIALIZED)
return 0;
/* host "should" check only in RNDIS_DATA_INITIALIZED state */
r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
resp = (rndis_keepalive_cmplt_type *) r->buf;
if (!resp) return -ENOMEM;
......@@ -1004,35 +1027,48 @@ int rndis_signal_disconnect (int configNr)
RNDIS_STATUS_MEDIA_DISCONNECT);
}
void rndis_set_host_mac (int configNr, const u8 *addr)
{
rndis_per_dev_params [configNr].host_mac = addr;
}
/*
* Message Parser
*/
int rndis_msg_parser (u8 configNr, u8 *buf)
{
u32 MsgType, MsgLength, *tmp;
struct rndis_params *params;
if (!buf) return -ENOMEM;
if (!buf)
return -ENOMEM;
tmp = (u32 *) buf;
MsgType = *tmp;
MsgLength = *(tmp + 1);
if (configNr >= RNDIS_MAX_CONFIGS) return -ENOTSUPP;
if (configNr >= RNDIS_MAX_CONFIGS)
return -ENOTSUPP;
params = &rndis_per_dev_params [configNr];
/* For USB: responses may take up to 10 seconds */
switch (MsgType)
{
case REMOTE_NDIS_INIZIALIZE_MSG:
DEBUG(KERN_INFO "%s: REMOTE_NDIS_INIZIALIZE_MSG\n",
case REMOTE_NDIS_INITIALIZE_MSG:
DEBUG(KERN_INFO "%s: REMOTE_NDIS_INITIALIZE_MSG\n",
__FUNCTION__ );
rndis_per_dev_params [configNr].state = RNDIS_INITIALIZED;
params->state = RNDIS_INITIALIZED;
return rndis_init_response (configNr,
(rndis_init_msg_type *) buf);
break;
case REMOTE_NDIS_HALT_MSG:
DEBUG(KERN_INFO "%s: REMOTE_NDIS_HALT_MSG\n",
__FUNCTION__ );
rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
params->state = RNDIS_UNINITIALIZED;
if (params->dev) {
netif_carrier_off (params->dev);
netif_stop_queue (params->dev);
}
return 0;
case REMOTE_NDIS_QUERY_MSG:
......@@ -1040,29 +1076,26 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
__FUNCTION__ );
return rndis_query_response (configNr,
(rndis_query_msg_type *) buf);
break;
case REMOTE_NDIS_SET_MSG:
DEBUG(KERN_INFO "%s: REMOTE_NDIS_SET_MSG\n",
__FUNCTION__ );
return rndis_set_response (configNr,
(rndis_set_msg_type *) buf);
break;
case REMOTE_NDIS_RESET_MSG:
DEBUG(KERN_INFO "%s: REMOTE_NDIS_RESET_MSG\n",
__FUNCTION__ );
return rndis_reset_response (configNr,
(rndis_reset_msg_type *) buf);
break;
case REMOTE_NDIS_KEEPALIVE_MSG:
/* For USB: host does this every 5 seconds */
DEBUG(KERN_INFO "%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
__FUNCTION__ );
return rndis_keepalive_response (configNr,
(rndis_keepalive_msg_type *)
buf);
break;
default:
printk (KERN_ERR "%s: unknown RNDIS Message Type 0x%08X\n",
......@@ -1240,9 +1273,15 @@ int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
"vendor ID : 0x%08X\n"
"vendor : %s\n",
param->confignr, (param->used) ? "y" : "n",
(param->state)
? "RNDIS_INITIALIZED"
: "RNDIS_UNINITIALIZED",
({ char *s = "?";
switch (param->state) {
case RNDIS_UNINITIALIZED:
s = "RNDIS_UNINITIALIZED"; break;
case RNDIS_INITIALIZED:
s = "RNDIS_INITIALIZED"; break;
case RNDIS_DATA_INITIALIZED:
s = "RNDIS_DATA_INITIALIZED"; break;
}; s; }),
param->medium,
(param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",
......@@ -1353,7 +1392,7 @@ int __init rndis_init (void)
return 0;
}
void __exit rndis_exit (void)
void rndis_exit (void)
{
u8 i;
char name [4];
......
......@@ -38,7 +38,7 @@
*/
/* Message Set for Connectionless (802.3) Devices */
#define REMOTE_NDIS_INIZIALIZE_MSG 0x00000002U /* Initialize device */
#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
#define REMOTE_NDIS_HALT_MSG 0x00000003U
#define REMOTE_NDIS_QUERY_MSG 0x00000004U
#define REMOTE_NDIS_SET_MSG 0x00000005U
......@@ -280,6 +280,7 @@ typedef struct rndis_params
u32 medium;
u32 speed;
u32 media_state;
const u8 *host_mac;
struct net_device *dev;
struct net_device_stats *stats;
u32 vendorID;
......@@ -301,11 +302,13 @@ void rndis_add_hdr (struct sk_buff *skb);
int rndis_rm_hdr (u8 *buf, u32 *length);
u8 *rndis_get_next_response (int configNr, u32 *length);
void rndis_free_response (int configNr, u8 *buf);
int rndis_signal_connect (int configNr);
int rndis_signal_disconnect (int configNr);
int rndis_state (int configNr);
extern void rndis_set_host_mac (int configNr, const u8 *addr);
int __init rndis_init (void);
void __exit rndis_exit (void);
void rndis_exit (void);
#endif /* _LINUX_RNDIS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment