From owner-p4-projects@FreeBSD.ORG Sun Dec 9 11:09:47 2007 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 2A35F16A468; Sun, 9 Dec 2007 11:09:47 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id A815816A420 for ; Sun, 9 Dec 2007 11:09:46 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 970F813C45A for ; Sun, 9 Dec 2007 11:09:46 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id lB9B9kbO072250 for ; Sun, 9 Dec 2007 11:09:46 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id lB9B9kLf072247 for perforce@freebsd.org; Sun, 9 Dec 2007 11:09:46 GMT (envelope-from hselasky@FreeBSD.org) Date: Sun, 9 Dec 2007 11:09:46 GMT Message-Id: <200712091109.lB9B9kLf072247@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 130539 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 09 Dec 2007 11:09:47 -0000 http://perforce.freebsd.org/chv.cgi?CH=130539 Change 130539 by hselasky@hselasky_laptop001 on 2007/12/09 11:09:42 This commit is part of USB device side support. o Slightly tweak the way we setup USB control transfers. This leads to some changes and simplifications here and there. Here is an overview on how USB control transfers should be setup: Example1: SETUP + STATUS xfer->nframes = 1; xfer->frlenghts[0] = 8; usbd_start_hardware(xfer); Example2: SETUP + DATA + STATUS xfer->nframes = 2; xfer->frlenghts[0] = 8; xfer->frlenghts[1] = 1; usbd_start_hardware(xfer); Example3: SETUP + DATA + STATUS - split 1st callback: xfer->nframes = 1; xfer->frlenghts[0] = 8; usbd_start_hardware(xfer); 2nd callback: /* IMPORTANT: frbuffer[0] must still point at the setup packet! */ xfer->nframes = 2; xfer->frlenghts[0] = 0; xfer->frlenghts[1] = 1; usbd_start_hardware(xfer); Affected files ... .. //depot/projects/usb/src/sys/dev/ata/ata-usb.c#28 edit .. //depot/projects/usb/src/sys/dev/sound/usb/uaudio.c#27 edit .. //depot/projects/usb/src/sys/dev/usb/README#26 edit .. //depot/projects/usb/src/sys/dev/usb/ehci.c#59 edit .. //depot/projects/usb/src/sys/dev/usb/ohci.c#48 edit .. //depot/projects/usb/src/sys/dev/usb/ucycom.c#27 edit .. //depot/projects/usb/src/sys/dev/usb/ufoma.c#34 edit .. //depot/projects/usb/src/sys/dev/usb/uhci.c#49 edit .. //depot/projects/usb/src/sys/dev/usb/uhid.c#26 edit .. //depot/projects/usb/src/sys/dev/usb/ukbd.c#35 edit .. //depot/projects/usb/src/sys/dev/usb/ulpt.c#35 edit .. //depot/projects/usb/src/sys/dev/usb/umass.c#38 edit .. //depot/projects/usb/src/sys/dev/usb/usb_compat_linux.c#16 edit .. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#66 edit .. //depot/projects/usb/src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c#21 edit Differences ... ==== //depot/projects/usb/src/sys/dev/ata/ata-usb.c#28 (text) ==== @@ -495,8 +495,7 @@ usbd_copy_in(xfer->frbuffers + 0, 0, &req, sizeof(req)); xfer->frlengths[0] = sizeof(req); - xfer->frlengths[1] = 0; - + xfer->nframes = 1; usbd_start_hardware(xfer); return; ==== //depot/projects/usb/src/sys/dev/sound/usb/uaudio.c#27 (text+ko) ==== @@ -3038,6 +3038,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = len; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; usbd_start_hardware(xfer); return; } ==== //depot/projects/usb/src/sys/dev/usb/README#26 (text+ko) ==== @@ -93,15 +93,42 @@ } } -NOTE: An USB control transfer has three parts. First the SETUP packet, -then DATA packet(s) and then a STATUS packet. The SETUP packet is +=== Notes for USB control transfers === + +An USB control transfer has three parts. First the SETUP packet, then +DATA packet(s) and then a STATUS packet. The SETUP packet is always pointed to by "xfer->frbuffers[0]" and the length is stored in -"xfer->frlengths[0]". Zero-length entries in the "xfer->frlengths[]" array -will be ignored. Typically the DATA packet(s) are pointed to by -"xfer->frbuffers[1]" and the length is stored in "xfer->frlengths[1]". -The STATUS packet is automatically executed unless "xfer->flags.manual_status" -is set. If "xfer->flags.manual_status" is set the STATUS packet will -be executed if the length of last USB frame is zero. +"xfer->frlengths[0]" also if there should not be sent any SETUP +packet! If an USB control transfer has no DATA stage, then +"xfer->nframes" should be set to 1. Else the default value is +"xfer->nframes" equal to 2. + +Example1: SETUP + STATUS + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + usbd_start_hardware(xfer); + +Example2: SETUP + DATA + STATUS + xfer->nframes = 2; + xfer->frlenghts[0] = 8; + xfer->frlenghts[1] = 1; + usbd_start_hardware(xfer); + +Example3: SETUP + DATA + STATUS - split +1st callback: + xfer->nframes = 1; + xfer->frlenghts[0] = 8; + usbd_start_hardware(xfer); + +2nd callback: + /* IMPORTANT: frbuffer[0] must still point at the setup packet! */ + xfer->nframes = 2; + xfer->frlenghts[0] = 0; + xfer->frlenghts[1] = 1; + usbd_start_hardware(xfer); + + +=== General USB transfer notes === 1) Something that one should be aware of is that all USB callbacks support recursation. That means one can start/stop whatever transfer from the callback @@ -319,11 +346,12 @@ This flag can not be changed during operation. manual_status - Setting this flag causes that the STATUS stage of a control - transfer needs to be sent separately like a zero-length USB - frame. This flag has currently no effect on non-control - transfers. This flag is mostly useful for the USB device side. - This flag can be changed during operation. + Setting this flag prevents an USB STATUS stage to be appended + to the end of the USB control transfer. If no control data is + transferred this flag must be cleared. Else an error will be + returned to the USB callback. This flag is mostly useful for + the USB device side. This flag can be changed during + operation. no_pipe_ok Setting this flag causes the USBD_NO_PIPE error to be ==== //depot/projects/usb/src/sys/dev/usb/ehci.c#59 (text+ko) ==== @@ -1240,10 +1240,12 @@ xfer->td_transfer_cache = xfer->td_transfer_first; - if (xfer->flags_int.control_xfr && - xfer->flags_int.control_hdr) { + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { err = ehci_non_isoc_done_sub(xfer); + } xfer->aframes = 1; @@ -1253,11 +1255,7 @@ } while (xfer->aframes != xfer->nframes) { - if ((!xfer->flags_int.control_xfr) || - (xfer->frlengths[xfer->aframes] > 0)) { - err = ehci_non_isoc_done_sub(xfer); - } xfer->aframes++; if (xfer->td_transfer_cache == NULL) { @@ -1826,12 +1824,11 @@ /* max 3 retries */ temp.qtd_status |= htole32(EHCI_QTD_SET_CERR(3)); } - x = 0; /* check if we should prepend a setup message */ - if (xfer->flags_int.control_xfr && - xfer->flags_int.control_hdr) { + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3)); temp.qtd_status |= htole32 @@ -1844,9 +1841,13 @@ temp.shortpkt = temp.len ? 1 : 0; ehci_setup_standard_chain_sub(&temp); + } x = 1; + } else { + x = 0; } + while (x != xfer->nframes) { /* DATA0 / DATA1 message */ @@ -1870,10 +1871,6 @@ temp.shortpkt = 0; - if (xfer->flags_int.control_xfr) { - /* we ignore zero length frames */ - continue; - } } else { /* regular data transfer */ ==== //depot/projects/usb/src/sys/dev/usb/ohci.c#48 (text+ko) ==== @@ -951,10 +951,12 @@ xfer->td_transfer_cache = xfer->td_transfer_first; - if (xfer->flags_int.control_xfr && - xfer->flags_int.control_hdr) { + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { err = ohci_non_isoc_done_sub(xfer); + } xfer->aframes = 1; @@ -964,11 +966,7 @@ } while (xfer->aframes != xfer->nframes) { - if ((!xfer->flags_int.control_xfr) || - (xfer->frlengths[xfer->aframes] > 0)) { - err = ohci_non_isoc_done_sub(xfer); - } xfer->aframes++; if (xfer->td_transfer_cache == NULL) { @@ -1532,12 +1530,10 @@ methods = xfer->pipe->methods; - x = 0; - /* check if we should prepend a setup message */ - if (xfer->flags_int.control_xfr && - xfer->flags_int.control_hdr) { + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { temp.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC | OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); @@ -1548,13 +1544,16 @@ ohci_setup_standard_chain_sub(&temp); - x = 1; - /* * XXX assume that the setup message is * contained within one USB packet: */ xfer->pipe->toggle_next = 1; + } + + x = 1; + } else { + x = 0; } temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR); @@ -1592,10 +1591,6 @@ temp.shortpkt = 0; - if (xfer->flags_int.control_xfr) { - /* we ignore zero length frames */ - continue; - } } else { /* regular data transfer */ ==== //depot/projects/usb/src/sys/dev/usb/ucycom.c#27 (text+ko) ==== @@ -436,7 +436,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = sc->sc_olen; - + xfer->nframes = xfer->frlengths[1] ? 2 : 1; usbd_start_hardware(xfer); } return; ==== //depot/projects/usb/src/sys/dev/usb/ufoma.c#34 (text+ko) ==== @@ -600,7 +600,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = UFOMA_CMD_BUF_SIZE; - + xfer->nframes = 2; usbd_start_hardware(xfer); } return; @@ -644,6 +644,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = 1; + xfer->nframes = 2; usbd_start_hardware(xfer); } ==== //depot/projects/usb/src/sys/dev/usb/uhci.c#49 (text+ko) ==== @@ -1245,10 +1245,11 @@ xfer->td_transfer_cache = xfer->td_transfer_first; - if (xfer->flags_int.control_xfr && - xfer->flags_int.control_hdr) { + if (xfer->flags_int.control_xfr) { + if (xfer->flags_int.control_hdr) { err = uhci_non_isoc_done_sub(xfer); + } xfer->aframes = 1; @@ -1258,11 +1259,7 @@ } while (xfer->aframes != xfer->nframes) { - if ((!xfer->flags_int.control_xfr) || - (xfer->frlengths[xfer->aframes] > 0)) { - err = uhci_non_isoc_done_sub(xfer); - } xfer->aframes++; if (xfer->td_transfer_cache == NULL) { @@ -1821,12 +1818,12 @@ /* DATA1 is next */ temp.td_token |= htole32(UHCI_TD_SET_DT(1)); } - x = 0; /* check if we should prepend a setup message */ - if (xfer->flags_int.control_xfr && - xfer->flags_int.control_hdr) { + if (xfer->flags_int.control_xfr) { + + if (xfer->flags_int.control_hdr) { temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF)); @@ -1838,9 +1835,13 @@ temp.shortpkt = temp.len ? 1 : 0; uhci_setup_standard_chain_sub(&temp); + } x = 1; + } else { + x = 0; } + while (x != xfer->nframes) { /* DATA0 / DATA1 message */ @@ -1868,10 +1869,6 @@ temp.shortpkt = 0; - if (xfer->flags_int.control_xfr) { - /* we ignore zero length frames */ - continue; - } } else { /* regular data transfer */ ==== //depot/projects/usb/src/sys/dev/usb/uhid.c#26 (text+ko) ==== @@ -237,7 +237,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = size; - + xfer->nframes = xfer->frlengths[1] ? 2 : 1; usbd_start_hardware(xfer); } return; @@ -274,7 +274,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = sc->sc_isize; - + xfer->nframes = xfer->frlengths[1] ? 2 : 1; usbd_start_hardware(xfer); } return; ==== //depot/projects/usb/src/sys/dev/usb/ukbd.c#35 (text+ko) ==== @@ -544,6 +544,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = sizeof(buf); + xfer->nframes = 2; usbd_start_hardware(xfer); } return; ==== //depot/projects/usb/src/sys/dev/usb/ulpt.c#35 (text+ko) ==== @@ -268,6 +268,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = 1; + xfer->nframes = 2; usbd_start_hardware(xfer); return; @@ -306,9 +307,8 @@ usbd_copy_in(xfer->frbuffers + 0, 0, &req, sizeof(req)); xfer->frlengths[0] = sizeof(req); - xfer->frlengths[1] = 0; + xfer->nframes = 1; usbd_start_hardware(xfer); - return; default: /* Error */ ==== //depot/projects/usb/src/sys/dev/usb/umass.c#38 (text+ko) ==== @@ -1760,7 +1760,7 @@ usbd_copy_in(xfer->frbuffers + 0, 0, &req, sizeof(req)); xfer->frlengths[0] = sizeof(req); - xfer->frlengths[1] = 0; + xfer->nframes = 1; usbd_start_hardware(xfer); return; @@ -2264,7 +2264,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = sizeof(buf); - + xfer->nframes = 2; usbd_start_hardware(xfer); return; @@ -2379,6 +2379,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = sc->sc_transfer.cmd_len; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; DIF(UDMASS_CBI, umass_cbi_dump_cmd(sc, ==== //depot/projects/usb/src/sys/dev/usb/usb_compat_linux.c#16 (text+ko) ==== @@ -1565,7 +1565,15 @@ urb->bsd_data_ptr, max_bulk); } xfer->frlengths[data_frame] = max_bulk; - xfer->nframes = data_frame + 1; + if (xfer->flags_int.control_xfr) { + if (max_bulk > 0) { + xfer->nframes = 2; + } else { + xfer->nframes = 1; + } + } else { + xfer->nframes = 1; + } usbd_start_hardware(xfer); return; ==== //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#66 (text+ko) ==== @@ -431,9 +431,13 @@ parm->err = USBD_INVAL; goto done; } - n_frlengths = xfer->nframes; - n_frbuffers = 1; - + if (xfer->nframes == 0) { + /* + * this is not a valid value + */ + parm->err = USBD_ZERO_NFRAMES; + goto done; + } } else { /* @@ -458,25 +462,8 @@ } } } - if (type == UE_CONTROL) { - xfer->flags_int.control_xfr = 1; - if (xfer->nframes == 0) { - xfer->nframes = 2; - } - } else { - if (xfer->nframes == 0) { - xfer->nframes = 1; - } - } - - n_frlengths = xfer->nframes; - n_frbuffers = xfer->nframes; } - if (xfer->nframes == 0) { - parm->err = USBD_ZERO_NFRAMES; - goto done; - } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence @@ -548,6 +535,36 @@ } xfer->max_data_length = parm->bufsize; + /* Setup "n_frlengths" and "n_frbuffers" */ + + if (type == UE_ISOCHRONOUS) { + n_frlengths = xfer->nframes; + n_frbuffers = 1; + } else { + + if (type == UE_CONTROL) { + xfer->flags_int.control_xfr = 1; + if (xfer->nframes == 0) { + if (parm->bufsize <= REQ_SIZE) { + /* + * there will never be any data + * stage + */ + xfer->nframes = 1; + } else { + xfer->nframes = 2; + } + } + } else { + if (xfer->nframes == 0) { + xfer->nframes = 1; + } + } + + n_frlengths = xfer->nframes; + n_frbuffers = xfer->nframes; + } + /* * check if we have room for the * USB device request structure: @@ -1235,6 +1252,31 @@ } /*------------------------------------------------------------------------* + * usbd_control_transfer_init + *------------------------------------------------------------------------*/ +static void +usbd_control_transfer_init(struct usbd_xfer *xfer) +{ + usb_device_request_t req; + + /* copy out the USB request header */ + + usbd_copy_out(xfer->frbuffers + 0, 0, &req, sizeof(req)); + + /* setup remainder */ + + xfer->flags_int.control_rem = UGETW(req.wLength); + + /* copy direction to endpoint variable */ + + xfer->endpoint &= ~(UE_DIR_IN | UE_DIR_OUT); + xfer->endpoint |= + (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; + + return; +} + +/*------------------------------------------------------------------------* * usbd_start_hardware_sub * * To support split control transfers we need a special wrapper which @@ -1243,7 +1285,6 @@ static uint8_t usbd_start_hardware_sub(struct usbd_xfer *xfer) { - usb_device_request_t req; uint32_t len; /* @@ -1252,42 +1293,61 @@ */ if (xfer->flags_int.control_act) { - /* clear send header flag */ + if (xfer->flags_int.control_hdr) { + + /* clear send header flag */ - xfer->flags_int.control_hdr = 0; + xfer->flags_int.control_hdr = 0; + /* setup control transfer */ + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { + usbd_control_transfer_init(xfer); + } + } /* get data length */ len = xfer->sumlen; } else { - /* set send header flag */ - - xfer->flags_int.control_hdr = 1; - /* the size of the SETUP structure is hardcoded ! */ - if (xfer->frlengths[0] != sizeof(req)) { + if (xfer->frlengths[0] != sizeof(usb_device_request_t)) { goto error; } - /* copy out the USB request header */ + /* check USB mode */ + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { - usbd_copy_out(xfer->frbuffers + 0, 0, &req, sizeof(req)); + /* check number of frames */ + if (xfer->nframes != 1) { + /* + * We need to receive the setup + * message first so that we know the + * data direction! + */ + PRINTFN(0, ("Misconfigured transfer\n")); + goto error; + } + /* + * Set a dummy "control_rem" value. This + * variable will be overwritten later by a + * call to "usbd_control_transfer_init()" ! + */ + xfer->flags_int.control_rem = 0xFFFF; + } else { - /* setup remainder */ + /* setup "endpoint" and "control_rem" */ - xfer->flags_int.control_rem = UGETW(req.wLength); + usbd_control_transfer_init(xfer); + } - /* copy direction to endpoint variable */ + /* set transfer-header flag */ - xfer->endpoint &= ~(UE_DIR_IN | UE_DIR_OUT); - xfer->endpoint |= - (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; + xfer->flags_int.control_hdr = 1; /* get data length */ - len = (xfer->sumlen - sizeof(req)); + len = (xfer->sumlen - sizeof(usb_device_request_t)); } /* check if there is a length mismatch */ @@ -1299,26 +1359,24 @@ /* the status part is executed when "control_act" is 0 */ - if (xfer->flags_int.control_rem > 0) { - /* - * Moving no data is not allowed - * in this case: - */ - if (xfer->sumlen == 0) { - goto error; - } + if ((xfer->flags_int.control_rem > 0) || + (xfer->flags.manual_status)) { + /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; - } else if (xfer->flags.manual_status && - xfer->frlengths[xfer->nframes - 1]) { + /* sanity check */ + if ((!xfer->flags_int.control_hdr) && + (xfer->nframes == 1)) { + /* + * This is not a valid operation! + */ - /* - * A zero length frame last signals - * a manual status stage - */ - xfer->flags_int.control_act = 1; - + PRINTFN(-1, ("Invalid parameter " + "combination\n")); + goto error; + } } else { + /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); @@ -1965,21 +2023,23 @@ /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USBD_ST_SETUP; - } else { - xfer->flags_int.transferring = 0; - if (xfer->error) { - xfer->usb_state = USBD_ST_ERROR; - } else { - xfer->usb_state = USBD_ST_TRANSFERRED; + goto callback; + } + xfer->flags_int.transferring = 0; - /* sync any DMA memory */ - if (xfer->flags_int.bdma_enable && - (!xfer->flags_int.bdma_no_post_sync)) { - usbd_bdma_post_sync(xfer); - } - } + if (xfer->error) { + xfer->usb_state = USBD_ST_ERROR; + goto callback; } + /* set transferred state */ + xfer->usb_state = USBD_ST_TRANSFERRED; + /* sync DMA memory, if any */ + if (xfer->flags_int.bdma_enable && + (!xfer->flags_int.bdma_no_post_sync)) { + usbd_bdma_post_sync(xfer); + } + callback: /* call processing routine */ (xfer->callback) (xfer); @@ -2289,6 +2349,9 @@ uc[0].bufsize = 1024; /* bytes */ uc[0].flags.proxy_buffer = 1; uc[0].flags.short_xfer_ok = 1; + if (udev->usb_mode == USB_MODE_DEVICE) { + uc[0].flags.manual_status = 1; + } uc[0].cb[USB_MODE_HOST] = &usbd_do_request_callback; uc[0].cb[USB_MODE_DEVICE] = &usbd_serve_request_callback; @@ -2423,10 +2486,12 @@ if (!(req->bmRequestType & UT_READ)) { usbd_copy_in(xfer->frbuffers + 1, 0, data, temp); } + xfer->nframes = 2; } else { if (xfer->frlengths[0] == 0) { break; } + xfer->nframes = 1; } usbd_transfer_start(xfer); ==== //depot/projects/usb/src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c#21 (text+ko) ==== @@ -723,6 +723,7 @@ xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = m->m_pkthdr.len; + xfer->nframes = xfer->frlengths[1] ? 2 : 1; NG_FREE_M(m);