]>
Commit | Line | Data |
---|---|---|
df4846c3 | 1 | /* |
612262a5 | 2 | * FireDTV driver (formerly known as FireSAT) |
df4846c3 | 3 | * |
612262a5 SR |
4 | * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> |
5 | * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> | |
df4846c3 HK |
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 | ||
15490795 SR |
13 | #include <linux/bitops.h> |
14 | #include <linux/device.h> | |
c81c8b68 | 15 | #include <linux/errno.h> |
612262a5 | 16 | #include <linux/kernel.h> |
15490795 SR |
17 | #include <linux/mod_devicetable.h> |
18 | #include <linux/module.h> | |
612262a5 | 19 | #include <linux/mutex.h> |
15490795 SR |
20 | #include <linux/slab.h> |
21 | #include <linux/string.h> | |
612262a5 | 22 | #include <linux/types.h> |
15490795 SR |
23 | #include <linux/wait.h> |
24 | #include <linux/workqueue.h> | |
612262a5 | 25 | |
15490795 | 26 | #include <dmxdev.h> |
612262a5 | 27 | #include <dvb_demux.h> |
c81c8b68 | 28 | #include <dvbdev.h> |
15490795 | 29 | #include <dvb_frontend.h> |
c81c8b68 | 30 | |
a70f81c1 | 31 | #include "firedtv.h" |
c81c8b68 | 32 | |
15490795 | 33 | static int alloc_channel(struct firedtv *fdtv) |
c81c8b68 | 34 | { |
15490795 | 35 | int i; |
c81c8b68 | 36 | |
15490795 SR |
37 | for (i = 0; i < 16; i++) |
38 | if (!__test_and_set_bit(i, &fdtv->channel_active)) | |
612262a5 | 39 | break; |
15490795 | 40 | return i; |
c81c8b68 GKH |
41 | } |
42 | ||
15490795 | 43 | static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) |
c81c8b68 | 44 | { |
15490795 | 45 | int i, n; |
c81c8b68 | 46 | |
15490795 SR |
47 | for (i = 0, n = 0; i < 16; i++) |
48 | if (test_bit(i, &fdtv->channel_active)) | |
49 | pid[n++] = fdtv->channel_pid[i]; | |
50 | *pidc = n; | |
c81c8b68 GKH |
51 | } |
52 | ||
15490795 | 53 | static inline void dealloc_channel(struct firedtv *fdtv, int i) |
c81c8b68 | 54 | { |
15490795 | 55 | __clear_bit(i, &fdtv->channel_active); |
c81c8b68 GKH |
56 | } |
57 | ||
a70f81c1 | 58 | int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) |
c81c8b68 | 59 | { |
15490795 SR |
60 | struct firedtv *fdtv = dvbdmxfeed->demux->priv; |
61 | int pidc, c, ret; | |
c81c8b68 GKH |
62 | u16 pids[16]; |
63 | ||
c81c8b68 GKH |
64 | switch (dvbdmxfeed->type) { |
65 | case DMX_TYPE_TS: | |
66 | case DMX_TYPE_SEC: | |
67 | break; | |
68 | default: | |
15490795 SR |
69 | dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", |
70 | dvbdmxfeed->type); | |
c81c8b68 GKH |
71 | return -EINVAL; |
72 | } | |
73 | ||
15490795 SR |
74 | if (mutex_lock_interruptible(&fdtv->demux_mutex)) |
75 | return -EINTR; | |
76 | ||
c81c8b68 GKH |
77 | if (dvbdmxfeed->type == DMX_TYPE_TS) { |
78 | switch (dvbdmxfeed->pes_type) { | |
79 | case DMX_TS_PES_VIDEO: | |
80 | case DMX_TS_PES_AUDIO: | |
81 | case DMX_TS_PES_TELETEXT: | |
82 | case DMX_TS_PES_PCR: | |
83 | case DMX_TS_PES_OTHER: | |
15490795 | 84 | c = alloc_channel(fdtv); |
c81c8b68 GKH |
85 | break; |
86 | default: | |
15490795 SR |
87 | dev_err(fdtv->device, |
88 | "can't start dmx feed: invalid pes type %u\n", | |
89 | dvbdmxfeed->pes_type); | |
90 | ret = -EINVAL; | |
91 | goto out; | |
c81c8b68 GKH |
92 | } |
93 | } else { | |
15490795 | 94 | c = alloc_channel(fdtv); |
c81c8b68 GKH |
95 | } |
96 | ||
15490795 SR |
97 | if (c > 15) { |
98 | dev_err(fdtv->device, "can't start dmx feed: busy\n"); | |
99 | ret = -EBUSY; | |
100 | goto out; | |
c81c8b68 GKH |
101 | } |
102 | ||
15490795 SR |
103 | dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; |
104 | fdtv->channel_pid[c] = dvbdmxfeed->pid; | |
105 | collect_channels(fdtv, &pidc, pids); | |
c81c8b68 | 106 | |
8ae83cdf | 107 | if (dvbdmxfeed->pid == 8192) { |
15490795 SR |
108 | ret = avc_tuner_get_ts(fdtv); |
109 | if (ret) { | |
110 | dealloc_channel(fdtv, c); | |
111 | dev_err(fdtv->device, "can't get TS\n"); | |
112 | goto out; | |
c81c8b68 | 113 | } |
8ae83cdf | 114 | } else { |
15490795 SR |
115 | ret = avc_tuner_set_pids(fdtv, pidc, pids); |
116 | if (ret) { | |
117 | dealloc_channel(fdtv, c); | |
118 | dev_err(fdtv->device, "can't set PIDs\n"); | |
119 | goto out; | |
c81c8b68 GKH |
120 | } |
121 | } | |
15490795 SR |
122 | out: |
123 | mutex_unlock(&fdtv->demux_mutex); | |
c81c8b68 | 124 | |
15490795 | 125 | return ret; |
c81c8b68 GKH |
126 | } |
127 | ||
a70f81c1 | 128 | int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) |
c81c8b68 GKH |
129 | { |
130 | struct dvb_demux *demux = dvbdmxfeed->demux; | |
15490795 SR |
131 | struct firedtv *fdtv = demux->priv; |
132 | int pidc, c, ret; | |
c81c8b68 GKH |
133 | u16 pids[16]; |
134 | ||
15490795 SR |
135 | if (dvbdmxfeed->type == DMX_TYPE_TS && |
136 | !((dvbdmxfeed->ts_type & TS_PACKET) && | |
137 | (demux->dmx.frontend->source != DMX_MEMORY_FE))) { | |
c81c8b68 GKH |
138 | |
139 | if (dvbdmxfeed->ts_type & TS_DECODER) { | |
c81c8b68 | 140 | if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || |
15490795 | 141 | !demux->pesfilter[dvbdmxfeed->pes_type]) |
c81c8b68 GKH |
142 | return -EINVAL; |
143 | ||
144 | demux->pids[dvbdmxfeed->pes_type] |= 0x8000; | |
8ae83cdf | 145 | demux->pesfilter[dvbdmxfeed->pes_type] = NULL; |
c81c8b68 GKH |
146 | } |
147 | ||
148 | if (!(dvbdmxfeed->ts_type & TS_DECODER && | |
15490795 | 149 | dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) |
c81c8b68 GKH |
150 | return 0; |
151 | } | |
152 | ||
a70f81c1 | 153 | if (mutex_lock_interruptible(&fdtv->demux_mutex)) |
c81c8b68 GKH |
154 | return -EINTR; |
155 | ||
15490795 SR |
156 | c = (unsigned long)dvbdmxfeed->priv; |
157 | dealloc_channel(fdtv, c); | |
158 | collect_channels(fdtv, &pidc, pids); | |
c81c8b68 | 159 | |
15490795 | 160 | ret = avc_tuner_set_pids(fdtv, pidc, pids); |
c81c8b68 | 161 | |
a70f81c1 | 162 | mutex_unlock(&fdtv->demux_mutex); |
15490795 SR |
163 | |
164 | return ret; | |
c81c8b68 GKH |
165 | } |
166 | ||
15490795 SR |
167 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
168 | ||
169 | int fdtv_dvb_register(struct firedtv *fdtv) | |
c81c8b68 | 170 | { |
8ae83cdf | 171 | int err; |
c81c8b68 | 172 | |
15490795 SR |
173 | err = dvb_register_adapter(&fdtv->adapter, fdtv_model_names[fdtv->type], |
174 | THIS_MODULE, fdtv->device, adapter_nr); | |
a40bf559 | 175 | if (err < 0) |
8ae83cdf | 176 | goto fail_log; |
c81c8b68 | 177 | |
8ae83cdf | 178 | /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ |
a70f81c1 | 179 | fdtv->demux.dmx.capabilities = 0; |
c81c8b68 | 180 | |
a70f81c1 R |
181 | fdtv->demux.priv = fdtv; |
182 | fdtv->demux.filternum = 16; | |
183 | fdtv->demux.feednum = 16; | |
184 | fdtv->demux.start_feed = fdtv_start_feed; | |
185 | fdtv->demux.stop_feed = fdtv_stop_feed; | |
186 | fdtv->demux.write_to_decoder = NULL; | |
c81c8b68 | 187 | |
a70f81c1 | 188 | err = dvb_dmx_init(&fdtv->demux); |
8ae83cdf SR |
189 | if (err) |
190 | goto fail_unreg_adapter; | |
c81c8b68 | 191 | |
15490795 SR |
192 | fdtv->dmxdev.filternum = 16; |
193 | fdtv->dmxdev.demux = &fdtv->demux.dmx; | |
194 | fdtv->dmxdev.capabilities = 0; | |
c81c8b68 | 195 | |
a70f81c1 | 196 | err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); |
8ae83cdf SR |
197 | if (err) |
198 | goto fail_dmx_release; | |
c81c8b68 | 199 | |
a70f81c1 | 200 | fdtv->frontend.source = DMX_FRONTEND_0; |
c81c8b68 | 201 | |
15490795 | 202 | err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); |
8ae83cdf SR |
203 | if (err) |
204 | goto fail_dmxdev_release; | |
c81c8b68 | 205 | |
a70f81c1 | 206 | err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, |
15490795 | 207 | &fdtv->frontend); |
8ae83cdf SR |
208 | if (err) |
209 | goto fail_rem_frontend; | |
c81c8b68 | 210 | |
a70f81c1 | 211 | dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); |
c81c8b68 | 212 | |
a70f81c1 R |
213 | fdtv_frontend_init(fdtv); |
214 | err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); | |
8ae83cdf SR |
215 | if (err) |
216 | goto fail_net_release; | |
c81c8b68 | 217 | |
a70f81c1 | 218 | err = fdtv_ca_register(fdtv); |
8ae83cdf | 219 | if (err) |
15490795 SR |
220 | dev_info(fdtv->device, |
221 | "Conditional Access Module not enabled\n"); | |
8ae83cdf | 222 | return 0; |
c81c8b68 | 223 | |
8ae83cdf | 224 | fail_net_release: |
a70f81c1 R |
225 | dvb_net_release(&fdtv->dvbnet); |
226 | fdtv->demux.dmx.close(&fdtv->demux.dmx); | |
8ae83cdf | 227 | fail_rem_frontend: |
15490795 | 228 | fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); |
8ae83cdf | 229 | fail_dmxdev_release: |
a70f81c1 | 230 | dvb_dmxdev_release(&fdtv->dmxdev); |
8ae83cdf | 231 | fail_dmx_release: |
a70f81c1 | 232 | dvb_dmx_release(&fdtv->demux); |
8ae83cdf | 233 | fail_unreg_adapter: |
a70f81c1 | 234 | dvb_unregister_adapter(&fdtv->adapter); |
8ae83cdf | 235 | fail_log: |
15490795 | 236 | dev_err(fdtv->device, "DVB initialization failed\n"); |
8ae83cdf | 237 | return err; |
c81c8b68 | 238 | } |
df4846c3 | 239 | |
15490795 SR |
240 | void fdtv_dvb_unregister(struct firedtv *fdtv) |
241 | { | |
242 | fdtv_ca_release(fdtv); | |
243 | dvb_unregister_frontend(&fdtv->fe); | |
244 | dvb_net_release(&fdtv->dvbnet); | |
245 | fdtv->demux.dmx.close(&fdtv->demux.dmx); | |
246 | fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); | |
247 | dvb_dmxdev_release(&fdtv->dmxdev); | |
248 | dvb_dmx_release(&fdtv->demux); | |
249 | dvb_unregister_adapter(&fdtv->adapter); | |
250 | } | |
251 | ||
252 | const char *fdtv_model_names[] = { | |
253 | [FIREDTV_UNKNOWN] = "unknown type", | |
254 | [FIREDTV_DVB_S] = "FireDTV S/CI", | |
255 | [FIREDTV_DVB_C] = "FireDTV C/CI", | |
256 | [FIREDTV_DVB_T] = "FireDTV T/CI", | |
257 | [FIREDTV_DVB_S2] = "FireDTV S2 ", | |
258 | }; | |
259 | ||
260 | struct firedtv *fdtv_alloc(struct device *dev, | |
261 | const struct firedtv_backend *backend, | |
262 | const char *name, size_t name_len) | |
263 | { | |
264 | struct firedtv *fdtv; | |
265 | int i; | |
266 | ||
267 | fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); | |
268 | if (!fdtv) | |
269 | return NULL; | |
270 | ||
79510cdb | 271 | dev_set_drvdata(dev, fdtv); |
15490795 SR |
272 | fdtv->device = dev; |
273 | fdtv->isochannel = -1; | |
274 | fdtv->voltage = 0xff; | |
275 | fdtv->tone = 0xff; | |
276 | fdtv->backend = backend; | |
277 | ||
278 | mutex_init(&fdtv->avc_mutex); | |
279 | init_waitqueue_head(&fdtv->avc_wait); | |
15490795 SR |
280 | mutex_init(&fdtv->demux_mutex); |
281 | INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); | |
282 | ||
283 | for (i = ARRAY_SIZE(fdtv_model_names); --i; ) | |
284 | if (strlen(fdtv_model_names[i]) <= name_len && | |
285 | strncmp(name, fdtv_model_names[i], name_len) == 0) | |
286 | break; | |
287 | fdtv->type = i; | |
288 | ||
289 | return fdtv; | |
290 | } | |
291 | ||
292 | #define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ | |
293 | IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) | |
294 | ||
295 | #define DIGITAL_EVERYWHERE_OUI 0x001287 | |
296 | #define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d | |
297 | #define AVC_SW_VERSION_ENTRY 0x010001 | |
298 | ||
87918334 | 299 | const struct ieee1394_device_id fdtv_id_table[] = { |
15490795 SR |
300 | { |
301 | /* FloppyDTV S/CI and FloppyDTV S2 */ | |
302 | .match_flags = MATCH_FLAGS, | |
303 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
304 | .model_id = 0x000024, | |
305 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
306 | .version = AVC_SW_VERSION_ENTRY, | |
307 | }, { | |
308 | /* FloppyDTV T/CI */ | |
309 | .match_flags = MATCH_FLAGS, | |
310 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
311 | .model_id = 0x000025, | |
312 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
313 | .version = AVC_SW_VERSION_ENTRY, | |
314 | }, { | |
315 | /* FloppyDTV C/CI */ | |
316 | .match_flags = MATCH_FLAGS, | |
317 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
318 | .model_id = 0x000026, | |
319 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
320 | .version = AVC_SW_VERSION_ENTRY, | |
321 | }, { | |
322 | /* FireDTV S/CI and FloppyDTV S2 */ | |
323 | .match_flags = MATCH_FLAGS, | |
324 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
325 | .model_id = 0x000034, | |
326 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
327 | .version = AVC_SW_VERSION_ENTRY, | |
328 | }, { | |
329 | /* FireDTV T/CI */ | |
330 | .match_flags = MATCH_FLAGS, | |
331 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
332 | .model_id = 0x000035, | |
333 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
334 | .version = AVC_SW_VERSION_ENTRY, | |
335 | }, { | |
336 | /* FireDTV C/CI */ | |
337 | .match_flags = MATCH_FLAGS, | |
338 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
339 | .model_id = 0x000036, | |
340 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
341 | .version = AVC_SW_VERSION_ENTRY, | |
342 | }, {} | |
343 | }; | |
344 | MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); | |
345 | ||
346 | static int __init fdtv_init(void) | |
347 | { | |
87918334 SR |
348 | int ret; |
349 | ||
350 | ret = fdtv_fw_init(); | |
351 | if (ret < 0) | |
352 | return ret; | |
353 | ||
354 | ret = fdtv_1394_init(); | |
355 | if (ret < 0) | |
356 | fdtv_fw_exit(); | |
357 | ||
358 | return ret; | |
15490795 SR |
359 | } |
360 | ||
361 | static void __exit fdtv_exit(void) | |
362 | { | |
363 | fdtv_1394_exit(); | |
87918334 | 364 | fdtv_fw_exit(); |
15490795 SR |
365 | } |
366 | ||
367 | module_init(fdtv_init); | |
368 | module_exit(fdtv_exit); | |
df4846c3 | 369 | |
15490795 SR |
370 | MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); |
371 | MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); | |
372 | MODULE_DESCRIPTION("FireDTV DVB Driver"); | |
373 | MODULE_LICENSE("GPL"); | |
374 | MODULE_SUPPORTED_DEVICE("FireDTV DVB"); |