📄 qc-driver.c
字号:
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_quickcam_entry==NULL) return -ENOTDIR; sprintf(name, "video%d", qc->vdev.minor); entry = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, qc_proc_quickcam_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; sprintf(name, "button%d", qc->vdev.minor); entry = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, qc_proc_quickcam_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_button_read; entry->write_proc = qc_proc_write; qc->proc_entry_button = 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) { TEST_BUG(!qc_proc_quickcam_entry); sprintf(name, "video%d", qc->vdev.minor); remove_proc_entry(name, qc_proc_quickcam_entry); POISON(qc->proc_entry); } if (qc->proc_entry_button) { TEST_BUG(!qc_proc_quickcam_entry); sprintf(name, "button%d", qc->vdev.minor); remove_proc_entry(name, qc_proc_quickcam_entry); POISON(qc->proc_entry_button); } if (qcdebug&QC_DEBUGLOGIC) PDEBUG("qc_proc_destroy() done");}/* }}} *//* {{{ [fold] qc_proc_init(void) *//* Called when the driver is initially loaded, creates "/proc/video/qcquickcam" subdirectory */static int qc_proc_init(void){ if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_proc_init()"); /* Might fail, if the directory already exists, but we don't care */ qc_proc_video_entry = proc_mkdir(qc_proc_video_name, NULL); if(!qc_proc_video_entry) { PRINTK(KERN_WARNING,"Could not register procfs dir entry"); return -ENXIO; } qc_proc_video_entry->owner = THIS_MODULE; qc_proc_quickcam_entry = create_proc_entry(qc_proc_quickcam_name, S_IFDIR, qc_proc_video_entry); if (!qc_proc_quickcam_entry) { PRINTK(KERN_WARNING,"Could not register procfs dir entry"); return -ENXIO; } qc_proc_quickcam_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){ if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_proc_exit()"); if (qc_proc_quickcam_entry) { remove_proc_entry(qc_proc_quickcam_name, qc_proc_video_entry); POISON(qc_proc_quickcam_entry); } if (qc_proc_video_entry) { remove_proc_entry(qc_proc_video_name, NULL); POISON(qc_proc_video_entry); }}/* }}} */#elsestatic inline int qc_proc_create(struct quickcam *qc) { return 0; }static inline void qc_proc_destroy(struct quickcam *qc) { }static inline int qc_proc_init(void) { return 0; }static inline void qc_proc_exit(void) { }#endif /* HAVE_PROCFS *//* }}} *//* {{{ [fold] **** qc_adapt: Automatic exposure control ************************ */#define MEASURE_ADAPT_DELAY 0 /* Measure adaptation delay, only for test purposes *//* {{{ [fold] qc_adapt_init(struct quickcam *qc) *//* Initialize automatic exposure control structure. */static int qc_adapt_init(struct quickcam *qc){ struct qc_adapt_data *ctrl = &qc->adapt_data; ctrl->olddelta = 4*256; /* best guess */ ctrl->gain = 32768; //ctrl->gain = 5000;#if 0 ctrl->exposure = 32768;#else ctrl->exposure = 20000; // avoid overexposed at start.#endif ctrl->oldexposure = ctrl->exposure + 1; /* Slightly different for _issettled() */ ctrl->midvaluesum = ctrl->oldmidvalue = 0; ctrl->framecounter = 0; ctrl->controlalg = EXPCONTROL_SATURATED; ctrl->shutter = 32768; IDEBUG_INIT(*ctrl); return 0;}/* }}} *//* {{{ [fold] qc_adapt_exit(struct quickcam *qc) */static inline void qc_adapt_exit(struct quickcam *qc){#ifdef DEBUG struct qc_adapt_data *ctrl = &qc->adapt_data; if (qcdebug&QC_DEBUGINIT) PDEBUG("qc_adapt_exit(ctrl=%p)",ctrl); IDEBUG_EXIT(*ctrl);#endif}/* }}} *//* {{{ [fold] qc_adapt_reset(struct quickcam *qc) *//* Must be called each time just before starting video adaptation */static inline void qc_adapt_reset(struct quickcam *qc){ IDEBUG_TEST(qc->adapt_data); if (!qc->settings.keepsettings) { IDEBUG_EXIT(qc->adapt_data); qc_adapt_init(qc); }}/* }}} *//* {{{ [fold] qc_adapt_hassettled(struct quickcam *qc) *//* Return TRUE if the image brightness has settled */static inline Bool qc_adapt_hassettled(struct quickcam *qc){ struct qc_adapt_data *ctrl = &qc->adapt_data; IDEBUG_TEST(*ctrl); if (ctrl->framecounter != 0) return FALSE;//PDEBUG("control=%i oldexp=%i exp=%i",ctrl->controlalg,ctrl->oldexposure,ctrl->exposure); return ctrl->controlalg==EXPCONTROL_FLOAT || ctrl->oldexposure==ctrl->exposure;}/* }}} */#if 1// This really need some tuning... 300 seem to work good outdoor.//#define MIN_SHUTTER 5000#define MIN_SHUTTER 300#define MAX_SHUTTER 0xC000#define EXTREME_MAX_SHUTTER 0xFFFF#else#define MIN_SHUTTER 2500#define MAX_SHUTTER 15000#define EXTREME_MAX_SHUTTER 50000#endifint max_shutter = MAX_SHUTTER;/* {{{ [fold] qc_adapt_shutter(struct quickcam *qc) */static void qc_adapt_shutter(struct quickcam *qc){ struct qc_adapt_data *ctrl = &qc->adapt_data; int r; int newshutter = 0;#if 1 int diff = ctrl->oldmidvalue - 120;#if 1#define INC_VAL 10#define DIFF_VAL_SMALL 10#define DIFF_VAL_BIG 50#else#define INC_VAL 5#define DIFF_VAL_SMALL 5#define DIFF_VAL_BIG 25#endif#else int diff = -((qc->adapt_data.exposure>>8) - ctrl->oldmidvalue);#define INC_VAL 10#define DIFF_VAL_SMALL 10#define DIFF_VAL_BIG 30#endif /* Just a test of adjusting the shutter value. Try to keep picture intensity (ctrl->oldmidvalue) constant 120. */#if 1 if(qc->adapt_data.exposure==0xffff && qc->adapt_data.gain==0xffff) { max_shutter *= 2; if(max_shutter > EXTREME_MAX_SHUTTER) max_shutter = EXTREME_MAX_SHUTTER; } else { if(max_shutter > MAX_SHUTTER) { diff = 0; max_shutter = MAX_SHUTTER; newshutter = max_shutter; } }#endif#if 0 if(ctrl->shutter == max_shutter && (qc->adapt_data.exposure>>12)<10) { newshutter = ctrl->shutter / 2; if(newshutter < MIN_SHUTTER) newshutter = MIN_SHUTTER; } else#endif if(diff > DIFF_VAL_BIG) { newshutter = ctrl->shutter - 2*INC_VAL*diff; if(newshutter < MIN_SHUTTER) newshutter = MIN_SHUTTER; } else if(diff > DIFF_VAL_SMALL) { newshutter = ctrl->shutter - INC_VAL*diff; if(newshutter < MIN_SHUTTER) newshutter = MIN_SHUTTER; } else if(diff < -DIFF_VAL_BIG) { newshutter = ctrl->shutter - 2*INC_VAL*diff; if(newshutter > max_shutter) newshutter = max_shutter; } else if(diff < -DIFF_VAL_SMALL) { newshutter = ctrl->shutter - INC_VAL*diff; if(newshutter > max_shutter) newshutter = max_shutter; } if(newshutter) { ctrl->shutter = newshutter; if (qc->sensor_data.sensor->set_shutter!=NULL) { if ((r = qc->sensor_data.sensor->set_shutter(qc, newshutter))<0) goto fail; } } fail: return;}/* }}} *//* {{{ [fold] qc_adapt(struct quickcam *qc, int midvalue, int target, int *ret_exposure, int *ret_gain) *//* Set image exposure and gain so that computed midvalue approaches the target value. * midvalue = average pixel intensity on image 0..255 * target = user settable preferable intensity 0..255 * *ret_exposure = the exposure value to use for the camera, 0..65535 * *ret_gain = the gain to use for the camera, 0..65535. */static void qc_adapt(struct quickcam *qc, int midvalue, int target, int *ret_exposure, int *ret_gain){#if !MEASURE_ADAPT_DELAY struct qc_adapt_data *ctrl = &qc->adapt_data; /* Here are some constant for controlling the adaptation algorithm. You may play with them. */ static const int saturation_min = 32; /* (0-127) If midvalue is out of this range, image is */ static const int saturation_max = 256 - 8; /* (128-255) considered saturated and no Newton is used */ static const int adaptation_min = 32; /* (0-128) For small variations, do not change exposure */ static const int delta_min = 256/2; /* (2-16*256) Minimum and maximum value for delta */ static const int delta_max = 256*256; /* (4*256-1024*256) */ static const int dmidvalue_min = 400; /* (1-128) Minimum differences, under which delta estimation (FIXME:disabled by changing values very big) */ static const int dexposure_min = 400; /* (1-32000) will not be done due to inaccuracies */ static const int delta_speed = 256; /* (0-256) How fast or slowly delta can change */ static const int small_adapt = 4; /* (0-1024) When very near optimal, exposure change size */ static const int underestimate = 16; /* (0-250) Underestimation, may prevent oscillation */ static const int bestguess = 256/2; /* (2-1024*256) If delta can not be computed, guess this */ static const int midvalueaccum = 2; /* (1-100) How many frames to use for midvalue averaging */ static const int framedelay = 5; /* (0-8) How many frames there are before a new exposure setting in effect */ /* With QuickCam Web: if set at frame #0, it will be in effect at frame #4; skip 3 frames #1,#2,#3 */ /* -> should be 3 with QuickCam Web, but it oscillates, FIXME:why? Setting to 4 fixes this */ static const int gainstep = 256; /* (0-32768) Amount to change gain at one step */ static const int gainneeded = 10; /* (0-255) How eagerly to change brightness with gain */ /* End of tunable constants */ int newexposure, delta=0; int dexposure=0, dmidvalue=0; int deviation=0; /* Deviation of actual brightness from target brightness */ int smoothdelta=0; /* Final, smoothed, value of delta */ TEST_BUG(ctrl==NULL || ret_gain==NULL || ret_exposure==NULL); IDEBUG_TEST(*ctrl); if (ctrl->framecounter >= framedelay) ctrl->midvaluesum += midvalue; ctrl->framecounter++; if (ctrl->framecounter < framedelay+midvalueaccum) { *ret_exposure = ctrl->exposure; *ret_gain = ctrl->gain; return; } // Wim van Eenennaam, do nothing before framedelay if (ctrl->framecounter <= framedelay) return; midvalue = ctrl->midvaluesum / midvalueaccum; ctrl->framecounter = 0; ctrl->midvaluesum = 0; if (ctrl->exposure >= qc->sensor_data.sensor->adapt_gainhigh && ctrl->oldexposure >= qc->sensor_data.sensor->adapt_gainhigh && target - ctrl->oldmidvalue > gainneeded && target - midvalue > gainneeded) { /* Exposure is at maximum, but image is still too dark. Increase gain.*/#define GAINSTEP 2 ctrl->gain = ctrl->gain + ctrl->gain/GAINSTEP + gainstep; if (ctrl->gain > 65535) ctrl->gain = 65535; if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("increasing gain to %i", ctrl->gain); } else if (ctrl->exposure <= qc->sensor_data.sensor->adapt_gainlow && ctrl->oldexposure <= qc->sensor_data.sensor->adapt_gainlow && target - ctrl->oldmidvalue <= gainneeded && target - midvalue <= gainneeded) { /* Decrease gain if unnecessarily high */ ctrl->gain = ctrl->gain - ctrl->gain/GAINSTEP - gainstep; if (ctrl->gain < 0) ctrl->gain = 0; if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("decreasing gain to %i", ctrl->gain); } if (ctrl->oldmidvalue<saturation_min || midvalue<saturation_min) { /* Image was undersaturated, Newton method would give inaccurate results */ if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("Increasing exposure"); ctrl->controlalg = EXPCONTROL_SATURATED; newexposure = ctrl->exposure * 2; } else if (ctrl->oldmidvalue>=saturation_max || midvalue>=saturation_max) { /* Image is oversaturated, Newton method would give inaccurate results */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -