patch-2.4.19 linux-2.4.19/drivers/ieee1394/dv1394-private.h

Next file: linux-2.4.19/drivers/ieee1394/dv1394.c
Previous file: linux-2.4.19/drivers/ieee1394/csr.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/ieee1394/dv1394-private.h linux-2.4.19/drivers/ieee1394/dv1394-private.h
@@ -0,0 +1,611 @@
+/*
+ * dv1394-private.h - DV input/output over IEEE 1394 on OHCI chips
+ *   Copyright (C)2001 Daniel Maas <dmaas@dcine.com>
+ *     receive, proc_fs by Dan Dennedy <dan@dennedy.org>
+ *
+ * based on:
+ *   video1394.h - driver for OHCI 1394 boards
+ *   Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ *                          Peter Schlaile <udbz@rz.uni-karlsruhe.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DV_1394_PRIVATE_H
+#define _DV_1394_PRIVATE_H
+
+#include "ieee1394.h"
+#include <linux/pci.h>
+#include <asm/scatterlist.h>
+
+/* data structures private to the dv1394 driver */
+/* none of this is exposed to user-space */
+
+
+/* 
+   the 8-byte CIP (Common Isochronous Packet) header that precedes
+   each packet of DV data.
+
+   See the IEC 61883 standard. 
+*/
+
+struct CIP_header { unsigned char b[8]; };
+
+static inline void fill_cip_header(struct CIP_header *cip,
+				   unsigned char source_node_id,
+				   unsigned long counter,
+				   enum pal_or_ntsc format,
+				   unsigned long timestamp)
+{
+	cip->b[0] = source_node_id;
+	cip->b[1] = 0x78; /* packet size in quadlets (480/4) - even for empty packets! */
+	cip->b[2] = 0x00;
+	cip->b[3] = counter;
+
+	cip->b[4] = 0x80; /* const */
+
+	switch(format) {
+	case DV1394_PAL:
+		cip->b[5] = 0x80;
+		break;
+	case DV1394_NTSC:
+		cip->b[5] = 0x00;
+		break;
+	}
+
+	cip->b[6] = timestamp >> 8;
+	cip->b[7] = timestamp & 0xFF;
+}
+
+
+
+/* 
+   DMA commands used to program the OHCI's DMA engine
+
+   See the Texas Instruments OHCI 1394 chipset documentation. 
+*/
+
+struct output_more_immediate { u32 q[8]; };
+struct output_more { u32 q[4]; };
+struct output_last { u32 q[4]; };
+struct input_more { u32 q[4]; };
+struct input_last { u32 q[4]; };
+
+/* outputs */
+
+static inline void fill_output_more_immediate(struct output_more_immediate *omi,
+					      unsigned char tag,
+					      unsigned char channel,
+					      unsigned char sync_tag,
+					      unsigned int  payload_size)
+{
+	omi->q[0] = 0x02000000 | 8 ; /* OUTPUT_MORE_IMMEDIATE; 8 is the size of the IT header */
+	omi->q[1] = 0;
+	omi->q[2] = 0;
+	omi->q[3] = 0;
+	
+	/* IT packet header */
+	omi->q[4] = (0x0 << 16)  /* DMA_SPEED_100 */
+			| (tag << 14)
+			| (channel << 8)
+			| (TCODE_ISO_DATA << 4) 
+			| (sync_tag);
+
+	omi->q[5] = payload_size << 16;
+	omi->q[5] |= (0x7F << 8) | 0xA0; /* reserved field; mimic behavior of my Sony DSR-40 */
+	
+	omi->q[6] = 0;
+	omi->q[7] = 0;
+}
+
+static inline void fill_output_more(struct output_more *om,
+				    unsigned int data_size,
+				    unsigned long data_phys_addr)
+{
+	om->q[0] = 0; /* OUTPUT_MORE */
+	om->q[0] |= data_size;
+
+	om->q[1] = data_phys_addr;
+	om->q[2] = 0;
+	om->q[3] = 0;
+}
+
+static inline void fill_output_last(struct output_last *ol,
+				    int want_timestamp,
+				    int want_interrupt,
+				    unsigned int data_size,
+				    unsigned long data_phys_addr)
+{
+	ol->q[0] = 0;
+	ol->q[0] |= 1 << 28; /* OUTPUT_LAST */
+
+	if(want_timestamp) /* controller will update timestamp at DMA time */
+		ol->q[0] |= 1 << 27;
+
+	if(want_interrupt)
+		ol->q[0] |= 3 << 20;
+
+	ol->q[0] |= 3 << 18; /* must take branch */
+	ol->q[0] |= data_size;
+	
+	ol->q[1] = data_phys_addr;
+	ol->q[2] = 0;
+	ol->q[3] = 0;
+}
+
+/* inputs */
+
+static inline void fill_input_more(struct input_more *im,
+				   int want_interrupt,
+				   unsigned int data_size,
+				   unsigned long data_phys_addr)
+{
+	im->q[0] =  2 << 28; /* INPUT_MORE */
+	im->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */
+	if (want_interrupt)
+		im->q[0] |= 0 << 20; /* interrupts, i=0 in packet-per-buffer mode */
+	im->q[0] |= 0x0 << 16; /* disable branch to address for packet-per-buffer mode */
+	                       /* disable wait on sync field, not used in DV :-( */
+       	im->q[0] |= data_size;
+
+	im->q[1] = data_phys_addr;
+	im->q[2] = 0; /* branchAddress and Z not use in packet-per-buffer mode */
+	im->q[3] = 0; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+ 
+static inline void fill_input_last(struct input_last *il,
+				    unsigned int data_size,
+				    unsigned long data_phys_addr)
+{
+	il->q[0] =  3 << 28; /* INPUT_LAST */
+	il->q[0] |= 8 << 24; /* s = 1, update xferStatus and resCount */
+	il->q[0] |= 3 << 20; /* enable interrupts */
+	il->q[0] |= 0xC << 16; /* enable branch to address */
+	                       /* disable wait on sync field, not used in DV :-( */
+	il->q[0] |= data_size;
+
+	il->q[1] = data_phys_addr;
+	il->q[2] = 1; /* branchAddress (filled in later) and Z = 1 descriptor in next block */
+	il->q[3] = data_size; /* xferStatus & resCount, resCount must be initialize to data_size */
+}
+
+
+
+/* 
+   A "DMA descriptor block" consists of several contiguous DMA commands.
+   struct DMA_descriptor_block encapsulates all of the commands necessary 
+   to send one packet of DV data. 
+   
+   There are three different types of these blocks:
+
+        1) command to send an empty packet (CIP header only, no DV data):
+
+	    OUTPUT_MORE-Immediate <-- contains the iso header in-line
+	    OUTPUT_LAST           <-- points to the CIP header
+
+	2) command to send a full packet when the DV data payload does NOT
+	   cross a page boundary:
+
+	    OUTPUT_MORE-Immediate <-- contains the iso header in-line
+	    OUTPUT_MORE           <-- points to the CIP header
+	    OUTPUT_LAST           <-- points to entire DV data payload
+
+	3) command to send a full packet when the DV payload DOES cross
+	   a page boundary:
+
+	    OUTPUT_MORE-Immediate <-- contains the iso header in-line
+	    OUTPUT_MORE           <-- points to the CIP header
+	    OUTPUT_MORE           <-- points to first part of DV data payload
+	    OUTPUT_LAST           <-- points to second part of DV data payload
+
+   This struct describes all three block types using unions.
+
+   !!! It is vital that an even number of these descriptor blocks fit on one
+   page of memory, since a block cannot cross a page boundary !!!
+
+ */
+
+struct DMA_descriptor_block {
+
+	union {
+		struct {
+			/*  iso header, common to all output block types */
+			struct output_more_immediate omi; 
+			
+			union {
+				/* empty packet */
+				struct {
+					struct output_last ol;  /* CIP header */
+				} empty;
+			
+				/* full packet */
+				struct {
+					struct output_more om;  /* CIP header */
+					
+					union {
+				               /* payload does not cross page boundary */
+						struct {
+							struct output_last ol;  /* data payload */
+						} nocross;
+						
+				               /* payload crosses page boundary */
+						struct {
+							struct output_more om;  /* data payload */
+							struct output_last ol;  /* data payload */
+						} cross;
+					} u;
+					
+				} full;
+			} u;
+		} out;
+
+		struct {
+			struct input_last il; 
+		} in;
+
+	} u;
+
+	/* ensure that PAGE_SIZE % sizeof(struct DMA_descriptor_block) == 0 
+	   by padding out to 128 bytes */
+	u32 __pad__[12]; 
+};
+
+
+/* struct frame contains all data associated with one frame in the
+   ringbuffer these are allocated when the DMA context is initialized
+   do_dv1394_init().  They are re-used after the card finishes
+   transmitting the frame. */
+
+struct video_card; /* forward declaration */
+
+struct frame {
+
+	/* points to the struct video_card that owns this frame */
+	struct video_card *video;
+
+	/* index of this frame in video_card->frames[] */
+	unsigned int frame_num;
+
+	/* FRAME_CLEAR - DMA program not set up, waiting for data 
+	   FRAME_READY - DMA program written, ready to transmit
+
+	   Changes to these should be locked against the interrupt
+	*/
+	enum {
+		FRAME_CLEAR = 0,
+		FRAME_READY
+	} state;
+	
+	/* whether this frame has been DMA'ed already; used only from
+	   the IRQ handler to determine whether the frame can be reset */
+	int done;
+
+
+	/* kernel virtual pointer to the start of this frame's data in
+	   the user ringbuffer. Use only for CPU access; to get the DMA
+	   bus address you must go through the video->user_dma mapping */
+	unsigned long data; 
+
+	/* Max # of packets per frame */
+        #define MAX_PACKETS 320
+
+
+	/* a PAGE_SIZE memory pool for allocating CIP headers
+	   !header_pool must be aligned to PAGE_SIZE! */
+	struct CIP_header *header_pool;
+	dma_addr_t         header_pool_dma;
+
+	
+	/* a physically contiguous memory pool for allocating DMA
+	   descriptor blocks; usually around 64KB in size
+	   !descriptor_pool must be aligned to PAGE_SIZE! */
+	struct DMA_descriptor_block *descriptor_pool;
+	dma_addr_t                   descriptor_pool_dma;
+	unsigned long                descriptor_pool_size;
+
+
+	/* # of packets allocated for this frame */
+	unsigned int n_packets;
+
+
+	/* below are several pointers (kernel virtual addresses, not
+	   DMA bus addresses) to parts of the DMA program.  These are
+	   set each time the DMA program is written in
+	   frame_prepare(). They are used later on, e.g. from the
+	   interrupt handler, to check the status of the frame */
+
+	/* points to status/timestamp field of first DMA packet */
+	/* (we'll check it later to monitor timestamp accuracy) */
+	u32 *frame_begin_timestamp;
+
+	/* the timestamp we assigned to the first packet in the frame */
+	u32 assigned_timestamp;
+
+	/* pointer to the first packet's CIP header (where the timestamp goes) */
+	struct CIP_header *cip_syt1;
+	
+	/* pointer to the second packet's CIP header
+	   (only set if the first packet was empty) */
+	struct CIP_header *cip_syt2;
+
+	/* in order to figure out what caused an interrupt,
+	   store pointers to the status fields of the two packets
+	   that can cause interrupts. We'll check these from the
+	   interrupt handler.
+	*/
+	u32 *mid_frame_timestamp;
+	u32 *frame_end_timestamp;
+
+	/* branch address field of final packet. This is effectively
+	   the "tail" in the chain of DMA descriptor blocks.
+	   We will fill it with the address of the first DMA descriptor
+	   block in the subsequent frame, once it is ready.
+	*/
+	u32 *frame_end_branch;
+
+	/* the number of descriptors in the first descriptor block
+	   of the frame. Needed to start DMA */
+	int first_n_descriptors;
+};
+
+
+struct packet {
+	u16	timestamp;
+	u16	invalid;
+	u16	iso_header;
+	u16	data_length;
+	u32	cip_h1;
+	u32	cip_h2;
+	unsigned char data[480];
+	unsigned char padding[16]; /* force struct size =512 for page alignment */
+};
+
+
+/* allocate/free a frame */
+static struct frame* frame_new(unsigned int frame_num, struct video_card *video);
+static void frame_delete(struct frame *f);
+
+/* reset f so that it can be used again */
+static void frame_reset(struct frame *f);
+
+
+/* structure for bookkeeping of a large non-physically-contiguous DMA buffer */
+
+struct dma_region {
+	unsigned int n_pages;
+	unsigned int n_dma_pages;
+	struct scatterlist *sglist;
+};
+
+/* return the DMA bus address of the byte with the given offset
+   relative to the beginning of the dma_region */
+
+static inline dma_addr_t dma_offset_to_bus(struct dma_region *dma, unsigned long offset)
+{
+	int i;
+	struct scatterlist *sg;
+	
+	for(i = 0, sg = &dma->sglist[0]; i < dma->n_dma_pages; i++, sg++) {
+		if(offset < sg_dma_len(sg)) {
+			return sg_dma_address(sg) + offset;
+		} 
+		offset -= sg_dma_len(sg);
+	}
+	
+	printk(KERN_ERR "dv1394: dma_offset_to_bus failed for offset %lu!\n", offset);
+	return 0;
+}
+
+
+/* struct video_card contains all data associated with one instance
+   of the dv1394 driver 
+*/
+enum modes {
+	MODE_RECEIVE,
+	MODE_TRANSMIT
+};
+
+struct video_card {
+
+	/* ohci card to which this instance corresponds */
+	struct ti_ohci *ohci;
+
+	/* OHCI card id; the link between the VFS inode and a specific video_card
+	   (essentially the device minor number) */
+	int id;
+
+	/* entry in dv1394_cards */
+	struct list_head list;
+
+	/* handle to /dev/ieee1394/dv/N, NULL if devfs not in use */
+	devfs_handle_t devfs_handle;
+
+	/* OHCI card IT DMA context number, -1 if not in use */
+	int ohci_it_ctx;
+
+	/* register offsets for current IT DMA context, 0 if not in use */
+	u32 ohci_IsoXmitContextControlSet;
+	u32 ohci_IsoXmitContextControlClear;
+	u32 ohci_IsoXmitCommandPtr;
+	
+	/* OHCI card IR DMA context number, -1 if not in use */
+	int ohci_ir_ctx;
+
+	/* register offsets for current IR DMA context, 0 if not in use */
+	u32 ohci_IsoRcvContextControlSet;
+	u32 ohci_IsoRcvContextControlClear;
+	u32 ohci_IsoRcvCommandPtr;
+	u32 ohci_IsoRcvContextMatch;
+	
+	
+	/* CONCURRENCY CONTROL */
+	
+	/* there are THREE levels of locking associated with video_card. */
+
+	/*
+	   1) the 'open' flag - this prevents more than one process from
+	   opening the device. (the driver currently assumes only one opener).
+	   This is a regular int, but use test_and_set_bit() (on bit zero) 
+	   for atomicity.
+	 */
+	unsigned long open;
+
+	/* 
+	   2) the spinlock - this provides mutual exclusion between the interrupt
+	   handler and process-context operations. Generally you must take the
+	   spinlock under the following conditions:
+	     1) DMA (and hence the interrupt handler) may be running
+	     AND
+	     2) you need to operate on the video_card, especially active_frame
+
+	     It is OK to play with video_card without taking the spinlock if
+	     you are certain that DMA is not running. Even if DMA is running,
+	     it is OK to *read* active_frame with the lock, then drop it
+	     immediately. This is safe because the interrupt handler will never
+	     advance active_frame onto a frame that is not READY (and the spinlock
+	     must be held while marking a frame READY).
+	 */
+	spinlock_t spinlock;
+
+	/*
+	  3) the sleeping semaphore 'sem' - this is used from process context only,
+	  to serialize various operations on the video_card. Even though only one
+	  open() is allowed, we still need to prevent multiple threads of execution
+	  from entering calls like read, write, ioctl, etc.
+
+	  I honestly can't think of a good reason to use dv1394 from several threads
+	  at once, but we need to serialize anyway to prevent oopses =).
+
+	  NOTE: if you need both spinlock and sem, take sem first to avoid deadlock!
+	 */
+	struct semaphore sem;
+
+	/* people waiting for buffer space, please form a line here... */
+	wait_queue_head_t waitq;
+
+	/* support asynchronous I/O signals (SIGIO) */
+	struct fasync_struct *fasync;
+	
+	/* the large, non-contiguous (rvmalloc()) ringbuffer for DV
+           data, exposed to user-space via mmap() */
+	unsigned char     *user_buf;
+	unsigned long      user_buf_size;
+	struct dma_region  user_dma;
+	
+	/* next byte in the ringbuffer that a write() call will fill */
+	size_t write_off;
+
+	struct frame *frames[DV1394_MAX_FRAMES];
+	
+	/* n_frames also serves as an indicator that this struct video_card is
+	   intialized and ready to run DMA buffers */
+
+	int n_frames;
+
+	/* this is the frame that is currently "owned" by the OHCI DMA controller
+	   (set to -1 iff DMA is not running) 
+
+	   ! must lock against the interrupt handler when accessing it !
+
+	   RULES:
+
+	       Only the interrupt handler may change active_frame if DMA
+	          is running; if not, process may change it
+
+	       If the next frame is READY, the interrupt handler will advance
+	       active_frame when the current frame is finished.
+
+	       If the next frame is CLEAR, the interrupt handler will re-transmit
+	       the current frame, and the dropped_frames counter will be  incremented.
+
+	       The interrupt handler will NEVER advance active_frame to a
+	       frame that is not READY.
+	       
+	*/
+	int active_frame;
+	int first_run;
+
+	/* the same locking rules apply to these three fields also: */
+
+	/* altered ONLY from process context. Must check first_clear_frame->state;
+	   if it's READY, that means the ringbuffer is full with READY frames;
+	   if it's CLEAR, that means one or more ringbuffer frames are CLEAR */
+	unsigned int first_clear_frame; 
+
+	/* altered both by process and interrupt */
+	unsigned int n_clear_frames;   
+
+	/* only altered by the interrupt */
+	unsigned int dropped_frames;
+
+
+
+	/* the CIP accumulator and continuity counter are properties
+	   of the DMA stream as a whole (not a single frame), so they
+	   are stored here in the video_card */
+
+	unsigned long cip_accum;
+	unsigned long cip_n, cip_d;
+	unsigned int syt_offset;
+	unsigned int continuity_counter;
+
+	enum pal_or_ntsc pal_or_ntsc;
+
+	/* redundant, but simplifies the code somewhat */
+	unsigned int frame_size; /* in bytes */
+
+	/* the isochronous channel to use, -1 if video card is inactive */
+	int channel;
+
+	
+	/* physically contiguous packet ringbuffer for receive */
+#define MAX_PACKET_BUFFER 30
+	struct packet *packet_buffer;
+	dma_addr_t     packet_buffer_dma;
+	unsigned long  packet_buffer_size;
+	
+	unsigned int current_packet;
+	int first_frame; 	/* received first start frame marker? */
+	enum modes mode;
+};
+
+/* 
+   if the video_card is not initialized, then the ONLY fields that are valid are:
+   ohci
+   open
+   n_frames
+*/
+
+static inline int video_card_initialized(struct video_card *v)
+{
+	return v->n_frames > 0;
+}
+
+static int do_dv1394_init(struct video_card *video, struct dv1394_init *init);
+static int do_dv1394_init_default(struct video_card *video);
+static int do_dv1394_shutdown(struct video_card *video, int free_user_buf);
+
+
+/* NTSC empty packet rate accurate to within 0.01%, 
+   calibrated against a Sony DSR-40 DVCAM deck */
+
+#define CIP_N_NTSC   68000000
+#define CIP_D_NTSC 1068000000
+
+#define CIP_N_PAL  1
+#define CIP_D_PAL 16
+
+#endif /* _DV_1394_PRIVATE_H */
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)