2006-06-27 03:58:46 +04:00
/*
*
*
* 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>
2006-12-28 05:28:54 +03:00
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 ;
2008-04-22 21:45:38 +04:00
} else if ( cptr - > info - > type = = pvr2_ctl_enum ) {
if ( val < 0 ) return - ERANGE ;
if ( val > = cptr - > info - > def . type_enum . count ) return - ERANGE ;
2006-12-28 05:28:54 +03:00
} 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 ;
}
2006-06-27 03:58:46 +04:00
/* 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 {
2008-03-29 06:07:38 +03:00
if ( cptr - > info - > set_value ) {
2006-06-27 03:58:46 +04:00
if ( cptr - > info - > type = = pvr2_ctl_bitmask ) {
mask & = cptr - > info - > def . type_bitmask . valid_bits ;
2008-04-22 21:45:38 +04:00
} else if ( ( cptr - > info - > type = = pvr2_ctl_int ) | |
( cptr - > info - > type = = pvr2_ctl_enum ) ) {
2006-12-28 05:28:54 +03:00
ret = pvr2_ctrl_range_check ( cptr , val ) ;
if ( ret < 0 ) break ;
2006-06-26 03:04:40 +04:00
} else if ( cptr - > info - > type ! = pvr2_ctl_bool ) {
break ;
2006-06-27 03:58:46 +04:00
}
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 {
2006-08-08 16:10:07 +04:00
if ( cptr - > info - > get_max_value ) {
cptr - > info - > get_max_value ( cptr , & ret ) ;
} else if ( cptr - > info - > type = = pvr2_ctl_int ) {
2006-06-27 03:58:46 +04:00
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 {
2006-08-08 16:10:07 +04:00
if ( cptr - > info - > get_min_value ) {
cptr - > info - > get_min_value ( cptr , & ret ) ;
} else if ( cptr - > info - > type = = pvr2_ctl_int ) {
2006-06-27 03:58:46 +04:00
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) */
2008-09-01 03:55:03 +04:00
int pvr2_ctrl_get_def ( struct pvr2_ctrl * cptr , int * valptr )
2006-06-27 03:58:46 +04:00
{
int ret = 0 ;
if ( ! cptr ) return 0 ;
LOCK_TAKE ( cptr - > hdw - > big_lock ) ; do {
if ( cptr - > info - > type = = pvr2_ctl_int ) {
2008-09-01 03:55:03 +04:00
if ( cptr - > info - > get_def_value ) {
ret = cptr - > info - > get_def_value ( cptr , valptr ) ;
} else {
* valptr = cptr - > info - > default_value ;
}
2006-06-27 03:58:46 +04:00
}
} 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 )
{
2006-06-30 18:35:28 +04:00
if ( ! cptr ) return NULL ;
2006-06-27 03:58:46 +04:00
return cptr - > info - > name ;
}
/* Retrieve the control's desc */
const char * pvr2_ctrl_get_desc ( struct pvr2_ctrl * cptr )
{
2006-06-30 18:35:28 +04:00
if ( ! cptr ) return NULL ;
2006-06-27 03:58:46 +04:00
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 ;
2008-04-22 21:45:38 +04:00
if ( pvr2_ctrl_range_check ( cptr , val ) = = 0 ) {
2006-06-27 03:58:46 +04:00
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 ;
}
2006-06-26 03:04:44 +04:00
/* 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 ) ;
}
2006-06-26 03:04:58 +04:00
if ( cptr - > info - > set_value ) {
flags & = ~ V4L2_CTRL_FLAG_READ_ONLY ;
} else {
flags | = V4L2_CTRL_FLAG_READ_ONLY ;
}
2006-06-26 03:04:44 +04:00
return flags ;
}
2006-06-27 03:58:46 +04:00
/* Return true if control is writable */
int pvr2_ctrl_is_writable ( struct pvr2_ctrl * cptr )
{
if ( ! cptr ) return 0 ;
2008-03-29 06:07:38 +03:00
return cptr - > info - > set_value ! = NULL ;
2006-06-27 03:58:46 +04:00
}
/* 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 ;
}
2006-06-26 03:04:40 +04:00
static const char * boolNames [ ] = {
" false " ,
" true " ,
" no " ,
" yes " ,
} ;
2006-06-27 03:58:46 +04:00
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 ;
2006-06-26 03:04:40 +04:00
return 1 ;
2006-06-27 03:58:46 +04:00
}
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 ) {
2007-10-29 04:15:33 +03:00
if ( ! ( msk & valid_bits ) ) continue ;
2006-06-27 03:58:46 +04:00
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 ) {
2006-06-30 18:35:28 +04:00
ret = parse_token ( ptr , len , valptr , NULL , 0 ) ;
2006-12-28 05:06:54 +03:00
if ( ret > = 0 ) {
2006-12-28 05:28:54 +03:00
ret = pvr2_ctrl_range_check ( cptr , * valptr ) ;
2006-06-27 03:58:46 +04:00
}
if ( maskptr ) * maskptr = ~ 0 ;
2006-06-26 03:04:40 +04:00
} else if ( cptr - > info - > type = = pvr2_ctl_bool ) {
2007-01-20 06:39:17 +03:00
ret = parse_token ( ptr , len , valptr , boolNames ,
ARRAY_SIZE ( boolNames ) ) ;
2006-06-26 03:04:40 +04:00
if ( ret = = 1 ) {
* valptr = * valptr ? ! 0 : 0 ;
} else if ( ret = = 0 ) {
* valptr = ( * valptr & 1 ) ? ! 0 : 0 ;
}
if ( maskptr ) * maskptr = 1 ;
2006-06-27 03:58:46 +04:00
} 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 ) ;
2008-04-22 21:45:38 +04:00
if ( ret > = 0 ) {
ret = pvr2_ctrl_range_check ( cptr , * valptr ) ;
2006-06-27 03:58:46 +04:00
}
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 ;
2006-06-26 03:04:40 +04:00
} else if ( cptr - > info - > type = = pvr2_ctl_bool ) {
* len = scnprintf ( buf , maxlen , " %s " , val ? " true " : " false " ) ;
ret = 0 ;
2006-06-27 03:58:46 +04:00
} 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 : * * *
*/