Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
df4846c3 | 2 | /* |
612262a5 | 3 | * FireDTV driver (formerly known as FireSAT) |
df4846c3 | 4 | * |
612262a5 SR |
5 | * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> |
6 | * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> | |
df4846c3 HK |
7 | */ |
8 | ||
15490795 SR |
9 | #include <linux/bitops.h> |
10 | #include <linux/device.h> | |
c81c8b68 | 11 | #include <linux/errno.h> |
612262a5 | 12 | #include <linux/kernel.h> |
15490795 | 13 | #include <linux/module.h> |
612262a5 SR |
14 | #include <linux/mutex.h> |
15 | #include <linux/types.h> | |
16 | ||
fada1935 MCC |
17 | #include <media/dmxdev.h> |
18 | #include <media/dvb_demux.h> | |
19 | #include <media/dvbdev.h> | |
20 | #include <media/dvb_frontend.h> | |
c81c8b68 | 21 | |
a70f81c1 | 22 | #include "firedtv.h" |
c81c8b68 | 23 | |
15490795 | 24 | static int alloc_channel(struct firedtv *fdtv) |
c81c8b68 | 25 | { |
15490795 | 26 | int i; |
c81c8b68 | 27 | |
15490795 SR |
28 | for (i = 0; i < 16; i++) |
29 | if (!__test_and_set_bit(i, &fdtv->channel_active)) | |
612262a5 | 30 | break; |
15490795 | 31 | return i; |
c81c8b68 GKH |
32 | } |
33 | ||
15490795 | 34 | static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) |
c81c8b68 | 35 | { |
15490795 | 36 | int i, n; |
c81c8b68 | 37 | |
15490795 SR |
38 | for (i = 0, n = 0; i < 16; i++) |
39 | if (test_bit(i, &fdtv->channel_active)) | |
40 | pid[n++] = fdtv->channel_pid[i]; | |
41 | *pidc = n; | |
c81c8b68 GKH |
42 | } |
43 | ||
15490795 | 44 | static inline void dealloc_channel(struct firedtv *fdtv, int i) |
c81c8b68 | 45 | { |
15490795 | 46 | __clear_bit(i, &fdtv->channel_active); |
c81c8b68 GKH |
47 | } |
48 | ||
a70f81c1 | 49 | int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) |
c81c8b68 | 50 | { |
15490795 SR |
51 | struct firedtv *fdtv = dvbdmxfeed->demux->priv; |
52 | int pidc, c, ret; | |
c81c8b68 GKH |
53 | u16 pids[16]; |
54 | ||
c81c8b68 GKH |
55 | switch (dvbdmxfeed->type) { |
56 | case DMX_TYPE_TS: | |
57 | case DMX_TYPE_SEC: | |
58 | break; | |
59 | default: | |
15490795 SR |
60 | dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", |
61 | dvbdmxfeed->type); | |
c81c8b68 GKH |
62 | return -EINVAL; |
63 | } | |
64 | ||
15490795 SR |
65 | if (mutex_lock_interruptible(&fdtv->demux_mutex)) |
66 | return -EINTR; | |
67 | ||
c81c8b68 GKH |
68 | if (dvbdmxfeed->type == DMX_TYPE_TS) { |
69 | switch (dvbdmxfeed->pes_type) { | |
fde04ab9 MCC |
70 | case DMX_PES_VIDEO: |
71 | case DMX_PES_AUDIO: | |
72 | case DMX_PES_TELETEXT: | |
73 | case DMX_PES_PCR: | |
74 | case DMX_PES_OTHER: | |
15490795 | 75 | c = alloc_channel(fdtv); |
c81c8b68 GKH |
76 | break; |
77 | default: | |
15490795 SR |
78 | dev_err(fdtv->device, |
79 | "can't start dmx feed: invalid pes type %u\n", | |
80 | dvbdmxfeed->pes_type); | |
81 | ret = -EINVAL; | |
82 | goto out; | |
c81c8b68 GKH |
83 | } |
84 | } else { | |
15490795 | 85 | c = alloc_channel(fdtv); |
c81c8b68 GKH |
86 | } |
87 | ||
15490795 SR |
88 | if (c > 15) { |
89 | dev_err(fdtv->device, "can't start dmx feed: busy\n"); | |
90 | ret = -EBUSY; | |
91 | goto out; | |
c81c8b68 GKH |
92 | } |
93 | ||
15490795 SR |
94 | dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; |
95 | fdtv->channel_pid[c] = dvbdmxfeed->pid; | |
96 | collect_channels(fdtv, &pidc, pids); | |
c81c8b68 | 97 | |
8ae83cdf | 98 | if (dvbdmxfeed->pid == 8192) { |
15490795 SR |
99 | ret = avc_tuner_get_ts(fdtv); |
100 | if (ret) { | |
101 | dealloc_channel(fdtv, c); | |
102 | dev_err(fdtv->device, "can't get TS\n"); | |
103 | goto out; | |
c81c8b68 | 104 | } |
8ae83cdf | 105 | } else { |
15490795 SR |
106 | ret = avc_tuner_set_pids(fdtv, pidc, pids); |
107 | if (ret) { | |
108 | dealloc_channel(fdtv, c); | |
109 | dev_err(fdtv->device, "can't set PIDs\n"); | |
110 | goto out; | |
c81c8b68 GKH |
111 | } |
112 | } | |
15490795 SR |
113 | out: |
114 | mutex_unlock(&fdtv->demux_mutex); | |
c81c8b68 | 115 | |
15490795 | 116 | return ret; |
c81c8b68 GKH |
117 | } |
118 | ||
a70f81c1 | 119 | int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) |
c81c8b68 GKH |
120 | { |
121 | struct dvb_demux *demux = dvbdmxfeed->demux; | |
15490795 SR |
122 | struct firedtv *fdtv = demux->priv; |
123 | int pidc, c, ret; | |
c81c8b68 GKH |
124 | u16 pids[16]; |
125 | ||
15490795 SR |
126 | if (dvbdmxfeed->type == DMX_TYPE_TS && |
127 | !((dvbdmxfeed->ts_type & TS_PACKET) && | |
128 | (demux->dmx.frontend->source != DMX_MEMORY_FE))) { | |
c81c8b68 GKH |
129 | |
130 | if (dvbdmxfeed->ts_type & TS_DECODER) { | |
fde04ab9 | 131 | if (dvbdmxfeed->pes_type >= DMX_PES_OTHER || |
15490795 | 132 | !demux->pesfilter[dvbdmxfeed->pes_type]) |
c81c8b68 GKH |
133 | return -EINVAL; |
134 | ||
135 | demux->pids[dvbdmxfeed->pes_type] |= 0x8000; | |
8ae83cdf | 136 | demux->pesfilter[dvbdmxfeed->pes_type] = NULL; |
c81c8b68 GKH |
137 | } |
138 | ||
139 | if (!(dvbdmxfeed->ts_type & TS_DECODER && | |
fde04ab9 | 140 | dvbdmxfeed->pes_type < DMX_PES_OTHER)) |
c81c8b68 GKH |
141 | return 0; |
142 | } | |
143 | ||
a70f81c1 | 144 | if (mutex_lock_interruptible(&fdtv->demux_mutex)) |
c81c8b68 GKH |
145 | return -EINTR; |
146 | ||
15490795 SR |
147 | c = (unsigned long)dvbdmxfeed->priv; |
148 | dealloc_channel(fdtv, c); | |
149 | collect_channels(fdtv, &pidc, pids); | |
c81c8b68 | 150 | |
15490795 | 151 | ret = avc_tuner_set_pids(fdtv, pidc, pids); |
c81c8b68 | 152 | |
a70f81c1 | 153 | mutex_unlock(&fdtv->demux_mutex); |
15490795 SR |
154 | |
155 | return ret; | |
c81c8b68 GKH |
156 | } |
157 | ||
15490795 SR |
158 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
159 | ||
92374e88 | 160 | int fdtv_dvb_register(struct firedtv *fdtv, const char *name) |
c81c8b68 | 161 | { |
8ae83cdf | 162 | int err; |
c81c8b68 | 163 | |
92374e88 | 164 | err = dvb_register_adapter(&fdtv->adapter, name, |
15490795 | 165 | THIS_MODULE, fdtv->device, adapter_nr); |
a40bf559 | 166 | if (err < 0) |
8ae83cdf | 167 | goto fail_log; |
c81c8b68 | 168 | |
8ae83cdf | 169 | /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ |
a70f81c1 | 170 | fdtv->demux.dmx.capabilities = 0; |
c81c8b68 | 171 | |
a70f81c1 R |
172 | fdtv->demux.priv = fdtv; |
173 | fdtv->demux.filternum = 16; | |
174 | fdtv->demux.feednum = 16; | |
175 | fdtv->demux.start_feed = fdtv_start_feed; | |
176 | fdtv->demux.stop_feed = fdtv_stop_feed; | |
177 | fdtv->demux.write_to_decoder = NULL; | |
c81c8b68 | 178 | |
a70f81c1 | 179 | err = dvb_dmx_init(&fdtv->demux); |
8ae83cdf SR |
180 | if (err) |
181 | goto fail_unreg_adapter; | |
c81c8b68 | 182 | |
15490795 SR |
183 | fdtv->dmxdev.filternum = 16; |
184 | fdtv->dmxdev.demux = &fdtv->demux.dmx; | |
185 | fdtv->dmxdev.capabilities = 0; | |
c81c8b68 | 186 | |
a70f81c1 | 187 | err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); |
8ae83cdf SR |
188 | if (err) |
189 | goto fail_dmx_release; | |
c81c8b68 | 190 | |
a70f81c1 | 191 | fdtv->frontend.source = DMX_FRONTEND_0; |
c81c8b68 | 192 | |
15490795 | 193 | err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); |
8ae83cdf SR |
194 | if (err) |
195 | goto fail_dmxdev_release; | |
c81c8b68 | 196 | |
a70f81c1 | 197 | err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, |
15490795 | 198 | &fdtv->frontend); |
8ae83cdf SR |
199 | if (err) |
200 | goto fail_rem_frontend; | |
c81c8b68 | 201 | |
f1d99f39 JN |
202 | err = dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); |
203 | if (err) | |
204 | goto fail_disconnect_frontend; | |
c81c8b68 | 205 | |
92374e88 | 206 | fdtv_frontend_init(fdtv, name); |
a70f81c1 | 207 | err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); |
8ae83cdf SR |
208 | if (err) |
209 | goto fail_net_release; | |
c81c8b68 | 210 | |
a70f81c1 | 211 | err = fdtv_ca_register(fdtv); |
8ae83cdf | 212 | if (err) |
15490795 SR |
213 | dev_info(fdtv->device, |
214 | "Conditional Access Module not enabled\n"); | |
8ae83cdf | 215 | return 0; |
c81c8b68 | 216 | |
8ae83cdf | 217 | fail_net_release: |
a70f81c1 | 218 | dvb_net_release(&fdtv->dvbnet); |
f1d99f39 | 219 | fail_disconnect_frontend: |
a70f81c1 | 220 | fdtv->demux.dmx.close(&fdtv->demux.dmx); |
8ae83cdf | 221 | fail_rem_frontend: |
15490795 | 222 | fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); |
8ae83cdf | 223 | fail_dmxdev_release: |
a70f81c1 | 224 | dvb_dmxdev_release(&fdtv->dmxdev); |
8ae83cdf | 225 | fail_dmx_release: |
a70f81c1 | 226 | dvb_dmx_release(&fdtv->demux); |
8ae83cdf | 227 | fail_unreg_adapter: |
a70f81c1 | 228 | dvb_unregister_adapter(&fdtv->adapter); |
8ae83cdf | 229 | fail_log: |
15490795 | 230 | dev_err(fdtv->device, "DVB initialization failed\n"); |
8ae83cdf | 231 | return err; |
c81c8b68 | 232 | } |
df4846c3 | 233 | |
15490795 SR |
234 | void fdtv_dvb_unregister(struct firedtv *fdtv) |
235 | { | |
236 | fdtv_ca_release(fdtv); | |
237 | dvb_unregister_frontend(&fdtv->fe); | |
238 | dvb_net_release(&fdtv->dvbnet); | |
239 | fdtv->demux.dmx.close(&fdtv->demux.dmx); | |
240 | fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); | |
241 | dvb_dmxdev_release(&fdtv->dmxdev); | |
242 | dvb_dmx_release(&fdtv->demux); | |
243 | dvb_unregister_adapter(&fdtv->adapter); | |
244 | } |