📄 csi.c
字号:
int korder;
int required_pages;
int extra_pages;
//alloc a tmp buffer in kernel space
required_pages = byte_size >> PAGE_SHIFT;
for (korder = 0 ; required_pages >> korder ; korder++) {;}
extra_pages = (1 << korder) - required_pages;
_kbuf = (unsigned int *)__get_free_pages(GFP_KERNEL, korder);
if(!_kbuf)
{
printk("csi error: buffer alloc failed\n");
return -1;
}
//poll sof
_reg_CSI_CSISR = BIT_SOF_INT;
while(!(_reg_CSI_CSISR & BIT_SOF_INT));
_reg_CSI_CSISR = BIT_SOF_INT;
//clear fifo overflow
if(_reg_CSI_CSISR & BIT_RFF_OR_INT)
_reg_CSI_CSISR = BIT_RFF_OR_INT;
//read rx fifo
j = 0;
while(1)
{
//overflow check
if(_reg_CSI_CSISR & BIT_RFF_OR_INT)
{
// printk("csi error: overflow\n");
_reg_CSI_CSISR = BIT_RFF_OR_INT;
}
//poll rx fifo full
if(_reg_CSI_CSISR & BIT_RXFF_INT)
{
for(i = 0; i < g_csi_cfg.rxff_level; i ++)
_kbuf[j ++] = _reg_CSI_CSIRXR;
}
if(j >= word_size)
break;
}
copy_to_user(_buf, _kbuf, byte_size);
free_pages((int)_kbuf, korder);
_kbuf = 0;
return 0;
}
static void csihw_reset_frame_count(void)
{
_reg_CSI_CSICR3 |= BIT_FRMCNT_RST;
return;
}
static int csihw_get_frame_count(void)
{
int count;
count = _reg_CSI_CSICR3 >> SHIFT_FRMCNT;
return count;
}
//block until eof is detected
//not to be used with prp
static int csihw_pol_eof(int rxcnt)
{
int cnt = 0;
int trial_cnt = 10000000;
if(rxcnt != 0)
{
//use this rxcnt
_reg_CSI_CSIRXCNT = rxcnt;
}
else
{
//otherwise use the rxcnt in csi config
_reg_CSI_CSIRXCNT = g_csi_cfg.rxcnt;
}
//clear interrupt first
if(_reg_CSI_CSISR & BIT_EOF_INT)
_reg_CSI_CSISR = BIT_EOF_INT;
while(1)
{
if(_reg_CSI_CSISR & BIT_EOF_INT)
{
_reg_CSI_CSISR = BIT_EOF_INT;
break;
}
else
{
if(cnt ++ > trial_cnt)
{
// printk("csi err: eof timeout\n");
return 1;
}
}
}
return 0;
}
//block until sof is detected
static int csihw_pol_sof(void)
{
int cnt = 0;
int trial_cnt = 10000000;
//clear interrupt first
if(_reg_CSI_CSISR & BIT_SOF_INT)
_reg_CSI_CSISR = BIT_SOF_INT;
while(1)
{
if(_reg_CSI_CSISR & BIT_SOF_INT)
{
_reg_CSI_CSISR = BIT_SOF_INT;
break;
}
else
{
if(cnt ++ > trial_cnt)
{
printk("csi err: sof timeout\n");
return 1;
}
}
}
return 0;
}
static void csihw_clock_enable(void)
{
//turn on hclk & perclk4
//caution -- if sensor clock (mclk) is provided by csi,
// make sure csi is resumed before sensor is resumed
_reg_CRM_PCCR0 |= 0x80000000;
if(g_csi_ver == 2)
_reg_CRM_PCCR0 |= 0x00400000;
return;
}
static void csihw_clock_disable(void)
{
//shutdown hclk & perclk4
//caution -- if sensor clock (mclk) is provided by csi,
// make sure the sensor is suspend before csi is suspend
_reg_CRM_PCCR0 &= ~0x80000000;
if(g_csi_ver == 2)
_reg_CRM_PCCR0 &= ~0x00400000;
return;
}
//csi reset, really can reset??
static void csihw_reset(void)
{
csihw_clock_enable();
_reg_CSI_CSICR1 = CSICR1_RESET_VAL;
_reg_CSI_CSICR2 = CSICR2_RESET_VAL;
if(g_csi_ver == 2)
{
_reg_CSI_CSICR3 = CSICR3_RESET_VAL;
//need to enable hclk before CSICR3 can be access
}
return;
}
/*******************************************************************************
*---------------------------- LINUX OS SPECIFIC -------------------------------
*/
//global static
static devfs_handle_t devfs_handle = NULL;
static int gMajor = 0;
static struct pm_dev *pmdev;
static struct apmc_user *g_csi_apmc;
//functions and interface
static int csi_open(struct inode *inode, struct file *filp);
static int csi_release(struct inode *inode, struct file *filp);
static ssize_t csi_read(struct file *filp, char *buf, size_t size, loff_t *l);
static ssize_t csi_write(struct file *filp, const char *buf, size_t size, loff_t *l);
static int csi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int csi_pm_handler(struct pm_dev *pmdev, pm_request_t rqst, void *data);
struct file_operations csi_fops =
{
open: csi_open,
release: csi_release,
read: csi_read,
write: csi_write,
ioctl: csi_ioctl,
};
/**
*@brief The module entry function of the CSI driver.
* It does only the basic things such as csihw_init() and register_chrdev().
* Module clock is turned on in open().
* DMA & IRQ are turned on later in the function csihw_config().
*
*@return @li 0 Success
* @li others Failure
*/
int __init init_module()
{
int result;
printk("Motorola CSI Linux driver ver 0.1\n"
" - Copyright (C) 2004 Motorola Inc\n\n");
csihw_init();
//register CSI character device
result = devfs_register_chrdev(0, "csi", &csi_fops);
if ( result < 0 )
{
printk("csi error: unable to register chr driver\n");
return -ENODEV;
}
devfs_handle = devfs_register(NULL, "csi", DEVFS_FL_DEFAULT,
result, 0,
S_IFCHR | S_IRUSR | S_IWUSR,
&csi_fops, NULL);
if(devfs_handle == NULL)
{
printk("csi error: unable to register driver\n");
return -ENODEV;
}
gMajor = result;
g_csi_busy = 0;
//power management
if ((pmdev = pm_register(PM_CSI_DEV, PM_SYS_UNKNOWN, csi_pm_handler)) == NULL)
{
printk("csi error: failed to register PM... continuing with driver init\n");
}
g_csi_apmc = apmc_register(APMC_LEVEL_HIGHEST);
return 0;
}
/**
*@brief The module exit function of the CSI driver.
*
*@return NULL
*/
void __exit cleanup_module()
{
csihw_cleanup();
apmc_unregister(g_csi_apmc);
pm_unregister(pmdev);
if(gMajor > 0)
{
if(devfs_unregister_chrdev(gMajor, "csi") < 0)
{
printk("csi error: failed to unregister from devfs\n");
return;
}
}
if(devfs_handle != NULL)
devfs_unregister(devfs_handle);
else
{
printk("csi error: failed to unregister from devfs, devfs_handle = 0x%08X\n", (int)devfs_handle);
return;
}
printk("CSI driver is unloaded sucessfully\n");
return;
}
/**
*@brief Open the CSI
*
*@param inode structure inode list pointer
*@param filp structure file list pointer
*@return 0
*/
static int csi_open(struct inode *inode, struct file *filp)
{
csihw_open();
MOD_INC_USE_COUNT;
return 0;
}
/**
*@brief Release the CSI
*
*@param inode structure inode list pointer
*@param filp structure file list pointer
*@return 0
*/
static int csi_release(struct inode *inode, struct file *filp)
{
csihw_release();
MOD_DEC_USE_COUNT;
return 0;
}
/**
*@brief This function is to read the data from the CSI client device through the CSI interface.
* Reading the data by calling the function csihw_poll().
*
*@param filp structure file list pointer
*@param buf the buffer to store the data
*@param size size of the buffer, the number of bytes
*@param l loff_t type pointer, not used
*@return 0
*/
static ssize_t csi_read(struct file *filp, char *buf, size_t size, loff_t *l)
{
g_csi_busy = 1;
csihw_poll((unsigned int *)buf, (int)size);
g_csi_busy = 0;
return 0;
}
/**
*@brief This function is only called when "csi error: write ioctl is not implemented" happens.
* csi_write() includes only a statement "printk" because it's unnecessary to write to a CSI client device.
*
*@param filp structure file list pointer
*@param buf the buffer to store the data
*@param size size of the buffer
*@param l the register number in the I2C slave (SSI sensor)
*@return -1
*/
static ssize_t csi_write(struct file *filp, const char *buf, size_t size, loff_t *l)
{
printk("csi error: write ioctl is not implemented\n");
return -1;
}
/**
*@brief CSI ioctl operation
*
*There are five kinds of commands
*@li IOCTL_CSI_READ_CONFIG
*@li IOCTL_CSI_CONFIG
*@li IOCTL_CSI_READ_STATUS
*@li IOCTL_CSI_GET_FRMCNT
*@li IOCTL_CSI_RST_FRMCNT
*
*@param inode structure inode list pointer
*@param filp structure file list pointer
*@param cmd The command to control the codec.
*@param arg The void pointer used to communicate with user space.
*@return @li 0 Success
* @li others Failure
*/
static int csi_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case IOCTL_CSI_READ_CONFIG:
{
csihw_read_config(&g_csi_cfg);
if(copy_to_user((void *)arg, (void *)&g_csi_cfg, sizeof(CSI_CFG)))
return -EFAULT;
break;
}
case IOCTL_CSI_CONFIG:
{
if(copy_from_user((void *)&g_csi_cfg, (void *)arg, sizeof(CSI_CFG)))
return -EFAULT;
csihw_config(&g_csi_cfg);
break;
}
case IOCTL_CSI_READ_STATUS:
{
csihw_read_status(&g_csi_status);
if(copy_to_user((void *)arg, (void *)&g_csi_status, sizeof(CSI_STATUS)))
return -EFAULT;
break;
}
case IOCTL_CSI_GET_FRMCNT:
{
int count;
count = csihw_get_frame_count();
if(copy_to_user((void *)arg, (void *)&count, sizeof(int)))
return -EFAULT;
break;
}
case IOCTL_CSI_RST_FRMCNT:
{
csihw_reset_frame_count();
break;
}
case IOCTL_CSI_POLL_EOF:
{
csihw_pol_eof(arg); //block
break;
}
case IOCTL_CSI_POLL_SOF:
{
csihw_pol_sof(); //block
break;
}
case IOCTL_CSI_RESET:
{
csihw_reset();
break;
}
}
return 0;
}
static int csi_pm_handler(struct pm_dev *pmdev, pm_request_t rqst, void *data)
{
switch(rqst)
{
case PM_SUSPEND:
{
if(g_csi_busy)
{
//does not allow suspend when there is a data transfer between csi fifo & memory
return 1;
}
else
{
csihw_clock_disable();
// printk("csi: suspend\n");
}
break;
}
case PM_RESUME:
{
csihw_clock_enable();
// printk("csi: resume\n");
break;
}
}
return 0;
}
/**@}*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -