941 lines
24 KiB
C
941 lines
24 KiB
C
// ===========================================================================
|
|
// cyio.c
|
|
// Copyright (C) 2008-2009 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 <cybook.h>
|
|
|
|
#include "cyio.h"
|
|
|
|
//#define CYIO_TIMER
|
|
#define CYIO_REPEAT
|
|
|
|
#define CYIO_ALTERNATE_KEY
|
|
|
|
#define DEBUG_MESSAGES
|
|
#define DBG_IRQ
|
|
|
|
#include <asm/hardware.h>
|
|
#include <asm/delay.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm-arm/irq.h>
|
|
#include <asm/arch/gpio.h>
|
|
#include <asm/arch/regs-gpio.h>
|
|
#include <asm/arch/regs-irq.h>
|
|
#include <asm-arm/arch-s3c2410/irqs.h>
|
|
#include <asm-arm/arch-s3c2410/gpio.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
// ===========================================================================
|
|
spinlock_t io_lock = SPIN_LOCK_UNLOCKED;
|
|
|
|
static unsigned long io_status = 0;
|
|
struct task_struct *ptsk = 0;
|
|
|
|
unsigned int platform_type = CYBOOK_GEN4;
|
|
|
|
#ifdef CYIO_TIMER
|
|
#define IO_TIMER_DELAY_1 ((HZ * 1) / 2) /* 500 ms */
|
|
#define IO_TIMER_DELAY_2 ((HZ * 2) / 1) /* 2 s */
|
|
#define IO_TIMER_DELAY_3 ((HZ * 1) / 1) /* 1 s */
|
|
static struct timer_list io_timer;
|
|
static int timer_inited = 0;
|
|
static int timer_run = 0;
|
|
#endif
|
|
|
|
#define TRUE (1==1)
|
|
#define FALSE (0==1)
|
|
|
|
#define PFX "CyIO:"
|
|
|
|
typedef struct _cyIrq_
|
|
{
|
|
int nIrq;
|
|
u32 nGpio;
|
|
u8 bActive;
|
|
u8 bKeyIrq; // IRQ generated by a key?
|
|
u8 bIsAltKey;
|
|
u8 nCodeActive; // All interrupts generate a code when active
|
|
u8 nCodeInactive; // Some interrupts generate a code when inactive
|
|
u8 nCodeAlternate;
|
|
char* sName;
|
|
} cyIrq;
|
|
|
|
typedef struct _cyEvent_
|
|
{
|
|
u8 nCode;
|
|
u8 nKeyEvent;
|
|
void* pNext;
|
|
} cyEvent;
|
|
|
|
static cyEvent s_nEvents[10];
|
|
static int s_nEventMax = sizeof(s_nEvents)/sizeof(s_nEvents[0]);
|
|
static int s_nEventCnt = 0;
|
|
static int s_nKeyLogMax = 1;
|
|
static int s_nKeyLogCnt = 0;
|
|
static cyEvent* s_pEventR = 0;
|
|
static cyEvent* s_pEventW = 0;
|
|
#ifdef CYIO_REPEAT
|
|
static u8 s_nPrevKey = 0;
|
|
static u8 s_bRepMode = 0;
|
|
#endif
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
static u8 s_altKeyPress = 0;
|
|
static u32 s_altKeyGpio = 0;
|
|
static u8 s_altKeyPresent = FALSE; /* By default we don't have a Alt Key */
|
|
#endif
|
|
|
|
#define GPIO_F0 S3C2410_GPF0
|
|
#define GPIO_F1 S3C2410_GPF1
|
|
#define GPIO_F2 S3C2410_GPF2
|
|
#define GPIO_F3 S3C2410_GPF3
|
|
#define GPIO_F4 S3C2410_GPF4
|
|
#define GPIO_F5 S3C2410_GPF5
|
|
#define GPIO_F6 S3C2410_GPF6
|
|
#define GPIO_F7 S3C2410_GPF7
|
|
#define GPIO_F8 S3C2410_GPF8
|
|
#define GPIO_F9 S3C2410_GPF9
|
|
#define GPIO_F10 S3C2410_GPF10
|
|
|
|
#define GPIO_G0 S3C2410_GPG0
|
|
#define GPIO_G1 S3C2410_GPG1
|
|
#define GPIO_G2 S3C2410_GPG2
|
|
#define GPIO_G3 S3C2410_GPG3
|
|
#define GPIO_G4 S3C2410_GPG4
|
|
#define GPIO_G5 S3C2410_GPG5
|
|
#define GPIO_G6 S3C2410_GPG6
|
|
#define GPIO_G7 S3C2410_GPG7
|
|
#define GPIO_G8 S3C2410_GPG8
|
|
#define GPIO_G9 S3C2410_GPG9
|
|
|
|
static cyIrq *s_nIrq ;
|
|
static int nCnt;
|
|
|
|
static cyIrq s_nIrq_GEN4[] =
|
|
{ /* Event structure for the Cybook Gen3 (2440) */
|
|
/* IRQ GPIO A B C Depress Event Release Event Alt Event Name Number */
|
|
{ IRQ_EINT0, GPIO_F0, 0, 1, 0, CYEVENT_KEY_OFF, 0, 0, "PowerBtn" } //0
|
|
//, { IRQ_EINT1, GPIO_F1, 0, 0, 0, CYEVENT_SD_IN, CYEVENT_SD_OUT, 0, "SD Card" } //1
|
|
//, { IRQ_EINT2, GPIO_F2, 0, 0, 0, CYEVENT_TP_PRESS, CYEVENT_TP_REL, 0, "Touch Panel" } //2
|
|
//
|
|
/* EINT3 is not Wifi EINT */
|
|
/* EINT4 is Headphone plug status */
|
|
/* EINT5 is Touchpanel Enable */
|
|
/* EINT6 is charger status */
|
|
, { IRQ_EINT7, GPIO_F7, 0, 1, 0, CYEVENT_KEY_LEFT, 0, 0, "Left" } //3
|
|
/* INT8 is Power Keey???? */
|
|
//, { IRQ_EINT9, GPIO_G1, 0, 0, 0, CYEVENT_USB_IN, CYEVENT_USB_OUT, 0, "USB" } //4
|
|
/* EINT10 is SD power enable */
|
|
, { IRQ_EINT15, GPIO_G7, 0, 1, 0, CYEVENT_KEY_RIGHT, 0, 0, "Right" } //5
|
|
/* IRQ GPIO A B C Depress Event Release Event Alt Event Name Number */
|
|
/* A:Reserved (must be 0) - B: Is Keypress Event? (or Allow Repeat?) (1: Yes, 0: No) - C: Is Alt Key? (1: Yes 0: No) */
|
|
};
|
|
|
|
static u8* s_pbUsbPowered = NULL;
|
|
static u8* s_pbAcPowered = NULL;
|
|
static u8* s_pbPowerOff = NULL;
|
|
static u8* s_pbVolMinus = NULL;
|
|
|
|
static irqreturn_t io_interrupt(int irq, void *dev_id);
|
|
|
|
#ifdef CYIO_TIMER
|
|
static void io_timer_handler(unsigned long nData);
|
|
#endif
|
|
|
|
#undef MSG
|
|
#undef DBG
|
|
#ifdef DEBUG_MESSAGES
|
|
#define MSG(str) { printk(KERN_ALERT str "\n"); }
|
|
#define DBG(str, ...) { printk(KERN_ALERT str "\n", __VA_ARGS__); }
|
|
#else
|
|
#define MSG(str)
|
|
#define DBG(str, ...)
|
|
#endif
|
|
|
|
//#define DEBUG_SPINLOCK
|
|
|
|
#ifdef DEBUG_SPINLOCK
|
|
|
|
#define spinLock(spin) {\
|
|
printk(KERN_ALERT ")))))))))))) (%s:%d) Wait for %p\n", __func__, __LINE__, spin);\
|
|
spin_lock(spin); \
|
|
printk(KERN_ALERT ")))))))))))) (%s:%d) Gain %p\n", __func__, __LINE__, spin);\
|
|
}
|
|
|
|
#define spinUnlock(spin) {\
|
|
printk(KERN_ALERT ")))))))))))) (%s:%d) Free %p\n", __func__, __LINE__, spin);\
|
|
spin_unlock(spin);\
|
|
}
|
|
|
|
#else
|
|
|
|
#define spinLock(spin) spin_lock(spin)
|
|
#define spinUnlock(spin) spin_unlock(spin)
|
|
|
|
#endif
|
|
|
|
/* Allow other modules/driver to push cyio event */
|
|
void Cyio_PushEvent(char eventId, char unique)
|
|
{
|
|
cyEvent *pEventC;
|
|
|
|
spinLock(&io_lock);
|
|
|
|
if (unique != 0)
|
|
{
|
|
pEventC = s_pEventR;
|
|
do
|
|
{
|
|
if (pEventC->nCode == eventId)
|
|
goto exit;
|
|
pEventC = pEventC->pNext;
|
|
} while ((pEventC != s_pEventW) && (s_pEventR != s_pEventW));
|
|
}
|
|
DBG("New Pushed event '%c'\n", eventId);
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
s_pEventW->nCode = eventId;
|
|
s_pEventW->nKeyEvent = 0;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
|
|
exit:
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
}
|
|
EXPORT_SYMBOL(Cyio_PushEvent);
|
|
|
|
#ifdef CYIO_TIMER
|
|
/* Allow other module/driver to reset the timer event */
|
|
void Cyio_ResetTimer(void)
|
|
{
|
|
if (timer_inited == 0)
|
|
return;
|
|
if (timer_run == 0)
|
|
return;
|
|
#ifndef ALTERNATE_TIMER_CHANGE
|
|
del_timer(&io_timer);
|
|
if (io_timer.data == CYEVENT_SUSPEND_SCREEN)
|
|
{
|
|
io_timer.expires = IO_TIMER_DELAY_1;
|
|
}
|
|
else //if (io_timer.data == CYEVENT_SUSPEND_DEVICE)
|
|
{
|
|
io_timer.expires = IO_TIMER_DELAY_2 - IO_TIMER_DELAY_1;
|
|
}
|
|
|
|
io_timer.expires += jiffies;
|
|
add_timer(&io_timer);
|
|
timer_run = 1;
|
|
#else
|
|
//io_timer.expires += IO_TIMER_DELAY_3;
|
|
mod_timer(&io_timer, IO_TIMER_DELAY_3 + io_timer.expires);
|
|
#endif
|
|
}
|
|
EXPORT_SYMBOL(Cyio_ResetTimer);
|
|
#endif /* CYIO_TIMER */
|
|
|
|
// ===========================================================================
|
|
void io_initEventList(void)
|
|
{
|
|
int i;
|
|
s_pEventR = 0;
|
|
s_nEventCnt = 0;
|
|
s_nKeyLogCnt = 0;
|
|
for (i=0; i<s_nEventMax; ++i)
|
|
{
|
|
cyEvent* pEvent = &s_nEvents[i];
|
|
pEvent->nCode = 0;
|
|
pEvent->nKeyEvent = 0;
|
|
if (s_pEventR)
|
|
s_pEventR->pNext = pEvent;
|
|
s_pEventR = pEvent;
|
|
}
|
|
s_pEventR = &s_nEvents[0];
|
|
s_nEvents[s_nEventMax-1].pNext = s_pEventR;
|
|
s_pEventW = s_pEventR;
|
|
#ifdef CYIO_REPEAT
|
|
s_nPrevKey = 0;
|
|
s_bRepMode = 0;
|
|
#endif
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
u8 io_factoryPowerOff(void)
|
|
{
|
|
return (s_pbPowerOff &&
|
|
s_pbVolMinus &&
|
|
(*s_pbPowerOff) &&
|
|
(*s_pbVolMinus))
|
|
? 1
|
|
: 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
void io_initIrq(void)
|
|
{
|
|
|
|
int i;
|
|
cyIrq *pIrq, *pIrq0;
|
|
int ret;
|
|
pIrq0 = &s_nIrq[0];
|
|
|
|
// Read state as fast as possible (important when resuming)
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
{
|
|
#if 1
|
|
pIrq->bActive = !gpio_get_value(pIrq->nGpio);
|
|
#else
|
|
int j, nUp = 0;
|
|
for (j=0; j<4; ++j)
|
|
if (gpio_get_value(pIrq->nGpio))
|
|
++nUp;
|
|
pIrq->bActive = (nUp < 2);
|
|
#endif
|
|
}
|
|
|
|
// Preparatory pass
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
{
|
|
switch (pIrq->nCodeActive)
|
|
{
|
|
case CYEVENT_USB_IN:
|
|
s_pbUsbPowered = &pIrq->bActive;
|
|
s_pbAcPowered = &pIrq->bActive;
|
|
break;
|
|
case CYEVENT_AC_IN:
|
|
break;
|
|
case CYEVENT_KEY_OFF:
|
|
s_pbPowerOff = &pIrq->bActive;
|
|
break;
|
|
case CYEVENT_KEY_VOLN:
|
|
s_pbVolMinus = &pIrq->bActive;
|
|
break;
|
|
}
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
if (pIrq->bIsAltKey)
|
|
{ /* If this event is configured as the Alt key, just fill the needed variables */
|
|
DBG("Found Alt key as '%s'\n", pIrq->sName);
|
|
s_altKeyPresent = TRUE;
|
|
s_altKeyGpio = pIrq->nGpio;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (io_factoryPowerOff())
|
|
{ // Special case
|
|
spinLock(&io_lock);
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
++s_nKeyLogCnt;
|
|
s_pEventW->nCode = CYEVENT_FACTORY_OFF;
|
|
s_pEventW->nKeyEvent = 1;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
}
|
|
else
|
|
{ // Register events as if they had just occurred (esp. when resuming)
|
|
spinLock(&io_lock);
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
{
|
|
if (!pIrq->bActive && pIrq->bKeyIrq)
|
|
continue;
|
|
|
|
if ((s_nEventMax <= s_nEventCnt) ||
|
|
(pIrq->bKeyIrq && s_nKeyLogMax <= s_nKeyLogCnt))
|
|
{
|
|
//break;
|
|
// !!! BUGBUG (we might miss system events once a key event is queued)
|
|
continue;
|
|
}
|
|
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
if (pIrq->bKeyIrq)
|
|
++s_nKeyLogCnt;
|
|
s_pEventW->nCode = pIrq->bActive ? pIrq->nCodeActive : pIrq->nCodeInactive;
|
|
s_pEventW->nKeyEvent = pIrq->bKeyIrq;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
}
|
|
spinUnlock(&io_lock);
|
|
}
|
|
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
{
|
|
int nIrq = pIrq->nIrq;
|
|
set_irq_type(nIrq, IRQT_BOTHEDGE);
|
|
|
|
DBG(".. io_initIrq [%s][%c] bActive[%d]", pIrq->sName, pIrq->bActive || !pIrq->nCodeInactive ? (char)pIrq->nCodeActive : (char)pIrq->nCodeInactive, pIrq->bActive);
|
|
ret = request_irq(nIrq, io_interrupt, SA_INTERRUPT|SA_SHIRQ, "cyio", pIrq);
|
|
if (ret != 0)
|
|
{
|
|
printk(KERN_ERR PFX "Error registering IRQ %d [%s]!\n", nIrq, pIrq->sName);
|
|
}
|
|
}
|
|
|
|
#ifdef CYIO_TIMER
|
|
init_timer(&io_timer);
|
|
timer_inited = 1;
|
|
io_timer.function = io_timer_handler;
|
|
io_timer.data = CYEVENT_SUSPEND_SCREEN;
|
|
#endif
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
void io_deinitIrq(void)
|
|
{
|
|
int i;
|
|
cyIrq *pIrq, *pIrq0;
|
|
|
|
#ifdef CYIO_TIMER
|
|
del_timer(&io_timer);
|
|
timer_run = 0;
|
|
#endif
|
|
|
|
//nCnt = sizeof(s_nIrq)/sizeof(s_nIrq[0]);
|
|
pIrq0 = &s_nIrq[0];
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
free_irq(pIrq->nIrq, pIrq);
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
//#define DBG_IRQ
|
|
static irqreturn_t io_interrupt(int irq, void *dev_id)
|
|
{
|
|
cyIrq* pIrq = (cyIrq*)dev_id;
|
|
u8 bActive;
|
|
u8 blCodeActive;
|
|
unsigned long flags;
|
|
|
|
spinlock_t lock = SPIN_LOCK_UNLOCKED;
|
|
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
static int bAltKeyUsed = FALSE;
|
|
#endif
|
|
|
|
#ifdef DBG_IRQ
|
|
static int s_nIrq_dbg = 0;
|
|
++s_nIrq_dbg;
|
|
#endif
|
|
|
|
spin_lock_irqsave(&lock, flags);
|
|
{ // Avoid bounces
|
|
int i, nUp = 0;
|
|
for (i=0; i<10000; ++i)
|
|
if (gpio_get_value(pIrq->nGpio))
|
|
++nUp;
|
|
bActive = (nUp < 5000);
|
|
}
|
|
spin_unlock_irqrestore(&lock, flags);
|
|
|
|
if (pIrq->bActive == bActive)
|
|
return IRQ_HANDLED;
|
|
|
|
#ifdef DBG_IRQ
|
|
DBG(".. io_irq #%d [%s][%c] alt[%d:%d] bActive[%d]", s_nIrq_dbg, pIrq->sName, bActive || !pIrq->nCodeInactive ? (char)pIrq->nCodeActive : (char)pIrq->nCodeInactive, (s_altKeyPress && pIrq->bKeyIrq) ? pIrq->nCodeAlternate : (bActive ? pIrq->nCodeActive : pIrq->nCodeInactive), s_altKeyPress, bActive);
|
|
#endif
|
|
|
|
blCodeActive = (s_altKeyPress && pIrq->bKeyIrq) ? pIrq->nCodeAlternate : (bActive ? pIrq->nCodeActive : pIrq->nCodeInactive);
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
if (s_altKeyPresent)
|
|
{
|
|
s_altKeyPress = !gpio_get_value(s_altKeyGpio);
|
|
DBG("alt status: %d", s_altKeyPress);
|
|
}
|
|
#endif
|
|
|
|
spinLock(&io_lock);
|
|
pIrq->bActive = bActive;
|
|
if (pIrq->bKeyIrq)
|
|
{
|
|
|
|
#ifdef CYIO_REPEAT
|
|
DBG("bfr: s_nPrevKey = %d", s_nPrevKey);
|
|
if (s_nPrevKey)
|
|
{
|
|
DBG("s_nPrevKey is != 0 [%d]", s_nPrevKey);
|
|
|
|
if (!pIrq->bActive /*|| s_nPrevKey != blCodeActive*/)
|
|
s_nPrevKey = 0;
|
|
|
|
DBG("s_nPrevKey == %d (s_bRepMode:%d, pIrq->bActive:%d)", s_nPrevKey, s_bRepMode, pIrq->bActive);
|
|
|
|
if (s_bRepMode && !s_nPrevKey)
|
|
{
|
|
MSG("Exit repeat mode...");
|
|
s_bRepMode = 0;
|
|
if (s_pEventW)
|
|
{ // NB: This is considered as a system event
|
|
++s_nEventCnt;
|
|
s_pEventW->nCode = CYEVENT_KEY_REPEAT_END;
|
|
s_pEventW->nKeyEvent = 0;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
if ((!pIrq->bActive) && (pIrq->nGpio != s_altKeyGpio))
|
|
#else
|
|
if (!pIrq->bActive)
|
|
#endif
|
|
{
|
|
spinUnlock(&io_lock);
|
|
MSG("Exiting IRQ Handler");
|
|
#ifdef CYIO_KERNEL_2_4
|
|
return;
|
|
#else
|
|
return IRQ_HANDLED;
|
|
#endif
|
|
}
|
|
}
|
|
spinUnlock(&io_lock);
|
|
|
|
//#ifdef DBG_IRQ
|
|
// DBG(".. io_irq #%d [%s][%c] bActive[%d]", s_nIrq_dbg, pIrq->sName, bActive || !pIrq->nCodeInactive ? (char)pIrq->nCodeActive : (char)pIrq->nCodeInactive, bActive);
|
|
//#endif
|
|
|
|
if (io_factoryPowerOff())
|
|
{ // Special case
|
|
spinLock(&io_lock);
|
|
MSG("Request Factory Setting...\n");
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
++s_nKeyLogCnt;
|
|
s_pEventW->nCode = CYEVENT_FACTORY_OFF;
|
|
s_pEventW->nKeyEvent = 1;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
}
|
|
else
|
|
{
|
|
spinLock(&io_lock);
|
|
if ((s_nEventMax <= s_nEventCnt) ||
|
|
(pIrq->bKeyIrq && s_nKeyLogMax <= s_nKeyLogCnt))
|
|
{
|
|
spinUnlock(&io_lock);
|
|
DBG("s_nEventMax:%d, s_nEventCnt:%d, s_nKeyLogMax:%d, s_nKeyLogCnt:%d",s_nEventMax,s_nEventCnt,s_nKeyLogMax,s_nKeyLogCnt);
|
|
MSG("!!! Event list full !!!");
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
|
|
if (s_altKeyPresent && bAltKeyUsed && !s_altKeyPress && (pIrq->nGpio == s_altKeyGpio))
|
|
{ /* If the released key is the Alt Key and it was used as the Alt Key, do not send an event */
|
|
MSG("Alt Key released... Do Nothing");
|
|
bAltKeyUsed = FALSE;
|
|
s_nPrevKey = 0;
|
|
spinUnlock(&io_lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
if (s_altKeyPresent && (pIrq->nGpio == s_altKeyGpio))
|
|
{ /* If the altKey is pressed, we have to do a little trick: Inverse it's active state */
|
|
MSG("Alt key event...");
|
|
/* To be sure */
|
|
s_nPrevKey = 0;
|
|
s_bRepMode = 0;
|
|
|
|
bActive = !bActive;
|
|
}
|
|
else if (s_altKeyPresent && s_altKeyPress && pIrq->bKeyIrq)
|
|
{/* The event is a keypress and the AltKey is pressed, set our internal value to prevent Alt to send an event */
|
|
MSG("Button press with Alt...");
|
|
if (pIrq->nCodeAlternate != 0)
|
|
bAltKeyUsed = TRUE;
|
|
}
|
|
MSG("Hum...");
|
|
#endif
|
|
|
|
if (s_pEventW)
|
|
{
|
|
MSG("Will push another event...");
|
|
++s_nEventCnt;
|
|
if (pIrq->bKeyIrq)
|
|
++s_nKeyLogCnt;
|
|
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
s_pEventW->nCode = (s_altKeyPress && pIrq->bKeyIrq) ? pIrq->nCodeAlternate : (bActive ? pIrq->nCodeActive : pIrq->nCodeInactive);
|
|
#else
|
|
s_pEventW->nCode = bActive ? pIrq->nCodeActive : pIrq->nCodeInactive;
|
|
#endif
|
|
|
|
s_pEventW->nKeyEvent = pIrq->bKeyIrq;
|
|
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
}
|
|
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
#ifdef CYIO_TIMER
|
|
static void io_timer_handler(unsigned long nData)
|
|
{
|
|
/* YEP inside */
|
|
#if 0
|
|
//#ifdef G_SENSOR
|
|
if (hold_wakeup==1) {
|
|
del_timer(&io_timer);
|
|
goto end_timer;
|
|
}
|
|
#endif
|
|
/* end YEP inside */
|
|
DBG("Timer [%ld] tick...\n", nData);
|
|
|
|
spin_lock_irq(&io_lock);
|
|
|
|
del_timer(&io_timer);
|
|
timer_run = 0;
|
|
if (s_nEventCnt < s_nEventMax)
|
|
{
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
s_pEventW->nCode = (u8)nData;
|
|
s_pEventW->nKeyEvent = 0;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
}
|
|
|
|
spin_unlock_irq(&io_lock);
|
|
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
//end_timer:
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// ===========================================================================
|
|
static int io_open(struct inode *inode, struct file *file)
|
|
{
|
|
//MSG(">> io_open");
|
|
|
|
//spin_lock_irq(&io_lock);
|
|
spinLock(&io_lock);
|
|
|
|
if (io_status)
|
|
{
|
|
//spin_unlock_irq(&io_lock);
|
|
spinUnlock(&io_lock);
|
|
//MSG(".. io_open !!! Busy");
|
|
return -EBUSY;
|
|
}
|
|
|
|
io_status = 1;
|
|
|
|
//spin_unlock_irq(&io_lock);
|
|
spinUnlock(&io_lock);
|
|
|
|
io_initIrq();
|
|
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static int io_release(struct inode *inode, struct file *file)
|
|
{
|
|
MSG(">> io_release");
|
|
|
|
io_deinitIrq();
|
|
|
|
spin_lock_irq(&io_lock);
|
|
|
|
io_status = 0;
|
|
|
|
// The driver is likely to be closed & reopened within suspend sequences
|
|
// Only initialize the list when registering the driver and when closing it
|
|
// This allows for a quicker opening of the driver
|
|
io_initEventList();
|
|
|
|
spin_unlock_irq(&io_lock);
|
|
|
|
MSG("<< io_release");
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
ssize_t io_read(struct file *file, char *buf, size_t count, loff_t *ppos)
|
|
{
|
|
int nBytes = sizeof(unsigned long);
|
|
unsigned long nData;
|
|
ssize_t nRes = 0;
|
|
|
|
if (count < sizeof(unsigned long))
|
|
return -EINVAL;
|
|
|
|
while (1)
|
|
{
|
|
//spin_lock_irq(&io_lock);
|
|
spinLock(&io_lock);
|
|
nData = 0;
|
|
if (s_nEventCnt)
|
|
{
|
|
nData = s_pEventR->nCode;
|
|
s_pEventR->nCode = 0;
|
|
--s_nEventCnt;
|
|
if (s_pEventR->nKeyEvent)
|
|
{
|
|
--s_nKeyLogCnt;
|
|
#ifdef CYIO_REPEAT
|
|
if (nData != CYEVENT_KEY_OFF)
|
|
{
|
|
DBG("(line %d)Set s_nPrevKey", __LINE__);
|
|
s_nPrevKey = nData;
|
|
s_bRepMode = 0;
|
|
}
|
|
#endif
|
|
}
|
|
s_pEventR = s_pEventR->pNext;
|
|
}
|
|
#ifdef CYIO_REPEAT
|
|
if (!nData && s_nPrevKey)
|
|
{ // Check new status
|
|
int i;
|
|
cyIrq *pIrq, *pIrq0;
|
|
|
|
pIrq0 = &s_nIrq[0];
|
|
MSG("Will test key...");
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
{
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
if ((pIrq->nCodeActive != s_nPrevKey) && (pIrq->nCodeAlternate != s_nPrevKey))
|
|
#else
|
|
if (pIrq->nCodeActive != s_nPrevKey)
|
|
#endif
|
|
{
|
|
MSG("!CodeActive and Alt+!AltCode");
|
|
continue;
|
|
}
|
|
|
|
MSG("May clear prevkey");
|
|
if (!pIrq->bActive || gpio_get_value(pIrq->nGpio))
|
|
{
|
|
DBG("(line %d)Set s_nPrevKey", __LINE__);
|
|
s_nPrevKey = 0;
|
|
}
|
|
break;
|
|
}
|
|
if (s_nPrevKey)
|
|
{
|
|
MSG("Will Set Repeat flag...");
|
|
nData = s_nPrevKey | CYEVENT_KEY_REPEAT_FLAG;
|
|
s_bRepMode = 1;
|
|
}
|
|
}
|
|
#endif
|
|
spinUnlock(&io_lock);
|
|
|
|
if (nData != 0)
|
|
{
|
|
#ifdef CYIO_TIMER
|
|
del_timer(&io_timer);
|
|
timer_run = 0;
|
|
io_timer.data = (nData == CYEVENT_SUSPEND_SCREEN) ? CYEVENT_SUSPEND_DEVICE : CYEVENT_SUSPEND_SCREEN;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#ifdef CYIO_TIMER
|
|
if (s_pbUsbPowered && s_pbAcPowered && !(*s_pbUsbPowered) && !(*s_pbAcPowered))
|
|
{
|
|
del_timer(&io_timer);
|
|
timer_run = 0;
|
|
if (io_timer.data == CYEVENT_SUSPEND_SCREEN)
|
|
{
|
|
io_timer.expires = IO_TIMER_DELAY_1;
|
|
io_timer.expires += jiffies;
|
|
add_timer(&io_timer);
|
|
}
|
|
else //if (io_timer.data == CYEVENT_SUSPEND_DEVICE)
|
|
{
|
|
io_timer.expires = IO_TIMER_DELAY_2 - IO_TIMER_DELAY_1;
|
|
io_timer.expires += jiffies;
|
|
add_timer(&io_timer);
|
|
}
|
|
timer_run = 1;
|
|
}
|
|
#endif
|
|
|
|
ptsk = current;
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
schedule();
|
|
set_current_state(TASK_RUNNING);
|
|
ptsk = 0;
|
|
|
|
if (signal_pending(current))
|
|
{
|
|
nRes = -ERESTARTSYS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nData)
|
|
{
|
|
nRes = copy_to_user(buf,&nData,nBytes);
|
|
if (!nRes)
|
|
nRes = nBytes;
|
|
}
|
|
|
|
return nRes;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static int io_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
// ===========================================================================
|
|
static struct file_operations s_io_fops =
|
|
{
|
|
owner: THIS_MODULE,
|
|
read: io_read,
|
|
ioctl: io_ioctl,
|
|
open: io_open,
|
|
release: io_release,
|
|
};
|
|
|
|
static struct miscdevice s_io_dev =
|
|
{
|
|
.minor = 250,
|
|
.name = "cyio",
|
|
.fops = &s_io_fops,
|
|
};
|
|
// ===========================================================================
|
|
// ---------------------------------------------------------------------------
|
|
static int io_probe(struct platform_device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static int io_remove(struct platform_device *dev)
|
|
{
|
|
misc_deregister(&s_io_dev);
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static int io_resume(struct platform_device *dev)
|
|
{
|
|
//cyIrq *pIrq;
|
|
//u8 bSdCd;
|
|
MSG(">>Resume() .......\n");
|
|
#if 0
|
|
if ((platform_type == CYBOOK_GEN3) || (platform_type == CYBOOK_GEN3GOLD))
|
|
{
|
|
pIrq = &s_nIrq[13];
|
|
}
|
|
else
|
|
{
|
|
pIrq = &s_nIrq[11];
|
|
}
|
|
|
|
bSdCd = !read_gpio_bit(pIrq->nGpio);
|
|
|
|
//MSG(">>Resume() .......bSdCd 0x%x\n",bSdCd);
|
|
if (pIrq->bActive == bSdCd)
|
|
return 0;
|
|
|
|
spinLock(&io_lock);
|
|
pIrq->bActive = bSdCd;
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
s_pEventW->nCode = pIrq->bActive ? pIrq->nCodeActive : pIrq->nCodeInactive;
|
|
s_pEventW->nKeyEvent = pIrq->bKeyIrq;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
#endif
|
|
|
|
MSG("<<Resume() .......\n");
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static struct platform_driver cyio_driver =
|
|
{
|
|
.driver =
|
|
{
|
|
.name = "cyio",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = io_probe,
|
|
.remove = io_remove,
|
|
.suspend = NULL,
|
|
.resume = io_resume,
|
|
};
|
|
// ---------------------------------------------------------------------------
|
|
// ===========================================================================
|
|
static int __init cyIo_init(void)
|
|
{
|
|
#if 0
|
|
if(platform_type == CYBOOK_OPUS)
|
|
{
|
|
s_nIrq = s_nIrq_OPUS;
|
|
nCnt = sizeof(s_nIrq_OPUS)/sizeof(s_nIrq_OPUS[0]);
|
|
}
|
|
else if (/* GEN3 && GEN3GOLD */
|
|
{
|
|
s_nIrq = s_nIrq_GEN3;
|
|
nCnt = sizeof(s_nIrq_GEN3)/sizeof(s_nIrq_GEN3[0]);
|
|
}
|
|
#endif
|
|
s_nIrq = s_nIrq_GEN4;
|
|
nCnt = sizeof(s_nIrq_GEN4)/sizeof(s_nIrq_GEN4[0]);
|
|
|
|
DBG("s_nEventMax:%d, s_nEventCnt:%d, s_nKeyLogMax:%d, s_nKeyLogCnt:%d",s_nEventMax,s_nEventCnt,s_nKeyLogMax,s_nKeyLogCnt);
|
|
|
|
|
|
/* end YEP inside */
|
|
|
|
io_initEventList();
|
|
|
|
if (misc_register(&s_io_dev))
|
|
return -EBUSY;
|
|
|
|
platform_driver_register(&cyio_driver);
|
|
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
static void __exit cyIo_exit(void)
|
|
{
|
|
platform_driver_unregister(&cyio_driver);
|
|
|
|
misc_deregister(&s_io_dev);
|
|
|
|
//MSG("<< cyIo_exit");
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
module_init(cyIo_init);
|
|
module_exit(cyIo_exit);
|
|
// ---------------------------------------------------------------------------
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Bookeen <developers@bookeen.com>");
|
|
MODULE_DESCRIPTION("Cybook Event Manager");
|
|
MODULE_VERSION("3.0");
|
|
// ===========================================================================
|