{-# LANGUAGE UnicodeSyntax
, NoImplicitPrelude
, DeriveDataTypeable
, KindSignatures
, RankNTypes
, MultiParamTypeClasses
, FunctionalDependencies
, TypeSynonymInstances
, FlexibleContexts
, UndecidableInstances
, GADTs
, EmptyDataDecls
, CPP
#-}--------------------------------------------------------------------------------- |-- Module : System.USB.Safe-- Copyright : (c) 2009–2010 Bas van Dijk-- License : BSD3 (see the file LICENSE)-- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>---- This modules provides the following guarantees for working with USB devices:---- * You can't reference handles to devices that are closed. In other words: no-- I/O with closed handles is possible.---- * The programmer can specify the /region/ in which devices should remain-- open. On exit from the region the opened devices will be closed-- automatically.---- * You can't reference handles to configurations that have not been set.---- * You can't reference handles to interfaces that have not been claimed.---- * Just like with devices, the programmer can specify the region in which-- interfaces should remain claimed. On exit from the region the claimed-- interfaces will be released automatically.---- * You can't reference handles to alternates that have not been set.---- * You can't reference endpoints that don't belong to a setted alternate.---- * You can't read from an endpoint with an 'Out' transfer direction.---- * You can't write to an endpoint with an 'In' transfer direction.---- * You can't read from or write to endpoints with the unsupported transfer-- types 'Control' and 'Isochronous'. Only I/O with endpoints with the-- supported 'Bulk' and 'Interrupt' transfer types is allowed.---- This modules makes use of a technique called /Lightweight monadic regions/-- invented by Oleg Kiselyov and Chung-chieh Shan---- See: <http://okmij.org/ftp/Haskell/regions.html#light-weight>---- This technique is implemented in the @regions@ package of which the-- @Control.Monad.Trans.Region@ module is re-exported by this module.---- See the @usb-safe-examples@ package for examples how to use this library:---- @darcs get@ <http://code.haskell.org/~basvandijk/code/usb-safe-examples>----------------------------------------------------------------------------------moduleSystem.USB.Safe(-- * USB devices as scarce resources{-|
Note that this module re-exports the @Control.Monad.Trans.Region@ module
from the @regions@ package which allows you to:
* Run regions using 'runRegionT'.
* Concurrently run /top-level/ regions inside another region using
'forkTopRegion'.
* Duplicate a 'RegionalDeviceHandle' to a parent region using 'dup'.
-}moduleControl.Monad.Trans.Region-- ** Regional device handles,RegionalDeviceHandle,openDevice,withDevice,withDeviceWhich,getDevice-- * Getting descriptors,GetDescriptor(getDesc)-- * Resetting devices,resetDevice-- * Configurations,Config,getConfigs-- ** Setting configurations,ConfigHandle,setConfig,SettingAlreadySet(SettingAlreadySet),useActiveConfig,NoActiveConfig(NoActiveConfig),setConfigWhich-- * Interfaces,Interface,getInterfaces-- ** Claiming interfaces,RegionalInterfaceHandle,claim,withInterface,withInterfaceWhich-- * Alternates,Alternate,getAlternates-- ** Setting alternates,AlternateHandle,setAlternate,useActiveAlternate,setAlternateWhich-- * Endpoints,Endpoint,getEndpoints,clearHalt-- *** Transfer directions,TransferDirection(..),Out,In-- *** Transfer types,TransferType(..),Control,Isochronous,Bulk,Interrupt-- * Endpoint I/O,ReadAction,WriteAction,readEndpoint,writeEndpoint,enumReadEndpoint-- ** Control transfers,ControlAction,RequestType(..),control,readControl,readControlExact,writeControl-- * String descriptors,getLanguages,getStrDesc,getStrDescFirstLang-- * USB kernel drivers,kernelDriverActive,detachKernelDriver,attachKernelDriver,withDetachedKernelDriver)where---------------------------------------------------------------------------------- Imports---------------------------------------------------------------------------------- from base:importPrelude(fromInteger)importControl.Concurrent.MVar(MVar,newMVar,takeMVar,putMVar,withMVar)importControl.Monad(Monad,return,(>>=),fail,(>>),when,liftM)importControl.Exception(Exception,throwIO)importData.Typeable(Typeable)importData.Function(($))importData.Word(Word8)importData.Int(Int)importData.Char(String)importData.Bool(Bool(True,False))importData.List(map,head,filter,find)importData.Maybe(Maybe(Nothing,Just),fromJust)importSystem.IO(IO)importText.Show(Show)-- from base-unicode-symbols:importData.Bool.Unicode((∧))importData.Eq.Unicode((≡))importData.Function.Unicode((∘))-- from bytestring:importData.ByteString(ByteString)-- from transformers:importControl.Monad.IO.Class(MonadIO,liftIO)-- from MonadCatchIO-transformers:importControl.Monad.CatchIO(MonadCatchIO,bracket_,throw,block)-- from iteratee:importData.Iteratee.Base(EnumeratorGM)importData.Iteratee.Base.StreamChunk(ReadableChunk)-- from regions:importControl.Monad.Trans.Region.OnExit(CloseHandle,onExit)importControl.Monad.Trans.Region-- (re-exported entirely)-- from usb:importqualifiedSystem.USB.InitializationasUSB(Ctx)importqualifiedSystem.USB.EnumerationasUSB(Device,getDevices,deviceDesc)importqualifiedSystem.USB.DeviceHandlingasUSB(DeviceHandle,openDevice,closeDevice,getDevice,getConfig,setConfig,InterfaceNumber,claimInterface,releaseInterface,setInterfaceAltSetting,clearHalt,resetDevice,kernelDriverActive,detachKernelDriver,attachKernelDriver)importqualifiedSystem.USB.DescriptorsasUSB(DeviceDesc,deviceConfigs,ConfigDesc,configValue,configInterfaces,Interface,InterfaceDesc,interfaceNumber,interfaceAltSetting,interfaceEndpoints,EndpointDesc,endpointAddress,endpointAttribs,EndpointAddress,transferDirection,TransferDirection(In,Out),TransferType(Control,Isochronous,Bulk,Interrupt),getLanguages,LangId,StrIx,getStrDesc,getStrDescFirstLang)importqualifiedSystem.USB.IO.SynchronousasUSB(Timeout,TimedOut,Size,RequestType(Class,Vendor),Recipient,Request,Value,Index,control,readControl,readControlExact,writeControl,readBulk,readInterrupt,writeBulk,writeInterrupt)importqualifiedSystem.USB.IO.StandardDeviceRequestsasUSB(getInterfaceAltSetting)importqualifiedSystem.USB.ExceptionsasUSB(USBException(..))#ifdef __HADDOCK__importSystem.USB.Descriptors(maxPacketSize,endpointMaxPacketSize)#endif-- from usb-enumerator:importqualifiedSystem.USB.IO.Synchronous.EnumeratorasUSB(enumReadBulk,enumReadInterrupt)---------------------------------------------------------------------------------- ** Regional device handles--------------------------------------------------------------------------------{-| A regional handle to an opened USB device.
A regional handle to an opened USB device can be created by applying
'openDevice' or 'withDevice' to the USB device you wish to open.
Note that you can also /duplicate/ a regional device handle by applying 'dup' to
it.
-}dataRegionalDeviceHandle(r∷*→*)=RegionalDeviceHandle(USB.DeviceHandle)(MVarBool)(CloseHandler)instanceDupRegionalDeviceHandlewheredup(RegionalDeviceHandlehmvch)=liftM(RegionalDeviceHandlehmv)$dupch{-| Open a device and obtain a regional device handle. The device is
automatically closed when the region terminates.
This is a non-blocking function; no requests are sent over the bus.
Exceptions:
* 'USB.NoMemException' if there is a memory allocation failure.
* 'USB.AccessException' if the user has insufficient permissions.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}openDevice∷MonadCatchIOpr⇒USB.Device→RegionTspr(RegionalDeviceHandle(RegionTspr))openDevicedev=block$doh←liftIO$USB.openDevicedevmv←liftIO$newMVarFalsech←onExit$USB.closeDevicehreturn$RegionalDeviceHandlehmvch{-| Convenience function which opens the device, applies the given continuation
function to the resulting regional device handle and runs the resulting region.
-}withDevice∷MonadCatchIOpr⇒USB.Device→(∀s.RegionalDeviceHandle(RegionTspr)→RegionTsprα)→prαwithDevicedevf=runRegionT$openDevicedev>>=f{-| Convenience function which finds the first device attached to the system
which satisfies the given predicate on its descriptor, then opens that device
and applies the given continuation function to the resulting device handle.
Exceptions:
* 'USB.NotFoundException' if no device is found which satisfies the given predicate.
* 'USB.NoMemException' if there is a memory allocation failure.
* 'USB.AccessException' if the user has insufficient permissions.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}withDeviceWhich∷∀prα.MonadCatchIOpr⇒USB.Ctx→(USB.DeviceDesc→Bool)-- ^ Predicate on the device descriptor.→(∀s.RegionalDeviceHandle(RegionTspr)→RegionTsprα)-- ^ Continuation function→prαwithDeviceWhichctxpf=dodevs←liftIO$USB.getDevicesctxuseWhichdevswithDevicepf-- | Internally used function which searches through the given list of USB-- entities (like Devices, Configs, Interfaces or Alternates) for the first-- entity which satisfies the given predicate on its descriptor. Then opens or-- sets that entity by applying the given open or set function to the entity.useWhich∷∀kdesce(m∷*→*)α.(GetDescriptoredesc,MonadIOm)⇒[e]-- ^→(e→k→mα)-- ^ With→(desc→Bool)-- ^ Predicate on descriptor→k-- ^ Continuation function→mαuseWhichdswpf=casefind(p∘getDesc)dsofNothing→throwUSB.NotFoundExceptionJustd→wdf-- | Internally used function for getting the actual USB device handle from a-- regional device handle.getInternalDevHndl∷RegionalDeviceHandler→USB.DeviceHandlegetInternalDevHndl(RegionalDeviceHandleh__)=h-- | Convenience function for retrieving the device from the given regional-- handle.getDevice∷RegionalDeviceHandler→USB.DevicegetDevice=USB.getDevice∘getInternalDevHndl---------------------------------------------------------------------------------- * Getting descriptors--------------------------------------------------------------------------------classGetDescriptorαdesc|α→desc,desc→αwhere-- | Get the descriptor of a given USB entity.getDesc∷α→descinstanceGetDescriptorUSB.DeviceUSB.DeviceDescwheregetDesc=USB.deviceDesc---------------------------------------------------------------------------------- * Resetting devices--------------------------------------------------------------------------------{-| Perform a USB port reset to reinitialize a device. The system will attempt
to restore the previous configuration and alternate settings after the reset has
completed.
Note the constraint: @pr \`ParentOf\` cr@. This allows this function to be
executed in any child region @cr@ of the parent region @pr@ in which the given
regional handle was created.
You can only reset a device when all computations passed to 'setConfig',
'useActiveConfig' and 'setConfigWhich' have been terminated. If you call
@resetDevice@ and such a computation is still running a 'SettingAlreadySet'
exception is thrown.
If the reset fails, the descriptors change, or the previous state cannot be
restored, the device will appear to be disconnected and reconnected. This means
that the device handle is no longer valid (you should close it) and rediscover
the device. A 'USB.NotFoundException' is raised to indicate that this is the case.
/TODO: Think about how to handle the implications of the the previous paragraph!/
This is a blocking function which usually incurs a noticeable delay.
Exceptions:
* 'SettingAlreadySet' if a configuration has been set using 'setConfig',
'useActiveConfig' and 'setConfigWhich'.
* 'USB.NotFoundException' if re-enumeration is required, or if the
device has been disconnected.
* Another 'USB.USBException'.
-}resetDevice∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→cr()resetDevice(RegionalDeviceHandleinternalDevHndlconfigAlreadySetMVar_)=liftIO$withMVarconfigAlreadySetMVar$\configAlreadySet→ifconfigAlreadySetthenthrowIOSettingAlreadySetelseUSB.resetDeviceinternalDevHndl---------------------------------------------------------------------------------- * Configurations--------------------------------------------------------------------------------{-| A supported configuration of a USB device parameterized by the region @r@ in
which it was created.
Note that, just like a regional device handle, a configuration can be duplicated
to a parent region using 'dup'.
Also note that you can get the descriptor of the configuration by applying
'getDesc' to it.
-}dataConfig(r∷*→*)=Config(RegionalDeviceHandler)USB.ConfigDesc{-| Retrieve the supported configurations from the given regional handle.
Note that the configuration is parameterized by the same region @r@ in which the
regional handle was created. This ensures you can never use a configuration
outside that region.
-}getConfigs∷RegionalDeviceHandler→[Configr]getConfigsregionalDevHndl=map(ConfigregionalDevHndl)∘getConfigDescs∘getInternalDevHndl$regionalDevHndl-- | Internally used function for getting all the configuration descriptors of-- the given device.getConfigDescs∷USB.DeviceHandle→[USB.ConfigDesc]getConfigDescs=USB.deviceConfigs∘USB.deviceDesc∘USB.getDeviceinstanceGetDescriptor(Configr)USB.ConfigDescwheregetDesc(Config_configDesc)=configDescinstanceDupConfigwheredup(ConfigregionalDevHndlCconfigDesc)=do-- Duplicating a configuration just means duplicating the associated-- regional device handle:regionalDevHndlP←dupregionalDevHndlC-- And returning a new configuration with the same parameters of the given-- one but with a type that is parameterized by the parent region:return$ConfigregionalDevHndlPconfigDesc---------------------------------------------------------------------------------- ** Setting configurations--------------------------------------------------------------------------------{-| A handle to an active 'Config' which you can get using: 'setConfig',
'useActiveConfig' or 'setConfigWhich'.
The type variable @sCfg@ is used to ensure that you can't return this handle
from these functions.
-}dataConfigHandlesCfg=ConfigHandleUSB.DeviceHandleUSB.ConfigDesc{-| Set the active configuration for a device and then apply the given
continuation function to the resulting configuration handle.
USB devices support multiple configurations of which only one can be active at
any given time. When a configuration is set using 'setConfig', 'useActiveConfig'
or 'setConfigWhich' no threads can set a new configuration until the computation
passed to these functions terminates. If you do try to set one a
'SettingAlreadySet' exception will be thrown.
The operating system may or may not have already set an active configuration on
the device. It is up to your application to ensure the correct configuration is
selected before you attempt to claim interfaces and perform other operations. If
you want to use the current active configuration use 'useActiveConfig'.
If you call this function on a device already configured with the selected
configuration, then this function will act as a lightweight device reset: it
will issue a SET_CONFIGURATION request using the current configuration, causing
most USB-related device state to be reset (altsetting reset to zero, endpoint
halts cleared, toggles reset).
You cannot change/reset configuration if other applications or drivers have
claimed interfaces.
This is a blocking function.
Exceptions:
* 'SettingAlreadySet' if a configuration has already been set using
'setConfig', 'useActiveConfig' or 'setConfigWhich'.
* 'USB.BusyException' if interfaces are currently claimed.
* 'USB.NoDeviceException' if the device has been disconnected
* Another 'USB.USBException'.
-}setConfig∷∀prcrα.(pr`ParentOf`cr,MonadCatchIOcr)⇒Configpr-- ^ The configuration you wish to set.→(∀sCfg.ConfigHandlesCfg→crα)-- ^ Continuation function.→crαsetConfig(Config(RegionalDeviceHandleinternalDevHndlconfigAlreadySetMVar_)configDesc)f=withUnsettedMVarconfigAlreadySetMVar$doliftIO$USB.setConfiginternalDevHndl$USB.configValueconfigDescf$ConfigHandleinternalDevHndlconfigDesc-- | Internally used function which throws a 'SettingAlreadySet' exception if-- the given @MVar@ was set. If the given @MVar@ wasn't set it will be set-- before the given computation is performed. When the computation terminates,-- wheter normally or by raising an exception, the @MVar@ will be unset again.withUnsettedMVar∷MonadCatchIOm⇒MVarBool→mα→mαwithUnsettedMVarsettingAlreadySetMVar=bracket_(liftIO$dosettingAlreadySet←takeMVarsettingAlreadySetMVarifsettingAlreadySetthendoputMVarsettingAlreadySetMVarsettingAlreadySetthrowIOSettingAlreadySetelseputMVarsettingAlreadySetMVarTrue)(liftIO$do_←takeMVarsettingAlreadySetMVarputMVarsettingAlreadySetMVarFalse){-| This exception can be thrown in:
* 'resetDevice'
* 'setConfig', 'useActiveConfig' or 'setConfigWhich'
* 'setAlternate', 'useActiveAlternate' or 'setAlternateWhich'
to indicate that the device was already configured with a setting.
-}dataSettingAlreadySet=SettingAlreadySetderiving(Show,Typeable)instanceExceptionSettingAlreadySet{-| Apply the given continuation function to the configuration handle of the
current active configuration of the given device handle.
This function needs to determine the current active configuration. This
information may be cached by the operating system. If it isn't cached this
function will block while a control transfer is submitted to retrieve the
information.
Exceptions:
* 'SettingAlreadySet' if a configuration has already been set using
'setConfig', 'useActiveConfig' or 'setConfigWhich'.
* 'NoActiveConfig' if the device is not configured.
* 'USB.NoDeviceException' if the device has been disconnected.
* Aanother 'USB.USBException'.
-}useActiveConfig∷∀prcrα.(pr`ParentOf`cr,MonadCatchIOcr)⇒RegionalDeviceHandlepr-- ^ Regional handle to the device-- from which you want to use the-- active configuration.→(∀sCfg.ConfigHandlesCfg→crα)-- ^ Continuation function→crαuseActiveConfig(RegionalDeviceHandleinternalDevHndlconfigAlreadySetMVar_)f=withUnsettedMVarconfigAlreadySetMVar$doactiveConfigValue←liftIO$USB.getConfiginternalDevHndlwhen(activeConfigValue≡0)$throwNoActiveConfigletactiveConfigDesc=fromJust$findisActive$getConfigDescsinternalDevHndlisActive=(activeConfigValue≡)∘USB.configValuef$ConfigHandleinternalDevHndlactiveConfigDesc{-| This exception can be thrown in 'useActiveConfig' to indicate that the
device is currently not configured.
-}dataNoActiveConfig=NoActiveConfigderiving(Show,Typeable)instanceExceptionNoActiveConfig{-| Convenience function which finds the first configuration of the given device
handle which satisfies the given predicate on its descriptor, then sets that
configuration and applies the given function to the resulting configuration
handle.
This function calls 'setConfig' so do see its documentation.
Exceptions:
* 'SettingAlreadySet' if a configuration has already been set using
'setConfig', 'useActiveConfig' or 'setConfigWhich'.
* 'USB.NotFoundException' if no configuration is found that satisfies the given
predicate.
* 'USB.BusyException' if interfaces are currently claimed.
* 'USB.NoDeviceException' if the device has been disconnected
* Another 'USB.USBException'.
-}setConfigWhich∷∀prcrα.(pr`ParentOf`cr,MonadCatchIOcr)⇒RegionalDeviceHandlepr-- ^ Regional handle to the device for-- which you want to set a-- configuration.→(USB.ConfigDesc→Bool)-- ^ Predicate on the configuration-- descriptor.→(∀sCfg.ConfigHandlesCfg→crα)-- ^ Continuation function.→crαsetConfigWhichh=useWhich(getConfigsh)setConfig---------------------------------------------------------------------------------- * Interfaces--------------------------------------------------------------------------------{-| A supported interface of a configuration which you can retrieve using
'getInterfaces'.
To retrieve the 'USB.Interface' descriptors of an interface use 'getDesc'.
-}dataInterfacesCfg=InterfaceUSB.DeviceHandleUSB.InterfaceNumberUSB.Interface{-| Retrieve the supported interfaces from the configuration handle.
Note that the interface is parameterized by the @sCfg@ of the configuration
handle it is derived from. This ensures that it can never be returned from the
functions that created this configuration handle: 'setConfig', 'useActiveConfig'
and 'setConfigWhich'.
The latter is useful because outside those functions the active configuration
may change. If at that moment you still have an interface of the old
configuration claiming it would be an error.
-}getInterfaces∷ConfigHandlesCfg→[InterfacesCfg]getInterfaces(ConfigHandleinternalDevHndlconfigDesc)=mapnewIf$USB.configInterfacesconfigDescwherenewIfalts=InterfaceinternalDevHndl(USB.interfaceNumber$headalts)altsinstanceGetDescriptor(InterfacesCfg)USB.InterfacewheregetDesc(Interface__ifDescs)=ifDescs---------------------------------------------------------------------------------- ** Interface regions--------------------------------------------------------------------------------{-| A regional handle to a claimed interface.
A regional handle to a claimed interface can be created by applying 'claim' or
'withInterface' to the interface you wish to claim.
-}dataRegionalInterfaceHandlesCfg(r∷*→*)=RegionalInterfaceHandle(InterfacesCfg)(MVarBool)(CloseHandler)instanceDup(RegionalInterfaceHandlesCfg)wheredup(RegionalInterfaceHandleinterfacemvch)=liftM(RegionalInterfaceHandleinterfacemv)$dupch{-| Claim the given interface in the region. When the region terminates the
interface is released automatically.
Note that it is allowed to claim an already-claimed interface.
Claiming of interfaces is a purely logical operation; it does not cause any
requests to be sent over the bus. Interface claiming is used to instruct the
underlying operating system that your application wishes to take ownership of
the interface.
This is a non-blocking function.
Exceptions:
* 'USB.BusyException' if another program or driver has claimed the interface.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}claim∷∀prsCfgs.MonadCatchIOpr⇒InterfacesCfg-- ^ Interface you wish to claim→RegionTspr(RegionalInterfaceHandlesCfg(RegionTspr))claiminterface@(InterfaceinternalDevHndlifNum_)=block$domv←liftIO$newMVarFalseliftIO$USB.claimInterfaceinternalDevHndlifNumch←onExit$USB.releaseInterfaceinternalDevHndlifNumreturn$RegionalInterfaceHandleinterfacemvchwithInterface∷∀prsCfgα.MonadCatchIOpr⇒InterfacesCfg-- ^ The interface you wish to claim.→(∀s.RegionalInterfaceHandlesCfg(RegionTspr)→RegionTsprα)-- ^ Continuation function.→prαwithInterfaceinterfacef=runRegionT$claiminterface>>=f{-| Convenience function which finds the first interface of the given
configuration handle which satisfies the given predicate on its descriptors,
then claims that interfaces and applies the given continuation function to the
resulting regional handle.
Exceptions:
* 'USB.NotFoundException' if no interface was found that satisfies the fiven predicate.
* 'USB.BusyException' if another program or driver has claimed the interface.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}withInterfaceWhich∷∀prsCfgα.MonadCatchIOpr⇒ConfigHandlesCfg-- ^ Handle to a configuration of which-- you want to claim an interface.→(USB.Interface→Bool)-- ^ Predicate on the interface descriptors.→(∀s.RegionalInterfaceHandlesCfg(RegionTspr)→RegionTsprα)-- ^ Continuation function.→prαwithInterfaceWhichh=useWhich(getInterfacesh)withInterface---------------------------------------------------------------------------------- * Alternates---------------------------------------------------------------------------------- | A supported 'Interface' alternate setting which you can retrieve using-- 'getAlternates'.dataAlternatesCfg(r∷*→*)=Alternate(RegionalInterfaceHandlesCfgr)USB.InterfaceDesc{-| Retrieve the supported alternate settings from the given interface handle.
Note that the alternate setting is parameterized by the same type variables as
the interface handle. This ensures you can never use an alternate setting
outside the region in which the interface handle was created.
-}getAlternates∷RegionalInterfaceHandlesCfgr→[AlternatesCfgr]getAlternatesregionalIfHandle@(RegionalInterfaceHandle(Interface__alts)__)=map(AlternateregionalIfHandle)altsinstanceGetDescriptor(AlternatesIntrfr)USB.InterfaceDescwheregetDesc(Alternate_ifDesc)=ifDescinstanceDup(AlternatesCfg)wheredup(AlternateregionalIfHndlCifDesc)=doregionalIfHndlP←dupregionalIfHndlCreturn$AlternateregionalIfHndlPifDesc---------------------------------------------------------------------------------- ** Setting alternates--------------------------------------------------------------------------------{-| A handle to a setted alternate setting.
You get a handle to an alternate using 'setAlternate', 'useActiveAlternate' or
'setAlternateWhich'. The type variable @sAlt@ is used to ensure that you can't
return this handle from these functions.
-}dataAlternateHandlesAlt(r∷*→*)=AlternateHandleUSB.DeviceHandleUSB.InterfaceDesc{-| Activate an alternate setting for an interface and then apply the given
continuation function to the resulting alternate handle.
Simillary to configurations, interfaces support multiple alternate settings of
which only one can be active at any given time. When an alternate is set using
'setAlternate', 'useActiveAlternate' or 'setAlternateWhich' no threads can set a
new alternate until the computation passed to these functions terminates. If you
do try to set one a 'SettingAlreadySet' exception will be thrown.
The operating system may already have set an alternate for the interface. If you
want to use this current active alternate use 'useActiveAlternate'.
This is a blocking function.
Exceptions:
* 'USB.NoDeviceException' if the device has been disconnected.
* 'SettingAlreadySet' if an alternate has already been set using
'setAlternate', 'useActiveAlternate' or 'setAlternateWhich'.
* Another 'USB.USBException'.
-}setAlternate∷∀prcrsCfgα.(pr`ParentOf`cr,MonadCatchIOcr)⇒AlternatesCfgpr-- ^ The alternate you wish to set.→(∀sAlt.AlternateHandlesAltpr→crα)-- ^ Continuation function.→crαsetAlternate(Alternate(RegionalInterfaceHandle(InterfaceinternalDevHndlifNum_)alternateAlreadySetMVar_)ifDesc)f=withUnsettedMVaralternateAlreadySetMVar$doliftIO$USB.setInterfaceAltSettinginternalDevHndlifNum(USB.interfaceAltSettingifDesc)f$AlternateHandleinternalDevHndlifDesc{-| Apply the given function to the alternate handle of the current active
alternate of the give interface handle.
To determine the current active alternate this function will block while a
control transfer is submitted to retrieve the information.
Exceptions:
* 'USB.NoDeviceException' if the device has been disconnected.
* 'SettingAlreadySet' if an alternate has already been set using
'setAlternate', 'useActiveAlternate' or 'setAlternateWhich'.
* Another 'USB.USBException'.
-}useActiveAlternate∷∀prcrsCfgα.(pr`ParentOf`cr,MonadCatchIOcr)⇒RegionalInterfaceHandlesCfgpr-- ^ Regional handle to the-- interface from which you want-- to use the active alternate.→(∀sAlt.AlternateHandlesAltpr→crα)-- ^ Continuation function.→crαuseActiveAlternate(RegionalInterfaceHandle(InterfaceinternalDevHndlifNumalts)alternateAlreadySetMVar_)f=withUnsettedMVaralternateAlreadySetMVar$dolettimeout=5000-- msactiveAltValue←liftIO$USB.getInterfaceAltSettinginternalDevHndlifNumtimeoutletactiveAlt=fromJust$findisActivealtsisActive=(activeAltValue≡)∘USB.interfaceAltSettingf$AlternateHandleinternalDevHndlactiveAlt{-| Convenience function which finds the first alternate of the given interface
handle which satisfies the given predicate on its descriptor, then sets that
alternate and applies the given function to the resulting alternate handle.
This function calls 'setAlternate' so do see its documentation.
Exceptions:
* 'USB.NotFoundException' if no alternate is found that satisfies the given
predicate.
* 'USB.NoDeviceException' if the device has been disconnected.
* 'SettingAlreadySet' if an alternate has already been set using
'setAlternate', 'useActiveAlternate' or 'setAlternateWhich'.
* Another 'USB.USBException'.
-}setAlternateWhich∷∀prcrsCfgα.(pr`ParentOf`cr,MonadCatchIOcr)⇒RegionalInterfaceHandlesCfgpr-- ^ Regional handle to the-- interface for which you want-- to set an alternate.→(USB.InterfaceDesc→Bool)-- ^ Predicate on the interface-- descriptor.→(∀sAlt.AlternateHandlesAltpr→crα)-- ^ Continuation function→crαsetAlternateWhichh=useWhich(getAlternatesh)setAlternate---------------------------------------------------------------------------------- * Endpoints--------------------------------------------------------------------------------{-| I/O operations on endpoints are type-safe. You can only read from an
endpoint with an 'In' transfer direction and you can only write to an endpoint
with an 'Out' transfer direction.
Reading and writing also have different implementations for the different
endpoint transfer types like: 'Bulk' and 'Interrupt'. I/O with endpoints of
other transfer types like 'Control' and 'Isochronous' is not possible.
This type lifts the transfer direction and transfer type information to the
type-level so that I/O operations like 'readEndpoint' and 'writeEndpoint' can
specify which endpoints they support.
You can retrieve the endpoints of an alternate using 'getEndpoints'.
-}dataEndpointtransDirtransTypesAlt(r∷*→*)=EndpointUSB.DeviceHandleUSB.EndpointDesceqDir∷TransferDirectiontransDir→USB.TransferDirection→BoolOut`eqDir`USB.Out=TrueIn`eqDir`USB.In=True_`eqDir`_=FalseeqType∷TransferTypetransType→USB.TransferType→BoolControl`eqType`USB.Control=TrueIsochronous`eqType`(USB.Isochronous__)=TrueBulk`eqType`USB.Bulk=TrueInterrupt`eqType`USB.Interrupt=True_`eqType`_=False-- | Retrieve all the endpoints from the given alternate handle which are of the-- given transfer direction and transfer type.getEndpoints∷∀transDirtransTypesAltr.AlternateHandlesAltr-- ^ Handle to the alternate from-- which you want to retrieve its-- endpoints.→TransferDirectiontransDir-- ^ Filter all endpoints which have-- this transfer direction.→TransferTypetransType-- ^ Filter all endpoints which have-- this transfer type.→[EndpointtransDirtransTypesAltr]getEndpoints(AlternateHandleinternalDevHndlifDesc)transDirtransType=map(EndpointinternalDevHndl)$filtereqDirAndType$USB.interfaceEndpointsifDescwhereeqDirAndTypeendpointDesc=transDir`eqDir`transDirUSB∧transType`eqType`transTypeUSBwheretransDirUSB=USB.transferDirection$USB.endpointAddressendpointDesctransTypeUSB=USB.endpointAttribsendpointDescinstanceGetDescriptor(EndpointtransDirtransTypesAltr)USB.EndpointDescwheregetDesc(Endpoint_endpointDesc)=endpointDesc{-| Clear the halt/stall condition for an endpoint.
Endpoints with halt status are unable to receive or transmit data until the halt
condition is stalled.
You should cancel all pending transfers before attempting to clear the halt
condition.
This is a blocking function.
Exceptions:
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}clearHalt∷(pr`ParentOf`cr,MonadIOcr)⇒EndpointtransDirtransTypesAltpr→cr()clearHalt(EndpointinternalDevHndlendpointDesc)=liftIO$USB.clearHaltinternalDevHndl$USB.endpointAddressendpointDesc---------------------------------------------------------------------------------- *** Transfer directions--------------------------------------------------------------------------------dataTransferDirectiontransDirwhereOut∷TransferDirectionOutIn∷TransferDirectionIn-- | Out transfer direction (host -> device) used for writing.dataOut-- | In transfer direction (device -> host) used for reading.dataIn---------------------------------------------------------------------------------- *** Transfer types--------------------------------------------------------------------------------dataTransferTypetransTypewhereControl∷TransferTypeControlIsochronous∷TransferTypeIsochronousBulk∷TransferTypeBulkInterrupt∷TransferTypeInterruptdataControldataIsochronousdataBulkdataInterrupt---------------------------------------------------------------------------------- * Endpoint I/O--------------------------------------------------------------------------------{-| Handy type synonym for read transfers.
A @ReadAction@ is a function which takes a size which defines how many bytes to
read and a timeout. The function returns an action which, when executed,
performs the actual read and returns the bytestring that was read paired with an
indication if the transfer timed out.
-}typeReadActionr=USB.Size→USB.Timeout→r(ByteString,USB.TimedOut)-- | Class of transfer types that support reading.classReadEndpointtransTypewhere{-| Read bytes from an 'In' endpoint with either a 'Bulk' or 'Interrupt'
transfer type.
Exceptions:
* 'USB.PipeException' if the endpoint halted.
* 'USB.OverflowException' if the device offered more data,
see /Packets and overflows/ in the libusb documentation:
<http://libusb.sourceforge.net/api-1.0/packetoverflow.html>.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}readEndpoint∷(pr`ParentOf`cr,MonadIOcr)⇒EndpointIntransTypesAltpr→ReadActioncrinstanceReadEndpointBulkwherereadEndpoint=transferWithUSB.readBulkinstanceReadEndpointInterruptwherereadEndpoint=transferWithUSB.readInterrupttransferWith∷(pr`ParentOf`cr,MonadIOcr)⇒(USB.DeviceHandle→USB.EndpointAddress→α→USB.Timeout→IOβ)→(EndpointtransDirtransTypesAltpr→α→USB.Timeout→crβ)transferWithf=\endpointsbstimeout→liftIO$wrapfendpointsbstimeoutwrap∷(USB.DeviceHandle→USB.EndpointAddress→α)→(EndpointtransDirtransTypesAltpr→α)wrapf=\(EndpointinternalDevHndlendpointDesc)→finternalDevHndl$USB.endpointAddressendpointDesc--------------------------------------------------------------------------------{-| Handy type synonym for write transfers.
A @WriteAction@ is a function which takes the bytestring to write and a
timeout. The function returns an action which, when exectued, returns the number
of bytes that were actually written paired with an indication if the transfer
timed out.
-}typeWriteActionr=ByteString→USB.Timeout→r(USB.Size,USB.TimedOut)-- | Class of transfer types that support writingclassWriteEndpointtransTypewhere{-| Write bytes to an 'Out' endpoint with either a 'Bulk' or 'Interrupt'
transfer type.
Exceptions:
* 'USB.PipeException' if the endpoint halted.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}writeEndpoint∷(pr`ParentOf`cr,MonadIOcr)⇒EndpointOuttransTypesAltpr→WriteActioncrinstanceWriteEndpointBulkwherewriteEndpoint=transferWithUSB.writeBulkinstanceWriteEndpointInterruptwherewriteEndpoint=transferWithUSB.writeInterrupt---------------------------------------------------------------------------------- | Class of transfer types that support enumerating.classEnumReadEndpointtransTypewhere-- | An enumerator for an 'In' endpoint with either a 'Bulk' or 'Interrupt'-- transfer type.enumReadEndpoint∷(pr`ParentOf`cr,MonadCatchIOcr,ReadableChunksWord8)⇒EndpointIntransTypesAltpr→USB.Size-- ^ Chunk size. A good value for this would be-- the @'maxPacketSize' . 'endpointMaxPacketSize'@.→USB.Timeout-- ^ Timeout (in milliseconds) that this function-- should wait for each chunk before giving up-- due to no response being received. For no-- timeout, use value 0.→EnumeratorGMsWord8crαinstanceEnumReadEndpointBulkwhereenumReadEndpoint=wrapUSB.enumReadBulkinstanceEnumReadEndpointInterruptwhereenumReadEndpoint=wrapUSB.enumReadInterrupt---------------------------------------------------------------------------------- ** Control transfers---------------------------------------------------------------------------------- | Handy type synonym that names the parameters of a control transfer.typeControlActionα=RequestType→USB.Recipient→USB.Request→USB.Value→USB.Index→α{-| Control transfers can have three request types: @Standard@, @Class@ and
@Vendor@. We disallow @Standard@ requests however because with them you can
destroy the safety guarantees that this module provides.
-}dataRequestType=Class|VendorreqTypeToInternal∷RequestType→USB.RequestTypereqTypeToInternalClass=USB.ClassreqTypeToInternalVendor=USB.Vendor{-| Perform a USB /control/ request that does not transfer data.
Exceptions:
* 'USB.TimeoutException' if the transfer timed out.
* 'USB.PipeException' if the control request was not supported by the device
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}control∷∀prcr.(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→ControlAction(USB.Timeout→cr())controlregionalDevHndl=\reqTypereqRecipientrequestvalueindex→\timeout→liftIO$USB.control(getInternalDevHndlregionalDevHndl)(reqTypeToInternalreqType)reqRecipientrequestvalueindextimeout{-| Perform a USB /control/ read.
Exceptions:
* 'USB.PipeException' if the control request was not supported by the device
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}readControl∷∀prcr.(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→ControlAction(ReadActioncr)readControlregionalDevHndl=\reqTypereqRecipientrequestvalueindex→\timeoutsize→liftIO$USB.readControl(getInternalDevHndlregionalDevHndl)(reqTypeToInternalreqType)reqRecipientrequestvalueindextimeoutsize-- | A convenience function similar to 'readControl' which checks if the-- specified number of bytes to read were actually read. Throws an 'USB.IOException'-- if this is not the case.readControlExact∷∀prcr.(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→ControlAction(USB.Size→USB.Timeout→crByteString)readControlExactregionalDevHndl=\reqTypereqRecipientrequestvalueindex→\timeoutsize→liftIO$USB.readControlExact(getInternalDevHndlregionalDevHndl)(reqTypeToInternalreqType)reqRecipientrequestvalueindextimeoutsize{-| Perform a USB /control/ write.
Exceptions:
* 'USB.PipeException' if the control request was not supported by the device
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}writeControl∷∀prcr.(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→ControlAction(WriteActioncr)writeControlregionalDevHndl=\reqTypereqRecipientrequestvalueindex→\timeoutinput→liftIO$USB.writeControl(getInternalDevHndlregionalDevHndl)(reqTypeToInternalreqType)reqRecipientrequestvalueindextimeoutinput---------------------------------------------------------------------------------- *** Standard Device Requests--------------------------------------------------------------------------------{- TODO: Think about which of these to export:
setHalt ∷ DeviceHandle → EndpointAddress → Timeout → IO ()
clearRemoteWakeup ∷ DeviceHandle → Timeout → IO ()
setRemoteWakeup ∷ DeviceHandle → Timeout → IO ()
setStandardTestMode ∷ DeviceHandle → TestMode → Timeout → IO ()
getInterfaceAltSetting ∷ DeviceHandle
→ InterfaceNumber
→ Timeout
→ IO InterfaceAltSetting
getDeviceStatus ∷ DeviceHandle → Timeout → IO DeviceStatus
getEndpointStatus ∷ DeviceHandle
→ EndpointAddress
→ Timeout
→ IO Bool
setDeviceAddress ∷ DeviceHandle → Word16 → Timeout → IO ()
synchFrame ∷ DeviceHandle → EndpointAddress → Timeout → IO Int
-}---------------------------------------------------------------------------------- * String descriptors--------------------------------------------------------------------------------{-| Retrieve a list of supported languages.
This function may throw 'USB.USBException's.
-}getLanguages∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→cr[USB.LangId]getLanguagesdevHndl=liftIO$USB.getLanguages(getInternalDevHndldevHndl){-| Retrieve a string descriptor from a device.
This is a convenience function which formulates the appropriate control message
to retrieve the descriptor. The string returned is Unicode, as detailed in the
USB specifications.
This function may throw 'USB.USBException's.
/TODO: The following can be made more type-safe!/
When I call 'getStrDesc' I would like the type system to guarantee that the
given @StrIx@ and @LangId@ actually belong to the given @Handle@. In other
words I would like to get a type error when they are some arbitrary number or
come from another device.
-}getStrDesc∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→USB.StrIx→USB.LangId→Int-- ^ Maximum number of characters in the requested string. An-- 'USB.IOException' will be thrown when the requested-- string is larger than this number.→crStringgetStrDescdevHndlstrIxlangIdsize=liftIO$USB.getStrDesc(getInternalDevHndldevHndl)strIxlangIdsize{-| Retrieve a string descriptor from a device using the first supported
language.
This is a convenience function which formulates the appropriate control message
to retrieve the descriptor. The string returned is Unicode, as detailed in the
USB specifications.
This function may throw 'USB.USBException's.
-}getStrDescFirstLang∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→USB.StrIx→Int-- ^ Maximum number of characters in the requested-- string. An 'USB.IOException' will be thrown when-- the requested string is larger than this number.→crStringgetStrDescFirstLangdevHndldescStrIxsize=liftIO$USB.getStrDescFirstLang(getInternalDevHndldevHndl)descStrIxsize---------------------------------------------------------------------------------- * USB kernel drivers--------------------------------------------------------------------------------{-| Determine if a kernel driver is active on an interface.
If a kernel driver is active, you cannot claim the interface, and libusb will be
unable to perform I/O.
Exceptions:
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}kernelDriverActive∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→USB.InterfaceNumber→crBoolkernelDriverActiveregionalDevHndl=liftIO∘USB.kernelDriverActive(getInternalDevHndlregionalDevHndl){-| Detach a kernel driver from an interface.
If successful, you will then be able to claim the interface and perform I/O.
Exceptions:
* 'USB.NotFoundException' if no kernel driver was active.
* 'USB.InvalidParamException' if the interface does not exist.
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}detachKernelDriver∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→USB.InterfaceNumber→cr()detachKernelDriverregionalDevHndl=liftIO∘USB.detachKernelDriver(getInternalDevHndlregionalDevHndl){-| Re-attach an interface's kernel driver, which was previously
detached using 'detachKernelDriver'.
Exceptions:
* 'USB.NotFoundException' if no kernel driver was active.
* 'USB.InvalidParamException' if the interface does not exist.
* 'USB.NoDeviceException' if the device has been disconnected.
* 'USB.BusyException' if the driver cannot be attached because the interface
is claimed by a program or driver.
* Another 'USB.USBException'.
-}attachKernelDriver∷(pr`ParentOf`cr,MonadIOcr)⇒RegionalDeviceHandlepr→USB.InterfaceNumber→cr()attachKernelDriverregionalDevHndl=liftIO∘USB.attachKernelDriver(getInternalDevHndlregionalDevHndl){-| If a kernel driver is active on the specified interface the driver is
detached and the given action is executed. If the action terminates, whether by
normal termination or by raising an exception, the kernel driver is attached
again. If a kernel driver is not active on the specified interface the action is
just executed.
Exceptions:
* 'USB.NoDeviceException' if the device has been disconnected.
* Another 'USB.USBException'.
-}withDetachedKernelDriver∷(pr`ParentOf`cr,MonadCatchIOcr)⇒RegionalDeviceHandlepr→USB.InterfaceNumber→crα→crαwithDetachedKernelDriverregionalDevHndlifNumaction=ifM(kernelDriverActiveregionalDevHndlifNum)(bracket_(detachKernelDriverregionalDevHndlifNum)(attachKernelDriverregionalDevHndlifNum)action)action---------------------------------------------------------------------------------- * Utils---------------------------------------------------------------------------------- | Monadic @if ... then ... else ...@ifM∷Monadm⇒mBool→mα→mα→mαifMcMtMeM=doc←cMifcthentMelseeM-- The End ---------------------------------------------------------------------