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