rndis.c 28.9 KB
Newer Older
1
/*
L
Linus Torvalds 已提交
2
 * RNDIS MSG parser
3
 *
L
Linus Torvalds 已提交
4
 * Authors:	Benedikt Spranger, Pengutronix
5 6
 *		Robert Schwebel, Pengutronix
 *
L
Linus Torvalds 已提交
7 8
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
9 10
 *              version 2, as published by the Free Software Foundation.
 *
L
Linus Torvalds 已提交
11 12
 *		This software was originally developed in conformance with
 *		Microsoft's Remote NDIS Specification License Agreement.
13
 *
L
Linus Torvalds 已提交
14 15
 * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
 *		Fixed message length bug in init_response
16
 *
L
Linus Torvalds 已提交
17
 * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
18
 *		Fixed rndis_rm_hdr length bug.
L
Linus Torvalds 已提交
19 20 21 22 23 24 25 26 27 28 29 30
 *
 * Copyright (C) 2004 by David Brownell
 *		updates to merge with Linux 2.6, better match RNDIS spec
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
31
#include <linux/seq_file.h>
L
Linus Torvalds 已提交
32 33 34 35 36
#include <linux/netdevice.h>

#include <asm/io.h>
#include <asm/byteorder.h>
#include <asm/system.h>
37
#include <asm/unaligned.h>
L
Linus Torvalds 已提交
38 39


D
David Brownell 已提交
40
#undef	VERBOSE_DEBUG
L
Linus Torvalds 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53

#include "rndis.h"


/* The driver for your USB chip needs to support ep0 OUT to work with
 * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
 *
 * Windows hosts need an INF file like Documentation/usb/linux.inf
 * and will be happier if you provide the host_addr module parameter.
 */

#if 0
static int rndis_debug = 0;
54
module_param (rndis_debug, int, 0);
L
Linus Torvalds 已提交
55 56 57 58 59 60 61 62 63 64 65
MODULE_PARM_DESC (rndis_debug, "enable debugging");
#else
#define rndis_debug		0
#endif

#define RNDIS_MAX_CONFIGS	1


static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS];

/* Driver Version */
66
static const __le32 rndis_driver_version = cpu_to_le32 (1);
L
Linus Torvalds 已提交
67 68 69 70 71

/* Function Prototypes */
static rndis_resp_t *rndis_add_response (int configNr, u32 length);


72
/* supported OIDs */
73
static const u32 oid_supported_list [] =
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
{
	/* the general stuff */
	OID_GEN_SUPPORTED_LIST,
	OID_GEN_HARDWARE_STATUS,
	OID_GEN_MEDIA_SUPPORTED,
	OID_GEN_MEDIA_IN_USE,
	OID_GEN_MAXIMUM_FRAME_SIZE,
	OID_GEN_LINK_SPEED,
	OID_GEN_TRANSMIT_BLOCK_SIZE,
	OID_GEN_RECEIVE_BLOCK_SIZE,
	OID_GEN_VENDOR_ID,
	OID_GEN_VENDOR_DESCRIPTION,
	OID_GEN_VENDOR_DRIVER_VERSION,
	OID_GEN_CURRENT_PACKET_FILTER,
	OID_GEN_MAXIMUM_TOTAL_SIZE,
	OID_GEN_MEDIA_CONNECT_STATUS,
	OID_GEN_PHYSICAL_MEDIUM,
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	/* the statistical stuff */
	OID_GEN_XMIT_OK,
	OID_GEN_RCV_OK,
	OID_GEN_XMIT_ERROR,
	OID_GEN_RCV_ERROR,
	OID_GEN_RCV_NO_BUFFER,
#ifdef	RNDIS_OPTIONAL_STATS
	OID_GEN_DIRECTED_BYTES_XMIT,
	OID_GEN_DIRECTED_FRAMES_XMIT,
	OID_GEN_MULTICAST_BYTES_XMIT,
	OID_GEN_MULTICAST_FRAMES_XMIT,
	OID_GEN_BROADCAST_BYTES_XMIT,
	OID_GEN_BROADCAST_FRAMES_XMIT,
	OID_GEN_DIRECTED_BYTES_RCV,
	OID_GEN_DIRECTED_FRAMES_RCV,
	OID_GEN_MULTICAST_BYTES_RCV,
	OID_GEN_MULTICAST_FRAMES_RCV,
	OID_GEN_BROADCAST_BYTES_RCV,
	OID_GEN_BROADCAST_FRAMES_RCV,
	OID_GEN_RCV_CRC_ERROR,
	OID_GEN_TRANSMIT_QUEUE_LENGTH,
#endif	/* RNDIS_OPTIONAL_STATS */

115
	/* mandatory 802.3 */
116 117 118 119 120 121
	/* the general stuff */
	OID_802_3_PERMANENT_ADDRESS,
	OID_802_3_CURRENT_ADDRESS,
	OID_802_3_MULTICAST_LIST,
	OID_802_3_MAC_OPTIONS,
	OID_802_3_MAXIMUM_LIST_SIZE,
122

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	/* the statistical stuff */
	OID_802_3_RCV_ERROR_ALIGNMENT,
	OID_802_3_XMIT_ONE_COLLISION,
	OID_802_3_XMIT_MORE_COLLISIONS,
#ifdef	RNDIS_OPTIONAL_STATS
	OID_802_3_XMIT_DEFERRED,
	OID_802_3_XMIT_MAX_COLLISIONS,
	OID_802_3_RCV_OVERRUN,
	OID_802_3_XMIT_UNDERRUN,
	OID_802_3_XMIT_HEARTBEAT_FAILURE,
	OID_802_3_XMIT_TIMES_CRS_LOST,
	OID_802_3_XMIT_LATE_COLLISIONS,
#endif	/* RNDIS_OPTIONAL_STATS */

#ifdef	RNDIS_PM
D
David Brownell 已提交
138 139 140 141 142 143 144 145
	/* PM and wakeup are "mandatory" for USB, but the RNDIS specs
	 * don't say what they mean ... and the NDIS specs are often
	 * confusing and/or ambiguous in this context.  (That is, more
	 * so than their specs for the other OIDs.)
	 *
	 * FIXME someone who knows what these should do, please
	 * implement them!
	 */
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

	/* power management */
	OID_PNP_CAPABILITIES,
	OID_PNP_QUERY_POWER,
	OID_PNP_SET_POWER,

#ifdef	RNDIS_WAKEUP
	/* wake up host */
	OID_PNP_ENABLE_WAKE_UP,
	OID_PNP_ADD_WAKE_UP_PATTERN,
	OID_PNP_REMOVE_WAKE_UP_PATTERN,
#endif	/* RNDIS_WAKEUP */
#endif	/* RNDIS_PM */
};


L
Linus Torvalds 已提交
162
/* NDIS Functions */
163 164 165
static int
gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
		rndis_resp_t *r)
L
Linus Torvalds 已提交
166
{
167 168
	int			retval = -ENOTSUPP;
	u32			length = 4;	/* usually */
169
	__le32			*outbuf;
L
Linus Torvalds 已提交
170 171
	int			i, count;
	rndis_query_cmplt_type	*resp;
D
David Brownell 已提交
172
	struct net_device	*net;
173
	const struct net_device_stats	*stats;
L
Linus Torvalds 已提交
174 175 176 177 178

	if (!r) return -ENOMEM;
	resp = (rndis_query_cmplt_type *) r->buf;

	if (!resp) return -ENOMEM;
179 180

	if (buf_len && rndis_debug > 1) {
181
		pr_debug("query OID %08x value, len %d:\n", OID, buf_len);
182
		for (i = 0; i < buf_len; i += 16) {
183
			pr_debug("%03d: %08x %08x %08x %08x\n", i,
184 185 186 187
				get_unaligned_le32(&buf[i]),
				get_unaligned_le32(&buf[i + 4]),
				get_unaligned_le32(&buf[i + 8]),
				get_unaligned_le32(&buf[i + 12]));
188 189 190 191 192
		}
	}

	/* response goes here, right after the header */
	outbuf = (__le32 *) &resp[1];
193
	resp->InformationBufferOffset = cpu_to_le32 (16);
194

D
David Brownell 已提交
195
	net = rndis_per_dev_params[configNr].dev;
196
	stats = dev_get_stats(net);
D
David Brownell 已提交
197

L
Linus Torvalds 已提交
198 199 200 201 202 203
	switch (OID) {

	/* general oids (table 4-1) */

	/* mandatory */
	case OID_GEN_SUPPORTED_LIST:
204
		pr_debug("%s: OID_GEN_SUPPORTED_LIST\n", __func__);
L
Linus Torvalds 已提交
205 206 207
		length = sizeof (oid_supported_list);
		count  = length / sizeof (u32);
		for (i = 0; i < count; i++)
208
			outbuf[i] = cpu_to_le32 (oid_supported_list[i]);
L
Linus Torvalds 已提交
209 210
		retval = 0;
		break;
211

L
Linus Torvalds 已提交
212 213
	/* mandatory */
	case OID_GEN_HARDWARE_STATUS:
214
		pr_debug("%s: OID_GEN_HARDWARE_STATUS\n", __func__);
215
		/* Bogus question!
L
Linus Torvalds 已提交
216
		 * Hardware must be ready to receive high level protocols.
217
		 * BTW:
L
Linus Torvalds 已提交
218 219 220
		 * reddite ergo quae sunt Caesaris Caesari
		 * et quae sunt Dei Deo!
		 */
221
		*outbuf = cpu_to_le32 (0);
L
Linus Torvalds 已提交
222 223
		retval = 0;
		break;
224

L
Linus Torvalds 已提交
225 226
	/* mandatory */
	case OID_GEN_MEDIA_SUPPORTED:
227
		pr_debug("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__);
228
		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
L
Linus Torvalds 已提交
229 230
		retval = 0;
		break;
231

L
Linus Torvalds 已提交
232 233
	/* mandatory */
	case OID_GEN_MEDIA_IN_USE:
234
		pr_debug("%s: OID_GEN_MEDIA_IN_USE\n", __func__);
L
Linus Torvalds 已提交
235
		/* one medium, one transport... (maybe you do it better) */
236
		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);
L
Linus Torvalds 已提交
237 238
		retval = 0;
		break;
239

L
Linus Torvalds 已提交
240 241
	/* mandatory */
	case OID_GEN_MAXIMUM_FRAME_SIZE:
242
		pr_debug("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
L
Linus Torvalds 已提交
243
		if (rndis_per_dev_params [configNr].dev) {
244
			*outbuf = cpu_to_le32 (
L
Linus Torvalds 已提交
245 246 247 248
				rndis_per_dev_params [configNr].dev->mtu);
			retval = 0;
		}
		break;
249

L
Linus Torvalds 已提交
250 251
	/* mandatory */
	case OID_GEN_LINK_SPEED:
252
		if (rndis_debug > 1)
253
			pr_debug("%s: OID_GEN_LINK_SPEED\n", __func__);
L
Linus Torvalds 已提交
254
		if (rndis_per_dev_params [configNr].media_state
255
				== NDIS_MEDIA_STATE_DISCONNECTED)
256
			*outbuf = cpu_to_le32 (0);
L
Linus Torvalds 已提交
257
		else
258
			*outbuf = cpu_to_le32 (
L
Linus Torvalds 已提交
259 260 261 262 263 264
				rndis_per_dev_params [configNr].speed);
		retval = 0;
		break;

	/* mandatory */
	case OID_GEN_TRANSMIT_BLOCK_SIZE:
265
		pr_debug("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
L
Linus Torvalds 已提交
266
		if (rndis_per_dev_params [configNr].dev) {
267
			*outbuf = cpu_to_le32 (
L
Linus Torvalds 已提交
268 269 270 271
				rndis_per_dev_params [configNr].dev->mtu);
			retval = 0;
		}
		break;
272

L
Linus Torvalds 已提交
273 274
	/* mandatory */
	case OID_GEN_RECEIVE_BLOCK_SIZE:
275
		pr_debug("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
L
Linus Torvalds 已提交
276
		if (rndis_per_dev_params [configNr].dev) {
277
			*outbuf = cpu_to_le32 (
L
Linus Torvalds 已提交
278 279 280 281
				rndis_per_dev_params [configNr].dev->mtu);
			retval = 0;
		}
		break;
282

L
Linus Torvalds 已提交
283 284
	/* mandatory */
	case OID_GEN_VENDOR_ID:
285
		pr_debug("%s: OID_GEN_VENDOR_ID\n", __func__);
286
		*outbuf = cpu_to_le32 (
L
Linus Torvalds 已提交
287 288 289
			rndis_per_dev_params [configNr].vendorID);
		retval = 0;
		break;
290

L
Linus Torvalds 已提交
291 292
	/* mandatory */
	case OID_GEN_VENDOR_DESCRIPTION:
293
		pr_debug("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__);
L
Linus Torvalds 已提交
294
		length = strlen (rndis_per_dev_params [configNr].vendorDescr);
295
		memcpy (outbuf,
L
Linus Torvalds 已提交
296 297 298 299 300
			rndis_per_dev_params [configNr].vendorDescr, length);
		retval = 0;
		break;

	case OID_GEN_VENDOR_DRIVER_VERSION:
301
		pr_debug("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
L
Linus Torvalds 已提交
302
		/* Created as LE */
303
		*outbuf = rndis_driver_version;
L
Linus Torvalds 已提交
304 305 306 307 308
		retval = 0;
		break;

	/* mandatory */
	case OID_GEN_CURRENT_PACKET_FILTER:
309
		pr_debug("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
310
		*outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);
L
Linus Torvalds 已提交
311 312 313 314 315
		retval = 0;
		break;

	/* mandatory */
	case OID_GEN_MAXIMUM_TOTAL_SIZE:
316
		pr_debug("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
317
		*outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
L
Linus Torvalds 已提交
318 319 320 321 322
		retval = 0;
		break;

	/* mandatory */
	case OID_GEN_MEDIA_CONNECT_STATUS:
323
		if (rndis_debug > 1)
324
			pr_debug("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
325
		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
L
Linus Torvalds 已提交
326 327 328 329 330
						.media_state);
		retval = 0;
		break;

	case OID_GEN_PHYSICAL_MEDIUM:
331
		pr_debug("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__);
332
		*outbuf = cpu_to_le32 (0);
L
Linus Torvalds 已提交
333 334 335 336 337 338 339 340
		retval = 0;
		break;

	/* The RNDIS specification is incomplete/wrong.   Some versions
	 * of MS-Windows expect OIDs that aren't specified there.  Other
	 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
	 */
	case OID_GEN_MAC_OPTIONS:		/* from WinME */
341
		pr_debug("%s: OID_GEN_MAC_OPTIONS\n", __func__);
342
		*outbuf = cpu_to_le32(
L
Linus Torvalds 已提交
343 344 345 346 347 348 349 350 351
			  NDIS_MAC_OPTION_RECEIVE_SERIALIZED
			| NDIS_MAC_OPTION_FULL_DUPLEX);
		retval = 0;
		break;

	/* statistics OIDs (table 4-2) */

	/* mandatory */
	case OID_GEN_XMIT_OK:
352
		if (rndis_debug > 1)
353
			pr_debug("%s: OID_GEN_XMIT_OK\n", __func__);
D
David Brownell 已提交
354 355 356
		if (stats) {
			*outbuf = cpu_to_le32(stats->tx_packets
				- stats->tx_errors - stats->tx_dropped);
L
Linus Torvalds 已提交
357 358 359 360 361 362
			retval = 0;
		}
		break;

	/* mandatory */
	case OID_GEN_RCV_OK:
363
		if (rndis_debug > 1)
364
			pr_debug("%s: OID_GEN_RCV_OK\n", __func__);
D
David Brownell 已提交
365 366 367
		if (stats) {
			*outbuf = cpu_to_le32(stats->rx_packets
				- stats->rx_errors - stats->rx_dropped);
L
Linus Torvalds 已提交
368 369 370
			retval = 0;
		}
		break;
371

L
Linus Torvalds 已提交
372 373
	/* mandatory */
	case OID_GEN_XMIT_ERROR:
374
		if (rndis_debug > 1)
375
			pr_debug("%s: OID_GEN_XMIT_ERROR\n", __func__);
D
David Brownell 已提交
376 377
		if (stats) {
			*outbuf = cpu_to_le32(stats->tx_errors);
L
Linus Torvalds 已提交
378 379 380
			retval = 0;
		}
		break;
381

L
Linus Torvalds 已提交
382 383
	/* mandatory */
	case OID_GEN_RCV_ERROR:
384
		if (rndis_debug > 1)
385
			pr_debug("%s: OID_GEN_RCV_ERROR\n", __func__);
D
David Brownell 已提交
386 387
		if (stats) {
			*outbuf = cpu_to_le32(stats->rx_errors);
L
Linus Torvalds 已提交
388 389 390
			retval = 0;
		}
		break;
391

L
Linus Torvalds 已提交
392 393
	/* mandatory */
	case OID_GEN_RCV_NO_BUFFER:
394
		pr_debug("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);
D
David Brownell 已提交
395 396
		if (stats) {
			*outbuf = cpu_to_le32(stats->rx_dropped);
L
Linus Torvalds 已提交
397 398 399
			retval = 0;
		}
		break;
400

L
Linus Torvalds 已提交
401 402 403 404
	/* ieee802.3 OIDs (table 4-3) */

	/* mandatory */
	case OID_802_3_PERMANENT_ADDRESS:
405
		pr_debug("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__);
L
Linus Torvalds 已提交
406 407
		if (rndis_per_dev_params [configNr].dev) {
			length = ETH_ALEN;
408
			memcpy (outbuf,
L
Linus Torvalds 已提交
409 410 411 412 413
				rndis_per_dev_params [configNr].host_mac,
				length);
			retval = 0;
		}
		break;
414

L
Linus Torvalds 已提交
415 416
	/* mandatory */
	case OID_802_3_CURRENT_ADDRESS:
417
		pr_debug("%s: OID_802_3_CURRENT_ADDRESS\n", __func__);
L
Linus Torvalds 已提交
418 419
		if (rndis_per_dev_params [configNr].dev) {
			length = ETH_ALEN;
420
			memcpy (outbuf,
L
Linus Torvalds 已提交
421 422 423 424 425
				rndis_per_dev_params [configNr].host_mac,
				length);
			retval = 0;
		}
		break;
426

L
Linus Torvalds 已提交
427 428
	/* mandatory */
	case OID_802_3_MULTICAST_LIST:
429
		pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__);
L
Linus Torvalds 已提交
430
		/* Multicast base address only */
431
		*outbuf = cpu_to_le32 (0xE0000000);
L
Linus Torvalds 已提交
432 433
		retval = 0;
		break;
434

L
Linus Torvalds 已提交
435 436
	/* mandatory */
	case OID_802_3_MAXIMUM_LIST_SIZE:
437
		pr_debug("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
L
Linus Torvalds 已提交
438
		/* Multicast base address only */
439
		*outbuf = cpu_to_le32 (1);
L
Linus Torvalds 已提交
440 441
		retval = 0;
		break;
442

L
Linus Torvalds 已提交
443
	case OID_802_3_MAC_OPTIONS:
444
		pr_debug("%s: OID_802_3_MAC_OPTIONS\n", __func__);
L
Linus Torvalds 已提交
445 446 447 448 449 450
		break;

	/* ieee802.3 statistics OIDs (table 4-4) */

	/* mandatory */
	case OID_802_3_RCV_ERROR_ALIGNMENT:
451
		pr_debug("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
D
David Brownell 已提交
452 453
		if (stats) {
			*outbuf = cpu_to_le32(stats->rx_frame_errors);
L
Linus Torvalds 已提交
454 455 456
			retval = 0;
		}
		break;
457

L
Linus Torvalds 已提交
458 459
	/* mandatory */
	case OID_802_3_XMIT_ONE_COLLISION:
460
		pr_debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__);
461
		*outbuf = cpu_to_le32 (0);
L
Linus Torvalds 已提交
462 463
		retval = 0;
		break;
464

L
Linus Torvalds 已提交
465 466
	/* mandatory */
	case OID_802_3_XMIT_MORE_COLLISIONS:
467
		pr_debug("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
468
		*outbuf = cpu_to_le32 (0);
L
Linus Torvalds 已提交
469 470
		retval = 0;
		break;
471

L
Linus Torvalds 已提交
472
	default:
473
		pr_warning("%s: query unknown OID 0x%08X\n",
474
			 __func__, OID);
L
Linus Torvalds 已提交
475
	}
476 477
	if (retval < 0)
		length = 0;
478

L
Linus Torvalds 已提交
479
	resp->InformationBufferLength = cpu_to_le32 (length);
480 481
	r->length = length + sizeof *resp;
	resp->MessageLength = cpu_to_le32 (r->length);
L
Linus Torvalds 已提交
482 483 484
	return retval;
}

485 486
static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
			rndis_resp_t *r)
L
Linus Torvalds 已提交
487 488
{
	rndis_set_cmplt_type		*resp;
489
	int				i, retval = -ENOTSUPP;
L
Linus Torvalds 已提交
490 491 492 493 494 495 496 497
	struct rndis_params		*params;

	if (!r)
		return -ENOMEM;
	resp = (rndis_set_cmplt_type *) r->buf;
	if (!resp)
		return -ENOMEM;

498
	if (buf_len && rndis_debug > 1) {
499
		pr_debug("set OID %08x value, len %d:\n", OID, buf_len);
500
		for (i = 0; i < buf_len; i += 16) {
501
			pr_debug("%03d: %08x %08x %08x %08x\n", i,
502 503 504 505
				get_unaligned_le32(&buf[i]),
				get_unaligned_le32(&buf[i + 4]),
				get_unaligned_le32(&buf[i + 8]),
				get_unaligned_le32(&buf[i + 12]));
506
		}
L
Linus Torvalds 已提交
507 508
	}

509
	params = &rndis_per_dev_params [configNr];
L
Linus Torvalds 已提交
510 511 512
	switch (OID) {
	case OID_GEN_CURRENT_PACKET_FILTER:

513 514
		/* these NDIS_PACKET_TYPE_* bitflags are shared with
		 * cdc_filter; it's not RNDIS-specific
L
Linus Torvalds 已提交
515 516 517 518
		 * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
		 *	PROMISCUOUS, DIRECTED,
		 *	MULTICAST, ALL_MULTICAST, BROADCAST
		 */
519
		*params->filter = (u16)get_unaligned_le32(buf);
520
		pr_debug("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",
521
			__func__, *params->filter);
L
Linus Torvalds 已提交
522 523 524 525 526

		/* this call has a significant side effect:  it's
		 * what makes the packet flow start and stop, like
		 * activating the CDC Ethernet altsetting.
		 */
527 528
		retval = 0;
		if (*params->filter) {
L
Linus Torvalds 已提交
529 530 531 532 533 534 535 536 537 538
			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;
539

L
Linus Torvalds 已提交
540
	case OID_802_3_MULTICAST_LIST:
541
		/* I think we can ignore this */
542
		pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__);
L
Linus Torvalds 已提交
543 544
		retval = 0;
		break;
545

L
Linus Torvalds 已提交
546
	default:
547
		pr_warning("%s: set unknown OID 0x%08X, size %d\n",
548
			 __func__, OID, buf_len);
L
Linus Torvalds 已提交
549
	}
550

L
Linus Torvalds 已提交
551 552 553
	return retval;
}

554 555
/*
 * Response Functions
L
Linus Torvalds 已提交
556 557 558 559
 */

static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
{
560
	rndis_init_cmplt_type	*resp;
L
Linus Torvalds 已提交
561
	rndis_resp_t            *r;
D
David Brownell 已提交
562
	struct rndis_params	*params = rndis_per_dev_params + configNr;
563

D
David Brownell 已提交
564 565
	if (!params->dev)
		return -ENOTSUPP;
566

L
Linus Torvalds 已提交
567
	r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
568 569
	if (!r)
		return -ENOMEM;
L
Linus Torvalds 已提交
570
	resp = (rndis_init_cmplt_type *) r->buf;
571

572
	resp->MessageType = cpu_to_le32 (
L
Linus Torvalds 已提交
573
			REMOTE_NDIS_INITIALIZE_CMPLT);
574
	resp->MessageLength = cpu_to_le32 (52);
L
Linus Torvalds 已提交
575
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
576 577 578 579 580 581
	resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS);
	resp->MajorVersion = cpu_to_le32 (RNDIS_MAJOR_VERSION);
	resp->MinorVersion = cpu_to_le32 (RNDIS_MINOR_VERSION);
	resp->DeviceFlags = cpu_to_le32 (RNDIS_DF_CONNECTIONLESS);
	resp->Medium = cpu_to_le32 (RNDIS_MEDIUM_802_3);
	resp->MaxPacketsPerTransfer = cpu_to_le32 (1);
L
Linus Torvalds 已提交
582
	resp->MaxTransferSize = cpu_to_le32 (
D
David Brownell 已提交
583
		  params->dev->mtu
L
Linus Torvalds 已提交
584 585 586
		+ sizeof (struct ethhdr)
		+ sizeof (struct rndis_packet_msg_type)
		+ 22);
587 588 589
	resp->PacketAlignmentFactor = cpu_to_le32 (0);
	resp->AFListOffset = cpu_to_le32 (0);
	resp->AFListSize = cpu_to_le32 (0);
590

D
David Brownell 已提交
591
	params->resp_avail(params->v);
L
Linus Torvalds 已提交
592 593 594 595 596 597 598
	return 0;
}

static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
{
	rndis_query_cmplt_type *resp;
	rndis_resp_t            *r;
D
David Brownell 已提交
599
	struct rndis_params	*params = rndis_per_dev_params + configNr;
600

601
	/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
D
David Brownell 已提交
602 603
	if (!params->dev)
		return -ENOTSUPP;
604

605 606 607 608 609
	/*
	 * we need more memory:
	 * gen_ndis_query_resp expects enough space for
	 * rndis_query_cmplt_type followed by data.
	 * oid_supported_list is the largest data reply
L
Linus Torvalds 已提交
610
	 */
611 612
	r = rndis_add_response (configNr,
		sizeof (oid_supported_list) + sizeof(rndis_query_cmplt_type));
613 614
	if (!r)
		return -ENOMEM;
L
Linus Torvalds 已提交
615
	resp = (rndis_query_cmplt_type *) r->buf;
616

617
	resp->MessageType = cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT);
L
Linus Torvalds 已提交
618
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
619

620 621 622 623 624
	if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID),
			le32_to_cpu(buf->InformationBufferOffset)
					+ 8 + (u8 *) buf,
			le32_to_cpu(buf->InformationBufferLength),
			r)) {
L
Linus Torvalds 已提交
625
		/* OID not supported */
626
		resp->Status = cpu_to_le32 (
L
Linus Torvalds 已提交
627
				RNDIS_STATUS_NOT_SUPPORTED);
628 629 630
		resp->MessageLength = cpu_to_le32 (sizeof *resp);
		resp->InformationBufferLength = cpu_to_le32 (0);
		resp->InformationBufferOffset = cpu_to_le32 (0);
L
Linus Torvalds 已提交
631
	} else
632
		resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS);
633

D
David Brownell 已提交
634
	params->resp_avail(params->v);
L
Linus Torvalds 已提交
635 636 637 638 639 640 641 642
	return 0;
}

static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
{
	u32			BufLength, BufOffset;
	rndis_set_cmplt_type	*resp;
	rndis_resp_t		*r;
D
David Brownell 已提交
643
	struct rndis_params	*params = rndis_per_dev_params + configNr;
644

L
Linus Torvalds 已提交
645
	r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
646 647
	if (!r)
		return -ENOMEM;
L
Linus Torvalds 已提交
648 649 650 651 652
	resp = (rndis_set_cmplt_type *) r->buf;

	BufLength = le32_to_cpu (buf->InformationBufferLength);
	BufOffset = le32_to_cpu (buf->InformationBufferOffset);

D
David Brownell 已提交
653
#ifdef	VERBOSE_DEBUG
654 655 656
	pr_debug("%s: Length: %d\n", __func__, BufLength);
	pr_debug("%s: Offset: %d\n", __func__, BufOffset);
	pr_debug("%s: InfoBuffer: ", __func__);
657

L
Linus Torvalds 已提交
658
	for (i = 0; i < BufLength; i++) {
659
		pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
L
Linus Torvalds 已提交
660
	}
661

662
	pr_debug("\n");
L
Linus Torvalds 已提交
663
#endif
664

665 666
	resp->MessageType = cpu_to_le32 (REMOTE_NDIS_SET_CMPLT);
	resp->MessageLength = cpu_to_le32 (16);
L
Linus Torvalds 已提交
667
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
668 669
	if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID),
			((u8 *) buf) + 8 + BufOffset, BufLength, r))
670
		resp->Status = cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED);
671
	else
672
		resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS);
673

D
David Brownell 已提交
674
	params->resp_avail(params->v);
L
Linus Torvalds 已提交
675 676 677 678 679 680 681
	return 0;
}

static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
{
	rndis_reset_cmplt_type	*resp;
	rndis_resp_t		*r;
D
David Brownell 已提交
682
	struct rndis_params	*params = rndis_per_dev_params + configNr;
683

L
Linus Torvalds 已提交
684
	r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
685 686
	if (!r)
		return -ENOMEM;
L
Linus Torvalds 已提交
687
	resp = (rndis_reset_cmplt_type *) r->buf;
688

689 690 691
	resp->MessageType = cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT);
	resp->MessageLength = cpu_to_le32 (16);
	resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS);
L
Linus Torvalds 已提交
692
	/* resent information */
693
	resp->AddressingReset = cpu_to_le32 (1);
694

D
David Brownell 已提交
695
	params->resp_avail(params->v);
L
Linus Torvalds 已提交
696 697 698 699
	return 0;
}

static int rndis_keepalive_response (int configNr,
700
				rndis_keepalive_msg_type *buf)
L
Linus Torvalds 已提交
701 702 703
{
	rndis_keepalive_cmplt_type	*resp;
	rndis_resp_t			*r;
D
David Brownell 已提交
704
	struct rndis_params	*params = rndis_per_dev_params + configNr;
L
Linus Torvalds 已提交
705 706 707 708

	/* host "should" check only in RNDIS_DATA_INITIALIZED state */

	r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type));
709 710
	if (!r)
		return -ENOMEM;
L
Linus Torvalds 已提交
711
	resp = (rndis_keepalive_cmplt_type *) r->buf;
712

713
	resp->MessageType = cpu_to_le32 (
L
Linus Torvalds 已提交
714
			REMOTE_NDIS_KEEPALIVE_CMPLT);
715
	resp->MessageLength = cpu_to_le32 (16);
L
Linus Torvalds 已提交
716
	resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
717
	resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS);
718

D
David Brownell 已提交
719
	params->resp_avail(params->v);
L
Linus Torvalds 已提交
720 721 722 723
	return 0;
}


724 725
/*
 * Device to Host Comunication
L
Linus Torvalds 已提交
726 727 728
 */
static int rndis_indicate_status_msg (int configNr, u32 status)
{
729
	rndis_indicate_status_msg_type	*resp;
L
Linus Torvalds 已提交
730
	rndis_resp_t			*r;
D
David Brownell 已提交
731
	struct rndis_params	*params = rndis_per_dev_params + configNr;
732

D
David Brownell 已提交
733
	if (params->state == RNDIS_UNINITIALIZED)
734 735 736
		return -ENOTSUPP;

	r = rndis_add_response (configNr,
L
Linus Torvalds 已提交
737
				sizeof (rndis_indicate_status_msg_type));
738 739
	if (!r)
		return -ENOMEM;
L
Linus Torvalds 已提交
740
	resp = (rndis_indicate_status_msg_type *) r->buf;
741

742
	resp->MessageType = cpu_to_le32 (
L
Linus Torvalds 已提交
743
			REMOTE_NDIS_INDICATE_STATUS_MSG);
744
	resp->MessageLength = cpu_to_le32 (20);
L
Linus Torvalds 已提交
745
	resp->Status = cpu_to_le32 (status);
746 747
	resp->StatusBufferLength = cpu_to_le32 (0);
	resp->StatusBufferOffset = cpu_to_le32 (0);
748

D
David Brownell 已提交
749
	params->resp_avail(params->v);
L
Linus Torvalds 已提交
750 751 752 753 754 755 756
	return 0;
}

int rndis_signal_connect (int configNr)
{
	rndis_per_dev_params [configNr].media_state
			= NDIS_MEDIA_STATE_CONNECTED;
757
	return rndis_indicate_status_msg (configNr,
L
Linus Torvalds 已提交
758 759 760 761 762 763 764 765 766 767 768
					  RNDIS_STATUS_MEDIA_CONNECT);
}

int rndis_signal_disconnect (int configNr)
{
	rndis_per_dev_params [configNr].media_state
			= NDIS_MEDIA_STATE_DISCONNECTED;
	return rndis_indicate_status_msg (configNr,
					  RNDIS_STATUS_MEDIA_DISCONNECT);
}

769 770
void rndis_uninit (int configNr)
{
771 772 773
	u8 *buf;
	u32 length;

774 775 776
	if (configNr >= RNDIS_MAX_CONFIGS)
		return;
	rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
777 778 779 780

	/* drain the response queue */
	while ((buf = rndis_get_next_response(configNr, &length)))
		rndis_free_response(configNr, buf);
781 782
}

L
Linus Torvalds 已提交
783 784 785 786 787
void rndis_set_host_mac (int configNr, const u8 *addr)
{
	rndis_per_dev_params [configNr].host_mac = addr;
}

788 789
/*
 * Message Parser
L
Linus Torvalds 已提交
790 791 792 793 794 795
 */
int rndis_msg_parser (u8 configNr, u8 *buf)
{
	u32 MsgType, MsgLength;
	__le32 *tmp;
	struct rndis_params		*params;
796

L
Linus Torvalds 已提交
797 798
	if (!buf)
		return -ENOMEM;
799 800

	tmp = (__le32 *) buf;
801 802
	MsgType   = get_unaligned_le32(tmp++);
	MsgLength = get_unaligned_le32(tmp++);
803

L
Linus Torvalds 已提交
804 805 806
	if (configNr >= RNDIS_MAX_CONFIGS)
		return -ENOTSUPP;
	params = &rndis_per_dev_params [configNr];
807

808 809 810 811 812
	/* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
	 * rx/tx statistics and link status, in addition to KEEPALIVE traffic
	 * and normal HC level polling to see if there's any IN traffic.
	 */

L
Linus Torvalds 已提交
813
	/* For USB: responses may take up to 10 seconds */
814
	switch (MsgType) {
L
Linus Torvalds 已提交
815
	case REMOTE_NDIS_INITIALIZE_MSG:
816
		pr_debug("%s: REMOTE_NDIS_INITIALIZE_MSG\n",
817
			__func__ );
L
Linus Torvalds 已提交
818 819
		params->state = RNDIS_INITIALIZED;
		return  rndis_init_response (configNr,
820 821
					(rndis_init_msg_type *) buf);

L
Linus Torvalds 已提交
822
	case REMOTE_NDIS_HALT_MSG:
823
		pr_debug("%s: REMOTE_NDIS_HALT_MSG\n",
824
			__func__ );
L
Linus Torvalds 已提交
825 826 827 828 829 830
		params->state = RNDIS_UNINITIALIZED;
		if (params->dev) {
			netif_carrier_off (params->dev);
			netif_stop_queue (params->dev);
		}
		return 0;
831

L
Linus Torvalds 已提交
832
	case REMOTE_NDIS_QUERY_MSG:
833 834 835
		return rndis_query_response (configNr,
					(rndis_query_msg_type *) buf);

L
Linus Torvalds 已提交
836
	case REMOTE_NDIS_SET_MSG:
837 838 839
		return rndis_set_response (configNr,
					(rndis_set_msg_type *) buf);

L
Linus Torvalds 已提交
840
	case REMOTE_NDIS_RESET_MSG:
841
		pr_debug("%s: REMOTE_NDIS_RESET_MSG\n",
842
			__func__ );
L
Linus Torvalds 已提交
843
		return rndis_reset_response (configNr,
844
					(rndis_reset_msg_type *) buf);
L
Linus Torvalds 已提交
845 846 847

	case REMOTE_NDIS_KEEPALIVE_MSG:
		/* For USB: host does this every 5 seconds */
848
		if (rndis_debug > 1)
849
			pr_debug("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",
850
				__func__ );
L
Linus Torvalds 已提交
851
		return rndis_keepalive_response (configNr,
852
						 (rndis_keepalive_msg_type *)
L
Linus Torvalds 已提交
853
						 buf);
854 855

	default:
L
Linus Torvalds 已提交
856 857 858 859
		/* At least Windows XP emits some undefined RNDIS messages.
		 * In one case those messages seemed to relate to the host
		 * suspending itself.
		 */
860
		pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
861
			__func__ , MsgType, MsgLength);
L
Linus Torvalds 已提交
862 863 864
		{
			unsigned i;
			for (i = 0; i < MsgLength; i += 16) {
865
				pr_debug("%03d: "
L
Linus Torvalds 已提交
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
					" %02x %02x %02x %02x"
					" %02x %02x %02x %02x"
					" %02x %02x %02x %02x"
					" %02x %02x %02x %02x"
					"\n",
					i,
					buf[i], buf [i+1],
						buf[i+2], buf[i+3],
					buf[i+4], buf [i+5],
						buf[i+6], buf[i+7],
					buf[i+8], buf [i+9],
						buf[i+10], buf[i+11],
					buf[i+12], buf [i+13],
						buf[i+14], buf[i+15]);
			}
		}
		break;
	}
884

L
Linus Torvalds 已提交
885 886 887
	return -ENOTSUPP;
}

D
David Brownell 已提交
888
int rndis_register(void (*resp_avail)(void *v), void *v)
L
Linus Torvalds 已提交
889 890
{
	u8 i;
891

D
David Brownell 已提交
892 893 894
	if (!resp_avail)
		return -EINVAL;

L
Linus Torvalds 已提交
895 896 897
	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
		if (!rndis_per_dev_params [i].used) {
			rndis_per_dev_params [i].used = 1;
D
David Brownell 已提交
898 899
			rndis_per_dev_params [i].resp_avail = resp_avail;
			rndis_per_dev_params [i].v = v;
900
			pr_debug("%s: configNr = %d\n", __func__, i);
L
Linus Torvalds 已提交
901 902 903
			return i;
		}
	}
904
	pr_debug("failed\n");
905

D
David Brownell 已提交
906
	return -ENODEV;
L
Linus Torvalds 已提交
907 908 909 910
}

void rndis_deregister (int configNr)
{
911
	pr_debug("%s: \n", __func__);
912

L
Linus Torvalds 已提交
913 914
	if (configNr >= RNDIS_MAX_CONFIGS) return;
	rndis_per_dev_params [configNr].used = 0;
915

L
Linus Torvalds 已提交
916 917 918
	return;
}

D
David Brownell 已提交
919
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
L
Linus Torvalds 已提交
920
{
921
	pr_debug("%s:\n", __func__);
D
David Brownell 已提交
922 923
	if (!dev)
		return -EINVAL;
L
Linus Torvalds 已提交
924
	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
925

L
Linus Torvalds 已提交
926
	rndis_per_dev_params [configNr].dev = dev;
927
	rndis_per_dev_params [configNr].filter = cdc_filter;
928

L
Linus Torvalds 已提交
929 930 931 932 933
	return 0;
}

int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)
{
934
	pr_debug("%s:\n", __func__);
L
Linus Torvalds 已提交
935 936
	if (!vendorDescr) return -1;
	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
937

L
Linus Torvalds 已提交
938 939
	rndis_per_dev_params [configNr].vendorID = vendorID;
	rndis_per_dev_params [configNr].vendorDescr = vendorDescr;
940

L
Linus Torvalds 已提交
941 942 943 944 945
	return 0;
}

int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)
{
946
	pr_debug("%s: %u %u\n", __func__, medium, speed);
L
Linus Torvalds 已提交
947
	if (configNr >= RNDIS_MAX_CONFIGS) return -1;
948

L
Linus Torvalds 已提交
949 950
	rndis_per_dev_params [configNr].medium = medium;
	rndis_per_dev_params [configNr].speed = speed;
951

L
Linus Torvalds 已提交
952 953 954 955 956 957 958 959 960 961 962
	return 0;
}

void rndis_add_hdr (struct sk_buff *skb)
{
	struct rndis_packet_msg_type	*header;

	if (!skb)
		return;
	header = (void *) skb_push (skb, sizeof *header);
	memset (header, 0, sizeof *header);
963
	header->MessageType = cpu_to_le32(REMOTE_NDIS_PACKET_MSG);
L
Linus Torvalds 已提交
964
	header->MessageLength = cpu_to_le32(skb->len);
965
	header->DataOffset = cpu_to_le32 (36);
966
	header->DataLength = cpu_to_le32(skb->len - sizeof *header);
L
Linus Torvalds 已提交
967 968 969 970 971 972
}

void rndis_free_response (int configNr, u8 *buf)
{
	rndis_resp_t		*r;
	struct list_head	*act, *tmp;
973 974 975

	list_for_each_safe (act, tmp,
			&(rndis_per_dev_params [configNr].resp_queue))
L
Linus Torvalds 已提交
976 977 978 979 980 981 982 983 984 985 986 987
	{
		r = list_entry (act, rndis_resp_t, list);
		if (r && r->buf == buf) {
			list_del (&r->list);
			kfree (r);
		}
	}
}

u8 *rndis_get_next_response (int configNr, u32 *length)
{
	rndis_resp_t		*r;
988 989
	struct list_head	*act, *tmp;

L
Linus Torvalds 已提交
990
	if (!length) return NULL;
991 992 993

	list_for_each_safe (act, tmp,
			&(rndis_per_dev_params [configNr].resp_queue))
L
Linus Torvalds 已提交
994 995 996 997 998 999 1000 1001
	{
		r = list_entry (act, rndis_resp_t, list);
		if (!r->send) {
			r->send = 1;
			*length = r->length;
			return r->buf;
		}
	}
1002

L
Linus Torvalds 已提交
1003 1004 1005 1006 1007 1008
	return NULL;
}

static rndis_resp_t *rndis_add_response (int configNr, u32 length)
{
	rndis_resp_t	*r;
1009

1010
	/* NOTE:  this gets copied into ether.c USB_BUFSIZ bytes ... */
L
Linus Torvalds 已提交
1011 1012
	r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC);
	if (!r) return NULL;
1013

L
Linus Torvalds 已提交
1014 1015 1016
	r->buf = (u8 *) (r + 1);
	r->length = length;
	r->send = 0;
1017 1018 1019

	list_add_tail (&r->list,
		&(rndis_per_dev_params [configNr].resp_queue));
L
Linus Torvalds 已提交
1020 1021 1022
	return r;
}

1023
int rndis_rm_hdr(struct sk_buff *skb)
L
Linus Torvalds 已提交
1024
{
1025 1026
	/* tmp points to a struct rndis_packet_msg_type */
	__le32		*tmp = (void *) skb->data;
L
Linus Torvalds 已提交
1027

1028
	/* MessageType, MessageLength */
1029
	if (cpu_to_le32(REMOTE_NDIS_PACKET_MSG)
1030 1031 1032 1033 1034
			!= get_unaligned(tmp++))
		return -EINVAL;
	tmp++;

	/* DataOffset, DataLength */
1035
	if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8))
1036
		return -EOVERFLOW;
1037
	skb_trim(skb, get_unaligned_le32(tmp++));
L
Linus Torvalds 已提交
1038 1039 1040 1041 1042 1043

	return 0;
}

#ifdef	CONFIG_USB_GADGET_DEBUG_FILES

1044
static int rndis_proc_show(struct seq_file *m, void *v)
L
Linus Torvalds 已提交
1045
{
1046
	rndis_params *param = m->private;
1047

1048
	seq_printf(m,
L
Linus Torvalds 已提交
1049 1050 1051 1052 1053 1054 1055
			 "Config Nr. %d\n"
			 "used      : %s\n"
			 "state     : %s\n"
			 "medium    : 0x%08X\n"
			 "speed     : %d\n"
			 "cable     : %s\n"
			 "vendor ID : 0x%08X\n"
1056 1057
			 "vendor    : %s\n",
			 param->confignr, (param->used) ? "y" : "n",
L
Linus Torvalds 已提交
1058 1059 1060 1061 1062 1063 1064 1065 1066
			 ({ 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; }),
1067 1068
			 param->medium,
			 (param->media_state) ? 0 : param->speed*100,
L
Linus Torvalds 已提交
1069
			 (param->media_state) ? "disconnected" : "connected",
1070
			 param->vendorID, param->vendorDescr);
1071
	return 0;
L
Linus Torvalds 已提交
1072 1073
}

1074 1075
static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
		size_t count, loff_t *ppos)
L
Linus Torvalds 已提交
1076
{
1077
	rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
L
Linus Torvalds 已提交
1078 1079
	u32 speed = 0;
	int i, fl_speed = 0;
1080

L
Linus Torvalds 已提交
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
	for (i = 0; i < count; i++) {
		char c;
		if (get_user(c, buffer))
			return -EFAULT;
		switch (c) {
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			fl_speed = 1;
			speed = speed*10 + c - '0';
			break;
		case 'C':
		case 'c':
			rndis_signal_connect (p->confignr);
			break;
		case 'D':
		case 'd':
			rndis_signal_disconnect(p->confignr);
			break;
1107
		default:
L
Linus Torvalds 已提交
1108
			if (fl_speed) p->speed = speed;
1109
			else pr_debug("%c is not valid\n", c);
L
Linus Torvalds 已提交
1110 1111
			break;
		}
1112

L
Linus Torvalds 已提交
1113 1114
		buffer++;
	}
1115

L
Linus Torvalds 已提交
1116 1117 1118
	return count;
}

1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
static int rndis_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, rndis_proc_show, PDE(inode)->data);
}

static const struct file_operations rndis_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= rndis_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
	.write		= rndis_proc_write,
};

L
Linus Torvalds 已提交
1133 1134 1135 1136 1137 1138 1139
#define	NAME_TEMPLATE	"driver/rndis-%03d"

static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];

#endif	/* CONFIG_USB_GADGET_DEBUG_FILES */


D
David Brownell 已提交
1140
int __init rndis_init (void)
L
Linus Torvalds 已提交
1141 1142 1143 1144 1145 1146 1147 1148 1149
{
	u8 i;

	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
		char name [20];

		sprintf (name, NAME_TEMPLATE, i);
		if (!(rndis_connect_state [i]
1150 1151 1152
				= proc_create_data(name, 0660, NULL,
					&rndis_proc_fops,
					(void *)(rndis_per_dev_params + i))))
L
Linus Torvalds 已提交
1153
		{
1154
			pr_debug("%s :remove entries", __func__);
L
Linus Torvalds 已提交
1155 1156 1157 1158
			while (i) {
				sprintf (name, NAME_TEMPLATE, --i);
				remove_proc_entry (name, NULL);
			}
1159
			pr_debug("\n");
L
Linus Torvalds 已提交
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
			return -EIO;
		}
#endif
		rndis_per_dev_params [i].confignr = i;
		rndis_per_dev_params [i].used = 0;
		rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED;
		rndis_per_dev_params [i].media_state
				= NDIS_MEDIA_STATE_DISCONNECTED;
		INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue));
	}
1170

L
Linus Torvalds 已提交
1171 1172 1173 1174 1175 1176 1177 1178
	return 0;
}

void rndis_exit (void)
{
#ifdef	CONFIG_USB_GADGET_DEBUG_FILES
	u8 i;
	char name [20];
1179

L
Linus Torvalds 已提交
1180 1181 1182 1183 1184 1185 1186
	for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
		sprintf (name, NAME_TEMPLATE, i);
		remove_proc_entry (name, NULL);
	}
#endif
}