/** * Handle SRP login rejection * * @v srp SRP device * @v iobuf I/O buffer * @ret rc Return status code */ static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) { struct srp_login_rej *login_rej = iobuf->data; int rc; DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n", srp, ntohl ( login_rej->tag.dwords[0] ), ntohl ( login_rej->tag.dwords[1] ) ); /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) { DBGC ( srp, "SRP %p RX login rejection too short (%zd " "bytes)\n", srp, iob_len ( iobuf ) ); rc = -EINVAL; goto out; } /* Login rejection always indicates an error */ DBGC ( srp, "SRP %p login rejected (reason %08x)\n", srp, ntohl ( login_rej->reason ) ); rc = -EPERM; out: free_iob ( iobuf ); return rc; }
/** * Handle SRP login response * * @v srp SRP device * @v iobuf I/O buffer * @ret rc Return status code */ static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) { struct srp_login_rsp *login_rsp = iobuf->data; int rc; DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n", srp, ntohl ( login_rsp->tag.dwords[0] ), ntohl ( login_rsp->tag.dwords[1] ) ); /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) { DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n", srp, iob_len ( iobuf ) ); rc = -EINVAL; goto out; } DBGC ( srp, "SRP %p logged in\n", srp ); /* Mark as logged in */ srp->state |= SRP_STATE_LOGGED_IN; /* Reset error counter */ srp->retry_count = 0; /* Issue pending command */ srp_cmd ( srp ); rc = 0; out: free_iob ( iobuf ); return rc; }
/** * Concatenate I/O buffers into a single buffer * * @v list List of I/O buffers * @ret iobuf Concatenated I/O buffer, or NULL on allocation failure * * After a successful concatenation, the list will be empty. */ struct io_buffer * iob_concatenate ( struct list_head *list ) { struct io_buffer *iobuf; struct io_buffer *tmp; struct io_buffer *concatenated; size_t len = 0; /* If the list contains only a single entry, avoid an * unnecessary additional allocation. */ if ( list_is_singular ( list ) ) { iobuf = list_first_entry ( list, struct io_buffer, list ); INIT_LIST_HEAD ( list ); return iobuf; } /* Calculate total length */ list_for_each_entry ( iobuf, list, list ) len += iob_len ( iobuf ); /* Allocate new I/O buffer */ concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 ); if ( ! concatenated ) return NULL; /* Move data to new I/O buffer */ list_for_each_entry_safe ( iobuf, tmp, list, list ) { list_del ( &iobuf->list ); memcpy ( iob_put ( concatenated, iob_len ( iobuf ) ), iobuf->data, iob_len ( iobuf ) ); free_iob ( iobuf ); }
/** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int rhine_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct rhine_nic *rhn = netdev->priv; struct rhine_descriptor *desc; physaddr_t address; unsigned int tx_idx; /* Get next transmit descriptor */ if ( ( rhn->tx.prod - rhn->tx.cons ) >= RHINE_TXDESC_NUM ) return -ENOBUFS; tx_idx = ( rhn->tx.prod++ % RHINE_TXDESC_NUM ); desc = &rhn->tx.desc[tx_idx]; /* Pad and align packet */ iob_pad ( iobuf, ETH_ZLEN ); address = virt_to_bus ( iobuf->data ); /* Populate transmit descriptor */ desc->buffer = cpu_to_le32 ( address ); desc->des1 = cpu_to_le32 ( RHINE_DES1_IC | RHINE_TDES1_STP | RHINE_TDES1_EDP | RHINE_DES1_CHAIN | RHINE_DES1_SIZE ( iob_len ( iobuf ) ) ); wmb(); desc->des0 = cpu_to_le32 ( RHINE_DES0_OWN ); wmb(); /* Notify card that there are packets ready to transmit */ writeb ( ( rhn->cr1 | RHINE_CR1_TXPOLL ), rhn->regs + RHINE_CR1 ); DBGC2 ( rhn, "RHINE %p TX %d is [%llx,%llx)\n", rhn, tx_idx, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); return 0; }
/** * Encapsulate and encrypt a packet using CCMP * * @v crypto CCMP cryptosystem * @v iob I/O buffer containing cleartext packet * @ret eiob I/O buffer containing encrypted packet */ struct io_buffer * ccmp_encrypt ( struct net80211_crypto *crypto, struct io_buffer *iob ) { struct ccmp_ctx *ctx = crypto->priv; struct ieee80211_frame *hdr = iob->data; struct io_buffer *eiob; const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; int datalen = iob_len ( iob ) - hdrlen; struct ccmp_head head; struct ccmp_nonce nonce; struct ccmp_aad aad; u8 mic[8], tx_pn[6]; void *edata, *emic; ctx->tx_seq++; u64_to_pn ( ctx->tx_seq, tx_pn, PN_LSB ); /* Allocate memory */ eiob = alloc_iob ( iob_len ( iob ) + CCMP_HEAD_LEN + CCMP_MIC_LEN ); if ( ! eiob ) return NULL; /* Copy frame header */ memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); hdr = eiob->data; hdr->fc |= IEEE80211_FC_PROTECTED; /* Fill in packet number and extended IV */ memcpy ( head.pn_lo, tx_pn, 2 ); memcpy ( head.pn_hi, tx_pn + 2, 4 ); head.kid = 0x20; /* have Extended IV, key ID 0 */ head._rsvd = 0; memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) ); /* Form nonce */ nonce.prio = 0; memcpy ( nonce.a2, hdr->addr2, ETH_ALEN ); u64_to_pn ( ctx->tx_seq, nonce.pn, PN_MSB ); /* Form additional authentication data */ aad.fc = hdr->fc & CCMP_AAD_FC_MASK; memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */ aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK; /* Calculate MIC over the data */ ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, mic ); /* Copy and encrypt data and MIC */ edata = iob_put ( eiob, datalen ); emic = iob_put ( eiob, CCMP_MIC_LEN ); ccmp_ctr_xor ( ctx, &nonce, iob->data + hdrlen, edata, datalen, mic, emic ); /* Done! */ DBGC2 ( ctx, "WPA-CCMP %p: encrypted packet %p -> %p\n", ctx, iob, eiob ); return eiob; }
/** * 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; }
/** * 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 ); }
static int legacy_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct nic *nic = netdev->priv; struct ethhdr *ethhdr; DBG ( "Transmitting %zd bytes\n", iob_len ( iobuf ) ); iob_pad ( iobuf, ETH_ZLEN ); ethhdr = iobuf->data; iob_pull ( iobuf, sizeof ( *ethhdr ) ); nic->nic_op->transmit ( nic, ( const char * ) ethhdr->h_dest, ntohs ( ethhdr->h_protocol ), iob_len ( iobuf ), iobuf->data ); netdev_tx_complete ( netdev, iobuf ); return 0; }
/** * e1000_transmit - Transmit a packet * * @v netdev Network device * @v iobuf I/O buffer * * @ret rc Returns 0 on success, negative on failure */ static int e1000_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct e1000_adapter *adapter = netdev_priv( netdev ); struct e1000_hw *hw = &adapter->hw; uint32_t tx_curr = adapter->tx_tail; struct e1000_tx_desc *tx_curr_desc; DBG ("e1000_transmit\n"); if ( adapter->tx_fill_ctr == NUM_TX_DESC ) { DBG ("TX overflow\n"); return -ENOBUFS; } /* Save pointer to iobuf we have been given to transmit, netdev_tx_complete() will need it later */ adapter->tx_iobuf[tx_curr] = iobuf; tx_curr_desc = ( void * ) ( adapter->tx_base ) + ( tx_curr * sizeof ( *adapter->tx_base ) ); DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); DBG ( "tx_curr_desc + 16 = %#08lx\n", virt_to_bus ( tx_curr_desc ) + 16 ); DBG ( "iobuf->data = %#08lx\n", virt_to_bus ( iobuf->data ) ); /* Add the packet to TX ring */ tx_curr_desc->buffer_addr = virt_to_bus ( iobuf->data ); tx_curr_desc->lower.data = E1000_TXD_CMD_RPS | E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS | iob_len ( iobuf ); tx_curr_desc->upper.data = 0; DBG ( "TX fill: %d tx_curr: %d addr: %#08lx len: %zd\n", adapter->tx_fill_ctr, tx_curr, virt_to_bus ( iobuf->data ), iob_len ( iobuf ) ); /* Point to next free descriptor */ adapter->tx_tail = ( adapter->tx_tail + 1 ) % NUM_TX_DESC; adapter->tx_fill_ctr++; /* Write new tail to NIC, making packet available for transmit */ wmb(); E1000_WRITE_REG ( hw, TDT, adapter->tx_tail ); return 0; }
/** * 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 ); }
static void gdbudp_send ( const char *buf, size_t len ) { struct io_buffer *iob; struct ethhdr *ethhdr; struct iphdr *iphdr; struct udp_header *udphdr; /* Check that we are connected */ if ( dest_addr.sin_port == 0 ) { return; } gdbudp_ensure_netdev_open ( netdev ); iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ); if ( !iob ) { return; } /* Payload */ iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) ); memcpy ( iob_put ( iob, len ), buf, len ); /* UDP header */ udphdr = iob_push ( iob, sizeof ( *udphdr ) ); udphdr->src = source_addr.sin_port; udphdr->dest = dest_addr.sin_port; udphdr->len = htons ( iob_len ( iob ) ); udphdr->chksum = 0; /* optional and we are not using it */ /* IP header */ iphdr = iob_push ( iob, sizeof ( *iphdr ) ); memset ( iphdr, 0, sizeof ( *iphdr ) ); iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); iphdr->service = IP_TOS; iphdr->len = htons ( iob_len ( iob ) ); iphdr->ttl = IP_TTL; iphdr->protocol = IP_UDP; iphdr->dest.s_addr = dest_addr.sin_addr.s_addr; iphdr->src.s_addr = source_addr.sin_addr.s_addr; iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); /* Ethernet header */ ethhdr = iob_push ( iob, sizeof ( *ethhdr ) ); memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN ); memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); ethhdr->h_protocol = htons ( ETH_P_IP ); netdev_tx ( netdev, iob ); }
/** Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int b44_transmit(struct net_device *netdev, struct io_buffer *iobuf) { struct b44_private *bp = netdev_priv(netdev); u32 cur = bp->tx_cur; u32 ctrl; /* Check for TX ring overflow */ if (bp->tx[cur].ctrl) { DBG("tx overflow\n"); return -ENOBUFS; } /* Will call netdev_tx_complete() on the iobuf later */ bp->tx_iobuf[cur] = iobuf; /* Set up TX descriptor */ ctrl = (iob_len(iobuf) & DESC_CTRL_LEN) | DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF; if (cur == B44_RING_LAST) ctrl |= DESC_CTRL_EOT; bp->tx[cur].ctrl = cpu_to_le32(ctrl); bp->tx[cur].addr = cpu_to_le32(VIRT_TO_B44(iobuf->data)); /* Update next available descriptor index */ cur = ring_next(cur); bp->tx_cur = cur; wmb(); /* Tell card that a new TX descriptor is ready */ bw32(bp, B44_DMATX_PTR, cur * sizeof(struct dma_desc)); return 0; }
/** * Transmit packet * * @v rndis RNDIS device * @v iobuf I/O buffer * @ret rc Return status code */ static int acm_transmit ( struct rndis_device *rndis, struct io_buffer *iobuf ) { struct acm_device *acm = rndis->priv; struct rndis_header *header = iobuf->data; /* Sanity check */ assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); /* Transmit packet via appropriate mechanism */ if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) { return acm_out_transmit ( acm, iobuf ); } else { return acm_control_transmit ( acm, iobuf ); } }
/** * Dump LACP packet * * @v iobuf I/O buffer * @v netdev Network device * @v label "RX" or "TX" */ static void eth_slow_lacp_dump ( struct io_buffer *iobuf, struct net_device *netdev, const char *label ) { union eth_slow_packet *eth_slow = iobuf->data; struct eth_slow_lacp *lacp = ð_slow->lacp; DBGC ( netdev, "SLOW %s %s LACP actor (%04x,%s,%04x,%02x,%04x) [%s]\n", netdev->name, label, ntohs ( lacp->actor.system_priority ), eth_ntoa ( lacp->actor.system ), ntohs ( lacp->actor.key ), ntohs ( lacp->actor.port_priority ), ntohs ( lacp->actor.port ), eth_slow_lacp_state_name ( lacp->actor.state ) ); DBGC ( netdev, "SLOW %s %s LACP partner (%04x,%s,%04x,%02x,%04x) [%s]\n", netdev->name, label, ntohs ( lacp->partner.system_priority ), eth_ntoa ( lacp->partner.system ), ntohs ( lacp->partner.key ), ntohs ( lacp->partner.port_priority ), ntohs ( lacp->partner.port ), eth_slow_lacp_state_name ( lacp->partner.state ) ); DBGC ( netdev, "SLOW %s %s LACP collector %04x (%d us)\n", netdev->name, label, ntohs ( lacp->collector.max_delay ), ( ntohs ( lacp->collector.max_delay ) * 10 ) ); DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); }
/** * Deliver datagram * * @v intf Data transfer interface * @v iobuf Datagram I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ int xfer_deliver ( struct interface *intf, struct io_buffer *iobuf, struct xfer_metadata *meta ) { struct interface *dest; xfer_deliver_TYPE ( void * ) *op = intf_get_dest_op ( intf, xfer_deliver, &dest ); void *object = intf_object ( dest ); int rc; DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " deliver %zd\n", INTF_INTF_DBG ( intf, dest ), iob_len ( iobuf ) ); if ( op ) { rc = op ( object, iobuf, meta ); } else { /* Default is to discard the I/O buffer */ free_iob ( iobuf ); rc = -EPIPE; } if ( rc != 0 ) { DBGC ( INTF_COL ( intf ), "INTF " INTF_INTF_FMT " deliver failed: %s\n", INTF_INTF_DBG ( intf, dest ), strerror ( rc ) ); } intf_put ( dest ); return rc; }
/** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int nii_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct nii_nic *nii = netdev->priv; PXE_CPB_TRANSMIT cpb; int stat; int rc; /* Defer the packet if there is already a transmission in progress */ if ( nii->txbuf ) { netdev_tx_defer ( netdev, iobuf ); return 0; } /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); cpb.FrameAddr = virt_to_bus ( iobuf->data ); cpb.DataLen = iob_len ( iobuf ); cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len; /* Transmit packet */ if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not transmit: %s\n", nii->dev.name, strerror ( rc ) ); return rc; } nii->txbuf = iobuf; return 0; }
/** * Handle received data * * @v downloader Downloader * @v iobuf Datagram I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ static int downloader_xfer_deliver ( struct downloader *downloader, struct io_buffer *iobuf, struct xfer_metadata *meta ) { size_t len; size_t max; int rc; /* Calculate new buffer position */ if ( meta->flags & XFER_FL_ABS_OFFSET ) downloader->pos = 0; downloader->pos += meta->offset; /* Ensure that we have enough buffer space for this data */ len = iob_len ( iobuf ); max = ( downloader->pos + len ); if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 ) goto done; /* Copy data to buffer */ copy_to_user ( downloader->image->data, downloader->pos, iobuf->data, len ); /* Update current buffer position */ downloader->pos += len; done: free_iob ( iobuf ); if ( rc != 0 ) downloader_finished ( downloader, rc ); return rc; }
/** * Transmit SRP SCSI command * * @v srp SRP device */ static void srp_cmd ( struct srp_device *srp ) { struct io_buffer *iobuf; struct srp_cmd *cmd; struct srp_memory_descriptor *data_out; struct srp_memory_descriptor *data_in; int rc; assert ( srp->state & SRP_STATE_LOGGED_IN ); /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN ); if ( ! iobuf ) { rc = -ENOMEM; goto err; } /* Construct base portion */ cmd = iob_put ( iobuf, sizeof ( *cmd ) ); memset ( cmd, 0, sizeof ( *cmd ) ); cmd->type = SRP_CMD; cmd->tag.dwords[1] = htonl ( ++srp_tag ); cmd->lun = srp->lun; memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) ); /* Construct data-out descriptor, if present */ if ( srp->command->data_out ) { cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT; data_out = iob_put ( iobuf, sizeof ( *data_out ) ); data_out->address = cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) ); data_out->handle = ntohl ( srp->memory_handle ); data_out->len = ntohl ( srp->command->data_out_len ); } /* Construct data-in descriptor, if present */ if ( srp->command->data_in ) { cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT; data_in = iob_put ( iobuf, sizeof ( *data_in ) ); data_in->address = cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) ); data_in->handle = ntohl ( srp->memory_handle ); data_in->len = ntohl ( srp->command->data_in_len ); } DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp, ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) ); DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); /* Send IU */ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { DBGC ( srp, "SRP %p could not send command: %s\n", srp, strerror ( rc ) ); goto err; } return; err: srp_fail ( srp, rc ); }
/** * Process received data * * @v file Data transfer file * @v iobuf I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ static int efi_download_deliver_iob ( struct efi_download_file *file, struct io_buffer *iobuf, struct xfer_metadata *meta ) { EFI_STATUS efirc; size_t len = iob_len ( iobuf ); int rc; /* Calculate new buffer position */ if ( meta->flags & XFER_FL_ABS_OFFSET ) file->pos = 0; file->pos += meta->offset; /* Call out to the data handler */ if ( ( efirc = file->data_callback ( file->context, iobuf->data, len, file->pos ) ) != 0 ) { rc = -EEFI ( efirc ); goto err_callback; } /* Update current buffer position */ file->pos += len; /* Success */ rc = 0; err_callback: free_iob ( iobuf ); return rc; }
/** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int snpnet_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct snp_nic *snp = netdev_priv ( netdev ); EFI_STATUS efirc; int rc; /* Defer the packet if there is already a transmission in progress */ if ( snp->txbuf ) { netdev_tx_defer ( netdev, iobuf ); return 0; } /* Transmit packet */ if ( ( efirc = snp->snp->Transmit ( snp->snp, 0, iob_len ( iobuf ), iobuf->data, NULL, NULL, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not transmit: %s\n", netdev->name, strerror ( rc ) ); return rc; } snp->txbuf = iobuf; return 0; }
/** * Add received data to data transfer buffer * * @v xferbuf Data transfer buffer * @v iobuf I/O buffer * @v meta Data transfer metadata * @ret rc Return status code */ int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ) { size_t len = iob_len ( iobuf ); size_t pos; int rc; /* Start profiling */ profile_start ( &xferbuf_deliver_profiler ); /* Calculate new buffer position */ pos = xferbuf->pos; if ( meta->flags & XFER_FL_ABS_OFFSET ) pos = 0; pos += meta->offset; /* Write data to buffer */ if ( ( rc = xferbuf_write ( xferbuf, pos, iobuf->data, len ) ) != 0 ) goto done; /* Update current buffer position */ xferbuf->pos = ( pos + len ); done: free_iob ( iobuf ); profile_stop ( &xferbuf_deliver_profiler ); return rc; }
/** * Handle SRP SCSI response * * @v srp SRP device * @v iobuf I/O buffer * @ret rc Returns status code */ static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) { struct srp_rsp *rsp = iobuf->data; int rc; DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp, ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) ); /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) { DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n", srp, iob_len ( iobuf ) ); rc = -EINVAL; goto out; } /* Report SCSI errors */ if ( rsp->status != 0 ) { DBGC ( srp, "SRP %p response status %02x\n", srp, rsp->status ); if ( srp_rsp_sense_data ( rsp ) ) { DBGC ( srp, "SRP %p sense data:\n", srp ); DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ), srp_rsp_sense_data_len ( rsp ) ); } } if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) { DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n", srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER ) ? "under" : "over" ), ntohl ( rsp->data_out_residual_count ) ); } if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) { DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n", srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER ) ? "under" : "over" ), ntohl ( rsp->data_in_residual_count ) ); } srp->command->status = rsp->status; /* Mark SCSI command as complete */ srp_scsi_done ( srp, 0 ); rc = 0; out: free_iob ( iobuf ); return rc; }
/** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int myson_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct myson_nic *myson = netdev->priv; struct myson_descriptor *tx; unsigned int tx_idx; physaddr_t address; /* Check address is usable by card */ address = virt_to_bus ( iobuf->data ); if ( ! myson_address_ok ( address ) ) { DBGC ( myson, "MYSON %p cannot support 64-bit TX buffer " "address\n", myson ); return -ENOTSUP; } /* Get next transmit descriptor */ if ( ( myson->tx.prod - myson->tx.cons ) >= MYSON_NUM_TX_DESC ) { DBGC ( myson, "MYSON %p out of transmit descriptors\n", myson ); return -ENOBUFS; } tx_idx = ( myson->tx.prod++ % MYSON_NUM_TX_DESC ); tx = &myson->tx.desc[tx_idx]; /* Populate transmit descriptor */ tx->address = cpu_to_le32 ( address ); tx->control = cpu_to_le32 ( MYSON_TX_CTRL_IC | MYSON_TX_CTRL_LD | MYSON_TX_CTRL_FD | MYSON_TX_CTRL_CRC | MYSON_TX_CTRL_PAD | MYSON_TX_CTRL_RTLC | MYSON_TX_CTRL_PKTS ( iob_len ( iobuf ) ) | MYSON_TX_CTRL_TBS ( iob_len ( iobuf ) ) ); wmb(); tx->status = cpu_to_le32 ( MYSON_TX_STAT_OWN ); wmb(); /* Notify card that there are packets ready to transmit */ writel ( 0, myson->regs + MYSON_TXPDR ); DBGC2 ( myson, "MYSON %p TX %d is [%llx,%llx)\n", myson, tx_idx, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); return 0; }
static int rtl818x_tx(struct net80211_device *dev, struct io_buffer *iob) { struct rtl818x_priv *priv = dev->priv; struct rtl818x_tx_desc *entry; u32 tx_flags; u16 plcp_len = 0; int len = iob_len(iob); tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | RTL818X_TX_DESC_FLAG_LS | (priv->hw_rate << 24) | len; if (priv->r8185) { tx_flags |= RTL818X_TX_DESC_FLAG_DMA | RTL818X_TX_DESC_FLAG_NO_ENC; } else { unsigned int remainder; plcp_len = DIV_ROUND_UP(16 * (len + 4), (dev->rates[dev->rate] * 2) / 10); remainder = (16 * (len + 4)) % ((dev->rates[dev->rate] * 2) / 10); if (remainder > 0 && remainder <= 6) plcp_len |= 1 << 15; } entry = &priv->tx_ring[priv->tx_prod]; if (dev->phy_flags & NET80211_PHY_USE_PROTECTION) { tx_flags |= RTL818X_TX_DESC_FLAG_CTS; tx_flags |= priv->hw_rtscts_rate << 19; entry->rts_duration = net80211_cts_duration(dev, len); } else { entry->rts_duration = 0; } if (entry->flags & RTL818X_TX_DESC_FLAG_OWN) { /* card hasn't processed the old packet yet! */ return -EBUSY; } priv->tx_buf[priv->tx_prod] = iob; priv->tx_prod = (priv->tx_prod + 1) % RTL818X_TX_RING_SIZE; entry->plcp_len = cpu_to_le16(plcp_len); entry->tx_buf = cpu_to_le32(virt_to_bus(iob->data)); entry->frame_len = cpu_to_le32(len); entry->flags2 = /* alternate retry rate in 100kbps << 4 */ 0; entry->retry_limit = RTL818X_MAX_RETRIES; entry->flags = cpu_to_le32(tx_flags); rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << 5)); return 0; }
/** * Complete interrupt transfer * * @v ep USB endpoint * @v iobuf I/O buffer * @v rc Completion status code */ static void smsc95xx_intr_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct smsc95xx_device *smsc95xx = container_of ( ep, struct smsc95xx_device, usbnet.intr ); struct net_device *netdev = smsc95xx->netdev; struct smsc95xx_interrupt *intr; /* Profile completions */ profile_start ( &smsc95xx_intr_profiler ); /* Ignore packets cancelled when the endpoint closes */ if ( ! ep->open ) goto done; /* Record USB errors against the network device */ if ( rc != 0 ) { DBGC ( smsc95xx, "SMSC95XX %p interrupt failed: %s\n", smsc95xx, strerror ( rc ) ); DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); netdev_rx_err ( netdev, NULL, rc ); goto done; } /* Extract interrupt data */ if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { DBGC ( smsc95xx, "SMSC95XX %p malformed interrupt\n", smsc95xx ); DBGC_HDA ( smsc95xx, 0, iobuf->data, iob_len ( iobuf ) ); netdev_rx_err ( netdev, NULL, rc ); goto done; } intr = iobuf->data; /* Record interrupt status */ smsc95xx->int_sts = le32_to_cpu ( intr->int_sts ); profile_stop ( &smsc95xx_intr_profiler ); done: /* Free I/O buffer */ free_iob ( iobuf ); }
/** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int netfront_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct netfront_nic *netfront = netdev->priv; struct xen_device *xendev = netfront->xendev; struct netif_tx_request *request; int notify; int rc; /* Check that we have space in the ring */ if ( netfront_ring_is_full ( &netfront->tx ) ) { DBGC ( netfront, "NETFRONT %s out of transmit descriptors\n", xendev->key ); return -ENOBUFS; } /* Add to descriptor ring */ request = RING_GET_REQUEST ( &netfront->tx_fring, netfront->tx_fring.req_prod_pvt ); if ( ( rc = netfront_push ( netfront, &netfront->tx, iobuf, &request->id, &request->gref ) ) != 0 ) { return rc; } request->offset = ( virt_to_phys ( iobuf->data ) & ( PAGE_SIZE - 1 ) ); request->flags = NETTXF_data_validated; request->size = iob_len ( iobuf ); DBGC2 ( netfront, "NETFRONT %s TX id %d ref %d is %#08lx+%zx\n", xendev->key, request->id, request->gref, virt_to_phys ( iobuf->data ), iob_len ( iobuf ) ); /* Consume descriptor */ netfront->tx_fring.req_prod_pvt++; /* Push new descriptor and notify backend if applicable */ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY ( &netfront->tx_fring, notify ); if ( notify ) netfront_send_event ( netfront ); return 0; }
/** * Dump marker packet * * @v iobuf I/O buffer * @v netdev Network device * @v label "RX" or "TX" */ static void eth_slow_marker_dump ( struct io_buffer *iobuf, struct net_device *netdev, const char *label ) { union eth_slow_packet *eth_slow = iobuf->data; struct eth_slow_marker *marker = ð_slow->marker; DBGC ( netdev, "SLOW %s %s marker %s port %04x system %s xact %08x\n", netdev->name, label, eth_slow_marker_tlv_name ( marker->marker.tlv.type ), ntohs ( marker->marker.port ), eth_ntoa ( marker->marker.system ), ntohl ( marker->marker.xact ) ); DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); }
/** * Complete interrupt transfer * * @v ep USB endpoint * @v iobuf I/O buffer * @v rc Completion status code */ static void dm96xx_intr_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, int rc ) { struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, usbnet.intr ); struct net_device *netdev = dm96xx->netdev; struct dm96xx_interrupt *intr; size_t len = iob_len ( iobuf ); /* Ignore packets cancelled when the endpoint closes */ if ( ! ep->open ) goto done; /* Record USB errors against the network device */ if ( rc != 0 ) { DBGC ( dm96xx, "DM96XX %p interrupt failed: %s\n", dm96xx, strerror ( rc ) ); DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); netdev_rx_err ( netdev, NULL, rc ); goto done; } /* Extract message header */ if ( len < sizeof ( *intr ) ) { DBGC ( dm96xx, "DM96XX %p underlength interrupt:\n", dm96xx ); DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); netdev_rx_err ( netdev, NULL, -EINVAL ); goto done; } intr = iobuf->data; /* Update link status */ dm96xx_link_nsr ( dm96xx, intr->nsr ); done: /* Free I/O buffer */ free_iob ( iobuf ); }
/** * Initiate SRP login * * @v srp SRP device */ static void srp_login ( struct srp_device *srp ) { struct io_buffer *iobuf; struct srp_login_req *login_req; int rc; assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ); /* Open underlying socket */ if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) { DBGC ( srp, "SRP %p could not open socket: %s\n", srp, strerror ( rc ) ); goto err; } srp->state |= SRP_STATE_SOCKET_OPEN; /* Allocate I/O buffer */ iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) ); if ( ! iobuf ) { rc = -ENOMEM; goto err; } /* Construct login request IU */ login_req = iob_put ( iobuf, sizeof ( *login_req ) ); memset ( login_req, 0, sizeof ( *login_req ) ); login_req->type = SRP_LOGIN_REQ; login_req->tag.dwords[1] = htonl ( ++srp_tag ); login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN ); login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD; memcpy ( &login_req->port_ids, &srp->port_ids, sizeof ( login_req->port_ids ) ); DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n", srp, ntohl ( login_req->tag.dwords[0] ), ntohl ( login_req->tag.dwords[1] ) ); DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) ); /* Send login request IU */ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) { DBGC ( srp, "SRP %p could not send login request: %s\n", srp, strerror ( rc ) ); goto err; } return; err: srp_fail ( srp, rc ); }
/** * Receive PXE UDP data * * @v pxe_udp PXE UDP connection * @v iobuf I/O buffer * @v meta Data transfer metadata * @ret rc Return status code * * Receives a packet as part of the current pxenv_udp_read() * operation. */ static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp, struct io_buffer *iobuf, struct xfer_metadata *meta ) { struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read; struct sockaddr_in *sin_src; struct sockaddr_in *sin_dest; userptr_t buffer; size_t len; int rc = 0; if ( ! pxenv_udp_read ) { DBG ( "PXE discarded UDP packet\n" ); rc = -ENOBUFS; goto done; } /* Copy packet to buffer and record length */ buffer = real_to_user ( pxenv_udp_read->buffer.segment, pxenv_udp_read->buffer.offset ); len = iob_len ( iobuf ); if ( len > pxenv_udp_read->buffer_size ) len = pxenv_udp_read->buffer_size; copy_to_user ( buffer, 0, iobuf->data, len ); pxenv_udp_read->buffer_size = len; /* Fill in source/dest information */ assert ( meta ); sin_src = ( struct sockaddr_in * ) meta->src; assert ( sin_src ); assert ( sin_src->sin_family == AF_INET ); pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr; pxenv_udp_read->s_port = sin_src->sin_port; sin_dest = ( struct sockaddr_in * ) meta->dest; assert ( sin_dest ); assert ( sin_dest->sin_family == AF_INET ); pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr; pxenv_udp_read->d_port = sin_dest->sin_port; /* Mark as received */ pxe_udp->pxenv_udp_read = NULL; done: free_iob ( iobuf ); return rc; }