vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / media-add-ons / firewire_dv / FireWireCard.cpp
blob13f58e94d70c6dfb80ff5d59e61cd73304e8c189
1 /*
2 * FireWire DV media addon for Haiku
4 * Copyright (c) 2008, JiSheng Zhang (jszhang3@mail.ustc.edu.cn)
5 * Distributed under the terms of the MIT License.
7 */
8 /*
9 * Copyright (C) 2003
10 * Hidetoshi Shimokawa. All rights reserved.
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:
23 * This product includes software developed by Hidetoshi Shimokawa.
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.
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.
42 #include "FireWireCard.h"
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>
54 #include "glue.h"
56 #define TAG (1<<6)
57 #define CHANNEL 63
59 /* for DV format */
60 #define FIX_FRAME 1
62 struct frac {
63 int n,d;
66 struct frac frame_cycle[2] = {
67 {8000*100, 2997}, /* NTSC 8000 cycle / 29.97 Hz */
68 {320, 1}, /* PAL 8000 cycle / 25 Hz */
70 int npackets[] = {
71 250 /* NTSC */,
72 300 /* PAL */
74 struct frac pad_rate[2] = {
75 {203, 2997}, /* = (8000 - 29.97 * 250)/(29.97 * 250) */
76 {1, 15}, /* = (8000 - 25 * 300)/(25 * 300) */
78 const char *system_name[] = {"NTSC", "PAL"};
79 int frame_rate[] = {30, 25};
81 #define DV_PSIZE 512
82 #define DV_DSIZE 480
83 #define DV_NCHUNK 64
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
93 /* for MPEGTS format */
94 typedef uint8_t mpeg_ts_pld[188];
96 struct 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,
105 #endif /* BYTE_ORDER == BIG_ENDIAN */
106 mpeg_ts_pld payload;
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)
116 FireWireCard::FireWireCard(const char* path)
117 : fInitStatus(B_OK),
118 fDev(-1),
119 fBuf(NULL),
120 fPad(NULL)
122 printf("FireWireCard opening %s\n", path);
124 fDev = open(path, O_RDWR);
125 if (fDev < 0) {
126 printf("FireWireCard opening %s failed\n", path);
127 fInitStatus = B_ERROR;
128 return;
133 FireWireCard::~FireWireCard()
135 if (fDev > 0)
136 close(fDev);
140 status_t
141 FireWireCard::InitCheck()
143 return fInitStatus;
147 ssize_t
148 FireWireCard::Read(void** data)
150 if (fFormat == FMT_MPEGTS)
151 return MpegtsRead(data);
152 else
153 return DvRead(data);
157 status_t
158 FireWireCard::Extract(void* dest, void** src, ssize_t* sizeUsed)
160 if (fFormat == FMT_MPEGTS)
161 return MpegtsExtract(dest, src, sizeUsed);
162 else
163 return DvExtract(dest, src, sizeUsed);
167 void
168 FireWireCard::GetBufInfo(size_t* rbufsize, int* rcount)
170 *rbufsize = fRbufSize;
171 *rcount = fRcount;
175 status_t
176 FireWireCard::DetectRecvFn()
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;
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;
193 if (ioctl(fDev, FW_SSTBUF, &bufreq) < 0)
194 return errno;
196 isoreq.ch = ich & 0x3f;
197 isoreq.tag = (ich >> 6) & 3;
199 if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
200 return errno;
202 buf = (char*)malloc(1024*16);
203 len = read(fDev, buf, 1024*16);
204 if (len < 0) {
205 free(buf);
206 return errno;
208 ptr = (u_int32_t*) buf;
209 ciph = (struct ciphdr*)(ptr + 1);
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);
231 free(buf);
232 return B_OK;
236 ssize_t
237 FireWireCard::DvRead(void** buffer)
239 struct fw_isochreq isoreq;
240 struct fw_isobufreq bufreq;
241 ssize_t len;
242 char ich = TAG|CHANNEL;
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;
253 isoreq.ch = ich & 0x3f;
254 isoreq.tag = (ich >> 6) & 3;
256 if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
257 return errno;
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;
268 *buffer = fBuf;
269 return len;
273 status_t
274 FireWireCard::DvExtract(void* dest, void** src, ssize_t* sizeUsed)
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;
283 k = m = 0;
284 ptr = (u_int32_t*) (*src);
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;
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) {
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]);
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;
324 #endif
325 k++;
326 if (k % frame_rate[system] == 0) {
327 /* every second */
328 fprintf(stderr, "\n");
330 fflush(stderr);
331 m = 0;
333 if (k == 0)
334 continue;
335 m++;
336 memcpy(dest, dv, DV_DSIZE);
337 dest = (char*)dest + DV_DSIZE;
339 ptr = (u_int32_t*)dv;
340 *src = ptr;
341 return B_OK;
345 ssize_t
346 FireWireCard::MpegtsRead(void** buffer)
348 struct fw_isochreq isoreq;
349 struct fw_isobufreq bufreq;
350 char ich = TAG|CHANNEL;
351 ssize_t len;
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;
363 isoreq.ch = ich & 0x3f;
364 isoreq.tag = (ich >> 6) & 3;
366 if (ioctl(fDev, FW_SRSTREAM, &isoreq) < 0)
367 return errno;
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;
378 *buffer = fBuf;
379 return len;
383 status_t
384 FireWireCard::MpegtsExtract(void* dest, void** src, ssize_t* sizeUsed)
386 uint32_t* ptr;
387 struct fw_pkt* pkt;
388 struct ciphdr* ciph;
389 struct mpeg_pldt* pld;
390 int pkt_size, startwr;
392 ptr = (uint32_t *)(*src);
393 startwr = 0;
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;
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;
407 ptr = (uint32_t*) (ciph + 1); /* skip cip header */
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;
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);
430 next:
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;