| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Endpoints (formerly known as AOX) se401 USB Camera Driver | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) | 
 | 5 |  * | 
 | 6 |  * Still somewhat based on the Linux ov511 driver. | 
 | 7 |  *  | 
 | 8 |  * This program is free software; you can redistribute it and/or modify it | 
 | 9 |  * under the terms of the GNU General Public License as published by the | 
 | 10 |  * Free Software Foundation; either version 2 of the License, or (at your | 
 | 11 |  * option) any later version. | 
 | 12 |  * | 
 | 13 |  * This program is distributed in the hope that it will be useful, but | 
 | 14 |  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | 
 | 15 |  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
 | 16 |  * for more details. | 
 | 17 |  * | 
 | 18 |  * You should have received a copy of the GNU General Public License | 
 | 19 |  * along with this program; if not, write to the Free Software Foundation, | 
 | 20 |  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
 | 21 |  * | 
 | 22 |  * | 
 | 23 |  * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on | 
 | 24 |  * their chipset available and supporting me while writing this driver. | 
 | 25 |  * 	- Jeroen Vreeken | 
 | 26 |  */ | 
 | 27 |  | 
 | 28 | static const char version[] = "0.24"; | 
 | 29 |  | 
 | 30 | #include <linux/config.h> | 
 | 31 | #include <linux/module.h> | 
 | 32 | #include <linux/init.h> | 
 | 33 | #include <linux/vmalloc.h> | 
 | 34 | #include <linux/slab.h> | 
 | 35 | #include <linux/pagemap.h> | 
 | 36 | #include <linux/usb.h> | 
 | 37 | #include "se401.h" | 
 | 38 |  | 
 | 39 | static int flickerless=0; | 
 | 40 | static int video_nr = -1; | 
 | 41 |  | 
 | 42 | static struct usb_device_id device_table [] = { | 
 | 43 | 	{ USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ | 
 | 44 | 	{ USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ | 
 | 45 | 	{ USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ | 
 | 46 | 	{ USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ | 
 | 47 | 	{ USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ | 
 | 48 | 	{ } | 
 | 49 | }; | 
 | 50 |  | 
 | 51 | MODULE_DEVICE_TABLE(usb, device_table); | 
 | 52 |  | 
 | 53 | MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>"); | 
 | 54 | MODULE_DESCRIPTION("SE401 USB Camera Driver"); | 
 | 55 | MODULE_LICENSE("GPL"); | 
 | 56 | module_param(flickerless, int, 0); | 
 | 57 | MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)"); | 
 | 58 | module_param(video_nr, int, 0); | 
 | 59 |  | 
 | 60 | static struct usb_driver se401_driver; | 
 | 61 |  | 
 | 62 |  | 
 | 63 | /********************************************************************** | 
 | 64 |  * | 
 | 65 |  * Memory management | 
 | 66 |  * | 
 | 67 |  **********************************************************************/ | 
 | 68 | static void *rvmalloc(unsigned long size) | 
 | 69 | { | 
 | 70 | 	void *mem; | 
 | 71 | 	unsigned long adr; | 
 | 72 |  | 
 | 73 | 	size = PAGE_ALIGN(size); | 
 | 74 | 	mem = vmalloc_32(size); | 
 | 75 | 	if (!mem) | 
 | 76 | 		return NULL; | 
 | 77 |  | 
 | 78 | 	memset(mem, 0, size); /* Clear the ram out, no junk to the user */ | 
 | 79 | 	adr = (unsigned long) mem; | 
 | 80 | 	while (size > 0) { | 
 | 81 | 		SetPageReserved(vmalloc_to_page((void *)adr)); | 
 | 82 | 		adr += PAGE_SIZE; | 
 | 83 | 		size -= PAGE_SIZE; | 
 | 84 | 	} | 
 | 85 |  | 
 | 86 | 	return mem; | 
 | 87 | } | 
 | 88 |  | 
 | 89 | static void rvfree(void *mem, unsigned long size) | 
 | 90 | { | 
 | 91 | 	unsigned long adr; | 
 | 92 |  | 
 | 93 | 	if (!mem) | 
 | 94 | 		return; | 
 | 95 |  | 
 | 96 | 	adr = (unsigned long) mem; | 
 | 97 | 	while ((long) size > 0) { | 
 | 98 | 		ClearPageReserved(vmalloc_to_page((void *)adr)); | 
 | 99 | 		adr += PAGE_SIZE; | 
 | 100 | 		size -= PAGE_SIZE; | 
 | 101 | 	} | 
 | 102 | 	vfree(mem); | 
 | 103 | } | 
 | 104 |  | 
 | 105 |  | 
 | 106 |  | 
 | 107 | /**************************************************************************** | 
 | 108 |  * | 
 | 109 |  * se401 register read/write functions | 
 | 110 |  * | 
 | 111 |  ***************************************************************************/ | 
 | 112 |  | 
 | 113 | static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, | 
 | 114 | 			 unsigned short value, unsigned char *cp, int size) | 
 | 115 | { | 
 | 116 | 	return usb_control_msg ( | 
 | 117 |                 se401->dev, | 
 | 118 |                 set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), | 
 | 119 |                 req, | 
 | 120 |                 (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | 
 | 121 |                 value, | 
 | 122 |                 0, | 
 | 123 |                 cp, | 
 | 124 |                 size, | 
 | 125 |                 1000 | 
 | 126 |         ); | 
 | 127 | } | 
 | 128 |  | 
 | 129 | static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, | 
 | 130 | 			     unsigned short param) | 
 | 131 | { | 
 | 132 | 	/* specs say that the selector (address) should go in the value field | 
 | 133 | 	   and the param in index, but in the logs of the windows driver they do | 
 | 134 | 	   this the other way around... | 
 | 135 | 	 */ | 
 | 136 | 	return usb_control_msg ( | 
 | 137 | 		se401->dev, | 
 | 138 | 		usb_sndctrlpipe(se401->dev, 0), | 
 | 139 | 		SE401_REQ_SET_EXT_FEATURE, | 
 | 140 | 		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | 
 | 141 | 		param, | 
 | 142 | 		selector, | 
 | 143 |                 NULL, | 
 | 144 |                 0, | 
 | 145 |                 1000 | 
 | 146 |         ); | 
 | 147 | } | 
 | 148 |  | 
 | 149 | static unsigned short se401_get_feature(struct usb_se401 *se401,  | 
 | 150 | 				        unsigned short selector) | 
 | 151 | { | 
 | 152 | 	/* For 'set' the selecetor should be in index, not sure if the spec is | 
 | 153 | 	   wrong here to.... | 
 | 154 | 	 */ | 
 | 155 | 	unsigned char cp[2]; | 
 | 156 |         usb_control_msg ( | 
 | 157 |                 se401->dev, | 
 | 158 |                 usb_rcvctrlpipe(se401->dev, 0), | 
 | 159 |                 SE401_REQ_GET_EXT_FEATURE, | 
 | 160 |                 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | 
 | 161 |         	0, | 
 | 162 |                 selector, | 
 | 163 |                 cp, | 
 | 164 |                 2, | 
 | 165 |                 1000 | 
 | 166 |         ); | 
 | 167 | 	return cp[0]+cp[1]*256; | 
 | 168 | } | 
 | 169 |  | 
 | 170 | /**************************************************************************** | 
 | 171 |  * | 
 | 172 |  * Camera control | 
 | 173 |  * | 
 | 174 |  ***************************************************************************/ | 
 | 175 |  | 
 | 176 |  | 
 | 177 | static int se401_send_pict(struct usb_se401 *se401) | 
 | 178 | { | 
 | 179 | 	se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */ | 
 | 180 | 	se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */ | 
 | 181 | 	se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */ | 
 | 182 | 	se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */ | 
 | 183 | 	se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */ | 
 | 184 | 	se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */ | 
 | 185 | 	se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */ | 
 | 186 | 	    	 | 
 | 187 | 	return 0; | 
 | 188 | } | 
 | 189 |  | 
 | 190 | static void se401_set_exposure(struct usb_se401 *se401, int brightness) | 
 | 191 | { | 
 | 192 | 	int integration=brightness<<5; | 
 | 193 | 	 | 
 | 194 | 	if (flickerless==50) { | 
 | 195 | 		integration=integration-integration%106667; | 
 | 196 | 	} | 
 | 197 | 	if (flickerless==60) { | 
 | 198 | 		integration=integration-integration%88889; | 
 | 199 | 	} | 
 | 200 | 	se401->brightness=integration>>5; | 
 | 201 | 	se401->expose_h=(integration>>16)&0xff; | 
 | 202 | 	se401->expose_m=(integration>>8)&0xff; | 
 | 203 | 	se401->expose_l=integration&0xff; | 
 | 204 | } | 
 | 205 |  | 
 | 206 | static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) | 
 | 207 | { | 
 | 208 | 	p->brightness=se401->brightness; | 
 | 209 | 	if (se401->enhance) { | 
 | 210 | 		p->whiteness=32768; | 
 | 211 | 	} else { | 
 | 212 | 		p->whiteness=0; | 
 | 213 | 	} | 
 | 214 | 	p->colour=65535; | 
 | 215 | 	p->contrast=65535; | 
 | 216 | 	p->hue=se401->rgain<<10; | 
 | 217 | 	p->palette=se401->palette; | 
 | 218 | 	p->depth=3; /* rgb24 */ | 
 | 219 | 	return 0; | 
 | 220 | } | 
 | 221 |  | 
 | 222 |  | 
 | 223 | static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) | 
 | 224 | { | 
 | 225 | 	if (p->palette != VIDEO_PALETTE_RGB24) | 
 | 226 | 		return 1; | 
 | 227 | 	se401->palette=p->palette; | 
 | 228 | 	if (p->hue!=se401->hue) { | 
 | 229 | 		se401->rgain= p->hue>>10; | 
 | 230 | 		se401->bgain= 0x40-(p->hue>>10); | 
 | 231 | 		se401->hue=p->hue; | 
 | 232 | 	} | 
 | 233 | 	if (p->brightness!=se401->brightness) { | 
 | 234 | 		se401_set_exposure(se401, p->brightness); | 
 | 235 | 	} | 
 | 236 | 	if (p->whiteness>=32768) { | 
 | 237 | 		se401->enhance=1; | 
 | 238 | 	} else { | 
 | 239 | 		se401->enhance=0; | 
 | 240 | 	} | 
 | 241 | 	se401_send_pict(se401); | 
 | 242 | 	se401_send_pict(se401); | 
 | 243 | 	return 0; | 
 | 244 | } | 
 | 245 |  | 
 | 246 | /* | 
 | 247 | 	Hyundai have some really nice docs about this and other sensor related | 
 | 248 | 	stuff on their homepage: www.hei.co.kr | 
 | 249 | */ | 
 | 250 | static void se401_auto_resetlevel(struct usb_se401 *se401) | 
 | 251 | { | 
 | 252 | 	unsigned int ahrc, alrc; | 
 | 253 | 	int oldreset=se401->resetlevel; | 
 | 254 |  | 
 | 255 | 	/* For some reason this normally read-only register doesn't get reset | 
 | 256 | 	   to zero after reading them just once... | 
 | 257 | 	 */ | 
 | 258 | 	se401_get_feature(se401, HV7131_REG_HIREFNOH);  | 
 | 259 | 	se401_get_feature(se401, HV7131_REG_HIREFNOL); | 
 | 260 | 	se401_get_feature(se401, HV7131_REG_LOREFNOH); | 
 | 261 | 	se401_get_feature(se401, HV7131_REG_LOREFNOL); | 
 | 262 | 	ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +  | 
 | 263 | 	    se401_get_feature(se401, HV7131_REG_HIREFNOL); | 
 | 264 | 	alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + | 
 | 265 | 	    se401_get_feature(se401, HV7131_REG_LOREFNOL); | 
 | 266 |  | 
 | 267 | 	/* Not an exact science, but it seems to work pretty well... */ | 
 | 268 | 	if (alrc > 10) { | 
 | 269 | 		while (alrc>=10 && se401->resetlevel < 63) { | 
 | 270 | 			se401->resetlevel++; | 
 | 271 | 			alrc /=2; | 
 | 272 | 		} | 
 | 273 | 	} else if (ahrc > 20) { | 
 | 274 | 		while (ahrc>=20 && se401->resetlevel > 0) { | 
 | 275 | 			se401->resetlevel--; | 
 | 276 | 			ahrc /=2; | 
 | 277 | 		} | 
 | 278 | 	} | 
 | 279 | 	if (se401->resetlevel!=oldreset) | 
 | 280 | 		se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); | 
 | 281 |  | 
 | 282 | 	return; | 
 | 283 | } | 
 | 284 |  | 
 | 285 | /* irq handler for snapshot button */ | 
 | 286 | static void se401_button_irq(struct urb *urb, struct pt_regs *regs) | 
 | 287 | { | 
 | 288 | 	struct usb_se401 *se401 = urb->context; | 
 | 289 | 	int status; | 
 | 290 | 	 | 
 | 291 | 	if (!se401->dev) { | 
 | 292 | 		info("ohoh: device vapourished"); | 
 | 293 | 		return; | 
 | 294 | 	} | 
 | 295 | 	 | 
 | 296 | 	switch (urb->status) { | 
 | 297 | 	case 0: | 
 | 298 | 		/* success */ | 
 | 299 | 		break; | 
 | 300 | 	case -ECONNRESET: | 
 | 301 | 	case -ENOENT: | 
 | 302 | 	case -ESHUTDOWN: | 
 | 303 | 		/* this urb is terminated, clean up */ | 
 | 304 | 		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); | 
 | 305 | 		return; | 
 | 306 | 	default: | 
 | 307 | 		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); | 
 | 308 | 		goto exit; | 
 | 309 | 	} | 
 | 310 |  | 
 | 311 | 	if (urb->actual_length >=2) { | 
 | 312 | 		if (se401->button) | 
 | 313 | 			se401->buttonpressed=1; | 
 | 314 | 	} | 
 | 315 | exit: | 
 | 316 | 	status = usb_submit_urb (urb, GFP_ATOMIC); | 
 | 317 | 	if (status) | 
 | 318 | 		err ("%s - usb_submit_urb failed with result %d", | 
 | 319 | 		     __FUNCTION__, status); | 
 | 320 | } | 
 | 321 |  | 
 | 322 | static void se401_video_irq(struct urb *urb, struct pt_regs *regs) | 
 | 323 | { | 
 | 324 | 	struct usb_se401 *se401 = urb->context; | 
 | 325 | 	int length = urb->actual_length; | 
 | 326 |  | 
 | 327 | 	/* ohoh... */ | 
 | 328 | 	if (!se401->streaming) | 
 | 329 | 		return; | 
 | 330 |  | 
 | 331 | 	if (!se401->dev) { | 
 | 332 | 		info ("ohoh: device vapourished"); | 
 | 333 | 		return; | 
 | 334 | 	} | 
 | 335 |  | 
 | 336 | 	/* 0 sized packets happen if we are to fast, but sometimes the camera | 
 | 337 | 	   keeps sending them forever... | 
 | 338 | 	 */ | 
 | 339 | 	if (length && !urb->status) { | 
 | 340 | 		se401->nullpackets=0; | 
 | 341 | 		switch(se401->scratch[se401->scratch_next].state) { | 
 | 342 | 			case BUFFER_READY: | 
 | 343 | 			case BUFFER_BUSY: { | 
 | 344 | 				se401->dropped++; | 
 | 345 | 				break; | 
 | 346 | 			} | 
 | 347 | 			case BUFFER_UNUSED: { | 
 | 348 | 				memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length); | 
 | 349 | 				se401->scratch[se401->scratch_next].state=BUFFER_READY; | 
 | 350 | 				se401->scratch[se401->scratch_next].offset=se401->bayeroffset; | 
 | 351 | 				se401->scratch[se401->scratch_next].length=length; | 
 | 352 | 				if (waitqueue_active(&se401->wq)) { | 
 | 353 | 					wake_up_interruptible(&se401->wq); | 
 | 354 | 				} | 
 | 355 | 				se401->scratch_overflow=0; | 
 | 356 | 				se401->scratch_next++; | 
 | 357 | 				if (se401->scratch_next>=SE401_NUMSCRATCH) | 
 | 358 | 					se401->scratch_next=0; | 
 | 359 | 				break; | 
 | 360 | 			} | 
 | 361 | 		} | 
 | 362 | 		se401->bayeroffset+=length; | 
 | 363 | 		if (se401->bayeroffset>=se401->cheight*se401->cwidth) { | 
 | 364 | 			se401->bayeroffset=0; | 
 | 365 | 		} | 
 | 366 | 	} else { | 
 | 367 | 		se401->nullpackets++; | 
 | 368 | 		if (se401->nullpackets > SE401_MAX_NULLPACKETS) { | 
 | 369 | 			if (waitqueue_active(&se401->wq)) { | 
 | 370 | 				wake_up_interruptible(&se401->wq); | 
 | 371 | 			}		 | 
 | 372 | 		} | 
 | 373 | 	} | 
 | 374 |  | 
 | 375 | 	/* Resubmit urb for new data */ | 
 | 376 | 	urb->status=0; | 
 | 377 | 	urb->dev=se401->dev; | 
 | 378 | 	if(usb_submit_urb(urb, GFP_KERNEL)) | 
 | 379 | 		info("urb burned down"); | 
 | 380 | 	return; | 
 | 381 | } | 
 | 382 |  | 
 | 383 | static void se401_send_size(struct usb_se401 *se401, int width, int height) | 
 | 384 | { | 
 | 385 | 	int i=0; | 
 | 386 | 	int mode=0x03; /* No compression */ | 
 | 387 | 	int sendheight=height; | 
 | 388 | 	int sendwidth=width; | 
 | 389 |  | 
 | 390 | 	/* JangGu compression can only be used with the camera supported sizes, | 
 | 391 | 	   but bayer seems to work with any size that fits on the sensor. | 
 | 392 | 	   We check if we can use compression with the current size with either | 
 | 393 | 	   4 or 16 times subcapturing, if not we use uncompressed bayer data | 
 | 394 | 	   but this will result in cutouts of the maximum size.... | 
 | 395 | 	 */ | 
 | 396 | 	while (i<se401->sizes && !(se401->width[i]==width && se401->height[i]==height)) | 
 | 397 | 		i++; | 
 | 398 | 	while (i<se401->sizes) { | 
 | 399 | 		if (se401->width[i]==width*2 && se401->height[i]==height*2) { | 
 | 400 | 			sendheight=se401->height[i]; | 
 | 401 | 			sendwidth=se401->width[i]; | 
 | 402 | 			mode=0x40; | 
 | 403 | 		} | 
 | 404 | 		if (se401->width[i]==width*4 && se401->height[i]==height*4) { | 
 | 405 | 			sendheight=se401->height[i]; | 
 | 406 | 			sendwidth=se401->width[i]; | 
 | 407 | 			mode=0x42; | 
 | 408 | 		} | 
 | 409 | 		i++; | 
 | 410 | 	} | 
 | 411 |  | 
 | 412 | 	se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); | 
 | 413 | 	se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); | 
 | 414 | 	se401_set_feature(se401, SE401_OPERATINGMODE, mode); | 
 | 415 |  | 
 | 416 | 	if (mode==0x03) { | 
 | 417 | 		se401->format=FMT_BAYER; | 
 | 418 | 	} else { | 
 | 419 | 		se401->format=FMT_JANGGU; | 
 | 420 | 	} | 
 | 421 |  | 
 | 422 | 	return; | 
 | 423 | } | 
 | 424 |  | 
 | 425 | /* | 
 | 426 | 	In this function se401_send_pict is called several times, | 
 | 427 | 	for some reason (depending on the state of the sensor and the phase of | 
 | 428 | 	the moon :) doing this only in either place doesn't always work... | 
 | 429 | */ | 
 | 430 | static int se401_start_stream(struct usb_se401 *se401) | 
 | 431 | { | 
 | 432 | 	struct urb *urb; | 
 | 433 | 	int err=0, i; | 
 | 434 | 	se401->streaming=1; | 
 | 435 |  | 
 | 436 |         se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); | 
 | 437 |         se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); | 
 | 438 |  | 
 | 439 | 	/* Set picture settings */ | 
 | 440 | 	se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */ | 
 | 441 | 	se401_send_pict(se401); | 
 | 442 |  | 
 | 443 | 	se401_send_size(se401, se401->cwidth, se401->cheight); | 
 | 444 |  | 
 | 445 | 	se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0); | 
 | 446 |  | 
 | 447 | 	/* Do some memory allocation */ | 
 | 448 | 	for (i=0; i<SE401_NUMFRAMES; i++) { | 
 | 449 | 		se401->frame[i].data=se401->fbuf + i * se401->maxframesize; | 
 | 450 | 		se401->frame[i].curpix=0; | 
 | 451 | 	} | 
 | 452 | 	for (i=0; i<SE401_NUMSBUF; i++) { | 
 | 453 | 		se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); | 
 | 454 | 	} | 
 | 455 |  | 
 | 456 | 	se401->bayeroffset=0; | 
 | 457 | 	se401->scratch_next=0; | 
 | 458 | 	se401->scratch_use=0; | 
 | 459 | 	se401->scratch_overflow=0; | 
 | 460 | 	for (i=0; i<SE401_NUMSCRATCH; i++) { | 
 | 461 | 		se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); | 
 | 462 | 		se401->scratch[i].state=BUFFER_UNUSED; | 
 | 463 | 	} | 
 | 464 |  | 
 | 465 | 	for (i=0; i<SE401_NUMSBUF; i++) { | 
 | 466 | 		urb=usb_alloc_urb(0, GFP_KERNEL); | 
 | 467 | 		if(!urb) | 
 | 468 | 			return -ENOMEM; | 
 | 469 |  | 
 | 470 | 		usb_fill_bulk_urb(urb, se401->dev, | 
 | 471 | 			usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), | 
 | 472 | 			se401->sbuf[i].data, SE401_PACKETSIZE, | 
 | 473 | 			se401_video_irq, | 
 | 474 | 			se401); | 
 | 475 |  | 
 | 476 | 		se401->urb[i]=urb; | 
 | 477 |  | 
 | 478 | 		err=usb_submit_urb(se401->urb[i], GFP_KERNEL); | 
 | 479 | 		if(err) | 
 | 480 | 			err("urb burned down"); | 
 | 481 | 	} | 
 | 482 |  | 
 | 483 | 	se401->framecount=0; | 
 | 484 |  | 
 | 485 | 	return 0; | 
 | 486 | } | 
 | 487 |  | 
 | 488 | static int se401_stop_stream(struct usb_se401 *se401) | 
 | 489 | { | 
 | 490 | 	int i; | 
 | 491 |  | 
 | 492 | 	if (!se401->streaming || !se401->dev) | 
 | 493 | 		return 1; | 
 | 494 |  | 
 | 495 | 	se401->streaming=0; | 
 | 496 |  | 
 | 497 | 	se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); | 
 | 498 |  | 
 | 499 | 	se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); | 
 | 500 | 	se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); | 
 | 501 |  | 
 | 502 | 	for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) { | 
 | 503 | 		usb_kill_urb(se401->urb[i]); | 
 | 504 | 		usb_free_urb(se401->urb[i]); | 
 | 505 | 		se401->urb[i]=NULL; | 
 | 506 | 		kfree(se401->sbuf[i].data); | 
 | 507 | 	} | 
 | 508 | 	for (i=0; i<SE401_NUMSCRATCH; i++) { | 
 | 509 | 		kfree(se401->scratch[i].data); | 
 | 510 | 		se401->scratch[i].data=NULL; | 
 | 511 | 	} | 
 | 512 |  | 
 | 513 | 	return 0; | 
 | 514 | } | 
 | 515 |  | 
 | 516 | static int se401_set_size(struct usb_se401 *se401, int width, int height) | 
 | 517 | { | 
 | 518 | 	int wasstreaming=se401->streaming; | 
 | 519 | 	/* Check to see if we need to change */ | 
 | 520 | 	if (se401->cwidth==width && se401->cheight==height) | 
 | 521 | 		return 0; | 
 | 522 |  | 
 | 523 | 	/* Check for a valid mode */ | 
 | 524 | 	if (!width || !height) | 
 | 525 | 		return 1; | 
 | 526 | 	if ((width & 1) || (height & 1)) | 
 | 527 | 		return 1; | 
 | 528 | 	if (width>se401->width[se401->sizes-1]) | 
 | 529 | 		return 1; | 
 | 530 | 	if (height>se401->height[se401->sizes-1]) | 
 | 531 | 		return 1; | 
 | 532 |  | 
 | 533 | 	/* Stop a current stream and start it again at the new size */ | 
 | 534 | 	if (wasstreaming) | 
 | 535 | 		se401_stop_stream(se401); | 
 | 536 | 	se401->cwidth=width; | 
 | 537 | 	se401->cheight=height; | 
 | 538 | 	if (wasstreaming) | 
 | 539 | 		se401_start_stream(se401); | 
 | 540 | 	return 0; | 
 | 541 | } | 
 | 542 |  | 
 | 543 |  | 
 | 544 | /**************************************************************************** | 
 | 545 |  * | 
 | 546 |  * Video Decoding | 
 | 547 |  * | 
 | 548 |  ***************************************************************************/ | 
 | 549 |  | 
 | 550 | /* | 
 | 551 | 	This shouldn't really be done in a v4l driver.... | 
 | 552 | 	But it does make the image look a lot more usable. | 
 | 553 | 	Basically it lifts the dark pixels more than the light pixels. | 
 | 554 | */ | 
 | 555 | static inline void enhance_picture(unsigned char *frame, int len) | 
 | 556 | { | 
 | 557 | 	while (len--) { | 
 | 558 | 		*frame=(((*frame^255)*(*frame^255))/255)^255; | 
 | 559 | 		frame++; | 
 | 560 | 	} | 
 | 561 | } | 
 | 562 |  | 
 | 563 | static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) | 
 | 564 | { | 
 | 565 | 	struct se401_frame *frame=&se401->frame[se401->curframe]; | 
 | 566 | 	int linelength=se401->cwidth*3; | 
 | 567 |  | 
 | 568 | 	if (frame->curlinepix >= linelength) { | 
 | 569 | 		frame->curlinepix=0; | 
 | 570 | 		frame->curline+=linelength; | 
 | 571 | 	} | 
 | 572 |  | 
 | 573 | 	/* First three are absolute, all others relative. | 
 | 574 | 	 * Format is rgb from right to left (mirrorred image),  | 
 | 575 | 	 * we flip it to get bgr from left to right. */ | 
 | 576 | 	if (frame->curlinepix < 3) { | 
 | 577 | 		*(frame->curline-frame->curlinepix)=1+data*4; | 
 | 578 | 	} else { | 
 | 579 | 		*(frame->curline-frame->curlinepix)= | 
 | 580 | 		    *(frame->curline-frame->curlinepix+3)+data*4; | 
 | 581 | 	} | 
 | 582 | 	frame->curlinepix++; | 
 | 583 | } | 
 | 584 |  | 
 | 585 | static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength) | 
 | 586 | { | 
 | 587 | 	int pos=0; | 
 | 588 | 	int vlc_cod=0; | 
 | 589 | 	int vlc_size=0; | 
 | 590 | 	int vlc_data=0; | 
 | 591 | 	int bit_cur; | 
 | 592 | 	int bit; | 
 | 593 | 	data+=4; | 
 | 594 | 	while (pos < packetlength) { | 
 | 595 | 		bit_cur=8; | 
 | 596 | 		while (bit_cur && bit_exp) { | 
 | 597 | 			bit=((*data)>>(bit_cur-1))&1; | 
 | 598 | 			if (!vlc_cod) { | 
 | 599 | 				if (bit) { | 
 | 600 | 					vlc_size++; | 
 | 601 | 				} else { | 
 | 602 | 					if (!vlc_size) { | 
 | 603 | 						decode_JangGu_integrate(se401, 0); | 
 | 604 | 					} else { | 
 | 605 | 						vlc_cod=2; | 
 | 606 | 						vlc_data=0; | 
 | 607 | 					} | 
 | 608 | 				} | 
 | 609 | 			} else { | 
 | 610 | 				if (vlc_cod==2) { | 
 | 611 | 					if (!bit) | 
 | 612 | 						vlc_data =  -(1<<vlc_size) + 1; | 
 | 613 | 					vlc_cod--; | 
 | 614 | 				} | 
 | 615 | 				vlc_size--; | 
 | 616 | 				vlc_data+=bit<<vlc_size; | 
 | 617 | 				if (!vlc_size) { | 
 | 618 | 					decode_JangGu_integrate(se401, vlc_data); | 
 | 619 | 					vlc_cod=0; | 
 | 620 | 				} | 
 | 621 | 			} | 
 | 622 | 			bit_cur--; | 
 | 623 | 			bit_exp--; | 
 | 624 | 		} | 
 | 625 | 		pos++; | 
 | 626 | 		data++; | 
 | 627 | 	} | 
 | 628 | } | 
 | 629 |  | 
 | 630 | static inline void decode_JangGu (struct usb_se401 *se401, struct se401_scratch *buffer) | 
 | 631 | { | 
 | 632 | 	unsigned char *data=buffer->data; | 
 | 633 | 	int len=buffer->length; | 
 | 634 | 	int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size; | 
 | 635 | 	int datapos=0; | 
 | 636 |  | 
 | 637 | 	/* New image? */ | 
 | 638 | 	if (!se401->frame[se401->curframe].curpix) { | 
 | 639 | 		se401->frame[se401->curframe].curlinepix=0; | 
 | 640 | 		se401->frame[se401->curframe].curline= | 
 | 641 | 		    se401->frame[se401->curframe].data+ | 
 | 642 | 		    se401->cwidth*3-1; | 
 | 643 | 		if (se401->frame[se401->curframe].grabstate==FRAME_READY) | 
 | 644 | 			se401->frame[se401->curframe].grabstate=FRAME_GRABBING; | 
 | 645 | 		se401->vlcdatapos=0; | 
 | 646 | 	} | 
 | 647 | 	while (datapos < len) { | 
 | 648 | 		size=1024-se401->vlcdatapos; | 
 | 649 | 		if (size+datapos > len) | 
 | 650 | 			size=len-datapos; | 
 | 651 | 		memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); | 
 | 652 | 		se401->vlcdatapos+=size; | 
 | 653 | 		packetlength=0; | 
 | 654 | 		if (se401->vlcdatapos >= 4) { | 
 | 655 | 			bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8); | 
 | 656 | 			pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8); | 
 | 657 | 			frameinfo=se401->vlcdata[0]&0xc0; | 
 | 658 | 			packetlength=((bit_exp+47)>>4)<<1; | 
 | 659 | 			if (packetlength > 1024) { | 
 | 660 | 				se401->vlcdatapos=0; | 
 | 661 | 				datapos=len; | 
 | 662 | 				packetlength=0; | 
 | 663 | 				se401->error++; | 
 | 664 | 				se401->frame[se401->curframe].curpix=0; | 
 | 665 | 			} | 
 | 666 | 		} | 
 | 667 | 		if (packetlength && se401->vlcdatapos >= packetlength) { | 
 | 668 | 			decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength); | 
 | 669 | 			se401->frame[se401->curframe].curpix+=pix_exp*3; | 
 | 670 | 			datapos+=size-(se401->vlcdatapos-packetlength); | 
 | 671 | 			se401->vlcdatapos=0; | 
 | 672 | 			if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) { | 
 | 673 | 				if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) { | 
 | 674 | 					if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) { | 
 | 675 | 						se401->frame[se401->curframe].grabstate=FRAME_DONE; | 
 | 676 | 						se401->framecount++; | 
 | 677 | 						se401->readcount++; | 
 | 678 | 					} | 
 | 679 | 					if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { | 
 | 680 | 						se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); | 
 | 681 | 					} | 
 | 682 | 				} else { | 
 | 683 | 					se401->error++; | 
 | 684 | 				} | 
 | 685 | 				se401->frame[se401->curframe].curpix=0; | 
 | 686 | 				datapos=len; | 
 | 687 | 			} | 
 | 688 | 		} else { | 
 | 689 | 			datapos+=size; | 
 | 690 | 		} | 
 | 691 | 	} | 
 | 692 | } | 
 | 693 |  | 
 | 694 | static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer) | 
 | 695 | { | 
 | 696 | 	unsigned char *data=buffer->data; | 
 | 697 | 	int len=buffer->length; | 
 | 698 | 	int offset=buffer->offset; | 
 | 699 | 	int datasize=se401->cwidth*se401->cheight; | 
 | 700 | 	struct se401_frame *frame=&se401->frame[se401->curframe]; | 
 | 701 |  | 
 | 702 | 	unsigned char *framedata=frame->data, *curline, *nextline; | 
 | 703 | 	int width=se401->cwidth; | 
 | 704 | 	int blineoffset=0, bline; | 
 | 705 | 	int linelength=width*3, i; | 
 | 706 | 	 | 
 | 707 |  | 
 | 708 | 	if (frame->curpix==0) { | 
 | 709 | 		if (frame->grabstate==FRAME_READY) { | 
 | 710 | 			frame->grabstate=FRAME_GRABBING; | 
 | 711 | 		} | 
 | 712 | 		frame->curline=framedata+linelength; | 
 | 713 | 		frame->curlinepix=0; | 
 | 714 | 	} | 
 | 715 |  | 
 | 716 | 	if (offset!=frame->curpix) { | 
 | 717 | 		/* Regard frame as lost :( */ | 
 | 718 | 		frame->curpix=0; | 
 | 719 | 		se401->error++; | 
 | 720 | 		return; | 
 | 721 | 	} | 
 | 722 |  | 
 | 723 | 	/* Check if we have to much data */ | 
 | 724 | 	if (frame->curpix+len > datasize) { | 
 | 725 | 		len=datasize-frame->curpix; | 
 | 726 | 	} | 
 | 727 | 	if (se401->cheight%4) | 
 | 728 | 		blineoffset=1; | 
 | 729 | 	bline=frame->curpix/se401->cwidth+blineoffset; | 
 | 730 |  | 
 | 731 | 	curline=frame->curline; | 
 | 732 | 	nextline=curline+linelength; | 
 | 733 | 	if (nextline >= framedata+datasize*3) | 
 | 734 | 		nextline=curline; | 
 | 735 | 	while (len) { | 
 | 736 | 		if (frame->curlinepix>=width) { | 
 | 737 | 			frame->curlinepix-=width; | 
 | 738 | 			bline=frame->curpix/width+blineoffset; | 
 | 739 | 			curline+=linelength*2; | 
 | 740 | 			nextline+=linelength*2; | 
 | 741 | 			if (curline >= framedata+datasize*3) { | 
 | 742 | 				frame->curlinepix++; | 
 | 743 | 				curline-=3; | 
 | 744 | 				nextline-=3; | 
 | 745 | 				len--; | 
 | 746 | 				data++; | 
 | 747 | 				frame->curpix++; | 
 | 748 | 			} | 
 | 749 | 			if (nextline >= framedata+datasize*3) | 
 | 750 | 				nextline=curline; | 
 | 751 | 		} | 
 | 752 | 		if ((bline&1)) { | 
 | 753 | 			if ((frame->curlinepix&1)) { | 
 | 754 | 				*(curline+2)=*data; | 
 | 755 | 				*(curline-1)=*data; | 
 | 756 | 				*(nextline+2)=*data; | 
 | 757 | 				*(nextline-1)=*data; | 
 | 758 | 			} else { | 
 | 759 | 				*(curline+1)= | 
 | 760 | 					(*(curline+1)+*data)/2; | 
 | 761 | 				*(curline-2)= | 
 | 762 | 					(*(curline-2)+*data)/2; | 
 | 763 | 				*(nextline+1)=*data; | 
 | 764 | 				*(nextline-2)=*data; | 
 | 765 | 			} | 
 | 766 | 		} else { | 
 | 767 | 			if ((frame->curlinepix&1)) { | 
 | 768 | 				*(curline+1)= | 
 | 769 | 					(*(curline+1)+*data)/2; | 
 | 770 | 				*(curline-2)= | 
 | 771 | 					(*(curline-2)+*data)/2; | 
 | 772 | 				*(nextline+1)=*data; | 
 | 773 | 				*(nextline-2)=*data; | 
 | 774 | 			} else { | 
 | 775 | 				*curline=*data; | 
 | 776 | 				*(curline-3)=*data; | 
 | 777 | 				*nextline=*data; | 
 | 778 | 				*(nextline-3)=*data; | 
 | 779 | 			} | 
 | 780 | 		} | 
 | 781 | 		frame->curlinepix++; | 
 | 782 | 		curline-=3; | 
 | 783 | 		nextline-=3; | 
 | 784 | 		len--; | 
 | 785 | 		data++; | 
 | 786 | 		frame->curpix++; | 
 | 787 | 	} | 
 | 788 | 	frame->curline=curline; | 
 | 789 |  | 
 | 790 | 	if (frame->curpix>=datasize) { | 
 | 791 | 		/* Fix the top line */ | 
 | 792 | 		framedata+=linelength; | 
 | 793 | 		for (i=0; i<linelength; i++) { | 
 | 794 | 			framedata--; | 
 | 795 | 			*framedata=*(framedata+linelength); | 
 | 796 | 		} | 
 | 797 | 		/* Fix the left side (green is already present) */ | 
 | 798 | 		for (i=0; i<se401->cheight; i++) { | 
 | 799 | 			*framedata=*(framedata+3); | 
 | 800 | 			*(framedata+1)=*(framedata+4); | 
 | 801 | 			*(framedata+2)=*(framedata+5); | 
 | 802 | 			framedata+=linelength; | 
 | 803 | 		} | 
 | 804 | 		frame->curpix=0; | 
 | 805 | 		frame->grabstate=FRAME_DONE; | 
 | 806 | 		se401->framecount++; | 
 | 807 | 		se401->readcount++; | 
 | 808 | 		if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { | 
 | 809 | 			se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); | 
 | 810 | 		} | 
 | 811 | 	} | 
 | 812 | } | 
 | 813 |  | 
 | 814 | static int se401_newframe(struct usb_se401 *se401, int framenr) | 
 | 815 | { | 
 | 816 | 	DECLARE_WAITQUEUE(wait, current); | 
 | 817 | 	int errors=0; | 
 | 818 |  | 
 | 819 | 	while (se401->streaming && | 
 | 820 | 	    (se401->frame[framenr].grabstate==FRAME_READY || | 
 | 821 | 	     se401->frame[framenr].grabstate==FRAME_GRABBING) ) { | 
 | 822 | 		if(!se401->frame[framenr].curpix) { | 
 | 823 | 			errors++; | 
 | 824 | 		} | 
 | 825 | 		wait_interruptible( | 
 | 826 | 		    se401->scratch[se401->scratch_use].state!=BUFFER_READY, | 
 | 827 | 		    &se401->wq, | 
 | 828 | 		    &wait | 
 | 829 | 		); | 
 | 830 | 		if (se401->nullpackets > SE401_MAX_NULLPACKETS) { | 
 | 831 | 			se401->nullpackets=0; | 
 | 832 | 			info("to many null length packets, restarting capture"); | 
 | 833 | 			se401_stop_stream(se401); | 
 | 834 | 			se401_start_stream(se401);			 | 
 | 835 | 		} else { | 
 | 836 | 			if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) { | 
 | 837 | 				se401->frame[framenr].grabstate=FRAME_ERROR; | 
 | 838 | 				return -EIO; | 
 | 839 | 			} | 
 | 840 | 			se401->scratch[se401->scratch_use].state=BUFFER_BUSY; | 
 | 841 | 			if (se401->format==FMT_JANGGU) { | 
 | 842 | 				decode_JangGu(se401, &se401->scratch[se401->scratch_use]); | 
 | 843 | 			} else { | 
 | 844 | 				decode_bayer(se401, &se401->scratch[se401->scratch_use]); | 
 | 845 | 			} | 
 | 846 | 			se401->scratch[se401->scratch_use].state=BUFFER_UNUSED; | 
 | 847 | 			se401->scratch_use++; | 
 | 848 | 			if (se401->scratch_use>=SE401_NUMSCRATCH) | 
 | 849 | 				se401->scratch_use=0; | 
 | 850 | 			if (errors > SE401_MAX_ERRORS) { | 
 | 851 | 				errors=0; | 
 | 852 | 				info("to much errors, restarting capture"); | 
 | 853 | 				se401_stop_stream(se401); | 
 | 854 | 				se401_start_stream(se401); | 
 | 855 | 			} | 
 | 856 | 		} | 
 | 857 | 	} | 
 | 858 |  | 
 | 859 | 	if (se401->frame[framenr].grabstate==FRAME_DONE) | 
 | 860 | 		if (se401->enhance) | 
 | 861 | 			enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3); | 
 | 862 | 	return 0; | 
 | 863 | } | 
 | 864 |  | 
 | 865 | static void usb_se401_remove_disconnected (struct usb_se401 *se401) | 
 | 866 | { | 
 | 867 | 	int i; | 
 | 868 |  | 
 | 869 |         se401->dev = NULL; | 
 | 870 |  | 
| Jesper Juhl | 1bc3c9e | 2005-04-18 17:39:34 -0700 | [diff] [blame] | 871 | 	for (i=0; i<SE401_NUMSBUF; i++) | 
 | 872 | 		if (se401->urb[i]) { | 
 | 873 | 			usb_kill_urb(se401->urb[i]); | 
 | 874 | 			usb_free_urb(se401->urb[i]); | 
 | 875 | 			se401->urb[i] = NULL; | 
 | 876 | 			kfree(se401->sbuf[i].data); | 
 | 877 | 		} | 
 | 878 | 	for (i=0; i<SE401_NUMSCRATCH; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 879 | 		kfree(se401->scratch[i].data); | 
 | 880 | 	} | 
 | 881 | 	if (se401->inturb) { | 
 | 882 | 		usb_kill_urb(se401->inturb); | 
 | 883 | 		usb_free_urb(se401->inturb); | 
 | 884 | 	} | 
 | 885 |         info("%s disconnected", se401->camera_name); | 
 | 886 |  | 
 | 887 |         /* Free the memory */ | 
 | 888 | 	kfree(se401->width); | 
 | 889 | 	kfree(se401->height); | 
 | 890 | 	kfree(se401); | 
 | 891 | } | 
 | 892 |  | 
 | 893 |  | 
 | 894 |  | 
 | 895 | /**************************************************************************** | 
 | 896 |  * | 
 | 897 |  * Video4Linux | 
 | 898 |  * | 
 | 899 |  ***************************************************************************/ | 
 | 900 |  | 
 | 901 |  | 
 | 902 | static int se401_open(struct inode *inode, struct file *file) | 
 | 903 | { | 
 | 904 | 	struct video_device *dev = video_devdata(file); | 
 | 905 | 	struct usb_se401 *se401 = (struct usb_se401 *)dev; | 
 | 906 | 	int err = 0; | 
 | 907 |  | 
 | 908 | 	if (se401->user) | 
 | 909 | 		return -EBUSY; | 
 | 910 | 	se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES); | 
 | 911 | 	if (se401->fbuf) | 
 | 912 | 		file->private_data = dev; | 
 | 913 | 	else  | 
 | 914 | 		err = -ENOMEM; | 
 | 915 | 	se401->user = !err; | 
 | 916 |  | 
 | 917 | 	return err; | 
 | 918 | } | 
 | 919 |  | 
 | 920 | static int se401_close(struct inode *inode, struct file *file) | 
 | 921 | { | 
 | 922 | 	struct video_device *dev = file->private_data; | 
 | 923 |         struct usb_se401 *se401 = (struct usb_se401 *)dev; | 
 | 924 | 	int i; | 
 | 925 |  | 
 | 926 | 	rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); | 
 | 927 |         if (se401->removed) { | 
 | 928 | 		usb_se401_remove_disconnected(se401); | 
 | 929 | 		info("device unregistered"); | 
 | 930 | 	} else { | 
 | 931 | 		for (i=0; i<SE401_NUMFRAMES; i++) | 
 | 932 | 			se401->frame[i].grabstate=FRAME_UNUSED; | 
 | 933 | 		if (se401->streaming) | 
 | 934 | 			se401_stop_stream(se401); | 
 | 935 | 		se401->user=0; | 
 | 936 | 	} | 
 | 937 | 	file->private_data = NULL; | 
 | 938 | 	return 0; | 
 | 939 | } | 
 | 940 |  | 
 | 941 | static int se401_do_ioctl(struct inode *inode, struct file *file, | 
 | 942 | 			  unsigned int cmd, void *arg) | 
 | 943 | { | 
 | 944 | 	struct video_device *vdev = file->private_data; | 
 | 945 |         struct usb_se401 *se401 = (struct usb_se401 *)vdev; | 
 | 946 |  | 
 | 947 |         if (!se401->dev) | 
 | 948 |                 return -EIO; | 
 | 949 |  | 
 | 950 |         switch (cmd) { | 
 | 951 | 	case VIDIOCGCAP: | 
 | 952 | 	{ | 
 | 953 | 		struct video_capability *b = arg; | 
 | 954 | 		strcpy(b->name, se401->camera_name); | 
 | 955 | 		b->type = VID_TYPE_CAPTURE; | 
 | 956 | 		b->channels = 1; | 
 | 957 | 		b->audios = 0; | 
 | 958 | 		b->maxwidth = se401->width[se401->sizes-1]; | 
 | 959 | 		b->maxheight = se401->height[se401->sizes-1]; | 
 | 960 | 		b->minwidth = se401->width[0]; | 
 | 961 | 		b->minheight = se401->height[0]; | 
 | 962 | 		return 0; | 
 | 963 | 	} | 
 | 964 | 	case VIDIOCGCHAN: | 
 | 965 | 	{ | 
 | 966 | 		struct video_channel *v = arg; | 
 | 967 |  | 
 | 968 | 		if (v->channel != 0) | 
 | 969 | 			return -EINVAL; | 
 | 970 | 		v->flags = 0; | 
 | 971 | 		v->tuners = 0; | 
 | 972 | 		v->type = VIDEO_TYPE_CAMERA; | 
 | 973 | 		strcpy(v->name, "Camera"); | 
 | 974 | 		return 0; | 
 | 975 | 	} | 
 | 976 | 	case VIDIOCSCHAN: | 
 | 977 | 	{ | 
 | 978 | 		struct video_channel *v = arg; | 
 | 979 |  | 
 | 980 | 		if (v->channel != 0) | 
 | 981 | 			return -EINVAL; | 
 | 982 | 		return 0; | 
 | 983 | 	} | 
 | 984 |         case VIDIOCGPICT: | 
 | 985 |         { | 
 | 986 | 		struct video_picture *p = arg; | 
 | 987 |  | 
 | 988 | 		se401_get_pict(se401, p); | 
 | 989 | 		return 0; | 
 | 990 | 	} | 
 | 991 | 	case VIDIOCSPICT: | 
 | 992 | 	{ | 
 | 993 | 		struct video_picture *p = arg; | 
 | 994 |  | 
 | 995 | 		if (se401_set_pict(se401, p)) | 
 | 996 | 			return -EINVAL; | 
 | 997 | 		return 0; | 
 | 998 | 	} | 
 | 999 | 	case VIDIOCSWIN: | 
 | 1000 | 	{ | 
 | 1001 | 		struct video_window *vw = arg; | 
 | 1002 |  | 
 | 1003 | 		if (vw->flags) | 
 | 1004 | 			return -EINVAL; | 
 | 1005 | 		if (vw->clipcount) | 
 | 1006 | 			return -EINVAL; | 
 | 1007 | 		if (se401_set_size(se401, vw->width, vw->height)) | 
 | 1008 | 			return -EINVAL; | 
 | 1009 | 		return 0; | 
 | 1010 |         } | 
 | 1011 | 	case VIDIOCGWIN: | 
 | 1012 | 	{ | 
 | 1013 | 		struct video_window *vw = arg; | 
 | 1014 |  | 
 | 1015 | 		vw->x = 0;               /* FIXME */ | 
 | 1016 | 		vw->y = 0; | 
 | 1017 | 		vw->chromakey = 0; | 
 | 1018 | 		vw->flags = 0; | 
 | 1019 | 		vw->clipcount = 0; | 
 | 1020 | 		vw->width = se401->cwidth; | 
 | 1021 | 		vw->height = se401->cheight; | 
 | 1022 | 		return 0; | 
 | 1023 | 	} | 
 | 1024 | 	case VIDIOCGMBUF: | 
 | 1025 | 	{ | 
 | 1026 | 		struct video_mbuf *vm = arg; | 
 | 1027 | 		int i; | 
 | 1028 |  | 
 | 1029 | 		memset(vm, 0, sizeof(*vm)); | 
 | 1030 | 		vm->size = SE401_NUMFRAMES * se401->maxframesize; | 
 | 1031 | 		vm->frames = SE401_NUMFRAMES; | 
 | 1032 | 		for (i=0; i<SE401_NUMFRAMES; i++) | 
 | 1033 | 			vm->offsets[i] = se401->maxframesize * i; | 
 | 1034 | 		return 0; | 
 | 1035 | 	} | 
 | 1036 | 	case VIDIOCMCAPTURE: | 
 | 1037 | 	{ | 
 | 1038 | 		struct video_mmap *vm = arg; | 
 | 1039 |  | 
 | 1040 | 		if (vm->format != VIDEO_PALETTE_RGB24) | 
 | 1041 | 			return -EINVAL; | 
 | 1042 | 		if (vm->frame >= SE401_NUMFRAMES) | 
 | 1043 | 			return -EINVAL; | 
 | 1044 | 		if (se401->frame[vm->frame].grabstate != FRAME_UNUSED) | 
 | 1045 | 			return -EBUSY; | 
 | 1046 |  | 
 | 1047 | 		/* Is this according to the v4l spec??? */ | 
 | 1048 | 		if (se401_set_size(se401, vm->width, vm->height)) | 
 | 1049 | 			return -EINVAL; | 
 | 1050 | 		se401->frame[vm->frame].grabstate=FRAME_READY; | 
 | 1051 |  | 
 | 1052 | 		if (!se401->streaming) | 
 | 1053 | 			se401_start_stream(se401); | 
 | 1054 |  | 
 | 1055 | 		/* Set the picture properties */ | 
 | 1056 | 		if (se401->framecount==0) | 
 | 1057 | 			se401_send_pict(se401); | 
 | 1058 | 		/* Calibrate the reset level after a few frames. */ | 
 | 1059 | 		if (se401->framecount%20==1) | 
 | 1060 | 			se401_auto_resetlevel(se401); | 
 | 1061 |  | 
 | 1062 | 		return 0; | 
 | 1063 | 	} | 
 | 1064 | 	case VIDIOCSYNC: | 
 | 1065 | 	{ | 
 | 1066 | 		int *frame = arg; | 
 | 1067 | 		int ret=0; | 
 | 1068 |  | 
 | 1069 | 		if(*frame <0 || *frame >= SE401_NUMFRAMES) | 
 | 1070 | 			return -EINVAL; | 
 | 1071 |  | 
 | 1072 | 		ret=se401_newframe(se401, *frame); | 
 | 1073 | 		se401->frame[*frame].grabstate=FRAME_UNUSED; | 
 | 1074 | 		return ret; | 
 | 1075 | 	} | 
 | 1076 | 	case VIDIOCGFBUF: | 
 | 1077 | 	{ | 
 | 1078 | 		struct video_buffer *vb = arg; | 
 | 1079 |  | 
 | 1080 | 		memset(vb, 0, sizeof(*vb)); | 
 | 1081 | 		return 0; | 
 | 1082 | 	} | 
 | 1083 | 	case VIDIOCKEY: | 
 | 1084 | 		return 0; | 
 | 1085 | 	case VIDIOCCAPTURE: | 
 | 1086 | 		return -EINVAL; | 
 | 1087 | 	case VIDIOCSFBUF: | 
 | 1088 | 		return -EINVAL; | 
 | 1089 | 	case VIDIOCGTUNER: | 
 | 1090 | 	case VIDIOCSTUNER: | 
 | 1091 | 		return -EINVAL; | 
 | 1092 | 	case VIDIOCGFREQ: | 
 | 1093 | 	case VIDIOCSFREQ: | 
 | 1094 | 		return -EINVAL; | 
 | 1095 | 	case VIDIOCGAUDIO: | 
 | 1096 | 	case VIDIOCSAUDIO: | 
 | 1097 | 		return -EINVAL; | 
 | 1098 |         default: | 
 | 1099 |                 return -ENOIOCTLCMD; | 
 | 1100 |         } /* end switch */ | 
 | 1101 |  | 
 | 1102 |         return 0; | 
 | 1103 | } | 
 | 1104 |  | 
 | 1105 | static int se401_ioctl(struct inode *inode, struct file *file, | 
 | 1106 | 		       unsigned int cmd, unsigned long arg) | 
 | 1107 | { | 
 | 1108 | 	return video_usercopy(inode, file, cmd, arg, se401_do_ioctl); | 
 | 1109 | } | 
 | 1110 |  | 
 | 1111 | static ssize_t se401_read(struct file *file, char __user *buf, | 
 | 1112 | 		     size_t count, loff_t *ppos) | 
 | 1113 | { | 
 | 1114 | 	int realcount=count, ret=0; | 
 | 1115 | 	struct video_device *dev = file->private_data; | 
 | 1116 | 	struct usb_se401 *se401 = (struct usb_se401 *)dev; | 
 | 1117 |  | 
 | 1118 |  | 
 | 1119 | 	if (se401->dev == NULL) | 
 | 1120 | 		return -EIO; | 
 | 1121 | 	if (realcount > se401->cwidth*se401->cheight*3) | 
 | 1122 | 		realcount=se401->cwidth*se401->cheight*3; | 
 | 1123 |  | 
 | 1124 | 	/* Shouldn't happen: */ | 
 | 1125 | 	if (se401->frame[0].grabstate==FRAME_GRABBING) | 
 | 1126 | 		return -EBUSY; | 
 | 1127 | 	se401->frame[0].grabstate=FRAME_READY; | 
 | 1128 | 	se401->frame[1].grabstate=FRAME_UNUSED; | 
 | 1129 | 	se401->curframe=0; | 
 | 1130 |  | 
 | 1131 | 	if (!se401->streaming) | 
 | 1132 | 		se401_start_stream(se401); | 
 | 1133 |  | 
 | 1134 | 	/* Set the picture properties */ | 
 | 1135 | 	if (se401->framecount==0) | 
 | 1136 | 		se401_send_pict(se401); | 
 | 1137 | 	/* Calibrate the reset level after a few frames. */ | 
 | 1138 | 	if (se401->framecount%20==1) | 
 | 1139 | 		se401_auto_resetlevel(se401); | 
 | 1140 |  | 
 | 1141 | 	ret=se401_newframe(se401, 0); | 
 | 1142 |  | 
 | 1143 | 	se401->frame[0].grabstate=FRAME_UNUSED; | 
 | 1144 | 	if (ret) | 
 | 1145 | 		return ret;	 | 
 | 1146 | 	if (copy_to_user(buf, se401->frame[0].data, realcount)) | 
 | 1147 | 		return -EFAULT; | 
 | 1148 |  | 
 | 1149 | 	return realcount; | 
 | 1150 | } | 
 | 1151 |  | 
 | 1152 | static int se401_mmap(struct file *file, struct vm_area_struct *vma) | 
 | 1153 | { | 
 | 1154 | 	struct video_device *dev = file->private_data; | 
 | 1155 | 	struct usb_se401 *se401 = (struct usb_se401 *)dev; | 
 | 1156 | 	unsigned long start = vma->vm_start; | 
 | 1157 | 	unsigned long size  = vma->vm_end-vma->vm_start; | 
 | 1158 | 	unsigned long page, pos; | 
 | 1159 |  | 
 | 1160 | 	down(&se401->lock); | 
 | 1161 |  | 
 | 1162 | 	if (se401->dev == NULL) { | 
 | 1163 | 		up(&se401->lock); | 
 | 1164 | 		return -EIO; | 
 | 1165 | 	} | 
 | 1166 | 	if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { | 
 | 1167 | 		up(&se401->lock); | 
 | 1168 | 		return -EINVAL; | 
 | 1169 | 	} | 
 | 1170 | 	pos = (unsigned long)se401->fbuf; | 
 | 1171 | 	while (size > 0) { | 
 | 1172 | 		page = vmalloc_to_pfn((void *)pos); | 
 | 1173 | 		if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { | 
 | 1174 | 			up(&se401->lock); | 
 | 1175 | 			return -EAGAIN; | 
 | 1176 | 		} | 
 | 1177 | 		start += PAGE_SIZE; | 
 | 1178 | 		pos += PAGE_SIZE; | 
 | 1179 | 		if (size > PAGE_SIZE) | 
 | 1180 | 			size -= PAGE_SIZE; | 
 | 1181 | 		else | 
 | 1182 | 			size = 0; | 
 | 1183 | 	} | 
 | 1184 | 	up(&se401->lock); | 
 | 1185 |  | 
 | 1186 |         return 0; | 
 | 1187 | } | 
 | 1188 |  | 
 | 1189 | static struct file_operations se401_fops = { | 
 | 1190 | 	.owner =	THIS_MODULE, | 
 | 1191 |         .open =         se401_open, | 
 | 1192 |         .release =      se401_close, | 
 | 1193 |         .read =         se401_read, | 
 | 1194 |         .mmap =         se401_mmap, | 
 | 1195 | 	.ioctl =        se401_ioctl, | 
 | 1196 | 	.llseek =       no_llseek, | 
 | 1197 | }; | 
 | 1198 | static struct video_device se401_template = { | 
 | 1199 | 	.owner =	THIS_MODULE, | 
 | 1200 |         .name =         "se401 USB camera", | 
 | 1201 |         .type =         VID_TYPE_CAPTURE, | 
 | 1202 |         .hardware =     VID_HARDWARE_SE401, | 
 | 1203 | 	.fops =         &se401_fops, | 
 | 1204 | }; | 
 | 1205 |  | 
 | 1206 |  | 
 | 1207 |  | 
 | 1208 | /***************************/ | 
 | 1209 | static int se401_init(struct usb_se401 *se401, int button) | 
 | 1210 | { | 
 | 1211 |         int i=0, rc; | 
 | 1212 |         unsigned char cp[0x40]; | 
 | 1213 | 	char temp[200]; | 
 | 1214 |  | 
 | 1215 | 	/* led on */ | 
 | 1216 |         se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); | 
 | 1217 |  | 
 | 1218 | 	/* get camera descriptor */ | 
 | 1219 | 	rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp)); | 
 | 1220 | 	if (cp[1]!=0x41) { | 
 | 1221 | 		err("Wrong descriptor type"); | 
 | 1222 | 		return 1; | 
 | 1223 | 	} | 
 | 1224 | 	sprintf (temp, "ExtraFeatures: %d", cp[3]); | 
 | 1225 |  | 
 | 1226 | 	se401->sizes=cp[4]+cp[5]*256; | 
 | 1227 | 	se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); | 
 | 1228 | 	if (!se401->width) | 
 | 1229 | 		return 1; | 
 | 1230 | 	se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); | 
 | 1231 | 	if (!se401->height) { | 
 | 1232 | 		kfree(se401->width); | 
 | 1233 | 		return 1; | 
 | 1234 | 	} | 
 | 1235 | 	for (i=0; i<se401->sizes; i++) { | 
 | 1236 | 		    se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256; | 
 | 1237 | 		    se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256; | 
 | 1238 | 	} | 
 | 1239 | 	sprintf (temp, "%s Sizes:", temp); | 
 | 1240 | 	for (i=0; i<se401->sizes; i++) { | 
 | 1241 | 		sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]); | 
 | 1242 | 	} | 
 | 1243 | 	info("%s", temp); | 
 | 1244 | 	se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3; | 
 | 1245 |  | 
 | 1246 | 	rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); | 
 | 1247 | 	se401->cwidth=cp[0]+cp[1]*256; | 
 | 1248 | 	rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); | 
 | 1249 | 	se401->cheight=cp[0]+cp[1]*256; | 
 | 1250 |  | 
 | 1251 | 	if (!cp[2] && SE401_FORMAT_BAYER) { | 
 | 1252 | 		err("Bayer format not supported!"); | 
 | 1253 | 		return 1; | 
 | 1254 | 	} | 
 | 1255 | 	/* set output mode (BAYER) */ | 
 | 1256 |         se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0); | 
 | 1257 |  | 
 | 1258 | 	rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); | 
 | 1259 | 	se401->brightness=cp[0]+cp[1]*256; | 
 | 1260 | 	/* some default values */ | 
 | 1261 | 	se401->resetlevel=0x2d; | 
 | 1262 | 	se401->rgain=0x20; | 
 | 1263 | 	se401->ggain=0x20; | 
 | 1264 | 	se401->bgain=0x20; | 
 | 1265 | 	se401_set_exposure(se401, 20000); | 
 | 1266 | 	se401->palette=VIDEO_PALETTE_RGB24; | 
 | 1267 | 	se401->enhance=1; | 
 | 1268 | 	se401->dropped=0; | 
 | 1269 | 	se401->error=0; | 
 | 1270 | 	se401->framecount=0; | 
 | 1271 | 	se401->readcount=0; | 
 | 1272 |  | 
 | 1273 | 	/* Start interrupt transfers for snapshot button */ | 
 | 1274 | 	if (button) { | 
 | 1275 | 		se401->inturb=usb_alloc_urb(0, GFP_KERNEL); | 
 | 1276 | 		if (!se401->inturb) { | 
 | 1277 | 			info("Allocation of inturb failed"); | 
 | 1278 | 			return 1; | 
 | 1279 | 		} | 
 | 1280 | 		usb_fill_int_urb(se401->inturb, se401->dev, | 
 | 1281 | 		    usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), | 
 | 1282 | 		    &se401->button, sizeof(se401->button), | 
 | 1283 | 		    se401_button_irq, | 
 | 1284 | 		    se401, | 
 | 1285 | 		    8 | 
 | 1286 | 		); | 
 | 1287 | 		if (usb_submit_urb(se401->inturb, GFP_KERNEL)) { | 
 | 1288 | 			info("int urb burned down"); | 
 | 1289 | 			return 1; | 
 | 1290 | 		} | 
 | 1291 | 	} else | 
 | 1292 | 		se401->inturb=NULL; | 
 | 1293 |  | 
 | 1294 |         /* Flash the led */ | 
 | 1295 |         se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); | 
 | 1296 |         se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); | 
 | 1297 |         se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); | 
 | 1298 | 	se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); | 
 | 1299 |  | 
 | 1300 |         return 0; | 
 | 1301 | } | 
 | 1302 |  | 
 | 1303 | static int se401_probe(struct usb_interface *intf, | 
 | 1304 | 	const struct usb_device_id *id) | 
 | 1305 | { | 
 | 1306 | 	struct usb_device *dev = interface_to_usbdev(intf); | 
 | 1307 |         struct usb_interface_descriptor *interface; | 
 | 1308 |         struct usb_se401 *se401; | 
 | 1309 |         char *camera_name=NULL; | 
 | 1310 | 	int button=1; | 
 | 1311 |  | 
 | 1312 |         /* We don't handle multi-config cameras */ | 
 | 1313 |         if (dev->descriptor.bNumConfigurations != 1) | 
 | 1314 |                 return -ENODEV; | 
 | 1315 |  | 
 | 1316 |         interface = &intf->cur_altsetting->desc; | 
 | 1317 |  | 
 | 1318 |         /* Is it an se401? */ | 
 | 1319 |         if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && | 
 | 1320 |             le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { | 
 | 1321 |                 camera_name="Endpoints/Aox SE401"; | 
 | 1322 |         } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && | 
 | 1323 |             le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { | 
 | 1324 |                 camera_name="Philips PCVC665K"; | 
 | 1325 |         } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && | 
 | 1326 | 	    le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { | 
 | 1327 | 		camera_name="Kensington VideoCAM 67014"; | 
 | 1328 |         } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && | 
 | 1329 | 	    le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { | 
 | 1330 | 		camera_name="Kensington VideoCAM 6701(5/7)"; | 
 | 1331 |         } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && | 
 | 1332 | 	    le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { | 
 | 1333 | 		camera_name="Kensington VideoCAM 67016"; | 
 | 1334 | 		button=0; | 
 | 1335 | 	} else | 
 | 1336 | 		return -ENODEV; | 
 | 1337 |  | 
 | 1338 |         /* Checking vendor/product should be enough, but what the hell */ | 
 | 1339 |         if (interface->bInterfaceClass != 0x00) | 
 | 1340 | 		return -ENODEV; | 
 | 1341 |         if (interface->bInterfaceSubClass != 0x00) | 
 | 1342 | 		return -ENODEV; | 
 | 1343 |  | 
 | 1344 |         /* We found one */ | 
 | 1345 |         info("SE401 camera found: %s", camera_name); | 
 | 1346 |  | 
 | 1347 |         if ((se401 = kmalloc(sizeof(*se401), GFP_KERNEL)) == NULL) { | 
 | 1348 |                 err("couldn't kmalloc se401 struct"); | 
 | 1349 | 		return -ENOMEM; | 
 | 1350 |         } | 
 | 1351 |  | 
 | 1352 |         memset(se401, 0, sizeof(*se401)); | 
 | 1353 |  | 
 | 1354 |         se401->dev = dev; | 
 | 1355 |         se401->iface = interface->bInterfaceNumber; | 
 | 1356 |         se401->camera_name = camera_name; | 
 | 1357 |  | 
 | 1358 | 	info("firmware version: %02x", le16_to_cpu(dev->descriptor.bcdDevice) & 255); | 
 | 1359 |  | 
 | 1360 |         if (se401_init(se401, button)) { | 
 | 1361 | 		kfree(se401); | 
 | 1362 | 		return -EIO; | 
 | 1363 | 	} | 
 | 1364 |  | 
 | 1365 | 	memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); | 
 | 1366 | 	memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name)); | 
 | 1367 | 	init_waitqueue_head(&se401->wq); | 
 | 1368 | 	init_MUTEX(&se401->lock); | 
 | 1369 | 	wmb(); | 
 | 1370 |  | 
 | 1371 | 	if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { | 
 | 1372 | 		kfree(se401); | 
 | 1373 | 		err("video_register_device failed"); | 
 | 1374 | 		return -EIO; | 
 | 1375 | 	} | 
 | 1376 | 	info("registered new video device: video%d", se401->vdev.minor); | 
 | 1377 |  | 
 | 1378 | 	usb_set_intfdata (intf, se401); | 
 | 1379 |         return 0; | 
 | 1380 | } | 
 | 1381 |  | 
 | 1382 | static void se401_disconnect(struct usb_interface *intf) | 
 | 1383 | { | 
 | 1384 | 	struct usb_se401 *se401 = usb_get_intfdata (intf); | 
 | 1385 |  | 
 | 1386 | 	usb_set_intfdata (intf, NULL); | 
 | 1387 | 	if (se401) { | 
 | 1388 | 		video_unregister_device(&se401->vdev); | 
 | 1389 | 		if (!se401->user){ | 
 | 1390 | 			usb_se401_remove_disconnected(se401); | 
 | 1391 | 		} else { | 
 | 1392 | 			se401->frame[0].grabstate = FRAME_ERROR; | 
 | 1393 | 			se401->frame[0].grabstate = FRAME_ERROR; | 
 | 1394 |  | 
 | 1395 | 			se401->streaming = 0; | 
 | 1396 |  | 
 | 1397 | 			wake_up_interruptible(&se401->wq); | 
 | 1398 | 			se401->removed = 1; | 
 | 1399 | 		} | 
 | 1400 | 	} | 
 | 1401 | } | 
 | 1402 |  | 
 | 1403 | static struct usb_driver se401_driver = { | 
 | 1404 | 	.owner		= THIS_MODULE, | 
 | 1405 |         .name		= "se401", | 
 | 1406 |         .id_table	= device_table, | 
 | 1407 | 	.probe		= se401_probe, | 
 | 1408 |         .disconnect	= se401_disconnect, | 
 | 1409 | }; | 
 | 1410 |  | 
 | 1411 |  | 
 | 1412 |  | 
 | 1413 | /**************************************************************************** | 
 | 1414 |  * | 
 | 1415 |  *  Module routines | 
 | 1416 |  * | 
 | 1417 |  ***************************************************************************/ | 
 | 1418 |  | 
 | 1419 | static int __init usb_se401_init(void) | 
 | 1420 | { | 
 | 1421 | 	info("SE401 usb camera driver version %s registering", version); | 
 | 1422 | 	if (flickerless) | 
 | 1423 | 		if (flickerless!=50 && flickerless!=60) { | 
 | 1424 | 			info("Invallid flickerless value, use 0, 50 or 60."); | 
 | 1425 | 			return -1; | 
 | 1426 | 	} | 
 | 1427 | 	return usb_register(&se401_driver); | 
 | 1428 | } | 
 | 1429 |  | 
 | 1430 | static void __exit usb_se401_exit(void) | 
 | 1431 | { | 
 | 1432 | 	usb_deregister(&se401_driver); | 
 | 1433 | 	info("SE401 driver deregistered"); | 
 | 1434 | } | 
 | 1435 |  | 
 | 1436 | module_init(usb_se401_init); | 
 | 1437 | module_exit(usb_se401_exit); |