iPXE
acm.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <string.h>
00028 #include <errno.h>
00029 #include <byteswap.h>
00030 #include <ipxe/profile.h>
00031 #include <ipxe/usb.h>
00032 #include <ipxe/usbnet.h>
00033 #include <ipxe/rndis.h>
00034 #include "acm.h"
00035 
00036 /** @file
00037  *
00038  * USB RNDIS driver
00039  *
00040  */
00041 
00042 /** Interrupt completion profiler */
00043 static struct profiler acm_intr_profiler __profiler =
00044         { .name = "acm.intr" };
00045 
00046 /** Bulk IN completion profiler */
00047 static struct profiler acm_in_profiler __profiler =
00048         { .name = "acm.in" };
00049 
00050 /** Bulk OUT profiler */
00051 static struct profiler acm_out_profiler __profiler =
00052         { .name = "acm.out" };
00053 
00054 /******************************************************************************
00055  *
00056  * USB RNDIS communications interface
00057  *
00058  ******************************************************************************
00059  */
00060 
00061 /**
00062  * Complete interrupt transfer
00063  *
00064  * @v ep                USB endpoint
00065  * @v iobuf             I/O buffer
00066  * @v rc                Completion status code
00067  */
00068 static void acm_intr_complete ( struct usb_endpoint *ep,
00069                                 struct io_buffer *iobuf, int rc ) {
00070         struct acm_device *acm = container_of ( ep, struct acm_device,
00071                                                 usbnet.intr );
00072         struct rndis_device *rndis = acm->rndis;
00073         struct usb_setup_packet *message;
00074 
00075         /* Profile completions */
00076         profile_start ( &acm_intr_profiler );
00077 
00078         /* Ignore packets cancelled when the endpoint closes */
00079         if ( ! ep->open )
00080                 goto ignore;
00081 
00082         /* Drop packets with errors */
00083         if ( rc != 0 ) {
00084                 DBGC ( acm, "ACM %p interrupt failed: %s\n",
00085                        acm, strerror ( rc ) );
00086                 DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
00087                 goto error;
00088         }
00089 
00090         /* Extract message header */
00091         if ( iob_len ( iobuf ) < sizeof ( *message ) ) {
00092                 DBGC ( acm, "ACM %p underlength interrupt:\n", acm );
00093                 DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
00094                 rc = -EINVAL;
00095                 goto error;
00096         }
00097         message = iobuf->data;
00098 
00099         /* Parse message header */
00100         switch ( message->request ) {
00101 
00102         case cpu_to_le16 ( CDC_RESPONSE_AVAILABLE ) :
00103         case cpu_to_le16 ( 0x0001 ) : /* qemu seems to use this value */
00104                 acm->responded = 1;
00105                 break;
00106 
00107         default:
00108                 DBGC ( acm, "ACM %p unrecognised interrupt:\n", acm );
00109                 DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) );
00110                 rc = -ENOTSUP;
00111                 goto error;
00112         }
00113 
00114         /* Free I/O buffer */
00115         free_iob ( iobuf );
00116         profile_stop ( &acm_intr_profiler );
00117 
00118         return;
00119 
00120  error:
00121         rndis_rx_err ( rndis, iob_disown ( iobuf ), rc );
00122  ignore:
00123         free_iob ( iobuf );
00124         return;
00125 }
00126 
00127 /** Interrupt endpoint operations */
00128 static struct usb_endpoint_driver_operations acm_intr_operations = {
00129         .complete = acm_intr_complete,
00130 };
00131 
00132 /******************************************************************************
00133  *
00134  * USB RNDIS data interface
00135  *
00136  ******************************************************************************
00137  */
00138 
00139 /**
00140  * Complete bulk IN transfer
00141  *
00142  * @v ep                USB endpoint
00143  * @v iobuf             I/O buffer
00144  * @v rc                Completion status code
00145  */
00146 static void acm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
00147                               int rc ) {
00148         struct acm_device *acm = container_of ( ep, struct acm_device,
00149                                                 usbnet.in );
00150         struct rndis_device *rndis = acm->rndis;
00151 
00152         /* Profile receive completions */
00153         profile_start ( &acm_in_profiler );
00154 
00155         /* Ignore packets cancelled when the endpoint closes */
00156         if ( ! ep->open )
00157                 goto ignore;
00158 
00159         /* Record USB errors against the RNDIS device */
00160         if ( rc != 0 ) {
00161                 DBGC ( acm, "ACM %p bulk IN failed: %s\n",
00162                        acm, strerror ( rc ) );
00163                 goto error;
00164         }
00165 
00166         /* Hand off to RNDIS */
00167         rndis_rx ( rndis, iob_disown ( iobuf ) );
00168 
00169         profile_stop ( &acm_in_profiler );
00170         return;
00171 
00172  error:
00173         rndis_rx_err ( rndis, iob_disown ( iobuf ), rc );
00174  ignore:
00175         free_iob ( iobuf );
00176 }
00177 
00178 /** Bulk IN endpoint operations */
00179 static struct usb_endpoint_driver_operations acm_in_operations = {
00180         .complete = acm_in_complete,
00181 };
00182 
00183 /**
00184  * Transmit packet
00185  *
00186  * @v acm               USB RNDIS device
00187  * @v iobuf             I/O buffer
00188  * @ret rc              Return status code
00189  */
00190 static int acm_out_transmit ( struct acm_device *acm,
00191                               struct io_buffer *iobuf ) {
00192         int rc;
00193 
00194         /* Profile transmissions */
00195         profile_start ( &acm_out_profiler );
00196 
00197         /* Enqueue I/O buffer */
00198         if ( ( rc = usb_stream ( &acm->usbnet.out, iobuf, 0 ) ) != 0 )
00199                 return rc;
00200 
00201         profile_stop ( &acm_out_profiler );
00202         return 0;
00203 }
00204 
00205 /**
00206  * Complete bulk OUT transfer
00207  *
00208  * @v ep                USB endpoint
00209  * @v iobuf             I/O buffer
00210  * @v rc                Completion status code
00211  */
00212 static void acm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf,
00213                                int rc ) {
00214         struct acm_device *acm = container_of ( ep, struct acm_device,
00215                                                 usbnet.out );
00216         struct rndis_device *rndis = acm->rndis;
00217 
00218         /* Report TX completion */
00219         rndis_tx_complete_err ( rndis, iobuf, rc );
00220 }
00221 
00222 /** Bulk OUT endpoint operations */
00223 static struct usb_endpoint_driver_operations acm_out_operations = {
00224         .complete = acm_out_complete,
00225 };
00226 
00227 /******************************************************************************
00228  *
00229  * USB RNDIS control interface
00230  *
00231  ******************************************************************************
00232  */
00233 
00234 /**
00235  * Send control packet
00236  *
00237  * @v acm               USB RNDIS device
00238  * @v iobuf             I/O buffer
00239  * @ret rc              Return status code
00240  */
00241 static int acm_control_transmit ( struct acm_device *acm,
00242                                   struct io_buffer *iobuf ) {
00243         struct rndis_device *rndis = acm->rndis;
00244         struct usb_device *usb = acm->usb;
00245         int rc;
00246 
00247         /* Send packet as an encapsulated command */
00248         if ( ( rc = cdc_send_encapsulated_command ( usb, acm->usbnet.comms,
00249                                                     iobuf->data,
00250                                                     iob_len ( iobuf ) ) ) != 0){
00251                 DBGC ( acm, "ACM %p could not send encapsulated command: %s\n",
00252                        acm, strerror ( rc ) );
00253                 return rc;
00254         }
00255 
00256         /* Complete packet immediately */
00257         rndis_tx_complete ( rndis, iobuf );
00258 
00259         return 0;
00260 }
00261 
00262 /**
00263  * Receive control packet
00264  *
00265  * @v acm               USB RNDIS device
00266  * @ret rc              Return status code
00267  */
00268 static int acm_control_receive ( struct acm_device *acm ) {
00269         struct rndis_device *rndis = acm->rndis;
00270         struct usb_device *usb = acm->usb;
00271         struct io_buffer *iobuf;
00272         struct rndis_header *header;
00273         size_t mtu = ACM_RESPONSE_MTU;
00274         size_t len;
00275         int rc;
00276 
00277         /* Allocate I/O buffer */
00278         iobuf = alloc_iob ( mtu );
00279         if ( ! iobuf ) {
00280                 rc = -ENOMEM;
00281                 goto err_alloc;
00282         }
00283 
00284         /* Get encapsulated response */
00285         if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms,
00286                                                     iobuf->data, mtu ) ) != 0 ){
00287                 DBGC ( acm, "ACM %p could not get encapsulated response: %s\n",
00288                        acm, strerror ( rc ) );
00289                 goto err_get_response;
00290         }
00291 
00292         /* Fix up buffer length */
00293         header = iobuf->data;
00294         len = le32_to_cpu ( header->len );
00295         if ( len > mtu ) {
00296                 DBGC ( acm, "ACM %p overlength encapsulated response\n", acm );
00297                 DBGC_HDA ( acm, 0, iobuf->data, mtu );
00298                 rc = -EPROTO;
00299                 goto err_len;
00300         }
00301         iob_put ( iobuf, len );
00302 
00303         /* Hand off to RNDIS */
00304         rndis_rx ( rndis, iob_disown ( iobuf ) );
00305 
00306         return 0;
00307 
00308  err_len:
00309  err_get_response:
00310         free_iob ( iobuf );
00311  err_alloc:
00312         return rc;
00313 }
00314 
00315 /******************************************************************************
00316  *
00317  * RNDIS interface
00318  *
00319  ******************************************************************************
00320  */
00321 
00322 /**
00323  * Open RNDIS device
00324  *
00325  * @v rndis             RNDIS device
00326  * @ret rc              Return status code
00327  */
00328 static int acm_open ( struct rndis_device *rndis ) {
00329         struct acm_device *acm = rndis->priv;
00330         int rc;
00331 
00332         /* Open USB network device */
00333         if ( ( rc = usbnet_open ( &acm->usbnet ) ) != 0 )
00334                 goto err_open;
00335 
00336         return 0;
00337 
00338         usbnet_close ( &acm->usbnet );
00339  err_open:
00340         return rc;
00341 }
00342 
00343 /**
00344  * Close RNDIS device
00345  *
00346  * @v rndis             RNDIS device
00347  */
00348 static void acm_close ( struct rndis_device *rndis ) {
00349         struct acm_device *acm = rndis->priv;
00350 
00351         /* Close USB network device */
00352         usbnet_close ( &acm->usbnet );
00353 }
00354 
00355 /**
00356  * Transmit packet
00357  *
00358  * @v rndis             RNDIS device
00359  * @v iobuf             I/O buffer
00360  * @ret rc              Return status code
00361  */
00362 static int acm_transmit ( struct rndis_device *rndis,
00363                           struct io_buffer *iobuf ) {
00364         struct acm_device *acm = rndis->priv;
00365         struct rndis_header *header = iobuf->data;
00366 
00367         /* Sanity check */
00368         assert ( iob_len ( iobuf ) >= sizeof ( *header ) );
00369         assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) );
00370 
00371         /* Transmit packet via appropriate mechanism */
00372         if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) {
00373                 return acm_out_transmit ( acm, iobuf );
00374         } else {
00375                 return acm_control_transmit ( acm, iobuf );
00376         }
00377 }
00378 
00379 /**
00380  * Poll for completed and received packets
00381  *
00382  * @v rndis             RNDIS device
00383  */
00384 static void acm_poll ( struct rndis_device *rndis ) {
00385         struct acm_device *acm = rndis->priv;
00386         int rc;
00387 
00388         /* Poll USB bus */
00389         usb_poll ( acm->bus );
00390 
00391         /* Refill rings */
00392         if ( ( rc = usbnet_refill ( &acm->usbnet ) ) != 0 )
00393                 rndis_rx_err ( rndis, NULL, rc );
00394 
00395         /* Retrieve encapsulated response, if applicable */
00396         if ( acm->responded ) {
00397 
00398                 /* Clear flag */
00399                 acm->responded = 0;
00400 
00401                 /* Get encapsulated response */
00402                 if ( ( rc = acm_control_receive ( acm ) ) != 0 )
00403                         rndis_rx_err ( rndis, NULL, rc );
00404         }
00405 }
00406 
00407 /** USB RNDIS operations */
00408 static struct rndis_operations acm_operations = {
00409         .open           = acm_open,
00410         .close          = acm_close,
00411         .transmit       = acm_transmit,
00412         .poll           = acm_poll,
00413 };
00414 
00415 /******************************************************************************
00416  *
00417  * USB interface
00418  *
00419  ******************************************************************************
00420  */
00421 
00422 /**
00423  * Probe device
00424  *
00425  * @v func              USB function
00426  * @v config            Configuration descriptor
00427  * @ret rc              Return status code
00428  */
00429 static int acm_probe ( struct usb_function *func,
00430                        struct usb_configuration_descriptor *config ) {
00431         struct usb_device *usb = func->usb;
00432         struct rndis_device *rndis;
00433         struct acm_device *acm;
00434         int rc;
00435 
00436         /* Allocate and initialise structure */
00437         rndis = alloc_rndis ( sizeof ( *acm ) );
00438         if ( ! rndis ) {
00439                 rc = -ENOMEM;
00440                 goto err_alloc;
00441         }
00442         rndis_init ( rndis, &acm_operations );
00443         rndis->netdev->dev = &func->dev;
00444         acm = rndis->priv;
00445         acm->usb = usb;
00446         acm->bus = usb->port->hub->bus;
00447         acm->rndis = rndis;
00448         usbnet_init ( &acm->usbnet, func, &acm_intr_operations,
00449                       &acm_in_operations, &acm_out_operations );
00450         usb_refill_init ( &acm->usbnet.intr, 0, 0, ACM_INTR_MAX_FILL );
00451         usb_refill_init ( &acm->usbnet.in, 0, ACM_IN_MTU, ACM_IN_MAX_FILL );
00452 
00453         /* Describe USB network device */
00454         if ( ( rc = usbnet_describe ( &acm->usbnet, config ) ) != 0 ) {
00455                 DBGC ( acm, "ACM %p could not describe: %s\n",
00456                        acm, strerror ( rc ) );
00457                 goto err_describe;
00458         }
00459 
00460         /* Register RNDIS device */
00461         if ( ( rc = register_rndis ( rndis ) ) != 0 )
00462                 goto err_register;
00463 
00464         usb_func_set_drvdata ( func, acm );
00465         return 0;
00466 
00467         unregister_rndis ( rndis );
00468  err_register:
00469  err_describe:
00470         free_rndis ( rndis );
00471  err_alloc:
00472         return rc;
00473 }
00474 
00475 /**
00476  * Remove device
00477  *
00478  * @v func              USB function
00479  */
00480 static void acm_remove ( struct usb_function *func ) {
00481         struct acm_device *acm = usb_func_get_drvdata ( func );
00482         struct rndis_device *rndis = acm->rndis;
00483 
00484         /* Unregister RNDIS device */
00485         unregister_rndis ( rndis );
00486 
00487         /* Free RNDIS device */
00488         free_rndis ( rndis );
00489 }
00490 
00491 /** USB CDC-ACM device IDs */
00492 static struct usb_device_id cdc_acm_ids[] = {
00493         {
00494                 .name = "cdc-acm",
00495                 .vendor = USB_ANY_ID,
00496                 .product = USB_ANY_ID,
00497         },
00498 };
00499 
00500 /** USB CDC-ACM driver */
00501 struct usb_driver cdc_acm_driver __usb_driver = {
00502         .ids = cdc_acm_ids,
00503         .id_count = ( sizeof ( cdc_acm_ids ) / sizeof ( cdc_acm_ids[0] ) ),
00504         .class = USB_CLASS_ID ( USB_CLASS_CDC, USB_SUBCLASS_CDC_ACM,
00505                                 USB_PROTOCOL_ACM_RNDIS ),
00506         .score = USB_SCORE_DEPRECATED,
00507         .probe = acm_probe,
00508         .remove = acm_remove,
00509 };
00510 
00511 /** USB RF-RNDIS device IDs */
00512 static struct usb_device_id rf_rndis_ids[] = {
00513         {
00514                 .name = "rf-rndis",
00515                 .vendor = USB_ANY_ID,
00516                 .product = USB_ANY_ID,
00517         },
00518 };
00519 
00520 /** USB RF-RNDIS driver */
00521 struct usb_driver rf_rndis_driver __usb_driver = {
00522         .ids = rf_rndis_ids,
00523         .id_count = ( sizeof ( rf_rndis_ids ) / sizeof ( rf_rndis_ids[0] ) ),
00524         .class = USB_CLASS_ID ( USB_CLASS_WIRELESS, USB_SUBCLASS_WIRELESS_RADIO,
00525                                 USB_PROTOCOL_RADIO_RNDIS ),
00526         .score = USB_SCORE_DEPRECATED,
00527         .probe = acm_probe,
00528         .remove = acm_remove,
00529 };