Quellcode durchsuchen

[rndis] Add generic RNDIS device abstraction

RNDIS provides an abstraction of a network device on top of a generic
packet transmission mechanism.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown vor 10 Jahren
Ursprung
Commit
1d2b7c91f7
4 geänderte Dateien mit 1204 neuen und 6 gelöschten Zeilen
  1. 1
    0
      src/include/ipxe/errfile.h
  2. 5
    6
      src/include/ipxe/netdevice.h
  3. 348
    0
      src/include/ipxe/rndis.h
  4. 850
    0
      src/net/rndis.c

+ 1
- 0
src/include/ipxe/errfile.h Datei anzeigen

@@ -227,6 +227,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
227 227
 #define ERRFILE_ping			( ERRFILE_NET | 0x003a0000 )
228 228
 #define ERRFILE_dhcpv6			( ERRFILE_NET | 0x003b0000 )
229 229
 #define ERRFILE_nfs_uri			( ERRFILE_NET | 0x003c0000 )
230
+#define ERRFILE_rndis			( ERRFILE_NET | 0x003d0000 )
230 231
 
231 232
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
232 233
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 5
- 6
src/include/ipxe/netdevice.h Datei anzeigen

@@ -36,13 +36,12 @@ struct device;
36 36
 
37 37
 /** Maximum length of a link-layer header
38 38
  *
39
- * The longest currently-supported link-layer header is for 802.11: a
40
- * 24-byte frame header plus an 8-byte 802.3 LLC/SNAP header, plus a
41
- * possible 4-byte VLAN header.  (The IPoIB link-layer pseudo-header
42
- * doesn't actually include link-layer addresses; see ipoib.c for
43
- * details.)
39
+ * The longest currently-supported link-layer header is for RNDIS: an
40
+ * 8-byte RNDIS header, a 32-byte RNDIS packet message header, a
41
+ * 14-byte Ethernet header and a possible 4-byte VLAN header.  Round
42
+ * up to 64 bytes.
44 43
  */
45
-#define MAX_LL_HEADER_LEN 36
44
+#define MAX_LL_HEADER_LEN 64
46 45
 
47 46
 /** Maximum length of a network-layer address */
48 47
 #define MAX_NET_ADDR_LEN 16

+ 348
- 0
src/include/ipxe/rndis.h Datei anzeigen

@@ -0,0 +1,348 @@
1
+#ifndef _IPXE_RNDIS_H
2
+#define _IPXE_RNDIS_H
3
+
4
+/** @file
5
+ *
6
+ * Remote Network Driver Interface Specification
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/netdevice.h>
14
+#include <ipxe/iobuf.h>
15
+
16
+/** Maximum time to wait for a transaction to complete
17
+ *
18
+ * This is a policy decision.
19
+ */
20
+#define RNDIS_MAX_WAIT_MS 1000
21
+
22
+/** RNDIS message header */
23
+struct rndis_header {
24
+	/** Message type */
25
+	uint32_t type;
26
+	/** Message length */
27
+	uint32_t len;
28
+} __attribute__ (( packed ));
29
+
30
+/** RNDIS initialise message */
31
+#define RNDIS_INITIALIZE_MSG 0x00000002UL
32
+
33
+/** RNDIS initialise message */
34
+struct rndis_initialize_message {
35
+	/** Request ID */
36
+	uint32_t id;
37
+	/** Major version */
38
+	uint32_t major;
39
+	/** Minor version */
40
+	uint32_t minor;
41
+	/** Maximum transfer size */
42
+	uint32_t mtu;
43
+} __attribute__ (( packed ));
44
+
45
+/** RNDIS initialise completion */
46
+#define RNDIS_INITIALISE_CMPLT 0x80000002UL
47
+
48
+/** RNDIS initialise completion */
49
+struct rndis_initialise_completion {
50
+	/** Request ID */
51
+	uint32_t id;
52
+	/** Status */
53
+	uint32_t status;
54
+	/** Major version */
55
+	uint32_t major;
56
+	/** Minor version */
57
+	uint32_t minor;
58
+	/** Device flags */
59
+	uint32_t flags;
60
+	/** Medium */
61
+	uint32_t medium;
62
+	/** Maximum packets per transfer */
63
+	uint32_t max_pkts;
64
+	/** Maximum transfer size */
65
+	uint32_t mtu;
66
+	/** Packet alignment factor */
67
+	uint32_t align;
68
+	/** Reserved */
69
+	uint32_t reserved;
70
+} __attribute__ (( packed ));
71
+
72
+/** RNDIS halt message */
73
+#define RNIS_HALT_MSG 0x00000003UL
74
+
75
+/** RNDIS halt message */
76
+struct rndis_halt_message {
77
+	/** Request ID */
78
+	uint32_t id;
79
+} __attribute__ (( packed ));
80
+
81
+/** RNDIS query OID message */
82
+#define RNDIS_QUERY_MSG 0x00000004UL
83
+
84
+/** RNDIS set OID message */
85
+#define RNDIS_SET_MSG 0x00000005UL
86
+
87
+/** RNDIS query or set OID message */
88
+struct rndis_oid_message {
89
+	/** Request ID */
90
+	uint32_t id;
91
+	/** Object ID */
92
+	uint32_t oid;
93
+	/** Information buffer length */
94
+	uint32_t len;
95
+	/** Information buffer offset */
96
+	uint32_t offset;
97
+	/** Reserved */
98
+	uint32_t reserved;
99
+} __attribute__ (( packed ));
100
+
101
+/** RNDIS query OID completion */
102
+#define RNDIS_QUERY_CMPLT 0x80000004UL
103
+
104
+/** RNDIS query OID completion */
105
+struct rndis_query_completion {
106
+	/** Request ID */
107
+	uint32_t id;
108
+	/** Status */
109
+	uint32_t status;
110
+	/** Information buffer length */
111
+	uint32_t len;
112
+	/** Information buffer offset */
113
+	uint32_t offset;
114
+} __attribute__ (( packed ));
115
+
116
+/** RNDIS set OID completion */
117
+#define RNDIS_SET_CMPLT 0x80000005UL
118
+
119
+/** RNDIS set OID completion */
120
+struct rndis_set_completion {
121
+	/** Request ID */
122
+	uint32_t id;
123
+	/** Status */
124
+	uint32_t status;
125
+} __attribute__ (( packed ));
126
+
127
+/** RNDIS reset message */
128
+#define RNDIS_RESET_MSG 0x00000006UL
129
+
130
+/** RNDIS reset message */
131
+struct rndis_reset_message {
132
+	/** Reserved */
133
+	uint32_t reserved;
134
+} __attribute__ (( packed ));
135
+
136
+/** RNDIS reset completion */
137
+#define RNDIS_RESET_CMPLT 0x80000006UL
138
+
139
+/** RNDIS reset completion */
140
+struct rndis_reset_completion {
141
+	/** Status */
142
+	uint32_t status;
143
+	/** Addressing reset */
144
+	uint32_t addr;
145
+} __attribute__ (( packed ));
146
+
147
+/** RNDIS indicate status message */
148
+#define RNDIS_INDICATE_STATUS_MSG 0x00000007UL
149
+
150
+/** RNDIS diagnostic information */
151
+struct rndis_diagnostic_info {
152
+	/** Status */
153
+	uint32_t status;
154
+	/** Error offset */
155
+	uint32_t offset;
156
+} __attribute__ (( packed ));
157
+
158
+/** RNDIS indicate status message */
159
+struct rndis_indicate_status_message {
160
+	/** Status */
161
+	uint32_t status;
162
+	/** Status buffer length */
163
+	uint32_t len;
164
+	/** Status buffer offset */
165
+	uint32_t offset;
166
+	/** Diagnostic information (optional) */
167
+	struct rndis_diagnostic_info diag[0];
168
+} __attribute__ (( packed ));
169
+
170
+/** RNDIS status codes */
171
+enum rndis_status {
172
+	/** Device is connected to a network medium */
173
+	RNDIS_STATUS_MEDIA_CONNECT = 0x4001000bUL,
174
+	/** Device is disconnected from the medium */
175
+	RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000cUL,
176
+};
177
+
178
+/** RNDIS keepalive message */
179
+#define RNDIS_KEEPALIVE_MSG 0x00000008UL
180
+
181
+/** RNDIS keepalive message */
182
+struct rndis_keepalive_message {
183
+	/** Request ID */
184
+	uint32_t id;
185
+} __attribute__ (( packed ));
186
+
187
+/** RNDIS keepalive completion */
188
+#define RNDIS_KEEPALIVE_CMPLT 0x80000008UL
189
+
190
+/** RNDIS keepalive completion */
191
+struct rndis_keepalive_completion {
192
+	/** Request ID */
193
+	uint32_t id;
194
+	/** Status */
195
+	uint32_t status;
196
+} __attribute__ (( packed ));
197
+
198
+/** RNDIS packet message */
199
+#define RNDIS_PACKET_MSG 0x00000001UL
200
+
201
+/** RNDIS packet field */
202
+struct rndis_packet_field {
203
+	/** Offset */
204
+	uint32_t offset;
205
+	/** Length */
206
+	uint32_t len;
207
+} __attribute__ (( packed ));
208
+
209
+/** RNDIS packet message */
210
+struct rndis_packet_message {
211
+	/** Data */
212
+	struct rndis_packet_field data;
213
+	/** Out-of-band data records */
214
+	struct rndis_packet_field oob;
215
+	/** Number of out-of-band data records */
216
+	uint32_t oob_count;
217
+	/** Per-packet information record */
218
+	struct rndis_packet_field ppi;
219
+	/** Reserved */
220
+	uint32_t reserved;
221
+} __attribute__ (( packed ));
222
+
223
+/** RNDIS packet record */
224
+struct rndis_packet_record {
225
+	/** Length */
226
+	uint32_t len;
227
+	/** Type */
228
+	uint32_t type;
229
+	/** Offset */
230
+	uint32_t offset;
231
+} __attribute__ (( packed ));
232
+
233
+/** OID for packet filter */
234
+#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010eUL
235
+
236
+/** Packet filter bits */
237
+enum rndis_packet_filter {
238
+	/** Unicast packets */
239
+	RNDIS_FILTER_UNICAST = 0x00000001UL,
240
+	/** Multicast packets */
241
+	RNDIS_FILTER_MULTICAST = 0x00000002UL,
242
+	/** All multicast packets */
243
+	RNDIS_FILTER_ALL_MULTICAST = 0x00000004UL,
244
+	/** Broadcast packets */
245
+	RNDIS_FILTER_BROADCAST = 0x00000008UL,
246
+	/** All packets */
247
+	RNDIS_FILTER_PROMISCUOUS = 0x00000020UL
248
+};
249
+
250
+/** OID for media status */
251
+#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114UL
252
+
253
+/** OID for permanent MAC address */
254
+#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101UL
255
+
256
+/** OID for current MAC address */
257
+#define RNDIS_OID_802_3_CURRENT_ADDRESS	0x01010102UL
258
+
259
+struct rndis_device;
260
+
261
+/** RNDIS device operations */
262
+struct rndis_operations {
263
+	/**
264
+	 * Open RNDIS device
265
+	 *
266
+	 * @v rndis		RNDIS device
267
+	 * @ret rc		Return status code
268
+	 */
269
+	int ( * open ) ( struct rndis_device *rndis );
270
+	/**
271
+	 * Close RNDIS device
272
+	 *
273
+	 * @v rndis		RNDIS device
274
+	 */
275
+	void ( * close ) ( struct rndis_device *rndis );
276
+	/**
277
+	 * Transmit packet
278
+	 *
279
+	 * @v rndis		RNDIS device
280
+	 * @v iobuf		I/O buffer
281
+	 * @ret rc		Return status code
282
+	 *
283
+	 * If this method returns success then the RNDIS device must
284
+	 * eventually report completion via rndis_tx_complete().
285
+	 */
286
+	int ( * transmit ) ( struct rndis_device *rndis,
287
+			     struct io_buffer *iobuf );
288
+	/**
289
+	 * Poll for completed and received packets
290
+	 *
291
+	 * @v rndis		RNDIS device
292
+	 */
293
+	void ( * poll ) ( struct rndis_device *rndis );
294
+};
295
+
296
+/** An RNDIS device */
297
+struct rndis_device {
298
+	/** Network device */
299
+	struct net_device *netdev;
300
+	/** Device name */
301
+	const char *name;
302
+	/** RNDIS operations */
303
+	struct rndis_operations *op;
304
+	/** Driver private data */
305
+	void *priv;
306
+
307
+	/** Request ID for current blocking request */
308
+	unsigned int wait_id;
309
+	/** Return status code for current blocking request */
310
+	int wait_rc;
311
+};
312
+
313
+/**
314
+ * Initialise an RNDIS device
315
+ *
316
+ * @v rndis		RNDIS device
317
+ * @v op		RNDIS device operations
318
+ */
319
+static inline void rndis_init ( struct rndis_device *rndis,
320
+				struct rndis_operations *op ) {
321
+
322
+	rndis->op = op;
323
+}
324
+
325
+extern void rndis_tx_complete_err ( struct rndis_device *rndis,
326
+				    struct io_buffer *iobuf, int rc );
327
+extern int rndis_tx_defer ( struct rndis_device *rndis,
328
+			    struct io_buffer *iobuf );
329
+extern void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf );
330
+
331
+extern struct rndis_device * alloc_rndis ( size_t priv_len );
332
+extern int register_rndis ( struct rndis_device *rndis );
333
+extern void unregister_rndis ( struct rndis_device *rndis );
334
+extern void free_rndis ( struct rndis_device *rndis );
335
+
336
+/**
337
+ * Complete message transmission
338
+ *
339
+ * @v rndis		RNDIS device
340
+ * @v iobuf		I/O buffer
341
+ */
342
+static inline void rndis_tx_complete ( struct rndis_device *rndis,
343
+				       struct io_buffer *iobuf ) {
344
+
345
+	rndis_tx_complete_err ( rndis, iobuf, 0 );
346
+}
347
+
348
+#endif /* _IPXE_RNDIS_H */

+ 850
- 0
src/net/rndis.c Datei anzeigen

@@ -0,0 +1,850 @@
1
+/*
2
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+/** @file
23
+ *
24
+ * Remote Network Driver Interface Specification
25
+ *
26
+ */
27
+
28
+#include <unistd.h>
29
+#include <string.h>
30
+#include <errno.h>
31
+#include <byteswap.h>
32
+#include <ipxe/iobuf.h>
33
+#include <ipxe/netdevice.h>
34
+#include <ipxe/ethernet.h>
35
+#include <ipxe/device.h>
36
+#include <ipxe/rndis.h>
37
+
38
+/**
39
+ * Allocate I/O buffer
40
+ *
41
+ * @v len		Length
42
+ * @ret iobuf		I/O buffer, or NULL
43
+ */
44
+static struct io_buffer * rndis_alloc_iob ( size_t len ) {
45
+	struct rndis_header *header;
46
+	struct io_buffer *iobuf;
47
+
48
+	/* Allocate I/O buffer and reserve space */
49
+	iobuf = alloc_iob ( sizeof ( *header ) + len );
50
+	if ( iobuf )
51
+		iob_reserve ( iobuf, sizeof ( *header ) );
52
+
53
+	return iobuf;
54
+}
55
+
56
+/**
57
+ * Transmit message
58
+ *
59
+ * @v rndis		RNDIS device
60
+ * @v iobuf		I/O buffer
61
+ * @v type		Message type
62
+ * @ret rc		Return status code
63
+ */
64
+static int rndis_tx_message ( struct rndis_device *rndis,
65
+			      struct io_buffer *iobuf, unsigned int type ) {
66
+	struct rndis_header *header;
67
+	int rc;
68
+
69
+	/* Prepend RNDIS header */
70
+	header = iob_push ( iobuf, sizeof ( *header ) );
71
+	header->type = cpu_to_le32 ( type );
72
+	header->len = cpu_to_le32 ( iob_len ( iobuf ) );
73
+
74
+	/* Transmit message */
75
+	if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) {
76
+		DBGC ( rndis, "RNDIS %s could not transmit: %s\n",
77
+		       rndis->name, strerror ( rc ) );
78
+		return rc;
79
+	}
80
+
81
+	return 0;
82
+}
83
+
84
+/**
85
+ * Complete message transmission
86
+ *
87
+ * @v rndis		RNDIS device
88
+ * @v iobuf		I/O buffer
89
+ * @v rc		Packet status code
90
+ */
91
+void rndis_tx_complete_err ( struct rndis_device *rndis,
92
+			     struct io_buffer *iobuf, int rc ) {
93
+	struct net_device *netdev = rndis->netdev;
94
+	struct rndis_header *header;
95
+	size_t len = iob_len ( iobuf );
96
+
97
+	/* Sanity check */
98
+	if ( len < sizeof ( *header ) ) {
99
+		DBGC ( rndis, "RNDIS %s completed underlength transmission:\n",
100
+		       rndis->name );
101
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
102
+		netdev_tx_err ( netdev, NULL, -EINVAL );
103
+		return;
104
+	}
105
+	header = iobuf->data;
106
+
107
+	/* Complete buffer */
108
+	if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
109
+		netdev_tx_complete_err ( netdev, iobuf, rc );
110
+	} else {
111
+		free_iob ( iobuf );
112
+	}
113
+}
114
+
115
+/**
116
+ * Transmit data packet
117
+ *
118
+ * @v rndis		RNDIS device
119
+ * @v iobuf		I/O buffer
120
+ * @ret rc		Return status code
121
+ */
122
+static int rndis_tx_data ( struct rndis_device *rndis,
123
+			   struct io_buffer *iobuf ) {
124
+	struct rndis_packet_message *msg;
125
+	size_t len = iob_len ( iobuf );
126
+	int rc;
127
+
128
+	/* Prepend packet message header */
129
+	msg = iob_push ( iobuf, sizeof ( *msg ) );
130
+	memset ( msg, 0, sizeof ( *msg ) );
131
+	msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) );
132
+	msg->data.len = cpu_to_le32 ( len );
133
+
134
+	/* Transmit message */
135
+	if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 )
136
+		return rc;
137
+
138
+	return 0;
139
+}
140
+
141
+/**
142
+ * Defer transmitted packet
143
+ *
144
+ * @v rndis		RNDIS device
145
+ * @v iobuf		I/O buffer
146
+ * @ret rc		Return status code
147
+ *
148
+ * As with netdev_tx_defer(), the caller must ensure that space in the
149
+ * transmit descriptor ring is freed up before calling
150
+ * rndis_tx_complete().
151
+ *
152
+ * Unlike netdev_tx_defer(), this call may fail.
153
+ */
154
+int rndis_tx_defer ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
155
+	struct net_device *netdev = rndis->netdev;
156
+	struct rndis_header *header;
157
+	struct rndis_packet_message *msg;
158
+
159
+	/* Fail unless this was a packet message.  Only packet
160
+	 * messages correspond to I/O buffers in the network device's
161
+	 * TX queue; other messages cannot be deferred in this way.
162
+	 */
163
+	assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
164
+	header = iobuf->data;
165
+	if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) )
166
+		return -ENOTSUP;
167
+
168
+	/* Strip RNDIS header and packet message header, to return
169
+	 * this packet to the state in which we received it.
170
+	 */
171
+	iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) );
172
+
173
+	/* Defer packet */
174
+	netdev_tx_defer ( netdev, iobuf );
175
+
176
+	return 0;
177
+}
178
+
179
+/**
180
+ * Receive data packet
181
+ *
182
+ * @v rndis		RNDIS device
183
+ * @v iobuf		I/O buffer
184
+ */
185
+static void rndis_rx_data ( struct rndis_device *rndis,
186
+			    struct io_buffer *iobuf ) {
187
+	struct net_device *netdev = rndis->netdev;
188
+	struct rndis_packet_message *msg;
189
+	size_t len = iob_len ( iobuf );
190
+	size_t data_offset;
191
+	size_t data_len;
192
+	int rc;
193
+
194
+	/* Sanity check */
195
+	if ( len < sizeof ( *msg ) ) {
196
+		DBGC ( rndis, "RNDIS %s received underlength data packet:\n",
197
+		       rndis->name );
198
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
199
+		rc = -EINVAL;
200
+		goto err_len;
201
+	}
202
+	msg = iobuf->data;
203
+
204
+	/* Locate and sanity check data buffer */
205
+	data_offset = le32_to_cpu ( msg->data.offset );
206
+	data_len = le32_to_cpu ( msg->data.len );
207
+	if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) {
208
+		DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n",
209
+		       rndis->name );
210
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
211
+		rc = -EINVAL;
212
+		goto err_data;
213
+	}
214
+
215
+	/* Strip non-data portions */
216
+	iob_pull ( iobuf, data_offset );
217
+	iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) );
218
+
219
+	/* Hand off to network stack */
220
+	netdev_rx ( netdev, iob_disown ( iobuf ) );
221
+
222
+	return;
223
+
224
+ err_data:
225
+ err_len:
226
+	/* Report error to network stack */
227
+	netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
228
+}
229
+
230
+/**
231
+ * Transmit OID message
232
+ *
233
+ * @v rndis		RNDIS device
234
+ * @v oid		Object ID
235
+ * @v data		New OID value (or NULL to query current value)
236
+ * @v len		Length of new OID value
237
+ * @ret rc		Return status code
238
+ */
239
+static int rndis_tx_oid ( struct rndis_device *rndis, unsigned int oid,
240
+			  const void *data, size_t len ) {
241
+	struct io_buffer *iobuf;
242
+	struct rndis_oid_message *msg;
243
+	unsigned int type;
244
+	int rc;
245
+
246
+	/* Allocate I/O buffer */
247
+	iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len );
248
+	if ( ! iobuf ) {
249
+		rc = -ENOMEM;
250
+		goto err_alloc;
251
+	}
252
+
253
+	/* Construct message.  We use the OID as the request ID. */
254
+	msg = iob_put ( iobuf, sizeof ( *msg ) );
255
+	memset ( msg, 0, sizeof ( *msg ) );
256
+	msg->id = oid; /* Non-endian */
257
+	msg->oid = cpu_to_le32 ( oid );
258
+	msg->offset = cpu_to_le32 ( sizeof ( *msg ) );
259
+	msg->len = cpu_to_le32 ( len );
260
+	memcpy ( iob_put ( iobuf, len ), data, len );
261
+
262
+	/* Transmit message */
263
+	type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG );
264
+	if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 )
265
+		goto err_tx;
266
+
267
+	return 0;
268
+
269
+ err_tx:
270
+	free_iob ( iobuf );
271
+ err_alloc:
272
+	return rc;
273
+}
274
+
275
+/**
276
+ * Receive query OID completion
277
+ *
278
+ * @v rndis		RNDIS device
279
+ * @v iobuf		I/O buffer
280
+ */
281
+static void rndis_rx_query_oid ( struct rndis_device *rndis,
282
+				 struct io_buffer *iobuf ) {
283
+	struct net_device *netdev = rndis->netdev;
284
+	struct rndis_query_completion *cmplt;
285
+	size_t len = iob_len ( iobuf );
286
+	size_t info_offset;
287
+	size_t info_len;
288
+	unsigned int id;
289
+	void *info;
290
+	uint32_t *link_status;
291
+	int rc;
292
+
293
+	/* Sanity check */
294
+	if ( len < sizeof ( *cmplt ) ) {
295
+		DBGC ( rndis, "RNDIS %s received underlength query "
296
+		       "completion:\n", rndis->name );
297
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
298
+		rc = -EINVAL;
299
+		goto err_len;
300
+	}
301
+	cmplt = iobuf->data;
302
+
303
+	/* Extract request ID */
304
+	id = cmplt->id; /* Non-endian */
305
+
306
+	/* Check status */
307
+	if ( cmplt->status ) {
308
+		DBGC ( rndis, "RNDIS %s received query completion failure "
309
+		       "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
310
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
311
+		rc = -EIO;
312
+		goto err_status;
313
+	}
314
+
315
+	/* Locate and sanity check information buffer */
316
+	info_offset = le32_to_cpu ( cmplt->offset );
317
+	info_len = le32_to_cpu ( cmplt->len );
318
+	if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) {
319
+		DBGC ( rndis, "RNDIS %s query completion information exceeds "
320
+		       "packet:\n", rndis->name );
321
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
322
+		rc = -EINVAL;
323
+		goto err_info;
324
+	}
325
+	info = ( ( ( void * ) cmplt ) + info_offset );
326
+
327
+	/* Handle OID */
328
+	switch ( id ) {
329
+
330
+	case RNDIS_OID_802_3_PERMANENT_ADDRESS:
331
+		if ( info_len > sizeof ( netdev->hw_addr ) )
332
+			info_len = sizeof ( netdev->hw_addr );
333
+		memcpy ( netdev->hw_addr, info, info_len );
334
+		break;
335
+
336
+	case RNDIS_OID_802_3_CURRENT_ADDRESS:
337
+		if ( info_len > sizeof ( netdev->ll_addr ) )
338
+			info_len = sizeof ( netdev->ll_addr );
339
+		memcpy ( netdev->ll_addr, info, info_len );
340
+		break;
341
+
342
+	case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
343
+		if ( info_len != sizeof ( *link_status ) ) {
344
+			DBGC ( rndis, "RNDIS %s invalid link status:\n",
345
+			       rndis->name );
346
+			DBGC_HDA ( rndis, 0, iobuf->data, len );
347
+			rc = -EPROTO;
348
+			goto err_link_status;
349
+		}
350
+		link_status = info;
351
+		if ( *link_status == 0 ) {
352
+			DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
353
+			netdev_link_up ( netdev );
354
+		} else {
355
+			DBGC ( rndis, "RNDIS %s link is down: %#08x\n",
356
+			       rndis->name, le32_to_cpu ( *link_status ) );
357
+			netdev_link_down ( netdev );
358
+		}
359
+		break;
360
+
361
+	default:
362
+		DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n",
363
+		       rndis->name, id );
364
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
365
+		rc = -EPROTO;
366
+		goto err_id;
367
+	}
368
+
369
+	/* Success */
370
+	rc = 0;
371
+
372
+ err_id:
373
+ err_link_status:
374
+ err_info:
375
+ err_status:
376
+	/* Record completion result if applicable */
377
+	if ( id == rndis->wait_id ) {
378
+		rndis->wait_id = 0;
379
+		rndis->wait_rc = rc;
380
+	}
381
+ err_len:
382
+	/* Free I/O buffer */
383
+	free_iob ( iobuf );
384
+}
385
+
386
+/**
387
+ * Receive set OID completion
388
+ *
389
+ * @v rndis		RNDIS device
390
+ * @v iobuf		I/O buffer
391
+ */
392
+static void rndis_rx_set_oid ( struct rndis_device *rndis,
393
+			       struct io_buffer *iobuf ) {
394
+	struct rndis_set_completion *cmplt;
395
+	size_t len = iob_len ( iobuf );
396
+	unsigned int id;
397
+	int rc;
398
+
399
+	/* Sanity check */
400
+	if ( len < sizeof ( *cmplt ) ) {
401
+		DBGC ( rndis, "RNDIS %s received underlength set completion:\n",
402
+		       rndis->name );
403
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
404
+		rc = -EINVAL;
405
+		goto err_len;
406
+	}
407
+	cmplt = iobuf->data;
408
+
409
+	/* Extract request ID */
410
+	id = cmplt->id; /* Non-endian */
411
+
412
+	/* Check status */
413
+	if ( cmplt->status ) {
414
+		DBGC ( rndis, "RNDIS %s received set completion failure "
415
+		       "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) );
416
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
417
+		rc = -EIO;
418
+		goto err_status;
419
+	}
420
+
421
+	/* Success */
422
+	rc = 0;
423
+
424
+ err_status:
425
+	/* Record completion result if applicable */
426
+	if ( id == rndis->wait_id ) {
427
+		rndis->wait_id = 0;
428
+		rndis->wait_rc = rc;
429
+	}
430
+ err_len:
431
+	/* Free I/O buffer */
432
+	free_iob ( iobuf );
433
+}
434
+
435
+/**
436
+ * Query or set OID
437
+ *
438
+ * @v rndis		RNDIS device
439
+ * @v oid		Object ID
440
+ * @v data		New OID value (or NULL to query current value)
441
+ * @v len		Length of new OID value
442
+ * @ret rc		Return status code
443
+ */
444
+static int rndis_oid ( struct rndis_device *rndis, unsigned int oid,
445
+		       const void *data, size_t len ) {
446
+	unsigned int i;
447
+	int rc;
448
+
449
+	/* Transmit query */
450
+	if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 )
451
+		return rc;
452
+
453
+	/* Record query ID */
454
+	rndis->wait_id = oid;
455
+
456
+	/* Wait for operation to complete */
457
+	for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) {
458
+
459
+		/* Check for completion */
460
+		if ( ! rndis->wait_id )
461
+			return rndis->wait_rc;
462
+
463
+		/* Poll RNDIS device */
464
+		rndis->op->poll ( rndis );
465
+
466
+		/* Delay for 1ms */
467
+		mdelay ( 1 );
468
+	}
469
+
470
+	DBGC ( rndis, "RNDIS %s timed out waiting for OID %#08x\n",
471
+	       rndis->name, oid );
472
+	return -ETIMEDOUT;
473
+}
474
+
475
+/**
476
+ * Receive indicate status message
477
+ *
478
+ * @v rndis		RNDIS device
479
+ * @v iobuf		I/O buffer
480
+ */
481
+static void rndis_rx_status ( struct rndis_device *rndis,
482
+			      struct io_buffer *iobuf ) {
483
+	struct net_device *netdev = rndis->netdev;
484
+	struct rndis_indicate_status_message *msg;
485
+	size_t len = iob_len ( iobuf );
486
+	unsigned int status;
487
+	int rc;
488
+
489
+	/* Sanity check */
490
+	if ( len < sizeof ( *msg ) ) {
491
+		DBGC ( rndis, "RNDIS %s received underlength status message:\n",
492
+		       rndis->name );
493
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
494
+		rc = -EINVAL;
495
+		goto err_len;
496
+	}
497
+	msg = iobuf->data;
498
+
499
+	/* Extract status */
500
+	status = le32_to_cpu ( msg->status );
501
+
502
+	/* Handle status */
503
+	switch ( msg->status ) {
504
+
505
+	case RNDIS_STATUS_MEDIA_CONNECT:
506
+		DBGC ( rndis, "RNDIS %s link is up\n", rndis->name );
507
+		netdev_link_up ( netdev );
508
+		break;
509
+
510
+	case RNDIS_STATUS_MEDIA_DISCONNECT:
511
+		DBGC ( rndis, "RNDIS %s link is down\n", rndis->name );
512
+		netdev_link_down ( netdev );
513
+		break;
514
+
515
+	default:
516
+		DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n",
517
+		       rndis->name, status );
518
+		DBGC_HDA ( rndis, 0, iobuf->data, len );
519
+		rc = -ENOTSUP;
520
+		goto err_status;
521
+	}
522
+
523
+	/* Free I/O buffer */
524
+	free_iob ( iobuf );
525
+
526
+	return;
527
+
528
+ err_status:
529
+ err_len:
530
+	/* Report error via network device statistics */
531
+	netdev_rx_err ( netdev, iobuf, rc );
532
+}
533
+
534
+/**
535
+ * Receive RNDIS message
536
+ *
537
+ * @v rndis		RNDIS device
538
+ * @v iobuf		I/O buffer
539
+ * @v type		Message type
540
+ */
541
+static void rndis_rx_message ( struct rndis_device *rndis,
542
+			       struct io_buffer *iobuf, unsigned int type ) {
543
+	struct net_device *netdev = rndis->netdev;
544
+	int rc;
545
+
546
+	/* Handle packet */
547
+	switch ( type ) {
548
+
549
+	case RNDIS_PACKET_MSG:
550
+		rndis_rx_data ( rndis, iob_disown ( iobuf ) );
551
+		break;
552
+
553
+	case RNDIS_QUERY_CMPLT:
554
+		rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) );
555
+		break;
556
+
557
+	case RNDIS_SET_CMPLT:
558
+		rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) );
559
+		break;
560
+
561
+	case RNDIS_INDICATE_STATUS_MSG:
562
+		rndis_rx_status ( rndis, iob_disown ( iobuf ) );
563
+		break;
564
+
565
+	default:
566
+		DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n",
567
+		       rndis->name, type );
568
+		DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
569
+		rc = -EPROTO;
570
+		goto err_type;
571
+	}
572
+
573
+	return;
574
+
575
+ err_type:
576
+	/* Report error via network device statistics */
577
+	netdev_rx_err ( netdev, iobuf, rc );
578
+}
579
+
580
+/**
581
+ * Receive packet from underlying transport layer
582
+ *
583
+ * @v rndis		RNDIS device
584
+ * @v iobuf		I/O buffer, or NULL if allocation failed
585
+ */
586
+void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) {
587
+	struct net_device *netdev = rndis->netdev;
588
+	struct rndis_header *header;
589
+	struct io_buffer *msg;
590
+	size_t len;
591
+	unsigned int type;
592
+	int rc;
593
+
594
+	/* Record dropped packet if I/O buffer is missing */
595
+	if ( ! iobuf ) {
596
+		DBGC2 ( rndis, "RNDIS %s received dropped packet\n",
597
+			rndis->name );
598
+		rc = -ENOBUFS;
599
+		goto drop;
600
+	}
601
+
602
+	/* Split packet into messages */
603
+	while ( iobuf ) {
604
+
605
+		/* Sanity check */
606
+		if ( iob_len ( iobuf ) < sizeof ( *header ) ) {
607
+			DBGC ( rndis, "RNDIS %s received underlength packet:\n",
608
+			       rndis->name );
609
+			DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
610
+			rc = -EINVAL;
611
+			goto drop;
612
+		}
613
+		header = iobuf->data;
614
+
615
+		/* Parse and check header */
616
+		type = le32_to_cpu ( header->type );
617
+		len = le32_to_cpu ( header->len );
618
+		if ( len > iob_len ( iobuf ) ) {
619
+			DBGC ( rndis, "RNDIS %s received underlength packet:\n",
620
+			       rndis->name );
621
+			DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) );
622
+			rc = -EINVAL;
623
+			goto drop;
624
+		}
625
+
626
+		/* Split buffer if required */
627
+		if ( len < iob_len ( iobuf ) ) {
628
+			msg = iob_split ( iobuf, len );
629
+			if ( ! msg ) {
630
+				rc = -ENOMEM;
631
+				goto drop;
632
+			}
633
+		} else {
634
+			msg = iobuf;
635
+			iobuf = NULL;
636
+		}
637
+
638
+		/* Strip header */
639
+		iob_pull ( msg, sizeof ( *header ) );
640
+
641
+		/* Handle message */
642
+		rndis_rx_message ( rndis, iob_disown ( msg ), type );
643
+	}
644
+
645
+	return;
646
+
647
+ drop:
648
+	/* Record error */
649
+	netdev_rx_err ( netdev, iob_disown ( iobuf ), rc );
650
+}
651
+
652
+/**
653
+ * Open network device
654
+ *
655
+ * @v netdev		Network device
656
+ * @ret rc		Return status code
657
+ */
658
+static int rndis_open ( struct net_device *netdev ) {
659
+	struct rndis_device *rndis = netdev->priv;
660
+	uint32_t filter;
661
+	int rc;
662
+
663
+	/* Open RNDIS device */
664
+	if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
665
+		DBGC ( rndis, "RNDIS %s could not open: %s\n",
666
+		       rndis->name, strerror ( rc ) );
667
+		goto err_open;
668
+	}
669
+
670
+	/* Set receive filter */
671
+	filter = cpu_to_le32 ( RNDIS_FILTER_UNICAST |
672
+			       RNDIS_FILTER_MULTICAST |
673
+			       RNDIS_FILTER_ALL_MULTICAST |
674
+			       RNDIS_FILTER_BROADCAST |
675
+			       RNDIS_FILTER_PROMISCUOUS );
676
+	if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
677
+				&filter, sizeof ( filter ) ) ) != 0 ) {
678
+		DBGC ( rndis, "RNDIS %s could not set receive filter: %s\n",
679
+		       rndis->name, strerror ( rc ) );
680
+		goto err_set_filter;
681
+	}
682
+
683
+	/* Update link status */
684
+	if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
685
+				NULL, 0 ) ) != 0 )
686
+		goto err_query_link;
687
+
688
+	return 0;
689
+
690
+ err_query_link:
691
+ err_set_filter:
692
+	rndis->op->close ( rndis );
693
+ err_open:
694
+	return rc;
695
+}
696
+
697
+/**
698
+ * Close network device
699
+ *
700
+ * @v netdev		Network device
701
+ */
702
+static void rndis_close ( struct net_device *netdev ) {
703
+	struct rndis_device *rndis = netdev->priv;
704
+
705
+	/* Close RNDIS device */
706
+	rndis->op->close ( rndis );
707
+}
708
+
709
+/**
710
+ * Transmit packet
711
+ *
712
+ * @v netdev		Network device
713
+ * @v iobuf		I/O buffer
714
+ * @ret rc		Return status code
715
+ */
716
+static int rndis_transmit ( struct net_device *netdev,
717
+			    struct io_buffer *iobuf ) {
718
+	struct rndis_device *rndis = netdev->priv;
719
+
720
+	/* Transmit data packet */
721
+	return rndis_tx_data ( rndis, iobuf );
722
+}
723
+
724
+/**
725
+ * Poll for completed and received packets
726
+ *
727
+ * @v netdev		Network device
728
+ */
729
+static void rndis_poll ( struct net_device *netdev ) {
730
+	struct rndis_device *rndis = netdev->priv;
731
+
732
+	/* Poll RNDIS device */
733
+	rndis->op->poll ( rndis );
734
+}
735
+
736
+/** Network device operations */
737
+static struct net_device_operations rndis_operations = {
738
+	.open		= rndis_open,
739
+	.close		= rndis_close,
740
+	.transmit	= rndis_transmit,
741
+	.poll		= rndis_poll,
742
+};
743
+
744
+/**
745
+ * Allocate RNDIS device
746
+ *
747
+ * @v priv_len		Length of private data
748
+ * @ret rndis		RNDIS device, or NULL on allocation failure
749
+ */
750
+struct rndis_device * alloc_rndis ( size_t priv_len ) {
751
+	struct net_device *netdev;
752
+	struct rndis_device *rndis;
753
+
754
+	/* Allocate and initialise structure */
755
+	netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len );
756
+	if ( ! netdev )
757
+		return NULL;
758
+	netdev_init ( netdev, &rndis_operations );
759
+	rndis = netdev->priv;
760
+	rndis->netdev = netdev;
761
+	rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) );
762
+
763
+	return rndis;
764
+}
765
+
766
+/**
767
+ * Register RNDIS device
768
+ *
769
+ * @v rndis		RNDIS device
770
+ * @ret rc		Return status code
771
+ *
772
+ * Note that this routine will open and use the RNDIS device in order
773
+ * to query the MAC address.  The device must be immediately ready for
774
+ * use prior to registration.
775
+ */
776
+int register_rndis ( struct rndis_device *rndis ) {
777
+	struct net_device *netdev = rndis->netdev;
778
+	int rc;
779
+
780
+	/* Assign device name (for debugging) */
781
+	rndis->name = netdev->dev->name;
782
+
783
+	/* Register network device */
784
+	if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
785
+		DBGC ( rndis, "RNDIS %s could not register: %s\n",
786
+		       rndis->name, strerror ( rc ) );
787
+		goto err_register;
788
+	}
789
+
790
+	/* Open RNDIS device to read MAC addresses */
791
+	if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) {
792
+		DBGC ( rndis, "RNDIS %s could not open: %s\n",
793
+		       rndis->name, strerror ( rc ) );
794
+		goto err_open;
795
+	}
796
+
797
+	/* Query permanent MAC address */
798
+	if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS,
799
+				NULL, 0 ) ) != 0 )
800
+		goto err_query_permanent;
801
+
802
+	/* Query current MAC address */
803
+	if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS,
804
+				NULL, 0 ) ) != 0 )
805
+		goto err_query_current;
806
+
807
+	/* Get link status */
808
+	if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
809
+				NULL, 0 ) ) != 0 )
810
+		goto err_query_link;
811
+
812
+	/* Close RNDIS device */
813
+	rndis->op->close ( rndis );
814
+
815
+	return 0;
816
+
817
+ err_query_link:
818
+ err_query_current:
819
+ err_query_permanent:
820
+	rndis->op->close ( rndis );
821
+ err_open:
822
+	unregister_netdev ( netdev );
823
+ err_register:
824
+	return rc;
825
+}
826
+
827
+/**
828
+ * Unregister RNDIS device
829
+ *
830
+ * @v rndis		RNDIS device
831
+ */
832
+void unregister_rndis ( struct rndis_device *rndis ) {
833
+	struct net_device *netdev = rndis->netdev;
834
+
835
+	/* Unregister network device */
836
+	unregister_netdev ( netdev );
837
+}
838
+
839
+/**
840
+ * Free RNDIS device
841
+ *
842
+ * @v rndis		RNDIS device
843
+ */
844
+void free_rndis ( struct rndis_device *rndis ) {
845
+	struct net_device *netdev = rndis->netdev;
846
+
847
+	/* Free network device */
848
+	netdev_nullify ( netdev );
849
+	netdev_put ( netdev );
850
+}

Laden…
Abbrechen
Speichern