[media] dvb_usb_v2: dynamic USB stream URB configuration
authorAntti Palosaari <crope@iki.fi>
Mon, 28 May 2012 20:25:40 +0000 (17:25 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 4 Aug 2012 10:56:22 +0000 (07:56 -0300)
Change URB count, buffer size and type [BULK/ISOC]
dynamically when needed if existing URB buffers are
big enough.

Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/dvb-usb/dvb_usb_common.h
drivers/media/dvb/dvb-usb/dvb_usb_dvb.c
drivers/media/dvb/dvb-usb/usb_urb.c

index 60f8ccba8dcda76d909981609c9c7741f0968c3a..2c73829f276436853ac6dbc52467e0cb0655ed29 100644 (file)
@@ -33,7 +33,8 @@ extern int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff);
 extern int usb_urb_init(struct usb_data_stream *stream,
                struct usb_data_stream_properties *props);
 extern int usb_urb_exit(struct usb_data_stream *stream);
-extern int usb_urb_submit(struct usb_data_stream *stream);
+extern int usb_urb_submit(struct usb_data_stream *stream,
+               struct usb_data_stream_properties *props);
 extern int usb_urb_kill(struct usb_data_stream *stream);
 
 extern int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap);
index 85db3f46e77dbf2cbe7ea2268b1be9bb0c86d48d..fd02be30b5323383fd70b07b4862600f8edb5e69 100644 (file)
@@ -57,7 +57,7 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
         */
        if (adap->feedcount == onoff && adap->feedcount > 0) {
                deb_ts("submitting all URBs\n");
-               usb_urb_submit(&adap->fe_adap[adap->active_fe].stream);
+               usb_urb_submit(&adap->fe_adap[adap->active_fe].stream, NULL);
 
                deb_ts("controlling pid parser\n");
                if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
index 065f67c6753881264e485ffba46de89f0e76d948..8792334642a245f7edd9e25507d2ed155e946dab 100644 (file)
 #include "dvb_usb_common.h"
 
 /* URB stuff for streaming */
+
+int usb_urb_reconfig(struct usb_data_stream *stream,
+               struct usb_data_stream_properties *props);
+
 static void usb_urb_complete(struct urb *urb)
 {
        struct usb_data_stream *stream = urb->context;
@@ -79,9 +83,17 @@ int usb_urb_kill(struct usb_data_stream *stream)
        return 0;
 }
 
-int usb_urb_submit(struct usb_data_stream *stream)
+int usb_urb_submit(struct usb_data_stream *stream,
+               struct usb_data_stream_properties *props)
 {
        int i, ret;
+
+       if (props) {
+               ret = usb_urb_reconfig(stream, props);
+               if (ret < 0)
+                       return ret;
+       }
+
        for (i = 0; i < stream->urbs_initialized; i++) {
                deb_ts("submitting URB no. %d\n", i);
                ret = usb_submit_urb(stream->urb_list[i], GFP_ATOMIC);
@@ -96,65 +108,32 @@ int usb_urb_submit(struct usb_data_stream *stream)
        return 0;
 }
 
-static int usb_free_stream_buffers(struct usb_data_stream *stream)
+int usb_urb_free_urbs(struct usb_data_stream *stream)
 {
-       if (stream->state & USB_STATE_URB_BUF) {
-               while (stream->buf_num) {
-                       stream->buf_num--;
-                       deb_mem("freeing buffer %d\n", stream->buf_num);
-                       usb_free_coherent(stream->udev, stream->buf_size,
-                                         stream->buf_list[stream->buf_num],
-                                         stream->dma_addr[stream->buf_num]);
-               }
-       }
-
-       stream->state &= ~USB_STATE_URB_BUF;
-
-       return 0;
-}
-
-static int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num,
-               unsigned long size)
-{
-       stream->buf_num = 0;
-       stream->buf_size = size;
+       int i;
 
-       deb_mem("all in all I will use %lu bytes for streaming\n", num * size);
+       usb_urb_kill(stream);
 
-       for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
-               deb_mem("allocating buffer %d\n", stream->buf_num);
-               stream->buf_list[stream->buf_num] = usb_alloc_coherent(
-                               stream->udev, size, GFP_ATOMIC,
-                               &stream->dma_addr[stream->buf_num]);
-               if (stream->buf_list[stream->buf_num] == NULL) {
-                       deb_mem("not enough memory for urb-buffer" \
-                                       " allocation.\n");
-                       usb_free_stream_buffers(stream);
-                       return -ENOMEM;
+       for (i = 0; i < stream->urbs_initialized; i++) {
+               if (stream->urb_list[i] != NULL) {
+                       deb_mem("freeing URB no. %d.\n", i);
+                       pr_debug("%s: free URB=%d\n", __func__, i);
+                       /* free the URBs */
+                       usb_free_urb(stream->urb_list[i]);
                }
-               deb_mem("buffer %d: %p (dma: %llu)\n",
-                               stream->buf_num,
-                               stream->buf_list[stream->buf_num],
-                               (long long)stream->dma_addr[stream->buf_num]);
-               memset(stream->buf_list[stream->buf_num], 0, size);
-               stream->state |= USB_STATE_URB_BUF;
        }
-       deb_mem("allocation successful\n");
+       stream->urbs_initialized = 0;
 
        return 0;
 }
 
-static int usb_bulk_urb_init(struct usb_data_stream *stream)
+static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)
 {
        int i, j;
 
-       i = usb_allocate_stream_buffers(stream, stream->props.count,
-                       stream->props.u.bulk.buffersize);
-       if (i < 0)
-               return i;
-
        /* allocate the URBs */
        for (i = 0; i < stream->props.count; i++) {
+               pr_debug("%s: alloc URB=%d\n", __func__, i);
                stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
                if (!stream->urb_list[i]) {
                        deb_mem("not enough memory for urb_alloc_urb!.\n");
@@ -162,9 +141,10 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
                                usb_free_urb(stream->urb_list[j]);
                        return -ENOMEM;
                }
-               usb_fill_bulk_urb(stream->urb_list[i], stream->udev,
+               usb_fill_bulk_urb(stream->urb_list[i],
+                               stream->udev,
                                usb_rcvbulkpipe(stream->udev,
-                                       stream->props.endpoint),
+                                               stream->props.endpoint),
                                stream->buf_list[i],
                                stream->props.u.bulk.buffersize,
                                usb_urb_complete, stream);
@@ -176,21 +156,15 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
        return 0;
 }
 
-static int usb_isoc_urb_init(struct usb_data_stream *stream)
+static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
 {
        int i, j;
 
-       i = usb_allocate_stream_buffers(stream, stream->props.count,
-                       stream->props.u.isoc.framesize *
-                       stream->props.u.isoc.framesperurb);
-       if (i < 0)
-               return i;
-
        /* allocate the URBs */
        for (i = 0; i < stream->props.count; i++) {
                struct urb *urb;
                int frame_offset = 0;
-
+               pr_debug("%s: alloc URB=%d\n", __func__, i);
                stream->urb_list[i] = usb_alloc_urb(
                                stream->props.u.isoc.framesperurb, GFP_ATOMIC);
                if (!stream->urb_list[i]) {
@@ -210,7 +184,8 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream)
                urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
                urb->interval = stream->props.u.isoc.interval;
                urb->number_of_packets = stream->props.u.isoc.framesperurb;
-               urb->transfer_buffer_length = stream->buf_size;
+               urb->transfer_buffer_length = stream->props.u.isoc.framesize *
+                               stream->props.u.isoc.framesperurb;
                urb->transfer_buffer = stream->buf_list[i];
                urb->transfer_dma = stream->dma_addr[i];
 
@@ -226,9 +201,110 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream)
        return 0;
 }
 
+int usb_free_stream_buffers(struct usb_data_stream *stream)
+{
+       if (stream->state & USB_STATE_URB_BUF) {
+               while (stream->buf_num) {
+                       stream->buf_num--;
+                       deb_mem("freeing buffer %d\n", stream->buf_num);
+                       usb_free_coherent(stream->udev, stream->buf_size,
+                                         stream->buf_list[stream->buf_num],
+                                         stream->dma_addr[stream->buf_num]);
+               }
+       }
+
+       stream->state &= ~USB_STATE_URB_BUF;
+
+       return 0;
+}
+
+int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
+               unsigned long size)
+{
+       stream->buf_num = 0;
+       stream->buf_size = size;
+
+       deb_mem("all in all I will use %lu bytes for streaming\n", num * size);
+
+       for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
+               deb_mem("allocating buffer %d\n", stream->buf_num);
+               stream->buf_list[stream->buf_num] = usb_alloc_coherent(
+                               stream->udev, size, GFP_ATOMIC,
+                               &stream->dma_addr[stream->buf_num]);
+               if (stream->buf_list[stream->buf_num] == NULL) {
+                       deb_mem("not enough memory for urb-buffer" \
+                                       " allocation.\n");
+                       usb_free_stream_buffers(stream);
+                       return -ENOMEM;
+               }
+               deb_mem("buffer %d: %p (dma: %llu)\n",
+                               stream->buf_num,
+                               stream->buf_list[stream->buf_num],
+                               (long long)stream->dma_addr[stream->buf_num]);
+               memset(stream->buf_list[stream->buf_num], 0, size);
+               stream->state |= USB_STATE_URB_BUF;
+       }
+       deb_mem("allocation successful\n");
+
+       return 0;
+}
+
+int usb_urb_reconfig(struct usb_data_stream *stream,
+               struct usb_data_stream_properties *props)
+{
+       int buf_size;
+
+       if (props == NULL)
+               return 0;
+
+       /* check allocated buffers are large enough for the request */
+       if (props->type == USB_BULK)
+               buf_size = stream->props.u.bulk.buffersize;
+       else if (props->type == USB_ISOC)
+               buf_size = props->u.isoc.framesize * props->u.isoc.framesperurb;
+       else
+               return -EINVAL;
+
+       if (stream->buf_num < props->count || stream->buf_size < buf_size) {
+               err("cannot reconfigure as allocated buffers are too small");
+               return -EINVAL;
+       }
+
+       /* check if all fields are same */
+       if (stream->props.type == props->type &&
+                       stream->props.count == props->count &&
+                       stream->props.endpoint == props->endpoint) {
+               if (props->type == USB_BULK &&
+                               props->u.bulk.buffersize ==
+                               stream->props.u.bulk.buffersize)
+                       return 0;
+               else if (props->type == USB_ISOC &&
+                               props->u.isoc.framesperurb ==
+                               stream->props.u.isoc.framesperurb &&
+                               props->u.isoc.framesize ==
+                               stream->props.u.isoc.framesize &&
+                               props->u.isoc.interval ==
+                               stream->props.u.isoc.interval)
+                       return 0;
+       }
+
+       pr_debug("%s: re-alloc URBs\n", __func__);
+
+       usb_urb_free_urbs(stream);
+       memcpy(&stream->props, props, sizeof(*props));
+       if (props->type == USB_BULK)
+               return usb_urb_alloc_bulk_urbs(stream);
+       else if (props->type == USB_ISOC)
+               return usb_urb_alloc_isoc_urbs(stream);
+
+       return 0;
+}
+
 int usb_urb_init(struct usb_data_stream *stream,
                struct usb_data_stream_properties *props)
 {
+       int ret;
+
        if (stream == NULL || props == NULL)
                return -EINVAL;
 
@@ -244,9 +320,20 @@ int usb_urb_init(struct usb_data_stream *stream,
 
        switch (stream->props.type) {
        case USB_BULK:
-               return usb_bulk_urb_init(stream);
+               ret = usb_alloc_stream_buffers(stream, stream->props.count,
+                               stream->props.u.bulk.buffersize);
+               if (ret < 0)
+                       return ret;
+
+               return usb_urb_alloc_bulk_urbs(stream);
        case USB_ISOC:
-               return usb_isoc_urb_init(stream);
+               ret = usb_alloc_stream_buffers(stream, stream->props.count,
+                               stream->props.u.isoc.framesize *
+                               stream->props.u.isoc.framesperurb);
+               if (ret < 0)
+                       return ret;
+
+               return usb_urb_alloc_isoc_urbs(stream);
        default:
                err("unknown URB-type for data transfer.");
                return -EINVAL;
@@ -255,19 +342,8 @@ int usb_urb_init(struct usb_data_stream *stream,
 
 int usb_urb_exit(struct usb_data_stream *stream)
 {
-       int i;
-
-       usb_urb_kill(stream);
-
-       for (i = 0; i < stream->urbs_initialized; i++) {
-               if (stream->urb_list[i] != NULL) {
-                       deb_mem("freeing URB no. %d.\n", i);
-                       /* free the URBs */
-                       usb_free_urb(stream->urb_list[i]);
-               }
-       }
-       stream->urbs_initialized = 0;
-
+       usb_urb_free_urbs(stream);
        usb_free_stream_buffers(stream);
+
        return 0;
 }