1 1.9 riastrad /* $NetBSD: dtv_buffer.c,v 1.9 2018/09/03 16:29:30 riastradh Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 3. All advertising materials mentioning features or use of this software 16 1.1 jmcneill * must display the following acknowledgement: 17 1.1 jmcneill * This product includes software developed by Jared D. McNeill. 18 1.1 jmcneill * 4. Neither the name of The NetBSD Foundation nor the names of its 19 1.1 jmcneill * contributors may be used to endorse or promote products derived 20 1.1 jmcneill * from this software without specific prior written permission. 21 1.1 jmcneill * 22 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE. 33 1.1 jmcneill */ 34 1.1 jmcneill 35 1.1 jmcneill #include <sys/cdefs.h> 36 1.9 riastrad __KERNEL_RCSID(0, "$NetBSD: dtv_buffer.c,v 1.9 2018/09/03 16:29:30 riastradh Exp $"); 37 1.1 jmcneill 38 1.1 jmcneill #include <sys/param.h> 39 1.1 jmcneill #include <sys/kernel.h> 40 1.1 jmcneill #include <sys/types.h> 41 1.1 jmcneill #include <sys/conf.h> 42 1.1 jmcneill #include <sys/device.h> 43 1.1 jmcneill #include <sys/vnode.h> 44 1.1 jmcneill #include <sys/poll.h> 45 1.1 jmcneill #include <sys/select.h> 46 1.1 jmcneill 47 1.1 jmcneill #include <dev/dtv/dtvvar.h> 48 1.1 jmcneill 49 1.4 jmcneill #define BLOCK_SIZE DTV_DEFAULT_BLOCKSIZE 50 1.4 jmcneill #define BLOCK_ALIGN(a) (((a) + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1)) 51 1.1 jmcneill 52 1.1 jmcneill static void 53 1.1 jmcneill dtv_buffer_write(struct dtv_softc *sc, const uint8_t *buf, size_t buflen) 54 1.1 jmcneill { 55 1.1 jmcneill struct dtv_stream *ds = &sc->sc_stream; 56 1.1 jmcneill struct dtv_buffer *db; 57 1.1 jmcneill struct dtv_scatter_io sio; 58 1.1 jmcneill size_t resid = buflen, avail; 59 1.1 jmcneill off_t offset = 0; 60 1.1 jmcneill 61 1.1 jmcneill KASSERT(buflen == TS_PKTLEN); 62 1.1 jmcneill 63 1.1 jmcneill while (resid > 0) { 64 1.4 jmcneill mutex_enter(&ds->ds_ingress_lock); 65 1.1 jmcneill 66 1.1 jmcneill if (SIMPLEQ_EMPTY(&ds->ds_ingress)) { 67 1.1 jmcneill aprint_debug_dev(sc->sc_dev, 68 1.3 jmcneill "dropping sample (%zu)\n", resid); 69 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 70 1.1 jmcneill return; 71 1.1 jmcneill } 72 1.1 jmcneill 73 1.1 jmcneill db = SIMPLEQ_FIRST(&ds->ds_ingress); 74 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 75 1.4 jmcneill 76 1.9 riastrad avail = uimin(db->db_length - db->db_bytesused, resid); 77 1.1 jmcneill if (dtv_scatter_io_init(&ds->ds_data, 78 1.1 jmcneill db->db_offset + db->db_bytesused, avail, &sio)) { 79 1.1 jmcneill dtv_scatter_io_copyin(&sio, buf + offset); 80 1.1 jmcneill db->db_bytesused += (avail - sio.sio_resid); 81 1.1 jmcneill offset += (avail - sio.sio_resid); 82 1.1 jmcneill resid -= (avail - sio.sio_resid); 83 1.1 jmcneill } 84 1.1 jmcneill 85 1.1 jmcneill if (db->db_bytesused == db->db_length) { 86 1.4 jmcneill mutex_enter(&ds->ds_ingress_lock); 87 1.1 jmcneill SIMPLEQ_REMOVE_HEAD(&ds->ds_ingress, db_entries); 88 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 89 1.4 jmcneill mutex_enter(&ds->ds_egress_lock); 90 1.1 jmcneill SIMPLEQ_INSERT_TAIL(&ds->ds_egress, db, db_entries); 91 1.4 jmcneill selnotify(&ds->ds_sel, 0, 0); 92 1.1 jmcneill cv_broadcast(&ds->ds_sample_cv); 93 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 94 1.1 jmcneill } 95 1.1 jmcneill } 96 1.1 jmcneill } 97 1.1 jmcneill 98 1.1 jmcneill void 99 1.7 jmcneill dtv_buffer_submit(void *priv, const struct dtv_payload *payload) 100 1.1 jmcneill { 101 1.7 jmcneill struct dtv_softc *sc = priv; 102 1.1 jmcneill struct dtv_ts *ts = &sc->sc_ts; 103 1.1 jmcneill const uint8_t *tspkt; 104 1.1 jmcneill unsigned int npkts, i; 105 1.1 jmcneill 106 1.1 jmcneill tspkt = payload->data; 107 1.1 jmcneill npkts = payload->size / TS_PKTLEN; 108 1.1 jmcneill for (i = 0; i < npkts; i++) { 109 1.5 jmcneill if (TS_HAS_SYNC(tspkt)) { 110 1.5 jmcneill if (ts->ts_pidfilter[TS_PID(tspkt)]) { 111 1.5 jmcneill dtv_buffer_write(sc, tspkt, TS_PKTLEN); 112 1.5 jmcneill } 113 1.6 jmcneill dtv_demux_write(sc, tspkt, TS_PKTLEN); 114 1.1 jmcneill } 115 1.1 jmcneill tspkt += TS_PKTLEN; 116 1.1 jmcneill } 117 1.1 jmcneill } 118 1.1 jmcneill 119 1.1 jmcneill static struct dtv_buffer * 120 1.1 jmcneill dtv_buffer_alloc(void) 121 1.1 jmcneill { 122 1.1 jmcneill return kmem_alloc(sizeof(struct dtv_buffer), KM_SLEEP); 123 1.1 jmcneill } 124 1.1 jmcneill 125 1.1 jmcneill static void 126 1.1 jmcneill dtv_buffer_free(struct dtv_buffer *db) 127 1.1 jmcneill { 128 1.1 jmcneill kmem_free(db, sizeof(*db)); 129 1.1 jmcneill } 130 1.1 jmcneill 131 1.2 jmcneill int 132 1.1 jmcneill dtv_buffer_realloc(struct dtv_softc *sc, size_t bufsize) 133 1.1 jmcneill { 134 1.1 jmcneill struct dtv_stream *ds = &sc->sc_stream; 135 1.1 jmcneill unsigned int i, nbufs, oldnbufs, minnbufs; 136 1.1 jmcneill struct dtv_buffer **oldbuf; 137 1.1 jmcneill off_t offset; 138 1.1 jmcneill int error; 139 1.1 jmcneill 140 1.4 jmcneill nbufs = BLOCK_ALIGN(bufsize) / BLOCK_SIZE; 141 1.1 jmcneill 142 1.1 jmcneill error = dtv_scatter_buf_set_size(&ds->ds_data, bufsize); 143 1.1 jmcneill if (error) 144 1.1 jmcneill return error; 145 1.1 jmcneill 146 1.1 jmcneill oldnbufs = ds->ds_nbufs; 147 1.1 jmcneill oldbuf = ds->ds_buf; 148 1.1 jmcneill 149 1.1 jmcneill ds->ds_nbufs = nbufs; 150 1.1 jmcneill if (nbufs > 0) { 151 1.1 jmcneill ds->ds_buf = kmem_alloc(sizeof(struct dtv_buffer *) * nbufs, 152 1.1 jmcneill KM_SLEEP); 153 1.1 jmcneill } else { 154 1.1 jmcneill ds->ds_buf = NULL; 155 1.1 jmcneill } 156 1.1 jmcneill 157 1.9 riastrad minnbufs = uimin(nbufs, oldnbufs); 158 1.1 jmcneill for (i = 0; i < minnbufs; i++) 159 1.1 jmcneill ds->ds_buf[i] = oldbuf[i]; 160 1.1 jmcneill for (; i < nbufs; i++) 161 1.1 jmcneill ds->ds_buf[i] = dtv_buffer_alloc(); 162 1.1 jmcneill for (; i < oldnbufs; i++) { 163 1.1 jmcneill dtv_buffer_free(oldbuf[i]); 164 1.1 jmcneill oldbuf[i] = NULL; 165 1.1 jmcneill } 166 1.1 jmcneill if (oldbuf != NULL) 167 1.1 jmcneill kmem_free(oldbuf, sizeof(struct dtv_buffer *) * oldnbufs); 168 1.1 jmcneill 169 1.1 jmcneill offset = 0; 170 1.1 jmcneill for (i = 0; i < nbufs; i++) { 171 1.1 jmcneill ds->ds_buf[i]->db_offset = offset; 172 1.1 jmcneill ds->ds_buf[i]->db_bytesused = 0; 173 1.4 jmcneill ds->ds_buf[i]->db_length = BLOCK_SIZE; 174 1.4 jmcneill offset += BLOCK_SIZE; 175 1.1 jmcneill } 176 1.1 jmcneill 177 1.1 jmcneill return 0; 178 1.1 jmcneill } 179 1.1 jmcneill 180 1.1 jmcneill static struct dtv_buffer * 181 1.1 jmcneill dtv_stream_dequeue(struct dtv_stream *ds) 182 1.1 jmcneill { 183 1.1 jmcneill struct dtv_buffer *db; 184 1.1 jmcneill 185 1.1 jmcneill if (!SIMPLEQ_EMPTY(&ds->ds_egress)) { 186 1.1 jmcneill db = SIMPLEQ_FIRST(&ds->ds_egress); 187 1.1 jmcneill SIMPLEQ_REMOVE_HEAD(&ds->ds_egress, db_entries); 188 1.1 jmcneill return db; 189 1.1 jmcneill } 190 1.1 jmcneill 191 1.1 jmcneill return NULL; 192 1.1 jmcneill } 193 1.1 jmcneill 194 1.1 jmcneill static void 195 1.1 jmcneill dtv_stream_enqueue(struct dtv_stream *ds, struct dtv_buffer *db) 196 1.1 jmcneill { 197 1.1 jmcneill db->db_bytesused = 0; 198 1.1 jmcneill SIMPLEQ_INSERT_TAIL(&ds->ds_ingress, db, db_entries); 199 1.1 jmcneill } 200 1.1 jmcneill 201 1.1 jmcneill int 202 1.2 jmcneill dtv_buffer_setup(struct dtv_softc *sc) 203 1.1 jmcneill { 204 1.1 jmcneill struct dtv_stream *ds = &sc->sc_stream; 205 1.1 jmcneill unsigned int i; 206 1.1 jmcneill 207 1.4 jmcneill mutex_enter(&ds->ds_ingress_lock); 208 1.1 jmcneill for (i = 0; i < ds->ds_nbufs; i++) 209 1.1 jmcneill dtv_stream_enqueue(ds, ds->ds_buf[i]); 210 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 211 1.1 jmcneill 212 1.1 jmcneill return 0; 213 1.1 jmcneill } 214 1.1 jmcneill 215 1.1 jmcneill int 216 1.1 jmcneill dtv_buffer_destroy(struct dtv_softc *sc) 217 1.1 jmcneill { 218 1.1 jmcneill struct dtv_stream *ds = &sc->sc_stream; 219 1.1 jmcneill 220 1.4 jmcneill mutex_enter(&ds->ds_ingress_lock); 221 1.1 jmcneill while (SIMPLEQ_FIRST(&ds->ds_ingress)) 222 1.1 jmcneill SIMPLEQ_REMOVE_HEAD(&ds->ds_ingress, db_entries); 223 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 224 1.4 jmcneill mutex_enter(&ds->ds_egress_lock); 225 1.1 jmcneill while (SIMPLEQ_FIRST(&ds->ds_egress)) 226 1.1 jmcneill SIMPLEQ_REMOVE_HEAD(&ds->ds_egress, db_entries); 227 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 228 1.1 jmcneill 229 1.1 jmcneill return 0; 230 1.1 jmcneill } 231 1.1 jmcneill 232 1.1 jmcneill int 233 1.1 jmcneill dtv_buffer_read(struct dtv_softc *sc, struct uio *uio, int flags) 234 1.1 jmcneill { 235 1.1 jmcneill struct dtv_stream *ds = &sc->sc_stream; 236 1.1 jmcneill struct dtv_buffer *db; 237 1.1 jmcneill struct dtv_scatter_io sio; 238 1.1 jmcneill off_t offset; 239 1.1 jmcneill size_t len, bread = 0; 240 1.1 jmcneill int error; 241 1.1 jmcneill 242 1.1 jmcneill while (uio->uio_resid > 0) { 243 1.1 jmcneill retry: 244 1.4 jmcneill mutex_enter(&ds->ds_egress_lock); 245 1.1 jmcneill while (SIMPLEQ_EMPTY(&ds->ds_egress)) { 246 1.1 jmcneill if (flags & IO_NDELAY) { 247 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 248 1.4 jmcneill return EWOULDBLOCK; 249 1.1 jmcneill } 250 1.1 jmcneill 251 1.4 jmcneill error = cv_wait_sig(&ds->ds_sample_cv, 252 1.4 jmcneill &ds->ds_egress_lock); 253 1.1 jmcneill if (error) { 254 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 255 1.1 jmcneill return EINTR; 256 1.1 jmcneill } 257 1.1 jmcneill } 258 1.1 jmcneill db = SIMPLEQ_FIRST(&ds->ds_egress); 259 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 260 1.1 jmcneill 261 1.1 jmcneill if (db->db_bytesused == 0) { 262 1.4 jmcneill mutex_enter(&ds->ds_egress_lock); 263 1.1 jmcneill db = dtv_stream_dequeue(ds); 264 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 265 1.4 jmcneill mutex_enter(&ds->ds_ingress_lock); 266 1.1 jmcneill dtv_stream_enqueue(ds, db); 267 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 268 1.1 jmcneill ds->ds_bytesread = 0; 269 1.1 jmcneill goto retry; 270 1.1 jmcneill } 271 1.1 jmcneill 272 1.9 riastrad len = uimin(uio->uio_resid, db->db_bytesused - ds->ds_bytesread); 273 1.1 jmcneill offset = db->db_offset + ds->ds_bytesread; 274 1.1 jmcneill 275 1.1 jmcneill if (dtv_scatter_io_init(&ds->ds_data, offset, len, &sio)) { 276 1.1 jmcneill error = dtv_scatter_io_uiomove(&sio, uio); 277 1.1 jmcneill if (error == EFAULT) 278 1.1 jmcneill return EFAULT; 279 1.1 jmcneill ds->ds_bytesread += (len - sio.sio_resid); 280 1.1 jmcneill bread += (len - sio.sio_resid); 281 1.1 jmcneill } 282 1.1 jmcneill 283 1.1 jmcneill if (ds->ds_bytesread >= db->db_bytesused) { 284 1.4 jmcneill mutex_enter(&ds->ds_egress_lock); 285 1.1 jmcneill db = dtv_stream_dequeue(ds); 286 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 287 1.4 jmcneill mutex_enter(&ds->ds_ingress_lock); 288 1.1 jmcneill dtv_stream_enqueue(ds, db); 289 1.4 jmcneill mutex_exit(&ds->ds_ingress_lock); 290 1.1 jmcneill 291 1.1 jmcneill ds->ds_bytesread = 0; 292 1.1 jmcneill } 293 1.1 jmcneill } 294 1.1 jmcneill 295 1.1 jmcneill return 0; 296 1.1 jmcneill } 297 1.1 jmcneill 298 1.1 jmcneill int 299 1.1 jmcneill dtv_buffer_poll(struct dtv_softc *sc, int events, lwp_t *l) 300 1.1 jmcneill { 301 1.1 jmcneill struct dtv_stream *ds = &sc->sc_stream; 302 1.1 jmcneill int revents = 0; 303 1.4 jmcneill #ifdef DTV_BUFFER_DEBUG 304 1.4 jmcneill struct dtv_buffer *db; 305 1.4 jmcneill size_t bufsize = 0; 306 1.4 jmcneill #endif 307 1.1 jmcneill 308 1.4 jmcneill mutex_enter(&ds->ds_egress_lock); 309 1.1 jmcneill if (!SIMPLEQ_EMPTY(&ds->ds_egress)) { 310 1.4 jmcneill #ifdef DTV_BUFFER_DEBUG 311 1.4 jmcneill SIMPLEQ_FOREACH(db, &ds->ds_egress, db_entries) 312 1.4 jmcneill bufsize += db->db_bytesused; 313 1.4 jmcneill #endif 314 1.1 jmcneill revents |= (POLLIN | POLLOUT | POLLPRI); 315 1.1 jmcneill } else { 316 1.1 jmcneill selrecord(l, &ds->ds_sel); 317 1.1 jmcneill } 318 1.4 jmcneill mutex_exit(&ds->ds_egress_lock); 319 1.4 jmcneill 320 1.4 jmcneill #ifdef DTV_BUFFER_DEBUG 321 1.4 jmcneill device_printf(sc->sc_dev, "%s: bufsize=%zu\n", __func__, bufsize); 322 1.4 jmcneill #endif 323 1.1 jmcneill 324 1.1 jmcneill return revents; 325 1.1 jmcneill } 326