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