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 | { | |
0c53c70f JS |
178 | struct dvb_device *dvbdev = file->private_data; |
179 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
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 | { | |
0c53c70f JS |
227 | struct dvb_device *dvbdev = file->private_data; |
228 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
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 | { | |
0c53c70f JS |
255 | struct dvb_device *dvbdev = file->private_data; |
256 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
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 | { | |
0c53c70f JS |
273 | struct dvb_device *dvbdev = file->private_data; |
274 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
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 | { | |
0c53c70f | 348 | struct dmxdev_filter *dmxdevfilter = filter->priv; |
1da177e4 LT |
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 | { | |
0c53c70f | 384 | struct dmxdev_filter *dmxdevfilter = feed->priv; |
1da177e4 LT |
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 | ||
76197924 PB |
672 | if (ret < 0) { |
673 | dmxdev->demux->release_ts_feed(dmxdev->demux, *tsfeed); | |
1da177e4 | 674 | return ret; |
76197924 | 675 | } |
1da177e4 LT |
676 | |
677 | break; | |
678 | } | |
679 | default: | |
680 | return -EINVAL; | |
681 | } | |
682 | ||
683 | dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); | |
684 | return 0; | |
685 | } | |
686 | ||
687 | static int dvb_demux_open(struct inode *inode, struct file *file) | |
688 | { | |
0c53c70f JS |
689 | struct dvb_device *dvbdev = file->private_data; |
690 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
691 | int i; |
692 | struct dmxdev_filter *dmxdevfilter; | |
693 | ||
694 | if (!dmxdev->filter) | |
695 | return -EINVAL; | |
696 | ||
697 | if (down_interruptible(&dmxdev->mutex)) | |
698 | return -ERESTARTSYS; | |
699 | ||
700 | for (i=0; i<dmxdev->filternum; i++) | |
701 | if (dmxdev->filter[i].state==DMXDEV_STATE_FREE) | |
702 | break; | |
703 | ||
704 | if (i==dmxdev->filternum) { | |
705 | up(&dmxdev->mutex); | |
706 | return -EMFILE; | |
707 | } | |
708 | ||
709 | dmxdevfilter=&dmxdev->filter[i]; | |
710 | sema_init(&dmxdevfilter->mutex, 1); | |
711 | dmxdevfilter->dvbdev=dmxdev->dvbdev; | |
712 | file->private_data=dmxdevfilter; | |
713 | ||
714 | dvb_dmxdev_buffer_init(&dmxdevfilter->buffer); | |
715 | dmxdevfilter->type=DMXDEV_TYPE_NONE; | |
716 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); | |
717 | dmxdevfilter->feed.ts=NULL; | |
718 | init_timer(&dmxdevfilter->timer); | |
719 | ||
720 | up(&dmxdev->mutex); | |
721 | return 0; | |
722 | } | |
723 | ||
724 | ||
725 | static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter) | |
726 | { | |
727 | if (down_interruptible(&dmxdev->mutex)) | |
728 | return -ERESTARTSYS; | |
729 | ||
730 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
731 | up(&dmxdev->mutex); | |
732 | return -ERESTARTSYS; | |
733 | } | |
734 | ||
735 | dvb_dmxdev_filter_stop(dmxdevfilter); | |
736 | dvb_dmxdev_filter_reset(dmxdevfilter); | |
737 | ||
738 | if (dmxdevfilter->buffer.data) { | |
739 | void *mem=dmxdevfilter->buffer.data; | |
740 | ||
741 | spin_lock_irq(&dmxdev->lock); | |
742 | dmxdevfilter->buffer.data=NULL; | |
743 | spin_unlock_irq(&dmxdev->lock); | |
744 | vfree(mem); | |
745 | } | |
746 | ||
747 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE); | |
748 | wake_up(&dmxdevfilter->buffer.queue); | |
749 | up(&dmxdevfilter->mutex); | |
750 | up(&dmxdev->mutex); | |
751 | return 0; | |
752 | } | |
753 | ||
754 | static inline void invert_mode(dmx_filter_t *filter) | |
755 | { | |
756 | int i; | |
757 | ||
758 | for (i=0; i<DMX_FILTER_SIZE; i++) | |
759 | filter->mode[i]^=0xff; | |
760 | } | |
761 | ||
762 | ||
763 | static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, | |
764 | struct dmxdev_filter *dmxdevfilter, | |
765 | struct dmx_sct_filter_params *params) | |
766 | { | |
767 | dprintk ("function : %s\n", __FUNCTION__); | |
768 | ||
769 | dvb_dmxdev_filter_stop(dmxdevfilter); | |
770 | ||
771 | dmxdevfilter->type=DMXDEV_TYPE_SEC; | |
772 | dmxdevfilter->pid=params->pid; | |
773 | memcpy(&dmxdevfilter->params.sec, | |
774 | params, sizeof(struct dmx_sct_filter_params)); | |
775 | invert_mode(&dmxdevfilter->params.sec.filter); | |
776 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | |
777 | ||
778 | if (params->flags&DMX_IMMEDIATE_START) | |
779 | return dvb_dmxdev_filter_start(dmxdevfilter); | |
780 | ||
781 | return 0; | |
782 | } | |
783 | ||
784 | static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, | |
785 | struct dmxdev_filter *dmxdevfilter, | |
786 | struct dmx_pes_filter_params *params) | |
787 | { | |
788 | dvb_dmxdev_filter_stop(dmxdevfilter); | |
789 | ||
790 | if (params->pes_type>DMX_PES_OTHER || params->pes_type<0) | |
791 | return -EINVAL; | |
792 | ||
793 | dmxdevfilter->type=DMXDEV_TYPE_PES; | |
794 | dmxdevfilter->pid=params->pid; | |
795 | memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params)); | |
796 | ||
797 | dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); | |
798 | ||
799 | if (params->flags&DMX_IMMEDIATE_START) | |
800 | return dvb_dmxdev_filter_start(dmxdevfilter); | |
801 | ||
802 | return 0; | |
803 | } | |
804 | ||
805 | static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, | |
806 | struct file *file, char __user *buf, size_t count, loff_t *ppos) | |
807 | { | |
808 | int result, hcount; | |
809 | int done=0; | |
810 | ||
811 | if (dfil->todo<=0) { | |
812 | hcount=3+dfil->todo; | |
813 | if (hcount>count) | |
814 | hcount=count; | |
815 | result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK, | |
816 | buf, hcount, ppos); | |
817 | if (result<0) { | |
818 | dfil->todo=0; | |
819 | return result; | |
820 | } | |
821 | if (copy_from_user(dfil->secheader-dfil->todo, buf, result)) | |
822 | return -EFAULT; | |
823 | buf+=result; | |
824 | done=result; | |
825 | count-=result; | |
826 | dfil->todo-=result; | |
827 | if (dfil->todo>-3) | |
828 | return done; | |
829 | dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff; | |
830 | if (!count) | |
831 | return done; | |
832 | } | |
833 | if (count>dfil->todo) | |
834 | count=dfil->todo; | |
835 | result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK, | |
836 | buf, count, ppos); | |
837 | if (result<0) | |
838 | return result; | |
839 | dfil->todo-=result; | |
840 | return (result+done); | |
841 | } | |
842 | ||
843 | ||
844 | static ssize_t | |
845 | dvb_demux_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |
846 | { | |
847 | struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file); | |
848 | int ret=0; | |
849 | ||
850 | if (down_interruptible(&dmxdevfilter->mutex)) | |
851 | return -ERESTARTSYS; | |
852 | ||
853 | if (dmxdevfilter->type==DMXDEV_TYPE_SEC) | |
854 | ret=dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos); | |
855 | else | |
856 | ret=dvb_dmxdev_buffer_read(&dmxdevfilter->buffer, | |
857 | file->f_flags&O_NONBLOCK, | |
858 | buf, count, ppos); | |
859 | ||
860 | up(&dmxdevfilter->mutex); | |
861 | return ret; | |
862 | } | |
863 | ||
864 | ||
865 | static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, | |
866 | unsigned int cmd, void *parg) | |
867 | { | |
868 | struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file); | |
869 | struct dmxdev *dmxdev=dmxdevfilter->dev; | |
870 | unsigned long arg=(unsigned long) parg; | |
871 | int ret=0; | |
872 | ||
873 | if (down_interruptible (&dmxdev->mutex)) | |
874 | return -ERESTARTSYS; | |
875 | ||
876 | switch (cmd) { | |
877 | case DMX_START: | |
878 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
879 | up(&dmxdev->mutex); | |
880 | return -ERESTARTSYS; | |
881 | } | |
882 | if (dmxdevfilter->state<DMXDEV_STATE_SET) | |
883 | ret = -EINVAL; | |
884 | else | |
885 | ret = dvb_dmxdev_filter_start(dmxdevfilter); | |
886 | up(&dmxdevfilter->mutex); | |
887 | break; | |
888 | ||
889 | case DMX_STOP: | |
890 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
891 | up(&dmxdev->mutex); | |
892 | return -ERESTARTSYS; | |
893 | } | |
894 | ret=dvb_dmxdev_filter_stop(dmxdevfilter); | |
895 | up(&dmxdevfilter->mutex); | |
896 | break; | |
897 | ||
898 | case DMX_SET_FILTER: | |
899 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
900 | up(&dmxdev->mutex); | |
901 | return -ERESTARTSYS; | |
902 | } | |
903 | ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, | |
904 | (struct dmx_sct_filter_params *)parg); | |
905 | up(&dmxdevfilter->mutex); | |
906 | break; | |
907 | ||
908 | case DMX_SET_PES_FILTER: | |
909 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
910 | up(&dmxdev->mutex); | |
911 | return -ERESTARTSYS; | |
912 | } | |
913 | ret=dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, | |
914 | (struct dmx_pes_filter_params *)parg); | |
915 | up(&dmxdevfilter->mutex); | |
916 | break; | |
917 | ||
918 | case DMX_SET_BUFFER_SIZE: | |
919 | if (down_interruptible(&dmxdevfilter->mutex)) { | |
920 | up(&dmxdev->mutex); | |
921 | return -ERESTARTSYS; | |
922 | } | |
923 | ret=dvb_dmxdev_set_buffer_size(dmxdevfilter, arg); | |
924 | up(&dmxdevfilter->mutex); | |
925 | break; | |
926 | ||
927 | case DMX_GET_EVENT: | |
928 | break; | |
929 | ||
930 | case DMX_GET_PES_PIDS: | |
931 | if (!dmxdev->demux->get_pes_pids) { | |
932 | ret=-EINVAL; | |
933 | break; | |
934 | } | |
935 | dmxdev->demux->get_pes_pids(dmxdev->demux, (u16 *)parg); | |
936 | break; | |
937 | ||
938 | case DMX_GET_STC: | |
939 | if (!dmxdev->demux->get_stc) { | |
940 | ret=-EINVAL; | |
941 | break; | |
942 | } | |
943 | ret = dmxdev->demux->get_stc(dmxdev->demux, | |
944 | ((struct dmx_stc *)parg)->num, | |
945 | &((struct dmx_stc *)parg)->stc, | |
946 | &((struct dmx_stc *)parg)->base); | |
947 | break; | |
948 | ||
949 | default: | |
950 | ret=-EINVAL; | |
951 | } | |
952 | up(&dmxdev->mutex); | |
953 | return ret; | |
954 | } | |
955 | ||
956 | static int dvb_demux_ioctl(struct inode *inode, struct file *file, | |
957 | unsigned int cmd, unsigned long arg) | |
958 | { | |
959 | return dvb_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl); | |
960 | } | |
961 | ||
962 | ||
963 | static unsigned int dvb_demux_poll (struct file *file, poll_table *wait) | |
964 | { | |
965 | struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file); | |
966 | unsigned int mask = 0; | |
967 | ||
968 | if (!dmxdevfilter) | |
969 | return -EINVAL; | |
970 | ||
971 | poll_wait(file, &dmxdevfilter->buffer.queue, wait); | |
972 | ||
973 | if (dmxdevfilter->state != DMXDEV_STATE_GO && | |
974 | dmxdevfilter->state != DMXDEV_STATE_DONE && | |
975 | dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT) | |
976 | return 0; | |
977 | ||
978 | if (dmxdevfilter->buffer.error) | |
979 | mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); | |
980 | ||
981 | if (dmxdevfilter->buffer.pread != dmxdevfilter->buffer.pwrite) | |
982 | mask |= (POLLIN | POLLRDNORM | POLLPRI); | |
983 | ||
984 | return mask; | |
985 | } | |
986 | ||
987 | ||
988 | static int dvb_demux_release(struct inode *inode, struct file *file) | |
989 | { | |
990 | struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file); | |
991 | struct dmxdev *dmxdev = dmxdevfilter->dev; | |
992 | ||
993 | return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); | |
994 | } | |
995 | ||
996 | ||
997 | static struct file_operations dvb_demux_fops = { | |
998 | .owner = THIS_MODULE, | |
999 | .read = dvb_demux_read, | |
1000 | .ioctl = dvb_demux_ioctl, | |
1001 | .open = dvb_demux_open, | |
1002 | .release = dvb_demux_release, | |
1003 | .poll = dvb_demux_poll, | |
1004 | }; | |
1005 | ||
1006 | ||
1007 | static struct dvb_device dvbdev_demux = { | |
1008 | .priv = NULL, | |
1009 | .users = 1, | |
1010 | .writers = 1, | |
1011 | .fops = &dvb_demux_fops | |
1012 | }; | |
1013 | ||
1014 | ||
1015 | static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file, | |
1016 | unsigned int cmd, void *parg) | |
1017 | { | |
0c53c70f JS |
1018 | struct dvb_device *dvbdev = file->private_data; |
1019 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
1020 | |
1021 | int ret=0; | |
1022 | ||
1023 | if (down_interruptible (&dmxdev->mutex)) | |
1024 | return -ERESTARTSYS; | |
1025 | ||
1026 | switch (cmd) { | |
1027 | case DMX_SET_BUFFER_SIZE: | |
1028 | // FIXME: implement | |
1029 | ret=0; | |
1030 | break; | |
1031 | ||
1032 | default: | |
1033 | ret=-EINVAL; | |
1034 | } | |
1035 | up(&dmxdev->mutex); | |
1036 | return ret; | |
1037 | } | |
1038 | ||
1039 | ||
1040 | static int dvb_dvr_ioctl(struct inode *inode, struct file *file, | |
1041 | unsigned int cmd, unsigned long arg) | |
1042 | { | |
1043 | return dvb_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl); | |
1044 | } | |
1045 | ||
1046 | ||
1047 | static unsigned int dvb_dvr_poll (struct file *file, poll_table *wait) | |
1048 | { | |
0c53c70f JS |
1049 | struct dvb_device *dvbdev = file->private_data; |
1050 | struct dmxdev *dmxdev = dvbdev->priv; | |
1da177e4 LT |
1051 | unsigned int mask = 0; |
1052 | ||
1053 | dprintk ("function : %s\n", __FUNCTION__); | |
1054 | ||
1055 | poll_wait(file, &dmxdev->dvr_buffer.queue, wait); | |
1056 | ||
1057 | if ((file->f_flags&O_ACCMODE) == O_RDONLY) { | |
1058 | if (dmxdev->dvr_buffer.error) | |
1059 | mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); | |
1060 | ||
1061 | if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite) | |
1062 | mask |= (POLLIN | POLLRDNORM | POLLPRI); | |
1063 | } else | |
1064 | mask |= (POLLOUT | POLLWRNORM | POLLPRI); | |
1065 | ||
1066 | return mask; | |
1067 | } | |
1068 | ||
1069 | ||
1070 | static struct file_operations dvb_dvr_fops = { | |
1071 | .owner = THIS_MODULE, | |
1072 | .read = dvb_dvr_read, | |
1073 | .write = dvb_dvr_write, | |
1074 | .ioctl = dvb_dvr_ioctl, | |
1075 | .open = dvb_dvr_open, | |
1076 | .release = dvb_dvr_release, | |
1077 | .poll = dvb_dvr_poll, | |
1078 | }; | |
1079 | ||
1080 | static struct dvb_device dvbdev_dvr = { | |
1081 | .priv = NULL, | |
1082 | .users = 1, | |
1083 | .writers = 1, | |
1084 | .fops = &dvb_dvr_fops | |
1085 | }; | |
1086 | ||
1087 | int | |
1088 | dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) | |
1089 | { | |
1090 | int i; | |
1091 | ||
1092 | if (dmxdev->demux->open(dmxdev->demux) < 0) | |
1093 | return -EUSERS; | |
1094 | ||
1095 | dmxdev->filter = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_filter)); | |
1096 | if (!dmxdev->filter) | |
1097 | return -ENOMEM; | |
1098 | ||
1099 | dmxdev->dvr = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_dvr)); | |
1100 | if (!dmxdev->dvr) { | |
1101 | vfree(dmxdev->filter); | |
1102 | dmxdev->filter = NULL; | |
1103 | return -ENOMEM; | |
1104 | } | |
1105 | ||
1106 | sema_init(&dmxdev->mutex, 1); | |
1107 | spin_lock_init(&dmxdev->lock); | |
1108 | for (i=0; i<dmxdev->filternum; i++) { | |
1109 | dmxdev->filter[i].dev=dmxdev; | |
1110 | dmxdev->filter[i].buffer.data=NULL; | |
1111 | dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE); | |
1112 | dmxdev->dvr[i].dev=dmxdev; | |
1113 | dmxdev->dvr[i].buffer.data=NULL; | |
1114 | dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE); | |
1115 | dvb_dmxdev_dvr_state_set(&dmxdev->dvr[i], DMXDEV_STATE_FREE); | |
1116 | } | |
1117 | ||
1118 | dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX); | |
1119 | dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR); | |
1120 | ||
1121 | dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer); | |
1122 | ||
1123 | return 0; | |
1124 | } | |
1125 | EXPORT_SYMBOL(dvb_dmxdev_init); | |
1126 | ||
1127 | void | |
1128 | dvb_dmxdev_release(struct dmxdev *dmxdev) | |
1129 | { | |
1130 | dvb_unregister_device(dmxdev->dvbdev); | |
1131 | dvb_unregister_device(dmxdev->dvr_dvbdev); | |
1132 | ||
1133 | vfree(dmxdev->filter); | |
1134 | dmxdev->filter=NULL; | |
1135 | vfree(dmxdev->dvr); | |
1136 | dmxdev->dvr=NULL; | |
1137 | dmxdev->demux->close(dmxdev->demux); | |
1138 | } | |
1139 | EXPORT_SYMBOL(dvb_dmxdev_release); |