10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * Benq DC E300 subdriver 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) 50c0d06caSMauro Carvalho Chehab * 60c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 70c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 80c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 90c0d06caSMauro Carvalho Chehab * any later version. 100c0d06caSMauro Carvalho Chehab * 110c0d06caSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 120c0d06caSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 130c0d06caSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 140c0d06caSMauro Carvalho Chehab * GNU General Public License for more details. 150c0d06caSMauro Carvalho Chehab * 160c0d06caSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 170c0d06caSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 180c0d06caSMauro Carvalho Chehab * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 190c0d06caSMauro Carvalho Chehab */ 200c0d06caSMauro Carvalho Chehab 210c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 220c0d06caSMauro Carvalho Chehab 230c0d06caSMauro Carvalho Chehab #define MODULE_NAME "benq" 240c0d06caSMauro Carvalho Chehab 250c0d06caSMauro Carvalho Chehab #include "gspca.h" 260c0d06caSMauro Carvalho Chehab 270c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); 280c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver"); 290c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 300c0d06caSMauro Carvalho Chehab 310c0d06caSMauro Carvalho Chehab /* specific webcam descriptor */ 320c0d06caSMauro Carvalho Chehab struct sd { 330c0d06caSMauro Carvalho Chehab struct gspca_dev gspca_dev; /* !! must be the first item */ 340c0d06caSMauro Carvalho Chehab }; 350c0d06caSMauro Carvalho Chehab 360c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format vga_mode[] = { 370c0d06caSMauro Carvalho Chehab {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, 380c0d06caSMauro Carvalho Chehab .bytesperline = 320, 390c0d06caSMauro Carvalho Chehab .sizeimage = 320 * 240 * 3 / 8 + 590, 400c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_JPEG}, 410c0d06caSMauro Carvalho Chehab }; 420c0d06caSMauro Carvalho Chehab 430c0d06caSMauro Carvalho Chehab static void sd_isoc_irq(struct urb *urb); 440c0d06caSMauro Carvalho Chehab 450c0d06caSMauro Carvalho Chehab /* -- write a register -- */ 460c0d06caSMauro Carvalho Chehab static void reg_w(struct gspca_dev *gspca_dev, 470c0d06caSMauro Carvalho Chehab u16 value, u16 index) 480c0d06caSMauro Carvalho Chehab { 490c0d06caSMauro Carvalho Chehab struct usb_device *dev = gspca_dev->dev; 500c0d06caSMauro Carvalho Chehab int ret; 510c0d06caSMauro Carvalho Chehab 520c0d06caSMauro Carvalho Chehab if (gspca_dev->usb_err < 0) 530c0d06caSMauro Carvalho Chehab return; 540c0d06caSMauro Carvalho Chehab ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 550c0d06caSMauro Carvalho Chehab 0x02, 560c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 570c0d06caSMauro Carvalho Chehab value, 580c0d06caSMauro Carvalho Chehab index, 590c0d06caSMauro Carvalho Chehab NULL, 600c0d06caSMauro Carvalho Chehab 0, 610c0d06caSMauro Carvalho Chehab 500); 620c0d06caSMauro Carvalho Chehab if (ret < 0) { 630c0d06caSMauro Carvalho Chehab pr_err("reg_w err %d\n", ret); 640c0d06caSMauro Carvalho Chehab gspca_dev->usb_err = ret; 650c0d06caSMauro Carvalho Chehab } 660c0d06caSMauro Carvalho Chehab } 670c0d06caSMauro Carvalho Chehab 680c0d06caSMauro Carvalho Chehab /* this function is called at probe time */ 690c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev, 700c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 710c0d06caSMauro Carvalho Chehab { 720c0d06caSMauro Carvalho Chehab gspca_dev->cam.cam_mode = vga_mode; 730c0d06caSMauro Carvalho Chehab gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); 740c0d06caSMauro Carvalho Chehab gspca_dev->cam.no_urb_create = 1; 750c0d06caSMauro Carvalho Chehab return 0; 760c0d06caSMauro Carvalho Chehab } 770c0d06caSMauro Carvalho Chehab 780c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */ 790c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev) 800c0d06caSMauro Carvalho Chehab { 810c0d06caSMauro Carvalho Chehab return 0; 820c0d06caSMauro Carvalho Chehab } 830c0d06caSMauro Carvalho Chehab 840c0d06caSMauro Carvalho Chehab /* -- start the camera -- */ 850c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev) 860c0d06caSMauro Carvalho Chehab { 870c0d06caSMauro Carvalho Chehab struct urb *urb; 880c0d06caSMauro Carvalho Chehab int i, n; 890c0d06caSMauro Carvalho Chehab 900c0d06caSMauro Carvalho Chehab /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */ 910c0d06caSMauro Carvalho Chehab #if MAX_NURBS < 4 920c0d06caSMauro Carvalho Chehab #error "Not enough URBs in the gspca table" 930c0d06caSMauro Carvalho Chehab #endif 940c0d06caSMauro Carvalho Chehab #define SD_PKT_SZ 64 950c0d06caSMauro Carvalho Chehab #define SD_NPKT 32 960c0d06caSMauro Carvalho Chehab for (n = 0; n < 4; n++) { 970c0d06caSMauro Carvalho Chehab urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); 980c0d06caSMauro Carvalho Chehab if (!urb) { 990c0d06caSMauro Carvalho Chehab pr_err("usb_alloc_urb failed\n"); 1000c0d06caSMauro Carvalho Chehab return -ENOMEM; 1010c0d06caSMauro Carvalho Chehab } 1020c0d06caSMauro Carvalho Chehab gspca_dev->urb[n] = urb; 1030c0d06caSMauro Carvalho Chehab urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, 1040c0d06caSMauro Carvalho Chehab SD_PKT_SZ * SD_NPKT, 1050c0d06caSMauro Carvalho Chehab GFP_KERNEL, 1060c0d06caSMauro Carvalho Chehab &urb->transfer_dma); 1070c0d06caSMauro Carvalho Chehab 1080c0d06caSMauro Carvalho Chehab if (urb->transfer_buffer == NULL) { 1090c0d06caSMauro Carvalho Chehab pr_err("usb_alloc_coherent failed\n"); 1100c0d06caSMauro Carvalho Chehab return -ENOMEM; 1110c0d06caSMauro Carvalho Chehab } 1120c0d06caSMauro Carvalho Chehab urb->dev = gspca_dev->dev; 1130c0d06caSMauro Carvalho Chehab urb->context = gspca_dev; 1140c0d06caSMauro Carvalho Chehab urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT; 1150c0d06caSMauro Carvalho Chehab urb->pipe = usb_rcvisocpipe(gspca_dev->dev, 1160c0d06caSMauro Carvalho Chehab n & 1 ? 0x82 : 0x83); 1170c0d06caSMauro Carvalho Chehab urb->transfer_flags = URB_ISO_ASAP 1180c0d06caSMauro Carvalho Chehab | URB_NO_TRANSFER_DMA_MAP; 1190c0d06caSMauro Carvalho Chehab urb->interval = 1; 1200c0d06caSMauro Carvalho Chehab urb->complete = sd_isoc_irq; 1210c0d06caSMauro Carvalho Chehab urb->number_of_packets = SD_NPKT; 1220c0d06caSMauro Carvalho Chehab for (i = 0; i < SD_NPKT; i++) { 1230c0d06caSMauro Carvalho Chehab urb->iso_frame_desc[i].length = SD_PKT_SZ; 1240c0d06caSMauro Carvalho Chehab urb->iso_frame_desc[i].offset = SD_PKT_SZ * i; 1250c0d06caSMauro Carvalho Chehab } 1260c0d06caSMauro Carvalho Chehab } 1270c0d06caSMauro Carvalho Chehab 1280c0d06caSMauro Carvalho Chehab return gspca_dev->usb_err; 1290c0d06caSMauro Carvalho Chehab } 1300c0d06caSMauro Carvalho Chehab 1310c0d06caSMauro Carvalho Chehab static void sd_stopN(struct gspca_dev *gspca_dev) 1320c0d06caSMauro Carvalho Chehab { 1330c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 1340c0d06caSMauro Carvalho Chehab 1350c0d06caSMauro Carvalho Chehab reg_w(gspca_dev, 0x003c, 0x0003); 1360c0d06caSMauro Carvalho Chehab reg_w(gspca_dev, 0x003c, 0x0004); 1370c0d06caSMauro Carvalho Chehab reg_w(gspca_dev, 0x003c, 0x0005); 1380c0d06caSMauro Carvalho Chehab reg_w(gspca_dev, 0x003c, 0x0006); 1390c0d06caSMauro Carvalho Chehab reg_w(gspca_dev, 0x003c, 0x0007); 1400c0d06caSMauro Carvalho Chehab 1410c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); 1420c0d06caSMauro Carvalho Chehab usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1430c0d06caSMauro Carvalho Chehab intf->num_altsetting - 1); 1440c0d06caSMauro Carvalho Chehab } 1450c0d06caSMauro Carvalho Chehab 1460c0d06caSMauro Carvalho Chehab static void sd_pkt_scan(struct gspca_dev *gspca_dev, 1470c0d06caSMauro Carvalho Chehab u8 *data, /* isoc packet */ 1480c0d06caSMauro Carvalho Chehab int len) /* iso packet length */ 1490c0d06caSMauro Carvalho Chehab { 1500c0d06caSMauro Carvalho Chehab /* unused */ 1510c0d06caSMauro Carvalho Chehab } 1520c0d06caSMauro Carvalho Chehab 1530c0d06caSMauro Carvalho Chehab /* reception of an URB */ 1540c0d06caSMauro Carvalho Chehab static void sd_isoc_irq(struct urb *urb) 1550c0d06caSMauro Carvalho Chehab { 1560c0d06caSMauro Carvalho Chehab struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; 1570c0d06caSMauro Carvalho Chehab struct urb *urb0; 1580c0d06caSMauro Carvalho Chehab u8 *data; 1590c0d06caSMauro Carvalho Chehab int i, st; 1600c0d06caSMauro Carvalho Chehab 1610c0d06caSMauro Carvalho Chehab PDEBUG(D_PACK, "sd isoc irq"); 1620c0d06caSMauro Carvalho Chehab if (!gspca_dev->streaming) 1630c0d06caSMauro Carvalho Chehab return; 1640c0d06caSMauro Carvalho Chehab if (urb->status != 0) { 1650c0d06caSMauro Carvalho Chehab if (urb->status == -ESHUTDOWN) 1660c0d06caSMauro Carvalho Chehab return; /* disconnection */ 1670c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM 1680c0d06caSMauro Carvalho Chehab if (gspca_dev->frozen) 1690c0d06caSMauro Carvalho Chehab return; 1700c0d06caSMauro Carvalho Chehab #endif 1710c0d06caSMauro Carvalho Chehab pr_err("urb status: %d\n", urb->status); 1720c0d06caSMauro Carvalho Chehab return; 1730c0d06caSMauro Carvalho Chehab } 1740c0d06caSMauro Carvalho Chehab 1750c0d06caSMauro Carvalho Chehab /* if this is a control URN (ep 0x83), wait */ 1760c0d06caSMauro Carvalho Chehab if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2]) 1770c0d06caSMauro Carvalho Chehab return; 1780c0d06caSMauro Carvalho Chehab 1790c0d06caSMauro Carvalho Chehab /* scan both received URBs */ 1800c0d06caSMauro Carvalho Chehab if (urb == gspca_dev->urb[1]) 1810c0d06caSMauro Carvalho Chehab urb0 = gspca_dev->urb[0]; 1820c0d06caSMauro Carvalho Chehab else 1830c0d06caSMauro Carvalho Chehab urb0 = gspca_dev->urb[2]; 1840c0d06caSMauro Carvalho Chehab for (i = 0; i < urb->number_of_packets; i++) { 1850c0d06caSMauro Carvalho Chehab 1860c0d06caSMauro Carvalho Chehab /* check the packet status and length */ 1870c0d06caSMauro Carvalho Chehab if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ 1880c0d06caSMauro Carvalho Chehab || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) { 1890c0d06caSMauro Carvalho Chehab PDEBUG(D_ERR, "ISOC bad lengths %d / %d", 1900c0d06caSMauro Carvalho Chehab urb0->iso_frame_desc[i].actual_length, 1910c0d06caSMauro Carvalho Chehab urb->iso_frame_desc[i].actual_length); 1920c0d06caSMauro Carvalho Chehab gspca_dev->last_packet_type = DISCARD_PACKET; 1930c0d06caSMauro Carvalho Chehab continue; 1940c0d06caSMauro Carvalho Chehab } 1950c0d06caSMauro Carvalho Chehab st = urb0->iso_frame_desc[i].status; 1960c0d06caSMauro Carvalho Chehab if (st == 0) 1970c0d06caSMauro Carvalho Chehab st = urb->iso_frame_desc[i].status; 1980c0d06caSMauro Carvalho Chehab if (st) { 1990c0d06caSMauro Carvalho Chehab pr_err("ISOC data error: [%d] status=%d\n", 2000c0d06caSMauro Carvalho Chehab i, st); 2010c0d06caSMauro Carvalho Chehab gspca_dev->last_packet_type = DISCARD_PACKET; 2020c0d06caSMauro Carvalho Chehab continue; 2030c0d06caSMauro Carvalho Chehab } 2040c0d06caSMauro Carvalho Chehab 2050c0d06caSMauro Carvalho Chehab /* 2060c0d06caSMauro Carvalho Chehab * The images are received in URBs of different endpoints 2070c0d06caSMauro Carvalho Chehab * (0x83 and 0x82). 2080c0d06caSMauro Carvalho Chehab * Image pieces in URBs of ep 0x83 are continuated in URBs of 2090c0d06caSMauro Carvalho Chehab * ep 0x82 of the same index. 2100c0d06caSMauro Carvalho Chehab * The packets in the URBs of endpoint 0x83 start with: 2110c0d06caSMauro Carvalho Chehab * - 80 ba/bb 00 00 = start of image followed by 'ff d8' 2120c0d06caSMauro Carvalho Chehab * - 04 ba/bb oo oo = image piece 2130c0d06caSMauro Carvalho Chehab * where 'oo oo' is the image offset 2140c0d06caSMauro Carvalho Chehab (not cheked) 2150c0d06caSMauro Carvalho Chehab * - (other -> bad frame) 2160c0d06caSMauro Carvalho Chehab * The images are JPEG encoded with full header and 2170c0d06caSMauro Carvalho Chehab * normal ff escape. 2180c0d06caSMauro Carvalho Chehab * The end of image ('ff d9') may occur in any URB. 2190c0d06caSMauro Carvalho Chehab * (not cheked) 2200c0d06caSMauro Carvalho Chehab */ 2210c0d06caSMauro Carvalho Chehab data = (u8 *) urb0->transfer_buffer 2220c0d06caSMauro Carvalho Chehab + urb0->iso_frame_desc[i].offset; 2230c0d06caSMauro Carvalho Chehab if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) { 2240c0d06caSMauro Carvalho Chehab 2250c0d06caSMauro Carvalho Chehab /* new image */ 2260c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, LAST_PACKET, 2270c0d06caSMauro Carvalho Chehab NULL, 0); 2280c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, FIRST_PACKET, 2290c0d06caSMauro Carvalho Chehab data + 4, SD_PKT_SZ - 4); 2300c0d06caSMauro Carvalho Chehab } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) { 2310c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, INTER_PACKET, 2320c0d06caSMauro Carvalho Chehab data + 4, SD_PKT_SZ - 4); 2330c0d06caSMauro Carvalho Chehab } else { 2340c0d06caSMauro Carvalho Chehab gspca_dev->last_packet_type = DISCARD_PACKET; 2350c0d06caSMauro Carvalho Chehab continue; 2360c0d06caSMauro Carvalho Chehab } 2370c0d06caSMauro Carvalho Chehab data = (u8 *) urb->transfer_buffer 2380c0d06caSMauro Carvalho Chehab + urb->iso_frame_desc[i].offset; 2390c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, INTER_PACKET, 2400c0d06caSMauro Carvalho Chehab data, SD_PKT_SZ); 2410c0d06caSMauro Carvalho Chehab } 2420c0d06caSMauro Carvalho Chehab 2430c0d06caSMauro Carvalho Chehab /* resubmit the URBs */ 2440c0d06caSMauro Carvalho Chehab st = usb_submit_urb(urb0, GFP_ATOMIC); 2450c0d06caSMauro Carvalho Chehab if (st < 0) 2460c0d06caSMauro Carvalho Chehab pr_err("usb_submit_urb(0) ret %d\n", st); 2470c0d06caSMauro Carvalho Chehab st = usb_submit_urb(urb, GFP_ATOMIC); 2480c0d06caSMauro Carvalho Chehab if (st < 0) 2490c0d06caSMauro Carvalho Chehab pr_err("usb_submit_urb() ret %d\n", st); 2500c0d06caSMauro Carvalho Chehab } 2510c0d06caSMauro Carvalho Chehab 2520c0d06caSMauro Carvalho Chehab /* sub-driver description */ 2530c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc = { 2540c0d06caSMauro Carvalho Chehab .name = MODULE_NAME, 2550c0d06caSMauro Carvalho Chehab .config = sd_config, 2560c0d06caSMauro Carvalho Chehab .init = sd_init, 2570c0d06caSMauro Carvalho Chehab .start = sd_start, 2580c0d06caSMauro Carvalho Chehab .stopN = sd_stopN, 2590c0d06caSMauro Carvalho Chehab .pkt_scan = sd_pkt_scan, 2600c0d06caSMauro Carvalho Chehab }; 2610c0d06caSMauro Carvalho Chehab 2620c0d06caSMauro Carvalho Chehab /* -- module initialisation -- */ 2630c0d06caSMauro Carvalho Chehab static const struct usb_device_id device_table[] = { 2640c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x04a5, 0x3035)}, 2650c0d06caSMauro Carvalho Chehab {} 2660c0d06caSMauro Carvalho Chehab }; 2670c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table); 2680c0d06caSMauro Carvalho Chehab 2690c0d06caSMauro Carvalho Chehab /* -- device connect -- */ 2700c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf, 2710c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 2720c0d06caSMauro Carvalho Chehab { 2730c0d06caSMauro Carvalho Chehab return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 2740c0d06caSMauro Carvalho Chehab THIS_MODULE); 2750c0d06caSMauro Carvalho Chehab } 2760c0d06caSMauro Carvalho Chehab 2770c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = { 2780c0d06caSMauro Carvalho Chehab .name = MODULE_NAME, 2790c0d06caSMauro Carvalho Chehab .id_table = device_table, 2800c0d06caSMauro Carvalho Chehab .probe = sd_probe, 2810c0d06caSMauro Carvalho Chehab .disconnect = gspca_disconnect, 2820c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM 2830c0d06caSMauro Carvalho Chehab .suspend = gspca_suspend, 2840c0d06caSMauro Carvalho Chehab .resume = gspca_resume, 2850c0d06caSMauro Carvalho Chehab .reset_resume = gspca_resume, 2860c0d06caSMauro Carvalho Chehab #endif 2870c0d06caSMauro Carvalho Chehab }; 2880c0d06caSMauro Carvalho Chehab 2890c0d06caSMauro Carvalho Chehab module_usb_driver(sd_driver); 290