1 | /* $NetBSD: dtv_buffer.c,v 1.7 2011/08/09 01:42:24 jmcneill Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2011 Jared D. McNeill <jmcneill@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_buffer.c,v 1.7 2011/08/09 01:42:24 jmcneill Exp $" ); |
37 | |
38 | #include <sys/param.h> |
39 | #include <sys/kernel.h> |
40 | #include <sys/types.h> |
41 | #include <sys/conf.h> |
42 | #include <sys/device.h> |
43 | #include <sys/vnode.h> |
44 | #include <sys/poll.h> |
45 | #include <sys/select.h> |
46 | |
47 | #include <dev/dtv/dtvvar.h> |
48 | |
49 | #define BLOCK_SIZE DTV_DEFAULT_BLOCKSIZE |
50 | #define BLOCK_ALIGN(a) (((a) + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1)) |
51 | |
52 | static void |
53 | dtv_buffer_write(struct dtv_softc *sc, const uint8_t *buf, size_t buflen) |
54 | { |
55 | struct dtv_stream *ds = &sc->sc_stream; |
56 | struct dtv_buffer *db; |
57 | struct dtv_scatter_io sio; |
58 | size_t resid = buflen, avail; |
59 | off_t offset = 0; |
60 | |
61 | KASSERT(buflen == TS_PKTLEN); |
62 | |
63 | while (resid > 0) { |
64 | mutex_enter(&ds->ds_ingress_lock); |
65 | |
66 | if (SIMPLEQ_EMPTY(&ds->ds_ingress)) { |
67 | aprint_debug_dev(sc->sc_dev, |
68 | "dropping sample (%zu)\n" , resid); |
69 | mutex_exit(&ds->ds_ingress_lock); |
70 | return; |
71 | } |
72 | |
73 | db = SIMPLEQ_FIRST(&ds->ds_ingress); |
74 | mutex_exit(&ds->ds_ingress_lock); |
75 | |
76 | avail = min(db->db_length - db->db_bytesused, resid); |
77 | if (dtv_scatter_io_init(&ds->ds_data, |
78 | db->db_offset + db->db_bytesused, avail, &sio)) { |
79 | dtv_scatter_io_copyin(&sio, buf + offset); |
80 | db->db_bytesused += (avail - sio.sio_resid); |
81 | offset += (avail - sio.sio_resid); |
82 | resid -= (avail - sio.sio_resid); |
83 | } |
84 | |
85 | if (db->db_bytesused == db->db_length) { |
86 | mutex_enter(&ds->ds_ingress_lock); |
87 | SIMPLEQ_REMOVE_HEAD(&ds->ds_ingress, db_entries); |
88 | mutex_exit(&ds->ds_ingress_lock); |
89 | mutex_enter(&ds->ds_egress_lock); |
90 | SIMPLEQ_INSERT_TAIL(&ds->ds_egress, db, db_entries); |
91 | selnotify(&ds->ds_sel, 0, 0); |
92 | cv_broadcast(&ds->ds_sample_cv); |
93 | mutex_exit(&ds->ds_egress_lock); |
94 | } |
95 | } |
96 | } |
97 | |
98 | void |
99 | dtv_buffer_submit(void *priv, const struct dtv_payload *payload) |
100 | { |
101 | struct dtv_softc *sc = priv; |
102 | struct dtv_ts *ts = &sc->sc_ts; |
103 | const uint8_t *tspkt; |
104 | unsigned int npkts, i; |
105 | |
106 | tspkt = payload->data; |
107 | npkts = payload->size / TS_PKTLEN; |
108 | for (i = 0; i < npkts; i++) { |
109 | if (TS_HAS_SYNC(tspkt)) { |
110 | if (ts->ts_pidfilter[TS_PID(tspkt)]) { |
111 | dtv_buffer_write(sc, tspkt, TS_PKTLEN); |
112 | } |
113 | dtv_demux_write(sc, tspkt, TS_PKTLEN); |
114 | } |
115 | tspkt += TS_PKTLEN; |
116 | } |
117 | } |
118 | |
119 | static struct dtv_buffer * |
120 | dtv_buffer_alloc(void) |
121 | { |
122 | return kmem_alloc(sizeof(struct dtv_buffer), KM_SLEEP); |
123 | } |
124 | |
125 | static void |
126 | dtv_buffer_free(struct dtv_buffer *db) |
127 | { |
128 | kmem_free(db, sizeof(*db)); |
129 | } |
130 | |
131 | int |
132 | dtv_buffer_realloc(struct dtv_softc *sc, size_t bufsize) |
133 | { |
134 | struct dtv_stream *ds = &sc->sc_stream; |
135 | unsigned int i, nbufs, oldnbufs, minnbufs; |
136 | struct dtv_buffer **oldbuf; |
137 | off_t offset; |
138 | int error; |
139 | |
140 | nbufs = BLOCK_ALIGN(bufsize) / BLOCK_SIZE; |
141 | |
142 | error = dtv_scatter_buf_set_size(&ds->ds_data, bufsize); |
143 | if (error) |
144 | return error; |
145 | |
146 | oldnbufs = ds->ds_nbufs; |
147 | oldbuf = ds->ds_buf; |
148 | |
149 | ds->ds_nbufs = nbufs; |
150 | if (nbufs > 0) { |
151 | ds->ds_buf = kmem_alloc(sizeof(struct dtv_buffer *) * nbufs, |
152 | KM_SLEEP); |
153 | if (ds->ds_buf == NULL) { |
154 | ds->ds_nbufs = oldnbufs; |
155 | ds->ds_buf = oldbuf; |
156 | return ENOMEM; |
157 | } |
158 | } else { |
159 | ds->ds_buf = NULL; |
160 | } |
161 | |
162 | minnbufs = min(nbufs, oldnbufs); |
163 | for (i = 0; i < minnbufs; i++) |
164 | ds->ds_buf[i] = oldbuf[i]; |
165 | for (; i < nbufs; i++) |
166 | ds->ds_buf[i] = dtv_buffer_alloc(); |
167 | for (; i < oldnbufs; i++) { |
168 | dtv_buffer_free(oldbuf[i]); |
169 | oldbuf[i] = NULL; |
170 | } |
171 | if (oldbuf != NULL) |
172 | kmem_free(oldbuf, sizeof(struct dtv_buffer *) * oldnbufs); |
173 | |
174 | offset = 0; |
175 | for (i = 0; i < nbufs; i++) { |
176 | ds->ds_buf[i]->db_offset = offset; |
177 | ds->ds_buf[i]->db_bytesused = 0; |
178 | ds->ds_buf[i]->db_length = BLOCK_SIZE; |
179 | offset += BLOCK_SIZE; |
180 | } |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | static struct dtv_buffer * |
186 | dtv_stream_dequeue(struct dtv_stream *ds) |
187 | { |
188 | struct dtv_buffer *db; |
189 | |
190 | if (!SIMPLEQ_EMPTY(&ds->ds_egress)) { |
191 | db = SIMPLEQ_FIRST(&ds->ds_egress); |
192 | SIMPLEQ_REMOVE_HEAD(&ds->ds_egress, db_entries); |
193 | return db; |
194 | } |
195 | |
196 | return NULL; |
197 | } |
198 | |
199 | static void |
200 | dtv_stream_enqueue(struct dtv_stream *ds, struct dtv_buffer *db) |
201 | { |
202 | db->db_bytesused = 0; |
203 | SIMPLEQ_INSERT_TAIL(&ds->ds_ingress, db, db_entries); |
204 | } |
205 | |
206 | int |
207 | dtv_buffer_setup(struct dtv_softc *sc) |
208 | { |
209 | struct dtv_stream *ds = &sc->sc_stream; |
210 | unsigned int i; |
211 | |
212 | mutex_enter(&ds->ds_ingress_lock); |
213 | for (i = 0; i < ds->ds_nbufs; i++) |
214 | dtv_stream_enqueue(ds, ds->ds_buf[i]); |
215 | mutex_exit(&ds->ds_ingress_lock); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | int |
221 | dtv_buffer_destroy(struct dtv_softc *sc) |
222 | { |
223 | struct dtv_stream *ds = &sc->sc_stream; |
224 | |
225 | mutex_enter(&ds->ds_ingress_lock); |
226 | while (SIMPLEQ_FIRST(&ds->ds_ingress)) |
227 | SIMPLEQ_REMOVE_HEAD(&ds->ds_ingress, db_entries); |
228 | mutex_exit(&ds->ds_ingress_lock); |
229 | mutex_enter(&ds->ds_egress_lock); |
230 | while (SIMPLEQ_FIRST(&ds->ds_egress)) |
231 | SIMPLEQ_REMOVE_HEAD(&ds->ds_egress, db_entries); |
232 | mutex_exit(&ds->ds_egress_lock); |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | int |
238 | dtv_buffer_read(struct dtv_softc *sc, struct uio *uio, int flags) |
239 | { |
240 | struct dtv_stream *ds = &sc->sc_stream; |
241 | struct dtv_buffer *db; |
242 | struct dtv_scatter_io sio; |
243 | off_t offset; |
244 | size_t len, bread = 0; |
245 | int error; |
246 | |
247 | while (uio->uio_resid > 0) { |
248 | retry: |
249 | mutex_enter(&ds->ds_egress_lock); |
250 | while (SIMPLEQ_EMPTY(&ds->ds_egress)) { |
251 | if (flags & IO_NDELAY) { |
252 | mutex_exit(&ds->ds_egress_lock); |
253 | return EWOULDBLOCK; |
254 | } |
255 | |
256 | error = cv_wait_sig(&ds->ds_sample_cv, |
257 | &ds->ds_egress_lock); |
258 | if (error) { |
259 | mutex_exit(&ds->ds_egress_lock); |
260 | return EINTR; |
261 | } |
262 | } |
263 | db = SIMPLEQ_FIRST(&ds->ds_egress); |
264 | mutex_exit(&ds->ds_egress_lock); |
265 | |
266 | if (db->db_bytesused == 0) { |
267 | mutex_enter(&ds->ds_egress_lock); |
268 | db = dtv_stream_dequeue(ds); |
269 | mutex_exit(&ds->ds_egress_lock); |
270 | mutex_enter(&ds->ds_ingress_lock); |
271 | dtv_stream_enqueue(ds, db); |
272 | mutex_exit(&ds->ds_ingress_lock); |
273 | ds->ds_bytesread = 0; |
274 | goto retry; |
275 | } |
276 | |
277 | len = min(uio->uio_resid, db->db_bytesused - ds->ds_bytesread); |
278 | offset = db->db_offset + ds->ds_bytesread; |
279 | |
280 | if (dtv_scatter_io_init(&ds->ds_data, offset, len, &sio)) { |
281 | error = dtv_scatter_io_uiomove(&sio, uio); |
282 | if (error == EFAULT) |
283 | return EFAULT; |
284 | ds->ds_bytesread += (len - sio.sio_resid); |
285 | bread += (len - sio.sio_resid); |
286 | } |
287 | |
288 | if (ds->ds_bytesread >= db->db_bytesused) { |
289 | mutex_enter(&ds->ds_egress_lock); |
290 | db = dtv_stream_dequeue(ds); |
291 | mutex_exit(&ds->ds_egress_lock); |
292 | mutex_enter(&ds->ds_ingress_lock); |
293 | dtv_stream_enqueue(ds, db); |
294 | mutex_exit(&ds->ds_ingress_lock); |
295 | |
296 | ds->ds_bytesread = 0; |
297 | } |
298 | } |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | int |
304 | dtv_buffer_poll(struct dtv_softc *sc, int events, lwp_t *l) |
305 | { |
306 | struct dtv_stream *ds = &sc->sc_stream; |
307 | int revents = 0; |
308 | #ifdef DTV_BUFFER_DEBUG |
309 | struct dtv_buffer *db; |
310 | size_t bufsize = 0; |
311 | #endif |
312 | |
313 | mutex_enter(&ds->ds_egress_lock); |
314 | if (!SIMPLEQ_EMPTY(&ds->ds_egress)) { |
315 | #ifdef DTV_BUFFER_DEBUG |
316 | SIMPLEQ_FOREACH(db, &ds->ds_egress, db_entries) |
317 | bufsize += db->db_bytesused; |
318 | #endif |
319 | revents |= (POLLIN | POLLOUT | POLLPRI); |
320 | } else { |
321 | selrecord(l, &ds->ds_sel); |
322 | } |
323 | mutex_exit(&ds->ds_egress_lock); |
324 | |
325 | #ifdef DTV_BUFFER_DEBUG |
326 | device_printf(sc->sc_dev, "%s: bufsize=%zu\n" , __func__, bufsize); |
327 | #endif |
328 | |
329 | return revents; |
330 | } |
331 | |