📄 qc-driver.c
字号:
* and set buf to point to the frame. If error happens, * return standard Linux negative error number. * qc_frame_free() must be called after the frame is not needed anymore. * qc->lock must be acquired when entering this routine * (it may release it temporarily and sleep). */static int qc_frame_get(struct quickcam *qc, unsigned char **buf){ struct qc_frame_data *fd = &qc->frame_data; int ret; TEST_BUGR(qc==NULL || fd==NULL || fd->tail_in_use); TEST_BUGR(in_interrupt()); IDEBUG_TEST(*fd); /* Wait until the next frame is available */ if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_get/consume(qc=%p,tail=%i,head=%i)",qc,fd->tail,fd->head); fd->waiting++; if (qcdebug&QC_DEBUGMUTEX) PDEBUG("up(%p) in qc_frame_get() : %i", qc, sem_getcount(&qc->lock)); up(&qc->lock); /* Release lock while waiting */ ret = wait_event_interruptible(fd->wq, fd->head!=fd->tail || fd->exiting); //FIXME:What if we get -ERESTARTSYS? if (qcdebug&QC_DEBUGMUTEX) PDEBUG("down(%p) in qc_frame_get() : %i", qc, sem_getcount(&qc->lock)); down(&qc->lock); if (!ret) { if (!fd->exiting) { unsigned int t; spin_lock(&fd->tail_lock); fd->tail_in_use = TRUE; t = fd->tail; spin_unlock(&fd->tail_lock); if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_get/consume(qc=%p,tail=%i,head=%i,tail->rawdatalen=%i), got frame",qc,t,fd->head,fd->buffers[t].rawdatalen); *buf = fd->rawdatabuf + t*FRAME_DATASIZE; ret = fd->buffers[t].rawdatalen; } else { ret = -ENODATA; } } fd->waiting--; fd->lost_frames = 0; if (ret<0 && (qcdebug&(QC_DEBUGERRORS|QC_DEBUGFRAME))) PDEBUG("failed qc_frame_get()=%i",ret); return ret;}/* }}} *//* {{{ [fold] qc_frame_free(struct quickcam *qc) *//* Free up the last frame returned from qc_frame_get() (it must be called first) */static inline void qc_frame_free(struct quickcam *qc){ struct qc_frame_data *fd = &qc->frame_data; TEST_BUG(qc==NULL || fd==NULL); TEST_BUG(in_interrupt()); TEST_BUG(fd->head==fd->tail); /* The current fd->tail is not available to be freed! */ IDEBUG_TEST(*fd); if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_free/consume(qc=%p,tail=%i,head=%i)",qc,fd->tail,fd->head); /* Free up previous frame and advance to next */ spin_lock(&fd->tail_lock); fd->tail_in_use = FALSE; fd->tail = (fd->tail + 1) % FRAME_BUFFERS; spin_unlock(&fd->tail_lock);}/* }}} *//* {{{ [fold] qc_frame_test(struct quickcam *qc) *//* Return TRUE if next frame is immediately available, FALSE otherwise. */static inline Bool qc_frame_test(struct quickcam *qc){ struct qc_frame_data *fd = &qc->frame_data; IDEBUG_TEST(*fd); if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_test/consume(qc=%p,tail=%i,head=%i)",qc,fd->tail,fd->head); return fd->head != fd->tail;}/* }}} *//* Producer routines, called from interrupt context *//* {{{ [fold] qc_frame_begin(struct quickcam *qc) *//* Begin capturing next frame from camera. If buffer is full, the frame will be lost */static void qc_frame_begin(struct quickcam *qc){ struct qc_frame_data *fd = &qc->frame_data; int framesize, h; TEST_BUG(qc==NULL || fd==NULL); IDEBUG_TEST(*fd); if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_begin/produce(qc=%p,tail=%i,head=%i)",qc,fd->tail,fd->head); if (fd->exiting) return; TEST_BUG(fd->rawdatabuf==NULL); h = fd->head; fd->buffers[h].rawdatalen = 0; /* Use sensor information to get the framesize (i.e. how much we expect to receive bytes per image) */ /* FIXME: should compute data size differently in compressed mode */ framesize = qc->sensor_data.width * qc->sensor_data.height; fd->maxrawdatalen = MIN(framesize, FRAME_DATASIZE);}/* }}} *//* {{{ [fold] qc_frame_add(struct quickcam *qc, unsigned char *data, int datalen) *//* Store more data for a frame, return nonzero if too much data or other error */static int qc_frame_add(struct quickcam *qc, unsigned char *data, int datalen){ struct qc_frame_data *fd = &qc->frame_data; int h = fd->head; int bytes; TEST_BUGR(qc==NULL || fd==NULL); IDEBUG_TEST(*fd); TEST_BUGR(fd->rawdatabuf==NULL); if (fd->maxrawdatalen <= fd->buffers[h].rawdatalen) { if (qcdebug&QC_DEBUGERRORS) PDEBUG("buffer disabled, maxrawdatalen=%i rawdatalen=%i datalen=%i",fd->maxrawdatalen,fd->buffers[h].rawdatalen, datalen); return -EBUSY; } bytes = MIN(datalen, fd->maxrawdatalen - fd->buffers[h].rawdatalen); memcpy(fd->rawdatabuf + h*FRAME_DATASIZE + fd->buffers[h].rawdatalen, data, bytes); fd->buffers[h].rawdatalen += bytes; if (bytes < datalen) { if (qcdebug&QC_DEBUGERRORS) PRINTK(KERN_ERR,"out of buffer space by %i, maxrawdatalen=%i rawdatalen=%i datalen=%i", datalen-bytes,fd->maxrawdatalen,fd->buffers[h].rawdatalen, datalen); return -ENOSPC; } return 0;}/* }}} *//* {{{ [fold] qc_frame_end(struct quickcam *qc) *//* Finished capturing most recent frame from camera *//* (may be premature end, in which case some data is missing) */static void qc_frame_end(struct quickcam *qc){ static const int minrawdatalen = 32*32; /* If frame length is less than this many bytes, discard it */ struct qc_frame_data *fd = &qc->frame_data; unsigned int t, h; Bool lost_frame; TEST_BUG(qc==NULL || fd==NULL); h = fd->head; if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_end/produce(qc=%p,tail=%i,head=%i), got %i bytes",qc,fd->tail,h,fd->buffers[h].rawdatalen); IDEBUG_TEST(*fd); fd->maxrawdatalen = 0; /* Stop frame data capturing */#if DUMPDATA PDEBUG("frame_end: got %i bytes", fd->buffers[h].rawdatalen);#endif if (fd->buffers[h].rawdatalen < minrawdatalen) { /* No enough data in buffer, don't advance index */ if (qcdebug&QC_DEBUGERRORS) PDEBUG("discarding frame with only %u bytes", fd->buffers[h].rawdatalen); return; } h = (h + 1) % FRAME_BUFFERS; /* Select next frame buffer to fill */ lost_frame = FALSE; spin_lock(&fd->tail_lock); t = fd->tail; if (t == h) { lost_frame = TRUE; /* FIXME: the below should work fine for two buffers, but not so well for more. It should be possible * to drop oldest frame even when the current tail is in use. */ if (fd->tail_in_use) { /* Can not drop the oldest frame, it is in use. Drop the newest frame */ h = (h + FRAME_BUFFERS - 1) % FRAME_BUFFERS; /* Decrease head by one back to the original */ if (qcdebug&QC_DEBUGFRAME) PDEBUG("dropping newest frame"); } else { /* Drop the oldest frame */ fd->tail = (t + 1) % FRAME_BUFFERS; /* Drop the oldest frame away */ if (qcdebug&QC_DEBUGFRAME) PDEBUG("dropping oldest frame"); } } spin_unlock(&fd->tail_lock); if (lost_frame) { if (qcdebug&QC_DEBUGCOMMON || qcdebug&QC_DEBUGFRAME) PRINTK(KERN_NOTICE,"frame lost"); fd->lost_frames++; if (fd->lost_frames > 10) { /* Here we should call qc_isoc_stop() to stop isochronous * streaming since the application is clearly not reading frames at all. * However, we are now in interrupt context but qc_isoc_stop() has * to be in process context... so we can't do that. * FIXME: add tasklet/bottomhalf/whatever needed to do it. */ if (qcdebug&QC_DEBUGFRAME) PDEBUG("too many lost frames: %i", fd->lost_frames); } } fd->head = h; wake_up(&fd->wq);}/* }}} *//* {{{ [fold] qc_frame_flush(struct quickcam *qc) *//* Reject the current data already captured into buffer and end frame */void qc_frame_flush(struct quickcam *qc){ struct qc_frame_data *fd = &qc->frame_data; unsigned int h = fd->head; TEST_BUG(qc==NULL || fd==NULL); IDEBUG_TEST(*fd); if (qcdebug&QC_DEBUGFRAME) PDEBUG("qc_frame_flush/produce(qc=%p,tail=%i,head=%i), flush %i bytes",qc,fd->tail,h,fd->buffers[h].rawdatalen); fd->buffers[h].rawdatalen = 0; /* Empty buffer */ fd->maxrawdatalen = 0; /* Stop frame data capturing */}/* }}} *//* }}} *//* {{{ [fold] **** qc_stream: USB datastream processing functions *************** *//* {{{ [fold] qc_stream_init(struct quickcam *qc) *//* Initialize datastream processing */static int qc_stream_init(struct quickcam *qc){ if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_stream_init(quickcam=%p)",qc); qc->stream_data.capturing = FALSE; qc->stream_data.frameskip = qc->settings.frameskip; IDEBUG_INIT(qc->stream_data); return 0;}/* }}} *//* {{{ [fold] qc_stream_exit(struct quickcam *qc) *//* Stop datastream processing, after this qc_stream_add should not be called */static void qc_stream_exit(struct quickcam *qc){ if (qcdebug&QC_DEBUGLOGIC || qcdebug&QC_DEBUGINIT) PDEBUG("qc_stream_exit(quickcam=%p)",qc); if (qc->stream_data.capturing) qc_frame_end(qc); IDEBUG_EXIT(qc->stream_data);}/* }}} *//* {{{ [fold] qc_stream_error(struct quickcam *qc) *//* This is called when there are data lost due to errors in the stream */static void qc_stream_error(struct quickcam *qc){ /* Skip rest of data for this frame */ if (qcdebug&QC_DEBUGERRORS) PDEBUG("qc_stream_error(qc=%p)", qc); if (qc->stream_data.capturing) qc_frame_end(qc); IDEBUG_EXIT(qc->stream_data); qc_stream_init(qc);}/* }}} *//* {{{ [fold] qc_stream_add(struct quickcam *qc, unsigned char *data, int datalen) *//* * Analyse an USB packet of the data stream and store it appropriately. * Each packet contains an integral number of chunks. Each chunk has * 2-bytes identification, followed by 2-bytes that describe the chunk * length. Known/guessed chunk identifications are: * 8001/8005/C001/C005 - Begin new frame * 8002/8006/C002/C006 - End frame * 0200/4200 - Contains actual image data, bayer or compressed * 0005 - 11 bytes of unknown data * 0100 - 2 bytes of unknown data * The 0005 and 0100 chunks seem to appear only in compressed stream. * Return the amount of image data received or negative value on error. */static int qc_stream_add(struct quickcam *qc, unsigned char *data, int datalen){ struct qc_stream_data *sd = &qc->stream_data; int id, len, error, totaldata = 0; IDEBUG_TEST(*sd); while (datalen) { if (datalen < 4) { if (qcdebug&QC_DEBUGBITSTREAM) PRINTK(KERN_ERR,"missing chunk header"); break; } id = (data[0]<<8) | data[1]; len = (data[2]<<8) | data[3]; data += 4; datalen -= 4; if (datalen < len) { if (qcdebug&QC_DEBUGBITSTREAM) PRINTK(KERN_ERR,"missing chunk contents"); break; } switch (id) { case 0x8001: case 0x8005: case 0xC001: case 0xC005: /* Begin new frame, len should be zero */ if (PARANOID && len!=0) PDEBUG("New frame: len!=0"); if (sd->capturing) { if (qcdebug&QC_DEBUGBITSTREAM) PDEBUG("Missing frame end mark in stream"); qc_frame_end(qc); } sd->capturing = TRUE; if (--sd->frameskip < 0) sd->frameskip = qc->settings.frameskip; if (sd->frameskip==0) qc_frame_begin(qc); break; case 0x8002: case 0x8006: case 0xC002: case 0xC006: /* End frame, len should be zero */ if (PARANOID && len!=0) PDEBUG("End frame: len!=0"); if (sd->capturing) { if (sd->frameskip==0) qc_frame_end(qc); } else { if (qcdebug&QC_DEBUGBITSTREAM) PDEBUG("Missing frame begin mark in stream"); } sd->capturing = FALSE; break; case 0x0200: case 0x4200: /* Image data */ if (!sd->capturing && (qcdebug&QC_DEBUGBITSTREAM)) PDEBUG("Chunk of data outside frames!"); if (sd->capturing && sd->frameskip==0) { error = qc_frame_add(qc, data, len); } else { error = 0; } if (error) { /* If qc_frame_add returns error, there is more data than the frame may have, * in which case we assume stream is corrupted and skip rest packet */ if (qcdebug&QC_DEBUGERRORS) PDEBUG("qc_frame_add error %i",error); } else { totaldata += len; } break; case 0x0005: /* Unknown chunk with 11 bytes of data, occurs just before end of each frame in compressed mode */ if (len==11) break; case 0x0100: /* Unknown chunk with 2 bytes of data, occurs 2-3 times per USB interrupt */ if (len==2) break; default: /* Unknown chunk */ #ifdef DEBUG if (qcdebug&QC_DEBUGBITSTREAM) { static char dump[4*1024]; char *dump_p = dump; int i; for (i=0; i<len && (3*i+9)<sizeof(dump); i++) dump_p+=sprintf(dump_p, "%02X ", data[i]); PDEBUG("Unknown chunk %04X: %s", id, dump); } #endif break; } data += len; datalen -= len; } return totaldata;}/* }}} *//* }}} *//* {{{ [fold] **** qc_isoc: Isochronous USB transfer related routines ********* *//* * On my system (Acer Travelmate 332T, usb-ohci) there happens frequently * errors. Most common are: * -18 EXDEV (even inside individual frames) * -84 EILSEQ * -71 EPROTO * -110 ETIMEDOUT * -75
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -