From a932f507463d996b6ec1647ee7d4e2fa1e6aeda6 Mon Sep 17 00:00:00 2001
From: Mike Isely <isely@pobox.com>
Date: Fri, 6 Mar 2009 23:47:10 -0300
Subject: V4L/DVB (11159): pvrusb2: Providing means to stop tracking an old i2c
 module

This implements a temporary mechanism to "untrack" an i2c module from
the old i2c layer.  The v4l2-subdev related code in the driver will
use this to remove a sub-device from the old i2c layer.  In the end,
once the old i2c layer is removed, this will also eventually go away.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/media/video/pvrusb2/pvrusb2-hdw.c       |  6 ++
 drivers/media/video/pvrusb2/pvrusb2-i2c-track.c | 76 +++++++++++++++++--------
 drivers/media/video/pvrusb2/pvrusb2-i2c-track.h |  5 ++
 3 files changed, 64 insertions(+), 23 deletions(-)

(limited to 'drivers/media/video/pvrusb2')

diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index faa94cef2c55..0e0d086bb278 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -2020,6 +2020,12 @@ static void pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
 						i2caddr);
 	}
 
+	/* If we have both old and new i2c layers enabled, make sure that
+	   old layer isn't also tracking this module.  This is a debugging
+	   aid, in normal situations there's no reason for both mechanisms
+	   to be enabled. */
+	pvr2_i2c_untrack_subdev(hdw, sd);
+
 	// ?????
 	/* Based on module ID, we should remember subdev pointers
 	   so that we can send certain custom commands where
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-track.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-track.c
index 4bb6f9453e0d..3387897ed897 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-track.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-track.c
@@ -421,41 +421,71 @@ void pvr2_i2c_track_attach_inform(struct i2c_client *client)
 	if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
 }
 
+static void pvr2_i2c_client_disconnect(struct pvr2_i2c_client *cp)
+{
+	if (cp->handler && cp->handler->func_table->detach) {
+		cp->handler->func_table->detach(cp->handler->func_data);
+	}
+	list_del(&cp->list);
+	kfree(cp);
+}
+
 void pvr2_i2c_track_detach_inform(struct i2c_client *client)
 {
 	struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
 	struct pvr2_i2c_client *cp, *ncp;
 	unsigned long amask = 0;
 	int foundfl = 0;
-	mutex_lock(&hdw->i2c_list_lock); do {
-		hdw->cropcap_stale = !0;
-		list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
-			if (cp->client == client) {
-				trace_i2c("pvr2_i2c_detach"
-					  " [client=%s @ 0x%x ctxt=%p]",
-					  client->name,
-					  client->addr,cp);
-				if (cp->handler &&
-				    cp->handler->func_table->detach) {
-					cp->handler->func_table->detach(
-						cp->handler->func_data);
-				}
-				list_del(&cp->list);
-				kfree(cp);
-				foundfl = !0;
-				continue;
-			}
-			amask |= cp->ctl_mask;
+	mutex_lock(&hdw->i2c_list_lock);
+	hdw->cropcap_stale = !0;
+	list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
+		if (cp->client == client) {
+			trace_i2c("pvr2_i2c_detach"
+				  " [client=%s @ 0x%x ctxt=%p]",
+				  client->name,
+				  client->addr, cp);
+			pvr2_i2c_client_disconnect(cp);
+			foundfl = !0;
+			continue;
 		}
-		hdw->i2c_active_mask = amask;
-	} while (0); mutex_unlock(&hdw->i2c_list_lock);
+		amask |= cp->ctl_mask;
+	}
+	hdw->i2c_active_mask = amask;
+	mutex_unlock(&hdw->i2c_list_lock);
 	if (!foundfl) {
 		trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
-			  client->name,
-			  client->addr);
+			  client->name, client->addr);
 	}
 }
 
+/* This function is used to remove an i2c client from our tracking
+   structure if the client happens to be the specified v4l2 sub-device.
+   The idea here is to ensure that sub-devices are not also tracked with
+   the old tracking mechanism - it's one or the other not both.  This is
+   only for debugging.  In a "real" environment, only one of these two
+   mechanisms should even be compiled in.  But by enabling both we can
+   incrementally test control of each sub-device. */
+void pvr2_i2c_untrack_subdev(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+{
+	struct i2c_client *client;
+	struct pvr2_i2c_client *cp, *ncp;
+	unsigned long amask = 0;
+	mutex_lock(&hdw->i2c_list_lock);
+	list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
+		client = cp->client;
+		if (i2c_get_clientdata(client) == sd) {
+			trace_i2c("pvr2_i2c_detach (subdev active)"
+				  " [client=%s @ 0x%x ctxt=%p]",
+				  client->name, client->addr, cp);
+			pvr2_i2c_client_disconnect(cp);
+			continue;
+		}
+		amask |= cp->ctl_mask;
+	}
+	hdw->i2c_active_mask = amask;
+	mutex_unlock(&hdw->i2c_list_lock);
+}
+
 void pvr2_i2c_track_init(struct pvr2_hdw *hdw)
 {
 	hdw->i2c_pend_mask = 0;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-track.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-track.h
index 7d0e4fb63785..eba48e4c9585 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-track.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-track.h
@@ -22,6 +22,8 @@
 
 #include <linux/list.h>
 #include <linux/i2c.h>
+#include <media/v4l2-device.h>
+
 
 struct pvr2_hdw;
 struct pvr2_i2c_client;
@@ -83,6 +85,9 @@ unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
 void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
 const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
 
+void pvr2_i2c_untrack_subdev(struct pvr2_hdw *, struct v4l2_subdev *sd);
+
+
 #endif /* __PVRUSB2_I2C_CORE_H */
 
 
-- 
cgit v1.2.2