Home | History | Annotate | Line # | Download | only in dtv
      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