Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754422Ab3COMko (ORCPT ); Fri, 15 Mar 2013 08:40:44 -0400 Received: from mail.free-electrons.com ([94.23.35.102]:54193 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754311Ab3COMkm (ORCPT ); Fri, 15 Mar 2013 08:40:42 -0400 Date: Fri, 15 Mar 2013 09:40:35 -0300 From: Ezequiel Garcia To: Jon Arne =?utf-8?Q?J=C3=B8rgensen?= Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, hverkuil@xs4all.nl, elezegarcia@gmail.com Subject: Re: [RFC V1 5/8] smi2021: Add smi2021_video.c Message-ID: <20130315124034.GF2989@localhost> References: <1363270024-12127-1-git-send-email-jonarne@jonarne.no> <1363270024-12127-6-git-send-email-jonarne@jonarne.no> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <1363270024-12127-6-git-send-email-jonarne@jonarne.no> User-Agent: Mutt/1.5.21 (2010-09-15) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 15741 Lines: 578 On Thu, Mar 14, 2013 at 03:07:01PM +0100, Jon Arne Jørgensen wrote: > This file is responsible for all communication with the video hardware > and also starting and stopping the capture. > > It also contains the setup and handling of the usb ISOCHRONOUS transfers. > > Signed-off-by: Jon Arne Jørgensen > --- > drivers/media/usb/smi2021/smi2021_video.c | 543 ++++++++++++++++++++++++++++++ > 1 file changed, 543 insertions(+) > create mode 100644 drivers/media/usb/smi2021/smi2021_video.c > > diff --git a/drivers/media/usb/smi2021/smi2021_video.c b/drivers/media/usb/smi2021/smi2021_video.c > new file mode 100644 > index 0000000..48f3e80 > --- /dev/null > +++ b/drivers/media/usb/smi2021/smi2021_video.c > @@ -0,0 +1,543 @@ > +/******************************************************************************* > + * smi2021_video.c * > + * * > + * USB Driver for SMI2021 - EasyCAP * > + * USB ID 1c88:003c * > + * * > + * ***************************************************************************** > + * > + * Copyright 2011-2013 Jon Arne Jørgensen > + * > + * > + * Copyright 2011, 2012 Tony Brown, Michal Demin, Jeffry Johnston > + * > + * This file is part of SMI2021x > + * http://code.google.com/p/easycap-somagic-linux/ > + * > + * This program is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation, either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see . > + * > + * This driver is heavily influensed by the STK1160 driver. > + * Copyright (C) 2012 Ezequiel Garcia > + * > + * > + */ > + > +#include "smi2021.h" > + > +static void print_usb_err(struct smi2021_dev *dev, int packet, int status) > +{ > + char *errmsg; > + > + switch (status) { > + case -ENOENT: > + errmsg = "unlinked synchronuously"; > + break; > + case -ECONNRESET: > + errmsg = "unlinked asynchronuously"; > + break; > + case -ENOSR: > + errmsg = "Buffer error (overrun)"; > + break; > + case -EPIPE: > + errmsg = "Stalled (device not responding)"; > + break; > + case -EOVERFLOW: > + errmsg = "Babble (bad cable?)"; > + break; > + case -EPROTO: > + errmsg = "Bit-stuff error (bad cable?)"; > + break; > + case -EILSEQ: > + errmsg = "CRC/Timeout (could be anything)"; > + break; > + case -ETIME: > + errmsg = "Device does not respond"; > + break; > + default: > + errmsg = "Unknown"; > + } > + > + if (packet < 0) { > + printk_ratelimited(KERN_WARNING "Urb status %d [%s]\n", > + status, errmsg); > + } else { > + printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s]\n", > + packet, status, errmsg); > + } > +} > + > +static struct smi2021_buffer *smi2021_next_buffer(struct smi2021_dev *dev) > +{ > + struct smi2021_buffer *buf = NULL; > + unsigned long flags = 0; > + > + BUG_ON(dev->isoc_ctl.buf); > + You should replace BUG_ON with WARN_ON. Linus' words: http://permalink.gmane.org/gmane.linux.kernel/1347333 > + spin_lock_irqsave(&dev->buf_lock, flags); > + if (!list_empty(&dev->avail_bufs)) { > + buf = list_first_entry(&dev->avail_bufs, struct smi2021_buffer, > + list); > + list_del(&buf->list); > + } > + spin_unlock_irqrestore(&dev->buf_lock, flags); > + > + return buf; > +} > + > +static void smi2021_buffer_done(struct smi2021_dev *dev) > +{ > + struct smi2021_buffer *buf = dev->isoc_ctl.buf; > + > + dev->buf_count++; > + > + buf->vb.v4l2_buf.sequence = dev->buf_count >> 1; > + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; > + buf->vb.v4l2_buf.bytesused = buf->pos; > + do_gettimeofday(&buf->vb.v4l2_buf.timestamp); > + > + vb2_set_plane_payload(&buf->vb, 0, buf->pos); > + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); > + > + dev->isoc_ctl.buf = NULL; > +} > + > +static void copy_video(struct smi2021_dev *dev, u8 p) > +{ > + struct smi2021_buffer *buf = dev->isoc_ctl.buf; > + > + int lines_per_field = dev->height / 2; > + int line = 0; > + int pos_in_line = 0; > + unsigned int offset = 0; > + u8 *dst; > + > + if (buf == NULL) > + return; > + > + if (buf->in_blank) > + return; > + > + if (buf->pos >= buf->length) { > + if (buf->second_field == 0) { > + /* We are probably trying to capture from > + * a unconnected input > + */ > + smi2021_buffer_done(dev); > + } else { > + printk_ratelimited(KERN_WARNING > + "Buffer overflow!, max: %d bytes, av_lines_found: %d, second_field: %d\n", > + buf->length, buf->trc_av, > + buf->second_field); > + } > + return; > + } > + > + pos_in_line = buf->pos % SMI2021_BYTES_PER_LINE; > + line = buf->pos / SMI2021_BYTES_PER_LINE; > + if (line >= lines_per_field) > + line -= lines_per_field; > + > + if (line != buf->trc_av - 1) { > + /* Keep video synchronized. > + * The device will sometimes give us to many bytes > + * for a line, before we get a new TRC. > + * We just drop these bytes */ > + return; > + } > + > + if (buf->second_field) > + offset += SMI2021_BYTES_PER_LINE; > + > + offset += (SMI2021_BYTES_PER_LINE * line * 2) + pos_in_line; > + > + /* Will this ever happen? */ > + if (offset >= buf->length) { > + printk_ratelimited(KERN_INFO > + "Offset calculation error, field: %d, line: %d, pos_in_line: %d\n", > + buf->second_field, line, pos_in_line); > + return; > + } > + > + dst = buf->mem + offset; > + *dst = p; > + buf->pos++; > +} > + > +#define is_sav(trc) \ > + ((trc & SMI2021_TRC_EAV) == 0x00) > +#define is_field2(trc) \ > + ((trc & SMI2021_TRC_FIELD_2) == SMI2021_TRC_FIELD_2) > +#define is_active_video(trc) \ > + ((trc & SMI2021_TRC_VBI) == 0x00) > +/* > + * Parse the TRC. > + * Grab a new buffer from the queue if don't have one > + * and we are recieving the start of a video frame. > + * > + * Mark video buffers as done if we have one full frame. > + */ > +static void parse_trc(struct smi2021_dev *dev, u8 trc) > +{ > + struct smi2021_buffer *buf = dev->isoc_ctl.buf; > + int lines_per_field = dev->height / 2; > + int line = 0; > + > + if (buf == NULL) { > + if (!is_sav(trc)) > + return; > + > + if (!is_active_video(trc)) > + return; > + > + if (is_field2(trc)) > + return; > + > + buf = smi2021_next_buffer(dev); > + if (buf == NULL) > + return; > + > + dev->isoc_ctl.buf = buf; > + } > + > + if (is_sav(trc)) { > + /* Start of VBI or ACTIVE VIDEO */ > + if (is_active_video(trc)) { > + buf->in_blank = false; > + buf->trc_av++; > + } else { > + /* VBI */ > + buf->in_blank = true; > + } > + > + if (!buf->second_field && is_field2(trc)) { > + line = buf->pos / SMI2021_BYTES_PER_LINE; > + if (line < lines_per_field) > + goto buf_done; > + > + buf->second_field = true; > + buf->trc_av = 0; > + } > + > + if (buf->second_field && !is_field2(trc)) > + goto buf_done; > + } else { > + /* End of VBI or ACTIVE VIDEO */ > + buf->in_blank = true; > + } > + > + return; > + > +buf_done: > + smi2021_buffer_done(dev); > +} > + > +/* > + * Scan the saa7113 Active video data. > + * This data is: > + * 4 bytes header (0xff 0x00 0x00 [TRC/SAV]) > + * 1440 bytes of UYUV Video data > + * 4 bytes footer (0xff 0x00 0x00 [TRC/EAV]) > + * > + * TRC = Time Reference Code. > + * SAV = Start Active Video. > + * EAV = End Active Video. > + * This is described in the saa7113 datasheet. > + */ > +static void parse_video(struct smi2021_dev *dev, u8 *p, int len) > +{ > + int i; > + > + for (i = 0; i < len; i++) { > + switch (dev->sync_state) { > + case HSYNC: > + if (p[i] == 0xff) > + dev->sync_state = SYNCZ1; > + else > + copy_video(dev, p[i]); > + break; > + case SYNCZ1: > + if (p[i] == 0x00) { > + dev->sync_state = SYNCZ2; > + } else { > + dev->sync_state = HSYNC; > + copy_video(dev, 0xff); > + copy_video(dev, p[i]); > + } > + break; > + case SYNCZ2: > + if (p[i] == 0x00) { > + dev->sync_state = TRC; > + } else { > + dev->sync_state = HSYNC; > + copy_video(dev, 0xff); > + copy_video(dev, 0x00); > + copy_video(dev, p[i]); > + } > + break; > + case TRC: > + dev->sync_state = HSYNC; > + parse_trc(dev, p[i]); > + break; > + } > + } > + > +} > +/* > + * > + * The device delivers data in chunks of 0x400 bytes. > + * The four first bytes is a magic header to identify the chunks. > + * 0xaa 0xaa 0x00 0x00 = saa7113 Active Video Data > + * 0xaa 0xaa 0x00 0x01 = PCM - 24Bit 2 Channel audio data > + */ > +static void process_packet(struct smi2021_dev *dev, u8 *p, int len) > +{ > + int i; > + u32 *header; > + > + if (len % 0x400 != 0) { > + printk_ratelimited(KERN_INFO "smi2021::%s: len: %d\n", > + __func__, len); > + return; > + } > + > + for (i = 0; i < len; i += 0x400) { > + header = (u32 *)(p + i); > + switch (__cpu_to_be32(*header)) { > + case 0xaaaa0000: { > + parse_video(dev, p+i+4, 0x400-4); > + break; > + } > + case 0xaaaa0001: { > + smi2021_audio(dev, p+i+4, 0x400-4); > + break; > + } > + default: { > + /* Nothing */ > + } > + } > + } > +} > + > +/* > + * Interrupt called by URB callback > + */ > +static void smi2021_isoc_isr(struct urb *urb) > +{ > + int i, rc, status, len; > + struct smi2021_dev *dev = urb->context; > + u8 *p; > + > + switch (urb->status) { > + case 0: > + break; > + case -ECONNRESET: /* kill */ > + case -ENOENT: > + case -ESHUTDOWN: > + /* uvc driver frees the queue here */ > + return; > + default: > + smi2021_err("urb error! status %d\n", urb->status); > + return; > + } > + > + if (urb->status < 0) > + print_usb_err(dev, -1, status); > + > + if (dev == NULL) { > + smi2021_warn("called with null device\n"); > + return; > + } > + > + for (i = 0; i < urb->number_of_packets; i++) { > + > + status = urb->iso_frame_desc[i].status; > + if (status == -18) { > + /* This seems to happen when the device > + * trying to stream from an unconnected input > + * */ > + continue; > + } > + > + if (status < 0) { > + print_usb_err(dev, i, status); > + continue; > + } > + > + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; > + len = urb->iso_frame_desc[i].actual_length; > + process_packet(dev, p, len); > + } > + > + for (i = 0; i < urb->number_of_packets; i++) { > + urb->iso_frame_desc[i].status = 0; > + urb->iso_frame_desc[i].actual_length = 0; > + } > + > + rc = usb_submit_urb(urb, GFP_ATOMIC); > + if (rc) > + smi2021_err("urb re-submit failed (%d)\n", rc); > +} > + > +/* > + * Cancel urbs > + * This function can not be called in atomic context > + */ > +void smi2021_cancel_isoc(struct smi2021_dev *dev) > +{ > + int i, num_bufs = dev->isoc_ctl.num_bufs; > + if (!num_bufs) > + return; > + > + smi2021_dbg("killing %d urbs...\n", num_bufs); > + > + for (i = 0; i < num_bufs; i++) > + usb_kill_urb(dev->isoc_ctl.urb[i]); > + > + smi2021_dbg("all urbs killed\n"); > + > +} > + > +/* > + * Releases urb and transfer buffers > + * Obviously, associated urb must be killed before releasing it > + */ > +void smi2021_free_isoc(struct smi2021_dev *dev) > +{ > + struct urb *urb; > + int i, num_bufs = dev->isoc_ctl.num_bufs; > + > + smi2021_dbg("freeing %d urb buffers...\n", num_bufs); > + > + for (i = 0; i < num_bufs; i++) { > + urb = dev->isoc_ctl.urb[i]; > + if (urb) { > + if (dev->isoc_ctl.transfer_buffer[i]) { > +#ifndef CONFIG_DMA_NONCOHERENT > + usb_free_coherent(dev->udev, > + urb->transfer_buffer_length, > + dev->isoc_ctl.transfer_buffer[i], > + urb->transfer_dma); > +#else > + kfree(dev->isoc_ctl.transfer_buffer[i]); > +#endif > + } > + 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; > + > + smi2021_dbg("all urb buffers freed\n"); > +} > + > +/* > + * Helper for canceling and freeing urbs > + * This function can not be called in atomic context > + */ > +void smi2021_uninit_isoc(struct smi2021_dev *dev) > +{ > + smi2021_cancel_isoc(dev); > + smi2021_free_isoc(dev); > +} > + > + > +int smi2021_alloc_isoc(struct smi2021_dev *dev) > +{ > + struct urb *urb; > + int i, j, k, sb_size, max_packets, num_bufs; > + > + if (dev->isoc_ctl.num_bufs) > + smi2021_uninit_isoc(dev); > + > + num_bufs = SMI2021_ISOC_BUFS; > + max_packets = SMI2021_ISOC_PACKETS; > + sb_size = max_packets * SMI2021_MAX_PKT_SIZE; > + > + dev->isoc_ctl.buf = NULL; > + dev->isoc_ctl.max_pkt_size = SMI2021_MAX_PKT_SIZE; > + dev->isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); > + if (!dev->isoc_ctl.urb) { > + smi2021_err("out of memory for urb array\n"); > + return -ENOMEM; > + } > + > + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *) * num_bufs, > + GFP_KERNEL); > + if (!dev->isoc_ctl.transfer_buffer) { > + smi2021_err("out of memory for usb transfer\n"); > + kfree(dev->isoc_ctl.urb); > + return -ENOMEM; > + } > + > + for (i = 0; i < num_bufs; i++) { > + urb = usb_alloc_urb(max_packets, GFP_KERNEL); > + if (!urb) { > + smi2021_err("connot allocate urb[%d]\n", i); > + goto free_i_bufs; > + } > + dev->isoc_ctl.urb[i] = urb; > +#ifndef CONFIG_DMA_NONCOHERENT > + dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent( > + dev->udev, sb_size, GFP_KERNEL, > + &urb->transfer_dma); > +#else > + dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, > + GFP_KERNEL); > +#endif > + if (!dev->isoc_ctl.transfer_buffer[i]) { > + smi2021_err("cannot alloc %d bytes for tx[%d] buffer", > + sb_size, i); > + goto free_i_bufs; > + } > + /* Do not leak kernel data */ > + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); > + > + urb->dev = dev->udev; > + urb->pipe = usb_rcvisocpipe(dev->udev, SMI2021_ISOC_EP); > + urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i]; > + urb->transfer_buffer_length = sb_size; > + urb->complete = smi2021_isoc_isr; > + urb->context = dev; > + urb->interval = 1; > + urb->start_frame = 0; > + urb->number_of_packets = max_packets; > +#ifndef CONFIG_DMA_NONCOHERENT > + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; > +#else > + urb->transfer_flags = URB_ISO_ASAP; > +#endif > + 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; > + } > + } > + smi2021_dbg("urbs allocated\n"); > + dev->isoc_ctl.num_bufs = num_bufs; > + return 0; > + > +free_i_bufs: > + dev->isoc_ctl.num_bufs = i+1; > + smi2021_free_isoc(dev); > + return -ENOMEM; > +} > -- > 1.8.1.1 > -- Ezequiel García, Free Electrons Embedded Linux, Kernel and Android Engineering http://free-electrons.com -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/