e944cee19f0a8580c4b9487c36249f45a7559526
[linux-2.6-block.git] / drivers / media / dvb / firesat / firesat_dvb.c
1 /*
2  * FireDTV driver (formerly known as FireSAT)
3  *
4  * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
5  * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
6  *
7  *      This program is free software; you can redistribute it and/or
8  *      modify it under the terms of the GNU General Public License as
9  *      published by the Free Software Foundation; either version 2 of
10  *      the License, or (at your option) any later version.
11  */
12
13 #include <linux/errno.h>
14 #include <linux/kernel.h>
15 #include <linux/mutex.h>
16 #include <linux/types.h>
17
18 #include <dvb_demux.h>
19 #include <dvb_frontend.h>
20 #include <dvbdev.h>
21
22 #include "avc_api.h"
23 #include "firesat.h"
24 #include "firesat-ci.h"
25
26 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
27
28 static struct firesat_channel *firesat_channel_allocate(struct firesat *firesat)
29 {
30         struct firesat_channel *c = NULL;
31         int k;
32
33         if (mutex_lock_interruptible(&firesat->demux_mutex))
34                 return NULL;
35
36         for (k = 0; k < 16; k++)
37                 if (firesat->channel[k].active == 0) {
38                         firesat->channel[k].active = 1;
39                         c = &firesat->channel[k];
40                         break;
41                 }
42
43         mutex_unlock(&firesat->demux_mutex);
44         return c;
45 }
46
47 static int firesat_channel_collect(struct firesat *firesat, int *pidc, u16 pid[])
48 {
49         int k, l = 0;
50
51         if (mutex_lock_interruptible(&firesat->demux_mutex))
52                 return -EINTR;
53
54         for (k = 0; k < 16; k++)
55                 if (firesat->channel[k].active == 1)
56                         pid[l++] = firesat->channel[k].pid;
57
58         mutex_unlock(&firesat->demux_mutex);
59
60         *pidc = l;
61
62         return 0;
63 }
64
65 static int firesat_channel_release(struct firesat *firesat,
66                                    struct firesat_channel *channel)
67 {
68         if (mutex_lock_interruptible(&firesat->demux_mutex))
69                 return -EINTR;
70
71         channel->active = 0;
72
73         mutex_unlock(&firesat->demux_mutex);
74         return 0;
75 }
76
77 int firesat_start_feed(struct dvb_demux_feed *dvbdmxfeed)
78 {
79         struct firesat *firesat = (struct firesat*)dvbdmxfeed->demux->priv;
80         struct firesat_channel *channel;
81         int pidc,k;
82         u16 pids[16];
83
84 //      printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid);
85
86         switch (dvbdmxfeed->type) {
87         case DMX_TYPE_TS:
88         case DMX_TYPE_SEC:
89                 break;
90         default:
91                 printk(KERN_ERR "%s: invalid type %u\n",
92                        __func__, dvbdmxfeed->type);
93                 return -EINVAL;
94         }
95
96         if (dvbdmxfeed->type == DMX_TYPE_TS) {
97                 switch (dvbdmxfeed->pes_type) {
98                 case DMX_TS_PES_VIDEO:
99                 case DMX_TS_PES_AUDIO:
100                 case DMX_TS_PES_TELETEXT:
101                 case DMX_TS_PES_PCR:
102                 case DMX_TS_PES_OTHER:
103                         //Dirty fix to keep firesat->channel pid-list up to date
104                         for(k=0;k<16;k++){
105                                 if(firesat->channel[k].active == 0)
106                                         firesat->channel[k].pid =
107                                                 dvbdmxfeed->pid;
108                                         break;
109                         }
110                         channel = firesat_channel_allocate(firesat);
111                         break;
112                 default:
113                         printk(KERN_ERR "%s: invalid pes type %u\n",
114                                __func__, dvbdmxfeed->pes_type);
115                         return -EINVAL;
116                 }
117         } else {
118                 channel = firesat_channel_allocate(firesat);
119         }
120
121         if (!channel) {
122                 printk(KERN_ERR "%s: busy!\n", __func__);
123                 return -EBUSY;
124         }
125
126         dvbdmxfeed->priv = channel;
127
128         channel->dvbdmxfeed = dvbdmxfeed;
129         channel->pid = dvbdmxfeed->pid;
130         channel->type = dvbdmxfeed->type;
131         channel->firesat = firesat;
132
133         if (firesat_channel_collect(firesat, &pidc, pids)) {
134                 firesat_channel_release(firesat, channel);
135                 printk(KERN_ERR "%s: could not collect pids!\n", __func__);
136                 return -EINTR;
137         }
138
139         if(dvbdmxfeed->pid == 8192) {
140                 if((k = AVCTuner_GetTS(firesat))) {
141                         firesat_channel_release(firesat, channel);
142                         printk("%s: AVCTuner_GetTS failed with error %d\n",
143                                __func__, k);
144                         return k;
145                 }
146         }
147         else {
148                 if((k = AVCTuner_SetPIDs(firesat, pidc, pids))) {
149                         firesat_channel_release(firesat, channel);
150                         printk("%s: AVCTuner_SetPIDs failed with error %d\n",
151                                __func__, k);
152                         return k;
153                 }
154         }
155
156         return 0;
157 }
158
159 int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
160 {
161         struct dvb_demux *demux = dvbdmxfeed->demux;
162         struct firesat *firesat = (struct firesat*)demux->priv;
163         struct firesat_channel *c = dvbdmxfeed->priv;
164         int k, l;
165         u16 pids[16];
166
167         //printk(KERN_INFO "%s (pid %u)\n", __func__, dvbdmxfeed->pid);
168
169         if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) &&
170                                 (demux->dmx.frontend->source != DMX_MEMORY_FE))) {
171
172                 if (dvbdmxfeed->ts_type & TS_DECODER) {
173
174                         if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER ||
175                                 !demux->pesfilter[dvbdmxfeed->pes_type])
176
177                                 return -EINVAL;
178
179                         demux->pids[dvbdmxfeed->pes_type] |= 0x8000;
180                         demux->pesfilter[dvbdmxfeed->pes_type] = 0;
181                 }
182
183                 if (!(dvbdmxfeed->ts_type & TS_DECODER &&
184                         dvbdmxfeed->pes_type < DMX_TS_PES_OTHER))
185
186                         return 0;
187         }
188
189         if (mutex_lock_interruptible(&firesat->demux_mutex))
190                 return -EINTR;
191
192         /* list except channel to be removed */
193         for (k = 0, l = 0; k < 16; k++)
194                 if (firesat->channel[k].active == 1) {
195                         if (&firesat->channel[k] != c)
196                                 pids[l++] = firesat->channel[k].pid;
197                         else
198                                 firesat->channel[k].active = 0;
199                 }
200
201         k = AVCTuner_SetPIDs(firesat, l, pids);
202         if (!k)
203                 c->active = 0;
204
205         mutex_unlock(&firesat->demux_mutex);
206         return k;
207 }
208
209 int firesat_dvbdev_init(struct firesat *firesat,
210                         struct device *dev,
211                         struct dvb_frontend *fe)
212 {
213         int result;
214
215         firesat->adapter = kmalloc(sizeof(*firesat->adapter), GFP_KERNEL);
216         if (!firesat->adapter) {
217                 printk(KERN_ERR "firedtv: couldn't allocate memory\n");
218                 return -ENOMEM;
219         }
220
221         result = DVB_REGISTER_ADAPTER(firesat->adapter,
222                                       firedtv_model_names[firesat->type],
223                                       THIS_MODULE, dev, adapter_nr);
224         if (result < 0) {
225                 printk(KERN_ERR "firedtv: dvb_register_adapter failed\n");
226                 kfree(firesat->adapter);
227                 return result;
228         }
229
230                 memset(&firesat->demux, 0, sizeof(struct dvb_demux));
231                 firesat->demux.dmx.capabilities = 0/*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/;
232
233                 firesat->demux.priv             = (void *)firesat;
234                 firesat->demux.filternum        = 16;
235                 firesat->demux.feednum          = 16;
236                 firesat->demux.start_feed       = firesat_start_feed;
237                 firesat->demux.stop_feed        = firesat_stop_feed;
238                 firesat->demux.write_to_decoder = NULL;
239
240                 if ((result = dvb_dmx_init(&firesat->demux)) < 0) {
241                         printk("%s: dvb_dmx_init failed: error %d\n", __func__,
242                                    result);
243
244                         dvb_unregister_adapter(firesat->adapter);
245
246                         return result;
247                 }
248
249                 firesat->dmxdev.filternum       = 16;
250                 firesat->dmxdev.demux           = &firesat->demux.dmx;
251                 firesat->dmxdev.capabilities    = 0;
252
253                 if ((result = dvb_dmxdev_init(&firesat->dmxdev, firesat->adapter)) < 0) {
254                         printk("%s: dvb_dmxdev_init failed: error %d\n",
255                                    __func__, result);
256
257                         dvb_dmx_release(&firesat->demux);
258                         dvb_unregister_adapter(firesat->adapter);
259
260                         return result;
261                 }
262
263                 firesat->frontend.source = DMX_FRONTEND_0;
264
265                 if ((result = firesat->demux.dmx.add_frontend(&firesat->demux.dmx,
266                                                           &firesat->frontend)) < 0) {
267                         printk("%s: dvb_dmx_init failed: error %d\n", __func__,
268                                    result);
269
270                         dvb_dmxdev_release(&firesat->dmxdev);
271                         dvb_dmx_release(&firesat->demux);
272                         dvb_unregister_adapter(firesat->adapter);
273
274                         return result;
275                 }
276
277                 if ((result = firesat->demux.dmx.connect_frontend(&firesat->demux.dmx,
278                                                                   &firesat->frontend)) < 0) {
279                         printk("%s: dvb_dmx_init failed: error %d\n", __func__,
280                                    result);
281
282                         firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, &firesat->frontend);
283                         dvb_dmxdev_release(&firesat->dmxdev);
284                         dvb_dmx_release(&firesat->demux);
285                         dvb_unregister_adapter(firesat->adapter);
286
287                         return result;
288                 }
289
290                 dvb_net_init(firesat->adapter, &firesat->dvbnet, &firesat->demux.dmx);
291
292 //              fe->ops = firesat_ops;
293 //              fe->dvb = firesat->adapter;
294                 firesat_frontend_attach(firesat, fe);
295
296                 fe->sec_priv = firesat; //IMPORTANT, functions depend on this!!!
297                 if ((result= dvb_register_frontend(firesat->adapter, fe)) < 0) {
298                         printk("%s: dvb_register_frontend_new failed: error %d\n", __func__, result);
299                         /* ### cleanup */
300                         return result;
301                 }
302
303                         firesat_ca_init(firesat);
304
305                 return 0;
306 }
307
308