1/*
2 * FireWire DV media addon for Haiku
3 *
4 * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn)
5 * Distributed under the terms of the MIT License.
6 *
7 */
8/*
9 * Copyright (C) 2003
10 * 	Hidetoshi Shimokawa. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 *    must display the following acknowledgement:
22 *
23 *	This product includes software developed by Hidetoshi Shimokawa.
24 *
25 * 4. Neither the name of the author nor the names of its contributors
26 *    may be used to endorse or promote products derived from this software
27 *    without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42#include "FireWireCard.h"
43
44#include <stdlib.h>
45#include <stdio.h>
46#include <string.h>
47#include <fcntl.h>
48#include <unistd.h>
49#include <sys/ioctl.h>
50#include <OS.h>
51#include <stdint.h>
52#include <errno.h>
53
54#include "glue.h"
55
56#define TAG	(1<<6)
57#define CHANNEL	63
58
59/* for DV format */
60#define FIX_FRAME	1
61
62struct frac {
63	int n,d;
64};
65
66struct frac frame_cycle[2]  = {
67	{8000*100, 2997},	/* NTSC 8000 cycle / 29.97 Hz */
68	{320, 1},		/* PAL  8000 cycle / 25 Hz */
69};
70int npackets[] = {
71	250		/* NTSC */,
72	300		/* PAL */
73};
74struct frac pad_rate[2]  = {
75	{203, 2997},	/* = (8000 - 29.97 * 250)/(29.97 * 250) */
76	{1, 15},	/* = (8000 - 25 * 300)/(25 * 300) */
77};
78const char *system_name[] = {"NTSC", "PAL"};
79int frame_rate[] = {30, 25};
80
81#define DV_PSIZE 512
82#define DV_DSIZE 480
83#define DV_NCHUNK 64
84
85#define DV_NPACKET_R 256
86#define DV_NPACKET_T 255
87#define DV_TNBUF 100	/* XXX too large value causes block noise */
88#define DV_NEMPTY 10	/* depends on DV_TNBUF */
89#define DV_RBUFSIZE (DV_PSIZE * DV_NPACKET_R)
90#define DV_MAXBLOCKS (300)
91#define DV_CYCLE_FRAC 0xc00
92
93/* for MPEGTS format */
94typedef uint8_t mpeg_ts_pld[188];
95
96struct mpeg_pldt {
97#if BYTE_ORDER == BIG_ENDIAN
98	uint32_t	:7,
99				c_count:13,
100				c_offset:12;
101#else /* BYTE_ORDER != BIG_ENDIAN */
102	uint32_t	c_offset:12,
103				c_count:13,
104				:7;
105#endif /* BYTE_ORDER == BIG_ENDIAN */
106	mpeg_ts_pld payload;
107};
108
109
110#define	MPEG_NCHUNK 8
111#define	MPEG_PSIZE 596
112#define	MPEG_NPACKET_R 4096
113#define	MPEG_RBUFSIZE (MPEG_PSIZE * MPEG_NPACKET_R)
114
115
116FireWireCard::FireWireCard(const char* path)
117	: fInitStatus(B_OK),
118	fDev(-1),
119	fBuf(NULL),
120	fPad(NULL)
121{
122	printf("FireWireCard opening %s\n", path);
123
124	fDev = open(path, O_RDWR);
125	if (fDev < 0) {
126		printf("FireWireCard opening %s failed\n", path);
127		fInitStatus = B_ERROR;
128		return;
129	}
130}
131
132
133FireWireCard::~FireWireCard()
134{
135	if (fDev > 0)
136		close(fDev);
137}
138
139
140status_t
141FireWireCard::InitCheck()
142{
143	return fInitStatus;
144}
145
146
147ssize_t
148FireWireCard::Read(void** data)
149{
150	if (fFormat == FMT_MPEGTS)
151		return MpegtsRead(data);
152	else
153		return DvRead(data);
154}
155
156
157status_t
158FireWireCard::Extract(void* dest, void** src, ssize_t* sizeUsed)
159{
160	if (fFormat == FMT_MPEGTS)
161		return MpegtsExtract(dest, src, sizeUsed);
162	else
163		return DvExtract(dest, src, sizeUsed);
164}
165
166
167void
168FireWireCard::GetBufInfo(size_t* rbufsize, int* rcount)
169{
170	*rbufsize = fRbufSize;
171	*rcount = fRcount;
172}
173
174
175status_t
176FireWireCard::DetectRecvFn()
177{
178	char* buf;
179	char ich = TAG | CHANNEL;
180	struct fw_isochreq isoreq;
181	struct fw_isobufreq bufreq;
182	int len;
183	u_int32_t* ptr;
184	struct ciphdr* ciph;
185
186	bufreq.rx.nchunk = 8;
187	bufreq.rx.npacket = 16;
188	bufreq.rx.psize = 1024;
189	bufreq.tx.nchunk = 0;
190	bufreq.tx.npacket = 0;
191	bufreq.tx.psize = 0;
192
193	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
194		return errno;
195
196	isoreq.ch = ich & 0x3f;
197	isoreq.tag = (ich >> 6) & 3;
198
199	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
200		return errno;
201
202	buf = (char*)malloc(1024*16);
203	len = read(fDev, buf, 1024*16);
204	if (len < 0) {
205		free(buf);
206		return errno;
207	}
208	ptr = (u_int32_t*) buf;
209	ciph = (struct ciphdr*)(ptr + 1);
210
211	switch(ciph->fmt) {
212		case CIP_FMT_DVCR:
213			fprintf(stderr, "Detected DV format on input.\n");
214			fFormat = FMT_DV;
215			fBuf = malloc(DV_RBUFSIZE);
216			fRbufSize = DV_PSIZE;
217			fRcount = DV_NPACKET_R;
218			fPad = malloc(DV_DSIZE*DV_MAXBLOCKS);
219			memset(fPad, 0xff, DV_DSIZE*DV_MAXBLOCKS);
220			break;
221		case CIP_FMT_MPEG:
222			fprintf(stderr, "Detected MPEG TS format on input.\n");
223			fFormat = FMT_MPEGTS;
224			fBuf = malloc(MPEG_RBUFSIZE);
225			fRbufSize = MPEG_PSIZE;
226			fRcount = MPEG_NPACKET_R;
227			break;
228		default:
229			fprintf(stderr, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
230	}
231	free(buf);
232	return B_OK;
233}
234
235
236ssize_t
237FireWireCard::DvRead(void** buffer)
238{
239	struct fw_isochreq isoreq;
240	struct fw_isobufreq bufreq;
241	ssize_t len;
242	char ich = TAG|CHANNEL;
243
244	bufreq.rx.nchunk = DV_NCHUNK;
245	bufreq.rx.npacket = DV_NPACKET_R;
246	bufreq.rx.psize = DV_PSIZE;
247	bufreq.tx.nchunk = 0;
248	bufreq.tx.npacket = 0;
249	bufreq.tx.psize = 0;
250	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
251		return errno;
252
253	isoreq.ch = ich & 0x3f;
254	isoreq.tag = (ich >> 6) & 3;
255
256	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
257		return errno;
258
259	len = read(fDev, fBuf, DV_RBUFSIZE);
260	if (len < 0) {
261		if (errno == EAGAIN) {
262			fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
263			fflush(stderr);
264		} else
265			fprintf(stderr, "read failed");
266		return errno;
267	}
268	*buffer = fBuf;
269	return len;
270}
271
272
273status_t
274FireWireCard::DvExtract(void* dest, void** src, ssize_t* sizeUsed)
275{
276	struct dvdbc* dv;
277	struct ciphdr* ciph;
278	struct fw_pkt* pkt;
279	u_int32_t* ptr;
280	int nblocks[] = {250 /* NTSC */, 300 /* PAL */};
281	int npad, k, m, system = -1, nb;
282
283	k = m = 0;
284	ptr = (u_int32_t*) (*src);
285
286	pkt = (struct fw_pkt*) ptr;
287	ciph = (struct ciphdr*)(ptr + 1);	/* skip iso header */
288	if (ciph->fmt != CIP_FMT_DVCR) {
289		fprintf(stderr, "unknown format 0x%x", ciph->fmt);
290		return B_ERROR;
291	}
292	ptr = (u_int32_t*) (ciph + 1);		/* skip cip header */
293	if (pkt->mode.stream.len <= sizeof(struct ciphdr))
294		/* no payload */
295		return B_ERROR;
296	for (dv = (struct dvdbc*)ptr;
297			(char*)dv < (char *)(ptr + ciph->len);
298			dv+=6) {
299
300		if  (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
301			if (system < 0) {
302				system = ciph->fdf.dv.fs;
303				fprintf(stderr, "%s\n", system_name[system]);
304			}
305
306			/* Fix DSF bit */
307			if (system == 1 &&
308				(dv->payload[0] & DV_DSF_12) == 0)
309				dv->payload[0] |= DV_DSF_12;
310			nb = nblocks[system];
311			fprintf(stderr, "%d", k%10);
312#if FIX_FRAME
313			if (m > 0 && m != nb) {
314				/* padding bad frame */
315				npad = ((nb - m) % nb);
316				if (npad < 0)
317					npad += nb;
318				fprintf(stderr, "(%d blocks padded)",
319							npad);
320				npad *= DV_DSIZE;
321				memcpy(dest, fPad, npad);
322				dest = (char*)dest + npad;
323			}
324#endif
325			k++;
326			if (k % frame_rate[system] == 0) {
327				/* every second */
328				fprintf(stderr, "\n");
329			}
330			fflush(stderr);
331			m = 0;
332		}
333		if (k == 0)
334			continue;
335		m++;
336		memcpy(dest, dv, DV_DSIZE);
337		dest = (char*)dest + DV_DSIZE;
338	}
339	ptr = (u_int32_t*)dv;
340	*src = ptr;
341	return B_OK;
342}
343
344
345ssize_t
346FireWireCard::MpegtsRead(void** buffer)
347{
348	struct fw_isochreq isoreq;
349	struct fw_isobufreq bufreq;
350	char ich = TAG|CHANNEL;
351	ssize_t len;
352
353
354	bufreq.rx.nchunk = MPEG_NCHUNK;
355	bufreq.rx.npacket = MPEG_NPACKET_R;
356	bufreq.rx.psize = MPEG_PSIZE;
357	bufreq.tx.nchunk = 0;
358	bufreq.tx.npacket = 0;
359	bufreq.tx.psize = 0;
360	if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
361		return errno;
362
363	isoreq.ch = ich & 0x3f;
364	isoreq.tag = (ich >> 6) & 3;
365
366	if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
367		return errno;
368
369	len = read(fDev, fBuf, MPEG_RBUFSIZE);
370	if (len < 0) {
371		if (errno == EAGAIN) {
372			fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
373			fflush(stderr);
374		} else
375			fprintf(stderr, "read failed");
376		return errno;
377	}
378	*buffer = fBuf;
379	return len;
380}
381
382
383status_t
384FireWireCard::MpegtsExtract(void* dest, void** src, ssize_t* sizeUsed)
385{
386	uint32_t* ptr;
387	struct fw_pkt* pkt;
388	struct ciphdr* ciph;
389	struct mpeg_pldt* pld;
390	int pkt_size, startwr;
391
392	ptr = (uint32_t *)(*src);
393	startwr = 0;
394
395	pkt = (struct fw_pkt*) ptr;
396	/* there is no CRC in the 1394 header */
397	ciph = (struct ciphdr*)(ptr + 1);	/* skip iso header */
398	if (ciph->fmt != CIP_FMT_MPEG) {
399		fprintf(stderr, "unknown format 0x%x", ciph->fmt);
400		return B_ERROR;
401	}
402	if (ciph->fn != 3) {
403		fprintf(stderr,	"unsupported MPEG TS stream, fn=%d (only"
404			"fn=3 is supported)", ciph->fn);
405		return B_ERROR;
406	}
407	ptr = (uint32_t*) (ciph + 1);		/* skip cip header */
408
409	if (pkt->mode.stream.len <= sizeof(struct ciphdr)) {
410		/* no payload */
411		/* tlen needs to be decremented before end of the loop */
412		goto next;
413	}
414
415	/* This is a condition that needs to be satisfied to start
416	   writing the data */
417	if (ciph->dbc % (1<<ciph->fn) == 0)
418		startwr = 1;
419	/* Read out all the MPEG TS data blocks from current packet */
420	for (pld = (struct mpeg_pldt *)ptr;
421	    (intptr_t)pld < (intptr_t)((char*)ptr +
422	    pkt->mode.stream.len - sizeof(struct ciphdr));
423	    pld++) {
424		if (startwr == 1) {
425			memcpy(dest, pld->payload, sizeof(pld->payload));
426			dest = (char*)dest + sizeof(pld->payload);
427		}
428	}
429
430next:
431	/* CRCs are removed from both header and trailer
432	so that only 4 bytes of 1394 header remains */
433	pkt_size = pkt->mode.stream.len + 4;
434	ptr = (uint32_t*)((intptr_t)pkt + pkt_size);
435	*src = ptr;
436	return B_OK;
437}
438
439