1a3550ea6SFederico Simoncelli /* 2a3550ea6SFederico Simoncelli * Copyright (c) 2013 Lubomir Rintel 3a3550ea6SFederico Simoncelli * All rights reserved. 4a3550ea6SFederico Simoncelli * 5a3550ea6SFederico Simoncelli * Redistribution and use in source and binary forms, with or without 6a3550ea6SFederico Simoncelli * modification, are permitted provided that the following conditions 7a3550ea6SFederico Simoncelli * are met: 8a3550ea6SFederico Simoncelli * 1. Redistributions of source code must retain the above copyright 9a3550ea6SFederico Simoncelli * notice, this list of conditions, and the following disclaimer, 10a3550ea6SFederico Simoncelli * without modification. 11a3550ea6SFederico Simoncelli * 2. The name of the author may not be used to endorse or promote products 12a3550ea6SFederico Simoncelli * derived from this software without specific prior written permission. 13a3550ea6SFederico Simoncelli * 14a3550ea6SFederico Simoncelli * Alternatively, this software may be distributed under the terms of the 15a3550ea6SFederico Simoncelli * GNU General Public License ("GPL"). 165ae1e2b2SLubomir Rintel * 175ae1e2b2SLubomir Rintel * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 185ae1e2b2SLubomir Rintel * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 195ae1e2b2SLubomir Rintel * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 205ae1e2b2SLubomir Rintel * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 215ae1e2b2SLubomir Rintel * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 225ae1e2b2SLubomir Rintel * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 235ae1e2b2SLubomir Rintel * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 245ae1e2b2SLubomir Rintel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 255ae1e2b2SLubomir Rintel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 265ae1e2b2SLubomir Rintel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 275ae1e2b2SLubomir Rintel * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 285ae1e2b2SLubomir Rintel */ 295ae1e2b2SLubomir Rintel /* 305ae1e2b2SLubomir Rintel * Fushicai USBTV007 Audio-Video Grabber Driver 315ae1e2b2SLubomir Rintel * 325ae1e2b2SLubomir Rintel * Product web site: 335ae1e2b2SLubomir Rintel * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html 345ae1e2b2SLubomir Rintel * 355ae1e2b2SLubomir Rintel * Following LWN articles were very useful in construction of this driver: 365ae1e2b2SLubomir Rintel * Video4Linux2 API series: http://lwn.net/Articles/203924/ 375ae1e2b2SLubomir Rintel * videobuf2 API explanation: http://lwn.net/Articles/447435/ 385ae1e2b2SLubomir Rintel * Thanks go to Jonathan Corbet for providing this quality documentation. 395ae1e2b2SLubomir Rintel * He is awesome. 405ae1e2b2SLubomir Rintel * 415ae1e2b2SLubomir Rintel * No physical hardware was harmed running Windows during the 425ae1e2b2SLubomir Rintel * reverse-engineering activity 43a3550ea6SFederico Simoncelli */ 44a3550ea6SFederico Simoncelli 45a3550ea6SFederico Simoncelli #include "usbtv.h" 46a3550ea6SFederico Simoncelli 47a3550ea6SFederico Simoncelli int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size) 48a3550ea6SFederico Simoncelli { 49a3550ea6SFederico Simoncelli int ret; 50a3550ea6SFederico Simoncelli int pipe = usb_rcvctrlpipe(usbtv->udev, 0); 51a3550ea6SFederico Simoncelli int i; 52a3550ea6SFederico Simoncelli 53a3550ea6SFederico Simoncelli for (i = 0; i < size; i++) { 54a3550ea6SFederico Simoncelli u16 index = regs[i][0]; 55a3550ea6SFederico Simoncelli u16 value = regs[i][1]; 56a3550ea6SFederico Simoncelli 57a3550ea6SFederico Simoncelli ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG, 58a3550ea6SFederico Simoncelli USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 59a3550ea6SFederico Simoncelli value, index, NULL, 0, 0); 60a3550ea6SFederico Simoncelli if (ret < 0) 61a3550ea6SFederico Simoncelli return ret; 62a3550ea6SFederico Simoncelli } 63a3550ea6SFederico Simoncelli 64a3550ea6SFederico Simoncelli return 0; 65a3550ea6SFederico Simoncelli } 66a3550ea6SFederico Simoncelli 67a3550ea6SFederico Simoncelli static int usbtv_probe(struct usb_interface *intf, 68a3550ea6SFederico Simoncelli const struct usb_device_id *id) 69a3550ea6SFederico Simoncelli { 70a3550ea6SFederico Simoncelli int ret; 71a3550ea6SFederico Simoncelli int size; 72a3550ea6SFederico Simoncelli struct device *dev = &intf->dev; 73a3550ea6SFederico Simoncelli struct usbtv *usbtv; 74b72b7979SFelipe Balbi struct usb_host_endpoint *ep; 75a3550ea6SFederico Simoncelli 76a3550ea6SFederico Simoncelli /* Checks that the device is what we think it is. */ 77a3550ea6SFederico Simoncelli if (intf->num_altsetting != 2) 78a3550ea6SFederico Simoncelli return -ENODEV; 79a3550ea6SFederico Simoncelli if (intf->altsetting[1].desc.bNumEndpoints != 4) 80a3550ea6SFederico Simoncelli return -ENODEV; 81a3550ea6SFederico Simoncelli 82b72b7979SFelipe Balbi ep = &intf->altsetting[1].endpoint[0]; 83b72b7979SFelipe Balbi 84a3550ea6SFederico Simoncelli /* Packet size is split into 11 bits of base size and count of 85a3550ea6SFederico Simoncelli * extra multiplies of it.*/ 86b72b7979SFelipe Balbi size = usb_endpoint_maxp(&ep->desc); 876981d6e5SJaejoong Kim size = size * usb_endpoint_maxp_mult(&ep->desc); 88a3550ea6SFederico Simoncelli 89a3550ea6SFederico Simoncelli /* Device structure */ 90a3550ea6SFederico Simoncelli usbtv = kzalloc(sizeof(struct usbtv), GFP_KERNEL); 91a3550ea6SFederico Simoncelli if (usbtv == NULL) 92a3550ea6SFederico Simoncelli return -ENOMEM; 93a3550ea6SFederico Simoncelli usbtv->dev = dev; 94a3550ea6SFederico Simoncelli usbtv->udev = usb_get_dev(interface_to_usbdev(intf)); 95a3550ea6SFederico Simoncelli 96a3550ea6SFederico Simoncelli usbtv->iso_size = size; 97a3550ea6SFederico Simoncelli 98a3550ea6SFederico Simoncelli usb_set_intfdata(intf, usbtv); 99a3550ea6SFederico Simoncelli 100a3550ea6SFederico Simoncelli ret = usbtv_video_init(usbtv); 101a3550ea6SFederico Simoncelli if (ret < 0) 102a3550ea6SFederico Simoncelli goto usbtv_video_fail; 103a3550ea6SFederico Simoncelli 10463ddf68dSFederico Simoncelli ret = usbtv_audio_init(usbtv); 10563ddf68dSFederico Simoncelli if (ret < 0) 10663ddf68dSFederico Simoncelli goto usbtv_audio_fail; 10763ddf68dSFederico Simoncelli 108a3550ea6SFederico Simoncelli /* for simplicity we exploit the v4l2_device reference counting */ 109a3550ea6SFederico Simoncelli v4l2_device_get(&usbtv->v4l2_dev); 110a3550ea6SFederico Simoncelli 11163ddf68dSFederico Simoncelli dev_info(dev, "Fushicai USBTV007 Audio-Video Grabber\n"); 112a3550ea6SFederico Simoncelli return 0; 113a3550ea6SFederico Simoncelli 11463ddf68dSFederico Simoncelli usbtv_audio_fail: 11563ddf68dSFederico Simoncelli usbtv_video_free(usbtv); 11663ddf68dSFederico Simoncelli 117a3550ea6SFederico Simoncelli usbtv_video_fail: 118ebdae650SAlexey Khoroshilov usb_set_intfdata(intf, NULL); 119ebdae650SAlexey Khoroshilov usb_put_dev(usbtv->udev); 120a3550ea6SFederico Simoncelli kfree(usbtv); 121a3550ea6SFederico Simoncelli 122a3550ea6SFederico Simoncelli return ret; 123a3550ea6SFederico Simoncelli } 124a3550ea6SFederico Simoncelli 125a3550ea6SFederico Simoncelli static void usbtv_disconnect(struct usb_interface *intf) 126a3550ea6SFederico Simoncelli { 127a3550ea6SFederico Simoncelli struct usbtv *usbtv = usb_get_intfdata(intf); 128146af9cbSAmber Thrall 129a3550ea6SFederico Simoncelli usb_set_intfdata(intf, NULL); 130a3550ea6SFederico Simoncelli 131a3550ea6SFederico Simoncelli if (!usbtv) 132a3550ea6SFederico Simoncelli return; 133a3550ea6SFederico Simoncelli 13463ddf68dSFederico Simoncelli usbtv_audio_free(usbtv); 135a3550ea6SFederico Simoncelli usbtv_video_free(usbtv); 136a3550ea6SFederico Simoncelli 137a3550ea6SFederico Simoncelli usb_put_dev(usbtv->udev); 138a3550ea6SFederico Simoncelli usbtv->udev = NULL; 139a3550ea6SFederico Simoncelli 140a3550ea6SFederico Simoncelli /* the usbtv structure will be deallocated when v4l2 will be 141a3550ea6SFederico Simoncelli done using it */ 142a3550ea6SFederico Simoncelli v4l2_device_put(&usbtv->v4l2_dev); 143a3550ea6SFederico Simoncelli } 144a3550ea6SFederico Simoncelli 1457fb2e072SArvind Yadav static const struct usb_device_id usbtv_id_table[] = { 146a3550ea6SFederico Simoncelli { USB_DEVICE(0x1b71, 0x3002) }, 14704226916SIcenowy Zheng { USB_DEVICE(0x1f71, 0x3301) }, 148a3550ea6SFederico Simoncelli {} 149a3550ea6SFederico Simoncelli }; 150a3550ea6SFederico Simoncelli MODULE_DEVICE_TABLE(usb, usbtv_id_table); 151a3550ea6SFederico Simoncelli 15263ddf68dSFederico Simoncelli MODULE_AUTHOR("Lubomir Rintel, Federico Simoncelli"); 15363ddf68dSFederico Simoncelli MODULE_DESCRIPTION("Fushicai USBTV007 Audio-Video Grabber Driver"); 154a3550ea6SFederico Simoncelli MODULE_LICENSE("Dual BSD/GPL"); 155a3550ea6SFederico Simoncelli 1569b058373SFengguang Wu static struct usb_driver usbtv_usb_driver = { 157a3550ea6SFederico Simoncelli .name = "usbtv", 158a3550ea6SFederico Simoncelli .id_table = usbtv_id_table, 159a3550ea6SFederico Simoncelli .probe = usbtv_probe, 160a3550ea6SFederico Simoncelli .disconnect = usbtv_disconnect, 161a3550ea6SFederico Simoncelli }; 162a3550ea6SFederico Simoncelli 163a3550ea6SFederico Simoncelli module_usb_driver(usbtv_usb_driver); 164