6fcb5b3ef7
In the previous patch we exploited the get_{min,max}_value facility to adjust min/max allowable frequencies on the fly, depending on tuner mode. Unfortunately, this facility was not used inside the *sym_to_val() function that translates what we echo to sysfs, which means we got an -ERANGE despite asking for a frequency between what we read to be min/max. This patch corrects this small omission. Signed-off-by: Pantelis Koukousoulas <pakt223@freemail.gr> Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
612 lines
14 KiB
C
612 lines
14 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-ctrl.h"
|
|
#include "pvrusb2-hdw-internal.h"
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mutex.h>
|
|
|
|
|
|
/* Set the given control. */
|
|
int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val)
|
|
{
|
|
return pvr2_ctrl_set_mask_value(cptr,~0,val);
|
|
}
|
|
|
|
|
|
/* Set/clear specific bits of the given control. */
|
|
int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return -EINVAL;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->set_value != 0) {
|
|
if (cptr->info->type == pvr2_ctl_bitmask) {
|
|
mask &= cptr->info->def.type_bitmask.valid_bits;
|
|
} else if (cptr->info->type == pvr2_ctl_int) {
|
|
int lim;
|
|
lim = cptr->info->def.type_int.min_value;
|
|
if (cptr->info->get_min_value) {
|
|
cptr->info->get_min_value(cptr,&lim);
|
|
}
|
|
if (val < lim) break;
|
|
lim = cptr->info->def.type_int.max_value;
|
|
if (cptr->info->get_max_value) {
|
|
cptr->info->get_max_value(cptr,&lim);
|
|
}
|
|
if (val > lim) break;
|
|
} else if (cptr->info->type == pvr2_ctl_enum) {
|
|
if (val >= cptr->info->def.type_enum.count) {
|
|
break;
|
|
}
|
|
} else if (cptr->info->type != pvr2_ctl_bool) {
|
|
break;
|
|
}
|
|
ret = cptr->info->set_value(cptr,mask,val);
|
|
} else {
|
|
ret = -EPERM;
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Get the current value of the given control. */
|
|
int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return -EINVAL;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
ret = cptr->info->get_value(cptr,valptr);
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Retrieve control's type */
|
|
enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr)
|
|
{
|
|
if (!cptr) return pvr2_ctl_int;
|
|
return cptr->info->type;
|
|
}
|
|
|
|
|
|
/* Retrieve control's maximum value (int type) */
|
|
int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return 0;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->get_max_value) {
|
|
cptr->info->get_max_value(cptr,&ret);
|
|
} else if (cptr->info->type == pvr2_ctl_int) {
|
|
ret = cptr->info->def.type_int.max_value;
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Retrieve control's minimum value (int type) */
|
|
int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return 0;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->get_min_value) {
|
|
cptr->info->get_min_value(cptr,&ret);
|
|
} else if (cptr->info->type == pvr2_ctl_int) {
|
|
ret = cptr->info->def.type_int.min_value;
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Retrieve control's default value (any type) */
|
|
int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return 0;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->type == pvr2_ctl_int) {
|
|
ret = cptr->info->default_value;
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Retrieve control's enumeration count (enum only) */
|
|
int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return 0;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->type == pvr2_ctl_enum) {
|
|
ret = cptr->info->def.type_enum.count;
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Retrieve control's valid mask bits (bit mask only) */
|
|
int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr)
|
|
{
|
|
int ret = 0;
|
|
if (!cptr) return 0;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->type == pvr2_ctl_bitmask) {
|
|
ret = cptr->info->def.type_bitmask.valid_bits;
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Retrieve the control's name */
|
|
const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr)
|
|
{
|
|
if (!cptr) return NULL;
|
|
return cptr->info->name;
|
|
}
|
|
|
|
|
|
/* Retrieve the control's desc */
|
|
const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
|
|
{
|
|
if (!cptr) return NULL;
|
|
return cptr->info->desc;
|
|
}
|
|
|
|
|
|
/* Retrieve a control enumeration or bit mask value */
|
|
int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
|
|
char *bptr,unsigned int bmax,
|
|
unsigned int *blen)
|
|
{
|
|
int ret = -EINVAL;
|
|
if (!cptr) return 0;
|
|
*blen = 0;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->type == pvr2_ctl_enum) {
|
|
const char **names;
|
|
names = cptr->info->def.type_enum.value_names;
|
|
if ((val >= 0) &&
|
|
(val < cptr->info->def.type_enum.count)) {
|
|
if (names[val]) {
|
|
*blen = scnprintf(
|
|
bptr,bmax,"%s",
|
|
names[val]);
|
|
} else {
|
|
*blen = 0;
|
|
}
|
|
ret = 0;
|
|
}
|
|
} else if (cptr->info->type == pvr2_ctl_bitmask) {
|
|
const char **names;
|
|
unsigned int idx;
|
|
int msk;
|
|
names = cptr->info->def.type_bitmask.bit_names;
|
|
val &= cptr->info->def.type_bitmask.valid_bits;
|
|
for (idx = 0, msk = 1; val; idx++, msk <<= 1) {
|
|
if (val & msk) {
|
|
*blen = scnprintf(bptr,bmax,"%s",
|
|
names[idx]);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Return V4L ID for this control or zero if none */
|
|
int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr)
|
|
{
|
|
if (!cptr) return 0;
|
|
return cptr->info->v4l_id;
|
|
}
|
|
|
|
|
|
unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr)
|
|
{
|
|
unsigned int flags = 0;
|
|
|
|
if (cptr->info->get_v4lflags) {
|
|
flags = cptr->info->get_v4lflags(cptr);
|
|
}
|
|
|
|
if (cptr->info->set_value) {
|
|
flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
|
|
} else {
|
|
flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
|
|
/* Return true if control is writable */
|
|
int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr)
|
|
{
|
|
if (!cptr) return 0;
|
|
return cptr->info->set_value != 0;
|
|
}
|
|
|
|
|
|
/* Return true if control has custom symbolic representation */
|
|
int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr)
|
|
{
|
|
if (!cptr) return 0;
|
|
if (!cptr->info->val_to_sym) return 0;
|
|
if (!cptr->info->sym_to_val) return 0;
|
|
return !0;
|
|
}
|
|
|
|
|
|
/* Convert a given mask/val to a custom symbolic value */
|
|
int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr,
|
|
int mask,int val,
|
|
char *buf,unsigned int maxlen,
|
|
unsigned int *len)
|
|
{
|
|
if (!cptr) return -EINVAL;
|
|
if (!cptr->info->val_to_sym) return -EINVAL;
|
|
return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len);
|
|
}
|
|
|
|
|
|
/* Convert a symbolic value to a mask/value pair */
|
|
int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr,
|
|
const char *buf,unsigned int len,
|
|
int *maskptr,int *valptr)
|
|
{
|
|
if (!cptr) return -EINVAL;
|
|
if (!cptr->info->sym_to_val) return -EINVAL;
|
|
return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr);
|
|
}
|
|
|
|
|
|
static unsigned int gen_bitmask_string(int msk,int val,int msk_only,
|
|
const char **names,
|
|
char *ptr,unsigned int len)
|
|
{
|
|
unsigned int idx;
|
|
long sm,um;
|
|
int spcFl;
|
|
unsigned int uc,cnt;
|
|
const char *idStr;
|
|
|
|
spcFl = 0;
|
|
uc = 0;
|
|
um = 0;
|
|
for (idx = 0, sm = 1; msk; idx++, sm <<= 1) {
|
|
if (sm & msk) {
|
|
msk &= ~sm;
|
|
idStr = names[idx];
|
|
if (idStr) {
|
|
cnt = scnprintf(ptr,len,"%s%s%s",
|
|
(spcFl ? " " : ""),
|
|
(msk_only ? "" :
|
|
((val & sm) ? "+" : "-")),
|
|
idStr);
|
|
ptr += cnt; len -= cnt; uc += cnt;
|
|
spcFl = !0;
|
|
} else {
|
|
um |= sm;
|
|
}
|
|
}
|
|
}
|
|
if (um) {
|
|
if (msk_only) {
|
|
cnt = scnprintf(ptr,len,"%s0x%lx",
|
|
(spcFl ? " " : ""),
|
|
um);
|
|
ptr += cnt; len -= cnt; uc += cnt;
|
|
spcFl = !0;
|
|
} else if (um & val) {
|
|
cnt = scnprintf(ptr,len,"%s+0x%lx",
|
|
(spcFl ? " " : ""),
|
|
um & val);
|
|
ptr += cnt; len -= cnt; uc += cnt;
|
|
spcFl = !0;
|
|
} else if (um & ~val) {
|
|
cnt = scnprintf(ptr,len,"%s+0x%lx",
|
|
(spcFl ? " " : ""),
|
|
um & ~val);
|
|
ptr += cnt; len -= cnt; uc += cnt;
|
|
spcFl = !0;
|
|
}
|
|
}
|
|
return uc;
|
|
}
|
|
|
|
|
|
static const char *boolNames[] = {
|
|
"false",
|
|
"true",
|
|
"no",
|
|
"yes",
|
|
};
|
|
|
|
|
|
static int parse_token(const char *ptr,unsigned int len,
|
|
int *valptr,
|
|
const char **names,unsigned int namecnt)
|
|
{
|
|
char buf[33];
|
|
unsigned int slen;
|
|
unsigned int idx;
|
|
int negfl;
|
|
char *p2;
|
|
*valptr = 0;
|
|
if (!names) namecnt = 0;
|
|
for (idx = 0; idx < namecnt; idx++) {
|
|
if (!names[idx]) continue;
|
|
slen = strlen(names[idx]);
|
|
if (slen != len) continue;
|
|
if (memcmp(names[idx],ptr,slen)) continue;
|
|
*valptr = idx;
|
|
return 0;
|
|
}
|
|
negfl = 0;
|
|
if ((*ptr == '-') || (*ptr == '+')) {
|
|
negfl = (*ptr == '-');
|
|
ptr++; len--;
|
|
}
|
|
if (len >= sizeof(buf)) return -EINVAL;
|
|
memcpy(buf,ptr,len);
|
|
buf[len] = 0;
|
|
*valptr = simple_strtol(buf,&p2,0);
|
|
if (negfl) *valptr = -(*valptr);
|
|
if (*p2) return -EINVAL;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int parse_mtoken(const char *ptr,unsigned int len,
|
|
int *valptr,
|
|
const char **names,int valid_bits)
|
|
{
|
|
char buf[33];
|
|
unsigned int slen;
|
|
unsigned int idx;
|
|
char *p2;
|
|
int msk;
|
|
*valptr = 0;
|
|
for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) {
|
|
if (!msk & valid_bits) continue;
|
|
valid_bits &= ~msk;
|
|
if (!names[idx]) continue;
|
|
slen = strlen(names[idx]);
|
|
if (slen != len) continue;
|
|
if (memcmp(names[idx],ptr,slen)) continue;
|
|
*valptr = msk;
|
|
return 0;
|
|
}
|
|
if (len >= sizeof(buf)) return -EINVAL;
|
|
memcpy(buf,ptr,len);
|
|
buf[len] = 0;
|
|
*valptr = simple_strtol(buf,&p2,0);
|
|
if (*p2) return -EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int parse_tlist(const char *ptr,unsigned int len,
|
|
int *maskptr,int *valptr,
|
|
const char **names,int valid_bits)
|
|
{
|
|
unsigned int cnt;
|
|
int mask,val,kv,mode,ret;
|
|
mask = 0;
|
|
val = 0;
|
|
ret = 0;
|
|
while (len) {
|
|
cnt = 0;
|
|
while ((cnt < len) &&
|
|
((ptr[cnt] <= 32) ||
|
|
(ptr[cnt] >= 127))) cnt++;
|
|
ptr += cnt;
|
|
len -= cnt;
|
|
mode = 0;
|
|
if ((*ptr == '-') || (*ptr == '+')) {
|
|
mode = (*ptr == '-') ? -1 : 1;
|
|
ptr++;
|
|
len--;
|
|
}
|
|
cnt = 0;
|
|
while (cnt < len) {
|
|
if (ptr[cnt] <= 32) break;
|
|
if (ptr[cnt] >= 127) break;
|
|
cnt++;
|
|
}
|
|
if (!cnt) break;
|
|
if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
ptr += cnt;
|
|
len -= cnt;
|
|
switch (mode) {
|
|
case 0:
|
|
mask = valid_bits;
|
|
val |= kv;
|
|
break;
|
|
case -1:
|
|
mask |= kv;
|
|
val &= ~kv;
|
|
break;
|
|
case 1:
|
|
mask |= kv;
|
|
val |= kv;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
*maskptr = mask;
|
|
*valptr = val;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Convert a symbolic value to a mask/value pair */
|
|
int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
|
|
const char *ptr,unsigned int len,
|
|
int *maskptr,int *valptr)
|
|
{
|
|
int ret = -EINVAL;
|
|
unsigned int cnt;
|
|
|
|
*maskptr = 0;
|
|
*valptr = 0;
|
|
|
|
cnt = 0;
|
|
while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++;
|
|
len -= cnt; ptr += cnt;
|
|
cnt = 0;
|
|
while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) ||
|
|
(ptr[len-(cnt+1)] >= 127))) cnt++;
|
|
len -= cnt;
|
|
|
|
if (!len) return -EINVAL;
|
|
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
if (cptr->info->type == pvr2_ctl_int) {
|
|
ret = parse_token(ptr,len,valptr,NULL,0);
|
|
if (ret >= 0) {
|
|
int min, max;
|
|
min = cptr->info->def.type_int.min_value;
|
|
if (cptr->info->get_min_value) {
|
|
cptr->info->get_min_value(cptr,&min);
|
|
}
|
|
max = cptr->info->def.type_int.max_value;
|
|
if (cptr->info->get_max_value) {
|
|
cptr->info->get_max_value(cptr,&max);
|
|
}
|
|
if ((*valptr < min) || (*valptr > max)) {
|
|
ret = -ERANGE;
|
|
}
|
|
}
|
|
if (maskptr) *maskptr = ~0;
|
|
} else if (cptr->info->type == pvr2_ctl_bool) {
|
|
ret = parse_token(
|
|
ptr,len,valptr,boolNames,
|
|
sizeof(boolNames)/sizeof(boolNames[0]));
|
|
if (ret == 1) {
|
|
*valptr = *valptr ? !0 : 0;
|
|
} else if (ret == 0) {
|
|
*valptr = (*valptr & 1) ? !0 : 0;
|
|
}
|
|
if (maskptr) *maskptr = 1;
|
|
} else if (cptr->info->type == pvr2_ctl_enum) {
|
|
ret = parse_token(
|
|
ptr,len,valptr,
|
|
cptr->info->def.type_enum.value_names,
|
|
cptr->info->def.type_enum.count);
|
|
if ((ret >= 0) &&
|
|
((*valptr < 0) ||
|
|
(*valptr >= cptr->info->def.type_enum.count))) {
|
|
ret = -ERANGE;
|
|
}
|
|
if (maskptr) *maskptr = ~0;
|
|
} else if (cptr->info->type == pvr2_ctl_bitmask) {
|
|
ret = parse_tlist(
|
|
ptr,len,maskptr,valptr,
|
|
cptr->info->def.type_bitmask.bit_names,
|
|
cptr->info->def.type_bitmask.valid_bits);
|
|
}
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Convert a given mask/val to a symbolic value */
|
|
int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr,
|
|
int mask,int val,
|
|
char *buf,unsigned int maxlen,
|
|
unsigned int *len)
|
|
{
|
|
int ret = -EINVAL;
|
|
|
|
*len = 0;
|
|
if (cptr->info->type == pvr2_ctl_int) {
|
|
*len = scnprintf(buf,maxlen,"%d",val);
|
|
ret = 0;
|
|
} else if (cptr->info->type == pvr2_ctl_bool) {
|
|
*len = scnprintf(buf,maxlen,"%s",val ? "true" : "false");
|
|
ret = 0;
|
|
} else if (cptr->info->type == pvr2_ctl_enum) {
|
|
const char **names;
|
|
names = cptr->info->def.type_enum.value_names;
|
|
if ((val >= 0) &&
|
|
(val < cptr->info->def.type_enum.count)) {
|
|
if (names[val]) {
|
|
*len = scnprintf(
|
|
buf,maxlen,"%s",
|
|
names[val]);
|
|
} else {
|
|
*len = 0;
|
|
}
|
|
ret = 0;
|
|
}
|
|
} else if (cptr->info->type == pvr2_ctl_bitmask) {
|
|
*len = gen_bitmask_string(
|
|
val & mask & cptr->info->def.type_bitmask.valid_bits,
|
|
~0,!0,
|
|
cptr->info->def.type_bitmask.bit_names,
|
|
buf,maxlen);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Convert a given mask/val to a symbolic value */
|
|
int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr,
|
|
int mask,int val,
|
|
char *buf,unsigned int maxlen,
|
|
unsigned int *len)
|
|
{
|
|
int ret;
|
|
LOCK_TAKE(cptr->hdw->big_lock); do {
|
|
ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val,
|
|
buf,maxlen,len);
|
|
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
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: ***
|
|
*/
|