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
usbtv_set_regs(struct usbtv * usbtv,const u16 regs[][2],int size)47a3550ea6SFederico Simoncelli int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
48a3550ea6SFederico Simoncelli {
49a3550ea6SFederico Simoncelli int ret;
50*91f13120SOliver Neukum int pipe = usb_sndctrlpipe(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,
59536f561dSJohan Hovold value, index, NULL, 0, USB_CTRL_GET_TIMEOUT);
60a3550ea6SFederico Simoncelli if (ret < 0)
61a3550ea6SFederico Simoncelli return ret;
62a3550ea6SFederico Simoncelli }
63a3550ea6SFederico Simoncelli
64a3550ea6SFederico Simoncelli return 0;
65a3550ea6SFederico Simoncelli }
66a3550ea6SFederico Simoncelli
usbtv_probe(struct usb_interface * intf,const struct usb_device_id * id)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:
11550e70445SOliver Neukum /* we must not free at this point */
116bf65f8aaSOliver Neukum v4l2_device_get(&usbtv->v4l2_dev);
117bf65f8aaSOliver Neukum /* this will undo the v4l2_device_get() */
11863ddf68dSFederico Simoncelli usbtv_video_free(usbtv);
11963ddf68dSFederico Simoncelli
120a3550ea6SFederico Simoncelli usbtv_video_fail:
121ebdae650SAlexey Khoroshilov usb_set_intfdata(intf, NULL);
122ebdae650SAlexey Khoroshilov usb_put_dev(usbtv->udev);
123a3550ea6SFederico Simoncelli kfree(usbtv);
124a3550ea6SFederico Simoncelli
125a3550ea6SFederico Simoncelli return ret;
126a3550ea6SFederico Simoncelli }
127a3550ea6SFederico Simoncelli
usbtv_disconnect(struct usb_interface * intf)128a3550ea6SFederico Simoncelli static void usbtv_disconnect(struct usb_interface *intf)
129a3550ea6SFederico Simoncelli {
130a3550ea6SFederico Simoncelli struct usbtv *usbtv = usb_get_intfdata(intf);
131146af9cbSAmber Thrall
132a3550ea6SFederico Simoncelli usb_set_intfdata(intf, NULL);
133a3550ea6SFederico Simoncelli
134a3550ea6SFederico Simoncelli if (!usbtv)
135a3550ea6SFederico Simoncelli return;
136a3550ea6SFederico Simoncelli
13763ddf68dSFederico Simoncelli usbtv_audio_free(usbtv);
138a3550ea6SFederico Simoncelli usbtv_video_free(usbtv);
139a3550ea6SFederico Simoncelli
140a3550ea6SFederico Simoncelli usb_put_dev(usbtv->udev);
141a3550ea6SFederico Simoncelli usbtv->udev = NULL;
142a3550ea6SFederico Simoncelli
143a3550ea6SFederico Simoncelli /* the usbtv structure will be deallocated when v4l2 will be
144a3550ea6SFederico Simoncelli done using it */
145a3550ea6SFederico Simoncelli v4l2_device_put(&usbtv->v4l2_dev);
146a3550ea6SFederico Simoncelli }
147a3550ea6SFederico Simoncelli
1487fb2e072SArvind Yadav static const struct usb_device_id usbtv_id_table[] = {
149a3550ea6SFederico Simoncelli { USB_DEVICE(0x1b71, 0x3002) },
15004226916SIcenowy Zheng { USB_DEVICE(0x1f71, 0x3301) },
1518a695a6cSIan Douglas Scott { USB_DEVICE(0x1f71, 0x3306) },
152a3550ea6SFederico Simoncelli {}
153a3550ea6SFederico Simoncelli };
154a3550ea6SFederico Simoncelli MODULE_DEVICE_TABLE(usb, usbtv_id_table);
155a3550ea6SFederico Simoncelli
15663ddf68dSFederico Simoncelli MODULE_AUTHOR("Lubomir Rintel, Federico Simoncelli");
15763ddf68dSFederico Simoncelli MODULE_DESCRIPTION("Fushicai USBTV007 Audio-Video Grabber Driver");
158a3550ea6SFederico Simoncelli MODULE_LICENSE("Dual BSD/GPL");
159a3550ea6SFederico Simoncelli
1609b058373SFengguang Wu static struct usb_driver usbtv_usb_driver = {
161a3550ea6SFederico Simoncelli .name = "usbtv",
162a3550ea6SFederico Simoncelli .id_table = usbtv_id_table,
163a3550ea6SFederico Simoncelli .probe = usbtv_probe,
164a3550ea6SFederico Simoncelli .disconnect = usbtv_disconnect,
165a3550ea6SFederico Simoncelli };
166a3550ea6SFederico Simoncelli
167a3550ea6SFederico Simoncelli module_usb_driver(usbtv_usb_driver);
168