linux/drivers/media/video/pvrusb2/pvrusb2-std.c
Mike Isely d855497edb V4L/DVB (4228a): pvrusb2 to kernel 2.6.18
Implement V4L2 driver for the Hauppauge PVR USB2 TV tuner.

The Hauppauge PVR USB2 is a USB connected TV tuner with an embedded
cx23416 hardware MPEG2 encoder.  There are two major variants of this
device; this driver handles both.  Any V4L2 application which
understands MPEG2 video stream data should be able to work with this
device.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2006-06-27 00:17:15 -03:00

407 lines
9.3 KiB
C

/*
*
* $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "pvrusb2-std.h"
#include "pvrusb2-debug.h"
struct std_name {
const char *name;
v4l2_std_id id;
};
#define CSTD_PAL \
(V4L2_STD_PAL_B| \
V4L2_STD_PAL_B1| \
V4L2_STD_PAL_G| \
V4L2_STD_PAL_H| \
V4L2_STD_PAL_I| \
V4L2_STD_PAL_D| \
V4L2_STD_PAL_D1| \
V4L2_STD_PAL_K| \
V4L2_STD_PAL_M| \
V4L2_STD_PAL_N| \
V4L2_STD_PAL_Nc| \
V4L2_STD_PAL_60)
#define CSTD_NTSC \
(V4L2_STD_NTSC_M| \
V4L2_STD_NTSC_M_JP| \
V4L2_STD_NTSC_M_KR| \
V4L2_STD_NTSC_443)
#define CSTD_SECAM \
(V4L2_STD_SECAM_B| \
V4L2_STD_SECAM_D| \
V4L2_STD_SECAM_G| \
V4L2_STD_SECAM_H| \
V4L2_STD_SECAM_K| \
V4L2_STD_SECAM_K1| \
V4L2_STD_SECAM_L| \
V4L2_STD_SECAM_LC)
#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B)
#define TSTD_B1 (V4L2_STD_PAL_B1)
#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D)
#define TSTD_D1 (V4L2_STD_PAL_D1)
#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G)
#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H)
#define TSTD_I (V4L2_STD_PAL_I)
#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K)
#define TSTD_K1 (V4L2_STD_SECAM_K1)
#define TSTD_L (V4L2_STD_SECAM_L)
#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M)
#define TSTD_N (V4L2_STD_PAL_N)
#define TSTD_Nc (V4L2_STD_PAL_Nc)
#define TSTD_60 (V4L2_STD_PAL_60)
#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM)
/* Mapping of standard bits to color system */
const static struct std_name std_groups[] = {
{"PAL",CSTD_PAL},
{"NTSC",CSTD_NTSC},
{"SECAM",CSTD_SECAM},
};
/* Mapping of standard bits to modulation system */
const static struct std_name std_items[] = {
{"B",TSTD_B},
{"B1",TSTD_B1},
{"D",TSTD_D},
{"D1",TSTD_D1},
{"G",TSTD_G},
{"H",TSTD_H},
{"I",TSTD_I},
{"K",TSTD_K},
{"K1",TSTD_K1},
{"L",TSTD_L},
{"LC",V4L2_STD_SECAM_LC},
{"M",TSTD_M},
{"Mj",V4L2_STD_NTSC_M_JP},
{"443",V4L2_STD_NTSC_443},
{"Mk",V4L2_STD_NTSC_M_KR},
{"N",TSTD_N},
{"Nc",TSTD_Nc},
{"60",TSTD_60},
};
// Search an array of std_name structures and return a pointer to the
// element with the matching name.
static const struct std_name *find_std_name(const struct std_name *arrPtr,
unsigned int arrSize,
const char *bufPtr,
unsigned int bufSize)
{
unsigned int idx;
const struct std_name *p;
for (idx = 0; idx < arrSize; idx++) {
p = arrPtr + idx;
if (strlen(p->name) != bufSize) continue;
if (!memcmp(bufPtr,p->name,bufSize)) return p;
}
return 0;
}
int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
unsigned int bufSize)
{
v4l2_std_id id = 0;
v4l2_std_id cmsk = 0;
v4l2_std_id t;
int mMode = 0;
unsigned int cnt;
char ch;
const struct std_name *sp;
while (bufSize) {
if (!mMode) {
cnt = 0;
while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++;
if (cnt >= bufSize) return 0; // No more characters
sp = find_std_name(
std_groups,
sizeof(std_groups)/sizeof(std_groups[0]),
bufPtr,cnt);
if (!sp) return 0; // Illegal color system name
cnt++;
bufPtr += cnt;
bufSize -= cnt;
mMode = !0;
cmsk = sp->id;
continue;
}
cnt = 0;
while (cnt < bufSize) {
ch = bufPtr[cnt];
if (ch == ';') {
mMode = 0;
break;
}
if (ch == '/') break;
cnt++;
}
sp = find_std_name(std_items,
sizeof(std_items)/sizeof(std_items[0]),
bufPtr,cnt);
if (!sp) return 0; // Illegal modulation system ID
t = sp->id & cmsk;
if (!t) return 0; // Specific color + modulation system illegal
id |= t;
if (cnt < bufSize) cnt++;
bufPtr += cnt;
bufSize -= cnt;
}
if (idPtr) *idPtr = id;
return !0;
}
unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
v4l2_std_id id)
{
unsigned int idx1,idx2;
const struct std_name *ip,*gp;
int gfl,cfl;
unsigned int c1,c2;
cfl = 0;
c1 = 0;
for (idx1 = 0;
idx1 < sizeof(std_groups)/sizeof(std_groups[0]);
idx1++) {
gp = std_groups + idx1;
gfl = 0;
for (idx2 = 0;
idx2 < sizeof(std_items)/sizeof(std_items[0]);
idx2++) {
ip = std_items + idx2;
if (!(gp->id & ip->id & id)) continue;
if (!gfl) {
if (cfl) {
c2 = scnprintf(bufPtr,bufSize,";");
c1 += c2;
bufSize -= c2;
bufPtr += c2;
}
cfl = !0;
c2 = scnprintf(bufPtr,bufSize,
"%s-",gp->name);
gfl = !0;
} else {
c2 = scnprintf(bufPtr,bufSize,"/");
}
c1 += c2;
bufSize -= c2;
bufPtr += c2;
c2 = scnprintf(bufPtr,bufSize,
ip->name);
c1 += c2;
bufSize -= c2;
bufPtr += c2;
}
}
return c1;
}
// Template data for possible enumerated video standards. Here we group
// standards which share common frame rates and resolution.
static struct v4l2_standard generic_standards[] = {
{
.id = (TSTD_B|TSTD_B1|
TSTD_D|TSTD_D1|
TSTD_G|
TSTD_H|
TSTD_I|
TSTD_K|TSTD_K1|
TSTD_L|
V4L2_STD_SECAM_LC |
TSTD_N|TSTD_Nc),
.frameperiod =
{
.numerator = 1,
.denominator= 25
},
.framelines = 625,
.reserved = {0,0,0,0}
}, {
.id = (TSTD_M|
V4L2_STD_NTSC_M_JP|
V4L2_STD_NTSC_M_KR),
.frameperiod =
{
.numerator = 1001,
.denominator= 30000
},
.framelines = 525,
.reserved = {0,0,0,0}
}, { // This is a total wild guess
.id = (TSTD_60),
.frameperiod =
{
.numerator = 1001,
.denominator= 30000
},
.framelines = 525,
.reserved = {0,0,0,0}
}, { // This is total wild guess
.id = V4L2_STD_NTSC_443,
.frameperiod =
{
.numerator = 1001,
.denominator= 30000
},
.framelines = 525,
.reserved = {0,0,0,0}
}
};
#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0]))
static struct v4l2_standard *match_std(v4l2_std_id id)
{
unsigned int idx;
for (idx = 0; idx < generic_standards_cnt; idx++) {
if (generic_standards[idx].id & id) {
return generic_standards + idx;
}
}
return 0;
}
static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id)
{
struct v4l2_standard *template;
int idx;
unsigned int bcnt;
template = match_std(id);
if (!template) return 0;
idx = std->index;
memcpy(std,template,sizeof(*template));
std->index = idx;
std->id = id;
bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id);
std->name[bcnt] = 0;
pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s",
std->index,std->name);
return !0;
}
/* These are special cases of combined standards that we should enumerate
separately if the component pieces are present. */
static v4l2_std_id std_mixes[] = {
V4L2_STD_PAL_B | V4L2_STD_PAL_G,
V4L2_STD_PAL_D | V4L2_STD_PAL_K,
V4L2_STD_SECAM_B | V4L2_STD_SECAM_G,
V4L2_STD_SECAM_D | V4L2_STD_SECAM_K,
};
struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
v4l2_std_id id)
{
unsigned int std_cnt = 0;
unsigned int idx,bcnt,idx2;
v4l2_std_id idmsk,cmsk,fmsk;
struct v4l2_standard *stddefs;
if (pvrusb2_debug & PVR2_TRACE_INIT) {
char buf[50];
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id);
pvr2_trace(
PVR2_TRACE_INIT,"Mapping standards mask=0x%x (%.*s)",
(int)id,bcnt,buf);
}
*countptr = 0;
std_cnt = 0;
fmsk = 0;
for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) {
if (!(idmsk & cmsk)) continue;
cmsk &= ~idmsk;
if (match_std(idmsk)) {
std_cnt++;
continue;
}
fmsk |= idmsk;
}
for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) {
if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++;
}
if (fmsk) {
char buf[50];
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk);
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"WARNING:"
" Failed to classify the following standard(s): %.*s",
bcnt,buf);
}
pvr2_trace(PVR2_TRACE_INIT,"Setting up %u unique standard(s)",
std_cnt);
if (!std_cnt) return 0; // paranoia
stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt,
GFP_KERNEL);
memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt);
for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx;
idx = 0;
/* Enumerate potential special cases */
for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) &&
(idx < std_cnt)); idx2++) {
if (!(id & std_mixes[idx2])) continue;
if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++;
}
/* Now enumerate individual pieces */
for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) {
if (!(idmsk & cmsk)) continue;
cmsk &= ~idmsk;
if (!pvr2_std_fill(stddefs+idx,idmsk)) continue;
idx++;
}
*countptr = std_cnt;
return stddefs;
}
v4l2_std_id pvr2_std_get_usable(void)
{
return CSTD_ALL;
}
/*
Stuff for Emacs to see, in order to encourage consistent editing style:
*** Local Variables: ***
*** mode: c ***
*** fill-column: 75 ***
*** tab-width: 8 ***
*** c-basic-offset: 8 ***
*** End: ***
*/