Commit e14c7478 authored by Mikhail Karpenko's avatar Mikhail Karpenko

Add multichannel support to circbuf.c, fix formatting

parent b6edd8a4
/** @file circbuf.c /** @file circbuf.c
* *
* @brief drivers to manipulate large circular buffer that holds compressed * @brief Drivers to manipulate large circular buffer that holds compressed
* images/video. Buffer frame data is filled in by the FPGA, frame pointers and * images/video. Buffer frame data is filled in by the FPGA, frame pointers and
* essential frames metadata filled during servicing of the interrupts. * essential frames metadata filled during servicing of the interrupts.
* *
...@@ -71,20 +71,17 @@ ...@@ -71,20 +71,17 @@
#include "x393_macro.h" #include "x393_macro.h"
#include "x393.h" #include "x393.h"
#define CIRCBUF_DRIVER_NAME "circbuf driver"
/** Wait queue for the processes waiting for a new frame to appear in the circular buffer */
wait_queue_head_t circbuf_wait_queue;
struct circbuf_priv_t circbuf_priv[IMAGE_CHN_NUM]; struct circbuf_priv_t circbuf_priv[IMAGE_CHN_NUM];
struct circbuf_priv_t *circbuf_priv_ptr = circbuf_priv; struct circbuf_priv_t *circbuf_priv_ptr = circbuf_priv;
static struct device *g_dev_ptr; static struct device *g_dev_ptr;
static const struct of_device_id elphel393_circbuf_of_match[]; static const struct of_device_id elphel393_circbuf_of_match[];
/* really huge static DMA buffer (1288+8)*1032/3=445824 long-s */
// DMA_SIZE - in 32-bit words, not bytes
//static unsigned long ccam_dma_buf[CCAM_DMA_SIZE + (PAGE_SIZE>>2)] __attribute__ ((aligned (PAGE_SIZE)));
static unsigned long *ccam_dma_buf = NULL;
//!Without "static" system hangs after "Uncompressing Linux...
unsigned long * ccam_dma_buf_ptr = NULL;
//EXPORT_SYMBOL_GPL(ccam_dma_buf_ptr);
//unsigned long * ccam_dma = NULL; //! still used in autoexposure or something - why is in needed there?
int init_ccam_dma_buf_ptr(struct platform_device *pdev) int init_ccam_dma_buf_ptr(struct platform_device *pdev)
{ {
...@@ -92,8 +89,7 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev) ...@@ -92,8 +89,7 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
dma_addr_t dma_handle; dma_addr_t dma_handle;
const size_t dma_size = (CCAM_DMA_SIZE + (PAGE_SIZE >> 2)) * sizeof(int); const size_t dma_size = (CCAM_DMA_SIZE + (PAGE_SIZE >> 2)) * sizeof(int);
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
//ccam_dma_buf_ptr = ccam_dma_buf; unsigned long *ccam_dma_buf_ptr = NULL;
//ccam_dma = ccam_dma_buf; //&ccam_dma_buf[0]; Use in autoexposure
// use Elphel_buf if it was allocated // use Elphel_buf if it was allocated
if (pElphel_buf != NULL) { if (pElphel_buf != NULL) {
...@@ -109,10 +105,8 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev) ...@@ -109,10 +105,8 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
dev_info(dev, "%d bytes of DMA memory allocated at address 0x%08p", dma_size , dma_handle); dev_info(dev, "%d bytes of DMA memory allocated at address 0x%08p", dma_size , dma_handle);
} }
} }
ccam_dma_buf = ccam_dma_buf_ptr;
// set circbuf size in bytes // set circular buffer size in bytes
//set_globalParam(G_CIRCBUFSIZE, pElphel_buf->size * PAGE_SIZE);
set_globalParam(G_CIRCBUFSIZE, CCAM_DMA_SIZE); set_globalParam(G_CIRCBUFSIZE, CCAM_DMA_SIZE);
for (i = 0; i < IMAGE_CHN_NUM; i++) { for (i = 0; i < IMAGE_CHN_NUM; i++) {
...@@ -123,231 +117,189 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev) ...@@ -123,231 +117,189 @@ int init_ccam_dma_buf_ptr(struct platform_device *pdev)
return 0; return 0;
} }
static inline unsigned int minor_to_chn(int minor) /**
* @brief Converts file minor number to image compressor channel.
*
* This function assumes that the least significant nibble of minor contains image compressor channel number and
* next nibble contains device type. Channel numbers and device type are defined in #driver_numbers.h
* @param[in] minor file minor number
* @param[out] dev_type pointer to a variable which will hold device type or NULL if this value is not needed
* @return compressor channel number in the range [0..#IMAGE_CHN_NUM)
*/
static inline unsigned int minor_to_chn(unsigned int minor, unsigned int *dev_type)
{ {
//return minor - CIRCBUF_MINOR_CHN_OFFSET; if (dev_type != NULL) {
return 0; if ((minor & 0xf0) == CIRCBUF_MINOR || (minor & 0xf0) == HUFFMAN_MINOR || (minor & 0xf0) == JPEGHEAD_MINOR)
*dev_type = minor & 0xf0;
else
*dev_type = 0;
}
if ((minor & 0x0f) < IMAGE_CHN_NUM)
return minor & 0x0f;
else
return 0;
} }
//extern struct interframe_params_t frame_params; // cc353.c
/*!======================================================================================
*! Wait queue for the processes waiting for a new frame to appear in the circular buffer
*!======================================================================================*/
wait_queue_head_t circbuf_wait_queue;
/*!=========================================================================================================
*! circbuf top level device drivers. Minors are the same as before
*! CMOSCAM_MINOR_CIRCBUF, CMOSCAM_MINOR_JPEAGHEAD - just a new major
*!========================================================================================================*/
#define CIRCBUF_DRIVER_NAME "Elphel (R) Model 353 video buffer device driver"
// Read/write to circular buffer. Needed to find out what Axis DMA is doing
// also - jpeg header
struct circbuf_pd {
int minor; /// should be the first, same as in jpeghead_pd
int daemon_bit; /// poll() will produce POLLHUP if this bit is >=0 (set through lseek (, LSEEK_DAEMON_CIRCBUF,SEEK_END)
/// and the corresponding bit in P_DAEMON_EN goes 0
int imageWidth; /// image width to compare to current. G_SKIP_DIFF_FRAME
int imageHeight; /// image height to compare to current. G_SKIP_DIFF_FRAME
int tolerated; /// number of frames with different size tolerated
struct wait_queue *circbuf_wait_queue; ///NOTE: not used at all?
// something else to be added here?
};
// CMOSCAM_MINOR_HUFFMAN // huffman tables R/W
int circbuf_all_open(struct inode *inode, struct file *filp) int circbuf_all_open(struct inode *inode, struct file *filp)
{ {
int res; int res;
int minor = MINOR(inode->i_rdev); unsigned int minor = MINOR(inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "circbuf_all_open, minor = 0x%x\n", minor); dev_dbg(g_dev_ptr, "circbuf_all_open, minor = 0x%x\n", minor);
switch (MINOR(inode->i_rdev)) { minor_to_chn(minor, &dev_type);
case CMOSCAM_MINOR_CIRCBUF : switch (dev_type) {
res=circbuf_open(inode,filp); case CIRCBUF_MINOR:
res = circbuf_open(inode, filp);
break; break;
case CMOSCAM_MINOR_JPEAGHEAD : case JPEGHEAD_MINOR:
res=jpeghead_open(inode,filp); res = jpeghead_open(inode, filp);
break; break;
case CMOSCAM_MINOR_HUFFMAN : case HUFFMAN_MINOR:
res=huffman_open(inode,filp); res = huffman_open(inode, filp);
break; break;
default: default:
// kfree(filp->private_data); // already allocated
return -EINVAL; return -EINVAL;
} }
return res; return res;
} }
int circbuf_all_release(struct inode *inode, struct file *filp) { int circbuf_all_release(struct inode *inode, struct file *filp)
int res=0; {
int p = MINOR(inode->i_rdev); int res=0;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", p); unsigned int minor = MINOR(inode->i_rdev);
switch ( p ) { unsigned int dev_type;
case CMOSCAM_MINOR_CIRCBUF : dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
// res=circbuf_release(inode,filp);
break; minor_to_chn(minor, &dev_type);
case CMOSCAM_MINOR_JPEAGHEAD : switch (dev_type) {
// res=jpeghead_release(inode,filp); case CIRCBUF_MINOR:
break; // res=circbuf_release(inode,filp);
case CMOSCAM_MINOR_HUFFMAN : break;
// res=huffman_release(inode,filp); case JPEGHEAD_MINOR:
break; // res=jpeghead_release(inode,filp);
default: break;
return -EINVAL; //! do not need to free anything - "wrong number" case HUFFMAN_MINOR:
} // res=huffman_release(inode,filp);
if (filp->private_data) kfree(filp->private_data); break;
return res; default:
return -EINVAL;
}
if (filp->private_data) kfree(filp->private_data);
return res;
} }
loff_t circbuf_all_lseek(struct file * file, loff_t offset, int orig) loff_t circbuf_all_lseek(struct file *file, loff_t offset, int orig)
{ {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
struct interframe_params_t *fp = NULL;
int rp; int rp;
int minor = privData->minor; struct interframe_params_t *fp = NULL;
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
unsigned int chn = minor_to_chn(minor, &dev_type);
dev_dbg(g_dev_ptr, "circbuf_all_lseek, minor = 0x%x\n", minor); dev_dbg(g_dev_ptr, "circbuf_all_lseek, minor = 0x%x\n", minor);
switch (minor) {
case CMOSCAM_MINOR_CIRCBUF : switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_lseek(file, offset, orig); return circbuf_lseek(file, offset, orig);
case CMOSCAM_MINOR_JPEAGHEAD : case JPEGHEAD_MINOR:
if (orig == SEEK_END && offset > 0) { if (orig == SEEK_END && offset > 0) {
rp = BYTE2DW(offset) & (~7); // convert to index to long, align to 32-bytes rp = BYTE2DW(offset) & (~7); // convert to index to long, align to 32-bytes
//fp = (struct interframe_params_t *) &ccam_dma_buf_ptr[X313_BUFFSUB(rp, 8)]; //! 32 bytes before the frame pointer, may roll-over to the end of ccam_dma_buf_ptr fp = (struct interframe_params_t *) &circbuf_priv[chn].buf_ptr[X393_BUFFSUB(rp, 8)];
fp = (struct interframe_params_t *) &circbuf_priv[minor_to_chn(minor)].buf_ptr[X393_BUFFSUB(rp, 8)];
} }
return jpeghead_lseek(file, offset, orig, fp); return jpeghead_lseek(file, offset, orig, fp);
case CMOSCAM_MINOR_HUFFMAN : case HUFFMAN_MINOR:
return huffman_lseek(file, offset, orig); return huffman_lseek(file, offset, orig);
default: default:
return -EINVAL; return -EINVAL;
} }
} }
ssize_t circbuf_all_read(struct file * file, char * buf, size_t count, loff_t *off) { ssize_t circbuf_all_read(struct file *file, char *buf, size_t count, loff_t *off)
struct circbuf_pd * privData; {
privData = (struct circbuf_pd *) file->private_data; unsigned int minor = MINOR(file->f_inode->i_rdev);
dev_dbg(g_dev_ptr, "minor = 0x%x\n", privData->minor); unsigned int dev_type;
switch (privData->minor) { dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
case CMOSCAM_MINOR_CIRCBUF : return circbuf_read (file, buf, count, off);
case CMOSCAM_MINOR_JPEAGHEAD : return jpeghead_read (file, buf, count, off);
case CMOSCAM_MINOR_HUFFMAN : return huffman_read (file, buf, count, off);
default: return -EINVAL;
}
}
ssize_t circbuf_all_write(struct file * file, const char * buf, size_t count, loff_t *off) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
dev_dbg(g_dev_ptr, "minor = 0x%x, count = %d, off = %d\n", privData->minor, (int)count, (int)*off);
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF : return circbuf_write (file, buf, count, off);
// case CMOSCAM_MINOR_JPEAGHEAD : return jpeghead_write (file, buf, count, off); // same as other - write header
case CMOSCAM_MINOR_HUFFMAN : return huffman_write (file, buf, count, off);
default: return -EINVAL;
}
}
int circbuf_all_mmap (struct file *file, struct vm_area_struct *vma) {
struct circbuf_pd * privData;
privData = (struct circbuf_pd *) file->private_data;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", privData->minor);
switch (privData->minor) {
case CMOSCAM_MINOR_CIRCBUF : return circbuf_mmap (file, vma);
default: return -EINVAL;
}
}
unsigned int circbuf_all_poll (struct file *file, poll_table *wait) { minor_to_chn(minor, &dev_type);
struct circbuf_pd * privData; switch (dev_type) {
privData = (struct circbuf_pd *) file->private_data; case CIRCBUF_MINOR:
dev_dbg(g_dev_ptr, "minor = 0x%x\n", privData->minor); return circbuf_read(file, buf, count, off);
switch (privData->minor) { case JPEGHEAD_MINOR:
case CMOSCAM_MINOR_CIRCBUF : return jpeghead_read(file, buf, count, off);
return circbuf_poll (file, wait); case HUFFMAN_MINOR:
default: return -EINVAL; return huffman_read(file, buf, count, off);
} default:
return -EINVAL;
}
} }
int circbuf_all_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { ssize_t circbuf_all_write(struct file *file, const char *buf, size_t count, loff_t *off)
struct circbuf_pd * privData; {
privData = (struct circbuf_pd *) filp->private_data; unsigned int minor = MINOR(file->f_inode->i_rdev);
printk("\n========== IOCTL is not implemented in circbuf_all_ioctl, minor=0x%x, cmd=0x%x, _IOC_NR(cmd)=0x%x, arg=0x%x\n",privData-> minor, (int) cmd, _IOC_NR(cmd), (int) arg); unsigned int dev_type;
return -EINVAL; dev_dbg(g_dev_ptr, "minor = 0x%x, count = %d, off = %d\n", minor, (int)count, (int)*off);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_write (file, buf, count, off);
case JPEGHEAD_MINOR:
// no method for this module
return -EINVAL;
case HUFFMAN_MINOR:
return huffman_write (file, buf, count, off);
default:
return -EINVAL;
}
} }
int circbuf_all_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
minor_to_chn(minor, &dev_type);
switch (dev_type) {
case CIRCBUF_MINOR:
return circbuf_mmap(file, vma);
default:
return -EINVAL;
}
}
unsigned int circbuf_all_poll (struct file *file, poll_table *wait)
{
unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int dev_type;
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
int circbuf_open(struct inode *inode, struct file *filp) { // set filesize minor_to_chn(minor, &dev_type);
struct circbuf_pd * privData; switch (dev_type) {
privData= (struct circbuf_pd *) kmalloc(sizeof(struct circbuf_pd),GFP_KERNEL); case CIRCBUF_MINOR:
if (!privData) return -ENOMEM; return circbuf_poll(file, wait);
filp->private_data = privData; default:
privData-> minor=MINOR(inode->i_rdev); return -EINVAL;
}
}
//inode->i_size = ((CCAM_DMA_SIZE) << 2); int circbuf_open(struct inode *inode, struct file *filp)
inode->i_size = CCAM_DMA_SIZE; {
dev_dbg(g_dev_ptr, "inode->i_size = 0x%x\n"); inode->i_size = CCAM_DMA_SIZE;
dev_dbg(g_dev_ptr, "inode->i_size = 0x%x\n");
//!should be removed (or else you can not ftp file - it will start from WP) return 0;
// circbuf_lseek(filp, LSEEK_CIRC_LAST, SEEK_END); //! position at last acquired frame, ignore result
return 0;
} }
/*!=============================================================================================
*! Overloading lseek with additional functionality (to avoid ioctls)
*! with orig==SEEK_END lseek will treat (offset>0) as a command
*! to manipulate frame pointer(s) or wait for the image to be ready
*! using these commands
*! LSEEK_CIRC_TORP .- set filepointer to global (shared) read pointer
*! LSEEK_CIRC_TOWP - set filepointer to FPGA write pointer (next frame to be acquired)
*! LSEEK_CIRC_PREV - move pointer to the previous frame, return -EOVERFLOW if there are none
*! LSEEK_CIRC_NEXT - advance pointer to the next frame, return -EOVERFLOW if was already
*! at the last
*! LSEEK_CIRC_LAST - move pointer to the last acquired frame (default after open)
*! (it is combination of 2+3)
*! LSEEK_CIRC_FIRST - move pointer to the first acquired frame. It s not safe to rely
*! on this pointer if more frames are expected - next incoming frame
*! can overwrite this one.
*! LSEEK_CIRC_SCND - move pointer to the second oldest acquired frame. A slightly safer
*! to use instead of LSEEK_CIRC_FIRST when constant acquisition is on
*! and sensor provides new frames - this frame will likely survive longer
*! LSEEK_CIRC_SETP - save current pointer to global read pointer
*! LSEEK_CIRC_VALID - verify that the frame at current location is valid (not overrun in the buffer)
*! Returns file pointer if valid, else - -1
*! LSEEK_CIRC_READY - verify frame at current loacation is available (valid and acquired)
*! Returns file pointer if ready, else - -1
*! LSEEK_CIRC_WAIT - sleep until next frame is acquired
*! All commands but (LSEEK_CIRC_TOWP,LSEEK_CIRC_LAST,LSEEK_CIRC_FIRST) will return -EINVAL if read
*! pointer is not valid (i.e buffer was overrun and data pointed is lost). if success they return
*! the current (byte *) to the start of the frame data (parameters are at
*! offsett =-32 from it)
*! (0, SEEK_CUR) also verifies that the header is not overwritten. It can be used
*! after buffering frame data to verify you got it all correctly
*! SEEK_CUR also supports the circular nature of the buffer and rolls over if needed
*! Additional commands for SEEK_END (they _DO_ modify the current file pointer !)
*! LSEEK_CIRC_FREE - returnes remaining memory in circbuf from the current file pointer,
*! or -EINVAL if the pointer is invalid. As this command uses the buffer write pointer
*! that is updated only when the complete frame is in the buffer, the actual
*! free memory may be less by a whole frame if compressor is running.
*! LSEEK_CIRC_USED - returnes memory used in the in circbuf from the current file pointer,
*! or -EINVAL if the pointer is invalid
*!=============================================================================================*/
//!returns 0 if rp is a valid read ponter
//! returns 1 if there is a frame at this address
//! returns 0 if the pointer is for the frame yet to be acquired
//! returns -1 if there is no frame at this index
//! returns -2 if the rp is not 32-bytes aligned
//!sets *fpp to the frame header, including signature and length
void dump_interframe_params(struct interframe_params_t *params, int offset) void dump_interframe_params(struct interframe_params_t *params, int offset)
{ {
dev_dbg(g_dev_ptr, "Dump of interframe parameters at offset 0x%x:\n", offset); dev_dbg(g_dev_ptr, "Dump of interframe parameters at offset 0x%x:\n", offset);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, params, sizeof(struct interframe_params_t)); print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, params, sizeof(struct interframe_params_t));
} }
/**
* @brief
*/
unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chunk_offset) unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chunk_offset)
{ {
unsigned long len32; unsigned long len32;
...@@ -365,8 +317,17 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun ...@@ -365,8 +317,17 @@ unsigned long get_image_length(int byte_offset, unsigned int chn, int *last_chun
return len32; return len32;
} }
// rp is byte offset /**
int circbufValidPointer(int rp, struct interframe_params_t ** fpp, unsigned int chn) * @brief Check that read pointer is valid
* @param[in] rp read pointer to be checked; this pointer is in bytes
* @param[out] fpp pointer to #interframe_params_t structure, this pointer will be set to
* frame header before \e rp and will point to its parameters
* @param[in] chn specify compressor channel number which pointer should be checked
* @return 0 if the pointer is for the frame yet to be acquired, 1 if there is a valid frame at this address,
* -1 if there is no frame at this index, -2 if the pointer is not 32-bytes aligned
* sets *fpp to the frame header, including signature and length
*/
int circbuf_valid_ptr(int rp, struct interframe_params_t **fpp, unsigned int chn)
{ {
int last_image_chunk; int last_image_chunk;
unsigned int sec; unsigned int sec;
...@@ -375,7 +336,8 @@ int circbufValidPointer(int rp, struct interframe_params_t ** fpp, unsigned int ...@@ -375,7 +336,8 @@ int circbufValidPointer(int rp, struct interframe_params_t ** fpp, unsigned int
unsigned int len32 = get_image_length(DW2BYTE(wp), chn, &last_image_chunk); unsigned int len32 = get_image_length(DW2BYTE(wp), chn, &last_image_chunk);
struct interframe_params_t *fp; struct interframe_params_t *fp;
if (rp & 0x1f) { //!rp is not 32-bytes aligned if (rp & 0x1f) {
// rp is not 32-bytes aligned
dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn); dev_dbg(g_dev_ptr, "misaligned pointer rp = 0x%x for channel %d\n", rp, chn);
return -2; return -2;
} }
...@@ -396,205 +358,241 @@ int circbufValidPointer(int rp, struct interframe_params_t ** fpp, unsigned int ...@@ -396,205 +358,241 @@ int circbufValidPointer(int rp, struct interframe_params_t ** fpp, unsigned int
return 1; return 1;
} }
loff_t circbuf_lseek(struct file * file, loff_t offset, int orig) { /**
unsigned int len32; * @brief Reposition read/write file offset
*
* This function is overloaded with additional functionality in order to avoid ioctls.
* In case user-space program set <em>orig == SEEK_END</em>, \e lseek will treat (offset > 0) as a command
* to manipulate frame pointer(s) or wait for the image to be ready using these commands:
*
* LSEEK_CIRC_TORP - set file pointer to global (shared) read pointer;
* LSEEK_CIRC_TOWP - set file pointer to FPGA write pointer (next frame to be acquired);
* LSEEK_CIRC_PREV - move pointer to the previous frame, return \e -EOVERFLOW if there are none;
* LSEEK_CIRC_NEXT - advance pointer to the next frame, return \e -EOVERFLOW if it was at the last frame
* LSEEK_CIRC_LAST - move pointer to the last acquired frame (default after open), this is a combination
* of LSEEK_CIRC_TOWP and LSEEK_CIRC_PREV;
* LSEEK_CIRC_FIRST - move pointer to the first acquired frame. It s not safe to rely
* on this pointer if more frames are expected - next incoming frame
* can overwrite this one;
* LSEEK_CIRC_SCND - move pointer to the second oldest acquired frame. A slightly safer
* to use instead of LSEEK_CIRC_FIRST when constant acquisition is on
* and sensor provides new frames - this frame will likely survive longer;
* LSEEK_CIRC_SETP - save current pointer to global read pointer
* LSEEK_CIRC_VALID - verify that the frame at current location is valid (not overrun in the buffer).
* Returns file pointer if it is valid and -1 otherwise;
* LSEEK_CIRC_READY - verify frame at current location is available (valid and acquired).
* Returns file pointer if it is ready or -1 otherwise
* LSEEK_CIRC_WAIT - sleep until next frame is acquired.
* All commands but (LSEEK_CIRC_TOWP, LSEEK_CIRC_LAST, LSEEK_CIRC_FIRST) will return -EINVAL if read
* pointer is not valid (i.e buffer was overrun and data pointed is lost). In case of success, they return
* current (byte *) to the start of the frame data (parameters are at offset - 32 from it).
* (0, SEEK_CUR) also verifies that the header is not overwritten. It can be used after buffering frame data to
* verify you got it all correctly.
*
* SEEK_CUR also supports the circular nature of the buffer and rolls over if needed.
*
* Additional commands for SEEK_END (they _DO_ modify the current file pointer):
* LSEEK_CIRC_FREE - returns remaining memory in circbuf from the current file pointer,
* or -EINVAL if the pointer is invalid. As this command uses the buffer write pointer
* that is updated only when the complete frame is in the buffer, the actual
* free memory may be less by a whole frame if compressor is running.
* LSEEK_CIRC_USED - returns memory used in the in circbuf from the current file pointer,
* or -EINVAL if the pointer is invalid
* @param[in] file pointer to \e file structure
* @param[in] offset offset inside buffer in bytes
* @param[in] orig origin
* @return current file pointer position if operation was successful and error code otherwise
*/
loff_t circbuf_lseek(struct file *file, loff_t offset, int orig)
{
unsigned int len32 = 0;
int inserted_bytes; int inserted_bytes;
int last_image_chunk; int last_image_chunk;
int img_start, next_img, padded_frame; int img_start, next_img, padded_frame;
unsigned int minor = MINOR(file->f_inode->i_rdev); unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor); unsigned int chn = minor_to_chn(minor, NULL);
// orig 0: position from begning struct interframe_params_t * fp;
// orig 1: relative from current int fvld = -1;
// orig 2: position from last address int rp, bp;
int l = CCAM_DMA_SIZE; dev_dbg(g_dev_ptr, "start processing LSEEK operation: offset = 0x%x, orig = 0x%x\n",(int) offset, (int) orig);
int fl=0;// frame length
struct interframe_params_t * fp; switch (orig) {
int fvld=-1; case SEEK_SET:
int rp, bp; //, p; file->f_pos = offset;
// int pf; // previous frame break;
dev_dbg(g_dev_ptr, "start processing LSEEK operation: offset = 0x%x, orig = 0x%x\n",(int) offset, (int) orig); case SEEK_CUR:
switch(orig) { if (offset) file->f_pos += offset;
case SEEK_SET: else if (circbuf_valid_ptr(file->f_pos, &fp, chn) < 0 ) return -EINVAL; //!no frames at the specified location or pointer is not 32-byte aligned
file->f_pos = offset; break;
break; case SEEK_END:
case SEEK_CUR: if (offset <= 0) {
if (offset) file->f_pos += offset; file->f_pos = CCAM_DMA_SIZE + offset;
else if (circbufValidPointer(file->f_pos, &fp, chn) <0 ) return -EINVAL; //!no frames at the specified location or pointer is not 32-byte aligned } else {
break; // verify current frame pointer
case SEEK_END: switch (offset) {
if (offset <= 0) { case LSEEK_CIRC_TORP:
file->f_pos = l + offset; file->f_pos = camseq_get_jpeg_rp(chn) << 2;
} else { //! New functionality case LSEEK_CIRC_PREV:
//!verify the frame pointer case LSEEK_CIRC_NEXT:
switch (offset) { case LSEEK_CIRC_SETP:
case LSEEK_CIRC_TORP: case LSEEK_CIRC_VALID:
//file->f_pos=camSeqGetJPEG_rp()<<2; //! set file pointer to global read pointer, and proceed case LSEEK_CIRC_READY:
file->f_pos = camseq_get_jpeg_rp(chn) << 2; case LSEEK_CIRC_FREE:
case LSEEK_CIRC_PREV: case LSEEK_CIRC_USED:
case LSEEK_CIRC_NEXT: if ((fvld = circbuf_valid_ptr(file->f_pos, &fp, chn)) < 0)
case LSEEK_CIRC_SETP: return -EINVAL; // no frames at the specified location
case LSEEK_CIRC_VALID: }
case LSEEK_CIRC_READY: switch (offset) {
case LSEEK_CIRC_FREE: case LSEEK_CIRC_FREE:
case LSEEK_CIRC_USED: dev_dbg(g_dev_ptr, "LSEEK_CIRC_FREE: checking remaining memory in circbuf\n");
if ((fvld = circbufValidPointer(file->f_pos, &fp, chn)) < 0) bp = file->f_pos - (camseq_get_jpeg_wp(chn) << 2);
return -EINVAL; //!no frames at the specified location return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); //!Has a side effect of moving a file pointer!
} case LSEEK_CIRC_USED:
switch (offset) { dev_dbg(g_dev_ptr, "LSEEK_CIRC_USED: checking used memory in circbuf\n");
case LSEEK_CIRC_FREE: bp = (camseq_get_jpeg_wp(chn) << 2) - file->f_pos;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FREE: checking remaining memory in circbuf\n"); return (file->f_pos = (bp > 0) ? bp : (bp + CCAM_DMA_SIZE)); //!Has a side effect of moving a file pointer!
bp = file->f_pos - (camseq_get_jpeg_wp(chn) << 2); case LSEEK_CIRC_TORP:
return (file->f_pos = (bp > 0) ? bp : (bp + l)); //!Has a side effect of moving a file pointer! // no actions to be done here, the pointer was set on previous step
case LSEEK_CIRC_USED: break;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_USED: checking used memory in circbuf\n"); case LSEEK_CIRC_TOWP:
bp = (camseq_get_jpeg_wp(chn) << 2) - file->f_pos; file->f_pos = camseq_get_jpeg_wp(chn) << 2;
return (file->f_pos = (bp > 0) ? bp : (bp + l)); //!Has a side effect of moving a file pointer! break;
case LSEEK_CIRC_TORP: case LSEEK_CIRC_LAST:
// no actions to be done here, the pointer was set on previous step next_img = camseq_get_jpeg_wp(chn) << 2;
break;
case LSEEK_CIRC_TOWP: dev_dbg(g_dev_ptr, "LSEEK_CIRC_LAST: next_img = 0x%x, fvld = %d\n", next_img, fvld);
//file->f_pos=camSeqGetJPEG_wp()<<2; // no checking if it is valid dev_dbg(g_dev_ptr, "mem dump of last 0x40 bytes in buffer number %d\n", chn);
file->f_pos = camseq_get_jpeg_wp(chn) << 2; print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40);
break;
case LSEEK_CIRC_LAST: len32 = get_image_length(next_img, chn, &last_image_chunk);
next_img = camseq_get_jpeg_wp(chn) << 2; if ((len32 & MARKER_FF) != MARKER_FF) {
//fvld = circbufValidPointer(next_img, &fp, chn); // we should not be here as the position was checked in circbufValidPointer
dev_dbg(g_dev_ptr, "failed to get marker 0xFF at 0x%x, len32: 0x%x\n", next_img, len32);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_LAST: next_img = 0x%x, fvld = %d\n", next_img, fvld); return -EOVERFLOW;
dev_dbg(g_dev_ptr, "mem dump of last 0x40 bytes in buffer number %d\n", chn); }
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40); len32 &= FRAME_LENGTH_MASK;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32);
len32 = get_image_length(next_img, chn, &last_image_chunk); dev_dbg(g_dev_ptr, "calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
if ((len32 & MARKER_FF) != MARKER_FF) { if (circbuf_valid_ptr(img_start, &fp, chn) < 0)
// we should not be here as the position was checked in circbufValidPointer return -EOVERFLOW;
dev_dbg(g_dev_ptr, "failed to get marker 0xFF at 0x%x, len32: 0x%x\n", next_img, len32); file->f_pos = img_start;
return -EOVERFLOW; dev_dbg(g_dev_ptr, "LSEEK_CIRC_LAST: moving file->f_pos to 0x%llx\n", file->f_pos);
} break;
len32 &= FRAME_LENGTH_MASK; case LSEEK_CIRC_PREV:
//inserted_bytes = ((CHUNK_SIZE - (((len32 % CHUNK_SIZE) + CCAM_MMAP_META) % CHUNK_SIZE) - ADJUSTMENT) % CHUNK_SIZE ) + ADJUSTMENT; next_img = camseq_get_jpeg_wp(chn) << 2;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - INSERTED_BYTES(len32) - CCAM_MMAP_META, len32); fvld = circbuf_valid_ptr(next_img, &fp, chn);
dev_dbg(g_dev_ptr, "calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
if (circbufValidPointer(img_start, &fp, chn) < 0) dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: next_img = 0x%x, fvld = %d\n", next_img, fvld);
return -EOVERFLOW; dev_dbg(g_dev_ptr, "mem dump of last 0x40 bytes in buffer number %d\n", chn);
file->f_pos = img_start; print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_LAST: moving file->f_pos to 0x%llx\n", file->f_pos);
break; len32 = get_image_length(next_img, chn, &last_image_chunk);
case LSEEK_CIRC_PREV: if ((len32 & MARKER_FF) != MARKER_FF) {
next_img = camseq_get_jpeg_wp(chn) << 2; // we should not be here as the position was checked in circbufValidPointer
fvld = circbufValidPointer(next_img, &fp, chn); dev_dbg(g_dev_ptr, "failed to get marker 0xFF at 0x%x, len32: 0x%x\n", next_img, len32);
return -EOVERFLOW;
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: next_img = 0x%x, fvld = %d\n", next_img, fvld); }
dev_dbg(g_dev_ptr, "mem dump of last 0x40 bytes in buffer number %d\n", chn); len32 &= 0xffffff;
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, &circbuf_priv[chn].buf_ptr[BYTE2DW(next_img - OFFSET_X40)], OFFSET_X40); inserted_bytes = ((CHUNK_SIZE - (((len32 % CHUNK_SIZE) + CCAM_MMAP_META) % CHUNK_SIZE) - ADJUSTMENT) % CHUNK_SIZE ) + ADJUSTMENT;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - inserted_bytes - CCAM_MMAP_META, len32);
len32 = get_image_length(next_img, chn, &last_image_chunk); dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
if ((len32 & MARKER_FF) != MARKER_FF) {
// we should not be here as the position was checked in circbufValidPointer // move file pointer only if previous frame valid
dev_dbg(g_dev_ptr, "failed to get marker 0xFF at 0x%x, len32: 0x%x\n", next_img, len32); len32 = get_image_length(img_start, chn, NULL);
return -EOVERFLOW; if (circbuf_valid_ptr(img_start, &fp, chn) < 0)
} return -EOVERFLOW;
len32 &= 0xffffff; file->f_pos = img_start;
inserted_bytes = ((CHUNK_SIZE - (((len32 % CHUNK_SIZE) + CCAM_MMAP_META) % CHUNK_SIZE) - ADJUSTMENT) % CHUNK_SIZE ) + ADJUSTMENT; break;
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - inserted_bytes - CCAM_MMAP_META, len32); case LSEEK_CIRC_NEXT:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_PREV: calculated start address = 0x%x, length = 0x%x\n", img_start, len32); dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: file->f_pos = 0x%x, fvld = %d, fp->len32 = 0x%x\n", file->f_pos, fvld, fp->frame_length);
if (fvld <= 0)
// move file pointer only if previous frame valid return -EOVERFLOW; //! no frames after current
len32 = get_image_length(img_start, chn, NULL); // calculate the full length of current frame and advance file pointer by this value
if (circbufValidPointer(img_start, &fp, chn) < 0) padded_frame = fp->frame_length + INSERTED_BYTES(fp->frame_length) + CHUNK_SIZE + CCAM_MMAP_META;
return -EOVERFLOW; file->f_pos = X393_BUFFADD(file->f_pos, padded_frame); // do it even if the next frame does not yet exist
file->f_pos = img_start; dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: moving file->f_pos to 0x%x\n", file->f_pos);
break; break;
case LSEEK_CIRC_NEXT: case LSEEK_CIRC_FIRST:
dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: file->f_pos = 0x%x, fvld = %d, fp->len32 = 0x%x\n", file->f_pos, fvld, fp->frame_length); case LSEEK_CIRC_SCND:
if (fvld <= 0) {
return -EOVERFLOW; //! no frames after current int nf = 0; // number of frames;
// calculate the full length of current frame and advance file pointer by this value int nz = 1; // number of start crossing of the circular buffer (counter to prevent looping forever)
padded_frame = fp->frame_length + INSERTED_BYTES(fp->frame_length) + CHUNK_SIZE + CCAM_MMAP_META; int prev_p, preprev_p;
file->f_pos = X393_BUFFADD(file->f_pos, padded_frame); // do it even if the next frame does not yet exist // starting from the write pointer to be able to count all the frames in the buffer
dev_dbg(g_dev_ptr, "LSEEK_CIRC_NEXT: moving file->f_pos to 0x%x\n", file->f_pos); rp = camseq_get_jpeg_wp(chn);
break; prev_p = preprev_p = rp;
case LSEEK_CIRC_FIRST: file->f_pos = DW2BYTE(rp);
case LSEEK_CIRC_SCND: while (((fvld = circbuf_valid_ptr(DW2BYTE(rp), &fp, chn)) >= 0) & (nz >= 0)) {
{ nf++;
int nf = 0; //! number of frames; preprev_p = prev_p; // second known good (at least first one)
int nz = 1; //! number of crossing of start of the circular buffer (counter to prevent looping forever) prev_p=rp; // now - current, known good
int prev_p, preprev_p; len32 = get_image_length(DW2BYTE(rp), chn, last_image_chunk);
//! Starting from the write pointer do be able to count all the frames in the buffer dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x", nf, rp, fvld, len32);
//rp=camSeqGetJPEG_wp(); if ((len32 & MARKER_FF) != MARKER_FF ) break; //! no frames before rp (==prev_p)
rp = camseq_get_jpeg_wp(chn); //! move rp to the previous frame
prev_p = preprev_p = rp; len32 &= 0xffffff;
file->f_pos = DW2BYTE(rp); inserted_bytes = ((CHUNK_SIZE - (((len32 % CHUNK_SIZE) + CCAM_MMAP_META) % CHUNK_SIZE) - ADJUSTMENT) % CHUNK_SIZE ) + ADJUSTMENT;
while (((fvld = circbufValidPointer(DW2BYTE(rp), &fp, chn)) >= 0) & (nz >= 0)) { img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - inserted_bytes - CCAM_MMAP_META, len32);
nf++; dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: calculated start address = 0x%x, length = 0x%x\n", img_start, len32);
preprev_p = prev_p; //! second known good (at least first one) rp = BYTE2DW(img_start);
prev_p=rp; //!now - current, known good if (rp > prev_p) nz-- ; // rolled through zero - make sure we'll not stuck in this loop forever
//fl=ccam_dma_buf[X313_BUFFSUB(rp, 9)] ^ X313_LENGTH_MASK; }
len32 = get_image_length(DW2BYTE(rp), chn, last_image_chunk); dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: finish stepping back through frames, number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x", nf, rp, fvld, len32);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x", nf, rp, fvld, len32); file->f_pos = ((offset == LSEEK_CIRC_SCND) ? preprev_p : prev_p) << 2;
if ((len32 & MARKER_FF) != MARKER_FF ) break; //! no frames before rp (==prev_p) break;
//! move rp to the previous frame }
len32 &= 0xffffff; case LSEEK_CIRC_SETP:
inserted_bytes = ((CHUNK_SIZE - (((len32 % CHUNK_SIZE) + CCAM_MMAP_META) % CHUNK_SIZE) - ADJUSTMENT) % CHUNK_SIZE ) + ADJUSTMENT; dev_dbg(g_dev_ptr, "LSEK_CIRC_SETP: Setting jpeg_rp for channel %d to file->f_pos = 0x%x\n", chn, file->f_pos);
img_start = X393_BUFFSUB(last_image_chunk + CHUNK_SIZE - inserted_bytes - CCAM_MMAP_META, len32); camseq_set_jpeg_rp(chn, file->f_pos >> 2);
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: calculated start address = 0x%x, length = 0x%x\n", img_start, len32); break;
rp = BYTE2DW(img_start); case LSEEK_CIRC_VALID:
if (rp > prev_p) nz-- ; // rolled through zero - make sure we'll not stuck in this loop forever // no actions to be done here, the pointer was checked on previous step
} dev_dbg(g_dev_ptr, "LSEEK_CIRC_VALID: no action required\n");
dev_dbg(g_dev_ptr, "LSEEK_CIRC_FIRST or LSEEK_CIRC_SCND: finish stepping back through frames, number of frames = %d, rp = 0x%x, fvld = %d, len32 = 0x%x", nf, rp, fvld, len32); break;
file->f_pos = ((offset == LSEEK_CIRC_SCND) ? preprev_p : prev_p) << 2; case LSEEK_CIRC_READY:
break; dev_dbg(g_dev_ptr, "LSEEK_CIRC_READY: checking fvld, fvld = %d\n", fvld);
} if (fvld <=0) return -EINVAL; // no frame is available better code?
case LSEEK_CIRC_SETP: break;
//camSeqSetJPEG_rp(file->f_pos>>2); case LSEEK_CIRC_WAIT:
dev_dbg(g_dev_ptr, "LSEK_CIRC_SETP: Setting jpeg_rp for channel %d to file->f_pos = 0x%x\n", chn, file->f_pos); dev_dbg(g_dev_ptr, "LSEEK_CIRC_WAIT\n");
camseq_set_jpeg_rp(chn, file->f_pos >> 2); while (((fvld=circbuf_valid_ptr(file->f_pos, &fp, chn)))==0) { // only while not ready, ready or BAD - return
break; wait_event_interruptible(circbuf_wait_queue, (camseq_get_jpeg_wp(chn) << 2) != file->f_pos);
case LSEEK_CIRC_VALID: }
// no actions to be done here, the pointer was checked on previous step if (fvld < 0) return -ESPIPE; // invalid seek - have better code?
dev_dbg(g_dev_ptr, "LSEEK_CIRC_VALID: no action required\n"); return file->f_pos ; // data already available, return file pointer
break; default:
case LSEEK_CIRC_READY: if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) {
dev_dbg(g_dev_ptr, "LSEEK_CIRC_READY: checking fvld, fvld = %d\n", fvld); wait_event_interruptible(circbuf_wait_queue, get_imageParamsThis(P_DAEMON_EN) & (1<<(offset & 0x1f)));
if (fvld <=0) return -EINVAL; //! no frame is available better code? }
break; }
case LSEEK_CIRC_WAIT: return ( file->f_pos ); // file position >= 0
dev_dbg(g_dev_ptr, "LSEEK_CIRC_WAIT\n"); }
while (((fvld=circbufValidPointer(file->f_pos, &fp, chn)))==0) { //! only while not ready, ready or BAD - return break;
//wait_event_interruptible(circbuf_wait_queue,(camSeqGetJPEG_wp()<<2)!=file->f_pos); default:
wait_event_interruptible(circbuf_wait_queue, (camseq_get_jpeg_wp(chn) << 2) != file->f_pos); return -EINVAL;
} }
if (fvld < 0) return -ESPIPE; //!invalid seek - have better code? // roll-over position
return file->f_pos ; //! data already available, return file pointer while (file->f_pos < 0) file->f_pos += CCAM_DMA_SIZE;
default: while (file->f_pos > CCAM_DMA_SIZE) file->f_pos -= CCAM_DMA_SIZE;
if ((offset & ~0x1f)==LSEEK_DAEMON_CIRCBUF) { if ((orig !=SEEK_END) && (file->f_pos == CCAM_DMA_SIZE)) file->f_pos=0; // only for lseek(fd,0,SEEK_END) the result will be file size, others will roll to 0
wait_event_interruptible(circbuf_wait_queue, get_imageParamsThis(P_DAEMON_EN) & (1<<(offset & 0x1f))); return file->f_pos ;
}
}
return ( file->f_pos ); //! file position >=0
}
break;
default:
return -EINVAL;
}
// roll-over position
while (file->f_pos < 0) file->f_pos+=l;
while (file->f_pos > l) file->f_pos-=l;
if ((orig !=SEEK_END) && (file->f_pos == l)) file->f_pos=0; //!only for lseek (fd,0,SEEK_END) the result will be file size, others will roll to 0
return file->f_pos ;
} }
/** /**
* @brief This function handles write operations for circbuf file * @brief This function handles write operations for circbuf files.
* Note: never use \e file->f_pos in this function.
* @param[in] file pointer to <em>struct file</em> * @param[in] file pointer to <em>struct file</em>
* @param[in] buf pointer to buffer containing data * @param[in] buf pointer to buffer containing data
* @param[in] count number of bytes in buffer * @param[in] count number of bytes in buffer
* @param[in] off offset * @param[in] off offset
* @return number of bytes read form \e buf * @return number of bytes read form \e buf
*/ */
ssize_t circbuf_write(struct file * file, const char * buf, size_t count, loff_t *off) { ssize_t circbuf_write(struct file *file, const char *buf, size_t count, loff_t *off)
{
unsigned long p; unsigned long p;
char *char_pb = (char *)ccam_dma_buf; unsigned int minor = MINOR(file->f_inode->i_rdev);
struct circbuf_pd *priv = file->private_data; unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%x", minor, count, off);
/* debug code follows*/ /* debug code follows*/
switch (buf[0] - 0x30) { switch (buf[0] - 0x30) {
...@@ -607,16 +605,13 @@ ssize_t circbuf_write(struct file * file, const char * buf, size_t count, loff_t ...@@ -607,16 +605,13 @@ ssize_t circbuf_write(struct file * file, const char * buf, size_t count, loff_t
} }
/* debug code end */ /* debug code end */
//D(printk("circbuf_write\n"));
/// ************* NOTE: Never use file->f_pos in write() and read() !!!
p = *off; p = *off;
if(p >= (CCAM_DMA_SIZE << 2)) if (p >= CCAM_DMA_SIZE)
p = (CCAM_DMA_SIZE << 2); p = CCAM_DMA_SIZE;
if((p + count) > (CCAM_DMA_SIZE << 2)) { // truncate count if ((p + count) > CCAM_DMA_SIZE)
count = (CCAM_DMA_SIZE << 2) - p; count = CCAM_DMA_SIZE - p;
} if (count) {
if(count) { if (copy_from_user(&circbuf_priv[chn].buf_ptr[BYTE2DW(p)], buf, count))
if(copy_from_user(&char_pb[p], buf, count))
return -EFAULT; return -EFAULT;
*off += count; *off += count;
} }
...@@ -624,36 +619,39 @@ ssize_t circbuf_write(struct file * file, const char * buf, size_t count, loff_t ...@@ -624,36 +619,39 @@ ssize_t circbuf_write(struct file * file, const char * buf, size_t count, loff_t
} }
/** /**
* @brief This function handles read operations for circbuf file * @brief This function handles read operations for circbuf files.
* Note: never use \e file->f_pos in this function.
* @param[in] file pointer to <em>struct file</em> * @param[in] file pointer to <em>struct file</em>
* @param[in] buf pointer to buffer where data will be written to * @param[in] buf pointer to buffer where data will be written to
* @param[in] count number of bytes written to \e buf * @param[in] count number of bytes written to \e buf
* @param[in] off offset * @param[in] off offset
* @return number of bytes written to \e buf * @return number of bytes written to \e buf
*/ */
ssize_t circbuf_read(struct file * file, char * buf, size_t count, loff_t *off) { ssize_t circbuf_read(struct file *file, char *buf, size_t count, loff_t *off)
unsigned long p; {
char * char_pb = (char *) ccam_dma_buf; unsigned long p;
struct circbuf_pd *priv = file->private_data; unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
p = *off; dev_dbg(g_dev_ptr, "minor = 0x%x, count = 0x%x, off = 0x%x", minor, count, off);
dev_dbg(g_dev_ptr, "read from circbuf pos = %d, count = %d, off = %d\n", p, count, off);
if (p >= (CCAM_DMA_SIZE<<2)) p = (CCAM_DMA_SIZE<<2); p = *off;
if( (p + count) > (CCAM_DMA_SIZE<<2)) { // truncate count if (p >= CCAM_DMA_SIZE)
count = (CCAM_DMA_SIZE<<2) - p; p = CCAM_DMA_SIZE;
} if ((p + count) > CCAM_DMA_SIZE)
if (count) { count = CCAM_DMA_SIZE - p;
if (copy_to_user(buf, &char_pb[p], count)) return -EFAULT; if (count) {
// file->f_pos+=count; if (copy_to_user(buf, &circbuf_priv[chn].buf_ptr[BYTE2DW(p)], count))
*off+=count; return -EFAULT;
} *off+=count;
return count; }
return count;
} }
int circbuf_mmap(struct file *file, struct vm_area_struct *vma) int circbuf_mmap(struct file *file, struct vm_area_struct *vma)
{ {
int ret; int ret;
int chn = minor_to_chn(MINOR(file->f_inode->i_rdev)); unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor, NULL);
dev_dbg(g_dev_ptr, "vm_start = 0x%lx\n", vma->vm_start); dev_dbg(g_dev_ptr, "vm_start = 0x%lx\n", vma->vm_start);
dev_dbg(g_dev_ptr, "vm_end = 0x%lx\n", vma->vm_end); dev_dbg(g_dev_ptr, "vm_end = 0x%lx\n", vma->vm_end);
...@@ -661,10 +659,9 @@ int circbuf_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -661,10 +659,9 @@ int circbuf_mmap(struct file *file, struct vm_area_struct *vma)
dev_dbg(g_dev_ptr, "vm_file = 0x%lx\n", (unsigned long)vma->vm_file); dev_dbg(g_dev_ptr, "vm_file = 0x%lx\n", (unsigned long)vma->vm_file);
dev_dbg(g_dev_ptr, "ccam_dma_buf = 0x%lx\n", (unsigned long)circbuf_priv[chn].phys_addr); dev_dbg(g_dev_ptr, "ccam_dma_buf = 0x%lx\n", (unsigned long)circbuf_priv[chn].phys_addr);
/* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ /* remap_pfn_range will mark the range VM_IO and VM_RESERVED */
ret = remap_pfn_range(vma, ret = remap_pfn_range(vma,
vma->vm_start, vma->vm_start,
//((unsigned long) virt_to_phys(&ccam_dma_buf[0])) >> PAGE_SHIFT, // Should be page-aligned
circbuf_priv[chn].phys_addr >> PAGE_SHIFT, circbuf_priv[chn].phys_addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_end - vma->vm_start,
vma->vm_page_prot); vma->vm_page_prot);
...@@ -674,34 +671,41 @@ int circbuf_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -674,34 +671,41 @@ int circbuf_mmap(struct file *file, struct vm_area_struct *vma)
return 0; return 0;
} }
/*!===========================================================================
*! If the current read pointer is invalid, circbuf_poll returns POLLHUP /**
*! as no data will be ever available until file poinetr is reset. * @brief This driver method is called when user-space program performs <em>poll, select</em> or
*! if it is valid, wait is setup and the blocking condition occurs * \e epoll system call.
*! ifthe current file pointer is equal to the FPGA write pointer *
*!===========================================================================*/ * If the current read pointer is invalid, circbuf_poll returns POLLHUP
* as no data will be ever available until file pointer is reset.
* If it is valid, wait is setup and blocking condition occurs in case
* current file pointer is equal to the FPGA write pointer.
* @param[in] file pointer to <em>struct file</em> structure
* @param[in] wait pointer to <em>struct poll_table</em> structure
* return POLLHUP if pointer is invalid, (POLLIN | POLLRDNORM) if frame is ready,
* 0 in case nothing is ready.
*/
unsigned int circbuf_poll (struct file *file, poll_table *wait) unsigned int circbuf_poll (struct file *file, poll_table *wait)
{ {
int w_ptr; int w_ptr;
unsigned int minor = MINOR(file->f_inode->i_rdev); unsigned int minor = MINOR(file->f_inode->i_rdev);
unsigned int chn = minor_to_chn(minor); unsigned int chn = minor_to_chn(minor, NULL);
struct interframe_params_t * fp; struct interframe_params_t * fp;
struct circbuf_pd * privData; int rslt;
privData = (struct circbuf_pd *) file->private_data;
int rslt; //!result of testing read poinetr
dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor); dev_dbg(g_dev_ptr, "minor = 0x%x\n", minor);
rslt= circbufValidPointer(file->f_pos, &fp, chn);
if (rslt < 0) { //! not a valid read pointer, probable buffer overrun rslt = circbuf_valid_ptr(file->f_pos, &fp, chn);
if (rslt < 0) {
// not a valid read pointer, probable buffer overrun
dev_dbg(g_dev_ptr, "invalid pointer file->f_pos = 0x%x\n", file->f_pos); dev_dbg(g_dev_ptr, "invalid pointer file->f_pos = 0x%x\n", file->f_pos);
return POLLHUP ; return POLLHUP ;
} else if (rslt > 0) { } else if (rslt > 0) {
return POLLIN | POLLRDNORM; //! there was frame already available return POLLIN | POLLRDNORM; //! there was frame already available
} else { //! pointer valid, no frame yet } else {
// pointer valid, no frame yet
poll_wait(file, &circbuf_wait_queue, wait); poll_wait(file, &circbuf_wait_queue, wait);
//! Frame might become available during call to poll_wait so nobody will wake us up. // Frame might become available during call to poll_wait so nobody will wake us up,
//! Let's see if there is stillno frame // let's see if there is still no frame.
//if ((camSeqGetJPEG_wp()<<2)!=file->f_pos) return POLLIN | POLLRDNORM; //! we are lucky - got it
w_ptr = camseq_get_jpeg_wp(chn) << 2; w_ptr = camseq_get_jpeg_wp(chn) << 2;
if (w_ptr != file->f_pos) if (w_ptr != file->f_pos)
return POLLIN | POLLRDNORM; //! we are lucky - got it return POLLIN | POLLRDNORM; //! we are lucky - got it
......
...@@ -27,12 +27,23 @@ ...@@ -27,12 +27,23 @@
#define IMAGERAW_MINOR_FPN 2 #define IMAGERAW_MINOR_FPN 2
#define IMAGERAW_MINOR_UNLOCK 3 #define IMAGERAW_MINOR_UNLOCK 3
#define CIRCBUF_MINOR_CHN_OFFSET 30 #define CIRCBUF_MINOR 0x20
#define CIRCBUF_MINOR_CHN_0 30 #define CIRCBUF_MINOR_CHN_0 0x20
#define CIRCBUF_MINOR_CHN_1 31 #define CIRCBUF_MINOR_CHN_1 0x21
#define CIRCBUF_MINOR_CHN_2 32 #define CIRCBUF_MINOR_CHN_2 0x22
#define CIRCBUF_MINOR_CHN_3 33 #define CIRCBUF_MINOR_CHN_3 0x23
#define JPEGHEAD_MINOR 0x30
#define JPEGHEAD_MINOR_CHN_0 0x30
#define JPEGHEAD_MINOR_CHN_1 0x31
#define JPEGHEAD_MINOR_CHN_2 0x32
#define JPEGHEAD_MINOR_CHN_3 0x33
#define HUFFMAN_MINOR 0x40
#define HUFFMAN_MINOR_CHN_0 0x40
#define HUFFMAN_MINOR_CHN_1 0x41
#define HUFFMAN_MINOR_CHN_2 0x42
#define HUFFMAN_MINOR_CHN_3 0x43
#define CMOSCAM_MINOR_RWTABLES 9 #define CMOSCAM_MINOR_RWTABLES 9
#define CMOSCAM_MINOR_CIRCBUF 11 #define CMOSCAM_MINOR_CIRCBUF 11
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment