/* * Sonix SN9C101 based webcam basic I/F routines * Copyright (C) 2004 Takafumi Mizuno * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * Credits: * Special Thanks to Sonix SN9C102 based webcam driver Project developers. * * Thanks to Windows USb Sniffer Project developers. */ #include #include #include #include #include #include #include #include #include #include "sonix.h" #include "sonixinit.h" static u_int8_t header[SNX_HEADER_LEN] = { 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01, }; /* * device probe */ int usbdev_probe(int fd, struct usb_dev_info *list) { int ret; struct usb_device_info udi; if (ioctl(fd, USB_GET_DEVICEINFO, &udi) < 0) { perror("USB_GET_DEVICEINFO"); return -1; } ret = -1; while (list->productName != NULL){ if ((udi.udi_vendorNo == list->vendorNo) && (udi.udi_productNo == list->productNo)) { fprintf(stderr,"%s Found.\n", list->productName); ret = list->returnNo; break; } list++; } return ret; } int sonix_init(int handle, int id) { int i, ret = -1; int urbnum; initURB_t *inits; u_int8_t readval[LENNUM_MAX]; memset(readval, 0x00, LENNUM_MAX); switch ( id ) { case 0x6005: /* Z-102 */ urbnum = INIT6005_URBSNUM; inits = init6005; break; default: fprintf(stderr, "Unknown sensor. The camera is not supported yet.\n"); return -1; } for ( i = 0; i < urbnum; i++ ) { switch ( (inits+i)->act ) { case ACT_WRITE: ret = camera_reg_write(handle, (initURB_t const *)(inits+i)); break; case ACT_READ: if ((ret = camera_reg_read(handle, (initURB_t const *)(inits+i), readval, RREAD_NOFLAG)) < 0 ) { ret = -1; break; } break; case ACT_SETIF: ret = usb_set_interface(handle, (inits+i)->idx); break; default: fprintf(stderr, "unknown init action: %d\n",i); ret = -1; break; } if ( ret != 0 ) { fprintf(stderr, "init fail: %d\n",i); break; } } return ret; } /* set interface */ int usb_set_interface(int fd, int alternate) { struct usb_alt_interface alt; /* interface/alternate selection */ alt.uai_config_index = USB_CURRENT_CONFIG_INDEX; alt.uai_interface_index = 0; alt.uai_alt_no = alternate; if (ioctl(fd, USB_SET_ALTINTERFACE, &alt) < 0) { perror("USB_SET_ALTINTERFACE"); return -1; } return 0; } /* * camera_reg_read * return byte stream to *val. * Note: *val alloc at leaset more than urb->len. */ int camera_reg_read(int fd, initURB_t const *urb, u_int8_t *val, int flag) { int i,ret; struct usb_ctl_request ur; if ( urb->len > LENNUM_MAX ) return -1; if ( val == NULL ) return -1; ur.ucr_request.bmRequestType = UT_READ_VENDOR_INTERFACE; ur.ucr_request.bRequest = UR_GET_STATUS; USETW(ur.ucr_request.wValue, urb->val); USETW(ur.ucr_request.wIndex, urb->idx); USETW(ur.ucr_request.wLength, urb->len); ur.ucr_data = (void *)val; ur.ucr_flags = 0; ur.ucr_actlen = 0; if (ioctl(fd, USB_DO_REQUEST, &ur) < 0) { perror("ioctl USB_DO_REQUEST"); return -1; } ret = 0; for ( i = 0; i < urb->len; i++ ) { if ( val[i] != urb->data[i] ) { ret = -2; break; } } if ( (ret != 0) && !(flag & RREAD_QUIET) ) { fprintf(stderr,"Expected data: "); for ( i = 0; i < urb->len; i++ ) fprintf(stderr,"%x ",urb->data[i]); fprintf(stderr,"\nReturned data: "); for ( i = 0; i < urb->len; i++ ) fprintf(stderr,"%x ",val[i]); fprintf(stderr,"\n"); } if ( flag & RREAD_IGNORE_ERR ) ret = 0; return ret; } /* * */ int camera_reg_write(int fd, initURB_t const *urb) { struct usb_ctl_request ur; ur.ucr_request.bmRequestType = UT_WRITE_VENDOR_INTERFACE; ur.ucr_request.bRequest = UR_GET_CONFIG; USETW(ur.ucr_request.wValue, urb->val); USETW(ur.ucr_request.wIndex, urb->idx); USETW(ur.ucr_request.wLength, urb->len); ur.ucr_data = (void *)urb->data; ur.ucr_flags = 0; ur.ucr_actlen = 0; if (ioctl(fd, USB_DO_REQUEST, &ur) < 0) { perror("ioctl USB(_DO)_REQUEST"); return -1; } return 0; } /* * find header * Reference: sonix SN9C102 based webcam driver * http://www.mnementh.co.uk/sonix/ * dist/osc.c */ int find_header (unsigned char *buffer, int length){ int i; if ( length < SNX_SKIPHEADER_LEN ) return -1; i = 0; while ( i < length-SNX_SKIPHEADER_LEN+1 ){ if (!memcmp((const void *)(buffer+i), (const void *)header, SNX_HEADER_LEN)) return i; i++; } return -1; } /* * Image data format is a raw RGB bayer pattern. * isoc transfer data * BGBGBG... * GRGRGR... */ void bayer2rgb24(unsigned char *dst, unsigned char *src, int srcsize) { int i; unsigned char *rawpt, *scanpt; int size; rawpt = src; scanpt = dst; size = ( srcsize < (WIDTH*HEIGHT) ) ? srcsize : (WIDTH*HEIGHT); for ( i = 0; i < size; i++ ) { if ( (i/WIDTH) % 2 == 0 ) { if ( (i % 2) == 0 ) { /* B */ if ( (i > WIDTH) && ((i % WIDTH) > 0) ) { *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */ *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ *(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */ *scanpt++ = *rawpt; /* B */ } else { /* first line or left column */ *scanpt++ = *(rawpt+WIDTH+1); /* R */ *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */ *scanpt++ = *rawpt; /* B */ } } else { /* (B)G */ if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) { *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */ } else { /* first line or right column */ *scanpt++ = *(rawpt+WIDTH); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt-1); /* B */ } } } else { if ( (i % 2) == 0 ) { /* G(R) */ if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) { *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */ } else { /* bottom line or left column */ *scanpt++ = *(rawpt+1); /* R */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt-WIDTH); /* B */ } } else { /* R */ if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) { *scanpt++ = *rawpt; /* R */ *scanpt++ = (*(rawpt-1)+*(rawpt+1)+ *(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */ *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+ *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */ } else { /* bottom line or right column */ *scanpt++ = *rawpt; /* R */ *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */ *scanpt++ = *(rawpt-WIDTH-1); /* B */ } } } rawpt++; } }