/** * Complete bulk IN transfer * * @v ep USB endpoint * @v iobuf I/O buffer * @v rc Completion status code */ static void acm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct acm_device *acm = container_of ( ep, struct acm_device, usbnet.in ); struct rndis_device *rndis = acm->rndis; /* Profile receive completions */ profile_start ( &acm_in_profiler ); /* Ignore packets cancelled when the endpoint closes */ if ( ! ep->open ) goto ignore; /* Record USB errors against the RNDIS device */ if ( rc != 0 ) { DBGC ( acm, "ACM %p bulk IN failed: %s\n", acm, strerror ( rc ) ); goto error; } /* Hand off to RNDIS */ rndis_rx ( rndis, iob_disown ( iobuf ) ); profile_stop ( &acm_in_profiler ); return; error: rndis_rx_err ( rndis, iob_disown ( iobuf ), rc ); ignore: free_iob ( iobuf ); }
/** * Complete bulk IN transfer * * @v ep USB endpoint * @v iobuf I/O buffer * @v rc Completion status code */ static void smsc95xx_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct smsc95xx_device *smsc95xx = container_of ( ep, struct smsc95xx_device, usbnet.in ); struct net_device *netdev = smsc95xx->netdev; struct smsc95xx_rx_header *header; /* Profile completions */ profile_start ( &smsc95xx_in_profiler ); /* Ignore packets cancelled when the endpoint closes */ if ( ! ep->open ) { free_iob ( iobuf ); return; } /* Record USB errors against the network device */ if ( rc != 0 ) { DBGC ( smsc95xx, "SMSC95XX %p bulk IN failed: %s\n", smsc95xx, strerror ( rc ) ); goto err; } /* Sanity check */ if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { DBGC ( smsc95xx, "SMSC95XX %p underlength bulk IN\n", smsc95xx ); DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EINVAL; goto err; } /* Strip header and CRC */ header = iobuf->data; iob_pull ( iobuf, sizeof ( *header ) ); iob_unput ( iobuf, 4 /* CRC */ ); /* Check for errors */ if ( header->command & cpu_to_le32 ( SMSC95XX_RX_RUNT | SMSC95XX_RX_LATE | SMSC95XX_RX_CRC ) ) { DBGC ( smsc95xx, "SMSC95XX %p receive error (%08x):\n", smsc95xx, le32_to_cpu ( header->command ) ); DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EIO; goto err; } /* Hand off to network stack */ netdev_rx ( netdev, iob_disown ( iobuf ) ); profile_stop ( &smsc95xx_in_profiler ); return; err: /* Hand off to network stack */ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); }
/** * Complete bulk IN transfer * * @v ep USB endpoint * @v iobuf I/O buffer * @v rc Completion status code */ static void dm96xx_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, usbnet.in ); struct net_device *netdev = dm96xx->netdev; struct dm96xx_rx_header *header; /* Ignore packets cancelled when the endpoint closes */ if ( ! ep->open ) { free_iob ( iobuf ); return; } /* Record USB errors against the network device */ if ( rc != 0 ) { DBGC ( dm96xx, "DM96XX %p bulk IN failed: %s\n", dm96xx, strerror ( rc ) ); goto err; } /* Sanity check */ if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { DBGC ( dm96xx, "DM96XX %p underlength bulk IN\n", dm96xx ); DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EINVAL; goto err; } /* Strip header and CRC */ header = iobuf->data; iob_pull ( iobuf, sizeof ( *header ) ); iob_unput ( iobuf, 4 /* CRC */ ); /* Check status */ if ( header->rsr & ~DM96XX_RSR_MF ) { DBGC ( dm96xx, "DM96XX %p receive error %02x:\n", dm96xx, header->rsr ); DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EIO; goto err; } /* Hand off to network stack */ netdev_rx ( netdev, iob_disown ( iobuf ) ); return; err: /* Hand off to network stack */ netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); }
/** * Complete interrupt transfer * * @v ep USB endpoint * @v iobuf I/O buffer * @v rc Completion status code */ static void acm_intr_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct acm_device *acm = container_of ( ep, struct acm_device, usbnet.intr ); struct rndis_device *rndis = acm->rndis; struct usb_setup_packet *message; /* Profile completions */ profile_start ( &acm_intr_profiler ); /* Ignore packets cancelled when the endpoint closes */ if ( ! ep->open ) goto ignore; /* Drop packets with errors */ if ( rc != 0 ) { DBGC ( acm, "ACM %p interrupt failed: %s\n", acm, strerror ( rc ) ); DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); goto error; } /* Extract message header */ if ( iob_len ( iobuf ) < sizeof ( *message ) ) { DBGC ( acm, "ACM %p underlength interrupt:\n", acm ); DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); rc = -EINVAL; goto error; } message = iobuf->data; /* Parse message header */ switch ( message->request ) { case cpu_to_le16 ( CDC_RESPONSE_AVAILABLE ) : case cpu_to_le16 ( 0x0001 ) : /* qemu seems to use this value */ acm->responded = 1; break; default: DBGC ( acm, "ACM %p unrecognised interrupt:\n", acm ); DBGC_HDA ( acm, 0, iobuf->data, iob_len ( iobuf ) ); rc = -ENOTSUP; goto error; } /* Free I/O buffer */ free_iob ( iobuf ); profile_stop ( &acm_intr_profiler ); return; error: rndis_rx_err ( rndis, iob_disown ( iobuf ), rc ); ignore: free_iob ( iobuf ); return; }
/** * Receive control packet * * @v acm USB RNDIS device * @ret rc Return status code */ static int acm_control_receive ( struct acm_device *acm ) { struct rndis_device *rndis = acm->rndis; struct usb_device *usb = acm->usb; struct io_buffer *iobuf; struct rndis_header *header; size_t mtu = ACM_RESPONSE_MTU; size_t len; int rc; /* Allocate I/O buffer */ iobuf = alloc_iob ( mtu ); if ( ! iobuf ) { rc = -ENOMEM; goto err_alloc; } /* Get encapsulated response */ if ( ( rc = cdc_get_encapsulated_response ( usb, acm->usbnet.comms, iobuf->data, mtu ) ) != 0 ){ DBGC ( acm, "ACM %p could not get encapsulated response: %s\n", acm, strerror ( rc ) ); goto err_get_response; } /* Fix up buffer length */ header = iobuf->data; len = le32_to_cpu ( header->len ); if ( len > mtu ) { DBGC ( acm, "ACM %p overlength encapsulated response\n", acm ); DBGC_HDA ( acm, 0, iobuf->data, mtu ); rc = -EPROTO; goto err_len; } iob_put ( iobuf, len ); /* Hand off to RNDIS */ rndis_rx ( rndis, iob_disown ( iobuf ) ); return 0; err_len: err_get_response: free_iob ( iobuf ); err_alloc: return rc; }