Am Dienstag, 8. Juni 2010 21:29:49 schrieb sjur.brandeland@xxxxxxxxxxxxxx: > +cdc_ncm_setup(struct cdc_ncm_softc *sc) > +{ > + struct cdc_ncm_parameters temp; > + struct cdc_ncm_device_request req; > + u32 val; > + u8 flags; > + u8 value[8]; > + u8 iface_no; > + int err; > + > + iface_no = sc->sc_control->cur_altsetting->desc.bInterfaceNumber; > + > + req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE; > + req.bRequest = CDC_NCM_GET_NTB_PARAMETERS; > + put_unaligned_le16(0, req.wValue); > + req.wIndex[0] = iface_no; > + req.wIndex[1] = 0; > + put_unaligned_le16(sizeof(temp), req.wLength); > + > + err = cdc_ncm_do_request(sc, &req, &temp, 0, NULL, 1000 /* ms */); DMA on stack. You must alloc buffers with kmalloc. > + if (err) > + return 1; > + > + /* Read correct set of parameters according to device mode */ > + > + sc->sc_rx_ncm.rx_max = get_unaligned_le16(temp.dwNtbInMaxSize); > + sc->sc_rx_ncm.tx_max = get_unaligned_le16(temp.dwNtbOutMaxSize); > + sc->sc_rx_ncm.tx_remainder = > + get_unaligned_le16(temp.wNdpOutPayloadRemainder); > + sc->sc_rx_ncm.tx_modulus = > + get_unaligned_le16(temp.wNdpOutDivisor); > + sc->sc_rx_ncm.tx_struct_align = > + get_unaligned_le16(temp.wNdpOutAlignment); > + > + if (sc->sc_func_desc != NULL) > + flags = sc->sc_func_desc->bmNetworkCapabilities; > + else > + flags = 0; > + > + /* compute the maximum number of TX datagrams */ > + /* we leave one entry for zero-padding */ > + > + if (flags & CDC_NCM_CAP_NTBINPUTSIZE) { > + val = get_unaligned_le16(temp.wNtbOutMaxDatagrams); > + > + sc->sc_rx_ncm.tx_max_datagrams = val; > + > + if ((val <= 0) || (val > (CDC_NCM_DPT_DATAGRAMS_MAX - 1))) { > + sc->sc_rx_ncm.tx_max_datagrams = > + (CDC_NCM_DPT_DATAGRAMS_MAX - 1); > + } > + } else { > + sc->sc_rx_ncm.tx_max_datagrams = > + (CDC_NCM_DPT_DATAGRAMS_MAX - 1); > + } > + > + /* Verify maximum receive length */ > + > + if (err || (sc->sc_rx_ncm.rx_max < 32) || > + (sc->sc_rx_ncm.rx_max > CDC_NCM_RX_MAXLEN)) { > + pr_debug("Using default maximum receive length\n"); > + sc->sc_rx_ncm.rx_max = CDC_NCM_RX_MAXLEN; > + } > + > + /* Verify maximum transmit length */ > + > + if (err || (sc->sc_rx_ncm.tx_max < 32) || > + (sc->sc_rx_ncm.tx_max > CDC_NCM_TX_MAXLEN)) { > + pr_debug("Using default maximum transmit length\n"); > + sc->sc_rx_ncm.tx_max = CDC_NCM_TX_MAXLEN; > + } > + > + /* > + * Verify that the structure alignment is: > + * - power of two > + * - not greater than the maximum transmit length > + * - not less than four bytes > + */ > + val = sc->sc_rx_ncm.tx_struct_align; > + > + if (err || (val < 4) || (val != ((-val) & val)) || > + (val >= sc->sc_rx_ncm.tx_max)) { > + pr_debug("Using default other alignment: 4 bytes\n"); > + sc->sc_rx_ncm.tx_struct_align = 4; > + } > + > + /* > + * Verify that the payload alignment is: > + * - power of two > + * - not greater than the maximum transmit length > + * - not less than four bytes > + */ > + val = sc->sc_rx_ncm.tx_modulus; > + > + if (err || (val < 4) || (val != ((-val) & val)) || > + (val >= sc->sc_rx_ncm.tx_max)) { > + pr_debug("Using default transmit modulus: 4 bytes\n"); > + sc->sc_rx_ncm.tx_modulus = 4; > + } > + > + /* Verify that the payload remainder */ > + > + if (err || (sc->sc_rx_ncm.tx_remainder >= sc->sc_rx_ncm.tx_modulus)) { > + pr_debug("Using default transmit remainder: 0 bytes\n"); > + sc->sc_rx_ncm.tx_remainder = 0; > + } > + > + /* Additional configuration */ > + > + req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE; > + req.bRequest = CDC_NCM_SET_NTB_INPUT_SIZE; > + put_unaligned_le16(0, req.wValue); > + req.wIndex[0] = iface_no; endianness? > + req.wIndex[1] = 0; > + > + if (flags & CDC_NCM_CAP_NTBINPUTSIZE) { > + put_unaligned_le16(8, req.wLength); > + put_unaligned_le32(sc->sc_rx_ncm.rx_max, value + 0); > + put_unaligned_le16(CDC_NCM_DPT_DATAGRAMS_MAX, value + 4); > + put_unaligned_le16(0, value + 6); > + } else { > + put_unaligned_le16(4, req.wLength); > + put_unaligned_le32(sc->sc_rx_ncm.rx_max, value); > + } > + > + err = cdc_ncm_do_request(sc, &req, &value, 0, NULL, 1000 /* ms */); DMA on stack. > +static void > +cdc_ncm_find_endpoints(struct cdc_ncm_softc *sc, struct usb_interface *intf) > +{ > + struct usb_host_endpoint *e; > + u8 ep; > + > + for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) { > + > + e = intf->cur_altsetting->endpoint + ep; > + switch (e->desc.bmAttributes & 0x03) { > + case USB_ENDPOINT_XFER_INT: > + if (usb_endpoint_dir_in(&e->desc)) { > + if (sc->sc_status_ep == NULL) > + sc->sc_status_ep = e; > + } > + break; > + > + case USB_ENDPOINT_XFER_BULK: > + if (usb_endpoint_dir_in(&e->desc)) { > + if (sc->sc_in_ep == NULL) > + sc->sc_in_ep = e; > + } else { > + if (sc->sc_out_ep == NULL) > + sc->sc_out_ep = e; > + } > + break; > + > + default: > + break; > + } > + } > +} > + > +static void > +cdc_ncm_softc_free(struct cdc_ncm_softc *sc) > +{ > + if (sc == NULL) > + return; > + > + del_timer(&sc->sc_tx_timer); Races with kfree. How do you prevent the timeout handler from using the memory you kfree? I suggest del_timer_sync(). [..] > + kfree(sc); > +} > + > + > +static void > +cdc_ncm_fill_tx_frame(struct cdc_ncm_softc *sc, struct sk_buff *skb, > + struct sk_buff **ppskb_out) > +{ > + struct sk_buff *skb_out; > + > + u32 rem; > + u32 offset; > + u32 last_offset; > + u16 n; > + > + u8 timeout = (skb == NULL); > + > + /* if there is a remaining SKB, it gets priority */ > + > + if (skb != NULL) > + swap(skb, sc->sc_tx_rem_skb); > + > + /* > + * +----------------+ > + * | skb_out | > + * +----------------+ > + * ^ offset > + * ^ last_offset > + */ > + > + /* check if we are resuming an OUT-SKB */ > + > + if (sc->sc_tx_curr_skb != NULL) { > + > + /* pop variables */ > + > + skb_out = sc->sc_tx_curr_skb; > + offset = sc->sc_tx_curr_offset; > + last_offset = sc->sc_tx_curr_last_offset; > + n = sc->sc_tx_curr_frame_num; > + } else { > + > + /* reset variables */ > + > + skb_out = alloc_skb(sc->sc_tx_ncm.tx_max, GFP_ATOMIC); > + if (skb_out == NULL) { > + if (skb != NULL) > + dev_kfree_skb_any(skb); > + > + goto error; > + } > + > + /* make room for headers */ > + offset = sizeof(sc->sc_tx_ncm.nth16) + > + sizeof(sc->sc_tx_ncm.ndp16) + sizeof(sc->sc_tx_ncm.dpe16); > + > + /* store last valid offset before alignment */ > + last_offset = offset; > + > + /* align offset correctly */ > + offset = sc->sc_tx_ncm.tx_remainder - > + ((0UL - offset) & (0UL - sc->sc_tx_ncm.tx_modulus)); > + > + n = 0; > + } > + > + for (; n != sc->sc_tx_ncm.tx_max_datagrams; n++) { > + > + /* check if end of transmit buffer is reached */ > + > + if (offset >= sc->sc_tx_ncm.tx_max) > + break; > + > + /* compute maximum buffer size */ > + > + rem = sc->sc_tx_ncm.tx_max - offset; > + > + if (skb == NULL) { > + skb = sc->sc_tx_rem_skb; > + sc->sc_tx_rem_skb = NULL; > + > + /* check for end of SKB's */ > + if (skb == NULL) > + break; > + } > + > + if (skb->len > rem) { > + > + if (n == 0) { > + /* won't fit */ > + dev_kfree_skb_any(skb); > + skb = NULL; > + } else { > + /* no room for SKB - store for later */ > + cdc_ncm_tx_set_rem_skb(sc, skb); > + skb = NULL; > + > + /* loop one more time */ > + timeout = 1; > + } > + break; > + } > + > + memcpy(((u8 *)skb_out->data) + offset, skb->data, skb->len); > + > + put_unaligned_le16(skb->len, > + sc->sc_tx_ncm.dpe16[n].wDatagramLength); > + put_unaligned_le16(offset, > + sc->sc_tx_ncm.dpe16[n].wDatagramIndex); > + > + /* Update offset */ > + offset += skb->len; > + > + /* Store last valid offset before alignment */ > + last_offset = offset; > + > + /* Align offset correctly */ > + offset = sc->sc_tx_ncm.tx_remainder - > + ((0UL - offset) & (0UL - sc->sc_tx_ncm.tx_modulus)); > + > + dev_kfree_skb_any(skb); > + skb = NULL; > + } > + > + /* free up any dangling skb */ > + if (skb != NULL) { > + dev_kfree_skb_any(skb); > + skb = NULL; > + } > + > + if (n == 0) { > + /* wait for more frames */ > + /* push variables */ > + sc->sc_tx_curr_skb = skb_out; > + sc->sc_tx_curr_offset = offset; > + sc->sc_tx_curr_last_offset = last_offset; > + sc->sc_tx_curr_frame_num = n; > + > + goto error; > + > + } else if ((n != sc->sc_tx_ncm.tx_max_datagrams) && (timeout == 0)) { > + /* wait for more frames */ > + /* push variables */ > + sc->sc_tx_curr_skb = skb_out; > + sc->sc_tx_curr_offset = offset; > + sc->sc_tx_curr_last_offset = last_offset; > + sc->sc_tx_curr_frame_num = n; > + > + /* set the pending count */ > + if (n < 8) > + sc->sc_tx_timer_pending = 2; > + > + goto error; > + } else { > + /* frame goes out */ > + /* variables will be reset at next call */ > + } > + > + rem = sc->sc_tx_ncm.tx_max - last_offset; > + > + /* XXX if there is a FORCE SHORT packet flag, use that instead! */ > + if (((rem & 63) == 0) && (rem != 0)) { > + /* force short packet */ > + *(((u8 *)skb_out->data) + last_offset) = 0; > + last_offset++; > + } > + > + rem = (sizeof(sc->sc_tx_ncm.ndp16) + (4 * n) + 4); > + > + put_unaligned_le16(rem, sc->sc_tx_ncm.ndp16.wLength); > + > + /* zero the rest of the data pointer entries */ > + for (; n != CDC_NCM_DPT_DATAGRAMS_MAX; n++) { > + put_unaligned_le16(0, sc->sc_tx_ncm.dpe16[n].wDatagramLength); > + put_unaligned_le16(0, sc->sc_tx_ncm.dpe16[n].wDatagramIndex); > + } > + > + /* Fill out 16-bit header */ > + sc->sc_tx_ncm.nth16.dwSignature[0] = 'N'; > + sc->sc_tx_ncm.nth16.dwSignature[1] = 'C'; > + sc->sc_tx_ncm.nth16.dwSignature[2] = 'M'; > + sc->sc_tx_ncm.nth16.dwSignature[3] = 'H'; > + put_unaligned_le16(sizeof(sc->sc_tx_ncm.nth16), > + sc->sc_tx_ncm.nth16.wHeaderLength); > + put_unaligned_le16(last_offset, sc->sc_tx_ncm.nth16.wBlockLength); > + put_unaligned_le16(sc->sc_tx_ncm.tx_seq, > + sc->sc_tx_ncm.nth16.wSequence); > + put_unaligned_le16(sizeof(sc->sc_tx_ncm.nth16), > + sc->sc_tx_ncm.nth16.wNdpIndex); > + > + sc->sc_tx_ncm.tx_seq++; > + > + /* Fill out 16-bit frame table header */ > + sc->sc_tx_ncm.ndp16.dwSignature[0] = 'N'; > + sc->sc_tx_ncm.ndp16.dwSignature[1] = 'C'; > + sc->sc_tx_ncm.ndp16.dwSignature[2] = 'M'; > + sc->sc_tx_ncm.ndp16.dwSignature[3] = '0'; > + /* Reserved: */ > + put_unaligned_le16(0, sc->sc_tx_ncm.ndp16.wNextNdpIndex); > + > + memcpy(skb_out->data, &(sc->sc_tx_ncm.nth16), > + sizeof(sc->sc_tx_ncm.nth16)); > + memcpy(((u8 *)skb_out->data) + sizeof(sc->sc_tx_ncm.nth16), > + &(sc->sc_tx_ncm.ndp16), sizeof(sc->sc_tx_ncm.ndp16)); > + memcpy(((u8 *)skb_out->data) + sizeof(sc->sc_tx_ncm.nth16) + > + sizeof(sc->sc_tx_ncm.ndp16), &(sc->sc_tx_ncm.dpe16), > + sizeof(sc->sc_tx_ncm.dpe16)); > + > + /* set frame length */ > + skb_put(skb_out, last_offset); > + > + /* return SKB */ > + sc->sc_tx_curr_skb = NULL; > + > + *ppskb_out = skb_out; > + > + return; > + > +error: > + *ppskb_out = NULL; > +} > + > +static void > +cdc_ncm_tx_timeout_start(struct cdc_ncm_softc *sc) > +{ > + if (timer_pending(&sc->sc_tx_timer) == 0) { Arg. What is this supposed to test for? > + sc->sc_tx_timer.function = &cdc_ncm_tx_timeout; > + sc->sc_tx_timer.data = (unsigned long)sc; > + sc->sc_tx_timer.expires = jiffies + ((HZ + 999) / 1000); > + add_timer(&sc->sc_tx_timer); > + } > +} > + > +static struct sk_buff * > +cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) > +{ > + struct sk_buff *skb_out; > + struct cdc_ncm_softc *sc; > + > + u8 need_timer; > + > + /* > + * The ethernet API we are using does not support transmitting > + * multiple ethernet frames in a single call. This driver will > + * accumulate multiple ethernet frames and send out a larger > + * USB frame when the USB buffer is full, or a single jiffies > + * timeout happens. > + */ So you send out a partially filled packet even if another packet is in flight? > + sc = (struct cdc_ncm_softc *)dev->data[0]; > + if (sc == NULL) > + goto error; > + > + /* The following function will setup the skb */ > + spin_lock(&sc->sc_mtx); > + cdc_ncm_fill_tx_frame(sc, skb, &skb_out); > + need_timer = (sc->sc_tx_curr_skb != NULL); > + spin_unlock(&sc->sc_mtx); > + > + /* Start timer, if there is a remaining skb */ > + if (need_timer) > + cdc_ncm_tx_timeout_start(sc); > + > + if (skb_out == NULL) { > + /* compensate the usbnet drop count increment */ > + dev->net->stats.tx_dropped--; > + } > + > + return skb_out; > + > +error: > + if (skb != NULL) > + dev_kfree_skb_any(skb); > + > + return NULL; > +} > + > +static struct usb_driver cdc_ncm_driver = { > + .name = "cdc_ncm", > + .id_table = cdc_devs, > + .probe = cdc_ncm_probe, > + .disconnect = cdc_ncm_disconnect, > + .suspend = cdc_ncm_suspend, > + .resume = cdc_ncm_resume, > + .supports_autosuspend = 1, Where do you do that? -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html