📄 qc-driver.c
字号:
/* Wait until all previosly set registers are set or abort all transmissions * and return error code. * After this function returns, there will not be uncompleted I2C URBs. */int qc_i2c_wait(struct quickcam *qc){ struct qc_i2c_data *id = &qc->i2c_data; int r; if (qcdebug&QC_DEBUGLOGIC) PDEBUG("qc_i2c_wait(quickcam=%p)",qc); TEST_BUGR(in_interrupt()); TEST_BUGR(qc==NULL); IDEBUG_TEST(*id); if (!qc->connected) goto cancel; r = qc_i2c_flush(qc); if (r>=0) r = wait_event_interruptible(id->wq, id->packets==0); if (r<0) goto cancel; return 0;cancel: if (qcdebug&QC_DEBUGLOGIC) PDEBUG("Canceling pending URB %p, packets=%i", id->urb, id->packets); PDEBUG("i2c_cancel: qc=%p, id=%p",qc,id); PDEBUG("i2c_cancel: id->urb=%p", id->urb);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) if (qc->connected) { PDEBUG("i2c_cancel: id->urb->dev=%p", id->urb->dev); if (id->urb->dev!=NULL) { PDEBUG("i2c_cancel: id->urb->dev->bus=%p", id->urb->dev->bus); if (id->urb->dev->bus!=NULL) { PDEBUG("i2c_cancel: id->urb->dev->bus->op=%p", id->urb->dev->bus->op); //PDEBUG("id->urb->dev->bus->op->unlink=%p", id->urb->dev->bus->op->unlink); } } }#endif /* Cancel URB if it is in progress or in completion handler */ if (id->packets > 0) usb_kill_urb(id->urb); TEST_BUGR_MSG(id->packets!=0, "i2c_wait: packets=%i", id->packets); return 0;}/* }}} *//* {{{ [fold] (private) qc_i2c_set0(struct quickcam *qc, unsigned char regnum, unsigned char loval, unsigned char hival, int flags) *//* Called from qc_i2c_set and qc_i2c_setw, should not be called elsewhere */static int qc_i2c_set0(struct quickcam *qc, unsigned char regnum, unsigned char loval, unsigned char hival, int flags){ struct qc_i2c_data *id = &qc->i2c_data; unsigned int newhead; signed int r; if (qcdebug&QC_DEBUGLOGIC) PDEBUG("qc_i2c_set0(quickcam=%p,reg=%02X,val=%02X%02X)",qc,regnum,hival,loval); TEST_BUGR(qc==NULL); IDEBUG_TEST(*id); newhead = id->newhead; id->commands[newhead].loval = loval; id->commands[newhead].hival = hival; id->commands[newhead].regnum = regnum; id->commands[newhead].flags = flags; newhead = (newhead + 1) % I2C_MAXCOMMANDS; if (newhead == id->tail) { /* If buffer is full, wait until it's empty */ if (qcdebug&QC_DEBUGCONTROLURBS) PDEBUG("i2c buffer is full, waiting"); r = qc_i2c_wait(qc); if (r<0) return r; } TEST_BUGR(newhead==id->tail); /* no i2c buffer space but nothing to send!!! */ id->newhead = newhead; return 0;}/* }}} *//* {{{ [fold] qc_i2c_set(struct quickcam *qc, unsigned char reg, unsigned char val) *//* Set an I2C register to desired value *//* (queue setting to be sent later when qc_i2c_flush() is called) */inline int qc_i2c_set(struct quickcam *qc, unsigned char reg, unsigned char val){ return qc_i2c_set0(qc, reg, val, 0, 0);}/* }}} *//* {{{ [fold] qc_i2c_setw(struct quickcam *qc, unsigned char reg, unsigned short val) *//* Set a two-byte (word length) I2C register to desired value (queue setting to be sent later) *//* (queue setting to be sent later when qc_i2c_flush() is called) */inline int qc_i2c_setw(struct quickcam *qc, unsigned char reg, unsigned short val){ return qc_i2c_set0(qc, reg, val & 0xFF, (val >> 8) & 0xFF, I2C_FLAG_WORD);}/* }}} *//* {{{ [fold] qc_i2c_break(struct quickcam *qc) *//* The next register written will be sent in another packet */int qc_i2c_break(struct quickcam *qc){ struct qc_i2c_data *id = &qc->i2c_data; unsigned int prevhead; if (qcdebug&QC_DEBUGLOGIC) PDEBUG("qc_i2c_break(quickcam=%p)",qc); TEST_BUGR(qc==NULL); IDEBUG_TEST(*id); /* We access an entry that may be already submitted and even finished */ /* But it should not harm */ prevhead = (id->newhead + I2C_MAXCOMMANDS - 1) % I2C_MAXCOMMANDS; id->commands[prevhead].flags |= I2C_FLAG_BREAK; barrier(); return qc_i2c_flush(qc);}/* }}} *//* {{{ [fold] qc_i2c_init(struct quickcam *qc) *//* Initialize structures and hardware for I2C communication */static int qc_i2c_init(struct quickcam *qc){ struct qc_i2c_data *id = &qc->i2c_data; struct urb *urb; struct usb_ctrlrequest *cr; int r = -ENOMEM; if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_i2c_init(quickcam=%p)",qc); TEST_BUGR(qc==NULL); id->tail = id->head = id->newhead = 0; /* Next position to be filled and sent is 0 */ id->packets = 0; init_waitqueue_head(&id->wq); /* Allocate an URB and associated buffers and fill them */ urb = id->urb = usb_alloc_urb(0,GFP_KERNEL); if (!urb) goto fail1; cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); urb->setup_packet = (unsigned char *)cr; if (!cr) goto fail2; urb->transfer_buffer = kmalloc(qc_i2c_maxbufsize*sizeof(u8), GFP_KERNEL); /* Allocate maximum ever needed */ if (!urb->transfer_buffer) goto fail3; spin_lock_init(&urb->lock); urb->complete = qc_i2c_handler; urb->context = qc;#if (LINUX_VERSION_CODE<KERNEL_VERSION(2,6,9) && !defined(CONFIG_SUSE_KERNEL)) || LINUX_VERSION_CODE<KERNEL_VERSION(2,6,8) urb->timeout = 3*HZ; /* 1 s */#endif cr->bRequestType = 0x40; cr->bRequest = 0x04; cr->wIndex = 0; IDEBUG_INIT(*id); return 0;fail3: kfree(cr);fail2: usb_free_urb(urb); POISON(id->urb);fail1: return r;}/* }}} *//* {{{ [fold] qc_i2c_exit(struct quickcam *qc) *//* Close messaging, free up memory, stop messaging */static void qc_i2c_exit(struct quickcam *qc){ struct qc_i2c_data *id = &qc->i2c_data; if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_i2c_exit(qc=%p)",qc); TEST_BUG(qc==NULL); qc_i2c_wait(qc); kfree(id->urb->setup_packet); kfree(id->urb->transfer_buffer); POISON(id->urb->setup_packet); POISON(id->urb->transfer_buffer); usb_free_urb(id->urb); IDEBUG_EXIT(*id);}/* }}} *//* }}} *//* {{{ [fold] **** qc_proc: /proc interface *********************************** */#if HAVE_PROCFSstatic struct proc_dir_entry *qc_proc_entry = NULL; /* Initialization should not be necessary, but just in case... *//* {{{ [fold] qc_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) */static inline const char *qc_proc_yesno(Bool b){ return b ? "Yes" : "No";}static int qc_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data){ struct quickcam *qc = data; char *out = page; int len; if (qc_lock(qc) < 0) return 0; out += sprintf(out, "\tGeneral driver status\n"); out += sprintf(out, "Driver version : %s\n", VERSION); out += sprintf(out, "Kernel version : %s\n", UTS_RELEASE); if (qc->dev!=NULL) { out += sprintf(out, "Device Id : %04X:%04X\n", (int)GET_VENDORID(qc), (int)GET_PRODUCTID(qc)); out += sprintf(out, "USB bus number : %i\n", qc->dev->bus->busnum); } out += sprintf(out, "Users : %i\n", qc->users); out += sprintf(out, "Connected : %s\n", qc_proc_yesno(qc->connected)); out += sprintf(out, "\n\tPicture settings set by user\n"); out += sprintf(out, "Brightness : %d\n", (int)qc->vpic.brightness); out += sprintf(out, "Hue : %d\n", (int)qc->vpic.hue); out += sprintf(out, "Color : %d\n", (int)qc->vpic.colour); out += sprintf(out, "Contrast : %d\n", (int)qc->vpic.contrast); out += sprintf(out, "Whiteness : %d\n", (int)qc->vpic.whiteness); if (qc->users > 0) { out += sprintf(out, "Depth : %d\n", (int)qc->vpic.depth); out += sprintf(out, "Palette : %s\n", qc_fmt_getname(qc->vpic.palette)); } if (qc->users > 0) { out += sprintf(out, "\n\tOutput window\n"); out += sprintf(out, "Width : %d\n", (int)qc->vwin.width); out += sprintf(out, "Height : %d\n", (int)qc->vwin.height); } out += sprintf(out, "\n\tSensor\n"); out += sprintf(out, "Type : %s\n", qc->sensor_data.sensor->name); out += sprintf(out, "Manufacturer : %s\n", qc->sensor_data.sensor->manufacturer); if (qc->users > 0) { out += sprintf(out, "Maximum width : %d\n", qc->sensor_data.maxwidth); out += sprintf(out, "Maximum height : %d\n", qc->sensor_data.maxheight); out += sprintf(out, "Current width : %d\n", qc->sensor_data.width); out += sprintf(out, "Current height : %d\n", qc->sensor_data.height); } out += sprintf(out, "\n\tI2C command stream\n"); out += sprintf(out, "Scheduled packets: %d\n", qc->i2c_data.packets); out += sprintf(out, "Packets on queue : %d\n", (I2C_MAXCOMMANDS + qc->i2c_data.head - qc->i2c_data.tail) % I2C_MAXCOMMANDS); if (qc->users > 0) { out += sprintf(out, "\n\tIsochronous data stream\n"); out += sprintf(out, "Stream enabled : %s\n", qc_proc_yesno(qc->isoc_data.streaming)); out += sprintf(out, "Transfer errors : %d\n", qc->isoc_data.errorcount); out += sprintf(out, "\n\tFrame buffering\n"); out += sprintf(out, "Frames on queue : %d\n", (FRAME_BUFFERS + qc->frame_data.head - qc->frame_data.tail) % FRAME_BUFFERS); out += sprintf(out, "Capturing : %s\n", qc_proc_yesno(qc->stream_data.capturing)); out += sprintf(out, "Waiting processes: %d\n", qc->frame_data.waiting); } out += sprintf(out, "\n\tAutomatic exposure control\n"); out += sprintf(out, "Picture intensity: %d\n", qc->adapt_data.oldmidvalue); out += sprintf(out, "Exposure setting : %d\n", qc->adapt_data.exposure); out += sprintf(out, "Gain setting : %d\n", qc->adapt_data.gain); out += sprintf(out, "Delta value : %d\n", qc->adapt_data.olddelta); out += sprintf(out, "Control algorithm: "); switch (qc->adapt_data.controlalg) { case EXPCONTROL_SATURATED: out += sprintf(out, "Saturated\n"); break; case EXPCONTROL_NEWTON: out += sprintf(out, "Newton\n"); break; case EXPCONTROL_FLOAT: out += sprintf(out, "Float\n"); break; default: out += sprintf(out, "?\n"); break; } out += sprintf(out, "\n\tDefault settings\n"); out += sprintf(out, "Debug : 0x%02X\n", qcdebug); out += sprintf(out, "Keep settings : %s\n", qc_proc_yesno(qc->settings.keepsettings)); out += sprintf(out, "Settle max frames: %i\n", qc->settings.settle); out += sprintf(out, "Subsampling : %s\n", qc_proc_yesno(qc->settings.subsample)); out += sprintf(out, "Compress : %s\n", qc_proc_yesno(qc->settings.compress)); out += sprintf(out, "Frame skipping : %i\n", qc->settings.frameskip); out += sprintf(out, "Image quality : %i\n", qc->settings.quality); out += sprintf(out, "Adaptive : %s\n", qc_proc_yesno(qc->settings.adaptive)); out += sprintf(out, "Equalize : %s\n", qc_proc_yesno(qc->settings.equalize)); out += sprintf(out, "User lookup-table: %s\n", qc_proc_yesno(qc->settings.userlut)); out += sprintf(out, "Retryerrors : %s\n", qc_proc_yesno(qc->settings.retryerrors)); out += sprintf(out, "Compatible 16x : %s\n", qc_proc_yesno(qc->settings.compat_16x)); out += sprintf(out, "Compatible DblBuf: %s\n", qc_proc_yesno(qc->settings.compat_dblbuf)); out += sprintf(out, "Compatible ToRgb : %s\n", qc_proc_yesno(qc->settings.compat_torgb)); up(&quickcam_list_lock); len = out - page; len -= off; if (len < count) { *eof = 1; if (len <= 0) return 0; } else len = count; *start = page + off; return len;}/* }}} *//* {{{ [fold] qc_proc_write(struct file *file, const char *buffer, unsigned long count, void *data) */static int qc_proc_write(struct file *file, const char *buffer, unsigned long count, void *data){ /* we don't support this....yet? Might replace qcset some day */ return -EINVAL;}/* }}} *//* {{{ [fold] qc_proc_create(struct quickcam *qc) *//* Called for each camera plugged in, create file containing information of the camera */static int qc_proc_create(struct quickcam *qc){ char name[9]; struct proc_dir_entry *entry; if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_proc_create(quickcam=%p)",qc); TEST_BUGR(!qc); qc->proc_entry = NULL; if (qc_proc_entry==NULL) return -ENOTDIR; sprintf(name, "video%d", qc->vdev.minor); entry = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, qc_proc_entry); if (!entry) { PRINTK(KERN_WARNING,"Could not register procfs file entry"); return -ENXIO; } entry->owner = THIS_MODULE; entry->data = qc; entry->read_proc = qc_proc_read; entry->write_proc = qc_proc_write; qc->proc_entry = entry; return 0;}/* }}} *//* {{{ [fold] qc_proc_destroy(struct quickcam *qc) *//* qc_proc_destroy may be called after qc_proc_create for given quickcam even if it failed */static void qc_proc_destroy(struct quickcam *qc){ char name[9]; if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_proc_destroy(quickcam=%p)",qc); TEST_BUG(!qc); if (!qc->proc_entry) return; TEST_BUG(!qc_proc_entry); sprintf(name, "video%d", qc->vdev.minor); remove_proc_entry(name, qc_proc_entry); POISON(qc->proc_entry); if (qcdebug&QC_DEBUGLOGIC) PDEBUG("qc_proc_destroy() done");}/* }}} *//* {{{ [fold] qc_proc_init(void) *//* Called when the driver is initially loaded, creates "/proc/video/quickcam" subdirectory */static int qc_proc_init(void){ if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_proc_init()"); qc_proc_entry = create_proc_entry(qc_proc_name, S_IFDIR, NULL); /* Create /proc/video/quickcam */ if (!qc_proc_entry) { /* create_proc_entry failed, possibly because /proc/video is missing (patch by aceJacek <acejacek@pop.e-wro.pl>) */ proc_mkdir("video", NULL); /* Might fail, if the directory already exists, but we don't care */ qc_proc_entry = create_proc_entry(qc_proc_name, S_IFDIR, NULL); if (!qc_proc_entry) { PRINTK(KERN_WARNING,"Could not register procfs dir entry"); return -ENXIO; } } qc_proc_entry->owner = THIS_MODULE; return 0;}/* }}} *//* {{{ [fold] qc_proc_exit(void) *//* Can be called after qc_proc_init() even if it has failed, in which case this does nothing */static void qc_proc_exit(void)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -