// =========================================================================== // cyio.c // Copyright (C) 2008-2010 Bookeen - All rights reserved // =========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #define CYIO_TIMER //#define CYIO_REPEAT //#define CYIO_POLLING //#define CYIO_ALTERNATE_KEY //#define DEBUG_MESSAGES //#define DBG_IRQ #include #include #include #include #include #include #include #include #include #include #include // =========================================================================== 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; inCode = 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; inGpio, 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; ibActive = !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; inCodeActive) { 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; ibActive && 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; inIrq; 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; inIrq); 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; inGpio); 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; inCodeActive != 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("<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 "); MODULE_DESCRIPTION("Cybook Event Manager"); MODULE_VERSION("3.0"); // ===========================================================================