Home | History | Annotate | Line # | Download | only in dtv
dtv_buffer.c revision 1.7
      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 (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.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