1145519Sdarrenr/* 2145510Sdarrenr * FireWire DV media addon for Haiku 3145510Sdarrenr * 4255332Scy * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn) 5145510Sdarrenr * Distributed under the terms of the MIT License. 6145510Sdarrenr * 7145510Sdarrenr */ 8145510Sdarrenr/* 9145510Sdarrenr * Copyright (C) 2003 10255332Scy * Hidetoshi Shimokawa. All rights reserved. 11145510Sdarrenr * 12145510Sdarrenr * Redistribution and use in source and binary forms, with or without 13145510Sdarrenr * modification, are permitted provided that the following conditions 14145510Sdarrenr * are met: 15145510Sdarrenr * 1. Redistributions of source code must retain the above copyright 16255332Scy * notice, this list of conditions and the following disclaimer. 17145510Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright 18145510Sdarrenr * notice, this list of conditions and the following disclaimer in the 19145510Sdarrenr * documentation and/or other materials provided with the distribution. 20369245Sgit2svn * 3. All advertising materials mentioning features or use of this software 21145510Sdarrenr * must display the following acknowledgement: 22145510Sdarrenr * 23145510Sdarrenr * This product includes software developed by Hidetoshi Shimokawa. 24145510Sdarrenr * 25145510Sdarrenr * 4. Neither the name of the author nor the names of its contributors 26145510Sdarrenr * may be used to endorse or promote products derived from this software 27145510Sdarrenr * without specific prior written permission. 28369245Sgit2svn * 29369245Sgit2svn * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30369245Sgit2svn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31145510Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32145510Sdarrenr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33145510Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34145510Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35255332Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36145510Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37369245Sgit2svn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38369245Sgit2svn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39369245Sgit2svn * SUCH DAMAGE. 40369245Sgit2svn */ 41369245Sgit2svn 42369245Sgit2svn#include "FireWireCard.h" 43369245Sgit2svn 44369245Sgit2svn#include <stdlib.h> 45145510Sdarrenr#include <stdio.h> 46369245Sgit2svn#include <string.h> 47369245Sgit2svn#include <fcntl.h> 48369245Sgit2svn#include <unistd.h> 49369245Sgit2svn#include <sys/ioctl.h> 50145510Sdarrenr#include <OS.h> 51145510Sdarrenr#include <stdint.h> 52145510Sdarrenr#include <errno.h> 53145510Sdarrenr 54145510Sdarrenr#include "glue.h" 55145510Sdarrenr 56255332Scy#define TAG (1<<6) 57255332Scy#define CHANNEL 63 58255332Scy 59145510Sdarrenr/* for DV format */ 60145510Sdarrenr#define FIX_FRAME 1 61145510Sdarrenr 62145510Sdarrenrstruct frac { 63145510Sdarrenr int n,d; 64145510Sdarrenr}; 65145510Sdarrenr 66145510Sdarrenrstruct frac frame_cycle[2] = { 67145510Sdarrenr {8000*100, 2997}, /* NTSC 8000 cycle / 29.97 Hz */ 68145510Sdarrenr {320, 1}, /* PAL 8000 cycle / 25 Hz */ 69145510Sdarrenr}; 70255332Scyint npackets[] = { 71255332Scy 250 /* NTSC */, 72145510Sdarrenr 300 /* PAL */ 73255332Scy}; 74145510Sdarrenrstruct frac pad_rate[2] = { 75145510Sdarrenr {203, 2997}, /* = (8000 - 29.97 * 250)/(29.97 * 250) */ 76145510Sdarrenr {1, 15}, /* = (8000 - 25 * 300)/(25 * 300) */ 77145510Sdarrenr}; 78255332Scyconst char *system_name[] = {"NTSC", "PAL"}; 79255332Scyint frame_rate[] = {30, 25}; 80255332Scy 81145510Sdarrenr#define DV_PSIZE 512 82145510Sdarrenr#define DV_DSIZE 480 83145510Sdarrenr#define DV_NCHUNK 64 84145510Sdarrenr 85145510Sdarrenr#define DV_NPACKET_R 256 86255332Scy#define DV_NPACKET_T 255 87255332Scy#define DV_TNBUF 100 /* XXX too large value causes block noise */ 88255332Scy#define DV_NEMPTY 10 /* depends on DV_TNBUF */ 89145510Sdarrenr#define DV_RBUFSIZE (DV_PSIZE * DV_NPACKET_R) 90145510Sdarrenr#define DV_MAXBLOCKS (300) 91145510Sdarrenr#define DV_CYCLE_FRAC 0xc00 92145510Sdarrenr 93145510Sdarrenr/* for MPEGTS format */ 94145510Sdarrenrtypedef uint8_t mpeg_ts_pld[188]; 95145510Sdarrenr 96145510Sdarrenrstruct mpeg_pldt { 97145510Sdarrenr#if BYTE_ORDER == BIG_ENDIAN 98145510Sdarrenr uint32_t :7, 99145510Sdarrenr c_count:13, 100145510Sdarrenr c_offset:12; 101145510Sdarrenr#else /* BYTE_ORDER != BIG_ENDIAN */ 102145510Sdarrenr uint32_t c_offset:12, 103145510Sdarrenr c_count:13, 104145510Sdarrenr :7; 105145510Sdarrenr#endif /* BYTE_ORDER == BIG_ENDIAN */ 106145510Sdarrenr mpeg_ts_pld payload; 107145510Sdarrenr}; 108145510Sdarrenr 109255332Scy 110145510Sdarrenr#define MPEG_NCHUNK 8 111145510Sdarrenr#define MPEG_PSIZE 596 112255332Scy#define MPEG_NPACKET_R 4096 113145510Sdarrenr#define MPEG_RBUFSIZE (MPEG_PSIZE * MPEG_NPACKET_R) 114145510Sdarrenr 115145510Sdarrenr 116145510SdarrenrFireWireCard::FireWireCard(const char* path) 117145510Sdarrenr : fInitStatus(B_OK), 118145510Sdarrenr fDev(-1), 119145510Sdarrenr fBuf(NULL), 120255332Scy fPad(NULL) 121255332Scy{ 122255332Scy printf("FireWireCard opening %s\n", path); 123145510Sdarrenr 124255332Scy fDev = open(path, O_RDWR); 125145510Sdarrenr if (fDev < 0) { 126145510Sdarrenr printf("FireWireCard opening %s failed\n", path); 127145510Sdarrenr fInitStatus = B_ERROR; 128145510Sdarrenr return; 129145510Sdarrenr } 130145510Sdarrenr} 131145510Sdarrenr 132145510Sdarrenr 133145510SdarrenrFireWireCard::~FireWireCard() 134145510Sdarrenr{ 135145510Sdarrenr if (fDev > 0) 136145510Sdarrenr close(fDev); 137145510Sdarrenr} 138145510Sdarrenr 139145510Sdarrenr 140145510Sdarrenrstatus_t 141145510SdarrenrFireWireCard::InitCheck() 142145510Sdarrenr{ 143145510Sdarrenr return fInitStatus; 144145510Sdarrenr} 145145510Sdarrenr 146145510Sdarrenr 147145510Sdarrenrssize_t 148145510SdarrenrFireWireCard::Read(void** data) 149145510Sdarrenr{ 150145510Sdarrenr if (fFormat == FMT_MPEGTS) 151145510Sdarrenr return MpegtsRead(data); 152145510Sdarrenr else 153145510Sdarrenr return DvRead(data); 154145510Sdarrenr} 155145510Sdarrenr 156145510Sdarrenr 157145510Sdarrenrstatus_t 158145510SdarrenrFireWireCard::Extract(void* dest, void** src, ssize_t* sizeUsed) 159145510Sdarrenr{ 160145510Sdarrenr if (fFormat == FMT_MPEGTS) 161145510Sdarrenr return MpegtsExtract(dest, src, sizeUsed); 162145510Sdarrenr else 163145510Sdarrenr return DvExtract(dest, src, sizeUsed); 164145510Sdarrenr} 165145510Sdarrenr 166145510Sdarrenr 167145510Sdarrenrvoid 168145510SdarrenrFireWireCard::GetBufInfo(size_t* rbufsize, int* rcount) 169255332Scy{ 170145510Sdarrenr *rbufsize = fRbufSize; 171145510Sdarrenr *rcount = fRcount; 172145510Sdarrenr} 173145510Sdarrenr 174145510Sdarrenr 175255332Scystatus_t 176255332ScyFireWireCard::DetectRecvFn() 177145510Sdarrenr{ 178145510Sdarrenr char* buf; 179145510Sdarrenr char ich = TAG | CHANNEL; 180145510Sdarrenr struct fw_isochreq isoreq; 181145510Sdarrenr struct fw_isobufreq bufreq; 182145510Sdarrenr int len; 183145510Sdarrenr u_int32_t* ptr; 184145510Sdarrenr struct ciphdr* ciph; 185145510Sdarrenr 186145510Sdarrenr bufreq.rx.nchunk = 8; 187145510Sdarrenr bufreq.rx.npacket = 16; 188145510Sdarrenr bufreq.rx.psize = 1024; 189145510Sdarrenr bufreq.tx.nchunk = 0; 190145510Sdarrenr bufreq.tx.npacket = 0; 191145510Sdarrenr bufreq.tx.psize = 0; 192255332Scy 193145510Sdarrenr if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0) 194145510Sdarrenr return errno; 195145510Sdarrenr 196145510Sdarrenr isoreq.ch = ich & 0x3f; 197145510Sdarrenr isoreq.tag = (ich >> 6) & 3; 198145510Sdarrenr 199145510Sdarrenr if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0) 200145510Sdarrenr return errno; 201145510Sdarrenr 202145510Sdarrenr buf = (char*)malloc(1024*16); 203145510Sdarrenr len = read(fDev, buf, 1024*16); 204145510Sdarrenr if (len < 0) { 205145510Sdarrenr free(buf); 206161357Sguido return errno; 207145510Sdarrenr } 208145510Sdarrenr ptr = (u_int32_t*) buf; 209145510Sdarrenr ciph = (struct ciphdr*)(ptr + 1); 210255332Scy 211145510Sdarrenr switch(ciph->fmt) { 212145510Sdarrenr case CIP_FMT_DVCR: 213145510Sdarrenr fprintf(stderr, "Detected DV format on input.\n"); 214145510Sdarrenr fFormat = FMT_DV; 215145510Sdarrenr fBuf = malloc(DV_RBUFSIZE); 216145510Sdarrenr fRbufSize = DV_PSIZE; 217145510Sdarrenr fRcount = DV_NPACKET_R; 218255332Scy fPad = malloc(DV_DSIZE*DV_MAXBLOCKS); 219145510Sdarrenr memset(fPad, 0xff, DV_DSIZE*DV_MAXBLOCKS); 220255332Scy break; 221145510Sdarrenr case CIP_FMT_MPEG: 222255332Scy fprintf(stderr, "Detected MPEG TS format on input.\n"); 223145510Sdarrenr fFormat = FMT_MPEGTS; 224145510Sdarrenr fBuf = malloc(MPEG_RBUFSIZE); 225255332Scy fRbufSize = MPEG_PSIZE; 226255332Scy fRcount = MPEG_NPACKET_R; 227255332Scy break; 228145510Sdarrenr default: 229255332Scy fprintf(stderr, "Unsupported format for receiving: fmt=0x%x", ciph->fmt); 230145510Sdarrenr } 231145510Sdarrenr free(buf); 232145510Sdarrenr return B_OK; 233145510Sdarrenr} 234255332Scy 235255332Scy 236145510Sdarrenrssize_t 237145510SdarrenrFireWireCard::DvRead(void** buffer) 238145510Sdarrenr{ 239145510Sdarrenr struct fw_isochreq isoreq; 240145510Sdarrenr struct fw_isobufreq bufreq; 241145510Sdarrenr ssize_t len; 242145510Sdarrenr char ich = TAG|CHANNEL; 243145510Sdarrenr 244145510Sdarrenr bufreq.rx.nchunk = DV_NCHUNK; 245145510Sdarrenr bufreq.rx.npacket = DV_NPACKET_R; 246145510Sdarrenr bufreq.rx.psize = DV_PSIZE; 247145510Sdarrenr bufreq.tx.nchunk = 0; 248145510Sdarrenr bufreq.tx.npacket = 0; 249145510Sdarrenr bufreq.tx.psize = 0; 250145510Sdarrenr if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0) 251255332Scy return errno; 252255332Scy 253255332Scy isoreq.ch = ich & 0x3f; 254255332Scy isoreq.tag = (ich >> 6) & 3; 255145510Sdarrenr 256145510Sdarrenr if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0) 257145510Sdarrenr return errno; 258145510Sdarrenr 259255332Scy len = read(fDev, fBuf, DV_RBUFSIZE); 260255332Scy if (len < 0) { 261255332Scy if (errno == EAGAIN) { 262145510Sdarrenr fprintf(stderr, "(EAGAIN) - push 'Play'?\n"); 263145510Sdarrenr fflush(stderr); 264145510Sdarrenr } else 265145510Sdarrenr fprintf(stderr, "read failed"); 266255332Scy return errno; 267145510Sdarrenr } 268145510Sdarrenr *buffer = fBuf; 269145510Sdarrenr return len; 270145510Sdarrenr} 271145510Sdarrenr 272145510Sdarrenr 273145510Sdarrenrstatus_t 274145510SdarrenrFireWireCard::DvExtract(void* dest, void** src, ssize_t* sizeUsed) 275145510Sdarrenr{ 276145510Sdarrenr struct dvdbc* dv; 277145510Sdarrenr struct ciphdr* ciph; 278145510Sdarrenr struct fw_pkt* pkt; 279145510Sdarrenr u_int32_t* ptr; 280145510Sdarrenr int nblocks[] = {250 /* NTSC */, 300 /* PAL */}; 281145510Sdarrenr int npad, k, m, system = -1, nb; 282145510Sdarrenr 283145510Sdarrenr k = m = 0; 284145510Sdarrenr ptr = (u_int32_t*) (*src); 285145510Sdarrenr 286145510Sdarrenr pkt = (struct fw_pkt*) ptr; 287145510Sdarrenr ciph = (struct ciphdr*)(ptr + 1); /* skip iso header */ 288145510Sdarrenr if (ciph->fmt != CIP_FMT_DVCR) { 289145510Sdarrenr fprintf(stderr, "unknown format 0x%x", ciph->fmt); 290283295Semaste return B_ERROR; 291145510Sdarrenr } 292145510Sdarrenr ptr = (u_int32_t*) (ciph + 1); /* skip cip header */ 293145510Sdarrenr if (pkt->mode.stream.len <= sizeof(struct ciphdr)) 294145510Sdarrenr /* no payload */ 295145510Sdarrenr return B_ERROR; 296145510Sdarrenr for (dv = (struct dvdbc*)ptr; 297145510Sdarrenr (char*)dv < (char *)(ptr + ciph->len); 298145510Sdarrenr dv+=6) { 299145510Sdarrenr 300145510Sdarrenr if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) { 301145510Sdarrenr if (system < 0) { 302145510Sdarrenr system = ciph->fdf.dv.fs; 303145510Sdarrenr fprintf(stderr, "%s\n", system_name[system]); 304145510Sdarrenr } 305255332Scy 306145510Sdarrenr /* Fix DSF bit */ 307145510Sdarrenr if (system == 1 && 308145510Sdarrenr (dv->payload[0] & DV_DSF_12) == 0) 309145510Sdarrenr dv->payload[0] |= DV_DSF_12; 310145510Sdarrenr nb = nblocks[system]; 311145510Sdarrenr fprintf(stderr, "%d", k%10); 312145510Sdarrenr#if FIX_FRAME 313145510Sdarrenr if (m > 0 && m != nb) { 314145510Sdarrenr /* padding bad frame */ 315145510Sdarrenr npad = ((nb - m) % nb); 316145510Sdarrenr if (npad < 0) 317145510Sdarrenr npad += nb; 318145510Sdarrenr fprintf(stderr, "(%d blocks padded)", 319145510Sdarrenr npad); 320255332Scy npad *= DV_DSIZE; 321145510Sdarrenr memcpy(dest, fPad, npad); 322145510Sdarrenr dest = (char*)dest + npad; 323145510Sdarrenr } 324255332Scy#endif 325145510Sdarrenr k++; 326145510Sdarrenr if (k % frame_rate[system] == 0) { 327145510Sdarrenr /* every second */ 328145510Sdarrenr fprintf(stderr, "\n"); 329145510Sdarrenr } 330145510Sdarrenr fflush(stderr); 331145510Sdarrenr m = 0; 332145510Sdarrenr } 333145510Sdarrenr if (k == 0) 334145510Sdarrenr continue; 335145510Sdarrenr m++; 336145510Sdarrenr memcpy(dest, dv, DV_DSIZE); 337255332Scy dest = (char*)dest + DV_DSIZE; 338145510Sdarrenr } 339145510Sdarrenr ptr = (u_int32_t*)dv; 340145510Sdarrenr *src = ptr; 341255332Scy return B_OK; 342145510Sdarrenr} 343145510Sdarrenr 344145510Sdarrenr 345145510Sdarrenrssize_t 346145510SdarrenrFireWireCard::MpegtsRead(void** buffer) 347145510Sdarrenr{ 348145510Sdarrenr struct fw_isochreq isoreq; 349255332Scy struct fw_isobufreq bufreq; 350255332Scy char ich = TAG|CHANNEL; 351255332Scy ssize_t len; 352145510Sdarrenr 353145510Sdarrenr 354145510Sdarrenr bufreq.rx.nchunk = MPEG_NCHUNK; 355145510Sdarrenr bufreq.rx.npacket = MPEG_NPACKET_R; 356145510Sdarrenr bufreq.rx.psize = MPEG_PSIZE; 357170268Sdarrenr bufreq.tx.nchunk = 0; 358145510Sdarrenr bufreq.tx.npacket = 0; 359145510Sdarrenr bufreq.tx.psize = 0; 360170268Sdarrenr if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0) 361170268Sdarrenr return errno; 362145510Sdarrenr 363170268Sdarrenr isoreq.ch = ich & 0x3f; 364145510Sdarrenr isoreq.tag = (ich >> 6) & 3; 365145510Sdarrenr 366145510Sdarrenr if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0) 367145510Sdarrenr return errno; 368145510Sdarrenr 369145510Sdarrenr len = read(fDev, fBuf, MPEG_RBUFSIZE); 370145510Sdarrenr if (len < 0) { 371145510Sdarrenr if (errno == EAGAIN) { 372255332Scy fprintf(stderr, "(EAGAIN) - push 'Play'?\n"); 373255332Scy fflush(stderr); 374255332Scy } else 375255332Scy fprintf(stderr, "read failed"); 376255332Scy return errno; 377255332Scy } 378255332Scy *buffer = fBuf; 379255332Scy return len; 380255332Scy} 381255332Scy 382255332Scy 383255332Scystatus_t 384255332ScyFireWireCard::MpegtsExtract(void* dest, void** src, ssize_t* sizeUsed) 385255332Scy{ 386145510Sdarrenr uint32_t* ptr; 387145510Sdarrenr struct fw_pkt* pkt; 388145510Sdarrenr struct ciphdr* ciph; 389255332Scy struct mpeg_pldt* pld; 390145510Sdarrenr int pkt_size, startwr; 391145510Sdarrenr 392145510Sdarrenr ptr = (uint32_t *)(*src); 393145510Sdarrenr startwr = 0; 394255332Scy 395145510Sdarrenr pkt = (struct fw_pkt*) ptr; 396145510Sdarrenr /* there is no CRC in the 1394 header */ 397255332Scy ciph = (struct ciphdr*)(ptr + 1); /* skip iso header */ 398255332Scy if (ciph->fmt != CIP_FMT_MPEG) { 399255332Scy fprintf(stderr, "unknown format 0x%x", ciph->fmt); 400145510Sdarrenr return B_ERROR; 401145510Sdarrenr } 402313461Scy if (ciph->fn != 3) { 403145510Sdarrenr fprintf(stderr, "unsupported MPEG TS stream, fn=%d (only" 404313441Scy "fn=3 is supported)", ciph->fn); 405145510Sdarrenr return B_ERROR; 406313441Scy } 407145510Sdarrenr ptr = (uint32_t*) (ciph + 1); /* skip cip header */ 408313441Scy 409313441Scy if (pkt->mode.stream.len <= sizeof(struct ciphdr)) { 410313441Scy /* no payload */ 411313441Scy /* tlen needs to be decremented before end of the loop */ 412145510Sdarrenr goto next; 413145510Sdarrenr } 414145510Sdarrenr 415145510Sdarrenr /* This is a condition that needs to be satisfied to start 416145510Sdarrenr writing the data */ 417145510Sdarrenr if (ciph->dbc % (1<<ciph->fn) == 0) 418145510Sdarrenr startwr = 1; 419145510Sdarrenr /* Read out all the MPEG TS data blocks from current packet */ 420145510Sdarrenr for (pld = (struct mpeg_pldt *)ptr; 421145510Sdarrenr (intptr_t)pld < (intptr_t)((char*)ptr + 422255332Scy pkt->mode.stream.len - sizeof(struct ciphdr)); 423145510Sdarrenr pld++) { 424145510Sdarrenr if (startwr == 1) { 425145510Sdarrenr memcpy(dest, pld->payload, sizeof(pld->payload)); 426145510Sdarrenr dest = (char*)dest + sizeof(pld->payload); 427255332Scy } 428145510Sdarrenr } 429145510Sdarrenr 430145510Sdarrenrnext: 431145510Sdarrenr /* CRCs are removed from both header and trailer 432145510Sdarrenr so that only 4 bytes of 1394 header remains */ 433255332Scy pkt_size = pkt->mode.stream.len + 4; 434145510Sdarrenr ptr = (uint32_t*)((intptr_t)pkt + pkt_size); 435145510Sdarrenr *src = ptr; 436145510Sdarrenr return B_OK; 437255332Scy} 438255332Scy 439255332Scy