msm: camera: send error to HAL if daemon crashed

When camera userspace daemon crashes, the server fd
will be close, which trigger the msm_server_close
function, we will check if there is an active camera
session. If camera is active, we will send a v4l2
event to HAL layer to signal an error. Application
will exit gracefully and close other open fds and
exit camera session.

Change-Id: I14ebc864d6cace7ca6f4dbfb935623a76eeccaa8
Signed-off-by: Kevin Chan <ktchan@codeaurora.org>
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index 84d441d..4598b31 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -163,6 +163,10 @@
 		if (!rc)
 			rc = -ETIMEDOUT;
 		if (rc < 0) {
+			rcmd = msm_dequeue(queue, list_control);
+			if (!rcmd)
+				free_qcmd(rcmd);
+			kfree(isp_event);
 			pr_err("%s: wait_event error %d\n", __func__, rc);
 			return rc;
 		}
@@ -943,7 +947,8 @@
 		not in use when we free the buffers */
 	mutex_lock(&pcam->vid_lock);
 	pcam_inst->streamon = 0;
-	rc = msm_server_streamoff(pcam, pcam_inst->my_index);
+	if (g_server_dev.use_count > 0)
+		rc = msm_server_streamoff(pcam, pcam_inst->my_index);
 	mutex_unlock(&pcam->vid_lock);
 	if (rc < 0)
 		pr_err("%s: hw failed to stop streaming\n", __func__);
@@ -1448,6 +1453,7 @@
 		}
 #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
 		pcam->mctl.client = msm_ion_client_create(-1, "camera");
+		kref_init(&pcam->mctl.refcount);
 #endif
 		/* Should be set to sensor ops if any but right now its OK!! */
 		if (!pcam->mctl.mctl_open) {
@@ -1502,6 +1508,7 @@
 
 
 	if (pcam->use_count == 1) {
+		msm_queue_init(&g_server_dev.ctrl_q, "control");
 		rc = msm_send_open_server(pcam->vnode_id);
 		if (rc < 0) {
 			mutex_unlock(&pcam->vid_lock);
@@ -1573,6 +1580,14 @@
 	return rc;
 }
 
+void msm_release_ion_client(struct kref *ref)
+{
+	struct msm_cam_media_controller *mctl = container_of(ref,
+			struct msm_cam_media_controller, refcount);
+	pr_err("%s Calling ion_client_destroy ", __func__);
+	ion_client_destroy(mctl->client);
+}
+
 static int msm_close(struct file *f)
 {
 	int rc = 0;
@@ -1581,13 +1596,11 @@
 	pcam_inst = container_of(f->private_data,
 		struct msm_cam_v4l2_dev_inst, eventHandle);
 	pcam = pcam_inst->pcam;
-	D("%s\n", __func__);
 	if (!pcam) {
 		pr_err("%s NULL pointer of camera device!\n", __func__);
 		return -EINVAL;
 	}
 
-
 	mutex_lock(&pcam->vid_lock);
 	pcam_inst->streamon = 0;
 	pcam->use_count--;
@@ -1610,17 +1623,21 @@
 		if (rc < 0)
 			pr_err("msm_cam_server_close_session fails %d\n", rc);
 
-		rc = msm_send_close_server(pcam->vnode_id);
-		if (rc < 0)
-			pr_err("msm_send_close_server failed %d\n", rc);
+		if (g_server_dev.use_count > 0) {
+			rc = msm_send_close_server(pcam->vnode_id);
+			if (rc < 0)
+				pr_err("msm_send_close_server failed %d\n", rc);
+		}
 		if (pcam->mctl.mctl_release) {
 			rc = pcam->mctl.mctl_release(&(pcam->mctl));
 			if (rc < 0)
 				pr_err("mctl_release fails %d\n", rc);
 		}
 #ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
-		ion_client_destroy(pcam->mctl.client);
+		kref_put(&pcam->mctl.refcount, msm_release_ion_client);
 #endif
+		if (g_server_dev.use_count == 0)
+			mutex_unlock(&g_server_dev.server_lock);
 	}
 	mutex_unlock(&pcam->vid_lock);
 	return rc;
@@ -1879,16 +1896,17 @@
 {
 	int rc;
 	D("%s: open %s\n", __func__, fp->f_path.dentry->d_name.name);
-
+	mutex_lock(&g_server_dev.server_lock);
 	rc = nonseekable_open(inode, fp);
 	if (rc < 0) {
 		pr_err("%s: nonseekable_open error %d\n", __func__, rc);
+		mutex_unlock(&g_server_dev.server_lock);
 		return rc;
 	}
 	g_server_dev.use_count++;
 	if (g_server_dev.use_count == 1)
 		msm_queue_init(&g_server_dev.ctrl_q, "control");
-
+	mutex_unlock(&g_server_dev.server_lock);
 	return rc;
 }
 
@@ -1909,6 +1927,29 @@
 	return rc;
 }
 
+static int msm_close_server(struct inode *inode, struct file *fp)
+{
+	mutex_lock(&g_server_dev.server_lock);
+	if (g_server_dev.use_count > 0)
+		g_server_dev.use_count--;
+	mutex_unlock(&g_server_dev.server_lock);
+	if (g_server_dev.use_count == 0) {
+		if (g_server_dev.pcam_active) {
+			struct v4l2_event v4l2_ev;
+			mutex_lock(&g_server_dev.server_lock);
+
+			v4l2_ev.type = V4L2_EVENT_PRIVATE_START
+				+ MSM_CAM_APP_NOTIFY_ERROR_EVENT;
+			ktime_get_ts(&v4l2_ev.timestamp);
+			v4l2_event_queue(
+				g_server_dev.pcam_active->pvdev, &v4l2_ev);
+		}
+		msm_queue_drain(&g_server_dev.ctrl_q, list_control);
+	}
+	return 0;
+}
+
+
 static long msm_v4l2_evt_notify(struct msm_cam_media_controller *mctl,
 		unsigned int cmd, unsigned long evt)
 {
@@ -2147,10 +2188,19 @@
 	spin_lock_init(&config_cam->p_mctl->sync.pmem_stats_spinlock);
 
 	config_cam->p_mctl->config_device = config_cam;
+	kref_get(&config_cam->p_mctl->refcount);
 	fp->private_data = config_cam;
 	return rc;
 }
 
+static int msm_close_config(struct inode *node, struct file *f)
+{
+	struct msm_cam_config_dev *config_cam = f->private_data;
+	D("%s Decrementing ref count of config node ", __func__);
+	kref_put(&config_cam->p_mctl->refcount, msm_release_ion_client);
+	return 0;
+}
+
 static struct v4l2_file_operations g_msm_fops = {
 	.owner   = THIS_MODULE,
 	.open	= msm_open,
@@ -2169,6 +2219,7 @@
 	.open  = msm_open_server,
 	.poll  = msm_poll_server,
 	.unlocked_ioctl = msm_ioctl_server,
+	.release = msm_close_server,
 };
 
 static const struct file_operations msm_fops_config = {
@@ -2177,6 +2228,7 @@
 	.poll  = msm_poll_config,
 	.unlocked_ioctl = msm_ioctl_config,
 	.mmap	= msm_mmap_config,
+	.release = msm_close_config,
 };
 
 int msm_setup_v4l2_event_queue(struct v4l2_fh *eventHandle,
@@ -2294,6 +2346,7 @@
 		return rc;
 	}
 
+	mutex_init(&g_server_dev.server_lock);
 	g_server_dev.pcam_active = NULL;
 	g_server_dev.camera_info.num_cameras = 0;
 	atomic_set(&g_server_dev.number_pcam_active, 0);
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 55c0da1..c90ab44 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -239,6 +239,7 @@
 	struct pm_qos_request_list pm_qos_req_list;
 	struct msm_mctl_pp_info pp_info;
 	struct ion_client *client;
+	struct kref refcount;
 	/* VFE output mode.
 	* Used to interpret the Primary/Secondary messages
 	* to preview/video/main/thumbnail image types*/
@@ -409,6 +410,7 @@
 	struct msm_isp_ops *isp_subdev[MSM_MAX_CAMERA_CONFIGS];
 	/* info of MCTL nodes successfully probed*/
 	struct msm_mctl_node_info mctl_node_info;
+	struct mutex server_lock;
 };
 
 /* camera server related functions */
@@ -525,6 +527,7 @@
 			int image_mode, struct msm_frame_buffer *buf);
 int msm_mctl_pp_mctl_divert_done(struct msm_cam_media_controller *p_mctl,
 					void __user *arg);
+void msm_release_ion_client(struct kref *ref);
 #endif /* __KERNEL__ */
 
 #endif /* _MSM_H */
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 54458d1..492bcce 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -890,7 +890,7 @@
 		return rc;
 	}
 	pcam_inst->vbqueue_initialized = 0;
-
+	kref_get(&pcam->mctl.refcount);
 	f->private_data = &pcam_inst->eventHandle;
 
 	D("f->private_data = 0x%x, pcam = 0x%x\n",
@@ -961,6 +961,7 @@
 	v4l2_fh_exit(&pcam_inst->eventHandle);
 
 	kfree(pcam_inst);
+	kref_put(&pcam->mctl.refcount, msm_release_ion_client);
 	f->private_data = NULL;
 	mutex_unlock(&pcam->mctl_node.dev_lock);
 	D("%s : X ", __func__);
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 867bd8a..6a7f99b 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -342,6 +342,7 @@
 #define MSM_CAM_RESP_MAX               8
 
 #define MSM_CAM_APP_NOTIFY_EVENT  0
+#define MSM_CAM_APP_NOTIFY_ERROR_EVENT  1
 
 /* this one is used to send ctrl/status up to config thread */