162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "pvrusb2-ctrl.h"
862306a36Sopenharmony_ci#include "pvrusb2-hdw-internal.h"
962306a36Sopenharmony_ci#include <linux/errno.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	if (cptr->info->check_value) {
1762306a36Sopenharmony_ci		if (!cptr->info->check_value(cptr,val)) return -ERANGE;
1862306a36Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_enum) {
1962306a36Sopenharmony_ci		if (val < 0) return -ERANGE;
2062306a36Sopenharmony_ci		if (val >= cptr->info->def.type_enum.count) return -ERANGE;
2162306a36Sopenharmony_ci	} else {
2262306a36Sopenharmony_ci		int lim;
2362306a36Sopenharmony_ci		lim = cptr->info->def.type_int.min_value;
2462306a36Sopenharmony_ci		if (cptr->info->get_min_value) {
2562306a36Sopenharmony_ci			cptr->info->get_min_value(cptr,&lim);
2662306a36Sopenharmony_ci		}
2762306a36Sopenharmony_ci		if (val < lim) return -ERANGE;
2862306a36Sopenharmony_ci		lim = cptr->info->def.type_int.max_value;
2962306a36Sopenharmony_ci		if (cptr->info->get_max_value) {
3062306a36Sopenharmony_ci			cptr->info->get_max_value(cptr,&lim);
3162306a36Sopenharmony_ci		}
3262306a36Sopenharmony_ci		if (val > lim) return -ERANGE;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Set the given control. */
3962306a36Sopenharmony_ciint pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	return pvr2_ctrl_set_mask_value(cptr,~0,val);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Set/clear specific bits of the given control. */
4662306a36Sopenharmony_ciint pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int ret = 0;
4962306a36Sopenharmony_ci	if (!cptr) return -EINVAL;
5062306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
5162306a36Sopenharmony_ci		if (cptr->info->set_value) {
5262306a36Sopenharmony_ci			if (cptr->info->type == pvr2_ctl_bitmask) {
5362306a36Sopenharmony_ci				mask &= cptr->info->def.type_bitmask.valid_bits;
5462306a36Sopenharmony_ci			} else if ((cptr->info->type == pvr2_ctl_int)||
5562306a36Sopenharmony_ci				   (cptr->info->type == pvr2_ctl_enum)) {
5662306a36Sopenharmony_ci				ret = pvr2_ctrl_range_check(cptr,val);
5762306a36Sopenharmony_ci				if (ret < 0) break;
5862306a36Sopenharmony_ci			} else if (cptr->info->type != pvr2_ctl_bool) {
5962306a36Sopenharmony_ci				break;
6062306a36Sopenharmony_ci			}
6162306a36Sopenharmony_ci			ret = cptr->info->set_value(cptr,mask,val);
6262306a36Sopenharmony_ci		} else {
6362306a36Sopenharmony_ci			ret = -EPERM;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Get the current value of the given control. */
7162306a36Sopenharmony_ciint pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int ret = 0;
7462306a36Sopenharmony_ci	if (!cptr) return -EINVAL;
7562306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
7662306a36Sopenharmony_ci		ret = cptr->info->get_value(cptr,valptr);
7762306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
7862306a36Sopenharmony_ci	return ret;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* Retrieve control's type */
8362306a36Sopenharmony_cienum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	if (!cptr) return pvr2_ctl_int;
8662306a36Sopenharmony_ci	return cptr->info->type;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/* Retrieve control's maximum value (int type) */
9162306a36Sopenharmony_ciint pvr2_ctrl_get_max(struct pvr2_ctrl *cptr)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int ret = 0;
9462306a36Sopenharmony_ci	if (!cptr) return 0;
9562306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
9662306a36Sopenharmony_ci		if (cptr->info->get_max_value) {
9762306a36Sopenharmony_ci			cptr->info->get_max_value(cptr,&ret);
9862306a36Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_int) {
9962306a36Sopenharmony_ci			ret = cptr->info->def.type_int.max_value;
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
10262306a36Sopenharmony_ci	return ret;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* Retrieve control's minimum value (int type) */
10762306a36Sopenharmony_ciint pvr2_ctrl_get_min(struct pvr2_ctrl *cptr)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int ret = 0;
11062306a36Sopenharmony_ci	if (!cptr) return 0;
11162306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
11262306a36Sopenharmony_ci		if (cptr->info->get_min_value) {
11362306a36Sopenharmony_ci			cptr->info->get_min_value(cptr,&ret);
11462306a36Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_int) {
11562306a36Sopenharmony_ci			ret = cptr->info->def.type_int.min_value;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
11862306a36Sopenharmony_ci	return ret;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* Retrieve control's default value (any type) */
12362306a36Sopenharmony_ciint pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int ret = 0;
12662306a36Sopenharmony_ci	if (!cptr) return -EINVAL;
12762306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
12862306a36Sopenharmony_ci		if (cptr->info->get_def_value) {
12962306a36Sopenharmony_ci			ret = cptr->info->get_def_value(cptr, valptr);
13062306a36Sopenharmony_ci		} else {
13162306a36Sopenharmony_ci			*valptr = cptr->info->default_value;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
13462306a36Sopenharmony_ci	return ret;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* Retrieve control's enumeration count (enum only) */
13962306a36Sopenharmony_ciint pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int ret = 0;
14262306a36Sopenharmony_ci	if (!cptr) return 0;
14362306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
14462306a36Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_enum) {
14562306a36Sopenharmony_ci			ret = cptr->info->def.type_enum.count;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
14862306a36Sopenharmony_ci	return ret;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/* Retrieve control's valid mask bits (bit mask only) */
15362306a36Sopenharmony_ciint pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	int ret = 0;
15662306a36Sopenharmony_ci	if (!cptr) return 0;
15762306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
15862306a36Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_bitmask) {
15962306a36Sopenharmony_ci			ret = cptr->info->def.type_bitmask.valid_bits;
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
16262306a36Sopenharmony_ci	return ret;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* Retrieve the control's name */
16762306a36Sopenharmony_ciconst char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	if (!cptr) return NULL;
17062306a36Sopenharmony_ci	return cptr->info->name;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* Retrieve the control's desc */
17562306a36Sopenharmony_ciconst char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	if (!cptr) return NULL;
17862306a36Sopenharmony_ci	return cptr->info->desc;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Retrieve a control enumeration or bit mask value */
18362306a36Sopenharmony_ciint pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
18462306a36Sopenharmony_ci			  char *bptr,unsigned int bmax,
18562306a36Sopenharmony_ci			  unsigned int *blen)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	int ret = -EINVAL;
18862306a36Sopenharmony_ci	if (!cptr) return 0;
18962306a36Sopenharmony_ci	*blen = 0;
19062306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
19162306a36Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_enum) {
19262306a36Sopenharmony_ci			const char * const *names;
19362306a36Sopenharmony_ci			names = cptr->info->def.type_enum.value_names;
19462306a36Sopenharmony_ci			if (pvr2_ctrl_range_check(cptr,val) == 0) {
19562306a36Sopenharmony_ci				if (names[val]) {
19662306a36Sopenharmony_ci					*blen = scnprintf(
19762306a36Sopenharmony_ci						bptr,bmax,"%s",
19862306a36Sopenharmony_ci						names[val]);
19962306a36Sopenharmony_ci				} else {
20062306a36Sopenharmony_ci					*blen = 0;
20162306a36Sopenharmony_ci				}
20262306a36Sopenharmony_ci				ret = 0;
20362306a36Sopenharmony_ci			}
20462306a36Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_bitmask) {
20562306a36Sopenharmony_ci			const char **names;
20662306a36Sopenharmony_ci			unsigned int idx;
20762306a36Sopenharmony_ci			int msk;
20862306a36Sopenharmony_ci			names = cptr->info->def.type_bitmask.bit_names;
20962306a36Sopenharmony_ci			val &= cptr->info->def.type_bitmask.valid_bits;
21062306a36Sopenharmony_ci			for (idx = 0, msk = 1; val; idx++, msk <<= 1) {
21162306a36Sopenharmony_ci				if (val & msk) {
21262306a36Sopenharmony_ci					*blen = scnprintf(bptr,bmax,"%s",
21362306a36Sopenharmony_ci							  names[idx]);
21462306a36Sopenharmony_ci					ret = 0;
21562306a36Sopenharmony_ci					break;
21662306a36Sopenharmony_ci				}
21762306a36Sopenharmony_ci			}
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
22062306a36Sopenharmony_ci	return ret;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/* Return V4L ID for this control or zero if none */
22562306a36Sopenharmony_ciint pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	if (!cptr) return 0;
22862306a36Sopenharmony_ci	return cptr->info->v4l_id;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciunsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	unsigned int flags = 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (cptr->info->get_v4lflags) {
23762306a36Sopenharmony_ci		flags = cptr->info->get_v4lflags(cptr);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (cptr->info->set_value) {
24162306a36Sopenharmony_ci		flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
24262306a36Sopenharmony_ci	} else {
24362306a36Sopenharmony_ci		flags |= V4L2_CTRL_FLAG_READ_ONLY;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return flags;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* Return true if control is writable */
25162306a36Sopenharmony_ciint pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	if (!cptr) return 0;
25462306a36Sopenharmony_ci	return cptr->info->set_value != NULL;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci/* Return true if control has custom symbolic representation */
25962306a36Sopenharmony_ciint pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	if (!cptr) return 0;
26262306a36Sopenharmony_ci	if (!cptr->info->val_to_sym) return 0;
26362306a36Sopenharmony_ci	if (!cptr->info->sym_to_val) return 0;
26462306a36Sopenharmony_ci	return !0;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/* Convert a given mask/val to a custom symbolic value */
26962306a36Sopenharmony_ciint pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr,
27062306a36Sopenharmony_ci				  int mask,int val,
27162306a36Sopenharmony_ci				  char *buf,unsigned int maxlen,
27262306a36Sopenharmony_ci				  unsigned int *len)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	if (!cptr) return -EINVAL;
27562306a36Sopenharmony_ci	if (!cptr->info->val_to_sym) return -EINVAL;
27662306a36Sopenharmony_ci	return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* Convert a symbolic value to a mask/value pair */
28162306a36Sopenharmony_ciint pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr,
28262306a36Sopenharmony_ci				  const char *buf,unsigned int len,
28362306a36Sopenharmony_ci				  int *maskptr,int *valptr)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	if (!cptr) return -EINVAL;
28662306a36Sopenharmony_ci	if (!cptr->info->sym_to_val) return -EINVAL;
28762306a36Sopenharmony_ci	return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr);
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic unsigned int gen_bitmask_string(int msk,int val,int msk_only,
29262306a36Sopenharmony_ci				       const char **names,
29362306a36Sopenharmony_ci				       char *ptr,unsigned int len)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	unsigned int idx;
29662306a36Sopenharmony_ci	long sm,um;
29762306a36Sopenharmony_ci	int spcFl;
29862306a36Sopenharmony_ci	unsigned int uc,cnt;
29962306a36Sopenharmony_ci	const char *idStr;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	spcFl = 0;
30262306a36Sopenharmony_ci	uc = 0;
30362306a36Sopenharmony_ci	um = 0;
30462306a36Sopenharmony_ci	for (idx = 0, sm = 1; msk; idx++, sm <<= 1) {
30562306a36Sopenharmony_ci		if (sm & msk) {
30662306a36Sopenharmony_ci			msk &= ~sm;
30762306a36Sopenharmony_ci			idStr = names[idx];
30862306a36Sopenharmony_ci			if (idStr) {
30962306a36Sopenharmony_ci				cnt = scnprintf(ptr,len,"%s%s%s",
31062306a36Sopenharmony_ci						(spcFl ? " " : ""),
31162306a36Sopenharmony_ci						(msk_only ? "" :
31262306a36Sopenharmony_ci						 ((val & sm) ? "+" : "-")),
31362306a36Sopenharmony_ci						idStr);
31462306a36Sopenharmony_ci				ptr += cnt; len -= cnt; uc += cnt;
31562306a36Sopenharmony_ci				spcFl = !0;
31662306a36Sopenharmony_ci			} else {
31762306a36Sopenharmony_ci				um |= sm;
31862306a36Sopenharmony_ci			}
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	if (um) {
32262306a36Sopenharmony_ci		if (msk_only) {
32362306a36Sopenharmony_ci			cnt = scnprintf(ptr,len,"%s0x%lx",
32462306a36Sopenharmony_ci					(spcFl ? " " : ""),
32562306a36Sopenharmony_ci					um);
32662306a36Sopenharmony_ci			ptr += cnt; len -= cnt; uc += cnt;
32762306a36Sopenharmony_ci			spcFl = !0;
32862306a36Sopenharmony_ci		} else if (um & val) {
32962306a36Sopenharmony_ci			cnt = scnprintf(ptr,len,"%s+0x%lx",
33062306a36Sopenharmony_ci					(spcFl ? " " : ""),
33162306a36Sopenharmony_ci					um & val);
33262306a36Sopenharmony_ci			ptr += cnt; len -= cnt; uc += cnt;
33362306a36Sopenharmony_ci			spcFl = !0;
33462306a36Sopenharmony_ci		} else if (um & ~val) {
33562306a36Sopenharmony_ci			cnt = scnprintf(ptr,len,"%s+0x%lx",
33662306a36Sopenharmony_ci					(spcFl ? " " : ""),
33762306a36Sopenharmony_ci					um & ~val);
33862306a36Sopenharmony_ci			ptr += cnt; len -= cnt; uc += cnt;
33962306a36Sopenharmony_ci			spcFl = !0;
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	return uc;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic const char *boolNames[] = {
34762306a36Sopenharmony_ci	"false",
34862306a36Sopenharmony_ci	"true",
34962306a36Sopenharmony_ci	"no",
35062306a36Sopenharmony_ci	"yes",
35162306a36Sopenharmony_ci};
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int parse_token(const char *ptr,unsigned int len,
35562306a36Sopenharmony_ci		       int *valptr,
35662306a36Sopenharmony_ci		       const char * const *names, unsigned int namecnt)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	unsigned int slen;
35962306a36Sopenharmony_ci	unsigned int idx;
36062306a36Sopenharmony_ci	*valptr = 0;
36162306a36Sopenharmony_ci	if (!names) namecnt = 0;
36262306a36Sopenharmony_ci	for (idx = 0; idx < namecnt; idx++) {
36362306a36Sopenharmony_ci		if (!names[idx]) continue;
36462306a36Sopenharmony_ci		slen = strlen(names[idx]);
36562306a36Sopenharmony_ci		if (slen != len) continue;
36662306a36Sopenharmony_ci		if (memcmp(names[idx],ptr,slen)) continue;
36762306a36Sopenharmony_ci		*valptr = idx;
36862306a36Sopenharmony_ci		return 0;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci	return kstrtoint(ptr, 0, valptr) ? -EINVAL : 1;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic int parse_mtoken(const char *ptr,unsigned int len,
37562306a36Sopenharmony_ci			int *valptr,
37662306a36Sopenharmony_ci			const char **names,int valid_bits)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	unsigned int slen;
37962306a36Sopenharmony_ci	unsigned int idx;
38062306a36Sopenharmony_ci	int msk;
38162306a36Sopenharmony_ci	*valptr = 0;
38262306a36Sopenharmony_ci	for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) {
38362306a36Sopenharmony_ci		if (!(msk & valid_bits)) continue;
38462306a36Sopenharmony_ci		valid_bits &= ~msk;
38562306a36Sopenharmony_ci		if (!names[idx]) continue;
38662306a36Sopenharmony_ci		slen = strlen(names[idx]);
38762306a36Sopenharmony_ci		if (slen != len) continue;
38862306a36Sopenharmony_ci		if (memcmp(names[idx],ptr,slen)) continue;
38962306a36Sopenharmony_ci		*valptr = msk;
39062306a36Sopenharmony_ci		return 0;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	return kstrtoint(ptr, 0, valptr);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int parse_tlist(const char *ptr,unsigned int len,
39762306a36Sopenharmony_ci		       int *maskptr,int *valptr,
39862306a36Sopenharmony_ci		       const char **names,int valid_bits)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	unsigned int cnt;
40162306a36Sopenharmony_ci	int mask,val,kv,mode,ret;
40262306a36Sopenharmony_ci	mask = 0;
40362306a36Sopenharmony_ci	val = 0;
40462306a36Sopenharmony_ci	ret = 0;
40562306a36Sopenharmony_ci	while (len) {
40662306a36Sopenharmony_ci		cnt = 0;
40762306a36Sopenharmony_ci		while ((cnt < len) &&
40862306a36Sopenharmony_ci		       ((ptr[cnt] <= 32) ||
40962306a36Sopenharmony_ci			(ptr[cnt] >= 127))) cnt++;
41062306a36Sopenharmony_ci		ptr += cnt;
41162306a36Sopenharmony_ci		len -= cnt;
41262306a36Sopenharmony_ci		mode = 0;
41362306a36Sopenharmony_ci		if ((*ptr == '-') || (*ptr == '+')) {
41462306a36Sopenharmony_ci			mode = (*ptr == '-') ? -1 : 1;
41562306a36Sopenharmony_ci			ptr++;
41662306a36Sopenharmony_ci			len--;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci		cnt = 0;
41962306a36Sopenharmony_ci		while (cnt < len) {
42062306a36Sopenharmony_ci			if (ptr[cnt] <= 32) break;
42162306a36Sopenharmony_ci			if (ptr[cnt] >= 127) break;
42262306a36Sopenharmony_ci			cnt++;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci		if (!cnt) break;
42562306a36Sopenharmony_ci		if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) {
42662306a36Sopenharmony_ci			ret = -EINVAL;
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci		ptr += cnt;
43062306a36Sopenharmony_ci		len -= cnt;
43162306a36Sopenharmony_ci		switch (mode) {
43262306a36Sopenharmony_ci		case 0:
43362306a36Sopenharmony_ci			mask = valid_bits;
43462306a36Sopenharmony_ci			val |= kv;
43562306a36Sopenharmony_ci			break;
43662306a36Sopenharmony_ci		case -1:
43762306a36Sopenharmony_ci			mask |= kv;
43862306a36Sopenharmony_ci			val &= ~kv;
43962306a36Sopenharmony_ci			break;
44062306a36Sopenharmony_ci		case 1:
44162306a36Sopenharmony_ci			mask |= kv;
44262306a36Sopenharmony_ci			val |= kv;
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		default:
44562306a36Sopenharmony_ci			break;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	*maskptr = mask;
44962306a36Sopenharmony_ci	*valptr = val;
45062306a36Sopenharmony_ci	return ret;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/* Convert a symbolic value to a mask/value pair */
45562306a36Sopenharmony_ciint pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
45662306a36Sopenharmony_ci			   const char *ptr,unsigned int len,
45762306a36Sopenharmony_ci			   int *maskptr,int *valptr)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	int ret = -EINVAL;
46062306a36Sopenharmony_ci	unsigned int cnt;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	*maskptr = 0;
46362306a36Sopenharmony_ci	*valptr = 0;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	cnt = 0;
46662306a36Sopenharmony_ci	while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++;
46762306a36Sopenharmony_ci	len -= cnt; ptr += cnt;
46862306a36Sopenharmony_ci	cnt = 0;
46962306a36Sopenharmony_ci	while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) ||
47062306a36Sopenharmony_ci			       (ptr[len-(cnt+1)] >= 127))) cnt++;
47162306a36Sopenharmony_ci	len -= cnt;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (!len) return -EINVAL;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
47662306a36Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_int) {
47762306a36Sopenharmony_ci			ret = parse_token(ptr,len,valptr,NULL,0);
47862306a36Sopenharmony_ci			if (ret >= 0) {
47962306a36Sopenharmony_ci				ret = pvr2_ctrl_range_check(cptr,*valptr);
48062306a36Sopenharmony_ci			}
48162306a36Sopenharmony_ci			*maskptr = ~0;
48262306a36Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_bool) {
48362306a36Sopenharmony_ci			ret = parse_token(ptr,len,valptr,boolNames,
48462306a36Sopenharmony_ci					  ARRAY_SIZE(boolNames));
48562306a36Sopenharmony_ci			if (ret == 1) {
48662306a36Sopenharmony_ci				*valptr = *valptr ? !0 : 0;
48762306a36Sopenharmony_ci			} else if (ret == 0) {
48862306a36Sopenharmony_ci				*valptr = (*valptr & 1) ? !0 : 0;
48962306a36Sopenharmony_ci			}
49062306a36Sopenharmony_ci			*maskptr = 1;
49162306a36Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_enum) {
49262306a36Sopenharmony_ci			ret = parse_token(
49362306a36Sopenharmony_ci				ptr,len,valptr,
49462306a36Sopenharmony_ci				cptr->info->def.type_enum.value_names,
49562306a36Sopenharmony_ci				cptr->info->def.type_enum.count);
49662306a36Sopenharmony_ci			if (ret >= 0) {
49762306a36Sopenharmony_ci				ret = pvr2_ctrl_range_check(cptr,*valptr);
49862306a36Sopenharmony_ci			}
49962306a36Sopenharmony_ci			*maskptr = ~0;
50062306a36Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_bitmask) {
50162306a36Sopenharmony_ci			ret = parse_tlist(
50262306a36Sopenharmony_ci				ptr,len,maskptr,valptr,
50362306a36Sopenharmony_ci				cptr->info->def.type_bitmask.bit_names,
50462306a36Sopenharmony_ci				cptr->info->def.type_bitmask.valid_bits);
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
50762306a36Sopenharmony_ci	return ret;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/* Convert a given mask/val to a symbolic value */
51262306a36Sopenharmony_ciint pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr,
51362306a36Sopenharmony_ci				    int mask,int val,
51462306a36Sopenharmony_ci				    char *buf,unsigned int maxlen,
51562306a36Sopenharmony_ci				    unsigned int *len)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	int ret = -EINVAL;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	*len = 0;
52062306a36Sopenharmony_ci	if (cptr->info->type == pvr2_ctl_int) {
52162306a36Sopenharmony_ci		*len = scnprintf(buf,maxlen,"%d",val);
52262306a36Sopenharmony_ci		ret = 0;
52362306a36Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_bool) {
52462306a36Sopenharmony_ci		*len = scnprintf(buf,maxlen,"%s",val ? "true" : "false");
52562306a36Sopenharmony_ci		ret = 0;
52662306a36Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_enum) {
52762306a36Sopenharmony_ci		const char * const *names;
52862306a36Sopenharmony_ci		names = cptr->info->def.type_enum.value_names;
52962306a36Sopenharmony_ci		if ((val >= 0) &&
53062306a36Sopenharmony_ci		    (val < cptr->info->def.type_enum.count)) {
53162306a36Sopenharmony_ci			if (names[val]) {
53262306a36Sopenharmony_ci				*len = scnprintf(
53362306a36Sopenharmony_ci					buf,maxlen,"%s",
53462306a36Sopenharmony_ci					names[val]);
53562306a36Sopenharmony_ci			} else {
53662306a36Sopenharmony_ci				*len = 0;
53762306a36Sopenharmony_ci			}
53862306a36Sopenharmony_ci			ret = 0;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_bitmask) {
54162306a36Sopenharmony_ci		*len = gen_bitmask_string(
54262306a36Sopenharmony_ci			val & mask & cptr->info->def.type_bitmask.valid_bits,
54362306a36Sopenharmony_ci			~0,!0,
54462306a36Sopenharmony_ci			cptr->info->def.type_bitmask.bit_names,
54562306a36Sopenharmony_ci			buf,maxlen);
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci	return ret;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci/* Convert a given mask/val to a symbolic value */
55262306a36Sopenharmony_ciint pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr,
55362306a36Sopenharmony_ci			   int mask,int val,
55462306a36Sopenharmony_ci			   char *buf,unsigned int maxlen,
55562306a36Sopenharmony_ci			   unsigned int *len)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	int ret;
55862306a36Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
55962306a36Sopenharmony_ci		ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val,
56062306a36Sopenharmony_ci						      buf,maxlen,len);
56162306a36Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
56262306a36Sopenharmony_ci	return ret;
56362306a36Sopenharmony_ci}
564