module System.USB.HID.Parsers (
parseHIDDesc
,parsePhysDescSet
,parseHIDReportDesc
,parseInt
,convertEnum) where
import Data.Bits ((.&.),rotate,rotateR,testBit)
import Data.Attoparsec.ByteString (Parser,anyWord8,peekWord8',peekWord8,choice,(<?>),many')
import qualified Data.Attoparsec.ByteString as AB (take)
import System.USB.HID.Descriptor
import Data.Word (Word8)
import Data.ByteString (ByteString, unpack,pack)
import qualified Data.ByteString as B (zip)
import Control.Applicative ((<$>))
import Control.Monad (when, unless)
parseHIDDesc :: Parser HIDDescriptor
parseHIDDesc = do
hidDesc1 <- anyWord8
hidcd <- parseVersion
hidCC <- anyWord8
hidND <- anyWord8
hidDesc2 <- anyWord8
hidDescL <- anyWord8
hidDescT' <- peekWord8
hidDescL' <- peekWord8
return (HIDDescriptor hidDesc1
hidcd
hidCC
hidND
hidDesc2
(fromEnum hidDescL)
hidDescT'
hidDescL')
parseHIDReportDesc :: Parser HIDReportDesc
parseHIDReportDesc = HIDReport <$> many' parseReportItem
parsePhysicalDesc :: Parser HIDPhysicalDescriptor
parsePhysicalDesc = do
desig <- convertEnum <$> anyWord8
bq <- anyWord8
return (PD desig (convertEnum . sig3 $ bq) (convertEnum . lsig5 $ bq))
parsePhysDescSet :: Parser HIDPhysDescSet
parsePhysDescSet = do
pref <- anyWord8
sets <- many' parsePhysicalDesc
return (PDS (convertEnum . sig3 $ pref) (fromEnum . lsig5 $ pref) sets)
parseVersion :: Parser Version
parseVersion = do
ma <- anyWord8
mi <- anyWord8
return (V (binDec ma) (binDec mi))
binDec :: Word8 -> Int
binDec w = (t * 10) + s
where beS = 0xF0
s = fromEnum $ w .&. beS
t = fromEnum $ (rotate w 4) .&. beS
parseReportItem :: Parser HIDReportItem
parseReportItem = choice [HIDReportS <$> parseShortItem
,HIDReportL <$> parseLongItem
]
parseShortItem :: Parser ShortItem
parseShortItem = choice [Main <$> parseMain
,Global <$> parseGlobal
,Local <$> parseLocal
]
parseTop1 :: (Word8 -> Bool) -> String -> Parser a -> Parser a
parseTop1 test err parser = do
prefix <- peekWord8'
unless (test prefix) $
fail err
parser
parseLongItem :: Parser LongItem
parseLongItem = parseTop1 isLong "Not a Long Item" (return $ Long ())
parseMain :: Parser HIDMainTag
parseMain = parseTop1 isMain "Not main Type" $ choice [parseMainInput,parseCollection,parseEndCollection]
parseGlobal :: Parser HIDGlobalTag
parseGlobal = parseTop1 isGlobal "Not Global" $ choice [parseUsagePage, parseGlobalRest]
parseLocal :: Parser HIDLocalTag
parseLocal = parseTop1 isLocal "Not Local Tag" $ choice [parseUsage,parseDesignatorI,parseDelim,parseLocalRest]
parseTop2 :: (Word8 -> Bool) -> String -> (Word8 -> Parser a)
-> Parser a
parseTop2 test err parseCons = do
prefix <- anyWord8
unless (test . preTag $ prefix) $
fail err
parseCons (preDataL $ prefix)
parseDesignatorI :: Parser HIDLocalTag
parseDesignatorI = parseTop2 (== 3) "Not DesignatorI" ((DesignatorIndex <$>) . parseDesIData . fromEnum)
parseDelim :: Parser HIDLocalTag
parseDelim = parseTop2 (== 9) "Not Delimiter" ((Delimiter <$>) . parseDelimData . fromEnum)
parseDelimData :: Int -> Parser HIDDelimeter
parseDelimData n = toEnum <$> parseInt n
parseDesIData :: Int -> Parser HIDDesignator
parseDesIData n = toEnum <$> parseInt n
parseLocalRest :: Parser HIDLocalTag
parseLocalRest = do
prefix <- anyWord8
let int = parseInt (fromEnum . preDataL $ prefix)
case preTag prefix of
1 -> UsageMinimum <$> int
2 -> UsageMaximum <$> int
4 -> DesignatorMinimum <$> int
5 -> DesignatorMaximum <$> int
7 -> StringIndex <$> int
8 -> StringMinimum <$> int
9 -> StringMaximum <$> int
_ -> fail "Not a data Int Local"
parseUsage :: Parser HIDLocalTag
parseUsage = parseTop2 (== 0) "Not Usage Tag" ((Usage <$>) . parseUsageData . fromEnum)
parseUsageData :: Int -> Parser HIDUsage
parseUsageData n = U <$> parseInt n
parseUsagePage :: Parser HIDGlobalTag
parseUsagePage = parseTop2 isUsagePage "Not UsagePage" ((UsagePage <$>) . parseUsagePageData . fromEnum)
parseUsagePageData :: Int -> Parser HIDUsagePage
parseUsagePageData n = UP <$> parseInt n
parseInt :: Int -> Parser Int
parseInt n = byteStringToInt <$> AB.take n
parseGlobalRest :: Parser HIDGlobalTag
parseGlobalRest = do
prefix <- anyWord8
let int = parseInt (fromEnum . preDataL $ prefix)
case preTag prefix of
1 -> LogicalMinimum <$> int
2 -> LogicalMaximum <$> int
3 -> PhysicalMinimum <$> int
4 -> PhysicalMaximum <$> int
5 -> UnitExponent <$> int
6 -> Unit <$> int
7 -> ReportSize <$> int
8 -> ReportID <$> int
9 -> ReportCount <$> int
10 -> Push <$> int
11 -> Pop <$> int
_ -> fail "Not a data Int global"
byteStringToInt :: ByteString -> Int
byteStringToInt = foldl f 0 . B.zip (pack [0..])
where f a (b,c) = 2^(8* (fromEnum b)) * (fromEnum c) + a
parseMainInput :: Parser HIDMainTag
parseMainInput = do
prefix <- anyWord8
let mainData = parseMainData (preDataL prefix)
case preTag prefix of
8 -> Input <$> mainData
9 -> Output <$> mainData
11 -> Feature <$> mainData
_ -> fail "Incorrect Tag"
parseCollection :: Parser HIDMainTag
parseCollection = parseTop2 isCollection "Not Collection" ((Collection <$>) . parseCollectionData)
parseEndCollection :: Parser HIDMainTag
parseEndCollection = parseTop2 isEndCollection "Not End Collection" (const (return EndCollection))
parseCollectionData :: Word8 -> Parser HIDCollectionData
parseCollectionData w = do
tdata <- convertEnum <$> anyWord8
AB.take (fromEnum (w 1))
return tdata
parseMainData :: Word8 -> Parser HIDMainData
parseMainData n = do
let a = fromEnum (if n <= 2
then n
else 2)
as <- AB.take a
let bs = concatMap takingBits (unpack as) ++ (repeat False)
return (constructHIDMainD (map fromEnum (takeBits bs)))
takeBits :: [Bool] -> [Bool]
takeBits xs = take 7 xs ++ [xs !! 9]
isMain :: Word8 -> Bool
isMain w = preType w == 0
isGlobal :: Word8 -> Bool
isGlobal w = preType w == 1
isLocal :: Word8 -> Bool
isLocal w = preType w == 2
isLong :: Word8 -> Bool
isLong w = preTag w == 15
isUsagePage :: Word8 -> Bool
isUsagePage w = preTag w == 0
isCollection :: Word8 -> Bool
isCollection w = preTag w == 10
isEndCollection :: Word8 -> Bool
isEndCollection w = preTag w == 12
preType :: Word8 -> Word8
preType p = rotateR (p .&. typeM) 2
where typeM = 0x0C
preTag :: Word8 -> Word8
preTag p = rotateR (p .&. tagM) 4
where tagM = 0xF0
preDataL :: Word8 -> Word8
preDataL p = p .&. dataLengthM
where dataLengthM = 0x03
sig3 :: Word8 -> Word8
sig3 w = rotate (w .&. m) 3
where m = 0xE0
lsig5 :: Word8 -> Word8
lsig5 w = w .&. m
where m = 0x1F
convertEnum :: (Enum a, Enum b) => a -> b
convertEnum = toEnum . fromEnum
takingBits :: Word8 -> [Bool]
takingBits x = map (testBit x) [0..7]