1194 lines
30 KiB
C
1194 lines
30 KiB
C
// ===========================================================================
|
|
// 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 <cybook.h>
|
|
|
|
#include <linux/cyio.h>
|
|
|
|
#define CYIO_TIMER
|
|
//#define CYIO_REPEAT
|
|
//#define CYIO_POLLING
|
|
|
|
//#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;
|
|
|
|
#ifdef CYIO_POLLING
|
|
#define GPROCFS_ROOTFOLDER "cybook"
|
|
#define GPROCFS_MODULEFOLDER "gsensor"
|
|
#define GPROCFS_CALIBFILE "calibration"
|
|
#define GPROCFS_IOFILE "io"
|
|
#define GPROCFS_STATUSFILE "status"
|
|
#define GPROCFS_AXISFILE "axis"
|
|
#define GPROCFS_THDXFILE "thdx"
|
|
#define GPROCFS_THDYFILE "thdy"
|
|
#define GPROCFS_DIRECTIONFILE "direction"
|
|
#define GPROCFS_DEBUGFILE "debug"
|
|
|
|
unsigned long tiltRotation;
|
|
static struct proc_dir_entry *rootDir, *ioProcEntry;
|
|
#endif
|
|
|
|
#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;
|
|
|
|
#ifdef CYIO_POLLING
|
|
static struct timer_list io_btn_timer;
|
|
#endif
|
|
|
|
static int timer_inited = 0;
|
|
static int timer_run = 0;
|
|
#endif
|
|
|
|
static volatile int CyIO_Running = 0;
|
|
|
|
#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;
|
|
|
|
#ifdef CYIO_POLLING
|
|
typedef struct _cyPoll_
|
|
{
|
|
u32 nGpio;
|
|
u8 oldState;
|
|
u8 nCodeActive;
|
|
u8 nCodeInactive;
|
|
char *sName;
|
|
} cyPoll;
|
|
#endif
|
|
|
|
typedef struct _cyEvent_
|
|
{
|
|
u8 nCode;
|
|
u8 nKeyEvent;
|
|
u8 bValid;
|
|
void* pNext;
|
|
} cyEvent;
|
|
|
|
static cyEvent s_nEvents[20];
|
|
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
|
|
|
|
|
|
#define GPIO_D10 S3C2410_GPD10
|
|
#define GPIO_D11 S3C2410_GPD11
|
|
#define GPIO_D14 S3C2410_GPD14
|
|
|
|
#define GPIO_H4 S3C2410_GPH4
|
|
#define GPIO_H5 S3C2410_GPH5
|
|
|
|
static cyIrq *s_nIrq ;
|
|
static int nCnt;
|
|
|
|
#ifdef CYIO_POLLING
|
|
static int nPCnt;
|
|
static cyPoll *s_nPio;
|
|
#endif
|
|
|
|
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_EINT7, GPIO_F7, 0, 1, 0, CYEVENT_KEY_DOWN, 0, 0, "Down" } //1
|
|
, { IRQ_EINT15, GPIO_G7, 0, 1, 0, CYEVENT_KEY_UP, 0, 0, "Up" } //2
|
|
, { IRQ_EINT8, GPIO_G0, 0, 1, 0, CYEVENT_KEY_LEFT, 0, 0, "Left" } //3
|
|
, { IRQ_EINT10, GPIO_G2, 0, 1, 0, CYEVENT_KEY_RIGHT, 0, 0, "Right" } //4
|
|
, { IRQ_EINT14, GPIO_G6, 0, 1, 0, CYEVENT_KEY_ENTER, 0, 0, "Center" } //5
|
|
, { IRQ_EINT9, GPIO_G1, 0, 0, 0, CYEVENT_USB_OUT, CYEVENT_USB_IN, 0, "USB" } //6
|
|
/* 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) */
|
|
};
|
|
|
|
#ifdef CYIO_POLLING
|
|
static cyPoll s_nPoll_GEN4[] =
|
|
{
|
|
/* GPIO, Must 0 Depress Event, Release Event Name */
|
|
// { GPIO_H4, 0, CYEVENT_KEY_LEFT, 0, "Left" }
|
|
//, { GPIO_H5, 0, CYEVENT_KEY_RIGHT, 0, "Right" }
|
|
//, { GPIO_D14, 0, CYEVENT_KEY_ENTER, 0, "Enter" }
|
|
/* GPIO, Must 0 Depress Event, Release Event, Name */
|
|
};
|
|
#endif
|
|
|
|
static u8* s_pbUsbPowered = 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);
|
|
#ifdef CYIO_POLLING
|
|
static void io_btn_timer_handler(unsigned long nData);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CYIO_POLLING
|
|
static int procReadIo (char *page, char **start, off_t off, int count,
|
|
int *eof, void *data);
|
|
static int procWriteIo (struct file *file, const char *buffer,
|
|
unsigned long count, void *data);
|
|
#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 */
|
|
int Cyio_PushEvent(char eventId, char unique)
|
|
{
|
|
cyEvent *pEventC;
|
|
int ret = 0;
|
|
|
|
spinLock(&io_lock);
|
|
|
|
/* CyIO is not running, ie no one take it, so don't accept events */
|
|
if (CyIO_Running == 0)
|
|
{
|
|
spinUnlock(&io_lock);
|
|
return -1;
|
|
}
|
|
|
|
if (unique != 0)
|
|
{
|
|
pEventC = s_pEventR;
|
|
do
|
|
{
|
|
if (pEventC->nCode == eventId)
|
|
{
|
|
ret = -EEXIST;
|
|
goto exit;
|
|
}
|
|
pEventC = pEventC->pNext;
|
|
} while ((pEventC != s_pEventW) && (s_pEventR != s_pEventW));
|
|
}
|
|
DBG("New Pushed event '%c'\n", eventId>8);
|
|
|
|
if (s_pEventW)
|
|
{
|
|
++s_nEventCnt;
|
|
s_pEventW->nCode = eventId;
|
|
s_pEventW->nKeyEvent = 0;
|
|
s_pEventW->bValid = 1;
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
ret = 0;
|
|
exit:
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
|
|
return ret;
|
|
}
|
|
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;
|
|
pEvent->bValid = 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;
|
|
#ifdef CYIO_POLLING
|
|
cyPoll *pPoll, *pPoll0;
|
|
pPoll0 = &s_nPio[0];
|
|
#endif
|
|
pIrq0 = &s_nIrq[0];
|
|
|
|
|
|
DBG(">>%s()\n", __func__);
|
|
|
|
#ifdef CYIO_POLLING
|
|
tiltRotation = 90;
|
|
for (i=0, pPoll=pPoll0; i<nPCnt; ++i, ++pPoll)
|
|
{
|
|
s3c2410_gpio_cfgpin(pPoll->nGpio, 0);
|
|
s3c2410_gpio_pullup(pPoll->nGpio, 0);
|
|
}
|
|
|
|
s3c2410_gpio_cfgpin(GPIO_D10, 0);
|
|
s3c2410_gpio_pullup(GPIO_D10, 0);
|
|
s3c2410_gpio_cfgpin(GPIO_D11, 0);
|
|
s3c2410_gpio_pullup(GPIO_D11, 0);
|
|
#endif
|
|
// 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:
|
|
case CYEVENT_USB_OUT:
|
|
s_pbUsbPowered = &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->bValid = 0;
|
|
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->bValid = 0;
|
|
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);
|
|
|
|
/* Set no Pullup and no Pulldown */
|
|
s3c2410_gpio_pullup(pIrq->nGpio, 0);
|
|
|
|
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, pIrq->sName, pIrq);
|
|
enable_irq_wake(nIrq);
|
|
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;
|
|
#ifdef CYIO_POLLING
|
|
init_timer(&io_btn_timer);
|
|
io_btn_timer.function = io_btn_timer_handler;
|
|
io_btn_timer.expires = jiffies + HZ/8;
|
|
add_timer(&io_btn_timer);
|
|
#endif
|
|
#endif
|
|
spinLock(&io_lock);
|
|
CyIO_Running = 1;
|
|
spinUnlock(&io_lock);
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
void io_deinitIrq(void)
|
|
{
|
|
int i;
|
|
cyIrq *pIrq, *pIrq0;
|
|
|
|
spinLock(&io_lock);
|
|
CyIO_Running = 0;
|
|
spinUnlock(&io_lock);
|
|
|
|
#ifdef CYIO_TIMER
|
|
del_timer(&io_timer);
|
|
timer_run = 0;
|
|
# ifdef CYIO_POLLING
|
|
del_timer(&io_btn_timer);
|
|
# endif
|
|
#endif
|
|
|
|
//nCnt = sizeof(s_nIrq)/sizeof(s_nIrq[0]);
|
|
pIrq0 = &s_nIrq[0];
|
|
for (i=0, pIrq=pIrq0; i<nCnt; ++i, ++pIrq)
|
|
{
|
|
disable_irq_wake(pIrq->nIrq);
|
|
free_irq(pIrq->nIrq, pIrq);
|
|
}
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
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
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
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);
|
|
#else
|
|
DBG(".. io_irq #%d [%s][%c] bActive[%d]", s_nIrq_dbg, pIrq->sName, bActive || !pIrq->nCodeInactive ? (char)pIrq->nCodeActive : (char)pIrq->nCodeInactive, (bActive ? pIrq->nCodeActive : pIrq->nCodeInactive), bActive);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CYIO_ALTERNATE_KEY
|
|
blCodeActive = (s_altKeyPress && pIrq->bKeyIrq) ? pIrq->nCodeAlternate : (bActive ? pIrq->nCodeActive : pIrq->nCodeInactive);
|
|
if (s_altKeyPresent)
|
|
{
|
|
s_altKeyPress = !gpio_get_value(s_altKeyGpio);
|
|
DBG("alt status: %d", s_altKeyPress);
|
|
}
|
|
#else
|
|
blCodeActive = bActive ? pIrq->nCodeActive : pIrq->nCodeInactive;
|
|
#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->bValid = 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->bValid = 0;
|
|
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->bValid = 0;
|
|
|
|
s_pEventW = s_pEventW->pNext;
|
|
}
|
|
spinUnlock(&io_lock);
|
|
}
|
|
|
|
if (ptsk)
|
|
wake_up_process(ptsk);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
#ifdef CYIO_TIMER
|
|
#ifdef CYIO_POLLING
|
|
static void io_btn_timer_handler(unsigned long nData)
|
|
{
|
|
/* read buttons */
|
|
cyPoll *pPoll, *pPoll0;
|
|
int i;
|
|
int curState;
|
|
int currentTilt, tE1, tE2;
|
|
pPoll0 = &s_nPio[0];
|
|
|
|
DBG(">>%s()\n", __func__);
|
|
|
|
for (i=0, pPoll=pPoll0; i<nPCnt; ++i, ++pPoll)
|
|
{
|
|
curState = !gpio_get_value(pPoll->nGpio);
|
|
DBG("|| Btn '%s' [current: %d, old: %d, gpio: %d]\n", pPoll->sName, curState, pPoll->oldState, pPoll->nGpio);
|
|
/* If button found pressed, then push event 'bout them */
|
|
if (pPoll->oldState != curState)
|
|
{
|
|
if (curState != 0)
|
|
Cyio_PushEvent(pPoll->nCodeActive, 1);
|
|
/*else
|
|
Cyio_PushEvent(CYEVENT_KEY_REPEAT_END, 1);*/
|
|
}
|
|
else if (pPoll->oldState != 0)
|
|
{
|
|
/* Push event with repeat flag */
|
|
//Cyio_PushEvent(pPoll->nCodeActive | CYEVENT_KEY_REPEAT_FLAG, 1);
|
|
}
|
|
|
|
pPoll->oldState = curState;
|
|
|
|
}
|
|
|
|
/* special case, look at the tilt sensor */
|
|
tE1 = !gpio_get_value(GPIO_D11);
|
|
tE2 = !gpio_get_value(GPIO_D10);
|
|
currentTilt = 0;
|
|
if (!tE2)
|
|
currentTilt |= 1 << 1;
|
|
if (!tE1)
|
|
currentTilt |= 1 << 0;
|
|
|
|
switch(currentTilt)
|
|
{
|
|
case 0x00: /* 90 */
|
|
currentTilt = 90;
|
|
break;
|
|
|
|
case 0x01: /* 0 */
|
|
currentTilt = 0;
|
|
break;
|
|
|
|
case 0x02: /* 180 */
|
|
currentTilt = 180;
|
|
break;
|
|
|
|
case 0x03: /* 270 */
|
|
currentTilt = 270;
|
|
break;
|
|
}
|
|
if (currentTilt != tiltRotation)
|
|
{ /* We move */
|
|
tiltRotation = currentTilt;
|
|
printk("New tilt: %d\n", tiltRotation);
|
|
Cyio_PushEvent(CYEVENT_ORIENTATIONCHANGED, 1);
|
|
}
|
|
|
|
/* Update the timer */
|
|
io_btn_timer.expires = jiffies + HZ/8;
|
|
mod_timer(&io_btn_timer, io_btn_timer.expires);
|
|
DBG("<<%s()\n", __func__);
|
|
}
|
|
#endif
|
|
static void io_timer_handler(unsigned long nData)
|
|
{
|
|
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->bValid = 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;
|
|
u8 bDataValid = 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;
|
|
bDataValid = s_pEventR->bValid;
|
|
s_pEventR->nCode = 0;
|
|
--s_nEventCnt;
|
|
if (s_pEventR->nKeyEvent)
|
|
{
|
|
--s_nKeyLogCnt;
|
|
#ifdef CYIO_REPEAT
|
|
if (nData != CYEVENT_KEY_OFF)
|
|
{
|
|
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];
|
|
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
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!pIrq->bActive || gpio_get_value(pIrq->nGpio))
|
|
{
|
|
s_nPrevKey = 0;
|
|
}
|
|
break;
|
|
}
|
|
if (s_nPrevKey)
|
|
{
|
|
nData = s_nPrevKey | CYEVENT_KEY_REPEAT_FLAG;
|
|
s_bRepMode = 1;
|
|
}
|
|
}
|
|
#endif
|
|
spinUnlock(&io_lock);
|
|
|
|
if ((nData != 0) | (bDataValid == 1))
|
|
{
|
|
#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_pbUsbPowered))
|
|
{
|
|
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 != 0) | (bDataValid == 1))
|
|
{
|
|
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;
|
|
#ifdef CYIO_POLLING
|
|
s_nPio = s_nPoll_GEN4;
|
|
nPCnt = sizeof(s_nPoll_GEN4)/sizeof(s_nPoll_GEN4[0]);
|
|
#endif
|
|
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);
|
|
|
|
#ifdef CYIO_POLLING
|
|
rootDir = proc_mkdir(GPROCFS_MODULEFOLDER, proc_root_driver);
|
|
ioProcEntry = create_proc_entry(GPROCFS_IOFILE, 0644, rootDir);
|
|
ioProcEntry->read_proc = procReadIo;
|
|
ioProcEntry->write_proc = procWriteIo;
|
|
ioProcEntry->owner = THIS_MODULE;
|
|
#endif
|
|
|
|
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");
|
|
}
|
|
|
|
#ifdef CYIO_POLLING
|
|
static int procReadIo (char *page, char **start, off_t off, int count,
|
|
int *eof, void *data)
|
|
{
|
|
int len;
|
|
char tmp = 0;
|
|
//printk("Read IO Tilt [%d]", tiltRotation);
|
|
switch (tiltRotation)
|
|
{
|
|
case 270:
|
|
tmp++;
|
|
case 180:
|
|
tmp++;
|
|
case 90:
|
|
tmp++;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
len = sprintf (page, "%02X", tmp);
|
|
DBG("io readed value: %02X", tmp);
|
|
return len;
|
|
}
|
|
|
|
static int procWriteIo (struct file *file, const char *buffer,
|
|
unsigned long count, void *data)
|
|
{
|
|
char cmd;
|
|
/* in case of... */
|
|
if ( count < 1 )
|
|
return 0;
|
|
|
|
cmd = buffer[0];
|
|
switch(cmd)
|
|
{
|
|
case G_SENSOR_CAL:
|
|
case G_SENSOR_ON:
|
|
case G_SENSOR_OFF:
|
|
case 254:
|
|
break;
|
|
|
|
default:
|
|
printk(KERN_ERR "ProcIO: Unknown command '%c'\n",cmd);
|
|
break;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
// ---------------------------------------------------------------------------
|
|
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");
|
|
// ===========================================================================
|