1 /* 2 * Copyright (C) 2003 3 * Hidetoshi Shimokawa. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * 16 * This product includes software developed by Hidetoshi Shimokawa. 17 * 18 * 4. Neither the name of the author nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $FreeBSD: src/usr.sbin/fwcontrol/fwdv.c,v 1.7 2007/06/17 10:20:55 simokawa Exp $ 35 */ 36 #include <sys/param.h> 37 #ifndef __HAIKU__ 38 #include <sys/ioctl.h> 39 #endif 40 #include <sys/time.h> 41 #include <sys/types.h> 42 #include <sys/uio.h> 43 44 #if __FreeBSD_version >= 500000 || defined(__HAIKU__) 45 #include <arpa/inet.h> 46 #endif 47 48 #include <err.h> 49 #include <errno.h> 50 #include <unistd.h> 51 #include <fcntl.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #ifdef __HAIKU__ 56 #include <stdint.h> 57 #include <endian.h> 58 #include "firewire.h" 59 #include "iec68113.h" 60 #else 61 #include <sysexits.h> 62 63 #include <dev/firewire/firewire.h> 64 #include <dev/firewire/iec68113.h> 65 #endif 66 67 #include "fwmethods.h" 68 69 #define DEBUG 0 70 #define FIX_FRAME 1 71 72 struct frac { 73 int n,d; 74 }; 75 76 struct frac frame_cycle[2] = { 77 {8000*100, 2997}, /* NTSC 8000 cycle / 29.97 Hz */ 78 {320, 1}, /* PAL 8000 cycle / 25 Hz */ 79 }; 80 int npackets[] = { 81 250 /* NTSC */, 82 300 /* PAL */ 83 }; 84 struct frac pad_rate[2] = { 85 {203, 2997}, /* = (8000 - 29.97 * 250)/(29.97 * 250) */ 86 {1, 15}, /* = (8000 - 25 * 300)/(25 * 300) */ 87 }; 88 char *system_name[] = {"NTSC", "PAL"}; 89 int frame_rate[] = {30, 25}; 90 91 #define PSIZE 512 92 #define DSIZE 480 93 #define NCHUNK 64 94 95 #define NPACKET_R 256 96 #define NPACKET_T 255 97 #define TNBUF 100 /* XXX too large value causes block noise */ 98 #define NEMPTY 10 /* depends on TNBUF */ 99 #define RBUFSIZE (PSIZE * NPACKET_R) 100 #define MAXBLOCKS (300) 101 #define CYCLE_FRAC 0xc00 102 103 void 104 dvrecv(int d, const char *filename, char ich, int count) 105 { 106 struct fw_isochreq isoreq; 107 struct fw_isobufreq bufreq; 108 struct dvdbc *dv; 109 struct ciphdr *ciph; 110 struct fw_pkt *pkt; 111 char *pad, *buf; 112 u_int32_t *ptr; 113 int len, tlen, npad, fd, k, m, vec, system = -1, nb; 114 int nblocks[] = {250 /* NTSC */, 300 /* PAL */}; 115 struct iovec wbuf[NPACKET_R]; 116 117 if(strcmp(filename, "-") == 0) { 118 fd = STDOUT_FILENO; 119 } else { 120 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660); 121 if (fd == -1) 122 err(EX_NOINPUT, filename); 123 } 124 buf = malloc(RBUFSIZE); 125 pad = malloc(DSIZE*MAXBLOCKS); 126 memset(pad, 0xff, DSIZE*MAXBLOCKS); 127 bzero(wbuf, sizeof(wbuf)); 128 129 bufreq.rx.nchunk = NCHUNK; 130 bufreq.rx.npacket = NPACKET_R; 131 bufreq.rx.psize = PSIZE; 132 bufreq.tx.nchunk = 0; 133 bufreq.tx.npacket = 0; 134 bufreq.tx.psize = 0; 135 if (ioctl(d, FW_SSTBUF, &bufreq) < 0) 136 err(1, "ioctl FW_SSTBUF"); 137 138 isoreq.ch = ich & 0x3f; 139 isoreq.tag = (ich >> 6) & 3; 140 141 if (ioctl(d, FW_SRSTREAM, &isoreq) < 0) 142 err(1, "ioctl"); 143 144 k = m = 0; 145 while (count <= 0 || k <= count) { 146 #if 0 147 tlen = 0; 148 while ((len = read(d, buf + tlen, PSIZE 149 /* RBUFSIZE - tlen */)) > 0) { 150 if (len < 0) { 151 if (errno == EAGAIN) { 152 fprintf(stderr, "(EAGAIN)\n"); 153 fflush(stderr); 154 if (len <= 0) 155 continue; 156 } else 157 err(1, "read failed"); 158 } 159 tlen += len; 160 if ((RBUFSIZE - tlen) < PSIZE) 161 break; 162 }; 163 #else 164 tlen = len = read(d, buf, RBUFSIZE); 165 if (len < 0) { 166 if (errno == EAGAIN) { 167 fprintf(stderr, "(EAGAIN) - push 'Play'?\n"); 168 fflush(stderr); 169 if (len <= 0) 170 continue; 171 } else 172 err(1, "read failed"); 173 } 174 #endif 175 vec = 0; 176 ptr = (u_int32_t *) buf; 177 again: 178 pkt = (struct fw_pkt *) ptr; 179 #if DEBUG 180 fprintf(stderr, "%08x %08x %08x %08x\n", 181 htonl(ptr[0]), htonl(ptr[1]), 182 htonl(ptr[2]), htonl(ptr[3])); 183 #endif 184 ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */ 185 if (ciph->fmt != CIP_FMT_DVCR) 186 errx(1, "unknown format 0x%x", ciph->fmt); 187 ptr = (u_int32_t *) (ciph + 1); /* skip cip header */ 188 #if DEBUG 189 if (ciph->fdf.dv.cyc != 0xffff && k == 0) { 190 fprintf(stderr, "0x%04x\n", ntohs(ciph->fdf.dv.cyc)); 191 } 192 #endif 193 if (pkt->mode.stream.len <= sizeof(struct ciphdr)) 194 /* no payload */ 195 goto next; 196 for (dv = (struct dvdbc *)ptr; 197 (char *)dv < (char *)(ptr + ciph->len); 198 dv+=6) { 199 200 #if DEBUG 201 fprintf(stderr, "(%d,%d) ", dv->sct, dv->dseq); 202 #endif 203 if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) { 204 if (system < 0) { 205 system = ciph->fdf.dv.fs; 206 fprintf(stderr, "%s\n", system_name[system]); 207 } 208 209 /* Fix DSF bit */ 210 if (system == 1 && 211 (dv->payload[0] & DV_DSF_12) == 0) 212 dv->payload[0] |= DV_DSF_12; 213 nb = nblocks[system]; 214 fprintf(stderr, "%d:%02d:%02d %d\r", 215 k / (3600 * frame_rate[system]), 216 (k / (60 * frame_rate[system])) % 60, 217 (k / frame_rate[system]) % 60, 218 k % frame_rate[system]); 219 220 #if FIX_FRAME 221 if (m > 0 && m != nb) { 222 /* padding bad frame */ 223 npad = ((nb - m) % nb); 224 if (npad < 0) 225 npad += nb; 226 fprintf(stderr, "\n%d blocks padded\n", 227 npad); 228 npad *= DSIZE; 229 wbuf[vec].iov_base = pad; 230 wbuf[vec++].iov_len = npad; 231 if (vec >= NPACKET_R) { 232 writev(fd, wbuf, vec); 233 vec = 0; 234 } 235 } 236 #endif 237 k++; 238 fflush(stderr); 239 m = 0; 240 } 241 if (k == 0 || (count > 0 && k > count)) 242 continue; 243 m++; 244 wbuf[vec].iov_base = (char *) dv; 245 wbuf[vec++].iov_len = DSIZE; 246 if (vec >= NPACKET_R) { 247 writev(fd, wbuf, vec); 248 vec = 0; 249 } 250 } 251 ptr = (u_int32_t *)dv; 252 next: 253 if ((char *)ptr < buf + tlen) 254 goto again; 255 if (vec > 0) 256 writev(fd, wbuf, vec); 257 } 258 if(fd != STDOUT_FILENO) 259 close(fd); 260 fprintf(stderr, "\n"); 261 } 262 263 264 void 265 dvsend(int d, const char *filename, char ich, int count) 266 { 267 struct fw_isochreq isoreq; 268 struct fw_isobufreq bufreq; 269 struct dvdbc *dv; 270 struct fw_pkt *pkt; 271 int len, tlen, header, fd, frames, packets, vec, offset, nhdr, i; 272 int system=-1, pad_acc, cycle_acc, cycle, f_cycle, f_frac; 273 struct iovec wbuf[TNBUF*2 + NEMPTY]; 274 char *pbuf; 275 u_int32_t iso_data, iso_empty, hdr[TNBUF + NEMPTY][3]; 276 struct ciphdr *ciph; 277 struct timeval start, end; 278 double rtime; 279 280 fd = open(filename, O_RDONLY); 281 if (fd == -1) 282 err(EX_NOINPUT, filename); 283 284 pbuf = malloc(DSIZE * TNBUF); 285 bzero(wbuf, sizeof(wbuf)); 286 287 bufreq.rx.nchunk = 0; 288 bufreq.rx.npacket = 0; 289 bufreq.rx.psize = 0; 290 bufreq.tx.nchunk = NCHUNK; 291 bufreq.tx.npacket = NPACKET_T; 292 bufreq.tx.psize = PSIZE; 293 if (ioctl(d, FW_SSTBUF, &bufreq) < 0) 294 err(1, "ioctl FW_SSTBUF"); 295 296 isoreq.ch = ich & 0x3f; 297 isoreq.tag = (ich >> 6) & 3; 298 299 if (ioctl(d, FW_STSTREAM, &isoreq) < 0) 300 err(1, "ioctl FW_STSTREAM"); 301 302 iso_data = 0; 303 pkt = (struct fw_pkt *) &iso_data; 304 pkt->mode.stream.len = DSIZE + sizeof(struct ciphdr); 305 pkt->mode.stream.sy = 0; 306 pkt->mode.stream.tcode = FWTCODE_STREAM; 307 pkt->mode.stream.chtag = ich; 308 iso_empty = iso_data; 309 pkt = (struct fw_pkt *) &iso_empty; 310 pkt->mode.stream.len = sizeof(struct ciphdr); 311 312 bzero(hdr[0], sizeof(hdr[0])); 313 hdr[0][0] = iso_data; 314 ciph = (struct ciphdr *)&hdr[0][1]; 315 ciph->src = 0; /* XXX */ 316 ciph->len = 120; 317 ciph->dbc = 0; 318 ciph->eoh1 = 1; 319 ciph->fdf.dv.cyc = 0xffff; 320 321 for (i = 1; i < TNBUF; i++) 322 bcopy(hdr[0], hdr[i], sizeof(hdr[0])); 323 324 gettimeofday(&start, NULL); 325 #if DEBUG 326 fprintf(stderr, "%08x %08x %08x\n", 327 htonl(hdr[0]), htonl(hdr[1]), htonl(hdr[2])); 328 #endif 329 frames = 0; 330 packets = 0; 331 pad_acc = 0; 332 while (1) { 333 tlen = 0; 334 while (tlen < DSIZE * TNBUF) { 335 len = read(fd, pbuf + tlen, DSIZE * TNBUF - tlen); 336 if (len <= 0) { 337 if (tlen > 0) 338 break; 339 if (len < 0) 340 warn("read"); 341 else 342 fprintf(stderr, "\nend of file\n"); 343 goto send_end; 344 } 345 tlen += len; 346 } 347 vec = 0; 348 offset = 0; 349 nhdr = 0; 350 next: 351 dv = (struct dvdbc *)(pbuf + offset * DSIZE); 352 #if 0 353 header = (dv->sct == 0 && dv->dseq == 0); 354 #else 355 header = (packets == 0 || packets % npackets[system] == 0); 356 #endif 357 358 ciph = (struct ciphdr *)&hdr[nhdr][1]; 359 if (header) { 360 if (system < 0) { 361 system = ((dv->payload[0] & DV_DSF_12) != 0); 362 printf("%s\n", system_name[system]); 363 cycle = 1; 364 cycle_acc = frame_cycle[system].d * cycle; 365 } 366 fprintf(stderr, "%d", frames % 10); 367 frames ++; 368 if (count > 0 && frames > count) 369 break; 370 if (frames % frame_rate[system] == 0) 371 fprintf(stderr, "\n"); 372 fflush(stderr); 373 f_cycle = (cycle_acc / frame_cycle[system].d) & 0xf; 374 f_frac = (cycle_acc % frame_cycle[system].d 375 * CYCLE_FRAC) / frame_cycle[system].d; 376 #if 0 377 ciph->fdf.dv.cyc = htons(f_cycle << 12 | f_frac); 378 #else 379 ciph->fdf.dv.cyc = htons(cycle << 12 | f_frac); 380 #endif 381 cycle_acc += frame_cycle[system].n; 382 cycle_acc %= frame_cycle[system].d * 0x10; 383 384 } else { 385 ciph->fdf.dv.cyc = 0xffff; 386 } 387 ciph->dbc = packets++ % 256; 388 pad_acc += pad_rate[system].n; 389 if (pad_acc >= pad_rate[system].d) { 390 pad_acc -= pad_rate[system].d; 391 bcopy(hdr[nhdr], hdr[nhdr+1], sizeof(hdr[0])); 392 hdr[nhdr][0] = iso_empty; 393 wbuf[vec].iov_base = (char *)hdr[nhdr]; 394 wbuf[vec++].iov_len = sizeof(hdr[0]); 395 nhdr ++; 396 cycle ++; 397 } 398 hdr[nhdr][0] = iso_data; 399 wbuf[vec].iov_base = (char *)hdr[nhdr]; 400 wbuf[vec++].iov_len = sizeof(hdr[0]); 401 wbuf[vec].iov_base = (char *)dv; 402 wbuf[vec++].iov_len = DSIZE; 403 nhdr ++; 404 cycle ++; 405 offset ++; 406 if (offset * DSIZE < tlen) 407 goto next; 408 409 again: 410 len = writev(d, wbuf, vec); 411 if (len < 0) { 412 if (errno == EAGAIN) { 413 fprintf(stderr, "(EAGAIN) - push 'Play'?\n"); 414 goto again; 415 } 416 err(1, "write failed"); 417 } 418 } 419 close(fd); 420 fprintf(stderr, "\n"); 421 send_end: 422 gettimeofday(&end, NULL); 423 rtime = end.tv_sec - start.tv_sec 424 + (end.tv_usec - start.tv_usec) * 1e-6; 425 fprintf(stderr, "%d frames, %.2f secs, %.2f frames/sec\n", 426 frames, rtime, frames/rtime); 427 } 428