firedtv: Use DEFINE_SPINLOCK
[pandora-kernel.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) {
38                         firesat->channel[k].active = true;
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)
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 = false;
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         switch (dvbdmxfeed->type) {
85         case DMX_TYPE_TS:
86         case DMX_TYPE_SEC:
87                 break;
88         default:
89                 printk(KERN_ERR "%s: invalid type %u\n",
90                        __func__, dvbdmxfeed->type);
91                 return -EINVAL;
92         }
93
94         if (dvbdmxfeed->type == DMX_TYPE_TS) {
95                 switch (dvbdmxfeed->pes_type) {
96                 case DMX_TS_PES_VIDEO:
97                 case DMX_TS_PES_AUDIO:
98                 case DMX_TS_PES_TELETEXT:
99                 case DMX_TS_PES_PCR:
100                 case DMX_TS_PES_OTHER:
101                         //Dirty fix to keep firesat->channel pid-list up to date
102                         for(k=0;k<16;k++){
103                                 if (!firesat->channel[k].active)
104                                         firesat->channel[k].pid =
105                                                 dvbdmxfeed->pid;
106                                         break;
107                         }
108                         channel = firesat_channel_allocate(firesat);
109                         break;
110                 default:
111                         printk(KERN_ERR "%s: invalid pes type %u\n",
112                                __func__, dvbdmxfeed->pes_type);
113                         return -EINVAL;
114                 }
115         } else {
116                 channel = firesat_channel_allocate(firesat);
117         }
118
119         if (!channel) {
120                 printk(KERN_ERR "%s: busy!\n", __func__);
121                 return -EBUSY;
122         }
123
124         dvbdmxfeed->priv = channel;
125         channel->pid = dvbdmxfeed->pid;
126
127         if (firesat_channel_collect(firesat, &pidc, pids)) {
128                 firesat_channel_release(firesat, channel);
129                 printk(KERN_ERR "%s: could not collect pids!\n", __func__);
130                 return -EINTR;
131         }
132
133         if (dvbdmxfeed->pid == 8192) {
134                 k = avc_tuner_get_ts(firesat);
135                 if (k) {
136                         firesat_channel_release(firesat, channel);
137                         printk("%s: AVCTuner_GetTS failed with error %d\n",
138                                __func__, k);
139                         return k;
140                 }
141         } else {
142                 k = avc_tuner_set_pids(firesat, pidc, pids);
143                 if (k) {
144                         firesat_channel_release(firesat, channel);
145                         printk("%s: AVCTuner_SetPIDs failed with error %d\n",
146                                __func__, k);
147                         return k;
148                 }
149         }
150
151         return 0;
152 }
153
154 int firesat_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
155 {
156         struct dvb_demux *demux = dvbdmxfeed->demux;
157         struct firesat *firesat = (struct firesat*)demux->priv;
158         struct firesat_channel *c = dvbdmxfeed->priv;
159         int k, l;
160         u16 pids[16];
161
162         if (dvbdmxfeed->type == DMX_TYPE_TS && !((dvbdmxfeed->ts_type & TS_PACKET) &&
163                                 (demux->dmx.frontend->source != DMX_MEMORY_FE))) {
164
165                 if (dvbdmxfeed->ts_type & TS_DECODER) {
166
167                         if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER ||
168                                 !demux->pesfilter[dvbdmxfeed->pes_type])
169
170                                 return -EINVAL;
171
172                         demux->pids[dvbdmxfeed->pes_type] |= 0x8000;
173                         demux->pesfilter[dvbdmxfeed->pes_type] = NULL;
174                 }
175
176                 if (!(dvbdmxfeed->ts_type & TS_DECODER &&
177                         dvbdmxfeed->pes_type < DMX_TS_PES_OTHER))
178
179                         return 0;
180         }
181
182         if (mutex_lock_interruptible(&firesat->demux_mutex))
183                 return -EINTR;
184
185         /* list except channel to be removed */
186         for (k = 0, l = 0; k < 16; k++)
187                 if (firesat->channel[k].active) {
188                         if (&firesat->channel[k] != c)
189                                 pids[l++] = firesat->channel[k].pid;
190                         else
191                                 firesat->channel[k].active = false;
192                 }
193
194         k = avc_tuner_set_pids(firesat, l, pids);
195         if (!k)
196                 c->active = false;
197
198         mutex_unlock(&firesat->demux_mutex);
199         return k;
200 }
201
202 int firesat_dvbdev_init(struct firesat *firesat, struct device *dev)
203 {
204         int err;
205
206         err = DVB_REGISTER_ADAPTER(&firesat->adapter,
207                                    firedtv_model_names[firesat->type],
208                                    THIS_MODULE, dev, adapter_nr);
209         if (err < 0)
210                 goto fail_log;
211
212         /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/
213         firesat->demux.dmx.capabilities = 0;
214
215         firesat->demux.priv             = (void *)firesat;
216         firesat->demux.filternum        = 16;
217         firesat->demux.feednum          = 16;
218         firesat->demux.start_feed       = firesat_start_feed;
219         firesat->demux.stop_feed        = firesat_stop_feed;
220         firesat->demux.write_to_decoder = NULL;
221
222         err = dvb_dmx_init(&firesat->demux);
223         if (err)
224                 goto fail_unreg_adapter;
225
226         firesat->dmxdev.filternum       = 16;
227         firesat->dmxdev.demux           = &firesat->demux.dmx;
228         firesat->dmxdev.capabilities    = 0;
229
230         err = dvb_dmxdev_init(&firesat->dmxdev, &firesat->adapter);
231         if (err)
232                 goto fail_dmx_release;
233
234         firesat->frontend.source = DMX_FRONTEND_0;
235
236         err = firesat->demux.dmx.add_frontend(&firesat->demux.dmx,
237                                               &firesat->frontend);
238         if (err)
239                 goto fail_dmxdev_release;
240
241         err = firesat->demux.dmx.connect_frontend(&firesat->demux.dmx,
242                                                   &firesat->frontend);
243         if (err)
244                 goto fail_rem_frontend;
245
246         dvb_net_init(&firesat->adapter, &firesat->dvbnet, &firesat->demux.dmx);
247
248         firesat_frontend_init(firesat);
249         err = dvb_register_frontend(&firesat->adapter, &firesat->fe);
250         if (err)
251                 goto fail_net_release;
252
253         err = firesat_ca_register(firesat);
254         if (err)
255                 dev_info(dev, "Conditional Access Module not enabled\n");
256
257         return 0;
258
259 fail_net_release:
260         dvb_net_release(&firesat->dvbnet);
261         firesat->demux.dmx.close(&firesat->demux.dmx);
262 fail_rem_frontend:
263         firesat->demux.dmx.remove_frontend(&firesat->demux.dmx,
264                                            &firesat->frontend);
265 fail_dmxdev_release:
266         dvb_dmxdev_release(&firesat->dmxdev);
267 fail_dmx_release:
268         dvb_dmx_release(&firesat->demux);
269 fail_unreg_adapter:
270         dvb_unregister_adapter(&firesat->adapter);
271 fail_log:
272         dev_err(dev, "DVB initialization failed\n");
273         return err;
274 }
275
276