Re: [RFC] CDC NCM USB host driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux