Browse Source

[acm] Add support for CDC-ACM (aka USB RNDIS) devices

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
296dee6d38
4 changed files with 648 additions and 0 deletions
  1. 529
    0
      src/drivers/net/acm.c
  2. 69
    0
      src/drivers/net/acm.h
  3. 49
    0
      src/include/ipxe/cdc.h
  4. 1
    0
      src/include/ipxe/errfile.h

+ 529
- 0
src/drivers/net/acm.c View File

@@ -0,0 +1,529 @@
1
+/*
2
+ * Copyright (C) 2015 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
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <stdint.h>
27
+#include <string.h>
28
+#include <errno.h>
29
+#include <byteswap.h>
30
+#include <ipxe/profile.h>
31
+#include <ipxe/usb.h>
32
+#include <ipxe/usbnet.h>
33
+#include <ipxe/rndis.h>
34
+#include "acm.h"
35
+
36
+/** @file
37
+ *
38
+ * USB RNDIS driver
39
+ *
40
+ */
41
+
42
+/** Interrupt completion profiler */
43
+static struct profiler acm_intr_profiler __profiler =
44
+	{ .name = "acm.intr" };
45
+
46
+/** Bulk IN completion profiler */
47
+static struct profiler acm_in_profiler __profiler =
48
+	{ .name = "acm.in" };
49
+
50
+/** Bulk OUT profiler */
51
+static struct profiler acm_out_profiler __profiler =
52
+	{ .name = "acm.out" };
53
+
54
+/******************************************************************************
55
+ *
56
+ * USB RNDIS communications interface
57
+ *
58
+ ******************************************************************************
59
+ */
60
+
61
+/**
62
+ * Complete interrupt transfer
63
+ *
64
+ * @v ep		USB endpoint
65
+ * @v iobuf		I/O buffer
66
+ * @v rc		Completion status code
67
+ */
68
+static void acm_intr_complete ( struct usb_endpoint *ep,
69
+				struct io_buffer *iobuf, int rc ) {
70
+	struct acm_device *acm = container_of ( ep, struct acm_device,
71
+						usbnet.intr );
72
+	struct rndis_device *rndis = acm->rndis;
73
+	struct usb_setup_packet *message;
74
+
75
+	/* Profile completions */
76
+	profile_start ( &acm_intr_profiler );
77
+
78
+	/* Ignore packets cancelled when the endpoint closes */
79
+	if ( ! ep->open )
80
+		goto ignore;
81
+
82
+	/* Drop packets with errors */
83
+	if ( rc != 0 ) {
84
+		DBGC ( acm, "ACM %p interrupt failed: %s\n",
85
+		       acm, strerror ( rc ) );
86
+		DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
87
+		goto error;
88
+	}
89
+
90
+	/* Extract message header */
91
+	if ( iob_len ( iobuf ) < sizeof ( *message ) ) {
92
+		DBGC ( acm, "ACM %p underlength interrupt:\n", acm );
93
+		DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
94
+		rc = -EINVAL;
95
+		goto error;
96
+	}
97
+	message = iobuf->data;
98
+
99
+	/* Parse message header */
100
+	switch ( message->request ) {
101
+
102
+	case cpu_to_le16 ( CDC_RESPONSE_AVAILABLE ) :
103
+	case cpu_to_le16 ( 0x0001 ) : /* qemu seems to use this value */
104
+		acm->responded = 1;
105
+		break;
106
+
107
+	default:
108
+		DBGC ( acm, "ACM %p unrecognised interrupt:\n", acm );
109
+		DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
110
+		rc = -ENOTSUP;
111
+		goto error;
112
+	}
113
+
114
+	/* Free I/O buffer */
115
+	free_iob ( iobuf );
116
+	profile_stop ( &acm_intr_profiler );
117
+
118
+	return;
119
+
120
+ error:
121
+	rndis_rx_err ( rndis, iob_disown ( iobuf ), rc );
122
+ ignore:
123
+	free_iob ( iobuf );
124
+	return;
125
+}
126
+
127
+/** Interrupt endpoint operations */
128
+static struct usb_endpoint_driver_operations acm_intr_operations = {
129
+	.complete = acm_intr_complete,
130
+};
131
+
132
+/******************************************************************************
133
+ *
134
+ * USB RNDIS data interface
135
+ *
136
+ ******************************************************************************
137
+ */
138
+
139
+/**
140
+ * Complete bulk IN transfer
141
+ *
142
+ * @v ep		USB endpoint
143
+ * @v iobuf		I/O buffer
144
+ * @v rc		Completion status code
145
+ */
146
+static void acm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
147
+			      int rc ) {
148
+	struct acm_device *acm = container_of ( ep, struct acm_device,
149
+						usbnet.in );
150
+	struct rndis_device *rndis = acm->rndis;
151
+
152
+	/* Profile receive completions */
153
+	profile_start ( &acm_in_profiler );
154
+
155
+	/* Ignore packets cancelled when the endpoint closes */
156
+	if ( ! ep->open )
157
+		goto ignore;
158
+
159
+	/* Record USB errors against the RNDIS device */
160
+	if ( rc != 0 ) {
161
+		DBGC ( acm, "ACM %p bulk IN failed: %s\n",
162
+		       acm, strerror ( rc ) );
163
+		goto error;
164
+	}
165
+
166
+	/* Hand off to RNDIS */
167
+	rndis_rx ( rndis, iob_disown ( iobuf ) );
168
+
169
+	profile_stop ( &acm_in_profiler );
170
+	return;
171
+
172
+ error:
173
+	rndis_rx_err ( rndis, iob_disown ( iobuf ), rc );
174
+ ignore:
175
+	free_iob ( iobuf );
176
+}
177
+
178
+/** Bulk IN endpoint operations */
179
+static struct usb_endpoint_driver_operations acm_in_operations = {
180
+	.complete = acm_in_complete,
181
+};
182
+
183
+/**
184
+ * Transmit packet
185
+ *
186
+ * @v acm		USB RNDIS device
187
+ * @v iobuf		I/O buffer
188
+ * @ret rc		Return status code
189
+ */
190
+static int acm_out_transmit ( struct acm_device *acm,
191
+			      struct io_buffer *iobuf ) {
192
+	int rc;
193
+
194
+	/* Profile transmissions */
195
+	profile_start ( &acm_out_profiler );
196
+
197
+	/* Enqueue I/O buffer */
198
+	if ( ( rc = usb_stream ( &acm->usbnet.out, iobuf, 0 ) ) != 0 )
199
+		return rc;
200
+
201
+	profile_stop ( &acm_out_profiler );
202
+	return 0;
203
+}
204
+
205
+/**
206
+ * Complete bulk OUT transfer
207
+ *
208
+ * @v ep		USB endpoint
209
+ * @v iobuf		I/O buffer
210
+ * @v rc		Completion status code
211
+ */
212
+static void acm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
213
+			       int rc ) {
214
+	struct acm_device *acm = container_of ( ep, struct acm_device,
215
+						usbnet.out );
216
+	struct rndis_device *rndis = acm->rndis;
217
+
218
+	/* Report TX completion */
219
+	rndis_tx_complete_err ( rndis, iobuf, rc );
220
+}
221
+
222
+/** Bulk OUT endpoint operations */
223
+static struct usb_endpoint_driver_operations acm_out_operations = {
224
+	.complete = acm_out_complete,
225
+};
226
+
227
+/******************************************************************************
228
+ *
229
+ * USB RNDIS control interface
230
+ *
231
+ ******************************************************************************
232
+ */
233
+
234
+/**
235
+ * Send control packet
236
+ *
237
+ * @v acm		USB RNDIS device
238
+ * @v iobuf		I/O buffer
239
+ * @ret rc		Return status code
240
+ */
241
+static int acm_control_transmit ( struct acm_device *acm,
242
+				  struct io_buffer *iobuf ) {
243
+	struct rndis_device *rndis = acm->rndis;
244
+	struct usb_device *usb = acm->usb;
245
+	int rc;
246
+
247
+	/* Send packet as an encapsulated command */
248
+	if ( ( rc = cdc_send_encapsulated_command ( usb, acm->usbnet.comms,
249
+						    iobuf->data,
250
+						    iob_len ( iobuf ) ) ) != 0){
251
+		DBGC ( acm, "ACM %p could not send encapsulated command: %s\n",
252
+		       acm, strerror ( rc ) );
253
+		return rc;
254
+	}
255
+
256
+	/* Complete packet immediately */
257
+	rndis_tx_complete ( rndis, iobuf );
258
+
259
+	return 0;
260
+}
261
+
262
+/**
263
+ * Receive control packet
264
+ *
265
+ * @v acm		USB RNDIS device
266
+ * @ret rc		Return status code
267
+ */
268
+static int acm_control_receive ( struct acm_device *acm ) {
269
+	struct rndis_device *rndis = acm->rndis;
270
+	struct usb_device *usb = acm->usb;
271
+	struct io_buffer *iobuf;
272
+	struct rndis_header *header;
273
+	size_t mtu = ACM_RESPONSE_MTU;
274
+	size_t len;
275
+	int rc;
276
+
277
+	/* Allocate I/O buffer */
278
+	iobuf = alloc_iob ( mtu );
279
+	if ( ! iobuf ) {
280
+		rc = -ENOMEM;
281
+		goto err_alloc;
282
+	}
283
+
284
+	/* Get encapsulated response */
285
+	if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms,
286
+						    iobuf->data, mtu ) ) != 0 ){
287
+		DBGC ( acm, "ACM %p could not get encapsulated response: %s\n",
288
+		       acm, strerror ( rc ) );
289
+		goto err_get_response;
290
+	}
291
+
292
+	/* Fix up buffer length */
293
+	header = iobuf->data;
294
+	len = le32_to_cpu ( header->len );
295
+	if ( len > mtu ) {
296
+		DBGC ( acm, "ACM %p overlength encapsulated response\n", acm );
297
+		DBGC_HDA ( acm, 0, iobuf->data, mtu );
298
+		rc = -EPROTO;
299
+		goto err_len;
300
+	}
301
+	iob_put ( iobuf, len );
302
+
303
+	/* Hand off to RNDIS */
304
+	rndis_rx ( rndis, iob_disown ( iobuf ) );
305
+
306
+	return 0;
307
+
308
+ err_len:
309
+ err_get_response:
310
+	free_iob ( iobuf );
311
+ err_alloc:
312
+	return rc;
313
+}
314
+
315
+/******************************************************************************
316
+ *
317
+ * RNDIS interface
318
+ *
319
+ ******************************************************************************
320
+ */
321
+
322
+/**
323
+ * Open RNDIS device
324
+ *
325
+ * @v rndis		RNDIS device
326
+ * @ret rc		Return status code
327
+ */
328
+static int acm_open ( struct rndis_device *rndis ) {
329
+	struct acm_device *acm = rndis->priv;
330
+	int rc;
331
+
332
+	/* Open USB network device */
333
+	if ( ( rc = usbnet_open ( &acm->usbnet ) ) != 0 )
334
+		goto err_open;
335
+
336
+	return 0;
337
+
338
+	usbnet_close ( &acm->usbnet );
339
+ err_open:
340
+	return rc;
341
+}
342
+
343
+/**
344
+ * Close RNDIS device
345
+ *
346
+ * @v rndis		RNDIS device
347
+ */
348
+static void acm_close ( struct rndis_device *rndis ) {
349
+	struct acm_device *acm = rndis->priv;
350
+
351
+	/* Close USB network device */
352
+	usbnet_close ( &acm->usbnet );
353
+}
354
+
355
+/**
356
+ * Transmit packet
357
+ *
358
+ * @v rndis		RNDIS device
359
+ * @v iobuf		I/O buffer
360
+ * @ret rc		Return status code
361
+ */
362
+static int acm_transmit ( struct rndis_device *rndis,
363
+			  struct io_buffer *iobuf ) {
364
+	struct acm_device *acm = rndis->priv;
365
+	struct rndis_header *header = iobuf->data;
366
+
367
+	/* Sanity check */
368
+	assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
369
+	assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
370
+
371
+	/* Transmit packet via appropriate mechanism */
372
+	if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
373
+		return acm_out_transmit ( acm, iobuf );
374
+	} else {
375
+		return acm_control_transmit ( acm, iobuf );
376
+	}
377
+}
378
+
379
+/**
380
+ * Poll for completed and received packets
381
+ *
382
+ * @v rndis		RNDIS device
383
+ */
384
+static void acm_poll ( struct rndis_device *rndis ) {
385
+	struct acm_device *acm = rndis->priv;
386
+	int rc;
387
+
388
+	/* Poll USB bus */
389
+	usb_poll ( acm->bus );
390
+
391
+	/* Refill rings */
392
+	if ( ( rc = usbnet_refill ( &acm->usbnet ) ) != 0 )
393
+		rndis_rx_err ( rndis, NULL, rc );
394
+
395
+	/* Retrieve encapsulated response, if applicable */
396
+	if ( acm->responded ) {
397
+
398
+		/* Clear flag */
399
+		acm->responded = 0;
400
+
401
+		/* Get encapsulated response */
402
+		if ( ( rc = acm_control_receive ( acm ) ) != 0 )
403
+			rndis_rx_err ( rndis, NULL, rc );
404
+	}
405
+}
406
+
407
+/** USB RNDIS operations */
408
+static struct rndis_operations acm_operations = {
409
+	.open		= acm_open,
410
+	.close		= acm_close,
411
+	.transmit	= acm_transmit,
412
+	.poll		= acm_poll,
413
+};
414
+
415
+/******************************************************************************
416
+ *
417
+ * USB interface
418
+ *
419
+ ******************************************************************************
420
+ */
421
+
422
+/**
423
+ * Probe device
424
+ *
425
+ * @v func		USB function
426
+ * @v config		Configuration descriptor
427
+ * @ret rc		Return status code
428
+ */
429
+static int acm_probe ( struct usb_function *func,
430
+		       struct usb_configuration_descriptor *config ) {
431
+	struct usb_device *usb = func->usb;
432
+	struct rndis_device *rndis;
433
+	struct acm_device *acm;
434
+	int rc;
435
+
436
+	/* Allocate and initialise structure */
437
+	rndis = alloc_rndis ( sizeof ( *acm ) );
438
+	if ( ! rndis ) {
439
+		rc = -ENOMEM;
440
+		goto err_alloc;
441
+	}
442
+	rndis_init ( rndis, &acm_operations );
443
+	rndis->netdev->dev = &func->dev;
444
+	acm = rndis->priv;
445
+	acm->usb = usb;
446
+	acm->bus = usb->port->hub->bus;
447
+	acm->rndis = rndis;
448
+	usbnet_init ( &acm->usbnet, func, &acm_intr_operations,
449
+		      &acm_in_operations, &acm_out_operations );
450
+	usb_refill_init ( &acm->usbnet.intr, 0, ACM_INTR_MAX_FILL );
451
+	usb_refill_init ( &acm->usbnet.in, ACM_IN_MTU, ACM_IN_MAX_FILL );
452
+
453
+	/* Describe USB network device */
454
+	if ( ( rc = usbnet_describe ( &acm->usbnet, config ) ) != 0 ) {
455
+		DBGC ( acm, "ACM %p could not describe: %s\n",
456
+		       acm, strerror ( rc ) );
457
+		goto err_describe;
458
+	}
459
+
460
+	/* Register RNDIS device */
461
+	if ( ( rc = register_rndis ( rndis ) ) != 0 )
462
+		goto err_register;
463
+
464
+	usb_func_set_drvdata ( func, acm );
465
+	return 0;
466
+
467
+	unregister_rndis ( rndis );
468
+ err_register:
469
+ err_describe:
470
+	free_rndis ( rndis );
471
+ err_alloc:
472
+	return rc;
473
+}
474
+
475
+/**
476
+ * Remove device
477
+ *
478
+ * @v func		USB function
479
+ */
480
+static void acm_remove ( struct usb_function *func ) {
481
+	struct acm_device *acm = usb_func_get_drvdata ( func );
482
+	struct rndis_device *rndis = acm->rndis;
483
+
484
+	/* Unregister RNDIS device */
485
+	unregister_rndis ( rndis );
486
+
487
+	/* Free RNDIS device */
488
+	free_rndis ( rndis );
489
+}
490
+
491
+/** USB CDC-ACM device IDs */
492
+static struct usb_device_id cdc_acm_ids[] = {
493
+	{
494
+		.name = "cdc-acm",
495
+		.vendor = USB_ANY_ID,
496
+		.product = USB_ANY_ID,
497
+	},
498
+};
499
+
500
+/** USB CDC-ACM driver */
501
+struct usb_driver cdc_acm_driver __usb_driver = {
502
+	.ids = cdc_acm_ids,
503
+	.id_count = ( sizeof ( cdc_acm_ids ) / sizeof ( cdc_acm_ids[0] ) ),
504
+	.class = USB_CLASS_ID ( USB_CLASS_CDC, USB_SUBCLASS_CDC_ACM,
505
+				USB_PROTOCOL_ACM_RNDIS ),
506
+	.score = USB_SCORE_DEPRECATED,
507
+	.probe = acm_probe,
508
+	.remove = acm_remove,
509
+};
510
+
511
+/** USB RF-RNDIS device IDs */
512
+static struct usb_device_id rf_rndis_ids[] = {
513
+	{
514
+		.name = "rf-rndis",
515
+		.vendor = USB_ANY_ID,
516
+		.product = USB_ANY_ID,
517
+	},
518
+};
519
+
520
+/** USB RF-RNDIS driver */
521
+struct usb_driver rf_rndis_driver __usb_driver = {
522
+	.ids = rf_rndis_ids,
523
+	.id_count = ( sizeof ( rf_rndis_ids ) / sizeof ( rf_rndis_ids[0] ) ),
524
+	.class = USB_CLASS_ID ( USB_CLASS_WIRELESS, USB_SUBCLASS_WIRELESS_RADIO,
525
+				USB_PROTOCOL_RADIO_RNDIS ),
526
+	.score = USB_SCORE_DEPRECATED,
527
+	.probe = acm_probe,
528
+	.remove = acm_remove,
529
+};

+ 69
- 0
src/drivers/net/acm.h View File

@@ -0,0 +1,69 @@
1
+#ifndef _ACM_H
2
+#define _ACM_H
3
+
4
+/** @file
5
+ *
6
+ * USB RNDIS Ethernet driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <ipxe/usb.h>
13
+#include <ipxe/cdc.h>
14
+
15
+/** CDC-ACM subclass */
16
+#define USB_SUBCLASS_CDC_ACM 0x02
17
+
18
+/** CDC-ACM RNDIS device protocol */
19
+#define USB_PROTOCOL_ACM_RNDIS 0xff
20
+
21
+/** Class code for wireless devices */
22
+#define USB_CLASS_WIRELESS 0xe0
23
+
24
+/** Radio frequency device subclass */
25
+#define USB_SUBCLASS_WIRELESS_RADIO 0x01
26
+
27
+/** Radio frequency RNDIS device protocol */
28
+#define USB_PROTOCOL_RADIO_RNDIS 0x03
29
+
30
+/** A USB RNDIS network device */
31
+struct acm_device {
32
+	/** USB device */
33
+	struct usb_device *usb;
34
+	/** USB bus */
35
+	struct usb_bus *bus;
36
+	/** RNDIS device */
37
+	struct rndis_device *rndis;
38
+	/** USB network device */
39
+	struct usbnet_device usbnet;
40
+
41
+	/** An encapsulated response is available */
42
+	int responded;
43
+};
44
+
45
+/** Interrupt maximum fill level
46
+ *
47
+ * This is a policy decision.
48
+ */
49
+#define ACM_INTR_MAX_FILL 2
50
+
51
+/** Bulk IN maximum fill level
52
+ *
53
+ * This is a policy decision.
54
+ */
55
+#define ACM_IN_MAX_FILL 8
56
+
57
+/** Bulk IN buffer size
58
+ *
59
+ * This is a policy decision.
60
+ */
61
+#define ACM_IN_MTU 2048
62
+
63
+/** Encapsulated response buffer size
64
+ *
65
+ * This is a policy decision.
66
+ */
67
+#define ACM_RESPONSE_MTU 128
68
+
69
+#endif /* _ACM_H */

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

@@ -14,6 +14,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
14 14
 /** Class code for communications devices */
15 15
 #define USB_CLASS_CDC 2
16 16
 
17
+/** Send encapsulated command */
18
+#define CDC_SEND_ENCAPSULATED_COMMAND					\
19
+	( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
20
+	  USB_REQUEST_TYPE ( 0x00 ) )
21
+
22
+/** Get encapsulated response */
23
+#define CDC_GET_ENCAPSULATED_RESPONSE					\
24
+	( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
25
+	  USB_REQUEST_TYPE ( 0x01 ) )
26
+
17 27
 /** Union functional descriptor */
18 28
 struct cdc_union_descriptor {
19 29
 	/** Descriptor header */
@@ -30,6 +40,11 @@ struct cdc_union_descriptor {
30 40
 /** Ethernet descriptor subtype */
31 41
 #define CDC_SUBTYPE_ETHERNET 15
32 42
 
43
+/** Response available */
44
+#define CDC_RESPONSE_AVAILABLE						\
45
+	( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
46
+	  USB_REQUEST_TYPE ( 0x01 ) )
47
+
33 48
 /** Network connection notification */
34 49
 #define CDC_NETWORK_CONNECTION						\
35 50
 	( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE |		\
@@ -52,4 +67,38 @@ extern struct cdc_union_descriptor *
52 67
 cdc_union_descriptor ( struct usb_configuration_descriptor *config,
53 68
 		       struct usb_interface_descriptor *interface );
54 69
 
70
+/**
71
+ * Send encapsulated command
72
+ *
73
+ * @v usb		USB device
74
+ * @v interface		Interface number
75
+ * @v data		Command
76
+ * @v len		Length of command
77
+ * @ret rc		Return status code
78
+ */
79
+static inline __attribute__ (( always_inline )) int
80
+cdc_send_encapsulated_command ( struct usb_device *usb, unsigned int interface,
81
+				void *data, size_t len ) {
82
+
83
+	return usb_control ( usb, CDC_SEND_ENCAPSULATED_COMMAND, 0, interface,
84
+			     data, len );
85
+}
86
+
87
+/**
88
+* Get encapsulated response
89
+*
90
+* @v usb		USB device
91
+* @v interface		Interface number
92
+* @v data		Response buffer
93
+* @v len		Length of response buffer
94
+* @ret rc		Return status code
95
+*/
96
+static inline __attribute__ (( always_inline )) int
97
+cdc_get_encapsulated_response ( struct usb_device *usb, unsigned int interface,
98
+				void *data, size_t len ) {
99
+
100
+	return usb_control ( usb, CDC_GET_ENCAPSULATED_RESPONSE, 0, interface,
101
+			     data, len );
102
+}
103
+
55 104
 #endif /* _IPXE_CDC_H */

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

@@ -184,6 +184,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
184 184
 #define ERRFILE_intelvf		     ( ERRFILE_DRIVER | 0x00780000 )
185 185
 #define ERRFILE_intelxvf	     ( ERRFILE_DRIVER | 0x00790000 )
186 186
 #define ERRFILE_smsc95xx	     ( ERRFILE_DRIVER | 0x007a0000 )
187
+#define ERRFILE_acm		     ( ERRFILE_DRIVER | 0x007b0000 )
187 188
 
188 189
 #define ERRFILE_aoe			( ERRFILE_NET | 0x00000000 )
189 190
 #define ERRFILE_arp			( ERRFILE_NET | 0x00010000 )

Loading…
Cancel
Save