diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/pvrusb2/pvrusb2-dvb.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/pvrusb2/pvrusb2-dvb.c')
-rw-r--r-- | drivers/media/video/pvrusb2/pvrusb2-dvb.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c new file mode 100644 index 00000000000..8c95793433e --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c | |||
@@ -0,0 +1,435 @@ | |||
1 | /* | ||
2 | * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. | ||
3 | * | ||
4 | * Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/kthread.h> | ||
22 | #include <linux/freezer.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include "dvbdev.h" | ||
26 | #include "pvrusb2-debug.h" | ||
27 | #include "pvrusb2-hdw-internal.h" | ||
28 | #include "pvrusb2-hdw.h" | ||
29 | #include "pvrusb2-io.h" | ||
30 | #include "pvrusb2-dvb.h" | ||
31 | |||
32 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
33 | |||
34 | static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) | ||
35 | { | ||
36 | int ret; | ||
37 | unsigned int count; | ||
38 | struct pvr2_buffer *bp; | ||
39 | struct pvr2_stream *stream; | ||
40 | |||
41 | pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started"); | ||
42 | set_freezable(); | ||
43 | |||
44 | stream = adap->channel.stream->stream; | ||
45 | |||
46 | for (;;) { | ||
47 | if (kthread_should_stop()) break; | ||
48 | |||
49 | /* Not sure about this... */ | ||
50 | try_to_freeze(); | ||
51 | |||
52 | bp = pvr2_stream_get_ready_buffer(stream); | ||
53 | if (bp != NULL) { | ||
54 | count = pvr2_buffer_get_count(bp); | ||
55 | if (count) { | ||
56 | dvb_dmx_swfilter( | ||
57 | &adap->demux, | ||
58 | adap->buffer_storage[ | ||
59 | pvr2_buffer_get_id(bp)], | ||
60 | count); | ||
61 | } else { | ||
62 | ret = pvr2_buffer_get_status(bp); | ||
63 | if (ret < 0) break; | ||
64 | } | ||
65 | ret = pvr2_buffer_queue(bp); | ||
66 | if (ret < 0) break; | ||
67 | |||
68 | /* Since we know we did something to a buffer, | ||
69 | just go back and try again. No point in | ||
70 | blocking unless we really ran out of | ||
71 | buffers to process. */ | ||
72 | continue; | ||
73 | } | ||
74 | |||
75 | |||
76 | /* Wait until more buffers become available or we're | ||
77 | told not to wait any longer. */ | ||
78 | ret = wait_event_interruptible( | ||
79 | adap->buffer_wait_data, | ||
80 | (pvr2_stream_get_ready_count(stream) > 0) || | ||
81 | kthread_should_stop()); | ||
82 | if (ret < 0) break; | ||
83 | } | ||
84 | |||
85 | /* If we get here and ret is < 0, then an error has occurred. | ||
86 | Probably would be a good idea to communicate that to DVB core... */ | ||
87 | |||
88 | pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped"); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int pvr2_dvb_feed_thread(void *data) | ||
94 | { | ||
95 | int stat = pvr2_dvb_feed_func(data); | ||
96 | /* from videobuf-dvb.c: */ | ||
97 | while (!kthread_should_stop()) { | ||
98 | set_current_state(TASK_INTERRUPTIBLE); | ||
99 | schedule(); | ||
100 | } | ||
101 | return stat; | ||
102 | } | ||
103 | |||
104 | static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) | ||
105 | { | ||
106 | wake_up(&adap->buffer_wait_data); | ||
107 | } | ||
108 | |||
109 | static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) | ||
110 | { | ||
111 | unsigned int idx; | ||
112 | struct pvr2_stream *stream; | ||
113 | |||
114 | if (adap->thread) { | ||
115 | kthread_stop(adap->thread); | ||
116 | adap->thread = NULL; | ||
117 | } | ||
118 | |||
119 | if (adap->channel.stream) { | ||
120 | stream = adap->channel.stream->stream; | ||
121 | } else { | ||
122 | stream = NULL; | ||
123 | } | ||
124 | if (stream) { | ||
125 | pvr2_hdw_set_streaming(adap->channel.hdw, 0); | ||
126 | pvr2_stream_set_callback(stream, NULL, NULL); | ||
127 | pvr2_stream_kill(stream); | ||
128 | pvr2_stream_set_buffer_count(stream, 0); | ||
129 | pvr2_channel_claim_stream(&adap->channel, NULL); | ||
130 | } | ||
131 | |||
132 | if (adap->stream_run) { | ||
133 | for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { | ||
134 | if (!(adap->buffer_storage[idx])) continue; | ||
135 | kfree(adap->buffer_storage[idx]); | ||
136 | adap->buffer_storage[idx] = NULL; | ||
137 | } | ||
138 | adap->stream_run = 0; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) | ||
143 | { | ||
144 | struct pvr2_context *pvr = adap->channel.mc_head; | ||
145 | unsigned int idx; | ||
146 | int ret; | ||
147 | struct pvr2_buffer *bp; | ||
148 | struct pvr2_stream *stream = NULL; | ||
149 | |||
150 | if (adap->stream_run) return -EIO; | ||
151 | |||
152 | ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); | ||
153 | /* somebody else already has the stream */ | ||
154 | if (ret < 0) return ret; | ||
155 | |||
156 | stream = adap->channel.stream->stream; | ||
157 | |||
158 | for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { | ||
159 | adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, | ||
160 | GFP_KERNEL); | ||
161 | if (!(adap->buffer_storage[idx])) return -ENOMEM; | ||
162 | } | ||
163 | |||
164 | pvr2_stream_set_callback(pvr->video_stream.stream, | ||
165 | (pvr2_stream_callback) pvr2_dvb_notify, adap); | ||
166 | |||
167 | ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); | ||
168 | if (ret < 0) return ret; | ||
169 | |||
170 | for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { | ||
171 | bp = pvr2_stream_get_buffer(stream, idx); | ||
172 | pvr2_buffer_set_buffer(bp, | ||
173 | adap->buffer_storage[idx], | ||
174 | PVR2_DVB_BUFFER_SIZE); | ||
175 | } | ||
176 | |||
177 | ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); | ||
178 | if (ret < 0) return ret; | ||
179 | |||
180 | while ((bp = pvr2_stream_get_idle_buffer(stream)) != NULL) { | ||
181 | ret = pvr2_buffer_queue(bp); | ||
182 | if (ret < 0) return ret; | ||
183 | } | ||
184 | |||
185 | adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); | ||
186 | |||
187 | if (IS_ERR(adap->thread)) { | ||
188 | ret = PTR_ERR(adap->thread); | ||
189 | adap->thread = NULL; | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | adap->stream_run = !0; | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) | ||
199 | { | ||
200 | int ret = pvr2_dvb_stream_do_start(adap); | ||
201 | if (ret < 0) pvr2_dvb_stream_end(adap); | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) | ||
206 | { | ||
207 | struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; | ||
208 | int ret = 0; | ||
209 | |||
210 | if (adap == NULL) return -ENODEV; | ||
211 | |||
212 | mutex_lock(&adap->lock); | ||
213 | do { | ||
214 | if (onoff) { | ||
215 | if (!adap->feedcount) { | ||
216 | pvr2_trace(PVR2_TRACE_DVB_FEED, | ||
217 | "start feeding demux"); | ||
218 | ret = pvr2_dvb_stream_start(adap); | ||
219 | if (ret < 0) break; | ||
220 | } | ||
221 | (adap->feedcount)++; | ||
222 | } else if (adap->feedcount > 0) { | ||
223 | (adap->feedcount)--; | ||
224 | if (!adap->feedcount) { | ||
225 | pvr2_trace(PVR2_TRACE_DVB_FEED, | ||
226 | "stop feeding demux"); | ||
227 | pvr2_dvb_stream_end(adap); | ||
228 | } | ||
229 | } | ||
230 | } while (0); | ||
231 | mutex_unlock(&adap->lock); | ||
232 | |||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) | ||
237 | { | ||
238 | pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid); | ||
239 | return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); | ||
240 | } | ||
241 | |||
242 | static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) | ||
243 | { | ||
244 | pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid); | ||
245 | return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); | ||
246 | } | ||
247 | |||
248 | static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) | ||
249 | { | ||
250 | struct pvr2_dvb_adapter *adap = fe->dvb->priv; | ||
251 | return pvr2_channel_limit_inputs( | ||
252 | &adap->channel, | ||
253 | (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); | ||
254 | } | ||
255 | |||
256 | static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) | ||
257 | { | ||
258 | int ret; | ||
259 | |||
260 | ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", | ||
261 | THIS_MODULE/*&hdw->usb_dev->owner*/, | ||
262 | &adap->channel.hdw->usb_dev->dev, | ||
263 | adapter_nr); | ||
264 | if (ret < 0) { | ||
265 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
266 | "dvb_register_adapter failed: error %d", ret); | ||
267 | goto err; | ||
268 | } | ||
269 | adap->dvb_adap.priv = adap; | ||
270 | |||
271 | adap->demux.dmx.capabilities = DMX_TS_FILTERING | | ||
272 | DMX_SECTION_FILTERING | | ||
273 | DMX_MEMORY_BASED_FILTERING; | ||
274 | adap->demux.priv = adap; | ||
275 | adap->demux.filternum = 256; | ||
276 | adap->demux.feednum = 256; | ||
277 | adap->demux.start_feed = pvr2_dvb_start_feed; | ||
278 | adap->demux.stop_feed = pvr2_dvb_stop_feed; | ||
279 | adap->demux.write_to_decoder = NULL; | ||
280 | |||
281 | ret = dvb_dmx_init(&adap->demux); | ||
282 | if (ret < 0) { | ||
283 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
284 | "dvb_dmx_init failed: error %d", ret); | ||
285 | goto err_dmx; | ||
286 | } | ||
287 | |||
288 | adap->dmxdev.filternum = adap->demux.filternum; | ||
289 | adap->dmxdev.demux = &adap->demux.dmx; | ||
290 | adap->dmxdev.capabilities = 0; | ||
291 | |||
292 | ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); | ||
293 | if (ret < 0) { | ||
294 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
295 | "dvb_dmxdev_init failed: error %d", ret); | ||
296 | goto err_dmx_dev; | ||
297 | } | ||
298 | |||
299 | dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); | ||
300 | |||
301 | return 0; | ||
302 | |||
303 | err_dmx_dev: | ||
304 | dvb_dmx_release(&adap->demux); | ||
305 | err_dmx: | ||
306 | dvb_unregister_adapter(&adap->dvb_adap); | ||
307 | err: | ||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) | ||
312 | { | ||
313 | pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices"); | ||
314 | dvb_net_release(&adap->dvb_net); | ||
315 | adap->demux.dmx.close(&adap->demux.dmx); | ||
316 | dvb_dmxdev_release(&adap->dmxdev); | ||
317 | dvb_dmx_release(&adap->demux); | ||
318 | dvb_unregister_adapter(&adap->dvb_adap); | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) | ||
323 | { | ||
324 | struct pvr2_hdw *hdw = adap->channel.hdw; | ||
325 | const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; | ||
326 | int ret = 0; | ||
327 | |||
328 | if (dvb_props == NULL) { | ||
329 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!"); | ||
330 | return -EINVAL; | ||
331 | } | ||
332 | |||
333 | ret = pvr2_channel_limit_inputs( | ||
334 | &adap->channel, | ||
335 | (1 << PVR2_CVAL_INPUT_DTV)); | ||
336 | if (ret) { | ||
337 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
338 | "failed to grab control of dtv input (code=%d)", | ||
339 | ret); | ||
340 | return ret; | ||
341 | } | ||
342 | |||
343 | if (dvb_props->frontend_attach == NULL) { | ||
344 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
345 | "frontend_attach not defined!"); | ||
346 | ret = -EINVAL; | ||
347 | goto done; | ||
348 | } | ||
349 | |||
350 | if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { | ||
351 | |||
352 | if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { | ||
353 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
354 | "frontend registration failed!"); | ||
355 | dvb_frontend_detach(adap->fe); | ||
356 | adap->fe = NULL; | ||
357 | ret = -ENODEV; | ||
358 | goto done; | ||
359 | } | ||
360 | |||
361 | if (dvb_props->tuner_attach) | ||
362 | dvb_props->tuner_attach(adap); | ||
363 | |||
364 | if (adap->fe->ops.analog_ops.standby) | ||
365 | adap->fe->ops.analog_ops.standby(adap->fe); | ||
366 | |||
367 | /* Ensure all frontends negotiate bus access */ | ||
368 | adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; | ||
369 | |||
370 | } else { | ||
371 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, | ||
372 | "no frontend was attached!"); | ||
373 | ret = -ENODEV; | ||
374 | return ret; | ||
375 | } | ||
376 | |||
377 | done: | ||
378 | pvr2_channel_limit_inputs(&adap->channel, 0); | ||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) | ||
383 | { | ||
384 | if (adap->fe != NULL) { | ||
385 | dvb_unregister_frontend(adap->fe); | ||
386 | dvb_frontend_detach(adap->fe); | ||
387 | } | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) | ||
392 | { | ||
393 | pvr2_dvb_stream_end(adap); | ||
394 | pvr2_dvb_frontend_exit(adap); | ||
395 | pvr2_dvb_adapter_exit(adap); | ||
396 | pvr2_channel_done(&adap->channel); | ||
397 | kfree(adap); | ||
398 | } | ||
399 | |||
400 | static void pvr2_dvb_internal_check(struct pvr2_channel *chp) | ||
401 | { | ||
402 | struct pvr2_dvb_adapter *adap; | ||
403 | adap = container_of(chp, struct pvr2_dvb_adapter, channel); | ||
404 | if (!adap->channel.mc_head->disconnect_flag) return; | ||
405 | pvr2_dvb_destroy(adap); | ||
406 | } | ||
407 | |||
408 | struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) | ||
409 | { | ||
410 | int ret = 0; | ||
411 | struct pvr2_dvb_adapter *adap; | ||
412 | if (!pvr->hdw->hdw_desc->dvb_props) { | ||
413 | /* Device lacks a digital interface so don't set up | ||
414 | the DVB side of the driver either. For now. */ | ||
415 | return NULL; | ||
416 | } | ||
417 | adap = kzalloc(sizeof(*adap), GFP_KERNEL); | ||
418 | if (!adap) return adap; | ||
419 | pvr2_channel_init(&adap->channel, pvr); | ||
420 | adap->channel.check_func = pvr2_dvb_internal_check; | ||
421 | init_waitqueue_head(&adap->buffer_wait_data); | ||
422 | mutex_init(&adap->lock); | ||
423 | ret = pvr2_dvb_adapter_init(adap); | ||
424 | if (ret < 0) goto fail1; | ||
425 | ret = pvr2_dvb_frontend_init(adap); | ||
426 | if (ret < 0) goto fail2; | ||
427 | return adap; | ||
428 | |||
429 | fail2: | ||
430 | pvr2_dvb_adapter_exit(adap); | ||
431 | fail1: | ||
432 | pvr2_channel_done(&adap->channel); | ||
433 | return NULL; | ||
434 | } | ||
435 | |||