Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
812 lines (741 sloc) 28.3 KB
/*!****************************************************************************//**
* @file exif393.c
* @brief Drivers for Exif manipulation
* @copyright Copyright (C) 2008-2016 Elphel, Inc.
* @par <b>License</b>
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
//copied from cxi2c.c - TODO:remove unneeded
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
//#include <linux/autoconf.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h> // dev_*
//#include <asm/system.h>
//#include <asm/svinto.h>
#include <asm/byteorder.h> // endians
//#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <uapi/elphel/c313a.h>
#include <uapi/elphel/exifa.h>
#include <uapi/elphel/x393_devices.h>
//#include "fpgactrl.h" // defines port_csp0_addr, port_csp4_addr
//
//#include "x3x3.h"
//#include "cc3x3.h"
#include "exif393.h"
#define D(x)
//#define D(x) printk(">>> %s:%d:",__FILE__,__LINE__);x
//Major
//#define X3X3_EXIF 136
//Minors
//#define X3X3_EXIF_EXIF 0 // read encoded Exif data (SEEK_END,
//#define X3X3_EXIF_META 1 // write metadata, concurently opened files. All writes atomic
// control/setup devices
//#define DEV393_MINOR(DEV393_EXIF_TEMPLATE) 2 // write Exif template
//#define DEV393_MINOR(DEV393_EXIF_METADIR) 3 // write metadata to Exif header translation (dir_table[MAX_EXIF_FIELDS])
// those 2 files will disable exif_enabled and exif_valid, truncate file size to file pointer on release.
//#define DEV393_MINOR(DEV393_EXIF_TIME) 4 // write today/tomorrow date (YYYY:MM:DD) and number of seconds at today/tomorrow
// midnight (00:00:00) in seconds from epoch (long, startting from LSB)
#define X3X3_EXIF_DRIVER_DESCRIPTION "Elphel (R) model 393 Exif device driver"
/** @brief Global pointer to basic device structure. This pointer is used in debugfs output functions */
static struct device *g_devfp_ptr = NULL;
static DEFINE_SPINLOCK(lock);
//#define MAX_EXIF_FIELDS 256 // number of Exif tags in the header
//#define MAX_EXIF_SIZE 4096 // Exif data size
static struct exif_dir_table_t dir_table[MAX_EXIF_FIELDS];
static int exif_fields = 0; // total number of the Exif fields in the header
static int exif_template_size = 0; // size of Exif template
static char exif_template[MAX_EXIF_SIZE];
static int aexif_meta_size[SENSOR_PORTS] = {0,0,0,0}; // size of Exif meta data page (is it the same for all ports?) 393: set as individual
static int aexif_wp[SENSOR_PORTS] = {1,1,1,1}; // frame write pointer in the meta_buffer
static int aexif_enabled[SENSOR_PORTS] = {0,0,0,0}; // enable storing of frame meta data, enable reading Exif data
static int aexif_valid[SENSOR_PORTS] = {0,0,0,0}; // Exif tables and buffer are valid.
static char * ameta_buffer[SENSOR_PORTS]= {NULL,NULL,NULL,NULL}; // dynamically allocated buffer to store frame meta data.
static char exif_tmp_buff[MAX_EXIF_SIZE];
//static char * meta_buffer=NULL; // dynamically allocated buffer to store frame meta data.
// page 0 - temporary storage, 1..MAX_EXIF_FRAMES - buffer
// Common for all sensor ports
struct exif_time_t {
char tomorrow_date[10]; //!"YYYY:MM:DD"
unsigned long tomorrow_sec; //!seconds from epoch tomorrow at 00:00
char today_date[10]; //!"YYYY:MM:DD"
unsigned long today_sec; //!seconds from epoch today at 00:00
} exif_time;
static struct exif_datetime_t {
char datetime[20]; //!"YYYY:MM:DD HH:MM:SS\0"
char subsec[7]; //!ASCII microseconds (0-padded), ."\0"
} now_datetime;
int exif_open (struct inode *inode, struct file *filp);
int exif_release(struct inode *inode, struct file *filp);
loff_t exif_lseek (struct file * file, loff_t offset, int orig);
ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off);
ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off);
static int __init exif_init(void);
static struct file_operations exif_fops = {
owner: THIS_MODULE,
open: exif_open,
release: exif_release,
read: exif_read,
write: exif_write,
llseek: exif_lseek
};
ssize_t minor_file_size(int minor) { //return current file size for different minors
int sensor_port;
switch (minor) {
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return exif_template_size;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
sensor_port = minor - DEV393_MINOR(DEV393_EXIF0);
return aexif_enabled[sensor_port]? (exif_template_size * (MAX_EXIF_FRAMES+1)):0;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = minor - DEV393_MINOR(DEV393_EXIF_META0);
return aexif_meta_size[sensor_port];
case DEV393_MINOR(DEV393_EXIF_METADIR):
return exif_fields * sizeof(struct exif_dir_table_t);
case DEV393_MINOR(DEV393_EXIF_TIME):
return sizeof(struct exif_time_t);
default:return 0;
}
}
ssize_t minor_max_size(int minor) { //return max file size for different minors
switch (minor) {
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
return MAX_EXIF_SIZE;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
return MAX_EXIF_SIZE * (MAX_EXIF_FRAMES+1);
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
return MAX_EXIF_SIZE;
case DEV393_MINOR(DEV393_EXIF_METADIR):
return MAX_EXIF_FIELDS * sizeof(struct exif_dir_table_t);
case DEV393_MINOR(DEV393_EXIF_TIME):
return sizeof(struct exif_time_t);
default:
return 0;
}
}
void exif_invalidate(void) { // 393: OK, only invalidates all ayt once
int sensor_port;
for (sensor_port =0; sensor_port < SENSOR_PORTS; sensor_port++){
aexif_enabled[sensor_port] = 0;
aexif_valid[sensor_port] = 0;
}
}
//reallocate meta buffer to store per-frame meta data (later output as Exif)
// 393: Make both individual and all at once
int exif_rebuild(int frames) {
int sensor_port,rslt;
for (sensor_port =0; sensor_port < SENSOR_PORTS; sensor_port++){
if ((rslt = exif_rebuild_chn(sensor_port, frames)) <0){
return rslt;
}
}
return 0;
}
int exif_rebuild_chn(int sensor_port, int frames) {
int i,ml;
char * meta_buffer = ameta_buffer[sensor_port];
aexif_enabled[sensor_port] = 0;
aexif_valid[sensor_port] = 0;
aexif_wp[sensor_port] = 1;
// free buffer, if allocated
if (meta_buffer) {
vfree (meta_buffer);
meta_buffer=NULL;
}
// calculate page size
if (exif_fields==0) return 0; // exif_valid==0;
for (i=0; i < exif_fields; i++) {
ml=dir_table[i].src+dir_table[i].len;
if (ml > aexif_meta_size[sensor_port]) aexif_meta_size[sensor_port] = ml;
}
if (aexif_meta_size[sensor_port] > MAX_EXIF_SIZE) {
dev_warn(g_devfp_ptr,"%s:%d: Meta frame size (0x%x) is too big (>0x%x)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port], MAX_EXIF_SIZE);
return -1;
}
meta_buffer= vmalloc(aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
if (!meta_buffer) {
dev_warn(g_devfp_ptr,"%s:%d: Failed to allocate memory (%d bytes)\n",__FILE__,__LINE__, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
return -1;
}
memset(meta_buffer, 0, aexif_meta_size[sensor_port] * (MAX_EXIF_FRAMES+1));
ameta_buffer[sensor_port] = meta_buffer;
aexif_valid[sensor_port] = 1;
return 0;
}
int exif_enable(int en) {
int sensor_port,rslt;
for (sensor_port =0; sensor_port < SENSOR_PORTS; sensor_port++){
if ((rslt = exif_enable_chn(sensor_port, en)) <0){
return rslt;
}
}
return 0;
}
int exif_enable_chn(int sensor_port, int en) {
int rslt;
if (en) {
if (!aexif_valid[sensor_port]) {
if (((rslt=exif_rebuild_chn(sensor_port, MAX_EXIF_FRAMES))) <0) return rslt;
}
aexif_enabled[sensor_port] = 1;
} else {
aexif_enabled[sensor_port] = 0;
}
return 0;
}
int dir_find_tag (unsigned long tag) { //find location of the tag field in meta page
int indx;
for (indx=0; indx < exif_fields; indx++) if (dir_table[indx].ltag==tag) return (int) dir_table[indx].src;
return -1;
}
inline void write_meta_raw_irq(int sensor_port, char * data, int offset, int len) { //write data to meta, called from IRQ
if (aexif_enabled[sensor_port])
memcpy(&ameta_buffer[sensor_port][offset], data, len);
}
inline int write_meta_irq(int sensor_port, char * data, int * indx, unsigned long ltag, int len) { //write data to meta, called from IRQ(len==0 => use field length)
int i;
if (!aexif_enabled[sensor_port]) return 0;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i<exif_fields; i++) if (dir_table[i].ltag==ltag) break;
if (i>=exif_fields) return -1; //ltag not found
}
if (len==0) len=dir_table[i].len;
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], data, len);
if (indx) * indx = i;
return dir_table[i].src;
}
inline void putlong_meta_raw_irq(int sensor_port, unsigned long data, int offset) { //write data to meta (4 bytes, big endian), called from IRQ
unsigned long bedata=__cpu_to_be32(data);
if (aexif_enabled[sensor_port]) {
memcpy(&ameta_buffer[sensor_port][ offset], &bedata, 4);
}
}
inline int putlong_meta_irq(int sensor_port, unsigned long data, int * indx, unsigned long ltag) { //write data to meta (4 bytes, big endian), from IRQ
int i;
unsigned long bedata=__cpu_to_be32(data);
if (!aexif_enabled[sensor_port]) return -10;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i<exif_fields; i++) if (dir_table[i].ltag==ltag) break;
if (i>=exif_fields) return -1; //ltag not found
}
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], &bedata, 4);
if (indx) * indx=i;
return dir_table[i].src;
}
void write_meta_raw(int sensor_port, char * data, int offset, int len) { //write data to meta, called from outside IRQ (atomic)
unsigned long flags;
if (aexif_enabled[sensor_port]) {
local_irq_save(flags);
//local_irq_disable();
memcpy(&ameta_buffer[sensor_port][ offset], data, len);
local_irq_restore(flags);
}
}
int write_meta(int sensor_port, char * data, int * indx, unsigned long ltag, int len) { //write data to meta, from outside IRQ (atomic) (len==0 => use field length)
int i;
unsigned long flags;
if (!aexif_enabled[sensor_port]) return 0;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i<exif_fields; i++) if (dir_table[i].ltag==ltag) break;
if (i>=exif_fields) return -1; //ltag not found
}
if (len==0) len=dir_table[i].len;
local_irq_save(flags);
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], data, len);
local_irq_restore(flags);
if (indx) * indx=i;
return dir_table[i].src;
}
void putlong_meta_raw(int sensor_port, unsigned long data, int offset) { //write data to meta (4 bytes, big endian), called from outside IRQ (atomic)
unsigned long flags;
unsigned long bedata=__cpu_to_be32(data);
if (aexif_enabled[sensor_port]) {
local_irq_save(flags);
//local_irq_disable();
memcpy(&ameta_buffer[sensor_port][ offset], &bedata, 4);
local_irq_restore(flags);
}
}
int putlong_meta(int sensor_port, unsigned long data, int * indx, unsigned long ltag) { //write data to meta (4 bytes, big endian), from outside IRQ (atomic)
int i;
unsigned long flags;
unsigned long bedata=__cpu_to_be32(data);
if (!aexif_enabled[sensor_port]) return -10;
if (indx && (dir_table[* indx].ltag==ltag)) i=*indx;
else {
for (i=0; i<exif_fields; i++) if (dir_table[i].ltag==ltag) break;
if (i>=exif_fields) return -1; //ltag not found
}
local_irq_save(flags);
memcpy(&ameta_buffer[sensor_port][dir_table[i].src], &bedata, 4);
local_irq_restore(flags);
if (indx) * indx=i;
return dir_table[i].src;
}
// The next function is normally called from the interrupt service routine
// Encode time (epoch sec, usec) into static buffer, return pointer to the buffer
// Uses struct exif_time that should be updated from the user space (once a day),
// calculates date/time ignoring leap seconds if not updated in time
/*
* 393: Continue to use same static buffers for exif_time - common to all channels
*/
char * encode_time(char buf[27], unsigned long sec, unsigned long usec) {
unsigned long s,d,m,y,y4,lp,h;
unsigned long flags;
spin_lock_irqsave(&lock,flags);
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is not valid, try tomorrow:
memcpy(&exif_time.today_date[0],&exif_time.tomorrow_date[0],sizeof(exif_time.today_date)+sizeof(exif_time.today_sec));
if (((sec-exif_time.today_sec)>86400) || (sec < exif_time.today_sec)) {// today's time is _still_ not valid, has to do it itself :-(
d=sec/86400;
s=d*86400;
y4=d/1461; // number of 4-year periods
d-=1461*y4; //days after 1970, 1974, ...
y=(d- ((d>=1095)?1:0))/365;
d-=y*365+((y>2)?1:0);
lp=(y==2);
y+=4*y4+1970;
if ((!lp) && (d>58)) d++;
// d+=(lp && (d>58))?1:0;
D(printk("d=%ld, y=%ld, y4=%ld, lp=%ld\n",d, y, y4, lp));
if (d>181) {
if (d>273) {
if (d>304) {
if (d>334) {m=12;d-=334;} // December
else {m=11;d-=304;} // November
} else {m=10;d-=273;} // October
} else {
if (d>212) {
if (d>243) {m= 9;d-=243;} // September
else {m= 8;d-=212;} // August
} else {m= 7;d-=181;} // July
}
} else {
if (d>90) {
if (d>120) {
if (d>151) {m=6; d-=151;} // June
else {m=5; d-=120;} // May
} else {m=4; d-= 90;} // April
} else {
if (d>30) {
if (d>59) {m=3; d-= 59;} // March
else {m=2; d-= 30;} // February
} else {m=1; d++; } // January
}
}
D(printk("d=%ld, y=%ld, y4=%ld\n",d, y, y4));
sprintf(exif_time.today_date,"%04ld:%02ld:%02ld",y,m,d);
exif_time.today_sec=s;
}
memcpy (&now_datetime.datetime[0],exif_time.today_date,10);
now_datetime.datetime[10]=' ';
now_datetime.datetime[19]='\0';
now_datetime.subsec[6]='\0';
}
// now we have valid exif_time.today_date, exif_time.today_sec;
s=sec-exif_time.today_sec;
h= s/3600;
s-= 3600*h;
m= s/60;
s-= 60*m;
sprintf(&now_datetime.datetime[11],"%02ld:%02ld:%02ld",h,m,s);
sprintf(&now_datetime.subsec[0],"%06ld",usec);
memcpy(buf,&now_datetime.datetime[0],sizeof(now_datetime));
// return &now_datetime.datetime[0];
spin_unlock_irqrestore(&lock,flags);
return buf;
}
int store_meta(int sensor_port) { //called from IRQ service - put current metadata to meta_buffer, return page index
int meta_index;
if (!aexif_enabled[sensor_port]) return 0;
meta_index=aexif_wp[sensor_port];
memcpy(&ameta_buffer[sensor_port][meta_index * aexif_meta_size[sensor_port]], ameta_buffer[sensor_port], aexif_meta_size[sensor_port]);
aexif_wp[sensor_port]++;
if (aexif_wp[sensor_port] > MAX_EXIF_FRAMES) aexif_wp[sensor_port] = 1;
return meta_index;
}
/**
* @brief Go through mapping table and invalidate all tags that will be updated
* @param curr_table current mapping table
* @param curr_num the number of entries in current table
* @param new_table new mapping table of just a part of it
* @param new_num the number of entries in new table
*/
static void tags_invalidate(struct exif_dir_table_t *curr_table, int curr_num,
struct exif_dir_table_t *new_table, int new_num)
{
int i, j;
for (i = 0; i < curr_num; i++) {
for (j = 0; j < new_num; j++) {
if (curr_table[i].ltag == new_table[j].ltag) {
memset(&curr_table[i], 0, sizeof(struct exif_dir_table_t));
}
}
}
}
//!++++++++++++++++++++++++++++++++++++ open() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//static
int exif_open(struct inode *inode, struct file *filp) {
int p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data);
switch (p) {
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
case DEV393_MINOR(DEV393_EXIF_METADIR):
case DEV393_MINOR(DEV393_EXIF_TIME):
break;
default:return -EINVAL;
}
dev_dbg(g_devfp_ptr,"exif_open, minor=%d\n",p);
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
}
//!++++++++++++++++++++++++++++++++++++ release() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//static
int exif_release(struct inode *inode, struct file *filp){
int p = MINOR(inode->i_rdev);
int * pd= (int *) &(filp->private_data);
switch (p) {
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
break;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
break;
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
break;
case DEV393_MINOR(DEV393_EXIF_METADIR):
break;
case DEV393_MINOR(DEV393_EXIF_TIME):
break;
default:return -EINVAL;
}
dev_dbg(g_devfp_ptr,"exif_open, minor=%d\n",p);
inode->i_size=minor_file_size(p);
pd[0]=p; // just a minor number
return 0;
}
//!++++++++++++++++++++++++++++++++++++ lseek() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//static
loff_t exif_lseek (struct file * file, loff_t offset, int orig) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
int maxsize=minor_max_size(p);
int fp;
dev_dbg(g_devfp_ptr,"exif_lseek, minor=%d, offset = 0x%llx, orig=%d\n",p,offset,orig);
// int sensor_port;
switch (orig) {
case SEEK_SET:
file->f_pos = offset;
break;
case SEEK_CUR:
file->f_pos += offset;
break;
case SEEK_END:
//!overload
if (offset<=0) {
file->f_pos = thissize + offset;
} else {
switch (p) {
case DEV393_MINOR(DEV393_EXIF_TEMPLATE): //enable/disable
switch (offset) {
case EXIF_LSEEK_DISABLE:
exif_enable(0);
break;
case EXIF_LSEEK_ENABLE:
if (exif_enable(1)<0) return -EOVERFLOW; //TODO: change code
break;
case EXIF_LSEEK_INVALIDATE:
exif_invalidate();
break;
case EXIF_LSEEK_REBUILD:
if (exif_rebuild(MAX_EXIF_FRAMES)<0) return -EOVERFLOW; //TODO: change code
break;
default:return -EINVAL;
}
break;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
// sensor_port = p - DEV393_MINOR(DEV393_EXIF0);
if (offset > MAX_EXIF_FRAMES) return -EOVERFLOW; //larger than buffer
// file->f_pos=exif_meta_size * offset;
file->f_pos=exif_template_size * offset;
break;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
fp= dir_find_tag (offset);
if (fp < 0) return -EOVERFLOW; // tag is not in the directory
file->f_pos=fp;
break;
case DEV393_MINOR(DEV393_EXIF_METADIR):
file->f_pos=offset*sizeof(struct exif_dir_table_t);
break;
case DEV393_MINOR(DEV393_EXIF_TIME):
switch (offset) {
case EXIF_LSEEK_TOMORROW_DATE:
file->f_pos=exif_time.tomorrow_date - ((char *) &exif_time);
break;
case EXIF_LSEEK_TOMORROW_SEC:
file->f_pos=((char *) &exif_time.tomorrow_sec) - ((char *) &exif_time);
break;
case EXIF_LSEEK_TODAY_DATE:
file->f_pos=exif_time.today_date - ((char *) &exif_time);
break;
case EXIF_LSEEK_TODAY_SEC:
file->f_pos=((char *) &exif_time.today_sec) - ((char *) &exif_time);
break;
default:return -EINVAL;
}
break;
default:return -EINVAL;
}
}
break;
default:
return -EINVAL;
}
/* truncate position */
if (file->f_pos < 0) {
file->f_pos = 0;
return (-EOVERFLOW);
}
if (file->f_pos > maxsize) {
file->f_pos = maxsize;
return (-EOVERFLOW);
}
return (file->f_pos);
}
//!++++++++++++++++++++++++++++++++++++ write() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//static
ssize_t exif_write (struct file * file, const char * buf, size_t count, loff_t *off) {
int p=(int)file->private_data;
int sensor_port;
// int thissize=minor_file_size(p);
int maxsize=minor_max_size(p);
int fields_num;
char * cp, *new_table;
char tmp[MAX_EXIF_SIZE]; //! Or is it possible to disable IRQ while copy_from_user()?
unsigned long flags;
int disabled_err=0;
dev_dbg(g_devfp_ptr,"minor=0x%x\n", p);
if ((*off+count)>maxsize) {
// dev_warn(g_devfp_ptr,"%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
dev_dbg(g_devfp_ptr,"Data (0x%x) does not fit into 0x%x bytes, minor = 0x%x\n",(int) (*off+count), maxsize, p);
return -EOVERFLOW;
}
switch (p) {
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
exif_invalidate();
if (copy_from_user(&exif_template[*off], buf, count)) return -EFAULT;
exif_template_size=*off+count;
break;
case DEV393_MINOR(DEV393_EXIF_METADIR):
exif_invalidate();
cp= (char *) &dir_table;
new_table = kmalloc(MAX_EXIF_FIELDS * sizeof(struct exif_dir_table_t), GFP_KERNEL);
if (!new_table) {
return -ENOMEM;
}
if (copy_from_user(new_table, buf, count)) {
kfree(new_table);
return -EFAULT;
}
fields_num = exif_fields;
exif_fields=(*off+count)/sizeof(struct exif_dir_table_t);
tags_invalidate(dir_table, fields_num, (struct exif_dir_table_t *)new_table, exif_fields);
memcpy(&cp[*off], new_table, count);
kfree(new_table);
break;
case DEV393_MINOR(DEV393_EXIF_TIME): //write date/time first, then - midnight seconds
cp= (char *) &exif_time;
if (copy_from_user(&cp[*off], buf, count)) return -EFAULT;
break;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = p - DEV393_MINOR(DEV393_EXIF_META0);
if (copy_from_user(tmp, buf, count)) return -EFAULT;
local_irq_save(flags);
//local_irq_disable();
if (aexif_enabled[sensor_port]) memcpy(&ameta_buffer[sensor_port][*off], tmp, count);
else disabled_err=1;
local_irq_restore(flags);
if (disabled_err) {
dev_warn(g_devfp_ptr,"tried to write meta channel %d while disabled\n",sensor_port);
count=0;
}
break;
case DEV393_MINOR(DEV393_EXIF0):
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
return -EINVAL; // no writing - read only
break;
default:return -EINVAL;
}
*off+=count;
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x\n", (int) count, (int)*off);
return count;
}
//!++++++++++++++++++++++++++++++++++++ read() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
//static
ssize_t exif_read (struct file * file, char * buf, size_t count, loff_t *off) {
int p=(int)file->private_data;
int thissize=minor_file_size(p);
char * cp, * metap;
int start_p, page_p,i;
int sensor_port;
char tmp[MAX_EXIF_SIZE]; //! Or is it possible to disable IRQ while copy_from_user()?
/*
Does not work with PHP - always read 8192 bytes
if ((*off+count)>maxsize) {
printk ("%s:%d: Data (0x%x) does not fit into 0x%x bytes\n",__FILE__,__LINE__, (int) (*off+count), maxsize);
return -EOVERFLOW;
}
*/
if (*off > thissize) {
return 0; // nothing to read
}
if ((*off + count) > thissize) {
count=thissize-*off;
}
switch (p) {
case DEV393_MINOR(DEV393_EXIF_TEMPLATE):
if (copy_to_user(buf, &exif_template[*off], count)) return -EFAULT;
break;
case DEV393_MINOR(DEV393_EXIF_METADIR):
cp= (char *) &dir_table;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case DEV393_MINOR(DEV393_EXIF_TIME):
cp= (char *) &exif_time;
if (copy_to_user(buf, &cp[*off], count)) return -EFAULT;
break;
case DEV393_MINOR(DEV393_EXIF_META0):
case DEV393_MINOR(DEV393_EXIF_META1):
case DEV393_MINOR(DEV393_EXIF_META2):
case DEV393_MINOR(DEV393_EXIF_META3):
sensor_port = p - DEV393_MINOR(DEV393_EXIF_META0);
if (!aexif_enabled[sensor_port]) return 0;
if (copy_to_user(buf, &ameta_buffer[sensor_port][*off], count)) return -EFAULT;
break;
case DEV393_MINOR(DEV393_EXIF0):// generates exif data by merging exif_template with the selected meta_buffer page
case DEV393_MINOR(DEV393_EXIF1):
case DEV393_MINOR(DEV393_EXIF2):
case DEV393_MINOR(DEV393_EXIF3):
sensor_port = p - DEV393_MINOR(DEV393_EXIF0);
//will truncate by the end of current page
if (!aexif_enabled[sensor_port]) return 0;
i=((int) *off) / exif_template_size;
dev_dbg(g_devfp_ptr,"count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int) i, (int) exif_template_size);
//arch/cris/arch-v32/drivers/elphel/exif353.c:590:count= 0x2000, *off= 0x410, i=0x82, exif_template_size=0x208
start_p=i*exif_template_size;
page_p= *off - start_p;
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p,(int) i, (int) exif_template_size);
//arch/cris/arch-v32/drivers/elphel/exif353.c:591:count= 0x2000, pos= 0x410, start_p=0x10810, page_p=0xfffefc00, i=0x82, exif_template_size=0x208
metap= &ameta_buffer[sensor_port][i*aexif_meta_size[sensor_port]]; // pointer to the start of the selected page in frame meta_buffer
if ((page_p+count) > exif_template_size) count=exif_template_size-page_p;
memcpy(tmp,exif_template, exif_template_size);
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", (int) count, (int) *off, (int)start_p, (int)page_p);
for (i=0;i<exif_fields;i++){
memcpy(&tmp[dir_table[i].dst],&metap[dir_table[i].src], dir_table[i].len);
}
if (copy_to_user(buf, &tmp[page_p], count)) return -EFAULT;
break;
default:return -EINVAL;
}
*off+=count;
dev_dbg(g_devfp_ptr,"count= 0x%x, pos= 0x%x\n", (int) count, (int)*off);
return count;
}
/* This code is copied from exif_read, consider replacing it with this function invocation */
size_t exif_get_data(int sensor_port, unsigned short meta_index, void *buff, size_t buff_sz)
{
size_t count = exif_template_size;
loff_t off;
int start_p, page_p, i;
char *metap;
//will truncate by the end of current page
if (!aexif_enabled[sensor_port])
return 0;
off = meta_index * exif_template_size;
D(printk("%s: count= 0x%x, *off= 0x%x, i=0x%x, exif_template_size=0x%x\n", __func__, (int) count, (int) off, (int) meta_index, (int) exif_template_size));
start_p = meta_index * exif_template_size;
page_p = off - start_p;
D(printk("%s: count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x, i=0x%x, exif_template_size=0x%x\n", __func__, (int) count, (int) off, (int)start_p, (int)page_p,(int) meta_index, (int) exif_template_size));
metap = &ameta_buffer[sensor_port][meta_index * aexif_meta_size[sensor_port]]; // pointer to the start of the selected page in frame meta_buffer
if ((page_p + count) > exif_template_size)
count = exif_template_size - page_p;
memcpy(exif_tmp_buff, exif_template, exif_template_size);
D(printk("%s: count= 0x%x, pos= 0x%x, start_p=0x%x, page_p=0x%x\n", __func__, (int) count, (int) off, (int)start_p, (int)page_p));
for (i = 0; i < exif_fields; i++) {
memcpy(&exif_tmp_buff[dir_table[i].dst], &metap[dir_table[i].src], dir_table[i].len);
}
memcpy(buff, &exif_tmp_buff[page_p], count);
return count;
}
EXPORT_SYMBOL_GPL(exif_get_data);
//!++++++++++++++++++++++++++++++++++++ _init() ++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int __init exif_init(void) {
int res;
res = register_chrdev(DEV393_MAJOR(DEV393_EXIF0), "Exif", &exif_fops);
if(res < 0) {
dev_err(g_devfp_ptr,"\nexif_init: couldn't get a major number %d.\n",DEV393_MAJOR(DEV393_EXIF0));
return res;
}
dev_dbg(g_devfp_ptr,DEV393_NAME(DEV393_EXIF0)" - %d\n",DEV393_MAJOR(DEV393_EXIF0));
return 0;
}
module_init(exif_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andrey Filippov <andrey@elphel.com>.");
MODULE_DESCRIPTION(X3X3_EXIF_DRIVER_DESCRIPTION);