staging/easycap: Add option to show conspicuous indication of signal loss

A new module parameter turns on the option of displaying a testcard when
the analogue input signal is lost (more precisely: when the hardware
detects no field/frame synchronization).  This feature has been requested
in the context of security cameras used at night.

Signed-off-by: Mike Thomas <rmthomas@sciolus.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Mike Thomas 2010-11-07 20:03:50 +00:00 committed by Greg Kroah-Hartman
parent 40b8d50ac9
commit 849322a0f1
3 changed files with 112 additions and 41 deletions

View file

@ -154,6 +154,7 @@
#error video_isoc_buffer[.] will not be big enough #error video_isoc_buffer[.] will not be big enough
#endif #endif
#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY #define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY
#define VIDEO_LOST_TOLERATE 50
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* VIDEO BUFFERS * VIDEO BUFFERS
@ -344,6 +345,7 @@ int usec;
int tolerate; int tolerate;
int skip; int skip;
int skipped; int skipped;
int lost[INPUT_MANY];
int merit[180]; int merit[180];
struct timeval timeval0; struct timeval timeval0;

View file

@ -33,7 +33,9 @@
#include "easycap_ioctl.h" #include "easycap_ioctl.h"
int debug; int debug;
int bars;
module_param(debug, int, S_IRUGO | S_IWUSR); module_param(debug, int, S_IRUGO | S_IWUSR);
module_param(bars, int, S_IRUGO | S_IWUSR);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
@ -868,7 +870,7 @@ return 0;
void void
easycap_delete(struct kref *pkref) easycap_delete(struct kref *pkref)
{ {
int k, m, lost; int k, m, gone;
int allocation_video_urb, allocation_video_page, allocation_video_struct; int allocation_video_urb, allocation_video_page, allocation_video_struct;
int allocation_audio_urb, allocation_audio_page, allocation_audio_struct; int allocation_audio_urb, allocation_audio_page, allocation_audio_struct;
int registered_video, registered_audio; int registered_video, registered_audio;
@ -941,7 +943,7 @@ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
JOM(4, "isoc video buffers freed: %i pages\n", m * (0x01 << VIDEO_ISOC_ORDER)); JOM(4, "isoc video buffers freed: %i pages\n", m * (0x01 << VIDEO_ISOC_ORDER));
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
JOM(4, "freeing video field buffers.\n"); JOM(4, "freeing video field buffers.\n");
lost = 0; gone = 0;
for (k = 0; k < FIELD_BUFFER_MANY; k++) { for (k = 0; k < FIELD_BUFFER_MANY; k++) {
for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
if ((void *)NULL != peasycap->field_buffer[k][m].pgo) { if ((void *)NULL != peasycap->field_buffer[k][m].pgo) {
@ -949,14 +951,14 @@ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
(peasycap->field_buffer[k][m].pgo)); (peasycap->field_buffer[k][m].pgo));
peasycap->field_buffer[k][m].pgo = (void *)NULL; peasycap->field_buffer[k][m].pgo = (void *)NULL;
peasycap->allocation_video_page -= 1; peasycap->allocation_video_page -= 1;
lost++; gone++;
} }
} }
} }
JOM(4, "video field buffers freed: %i pages\n", lost); JOM(4, "video field buffers freed: %i pages\n", gone);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
JOM(4, "freeing video frame buffers.\n"); JOM(4, "freeing video frame buffers.\n");
lost = 0; gone = 0;
for (k = 0; k < FRAME_BUFFER_MANY; k++) { for (k = 0; k < FRAME_BUFFER_MANY; k++) {
for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
if ((void *)NULL != peasycap->frame_buffer[k][m].pgo) { if ((void *)NULL != peasycap->frame_buffer[k][m].pgo) {
@ -964,11 +966,11 @@ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
(peasycap->frame_buffer[k][m].pgo)); (peasycap->frame_buffer[k][m].pgo));
peasycap->frame_buffer[k][m].pgo = (void *)NULL; peasycap->frame_buffer[k][m].pgo = (void *)NULL;
peasycap->allocation_video_page -= 1; peasycap->allocation_video_page -= 1;
lost++; gone++;
} }
} }
} }
JOM(4, "video frame buffers freed: %i pages\n", lost); JOM(4, "video frame buffers freed: %i pages\n", gone);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
* FREE AUDIO. * FREE AUDIO.
@ -1027,16 +1029,16 @@ JOM(4, "easysnd_delete(): isoc audio buffers freed: %i pages\n", \
m * (0x01 << AUDIO_ISOC_ORDER)); m * (0x01 << AUDIO_ISOC_ORDER));
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
JOM(4, "freeing audio buffers.\n"); JOM(4, "freeing audio buffers.\n");
lost = 0; gone = 0;
for (k = 0; k < peasycap->audio_buffer_page_many; k++) { for (k = 0; k < peasycap->audio_buffer_page_many; k++) {
if ((void *)NULL != peasycap->audio_buffer[k].pgo) { if ((void *)NULL != peasycap->audio_buffer[k].pgo) {
free_page((unsigned long)(peasycap->audio_buffer[k].pgo)); free_page((unsigned long)(peasycap->audio_buffer[k].pgo));
peasycap->audio_buffer[k].pgo = (void *)NULL; peasycap->audio_buffer[k].pgo = (void *)NULL;
peasycap->allocation_audio_page -= 1; peasycap->allocation_audio_page -= 1;
lost++; gone++;
} }
} }
JOM(4, "easysnd_delete(): audio buffers freed: %i pages\n", lost); JOM(4, "easysnd_delete(): audio buffers freed: %i pages\n", gone);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
JOM(4, "freeing easycap structure.\n"); JOM(4, "freeing easycap structure.\n");
allocation_video_urb = peasycap->allocation_video_urb; allocation_video_urb = peasycap->allocation_video_urb;
@ -1103,7 +1105,7 @@ else
int int
easycap_dqbuf(struct easycap *peasycap, int mode) easycap_dqbuf(struct easycap *peasycap, int mode)
{ {
int ifield, miss, rc; int input, ifield, miss, rc;
JOT(8, "\n"); JOT(8, "\n");
@ -1114,6 +1116,36 @@ if (NULL == peasycap) {
ifield = 0; ifield = 0;
JOM(8, "%i=ifield\n", ifield); JOM(8, "%i=ifield\n", ifield);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/*
* CHECK FOR LOST INPUT SIGNAL.
*
* FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
* IF INPUT 0 IS PRESENT AND LOCKED, UNPLUGGING INPUT 4 DOES NOT RESULT IN
* SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE IS FLYWHEELING
* ON INPUT 0. THE UPSHOT IS:
*
* INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK
* INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK
* INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK
* INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS
*/
/*---------------------------------------------------------------------------*/
input = peasycap->input;
if (0 <= input && INPUT_MANY > input) {
rc = read_saa(peasycap->pusb_device, 0x1F);
if (0 <= rc) {
if (rc & 0x40)
peasycap->lost[input] += 1;
else
peasycap->lost[input] -= 2;
if (0 > peasycap->lost[input])
peasycap->lost[input] = 0;
else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input])
peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE);
}
}
/*---------------------------------------------------------------------------*/
/* /*
* WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM) * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM)
*/ */
@ -1304,7 +1336,7 @@ struct signed_div_result sdr;
void *pex, *pad; void *pex, *pad;
int kex, kad, mex, mad, rex, rad, rad2; int kex, kad, mex, mad, rex, rad, rad2;
int c2, c3, w2, w3, cz, wz; int c2, c3, w2, w3, cz, wz;
int rc, bytesperpixel, multiplier, much, more, over, rump, caches; int rc, bytesperpixel, multiplier, much, more, over, rump, caches, input;
__u8 mask, margin; __u8 mask, margin;
bool odd, isuy, decimatepixel, offerfields, badinput; bool odd, isuy, decimatepixel, offerfields, badinput;
@ -1314,6 +1346,7 @@ if ((struct easycap *)NULL == peasycap) {
} }
badinput = false; badinput = false;
input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input;
JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> " \ JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> " \
"frame buffer %i\n", \ "frame buffer %i\n", \
@ -1337,8 +1370,10 @@ if (peasycap->field_read == peasycap->field_fill) {
#if defined(EASYCAP_TESTCARD) #if defined(EASYCAP_TESTCARD)
easycap_testcard(peasycap, peasycap->field_read); easycap_testcard(peasycap, peasycap->field_read);
#else #else
if (0 != (0x0400 & peasycap->field_buffer[peasycap->field_read][0].kount)) if (0 <= input && INPUT_MANY > input) {
easycap_testcard(peasycap, peasycap->field_read); if (bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input])
easycap_testcard(peasycap, peasycap->field_read);
}
#endif /*EASYCAP_TESTCARD*/ #endif /*EASYCAP_TESTCARD*/
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -3491,6 +3526,8 @@ if (0 == bInterfaceNumber) {
peasycap->frame_buffer_many = FRAME_BUFFER_MANY; peasycap->frame_buffer_many = FRAME_BUFFER_MANY;
for (k = 0; k < INPUT_MANY; k++)
peasycap->lost[k] = 0;
peasycap->skip = 0; peasycap->skip = 0;
peasycap->skipped = 0; peasycap->skipped = 0;
peasycap->offerfields = 0; peasycap->offerfields = 0;
@ -4733,7 +4770,7 @@ easycap_module_init(void)
int result; int result;
SAY("========easycap=======\n"); SAY("========easycap=======\n");
JOT(4, "begins. %i=debug\n", debug); JOT(4, "begins. %i=debug %i=bars\n", debug, bars);
SAY("version: " EASYCAP_DRIVER_VERSION "\n"); SAY("version: " EASYCAP_DRIVER_VERSION "\n");
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* /*
@ -4774,6 +4811,8 @@ MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>");
MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION); MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION);
MODULE_VERSION(EASYCAP_DRIVER_VERSION); MODULE_VERSION(EASYCAP_DRIVER_VERSION);
#if defined(EASYCAP_DEBUG) #if defined(EASYCAP_DEBUG)
MODULE_PARM_DESC(debug, "debug: 0 (default), 1, 2,..."); MODULE_PARM_DESC(debug, "Debug level: 0 (default),1,2,...,9");
#endif /*EASYCAP_DEBUG*/ #endif /*EASYCAP_DEBUG*/
MODULE_PARM_DESC(bars, \
"Testcard bars on input signal failure: 0=>no, 1=>yes(default)");
/*****************************************************************************/ /*****************************************************************************/

View file

@ -29,37 +29,69 @@
#include "easycap_debug.h" #include "easycap_debug.h"
/*****************************************************************************/ /*****************************************************************************/
#define TESTCARD_BYTESPERLINE (2 * 1440) #define TESTCARD_BYTESPERLINE (2 * 720)
void void
easycap_testcard(struct easycap *peasycap, int field_fill) easycap_testcard(struct easycap *peasycap, int field)
{ {
int total; int total;
int y, u, v, r, g, b; int y, u, v, r, g, b;
unsigned char uyvy[4]; unsigned char uyvy[4];
int i1, line, k, m, n, more, much, barwidth, barheight;
int i1, line, k, m, n, more, much, barwidth;
unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2; unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2;
struct data_buffer *pfield_buffer; struct data_buffer *pfield_buffer;
JOT(8, "%i=field_fill\n", field_fill); if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
if ((TESTCARD_BYTESPERLINE / 2) < peasycap->width) {
SAY("ERROR: image is too wide\n");
return; return;
} }
if (peasycap->width % 16) { JOM(8, "%i=field\n", field);
SAY("ERROR: indivisible image width\n"); switch (peasycap->width) {
case 720:
case 360: {
barwidth = (2 * 720) / 8;
break;
}
case 704:
case 352: {
barwidth = (2 * 704) / 8;
break;
}
case 640:
case 320: {
barwidth = (2 * 640) / 8;
break;
}
default: {
SAM("ERROR: cannot set barwidth\n");
return; return;
} }
}
if (TESTCARD_BYTESPERLINE < barwidth) {
SAM("ERROR: barwidth is too large\n");
return;
}
switch (peasycap->height) {
case 576:
case 288: {
barheight = 576;
break;
}
case 480:
case 240: {
barheight = 480;
break;
}
default: {
SAM("ERROR: cannot set barheight\n");
return;
}
}
total = 0; total = 0;
barwidth = (2 * peasycap->width) / 8; k = field;
k = field_fill;
m = 0; m = 0;
n = 0; n = 0;
for (line = 0; line < (peasycap->height / 2); line++) { for (line = 0; line < (barheight / 2); line++) {
for (i1 = 0; i1 < 8; i1++) { for (i1 = 0; i1 < 8; i1++) {
r = (i1 * 256)/8; r = (i1 * 256)/8;
g = (i1 * 256)/8; g = (i1 * 256)/8;
@ -88,15 +120,15 @@ for (line = 0; line < (peasycap->height / 2); line++) {
while (more) { while (more) {
if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) { if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) {
SAY("ERROR: bad m reached\n"); SAM("ERROR: bad m reached\n");
return; return;
} }
if (PAGE_SIZE < n) { if (PAGE_SIZE < n) {
SAY("ERROR: bad n reached\n"); return; SAM("ERROR: bad n reached\n"); return;
} }
if (0 > more) { if (0 > more) {
SAY("ERROR: internal fault\n"); SAM("ERROR: internal fault\n");
return; return;
} }
@ -117,10 +149,6 @@ for (line = 0; line < (peasycap->height / 2); line++) {
} }
} }
} }
JOT(8, "%i=total\n", total);
if (total != peasycap->width * peasycap->height)
SAY("ERROR: wrong number of bytes written: %i\n", total);
return; return;
} }
/*****************************************************************************/ /*****************************************************************************/
@ -375,10 +403,12 @@ int i1;
unsigned char *p2; unsigned char *p2;
struct data_buffer *paudio_buffer; struct data_buffer *paudio_buffer;
JOT(8, "%i=audio_fill\n", audio_fill); if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return;
}
JOM(8, "%i=audio_fill\n", audio_fill);
paudio_buffer = &peasycap->audio_buffer[audio_fill]; paudio_buffer = &peasycap->audio_buffer[audio_fill];
p2 = (unsigned char *)(paudio_buffer->pgo); p2 = (unsigned char *)(paudio_buffer->pgo);
for (i1 = 0; i1 < PAGE_SIZE; i1 += 4, p2 += 4) { for (i1 = 0; i1 < PAGE_SIZE; i1 += 4, p2 += 4) {
*p2 = (unsigned char) (0x00FF & tones[i1/2]); *p2 = (unsigned char) (0x00FF & tones[i1/2]);