--******************************************************************** --[[PSP HOMEBREW Nikkai 'Universal PDA Thumb Board': Driver for Sony 'PSP' --------------------------------------------------------- The Thumb board sends 4 or 5 byte packets as follows: All packets have header byte = 192 (0xc0), All packets have 1 or 2 status bytes following : Status byte A1 = 5 (0x05) Status byte A2 = 2 (0x02) Status byte B1 = 10 (0x0a) Following the status bytes are 0,1 or 2 Keycode bytes Key presses have codes in the range 160 - 254 (0xa0 - 0xfe) Key releases have codes in the range 32 - 126 (0x10 - 0x7e) All packets have End of Packet byte = 193 (0xc1) Up to 3 simultaneous key presses are handled. The 4th and subsequent presses are ignored. Initialisation = First Keypress = 2nd/3rd Keypress = 2nd/3rd Key release = First Key release = ]]-- --******************************************************************** local AUTHOR = 'Dangee' local LANGUAGE = 'Lua 5.1' local LICENSE = 'OpenSource,Homebrew,No Guarantee etc.' local VERSION = '1.51 (Alpha)' local VDATE = 'February \'08' --******************************************************************** --[[ global functions ^^^^^^^^^^^^^^^^ IKB.enable() : enable IKB read (string = ) IKB.read() : update IKB from keypad,return string IKB.disable() : disable IKB read required external functions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ System.irdaInit() System.irdaRead() string.len(string) string.char(byte) string.byte(string,[i]) string.lower(string) string.upper(string) ( = ) Timer.new() T1:reset([t]) T1:start() T1:stop() ]]-- --******************************************************************** --IKB definitions local ikd = {} --read states ikd.RUNCLR = 0 --unknown or undefined ikd.RCLEAR = 1 --no keypress ikd.RACTIV = 2 --keypress active --packet status ikd.G192 = 1 --waiting for packet header byte (192) ikd.STB1 = 2 --waiting for status byte 1 ikd.STB2 = 3 --waiting for status byte 2 ikd.G193 = 4 --waiting for keycode, or packet end byte (193) --packet byte types ikd.PHEAD = 192 --packet header byte ikd.PTAIL = 193 --end of packet byte ikd.ST1CL = 5 --single keypush status byte 1 ikd.ST2CL = 2 --single keypush status byte 2 ikd.ST1AC = 10 --multiple keypush/key release status byte --key sets ikd.LOWER = 1 --default key set (lower case) ikd.UPPER = 2 --shift/caps lock key set (upper case) --key repeat defaults ikd.R1DEF = 360 --time to first repeat ikd.RPDEF = 60 --time between repeats --output modes ikd.OCLR = 0 --no active keypress ikd.OBLK = 1 --output blocked ikd.CAPS = 2 --Caplock key active ikd.NRK1 = 3 --non-repeating key active ikd.NRK2 = 4 --NRK1+mode key active ikd.NRK3 = 5 --NRK1+2 mode keys active ikd.MOD1 = 6 --mode key active ikd.MOD2 = 7 --2 mode keys active ikd.MOD3 = 8 --3 mode keys active ikd.ALP1 = 9 --repeating key active ikd.ALPM = 10 --repeatingkey+mode key active ikd.ALM2 = 11 --repeating key+2 mode keys active --mode flags ikd.SHFF = 1 --Shift ikd.CTLF = 2 --Contrl ikd.ALTF = 4 --Alt --keymap index modifiers ikd.KCPSUB = 159 --key press modifier ikd.KCRSUB = 31 --key release modifier --mode combinations ikd.CSH = ikd.SHFF+ikd.CTLF --Contrl/Shift ikd.CSA = ikd.CSH+ikd.ALTF --Contrl/Shift/Alt --keycode ranges ikd.KCPMIN = 160 --minimum keypress code ikd.KCPMAX = 254 --maximum keypress code ikd.KCRMIN = 32 --minimum key release code ikd.KCRMAX = 126 --maximum key release code --aliases local U,T,F = nil,true,false --******************************************************************** --[[ Key mapping ----------- ASCII,or defined non-printing Keycodes are mapped from the keypress/key release codes sent from the thumb board IRDA port; The keymaps are indexed as follows: keymap 1: default (lower case) 2: Caps lock/Shift (upper case) keymap index: key press = (keypress code-159) key release = (keyrelease code-31) e.g. keypress code = 190, no Caplock,Shift active: index = keymap[1][190-159] (ENTER) ]]-- --Key codes (global defs.) KYUP,KYDN,KYLF,KYRT = 1,2,3,4 --arrow keys CAPLCK,SHIFT,ALT,BCKSP,TAB,SPACE = 5,6,7,8,9,32 ENTER,CTRL = 10,11 FN,CMD,SYM,CCL,DEL,DATE,PHON = 14,15,16,17,18,19,20 TODO,MEMO = 21,22 SHUP,SHDN,SHLF,SHRT = 23,24,25,26 --SHIFT+arrow CSUP,CSDN,CSLF,CSRT = 27,28,29,30 --CTRL+SHIFT+arrow CTRLX,CTRLC,CTRLV = 129,130,131 --CTRL keys CTRUP,CTRDN = 132,133 --CTRL+arrow CTRLF,CTRRT = 134,135 -- " " ALTUP,ALTDN = 136,137 --ALT+arrow ALTLF,ALTRT = 138,139 -- " " CTSHA = 254 --CTRL+SHIFT+ALT local IKmap = { [1] = { --Default (lowercase) keyset 112,111,49,51,0,0,0,SHIFT,0,0, 0,0,KYRT,KYDN,KYLF,DEL,SYM,46,44,109, 0,MEMO,KYUP,47,59,108,107,106,0,TODO, ENTER,39,0,0,105,117,PHON,92,93,91, CCL,48,57,56,DATE,BCKSP,61,45,110,98, 118,99,0,0,0,0,0,0,0,0, ALT,FN,0,0,0,0,0,0,0,CTRL, TAB,CAPLCK,SPACE,104,103,102,100,115, 97,120,96,121,116,114,101,119,113,CMD, 55,54,53,52,122,0,50 }, [2] = { --Shift keyset 80,79,33,35,0,0,0,SHIFT,0,0, 0,0,KYRT,KYDN,KYLF,DEL,SYM,62,60,77, 0,MEMO,KYUP,63,58,76,75,74,0,TODO, ENTER,34,0,0,73,85,PHON,124,125,123, CCL,41,40,42,DATE,BCKSP,43,95,78,66, 86,67,0,0,0,0,0,0,0,0, ALT,FN,0,0,0,0,0,0,0,CTRL, TAB,CAPLCK,SPACE,72,71,70,68,83, 65,88,126,89,84,82,69,87,81,CMD, 38,94,37,36,90,0,64 } } --******************************************************************** IKB = { --Properties nflg, -- on/off flag kstate, -- keypad read state bstate, -- packet status pkcode, -- packet keycode ncodes, -- keycode counter nkeys, -- no. of active keys keyset, -- key set Itime, -- IKB Timer rptflg, -- key repeat enable flag rept1, -- millisecs to first key repeat rpt, -- millisecs between key repeats rtime, -- time until next repeat activ = {}, -- active keypush table shift, -- SHIFT counter caplock, -- CAPLOCK flag oblock, -- output lock obyte, -- output byte obidx, -- output byte keymap index omode, -- output mode modef, -- mode flags obuf, -- output buffer jflag, -- key repeat lock flag --Methods: enable, -- enable and initialise IKB disable, -- disable IKB read, -- update,read,clear IKB output } --******************************************************************** -- local functions --Key classes --direction arrows local function hdirk(code) return code==KYLF or code==KYRT end local function vdirk(code) return code==KYUP or code==KYDN end local function dirk(code) return hdirk(code) or vdirk(code) end local function modk(code) --mode keys return code==ALT or code==SHIFT or code==CTRL end local function nork(code) --non-repeating keys return code==ENTER or code==CMD or code==SYM or code==CCL or code==DEL or code==DATE or code==PHON or code==TODO or code==MEMO or code==FN end local function nosrk(code) --repeating,non-shifting keys return code==BCKSP or code==SPACE end --CTRL activated keys local function ctrlk(code) local achar = string.lower(string.char(code)) if achar=='x' then return CTRLX end if achar=='c' then return CTRLC end if achar=='v' then return CTRLV end return 0 end --Resets local function mdrst() -- mode keys IKB.alt,IKB.shift,IKB.ctrl = F,0,F end --IKB reset local function rst(sflg) --sflg blocks keyset change IKB.bstate,IKB.kstate = ikd.G192,ikd.RUNCLR IKB.Itime:stop() ; IKB.Itime:reset(0) IKB.ncodes,IKB.nkeys = 0,0 if not sflg then IKB.keyset,IKB.caplock = ikd.LOWER,F end IKB.pckcode,IKB.obyte,IKB.obidx,IKB.oblock = 0,0,0,F IKB.omode,IKB.modef = ikd.OCLR,0 IKB.rptflg,IKB.rtime = F,0 for i = 1,3 do IKB.activ[i] = 0 end mdrst() end --******************************************************************** --IKB Functions --enable IKB IKB.enable = function() --Required System functions local ok1,err = pcall(System.irdaInit) local ok2,err = pcall(System.irdaRead) if not (ok1 and ok2) then return F end if IKB.nflg==U then --Initialise IKB System.irdaInit() IKB.Itime = Timer.new() IKB.rept1,IKB.rpt,IKB.nflg = ikd.R1DEF,ikd.RPDEF,F end if IKB.nflg==F then rst() ; IKB.nflg = T System.irdaRead() --flush irda port end end --disable IKB read IKB.disable = function() if IKB.nflg then IKB.nflg = F end end --======================================================== --IKB input handlers local function cod() --next byte expected is keycode/end of packet IKB.ncodes,IKB.bstate = 0,ikd.G193 end local function rptsw() --initialise key repeats IKB.rptflg,IKB.rtime = T,IKB.rept1 IKB.jflag = T IKB.Itime:start() end local function rptclr() --disable key repeats IKB.rptflg = F IKB.Itime:stop() ; IKB.Itime:reset(0) end local function newk() --keypush handler IKB.nkeys = IKB.nkeys+1 if IKB.nkeys>3 then rst() ; return end --ascii/control code of key push IKB.activ[IKB.nkeys] = IKB.pkcode local kmidx = IKB.pkcode-ikd.KCPSUB --keymap index local acode = IKmap[IKB.keyset][kmidx] if IKB.caplock then --caplock handler local achar = string.char(acode) if achar>='a' and achar<='z' then acode = string.byte(string.upper(achar)) end end if modk(acode) then --mode key flags if acode==SHIFT and IKB.shift<1 then IKB.modef = IKB.modef+ikd.SHFF end if acode==CTRL then IKB.modef = IKB.modef+ikd.CTLF end if acode==ALT then IKB.modef = IKB.modef+ikd.ALTF end end if IKB.oblock then if acode==SHIFT then IKB.shift = IKB.shift+1 if IKB.omode~=ikd.ALP1 and IKB.omode~=ikd.ALPM then return end if IKB.omode==ikd.ALPM and IKB.shift==2 then IKB.omode = ikd.ALS2 end if IKB.omode==ikd.ALP1 then IKB.omode = ikd.ALPM if not nosrk(IKB.obyte) and not dirk(IKB.obyte) then IKB.obyte = IKmap[ikd.UPPER][IKB.obidx] end if dirk(IKB.obyte) then IKB.obyte = SHUP+(IKB.obyte-KYUP) end end else return end else if IKB.nkeys>1 and acode==CAPLCK then IKB.oblock,IKB.omode = T,ikd.OBLK ; return end if IKB.nkeys==1 then if acode==CAPLCK then IKB.oblock,IKB.omode = T,ikd.CAPS ; return end if modk(acode) then IKB.omode = ikd.MOD1 ; return end if nork(acode) then IKB.oblock,IKB.omode = T,ikd.NRK1 IKB.obyte = acode else --repeating key IKB.oblock,IKB.omode = T,ikd.ALP1 IKB.obyte = acode ; rptsw() end end if IKB.nkeys==2 then if IKB.omode==ikd.MOD1 then if modk(acode) then IKB.omode = ikd.MOD2 ; return end if nork(acode) then IKB.oblock = T if IKB.modef==ikd.SHFF then IKB.omode,IKB.obyte = ikd.NRK2,acode else IKB.omode = ikd.OBLK ; return end else --mode key+repeating key IKB.oblock,IKB.omode = T,ikd.ALPM if IKB.modef==ikd.SHFF then if dirk(acode) then IKB.obyte = SHUP+(acode-KYUP) else IKB.obyte = IKmap[ikd.UPPER][kmidx] end rptsw() end if IKB.modef==ikd.ALTF and dirk(acode) then IKB.obyte = ALTUP+(acode-KYUP) ; rptsw() end if IKB.modef==ikd.CTLF then if dirk(acode) then IKB.obyte = CTRUP+(acode-KYUP) ; rptsw() else local ck = ctrlk(acode) if ck>0 then IKB.obyte = ck end end end end end end if IKB.nkeys==3 then IKB.oblock = T if IKB.omode==ikd.MOD2 then if modk(acode) then if IKB.modef==ikd.CSA then IKB.omode,IKB.obyte = ikd.MOD3,CTSHA end end if IKB.shift==2 then if nork(acode) then IKB.omode,IKB.obyte = ikd.NRK3,acode else --repeating key IKB.oblock,IKB.omode = T,ikd.ALS2 if dirk(acode) then IKB.obyte = SHUP+(acode-KYUP) else IKB.obyte = IKmap[ikd.UPPER][kmidx] end end end if IKB.modef==ikd.CSH then if hdirk(acode) then IKB.omode = ikd.CSDK IKB.obyte = CSLF+(acode-KYLF) rptsw() end end if IKB.obyte==0 then IKB.omode = ikd.OBLK end end end end if IKB.obyte>0 then IKB.obidx = kmidx IKB.obuf = IKB.obuf..string.char(IKB.obyte) end end local function oldk() --key release handler local ac,aci = {} for i = 1,IKB.nkeys do ac[i] = IKmap[IKB.keyset][IKB.activ[i]-ikd.KCPSUB] end if IKB.nkeys==1 then if modk(ac[1]) then IKB.omode,IKB.oblock = ikd.MOD1,F IKB.obyte = 0 ; rptclr() ; return end if IKB.omode==ikd.NRK2 then IKB.omode,IKB.obyte = ikd.OBLK,0 ; return end if IKB.omode==ikd.ALP1 then return end if IKB.omode==ikd.ALPM then if dirk(ac[1]) then IKB.omode,IKB.obyte = ikd.ALP1,ac[1] return end if nosrk(ac[1]) then IKB.omode = ikd.ALP1 else IKB.omode,IKB.obyte = ikd.OBLK,0 rptclr() end return end end if IKB.nkeys==2 then if modk(ac[1]) and modk(ac[2]) then IKB.omode,IKB.oblock = ikd.MOD2,F IKB.obyte = 0 ; rptclr() ; return end if IKB.omode==ikd.NRK3 then IKB.omode,IKB.obyte = ikd.OBLK,0 ; return end if IKB.omode==ikd.ALS2 then IKB.omode = ikd.ALPM ; return end if IKB.omode==ikd.CSDK then IKB.omode,IKB.obyte = ikd.OBLK,0 rptclr() ; return end end IKB.oblock,IKB.omode,IKB.obyte = T,ikd.OBLK,0 end local function bythandl(by) --serial byte read handler if IKB.bstate==ikd.G192 then --packet header if by~=ikd.PHEAD then rst() else IKB.bstate = ikd.STB1 end return end if IKB.bstate==ikd.STB1 then --packet status byte 1 if IKB.kstate==ikd.RACTIV then if by~=ikd.ST1AC then rst() else cod() end else if by==ikd.ST1CL then IKB.bstate,IKB.kstate = ikd.STB2,ikd.RCLEAR else rst() end end return end if IKB.bstate==ikd.STB2 then --packet status byte 2 if by~=ikd.ST2CL then rst() else cod() end ; return end if IKB.bstate==ikd.G193 then --keycode or end of packet if by==ikd.PTAIL then --end of packet IKB.bstate = ikd.G192 if IKB.ncodes==0 or IKB.ncodes>2 then rst() ; return end if IKB.kstate==ikd.RCLEAR then if IKB.ncodes==2 then rst() ; return end IKB.kstate = ikd.RACTIV newk() else if IKB.pkcode>=ikd.KCPMIN then --keypress packet handler newk() else --key release packet handler local kpc = IKB.pkcode+128 local acode = IKmap[IKB.keyset][kpc-ikd.KCPSUB] --remove key from active table local aiq for i = 1,4 do if i==4 then rst() ; return end if IKB.activ[i]==kpc then if i<3 then aiq = IKB.activ[i] IKB.activ[i] = IKB.activ[i+1] IKB.activ[i+1] = aiq else IKB.activ[i] = 0 ; break end end end IKB.nkeys = IKB.nkeys-1 if modk(acode) then --update mode key flags if acode==SHIFT then IKB.shift = IKB.shift-1 if IKB.shift<1 then IKB.modef = IKB.modef-ikd.SHFF end end if acode==CTRL then IKB.modef = IKB.modef-ikd.CTLF end if acode==ALT then IKB.modef = IKB.modef-ikd.ALTF end end if IKB.nkeys==0 then if IKB.omode~=ikd.OBLK then if acode==CAPLCK then IKB.caplock = not IKB.caplock end end IKB.omode,IKB.oblock = ikd.OCLR,F IKB.obyte = 0 ; rptclr() else oldk() end if IKB.ncodes==2 then rst(T) end end end else --keycode byte IKB.ncodes = IKB.ncodes+1 if IKB.ncodes>2 then rst() ; return end if IKB.kstate==ikd.RCLEAR then if IKB.ncodes>1 or byikd.KCPMAX then rst() ; return end end if (byikd.KCPMAX) or (by>ikd.KCRMAX and by0 then for i = 1,nbyt do bythandl(string.byte(inbuf,i)) end end --key repeat handler if (not IKB.jflag) and IKB.kstate==ikd.RACTIV and IKB.rptflg and IKB.obyte>0 then local ptime = IKB.Itime:reset(0) IKB.Itime:start() local airt = IKB.rtime-ptime if airt<=0 then IKB.obuf = IKB.obuf..string.char(IKB.obyte) IKB.rtime = IKB.rpt else IKB.rtime = airt end end IKB.jflag = F return IKB.obuf end --******************************************************************** --[[ Change Log ========== Version 1.0 (Alpha) March '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - First Release. Version 1.1 (Alpha) April '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Added Change Log - Keymap corrections : ('") and (>,) keys - code condensed Version 1.2 (Alpha) May '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Caps Lock handler changed : only affects alpha keys Version 1.3 (Alpha) July '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- changed ON/OFF to T/F -- check for required irda functions before IKB enable -- flush irda port in IKB.enable() -- IKB.nflg enables IKB.read() -- Improved shift & mode key combination handling -- removed property IKB.kshf,SHMOD/CLMOD -- removed properties IKB.ctrl,IKB.alt -- new properties,IKB.omode,IKB.obidx,IKB.modef -- rewrite newk() for key press -- added oldk() for key release -- added key class: repeating/non shifting keys -- implemented SHIFT+arrows (repeating/non-blocking) -- implemented CTRL+arrows (non-repeating) Version 1.35 (Alpha) July '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- changed local DATE in header to local VDATE -- (clash with global DATE key identifier) -- implemented ALT+arrows (repeating) -- changed CTRL+arrows to repeating Version 1.36 (Alpha) September '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- put local constants into table (ikd) to reduce -- no. of upvalues Version 1.4 (Alpha) November '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- put key repeat handler after byte handler and -- Added jflag, to fix spurious key repeat. Version 1.5 (Alpha) November '07 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- de-nested all local functions to ease stack pressure -- changed obuf and jflag from locals to IKB properties Version 1.51 (Alpha) February '08 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --use pcall() to test Irda functions in IKB.enable ]]-- --********************************************************************