18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "pvrusb2-ctrl.h"
88c2ecf20Sopenharmony_ci#include "pvrusb2-hdw-internal.h"
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/mutex.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	if (cptr->info->check_value) {
178c2ecf20Sopenharmony_ci		if (!cptr->info->check_value(cptr,val)) return -ERANGE;
188c2ecf20Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_enum) {
198c2ecf20Sopenharmony_ci		if (val < 0) return -ERANGE;
208c2ecf20Sopenharmony_ci		if (val >= cptr->info->def.type_enum.count) return -ERANGE;
218c2ecf20Sopenharmony_ci	} else {
228c2ecf20Sopenharmony_ci		int lim;
238c2ecf20Sopenharmony_ci		lim = cptr->info->def.type_int.min_value;
248c2ecf20Sopenharmony_ci		if (cptr->info->get_min_value) {
258c2ecf20Sopenharmony_ci			cptr->info->get_min_value(cptr,&lim);
268c2ecf20Sopenharmony_ci		}
278c2ecf20Sopenharmony_ci		if (val < lim) return -ERANGE;
288c2ecf20Sopenharmony_ci		lim = cptr->info->def.type_int.max_value;
298c2ecf20Sopenharmony_ci		if (cptr->info->get_max_value) {
308c2ecf20Sopenharmony_ci			cptr->info->get_max_value(cptr,&lim);
318c2ecf20Sopenharmony_ci		}
328c2ecf20Sopenharmony_ci		if (val > lim) return -ERANGE;
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci	return 0;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Set the given control. */
398c2ecf20Sopenharmony_ciint pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	return pvr2_ctrl_set_mask_value(cptr,~0,val);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* Set/clear specific bits of the given control. */
468c2ecf20Sopenharmony_ciint pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	int ret = 0;
498c2ecf20Sopenharmony_ci	if (!cptr) return -EINVAL;
508c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
518c2ecf20Sopenharmony_ci		if (cptr->info->set_value) {
528c2ecf20Sopenharmony_ci			if (cptr->info->type == pvr2_ctl_bitmask) {
538c2ecf20Sopenharmony_ci				mask &= cptr->info->def.type_bitmask.valid_bits;
548c2ecf20Sopenharmony_ci			} else if ((cptr->info->type == pvr2_ctl_int)||
558c2ecf20Sopenharmony_ci				   (cptr->info->type == pvr2_ctl_enum)) {
568c2ecf20Sopenharmony_ci				ret = pvr2_ctrl_range_check(cptr,val);
578c2ecf20Sopenharmony_ci				if (ret < 0) break;
588c2ecf20Sopenharmony_ci			} else if (cptr->info->type != pvr2_ctl_bool) {
598c2ecf20Sopenharmony_ci				break;
608c2ecf20Sopenharmony_ci			}
618c2ecf20Sopenharmony_ci			ret = cptr->info->set_value(cptr,mask,val);
628c2ecf20Sopenharmony_ci		} else {
638c2ecf20Sopenharmony_ci			ret = -EPERM;
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
668c2ecf20Sopenharmony_ci	return ret;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Get the current value of the given control. */
718c2ecf20Sopenharmony_ciint pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int ret = 0;
748c2ecf20Sopenharmony_ci	if (!cptr) return -EINVAL;
758c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
768c2ecf20Sopenharmony_ci		ret = cptr->info->get_value(cptr,valptr);
778c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
788c2ecf20Sopenharmony_ci	return ret;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* Retrieve control's type */
838c2ecf20Sopenharmony_cienum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	if (!cptr) return pvr2_ctl_int;
868c2ecf20Sopenharmony_ci	return cptr->info->type;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* Retrieve control's maximum value (int type) */
918c2ecf20Sopenharmony_ciint pvr2_ctrl_get_max(struct pvr2_ctrl *cptr)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int ret = 0;
948c2ecf20Sopenharmony_ci	if (!cptr) return 0;
958c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
968c2ecf20Sopenharmony_ci		if (cptr->info->get_max_value) {
978c2ecf20Sopenharmony_ci			cptr->info->get_max_value(cptr,&ret);
988c2ecf20Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_int) {
998c2ecf20Sopenharmony_ci			ret = cptr->info->def.type_int.max_value;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
1028c2ecf20Sopenharmony_ci	return ret;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Retrieve control's minimum value (int type) */
1078c2ecf20Sopenharmony_ciint pvr2_ctrl_get_min(struct pvr2_ctrl *cptr)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	int ret = 0;
1108c2ecf20Sopenharmony_ci	if (!cptr) return 0;
1118c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
1128c2ecf20Sopenharmony_ci		if (cptr->info->get_min_value) {
1138c2ecf20Sopenharmony_ci			cptr->info->get_min_value(cptr,&ret);
1148c2ecf20Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_int) {
1158c2ecf20Sopenharmony_ci			ret = cptr->info->def.type_int.min_value;
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
1188c2ecf20Sopenharmony_ci	return ret;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* Retrieve control's default value (any type) */
1238c2ecf20Sopenharmony_ciint pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int ret = 0;
1268c2ecf20Sopenharmony_ci	if (!cptr) return -EINVAL;
1278c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
1288c2ecf20Sopenharmony_ci		if (cptr->info->get_def_value) {
1298c2ecf20Sopenharmony_ci			ret = cptr->info->get_def_value(cptr, valptr);
1308c2ecf20Sopenharmony_ci		} else {
1318c2ecf20Sopenharmony_ci			*valptr = cptr->info->default_value;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
1348c2ecf20Sopenharmony_ci	return ret;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* Retrieve control's enumeration count (enum only) */
1398c2ecf20Sopenharmony_ciint pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int ret = 0;
1428c2ecf20Sopenharmony_ci	if (!cptr) return 0;
1438c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
1448c2ecf20Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_enum) {
1458c2ecf20Sopenharmony_ci			ret = cptr->info->def.type_enum.count;
1468c2ecf20Sopenharmony_ci		}
1478c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
1488c2ecf20Sopenharmony_ci	return ret;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* Retrieve control's valid mask bits (bit mask only) */
1538c2ecf20Sopenharmony_ciint pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int ret = 0;
1568c2ecf20Sopenharmony_ci	if (!cptr) return 0;
1578c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
1588c2ecf20Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_bitmask) {
1598c2ecf20Sopenharmony_ci			ret = cptr->info->def.type_bitmask.valid_bits;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
1628c2ecf20Sopenharmony_ci	return ret;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/* Retrieve the control's name */
1678c2ecf20Sopenharmony_ciconst char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	if (!cptr) return NULL;
1708c2ecf20Sopenharmony_ci	return cptr->info->name;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* Retrieve the control's desc */
1758c2ecf20Sopenharmony_ciconst char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	if (!cptr) return NULL;
1788c2ecf20Sopenharmony_ci	return cptr->info->desc;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* Retrieve a control enumeration or bit mask value */
1838c2ecf20Sopenharmony_ciint pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
1848c2ecf20Sopenharmony_ci			  char *bptr,unsigned int bmax,
1858c2ecf20Sopenharmony_ci			  unsigned int *blen)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int ret = -EINVAL;
1888c2ecf20Sopenharmony_ci	if (!cptr) return 0;
1898c2ecf20Sopenharmony_ci	*blen = 0;
1908c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
1918c2ecf20Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_enum) {
1928c2ecf20Sopenharmony_ci			const char * const *names;
1938c2ecf20Sopenharmony_ci			names = cptr->info->def.type_enum.value_names;
1948c2ecf20Sopenharmony_ci			if (pvr2_ctrl_range_check(cptr,val) == 0) {
1958c2ecf20Sopenharmony_ci				if (names[val]) {
1968c2ecf20Sopenharmony_ci					*blen = scnprintf(
1978c2ecf20Sopenharmony_ci						bptr,bmax,"%s",
1988c2ecf20Sopenharmony_ci						names[val]);
1998c2ecf20Sopenharmony_ci				} else {
2008c2ecf20Sopenharmony_ci					*blen = 0;
2018c2ecf20Sopenharmony_ci				}
2028c2ecf20Sopenharmony_ci				ret = 0;
2038c2ecf20Sopenharmony_ci			}
2048c2ecf20Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_bitmask) {
2058c2ecf20Sopenharmony_ci			const char **names;
2068c2ecf20Sopenharmony_ci			unsigned int idx;
2078c2ecf20Sopenharmony_ci			int msk;
2088c2ecf20Sopenharmony_ci			names = cptr->info->def.type_bitmask.bit_names;
2098c2ecf20Sopenharmony_ci			val &= cptr->info->def.type_bitmask.valid_bits;
2108c2ecf20Sopenharmony_ci			for (idx = 0, msk = 1; val; idx++, msk <<= 1) {
2118c2ecf20Sopenharmony_ci				if (val & msk) {
2128c2ecf20Sopenharmony_ci					*blen = scnprintf(bptr,bmax,"%s",
2138c2ecf20Sopenharmony_ci							  names[idx]);
2148c2ecf20Sopenharmony_ci					ret = 0;
2158c2ecf20Sopenharmony_ci					break;
2168c2ecf20Sopenharmony_ci				}
2178c2ecf20Sopenharmony_ci			}
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
2208c2ecf20Sopenharmony_ci	return ret;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* Return V4L ID for this control or zero if none */
2258c2ecf20Sopenharmony_ciint pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (!cptr) return 0;
2288c2ecf20Sopenharmony_ci	return cptr->info->v4l_id;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ciunsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	unsigned int flags = 0;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (cptr->info->get_v4lflags) {
2378c2ecf20Sopenharmony_ci		flags = cptr->info->get_v4lflags(cptr);
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (cptr->info->set_value) {
2418c2ecf20Sopenharmony_ci		flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
2428c2ecf20Sopenharmony_ci	} else {
2438c2ecf20Sopenharmony_ci		flags |= V4L2_CTRL_FLAG_READ_ONLY;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return flags;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/* Return true if control is writable */
2518c2ecf20Sopenharmony_ciint pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	if (!cptr) return 0;
2548c2ecf20Sopenharmony_ci	return cptr->info->set_value != NULL;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/* Return true if control has custom symbolic representation */
2598c2ecf20Sopenharmony_ciint pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	if (!cptr) return 0;
2628c2ecf20Sopenharmony_ci	if (!cptr->info->val_to_sym) return 0;
2638c2ecf20Sopenharmony_ci	if (!cptr->info->sym_to_val) return 0;
2648c2ecf20Sopenharmony_ci	return !0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/* Convert a given mask/val to a custom symbolic value */
2698c2ecf20Sopenharmony_ciint pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr,
2708c2ecf20Sopenharmony_ci				  int mask,int val,
2718c2ecf20Sopenharmony_ci				  char *buf,unsigned int maxlen,
2728c2ecf20Sopenharmony_ci				  unsigned int *len)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	if (!cptr) return -EINVAL;
2758c2ecf20Sopenharmony_ci	if (!cptr->info->val_to_sym) return -EINVAL;
2768c2ecf20Sopenharmony_ci	return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/* Convert a symbolic value to a mask/value pair */
2818c2ecf20Sopenharmony_ciint pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr,
2828c2ecf20Sopenharmony_ci				  const char *buf,unsigned int len,
2838c2ecf20Sopenharmony_ci				  int *maskptr,int *valptr)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	if (!cptr) return -EINVAL;
2868c2ecf20Sopenharmony_ci	if (!cptr->info->sym_to_val) return -EINVAL;
2878c2ecf20Sopenharmony_ci	return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic unsigned int gen_bitmask_string(int msk,int val,int msk_only,
2928c2ecf20Sopenharmony_ci				       const char **names,
2938c2ecf20Sopenharmony_ci				       char *ptr,unsigned int len)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	unsigned int idx;
2968c2ecf20Sopenharmony_ci	long sm,um;
2978c2ecf20Sopenharmony_ci	int spcFl;
2988c2ecf20Sopenharmony_ci	unsigned int uc,cnt;
2998c2ecf20Sopenharmony_ci	const char *idStr;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	spcFl = 0;
3028c2ecf20Sopenharmony_ci	uc = 0;
3038c2ecf20Sopenharmony_ci	um = 0;
3048c2ecf20Sopenharmony_ci	for (idx = 0, sm = 1; msk; idx++, sm <<= 1) {
3058c2ecf20Sopenharmony_ci		if (sm & msk) {
3068c2ecf20Sopenharmony_ci			msk &= ~sm;
3078c2ecf20Sopenharmony_ci			idStr = names[idx];
3088c2ecf20Sopenharmony_ci			if (idStr) {
3098c2ecf20Sopenharmony_ci				cnt = scnprintf(ptr,len,"%s%s%s",
3108c2ecf20Sopenharmony_ci						(spcFl ? " " : ""),
3118c2ecf20Sopenharmony_ci						(msk_only ? "" :
3128c2ecf20Sopenharmony_ci						 ((val & sm) ? "+" : "-")),
3138c2ecf20Sopenharmony_ci						idStr);
3148c2ecf20Sopenharmony_ci				ptr += cnt; len -= cnt; uc += cnt;
3158c2ecf20Sopenharmony_ci				spcFl = !0;
3168c2ecf20Sopenharmony_ci			} else {
3178c2ecf20Sopenharmony_ci				um |= sm;
3188c2ecf20Sopenharmony_ci			}
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci	if (um) {
3228c2ecf20Sopenharmony_ci		if (msk_only) {
3238c2ecf20Sopenharmony_ci			cnt = scnprintf(ptr,len,"%s0x%lx",
3248c2ecf20Sopenharmony_ci					(spcFl ? " " : ""),
3258c2ecf20Sopenharmony_ci					um);
3268c2ecf20Sopenharmony_ci			ptr += cnt; len -= cnt; uc += cnt;
3278c2ecf20Sopenharmony_ci			spcFl = !0;
3288c2ecf20Sopenharmony_ci		} else if (um & val) {
3298c2ecf20Sopenharmony_ci			cnt = scnprintf(ptr,len,"%s+0x%lx",
3308c2ecf20Sopenharmony_ci					(spcFl ? " " : ""),
3318c2ecf20Sopenharmony_ci					um & val);
3328c2ecf20Sopenharmony_ci			ptr += cnt; len -= cnt; uc += cnt;
3338c2ecf20Sopenharmony_ci			spcFl = !0;
3348c2ecf20Sopenharmony_ci		} else if (um & ~val) {
3358c2ecf20Sopenharmony_ci			cnt = scnprintf(ptr,len,"%s+0x%lx",
3368c2ecf20Sopenharmony_ci					(spcFl ? " " : ""),
3378c2ecf20Sopenharmony_ci					um & ~val);
3388c2ecf20Sopenharmony_ci			ptr += cnt; len -= cnt; uc += cnt;
3398c2ecf20Sopenharmony_ci			spcFl = !0;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci	return uc;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic const char *boolNames[] = {
3478c2ecf20Sopenharmony_ci	"false",
3488c2ecf20Sopenharmony_ci	"true",
3498c2ecf20Sopenharmony_ci	"no",
3508c2ecf20Sopenharmony_ci	"yes",
3518c2ecf20Sopenharmony_ci};
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int parse_token(const char *ptr,unsigned int len,
3558c2ecf20Sopenharmony_ci		       int *valptr,
3568c2ecf20Sopenharmony_ci		       const char * const *names, unsigned int namecnt)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	char buf[33];
3598c2ecf20Sopenharmony_ci	unsigned int slen;
3608c2ecf20Sopenharmony_ci	unsigned int idx;
3618c2ecf20Sopenharmony_ci	int negfl;
3628c2ecf20Sopenharmony_ci	char *p2;
3638c2ecf20Sopenharmony_ci	*valptr = 0;
3648c2ecf20Sopenharmony_ci	if (!names) namecnt = 0;
3658c2ecf20Sopenharmony_ci	for (idx = 0; idx < namecnt; idx++) {
3668c2ecf20Sopenharmony_ci		if (!names[idx]) continue;
3678c2ecf20Sopenharmony_ci		slen = strlen(names[idx]);
3688c2ecf20Sopenharmony_ci		if (slen != len) continue;
3698c2ecf20Sopenharmony_ci		if (memcmp(names[idx],ptr,slen)) continue;
3708c2ecf20Sopenharmony_ci		*valptr = idx;
3718c2ecf20Sopenharmony_ci		return 0;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci	negfl = 0;
3748c2ecf20Sopenharmony_ci	if ((*ptr == '-') || (*ptr == '+')) {
3758c2ecf20Sopenharmony_ci		negfl = (*ptr == '-');
3768c2ecf20Sopenharmony_ci		ptr++; len--;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	if (len >= sizeof(buf)) return -EINVAL;
3798c2ecf20Sopenharmony_ci	memcpy(buf,ptr,len);
3808c2ecf20Sopenharmony_ci	buf[len] = 0;
3818c2ecf20Sopenharmony_ci	*valptr = simple_strtol(buf,&p2,0);
3828c2ecf20Sopenharmony_ci	if (negfl) *valptr = -(*valptr);
3838c2ecf20Sopenharmony_ci	if (*p2) return -EINVAL;
3848c2ecf20Sopenharmony_ci	return 1;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int parse_mtoken(const char *ptr,unsigned int len,
3898c2ecf20Sopenharmony_ci			int *valptr,
3908c2ecf20Sopenharmony_ci			const char **names,int valid_bits)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	char buf[33];
3938c2ecf20Sopenharmony_ci	unsigned int slen;
3948c2ecf20Sopenharmony_ci	unsigned int idx;
3958c2ecf20Sopenharmony_ci	char *p2;
3968c2ecf20Sopenharmony_ci	int msk;
3978c2ecf20Sopenharmony_ci	*valptr = 0;
3988c2ecf20Sopenharmony_ci	for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) {
3998c2ecf20Sopenharmony_ci		if (!(msk & valid_bits)) continue;
4008c2ecf20Sopenharmony_ci		valid_bits &= ~msk;
4018c2ecf20Sopenharmony_ci		if (!names[idx]) continue;
4028c2ecf20Sopenharmony_ci		slen = strlen(names[idx]);
4038c2ecf20Sopenharmony_ci		if (slen != len) continue;
4048c2ecf20Sopenharmony_ci		if (memcmp(names[idx],ptr,slen)) continue;
4058c2ecf20Sopenharmony_ci		*valptr = msk;
4068c2ecf20Sopenharmony_ci		return 0;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci	if (len >= sizeof(buf)) return -EINVAL;
4098c2ecf20Sopenharmony_ci	memcpy(buf,ptr,len);
4108c2ecf20Sopenharmony_ci	buf[len] = 0;
4118c2ecf20Sopenharmony_ci	*valptr = simple_strtol(buf,&p2,0);
4128c2ecf20Sopenharmony_ci	if (*p2) return -EINVAL;
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int parse_tlist(const char *ptr,unsigned int len,
4188c2ecf20Sopenharmony_ci		       int *maskptr,int *valptr,
4198c2ecf20Sopenharmony_ci		       const char **names,int valid_bits)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	unsigned int cnt;
4228c2ecf20Sopenharmony_ci	int mask,val,kv,mode,ret;
4238c2ecf20Sopenharmony_ci	mask = 0;
4248c2ecf20Sopenharmony_ci	val = 0;
4258c2ecf20Sopenharmony_ci	ret = 0;
4268c2ecf20Sopenharmony_ci	while (len) {
4278c2ecf20Sopenharmony_ci		cnt = 0;
4288c2ecf20Sopenharmony_ci		while ((cnt < len) &&
4298c2ecf20Sopenharmony_ci		       ((ptr[cnt] <= 32) ||
4308c2ecf20Sopenharmony_ci			(ptr[cnt] >= 127))) cnt++;
4318c2ecf20Sopenharmony_ci		ptr += cnt;
4328c2ecf20Sopenharmony_ci		len -= cnt;
4338c2ecf20Sopenharmony_ci		mode = 0;
4348c2ecf20Sopenharmony_ci		if ((*ptr == '-') || (*ptr == '+')) {
4358c2ecf20Sopenharmony_ci			mode = (*ptr == '-') ? -1 : 1;
4368c2ecf20Sopenharmony_ci			ptr++;
4378c2ecf20Sopenharmony_ci			len--;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci		cnt = 0;
4408c2ecf20Sopenharmony_ci		while (cnt < len) {
4418c2ecf20Sopenharmony_ci			if (ptr[cnt] <= 32) break;
4428c2ecf20Sopenharmony_ci			if (ptr[cnt] >= 127) break;
4438c2ecf20Sopenharmony_ci			cnt++;
4448c2ecf20Sopenharmony_ci		}
4458c2ecf20Sopenharmony_ci		if (!cnt) break;
4468c2ecf20Sopenharmony_ci		if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) {
4478c2ecf20Sopenharmony_ci			ret = -EINVAL;
4488c2ecf20Sopenharmony_ci			break;
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci		ptr += cnt;
4518c2ecf20Sopenharmony_ci		len -= cnt;
4528c2ecf20Sopenharmony_ci		switch (mode) {
4538c2ecf20Sopenharmony_ci		case 0:
4548c2ecf20Sopenharmony_ci			mask = valid_bits;
4558c2ecf20Sopenharmony_ci			val |= kv;
4568c2ecf20Sopenharmony_ci			break;
4578c2ecf20Sopenharmony_ci		case -1:
4588c2ecf20Sopenharmony_ci			mask |= kv;
4598c2ecf20Sopenharmony_ci			val &= ~kv;
4608c2ecf20Sopenharmony_ci			break;
4618c2ecf20Sopenharmony_ci		case 1:
4628c2ecf20Sopenharmony_ci			mask |= kv;
4638c2ecf20Sopenharmony_ci			val |= kv;
4648c2ecf20Sopenharmony_ci			break;
4658c2ecf20Sopenharmony_ci		default:
4668c2ecf20Sopenharmony_ci			break;
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci	*maskptr = mask;
4708c2ecf20Sopenharmony_ci	*valptr = val;
4718c2ecf20Sopenharmony_ci	return ret;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/* Convert a symbolic value to a mask/value pair */
4768c2ecf20Sopenharmony_ciint pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
4778c2ecf20Sopenharmony_ci			   const char *ptr,unsigned int len,
4788c2ecf20Sopenharmony_ci			   int *maskptr,int *valptr)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	int ret = -EINVAL;
4818c2ecf20Sopenharmony_ci	unsigned int cnt;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	*maskptr = 0;
4848c2ecf20Sopenharmony_ci	*valptr = 0;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	cnt = 0;
4878c2ecf20Sopenharmony_ci	while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++;
4888c2ecf20Sopenharmony_ci	len -= cnt; ptr += cnt;
4898c2ecf20Sopenharmony_ci	cnt = 0;
4908c2ecf20Sopenharmony_ci	while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) ||
4918c2ecf20Sopenharmony_ci			       (ptr[len-(cnt+1)] >= 127))) cnt++;
4928c2ecf20Sopenharmony_ci	len -= cnt;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (!len) return -EINVAL;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
4978c2ecf20Sopenharmony_ci		if (cptr->info->type == pvr2_ctl_int) {
4988c2ecf20Sopenharmony_ci			ret = parse_token(ptr,len,valptr,NULL,0);
4998c2ecf20Sopenharmony_ci			if (ret >= 0) {
5008c2ecf20Sopenharmony_ci				ret = pvr2_ctrl_range_check(cptr,*valptr);
5018c2ecf20Sopenharmony_ci			}
5028c2ecf20Sopenharmony_ci			*maskptr = ~0;
5038c2ecf20Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_bool) {
5048c2ecf20Sopenharmony_ci			ret = parse_token(ptr,len,valptr,boolNames,
5058c2ecf20Sopenharmony_ci					  ARRAY_SIZE(boolNames));
5068c2ecf20Sopenharmony_ci			if (ret == 1) {
5078c2ecf20Sopenharmony_ci				*valptr = *valptr ? !0 : 0;
5088c2ecf20Sopenharmony_ci			} else if (ret == 0) {
5098c2ecf20Sopenharmony_ci				*valptr = (*valptr & 1) ? !0 : 0;
5108c2ecf20Sopenharmony_ci			}
5118c2ecf20Sopenharmony_ci			*maskptr = 1;
5128c2ecf20Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_enum) {
5138c2ecf20Sopenharmony_ci			ret = parse_token(
5148c2ecf20Sopenharmony_ci				ptr,len,valptr,
5158c2ecf20Sopenharmony_ci				cptr->info->def.type_enum.value_names,
5168c2ecf20Sopenharmony_ci				cptr->info->def.type_enum.count);
5178c2ecf20Sopenharmony_ci			if (ret >= 0) {
5188c2ecf20Sopenharmony_ci				ret = pvr2_ctrl_range_check(cptr,*valptr);
5198c2ecf20Sopenharmony_ci			}
5208c2ecf20Sopenharmony_ci			*maskptr = ~0;
5218c2ecf20Sopenharmony_ci		} else if (cptr->info->type == pvr2_ctl_bitmask) {
5228c2ecf20Sopenharmony_ci			ret = parse_tlist(
5238c2ecf20Sopenharmony_ci				ptr,len,maskptr,valptr,
5248c2ecf20Sopenharmony_ci				cptr->info->def.type_bitmask.bit_names,
5258c2ecf20Sopenharmony_ci				cptr->info->def.type_bitmask.valid_bits);
5268c2ecf20Sopenharmony_ci		}
5278c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
5288c2ecf20Sopenharmony_ci	return ret;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/* Convert a given mask/val to a symbolic value */
5338c2ecf20Sopenharmony_ciint pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr,
5348c2ecf20Sopenharmony_ci				    int mask,int val,
5358c2ecf20Sopenharmony_ci				    char *buf,unsigned int maxlen,
5368c2ecf20Sopenharmony_ci				    unsigned int *len)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	int ret = -EINVAL;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	*len = 0;
5418c2ecf20Sopenharmony_ci	if (cptr->info->type == pvr2_ctl_int) {
5428c2ecf20Sopenharmony_ci		*len = scnprintf(buf,maxlen,"%d",val);
5438c2ecf20Sopenharmony_ci		ret = 0;
5448c2ecf20Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_bool) {
5458c2ecf20Sopenharmony_ci		*len = scnprintf(buf,maxlen,"%s",val ? "true" : "false");
5468c2ecf20Sopenharmony_ci		ret = 0;
5478c2ecf20Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_enum) {
5488c2ecf20Sopenharmony_ci		const char * const *names;
5498c2ecf20Sopenharmony_ci		names = cptr->info->def.type_enum.value_names;
5508c2ecf20Sopenharmony_ci		if ((val >= 0) &&
5518c2ecf20Sopenharmony_ci		    (val < cptr->info->def.type_enum.count)) {
5528c2ecf20Sopenharmony_ci			if (names[val]) {
5538c2ecf20Sopenharmony_ci				*len = scnprintf(
5548c2ecf20Sopenharmony_ci					buf,maxlen,"%s",
5558c2ecf20Sopenharmony_ci					names[val]);
5568c2ecf20Sopenharmony_ci			} else {
5578c2ecf20Sopenharmony_ci				*len = 0;
5588c2ecf20Sopenharmony_ci			}
5598c2ecf20Sopenharmony_ci			ret = 0;
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci	} else if (cptr->info->type == pvr2_ctl_bitmask) {
5628c2ecf20Sopenharmony_ci		*len = gen_bitmask_string(
5638c2ecf20Sopenharmony_ci			val & mask & cptr->info->def.type_bitmask.valid_bits,
5648c2ecf20Sopenharmony_ci			~0,!0,
5658c2ecf20Sopenharmony_ci			cptr->info->def.type_bitmask.bit_names,
5668c2ecf20Sopenharmony_ci			buf,maxlen);
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	return ret;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci/* Convert a given mask/val to a symbolic value */
5738c2ecf20Sopenharmony_ciint pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr,
5748c2ecf20Sopenharmony_ci			   int mask,int val,
5758c2ecf20Sopenharmony_ci			   char *buf,unsigned int maxlen,
5768c2ecf20Sopenharmony_ci			   unsigned int *len)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	int ret;
5798c2ecf20Sopenharmony_ci	LOCK_TAKE(cptr->hdw->big_lock); do {
5808c2ecf20Sopenharmony_ci		ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val,
5818c2ecf20Sopenharmony_ci						      buf,maxlen,len);
5828c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(cptr->hdw->big_lock);
5838c2ecf20Sopenharmony_ci	return ret;
5848c2ecf20Sopenharmony_ci}
585