Commit | Line | Data |
---|---|---|
41b44e04 PH |
1 | /* |
2 | * Abilis Systems Single DVB-T Receiver | |
3 | * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com> | |
c6ccdca9 | 4 | * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> |
41b44e04 PH |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/errno.h> | |
41b44e04 PH |
22 | #include <linux/slab.h> |
23 | #include <linux/module.h> | |
24 | #include <linux/mm.h> | |
25 | #include <linux/kref.h> | |
3288e201 | 26 | #include <linux/uaccess.h> |
41b44e04 PH |
27 | #include <linux/usb.h> |
28 | ||
b47acf2a | 29 | /* header file for usb device driver*/ |
41b44e04 PH |
30 | #include "as102_drv.h" |
31 | #include "as102_fw.h" | |
41b44e04 | 32 | #include "dvbdev.h" |
41b44e04 | 33 | |
d29387e8 RD |
34 | int as102_debug; |
35 | module_param_named(debug, as102_debug, int, 0644); | |
41b44e04 PH |
36 | MODULE_PARM_DESC(debug, "Turn on/off debugging (default: off)"); |
37 | ||
c6ccdca9 | 38 | int dual_tuner; |
41b44e04 | 39 | module_param_named(dual_tuner, dual_tuner, int, 0644); |
c6ccdca9 | 40 | MODULE_PARM_DESC(dual_tuner, "Activate Dual-Tuner config (default: off)"); |
41b44e04 PH |
41 | |
42 | static int fw_upload = 1; | |
43 | module_param_named(fw_upload, fw_upload, int, 0644); | |
44 | MODULE_PARM_DESC(fw_upload, "Turn on/off default FW upload (default: on)"); | |
45 | ||
c6ccdca9 | 46 | static int pid_filtering; |
41b44e04 PH |
47 | module_param_named(pid_filtering, pid_filtering, int, 0644); |
48 | MODULE_PARM_DESC(pid_filtering, "Activate HW PID filtering (default: off)"); | |
49 | ||
c6ccdca9 | 50 | static int ts_auto_disable; |
41b44e04 PH |
51 | module_param_named(ts_auto_disable, ts_auto_disable, int, 0644); |
52 | MODULE_PARM_DESC(ts_auto_disable, "Stream Auto Enable on FW (default: off)"); | |
53 | ||
54 | int elna_enable = 1; | |
55 | module_param_named(elna_enable, elna_enable, int, 0644); | |
56 | MODULE_PARM_DESC(elna_enable, "Activate eLNA (default: on)"); | |
57 | ||
41b44e04 | 58 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
41b44e04 | 59 | |
c6ccdca9 DH |
60 | static void as102_stop_stream(struct as102_dev_t *dev) |
61 | { | |
34490a0a | 62 | struct as10x_bus_adapter_t *bus_adap; |
41b44e04 PH |
63 | |
64 | if (dev != NULL) | |
65 | bus_adap = &dev->bus_adap; | |
66 | else | |
67 | return; | |
68 | ||
69 | if (bus_adap->ops->stop_stream != NULL) | |
70 | bus_adap->ops->stop_stream(dev); | |
71 | ||
72 | if (ts_auto_disable) { | |
73 | if (mutex_lock_interruptible(&dev->bus_adap.lock)) | |
74 | return; | |
75 | ||
c6ccdca9 | 76 | if (as10x_cmd_stop_streaming(bus_adap) < 0) |
41b44e04 | 77 | dprintk(debug, "as10x_cmd_stop_streaming failed\n"); |
41b44e04 PH |
78 | |
79 | mutex_unlock(&dev->bus_adap.lock); | |
80 | } | |
81 | } | |
82 | ||
c6ccdca9 DH |
83 | static int as102_start_stream(struct as102_dev_t *dev) |
84 | { | |
34490a0a | 85 | struct as10x_bus_adapter_t *bus_adap; |
41b44e04 PH |
86 | int ret = -EFAULT; |
87 | ||
88 | if (dev != NULL) | |
89 | bus_adap = &dev->bus_adap; | |
90 | else | |
91 | return ret; | |
92 | ||
c6ccdca9 | 93 | if (bus_adap->ops->start_stream != NULL) |
41b44e04 | 94 | ret = bus_adap->ops->start_stream(dev); |
41b44e04 PH |
95 | |
96 | if (ts_auto_disable) { | |
97 | if (mutex_lock_interruptible(&dev->bus_adap.lock)) | |
98 | return -EFAULT; | |
99 | ||
100 | ret = as10x_cmd_start_streaming(bus_adap); | |
101 | ||
102 | mutex_unlock(&dev->bus_adap.lock); | |
103 | } | |
104 | ||
105 | return ret; | |
106 | } | |
107 | ||
108 | static int as10x_pid_filter(struct as102_dev_t *dev, | |
109 | int index, u16 pid, int onoff) { | |
110 | ||
34490a0a | 111 | struct as10x_bus_adapter_t *bus_adap = &dev->bus_adap; |
41b44e04 PH |
112 | int ret = -EFAULT; |
113 | ||
114 | ENTER(); | |
115 | ||
116 | if (mutex_lock_interruptible(&dev->bus_adap.lock)) { | |
117 | dprintk(debug, "mutex_lock_interruptible(lock) failed !\n"); | |
118 | return -EBUSY; | |
119 | } | |
120 | ||
c6ccdca9 DH |
121 | switch (onoff) { |
122 | case 0: | |
14e0e4bf SN |
123 | ret = as10x_cmd_del_PID_filter(bus_adap, (uint16_t) pid); |
124 | dprintk(debug, "DEL_PID_FILTER([%02d] 0x%04x) ret = %d\n", | |
125 | index, pid, ret); | |
126 | break; | |
c6ccdca9 DH |
127 | case 1: |
128 | { | |
14e0e4bf | 129 | struct as10x_ts_filter filter; |
c6ccdca9 | 130 | |
14e0e4bf SN |
131 | filter.type = TS_PID_TYPE_TS; |
132 | filter.idx = 0xFF; | |
133 | filter.pid = pid; | |
c6ccdca9 | 134 | |
14e0e4bf SN |
135 | ret = as10x_cmd_add_PID_filter(bus_adap, &filter); |
136 | dprintk(debug, "ADD_PID_FILTER([%02d -> %02d], 0x%04x) ret = %d\n", | |
137 | index, filter.idx, filter.pid, ret); | |
138 | break; | |
c6ccdca9 | 139 | } |
41b44e04 PH |
140 | } |
141 | ||
142 | mutex_unlock(&dev->bus_adap.lock); | |
143 | ||
144 | LEAVE(); | |
145 | return ret; | |
146 | } | |
147 | ||
c6ccdca9 DH |
148 | static int as102_dvb_dmx_start_feed(struct dvb_demux_feed *dvbdmxfeed) |
149 | { | |
41b44e04 PH |
150 | int ret = 0; |
151 | struct dvb_demux *demux = dvbdmxfeed->demux; | |
152 | struct as102_dev_t *as102_dev = demux->priv; | |
153 | ||
154 | ENTER(); | |
155 | ||
156 | if (mutex_lock_interruptible(&as102_dev->sem)) | |
157 | return -ERESTARTSYS; | |
158 | ||
14e0e4bf SN |
159 | if (pid_filtering) |
160 | as10x_pid_filter(as102_dev, dvbdmxfeed->index, | |
161 | dvbdmxfeed->pid, 1); | |
41b44e04 | 162 | |
c6ccdca9 | 163 | if (as102_dev->streaming++ == 0) |
41b44e04 | 164 | ret = as102_start_stream(as102_dev); |
41b44e04 PH |
165 | |
166 | mutex_unlock(&as102_dev->sem); | |
167 | LEAVE(); | |
168 | return ret; | |
169 | } | |
170 | ||
c6ccdca9 DH |
171 | static int as102_dvb_dmx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) |
172 | { | |
41b44e04 PH |
173 | struct dvb_demux *demux = dvbdmxfeed->demux; |
174 | struct as102_dev_t *as102_dev = demux->priv; | |
175 | ||
176 | ENTER(); | |
177 | ||
178 | if (mutex_lock_interruptible(&as102_dev->sem)) | |
179 | return -ERESTARTSYS; | |
180 | ||
c6ccdca9 | 181 | if (--as102_dev->streaming == 0) |
41b44e04 | 182 | as102_stop_stream(as102_dev); |
41b44e04 | 183 | |
14e0e4bf SN |
184 | if (pid_filtering) |
185 | as10x_pid_filter(as102_dev, dvbdmxfeed->index, | |
186 | dvbdmxfeed->pid, 0); | |
41b44e04 PH |
187 | |
188 | mutex_unlock(&as102_dev->sem); | |
189 | LEAVE(); | |
190 | return 0; | |
191 | } | |
41b44e04 | 192 | |
c6ccdca9 DH |
193 | int as102_dvb_register(struct as102_dev_t *as102_dev) |
194 | { | |
88010289 SN |
195 | struct device *dev = &as102_dev->bus_adap.usb_dev->dev; |
196 | int ret; | |
41b44e04 | 197 | |
41b44e04 | 198 | ret = dvb_register_adapter(&as102_dev->dvb_adap, |
88010289 SN |
199 | as102_dev->name, THIS_MODULE, |
200 | dev, adapter_nr); | |
41b44e04 | 201 | if (ret < 0) { |
88010289 SN |
202 | dev_err(dev, "%s: dvb_register_adapter() failed: %d\n", |
203 | __func__, ret); | |
204 | return ret; | |
41b44e04 PH |
205 | } |
206 | ||
207 | as102_dev->dvb_dmx.priv = as102_dev; | |
208 | as102_dev->dvb_dmx.filternum = pid_filtering ? 16 : 256; | |
209 | as102_dev->dvb_dmx.feednum = 256; | |
210 | as102_dev->dvb_dmx.start_feed = as102_dvb_dmx_start_feed; | |
211 | as102_dev->dvb_dmx.stop_feed = as102_dvb_dmx_stop_feed; | |
212 | ||
213 | as102_dev->dvb_dmx.dmx.capabilities = DMX_TS_FILTERING | | |
214 | DMX_SECTION_FILTERING; | |
215 | ||
216 | as102_dev->dvb_dmxdev.filternum = as102_dev->dvb_dmx.filternum; | |
217 | as102_dev->dvb_dmxdev.demux = &as102_dev->dvb_dmx.dmx; | |
218 | as102_dev->dvb_dmxdev.capabilities = 0; | |
219 | ||
c6ccdca9 DH |
220 | ret = dvb_dmx_init(&as102_dev->dvb_dmx); |
221 | if (ret < 0) { | |
1ec9a35e | 222 | dev_err(dev, "%s: dvb_dmx_init() failed: %d\n", __func__, ret); |
88010289 | 223 | goto edmxinit; |
41b44e04 PH |
224 | } |
225 | ||
226 | ret = dvb_dmxdev_init(&as102_dev->dvb_dmxdev, &as102_dev->dvb_adap); | |
227 | if (ret < 0) { | |
88010289 SN |
228 | dev_err(dev, "%s: dvb_dmxdev_init() failed: %d\n", |
229 | __func__, ret); | |
230 | goto edmxdinit; | |
41b44e04 PH |
231 | } |
232 | ||
233 | ret = as102_dvb_register_fe(as102_dev, &as102_dev->dvb_fe); | |
234 | if (ret < 0) { | |
88010289 | 235 | dev_err(dev, "%s: as102_dvb_register_frontend() failed: %d", |
c6ccdca9 | 236 | __func__, ret); |
88010289 | 237 | goto efereg; |
41b44e04 | 238 | } |
41b44e04 PH |
239 | |
240 | /* init bus mutex for token locking */ | |
241 | mutex_init(&as102_dev->bus_adap.lock); | |
242 | ||
243 | /* init start / stop stream mutex */ | |
244 | mutex_init(&as102_dev->sem); | |
245 | ||
41b44e04 PH |
246 | /* |
247 | * try to load as102 firmware. If firmware upload failed, we'll be | |
248 | * able to upload it later. | |
249 | */ | |
250 | if (fw_upload) | |
251 | try_then_request_module(as102_fw_upload(&as102_dev->bus_adap), | |
252 | "firmware_class"); | |
88010289 SN |
253 | |
254 | pr_info("Registered device %s", as102_dev->name); | |
255 | return 0; | |
256 | ||
257 | efereg: | |
258 | dvb_dmxdev_release(&as102_dev->dvb_dmxdev); | |
259 | edmxdinit: | |
260 | dvb_dmx_release(&as102_dev->dvb_dmx); | |
261 | edmxinit: | |
262 | dvb_unregister_adapter(&as102_dev->dvb_adap); | |
41b44e04 PH |
263 | return ret; |
264 | } | |
265 | ||
c6ccdca9 DH |
266 | void as102_dvb_unregister(struct as102_dev_t *as102_dev) |
267 | { | |
41b44e04 PH |
268 | /* unregister as102 frontend */ |
269 | as102_dvb_unregister_fe(&as102_dev->dvb_fe); | |
270 | ||
271 | /* unregister demux device */ | |
272 | dvb_dmxdev_release(&as102_dev->dvb_dmxdev); | |
273 | dvb_dmx_release(&as102_dev->dvb_dmx); | |
274 | ||
275 | /* unregister dvb adapter */ | |
276 | dvb_unregister_adapter(&as102_dev->dvb_adap); | |
ff7029f5 | 277 | |
88010289 | 278 | pr_info("Unregistered device %s", as102_dev->name); |
41b44e04 PH |
279 | } |
280 | ||
9451df0e | 281 | module_usb_driver(as102_usb_driver); |
41b44e04 | 282 | |
41b44e04 PH |
283 | /* modinfo details */ |
284 | MODULE_DESCRIPTION(DRIVER_FULL_NAME); | |
285 | MODULE_LICENSE("GPL"); | |
286 | MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>"); |