V4L/DVB (4239): Handle boolean controls in pvrusb2

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
index 3577d5b..d644afe 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -53,6 +53,8 @@
 				if (val >= cptr->info->def.type_enum.count) {
 					break;
 				}
+			} else if (cptr->info->type != pvr2_ctl_bool) {
+				break;
 			}
 			ret = cptr->info->set_value(cptr,mask,val);
 		} else {
@@ -308,6 +310,14 @@
 }
 
 
+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)
@@ -338,7 +348,7 @@
 	*valptr = simple_strtol(buf,&p2,0);
 	if (negfl) *valptr = -(*valptr);
 	if (*p2) return -EINVAL;
-	return 0;
+	return 1;
 }
 
 
@@ -453,21 +463,31 @@
 	LOCK_TAKE(cptr->hdw->big_lock); do {
 		if (cptr->info->type == pvr2_ctl_int) {
 			ret = parse_token(ptr,len,valptr,0,0);
-			if ((ret == 0) &&
+			if ((ret >= 0) &&
 			    ((*valptr < cptr->info->def.type_int.min_value) ||
 			     (*valptr > cptr->info->def.type_int.max_value))) {
-				ret = -EINVAL;
+				ret = -ERANGE;
 			}
 			if (maskptr) *maskptr = ~0;
+		} else if (cptr->info->type == pvr2_ctl_bool) {
+			ret = parse_token(
+				ptr,len,valptr,boolNames,
+				sizeof(boolNames)/sizeof(boolNames[0]));
+			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) &&
+			if ((ret >= 0) &&
 			    ((*valptr < 0) ||
 			     (*valptr >= cptr->info->def.type_enum.count))) {
-				ret = -EINVAL;
+				ret = -ERANGE;
 			}
 			if (maskptr) *maskptr = ~0;
 		} else if (cptr->info->type == pvr2_ctl_bitmask) {
@@ -493,6 +513,9 @@
 	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;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
index 9d74151..bf4cf65 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
@@ -27,6 +27,7 @@
 	pvr2_ctl_int = 0,
 	pvr2_ctl_enum = 1,
 	pvr2_ctl_bitmask = 2,
+	pvr2_ctl_bool = 3,
 };
 
 
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 8727056..7d46bc1 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -436,6 +436,9 @@
 	.def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \
 	.def.type_enum.value_names = tab
 
+#define DEFBOOL \
+	.type = pvr2_ctl_bool
+
 #define DEFMASK(msk,tab) \
 	.type = pvr2_ctl_bitmask, \
 	.def.type_bitmask.valid_bits = msk, \
@@ -548,7 +551,7 @@
 		.name = "mute",
 		.default_value = 0,
 		DEFREF(mute),
-		DEFINT(0,1),
+		DEFBOOL,
 	},{
 		.desc = "Video Source",
 		.name = "input",
@@ -597,7 +600,7 @@
 		.name = "audio_crc",
 		.default_value = 1,
 		DEFREF(audiocrc),
-		DEFINT(0,1),
+		DEFBOOL,
 	},{
 		.v4l_id = V4L2_CID_PVR_AUDIOEMPHASIS,
 		.desc = "Audio Emphasis",
@@ -611,7 +614,7 @@
 		.name = "vbr",
 		.default_value = 0,
 		DEFREF(vbr),
-		DEFINT(0,1),
+		DEFBOOL,
 	},{
 		.v4l_id = V4L2_CID_PVR_VIDEOBITRATE,
 		.desc = "Average video bitrate",
@@ -632,7 +635,7 @@
 		.internal_id = PVR2_CID_INTERLACE,
 		.default_value = 0,
 		DEFREF(interlace),
-		DEFINT(0,1),
+		DEFBOOL,
 	},{
 		.desc = "Audio Layer",
 		.name = "audio_layer",
@@ -671,7 +674,7 @@
 		.desc = "Streaming Enabled",
 		.name = "streaming_enabled",
 		.get_value = ctrl_streamingenabled_get,
-		DEFINT(0,1),
+		DEFBOOL,
 	},{
 		.desc = "USB Speed",
 		.name = "usb_speed",
@@ -681,7 +684,7 @@
 		.desc = "Signal Present",
 		.name = "signal_present",
 		.get_value = ctrl_signal_get,
-		DEFINT(0,1),
+		DEFBOOL,
 	},{
 		.desc = "Video Standards Available Mask",
 		.name = "video_standard_mask_available",
@@ -2007,6 +2010,7 @@
 	switch (tp) {
 	case pvr2_ctl_int: return "integer";
 	case pvr2_ctl_enum: return "enum";
+	case pvr2_ctl_bool: return "boolean";
 	case pvr2_ctl_bitmask: return "bitmask";
 	}
 	return "";
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 4254062..c6e6523 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -55,6 +55,7 @@
 
 struct pvr2_sysfs_ctl_item {
 	struct class_device_attribute attr_name;
+	struct class_device_attribute attr_type;
 	struct class_device_attribute attr_min;
 	struct class_device_attribute attr_max;
 	struct class_device_attribute attr_enum;
@@ -64,7 +65,7 @@
 	struct pvr2_ctrl *cptr;
 	struct pvr2_sysfs *chptr;
 	struct pvr2_sysfs_ctl_item *item_next;
-	struct attribute *attr_gen[6];
+	struct attribute *attr_gen[7];
 	struct attribute_group grp;
 	char name[80];
 };
@@ -92,6 +93,33 @@
 	return scnprintf(buf,PAGE_SIZE,"%s\n",name);
 }
 
+static ssize_t show_type(int id,struct class_device *class_dev,char *buf)
+{
+	struct pvr2_ctrl *cptr;
+	struct pvr2_sysfs *sfp;
+	const char *name;
+	enum pvr2_ctl_type tp;
+
+	sfp = (struct pvr2_sysfs *)class_dev->class_data;
+	if (!sfp) return -EINVAL;
+	cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+	if (!cptr) return -EINVAL;
+
+	tp = pvr2_ctrl_get_type(cptr);
+	switch (tp) {
+	case pvr2_ctl_int: name = "integer"; break;
+	case pvr2_ctl_enum: name = "enum"; break;
+	case pvr2_ctl_bitmask: name = "bitmask"; break;
+	case pvr2_ctl_bool: name = "boolean"; break;
+	default: name = "?"; break;
+	}
+	pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name);
+
+	if (!name) return -EINVAL;
+
+	return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+}
+
 static ssize_t show_min(int id,struct class_device *class_dev,char *buf)
 {
 	struct pvr2_ctrl *cptr;
@@ -289,6 +317,7 @@
 
 #define CREATE_BATCH(ctl_id) \
 CREATE_SHOW_INSTANCE(show_name,ctl_id) \
+CREATE_SHOW_INSTANCE(show_type,ctl_id) \
 CREATE_SHOW_INSTANCE(show_min,ctl_id) \
 CREATE_SHOW_INSTANCE(show_max,ctl_id) \
 CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \
@@ -361,6 +390,7 @@
 
 struct pvr2_sysfs_func_set {
 	ssize_t (*show_name)(struct class_device *,char *);
+	ssize_t (*show_type)(struct class_device *,char *);
 	ssize_t (*show_min)(struct class_device *,char *);
 	ssize_t (*show_max)(struct class_device *,char *);
 	ssize_t (*show_enum)(struct class_device *,char *);
@@ -376,6 +406,7 @@
 #define INIT_BATCH(ctl_id) \
 [ctl_id] = { \
     .show_name = show_name_##ctl_id, \
+    .show_type = show_type_##ctl_id, \
     .show_min = show_min_##ctl_id, \
     .show_max = show_max_##ctl_id, \
     .show_enum = show_enum_##ctl_id, \
@@ -486,6 +517,11 @@
 	cip->attr_name.attr.mode = S_IRUGO;
 	cip->attr_name.show = fp->show_name;
 
+	cip->attr_type.attr.owner = THIS_MODULE;
+	cip->attr_type.attr.name = "type";
+	cip->attr_type.attr.mode = S_IRUGO;
+	cip->attr_type.show = fp->show_type;
+
 	cip->attr_min.attr.owner = THIS_MODULE;
 	cip->attr_min.attr.name = "min_val";
 	cip->attr_min.attr.mode = S_IRUGO;
@@ -521,6 +557,7 @@
 
 	acnt = 0;
 	cip->attr_gen[acnt++] = &cip->attr_name.attr;
+	cip->attr_gen[acnt++] = &cip->attr_type.attr;
 	cip->attr_gen[acnt++] = &cip->attr_val.attr;
 	cip->attr_val.show = fp->show_val_norm;
 	cip->attr_val.store = fp->store_val_norm;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index da5d8fd..72f28a8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -534,6 +534,12 @@
 			vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
 			vc->step = 1;
 			break;
+		case pvr2_ctl_bool:
+			vc->type = V4L2_CTRL_TYPE_INTEGER;
+			vc->minimum = 0;
+			vc->maximum = 1;
+			vc->step = 1;
+			break;
 		case pvr2_ctl_int:
 			vc->type = V4L2_CTRL_TYPE_INTEGER;
 			vc->minimum = pvr2_ctrl_get_min(cptr);