]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * dmxdev.c - DVB demultiplexer device | |
3 | * | |
4 | * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> | |
5 | * & Marcus Metzler <marcus@convergence.de> | |
6 | for convergence integrated media GmbH | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public License | |
10 | * as published by the Free Software Foundation; either version 2.1 | |
11 | * of the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/spinlock.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/vmalloc.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/moduleparam.h> | |
29 | #include <linux/sched.h> | |
30 | #include <linux/poll.h> | |
31 | #include <linux/ioctl.h> | |
32 | #include <linux/wait.h> | |
33 | #include <asm/uaccess.h> | |
34 | #include <asm/system.h> | |
35 | ||
36 | #include "dmxdev.h" | |
37 | ||
38 | static int debug; | |
39 | ||
40 | module_param(debug, int, 0644); | |
41 | MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); | |
42 | ||
43 | #define dprintk if (debug) printk | |
44 | ||
45 | static inline struct dmxdev_filter * | |
46 | dvb_dmxdev_file_to_filter(struct file *file) | |
47 | { | |
48 | return (struct dmxdev_filter *) file->private_data; | |
49 | } | |
50 | ||
51 | static inline void dvb_dmxdev_buffer_init(struct dmxdev_buffer *buffer) | |
52 | { | |
53 | buffer->data=NULL; | |
54 | buffer->size=8192; | |
55 | buffer->pread=0; | |
56 | buffer->pwrite=0; | |
57 | buffer->error=0; | |
58 | init_waitqueue_head(&buffer->queue); | |
59 | } | |
60 | ||
61 | static inline int dvb_dmxdev_buffer_write(struct dmxdev_buffer *buf, const u8 *src, int len) | |
62 | { | |
63 | int split; | |
64 | int free; | |
65 | int todo; | |
66 | ||
67 | if (!len) | |
68 | return 0; | |
69 | if (!buf->data) | |
70 | return 0; | |
71 | ||
72 | free=buf->pread-buf->pwrite; | |
73 | split=0; | |
74 | if (free<=0) { | |
75 | free+=buf->size; | |
76 | split=buf->size-buf->pwrite; | |
77 | } | |
78 | if (len>=free) { | |
79 | dprintk("dmxdev: buffer overflow\n"); | |
80 | return -1; | |
81 | } | |
82 | if (split>=len) | |
83 | split=0; | |
84 | todo=len; | |
85 | if (split) { | |
86 | memcpy(buf->data + buf->pwrite, src, split); | |
87 | todo-=split; | |
88 | buf->pwrite=0; | |
89 | } | |
90 | memcpy(buf->data + buf->pwrite, src+split, todo); | |
91 | buf->pwrite=(buf->pwrite+todo)%buf->size; | |
92 | return len; | |
93 | } | |
94 | ||
95 | static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_buffer *src, | |
96 | int non_blocking, char __user *buf, size_t count, loff_t *ppos) | |
97 | { | |
98 | unsigned long todo=count; | |
99 | int split, avail, error; | |
100 | ||
101 | if (!src->data) | |
102 | return 0; | |
103 | ||
104 | if ((error=src->error)) { | |
105 | src->pwrite=src->pread; | |
106 | src->error=0; | |
107 | return error; | |
108 | } | |
109 | ||
110 | if (non_blocking && (src->pwrite==src->pread)) | |
111 | return -EWOULDBLOCK; | |
112 | ||
113 | while (todo>0) { | |
114 | if (non_blocking && (src->pwrite==src->pread)) | |
115 | return (count-todo) ? (count-todo) : -EWOULDBLOCK; | |
116 | ||
117 | if (wait_event_interruptible(src->queue, | |
118 | (src->pread!=src->pwrite) || | |
119 | (src->error))<0) | |
120 | return count-todo; | |
121 | ||
122 | if ((error=src->error)) { | |
123 | src->pwrite=src->pread; | |
124 | src->error=0; | |
125 | return error; | |
126 | } | |
127 | ||
128 | split=src->size; | |
129 | avail=src->pwrite - src->pread; | |
130 | if (avail<0) { | |
131 | avail+=src->size; | |
132 | split=src->size - src->pread; | |
133 | } | |
134 | if (avail>todo) | |
135 | avail=todo; | |
136 | if (split<avail) { | |
137 | if (copy_to_user(buf, src->data+src->pread, split)) | |
138 | return -EFAULT; | |
139 | buf+=split; | |
140 | src->pread=0; | |
141 | todo-=split; | |
142 | avail-=split; | |
143 | } | |
144 | if (avail) { | |
145 | if (copy_to_user(buf, src->data+src->pread, avail)) | |
146 | return -EFAULT; | |
147 | src->pread = (src->pread + avail) % src->size; | |
148 | todo-=avail; | |
149 | buf+=avail; | |
150 | } | |
151 | } | |
152 | return count; | |
153 | } | |
154 | ||
155 | static struct dmx_frontend * get_fe(struct dmx_demux *demux, int type) | |
156 | { | |
157 | struct list_head *head, *pos; | |
158 | ||
159 | head=demux->get_frontends(demux); | |
160 | if (!head) | |
161 | return NULL; | |
162 | list_for_each(pos, head) | |
163 | if (DMX_FE_ENTRY(pos)->source==type) | |
164 | return DMX_FE_ENTRY(pos); | |
165 | ||
166 | return NULL; | |
167 | } | |
168 | ||
169 | static inline void dvb_dmxdev_dvr_state_set(struct dmxdev_dvr *dmxdevdvr, int state) | |
170 | { | |
171 | spin_lock_irq(&dmxdevdvr->dev->lock); | |
172 | dmxdevdvr->state=state; | |
173 | spin_unlock_irq(&dmxdevdvr->dev->lock); | |
174 | } | |
175 | ||
176 | static int dvb_dvr_open(struct inode *inode, struct file *file) | |
177 | { | |
178 | struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; | |
179 | struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv; | |
180 | struct dmx_frontend *front; | |
181 | ||
182 | dprintk ("function : %s\n", __FUNCTION__); | |
183 | ||
184 | if (down_interruptible (&dmxdev->mutex)) | |
185 | return -ERESTARTSYS; | |
186 | ||
187 | if ((file->f_flags&O_ACCMODE)==O_RDWR) { | |
188 | if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) { | |
189 | up(&dmxdev->mutex); | |
190 | return -EOPNOTSUPP; | |
191 | } | |
192 | } | |
193 | ||
194 | if ((file->f_flags&O_ACCMODE)==O_RDONLY) { | |
195 | dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer); | |
196 | dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE; | |
197 | dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE); | |
198 | if (!dmxdev->dvr_buffer.data) { | |
199 | up(&dmxdev->mutex); | |
200 | return -ENOMEM; | |
201 | } | |
202 | } | |
203 | ||
204 | if ((file->f_flags&O_ACCMODE)==O_WRONLY) { | |
205 | dmxdev->dvr_orig_fe=dmxdev->demux->frontend; | |
206 | ||
207 | if (!dmxdev->demux->write) { | |
208 | up(&dmxdev->mutex); | |
209 | return -EOPNOTSUPP; | |
210 | } | |
211 | ||
212 | front=get_fe(dmxdev->demux, DMX_MEMORY_FE); | |
213 | ||
214 | if (!front) { | |
215 | up(&dmxdev->mutex); | |
216 | return -EINVAL; | |
217 | } | |
218 | dmxdev->demux->disconnect_frontend(dmxdev->demux); | |
219 | dmxdev->demux->connect_frontend(dmxdev->demux, front); | |
220 | } | |
221 | up(&dmxdev->mutex); | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static int dvb_dvr_release(struct inode *inode, struct file *file) | |
226 | { | |
227 | struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; | |
228 | struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv; | |
229 | ||
230 | if (down_interruptible (&dmxdev->mutex)) | |
231 | return -ERESTARTSYS; | |
232 | ||
233 | if ((file->f_flags&O_ACCMODE)==O_WRONLY) { | |
234 | dmxdev->demux->disconnect_frontend(dmxdev->demux); | |
235 | dmxdev->demux->connect_frontend(dmxdev->demux, | |
236 | dmxdev->dvr_orig_fe); | |
237 | } | |
238 | if ((file->f_flags&O_ACCMODE)==O_RDONLY) { | |
239 | if (dmxdev->dvr_buffer.data) { | |
240 | void *mem=dmxdev->dvr_buffer.data; | |
241 | mb(); | |
242 | spin_lock_irq(&dmxdev->lock); | |
243 | dmxdev->dvr_buffer.data=NULL; | |
244 | spin_unlock_irq(&dmxdev->lock); | |
245 | vfree(mem); | |
246 | } | |
247 | } | |
248 | up(&dmxdev->mutex); | |
249 | return 0; | |
250 | } | |
251 | ||
252 | static ssize_t dvb_dvr_write(struct file *file, const char __user *buf, | |
253 | size_t count, loff_t *ppos) | |
254 | { | |
255 | struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; | |
256 | struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv; | |
257 | int ret; | |
258 | ||
259 | if (!dmxdev->demux->write) | |
260 | return -EOPNOTSUPP; | |
261 | if ((file->f_flags&O_ACCMODE)!=O_WRONLY) | |
262 | return -EINVAL; | |
263 | if (down_interruptible (&dmxdev->mutex)) | |
264 | return -ERESTARTSYS; | |
265 | ret=dmxdev->demux->write(dmxdev->demux, buf, count); | |
266 | up(&dmxdev->mutex); | |
267 | return ret; | |
268 | } | |
269 | ||
270 | static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, | |
271 | loff_t *ppos) | |
272 | { | |
273 | struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; | |
274 | struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv; | |
275 | int ret; | |
276 | ||
277 | //down(&dmxdev->mutex); | |
278 | ret= dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, | |
279 | file->f_flags&O_NONBLOCK, | |
280 | buf, count, ppos); | |
281 | //up(&dmxdev->mutex); | |
282 | return ret; | |
283 | } | |
284 | ||
285 | static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter *dmxdevfilter, int state) | |
286 | { | |
287 | spin_lock_irq(&dmxdevfilter->dev->lock); | |
288 | dmxdevfilter->state=state; | |
289 | spin_unlock_irq(&dmxdevfilter->dev->lock); | |
290 | } | |
291 | ||
292 | static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, unsigned long size) | |
293 | { | |
294 | struct dmxdev_buffer *buf=&dmxdevfilter->buffer; | |
295 | void *mem; | |
296 | ||
297 | if (buf->size==size) | |
298 | return 0; | |
299 | if (dmxdevfilter->state>=DMXDEV_STATE_GO) | |
300 | return -EBUSY; | |
301 | spin_lock_irq(&dmxdevfilter->dev->lock); | |
302 | mem=buf->data; | |
303 | buf->data=NULL; | |
304 | buf->size=size; | |
305 | buf->pwrite=buf->pread=0; | |
306 | spin_unlock_irq(&dmxdevfilter->dev->lock); | |
307 | vfree(mem); | |
308 | ||
309 | if (buf->size) { | |
310 | mem=vmalloc(dmxdevfilter->buffer.size); | |
311 | if (!mem) | |
312 | return -ENOMEM; | |
313 | spin_lock_irq(&dmxdevfilter->dev->lock); | |
314 | buf->data=mem; | |
315 | spin_unlock_irq(&dmxdevfilter->dev->lock); | |
316 | } | |
317 | return 0; | |
318 | } | |
319 | ||
320 | static void dvb_dmxdev_filter_timeout(unsigned long data) | |
321 | { | |
322 | struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *)data; | |
323 | ||
324 | dmxdevfilter->buffer.error=-ETIMEDOUT; | |
325 | spin_lock_irq(&dmxdevfilter->dev->lock); | |
326 | dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT; | |
327 | spin_unlock_irq(&dmxdevfilter->dev->lock); | |
328 | wake_up(&dmxdevfilter->buffer.queue); | |
329 | } | |
330 | ||
331 | static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter) | |
332 | { | |
333 | struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec; | |
334 | ||
335 | del_timer(&dmxdevfilter->timer); | |
336 | if (para->timeout) { | |
337 | dmxdevfilter->timer.function=dvb_dmxdev_filter_timeout; | |
338 | dmxdevfilter->timer.data=(unsigned long) dmxdevfilter; | |
339 | dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000; | |
340 | add_timer(&dmxdevfilter->timer); | |
341 | } | |
342 | } | |
343 | ||
344 | static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, | |
345 | const u8 *buffer2, size_t buffer2_len, | |
346 | struct dmx_section_filter *filter, enum dmx_success success) | |
347 | { | |
348 | struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) filter->priv; | |
349 | int ret; | |
350 | ||
351 | if (dmxdevfilter->buffer.error) { | |
352 | wake_up(&dmxdevfilter->buffer.queue); | |
353 | return 0; | |
354 | } | |
355 | spin_lock(&dmxdevfilter->dev->lock); | |
356 | if (dmxdevfilter->state!=DMXDEV_STATE_GO) { | |
357 | spin_unlock(&dmxdevfilter->dev->lock); | |
358 | return 0; | |
359 | } | |
360 | del_timer(&dmxdevfilter->timer); | |
361 | dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n", | |
362 | buffer1[0], buffer1[1], | |
363 | buffer1[2], buffer1[3], | |
364 | buffer1[4], buffer1[5]); | |
365 | ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len); | |
366 | if (ret==buffer1_len) { | |
367 | ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, buffer2_len); | |
368 | } | |
369 | if (ret<0) { | |
370 | dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread; | |
371 | dmxdevfilter->buffer.error=-EOVERFLOW; | |
372 | } | |
373 | if (dmxdevfilter->params.sec.flags&DMX_ONESHOT) | |
374 | dmxdevfilter->state=DMXDEV_STATE_DONE; | |
375 | spin_unlock(&dmxdevfilter->dev->lock); | |
376 | wake_up(&dmxdevfilter->buffer.queue); | |
377 | return 0; | |
378 | } | |
379 | ||
380 | static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, | |
381 | const u8 *buffer2, size_t buffer2_len, | |
382 | struct dmx_ts_feed *feed, enum dmx_success success) | |
383 | { | |
384 | struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) feed->priv; | |
385 | struct dmxdev_buffer *buffer; | |
386 | int ret; | |
387 | ||
388 | spin_lock(&dmxdevfilter->dev->lock); | |
389 | if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) { | |
390 | spin_unlock(&dmxdevfilter->dev->lock); | |
391 | return 0; | |
392 | } | |
393 | ||
394 | if (dmxdevfilter->params.pes.output==DMX_OUT_TAP) | |
395 | buffer=&dmxdevfilter->buffer; | |
396 | else | |
397 | buffer=&dmxdevfilter->dev->dvr_buffer; | |
398 | if (buffer->error) { | |
399 | spin_unlock(&dmxdevfilter->dev->lock); | |
400 | wake_up(&buffer->queue); | |
401 | return 0; | |
402 | } | |
403 | ret=dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len); | |
404 | if (ret==buffer1_len) | |
405 | ret=dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len); | |
406 | if (ret<0) { | |
407 | buffer->pwrite=buffer->pread; | |
408 | buffer->error=-EOVERFLOW; | |
409 | } | |
410 | spin_unlock(&dmxdevfilter->dev->lock); | |
411 | wake_up(&buffer->queue); | |
412 | return 0; | |
413 | } | |
414 | ||
415 | ||
416 | /* stop feed but only mark the specified filter as stopped (state set) */ | |
417 | ||
418 | static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) | |
419 | { | |
420 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | |
421 | ||
422 | switch (dmxdevfilter->type) { | |
423 | case DMXDEV_TYPE_SEC: | |
424 | del_timer(&dmxdevfilter->timer); | |
425 | dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); | |
426 | break; | |
427 | case DMXDEV_TYPE_PES: | |
428 | dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts); | |
429 | break; | |
430 | default: | |
431 | return -EINVAL; | |
432 | } | |
433 | return 0; | |
434 | } | |
435 | ||
436 | ||
437 | /* start feed associated with the specified filter */ | |
438 | ||
439 | static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) | |
440 | { | |
441 | dvb_dmxdev_filter_state_set (filter, DMXDEV_STATE_GO); | |
442 | ||
443 | switch (filter->type) { | |
444 | case DMXDEV_TYPE_SEC: | |
445 | return filter->feed.sec->start_filtering(filter->feed.sec); | |
446 | break; | |
447 | case DMXDEV_TYPE_PES: | |
448 | return filter->feed.ts->start_filtering(filter->feed.ts); | |
449 | break; | |
450 | default: | |
451 | return -EINVAL; | |
452 | } | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
457 | ||
458 | /* restart section feed if it has filters left associated with it, | |
459 | otherwise release the feed */ | |
460 | ||
461 | static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter) | |
462 | { | |
463 | int i; | |
464 | struct dmxdev *dmxdev = filter->dev; | |
465 | u16 pid = filter->params.sec.pid; | |
466 | ||
467 | for (i=0; i<dmxdev->filternum; i++) | |
468 | if (dmxdev->filter[i].state>=DMXDEV_STATE_GO && | |
469 | dmxdev->filter[i].type==DMXDEV_TYPE_SEC && | |
470 | dmxdev->filter[i].pid==pid) { | |
471 | dvb_dmxdev_feed_start(&dmxdev->filter[i]); | |
472 | return 0; | |
473 | } | |
474 | ||
475 | filter->dev->demux->release_section_feed(dmxdev->demux, filter->feed.sec); | |
476 | ||
477 | return 0; | |
478 | } | |
479 | ||
480 | static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) | |
481 | { | |
482 | if (dmxdevfilter->state<DMXDEV_STATE_GO) | |
483 | return 0; | |
484 | ||
485 | switch (dmxdevfilter->type) { | |
486 | case DMXDEV_TYPE_SEC: | |
487 | if (!dmxdevfilter->feed.sec) | |
488 | break; | |
489 | dvb_dmxdev_feed_stop(dmxdevfilter); | |
490 | if (dmxdevfilter->filter.sec) | |
491 | dmxdevfilter->feed.sec-> | |
492 | release_filter(dmxdevfilter->feed.sec, | |
493 | dmxdevfilter->filter.sec); | |
494 | dvb_dmxdev_feed_restart(dmxdevfilter); | |
495 | dmxdevfilter->feed.sec=NULL; | |
496 | break; | |
497 | case DMXDEV_TYPE_PES: | |
498 | if (!dmxdevfilter->feed.ts) | |
499 | break; | |
500 | dvb_dmxdev_feed_stop(dmxdevfilter); | |
501 | dmxdevfilter->dev->demux-> | |
502 | release_ts_feed(dmxdevfilter->dev->demux, | |
503 | dmxdevfilter->feed.ts); | |
504 | dmxdevfilter->feed.ts=NULL; | |
505 | break; | |
506 | default: | |
507 | if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED) | |
508 | return 0; | |
509 | return -EINVAL; | |
510 | } | |
511 | dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0; | |
512 | return 0; | |
513 | } | |
514 | ||
515 | static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) | |
516 | { | |
517 | if (dmxdevfilter->state<DMXDEV_STATE_SET) | |
518 | return 0; | |
519 | ||
520 | dmxdevfilter->type=DMXDEV_TYPE_NONE; | |
521 | dmxdevfilter->pid=0xffff; | |
522 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); | |
523 | return 0; | |
524 | } | |
525 | ||
526 | static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) | |
527 | { | |
528 | struct dmxdev *dmxdev = filter->dev; | |
529 | void *mem; | |
530 | int ret, i; | |
531 | ||
532 | if (filter->state < DMXDEV_STATE_SET) | |
533 | return -EINVAL; | |
534 | ||
535 | if (filter->state >= DMXDEV_STATE_GO) | |
536 | dvb_dmxdev_filter_stop(filter); | |
537 | ||
538 | if (!(mem = filter->buffer.data)) { | |
539 | mem = vmalloc(filter->buffer.size); | |
540 | spin_lock_irq(&filter->dev->lock); | |
541 | filter->buffer.data=mem; | |
542 | spin_unlock_irq(&filter->dev->lock); | |
543 | if (!filter->buffer.data) | |
544 | return -ENOMEM; | |
545 | } | |
546 | ||
547 | filter->buffer.pwrite = filter->buffer.pread = 0; | |
548 | ||
549 | switch (filter->type) { | |
550 | case DMXDEV_TYPE_SEC: | |
551 | { | |
552 | struct dmx_sct_filter_params *para=&filter->params.sec; | |
553 | struct dmx_section_filter **secfilter=&filter->filter.sec; | |
554 | struct dmx_section_feed **secfeed=&filter->feed.sec; | |
555 | ||
556 | *secfilter=NULL; | |
557 | *secfeed=NULL; | |
558 | ||
559 | /* find active filter/feed with same PID */ | |
560 | for (i=0; i<dmxdev->filternum; i++) { | |
561 | if (dmxdev->filter[i].state >= DMXDEV_STATE_GO && | |
562 | dmxdev->filter[i].pid == para->pid && | |
563 | dmxdev->filter[i].type == DMXDEV_TYPE_SEC) { | |
564 | *secfeed = dmxdev->filter[i].feed.sec; | |
565 | break; | |
566 | } | |
567 | } | |
568 | ||
569 | /* if no feed found, try to allocate new one */ | |
570 | if (!*secfeed) { | |
571 | ret=dmxdev->demux->allocate_section_feed(dmxdev->demux, | |
572 | secfeed, | |
573 | dvb_dmxdev_section_callback); | |
574 | if (ret<0) { | |
575 | printk ("DVB (%s): could not alloc feed\n", | |
576 | __FUNCTION__); | |
577 | return ret; | |
578 | } | |
579 | ||
580 | ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0, | |
581 | (para->flags & DMX_CHECK_CRC) ? 1 : 0); | |
582 | ||
583 | if (ret<0) { | |
584 | printk ("DVB (%s): could not set feed\n", | |
585 | __FUNCTION__); | |
586 | dvb_dmxdev_feed_restart(filter); | |
587 | return ret; | |
588 | } | |
589 | } else { | |
590 | dvb_dmxdev_feed_stop(filter); | |
591 | } | |
592 | ||
593 | ret=(*secfeed)->allocate_filter(*secfeed, secfilter); | |
594 | ||
595 | if (ret < 0) { | |
596 | dvb_dmxdev_feed_restart(filter); | |
597 | filter->feed.sec->start_filtering(*secfeed); | |
598 | dprintk ("could not get filter\n"); | |
599 | return ret; | |
600 | } | |
601 | ||
602 | (*secfilter)->priv = filter; | |
603 | ||
604 | memcpy(&((*secfilter)->filter_value[3]), | |
605 | &(para->filter.filter[1]), DMX_FILTER_SIZE-1); | |
606 | memcpy(&(*secfilter)->filter_mask[3], | |
607 | ¶->filter.mask[1], DMX_FILTER_SIZE-1); | |
608 | memcpy(&(*secfilter)->filter_mode[3], | |
609 | ¶->filter.mode[1], DMX_FILTER_SIZE-1); | |
610 | ||
611 | (*secfilter)->filter_value[0]=para->filter.filter[0]; | |
612 | (*secfilter)->filter_mask[0]=para->filter.mask[0]; | |
613 | (*secfilter)->filter_mode[0]=para->filter.mode[0]; | |
614 | (*secfilter)->filter_mask[1]=0; | |
615 | (*secfilter)->filter_mask[2]=0; | |
616 | ||
617 | filter->todo = 0; | |
618 | ||
619 | ret = filter->feed.sec->start_filtering (filter->feed.sec); | |
620 | ||
621 | if (ret < 0) | |
622 | return ret; | |
623 | ||
624 | dvb_dmxdev_filter_timer(filter); | |
625 | break; | |
626 | } | |
627 | ||
628 | case DMXDEV_TYPE_PES: | |
629 | { | |
630 | struct timespec timeout = { 0 }; | |
631 | struct dmx_pes_filter_params *para = &filter->params.pes; | |
632 | dmx_output_t otype; | |
633 | int ret; | |
634 | int ts_type; | |
635 | enum dmx_ts_pes ts_pes; | |
636 | struct dmx_ts_feed **tsfeed = &filter->feed.ts; | |
637 | ||
638 | filter->feed.ts = NULL; | |
639 | otype=para->output; | |
640 | ||
641 | ts_pes=(enum dmx_ts_pes) para->pes_type; | |
642 | ||
643 | if (ts_pes<DMX_PES_OTHER) | |
644 | ts_type=TS_DECODER; | |
645 | else | |
646 | ts_type=0; | |
647 | ||
648 | if (otype == DMX_OUT_TS_TAP) | |
649 | ts_type |= TS_PACKET; | |
650 | ||
651 | if (otype == DMX_OUT_TAP) | |
652 | ts_type |= TS_PAYLOAD_ONLY|TS_PACKET; | |
653 | ||
654 | ret=dmxdev->demux->allocate_ts_feed(dmxdev->demux, | |
655 | tsfeed, | |
656 | dvb_dmxdev_ts_callback); | |
657 | if (ret<0) | |
658 | return ret; | |
659 | ||
660 | (*tsfeed)->priv = (void *) filter; | |
661 | ||
662 | ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes, | |
663 | 188, 32768, 0, timeout); | |
664 | ||
665 | if (ret < 0) { | |
666 | dmxdev->demux->release_ts_feed(dmxdev->demux, *tsfeed); | |
667 | return ret; | |
668 | } | |
669 | ||
670 | ret = filter->feed.ts->start_filtering(filter->feed.ts); | |
671 | ||
672 | if (ret < 0) | |
673 | return ret; | |
674 | ||
675 | break; | |
676 | } | |
677 | default: | |
678 | return -EINVAL; | |
679 | } | |
680 | ||
681 | dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); | |
682 | return 0; | |
683 | } | |
684 | ||
685 | static int dvb_demux_open(struct inode *inode, struct file *file) | |
686 | { | |
687 | struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; | |
688 | struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv; | |
689 | int i; | |
690 | struct dmxdev_filter *dmxdevfilter; | |
691 | ||
692 | if (!dmxdev->filter) | |
693 | return -EINVAL; | |
694 | ||
695 | if (down_interruptible(&dmxdev->mutex)) | |
696 | return -ERESTARTSYS; | |
697 | ||
698 | for (i=0; i<dmxdev->filternum; i++) | |
699 | if (dmxdev->filter[i].state==DMXDEV_STATE_FREE) | |
700 | break; | |
701 | ||
702 | if (i==dmxdev->filternum) { | |
703 | up(&dmxdev->mutex); | |
704 | return -EMFILE; | |
705 | } | |
706 | ||
707 | dmxdevfilter=&dmxdev->filter[i]; | |
708 | sema_init(&dmxdevfilter->mutex, 1); | |
709 | dmxdevfilter->dvbdev=dmxdev->dvbdev; | |
710 | file->private_data=dmxdevfilter; | |
711 | ||
712 | dvb_dmxdev_buffer_init(&dmxdevfilter->buffer); | |
713 | dmxdevfilter->type=DMXDEV_TYPE_NONE; | |
714 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); | |
715 | dmxdevfilter->feed.ts=NULL; | |
716 | init_timer(&dmxdevfilter->timer); | |
717 | ||
718 | up(&dmxdev->mutex); | |
719 | return 0; | |
720 | } | |
721 | ||
722 | ||
723 | static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter) | |
724 | { | |
725 | if (down_interruptible(&dmxdev->mutex)) | |
726 | return -ERESTARTSYS; | |
727 | ||
728 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
729 | up(&dmxdev->mutex); | |
730 | return -ERESTARTSYS; | |
731 | } | |
732 | ||
733 | dvb_dmxdev_filter_stop(dmxdevfilter); | |
734 | dvb_dmxdev_filter_reset(dmxdevfilter); | |
735 | ||
736 | if (dmxdevfilter->buffer.data) { | |
737 | void *mem=dmxdevfilter->buffer.data; | |
738 | ||
739 | spin_lock_irq(&dmxdev->lock); | |
740 | dmxdevfilter->buffer.data=NULL; | |
741 | spin_unlock_irq(&dmxdev->lock); | |
742 | vfree(mem); | |
743 | } | |
744 | ||
745 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE); | |
746 | wake_up(&dmxdevfilter->buffer.queue); | |
747 | up(&dmxdevfilter->mutex); | |
748 | up(&dmxdev->mutex); | |
749 | return 0; | |
750 | } | |
751 | ||
752 | static inline void invert_mode(dmx_filter_t *filter) | |
753 | { | |
754 | int i; | |
755 | ||
756 | for (i=0; i<DMX_FILTER_SIZE; i++) | |
757 | filter->mode[i]^=0xff; | |
758 | } | |
759 | ||
760 | ||
761 | static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, | |
762 | struct dmxdev_filter *dmxdevfilter, | |
763 | struct dmx_sct_filter_params *params) | |
764 | { | |
765 | dprintk ("function : %s\n", __FUNCTION__); | |
766 | ||
767 | dvb_dmxdev_filter_stop(dmxdevfilter); | |
768 | ||
769 | dmxdevfilter->type=DMXDEV_TYPE_SEC; | |
770 | dmxdevfilter->pid=params->pid; | |
771 | memcpy(&dmxdevfilter->params.sec, | |
772 | params, sizeof(struct dmx_sct_filter_params)); | |
773 | invert_mode(&dmxdevfilter->params.sec.filter); | |
774 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | |
775 | ||
776 | if (params->flags&DMX_IMMEDIATE_START) | |
777 | return dvb_dmxdev_filter_start(dmxdevfilter); | |
778 | ||
779 | return 0; | |
780 | } | |
781 | ||
782 | static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, | |
783 | struct dmxdev_filter *dmxdevfilter, | |
784 | struct dmx_pes_filter_params *params) | |
785 | { | |
786 | dvb_dmxdev_filter_stop(dmxdevfilter); | |
787 | ||
788 | if (params->pes_type>DMX_PES_OTHER || params->pes_type<0) | |
789 | return -EINVAL; | |
790 | ||
791 | dmxdevfilter->type=DMXDEV_TYPE_PES; | |
792 | dmxdevfilter->pid=params->pid; | |
793 | memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params)); | |
794 | ||
795 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | |
796 | ||
797 | if (params->flags&DMX_IMMEDIATE_START) | |
798 | return dvb_dmxdev_filter_start(dmxdevfilter); | |
799 | ||
800 | return 0; | |
801 | } | |
802 | ||
803 | static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, | |
804 | struct file *file, char __user *buf, size_t count, loff_t *ppos) | |
805 | { | |
806 | int result, hcount; | |
807 | int done=0; | |
808 | ||
809 | if (dfil->todo<=0) { | |
810 | hcount=3+dfil->todo; | |
811 | if (hcount>count) | |
812 | hcount=count; | |
813 | result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK, | |
814 | buf, hcount, ppos); | |
815 | if (result<0) { | |
816 | dfil->todo=0; | |
817 | return result; | |
818 | } | |
819 | if (copy_from_user(dfil->secheader-dfil->todo, buf, result)) | |
820 | return -EFAULT; | |
821 | buf+=result; | |
822 | done=result; | |
823 | count-=result; | |
824 | dfil->todo-=result; | |
825 | if (dfil->todo>-3) | |
826 | return done; | |
827 | dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff; | |
828 | if (!count) | |
829 | return done; | |
830 | } | |
831 | if (count>dfil->todo) | |
832 | count=dfil->todo; | |
833 | result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK, | |
834 | buf, count, ppos); | |
835 | if (result<0) | |
836 | return result; | |
837 | dfil->todo-=result; | |
838 | return (result+done); | |
839 | } | |
840 | ||
841 | ||
842 | static ssize_t | |
843 | dvb_demux_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |
844 | { | |
845 | struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file); | |
846 | int ret=0; | |
847 | ||
848 | if (down_interruptible(&dmxdevfilter->mutex)) | |
849 | return -ERESTARTSYS; | |
850 | ||
851 | if (dmxdevfilter->type==DMXDEV_TYPE_SEC) | |
852 | ret=dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos); | |
853 | else | |
854 | ret=dvb_dmxdev_buffer_read(&dmxdevfilter->buffer, | |
855 | file->f_flags&O_NONBLOCK, | |
856 | buf, count, ppos); | |
857 | ||
858 | up(&dmxdevfilter->mutex); | |
859 | return ret; | |
860 | } | |
861 | ||
862 | ||
863 | static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, | |
864 | unsigned int cmd, void *parg) | |
865 | { | |
866 | struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file); | |
867 | struct dmxdev *dmxdev=dmxdevfilter->dev; | |
868 | unsigned long arg=(unsigned long) parg; | |
869 | int ret=0; | |
870 | ||
871 | if (down_interruptible (&dmxdev->mutex)) | |
872 | return -ERESTARTSYS; | |
873 | ||
874 | switch (cmd) { | |
875 | case DMX_START: | |
876 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
877 | up(&dmxdev->mutex); | |
878 | return -ERESTARTSYS; | |
879 | } | |
880 | if (dmxdevfilter->state<DMXDEV_STATE_SET) | |
881 | ret = -EINVAL; | |
882 | else | |
883 | ret = dvb_dmxdev_filter_start(dmxdevfilter); | |
884 | up(&dmxdevfilter->mutex); | |
885 | break; | |
886 | ||
887 | case DMX_STOP: | |
888 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
889 | up(&dmxdev->mutex); | |
890 | return -ERESTARTSYS; | |
891 | } | |
892 | ret=dvb_dmxdev_filter_stop(dmxdevfilter); | |
893 | up(&dmxdevfilter->mutex); | |
894 | break; | |
895 | ||
896 | case DMX_SET_FILTER: | |
897 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
898 | up(&dmxdev->mutex); | |
899 | return -ERESTARTSYS; | |
900 | } | |
901 | ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, | |
902 | (struct dmx_sct_filter_params *)parg); | |
903 | up(&dmxdevfilter->mutex); | |
904 | break; | |
905 | ||
906 | case DMX_SET_PES_FILTER: | |
907 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
908 | up(&dmxdev->mutex); | |
909 | return -ERESTARTSYS; | |
910 | } | |
911 | ret=dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, | |
912 | (struct dmx_pes_filter_params *)parg); | |
913 | up(&dmxdevfilter->mutex); | |
914 | break; | |
915 | ||
916 | case DMX_SET_BUFFER_SIZE: | |
917 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
918 | up(&dmxdev->mutex); | |
919 | return -ERESTARTSYS; | |
920 | } | |
921 | ret=dvb_dmxdev_set_buffer_size(dmxdevfilter, arg); | |
922 | up(&dmxdevfilter->mutex); | |
923 | break; | |
924 | ||
925 | case DMX_GET_EVENT: | |
926 | break; | |
927 | ||
928 | case DMX_GET_PES_PIDS: | |
929 | if (!dmxdev->demux->get_pes_pids) { | |
930 | ret=-EINVAL; | |
931 | break; | |
932 | } | |
933 | dmxdev->demux->get_pes_pids(dmxdev->demux, (u16 *)parg); | |
934 | break; | |
935 | ||
936 | case DMX_GET_STC: | |
937 | if (!dmxdev->demux->get_stc) { | |
938 | ret=-EINVAL; | |
939 | break; | |
940 | } | |
941 | ret = dmxdev->demux->get_stc(dmxdev->demux, | |
942 | ((struct dmx_stc *)parg)->num, | |
943 | &((struct dmx_stc *)parg)->stc, | |
944 | &((struct dmx_stc *)parg)->base); | |
945 | break; | |
946 | ||
947 | default: | |
948 | ret=-EINVAL; | |
949 | } | |
950 | up(&dmxdev->mutex); | |
951 | return ret; | |
952 | } | |
953 | ||
954 | static int dvb_demux_ioctl(struct inode *inode, struct file *file, | |
955 | unsigned int cmd, unsigned long arg) | |
956 | { | |
957 | return dvb_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl); | |
958 | } | |
959 | ||
960 | ||
961 | static unsigned int dvb_demux_poll (struct file *file, poll_table *wait) | |
962 | { | |
963 | struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file); | |
964 | unsigned int mask = 0; | |
965 | ||
966 | if (!dmxdevfilter) | |
967 | return -EINVAL; | |
968 | ||
969 | poll_wait(file, &dmxdevfilter->buffer.queue, wait); | |
970 | ||
971 | if (dmxdevfilter->state != DMXDEV_STATE_GO && | |
972 | dmxdevfilter->state != DMXDEV_STATE_DONE && | |
973 | dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT) | |
974 | return 0; | |
975 | ||
976 | if (dmxdevfilter->buffer.error) | |
977 | mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); | |
978 | ||
979 | if (dmxdevfilter->buffer.pread != dmxdevfilter->buffer.pwrite) | |
980 | mask |= (POLLIN | POLLRDNORM | POLLPRI); | |
981 | ||
982 | return mask; | |
983 | } | |
984 | ||
985 | ||
986 | static int dvb_demux_release(struct inode *inode, struct file *file) | |
987 | { | |
988 | struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file); | |
989 | struct dmxdev *dmxdev = dmxdevfilter->dev; | |
990 | ||
991 | return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); | |
992 | } | |
993 | ||
994 | ||
995 | static struct file_operations dvb_demux_fops = { | |
996 | .owner = THIS_MODULE, | |
997 | .read = dvb_demux_read, | |
998 | .ioctl = dvb_demux_ioctl, | |
999 | .open = dvb_demux_open, | |
1000 | .release = dvb_demux_release, | |
1001 | .poll = dvb_demux_poll, | |
1002 | }; | |
1003 | ||
1004 | ||
1005 | static struct dvb_device dvbdev_demux = { | |
1006 | .priv = NULL, | |
1007 | .users = 1, | |
1008 | .writers = 1, | |
1009 | .fops = &dvb_demux_fops | |
1010 | }; | |
1011 | ||
1012 | ||
1013 | static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file, | |
1014 | unsigned int cmd, void *parg) | |
1015 | { | |
1016 | struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; | |
1017 | struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv; | |
1018 | ||
1019 | int ret=0; | |
1020 | ||
1021 | if (down_interruptible (&dmxdev->mutex)) | |
1022 | return -ERESTARTSYS; | |
1023 | ||
1024 | switch (cmd) { | |
1025 | case DMX_SET_BUFFER_SIZE: | |
1026 | // FIXME: implement | |
1027 | ret=0; | |
1028 | break; | |
1029 | ||
1030 | default: | |
1031 | ret=-EINVAL; | |
1032 | } | |
1033 | up(&dmxdev->mutex); | |
1034 | return ret; | |
1035 | } | |
1036 | ||
1037 | ||
1038 | static int dvb_dvr_ioctl(struct inode *inode, struct file *file, | |
1039 | unsigned int cmd, unsigned long arg) | |
1040 | { | |
1041 | return dvb_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl); | |
1042 | } | |
1043 | ||
1044 | ||
1045 | static unsigned int dvb_dvr_poll (struct file *file, poll_table *wait) | |
1046 | { | |
1047 | struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; | |
1048 | struct dmxdev *dmxdev = (struct dmxdev *) dvbdev->priv; | |
1049 | unsigned int mask = 0; | |
1050 | ||
1051 | dprintk ("function : %s\n", __FUNCTION__); | |
1052 | ||
1053 | poll_wait(file, &dmxdev->dvr_buffer.queue, wait); | |
1054 | ||
1055 | if ((file->f_flags&O_ACCMODE) == O_RDONLY) { | |
1056 | if (dmxdev->dvr_buffer.error) | |
1057 | mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); | |
1058 | ||
1059 | if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite) | |
1060 | mask |= (POLLIN | POLLRDNORM | POLLPRI); | |
1061 | } else | |
1062 | mask |= (POLLOUT | POLLWRNORM | POLLPRI); | |
1063 | ||
1064 | return mask; | |
1065 | } | |
1066 | ||
1067 | ||
1068 | static struct file_operations dvb_dvr_fops = { | |
1069 | .owner = THIS_MODULE, | |
1070 | .read = dvb_dvr_read, | |
1071 | .write = dvb_dvr_write, | |
1072 | .ioctl = dvb_dvr_ioctl, | |
1073 | .open = dvb_dvr_open, | |
1074 | .release = dvb_dvr_release, | |
1075 | .poll = dvb_dvr_poll, | |
1076 | }; | |
1077 | ||
1078 | static struct dvb_device dvbdev_dvr = { | |
1079 | .priv = NULL, | |
1080 | .users = 1, | |
1081 | .writers = 1, | |
1082 | .fops = &dvb_dvr_fops | |
1083 | }; | |
1084 | ||
1085 | int | |
1086 | dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) | |
1087 | { | |
1088 | int i; | |
1089 | ||
1090 | if (dmxdev->demux->open(dmxdev->demux) < 0) | |
1091 | return -EUSERS; | |
1092 | ||
1093 | dmxdev->filter = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_filter)); | |
1094 | if (!dmxdev->filter) | |
1095 | return -ENOMEM; | |
1096 | ||
1097 | dmxdev->dvr = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_dvr)); | |
1098 | if (!dmxdev->dvr) { | |
1099 | vfree(dmxdev->filter); | |
1100 | dmxdev->filter = NULL; | |
1101 | return -ENOMEM; | |
1102 | } | |
1103 | ||
1104 | sema_init(&dmxdev->mutex, 1); | |
1105 | spin_lock_init(&dmxdev->lock); | |
1106 | for (i=0; i<dmxdev->filternum; i++) { | |
1107 | dmxdev->filter[i].dev=dmxdev; | |
1108 | dmxdev->filter[i].buffer.data=NULL; | |
1109 | dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE); | |
1110 | dmxdev->dvr[i].dev=dmxdev; | |
1111 | dmxdev->dvr[i].buffer.data=NULL; | |
1112 | dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE); | |
1113 | dvb_dmxdev_dvr_state_set(&dmxdev->dvr[i], DMXDEV_STATE_FREE); | |
1114 | } | |
1115 | ||
1116 | dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX); | |
1117 | dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR); | |
1118 | ||
1119 | dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer); | |
1120 | ||
1121 | return 0; | |
1122 | } | |
1123 | EXPORT_SYMBOL(dvb_dmxdev_init); | |
1124 | ||
1125 | void | |
1126 | dvb_dmxdev_release(struct dmxdev *dmxdev) | |
1127 | { | |
1128 | dvb_unregister_device(dmxdev->dvbdev); | |
1129 | dvb_unregister_device(dmxdev->dvr_dvbdev); | |
1130 | ||
1131 | vfree(dmxdev->filter); | |
1132 | dmxdev->filter=NULL; | |
1133 | vfree(dmxdev->dvr); | |
1134 | dmxdev->dvr=NULL; | |
1135 | dmxdev->demux->close(dmxdev->demux); | |
1136 | } | |
1137 | EXPORT_SYMBOL(dvb_dmxdev_release); |