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@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