packages feed

usb-hid (empty) → 0.1.0.0

raw patch · 6 files changed

+649/−0 lines, 6 filesdep +attoparsecdep +basedep +bytestringsetup-changed

Dependencies added: attoparsec, base, bytestring, usb

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Paul Bennett++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Paul Bennett nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ System/USB/HID/Descriptor.hs view
@@ -0,0 +1,202 @@+{-# LANGUAGE DeriveGeneric , AutoDeriveTypeable  #-}+-- | Notice that 'LongItem's are not supported as none are specified on the <http://www.usb.org/developers/hidpage/ USB Human Interface device page>+module System.USB.HID.Descriptor where +++import Data.ByteString as B+import Data.Word +import GHC.Generics++data HIDDescriptor = HIDDescriptor +    {hIDDescriptorType :: !Word8,+     hIDcdHID :: !Version,+     hIDCountryCode :: !Word8,+     hIDNumDesc :: !Word8,+     hIDDescType :: !Word8,+     hIDDescLength :: !Int,+     hIDDescType' :: !(Maybe Word8),+     hIDDescLength' :: !(Maybe Word8)+    } deriving (Show,Eq,Generic)++newtype HIDReportDesc = HIDReport [HIDReportItem]+                 deriving (Eq,Show,Generic)++data HIDPhysicalDescriptor = PD !HIDDesignator !HIDQualifier !HIDEffort+                             deriving (Eq,Show,Generic)++data HIDReportItem = HIDReportS !ShortItem +                   | HIDReportL !LongItem+                     deriving (Eq)++instance Show HIDReportItem where +    show (HIDReportS x) = show x+    show (HIDReportL x) = show x ++data ShortItem = Main !HIDMainTag+               | Global !HIDGlobalTag+               | Local !HIDLocalTag+                 deriving (Eq,Generic)++instance Show ShortItem where +    show (Main x) = show x+    show (Global x) = show x +    show (Local x) = show x++newtype LongItem = Long ()+                deriving (Eq,Show,Generic)++data HIDMainTag = Input !HIDMainData +                | Output !HIDMainData +                | Feature !HIDMainData+                | Collection !HIDCollectionData+                | EndCollection+                  deriving (Show,Eq,Generic)++--Name these more meaningfully+data HIDMainData = HIDMainData +    {bit0 :: !HDMBit0,+     bit1 :: !HDMBit1,+     bit2 :: !HDMBit2,+     bit3 :: !HDMBit3,+     bit4 :: !HDMBit4,+     bit5 :: !HDMBit5,+     bit6 :: !HDMBit6,+     bit8 :: !HDMBit8+    } deriving (Show,Eq,Generic)++constructHIDMainD :: [Int] -> HIDMainData+constructHIDMainD [a,b,c,d,e,f,g,h] = HIDMainData (toEnum a) (toEnum b) (toEnum c) (toEnum d) (toEnum e) (toEnum f) (toEnum g) (toEnum h)++data HDMBit0 = Data | Constant+               deriving (Eq,Show,Enum,Generic)+data HDMBit1 = Array | Variable+               deriving (Eq,Show,Enum,Generic)+data HDMBit2 = Absolute | Relative+               deriving (Eq,Show,Enum,Generic)+data HDMBit3 = NoWrap | Wrap+               deriving (Eq,Show,Enum,Generic)+data HDMBit4 = Linear | NonLinear+               deriving (Eq,Show,Enum,Generic)+data HDMBit5 = PreferredState | NoPreferred+               deriving (Eq,Show,Enum,Generic)+data HDMBit6 = NoNullPosition | NullState+               deriving (Eq,Show,Enum,Generic)+data HDMBit8 = BitField | BufferedBytes+               deriving (Eq,Show,Enum,Generic)++data HIDCollectionData = Physical +                       | Application+                       | Logical+                       | Report+                       | NamedArray +                       | UsageSwitch+                       | UsageModifier+                         deriving (Eq,Show,Enum,Generic)++data HIDGlobalTag = UsagePage !HIDUsagePage+                  | LogicalMinimum !Int+                  | LogicalMaximum !Int+                  | PhysicalMinimum !Int+                  | PhysicalMaximum !Int+                  | UnitExponent !Int+                  | Unit !Int+                  | ReportSize !Int+                  | ReportID !Int+                  | ReportCount !Int+                  | Push !Int+                  | Pop !Int +                    deriving (Show,Eq,Generic)++data HIDLocalTag = Usage !HIDUsage+                 | UsageMinimum !Int+                 | UsageMaximum !Int+                 | DesignatorIndex !HIDDesignator+                 | DesignatorMinimum !Int+                 | DesignatorMaximum !Int+                 | StringIndex !Int+                 | StringMinimum !Int+                 | StringMaximum !Int+                 | Delimiter !HIDDelimeter+                   deriving (Show,Eq,Generic)++data HIDDelimeter = Open | Close+                    deriving (Show,Eq,Enum,Generic)++newtype HIDUsagePage = UP Int+    deriving (Show,Eq,Generic)+             +newtype HIDUsage = U Int+    deriving (Show,Eq,Generic)++data HIDDesignator = None+                   | Hand+                   | Eyeball+                   | Eyebrow+                   | Eyelid+                   | Ear+                   | Nose+                   | Mouth+                   | UpperLip+                   | LowerLip+                   | Jaw+                   | Neck+                   | UpperArm+                   | Elbow+                   | Forearm+                   | Wrist+                   | Palm+                   | Thumb+                   | IndexFinger+                   | MiddleFinger+                   | RingFinger+                   | LittleFinger+                   | Head+                   | Shoulder+                   | Hip+                   | Waist+                   | Thigh+                   | Knee+                   | Calf+                   | Ankle+                   | Foot+                   | Heel+                   | BallOfFoot+                   | BigToe+                   | SecondToe+                   | ThirdToe+                   | FourthToe+                   | LittleToe+                   | Brow+                   | Cheek+                     deriving (Show,Eq,Enum,Generic)++data HIDQualifier = QNotApplicable+                  | RightSide+                  | LeftSide+                  | BothSides+                  | Either+                  | Center+                    deriving (Eq,Show,Enum,Generic)++data HIDBias = BNotApplicable+             | RightHand+             | LeftHand+             | BothHands+             | EitherHand+               deriving (Eq,Show,Enum,Generic)++data HIDPhysDescSet = PDS !HIDBias !HIDPreference ![HIDPhysicalDescriptor]+                    deriving (Eq,Show,Generic)++type HIDEffort = Int++type HIDPreference = Int+++data Version = V {main :: !Int,+                  minor :: !Int+                 }+             deriving (Eq,Generic)++instance Show Version where +    show (V ma mi) = (show ma) ++ "." ++ (show mi)
+ System/USB/HID/Parsers.hs view
@@ -0,0 +1,252 @@+module System.USB.HID.Parsers (-- * Top Level Parsers+                               -- All "Data.Attoparsec" parsers+                               parseHIDDesc+                              ,parsePhysDescSet+                              ,parseHIDReportDesc+                               -- ** Second Level Parsers+                              ,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)++-- | Parse a 'HIDDescriptor'+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')++-- | Parse a 'HIDReportDesc'+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))++-- | Parse a 'HIDPhysDescSet' +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 n@ parses an @Int@ of length encoded in @n@ 'Word8''s+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]
+ System/USB/HID/Requests.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE DeriveGeneric , AutoDeriveTypeable  #-}+-- consider writing parsers for physical descriptors+-- write documentation for all.+module System.USB.HID.Requests (-- * Request Types +                                HIDDescriptorClass+                               ,HIDControlRequest+                               ,HIDProtocol+                               ,DescriptorIndex+                               ,HIDInterfaceNumber+                               ,Duration+                               ,ReportID+                               ,Report+                               ,ReportType +                                -- * HID Requests+                                -- | These are all specified in the HID Specification Section 7+                               ,getHIDReportDesc+                               ,getPhysicalDescriptor+                               ,getReport+                               ,setReport+                               ,getIdle+                               ,setIdle+                               ,getProtocol+                               ,setProtocol+                               ) where+import GHC.Generics+import Data.Word+import System.USB+import Data.Attoparsec.ByteString+import System.USB.HID.Descriptor+import System.USB.HID.Parsers+import Data.ByteString as B (ByteString,empty,head)+import Data.Maybe (fromJust)+import Data.Tuple (swap)++data HIDDescriptorClass = HIDR+                        | HIDReportR +                        | PhysicalDescriptorR+                          deriving (Eq,Show,Generic)++data HIDControlRequest = GetReport +                       | GetIdle +                       | GetProtocol +                       | SetReport +                       | SetIdle +                       | SetProtocol+                         deriving (Eq,Show,Generic)++data HIDProtocol = Boot | Report +                   deriving (Eq, Show,Enum,Generic)++instance Enum HIDControlRequest where + toEnum x = fromJust (lookup x (zip [1,2,3,9,10,11] [GetReport, GetIdle, GetProtocol, SetReport, SetIdle, SetProtocol]))+ fromEnum x = fromJust (lookup x (zip [GetReport, GetIdle, GetProtocol, SetReport, SetIdle, SetProtocol] [1,2,3,9,10,11]))++type DescriptorIndex = Word8++type HIDInterfaceNumber = Word16++type Duration = Word8++type ReportID = Word8++type Report = ByteString++data ReportType = RInput | ROutput | RFeature+                  deriving (Eq,Show,Generic)++instance Enum ReportType where +    toEnum x = fromJust (lookup x as)+        where as = [(1,RInput),(2,ROutput),(3,RFeature)]+    fromEnum x = fromJust (lookup x (map swap as))+        where as = [(1,RInput),(2,ROutput),(3,RFeature)]++instance Enum HIDDescriptorClass where +    toEnum 0x21 = HIDR+    toEnum 0x22 = HIDReportR +    toEnum 0x23 = PhysicalDescriptorR+    fromEnum HIDR = 0x21+    fromEnum HIDReportR = 0x22+    fromEnum PhysicalDescriptorR = 0x23++getHIDDesc :: DeviceHandle -> Parser a -> HIDControlRequest -> HIDDescriptorClass -> DescriptorIndex -> HIDInterfaceNumber -> Size -> Timeout -> IO a +getHIDDesc h parser cr dc di intN s t = do +  (bs,s) <- readControl h (ControlSetup Class ToInterface (convertEnum $ cr) ((convertEnum $ dc )*256 + (convertEnum $ di)) intN) s t+  case parseOnly parser bs of +    Left x -> fail "Could not Parse descriptor"+    Right x -> return x++-- | Performs 'Class' request for the HID report descriptor using the Control pipe+getHIDReportDesc :: DeviceHandle -> HIDInterfaceNumber -> Size -> Timeout -> IO HIDReportDesc+getHIDReportDesc h intN = getHIDDesc h parseHIDReportDesc GetReport HIDReportR 0 intN +++-- | Performs 'Class' request for a HID Physical Descriptor set using the Control pipe+getPhysicalDescriptor :: DeviceHandle -> HIDInterfaceNumber -> DescriptorIndex -> Size -> Timeout -> IO HIDPhysDescSet+getPhysicalDescriptor h intN di = getHIDDesc h parsePhysDescSet GetReport PhysicalDescriptorR di intN ++-- | The GetReport request allows the host to receive a report via the Control pipe.++getReport :: DeviceHandle -> ReportType -> ReportID -> HIDInterfaceNumber -> Size -> Timeout -> IO (ByteString,Status)+getReport h rt ri intN s t = hidGet h (convertEnum $ SetReport) ((convertEnum $ rt )*256 + (convertEnum $ ri)) intN s t++-- | The SetReport request allows the host to send a report to the device, possibly setting the state of input, output, or feature controls.++setReport :: DeviceHandle -> ReportType -> ReportID -> HIDInterfaceNumber -> Report -> Timeout -> IO (Size,Status)+setReport h rt ri intN r t = hidSet h (convertEnum $ SetReport) ((convertEnum $ rt )*256 + (convertEnum $ ri)) intN r t++-- | The GetIdle request reads the current idle rate for a particular Input report++getIdle :: DeviceHandle -> HIDInterfaceNumber -> ReportID -> Timeout -> IO (HIDProtocol,Status)+getIdle h intN rid t = do +  (p,s) <- hidGet h (convertEnum $ GetIdle) (convertEnum $ rid) intN 1 t+  let i = fromEnum (B.head p)+  return (toEnum i ,s)++-- | The SetIdle request silences a particular report on the Interrupt In pipe until a new event occurs or the specified amount of time passes.++setIdle :: DeviceHandle -> HIDInterfaceNumber -> Duration -> ReportID -> Timeout -> IO (Size,Status)+setIdle h intN d rid = hidSet h (convertEnum $ SetIdle) ((convertEnum $ d )*256 + (convertEnum $ rid)) intN empty++-- | The GetProtocol request reads which protocol is currently active (either the boot protocol or the report protocol.)++getProtocol :: DeviceHandle -> HIDInterfaceNumber -> Timeout -> IO (HIDProtocol,Status)+getProtocol h intN t = do +  (p,s) <- hidGet h (convertEnum $ GetProtocol) 0 intN 1 t+  let i = fromEnum (B.head p)+  return (toEnum i ,s)++-- | The SetProtocol switches between the boot protocol and the report protocol (or vice versa).++setProtocol :: DeviceHandle -> HIDProtocol -> HIDInterfaceNumber -> Timeout -> IO (Size,Status)+setProtocol h p intN = hidSet h (convertEnum $ SetProtocol) (convertEnum $ p) intN empty++hidSet :: DeviceHandle -> Word8 -> Word16 -> Word16 -> ByteString ->Timeout -> IO (Size,Status)+hidSet h r v = writeControl h . ControlSetup Class ToInterface r v++hidGet :: DeviceHandle -> Word8 -> Word16 -> Word16 -> Size -> Timeout -> IO (ByteString,Status)+hidGet h r v = readControl h . ControlSetup Class ToInterface r v
+ usb-hid.cabal view
@@ -0,0 +1,25 @@+-- Initial usb-hid.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++name:                usb-hid+version:             0.1.0.0+synopsis:            Parser and request Library for USB HIDs+-- description:         +homepage:            https://github.com/mapinguari/usb-hid+license:             BSD3+license-file:        LICENSE+author:              Paul Bennett+maintainer:          paul.mic.bennett@gmail.com+-- copyright:           +category:            System+build-type:          Simple+-- extra-source-files:  +cabal-version:       >=1.10++library+  exposed-modules:     System.USB.HID.Parsers, System.USB.HID.Requests, System.USB.HID.Descriptor+  -- other-modules:       +  other-extensions:    DeriveGeneric, AutoDeriveTypeable+  build-depends:       base >=4.7 && <4.8, attoparsec >=0.12 && <0.13, bytestring >=0.10 && <0.11, usb >=1.3 && <1.4+  -- hs-source-dirs:      +  default-language:    Haskell2010