aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Isely <isely@pobox.com>2008-04-22 13:45:45 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-24 13:07:48 -0400
commit794b16072e00d0a40a8c773dd4319fb1e460a632 (patch)
tree4ce7b55af0ae1208d72bee673c16311acf085f66
parent8f59100a42576c49e2170e9dc04f8b7ac922a74d (diff)
V4L/DVB (7321): pvrusb2: Rework context handling and initialization
This change significantly rearranges pvr2_context level initialization and operation: 1. A new kernel thread is set up for management of the context. 2. Destruction of the pvr2_context instance is moved into the kernel thread. No other context is able to remove the instance; doing this simplifies lock handling. 3. The callback into pvrusb2-main, which is used to trigger initialization of each interface, is now issued from this kernel thread. Previously it had been indirectly issued out of the work queue thread in pvr2_hdw, which led to deadlock issues if the interface needed to change a control setting (which in turn requires dispatch of another work queue entry). 4. Callbacks into the interfaces (via the pvr2_channel structure) are now issued strictly from this thread. The net result of this is that such callback functions can now also safely operate driver controls without deadlocking the work queue. (At the moment this is not actually a problem, but I'm anticipating issues with this in the future). 5. There is no longer any need for anyone to enter / exit the pvr2_context structure. Implementation of the kernel thread here allows this all to be internal now, simplifying other logic. 6. A very very longstanding issue involving a mutex deadlock between the pvrusb2 driver and v4l should now be solved. The deadlock involved the pvr2_context mutex and a globals-protecting mutex in v4l. During initialization the driver would take the pvr2_context mutex first then the v4l2 interface would register with v4l and implicitly take the v4l mutex. Later when v4l would call back into the driver, the two mutexes could possibly be taken in the opposite order, a situation that can lead to deadlock. In practice this really wasn't an issue unless a v4l app tried to start VERY early after the driver appeared. However it still needed to be solved, and with the use of the kernel thread relieving need for pvr2_context mutex, the problem should be finally solved. Signed-off-by: Mike Isely <isely@pobox.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.c113
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h7
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c20
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h6
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c115
7 files changed, 137 insertions, 127 deletions
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c
index 953784c08abb..22bd8302c4aa 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -23,6 +23,7 @@
23#include "pvrusb2-ioread.h" 23#include "pvrusb2-ioread.h"
24#include "pvrusb2-hdw.h" 24#include "pvrusb2-hdw.h"
25#include "pvrusb2-debug.h" 25#include "pvrusb2-debug.h"
26#include <linux/kthread.h>
26#include <linux/errno.h> 27#include <linux/errno.h>
27#include <linux/string.h> 28#include <linux/string.h>
28#include <linux/slab.h> 29#include <linux/slab.h>
@@ -30,32 +31,65 @@
30 31
31static void pvr2_context_destroy(struct pvr2_context *mp) 32static void pvr2_context_destroy(struct pvr2_context *mp)
32{ 33{
33 pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); 34 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
34 if (mp->hdw) pvr2_hdw_destroy(mp->hdw); 35 if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
35 kfree(mp); 36 kfree(mp);
36} 37}
37 38
38 39
39static void pvr2_context_state_check(struct pvr2_context *mp) 40static void pvr2_context_notify(struct pvr2_context *mp)
40{ 41{
41 if (mp->init_flag) return; 42 mp->notify_flag = !0;
42 43 wake_up(&mp->wait_data);
43 switch (pvr2_hdw_get_state(mp->hdw)) { 44}
44 case PVR2_STATE_WARM: break; 45
45 case PVR2_STATE_ERROR: break; 46
46 case PVR2_STATE_READY: break; 47static int pvr2_context_thread(void *_mp)
47 case PVR2_STATE_RUN: break; 48{
48 default: return; 49 struct pvr2_channel *ch1,*ch2;
50 struct pvr2_context *mp = _mp;
51 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread start)",mp);
52
53 /* Finish hardware initialization */
54 if (pvr2_hdw_initialize(mp->hdw,
55 (void (*)(void *))pvr2_context_notify,mp)) {
56 mp->video_stream.stream =
57 pvr2_hdw_get_video_stream(mp->hdw);
58 /* Trigger interface initialization. By doing this here
59 initialization runs in our own safe and cozy thread
60 context. */
61 if (mp->setup_func) mp->setup_func(mp);
62 } else {
63 pvr2_trace(PVR2_TRACE_CTXT,
64 "pvr2_context %p (thread skipping setup)",mp);
65 /* Even though initialization did not succeed, we're still
66 going to enter the wait loop anyway. We need to do this
67 in order to await the expected disconnect (which we will
68 detect in the normal course of operation). */
49 } 69 }
50 70
51 pvr2_context_enter(mp); do { 71 /* Now just issue callbacks whenever hardware state changes or if
52 mp->init_flag = !0; 72 there is a disconnect. If there is a disconnect and there are
53 mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); 73 no channels left, then there's no reason to stick around anymore
54 if (mp->setup_func) { 74 so we'll self-destruct - tearing down the rest of this driver
55 mp->setup_func(mp); 75 instance along the way. */
76 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread enter loop)",mp);
77 while (!mp->disconnect_flag || mp->mc_first) {
78 if (mp->notify_flag) {
79 mp->notify_flag = 0;
80 pvr2_trace(PVR2_TRACE_CTXT,
81 "pvr2_context %p (thread notify)",mp);
82 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
83 ch2 = ch1->mc_next;
84 if (ch1->check_func) ch1->check_func(ch1);
85 }
56 } 86 }
57 } while (0); pvr2_context_exit(mp); 87 wait_event_interruptible(mp->wait_data, mp->notify_flag);
58 } 88 }
89 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread end)",mp);
90 pvr2_context_destroy(mp);
91 return 0;
92}
59 93
60 94
61struct pvr2_context *pvr2_context_create( 95struct pvr2_context *pvr2_context_create(
@@ -63,10 +97,12 @@ struct pvr2_context *pvr2_context_create(
63 const struct usb_device_id *devid, 97 const struct usb_device_id *devid,
64 void (*setup_func)(struct pvr2_context *)) 98 void (*setup_func)(struct pvr2_context *))
65{ 99{
100 struct task_struct *thread;
66 struct pvr2_context *mp = NULL; 101 struct pvr2_context *mp = NULL;
67 mp = kzalloc(sizeof(*mp),GFP_KERNEL); 102 mp = kzalloc(sizeof(*mp),GFP_KERNEL);
68 if (!mp) goto done; 103 if (!mp) goto done;
69 pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); 104 pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
105 init_waitqueue_head(&mp->wait_data);
70 mp->setup_func = setup_func; 106 mp->setup_func = setup_func;
71 mutex_init(&mp->mutex); 107 mutex_init(&mp->mutex);
72 mp->hdw = pvr2_hdw_create(intf,devid); 108 mp->hdw = pvr2_hdw_create(intf,devid);
@@ -75,57 +111,45 @@ struct pvr2_context *pvr2_context_create(
75 mp = NULL; 111 mp = NULL;
76 goto done; 112 goto done;
77 } 113 }
78 pvr2_hdw_initialize(mp->hdw, 114 thread = kthread_run(pvr2_context_thread, mp, "pvrusb2-context");
79 (void (*)(void *))pvr2_context_state_check, 115 if (!thread) {
80 mp); 116 pvr2_context_destroy(mp);
117 mp = NULL;
118 goto done;
119 }
81 done: 120 done:
82 return mp; 121 return mp;
83} 122}
84 123
85 124
86void pvr2_context_enter(struct pvr2_context *mp) 125static void pvr2_context_enter(struct pvr2_context *mp)
87{ 126{
88 mutex_lock(&mp->mutex); 127 mutex_lock(&mp->mutex);
89 pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp);
90} 128}
91 129
92 130
93void pvr2_context_exit(struct pvr2_context *mp) 131static void pvr2_context_exit(struct pvr2_context *mp)
94{ 132{
95 int destroy_flag = 0; 133 int destroy_flag = 0;
96 if (!(mp->mc_first || !mp->disconnect_flag)) { 134 if (!(mp->mc_first || !mp->disconnect_flag)) {
97 destroy_flag = !0; 135 destroy_flag = !0;
98 } 136 }
99 pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp);
100 mutex_unlock(&mp->mutex); 137 mutex_unlock(&mp->mutex);
101 if (destroy_flag) pvr2_context_destroy(mp); 138 if (destroy_flag) pvr2_context_notify(mp);
102}
103
104
105static void pvr2_context_run_checks(struct pvr2_context *mp)
106{
107 struct pvr2_channel *ch1,*ch2;
108 for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
109 ch2 = ch1->mc_next;
110 if (ch1->check_func) {
111 ch1->check_func(ch1);
112 }
113 }
114} 139}
115 140
116 141
117void pvr2_context_disconnect(struct pvr2_context *mp) 142void pvr2_context_disconnect(struct pvr2_context *mp)
118{ 143{
119 pvr2_context_enter(mp); do { 144 pvr2_hdw_disconnect(mp->hdw);
120 pvr2_hdw_disconnect(mp->hdw); 145 mp->disconnect_flag = !0;
121 mp->disconnect_flag = !0; 146 pvr2_context_notify(mp);
122 pvr2_context_run_checks(mp);
123 } while (0); pvr2_context_exit(mp);
124} 147}
125 148
126 149
127void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) 150void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
128{ 151{
152 pvr2_context_enter(mp);
129 cp->hdw = mp->hdw; 153 cp->hdw = mp->hdw;
130 cp->mc_head = mp; 154 cp->mc_head = mp;
131 cp->mc_next = NULL; 155 cp->mc_next = NULL;
@@ -136,6 +160,7 @@ void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
136 mp->mc_first = cp; 160 mp->mc_first = cp;
137 } 161 }
138 mp->mc_last = cp; 162 mp->mc_last = cp;
163 pvr2_context_exit(mp);
139} 164}
140 165
141 166
@@ -151,6 +176,7 @@ static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
151void pvr2_channel_done(struct pvr2_channel *cp) 176void pvr2_channel_done(struct pvr2_channel *cp)
152{ 177{
153 struct pvr2_context *mp = cp->mc_head; 178 struct pvr2_context *mp = cp->mc_head;
179 pvr2_context_enter(mp);
154 pvr2_channel_disclaim_stream(cp); 180 pvr2_channel_disclaim_stream(cp);
155 if (cp->mc_next) { 181 if (cp->mc_next) {
156 cp->mc_next->mc_prev = cp->mc_prev; 182 cp->mc_next->mc_prev = cp->mc_prev;
@@ -163,6 +189,7 @@ void pvr2_channel_done(struct pvr2_channel *cp)
163 mp->mc_first = cp->mc_next; 189 mp->mc_first = cp->mc_next;
164 } 190 }
165 cp->hdw = NULL; 191 cp->hdw = NULL;
192 pvr2_context_exit(mp);
166} 193}
167 194
168 195
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h
index 96b255f02381..127ec53e0913 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -43,8 +43,10 @@ struct pvr2_context {
43 struct pvr2_hdw *hdw; 43 struct pvr2_hdw *hdw;
44 struct pvr2_context_stream video_stream; 44 struct pvr2_context_stream video_stream;
45 struct mutex mutex; 45 struct mutex mutex;
46 int notify_flag;
46 int disconnect_flag; 47 int disconnect_flag;
47 int init_flag; 48
49 wait_queue_head_t wait_data;
48 50
49 /* Called after pvr2_context initialization is complete */ 51 /* Called after pvr2_context initialization is complete */
50 void (*setup_func)(struct pvr2_context *); 52 void (*setup_func)(struct pvr2_context *);
@@ -60,9 +62,6 @@ struct pvr2_channel {
60 void (*check_func)(struct pvr2_channel *); 62 void (*check_func)(struct pvr2_channel *);
61}; 63};
62 64
63void pvr2_context_enter(struct pvr2_context *);
64void pvr2_context_exit(struct pvr2_context *);
65
66struct pvr2_context *pvr2_context_create(struct usb_interface *intf, 65struct pvr2_context *pvr2_context_create(struct usb_interface *intf,
67 const struct usb_device_id *devid, 66 const struct usb_device_id *devid,
68 void (*setup_func)(struct pvr2_context *)); 67 void (*setup_func)(struct pvr2_context *));
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
index fca49d8a9311..11537ddf8aa3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -39,7 +39,7 @@ extern int pvrusb2_debug;
39#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ 39#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */
40#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ 40#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */
41#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ 41#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
42#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */ 42#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */
43#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ 43#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */
44#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ 44#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */
45#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ 45#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index bc341ae5c79a..c725495826ce 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -187,7 +187,6 @@ struct pvr2_hdw {
187 struct workqueue_struct *workqueue; 187 struct workqueue_struct *workqueue;
188 struct work_struct workpoll; /* Update driver state */ 188 struct work_struct workpoll; /* Update driver state */
189 struct work_struct worki2csync; /* Update i2c clients */ 189 struct work_struct worki2csync; /* Update i2c clients */
190 struct work_struct workinit; /* Driver initialization sequence */
191 190
192 /* Video spigot */ 191 /* Video spigot */
193 struct pvr2_stream *vid_stream; 192 struct pvr2_stream *vid_stream;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 1dff2d04a5d9..7e7c570cf830 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -221,7 +221,6 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *);
221static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); 221static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
222static void pvr2_hdw_worker_i2c(struct work_struct *work); 222static void pvr2_hdw_worker_i2c(struct work_struct *work);
223static void pvr2_hdw_worker_poll(struct work_struct *work); 223static void pvr2_hdw_worker_poll(struct work_struct *work);
224static void pvr2_hdw_worker_init(struct work_struct *work);
225static int pvr2_hdw_wait(struct pvr2_hdw *,int state); 224static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
226static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); 225static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
227static void pvr2_hdw_state_log_state(struct pvr2_hdw *); 226static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
@@ -1816,15 +1815,16 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
1816/* Perform second stage initialization. Set callback pointer first so that 1815/* Perform second stage initialization. Set callback pointer first so that
1817 we can avoid a possible initialization race (if the kernel thread runs 1816 we can avoid a possible initialization race (if the kernel thread runs
1818 before the callback has been set). */ 1817 before the callback has been set). */
1819void pvr2_hdw_initialize(struct pvr2_hdw *hdw, 1818int pvr2_hdw_initialize(struct pvr2_hdw *hdw,
1820 void (*callback_func)(void *), 1819 void (*callback_func)(void *),
1821 void *callback_data) 1820 void *callback_data)
1822{ 1821{
1823 LOCK_TAKE(hdw->big_lock); do { 1822 LOCK_TAKE(hdw->big_lock); do {
1824 hdw->state_data = callback_data; 1823 hdw->state_data = callback_data;
1825 hdw->state_func = callback_func; 1824 hdw->state_func = callback_func;
1826 } while (0); LOCK_GIVE(hdw->big_lock); 1825 } while (0); LOCK_GIVE(hdw->big_lock);
1827 queue_work(hdw->workqueue,&hdw->workinit); 1826 pvr2_hdw_setup(hdw);
1827 return hdw->flag_init_ok;
1828} 1828}
1829 1829
1830 1830
@@ -2032,7 +2032,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
2032 hdw->workqueue = create_singlethread_workqueue(hdw->name); 2032 hdw->workqueue = create_singlethread_workqueue(hdw->name);
2033 INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); 2033 INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
2034 INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); 2034 INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c);
2035 INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init);
2036 2035
2037 pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", 2036 pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
2038 hdw->unit_number,hdw->name); 2037 hdw->unit_number,hdw->name);
@@ -2517,15 +2516,6 @@ static void pvr2_hdw_worker_poll(struct work_struct *work)
2517} 2516}
2518 2517
2519 2518
2520static void pvr2_hdw_worker_init(struct work_struct *work)
2521{
2522 struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit);
2523 LOCK_TAKE(hdw->big_lock); do {
2524 pvr2_hdw_setup(hdw);
2525 } while (0); LOCK_GIVE(hdw->big_lock);
2526}
2527
2528
2529static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) 2519static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
2530{ 2520{
2531 return wait_event_interruptible( 2521 return wait_event_interruptible(
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 597ee5033149..a868e52a73a3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -103,9 +103,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
103 103
104/* Perform second stage initialization, passing in a notification callback 104/* Perform second stage initialization, passing in a notification callback
105 for when the master state changes. */ 105 for when the master state changes. */
106void pvr2_hdw_initialize(struct pvr2_hdw *, 106int pvr2_hdw_initialize(struct pvr2_hdw *,
107 void (*callback_func)(void *), 107 void (*callback_func)(void *),
108 void *callback_data); 108 void *callback_data);
109 109
110/* Destroy hardware interaction structure */ 110/* Destroy hardware interaction structure */
111void pvr2_hdw_destroy(struct pvr2_hdw *); 111void pvr2_hdw_destroy(struct pvr2_hdw *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index e878c6445ae2..249d7488e482 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -884,7 +884,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file)
884{ 884{
885 struct pvr2_v4l2_fh *fhp = file->private_data; 885 struct pvr2_v4l2_fh *fhp = file->private_data;
886 struct pvr2_v4l2 *vp = fhp->vhead; 886 struct pvr2_v4l2 *vp = fhp->vhead;
887 struct pvr2_context *mp = fhp->vhead->channel.mc_head;
888 struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; 887 struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
889 888
890 pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); 889 pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
@@ -901,42 +900,40 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file)
901 v4l2_prio_close(&vp->prio, &fhp->prio); 900 v4l2_prio_close(&vp->prio, &fhp->prio);
902 file->private_data = NULL; 901 file->private_data = NULL;
903 902
904 pvr2_context_enter(mp); do { 903 /* Restore the previous input selection, if it makes sense
905 /* Restore the previous input selection, if it makes sense 904 to do so. */
906 to do so. */ 905 if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) {
907 if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { 906 struct pvr2_ctrl *cp;
908 struct pvr2_ctrl *cp; 907 int pval;
909 int pval; 908 cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
910 cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); 909 pvr2_ctrl_get_value(cp,&pval);
911 pvr2_ctrl_get_value(cp,&pval); 910 /* Only restore if we're still selecting the radio */
912 /* Only restore if we're still selecting the radio */ 911 if (pval == PVR2_CVAL_INPUT_RADIO) {
913 if (pval == PVR2_CVAL_INPUT_RADIO) { 912 pvr2_ctrl_set_value(cp,fhp->prev_input_val);
914 pvr2_ctrl_set_value(cp,fhp->prev_input_val); 913 pvr2_hdw_commit_ctl(hdw);
915 pvr2_hdw_commit_ctl(hdw);
916 }
917 } 914 }
915 }
918 916
919 if (fhp->vnext) { 917 if (fhp->vnext) {
920 fhp->vnext->vprev = fhp->vprev; 918 fhp->vnext->vprev = fhp->vprev;
921 } else { 919 } else {
922 vp->vlast = fhp->vprev; 920 vp->vlast = fhp->vprev;
923 } 921 }
924 if (fhp->vprev) { 922 if (fhp->vprev) {
925 fhp->vprev->vnext = fhp->vnext; 923 fhp->vprev->vnext = fhp->vnext;
926 } else { 924 } else {
927 vp->vfirst = fhp->vnext; 925 vp->vfirst = fhp->vnext;
928 } 926 }
929 fhp->vnext = NULL; 927 fhp->vnext = NULL;
930 fhp->vprev = NULL; 928 fhp->vprev = NULL;
931 fhp->vhead = NULL; 929 fhp->vhead = NULL;
932 pvr2_channel_done(&fhp->channel); 930 pvr2_channel_done(&fhp->channel);
933 pvr2_trace(PVR2_TRACE_STRUCT, 931 pvr2_trace(PVR2_TRACE_STRUCT,
934 "Destroying pvr_v4l2_fh id=%p",fhp); 932 "Destroying pvr_v4l2_fh id=%p",fhp);
935 kfree(fhp); 933 kfree(fhp);
936 if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { 934 if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
937 pvr2_v4l2_destroy_no_lock(vp); 935 pvr2_v4l2_destroy_no_lock(vp);
938 } 936 }
939 } while (0); pvr2_context_exit(mp);
940 return 0; 937 return 0;
941} 938}
942 939
@@ -969,32 +966,30 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
969 init_waitqueue_head(&fhp->wait_data); 966 init_waitqueue_head(&fhp->wait_data);
970 fhp->dev_info = dip; 967 fhp->dev_info = dip;
971 968
972 pvr2_context_enter(vp->channel.mc_head); do { 969 pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
973 pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); 970 pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
974 pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
975 971
976 fhp->vnext = NULL; 972 fhp->vnext = NULL;
977 fhp->vprev = vp->vlast; 973 fhp->vprev = vp->vlast;
978 if (vp->vlast) { 974 if (vp->vlast) {
979 vp->vlast->vnext = fhp; 975 vp->vlast->vnext = fhp;
980 } else { 976 } else {
981 vp->vfirst = fhp; 977 vp->vfirst = fhp;
982 } 978 }
983 vp->vlast = fhp; 979 vp->vlast = fhp;
984 fhp->vhead = vp; 980 fhp->vhead = vp;
985 981
986 /* Opening the /dev/radioX device implies a mode switch. 982 /* Opening the /dev/radioX device implies a mode switch.
987 So execute that here. Note that you can get the 983 So execute that here. Note that you can get the
988 IDENTICAL effect merely by opening the normal video 984 IDENTICAL effect merely by opening the normal video
989 device and setting the input appropriately. */ 985 device and setting the input appropriately. */
990 if (dip->v4l_type == VFL_TYPE_RADIO) { 986 if (dip->v4l_type == VFL_TYPE_RADIO) {
991 struct pvr2_ctrl *cp; 987 struct pvr2_ctrl *cp;
992 cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); 988 cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
993 pvr2_ctrl_get_value(cp,&fhp->prev_input_val); 989 pvr2_ctrl_get_value(cp,&fhp->prev_input_val);
994 pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); 990 pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO);
995 pvr2_hdw_commit_ctl(hdw); 991 pvr2_hdw_commit_ctl(hdw);
996 } 992 }
997 } while (0); pvr2_context_exit(vp->channel.mc_head);
998 993
999 fhp->file = file; 994 fhp->file = file;
1000 file->private_data = fhp; 995 file->private_data = fhp;