mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
584 lines
13 KiB
584 lines
13 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* |
|
* Copyright (C) 2005 Mike Isely <[email protected]> |
|
*/ |
|
|
|
#include "pvrusb2-ctrl.h" |
|
#include "pvrusb2-hdw-internal.h" |
|
#include <linux/errno.h> |
|
#include <linux/string.h> |
|
#include <linux/mutex.h> |
|
|
|
|
|
static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) |
|
{ |
|
if (cptr->info->check_value) { |
|
if (!cptr->info->check_value(cptr,val)) return -ERANGE; |
|
} else if (cptr->info->type == pvr2_ctl_enum) { |
|
if (val < 0) return -ERANGE; |
|
if (val >= cptr->info->def.type_enum.count) return -ERANGE; |
|
} else { |
|
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) return -ERANGE; |
|
lim = cptr->info->def.type_int.max_value; |
|
if (cptr->info->get_max_value) { |
|
cptr->info->get_max_value(cptr,&lim); |
|
} |
|
if (val > lim) return -ERANGE; |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
/* 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) { |
|
if (cptr->info->type == pvr2_ctl_bitmask) { |
|
mask &= cptr->info->def.type_bitmask.valid_bits; |
|
} else if ((cptr->info->type == pvr2_ctl_int)|| |
|
(cptr->info->type == pvr2_ctl_enum)) { |
|
ret = pvr2_ctrl_range_check(cptr,val); |
|
if (ret < 0) 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 *valptr) |
|
{ |
|
int ret = 0; |
|
if (!cptr) return -EINVAL; |
|
LOCK_TAKE(cptr->hdw->big_lock); do { |
|
if (cptr->info->get_def_value) { |
|
ret = cptr->info->get_def_value(cptr, valptr); |
|
} else { |
|
*valptr = 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 * const *names; |
|
names = cptr->info->def.type_enum.value_names; |
|
if (pvr2_ctrl_range_check(cptr,val) == 0) { |
|
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 != NULL; |
|
} |
|
|
|
|
|
/* 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 * const *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) { |
|
ret = pvr2_ctrl_range_check(cptr,*valptr); |
|
} |
|
*maskptr = ~0; |
|
} else if (cptr->info->type == pvr2_ctl_bool) { |
|
ret = parse_token(ptr,len,valptr,boolNames, |
|
ARRAY_SIZE(boolNames)); |
|
if (ret == 1) { |
|
*valptr = *valptr ? !0 : 0; |
|
} else if (ret == 0) { |
|
*valptr = (*valptr & 1) ? !0 : 0; |
|
} |
|
*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) { |
|
ret = pvr2_ctrl_range_check(cptr,*valptr); |
|
} |
|
*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 * const *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; |
|
}
|
|
|