📄 qc-driver.c
字号:
{ if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_proc_exit()"); if (!qc_proc_entry) return; remove_proc_entry(qc_proc_name, NULL); POISON(qc_proc_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->gain = 32768; ctrl->olddelta = 4*256; /* best guess */ ctrl->exposure = 32768; ctrl->oldexposure = ctrl->exposure + 1; /* Slightly different for _issettled() */ ctrl->midvaluesum = ctrl->oldmidvalue = 0; ctrl->framecounter = 0; ctrl->controlalg = EXPCONTROL_SATURATED; 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;}/* }}} *//* {{{ [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 = 5; /* (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; } 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.*/ ctrl->gain = ctrl->gain + ctrl->gain/2 + 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/2 - 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 */ if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("Decreasing exposure"); ctrl->controlalg = EXPCONTROL_SATURATED; newexposure = ctrl->exposure / 2; } else { deviation = target - midvalue; if (ABS(deviation) < adaptation_min) { /* For small variations, adapt linearly */ if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("Small deviation %i",deviation); ctrl->controlalg = EXPCONTROL_FLOAT; newexposure = small_adapt * SGN(deviation) + ctrl->exposure; } else { /* Try using Newton method for estimating correct exposure value */ ctrl->controlalg = EXPCONTROL_NEWTON; dmidvalue = midvalue - ctrl->oldmidvalue; dexposure = ctrl->exposure - ctrl->oldexposure; if (ABS(dmidvalue) < dmidvalue_min || ABS(dexposure) < dexposure_min || SGN(dmidvalue) != SGN(dexposure)) { /* Can not estimate delta with Newton method, just guess */ if (ctrl->olddelta < 2) { if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("Best guessing"); smoothdelta = bestguess; } else { Bool cross = SGN(midvalue-target) != SGN(ctrl->oldmidvalue-target); smoothdelta = cross ? (ctrl->olddelta / 2) : (ctrl->olddelta * 3 / 2); if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("Change more exposure, smoothdelta=%i",smoothdelta); } } else { /* Everything is well, use here actual Newton method */ delta = (256 - underestimate) * dexposure / dmidvalue; smoothdelta = (delta_speed*delta + (256-delta_speed)*ctrl->olddelta) / 256; if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("Using Newton, delta=%i",delta); } } /* Compute new exposure based on guessed/computed delta */ smoothdelta = CLIP(smoothdelta, delta_min,delta_max); dexposure = deviation * smoothdelta / 256; /* Newton works linearly, but exposure/brightness are not linearly related */ /* The following test fixes the worst deficiencies due to that (I hope) */ if (-dexposure > ctrl->exposure/2) dexposure = -ctrl->exposure/2; newexposure = dexposure + ctrl->exposure; ctrl->olddelta = smoothdelta; } newexposure = CLIP(newexposure, 2,65535); if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("midval=%i dev=%i dmidv=%i dexp=%i smdelta=%i olddelta=%i newexp=%i gain=%i", midvalue,deviation,dmidvalue,dexposure,smoothdelta,ctrl->olddelta,newexposure,ctrl->gain); ctrl->oldexposure = ctrl->exposure; ctrl->exposure = newexposure; ctrl->oldmidvalue = midvalue; *ret_exposure = newexposure; *ret_gain = ctrl->gain;#else /* This code is for measuring the delay between an exposure settings and until * it becomes in effect. Only useful for developing the adaptation algorithm. */ /* Some delays: when a setting is changed at frame number #0, * it becomes in effect in frame xx for exposure gain * QuickCam Web/0850/normal mode 4 4 * QuickCam Web/0850/compressed mode 5 5 * QuickCam Express/840 2 1-5 * */ static int exp = 0; static int gain = 0; static const int changedel = 20; static int state = 0; static int framenum = 0; PRINTK(KERN_CRIT,"Measuring: framenum=%i, midvalue=%i",framenum,midvalue); if ((framenum%changedel)==0) { switch (state) { default: case 0: PRINTK(KERN_CRIT,"Measuring: set to black"); exp = 0; gain = 0; break; case 1: PRINTK(KERN_CRIT,"Measuring: changing exposure"); exp = 65535; break; case 2: PRINTK(KERN_CRIT,"Measuring: changing gain"); gain = 32535; break; } state = ((state+1) % 3); } *ret_exposure = exp; *ret_gain = gain; framenum++;#endif}/* }}} *//* }}} *//* {{{ [fold] **** qc_frame: Frame capturing functions ************************* *//* From /usr/src/linux/Documentation/smp.tex: * + Kernel mode process (e.g. system calls): * - No other kernel mode processes may run simultaneously/pre-empt * (kernel mode processes are atomic with respect to each other) * (Does not hold for 2.6.x) * - Exception is voluntary sleeping, in which case re-entry is allowed * (Does not hold for 2.6.x) * - Interrupts may pre-empt (but return to same process) * (interrupts can be disabled if necessary) * + Interrupt mode execution * - Kernel mode process may not pre-empt/execute simultaneously * - Other interrupts may pre-empt, however same interrupt is not nested *//* We have here a quite typical producer-consumer scheme: * Interrupt routine produces more frame data, while * kernel mode processes consume it * Read: Linux Device Drivers, Alessandro Rubini et al, 2nd edition, pg. 279 * "Using Circular Buffers" *//* Initialization and cleanup routines, called from kernel mode processes *//* {{{ [fold] qc_frame_init(struct quickcam *qc) */static int qc_frame_init(struct quickcam *qc){ struct qc_frame_data *fd = &qc->frame_data; int n; if (qcdebug&QC_DEBUGFRAME || qcdebug&QC_DEBUGINIT) PDEBUG("qc_frame_init(qc=%p)",qc); TEST_BUGR(qc==NULL || fd==NULL); TEST_BUGR(in_interrupt()); fd->rawdatabuf = vmalloc(FRAME_DATASIZE * FRAME_BUFFERS); if (!fd->rawdatabuf) return -ENOMEM; memset(fd->rawdatabuf, 0, FRAME_DATASIZE * FRAME_BUFFERS); /* Never let user access random kernel data */ fd->head = 0; /* First buffer to fill */ fd->tail = 0; /* First buffer to get */ spin_lock_init(&fd->tail_lock); fd->tail_in_use= FALSE; init_waitqueue_head(&fd->wq); fd->waiting = 0; fd->exiting = FALSE; for (n=0; n<FRAME_BUFFERS; n++) fd->buffers[n].rawdatalen = 0; fd->lost_frames = 0; IDEBUG_INIT(*fd); return 0;}/* }}} *//* {{{ [fold] qc_frame_exit(struct quickcam *qc) *//* This function must be called with qc->lock acquired * (it may release it temporarily and sleep) */static void qc_frame_exit(struct quickcam *qc){ struct qc_frame_data *fd = &qc->frame_data;#if PARANOID unsigned long startjiffy = jiffies;#endif if (qcdebug&QC_DEBUGFRAME || qcdebug&QC_DEBUGINIT) PDEBUG("qc_frame_exit(qc=%p,tail=%i,head=%i)",qc,fd->tail,fd->head); TEST_BUG(in_interrupt()); TEST_BUG(qc==NULL || fd==NULL); fd->exiting = TRUE; fd->maxrawdatalen = 0; /* Hopefully stops all ongoing captures, might need locking though */ wake_up(&fd->wq); if (qcdebug&QC_DEBUGFRAME) PDEBUG("waiting=%i",fd->waiting); if (qcdebug&QC_DEBUGMUTEX) PDEBUG("up(%p) in qc_frame_exit() : %i", qc, sem_getcount(&qc->lock)); up(&qc->lock); /* The lock was down when entering this function */ while (fd->waiting > 0) { schedule();#if PARANOID if (jiffies-startjiffy > 60*HZ) { PRINTK(KERN_CRIT,"Wait queue never completing!! (waiting=%i)",fd->waiting); break; }#endif } if (qcdebug&QC_DEBUGMUTEX) PDEBUG("down(%p) in qc_frame_exit() : %i", qc, sem_getcount(&qc->lock)); down(&qc->lock); vfree(fd->rawdatabuf); POISON(fd->rawdatabuf); IDEBUG_EXIT(*fd);}/* }}} *//* Consumer routines, called from kernel mode processes *//* {{{ [fold] qc_frame_get(struct quickcam *qc, unsigned char **buf) *//* Wait until next frame is ready and return the frame length
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -