Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 3 | * |
| 4 | * Thanks to STMicroelectronics for information on the usb commands, and |
| 5 | * to Steve Miller at STM for his help and encouragement while I was |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 | * writing this driver. |
| 7 | * |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 8 | * This driver is based heavily on the |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 9 | * Endpoints (formerly known as AOX) se401 USB Camera Driver |
| 10 | * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) |
| 11 | * |
| 12 | * Still somewhat based on the Linux ov511 driver. |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 13 | * |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | * This program is free software; you can redistribute it and/or modify it |
| 15 | * under the terms of the GNU General Public License as published by the |
| 16 | * Free Software Foundation; either version 2 of the License, or (at your |
| 17 | * option) any later version. |
| 18 | * |
| 19 | * This program is distributed in the hope that it will be useful, but |
| 20 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 21 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 22 | * for more details. |
| 23 | * |
| 24 | * You should have received a copy of the GNU General Public License |
| 25 | * along with this program; if not, write to the Free Software Foundation, |
| 26 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 27 | * |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 28 | * History: |
| 29 | * ver 0.1 October, 2001. Initial attempt. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | * |
| 31 | * ver 0.2 November, 2001. Fixed asbility to resize, added brightness |
| 32 | * function, made more stable (?) |
| 33 | * |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 34 | * ver 0.21 Nov, 2001. Added gamma correction and white balance, |
| 35 | * due to Alexander Schwartz. Still trying to |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 | * improve stablility. Moved stuff into stv680.h |
| 37 | * |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 38 | * ver 0.22 Nov, 2001. Added sharpen function (by Michael Sweet, |
| 39 | * mike@easysw.com) from GIMP, also used in pencam. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | * Simple, fast, good integer math routine. |
| 41 | * |
| 42 | * ver 0.23 Dec, 2001 (gkh) |
| 43 | * Took out sharpen function, ran code through |
| 44 | * Lindent, and did other minor tweaks to get |
| 45 | * things to work properly with 2.5.1 |
| 46 | * |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 47 | * ver 0.24 Jan, 2002 (kjs) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | * Fixed the problem with webcam crashing after |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 49 | * two pictures. Changed the way pic is halved to |
| 50 | * improve quality. Got rid of green line around |
| 51 | * frame. Fix brightness reset when changing size |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | * bug. Adjusted gamma filters slightly. |
| 53 | * |
| 54 | * ver 0.25 Jan, 2002 (kjs) |
| 55 | * Fixed a bug in which the driver sometimes attempted |
| 56 | * to set to a non-supported size. This allowed |
| 57 | * gnomemeeting to work. |
| 58 | * Fixed proc entry removal bug. |
| 59 | */ |
| 60 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 | #include <linux/module.h> |
| 62 | #include <linux/init.h> |
| 63 | #include <linux/vmalloc.h> |
| 64 | #include <linux/slab.h> |
Alexey Dobriyan | 405f557 | 2009-07-11 22:08:37 +0400 | [diff] [blame] | 65 | #include <linux/smp_lock.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | #include <linux/pagemap.h> |
| 67 | #include <linux/errno.h> |
| 68 | #include <linux/videodev.h> |
Mauro Carvalho Chehab | 5e87efa | 2006-06-05 10:26:32 -0300 | [diff] [blame] | 69 | #include <media/v4l2-common.h> |
Hans Verkuil | 35ea11f | 2008-07-20 08:12:02 -0300 | [diff] [blame] | 70 | #include <media/v4l2-ioctl.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 | #include <linux/usb.h> |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 72 | #include <linux/mutex.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 | |
| 74 | #include "stv680.h" |
| 75 | |
| 76 | static int video_nr = -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 77 | |
Douglas Schilling Landgraf | ff699e6 | 2008-04-22 14:41:48 -0300 | [diff] [blame] | 78 | static int swapRGB; /* 0 = default for auto select */ |
| 79 | |
| 80 | /* 0 = default to allow auto select; -1 = swap never, +1 = swap always */ |
| 81 | static int swapRGB_on; |
| 82 | |
| 83 | static unsigned int debug; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | |
| 85 | #define PDEBUG(level, fmt, args...) \ |
| 86 | do { \ |
| 87 | if (debug >= level) \ |
Greg Kroah-Hartman | a482f32 | 2008-10-10 05:08:23 -0300 | [diff] [blame] | 88 | printk(KERN_INFO KBUILD_MODNAME " [%s:%d] \n" fmt, \ |
| 89 | __func__, __LINE__ , ## args); \ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | } while (0) |
| 91 | |
| 92 | |
| 93 | /* |
| 94 | * Version Information |
| 95 | */ |
| 96 | #define DRIVER_VERSION "v0.25" |
| 97 | #define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>" |
| 98 | #define DRIVER_DESC "STV0680 USB Camera Driver" |
| 99 | |
| 100 | MODULE_AUTHOR (DRIVER_AUTHOR); |
| 101 | MODULE_DESCRIPTION (DRIVER_DESC); |
| 102 | MODULE_LICENSE ("GPL"); |
| 103 | module_param(debug, int, S_IRUGO | S_IWUSR); |
| 104 | MODULE_PARM_DESC (debug, "Debug enabled or not"); |
| 105 | module_param(swapRGB_on, int, 0); |
| 106 | MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never"); |
| 107 | module_param(video_nr, int, 0); |
| 108 | |
| 109 | /******************************************************************** |
| 110 | * |
| 111 | * Memory management |
| 112 | * |
| 113 | * This is a shameless copy from the USB-cpia driver (linux kernel |
| 114 | * version 2.3.29 or so, I have no idea what this code actually does ;). |
| 115 | * Actually it seems to be a copy of a shameless copy of the bttv-driver. |
| 116 | * Or that is a copy of a shameless copy of ... (To the powers: is there |
| 117 | * no generic kernel-function to do this sort of stuff?) |
| 118 | * |
| 119 | * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says |
| 120 | * there will be one, but apparentely not yet -jerdfelt |
| 121 | * |
| 122 | * So I copied it again for the ov511 driver -claudio |
| 123 | * |
| 124 | * Same for the se401 driver -Jeroen |
| 125 | * |
| 126 | * And the STV0680 driver - Kevin |
| 127 | ********************************************************************/ |
| 128 | static void *rvmalloc (unsigned long size) |
| 129 | { |
| 130 | void *mem; |
| 131 | unsigned long adr; |
| 132 | |
| 133 | size = PAGE_ALIGN(size); |
| 134 | mem = vmalloc_32 (size); |
| 135 | if (!mem) |
| 136 | return NULL; |
| 137 | |
| 138 | memset (mem, 0, size); /* Clear the ram out, no junk to the user */ |
| 139 | adr = (unsigned long) mem; |
| 140 | while (size > 0) { |
| 141 | SetPageReserved(vmalloc_to_page((void *)adr)); |
| 142 | adr += PAGE_SIZE; |
| 143 | size -= PAGE_SIZE; |
| 144 | } |
| 145 | return mem; |
| 146 | } |
| 147 | |
| 148 | static void rvfree (void *mem, unsigned long size) |
| 149 | { |
| 150 | unsigned long adr; |
| 151 | |
| 152 | if (!mem) |
| 153 | return; |
| 154 | |
| 155 | adr = (unsigned long) mem; |
| 156 | while ((long) size > 0) { |
| 157 | ClearPageReserved(vmalloc_to_page((void *)adr)); |
| 158 | adr += PAGE_SIZE; |
| 159 | size -= PAGE_SIZE; |
| 160 | } |
| 161 | vfree (mem); |
| 162 | } |
| 163 | |
| 164 | |
| 165 | /********************************************************************* |
| 166 | * pencam read/write functions |
| 167 | ********************************************************************/ |
| 168 | |
| 169 | static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size) |
| 170 | { |
| 171 | int ret = -1; |
| 172 | |
| 173 | switch (set) { |
| 174 | case 0: /* 0xc1 */ |
| 175 | ret = usb_control_msg (stv680->udev, |
| 176 | usb_rcvctrlpipe (stv680->udev, 0), |
| 177 | req, |
| 178 | (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), |
| 179 | value, 0, buffer, size, PENCAM_TIMEOUT); |
| 180 | break; |
| 181 | |
| 182 | case 1: /* 0x41 */ |
| 183 | ret = usb_control_msg (stv680->udev, |
| 184 | usb_sndctrlpipe (stv680->udev, 0), |
| 185 | req, |
| 186 | (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), |
| 187 | value, 0, buffer, size, PENCAM_TIMEOUT); |
| 188 | break; |
| 189 | |
| 190 | case 2: /* 0x80 */ |
| 191 | ret = usb_control_msg (stv680->udev, |
| 192 | usb_rcvctrlpipe (stv680->udev, 0), |
| 193 | req, |
| 194 | (USB_DIR_IN | USB_RECIP_DEVICE), |
| 195 | value, 0, buffer, size, PENCAM_TIMEOUT); |
| 196 | break; |
| 197 | |
| 198 | case 3: /* 0x40 */ |
| 199 | ret = usb_control_msg (stv680->udev, |
| 200 | usb_sndctrlpipe (stv680->udev, 0), |
| 201 | req, |
| 202 | (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), |
| 203 | value, 0, buffer, size, PENCAM_TIMEOUT); |
| 204 | break; |
| 205 | |
| 206 | } |
| 207 | if ((ret < 0) && (req != 0x0a)) { |
| 208 | PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret); |
| 209 | } |
| 210 | return ret; |
| 211 | } |
| 212 | |
| 213 | static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate) |
| 214 | { |
| 215 | |
| 216 | if (configuration != dev->udev->actconfig->desc.bConfigurationValue |
| 217 | || usb_reset_configuration (dev->udev) < 0) { |
| 218 | PDEBUG (1, "STV(e): FAILED to reset configuration %i", configuration); |
| 219 | return -1; |
| 220 | } |
| 221 | if (usb_set_interface (dev->udev, interface, alternate) < 0) { |
| 222 | PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate); |
| 223 | return -1; |
| 224 | } |
| 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | static int stv_stop_video (struct usb_stv *dev) |
| 229 | { |
| 230 | int i; |
| 231 | unsigned char *buf; |
| 232 | |
| 233 | buf = kmalloc (40, GFP_KERNEL); |
| 234 | if (buf == NULL) { |
| 235 | PDEBUG (0, "STV(e): Out of (small buf) memory"); |
| 236 | return -1; |
| 237 | } |
| 238 | |
| 239 | /* this is a high priority command; it stops all lower order commands */ |
| 240 | if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) { |
| 241 | i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02); /* Get Last Error; 2 = busy */ |
| 242 | PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buf[0], buf[1]); |
| 243 | } else { |
| 244 | PDEBUG (1, "STV(i): Camera reset to idle mode."); |
| 245 | } |
| 246 | |
| 247 | if ((i = stv_set_config (dev, 1, 0, 0)) < 0) |
| 248 | PDEBUG (1, "STV(e): Reset config during exit failed"); |
| 249 | |
| 250 | /* get current mode */ |
| 251 | buf[0] = 0xf0; |
| 252 | if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08) /* get mode */ |
| 253 | PDEBUG (0, "STV(e): Stop_video: problem setting original mode"); |
| 254 | if (dev->origMode != buf[0]) { |
| 255 | memset (buf, 0, 8); |
| 256 | buf[0] = (unsigned char) dev->origMode; |
| 257 | if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) { |
| 258 | PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed"); |
| 259 | i = -1; |
| 260 | } |
| 261 | buf[0] = 0xf0; |
| 262 | i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08); |
| 263 | if ((i != 0x08) || (buf[0] != dev->origMode)) { |
| 264 | PDEBUG (0, "STV(e): camera NOT set to original resolution."); |
| 265 | i = -1; |
| 266 | } else |
| 267 | PDEBUG (0, "STV(i): Camera set to original resolution"); |
| 268 | } |
| 269 | /* origMode */ |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 270 | kfree(buf); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 271 | return i; |
| 272 | } |
| 273 | |
| 274 | static int stv_set_video_mode (struct usb_stv *dev) |
| 275 | { |
| 276 | int i, stop_video = 1; |
| 277 | unsigned char *buf; |
| 278 | |
| 279 | buf = kmalloc (40, GFP_KERNEL); |
| 280 | if (buf == NULL) { |
| 281 | PDEBUG (0, "STV(e): Out of (small buf) memory"); |
| 282 | return -1; |
| 283 | } |
| 284 | |
| 285 | if ((i = stv_set_config (dev, 1, 0, 0)) < 0) { |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 286 | kfree(buf); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 287 | return i; |
| 288 | } |
| 289 | |
| 290 | i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12); |
| 291 | if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) { |
| 292 | PDEBUG (1, "STV(e): Could not get descriptor 0100."); |
| 293 | goto error; |
| 294 | } |
| 295 | |
| 296 | /* set alternate interface 1 */ |
| 297 | if ((i = stv_set_config (dev, 1, 0, 1)) < 0) |
| 298 | goto error; |
| 299 | |
| 300 | if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10) |
| 301 | goto error; |
| 302 | PDEBUG (1, "STV(i): Setting video mode."); |
| 303 | /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */ |
| 304 | if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) { |
| 305 | stop_video = 0; |
| 306 | goto error; |
| 307 | } |
| 308 | goto exit; |
| 309 | |
| 310 | error: |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 311 | kfree(buf); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | if (stop_video == 1) |
| 313 | stv_stop_video (dev); |
| 314 | return -1; |
| 315 | |
| 316 | exit: |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 317 | kfree(buf); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | static int stv_init (struct usb_stv *stv680) |
| 322 | { |
| 323 | int i = 0; |
| 324 | unsigned char *buffer; |
| 325 | unsigned long int bufsize; |
| 326 | |
Eric Sesterhenn | 80b6ca4 | 2006-02-27 21:29:43 +0100 | [diff] [blame] | 327 | buffer = kzalloc (40, GFP_KERNEL); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 | if (buffer == NULL) { |
| 329 | PDEBUG (0, "STV(e): Out of (small buf) memory"); |
| 330 | return -1; |
| 331 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | udelay (100); |
| 333 | |
| 334 | /* set config 1, interface 0, alternate 0 */ |
| 335 | if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) { |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 336 | kfree(buffer); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 | PDEBUG (0, "STV(e): set config 1,0,0 failed"); |
| 338 | return -1; |
| 339 | } |
| 340 | /* ping camera to be sure STV0680 is present */ |
| 341 | if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02) |
| 342 | goto error; |
| 343 | if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) { |
| 344 | PDEBUG (1, "STV(e): camera ping failed!!"); |
| 345 | goto error; |
| 346 | } |
| 347 | |
| 348 | /* get camera descriptor */ |
| 349 | if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09) |
| 350 | goto error; |
| 351 | i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22); |
| 352 | if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) { |
| 353 | PDEBUG (1, "STV(e): Could not get descriptor 0200."); |
| 354 | goto error; |
| 355 | } |
| 356 | if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02) |
| 357 | goto error; |
| 358 | if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24) |
| 359 | goto error; |
| 360 | if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) |
| 361 | goto error; |
| 362 | |
| 363 | stv680->SupportedModes = buffer[7]; |
| 364 | i = stv680->SupportedModes; |
| 365 | stv680->CIF = 0; |
| 366 | stv680->VGA = 0; |
| 367 | stv680->QVGA = 0; |
| 368 | if (i & 1) |
| 369 | stv680->CIF = 1; |
| 370 | if (i & 2) |
| 371 | stv680->VGA = 1; |
| 372 | if (i & 8) |
| 373 | stv680->QVGA = 1; |
| 374 | if (stv680->SupportedModes == 0) { |
| 375 | PDEBUG (0, "STV(e): There are NO supported STV680 modes!!"); |
| 376 | i = -1; |
| 377 | goto error; |
| 378 | } else { |
| 379 | if (stv680->CIF) |
| 380 | PDEBUG (0, "STV(i): CIF is supported"); |
| 381 | if (stv680->QVGA) |
| 382 | PDEBUG (0, "STV(i): QVGA is supported"); |
| 383 | } |
| 384 | /* FW rev, ASIC rev, sensor ID */ |
| 385 | PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]); |
| 386 | PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]); |
| 387 | PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4)); |
| 388 | |
| 389 | /* set alternate interface 1 */ |
| 390 | if ((i = stv_set_config (stv680, 1, 0, 1)) < 0) |
| 391 | goto error; |
| 392 | |
| 393 | if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) |
| 394 | goto error; |
| 395 | if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08) |
| 396 | goto error; |
| 397 | i = buffer[3]; |
| 398 | PDEBUG (0, "STV(i): Camera has %i pictures.", i); |
| 399 | |
| 400 | /* get current mode */ |
| 401 | if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08) |
| 402 | goto error; |
| 403 | stv680->origMode = buffer[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ |
| 404 | |
| 405 | /* This will attemp CIF mode, if supported. If not, set to QVGA */ |
| 406 | memset (buffer, 0, 8); |
| 407 | if (stv680->CIF) |
| 408 | buffer[0] = 0x00; |
| 409 | else if (stv680->QVGA) |
| 410 | buffer[0] = 0x03; |
| 411 | if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) { |
| 412 | PDEBUG (0, "STV(i): Set_Camera_Mode failed"); |
| 413 | i = -1; |
| 414 | goto error; |
| 415 | } |
| 416 | buffer[0] = 0xf0; |
| 417 | stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08); |
| 418 | if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) { |
| 419 | PDEBUG (0, "STV(e): Error setting camera video mode!"); |
| 420 | i = -1; |
| 421 | goto error; |
| 422 | } else { |
| 423 | if (buffer[0] == 0) { |
| 424 | stv680->VideoMode = 0x0000; |
| 425 | PDEBUG (0, "STV(i): Video Mode set to CIF"); |
| 426 | } |
| 427 | if (buffer[0] == 0x03) { |
| 428 | stv680->VideoMode = 0x0300; |
| 429 | PDEBUG (0, "STV(i): Video Mode set to QVGA"); |
| 430 | } |
| 431 | } |
| 432 | if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10) |
| 433 | goto error; |
| 434 | bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]); |
| 435 | stv680->cwidth = (buffer[4] << 8) | (buffer[5]); /* ->camera = 322, 356, 644 */ |
| 436 | stv680->cheight = (buffer[6] << 8) | (buffer[7]); /* ->camera = 242, 292, 484 */ |
| 437 | stv680->origGain = buffer[12]; |
| 438 | |
| 439 | goto exit; |
| 440 | |
| 441 | error: |
| 442 | i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02); /* Get Last Error */ |
| 443 | PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buffer[0], buffer[1]); |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 444 | kfree(buffer); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 445 | return -1; |
| 446 | |
| 447 | exit: |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 448 | kfree(buffer); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 449 | |
| 450 | /* video = 320x240, 352x288 */ |
| 451 | if (stv680->CIF == 1) { |
| 452 | stv680->maxwidth = 352; |
| 453 | stv680->maxheight = 288; |
| 454 | stv680->vwidth = 352; |
| 455 | stv680->vheight = 288; |
| 456 | } |
| 457 | if (stv680->QVGA == 1) { |
| 458 | stv680->maxwidth = 320; |
| 459 | stv680->maxheight = 240; |
| 460 | stv680->vwidth = 320; |
| 461 | stv680->vheight = 240; |
| 462 | } |
| 463 | |
| 464 | stv680->rawbufsize = bufsize; /* must be ./. by 8 */ |
| 465 | stv680->maxframesize = bufsize * 3; /* RGB size */ |
| 466 | PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight); |
| 467 | PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize); |
| 468 | |
| 469 | /* some default values */ |
| 470 | stv680->bulk_in_endpointAddr = 0x82; |
| 471 | stv680->dropped = 0; |
| 472 | stv680->error = 0; |
| 473 | stv680->framecount = 0; |
| 474 | stv680->readcount = 0; |
| 475 | stv680->streaming = 0; |
| 476 | /* bright, white, colour, hue, contrast are set by software, not in stv0680 */ |
| 477 | stv680->brightness = 32767; |
| 478 | stv680->chgbright = 0; |
| 479 | stv680->whiteness = 0; /* only for greyscale */ |
| 480 | stv680->colour = 32767; |
| 481 | stv680->contrast = 32767; |
| 482 | stv680->hue = 32767; |
| 483 | stv680->palette = STV_VIDEO_PALETTE; |
| 484 | stv680->depth = 24; /* rgb24 bits */ |
| 485 | if ((swapRGB_on == 0) && (swapRGB == 0)) |
| 486 | PDEBUG (1, "STV(i): swapRGB is (auto) OFF"); |
| 487 | else if ((swapRGB_on == 0) && (swapRGB == 1)) |
| 488 | PDEBUG (1, "STV(i): swapRGB is (auto) ON"); |
| 489 | else if (swapRGB_on == 1) |
| 490 | PDEBUG (1, "STV(i): swapRGB is (forced) ON"); |
| 491 | else if (swapRGB_on == -1) |
| 492 | PDEBUG (1, "STV(i): swapRGB is (forced) OFF"); |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 493 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 494 | if (stv_set_video_mode (stv680) < 0) { |
| 495 | PDEBUG (0, "STV(e): Could not set video mode in stv_init"); |
| 496 | return -1; |
| 497 | } |
| 498 | |
| 499 | return 0; |
| 500 | } |
| 501 | |
| 502 | /***************** last of pencam routines *******************/ |
| 503 | |
| 504 | /**************************************************************************** |
| 505 | * sysfs |
| 506 | ***************************************************************************/ |
| 507 | #define stv680_file(name, variable, field) \ |
Kay Sievers | 54bd5b6 | 2007-10-08 16:26:13 -0300 | [diff] [blame] | 508 | static ssize_t show_##name(struct device *class_dev, \ |
| 509 | struct device_attribute *attr, char *buf) \ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 | { \ |
| 511 | struct video_device *vdev = to_video_device(class_dev); \ |
| 512 | struct usb_stv *stv = video_get_drvdata(vdev); \ |
| 513 | return sprintf(buf, field, stv->variable); \ |
| 514 | } \ |
Kay Sievers | 54bd5b6 | 2007-10-08 16:26:13 -0300 | [diff] [blame] | 515 | static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 516 | |
| 517 | stv680_file(model, camera_name, "%s\n"); |
| 518 | stv680_file(in_use, user, "%d\n"); |
| 519 | stv680_file(streaming, streaming, "%d\n"); |
| 520 | stv680_file(palette, palette, "%i\n"); |
| 521 | stv680_file(frames_total, readcount, "%d\n"); |
| 522 | stv680_file(frames_read, framecount, "%d\n"); |
| 523 | stv680_file(packets_dropped, dropped, "%d\n"); |
| 524 | stv680_file(decoding_errors, error, "%d\n"); |
| 525 | |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 526 | static int stv680_create_sysfs_files(struct video_device *vdev) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 527 | { |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 528 | int rc; |
| 529 | |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 530 | rc = device_create_file(&vdev->dev, &dev_attr_model); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 531 | if (rc) goto err; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 532 | rc = device_create_file(&vdev->dev, &dev_attr_in_use); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 533 | if (rc) goto err_model; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 534 | rc = device_create_file(&vdev->dev, &dev_attr_streaming); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 535 | if (rc) goto err_inuse; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 536 | rc = device_create_file(&vdev->dev, &dev_attr_palette); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 537 | if (rc) goto err_stream; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 538 | rc = device_create_file(&vdev->dev, &dev_attr_frames_total); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 539 | if (rc) goto err_pal; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 540 | rc = device_create_file(&vdev->dev, &dev_attr_frames_read); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 541 | if (rc) goto err_framtot; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 542 | rc = device_create_file(&vdev->dev, &dev_attr_packets_dropped); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 543 | if (rc) goto err_framread; |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 544 | rc = device_create_file(&vdev->dev, &dev_attr_decoding_errors); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 545 | if (rc) goto err_dropped; |
| 546 | |
| 547 | return 0; |
| 548 | |
| 549 | err_dropped: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 550 | device_remove_file(&vdev->dev, &dev_attr_packets_dropped); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 551 | err_framread: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 552 | device_remove_file(&vdev->dev, &dev_attr_frames_read); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 553 | err_framtot: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 554 | device_remove_file(&vdev->dev, &dev_attr_frames_total); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 555 | err_pal: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 556 | device_remove_file(&vdev->dev, &dev_attr_palette); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 557 | err_stream: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 558 | device_remove_file(&vdev->dev, &dev_attr_streaming); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 559 | err_inuse: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 560 | device_remove_file(&vdev->dev, &dev_attr_in_use); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 561 | err_model: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 562 | device_remove_file(&vdev->dev, &dev_attr_model); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 563 | err: |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 564 | PDEBUG(0, "STV(e): Could not create sysfs files"); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 565 | return rc; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 566 | } |
| 567 | |
| 568 | static void stv680_remove_sysfs_files(struct video_device *vdev) |
| 569 | { |
Hans Verkuil | f894dfd | 2008-07-25 07:39:54 -0300 | [diff] [blame] | 570 | device_remove_file(&vdev->dev, &dev_attr_model); |
| 571 | device_remove_file(&vdev->dev, &dev_attr_in_use); |
| 572 | device_remove_file(&vdev->dev, &dev_attr_streaming); |
| 573 | device_remove_file(&vdev->dev, &dev_attr_palette); |
| 574 | device_remove_file(&vdev->dev, &dev_attr_frames_total); |
| 575 | device_remove_file(&vdev->dev, &dev_attr_frames_read); |
| 576 | device_remove_file(&vdev->dev, &dev_attr_packets_dropped); |
| 577 | device_remove_file(&vdev->dev, &dev_attr_decoding_errors); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 578 | } |
| 579 | |
| 580 | /******************************************************************** |
| 581 | * Camera control |
| 582 | *******************************************************************/ |
| 583 | |
| 584 | static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p) |
| 585 | { |
| 586 | /* This sets values for v4l interface. max/min = 65535/0 */ |
| 587 | |
| 588 | p->brightness = stv680->brightness; |
| 589 | p->whiteness = stv680->whiteness; /* greyscale */ |
| 590 | p->colour = stv680->colour; |
| 591 | p->contrast = stv680->contrast; |
| 592 | p->hue = stv680->hue; |
| 593 | p->palette = stv680->palette; |
| 594 | p->depth = stv680->depth; |
| 595 | return 0; |
| 596 | } |
| 597 | |
| 598 | static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p) |
| 599 | { |
| 600 | /* See above stv680_get_pict */ |
| 601 | |
| 602 | if (p->palette != STV_VIDEO_PALETTE) { |
| 603 | PDEBUG (2, "STV(e): Palette set error in _set_pic"); |
| 604 | return 1; |
| 605 | } |
| 606 | |
| 607 | if (stv680->brightness != p->brightness) { |
| 608 | stv680->chgbright = 1; |
| 609 | stv680->brightness = p->brightness; |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 610 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 611 | |
| 612 | stv680->whiteness = p->whiteness; /* greyscale */ |
| 613 | stv680->colour = p->colour; |
| 614 | stv680->contrast = p->contrast; |
| 615 | stv680->hue = p->hue; |
| 616 | stv680->palette = p->palette; |
| 617 | stv680->depth = p->depth; |
| 618 | |
| 619 | return 0; |
| 620 | } |
| 621 | |
David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 622 | static void stv680_video_irq (struct urb *urb) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 623 | { |
| 624 | struct usb_stv *stv680 = urb->context; |
| 625 | int length = urb->actual_length; |
| 626 | |
| 627 | if (length < stv680->rawbufsize) |
| 628 | PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length); |
| 629 | |
| 630 | /* ohoh... */ |
| 631 | if (!stv680->streaming) |
| 632 | return; |
| 633 | |
| 634 | if (!stv680->udev) { |
| 635 | PDEBUG (0, "STV(e): device vapourished in video_irq"); |
| 636 | return; |
| 637 | } |
| 638 | |
| 639 | /* 0 sized packets happen if we are to fast, but sometimes the camera |
| 640 | keeps sending them forever... |
| 641 | */ |
| 642 | if (length && !urb->status) { |
| 643 | stv680->nullpackets = 0; |
| 644 | switch (stv680->scratch[stv680->scratch_next].state) { |
| 645 | case BUFFER_READY: |
| 646 | case BUFFER_BUSY: |
| 647 | stv680->dropped++; |
| 648 | break; |
| 649 | |
| 650 | case BUFFER_UNUSED: |
| 651 | memcpy (stv680->scratch[stv680->scratch_next].data, |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 652 | (unsigned char *) urb->transfer_buffer, length); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 653 | stv680->scratch[stv680->scratch_next].state = BUFFER_READY; |
| 654 | stv680->scratch[stv680->scratch_next].length = length; |
| 655 | if (waitqueue_active (&stv680->wq)) { |
| 656 | wake_up_interruptible (&stv680->wq); |
| 657 | } |
| 658 | stv680->scratch_overflow = 0; |
| 659 | stv680->scratch_next++; |
| 660 | if (stv680->scratch_next >= STV680_NUMSCRATCH) |
| 661 | stv680->scratch_next = 0; |
| 662 | break; |
| 663 | } /* switch */ |
| 664 | } else { |
| 665 | stv680->nullpackets++; |
| 666 | if (stv680->nullpackets > STV680_MAX_NULLPACKETS) { |
| 667 | if (waitqueue_active (&stv680->wq)) { |
| 668 | wake_up_interruptible (&stv680->wq); |
| 669 | } |
| 670 | } |
| 671 | } /* if - else */ |
| 672 | |
| 673 | /* Resubmit urb for new data */ |
| 674 | urb->status = 0; |
| 675 | urb->dev = stv680->udev; |
| 676 | if (usb_submit_urb (urb, GFP_ATOMIC)) |
| 677 | PDEBUG (0, "STV(e): urb burned down in video irq"); |
| 678 | return; |
| 679 | } /* _video_irq */ |
| 680 | |
| 681 | static int stv680_start_stream (struct usb_stv *stv680) |
| 682 | { |
| 683 | struct urb *urb; |
| 684 | int err = 0, i; |
| 685 | |
| 686 | stv680->streaming = 1; |
| 687 | |
| 688 | /* Do some memory allocation */ |
| 689 | for (i = 0; i < STV680_NUMFRAMES; i++) { |
| 690 | stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize; |
| 691 | stv680->frame[i].curpix = 0; |
| 692 | } |
| 693 | /* packet size = 4096 */ |
| 694 | for (i = 0; i < STV680_NUMSBUF; i++) { |
| 695 | stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); |
| 696 | if (stv680->sbuf[i].data == NULL) { |
| 697 | PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i); |
Amit Choudhary | c85f49d | 2006-10-17 11:39:06 -0300 | [diff] [blame] | 698 | goto nomem_err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 699 | } |
| 700 | } |
| 701 | |
| 702 | stv680->scratch_next = 0; |
| 703 | stv680->scratch_use = 0; |
| 704 | stv680->scratch_overflow = 0; |
| 705 | for (i = 0; i < STV680_NUMSCRATCH; i++) { |
| 706 | stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); |
| 707 | if (stv680->scratch[i].data == NULL) { |
| 708 | PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i); |
Amit Choudhary | c85f49d | 2006-10-17 11:39:06 -0300 | [diff] [blame] | 709 | goto nomem_err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 710 | } |
| 711 | stv680->scratch[i].state = BUFFER_UNUSED; |
| 712 | } |
| 713 | |
| 714 | for (i = 0; i < STV680_NUMSBUF; i++) { |
| 715 | urb = usb_alloc_urb (0, GFP_KERNEL); |
| 716 | if (!urb) |
Amit Choudhary | c85f49d | 2006-10-17 11:39:06 -0300 | [diff] [blame] | 717 | goto nomem_err; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 718 | |
| 719 | /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */ |
| 720 | usb_fill_bulk_urb (urb, stv680->udev, |
| 721 | usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr), |
| 722 | stv680->sbuf[i].data, stv680->rawbufsize, |
| 723 | stv680_video_irq, stv680); |
| 724 | stv680->urb[i] = urb; |
| 725 | err = usb_submit_urb (stv680->urb[i], GFP_KERNEL); |
Mauro Carvalho Chehab | 2f3ed05 | 2007-06-25 15:33:41 -0300 | [diff] [blame] | 726 | if (err) { |
| 727 | PDEBUG (0, "STV(e): urb burned down with err " |
| 728 | "%d in start stream %d", err, i); |
| 729 | goto nomem_err; |
| 730 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 731 | } /* i STV680_NUMSBUF */ |
| 732 | |
| 733 | stv680->framecount = 0; |
| 734 | return 0; |
Amit Choudhary | c85f49d | 2006-10-17 11:39:06 -0300 | [diff] [blame] | 735 | |
| 736 | nomem_err: |
Amit Choudhary | c85f49d | 2006-10-17 11:39:06 -0300 | [diff] [blame] | 737 | for (i = 0; i < STV680_NUMSBUF; i++) { |
| 738 | usb_kill_urb(stv680->urb[i]); |
| 739 | usb_free_urb(stv680->urb[i]); |
| 740 | stv680->urb[i] = NULL; |
| 741 | kfree(stv680->sbuf[i].data); |
| 742 | stv680->sbuf[i].data = NULL; |
| 743 | } |
Oliver Neukum | 085575a | 2009-07-28 11:52:10 -0300 | [diff] [blame] | 744 | /* used in irq, free only as all URBs are dead */ |
| 745 | for (i = 0; i < STV680_NUMSCRATCH; i++) { |
| 746 | kfree(stv680->scratch[i].data); |
| 747 | stv680->scratch[i].data = NULL; |
| 748 | } |
Amit Choudhary | c85f49d | 2006-10-17 11:39:06 -0300 | [diff] [blame] | 749 | return -ENOMEM; |
| 750 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 751 | } |
| 752 | |
| 753 | static int stv680_stop_stream (struct usb_stv *stv680) |
| 754 | { |
| 755 | int i; |
| 756 | |
| 757 | if (!stv680->streaming || !stv680->udev) |
| 758 | return 1; |
| 759 | |
| 760 | stv680->streaming = 0; |
| 761 | |
| 762 | for (i = 0; i < STV680_NUMSBUF; i++) |
| 763 | if (stv680->urb[i]) { |
| 764 | usb_kill_urb (stv680->urb[i]); |
| 765 | usb_free_urb (stv680->urb[i]); |
| 766 | stv680->urb[i] = NULL; |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 767 | kfree(stv680->sbuf[i].data); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 768 | } |
| 769 | for (i = 0; i < STV680_NUMSCRATCH; i++) { |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 770 | kfree(stv680->scratch[i].data); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 771 | stv680->scratch[i].data = NULL; |
| 772 | } |
| 773 | |
| 774 | return 0; |
| 775 | } |
| 776 | |
| 777 | static int stv680_set_size (struct usb_stv *stv680, int width, int height) |
| 778 | { |
| 779 | int wasstreaming = stv680->streaming; |
| 780 | |
| 781 | /* Check to see if we need to change */ |
| 782 | if ((stv680->vwidth == width) && (stv680->vheight == height)) |
| 783 | return 0; |
| 784 | |
| 785 | PDEBUG (1, "STV(i): size request for %i x %i", width, height); |
| 786 | /* Check for a valid mode */ |
| 787 | if ((!width || !height) || ((width & 1) || (height & 1))) { |
| 788 | PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight); |
| 789 | return 1; |
| 790 | } |
| 791 | |
| 792 | if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) { |
| 793 | width = stv680->maxwidth / 2; |
| 794 | height = stv680->maxheight / 2; |
| 795 | } else if ((width >= 158) && (width <= 166) && (stv680->QVGA == 1)) { |
| 796 | width = 160; |
| 797 | height = 120; |
| 798 | } else if ((width >= 172) && (width <= 180) && (stv680->CIF == 1)) { |
| 799 | width = 176; |
| 800 | height = 144; |
| 801 | } else if ((width >= 318) && (width <= 350) && (stv680->QVGA == 1)) { |
| 802 | width = 320; |
| 803 | height = 240; |
| 804 | } else if ((width >= 350) && (width <= 358) && (stv680->CIF == 1)) { |
| 805 | width = 352; |
| 806 | height = 288; |
| 807 | } else { |
| 808 | PDEBUG (1, "STV(e): request for non-supported size: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight); |
| 809 | return 1; |
| 810 | } |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 811 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 812 | /* Stop a current stream and start it again at the new size */ |
| 813 | if (wasstreaming) |
| 814 | stv680_stop_stream (stv680); |
| 815 | stv680->vwidth = width; |
| 816 | stv680->vheight = height; |
| 817 | PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight); |
| 818 | if (wasstreaming) |
| 819 | stv680_start_stream (stv680); |
| 820 | |
| 821 | return 0; |
| 822 | } |
| 823 | |
| 824 | /********************************************************************** |
| 825 | * Video Decoding |
| 826 | **********************************************************************/ |
| 827 | |
| 828 | /******* routines from the pencam program; hey, they work! ********/ |
| 829 | |
| 830 | /* |
| 831 | * STV0680 Vision Camera Chipset Driver |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 832 | * Copyright (C) 2000 Adam Harrison <adam@antispin.org> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 833 | */ |
| 834 | |
| 835 | #define RED 0 |
| 836 | #define GREEN 1 |
| 837 | #define BLUE 2 |
| 838 | #define AD(x, y, w) (((y)*(w)+(x))*3) |
| 839 | |
| 840 | static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer) |
| 841 | { |
| 842 | int x, y, i; |
| 843 | int w = stv680->cwidth; |
| 844 | int vw = stv680->cwidth, vh = stv680->cheight; |
| 845 | unsigned int p = 0; |
| 846 | int colour = 0, bayer = 0; |
| 847 | unsigned char *raw = buffer->data; |
| 848 | struct stv680_frame *frame = &stv680->frame[stv680->curframe]; |
| 849 | unsigned char *output = frame->data; |
| 850 | unsigned char *temp = frame->data; |
| 851 | int offset = buffer->offset; |
| 852 | |
| 853 | if (frame->curpix == 0) { |
| 854 | if (frame->grabstate == FRAME_READY) { |
| 855 | frame->grabstate = FRAME_GRABBING; |
| 856 | } |
| 857 | } |
| 858 | if (offset != frame->curpix) { /* Regard frame as lost :( */ |
| 859 | frame->curpix = 0; |
| 860 | stv680->error++; |
| 861 | return; |
| 862 | } |
| 863 | |
| 864 | if ((stv680->vwidth == 320) || (stv680->vwidth == 160)) { |
| 865 | vw = 320; |
| 866 | vh = 240; |
| 867 | } |
| 868 | if ((stv680->vwidth == 352) || (stv680->vwidth == 176)) { |
| 869 | vw = 352; |
| 870 | vh = 288; |
| 871 | } |
| 872 | |
| 873 | memset (output, 0, 3 * vw * vh); /* clear output matrix. */ |
| 874 | |
| 875 | for (y = 0; y < vh; y++) { |
| 876 | for (x = 0; x < vw; x++) { |
| 877 | if (x & 1) |
| 878 | p = *(raw + y * w + (x >> 1)); |
| 879 | else |
| 880 | p = *(raw + y * w + (x >> 1) + (w >> 1)); |
| 881 | |
| 882 | if (y & 1) |
| 883 | bayer = 2; |
| 884 | else |
| 885 | bayer = 0; |
| 886 | if (x & 1) |
| 887 | bayer++; |
| 888 | |
| 889 | switch (bayer) { |
| 890 | case 0: |
| 891 | case 3: |
| 892 | colour = 1; |
| 893 | break; |
| 894 | case 1: |
| 895 | colour = 0; |
| 896 | break; |
| 897 | case 2: |
| 898 | colour = 2; |
| 899 | break; |
| 900 | } |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 901 | i = (y * vw + x) * 3; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 902 | *(output + i + colour) = (unsigned char) p; |
| 903 | } /* for x */ |
| 904 | |
| 905 | } /* for y */ |
| 906 | |
| 907 | /****** gamma correction plus hardcoded white balance */ |
| 908 | /* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code. |
Mauro Carvalho Chehab | d56410e | 2006-03-25 09:19:53 -0300 | [diff] [blame] | 909 | Correction values red[], green[], blue[], are generated by |
| 910 | (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255. |
| 911 | White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 912 | converted to unsigned char. Values are in stv680.h */ |
| 913 | |
| 914 | for (y = 0; y < vh; y++) { |
| 915 | for (x = 0; x < vw; x++) { |
| 916 | i = (y * vw + x) * 3; |
| 917 | *(output + i) = red[*(output + i)]; |
| 918 | *(output + i + 1) = green[*(output + i + 1)]; |
| 919 | *(output + i + 2) = blue[*(output + i + 2)]; |
| 920 | } |
| 921 | } |
| 922 | |
| 923 | /****** bayer demosaic ******/ |
| 924 | for (y = 1; y < (vh - 1); y++) { |
| 925 | for (x = 1; x < (vw - 1); x++) { /* work out pixel type */ |
| 926 | if (y & 1) |
| 927 | bayer = 0; |
| 928 | else |
| 929 | bayer = 2; |
| 930 | if (!(x & 1)) |
| 931 | bayer++; |
| 932 | |
| 933 | switch (bayer) { |
| 934 | case 0: /* green. blue lr, red tb */ |
| 935 | *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y, vw) + BLUE) + (int) *(output + AD (x + 1, y, vw) + BLUE)) >> 1; |
| 936 | *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1; |
| 937 | break; |
| 938 | |
| 939 | case 1: /* blue. green lrtb, red diagonals */ |
| 940 | *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2; |
| 941 | *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2; |
| 942 | break; |
| 943 | |
| 944 | case 2: /* red. green lrtb, blue diagonals */ |
| 945 | *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2; |
| 946 | *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2; |
| 947 | break; |
| 948 | |
| 949 | case 3: /* green. red lr, blue tb */ |
| 950 | *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1; |
| 951 | *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1; |
| 952 | break; |
| 953 | } /* switch */ |
| 954 | } /* for x */ |
| 955 | } /* for y - end demosaic */ |
| 956 | |
| 957 | /* fix top and bottom row, left and right side */ |
| 958 | i = vw * 3; |
| 959 | memcpy (output, (output + i), i); |
| 960 | memcpy ((output + (vh * i)), (output + ((vh - 1) * i)), i); |
| 961 | for (y = 0; y < vh; y++) { |
| 962 | i = y * vw * 3; |
| 963 | memcpy ((output + i), (output + i + 3), 3); |
| 964 | memcpy ((output + i + (vw * 3)), (output + i + (vw - 1) * 3), 3); |
| 965 | } |
| 966 | |
| 967 | /* process all raw data, then trim to size if necessary */ |
| 968 | if ((stv680->vwidth == 160) || (stv680->vwidth == 176)) { |
| 969 | i = 0; |
| 970 | for (y = 0; y < vh; y++) { |
| 971 | if (!(y & 1)) { |
| 972 | for (x = 0; x < vw; x++) { |
| 973 | p = (y * vw + x) * 3; |
| 974 | if (!(x & 1)) { |
| 975 | *(output + i) = *(output + p); |
| 976 | *(output + i + 1) = *(output + p + 1); |
| 977 | *(output + i + 2) = *(output + p + 2); |
| 978 | i += 3; |
| 979 | } |
| 980 | } /* for x */ |
| 981 | } |
| 982 | } /* for y */ |
| 983 | } |
| 984 | /* reset to proper width */ |
| 985 | if ((stv680->vwidth == 160)) { |
| 986 | vw = 160; |
| 987 | vh = 120; |
| 988 | } |
| 989 | if ((stv680->vwidth == 176)) { |
| 990 | vw = 176; |
| 991 | vh = 144; |
| 992 | } |
| 993 | |
| 994 | /* output is RGB; some programs want BGR */ |
| 995 | /* swapRGB_on=0 -> program decides; swapRGB_on=1, always swap */ |
| 996 | /* swapRGB_on=-1, never swap */ |
| 997 | if (((swapRGB == 1) && (swapRGB_on != -1)) || (swapRGB_on == 1)) { |
| 998 | for (y = 0; y < vh; y++) { |
| 999 | for (x = 0; x < vw; x++) { |
| 1000 | i = (y * vw + x) * 3; |
| 1001 | *(temp) = *(output + i); |
| 1002 | *(output + i) = *(output + i + 2); |
| 1003 | *(output + i + 2) = *(temp); |
| 1004 | } |
| 1005 | } |
| 1006 | } |
| 1007 | /* brightness */ |
| 1008 | if (stv680->chgbright == 1) { |
| 1009 | if (stv680->brightness >= 32767) { |
| 1010 | p = (stv680->brightness - 32767) / 256; |
| 1011 | for (x = 0; x < (vw * vh * 3); x++) { |
| 1012 | if ((*(output + x) + (unsigned char) p) > 255) |
| 1013 | *(output + x) = 255; |
| 1014 | else |
| 1015 | *(output + x) += (unsigned char) p; |
| 1016 | } /* for */ |
| 1017 | } else { |
| 1018 | p = (32767 - stv680->brightness) / 256; |
| 1019 | for (x = 0; x < (vw * vh * 3); x++) { |
| 1020 | if ((unsigned char) p > *(output + x)) |
| 1021 | *(output + x) = 0; |
| 1022 | else |
| 1023 | *(output + x) -= (unsigned char) p; |
| 1024 | } /* for */ |
| 1025 | } /* else */ |
| 1026 | } |
| 1027 | /* if */ |
| 1028 | frame->curpix = 0; |
| 1029 | frame->curlinepix = 0; |
| 1030 | frame->grabstate = FRAME_DONE; |
| 1031 | stv680->framecount++; |
| 1032 | stv680->readcount++; |
| 1033 | if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) { |
| 1034 | stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1); |
| 1035 | } |
| 1036 | |
| 1037 | } /* bayer_unshuffle */ |
| 1038 | |
| 1039 | /******* end routines from the pencam program *********/ |
| 1040 | |
| 1041 | static int stv680_newframe (struct usb_stv *stv680, int framenr) |
| 1042 | { |
| 1043 | int errors = 0; |
| 1044 | |
| 1045 | while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) { |
| 1046 | if (!stv680->frame[framenr].curpix) { |
| 1047 | errors++; |
| 1048 | } |
| 1049 | wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY)); |
| 1050 | |
| 1051 | if (stv680->nullpackets > STV680_MAX_NULLPACKETS) { |
| 1052 | stv680->nullpackets = 0; |
| 1053 | PDEBUG (2, "STV(i): too many null length packets, restarting capture"); |
| 1054 | stv680_stop_stream (stv680); |
| 1055 | stv680_start_stream (stv680); |
| 1056 | } else { |
| 1057 | if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) { |
| 1058 | stv680->frame[framenr].grabstate = FRAME_ERROR; |
| 1059 | PDEBUG (2, "STV(e): FRAME_ERROR in _newframe"); |
| 1060 | return -EIO; |
| 1061 | } |
| 1062 | stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY; |
| 1063 | |
| 1064 | bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]); |
| 1065 | |
| 1066 | stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED; |
| 1067 | stv680->scratch_use++; |
| 1068 | if (stv680->scratch_use >= STV680_NUMSCRATCH) |
| 1069 | stv680->scratch_use = 0; |
| 1070 | if (errors > STV680_MAX_ERRORS) { |
| 1071 | errors = 0; |
| 1072 | PDEBUG (2, "STV(i): too many errors, restarting capture"); |
| 1073 | stv680_stop_stream (stv680); |
| 1074 | stv680_start_stream (stv680); |
| 1075 | } |
| 1076 | } /* else */ |
| 1077 | } /* while */ |
| 1078 | return 0; |
| 1079 | } |
| 1080 | |
| 1081 | /********************************************************************* |
| 1082 | * Video4Linux |
| 1083 | *********************************************************************/ |
| 1084 | |
Hans Verkuil | bec4366 | 2008-12-30 06:58:20 -0300 | [diff] [blame] | 1085 | static int stv_open(struct file *file) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1086 | { |
| 1087 | struct video_device *dev = video_devdata(file); |
| 1088 | struct usb_stv *stv680 = video_get_drvdata(dev); |
| 1089 | int err = 0; |
| 1090 | |
| 1091 | /* we are called with the BKL held */ |
Hans Verkuil | d56dc61 | 2008-07-30 08:43:36 -0300 | [diff] [blame] | 1092 | lock_kernel(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1093 | stv680->user = 1; |
| 1094 | err = stv_init (stv680); /* main initialization routine for camera */ |
| 1095 | |
| 1096 | if (err >= 0) { |
| 1097 | stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES); |
| 1098 | if (!stv680->fbuf) { |
| 1099 | PDEBUG (0, "STV(e): Could not rvmalloc frame bufer"); |
| 1100 | err = -ENOMEM; |
| 1101 | } |
| 1102 | file->private_data = dev; |
| 1103 | } |
| 1104 | if (err) |
| 1105 | stv680->user = 0; |
Hans Verkuil | d56dc61 | 2008-07-30 08:43:36 -0300 | [diff] [blame] | 1106 | unlock_kernel(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1107 | |
| 1108 | return err; |
| 1109 | } |
| 1110 | |
Hans Verkuil | bec4366 | 2008-12-30 06:58:20 -0300 | [diff] [blame] | 1111 | static int stv_close(struct file *file) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1112 | { |
| 1113 | struct video_device *dev = file->private_data; |
| 1114 | struct usb_stv *stv680 = video_get_drvdata(dev); |
| 1115 | int i; |
| 1116 | |
| 1117 | for (i = 0; i < STV680_NUMFRAMES; i++) |
| 1118 | stv680->frame[i].grabstate = FRAME_UNUSED; |
| 1119 | if (stv680->streaming) |
| 1120 | stv680_stop_stream (stv680); |
| 1121 | |
| 1122 | if ((i = stv_stop_video (stv680)) < 0) |
| 1123 | PDEBUG (1, "STV(e): stop_video failed in stv_close"); |
| 1124 | |
| 1125 | rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES); |
| 1126 | stv680->user = 0; |
| 1127 | |
| 1128 | if (stv680->removed) { |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 1129 | kfree(stv680); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1130 | stv680 = NULL; |
| 1131 | PDEBUG (0, "STV(i): device unregistered"); |
| 1132 | } |
| 1133 | file->private_data = NULL; |
| 1134 | return 0; |
| 1135 | } |
| 1136 | |
Hans Verkuil | 069b747 | 2008-12-30 07:04:34 -0300 | [diff] [blame] | 1137 | static long stv680_do_ioctl(struct file *file, unsigned int cmd, void *arg) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1138 | { |
| 1139 | struct video_device *vdev = file->private_data; |
| 1140 | struct usb_stv *stv680 = video_get_drvdata(vdev); |
| 1141 | |
| 1142 | if (!stv680->udev) |
| 1143 | return -EIO; |
| 1144 | |
| 1145 | switch (cmd) { |
| 1146 | case VIDIOCGCAP:{ |
| 1147 | struct video_capability *b = arg; |
| 1148 | |
| 1149 | strcpy (b->name, stv680->camera_name); |
| 1150 | b->type = VID_TYPE_CAPTURE; |
| 1151 | b->channels = 1; |
| 1152 | b->audios = 0; |
| 1153 | b->maxwidth = stv680->maxwidth; |
| 1154 | b->maxheight = stv680->maxheight; |
| 1155 | b->minwidth = stv680->maxwidth / 2; |
| 1156 | b->minheight = stv680->maxheight / 2; |
| 1157 | return 0; |
| 1158 | } |
| 1159 | case VIDIOCGCHAN:{ |
| 1160 | struct video_channel *v = arg; |
| 1161 | |
| 1162 | if (v->channel != 0) |
| 1163 | return -EINVAL; |
| 1164 | v->flags = 0; |
| 1165 | v->tuners = 0; |
| 1166 | v->type = VIDEO_TYPE_CAMERA; |
| 1167 | strcpy (v->name, "STV Camera"); |
| 1168 | return 0; |
| 1169 | } |
| 1170 | case VIDIOCSCHAN:{ |
| 1171 | struct video_channel *v = arg; |
| 1172 | if (v->channel != 0) |
| 1173 | return -EINVAL; |
| 1174 | return 0; |
| 1175 | } |
| 1176 | case VIDIOCGPICT:{ |
| 1177 | struct video_picture *p = arg; |
| 1178 | |
| 1179 | stv680_get_pict (stv680, p); |
| 1180 | return 0; |
| 1181 | } |
| 1182 | case VIDIOCSPICT:{ |
| 1183 | struct video_picture *p = arg; |
| 1184 | |
| 1185 | if (stv680_set_pict (stv680, p)) |
| 1186 | return -EINVAL; |
| 1187 | return 0; |
| 1188 | } |
| 1189 | case VIDIOCSWIN:{ |
| 1190 | struct video_window *vw = arg; |
| 1191 | |
| 1192 | if (vw->flags) |
| 1193 | return -EINVAL; |
| 1194 | if (vw->clipcount) |
| 1195 | return -EINVAL; |
| 1196 | if (vw->width != stv680->vwidth) { |
| 1197 | if (stv680_set_size (stv680, vw->width, vw->height)) { |
| 1198 | PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN"); |
| 1199 | return -EINVAL; |
| 1200 | } |
| 1201 | } |
| 1202 | return 0; |
| 1203 | } |
| 1204 | case VIDIOCGWIN:{ |
| 1205 | struct video_window *vw = arg; |
| 1206 | |
| 1207 | vw->x = 0; /* FIXME */ |
| 1208 | vw->y = 0; |
| 1209 | vw->chromakey = 0; |
| 1210 | vw->flags = 0; |
| 1211 | vw->clipcount = 0; |
| 1212 | vw->width = stv680->vwidth; |
| 1213 | vw->height = stv680->vheight; |
| 1214 | return 0; |
| 1215 | } |
| 1216 | case VIDIOCGMBUF:{ |
| 1217 | struct video_mbuf *vm = arg; |
| 1218 | int i; |
| 1219 | |
| 1220 | memset (vm, 0, sizeof (*vm)); |
| 1221 | vm->size = STV680_NUMFRAMES * stv680->maxframesize; |
| 1222 | vm->frames = STV680_NUMFRAMES; |
| 1223 | for (i = 0; i < STV680_NUMFRAMES; i++) |
| 1224 | vm->offsets[i] = stv680->maxframesize * i; |
| 1225 | return 0; |
| 1226 | } |
| 1227 | case VIDIOCMCAPTURE:{ |
| 1228 | struct video_mmap *vm = arg; |
| 1229 | |
| 1230 | if (vm->format != STV_VIDEO_PALETTE) { |
| 1231 | PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)", |
| 1232 | vm->format, STV_VIDEO_PALETTE); |
| 1233 | if ((vm->format == 3) && (swapRGB_on == 0)) { |
| 1234 | PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON"); |
| 1235 | /* this may fix those apps (e.g., xawtv) that want BGR */ |
| 1236 | swapRGB = 1; |
| 1237 | } |
| 1238 | return -EINVAL; |
| 1239 | } |
| 1240 | if (vm->frame >= STV680_NUMFRAMES) { |
| 1241 | PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES"); |
| 1242 | return -EINVAL; |
| 1243 | } |
| 1244 | if ((stv680->frame[vm->frame].grabstate == FRAME_ERROR) |
| 1245 | || (stv680->frame[vm->frame].grabstate == FRAME_GRABBING)) { |
| 1246 | PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate (%i) error", |
| 1247 | stv680->frame[vm->frame].grabstate); |
| 1248 | return -EBUSY; |
| 1249 | } |
| 1250 | /* Is this according to the v4l spec??? */ |
| 1251 | if (stv680->vwidth != vm->width) { |
| 1252 | if (stv680_set_size (stv680, vm->width, vm->height)) { |
| 1253 | PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed"); |
| 1254 | return -EINVAL; |
| 1255 | } |
| 1256 | } |
| 1257 | stv680->frame[vm->frame].grabstate = FRAME_READY; |
| 1258 | |
| 1259 | if (!stv680->streaming) |
| 1260 | stv680_start_stream (stv680); |
| 1261 | |
| 1262 | return 0; |
| 1263 | } |
| 1264 | case VIDIOCSYNC:{ |
| 1265 | int *frame = arg; |
| 1266 | int ret = 0; |
| 1267 | |
| 1268 | if (*frame < 0 || *frame >= STV680_NUMFRAMES) { |
| 1269 | PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC"); |
| 1270 | return -EINVAL; |
| 1271 | } |
| 1272 | ret = stv680_newframe (stv680, *frame); |
| 1273 | stv680->frame[*frame].grabstate = FRAME_UNUSED; |
| 1274 | return ret; |
| 1275 | } |
| 1276 | case VIDIOCGFBUF:{ |
| 1277 | struct video_buffer *vb = arg; |
| 1278 | |
| 1279 | memset (vb, 0, sizeof (*vb)); |
| 1280 | return 0; |
| 1281 | } |
| 1282 | case VIDIOCKEY: |
| 1283 | return 0; |
| 1284 | case VIDIOCCAPTURE: |
| 1285 | { |
| 1286 | PDEBUG (2, "STV(e): VIDIOCCAPTURE failed"); |
| 1287 | return -EINVAL; |
| 1288 | } |
| 1289 | case VIDIOCSFBUF: |
| 1290 | case VIDIOCGTUNER: |
| 1291 | case VIDIOCSTUNER: |
| 1292 | case VIDIOCGFREQ: |
| 1293 | case VIDIOCSFREQ: |
| 1294 | case VIDIOCGAUDIO: |
| 1295 | case VIDIOCSAUDIO: |
| 1296 | return -EINVAL; |
| 1297 | default: |
| 1298 | return -ENOIOCTLCMD; |
| 1299 | } /* end switch */ |
| 1300 | |
| 1301 | return 0; |
| 1302 | } |
| 1303 | |
Hans Verkuil | 069b747 | 2008-12-30 07:04:34 -0300 | [diff] [blame] | 1304 | static long stv680_ioctl(struct file *file, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1305 | unsigned int cmd, unsigned long arg) |
| 1306 | { |
Hans Verkuil | f473bf7 | 2008-11-01 08:25:11 -0300 | [diff] [blame] | 1307 | return video_usercopy(file, cmd, arg, stv680_do_ioctl); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1308 | } |
| 1309 | |
| 1310 | static int stv680_mmap (struct file *file, struct vm_area_struct *vma) |
| 1311 | { |
| 1312 | struct video_device *dev = file->private_data; |
| 1313 | struct usb_stv *stv680 = video_get_drvdata(dev); |
| 1314 | unsigned long start = vma->vm_start; |
| 1315 | unsigned long size = vma->vm_end-vma->vm_start; |
| 1316 | unsigned long page, pos; |
| 1317 | |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 1318 | mutex_lock(&stv680->lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1319 | |
| 1320 | if (stv680->udev == NULL) { |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 1321 | mutex_unlock(&stv680->lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1322 | return -EIO; |
| 1323 | } |
| 1324 | if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1) |
| 1325 | & ~(PAGE_SIZE - 1))) { |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 1326 | mutex_unlock(&stv680->lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1327 | return -EINVAL; |
| 1328 | } |
| 1329 | pos = (unsigned long) stv680->fbuf; |
| 1330 | while (size > 0) { |
| 1331 | page = vmalloc_to_pfn((void *)pos); |
| 1332 | if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 1333 | mutex_unlock(&stv680->lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1334 | return -EAGAIN; |
| 1335 | } |
| 1336 | start += PAGE_SIZE; |
| 1337 | pos += PAGE_SIZE; |
| 1338 | if (size > PAGE_SIZE) |
| 1339 | size -= PAGE_SIZE; |
| 1340 | else |
| 1341 | size = 0; |
| 1342 | } |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 1343 | mutex_unlock(&stv680->lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1344 | |
| 1345 | return 0; |
| 1346 | } |
| 1347 | |
| 1348 | static ssize_t stv680_read (struct file *file, char __user *buf, |
| 1349 | size_t count, loff_t *ppos) |
| 1350 | { |
| 1351 | struct video_device *dev = file->private_data; |
| 1352 | unsigned long int realcount = count; |
| 1353 | int ret = 0; |
| 1354 | struct usb_stv *stv680 = video_get_drvdata(dev); |
| 1355 | unsigned long int i; |
| 1356 | |
| 1357 | if (STV680_NUMFRAMES != 2) { |
| 1358 | PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!"); |
| 1359 | return -1; |
| 1360 | } |
| 1361 | if (stv680->udev == NULL) |
| 1362 | return -EIO; |
| 1363 | if (realcount > (stv680->vwidth * stv680->vheight * 3)) |
| 1364 | realcount = stv680->vwidth * stv680->vheight * 3; |
| 1365 | |
| 1366 | /* Shouldn't happen: */ |
| 1367 | if (stv680->frame[0].grabstate == FRAME_GRABBING) { |
| 1368 | PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read"); |
| 1369 | return -EBUSY; |
| 1370 | } |
| 1371 | stv680->frame[0].grabstate = FRAME_READY; |
| 1372 | stv680->frame[1].grabstate = FRAME_UNUSED; |
| 1373 | stv680->curframe = 0; |
| 1374 | |
| 1375 | if (!stv680->streaming) |
| 1376 | stv680_start_stream (stv680); |
| 1377 | |
| 1378 | if (!stv680->streaming) { |
| 1379 | ret = stv680_newframe (stv680, 0); /* ret should = 0 */ |
| 1380 | } |
| 1381 | |
| 1382 | ret = stv680_newframe (stv680, 0); |
| 1383 | |
| 1384 | if (!ret) { |
| 1385 | if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) { |
| 1386 | PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i); |
| 1387 | return -EFAULT; |
| 1388 | } |
| 1389 | } else { |
| 1390 | realcount = ret; |
| 1391 | } |
| 1392 | stv680->frame[0].grabstate = FRAME_UNUSED; |
| 1393 | return realcount; |
| 1394 | } /* stv680_read */ |
| 1395 | |
Hans Verkuil | bec4366 | 2008-12-30 06:58:20 -0300 | [diff] [blame] | 1396 | static const struct v4l2_file_operations stv680_fops = { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1397 | .owner = THIS_MODULE, |
| 1398 | .open = stv_open, |
| 1399 | .release = stv_close, |
| 1400 | .read = stv680_read, |
| 1401 | .mmap = stv680_mmap, |
| 1402 | .ioctl = stv680_ioctl, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1403 | }; |
| 1404 | static struct video_device stv680_template = { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1405 | .name = "STV0680 USB camera", |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1406 | .fops = &stv680_fops, |
| 1407 | .release = video_device_release, |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1408 | }; |
| 1409 | |
| 1410 | static int stv680_probe (struct usb_interface *intf, const struct usb_device_id *id) |
| 1411 | { |
| 1412 | struct usb_device *dev = interface_to_usbdev(intf); |
| 1413 | struct usb_host_interface *interface; |
| 1414 | struct usb_stv *stv680 = NULL; |
| 1415 | char *camera_name = NULL; |
| 1416 | int retval = 0; |
| 1417 | |
| 1418 | /* We don't handle multi-config cameras */ |
| 1419 | if (dev->descriptor.bNumConfigurations != 1) { |
| 1420 | PDEBUG (0, "STV(e): Number of Configurations != 1"); |
| 1421 | return -ENODEV; |
| 1422 | } |
| 1423 | |
| 1424 | interface = &intf->altsetting[0]; |
| 1425 | /* Is it a STV680? */ |
| 1426 | if ((le16_to_cpu(dev->descriptor.idVendor) == USB_PENCAM_VENDOR_ID) && |
| 1427 | (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) { |
| 1428 | camera_name = "STV0680"; |
| 1429 | PDEBUG (0, "STV(i): STV0680 camera found."); |
Kiril Jovchev | 1636787 | 2005-06-05 01:52:33 +0300 | [diff] [blame] | 1430 | } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) && |
| 1431 | (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) { |
| 1432 | camera_name = "Creative WebCam Go Mini"; |
| 1433 | PDEBUG (0, "STV(i): Creative WebCam Go Mini found."); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1434 | } else { |
Kiril Jovchev | 1636787 | 2005-06-05 01:52:33 +0300 | [diff] [blame] | 1435 | PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values."); |
| 1436 | PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer."); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1437 | retval = -ENODEV; |
| 1438 | goto error; |
| 1439 | } |
| 1440 | /* We found one */ |
Eric Sesterhenn | 80b6ca4 | 2006-02-27 21:29:43 +0100 | [diff] [blame] | 1441 | if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1442 | PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct."); |
| 1443 | retval = -ENOMEM; |
| 1444 | goto error; |
| 1445 | } |
| 1446 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1447 | stv680->udev = dev; |
| 1448 | stv680->camera_name = camera_name; |
| 1449 | |
| 1450 | stv680->vdev = video_device_alloc(); |
| 1451 | if (!stv680->vdev) { |
| 1452 | retval = -ENOMEM; |
| 1453 | goto error; |
| 1454 | } |
| 1455 | memcpy(stv680->vdev, &stv680_template, sizeof(stv680_template)); |
Hans Verkuil | 5e85e73 | 2008-07-20 06:31:39 -0300 | [diff] [blame] | 1456 | stv680->vdev->parent = &intf->dev; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1457 | video_set_drvdata(stv680->vdev, stv680); |
| 1458 | |
| 1459 | memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name)); |
| 1460 | init_waitqueue_head (&stv680->wq); |
Arjan van de Ven | 4186ecf | 2006-01-11 15:55:29 +0100 | [diff] [blame] | 1461 | mutex_init (&stv680->lock); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1462 | wmb (); |
| 1463 | |
Hans Verkuil | dc60de3 | 2008-09-03 17:11:58 -0300 | [diff] [blame] | 1464 | if (video_register_device(stv680->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1465 | PDEBUG (0, "STV(e): video_register_device failed"); |
| 1466 | retval = -EIO; |
| 1467 | goto error_vdev; |
| 1468 | } |
Laurent Pinchart | 38c7c03 | 2009-11-27 13:57:15 -0300 | [diff] [blame] | 1469 | PDEBUG(0, "STV(i): registered new video device: %s", |
| 1470 | video_device_node_name(stv680->vdev)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1471 | |
| 1472 | usb_set_intfdata (intf, stv680); |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 1473 | retval = stv680_create_sysfs_files(stv680->vdev); |
| 1474 | if (retval) |
| 1475 | goto error_unreg; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1476 | return 0; |
| 1477 | |
Jeff Garzik | 2444a2f | 2006-10-10 15:09:43 -0300 | [diff] [blame] | 1478 | error_unreg: |
| 1479 | video_unregister_device(stv680->vdev); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1480 | error_vdev: |
| 1481 | video_device_release(stv680->vdev); |
| 1482 | error: |
| 1483 | kfree(stv680); |
| 1484 | return retval; |
| 1485 | } |
| 1486 | |
| 1487 | static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680) |
| 1488 | { |
| 1489 | int i; |
| 1490 | |
| 1491 | stv680->udev = NULL; |
| 1492 | stv680->frame[0].grabstate = FRAME_ERROR; |
| 1493 | stv680->frame[1].grabstate = FRAME_ERROR; |
| 1494 | stv680->streaming = 0; |
| 1495 | |
| 1496 | wake_up_interruptible (&stv680->wq); |
| 1497 | |
| 1498 | for (i = 0; i < STV680_NUMSBUF; i++) |
| 1499 | if (stv680->urb[i]) { |
| 1500 | usb_kill_urb (stv680->urb[i]); |
| 1501 | usb_free_urb (stv680->urb[i]); |
| 1502 | stv680->urb[i] = NULL; |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 1503 | kfree(stv680->sbuf[i].data); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1504 | } |
| 1505 | for (i = 0; i < STV680_NUMSCRATCH; i++) |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 1506 | kfree(stv680->scratch[i].data); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1507 | PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name); |
| 1508 | |
| 1509 | /* Free the memory */ |
Jesper Juhl | f9101210 | 2005-09-10 00:26:54 -0700 | [diff] [blame] | 1510 | kfree(stv680); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1511 | } |
| 1512 | |
| 1513 | static void stv680_disconnect (struct usb_interface *intf) |
| 1514 | { |
| 1515 | struct usb_stv *stv680 = usb_get_intfdata (intf); |
| 1516 | |
| 1517 | usb_set_intfdata (intf, NULL); |
| 1518 | |
| 1519 | if (stv680) { |
| 1520 | /* We don't want people trying to open up the device */ |
| 1521 | if (stv680->vdev) { |
| 1522 | stv680_remove_sysfs_files(stv680->vdev); |
| 1523 | video_unregister_device(stv680->vdev); |
| 1524 | stv680->vdev = NULL; |
| 1525 | } |
| 1526 | if (!stv680->user) { |
| 1527 | usb_stv680_remove_disconnected (stv680); |
| 1528 | } else { |
| 1529 | stv680->removed = 1; |
| 1530 | } |
| 1531 | } |
| 1532 | } |
| 1533 | |
| 1534 | static struct usb_driver stv680_driver = { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1535 | .name = "stv680", |
| 1536 | .probe = stv680_probe, |
| 1537 | .disconnect = stv680_disconnect, |
| 1538 | .id_table = device_table |
| 1539 | }; |
| 1540 | |
| 1541 | /******************************************************************** |
| 1542 | * Module routines |
| 1543 | ********************************************************************/ |
| 1544 | |
| 1545 | static int __init usb_stv680_init (void) |
| 1546 | { |
| 1547 | if (usb_register (&stv680_driver) < 0) { |
| 1548 | PDEBUG (0, "STV(e): Could not setup STV0680 driver"); |
| 1549 | return -1; |
| 1550 | } |
| 1551 | PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION); |
| 1552 | |
Greg Kroah-Hartman | a482f32 | 2008-10-10 05:08:23 -0300 | [diff] [blame] | 1553 | printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" |
| 1554 | DRIVER_DESC "\n"); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1555 | return 0; |
| 1556 | } |
| 1557 | |
| 1558 | static void __exit usb_stv680_exit (void) |
| 1559 | { |
| 1560 | usb_deregister (&stv680_driver); |
| 1561 | PDEBUG (0, "STV(i): driver deregistered"); |
| 1562 | } |
| 1563 | |
| 1564 | module_init (usb_stv680_init); |
| 1565 | module_exit (usb_stv680_exit); |