/* * * * Copyright (C) 2005 Mike Isely * * 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 "compat.h" #include #include #include 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 **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 **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); } if (maskptr) *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; } 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) { ret = pvr2_ctrl_range_check(cptr,*valptr); } 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: *** */