Home | History | Annotate | Line # | Download | only in dtv
      1 /* $NetBSD: dtv_buffer.c,v 1.9 2018/09/03 16:29:30 riastradh 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_buffer.c,v 1.9 2018/09/03 16:29:30 riastradh 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 = uimin(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 	} else {
    154 		ds->ds_buf = NULL;
    155 	}
    156 
    157 	minnbufs = uimin(nbufs, oldnbufs);
    158 	for (i = 0; i < minnbufs; i++)
    159 		ds->ds_buf[i] = oldbuf[i];
    160 	for (; i < nbufs; i++)
    161 		ds->ds_buf[i] = dtv_buffer_alloc();
    162 	for (; i < oldnbufs; i++) {
    163 		dtv_buffer_free(oldbuf[i]);
    164 		oldbuf[i] = NULL;
    165 	}
    166 	if (oldbuf != NULL)
    167 		kmem_free(oldbuf, sizeof(struct dtv_buffer *) * oldnbufs);
    168 
    169 	offset = 0;
    170 	for (i = 0; i < nbufs; i++) {
    171 		ds->ds_buf[i]->db_offset = offset;
    172 		ds->ds_buf[i]->db_bytesused = 0;
    173 		ds->ds_buf[i]->db_length = BLOCK_SIZE;
    174 		offset += BLOCK_SIZE;
    175 	}
    176 
    177 	return 0;
    178 }
    179 
    180 static struct dtv_buffer *
    181 dtv_stream_dequeue(struct dtv_stream *ds)
    182 {
    183 	struct dtv_buffer *db;
    184 
    185 	if (!SIMPLEQ_EMPTY(&ds->ds_egress)) {
    186 		db = SIMPLEQ_FIRST(&ds->ds_egress);
    187 		SIMPLEQ_REMOVE_HEAD(&ds->ds_egress, db_entries);
    188 		return db;
    189 	}
    190 
    191 	return NULL;
    192 }
    193 
    194 static void
    195 dtv_stream_enqueue(struct dtv_stream *ds, struct dtv_buffer *db)
    196 {
    197 	db->db_bytesused = 0;
    198 	SIMPLEQ_INSERT_TAIL(&ds->ds_ingress, db, db_entries);
    199 }
    200 
    201 int
    202 dtv_buffer_setup(struct dtv_softc *sc)
    203 {
    204 	struct dtv_stream *ds = &sc->sc_stream;
    205 	unsigned int i;
    206 
    207 	mutex_enter(&ds->ds_ingress_lock);
    208 	for (i = 0; i < ds->ds_nbufs; i++)
    209 		dtv_stream_enqueue(ds, ds->ds_buf[i]);
    210 	mutex_exit(&ds->ds_ingress_lock);
    211 
    212 	return 0;
    213 }
    214 
    215 int
    216 dtv_buffer_destroy(struct dtv_softc *sc)
    217 {
    218 	struct dtv_stream *ds = &sc->sc_stream;
    219 
    220 	mutex_enter(&ds->ds_ingress_lock);
    221 	while (SIMPLEQ_FIRST(&ds->ds_ingress))
    222 		SIMPLEQ_REMOVE_HEAD(&ds->ds_ingress, db_entries);
    223 	mutex_exit(&ds->ds_ingress_lock);
    224 	mutex_enter(&ds->ds_egress_lock);
    225 	while (SIMPLEQ_FIRST(&ds->ds_egress))
    226 		SIMPLEQ_REMOVE_HEAD(&ds->ds_egress, db_entries);
    227 	mutex_exit(&ds->ds_egress_lock);
    228 
    229 	return 0;
    230 }
    231 
    232 int
    233 dtv_buffer_read(struct dtv_softc *sc, struct uio *uio, int flags)
    234 {
    235 	struct dtv_stream *ds = &sc->sc_stream;
    236 	struct dtv_buffer *db;
    237 	struct dtv_scatter_io sio;
    238 	off_t offset;
    239 	size_t len, bread = 0;
    240 	int error;
    241 
    242 	while (uio->uio_resid > 0) {
    243 retry:
    244 		mutex_enter(&ds->ds_egress_lock);
    245 		while (SIMPLEQ_EMPTY(&ds->ds_egress)) {
    246 			if (flags & IO_NDELAY) {
    247 				mutex_exit(&ds->ds_egress_lock);
    248 				return EWOULDBLOCK;
    249 			}
    250 
    251 			error = cv_wait_sig(&ds->ds_sample_cv,
    252 			    &ds->ds_egress_lock);
    253 			if (error) {
    254 				mutex_exit(&ds->ds_egress_lock);
    255 				return EINTR;
    256 			}
    257 		}
    258 		db = SIMPLEQ_FIRST(&ds->ds_egress);
    259 		mutex_exit(&ds->ds_egress_lock);
    260 
    261 		if (db->db_bytesused == 0) {
    262 			mutex_enter(&ds->ds_egress_lock);
    263 			db = dtv_stream_dequeue(ds);
    264 			mutex_exit(&ds->ds_egress_lock);
    265 			mutex_enter(&ds->ds_ingress_lock);
    266 			dtv_stream_enqueue(ds, db);
    267 			mutex_exit(&ds->ds_ingress_lock);
    268 			ds->ds_bytesread = 0;
    269 			goto retry;
    270 		}
    271 
    272 		len = uimin(uio->uio_resid, db->db_bytesused - ds->ds_bytesread);
    273 		offset = db->db_offset + ds->ds_bytesread;
    274 
    275 		if (dtv_scatter_io_init(&ds->ds_data, offset, len, &sio)) {
    276 			error = dtv_scatter_io_uiomove(&sio, uio);
    277 			if (error == EFAULT)
    278 				return EFAULT;
    279 			ds->ds_bytesread += (len - sio.sio_resid);
    280 			bread += (len - sio.sio_resid);
    281 		}
    282 
    283 		if (ds->ds_bytesread >= db->db_bytesused) {
    284 			mutex_enter(&ds->ds_egress_lock);
    285 			db = dtv_stream_dequeue(ds);
    286 			mutex_exit(&ds->ds_egress_lock);
    287 			mutex_enter(&ds->ds_ingress_lock);
    288 			dtv_stream_enqueue(ds, db);
    289 			mutex_exit(&ds->ds_ingress_lock);
    290 
    291 			ds->ds_bytesread = 0;
    292 		}
    293 	}
    294 
    295 	return 0;
    296 }
    297 
    298 int
    299 dtv_buffer_poll(struct dtv_softc *sc, int events, lwp_t *l)
    300 {
    301 	struct dtv_stream *ds = &sc->sc_stream;
    302 	int revents = 0;
    303 #ifdef DTV_BUFFER_DEBUG
    304 	struct dtv_buffer *db;
    305 	size_t bufsize = 0;
    306 #endif
    307 
    308 	mutex_enter(&ds->ds_egress_lock);
    309 	if (!SIMPLEQ_EMPTY(&ds->ds_egress)) {
    310 #ifdef DTV_BUFFER_DEBUG
    311 		SIMPLEQ_FOREACH(db, &ds->ds_egress, db_entries)
    312 			bufsize += db->db_bytesused;
    313 #endif
    314 		revents |= (POLLIN | POLLOUT | POLLPRI);
    315 	} else {
    316 		selrecord(l, &ds->ds_sel);
    317 	}
    318 	mutex_exit(&ds->ds_egress_lock);
    319 
    320 #ifdef DTV_BUFFER_DEBUG
    321 	device_printf(sc->sc_dev, "%s: bufsize=%zu\n", __func__, bufsize);
    322 #endif
    323 
    324 	return revents;
    325 }
    326