dtv_device.c revision 1.5 1 /* $NetBSD: dtv_device.c,v 1.5 2011/07/13 22:43:04 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Jared D. McNeill.
18 * 4. Neither the name of The NetBSD Foundation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: dtv_device.c,v 1.5 2011/07/13 22:43:04 jmcneill Exp $");
37
38 #include <sys/types.h>
39 #include <sys/conf.h>
40 #include <sys/device.h>
41 #include <sys/atomic.h>
42 #include <sys/module.h>
43 #include <sys/poll.h>
44 #include <sys/select.h>
45
46 #include <dev/dtv/dtvvar.h>
47
48 MODULE(MODULE_CLASS_DRIVER, dtv, NULL);
49
50 static dev_type_open(dtvopen);
51 static dev_type_close(dtvclose);
52 static dev_type_read(dtvread);
53 static dev_type_ioctl(dtvioctl);
54 static dev_type_poll(dtvpoll);
55
56 const struct cdevsw dtv_cdevsw = {
57 .d_open = dtvopen,
58 .d_close = dtvclose,
59 .d_read = dtvread,
60 .d_write = nowrite,
61 .d_ioctl = dtvioctl,
62 .d_stop = nostop,
63 .d_tty = notty,
64 .d_poll = dtvpoll,
65 .d_mmap = nommap,
66 .d_kqfilter = nokqfilter,
67 .d_flag = D_OTHER|D_MPSAFE,
68 };
69
70 static int dtv_match(device_t, cfdata_t, void *);
71 static void dtv_attach(device_t, device_t, void *);
72 static int dtv_detach(device_t, int);
73
74 CFATTACH_DECL_NEW(dtv,
75 sizeof(struct dtv_softc),
76 dtv_match,
77 dtv_attach,
78 dtv_detach,
79 NULL
80 );
81
82 extern struct cfdriver dtv_cd;
83
84 static int
85 dtv_match(device_t parent, cfdata_t cfdata, void *aa)
86 {
87 return 1;
88 }
89
90 static void
91 dtv_attach(device_t parent, device_t self, void *aa)
92 {
93 struct dtv_attach_args *daa = aa;
94 struct dtv_softc *sc = device_private(self);
95 struct dtv_stream *ds = &sc->sc_stream;
96 struct dvb_frontend_info info;
97
98 sc->sc_dev = self;
99 sc->sc_hw = daa->hw;
100 sc->sc_priv = daa->priv;
101 sc->sc_open = 0;
102 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
103
104 ds->ds_nbufs = 0;
105 ds->ds_buf = NULL;
106 SIMPLEQ_INIT(&ds->ds_ingress);
107 SIMPLEQ_INIT(&ds->ds_egress);
108 mutex_init(&ds->ds_egress_lock, MUTEX_DEFAULT, IPL_VM);
109 mutex_init(&ds->ds_ingress_lock, MUTEX_DEFAULT, IPL_VM);
110 cv_init(&ds->ds_sample_cv, "dtv");
111 selinit(&ds->ds_sel);
112 dtv_scatter_buf_init(&ds->ds_data);
113 if (dtv_buffer_realloc(sc, DTV_DEFAULT_BUFSIZE) != 0) {
114 aprint_error(": no memory\n");
115 sc->sc_dying = true;
116 return;
117 }
118
119 mutex_init(&sc->sc_demux_lock, MUTEX_DEFAULT, IPL_VM);
120 TAILQ_INIT(&sc->sc_demux_list);
121
122 dtv_device_get_devinfo(sc, &info);
123
124 aprint_naive("\n");
125 aprint_normal(": %s", info.name);
126 switch (info.type) {
127 case FE_QPSK:
128 aprint_normal(" [QPSK]");
129 break;
130 case FE_QAM:
131 aprint_normal(" [QAM]");
132 break;
133 case FE_OFDM:
134 aprint_normal(" [OFDM]");
135 break;
136 case FE_ATSC:
137 aprint_normal(" [ATSC]");
138 break;
139 }
140 aprint_normal("\n");
141 }
142
143 static int
144 dtv_detach(device_t self, int flags)
145 {
146 struct dtv_softc *sc = device_private(self);
147 struct dtv_stream *ds = &sc->sc_stream;
148
149 cv_destroy(&ds->ds_sample_cv);
150 mutex_destroy(&ds->ds_ingress_lock);
151 mutex_destroy(&ds->ds_egress_lock);
152 seldestroy(&ds->ds_sel);
153 dtv_buffer_realloc(sc, 0);
154 dtv_scatter_buf_destroy(&ds->ds_data);
155
156 mutex_destroy(&sc->sc_demux_lock);
157
158 return 0;
159 }
160
161 #ifdef _MODULE
162 #include "ioconf.c"
163 #endif
164
165 static int
166 dtv_modcmd(modcmd_t cmd, void *arg)
167 {
168 #ifdef _MODULE
169 int error, bmaj = -1, cmaj = -1;
170 #endif
171
172 switch (cmd) {
173 case MODULE_CMD_INIT:
174 #ifdef _MODULE
175 error = config_init_component(cfdriver_ioconf_dtv,
176 cfattach_ioconf_dtv, cfdata_ioconf_dtv);
177 if (error)
178 return error;
179 error = devsw_attach("dtv", NULL, &bmaj, &dtv_cdevsw, &cmaj);
180 if (error)
181 config_fini_component(cfdriver_ioconf_dtv,
182 cfattach_ioconf_dtv, cfdata_ioconf_dtv);
183 return error;
184 #else
185 return 0;
186 #endif
187 case MODULE_CMD_FINI:
188 #ifdef _MODULE
189 devsw_detach(NULL, &dtv_cdevsw);
190 return config_fini_component(cfdriver_ioconf_dtv,
191 cfattach_ioconf_dtv, cfdata_ioconf_dtv);
192 #else
193 return 0;
194 #endif
195 default:
196 return ENOTTY;
197 }
198 }
199
200 static int
201 dtvopen(dev_t dev, int flags, int mode, lwp_t *l)
202 {
203 struct dtv_softc *sc;
204 struct dtv_ts *ts;
205 int error;
206
207 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
208 return ENXIO;
209 if (sc->sc_dying == true)
210 return ENODEV;
211 ts = &sc->sc_ts;
212
213 mutex_enter(&sc->sc_lock);
214 if (sc->sc_open == 0) {
215 error = dtv_device_open(sc, flags);
216 if (error)
217 return error;
218 sc->sc_bufsize = DTV_DEFAULT_BUFSIZE;
219 sc->sc_bufsize_chg = true;
220 memset(ts->ts_pidfilter, 0, sizeof(ts->ts_pidfilter));
221 }
222 ++sc->sc_open;
223 mutex_exit(&sc->sc_lock);
224
225 if (ISDTVDEMUX(dev))
226 return dtv_demux_open(sc, flags, mode, l);
227
228 return 0;
229 }
230
231 static int
232 dtvclose(dev_t dev, int flags, int mode, lwp_t *l)
233 {
234 struct dtv_softc *sc;
235
236 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
237 return ENXIO;
238
239 dtv_close_common(sc);
240
241 return 0;
242 }
243
244 static int
245 dtvread(dev_t dev, struct uio *uio, int flags)
246 {
247 struct dtv_softc *sc;
248
249 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
250 return ENXIO;
251
252 if (ISDTVDVR(dev))
253 return dtv_buffer_read(sc, uio, flags);
254
255 return ENXIO;
256 }
257
258 static int
259 dtvioctl(dev_t dev, u_long cmd, void *ptr, int flags, lwp_t *l)
260 {
261 struct dtv_softc *sc;
262
263 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
264 return ENXIO;
265
266 if (ISDTVFRONTEND(dev)) {
267 return dtv_frontend_ioctl(sc, cmd, ptr, flags);
268 }
269
270 return EINVAL;
271 }
272
273 static int
274 dtvpoll(dev_t dev, int events, lwp_t *l)
275 {
276 struct dtv_softc *sc;
277
278 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
279 return POLLERR;
280
281 if (ISDTVFRONTEND(dev)) {
282 return POLLPRI|POLLIN; /* XXX event */
283 } else if (ISDTVDVR(dev)) {
284 return dtv_buffer_poll(sc, events, l);
285 }
286
287 return POLLERR;
288 }
289
290 void
291 dtv_close_common(struct dtv_softc *sc)
292 {
293 mutex_enter(&sc->sc_lock);
294 KASSERT(sc->sc_open > 0);
295 --sc->sc_open;
296 if (sc->sc_open == 0) {
297 dtv_device_close(sc);
298 dtv_buffer_destroy(sc);
299 }
300 mutex_exit(&sc->sc_lock);
301 }
302
303 int
304 dtv_print(void *arg, const char *pnp)
305 {
306 if (pnp)
307 aprint_normal("dtv at %s", pnp);
308
309 return UNCONF;
310 }
311