2021-11-21 00:20:20 +00:00

478 lines
13 KiB
C

h
// ===========================================================================
// cyio.c
// Copyright (C) 2008-2010 Bookeen - All rights reserved
// ===========================================================================
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/proc_fs.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-gpioj.h>
#include <cybook.h>
#include <linux/cyevent.h>
#define CYEV_MAX_EVENT 20
#define CYEV_REPEAT_DELAY (HZ/2); /* ~500ms */
#define CYEV_CURRENT_VERSION 0x10 /* 1.0 */
enum { INVALID = 0, VALID };
struct
{
char valid;
char wantRepeat;
char seen;
char unique;
int repeatDelay;
CyEvent_t payload;
} CyEv_PayList;
CyEv_PayList *cyev_eventList[CYEV_MAX_EVENT];
struct task_struct *openingTask = 0;
/* TODO: Add a locking method for accessing the eventList */
// ===========================================================================
// List management
void CyEv_ClearEventList(void)
{
int i;
for (i = 0; i < CYEV_MAX_EVENT; i++)
{
cyev_eventList[i].wantRepeat = false;
cyev_eventList[i].seen = false;
cyev_eventList[i].unique = false;
cyev_eventList[i].repeatDelay = 0;
cyev_eventList[i].valid = INVALID;
}
}
/* Get Next Event will invalidate the event if it is not marked as repeat */
/* After the event is selected, it will point to the next one */
/* We first start by the first event of the list then search for the first valid and return it */
CyEvent_t *CyEv_GetNextEvent(void)
{
static int listPos = 0;
int i;
CyEvent_t ret = NULL;
/* Scan the list starting from the last position stored */
for(i = listPos; i < (CYEV_MAX_EVENT + listPos); i++)
{
/* Stop on the first "VALID" event */
if (cyev_eventList[i % CYEV_MAX_EVENT].valid == VALID)
{
/* We already send this repeatable event. Did the delay occur ?
if not, ignore it and go to the next one */
if ( (cyev_eventList[i % CYEV_MAX_EVENT].seen == true) &&
(cyev_eventList[i % CYEV_MAX_EVENT].wantRepeat == true) )
{
if (cyev_eventList[i % CYEV_MAX_EVENT].repeatDelay < jiffies) )
{
continue;
}
else /* if (cyev_eventList[i % CYEV_MAX_EVENT].repeatDelay >= jiffies) ) */
{
/* Set the Repeat flag to true */
cyev_eventList[i % CYEV_MAX_EVENT].payload.flags |= CYEVENT_FLAG_REPEATEVENT;
}
}
cyev_eventList[i % CYEV_MAX_EVENT].seen = true;
/* Return the event */
ret = &(cyev_eventList[i % CYEV_MAX_EVENT].payload);
/* If the event is not a repeatable event, invalidate it! */
if (cyev_eventList[i % CYEV_MAX_EVENT].wantRepeat == false)
{
cyev_eventList[i % CYEV_MAX_EVENT].valid = INVALID;
}
/* Update the index for the next element to scan */
listPos = (listPos + i + 1) % CYEV_MAX_EVENT;
break;
}
}
return ret; /* We will return NULL if no valid payload */
}
int isEventNotDuplicate(CyEvent_t *CyEvent)
{
int i;
CyEvent_t *cur:
for(i = 0; i < CYEV_MAX_EVENT; i++)
{
cur = &(cyev_eventList[i].payload);
if (cyev_eventList[i].unique == true)
{
if (cur->type == CyEvent->type) /* To be verified */
return false;
}
}
return true;
}
int CyEv_AddNewEvent(CyEvent_t *CyEvent, char wantRepeat, char wantUnique)
{
/* Search for the first "invalid" event, then put the given CyEvent in it. */
/* If no slot is valid, return an error */
int i;
int ret = -EIO; /* TODO: find a better error code.*/
/* If wantUnique, first check that there is not another unique event */
if ((wantUnique) && !isEventNotDuplicate(CyEvent))
goto exit;
/* Scan the list starting from the last position stored */
for(i = 0; i < CYEV_MAX_EVENT; i++)
{
/* Stop on the first "VALID" event */
if (cyev_eventList[i].valid == INVALID)
{
/* Store the event */
memcpy(&(cyev_eventList[i].payload), CyEvent, sizeof(CyEvent_t));
/* Force version in the structure */
cyev_eventList[i].payload.version = CYEV_CURRENT_VERSION;
cyev_eventList[i].valid = VALID;
if (wantUnique)
cyev_eventList[i].unique = true;
else
cyev_eventList[i].unique = false;
if (wantRepeat)
{
cyev_eventList[i].wantRepeat = true;
cyev_eventList[i].repeatDelay = jiffies + CYEV_REPEAT_DELAY;
}
else
cyev_eventList[i].wantRepeat = false;
if (openingTask)
wake_up_process(openingTask);
ret = 0;
break;
}
}
exit:
return ret;
}
/* Return 0 if event found and updated, or anything else */
/* We could ONLY update event that are declared as unique */
int CyEv_UpdateUniqueEvent(CyEvent_t *CyEvent, char wantRepeat)
{
int i;
int ret = -1;
/* Search for the event */
for(i = 0; i < CYEV_MAX_EVENT; i++)
{
if ( (cyev_eventList[i].unique == true) &&
(cyev_eventList[i].payload.type == CyEvent.type) )
{ /* Found it ! Now update the fields */
memcpy(&(cyev_eventList[i].payload), CyEvent, sizeof(CyEvent_t));
if (wantRepeat)
{
cyev_eventList[i].wantRepeat = true;
cyev_eventList[i].repeatDelay = jiffies + CYEV_REPEAT_DELAY;
}
else
cyev_eventList[i].wantRepeat = false;
if (openingTask)
wake_up_process(openingTask);
ret = 0;
}
}
return ret;
}
int CyEv_InvalidateRepeatableEvent(CyEvent_t *CyEvent)
{
int i;
int ret = -1;
/* Search for the event */
for(i = 0; i < CYEV_MAX_EVENT; i++)
{
if ( (cyev_eventList[i].wantRepeat == true) &&
(cyev_eventList[i].payload.type == CyEvent.type) )
{ /* Found it ! Now update the fields */
/* In case the event has not been eaten, just remove the "repeat want"
ie: do not invalidate it */
cyev_eventList[i].wantRepeat = false;
if (cyev_eventList[i].seen == true)
{
/* set the event "end of repeat" flag */
cyev_eventList[i].payload.flags |= CYEVENT_FLAG_ENDOFREPEAT;
}
if (openingTask)
wake_up_process(openingTask);
ret = 0;
break;
}
}
return ret;
}
// ===========================================================================
// ===========================================================================
// External event managment
int CyEvent_PushNewEvent(CyEvent_t *CyEvent, char wantRepeat)
{
if (wantRepeat)
return CyEv_AddNewEvent(CyEvent, wantRepeat, true);
else
return CyEv_AddNewEvent(CyEvent, wantRepeat, false);
}
EXPORT_SYMBOL(CyEvent_PushNewEvent);
int CyEvent_PushOrUpdateUniqueEvent(CyEvent_t *CyEvent, char wantRepeat)
{
int ret;
/* For now a simple call to AddNewEvent */
ret = CyEv_UpdateUniqueEvent(CyEvent, wantRepeat);
if (ret != 0) /* The event is not present */
ret = CyEv_AddNewEvent(CyEvent, wantRepeat, true);
return ret;
}
EXPORT_SYMBOL(CyEvent_PushOrUpdateUniqueEvent);
int CyEvent_InvalidateRepeatableEvent(CyEvent_t *CyEvent)
{
ret = CyEv_InvalidateRepeatableEvent(CyEvent);
}
EXPORT_SYMBOL(CyEvent_InvalidateRepeatableEvent);
// ===========================================================================
static int ev_open(struct inode *inode, struct file *file)
{
/* Clear pending event list... */
CyEv_ClearEventList();
return 0;
}
// ---------------------------------------------------------------------------
static int ev_release(struct inode *inode, struct file *file)
{
/* Clear pending event list... */
CyEv_ClearEventList();
return 0;
}
// ---------------------------------------------------------------------------
ssize_t ev_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
int nBytes = sizeof(CyEvent_t);
int ret = -EIO;
CyEvent_t *CyEvent = NULL;
if (count < nBytes)
{
ret = -EINVAL;
goto exit;
}
while (1)
{
if ((CyEvent = CyEv_GetNextEvent()) != NULL)
{
break;
}
openingTask = current;
set_current_state(TASK_INTERRUPTIBLE);
schedule();
set_current_state(TASK_RUNNING);
openingTask = 0;
if (signal_pending(current))
{
ret = -ERESTARTSYS;
break;
}
}
if (CyEvent != NULL)
{
nRes = copy_to_user(buf, &CyEvent, nBytes);
if (!nRes)
nRes = nBytes;
}
return ret;
}
// ---------------------------------------------------------------------------
static int ev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = -EINVAL;
unsigned long value;
switch(cmd)
{
default:
printk(KERN_ERR "Bad IOCTL\n");
break;
case CYIO_CTL_LED_CMD:
DBG("CYIO_CTL_LED_CMD arg[0x%03X]", arg);
if ((arg & 0x2) && (arg & 0x1)) /* Power LED */
{
MSG("\n----------------> POWER LED ON\n");
//GPH12
__raw_writel(__raw_readl(S3C2410_GPHDAT) | (1 << 12), S3C2410_GPHDAT);
}
else if (arg & 0x2)
{
MSG("\n----------------> POWER LED OFF\n");
__raw_writel(__raw_readl(S3C2410_GPHDAT) & ~(1 << 12), S3C2410_GPHDAT);
}
if ((arg & 0x20) && (arg & 0x10)) /* Wifi LED */
{
MSG("\n----------------> POWER WFI ON\n");
//GPK8
__raw_writel(__raw_readl(S3C2416_GPKDAT) | (1 << 8), S3C2416_GPKDAT);
}
else if (arg & 0x20)
{
MSG("\n----------------> POWER WFI OFF\n");
__raw_writel(__raw_readl(S3C2416_GPKDAT) & ~(1 << 8), S3C2416_GPKDAT);
}
if ((arg & 0x200) && (arg & 0x100)) /* Bluetooth LED */
{
MSG("\n----------------> POWER BTH ON\n");
//GPK9
__raw_writel(__raw_readl(S3C2416_GPKDAT) | (1 << 9), S3C2416_GPKDAT);
}
else if (arg & 0x200)
{
MSG("\n----------------> POWER BTH OFF\n");
__raw_writel(__raw_readl(S3C2416_GPKDAT) & ~(1 << 9), S3C2416_GPKDAT);
}
ret = 0;
break;
case CYIO_CTL_USB_STATUS:
value = gpio_get_value(S3C2410_GPG1);
put_user(value, (unsigned long __user *)arg);
ret = 0;
break;
case CYIO_CTL_SD_STATUS:
value = gpio_get_value(S3C2410_GPF1);
put_user(value, (unsigned long __user *)arg);
ret = 0;
break;
}
return ret;
}
// ===========================================================================
static struct file_operations s_io_fops =
{
owner: THIS_MODULE,
read: ev_read,
ioctl: ev_ioctl,
open: ev_open,
release: ev_release,
};
gstatic struct miscdevice s_ev_dev =
{
.minor = 250,
.name = "cyio",
.fops = &s_ev_fops,
};
// ===========================================================================
// ---------------------------------------------------------------------------
static int io_probe(struct platform_device *dev)
{
return 0;
}
// ---------------------------------------------------------------------------
static int io_remove(struct platform_device *dev)
{
misc_deregister(&s_ev_dev);
return 0;
}
// --------------------------------------------------------------------------
static int io_resume(struct platform_device *dev)
{
return 0;
}
// ---------------------------------------------------------------------------
static struct platform_driver cyev_driver =
{
.driver =
{
.name = "cyevent",
.owner = THIS_MODULE,
},
.probe = ev_probe,
.remove = ev_remove,
.suspend = NULL,
.resume = ev_resume,
};
// ---------------------------------------------------------------------------
// ===========================================================================
static int __init cyEv_init(void)
{
if (misc_register(&s_ev_dev))
return -EBUSY;
platform_driver_register(&cyev_driver);
return 0;
}
// ---------------------------------------------------------------------------
static void __exit cyEv_exit(void)
{
platform_driver_unregister(&cyev_driver);
misc_deregister(&s_ev_dev);
}
// ---------------------------------------------------------------------------
module_init(cyEv_init);
module_exit(cyEv_exit);
// ---------------------------------------------------------------------------
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bookeen <developers@bookeen.com>");
MODULE_DESCRIPTION("Cybook Event Manager");
MODULE_VERSION("3.0");
// ===========================================================================