blob: e7f8d8d7d67a59b18947f66dccf53af687957035 [file] [log] [blame]
/****************************************************************
N-Trig quirk character driver
Zoro Software.
D-Trig Digitizer modules files
Copyright (C) 2010, Dmitry Kuzminov
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, see <http://www.gnu.org/licenses/>.
****************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <asm/current.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include "typedef-ntrig.h"
#include "ntrig-common.h"
#include "ntrig-dispatcher.h"
/**********************************************************************************/
#define DEVICE_NAME "ntrig_quirk"
#define NTRIG_QUIRK_DRIVER_DRIVER_AUTHOR "Dmitry Kuminov <dima@zoro-sw.com>"
#define NTRIG_QUIRK_DRIVER_DRIVER_DESC "ntrig quirk driver,based on character device"
#define NTRIG_QUIRK_DRIVER_PLATFORM_NAME "ntrig_quirk"
#define NTRIG_DEV_NAME "ntrig_ev"
#define NTRIG_QUIRK_MAJOR 222
#define SEMAPHORE_TIME_OUT (12) /* Jiffy=4 ms on 2.6 48 milisecond */
#define BLOCKING_MODE 0x01
#define POLLING_MODE 0x02
/**********************************************************************************/
/* we need to ensure that only one process/thread is reading from char driver */
static int g_singleton = 0;
/* flag that indicates file mode operations */
static int g_file_mode = BLOCKING_MODE;
/* data buffer */
mr_message_types_t g_data;
/* major number for driver recognition */
static int g_major = NTRIG_QUIRK_MAJOR;
/* callback for quirk message arrived via write operation */
message_callback dispatch_quirk_message = NULL;
/* my class to creare /dev/ntrig_ev pipe */
static struct class *driver_class;
/**********************************************************************************/
/*
* Callback function from dispatcher when driver is ready
*/
static int on_message(void * buf);
/* Exported API */
int bus_init (void);
void bus_exit (void);
static int my_open (struct inode *inode,struct file *filep);
static int my_release (struct inode *inode,struct file *filep);
static ssize_t my_read (struct file *filep,char *buff,size_t count,loff_t *offp );
static ssize_t my_write (struct file *filep,const char *buff,size_t count,loff_t *offp );
static unsigned int my_poll (struct file *filep, struct poll_table_struct* poll_table);
static void memory_lock (unsigned long *flag);
static void memory_unlock (unsigned long *flag);
static void wait_for_data (void);
static void notify_data_ready (void);
static int is_data_ready (void);
static void reset_data_ready (void);
static void init_synch_data (void);
EXPORT_SYMBOL_GPL(bus_init );
EXPORT_SYMBOL_GPL(bus_exit );
/**********************************************************************************/
struct file_operations my_fops={
open :my_open,
read :my_read,
write :my_write,
release :my_release,
poll :my_poll,
};
/**********************************************************************************/
/* Flag to indicate data readiness */
static int data_ready = 0;
/* working queues to block reading/polling operations, when data not ready */
static wait_queue_head_t buffer_ready;
static wait_queue_head_t poll_buffer_ready;
/* Threads protection on data */
static spinlock_t g_mem_lock;
static void memory_lock(unsigned long *flag)
{
spin_lock_irqsave(&g_mem_lock, *flag);
}
static void memory_unlock(unsigned long *flag)
{
spin_unlock_irqrestore(&g_mem_lock, *flag);
}
static void wait_for_data(void)
{
if(BLOCKING_MODE == g_file_mode)
interruptible_sleep_on(&buffer_ready);
}
static void notify_data_ready(void)
{
data_ready = 1;
/* wake up queue for reading */
wake_up_interruptible(&buffer_ready);
/* wake up queue for polling */
wake_up_interruptible(&poll_buffer_ready);
}
static int is_data_ready(void)
{
return data_ready;
}
static void reset_data_ready(void)
{
data_ready = 0;
}
static void init_synch_data(void)
{
reset_data_ready();
/* schedule syncronization */
init_waitqueue_head (&buffer_ready);
init_waitqueue_head (&poll_buffer_ready);
/* resources protection */
spin_lock_init(&g_mem_lock);
}
/**********************************************************************************/
static int on_message(void * buf)
{
unsigned long flag;
ntrig_dbg("ntrig-quirk-driver inside %s \n", __FUNCTION__);
/* Copy data to emp buf */
memory_lock(&flag);
memcpy(&g_data, buf, sizeof(g_data));
/* check if unblockig required */
notify_data_ready();
memory_unlock(&flag);
return 0;
}
static int my_open(struct inode *inode,struct file *filep)
{
if(g_singleton && !(filep->f_flags & O_WRONLY)){
printk(KERN_ALERT"%s file already opened by other thread\n", __FUNCTION__);
return -EBUSY;
}
if(filep->f_flags & O_NONBLOCK)
g_file_mode = POLLING_MODE;
else
g_file_mode = BLOCKING_MODE;
if(!(filep->f_flags & O_WRONLY)){
g_singleton = 1;
}
return DTRG_NO_ERROR;
}
static int my_release(struct inode *inode,struct file *filep)
{
if(!(filep->f_flags & O_WRONLY)){
g_singleton = 0;
}
return DTRG_NO_ERROR;
}
static ssize_t my_read(struct file *filep,char *buff,size_t count,loff_t *offp )
{
int ret_count = 0;
unsigned long flag;
mr_message_types_t data;
int ready;
ntrig_dbg("ntrig-quirk-driver inside %s \n", __FUNCTION__);
/* block reading till there is data to send */
wait_for_data();
/* we protect quirk buffer when there is process on data */
memory_lock(&flag);
ready = is_data_ready();
if(ready) {
memcpy(&data, &g_data, sizeof(data));
}
memory_unlock(&flag);
if (ready){
if (copy_to_user(buff,&data,sizeof(data)) != 0){
ntrig_dbg("ntrig-quirk-driver Kernel -> userspace copy failed!\n" );
}
memory_lock(&flag);
reset_data_ready();
memory_unlock(&flag);
ret_count = sizeof(g_data);
}
return ret_count;
}
static ssize_t my_write(struct file *filep,const char *buff,size_t count,loff_t *offp )
{
// unsigned long flag;
mr_message_types_t data;
ntrig_dbg("ntrig-quirk-driver inside %s filep=%p,buff=%p,count=%d,offp=%p\n", __FUNCTION__, filep,buff, (int)count,offp);
/* memory_lock(&flag); */
memset(&data, 0, sizeof(data));
if ( copy_from_user(&data,buff,count) != 0 ){
ntrig_dbg("ntrig-quirk-driver Userspace -> kernel copy failed!\n" );
} else {
ntrig_dbg("ntrig-quirk-driver inside %s message_type=%d\n", __FUNCTION__, data.type);
}
dispatch_quirk_message(&data);
/* memory_unlock(&flag); */
return count;
}
int bus_init(void)
{
ntrig_dbg("ntrig-quirk-driver inside %s \n", __FUNCTION__);
if(ntrig_setup_quirk(on_message, &dispatch_quirk_message)){
ntrig_dbg("ntrig-quirk-driver inside %s cannot start spi dispatcher\n", __FUNCTION__);
goto ERROR;
}
if(register_chrdev(g_major,DEVICE_NAME,&my_fops)){
ntrig_dbg("ntrig-quirk-driver inside %s failed to register driver\n", __FUNCTION__);
goto ERROR_START;
}
/**
* Prepare synchronisation variables
*/
init_synch_data();
/**
* create device
*/
driver_class=class_create(THIS_MODULE, DEVICE_NAME);
device_create(driver_class,NULL, MKDEV(NTRIG_QUIRK_MAJOR, 0),"%s",NTRIG_DEV_NAME);
return DTRG_NO_ERROR;
ERROR_START:
ERROR:
return DTRG_FAILED;
}
void bus_exit(void)
{
ntrig_dbg("ntrig-quirk-driver inside %s \n", __FUNCTION__);
/**
* remove dev/ntrig_ev
*/
device_destroy(driver_class,MKDEV(NTRIG_QUIRK_MAJOR, 0));
class_destroy (driver_class);
unregister_chrdev(g_major,DEVICE_NAME);
}
static unsigned int my_poll(struct file *filep, struct poll_table_struct* poll_table)
{
unsigned int mask = 0;
unsigned int data_ready = 0;
unsigned long flag;
memory_lock(&flag);
data_ready = is_data_ready();
memory_unlock(&flag);
if(data_ready)
mask |= POLLIN | POLLRDNORM;
poll_wait(filep, &poll_buffer_ready, poll_table);
return mask;
}
MODULE_LICENSE ("GPL");
MODULE_AUTHOR (NTRIG_QUIRK_DRIVER_DRIVER_AUTHOR);
MODULE_DESCRIPTION (NTRIG_QUIRK_DRIVER_DRIVER_DESC);
MODULE_SUPPORTED_DEVICE (NTRIG_QUIRK_DRIVER_PLATFORM_NAME);