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