dtv_device.c revision 1.4 1 /* $NetBSD: dtv_device.c,v 1.4 2011/07/12 00:57:19 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.4 2011/07/12 00:57:19 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 atomic_swap_uint(&sc->sc_open, 0);
102
103 ds->ds_nbufs = 0;
104 ds->ds_buf = NULL;
105 SIMPLEQ_INIT(&ds->ds_ingress);
106 SIMPLEQ_INIT(&ds->ds_egress);
107 mutex_init(&ds->ds_egress_lock, MUTEX_DEFAULT, IPL_VM);
108 mutex_init(&ds->ds_ingress_lock, MUTEX_DEFAULT, IPL_VM);
109 cv_init(&ds->ds_sample_cv, "dtv");
110 selinit(&ds->ds_sel);
111 dtv_scatter_buf_init(&ds->ds_data);
112 if (dtv_buffer_realloc(sc, DTV_DEFAULT_BUFSIZE) != 0) {
113 aprint_error(": no memory\n");
114 sc->sc_dying = true;
115 return;
116 }
117
118 dtv_device_get_devinfo(sc, &info);
119
120 aprint_naive("\n");
121 aprint_normal(": %s", info.name);
122 switch (info.type) {
123 case FE_QPSK:
124 aprint_normal(" [QPSK]");
125 break;
126 case FE_QAM:
127 aprint_normal(" [QAM]");
128 break;
129 case FE_OFDM:
130 aprint_normal(" [OFDM]");
131 break;
132 case FE_ATSC:
133 aprint_normal(" [ATSC]");
134 break;
135 }
136 aprint_normal("\n");
137 }
138
139 static int
140 dtv_detach(device_t self, int flags)
141 {
142 struct dtv_softc *sc = device_private(self);
143 struct dtv_stream *ds = &sc->sc_stream;
144
145 cv_destroy(&ds->ds_sample_cv);
146 mutex_destroy(&ds->ds_ingress_lock);
147 mutex_destroy(&ds->ds_egress_lock);
148 seldestroy(&ds->ds_sel);
149 dtv_buffer_realloc(sc, 0);
150 dtv_scatter_buf_destroy(&ds->ds_data);
151
152 return 0;
153 }
154
155 #ifdef _MODULE
156 #include "ioconf.c"
157 #endif
158
159 static int
160 dtv_modcmd(modcmd_t cmd, void *arg)
161 {
162 #ifdef _MODULE
163 int error, bmaj = -1, cmaj = -1;
164 #endif
165
166 switch (cmd) {
167 case MODULE_CMD_INIT:
168 #ifdef _MODULE
169 error = config_init_component(cfdriver_ioconf_dtv,
170 cfattach_ioconf_dtv, cfdata_ioconf_dtv);
171 if (error)
172 return error;
173 error = devsw_attach("dtv", NULL, &bmaj, &dtv_cdevsw, &cmaj);
174 if (error)
175 config_fini_component(cfdriver_ioconf_dtv,
176 cfattach_ioconf_dtv, cfdata_ioconf_dtv);
177 return error;
178 #else
179 return 0;
180 #endif
181 case MODULE_CMD_FINI:
182 #ifdef _MODULE
183 devsw_detach(NULL, &dtv_cdevsw);
184 return config_fini_component(cfdriver_ioconf_dtv,
185 cfattach_ioconf_dtv, cfdata_ioconf_dtv);
186 #else
187 return 0;
188 #endif
189 default:
190 return ENOTTY;
191 }
192 }
193
194 static int
195 dtvopen(dev_t dev, int flags, int mode, lwp_t *l)
196 {
197 struct dtv_softc *sc;
198 struct dtv_ts *ts;
199 unsigned int n;
200 int error;
201
202 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
203 return ENXIO;
204 if (sc->sc_dying == true)
205 return ENODEV;
206 ts = &sc->sc_ts;
207
208 n = atomic_cas_uint(&sc->sc_open, 0, 1);
209 if (n == 0) {
210 error = dtv_device_open(sc, flags);
211 if (error) {
212 atomic_swap_uint(&sc->sc_open, 0);
213 return error;
214 }
215 sc->sc_bufsize = DTV_DEFAULT_BUFSIZE;
216 sc->sc_bufsize_chg = true;
217 memset(ts->ts_pidfilter, 0, sizeof(ts->ts_pidfilter));
218 }
219
220 return 0;
221 }
222
223 static int
224 dtvclose(dev_t dev, int flags, int mode, lwp_t *l)
225 {
226 struct dtv_softc *sc;
227
228 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
229 return ENXIO;
230
231 dtv_device_close(sc);
232 dtv_buffer_destroy(sc);
233 atomic_swap_uint(&sc->sc_open, 0);
234
235 return 0;
236 }
237
238 static int
239 dtvread(dev_t dev, struct uio *uio, int flags)
240 {
241 struct dtv_softc *sc;
242
243 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
244 return ENXIO;
245
246 if (ISDTVDVR(dev))
247 return dtv_buffer_read(sc, uio, flags);
248
249 return ENXIO;
250 }
251
252 static int
253 dtvioctl(dev_t dev, u_long cmd, void *ptr, int flags, lwp_t *l)
254 {
255 struct dtv_softc *sc;
256
257 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
258 return ENXIO;
259
260 if (ISDTVFRONTEND(dev)) {
261 return dtv_frontend_ioctl(sc, cmd, ptr, flags);
262 } else if (ISDTVDEMUX(dev)) {
263 return dtv_demux_ioctl(sc, cmd, ptr, flags);
264 }
265
266 return EINVAL;
267 }
268
269 static int
270 dtvpoll(dev_t dev, int events, lwp_t *l)
271 {
272 struct dtv_softc *sc;
273
274 if ((sc = device_lookup_private(&dtv_cd, DTVUNIT(dev))) == NULL)
275 return POLLERR;
276
277 if (ISDTVFRONTEND(dev)) {
278 return POLLPRI|POLLIN; /* XXX event */
279 } else if (ISDTVDEMUX(dev)) {
280 return POLLIN|POLLOUT; /* XXX */
281 } else if (ISDTVDVR(dev)) {
282 return dtv_buffer_poll(sc, events, l);
283 }
284
285 return POLLERR;
286 }
287
288 int
289 dtv_print(void *arg, const char *pnp)
290 {
291 if (pnp)
292 aprint_normal("dtv at %s", pnp);
293
294 return UNCONF;
295 }
296