blob: 780342f7b23965b6891e181fd531d06d33154613 [file] [log] [blame]
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001/*
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -08002 em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08003
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -08004 Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
5 Markus Rechberger <mrechberger@gmail.com>
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -08006 Mauro Carvalho Chehab <mchehab@brturbo.com.br>
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -08007 Sascha Sommer <saschasommer@freenet.de>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08008
Mauro Carvalho Chehab439090d2006-01-23 17:10:54 -02009 Some parts based on SN9C10x PC Camera Controllers GPL driver made
10 by Luca Risolia <luca.risolia@studio.unibo.it>
11
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080012 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27#include <linux/init.h>
28#include <linux/list.h>
29#include <linux/module.h>
30#include <linux/kernel.h>
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020031#include <linux/bitmap.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080032#include <linux/usb.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080033#include <linux/i2c.h>
Mauro Carvalho Chehabb296fc62005-11-08 21:38:37 -080034#include <linux/version.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080035#include <linux/video_decoder.h>
Ingo Molnar1e4baed2006-01-15 07:52:23 -020036#include <linux/mutex.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080037
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080038#include "em28xx.h"
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -080039#include <media/tuner.h>
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -020040#include <media/v4l2-common.h>
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080041
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080042#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
43 "Markus Rechberger <mrechberger@gmail.com>, " \
44 "Mauro Carvalho Chehab <mchehab@brturbo.com.br>, " \
45 "Sascha Sommer <saschasommer@freenet.de>"
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080046
Mauro Carvalho Chehabf7abcd32005-11-08 21:38:25 -080047#define DRIVER_NAME "em28xx"
48#define DRIVER_DESC "Empia em28xx based USB video device driver"
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080049#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080050
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080051#define em28xx_videodbg(fmt, arg...) do {\
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080052 if (video_debug) \
53 printk(KERN_INFO "%s %s :"fmt, \
Jean Delvaref85c6572005-12-19 08:53:59 -020054 dev->name, __FUNCTION__ , ##arg); } while (0)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080055
56MODULE_AUTHOR(DRIVER_AUTHOR);
57MODULE_DESCRIPTION(DRIVER_DESC);
58MODULE_LICENSE("GPL");
59
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080060static LIST_HEAD(em28xx_devlist);
Markus Rechberger9c755412005-11-08 21:37:52 -080061
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -080062static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020063static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
64static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -080065module_param_array(card, int, NULL, 0444);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020066module_param_array(video_nr, int, NULL, 0444);
67module_param_array(vbi_nr, int, NULL, 0444);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -080068MODULE_PARM_DESC(card,"card type");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020069MODULE_PARM_DESC(video_nr,"video device numbers");
70MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -080071
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080072static int tuner = -1;
73module_param(tuner, int, 0444);
74MODULE_PARM_DESC(tuner, "tuner type");
75
76static unsigned int video_debug = 0;
77module_param(video_debug,int,0644);
78MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
79
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -020080/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
81static unsigned long em28xx_devused;
82
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080083/* supported tv norms */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -080084static struct em28xx_tvnorm tvnorms[] = {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -080085 {
86 .name = "PAL",
87 .id = V4L2_STD_PAL,
88 .mode = VIDEO_MODE_PAL,
89 }, {
90 .name = "NTSC",
91 .id = V4L2_STD_NTSC,
92 .mode = VIDEO_MODE_NTSC,
93 }, {
94 .name = "SECAM",
95 .id = V4L2_STD_SECAM,
96 .mode = VIDEO_MODE_SECAM,
97 }, {
98 .name = "PAL-M",
99 .id = V4L2_STD_PAL_M,
100 .mode = VIDEO_MODE_PAL,
101 }
102};
103
104#define TVNORMS ARRAY_SIZE(tvnorms)
105
106/* supported controls */
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200107/* Common to all boards */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800108static struct v4l2_queryctrl em28xx_qctrl[] = {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800109 {
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200110 .id = V4L2_CID_AUDIO_VOLUME,
111 .type = V4L2_CTRL_TYPE_INTEGER,
112 .name = "Volume",
113 .minimum = 0x0,
114 .maximum = 0x1f,
115 .step = 0x1,
116 .default_value = 0x1f,
117 .flags = 0,
118 },{
119 .id = V4L2_CID_AUDIO_MUTE,
120 .type = V4L2_CTRL_TYPE_BOOLEAN,
121 .name = "Mute",
122 .minimum = 0,
123 .maximum = 1,
124 .step = 1,
125 .default_value = 1,
126 .flags = 0,
127 }
128};
129
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800130static struct usb_driver em28xx_usb_driver;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800131
Ingo Molnar1e4baed2006-01-15 07:52:23 -0200132static DEFINE_MUTEX(em28xx_sysfs_lock);
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800133static DECLARE_RWSEM(em28xx_disconnect);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800134
135/********************* v4l2 interface ******************************************/
136
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800137/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800138 * em28xx_config()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800139 * inits registers with sane defaults
140 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800141static int em28xx_config(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800142{
143
144 /* Sets I2C speed to 100 KHz */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800145 em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800146
147 /* enable vbi capturing */
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200148
Markus Rechberger9475fb12006-02-27 00:07:34 -0300149/* em28xx_write_regs_req(dev,0x00,0x0e,"\xC0",1); audio register */
150/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200151 em28xx_write_regs_req(dev,0x00,0x11,"\x51",1);
152
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800153 em28xx_audio_usb_mute(dev, 1);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800154 dev->mute = 1; /* maybe not the right place... */
155 dev->volume = 0x1f;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800156 em28xx_audio_analog_set(dev);
157 em28xx_audio_analog_setup(dev);
158 em28xx_outfmt_set_yuv422(dev);
159 em28xx_colorlevels_set_default(dev);
160 em28xx_compression_disable(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800161
162 return 0;
163}
164
165/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800166 * em28xx_config_i2c()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800167 * configure i2c attached devices
168 */
Adrian Bunk943a4902005-12-01 00:51:35 -0800169static void em28xx_config_i2c(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800170{
171 struct v4l2_frequency f;
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -0300172 em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
173 em28xx_i2c_call_clients(dev, VIDIOC_S_INPUT, &dev->ctl_input);
174 em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800175
176 /* configure tuner */
177 f.tuner = 0;
178 f.type = V4L2_TUNER_ANALOG_TV;
179 f.frequency = 9076; /* FIXME:remove magic number */
180 dev->ctl_freq = f.frequency;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800181 em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800182
183 /* configure tda9887 */
184
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800185
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800186/* em28xx_i2c_call_clients(dev,VIDIOC_S_STD,&dev->tvnorm->id); */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800187}
188
189/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800190 * em28xx_empty_framequeues()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800191 * prepare queues for incoming and outgoing frames
192 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800193static void em28xx_empty_framequeues(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800194{
195 u32 i;
196
197 INIT_LIST_HEAD(&dev->inqueue);
198 INIT_LIST_HEAD(&dev->outqueue);
199
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800200 for (i = 0; i < EM28XX_NUM_FRAMES; i++) {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800201 dev->frame[i].state = F_UNUSED;
202 dev->frame[i].buf.bytesused = 0;
203 }
204}
205
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800206static void video_mux(struct em28xx *dev, int index)
207{
208 int input, ainput;
209
210 input = INPUT(index)->vmux;
211 dev->ctl_input = index;
212 dev->ctl_ainput = INPUT(index)->amux;
213
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -0300214 em28xx_i2c_call_clients(dev, VIDIOC_S_INPUT, &input);
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800215
216 em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,input,dev->ctl_ainput);
217
218 if (dev->has_msp34xx) {
Mauro Carvalho Chehab9bb13a62006-01-09 15:25:37 -0200219 if (dev->i2s_speed)
220 em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed);
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800221 em28xx_i2c_call_clients(dev, VIDIOC_S_AUDIO, &dev->ctl_ainput);
222 ainput = EM28XX_AUDIO_SRC_TUNER;
223 em28xx_audio_source(dev, ainput);
224 } else {
225 switch (dev->ctl_ainput) {
Markus Rechberger9475fb12006-02-27 00:07:34 -0300226 case 0:
227 ainput = EM28XX_AUDIO_SRC_TUNER;
228 break;
229 default:
230 ainput = EM28XX_AUDIO_SRC_LINE;
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800231 }
232 em28xx_audio_source(dev, ainput);
233 }
234}
235
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800236/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800237 * em28xx_v4l2_open()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800238 * inits the device and starts isoc transfer
239 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800240static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800241{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800242 int minor = iminor(inode);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800243 int errCode = 0;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800244 struct em28xx *h,*dev = NULL;
Markus Rechberger9c755412005-11-08 21:37:52 -0800245 struct list_head *list;
246
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800247 list_for_each(list,&em28xx_devlist) {
248 h = list_entry(list, struct em28xx, devlist);
Markus Rechberger9c755412005-11-08 21:37:52 -0800249 if (h->vdev->minor == minor) {
250 dev = h;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200251 dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
252 }
253 if (h->vbi_dev->minor == minor) {
254 dev = h;
255 dev->type = V4L2_BUF_TYPE_VBI_CAPTURE;
Markus Rechberger9c755412005-11-08 21:37:52 -0800256 }
257 }
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200258 if (NULL == dev)
259 return -ENODEV;
Markus Rechberger9c755412005-11-08 21:37:52 -0800260
261 filp->private_data=dev;
262
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200263 em28xx_videodbg("open minor=%d type=%s users=%d\n",
264 minor,v4l2_type_names[dev->type],dev->users);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800265
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800266 if (!down_read_trylock(&em28xx_disconnect))
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800267 return -ERESTARTSYS;
268
269 if (dev->users) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800270 em28xx_warn("this driver can be opened only once\n");
271 up_read(&em28xx_disconnect);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800272 return -EBUSY;
273 }
274
Ingo Molnar3593cab2006-02-07 06:49:14 -0200275 mutex_init(&dev->fileop_lock); /* to 1 == available */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800276 spin_lock_init(&dev->queue_lock);
277 init_waitqueue_head(&dev->wait_frame);
278 init_waitqueue_head(&dev->wait_stream);
279
Ingo Molnar3593cab2006-02-07 06:49:14 -0200280 mutex_lock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800281
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200282 if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
283 em28xx_set_alternate(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800284
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200285 dev->width = norm_maxw(dev);
286 dev->height = norm_maxh(dev);
287 dev->frame_size = dev->width * dev->height * 2;
288 dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
289 dev->bytesperline = dev->width * 2;
290 dev->hscale = 0;
291 dev->vscale = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800292
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200293 em28xx_capture_start(dev, 1);
294 em28xx_resolution_set(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800295
Markus Rechbergere5d4a562006-02-07 06:49:13 -0200296 /* device needs to be initialized before isoc transfer */
297 video_mux(dev, 0);
298
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200299 /* start the transfer */
300 errCode = em28xx_init_isoc(dev);
301 if (errCode)
302 goto err;
303
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200304 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800305
306 dev->users++;
307 filp->private_data = dev;
308 dev->io = IO_NONE;
309 dev->stream = STREAM_OFF;
310 dev->num_frames = 0;
311
312 /* prepare queues */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800313 em28xx_empty_framequeues(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800314
315 dev->state |= DEV_INITIALIZED;
316
Ingo Molnar3593cab2006-02-07 06:49:14 -0200317err:
318 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800319 up_read(&em28xx_disconnect);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800320 return errCode;
321}
322
323/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800324 * em28xx_realease_resources()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800325 * unregisters the v4l2,i2c and usb devices
326 * called when the device gets disconected or at module unload
327*/
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800328static void em28xx_release_resources(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800329{
Ingo Molnar1e4baed2006-01-15 07:52:23 -0200330 mutex_lock(&em28xx_sysfs_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800331
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200332 /*FIXME: I2C IR should be disconnected */
333
334 em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
335 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
336 dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
Markus Rechberger9c755412005-11-08 21:37:52 -0800337 list_del(&dev->devlist);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800338 video_unregister_device(dev->vdev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200339 video_unregister_device(dev->vbi_dev);
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800340 em28xx_i2c_unregister(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800341 usb_put_dev(dev->udev);
Ingo Molnar1e4baed2006-01-15 07:52:23 -0200342 mutex_unlock(&em28xx_sysfs_lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200343
344
345 /* Mark device as unused */
346 em28xx_devused&=~(1<<dev->devno);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800347}
348
349/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800350 * em28xx_v4l2_close()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800351 * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
352 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800353static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800354{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800355 int errCode;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800356 struct em28xx *dev=filp->private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800357
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800358 em28xx_videodbg("users=%d\n", dev->users);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800359
Ingo Molnar3593cab2006-02-07 06:49:14 -0200360 mutex_lock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800361
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800362 em28xx_uninit_isoc(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800363
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800364 em28xx_release_buffers(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800365
366 /* the device is already disconnect, free the remaining resources */
367 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800368 em28xx_release_resources(dev);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200369 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800370 kfree(dev);
371 return 0;
372 }
373
374 /* set alternate 0 */
375 dev->alt = 0;
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800376 em28xx_videodbg("setting alternate 0\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800377 errCode = usb_set_interface(dev->udev, 0, 0);
378 if (errCode < 0) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800379 em28xx_errdev ("cannot change alternate number to 0 (error=%i)\n",
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800380 errCode);
381 }
382
383 dev->users--;
384 wake_up_interruptible_nr(&dev->open, 1);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200385 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800386 return 0;
387}
388
389/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800390 * em28xx_v4l2_read()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800391 * will allocate buffers when called for the first time
392 */
393static ssize_t
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800394em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800395 loff_t * f_pos)
396{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800397 struct em28xx_frame_t *f, *i;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800398 unsigned long lock_flags;
399 int ret = 0;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800400 struct em28xx *dev = filp->private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800401
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200402 if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
403 em28xx_videodbg("V4l2_Buf_type_videocapture is set\n");
404 }
405 if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
406 em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n");
407 em28xx_videodbg("not supported yet! ...\n");
408 if (copy_to_user(buf, "", 1)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200409 mutex_unlock(&dev->fileop_lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200410 return -EFAULT;
411 }
412 return (1);
413 }
414 if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
415 em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n");
416 em28xx_videodbg("not supported yet! ...\n");
417 if (copy_to_user(buf, "", 1)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200418 mutex_unlock(&dev->fileop_lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200419 return -EFAULT;
420 }
421 return (1);
422 }
423
Ingo Molnar3593cab2006-02-07 06:49:14 -0200424 if (mutex_lock_interruptible(&dev->fileop_lock))
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800425 return -ERESTARTSYS;
426
427 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800428 em28xx_videodbg("device not present\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200429 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800430 return -ENODEV;
431 }
432
433 if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800434 em28xx_videodbg("device misconfigured; close and open it again\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200435 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800436 return -EIO;
437 }
438
439 if (dev->io == IO_MMAP) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800440 em28xx_videodbg ("IO method is set to mmap; close and open"
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800441 " the device again to choose the read method\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200442 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800443 return -EINVAL;
444 }
445
446 if (dev->io == IO_NONE) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800447 if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) {
448 em28xx_errdev("read failed, not enough memory\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200449 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800450 return -ENOMEM;
451 }
452 dev->io = IO_READ;
453 dev->stream = STREAM_ON;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800454 em28xx_queue_unusedframes(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800455 }
456
457 if (!count) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200458 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800459 return 0;
460 }
461
462 if (list_empty(&dev->outqueue)) {
463 if (filp->f_flags & O_NONBLOCK) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200464 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800465 return -EAGAIN;
466 }
467 ret = wait_event_interruptible
468 (dev->wait_frame,
469 (!list_empty(&dev->outqueue)) ||
470 (dev->state & DEV_DISCONNECTED));
471 if (ret) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200472 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800473 return ret;
474 }
475 if (dev->state & DEV_DISCONNECTED) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200476 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800477 return -ENODEV;
478 }
479 }
480
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800481 f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800482
483 spin_lock_irqsave(&dev->queue_lock, lock_flags);
484 list_for_each_entry(i, &dev->outqueue, frame)
485 i->state = F_UNUSED;
486 INIT_LIST_HEAD(&dev->outqueue);
487 spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
488
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800489 em28xx_queue_unusedframes(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800490
491 if (count > f->buf.length)
492 count = f->buf.length;
493
494 if (copy_to_user(buf, f->bufmem, count)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200495 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800496 return -EFAULT;
497 }
498 *f_pos += count;
499
Ingo Molnar3593cab2006-02-07 06:49:14 -0200500 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800501
502 return count;
503}
504
505/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800506 * em28xx_v4l2_poll()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800507 * will allocate buffers when called for the first time
508 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800509static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800510{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800511 unsigned int mask = 0;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800512 struct em28xx *dev = filp->private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800513
Ingo Molnar3593cab2006-02-07 06:49:14 -0200514 if (mutex_lock_interruptible(&dev->fileop_lock))
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800515 return POLLERR;
516
517 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800518 em28xx_videodbg("device not present\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800519 } else if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800520 em28xx_videodbg("device is misconfigured; close and open it again\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800521 } else {
522 if (dev->io == IO_NONE) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800523 if (!em28xx_request_buffers
524 (dev, EM28XX_NUM_READ_FRAMES)) {
525 em28xx_warn
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800526 ("poll() failed, not enough memory\n");
527 } else {
528 dev->io = IO_READ;
529 dev->stream = STREAM_ON;
530 }
531 }
532
533 if (dev->io == IO_READ) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800534 em28xx_queue_unusedframes(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800535 poll_wait(filp, &dev->wait_frame, wait);
536
537 if (!list_empty(&dev->outqueue))
538 mask |= POLLIN | POLLRDNORM;
539
Ingo Molnar3593cab2006-02-07 06:49:14 -0200540 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800541
542 return mask;
543 }
544 }
545
Ingo Molnar3593cab2006-02-07 06:49:14 -0200546 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800547 return POLLERR;
548}
549
550/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800551 * em28xx_vm_open()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800552 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800553static void em28xx_vm_open(struct vm_area_struct *vma)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800554{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800555 struct em28xx_frame_t *f = vma->vm_private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800556 f->vma_use_count++;
557}
558
559/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800560 * em28xx_vm_close()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800561 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800562static void em28xx_vm_close(struct vm_area_struct *vma)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800563{
564 /* NOTE: buffers are not freed here */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800565 struct em28xx_frame_t *f = vma->vm_private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800566 f->vma_use_count--;
567}
568
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800569static struct vm_operations_struct em28xx_vm_ops = {
570 .open = em28xx_vm_open,
571 .close = em28xx_vm_close,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800572};
573
574/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800575 * em28xx_v4l2_mmap()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800576 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800577static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800578{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800579 unsigned long size = vma->vm_end - vma->vm_start,
Sascha Sommer3639c862005-12-12 00:37:30 -0800580 start = vma->vm_start;
581 void *pos;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800582 u32 i;
Markus Rechberger9c755412005-11-08 21:37:52 -0800583
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800584 struct em28xx *dev = filp->private_data;
Markus Rechberger9c755412005-11-08 21:37:52 -0800585
Ingo Molnar3593cab2006-02-07 06:49:14 -0200586 if (mutex_lock_interruptible(&dev->fileop_lock))
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800587 return -ERESTARTSYS;
588
589 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800590 em28xx_videodbg("mmap: device not present\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200591 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800592 return -ENODEV;
593 }
594
595 if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800596 em28xx_videodbg ("mmap: Device is misconfigured; close and "
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800597 "open it again\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200598 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800599 return -EIO;
600 }
601
602 if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
603 size != PAGE_ALIGN(dev->frame[0].buf.length)) {
Ingo Molnar3593cab2006-02-07 06:49:14 -0200604 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800605 return -EINVAL;
606 }
607
608 for (i = 0; i < dev->num_frames; i++) {
609 if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
610 break;
611 }
612 if (i == dev->num_frames) {
Mauro Carvalho Chehabeac94352005-11-08 21:38:43 -0800613 em28xx_videodbg("mmap: user supplied mapping address is out of range\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200614 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800615 return -EINVAL;
616 }
617
618 /* VM_IO is eventually going to replace PageReserved altogether */
619 vma->vm_flags |= VM_IO;
620 vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
621
Sascha Sommer3639c862005-12-12 00:37:30 -0800622 pos = dev->frame[i].bufmem;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800623 while (size > 0) { /* size is page-aligned */
Sascha Sommer3639c862005-12-12 00:37:30 -0800624 if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
625 em28xx_videodbg("mmap: vm_insert_page failed\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -0200626 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800627 return -EAGAIN;
628 }
629 start += PAGE_SIZE;
630 pos += PAGE_SIZE;
631 size -= PAGE_SIZE;
632 }
633
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800634 vma->vm_ops = &em28xx_vm_ops;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800635 vma->vm_private_data = &dev->frame[i];
636
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800637 em28xx_vm_open(vma);
Ingo Molnar3593cab2006-02-07 06:49:14 -0200638 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800639 return 0;
640}
641
642/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800643 * em28xx_get_ctrl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800644 * return the current saturation, brightness or contrast, mute state
645 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800646static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800647{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800648 switch (ctrl->id) {
649 case V4L2_CID_AUDIO_MUTE:
650 ctrl->value = dev->mute;
651 return 0;
652 case V4L2_CID_AUDIO_VOLUME:
653 ctrl->value = dev->volume;
654 return 0;
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200655 default:
656 return -EINVAL;
657 }
658}
659
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800660/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800661 * em28xx_set_ctrl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800662 * mute or set new saturation, brightness or contrast
663 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800664static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800665{
666 switch (ctrl->id) {
667 case V4L2_CID_AUDIO_MUTE:
668 if (ctrl->value != dev->mute) {
669 dev->mute = ctrl->value;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800670 em28xx_audio_usb_mute(dev, ctrl->value);
671 return em28xx_audio_analog_set(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800672 }
673 return 0;
674 case V4L2_CID_AUDIO_VOLUME:
675 dev->volume = ctrl->value;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800676 return em28xx_audio_analog_set(dev);
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -0200677 default:
678 return -EINVAL;
679 }
680}
681
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800682/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800683 * em28xx_stream_interrupt()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800684 * stops streaming
685 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800686static int em28xx_stream_interrupt(struct em28xx *dev)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800687{
688 int ret = 0;
689
690 /* stop reading from the device */
691
692 dev->stream = STREAM_INTERRUPT;
693 ret = wait_event_timeout(dev->wait_stream,
694 (dev->stream == STREAM_OFF) ||
695 (dev->state & DEV_DISCONNECTED),
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800696 EM28XX_URB_TIMEOUT);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800697 if (dev->state & DEV_DISCONNECTED)
698 return -ENODEV;
699 else if (ret) {
700 dev->state |= DEV_MISCONFIGURED;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800701 em28xx_videodbg("device is misconfigured; close and "
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200702 "open /dev/video%d again\n",
703 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800704 return ret;
705 }
706
707 return 0;
708}
709
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800710static int em28xx_set_norm(struct em28xx *dev, int width, int height)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800711{
712 unsigned int hscale, vscale;
713 unsigned int maxh, maxw;
714
715 maxw = norm_maxw(dev);
716 maxh = norm_maxh(dev);
717
718 /* width must even because of the YUYV format */
719 /* height must be even because of interlacing */
720 height &= 0xfffe;
721 width &= 0xfffe;
722
723 if (height < 32)
724 height = 32;
725 if (height > maxh)
726 height = maxh;
727 if (width < 48)
728 width = 48;
729 if (width > maxw)
730 width = maxw;
731
732 if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
733 hscale = 0x3fff;
734 width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
735
736 if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
737 vscale = 0x3fff;
738 height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
739
740 /* set new image size */
741 dev->width = width;
742 dev->height = height;
743 dev->frame_size = dev->width * dev->height * 2;
744 dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
745 dev->bytesperline = dev->width * 2;
746 dev->hscale = hscale;
747 dev->vscale = vscale;
748
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800749 em28xx_resolution_set(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800750
751 return 0;
752}
753
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200754static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format)
755{
756 em28xx_videodbg("VIDIOC_G_FMT: type=%s\n",
757 (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
758 "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
759 (format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ?
760 "V4L2_BUF_TYPE_VBI_CAPTURE" :
761 (format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ?
762 "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " :
763 "not supported");
764
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200765 switch (format->type) {
766 case V4L2_BUF_TYPE_VIDEO_CAPTURE:
767 {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200768 format->fmt.pix.width = dev->width;
769 format->fmt.pix.height = dev->height;
770 format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
771 format->fmt.pix.bytesperline = dev->bytesperline;
772 format->fmt.pix.sizeimage = dev->frame_size;
773 format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
774 format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
775
776 em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width,
777 dev->height);
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200778 break;
779 }
780
781 case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
782 {
783 format->fmt.sliced.service_set=0;
784
785 em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
786
787 if (format->fmt.sliced.service_set==0)
788 return -EINVAL;
789
790 break;
791 }
792
793 default:
794 return -EINVAL;
795 }
796 return (0);
797}
798
799static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format)
800{
801 u32 i;
802 int ret = 0;
803 int width = format->fmt.pix.width;
804 int height = format->fmt.pix.height;
805 unsigned int hscale, vscale;
806 unsigned int maxh, maxw;
807
808 maxw = norm_maxw(dev);
809 maxh = norm_maxh(dev);
810
811 em28xx_videodbg("%s: type=%s\n",
812 cmd == VIDIOC_TRY_FMT ?
813 "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
814 format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
815 "V4L2_BUF_TYPE_VIDEO_CAPTURE" :
816 format->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
817 "V4L2_BUF_TYPE_VBI_CAPTURE " :
818 "not supported");
819
820 if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
821 em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format);
822
823 if (format->fmt.sliced.service_set==0)
824 return -EINVAL;
825
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200826 return 0;
827 }
828
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200829
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200830 if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
831 return -EINVAL;
832
833 em28xx_videodbg("%s: requested %dx%d\n",
834 cmd == VIDIOC_TRY_FMT ?
835 "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT",
836 format->fmt.pix.width, format->fmt.pix.height);
837
838 /* FIXME: Move some code away from here */
839 /* width must even because of the YUYV format */
840 /* height must be even because of interlacing */
841 height &= 0xfffe;
842 width &= 0xfffe;
843
844 if (height < 32)
845 height = 32;
846 if (height > maxh)
847 height = maxh;
848 if (width < 48)
849 width = 48;
850 if (width > maxw)
851 width = maxw;
852
853 if(dev->is_em2800){
854 /* the em2800 can only scale down to 50% */
855 if(height % (maxh / 2))
856 height=maxh;
857 if(width % (maxw / 2))
858 width=maxw;
859 /* according to empiatech support */
860 /* the MaxPacketSize is to small to support */
861 /* framesizes larger than 640x480 @ 30 fps */
862 /* or 640x576 @ 25 fps. As this would cut */
863 /* of a part of the image we prefer */
864 /* 360x576 or 360x480 for now */
865 if(width == maxw && height == maxh)
866 width /= 2;
867 }
868
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200869 if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000)
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200870 hscale = 0x3fff;
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200871
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200872 width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
873
874 if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000)
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200875 vscale = 0x3fff;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200876
877 height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200878
879 format->fmt.pix.width = width;
880 format->fmt.pix.height = height;
881 format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
882 format->fmt.pix.bytesperline = width * 2;
883 format->fmt.pix.sizeimage = width * 2 * height;
884 format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
885 format->fmt.pix.field = V4L2_FIELD_INTERLACED;
886
887 em28xx_videodbg("%s: returned %dx%d (%d, %d)\n",
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200888 cmd == VIDIOC_TRY_FMT ?
889 "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT",
890 format->fmt.pix.width, format->fmt.pix.height, hscale, vscale);
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200891
892 if (cmd == VIDIOC_TRY_FMT)
893 return 0;
894
895 for (i = 0; i < dev->num_frames; i++)
896 if (dev->frame[i].vma_use_count) {
897 em28xx_videodbg("VIDIOC_S_FMT failed. "
898 "Unmap the buffers first.\n");
899 return -EINVAL;
900 }
901
902 /* stop io in case it is already in progress */
903 if (dev->stream == STREAM_ON) {
904 em28xx_videodbg("VIDIOC_SET_FMT: interupting stream\n");
905 if ((ret = em28xx_stream_interrupt(dev)))
906 return ret;
907 }
908
909 em28xx_release_buffers(dev);
910 dev->io = IO_NONE;
911
912 /* set new image size */
913 dev->width = width;
914 dev->height = height;
915 dev->frame_size = dev->width * dev->height * 2;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200916 dev->field_size = dev->frame_size >> 1;
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -0200917 dev->bytesperline = dev->width * 2;
918 dev->hscale = hscale;
919 dev->vscale = vscale;
920 em28xx_uninit_isoc(dev);
921 em28xx_set_alternate(dev);
922 em28xx_capture_start(dev, 1);
923 em28xx_resolution_set(dev);
924 em28xx_init_isoc(dev);
925
926 return 0;
927}
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -0200928
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800929/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800930 * em28xx_v4l2_do_ioctl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800931 * This function is _not_ called directly, but from
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800932 * em28xx_v4l2_ioctl. Userspace
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800933 * copying is done already, arg is a kernel pointer.
934 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -0800935static int em28xx_do_ioctl(struct inode *inode, struct file *filp,
936 struct em28xx *dev, unsigned int cmd, void *arg,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800937 v4l2_kioctl driver_ioctl)
938{
939 int ret;
940
941 switch (cmd) {
942 /* ---------- tv norms ---------- */
943 case VIDIOC_ENUMSTD:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200944 {
945 struct v4l2_standard *e = arg;
946 unsigned int i;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800947
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200948 i = e->index;
949 if (i >= TVNORMS)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -0800950 return -EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200951 ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
952 tvnorms[e->index].name);
953 e->index = i;
954 if (ret < 0)
955 return ret;
956 return 0;
957 }
958 case VIDIOC_G_STD:
959 {
960 v4l2_std_id *id = arg;
961
962 *id = dev->tvnorm->id;
963 return 0;
964 }
965 case VIDIOC_S_STD:
966 {
967 v4l2_std_id *id = arg;
968 unsigned int i;
969
970 for (i = 0; i < TVNORMS; i++)
971 if (*id == tvnorms[i].id)
972 break;
973 if (i == TVNORMS)
974 for (i = 0; i < TVNORMS; i++)
975 if (*id & tvnorms[i].id)
976 break;
977 if (i == TVNORMS)
978 return -EINVAL;
979
Ingo Molnar3593cab2006-02-07 06:49:14 -0200980 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200981 dev->tvnorm = &tvnorms[i];
982
983 em28xx_set_norm(dev, dev->width, dev->height);
984
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200985 em28xx_i2c_call_clients(dev, VIDIOC_S_STD,
986 &dev->tvnorm->id);
987
Ingo Molnar3593cab2006-02-07 06:49:14 -0200988 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -0200989
990 return 0;
991 }
992
993 /* ------ input switching ---------- */
994 case VIDIOC_ENUMINPUT:
995 {
996 struct v4l2_input *i = arg;
997 unsigned int n;
998 static const char *iname[] = {
999 [EM28XX_VMUX_COMPOSITE1] = "Composite1",
1000 [EM28XX_VMUX_COMPOSITE2] = "Composite2",
1001 [EM28XX_VMUX_COMPOSITE3] = "Composite3",
1002 [EM28XX_VMUX_COMPOSITE4] = "Composite4",
1003 [EM28XX_VMUX_SVIDEO] = "S-Video",
1004 [EM28XX_VMUX_TELEVISION] = "Television",
1005 [EM28XX_VMUX_CABLE] = "Cable TV",
1006 [EM28XX_VMUX_DVB] = "DVB",
1007 [EM28XX_VMUX_DEBUG] = "for debug only",
1008 };
1009
1010 n = i->index;
1011 if (n >= MAX_EM28XX_INPUT)
1012 return -EINVAL;
1013 if (0 == INPUT(n)->type)
1014 return -EINVAL;
1015 memset(i, 0, sizeof(*i));
1016 i->index = n;
1017 i->type = V4L2_INPUT_TYPE_CAMERA;
1018 strcpy(i->name, iname[INPUT(n)->type]);
1019 if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
1020 (EM28XX_VMUX_CABLE == INPUT(n)->type))
1021 i->type = V4L2_INPUT_TYPE_TUNER;
1022 for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
1023 i->std |= tvnorms[n].id;
1024 return 0;
1025 }
1026 case VIDIOC_G_INPUT:
1027 {
1028 int *i = arg;
1029 *i = dev->ctl_input;
1030
1031 return 0;
1032 }
1033 case VIDIOC_S_INPUT:
1034 {
1035 int *index = arg;
1036
1037 if (*index >= MAX_EM28XX_INPUT)
1038 return -EINVAL;
1039 if (0 == INPUT(*index)->type)
1040 return -EINVAL;
1041
Ingo Molnar3593cab2006-02-07 06:49:14 -02001042 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001043 video_mux(dev, *index);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001044 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001045
1046 return 0;
1047 }
1048 case VIDIOC_G_AUDIO:
1049 {
1050 struct v4l2_audio *a = arg;
1051 unsigned int index = a->index;
1052
1053 if (a->index > 1)
1054 return -EINVAL;
1055 memset(a, 0, sizeof(*a));
1056 index = dev->ctl_ainput;
1057
1058 if (index == 0) {
1059 strcpy(a->name, "Television");
1060 } else {
1061 strcpy(a->name, "Line In");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001062 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001063 a->capability = V4L2_AUDCAP_STEREO;
1064 a->index = index;
1065 return 0;
1066 }
1067 case VIDIOC_S_AUDIO:
1068 {
1069 struct v4l2_audio *a = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001070
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001071 if (a->index != dev->ctl_ainput)
1072 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001073
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001074 return 0;
1075 }
1076
1077 /* --- controls ---------------------------------------------- */
1078 case VIDIOC_QUERYCTRL:
1079 {
1080 struct v4l2_queryctrl *qc = arg;
1081 int i, id=qc->id;
1082
1083 memset(qc,0,sizeof(*qc));
1084 qc->id=id;
1085
1086 if (!dev->has_msp34xx) {
1087 for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
1088 if (qc->id && qc->id == em28xx_qctrl[i].id) {
1089 memcpy(qc, &(em28xx_qctrl[i]),
1090 sizeof(*qc));
Mauro Carvalho Chehabc0477ad2006-01-09 15:25:14 -02001091 return 0;
1092 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001093 }
1094 }
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001095 em28xx_i2c_call_clients(dev,cmd,qc);
1096 if (qc->type)
1097 return 0;
1098 else
1099 return -EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001100 }
1101 case VIDIOC_G_CTRL:
1102 {
1103 struct v4l2_control *ctrl = arg;
1104 int retval=-EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001105
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001106 if (!dev->has_msp34xx)
1107 retval=em28xx_get_ctrl(dev, ctrl);
1108 if (retval==-EINVAL) {
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001109 em28xx_i2c_call_clients(dev,cmd,arg);
1110 return 0;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001111 } else return retval;
1112 }
1113 case VIDIOC_S_CTRL:
1114 {
1115 struct v4l2_control *ctrl = arg;
1116 u8 i;
1117
1118 if (!dev->has_msp34xx){
1119 for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
1120 if (ctrl->id == em28xx_qctrl[i].id) {
1121 if (ctrl->value <
1122 em28xx_qctrl[i].minimum
1123 || ctrl->value >
1124 em28xx_qctrl[i].maximum)
1125 return -ERANGE;
1126 return em28xx_set_ctrl(dev, ctrl);
1127 }
1128 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001129 }
1130
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001131 em28xx_i2c_call_clients(dev,cmd,arg);
1132 return 0;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001133 }
1134 /* --- tuner ioctls ------------------------------------------ */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001135 case VIDIOC_G_TUNER:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001136 {
1137 struct v4l2_tuner *t = arg;
1138 int status = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001139
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001140 if (0 != t->index)
1141 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001142
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001143 memset(t, 0, sizeof(*t));
1144 strcpy(t->name, "Tuner");
1145 t->type = V4L2_TUNER_ANALOG_TV;
1146 t->capability = V4L2_TUNER_CAP_NORM;
1147 t->rangehigh = 0xffffffffUL; /* FIXME: set correct range */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001148/* t->signal = 0xffff;*/
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001149/* em28xx_i2c_call_clients(dev,VIDIOC_G_TUNER,t);*/
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001150 /* No way to get signal strength? */
Ingo Molnar3593cab2006-02-07 06:49:14 -02001151 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001152 em28xx_i2c_call_clients(dev, DECODER_GET_STATUS,
1153 &status);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001154 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001155 t->signal =
1156 (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001157
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001158 em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal,
1159 t->afc);
1160 return 0;
1161 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001162 case VIDIOC_S_TUNER:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001163 {
1164 struct v4l2_tuner *t = arg;
1165 int status = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001166
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001167 if (0 != t->index)
1168 return -EINVAL;
1169 memset(t, 0, sizeof(*t));
1170 strcpy(t->name, "Tuner");
1171 t->type = V4L2_TUNER_ANALOG_TV;
1172 t->capability = V4L2_TUNER_CAP_NORM;
1173 t->rangehigh = 0xffffffffUL; /* FIXME: set correct range */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001174/* t->signal = 0xffff; */
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001175 /* No way to get signal strength? */
Ingo Molnar3593cab2006-02-07 06:49:14 -02001176 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001177 em28xx_i2c_call_clients(dev, DECODER_GET_STATUS,
1178 &status);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001179 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001180 t->signal =
1181 (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001182
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001183 em28xx_videodbg("VIDIO_S_TUNER: signal=%x, afc=%x\n",
1184 t->signal, t->afc);
1185 return 0;
1186 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001187 case VIDIOC_G_FREQUENCY:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001188 {
1189 struct v4l2_frequency *f = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001190
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001191 memset(f, 0, sizeof(*f));
1192 f->type = V4L2_TUNER_ANALOG_TV;
1193 f->frequency = dev->ctl_freq;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001194
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001195 return 0;
1196 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001197 case VIDIOC_S_FREQUENCY:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001198 {
1199 struct v4l2_frequency *f = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001200
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001201 if (0 != f->tuner)
1202 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001203
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001204 if (V4L2_TUNER_ANALOG_TV != f->type)
1205 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001206
Ingo Molnar3593cab2006-02-07 06:49:14 -02001207 mutex_lock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001208 dev->ctl_freq = f->frequency;
1209 em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001210 mutex_unlock(&dev->lock);
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001211 return 0;
1212 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001213 case VIDIOC_CROPCAP:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001214 {
1215 struct v4l2_cropcap *cc = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001216
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001217 if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1218 return -EINVAL;
1219 cc->bounds.left = 0;
1220 cc->bounds.top = 0;
1221 cc->bounds.width = dev->width;
1222 cc->bounds.height = dev->height;
1223 cc->defrect = cc->bounds;
1224 cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
1225 cc->pixelaspect.denominator = 59;
1226 return 0;
1227 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001228 case VIDIOC_STREAMON:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001229 {
1230 int *type = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001231
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001232 if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
1233 || dev->io != IO_MMAP)
1234 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001235
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001236 if (list_empty(&dev->inqueue))
1237 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001238
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001239 dev->stream = STREAM_ON; /* FIXME: Start video capture here? */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001240
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001241 em28xx_videodbg("VIDIOC_STREAMON: starting stream\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001242
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001243 return 0;
1244 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001245 case VIDIOC_STREAMOFF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001246 {
1247 int *type = arg;
1248 int ret;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001249
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001250 if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE
1251 || dev->io != IO_MMAP)
1252 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001253
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001254 if (dev->stream == STREAM_ON) {
1255 em28xx_videodbg ("VIDIOC_STREAMOFF: interrupting stream\n");
1256 if ((ret = em28xx_stream_interrupt(dev)))
1257 return ret;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001258 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001259 em28xx_empty_framequeues(dev);
1260
1261 return 0;
1262 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001263 default:
1264 return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
1265 driver_ioctl);
1266 }
1267 return 0;
1268}
1269
1270/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001271 * em28xx_v4l2_do_ioctl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001272 * This function is _not_ called directly, but from
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001273 * em28xx_v4l2_ioctl. Userspace
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001274 * copying is done already, arg is a kernel pointer.
1275 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001276static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001277 unsigned int cmd, void *arg)
1278{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001279 struct em28xx *dev = filp->private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001280
1281 if (!dev)
1282 return -ENODEV;
1283
1284 if (video_debug > 1)
Michael Krufky5e453dc2006-01-09 15:32:31 -02001285 v4l_print_ioctl(dev->name,cmd);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001286
1287 switch (cmd) {
1288
1289 /* --- capabilities ------------------------------------------ */
1290 case VIDIOC_QUERYCAP:
1291 {
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001292 struct v4l2_capability *cap = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001293
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001294 memset(cap, 0, sizeof(*cap));
1295 strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
1296 strlcpy(cap->card, em28xx_boards[dev->model].name,
1297 sizeof(cap->card));
1298 strlcpy(cap->bus_info, dev->udev->dev.bus_id,
1299 sizeof(cap->bus_info));
1300 cap->version = EM28XX_VERSION_CODE;
1301 cap->capabilities =
1302 V4L2_CAP_SLICED_VBI_CAPTURE |
1303 V4L2_CAP_VIDEO_CAPTURE |
1304 V4L2_CAP_AUDIO |
1305 V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
1306 if (dev->has_tuner)
1307 cap->capabilities |= V4L2_CAP_TUNER;
1308 return 0;
1309 }
1310 /* --- capture ioctls ---------------------------------------- */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001311 case VIDIOC_ENUM_FMT:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001312 {
1313 struct v4l2_fmtdesc *fmtd = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001314
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001315 if (fmtd->index != 0)
1316 return -EINVAL;
1317 memset(fmtd, 0, sizeof(*fmtd));
1318 fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1319 strcpy(fmtd->description, "Packed YUY2");
1320 fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
1321 memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
1322 return 0;
1323 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001324 case VIDIOC_G_FMT:
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001325 return em28xx_get_fmt(dev, (struct v4l2_format *) arg);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001326
1327 case VIDIOC_TRY_FMT:
1328 case VIDIOC_S_FMT:
Mauro Carvalho Chehab2d50f842006-01-23 17:11:08 -02001329 return em28xx_set_fmt(dev, cmd, (struct v4l2_format *)arg);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001330
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001331 case VIDIOC_REQBUFS:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001332 {
1333 struct v4l2_requestbuffers *rb = arg;
1334 u32 i;
1335 int ret;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001336
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001337 if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
1338 rb->memory != V4L2_MEMORY_MMAP)
1339 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001340
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001341 if (dev->io == IO_READ) {
1342 em28xx_videodbg ("method is set to read;"
1343 " close and open the device again to"
1344 " choose the mmap I/O method\n");
1345 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001346 }
1347
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001348 for (i = 0; i < dev->num_frames; i++)
1349 if (dev->frame[i].vma_use_count) {
1350 em28xx_videodbg ("VIDIOC_REQBUFS failed; previous buffers are still mapped\n");
1351 return -EINVAL;
1352 }
1353
1354 if (dev->stream == STREAM_ON) {
1355 em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n");
1356 if ((ret = em28xx_stream_interrupt(dev)))
1357 return ret;
1358 }
1359
1360 em28xx_empty_framequeues(dev);
1361
1362 em28xx_release_buffers(dev);
1363 if (rb->count)
1364 rb->count =
1365 em28xx_request_buffers(dev, rb->count);
1366
1367 dev->frame_current = NULL;
1368
1369 em28xx_videodbg ("VIDIOC_REQBUFS: setting io method to mmap: num bufs %i\n",
1370 rb->count);
1371 dev->io = rb->count ? IO_MMAP : IO_NONE;
1372 return 0;
1373 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001374 case VIDIOC_QUERYBUF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001375 {
1376 struct v4l2_buffer *b = arg;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001377
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001378 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
1379 b->index >= dev->num_frames || dev->io != IO_MMAP)
1380 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001381
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001382 memcpy(b, &dev->frame[b->index].buf, sizeof(*b));
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001383
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001384 if (dev->frame[b->index].vma_use_count) {
1385 b->flags |= V4L2_BUF_FLAG_MAPPED;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001386 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001387 if (dev->frame[b->index].state == F_DONE)
1388 b->flags |= V4L2_BUF_FLAG_DONE;
1389 else if (dev->frame[b->index].state != F_UNUSED)
1390 b->flags |= V4L2_BUF_FLAG_QUEUED;
1391 return 0;
1392 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001393 case VIDIOC_QBUF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001394 {
1395 struct v4l2_buffer *b = arg;
1396 unsigned long lock_flags;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001397
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001398 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
1399 b->index >= dev->num_frames || dev->io != IO_MMAP) {
1400 return -EINVAL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001401 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001402
1403 if (dev->frame[b->index].state != F_UNUSED) {
1404 return -EAGAIN;
1405 }
1406 dev->frame[b->index].state = F_QUEUED;
1407
1408 /* add frame to fifo */
1409 spin_lock_irqsave(&dev->queue_lock, lock_flags);
1410 list_add_tail(&dev->frame[b->index].frame,
1411 &dev->inqueue);
1412 spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
1413
1414 return 0;
1415 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001416 case VIDIOC_DQBUF:
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001417 {
1418 struct v4l2_buffer *b = arg;
1419 struct em28xx_frame_t *f;
1420 unsigned long lock_flags;
1421 int ret = 0;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001422
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001423 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
1424 || dev->io != IO_MMAP)
1425 return -EINVAL;
1426
1427 if (list_empty(&dev->outqueue)) {
1428 if (dev->stream == STREAM_OFF)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001429 return -EINVAL;
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001430 if (filp->f_flags & O_NONBLOCK)
1431 return -EAGAIN;
1432 ret = wait_event_interruptible
1433 (dev->wait_frame,
1434 (!list_empty(&dev->outqueue)) ||
1435 (dev->state & DEV_DISCONNECTED));
1436 if (ret)
1437 return ret;
1438 if (dev->state & DEV_DISCONNECTED)
1439 return -ENODEV;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001440 }
Mauro Carvalho Chehab9aeb4b02006-01-23 17:11:09 -02001441
1442 spin_lock_irqsave(&dev->queue_lock, lock_flags);
1443 f = list_entry(dev->outqueue.next,
1444 struct em28xx_frame_t, frame);
1445 list_del(dev->outqueue.next);
1446 spin_unlock_irqrestore(&dev->queue_lock, lock_flags);
1447
1448 f->state = F_UNUSED;
1449 memcpy(b, &f->buf, sizeof(*b));
1450
1451 if (f->vma_use_count)
1452 b->flags |= V4L2_BUF_FLAG_MAPPED;
1453
1454 return 0;
1455 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001456 default:
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001457 return em28xx_do_ioctl(inode, filp, dev, cmd, arg,
1458 em28xx_video_do_ioctl);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001459 }
1460 return 0;
1461}
1462
1463/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001464 * em28xx_v4l2_ioctl()
1465 * handle v4l2 ioctl the main action happens in em28xx_v4l2_do_ioctl()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001466 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001467static int em28xx_v4l2_ioctl(struct inode *inode, struct file *filp,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001468 unsigned int cmd, unsigned long arg)
1469{
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001470 int ret = 0;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001471 struct em28xx *dev = filp->private_data;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001472
Ingo Molnar3593cab2006-02-07 06:49:14 -02001473 if (mutex_lock_interruptible(&dev->fileop_lock))
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001474 return -ERESTARTSYS;
1475
1476 if (dev->state & DEV_DISCONNECTED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001477 em28xx_errdev("v4l2 ioctl: device not present\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02001478 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001479 return -ENODEV;
1480 }
1481
1482 if (dev->state & DEV_MISCONFIGURED) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001483 em28xx_errdev
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001484 ("v4l2 ioctl: device is misconfigured; close and open it again\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02001485 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001486 return -EIO;
1487 }
1488
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001489 ret = video_usercopy(inode, filp, cmd, arg, em28xx_video_do_ioctl);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001490
Ingo Molnar3593cab2006-02-07 06:49:14 -02001491 mutex_unlock(&dev->fileop_lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001492
1493 return ret;
1494}
1495
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001496static struct file_operations em28xx_v4l_fops = {
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001497 .owner = THIS_MODULE,
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001498 .open = em28xx_v4l2_open,
1499 .release = em28xx_v4l2_close,
1500 .ioctl = em28xx_v4l2_ioctl,
1501 .read = em28xx_v4l2_read,
1502 .poll = em28xx_v4l2_poll,
1503 .mmap = em28xx_v4l2_mmap,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001504 .llseek = no_llseek,
Mauro Carvalho Chehab17cbe2e2006-01-09 15:24:58 -02001505 .compat_ioctl = v4l_compat_ioctl32,
1506
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001507};
1508
1509/******************************** usb interface *****************************************/
1510
1511/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001512 * em28xx_init_dev()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001513 * allocates and inits the device structs, registers i2c bus and v4l device
1514 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001515static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001516 int minor, int model)
1517{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001518 struct em28xx *dev = *devhandle;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001519 int retval = -ENOMEM;
1520 int errCode, i;
1521 unsigned int maxh, maxw;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001522
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001523 dev->udev = udev;
1524 dev->model = model;
Ingo Molnar3593cab2006-02-07 06:49:14 -02001525 mutex_init(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001526 init_waitqueue_head(&dev->open);
1527
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001528 dev->em28xx_write_regs = em28xx_write_regs;
1529 dev->em28xx_read_reg = em28xx_read_reg;
1530 dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
1531 dev->em28xx_write_regs_req = em28xx_write_regs_req;
1532 dev->em28xx_read_reg_req = em28xx_read_reg_req;
1533 dev->is_em2800 = em28xx_boards[model].is_em2800;
1534 dev->has_tuner = em28xx_boards[model].has_tuner;
1535 dev->has_msp34xx = em28xx_boards[model].has_msp34xx;
1536 dev->tda9887_conf = em28xx_boards[model].tda9887_conf;
1537 dev->decoder = em28xx_boards[model].decoder;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001538
1539 if (tuner >= 0)
1540 dev->tuner_type = tuner;
1541 else
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001542 dev->tuner_type = em28xx_boards[model].tuner_type;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001543
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001544 dev->video_inputs = em28xx_boards[model].vchannels;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001545
1546 for (i = 0; i < TVNORMS; i++)
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001547 if (em28xx_boards[model].norm == tvnorms[i].mode)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001548 break;
1549 if (i == TVNORMS)
1550 i = 0;
1551
1552 dev->tvnorm = &tvnorms[i]; /* set default norm */
1553
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001554 em28xx_videodbg("tvnorm=%s\n", dev->tvnorm->name);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001555
1556 maxw = norm_maxw(dev);
1557 maxh = norm_maxh(dev);
1558
1559 /* set default image size */
1560 dev->width = maxw;
1561 dev->height = maxh;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001562 dev->interlaced = EM28XX_INTERLACED_DEFAULT;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001563 dev->field_size = dev->width * dev->height;
1564 dev->frame_size =
1565 dev->interlaced ? dev->field_size << 1 : dev->field_size;
1566 dev->bytesperline = dev->width * 2;
1567 dev->hscale = 0;
1568 dev->vscale = 0;
1569 dev->ctl_input = 2;
1570
1571 /* setup video picture settings for saa7113h */
1572 memset(&dev->vpic, 0, sizeof(dev->vpic));
1573 dev->vpic.colour = 128 << 8;
1574 dev->vpic.hue = 128 << 8;
1575 dev->vpic.brightness = 128 << 8;
1576 dev->vpic.contrast = 192 << 8;
1577 dev->vpic.whiteness = 128 << 8; /* This one isn't used */
1578 dev->vpic.depth = 16;
1579 dev->vpic.palette = VIDEO_PALETTE_YUV422;
1580
Markus Rechbergera94e95b2006-01-23 17:11:10 -02001581 em28xx_pre_card_setup(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001582#ifdef CONFIG_MODULES
1583 /* request some modules */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001584 if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
Mauro Carvalho Chehabf5762e42006-03-13 13:31:31 -03001585 request_module("saa7115");
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001586 if (dev->decoder == EM28XX_TVP5150)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001587 request_module("tvp5150");
1588 if (dev->has_tuner)
1589 request_module("tuner");
1590 if (dev->tda9887_conf)
1591 request_module("tda9887");
1592#endif
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001593 errCode = em28xx_config(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001594 if (errCode) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001595 em28xx_errdev("error configuring device\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001596 kfree(dev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001597 em28xx_devused&=~(1<<dev->devno);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001598 return -ENOMEM;
1599 }
1600
Ingo Molnar3593cab2006-02-07 06:49:14 -02001601 mutex_lock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001602 /* register i2c bus */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001603 em28xx_i2c_register(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001604
1605 /* Do board specific init and eeprom reading */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001606 em28xx_card_setup(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001607
1608 /* configure the device */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001609 em28xx_config_i2c(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001610
Ingo Molnar3593cab2006-02-07 06:49:14 -02001611 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001612
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001613 errCode = em28xx_config(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001614
1615#ifdef CONFIG_MODULES
1616 if (dev->has_msp34xx)
1617 request_module("msp3400");
1618#endif
1619 /* allocate and fill v4l2 device struct */
1620 dev->vdev = video_device_alloc();
1621 if (NULL == dev->vdev) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001622 em28xx_errdev("cannot allocate video_device.\n");
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001623 kfree(dev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001624 em28xx_devused&=~(1<<dev->devno);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001625 return -ENOMEM;
1626 }
1627
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001628 dev->vbi_dev = video_device_alloc();
1629 if (NULL == dev->vbi_dev) {
1630 em28xx_errdev("cannot allocate video_device.\n");
1631 kfree(dev->vdev);
1632 kfree(dev);
1633 em28xx_devused&=~(1<<dev->devno);
1634 return -ENOMEM;
1635 }
1636
1637 /* Fills VBI device info */
1638 dev->vbi_dev->type = VFL_TYPE_VBI;
1639 dev->vbi_dev->hardware = 0;
1640 dev->vbi_dev->fops = &em28xx_v4l_fops;
1641 dev->vbi_dev->minor = -1;
1642 dev->vbi_dev->dev = &dev->udev->dev;
1643 dev->vbi_dev->release = video_device_release;
1644 snprintf(dev->vbi_dev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
1645 "em28xx",dev->devno,"vbi");
1646
1647 /* Fills CAPTURE device info */
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001648 dev->vdev->type = VID_TYPE_CAPTURE;
1649 if (dev->has_tuner)
1650 dev->vdev->type |= VID_TYPE_TUNER;
1651 dev->vdev->hardware = 0;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001652 dev->vdev->fops = &em28xx_v4l_fops;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001653 dev->vdev->minor = -1;
1654 dev->vdev->dev = &dev->udev->dev;
1655 dev->vdev->release = video_device_release;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001656 snprintf(dev->vdev->name, sizeof(dev->vbi_dev->name), "%s#%d %s",
1657 "em28xx",dev->devno,"video");
1658
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001659 list_add_tail(&dev->devlist,&em28xx_devlist);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001660
1661 /* register v4l2 device */
Ingo Molnar3593cab2006-02-07 06:49:14 -02001662 mutex_lock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001663 if ((retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
1664 video_nr[dev->devno]))) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001665 em28xx_errdev("unable to register video device (error=%i).\n",
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001666 retval);
Ingo Molnar3593cab2006-02-07 06:49:14 -02001667 mutex_unlock(&dev->lock);
Markus Rechberger9c755412005-11-08 21:37:52 -08001668 list_del(&dev->devlist);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001669 video_device_release(dev->vdev);
1670 kfree(dev);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001671 em28xx_devused&=~(1<<dev->devno);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001672 return -ENODEV;
1673 }
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001674
1675 if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
1676 vbi_nr[dev->devno]) < 0) {
1677 printk("unable to register vbi device\n");
Ingo Molnar3593cab2006-02-07 06:49:14 -02001678 mutex_unlock(&dev->lock);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001679 list_del(&dev->devlist);
1680 video_device_release(dev->vbi_dev);
1681 video_device_release(dev->vdev);
1682 kfree(dev);
1683 em28xx_devused&=~(1<<dev->devno);
1684 return -ENODEV;
1685 } else {
1686 printk("registered VBI\n");
1687 }
1688
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001689 if (dev->has_msp34xx) {
1690 /* Send a reset to other chips via gpio */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001691 em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001692 udelay(2500);
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001693 em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001694 udelay(2500);
1695
1696 }
1697 video_mux(dev, 0);
1698
Ingo Molnar3593cab2006-02-07 06:49:14 -02001699 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001700
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001701 em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
1702 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
1703 dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001704
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001705 return 0;
1706}
1707
1708/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001709 * em28xx_usb_probe()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001710 * checks for supported devices
1711 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001712static int em28xx_usb_probe(struct usb_interface *interface,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001713 const struct usb_device_id *id)
1714{
1715 const struct usb_endpoint_descriptor *endpoint;
1716 struct usb_device *udev;
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001717 struct usb_interface *uif;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001718 struct em28xx *dev = NULL;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001719 int retval = -ENODEV;
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001720 int model,i,nr,ifnum;
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001721
1722 udev = usb_get_dev(interface_to_usbdev(interface));
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001723 ifnum = interface->altsetting[0].desc.bInterfaceNumber;
1724
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001725 /* Check to see next free device and mark as used */
1726 nr=find_first_zero_bit(&em28xx_devused,EM28XX_MAXBOARDS);
1727 em28xx_devused|=1<<nr;
Mauro Carvalho Chehab91cad0f2005-11-08 21:38:13 -08001728
1729 /* Don't register audio interfaces */
1730 if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001731 em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n",
Mauro Carvalho Chehab91cad0f2005-11-08 21:38:13 -08001732 udev->descriptor.idVendor,udev->descriptor.idProduct,
1733 ifnum,
1734 interface->altsetting[0].desc.bInterfaceClass);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001735
1736 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab91cad0f2005-11-08 21:38:13 -08001737 return -ENODEV;
1738 }
1739
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001740 em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n",
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001741 udev->descriptor.idVendor,udev->descriptor.idProduct,
1742 ifnum,
1743 interface->altsetting[0].desc.bInterfaceClass);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001744
Mauro Carvalho Chehabd5e52652005-11-08 21:37:32 -08001745 endpoint = &interface->cur_altsetting->endpoint[1].desc;
1746
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001747 /* check if the the device has the iso in endpoint at the correct place */
1748 if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
1749 USB_ENDPOINT_XFER_ISOC) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001750 em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001751 em28xx_devused&=~(1<<nr);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001752 return -ENODEV;
1753 }
1754 if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001755 em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001756 em28xx_devused&=~(1<<nr);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001757 return -ENODEV;
1758 }
1759
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001760 model=id->driver_info;
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001761
Mauro Carvalho Chehab19478842006-03-14 17:24:57 -03001762 if (nr >= EM28XX_MAXBOARDS) {
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001763 printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001764 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001765 return -ENOMEM;
1766 }
1767
1768 /* allocate memory for our device state and initialize it */
Panagiotis Issaris74081872006-01-11 19:40:56 -02001769 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001770 if (dev == NULL) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001771 em28xx_err(DRIVER_NAME ": out of memory!\n");
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001772 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001773 return -ENOMEM;
1774 }
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001775
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001776 snprintf(dev->name, 29, "em28xx #%d", nr);
1777 dev->devno=nr;
1778
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001779 /* compute alternate max packet sizes */
1780 uif = udev->actconfig->interface[0];
1781
1782 dev->num_alt=uif->num_altsetting;
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001783 em28xx_info("Alternate settings: %i\n",dev->num_alt);
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001784// dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)*
1785 dev->alt_max_pkt_size = kmalloc(32*
1786 dev->num_alt,GFP_KERNEL);
1787 if (dev->alt_max_pkt_size == NULL) {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001788 em28xx_errdev("out of memory!\n");
1789 em28xx_devused&=~(1<<nr);
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001790 return -ENOMEM;
1791 }
1792
1793 for (i = 0; i < dev->num_alt ; i++) {
1794 u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
1795 wMaxPacketSize);
1796 dev->alt_max_pkt_size[i] =
1797 (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001798 em28xx_info("Alternate setting %i, max size= %i\n",i,
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001799 dev->alt_max_pkt_size[i]);
1800 }
1801
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001802 if ((card[nr]>=0)&&(card[nr]<em28xx_bcount))
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001803 model=card[nr];
1804
1805 if ((model==EM2800_BOARD_UNKNOWN)||(model==EM2820_BOARD_UNKNOWN)) {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001806 em28xx_errdev( "Your board has no eeprom inside it and thus can't\n"
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001807 "%s: be autodetected. Please pass card=<n> insmod option to\n"
1808 "%s: workaround that. Redirect complaints to the vendor of\n"
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001809 "%s: the TV card. Generic type will be used."
1810 "%s: Best regards,\n"
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001811 "%s: -- tux\n",
1812 dev->name,dev->name,dev->name,dev->name,dev->name);
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001813 em28xx_errdev("%s: Here is a list of valid choices for the card=<n> insmod option:\n",
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001814 dev->name);
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001815 for (i = 0; i < em28xx_bcount; i++) {
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001816 em28xx_errdev(" card=%d -> %s\n", i,
1817 em28xx_boards[i].name);
Mauro Carvalho Chehab596d92d2005-11-08 21:37:24 -08001818 }
1819 }
1820
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001821 /* allocate device struct */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001822 retval = em28xx_init_dev(&dev, udev, nr, model);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001823 if (retval)
1824 return retval;
1825
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001826 em28xx_info("Found %s\n", em28xx_boards[model].name);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001827
1828 /* save our data pointer in this interface device */
1829 usb_set_intfdata(interface, dev);
1830 return 0;
1831}
1832
1833/*
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001834 * em28xx_usb_disconnect()
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001835 * called when the device gets diconencted
1836 * video device will be unregistered on v4l2_close in case it is still open
1837 */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001838static void em28xx_usb_disconnect(struct usb_interface *interface)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001839{
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001840 struct em28xx *dev = usb_get_intfdata(interface);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001841 usb_set_intfdata(interface, NULL);
1842
1843 if (!dev)
1844 return;
1845
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001846 down_write(&em28xx_disconnect);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001847
Ingo Molnar3593cab2006-02-07 06:49:14 -02001848 mutex_lock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001849
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001850 em28xx_info("disconnecting %s\n", dev->vdev->name);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001851
1852 wake_up_interruptible_all(&dev->open);
1853
1854 if (dev->users) {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001855 em28xx_warn
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001856 ("device /dev/video%d is open! Deregistration and memory "
Mauro Carvalho Chehabe5589be2006-01-23 17:11:08 -02001857 "deallocation are deferred on close.\n",
1858 dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
1859
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001860 dev->state |= DEV_MISCONFIGURED;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001861 em28xx_uninit_isoc(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001862 dev->state |= DEV_DISCONNECTED;
1863 wake_up_interruptible(&dev->wait_frame);
1864 wake_up_interruptible(&dev->wait_stream);
1865 } else {
1866 dev->state |= DEV_DISCONNECTED;
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001867 em28xx_release_resources(dev);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001868 }
1869
Ingo Molnar3593cab2006-02-07 06:49:14 -02001870 mutex_unlock(&dev->lock);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001871
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001872 if (!dev->users) {
1873 kfree(dev->alt_max_pkt_size);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001874 kfree(dev);
Mauro Carvalho Chehab9d4d9c02005-11-08 21:38:52 -08001875 }
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001876
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001877 up_write(&em28xx_disconnect);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001878}
1879
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001880static struct usb_driver em28xx_usb_driver = {
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001881 .name = "em28xx",
1882 .probe = em28xx_usb_probe,
1883 .disconnect = em28xx_usb_disconnect,
1884 .id_table = em28xx_id_table,
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001885};
1886
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001887static int __init em28xx_module_init(void)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001888{
1889 int result;
1890
1891 printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n",
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001892 (EM28XX_VERSION_CODE >> 16) & 0xff,
1893 (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001894#ifdef SNAPSHOT
1895 printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n",
1896 SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);
1897#endif
1898
1899 /* register this driver with the USB subsystem */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001900 result = usb_register(&em28xx_usb_driver);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001901 if (result)
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001902 em28xx_err(DRIVER_NAME
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001903 " usb_register failed. Error number %d.\n", result);
1904
1905 return result;
1906}
1907
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001908static void __exit em28xx_module_exit(void)
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001909{
1910 /* deregister this driver with the USB subsystem */
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001911 usb_deregister(&em28xx_usb_driver);
akpm@osdl.orga6c2ba22005-11-08 21:37:07 -08001912}
1913
Mauro Carvalho Chehab3acf2802005-11-08 21:38:27 -08001914module_init(em28xx_module_init);
1915module_exit(em28xx_module_exit);