Commit 579f72e4 authored by Aidan Thornton's avatar Aidan Thornton Committed by Mauro Carvalho Chehab

V4L/DVB (7602): em28xx: generalise URB setup code

Move the URB setup and management code to em28xx-core.c and generalise
it slighlty so that the DVB code can use it.
Signed-off-by: default avatarAidan Thornton <makosoft@googlemail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 7e6388a1
......@@ -53,6 +53,12 @@ static int alt = EM28XX_PINOUT;
module_param(alt, int, 0644);
MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
/* FIXME */
#define em28xx_isocdbg(fmt, arg...) do {\
if (core_debug) \
printk(KERN_INFO "%s %s :"fmt, \
dev->name, __func__ , ##arg); } while (0)
/*
* em28xx_read_reg_req()
* reads data from the usb device specifying bRequest
......@@ -455,3 +461,178 @@ int em28xx_set_alternate(struct em28xx *dev)
}
return 0;
}
/* ------------------------------------------------------------------
URB control
------------------------------------------------------------------*/
/*
* IRQ callback, called by URB callback
*/
static void em28xx_irq_callback(struct urb *urb)
{
struct em28xx_dmaqueue *dma_q = urb->context;
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
int rc, i;
/* Copy data from URB */
spin_lock(&dev->slock);
rc = dev->isoc_ctl.isoc_copy(dev, urb);
spin_unlock(&dev->slock);
/* Reset urb buffers */
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
urb->status = 0;
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
em28xx_err("urb resubmit failed (error=%i)\n",
urb->status);
}
}
/*
* Stop and Deallocate URBs
*/
void em28xx_uninit_isoc(struct em28xx *dev)
{
struct urb *urb;
int i;
em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n");
dev->isoc_ctl.nfields = -1;
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = dev->isoc_ctl.urb[i];
if (urb) {
usb_kill_urb(urb);
usb_unlink_urb(urb);
if (dev->isoc_ctl.transfer_buffer[i]) {
usb_buffer_free(dev->udev,
urb->transfer_buffer_length,
dev->isoc_ctl.transfer_buffer[i],
urb->transfer_dma);
}
usb_free_urb(urb);
dev->isoc_ctl.urb[i] = NULL;
}
dev->isoc_ctl.transfer_buffer[i] = NULL;
}
kfree(dev->isoc_ctl.urb);
kfree(dev->isoc_ctl.transfer_buffer);
dev->isoc_ctl.urb = NULL;
dev->isoc_ctl.transfer_buffer = NULL;
dev->isoc_ctl.num_bufs = 0;
em28xx_capture_start(dev, 0);
}
EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
/*
* Allocate URBs and start IRQ
*/
int em28xx_init_isoc(struct em28xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
int cap_type)
{
struct em28xx_dmaqueue *dma_q = &dev->vidq;
int i;
int sb_size, pipe;
struct urb *urb;
int j, k;
int rc;
em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n");
/* De-allocates all pending stuff */
em28xx_uninit_isoc(dev);
dev->isoc_ctl.isoc_copy = isoc_copy;
dev->isoc_ctl.num_bufs = num_bufs;
dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
if (!dev->isoc_ctl.urb) {
em28xx_errdev("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
GFP_KERNEL);
if (!dev->isoc_ctl.urb) {
em28xx_errdev("cannot allocate memory for usbtransfer\n");
kfree(dev->isoc_ctl.urb);
return -ENOMEM;
}
dev->isoc_ctl.max_pkt_size = max_pkt_size;
dev->isoc_ctl.buf = NULL;
sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
/* allocate urbs and transfer buffers */
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!urb) {
em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
em28xx_uninit_isoc(dev);
return -ENOMEM;
}
dev->isoc_ctl.urb[i] = urb;
dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev,
sb_size, GFP_KERNEL, &urb->transfer_dma);
if (!dev->isoc_ctl.transfer_buffer[i]) {
em28xx_err("unable to allocate %i bytes for transfer"
" buffer %i%s\n",
sb_size, i,
in_interrupt()?" while in int":"");
em28xx_uninit_isoc(dev);
return -ENOMEM;
}
memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
/* FIXME: this is a hack - should be
'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
should also be using 'desc.bInterval'
*/
pipe = usb_rcvisocpipe(dev->udev, cap_type == EM28XX_ANALOG_CAPTURE ? 0x82 : 0x84);
usb_fill_int_urb(urb, dev->udev, pipe,
dev->isoc_ctl.transfer_buffer[i], sb_size,
em28xx_irq_callback, dma_q, 1);
urb->number_of_packets = max_packets;
urb->transfer_flags = URB_ISO_ASAP;
k = 0;
for (j = 0; j < max_packets; j++) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length =
dev->isoc_ctl.max_pkt_size;
k += dev->isoc_ctl.max_pkt_size;
}
}
init_waitqueue_head(&dma_q->wq);
em28xx_capture_start(dev, cap_type);
/* submit urbs and enables IRQ */
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
if (rc) {
em28xx_err("submit of urb %i failed (error=%i)\n", i,
rc);
em28xx_uninit_isoc(dev);
return rc;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(em28xx_init_isoc);
......@@ -285,11 +285,10 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
/*
* Controls the isoc copy of each urb packet
*/
static inline int em28xx_isoc_copy(struct urb *urb)
static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
{
struct em28xx_buffer *buf;
struct em28xx_dmaqueue *dma_q = urb->context;
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
unsigned char *outp = NULL;
int i, len = 0, rc = 1;
unsigned char *p;
......@@ -370,188 +369,6 @@ static inline int em28xx_isoc_copy(struct urb *urb)
return rc;
}
/* ------------------------------------------------------------------
URB control
------------------------------------------------------------------*/
/*
* IRQ callback, called by URB callback
*/
static void em28xx_irq_callback(struct urb *urb)
{
struct em28xx_dmaqueue *dma_q = urb->context;
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
int rc, i;
/* Copy data from URB */
spin_lock(&dev->slock);
rc = em28xx_isoc_copy(urb);
spin_unlock(&dev->slock);
/* Reset urb buffers */
for (i = 0; i < urb->number_of_packets; i++) {
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
urb->status = 0;
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
em28xx_err("urb resubmit failed (error=%i)\n",
urb->status);
}
}
/*
* Stop and Deallocate URBs
*/
static void em28xx_uninit_isoc(struct em28xx *dev)
{
struct urb *urb;
int i;
em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n");
dev->isoc_ctl.nfields = -1;
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = dev->isoc_ctl.urb[i];
if (urb) {
usb_kill_urb(urb);
usb_unlink_urb(urb);
if (dev->isoc_ctl.transfer_buffer[i]) {
usb_buffer_free(dev->udev,
urb->transfer_buffer_length,
dev->isoc_ctl.transfer_buffer[i],
urb->transfer_dma);
}
usb_free_urb(urb);
dev->isoc_ctl.urb[i] = NULL;
}
dev->isoc_ctl.transfer_buffer[i] = NULL;
}
kfree(dev->isoc_ctl.urb);
kfree(dev->isoc_ctl.transfer_buffer);
dev->isoc_ctl.urb = NULL;
dev->isoc_ctl.transfer_buffer = NULL;
dev->isoc_ctl.num_bufs = 0;
em28xx_capture_start(dev, 0);
}
/*
* Allocate URBs and start IRQ
*/
static int em28xx_prepare_isoc(struct em28xx *dev, int max_packets,
int num_bufs)
{
struct em28xx_dmaqueue *dma_q = &dev->vidq;
int i;
int sb_size, pipe;
struct urb *urb;
int j, k;
em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n");
/* De-allocates all pending stuff */
em28xx_uninit_isoc(dev);
dev->isoc_ctl.num_bufs = num_bufs;
dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
if (!dev->isoc_ctl.urb) {
em28xx_errdev("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
GFP_KERNEL);
if (!dev->isoc_ctl.urb) {
em28xx_errdev("cannot allocate memory for usbtransfer\n");
kfree(dev->isoc_ctl.urb);
return -ENOMEM;
}
dev->isoc_ctl.max_pkt_size = dev->max_pkt_size;
dev->isoc_ctl.buf = NULL;
sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
/* allocate urbs and transfer buffers */
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!urb) {
em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
em28xx_uninit_isoc(dev);
return -ENOMEM;
}
dev->isoc_ctl.urb[i] = urb;
dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev,
sb_size, GFP_KERNEL, &urb->transfer_dma);
if (!dev->isoc_ctl.transfer_buffer[i]) {
em28xx_err("unable to allocate %i bytes for transfer"
" buffer %i%s\n",
sb_size, i,
in_interrupt()?" while in int":"");
em28xx_uninit_isoc(dev);
return -ENOMEM;
}
memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
/* FIXME: this is a hack - should be
'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
should also be using 'desc.bInterval'
*/
pipe = usb_rcvisocpipe(dev->udev, 0x82);
usb_fill_int_urb(urb, dev->udev, pipe,
dev->isoc_ctl.transfer_buffer[i], sb_size,
em28xx_irq_callback, dma_q, 1);
urb->number_of_packets = max_packets;
urb->transfer_flags = URB_ISO_ASAP;
k = 0;
for (j = 0; j < max_packets; j++) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length =
dev->isoc_ctl.max_pkt_size;
k += dev->isoc_ctl.max_pkt_size;
}
}
return 0;
}
static int em28xx_start_thread(struct em28xx_dmaqueue *dma_q)
{
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
int i, rc = 0;
em28xx_videodbg("Called em28xx_start_thread\n");
init_waitqueue_head(&dma_q->wq);
em28xx_capture_start(dev, 1);
/* submit urbs and enables IRQ */
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
if (rc) {
em28xx_err("submit of urb %i failed (error=%i)\n", i,
rc);
em28xx_uninit_isoc(dev);
return rc;
}
}
if (rc < 0)
return rc;
return 0;
}
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
......@@ -615,7 +432,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct em28xx_fh *fh = vq->priv_data;
struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
struct em28xx *dev = fh->dev;
struct em28xx_dmaqueue *vidq = &dev->vidq;
int rc = 0, urb_init = 0;
/* FIXME: It assumes depth = 16 */
......@@ -639,12 +455,9 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
urb_init = 1;
if (urb_init) {
rc = em28xx_prepare_isoc(dev, EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS);
if (rc < 0)
goto fail;
rc = em28xx_start_thread(vidq);
rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS, dev->max_pkt_size,
em28xx_isoc_copy, EM28XX_ANALOG_CAPTURE);
if (rc < 0)
goto fail;
}
......
......@@ -119,6 +119,8 @@ enum em28xx_stream_state {
STREAM_ON,
};
struct em28xx;
struct em28xx_usb_isoc_ctl {
/* max packet size of isoc transaction */
int max_pkt_size;
......@@ -148,6 +150,10 @@ struct em28xx_usb_isoc_ctl {
/* Stores the number of received fields */
int nfields;
/* isoc urb callback */
int (*isoc_copy) (struct em28xx *dev, struct urb *urb);
};
struct em28xx_fmt {
......@@ -279,6 +285,12 @@ enum em28xx_dev_state {
DEV_MISCONFIGURED = 0x04,
};
enum em28xx_capture_mode {
EM28XX_CAPTURE_OFF = 0,
EM28XX_ANALOG_CAPTURE,
EM28XX_DIGITAL_CAPTURE,
};
#define EM28XX_AUDIO_BUFS 5
#define EM28XX_NUM_AUDIO_PACKETS 64
#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
......@@ -452,6 +464,11 @@ int em28xx_capture_start(struct em28xx *dev, int start);
int em28xx_outfmt_set_yuv422(struct em28xx *dev);
int em28xx_resolution_set(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
int em28xx_init_isoc(struct em28xx *dev, int max_packets,
int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
int cap_type);
void em28xx_uninit_isoc(struct em28xx *dev);
/* Provided by em28xx-video.c */
int em28xx_register_extension(struct em28xx_ops *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment