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

1266 lines
32 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 <asm/arch/regs-gpio.h>
#include <asm/arch/regs-gpioj.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>
#include "asm/io.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_ERR str "\n"); }
#define DBG(str, ...) { printk(KERN_ERR 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)
{
int ret = -EINVAL;
unsigned long value;
switch(cmd)
{
default:
printk(KERN_ERR "Bad IOCTL\n");
ret = 0x42;
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: 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 IO Manager");
MODULE_VERSION("3.0");
// ===========================================================================