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 */
acm_intr_complete(struct usb_endpoint * ep,struct io_buffer * iobuf,int rc)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 */
acm_in_complete(struct usb_endpoint * ep,struct io_buffer * iobuf,int rc)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 */
acm_out_transmit(struct acm_device * acm,struct io_buffer * iobuf)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 */
acm_out_complete(struct usb_endpoint * ep,struct io_buffer * iobuf,int rc)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 */
acm_control_transmit(struct acm_device * acm,struct io_buffer * iobuf)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 */
acm_control_receive(struct acm_device * acm)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 */
acm_open(struct rndis_device * rndis)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 */
acm_close(struct rndis_device * rndis)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 */
acm_transmit(struct rndis_device * rndis,struct io_buffer * iobuf)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 */
acm_poll(struct rndis_device * rndis)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 */
acm_probe(struct usb_function * func,struct usb_configuration_descriptor * config)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, 0, ACM_INTR_MAX_FILL );
451 usb_refill_init ( &acm->usbnet.in, 0, 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 */
acm_remove(struct usb_function * func)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 };
530