Browse Source

[ncm] Add support for CDC-NCM USB Ethernet devices

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
cc5a27f9cb
6 changed files with 1198 additions and 0 deletions
  1. 89
    0
      src/drivers/net/ecm.c
  2. 40
    0
      src/drivers/net/ecm.h
  3. 843
    0
      src/drivers/net/ncm.c
  4. 186
    0
      src/drivers/net/ncm.h
  5. 38
    0
      src/include/ipxe/cdc.h
  6. 2
    0
      src/include/ipxe/errfile.h

+ 89
- 0
src/drivers/net/ecm.c View File

@@ -0,0 +1,89 @@
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
+#include <stdint.h>
23
+#include <errno.h>
24
+#include <ipxe/if_ether.h>
25
+#include <ipxe/base16.h>
26
+#include <ipxe/usb.h>
27
+#include "ecm.h"
28
+
29
+/** @file
30
+ *
31
+ * CDC-ECM USB Ethernet driver
32
+ *
33
+ */
34
+
35
+/**
36
+ * Locate Ethernet functional descriptor
37
+ *
38
+ * @v config		Configuration descriptor
39
+ * @v interface		Interface descriptor
40
+ * @ret desc		Descriptor, or NULL if not found
41
+ */
42
+struct ecm_ethernet_descriptor *
43
+ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
44
+			  struct usb_interface_descriptor *interface ) {
45
+	struct ecm_ethernet_descriptor *desc;
46
+
47
+	for_each_interface_descriptor ( desc, config, interface ) {
48
+		if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) &&
49
+		     ( desc->subtype == CDC_SUBTYPE_ETHERNET ) )
50
+			return desc;
51
+	}
52
+	return NULL;
53
+}
54
+
55
+/**
56
+ * Get hardware MAC address
57
+ *
58
+ * @v usb		USB device
59
+ * @v desc		Ethernet functional descriptor
60
+ * @v hw_addr		Hardware address to fill in
61
+ * @ret rc		Return status code
62
+ */
63
+int ecm_fetch_mac ( struct usb_device *usb,
64
+		    struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) {
65
+	char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ];
66
+	int len;
67
+	int rc;
68
+
69
+	/* Fetch MAC address string */
70
+	len = usb_get_string_descriptor ( usb, desc->mac, 0, buf,
71
+					  sizeof ( buf ) );
72
+	if ( len < 0 ) {
73
+		rc = len;
74
+		return rc;
75
+	}
76
+
77
+	/* Sanity check */
78
+	if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) )
79
+		return -EINVAL;
80
+
81
+	/* Decode MAC address */
82
+	len = base16_decode ( buf, hw_addr );
83
+	if ( len < 0 ) {
84
+		rc = len;
85
+		return rc;
86
+	}
87
+
88
+	return 0;
89
+}

+ 40
- 0
src/drivers/net/ecm.h View File

@@ -0,0 +1,40 @@
1
+#ifndef _ECM_H
2
+#define _ECM_H
3
+
4
+/** @file
5
+ *
6
+ * CDC-ECM USB Ethernet driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/usb.h>
13
+#include <ipxe/cdc.h>
14
+
15
+/** An Ethernet Functional Descriptor */
16
+struct ecm_ethernet_descriptor {
17
+	/** Descriptor header */
18
+	struct usb_descriptor_header header;
19
+	/** Descriptor subtype */
20
+	uint8_t subtype;
21
+	/** MAC addres string */
22
+	uint8_t mac;
23
+	/** Ethernet statistics bitmap */
24
+	uint32_t statistics;
25
+	/** Maximum segment size */
26
+	uint16_t mtu;
27
+	/** Multicast filter configuration */
28
+	uint16_t mcast;
29
+	/** Number of wake-on-LAN filters */
30
+	uint8_t wol;
31
+} __attribute__ (( packed ));
32
+
33
+extern struct ecm_ethernet_descriptor *
34
+ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config,
35
+			  struct usb_interface_descriptor *interface );
36
+extern int ecm_fetch_mac ( struct usb_device *usb,
37
+			   struct ecm_ethernet_descriptor *desc,
38
+			   uint8_t *hw_addr );
39
+
40
+#endif /* _ECM_H */

+ 843
- 0
src/drivers/net/ncm.c View File

@@ -0,0 +1,843 @@
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
+#include <string.h>
23
+#include <errno.h>
24
+#include <ipxe/netdevice.h>
25
+#include <ipxe/ethernet.h>
26
+#include <ipxe/if_ether.h>
27
+#include <ipxe/profile.h>
28
+#include <ipxe/usb.h>
29
+#include "ecm.h"
30
+#include "ncm.h"
31
+
32
+/** @file
33
+ *
34
+ * CDC-NCM USB Ethernet driver
35
+ *
36
+ */
37
+
38
+/** Interrupt completion profiler */
39
+static struct profiler ncm_intr_profiler __profiler =
40
+	{ .name = "ncm.intr" };
41
+
42
+/** Interrupt refill profiler */
43
+static struct profiler ncm_intr_refill_profiler __profiler =
44
+	{ .name = "ncm.intr_refill" };
45
+
46
+/** Bulk IN completion profiler */
47
+static struct profiler ncm_in_profiler __profiler =
48
+	{ .name = "ncm.in" };
49
+
50
+/** Bulk IN per-datagram profiler */
51
+static struct profiler ncm_in_datagram_profiler __profiler =
52
+	{ .name = "ncm.in_dgram" };
53
+
54
+/** Bulk IN refill profiler */
55
+static struct profiler ncm_in_refill_profiler __profiler =
56
+	{ .name = "ncm.in_refill" };
57
+
58
+/** Bulk OUT profiler */
59
+static struct profiler ncm_out_profiler __profiler =
60
+	{ .name = "ncm.out" };
61
+
62
+/******************************************************************************
63
+ *
64
+ * CDC-NCM communications interface
65
+ *
66
+ ******************************************************************************
67
+ */
68
+
69
+/**
70
+ * Refill interrupt ring
71
+ *
72
+ * @v ncm		CDC-NCM device
73
+ */
74
+static void ncm_intr_refill ( struct ncm_device *ncm ) {
75
+	struct io_buffer *iobuf;
76
+	size_t mtu = ncm->intr.mtu;
77
+	int rc;
78
+
79
+	/* Enqueue any available I/O buffers */
80
+	while ( ( iobuf = list_first_entry ( &ncm->intrs, struct io_buffer,
81
+					     list ) ) ) {
82
+
83
+		/* Profile refill */
84
+		profile_start ( &ncm_intr_refill_profiler );
85
+
86
+		/* Reset size */
87
+		iob_put ( iobuf, ( mtu - iob_len ( iobuf ) ) );
88
+
89
+		/* Enqueue I/O buffer */
90
+		if ( ( rc = usb_stream ( &ncm->intr, iobuf ) ) != 0 ) {
91
+			DBGC ( ncm, "NCM %p could not enqueue interrupt: %s\n",
92
+			       ncm, strerror ( rc ) );
93
+			/* Leave in available list and wait for next refill */
94
+			return;
95
+		}
96
+
97
+		/* Remove from available list */
98
+		list_del ( &iobuf->list );
99
+		profile_stop ( &ncm_intr_refill_profiler );
100
+	}
101
+}
102
+
103
+/**
104
+ * Complete interrupt transfer
105
+ *
106
+ * @v ep		USB endpoint
107
+ * @v iobuf		I/O buffer
108
+ * @v rc		Completion status code
109
+ */
110
+static void ncm_intr_complete ( struct usb_endpoint *ep,
111
+				struct io_buffer *iobuf, int rc ) {
112
+	struct ncm_device *ncm = container_of ( ep, struct ncm_device, intr );
113
+	struct net_device *netdev = ncm->netdev;
114
+	struct usb_setup_packet *message;
115
+	size_t len = iob_len ( iobuf );
116
+
117
+	/* Profile completions */
118
+	profile_start ( &ncm_intr_profiler );
119
+
120
+	/* Ignore packets cancelled when the endpoint closes */
121
+	if ( ! ep->open )
122
+		goto done;
123
+
124
+	/* Ignore packets with errors */
125
+	if ( rc != 0 ) {
126
+		DBGC ( ncm, "NCM %p interrupt failed: %s\n",
127
+		       ncm, strerror ( rc ) );
128
+		DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
129
+		goto done;
130
+	}
131
+
132
+	/* Extract message header */
133
+	if ( len < sizeof ( *message ) ) {
134
+		DBGC ( ncm, "NCM %p underlength interrupt:\n", ncm );
135
+		DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
136
+		goto done;
137
+	}
138
+	message = iobuf->data;
139
+
140
+	/* Parse message header */
141
+	switch ( message->request ) {
142
+
143
+	case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) :
144
+		if ( message->value ) {
145
+			DBGC ( ncm, "NCM %p link up\n", ncm );
146
+			netdev_link_up ( netdev );
147
+		} else {
148
+			DBGC ( ncm, "NCM %p link down\n", ncm );
149
+			netdev_link_down ( netdev );
150
+		}
151
+		break;
152
+
153
+	case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) :
154
+		/* Ignore */
155
+		break;
156
+
157
+	default:
158
+		DBGC ( ncm, "NCM %p unrecognised interrupt:\n", ncm );
159
+		DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
160
+		break;
161
+	}
162
+
163
+ done:
164
+	/* Return I/O buffer to available list */
165
+	list_add_tail ( &iobuf->list, &ncm->intrs );
166
+	profile_stop ( &ncm_intr_profiler );
167
+}
168
+
169
+/** Interrupt endpoint operations */
170
+static struct usb_endpoint_driver_operations ncm_intr_operations = {
171
+	.complete = ncm_intr_complete,
172
+};
173
+
174
+/**
175
+ * Open communications interface
176
+ *
177
+ * @v ncm		CDC-NCM device
178
+ * @ret rc		Return status code
179
+ */
180
+static int ncm_comms_open ( struct ncm_device *ncm ) {
181
+	struct io_buffer *iobuf;
182
+	struct io_buffer *tmp;
183
+	unsigned int i;
184
+	int rc;
185
+
186
+	/* Allocate I/O buffers */
187
+	for ( i = 0 ; i < NCM_INTR_FILL ; i++ ) {
188
+		iobuf = alloc_iob ( ncm->intr.mtu );
189
+		if ( ! iobuf ) {
190
+			rc = -ENOMEM;
191
+			goto err_alloc_iob;
192
+		}
193
+		list_add ( &iobuf->list, &ncm->intrs );
194
+	}
195
+
196
+	/* Open interrupt endpoint */
197
+	if ( ( rc = usb_endpoint_open ( &ncm->intr ) ) != 0 ) {
198
+		DBGC ( ncm, "NCM %p could not open interrupt: %s\n",
199
+		       ncm, strerror ( rc ) );
200
+		goto err_open;
201
+	}
202
+
203
+	return 0;
204
+
205
+	usb_endpoint_close ( &ncm->intr );
206
+ err_open:
207
+ err_alloc_iob:
208
+	list_for_each_entry_safe ( iobuf, tmp, &ncm->intrs, list ) {
209
+		list_del ( &iobuf->list );
210
+		free_iob ( iobuf );
211
+	}
212
+	return rc;
213
+}
214
+
215
+/**
216
+ * Close communications interface
217
+ *
218
+ * @v ncm		CDC-NCM device
219
+ */
220
+static void ncm_comms_close ( struct ncm_device *ncm ) {
221
+	struct io_buffer *iobuf;
222
+	struct io_buffer *tmp;
223
+
224
+	/* Close interrupt endpoint */
225
+	usb_endpoint_close ( &ncm->intr );
226
+
227
+	/* Free I/O buffers */
228
+	list_for_each_entry_safe ( iobuf, tmp, &ncm->intrs, list ) {
229
+		list_del ( &iobuf->list );
230
+		free_iob ( iobuf );
231
+	}
232
+}
233
+
234
+/******************************************************************************
235
+ *
236
+ * CDC-NCM data interface
237
+ *
238
+ ******************************************************************************
239
+ */
240
+
241
+/**
242
+ * Refill bulk IN ring
243
+ *
244
+ * @v ncm		CDC-NCM device
245
+ */
246
+static void ncm_in_refill ( struct ncm_device *ncm ) {
247
+	struct net_device *netdev = ncm->netdev;
248
+	struct io_buffer *iobuf;
249
+	int rc;
250
+
251
+	/* Refill ring */
252
+	while ( ncm->fill < NCM_IN_FILL ) {
253
+
254
+		/* Profile refill */
255
+		profile_start ( &ncm_in_refill_profiler );
256
+
257
+		/* Allocate I/O buffer */
258
+		iobuf = alloc_iob ( NCM_NTB_INPUT_SIZE );
259
+		if ( ! iobuf ) {
260
+			/* Wait for next refill */
261
+			break;
262
+		}
263
+		iob_put ( iobuf, NCM_NTB_INPUT_SIZE );
264
+
265
+		/* Enqueue I/O buffer */
266
+		if ( ( rc = usb_stream ( &ncm->in, iobuf ) ) != 0 ) {
267
+			netdev_rx_err ( netdev, iobuf, rc );
268
+			break;
269
+		}
270
+
271
+		/* Increment fill level */
272
+		ncm->fill++;
273
+		profile_stop ( &ncm_in_refill_profiler );
274
+	}
275
+}
276
+
277
+/**
278
+ * Complete bulk IN transfer
279
+ *
280
+ * @v ep		USB endpoint
281
+ * @v iobuf		I/O buffer
282
+ * @v rc		Completion status code
283
+ */
284
+static void ncm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
285
+			      int rc ) {
286
+	struct ncm_device *ncm = container_of ( ep, struct ncm_device, in );
287
+	struct net_device *netdev = ncm->netdev;
288
+	struct ncm_transfer_header *nth;
289
+	struct ncm_datagram_pointer *ndp;
290
+	struct ncm_datagram_descriptor *desc;
291
+	struct io_buffer *pkt;
292
+	unsigned int remaining;
293
+	size_t ndp_offset;
294
+	size_t ndp_len;
295
+	size_t pkt_offset;
296
+	size_t pkt_len;
297
+	size_t len;
298
+
299
+	/* Profile overall bulk IN completion */
300
+	profile_start ( &ncm_in_profiler );
301
+
302
+	/* Decrement fill level */
303
+	ncm->fill--;
304
+
305
+	/* Ignore packets cancelled when the endpoint closes */
306
+	if ( ! ep->open )
307
+		goto ignore;
308
+
309
+	/* Record USB errors against the network device */
310
+	if ( rc != 0 )
311
+		goto drop;
312
+
313
+	/* Locate transfer header */
314
+	len = iob_len ( iobuf );
315
+	if ( sizeof ( *nth ) > len ) {
316
+		DBGC ( ncm, "NCM %p packet too short for NTH:\n", ncm );
317
+		goto error;
318
+	}
319
+	nth = iobuf->data;
320
+
321
+	/* Locate datagram pointer */
322
+	ndp_offset = le16_to_cpu ( nth->offset );
323
+	if ( ( ndp_offset + sizeof ( *ndp ) ) > len ) {
324
+		DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm );
325
+		goto error;
326
+	}
327
+	ndp = ( iobuf->data + ndp_offset );
328
+	ndp_len = le16_to_cpu ( ndp->header_len );
329
+	if ( ndp_len < offsetof ( typeof ( *ndp ), desc ) ) {
330
+		DBGC ( ncm, "NCM %p NDP header length too short:\n", ncm );
331
+		goto error;
332
+	}
333
+	if ( ( ndp_offset + ndp_len ) > len ) {
334
+		DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm );
335
+		goto error;
336
+	}
337
+
338
+	/* Process datagrams */
339
+	remaining = ( ( ndp_len - offsetof ( typeof ( *ndp ), desc ) ) /
340
+		      sizeof ( ndp->desc[0] ) );
341
+	for ( desc = ndp->desc ; remaining && desc->offset ; remaining-- ) {
342
+
343
+		/* Profile individual datagrams */
344
+		profile_start ( &ncm_in_datagram_profiler );
345
+
346
+		/* Locate datagram */
347
+		pkt_offset = le16_to_cpu ( desc->offset );
348
+		pkt_len = le16_to_cpu ( desc->len );
349
+		if ( pkt_len < ETH_HLEN ) {
350
+			DBGC ( ncm, "NCM %p underlength datagram:\n", ncm );
351
+			goto error;
352
+		}
353
+		if ( ( pkt_offset + pkt_len ) > len ) {
354
+			DBGC ( ncm, "NCM %p datagram exceeds packet:\n", ncm );
355
+			goto error;
356
+		}
357
+
358
+		/* Move to next descriptor */
359
+		desc++;
360
+
361
+		/* Create new I/O buffer if necessary */
362
+		if ( remaining && desc->offset ) {
363
+			/* More packets remain: create new buffer */
364
+			pkt = alloc_iob ( pkt_len );
365
+			if ( ! pkt ) {
366
+				/* Record error and continue */
367
+				netdev_rx_err ( netdev, NULL, -ENOMEM );
368
+				continue;
369
+			}
370
+			memcpy ( iob_put ( pkt, pkt_len ),
371
+				 ( iobuf->data + pkt_offset ), pkt_len );
372
+		} else {
373
+			/* This is the last packet: use in situ */
374
+			pkt = iob_disown ( iobuf );
375
+			iob_pull ( pkt, pkt_offset );
376
+			iob_unput ( pkt, ( iob_len ( pkt ) - pkt_len ) );
377
+		}
378
+
379
+		/* Strip CRC, if present */
380
+		if ( ndp->magic & cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC_CRC))
381
+			iob_unput ( pkt, 4 /* CRC32 */ );
382
+
383
+		/* Hand off to network stack */
384
+		netdev_rx ( netdev, pkt );
385
+		profile_stop ( &ncm_in_datagram_profiler );
386
+	}
387
+
388
+	/* Free I/O buffer (if still present) */
389
+	free_iob ( iobuf );
390
+
391
+	profile_stop ( &ncm_in_profiler );
392
+	return;
393
+
394
+ error:
395
+	rc = -EIO;
396
+ drop:
397
+	DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) );
398
+	netdev_rx_err ( netdev, iobuf, rc );
399
+	return;
400
+
401
+ ignore:
402
+	free_iob ( iobuf );
403
+	return;
404
+}
405
+
406
+/** Bulk IN endpoint operations */
407
+static struct usb_endpoint_driver_operations ncm_in_operations = {
408
+	.complete = ncm_in_complete,
409
+};
410
+
411
+/**
412
+ * Transmit packet
413
+ *
414
+ * @v ncm		CDC-NCM device
415
+ * @v iobuf		I/O buffer
416
+ * @ret rc		Return status code
417
+ */
418
+static int ncm_out_transmit ( struct ncm_device *ncm,
419
+			      struct io_buffer *iobuf ) {
420
+	struct ncm_ntb_header *header;
421
+	size_t len = iob_len ( iobuf );
422
+	size_t header_len = ( sizeof ( *header ) + ncm->padding );
423
+	int rc;
424
+
425
+	/* Profile transmissions */
426
+	profile_start ( &ncm_out_profiler );
427
+
428
+	/* Prepend header */
429
+	if ( ( rc = iob_ensure_headroom ( iobuf, header_len ) ) != 0 )
430
+		return rc;
431
+	header = iob_push ( iobuf, header_len );
432
+
433
+	/* Populate header */
434
+	header->nth.magic = cpu_to_le32 ( NCM_TRANSFER_HEADER_MAGIC );
435
+	header->nth.header_len = cpu_to_le16 ( sizeof ( header->nth ) );
436
+	header->nth.sequence = cpu_to_le16 ( ncm->sequence );
437
+	header->nth.len = cpu_to_le16 ( iob_len ( iobuf ) );
438
+	header->nth.offset =
439
+		cpu_to_le16 ( offsetof ( typeof ( *header ), ndp ) );
440
+	header->ndp.magic = cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC );
441
+	header->ndp.header_len = cpu_to_le16 ( sizeof ( header->ndp ) +
442
+					       sizeof ( header->desc ) );
443
+	header->ndp.offset = cpu_to_le16 ( 0 );
444
+	header->desc[0].offset = cpu_to_le16 ( header_len );
445
+	header->desc[0].len = cpu_to_le16 ( len );
446
+	memset ( &header->desc[1], 0, sizeof ( header->desc[1] ) );
447
+
448
+	/* Enqueue I/O buffer */
449
+	if ( ( rc = usb_stream ( &ncm->out, iobuf ) ) != 0 )
450
+		return rc;
451
+
452
+	/* Increment sequence number */
453
+	ncm->sequence++;
454
+
455
+	profile_stop ( &ncm_out_profiler );
456
+	return 0;
457
+}
458
+
459
+/**
460
+ * Complete bulk OUT transfer
461
+ *
462
+ * @v ep		USB endpoint
463
+ * @v iobuf		I/O buffer
464
+ * @v rc		Completion status code
465
+ */
466
+static void ncm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
467
+			       int rc ) {
468
+	struct ncm_device *ncm = container_of ( ep, struct ncm_device, out );
469
+	struct net_device *netdev = ncm->netdev;
470
+
471
+	/* Report TX completion */
472
+	netdev_tx_complete_err ( netdev, iobuf, rc );
473
+}
474
+
475
+/** Bulk OUT endpoint operations */
476
+static struct usb_endpoint_driver_operations ncm_out_operations = {
477
+	.complete = ncm_out_complete,
478
+};
479
+
480
+/**
481
+ * Open data interface
482
+ *
483
+ * @v ncm		CDC-NCM device
484
+ * @ret rc		Return status code
485
+ */
486
+static int ncm_data_open ( struct ncm_device *ncm ) {
487
+	struct usb_device *usb = ncm->usb;
488
+	struct ncm_set_ntb_input_size size;
489
+	int rc;
490
+
491
+	/* Set maximum input size */
492
+	memset ( &size, 0, sizeof ( size ) );
493
+	size.mtu = cpu_to_le32 ( NCM_NTB_INPUT_SIZE );
494
+	if ( ( rc = usb_control ( usb, NCM_SET_NTB_INPUT_SIZE, 0, ncm->comms,
495
+				  &size, sizeof ( size ) ) ) != 0 ) {
496
+		DBGC ( ncm, "NCM %p could not set input size: %s\n",
497
+		       ncm, strerror ( rc ) );
498
+		goto err_set_ntb_input_size;
499
+	}
500
+
501
+	/* Select alternate setting for data interface */
502
+	if ( ( rc = usb_set_interface ( usb, ncm->data,
503
+					NCM_DATA_ALTERNATE ) ) != 0 ) {
504
+		DBGC ( ncm, "NCM %p could not set alternate interface: %s\n",
505
+		       ncm, strerror ( rc ) );
506
+		goto err_set_interface;
507
+	}
508
+
509
+	/* Open bulk IN endpoint */
510
+	if ( ( rc = usb_endpoint_open ( &ncm->in ) ) != 0 ) {
511
+		DBGC ( ncm, "NCM %p could not open bulk IN: %s\n",
512
+		       ncm, strerror ( rc ) );
513
+		goto err_open_in;
514
+	}
515
+
516
+	/* Open bulk OUT endpoint */
517
+	if ( ( rc = usb_endpoint_open ( &ncm->out ) ) != 0 ) {
518
+		DBGC ( ncm, "NCM %p could not open bulk OUT: %s\n",
519
+		       ncm, strerror ( rc ) );
520
+		goto err_open_out;
521
+	}
522
+
523
+	return 0;
524
+
525
+	usb_endpoint_close ( &ncm->out );
526
+ err_open_out:
527
+	usb_endpoint_close ( &ncm->in );
528
+ err_open_in:
529
+	usb_set_interface ( usb, ncm->data, 0 );
530
+ err_set_interface:
531
+ err_set_ntb_input_size:
532
+	return rc;
533
+}
534
+
535
+/**
536
+ * Close data interface
537
+ *
538
+ * @v ncm		CDC-NCM device
539
+ */
540
+static void ncm_data_close ( struct ncm_device *ncm ) {
541
+	struct usb_device *usb = ncm->usb;
542
+
543
+	/* Close endpoints */
544
+	usb_endpoint_close ( &ncm->out );
545
+	usb_endpoint_close ( &ncm->in );
546
+
547
+	/* Reset data interface */
548
+	usb_set_interface ( usb, ncm->data, 0 );
549
+}
550
+
551
+/******************************************************************************
552
+ *
553
+ * Network device interface
554
+ *
555
+ ******************************************************************************
556
+ */
557
+
558
+/**
559
+ * Open network device
560
+ *
561
+ * @v netdev		Network device
562
+ * @ret rc		Return status code
563
+ */
564
+static int ncm_open ( struct net_device *netdev ) {
565
+	struct ncm_device *ncm = netdev->priv;
566
+	int rc;
567
+
568
+	/* Reset sequence number */
569
+	ncm->sequence = 0;
570
+
571
+	/* Open communications interface */
572
+	if ( ( rc = ncm_comms_open ( ncm ) ) != 0 )
573
+		goto err_comms_open;
574
+
575
+	/* Refill interrupt ring */
576
+	ncm_intr_refill ( ncm );
577
+
578
+	/* Open data interface */
579
+	if ( ( rc = ncm_data_open ( ncm ) ) != 0 )
580
+		goto err_data_open;
581
+
582
+	/* Refill bulk IN ring */
583
+	ncm_in_refill ( ncm );
584
+
585
+	return 0;
586
+
587
+	ncm_data_close ( ncm );
588
+ err_data_open:
589
+	ncm_comms_close ( ncm );
590
+ err_comms_open:
591
+	return rc;
592
+}
593
+
594
+/**
595
+ * Close network device
596
+ *
597
+ * @v netdev		Network device
598
+ */
599
+static void ncm_close ( struct net_device *netdev ) {
600
+	struct ncm_device *ncm = netdev->priv;
601
+
602
+	/* Close data interface */
603
+	ncm_data_close ( ncm );
604
+
605
+	/* Close communications interface */
606
+	ncm_comms_close ( ncm );
607
+
608
+	/* Sanity check */
609
+	assert ( ncm->fill == 0 );
610
+}
611
+
612
+/**
613
+ * Transmit packet
614
+ *
615
+ * @v netdev		Network device
616
+ * @v iobuf		I/O buffer
617
+ * @ret rc		Return status code
618
+ */
619
+static int ncm_transmit ( struct net_device *netdev,
620
+			  struct io_buffer *iobuf ) {
621
+	struct ncm_device *ncm = netdev->priv;
622
+	int rc;
623
+
624
+	/* Transmit packet */
625
+	if ( ( rc = ncm_out_transmit ( ncm, iobuf ) ) != 0 )
626
+		return rc;
627
+
628
+	return 0;
629
+}
630
+
631
+/**
632
+ * Poll for completed and received packets
633
+ *
634
+ * @v netdev		Network device
635
+ */
636
+static void ncm_poll ( struct net_device *netdev ) {
637
+	struct ncm_device *ncm = netdev->priv;
638
+
639
+	/* Poll USB bus */
640
+	usb_poll ( ncm->bus );
641
+
642
+	/* Refill interrupt ring */
643
+	ncm_intr_refill ( ncm );
644
+
645
+	/* Refill bulk IN ring */
646
+	ncm_in_refill ( ncm );
647
+}
648
+
649
+/** CDC-NCM network device operations */
650
+static struct net_device_operations ncm_operations = {
651
+	.open		= ncm_open,
652
+	.close		= ncm_close,
653
+	.transmit	= ncm_transmit,
654
+	.poll		= ncm_poll,
655
+};
656
+
657
+/******************************************************************************
658
+ *
659
+ * USB interface
660
+ *
661
+ ******************************************************************************
662
+ */
663
+
664
+/**
665
+ * Probe device
666
+ *
667
+ * @v func		USB function
668
+ * @v config		Configuration descriptor
669
+ * @ret rc		Return status code
670
+ */
671
+static int ncm_probe ( struct usb_function *func,
672
+		       struct usb_configuration_descriptor *config ) {
673
+	struct usb_device *usb = func->usb;
674
+	struct net_device *netdev;
675
+	struct ncm_device *ncm;
676
+	struct usb_interface_descriptor *comms;
677
+	struct usb_interface_descriptor *data;
678
+	struct ecm_ethernet_descriptor *ethernet;
679
+	struct ncm_ntb_parameters params;
680
+	int rc;
681
+
682
+	/* Allocate and initialise structure */
683
+	netdev = alloc_etherdev ( sizeof ( *ncm ) );
684
+	if ( ! netdev ) {
685
+		rc = -ENOMEM;
686
+		goto err_alloc;
687
+	}
688
+	netdev_init ( netdev, &ncm_operations );
689
+	netdev->dev = &func->dev;
690
+	ncm = netdev->priv;
691
+	memset ( ncm, 0, sizeof ( *ncm ) );
692
+	ncm->usb = usb;
693
+	ncm->bus = usb->port->hub->bus;
694
+	ncm->netdev = netdev;
695
+	usb_endpoint_init ( &ncm->intr, usb, &ncm_intr_operations );
696
+	usb_endpoint_init ( &ncm->in, usb, &ncm_in_operations );
697
+	usb_endpoint_init ( &ncm->out, usb, &ncm_out_operations );
698
+	INIT_LIST_HEAD ( &ncm->intrs );
699
+	DBGC ( ncm, "NCM %p on %s\n", ncm, func->name );
700
+
701
+	/* Identify interfaces */
702
+	if ( func->count < NCM_INTERFACE_COUNT ) {
703
+		DBGC ( ncm, "NCM %p has only %d interfaces\n",
704
+		       ncm, func->count );
705
+		rc = -EINVAL;
706
+		goto err_count;
707
+	}
708
+	ncm->comms = func->interface[NCM_INTERFACE_COMMS];
709
+	ncm->data = func->interface[NCM_INTERFACE_DATA];
710
+
711
+	/* Locate communications interface descriptor */
712
+	comms = usb_interface_descriptor ( config, ncm->comms, 0 );
713
+	if ( ! comms ) {
714
+		DBGC ( ncm, "NCM %p has no communications interface\n", ncm );
715
+		rc = -EINVAL;
716
+		goto err_comms;
717
+	}
718
+
719
+	/* Locate data interface descriptor */
720
+	data = usb_interface_descriptor ( config, ncm->data,
721
+					  NCM_DATA_ALTERNATE );
722
+	if ( ! data ) {
723
+		DBGC ( ncm, "NCM %p has no data interface\n", ncm );
724
+		rc = -EINVAL;
725
+		goto err_data;
726
+	}
727
+
728
+	/* Describe interrupt endpoint */
729
+	if ( ( rc = usb_endpoint_described ( &ncm->intr, config, comms,
730
+					     USB_INTERRUPT, 0 ) ) != 0 ) {
731
+		DBGC ( ncm, "NCM %p could not describe interrupt endpoint: "
732
+		       "%s\n", ncm, strerror ( rc ) );
733
+		goto err_interrupt;
734
+	}
735
+
736
+	/* Describe bulk IN endpoint */
737
+	if ( ( rc = usb_endpoint_described ( &ncm->in, config, data,
738
+					     USB_BULK_IN, 0 ) ) != 0 ) {
739
+		DBGC ( ncm, "NCM %p could not describe bulk IN endpoint: "
740
+		       "%s\n", ncm, strerror ( rc ) );
741
+		goto err_bulk_in;
742
+	}
743
+
744
+	/* Describe bulk OUT endpoint */
745
+	if ( ( rc = usb_endpoint_described ( &ncm->out, config, data,
746
+					     USB_BULK_OUT, 0 ) ) != 0 ) {
747
+		DBGC ( ncm, "NCM %p could not describe bulk OUT endpoint: "
748
+		       "%s\n", ncm, strerror ( rc ) );
749
+		goto err_bulk_out;
750
+	}
751
+
752
+	/* Locate Ethernet descriptor */
753
+	ethernet = ecm_ethernet_descriptor ( config, comms );
754
+	if ( ! ethernet ) {
755
+		DBGC ( ncm, "NCM %p has no Ethernet descriptor\n", ncm );
756
+		rc = -EINVAL;
757
+		goto err_ethernet;
758
+	}
759
+
760
+	/* Fetch MAC address */
761
+	if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) {
762
+		DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n",
763
+		       ncm, strerror ( rc ) );
764
+		goto err_fetch_mac;
765
+	}
766
+
767
+	/* Get NTB parameters */
768
+	if ( ( rc = usb_control ( usb, NCM_GET_NTB_PARAMETERS, 0, ncm->comms,
769
+				  &params, sizeof ( params ) ) ) != 0 ) {
770
+		DBGC ( ncm, "NCM %p could not get NTB parameters: %s\n",
771
+		       ncm, strerror ( rc ) );
772
+		goto err_ntb_parameters;
773
+	}
774
+
775
+	/* Calculate transmit padding */
776
+	ncm->padding = ( ( le16_to_cpu ( params.out.remainder ) -
777
+			   sizeof ( struct ncm_ntb_header ) - ETH_HLEN ) &
778
+			 ( le16_to_cpu ( params.out.divisor ) - 1 ) );
779
+	DBGC2 ( ncm, "NCM %p using %zd-byte transmit padding\n",
780
+		ncm, ncm->padding );
781
+	assert ( ( ( sizeof ( struct ncm_ntb_header ) + ncm->padding +
782
+		     ETH_HLEN ) % le16_to_cpu ( params.out.divisor ) ) ==
783
+		 le16_to_cpu ( params.out.remainder ) );
784
+
785
+	/* Register network device */
786
+	if ( ( rc = register_netdev ( netdev ) ) != 0 )
787
+		goto err_register;
788
+
789
+	usb_func_set_drvdata ( func, ncm );
790
+	return 0;
791
+
792
+	unregister_netdev ( netdev );
793
+ err_register:
794
+ err_ntb_parameters:
795
+ err_fetch_mac:
796
+ err_ethernet:
797
+ err_bulk_out:
798
+ err_bulk_in:
799
+ err_interrupt:
800
+ err_data:
801
+ err_comms:
802
+ err_count:
803
+	netdev_nullify ( netdev );
804
+	netdev_put ( netdev );
805
+ err_alloc:
806
+	return rc;
807
+}
808
+
809
+/**
810
+ * Remove device
811
+ *
812
+ * @v func		USB function
813
+ */
814
+static void ncm_remove ( struct usb_function *func ) {
815
+	struct ncm_device *ncm = usb_func_get_drvdata ( func );
816
+	struct net_device *netdev = ncm->netdev;
817
+
818
+	unregister_netdev ( netdev );
819
+	netdev_nullify ( netdev );
820
+	netdev_put ( netdev );
821
+}
822
+
823
+/** CDC-NCM device IDs */
824
+static struct usb_device_id ncm_ids[] = {
825
+	{
826
+		.name = "cdc-ncm",
827
+		.vendor = USB_ANY_ID,
828
+		.product = USB_ANY_ID,
829
+		.class = {
830
+			.class = USB_CLASS_CDC,
831
+			.subclass = USB_SUBCLASS_CDC_NCM,
832
+			.protocol = 0,
833
+		},
834
+	},
835
+};
836
+
837
+/** CDC-NCM driver */
838
+struct usb_driver ncm_driver __usb_driver = {
839
+	.ids = ncm_ids,
840
+	.id_count = ( sizeof ( ncm_ids ) / sizeof ( ncm_ids[0] ) ),
841
+	.probe = ncm_probe,
842
+	.remove = ncm_remove,
843
+};

+ 186
- 0
src/drivers/net/ncm.h View File

@@ -0,0 +1,186 @@
1
+#ifndef _NCM_H
2
+#define _NCM_H
3
+
4
+/** @file
5
+ *
6
+ * CDC-NCM USB Ethernet driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/usb.h>
14
+#include <ipxe/cdc.h>
15
+#include <byteswap.h>
16
+#include "ecm.h"
17
+
18
+/** CDC-NCM subclass */
19
+#define USB_SUBCLASS_CDC_NCM 0x0d
20
+
21
+/** CDC-NCM interfaces */
22
+enum ncm_interfaces {
23
+	/** Communications interface */
24
+	NCM_INTERFACE_COMMS = 0,
25
+	/** Data interface */
26
+	NCM_INTERFACE_DATA,
27
+	NCM_INTERFACE_COUNT
28
+};
29
+
30
+/** Alternate setting for CDC-NCM data interface */
31
+#define NCM_DATA_ALTERNATE 1
32
+
33
+/** Get NTB parameters */
34
+#define NCM_GET_NTB_PARAMETERS						\
35
+	( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
36
+	  USB_REQUEST_TYPE ( 0x80 ) )
37
+
38
+/** NTB datagram parameters */
39
+struct ncm_ntb_datagram_parameters {
40
+	/** Maximum size */
41
+	uint32_t mtu;
42
+	/** Alignment divisor */
43
+	uint16_t divisor;
44
+	/** Alignment remainder */
45
+	uint16_t remainder;
46
+	/** Alignment modulus */
47
+	uint16_t modulus;
48
+} __attribute__ (( packed ));
49
+
50
+/** NTB parameters */
51
+struct ncm_ntb_parameters {
52
+	/** Length */
53
+	uint16_t len;
54
+	/** Supported formats */
55
+	uint16_t formats;
56
+	/** IN datagram parameters */
57
+	struct ncm_ntb_datagram_parameters in;
58
+	/** Reserved */
59
+	uint16_t reserved;
60
+	/** OUT datagram parameters */
61
+	struct ncm_ntb_datagram_parameters out;
62
+	/** Maximum number of datagrams per OUT NTB */
63
+	uint16_t max;
64
+} __attribute__ (( packed ));
65
+
66
+/** Set NTB input size */
67
+#define NCM_SET_NTB_INPUT_SIZE						\
68
+	( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
69
+	  USB_REQUEST_TYPE ( 0x86 ) )
70
+
71
+/** Set NTB input size */
72
+struct ncm_set_ntb_input_size {
73
+	/** Maximum size */
74
+	uint32_t mtu;
75
+} __attribute__ (( packed ));
76
+
77
+/** NTB input size
78
+ *
79
+ * This is a policy decision.  2048 is the minimum size which must be
80
+ * supported according to the specification.
81
+ */
82
+#define NCM_NTB_INPUT_SIZE 2048
83
+
84
+/** CDC-NCM transfer header (16-bit) */
85
+struct ncm_transfer_header {
86
+	/** Signature */
87
+	uint32_t magic;
88
+	/** Header length */
89
+	uint16_t header_len;
90
+	/** Sequence number */
91
+	uint16_t sequence;
92
+	/** Total length */
93
+	uint16_t len;
94
+	/** Offset of first datagram pointer */
95
+	uint16_t offset;
96
+} __attribute__ (( packed ));
97
+
98
+/** CDC-NCM transfer header magic */
99
+#define NCM_TRANSFER_HEADER_MAGIC 0x484d434eUL
100
+
101
+/** CDC-NCM datagram descriptor (16-bit) */
102
+struct ncm_datagram_descriptor {
103
+	/** Starting offset */
104
+	uint16_t offset;
105
+	/** Length */
106
+	uint16_t len;
107
+} __attribute__ (( packed ));
108
+
109
+/** CDC-NCM datagram pointer (16-bit) */
110
+struct ncm_datagram_pointer {
111
+	/** Signature */
112
+	uint32_t magic;
113
+	/** Header length */
114
+	uint16_t header_len;
115
+	/** Offset of next datagram pointer */
116
+	uint16_t offset;
117
+	/** Datagram descriptors
118
+	 *
119
+	 * Must be terminated by an empty descriptor.
120
+	 */
121
+	struct ncm_datagram_descriptor desc[0];
122
+} __attribute__ (( packed ));
123
+
124
+/** CDC-NCM datagram pointer magic */
125
+#define NCM_DATAGRAM_POINTER_MAGIC 0x304d434eUL
126
+
127
+/** CDC-NCM datagram pointer CRC present flag */
128
+#define NCM_DATAGRAM_POINTER_MAGIC_CRC 0x01000000UL
129
+
130
+/** NTB constructed for transmitted packets (excluding padding)
131
+ *
132
+ * This is a policy decision.
133
+ */
134
+struct ncm_ntb_header {
135
+	/** Transfer header */
136
+	struct ncm_transfer_header nth;
137
+	/** Datagram pointer */
138
+	struct ncm_datagram_pointer ndp;
139
+	/** Datagram descriptors */
140
+	struct ncm_datagram_descriptor desc[2];
141
+} __attribute__ (( packed ));
142
+
143
+/** A CDC-NCM network device */
144
+struct ncm_device {
145
+	/** USB device */
146
+	struct usb_device *usb;
147
+	/** USB bus */
148
+	struct usb_bus *bus;
149
+	/** Network device */
150
+	struct net_device *netdev;
151
+
152
+	/** Communications interface */
153
+	unsigned int comms;
154
+	/** Data interface */
155
+	unsigned int data;
156
+
157
+	/** Interrupt endpoint */
158
+	struct usb_endpoint intr;
159
+	/** Bulk IN endpoint */
160
+	struct usb_endpoint in;
161
+	/** Bulk OUT endpoint */
162
+	struct usb_endpoint out;
163
+
164
+	/** Recycled interrupt I/O buffers */
165
+	struct list_head intrs;
166
+	/** Current bulk IN ring fill level */
167
+	unsigned int fill;
168
+	/** Transmitted packet sequence number */
169
+	uint16_t sequence;
170
+	/** Alignment padding required on transmitted packets */
171
+	size_t padding;
172
+};
173
+
174
+/** Bulk IN ring fill level
175
+ *
176
+ * This is a policy decision.
177
+ */
178
+#define NCM_IN_FILL 16
179
+
180
+/** Interrupt ring fill level
181
+ *
182
+ * This is a policy decision.
183
+ */
184
+#define NCM_INTR_FILL 2
185
+
186
+#endif /* _NCM_H */

+ 38
- 0
src/include/ipxe/cdc.h View File

@@ -0,0 +1,38 @@
1
+#ifndef _IPXE_CDC_H
2
+#define _IPXE_CDC_H
3
+
4
+/** @file
5
+ *
6
+ * USB Communications Device Class (CDC)
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/usb.h>
13
+
14
+/** Class code for communications devices */
15
+#define USB_CLASS_CDC 2
16
+
17
+/** Ethernet descriptor subtype */
18
+#define CDC_SUBTYPE_ETHERNET 15
19
+
20
+/** Network connection notification */
21
+#define CDC_NETWORK_CONNECTION						\
22
+	( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
23
+	  USB_REQUEST_TYPE ( 0x00 ) )
24
+
25
+/** Connection speed change notification */
26
+#define CDC_CONNECTION_SPEED_CHANGE					\
27
+	( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
28
+	  USB_REQUEST_TYPE ( 0x2a ) )
29
+
30
+/** Connection speed change notification */
31
+struct cdc_connection_speed_change {
32
+	/** Downlink bit rate, in bits per second */
33
+	uint32_t down;
34
+	/** Uplink bit rate, in bits per second */
35
+	uint32_t up;
36
+} __attribute__ (( packed ));
37
+
38
+#endif /* _IPXE_CDC_H */

+ 2
- 0
src/include/ipxe/errfile.h View File

@@ -161,6 +161,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
161 161
 #define ERRFILE_netfront	     ( ERRFILE_DRIVER | 0x00690000 )
162 162
 #define ERRFILE_nii		     ( ERRFILE_DRIVER | 0x006a0000 )
163 163
 #define ERRFILE_netvsc		     ( ERRFILE_DRIVER | 0x006b0000 )
164
+#define ERRFILE_ecm		     ( ERRFILE_DRIVER | 0x006c0000 )
165
+#define ERRFILE_ncm		     ( ERRFILE_DRIVER | 0x006d0000 )
164 166
 
165 167
 #define ERRFILE_scsi		     ( ERRFILE_DRIVER | 0x00700000 )
166 168
 #define ERRFILE_arbel		     ( ERRFILE_DRIVER | 0x00710000 )

Loading…
Cancel
Save