-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Wrapper around the usb package adding extra type-safety -- -- The usb library provides a standard Haskell abstracting layer over -- bindings-libusb providing: abstract types instead of Ptrs, automatic -- marshalling and unmarshalling, automatic garbage collection, -- exceptions instead of integer return codes, etc.. -- -- While all that is very nice there are still some things that you can -- do wrong. For example doing I/O with a closed device or reading from -- or writing to an endpoint which doesn't belong to the claimed -- interface. Or reading from an Out endpoint or writing to an In -- endpoint. -- -- usb-safe provides the following guarantees: -- -- -- -- The primary technique used in usb-safe is called "Lightweight monadic -- regions" which was invented by Oleg Kiselyov and Chung-chieh Shan. -- See: -- -- http://okmij.org/ftp/Haskell/regions.html#light-weight @package usb-safe @version 0.3 -- | This modules provides the following guarantees for working with USB -- devices: -- -- -- -- 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 module System.USB.Safe -- | A monad transformer in which Devices can be opened wich are -- automatically closed on exit from the region. -- -- Note that regions can be nested. pr (for parent region) is a -- monad which is usually the region which is running this region. -- However when you are running a TopDeviceRegion the parent -- region will be IO. data DeviceRegionT s pr :: (* -> *) ± -- | Execute a region inside its parent region pr. -- -- All Devices which have been opened in the given region using -- openDevice, and which haven't been duplicated using -- dupDeviceHandle, will be closed on exit from this function -- wether by normal termination or by raising an exception. -- -- Also all devices which have been duplicated to this region from a -- child region are closed on exit if they haven't been duplicated -- themselves. -- -- Note the type variable s of the region wich is only -- quantified over the region itself. This ensures that no values, that -- have a type which has s in it, can be returned from this -- function. (Note the similarity with the ST monad.) -- -- DeviceHandles are parameterized by the region in which they -- were created. So device handles which were created by -- openDevice in the given region have this s in their -- type. This ensures that these device handles, which may have been -- closed on exit from this function, can't be returned by this function. -- This ensures you can never do any IO with closed device handles. -- -- Note that it is possible to run a region inside another region. -- -- TODO: Say something more about this nesting of regions... runDeviceRegionT :: (MonadCatchIO pr) => (forall s. DeviceRegionT s pr ±) -> pr ± -- | A region which has IO as its parent region which enables it to -- be directly executed in IO by runTopDeviceRegion or -- concurrently executed in another region by forkTopDeviceRegion. type TopDeviceRegion s = DeviceRegionT s IO -- | Convenience funtion for running a top-level region in -- IO. -- -- Note that: runTopDeviceRegion = runDeviceRegionT runTopDeviceRegion :: (forall s. TopDeviceRegion s ±) -> IO ± -- | Return a region which executes the given top-level region in a -- new thread. -- -- Note that the forked region has the same type variable s as -- the resulting region. This means that all DeviceHandles which -- can be referenced in the resulting region can also be referenced in -- the forked region. forkTopDeviceRegion :: (MonadIO pr) => TopDeviceRegion s () -> DeviceRegionT s pr ThreadId -- | Transform the computation inside a region. mapDeviceRegionT :: (m ± -> n ²) -> DeviceRegionT s m ± -> DeviceRegionT s n ² -- | Lift a catchError operation to the new monad. liftCatch :: (pr ± -> (e -> pr ±) -> pr ±) -> DeviceRegionT s pr ± -> (e -> DeviceRegionT s pr ±) -> DeviceRegionT s pr ± -- | A handle to an opened Device parameterized by the region -- r in which it was created. data DeviceHandle r :: (* -> *) -- | Open a device in a region. -- -- Note that the returned device handle is parameterized by the region in -- which it was created. This is to ensure that device handles can never -- escape their region and to support operations on device handles that -- are used in a child region of the region in which the device was -- created. -- -- This is a non-blocking function; no requests are sent over the bus. -- -- Exceptions: -- -- openDevice :: (MonadCatchIO pr) => Device -> DeviceRegionT s pr (DeviceHandle (DeviceRegionT s pr)) -- | Duplicate a device handle in the parent region. -- -- For example, suppose you run the following region: -- --
--   runDeviceRegionT $ do
--   
-- -- Inside this region you run a nested child region like: -- --
--   d1hDup <- runDeviceRegionT $ do
--   
-- -- Now in this child region you open the device d1: -- --
--   d1h <- openDevice d1
--   
-- -- Note that d1h :: DeviceHandle (DeviceRegion cs (DeviceRegion ps -- ppr)) where cs is bound by the inner (child) -- runDeviceRegionT and ps is bound by the outer -- (parent) runDeviceRegionT. -- -- Suppose you want to use the resulting device handle d1h in -- the parent device region. You can't simply return d1h -- because then the type variable cs, escapes the inner region. -- -- However, if you duplicate the device handle you can safely return it. -- --
--   d1hDup <- dupDeviceHandle d1h
--   return d1hDup
--   
-- -- Note that d1hDup :: DeviceHandle (DeviceRegionT ps ppr) -- -- Back in the parent region you can safely operate on d1hDup. dupDeviceHandle :: (MonadCatchIO ppr) => DeviceHandle (DeviceRegionT cs (DeviceRegionT ps ppr)) -> DeviceRegionT cs (DeviceRegionT ps ppr) (DeviceHandle (DeviceRegionT ps ppr)) -- | A convenience function which opens the given device, applies the given -- function to the resulting device handle and runs the resulting region. -- -- Note that: withDevice dev f = runDeviceRegionT $ -- openDevice dev >>= f withDevice :: (MonadCatchIO pr) => Device -> (forall s. DeviceHandle (DeviceRegionT s pr) -> DeviceRegionT s pr ±) -> pr ± -- | Retrieve the device from the device handle. getDevice :: DeviceHandle region -> Device -- | 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 pr `ParentOf` cr which ensures that this function -- can be executed in any child region of the region in which the given -- device handle was created. -- -- You can only reset a device when all computations passed to -- withConfig or withActiveConfig 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 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: -- -- resetDevice :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> cr () -- | A supported configuration of a Device parameterized by the -- region r in which it was created. data Config r :: (* -> *) -- | Retrieve the supported configurations from the device handle. -- -- Note that the configuration is parameterized by the same region -- r in which the device handle was created. This ensures you -- can never use a configuration outside that region. getConfigs :: DeviceHandle r -> [Config r] -- | Retrieve the configuration descriptor from the given configuration. getConfigDesc :: Config r -> ConfigDesc -- | Duplicate a configuration in the parent region. -- -- Also see: dupDeviceHandle. dupConfig :: (MonadCatchIO ppr) => Config (DeviceRegionT cs (DeviceRegionT ps ppr)) -> DeviceRegionT cs (DeviceRegionT ps ppr) (Config (DeviceRegionT ps ppr)) -- | A handle to an active Config parameterized by the region -- r in which it was created. -- -- You get a handle to a configuration using withConfig or -- withActiveConfig. The type variable sCfg is used to -- ensure that you can't return this handle from these functions. data ConfigHandle sCfg r :: (* -> *) -- | This exception can be thrown in: -- -- -- -- to indicate that the device was already configured with a setting. data SettingAlreadySet -- | Set the active configuration for a device and then apply the given -- 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 -- withConfig or withActiveConfig 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 withActiveConfig. -- -- 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: -- -- withConfig :: (ParentOf pr cr, MonadCatchIO cr) => Config pr -> (forall sCfg. ConfigHandle sCfg pr -> cr ±) -> cr ± -- | This exception can be thrown in withActiveConfig to indicate -- that the device is currently not configured. data NoActiveConfig -- | Apply the given 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. -- -- TODO: I'm not yet sure if this is the best way of handling already -- configured devices. So this may change in the future! -- -- Exceptions: -- -- withActiveConfig :: (ParentOf pr cr, MonadCatchIO cr) => DeviceHandle pr -> (forall sCfg. ConfigHandle sCfg pr -> cr ±) -> cr ± -- | A supported interface of a Config parameterized by the region -- r in which it was created and the sCfg of the -- configuration it is derived from. data Interface sCfg r :: (* -> *) -- | Retrieve the supported interfaces from the configuration handle. -- -- Note that the interface is parameterized by the same type variables as -- the configuration handle. This ensures you can never use an interface -- outside the scope of the function passed to withConfig or -- withActiveConfig. getInterfaces :: ConfigHandle sCfg r -> [Interface sCfg r] -- | Retrieve the alternate interface descriptors of the interface. -- -- Note that: type Interface = [InterfaceDesc]. getInterfaceDescs :: Interface sCfg r -> Interface -- | A handle to a claimed Interface parameterized with the -- region r in which it was created. -- -- You get a handle to an interface using withInterface. The type -- variable sIntrf is used to ensure that you can't return this -- handle from this function. data InterfaceHandle sIntrf r :: (* -> *) -- | Claim the given interface, then apply the given function to the -- resulting interface handle and finally release the interface on exit -- from the function wether by normal termination or by raising an -- exception. -- -- 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: -- -- withInterface :: (ParentOf pr cr, MonadCatchIO cr) => Interface sCfg pr -> (forall sIntrf. InterfaceHandle sIntrf pr -> cr ±) -> cr ± -- | A supported Interface alternate setting parameterized by the -- region r in which it was created and the sIntrf of -- the interface it is derived from. data Alternate sIntrf r :: (* -> *) -- | Retrieve the supported alternate settings from the 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 scope of the function passed to -- withInterface. getAlternates :: InterfaceHandle sIntrf r -> [Alternate sIntrf r] -- | Retrieve the interface descriptor of this alternate setting. getInterfaceDesc :: Alternate sIntrf r -> InterfaceDesc -- | A handle to a setted alternate setting parameterized by the region -- r in which it was created. -- -- You get a handle to an alternate using withAlternate or -- withActiveAlternate. The type variable sAlt is used to -- ensure that you can't return this handle from these functions. data AlternateHandle sAlt r :: (* -> *) -- | Activate an alternate setting for an interface and then apply the -- given 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 withAlternate or -- withActiveAlternate 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 -- withActiveAlternate. -- -- This is a blocking function. -- -- Exceptions: -- -- withAlternate :: (ParentOf pr cr, MonadCatchIO cr) => Alternate sIntrf pr -> (forall sAlt. AlternateHandle sAlt pr -> cr ±) -> cr ± -- | 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. -- -- TODO: I'm not yet sure if this is the best way of handling already -- configured devices. So this may change in the future! -- -- Exceptions: -- -- withActiveAlternate :: (ParentOf pr cr, MonadCatchIO cr) => InterfaceHandle sIntrf pr -> (forall sAlt. AlternateHandle sAlt pr -> cr ±) -> cr ± -- | A supported endpoint from an Alternate parameterized by the -- region r in which it was created and the sAlt of the -- alternate it is derived from. data Endpoint sAlt r :: (* -> *) -- | Retrieve the supported endpoints from the alternate handle. -- -- Note that the endpoint is parameterized by the same type variables as -- the alternate handle. This ensures you can never use an endpoint -- outside the scope of the function passed to withAlternate or -- withActiveAlternate. getEndpoints :: AlternateHandle sAlt r -> [Endpoint sAlt r] -- | 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. -- -- The reason it is called a FilteredEndpoint is that to create -- it you have to filter a list of Endpoints with -- filterEndpoints. data FilteredEndpoint transDir transType sAlt r :: (* -> *) -- | The Endpoint type is not rich enough to encode the transfer -- direction and transfer type. In order to introduce this type -- information we have to filter the list of endpoints and get back a -- list of filtered endpoints which have the specified transfer direction -- and transfer type and also expres this information in their type. filterEndpoints :: (TransferDirection transDir, TransferType transType) => [Endpoint sAlt r] -> [FilteredEndpoint transDir transType sAlt r] -- | Retrieve the endpoint descriptor from the given endpoint handle. getEndpointDesc :: FilteredEndpoint transDir transType sAlt r -> 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: -- -- clearHalt :: (ParentOf pr cr, MonadIO cr) => FilteredEndpoint transDir transType sAlt pr -> cr () -- | In transfer direction (device -> host) used for reading. data In -- | Out transfer direction (host -> device) used for writing. data Out -- | Control endpoints don't support read and write operations. data Control -- | Isochronous endpoints don't support read and write -- operations. data Isochronous -- | Bulk endpoints support read and write operations. data Bulk -- | Interrupt endpoints support read and write operations. data Interrupt -- | Handy type synonym for read transfers. -- -- A ReadAction is a function which takes a timeout and a size -- which defines how many bytes to read. 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. type ReadAction r = Timeout -> Size -> r (ByteString, Bool) -- | Class of transfer types that support reading. -- -- (Only Bulk and Interrupt transfer types are supported.) class (TransferType transType) => ReadEndpoint transType readEndpoint :: (ReadEndpoint transType, ParentOf pr cr, MonadIO cr) => FilteredEndpoint In transType sAlt pr -> ReadAction cr -- | Handy type synonym for write transfers. -- -- A WriteAction is a function which takes a timeout and the -- bytestring to write. 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. type WriteAction r = Timeout -> ByteString -> r (Size, Bool) -- | Class of transfer types that support writing -- -- (Only Bulk and Interrupt transfer types are supported.) class (TransferType transType) => WriteEndpoint transType writeEndpoint :: (WriteEndpoint transType, ParentOf pr cr, MonadIO cr) => FilteredEndpoint Out transType sAlt pr -> WriteAction cr -- | 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. data RequestType Class :: RequestType Vendor :: RequestType -- | Perform a USB control request that does not transfer data. -- -- The value and index values should be given in -- host-endian byte order. -- -- Exceptions: -- -- control :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> RequestType -> Recipient -> Word8 -> Word16 -> Word16 -> Timeout -> cr () -- | Perform a USB control read. -- -- The value and index values should be given in -- host-endian byte order. -- -- Exceptions: -- -- readControl :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> RequestType -> Recipient -> Word8 -> Word16 -> Word16 -> ReadAction cr -- | Perform a USB control write. -- -- The value and index values should be given in -- host-endian byte order. -- -- Exceptions: -- -- writeControl :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> RequestType -> Recipient -> Word8 -> Word16 -> Word16 -> WriteAction cr -- | Retrieve a list of supported languages. -- -- This function may throw USBExceptions. getLanguages :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> cr [LangId] -- | 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 USBExceptions. -- -- 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 DeviceHandle. In other words I would like -- to get a type error when they are some arbitrary number or come from -- another device. getStrDesc :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> StrIx -> LangId -> Size -> cr String -- | 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 USBExceptions. getStrDescFirstLang :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> StrIx -> Size -> cr String -- | 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: -- -- kernelDriverActive :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> InterfaceNumber -> cr Bool -- | Detach a kernel driver from an interface. -- -- If successful, you will then be able to claim the interface and -- perform I/O. -- -- Exceptions: -- -- detachKernelDriver :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> InterfaceNumber -> cr () -- | Re-attach an interface's kernel driver, which was previously detached -- using detachKernelDriver. -- -- Exceptions: -- -- attachKernelDriver :: (ParentOf pr cr, MonadIO cr) => DeviceHandle pr -> InterfaceNumber -> cr () -- | 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: -- -- withDetachedKernelDriver :: (ParentOf pr cr, MonadCatchIO cr) => DeviceHandle pr -> InterfaceNumber -> cr ± -> cr ± instance Typeable NoActiveConfig instance Typeable SettingAlreadySet instance Show NoActiveConfig instance Show SettingAlreadySet instance (Monad pr) => Monad (DeviceRegionT s pr) instance MonadTrans (DeviceRegionT s) instance (MonadIO pr) => MonadIO (DeviceRegionT s pr) instance (MonadCatchIO pr) => MonadCatchIO (DeviceRegionT s pr) instance (MonadCont pr) => MonadCont (DeviceRegionT s pr) instance WriteEndpoint Interrupt instance WriteEndpoint Bulk instance ReadEndpoint Interrupt instance ReadEndpoint Bulk instance TransferType Interrupt instance TransferType Bulk instance TransferType Isochronous instance TransferType Control instance TransferDirection In instance TransferDirection Out instance Exception NoActiveConfig instance Exception SettingAlreadySet instance TypeCast2'' () a a instance (TypeCast2'' t a b) => TypeCast2' t a b instance (TypeCast2' () a b) => TypeCast2 a b instance (Monad cr, TypeCast2 cr (DeviceRegionT s pcr), ParentOf pr pcr) => ParentOf pr cr instance (Monad r) => ParentOf r r instance (MonadWriter w pr) => MonadWriter w (DeviceRegionT s pr) instance (MonadState st pr) => MonadState st (DeviceRegionT s pr) instance (MonadReader r pr) => MonadReader r (DeviceRegionT s pr) instance (MonadRWS r w st pr) => MonadRWS r w st (DeviceRegionT s pr) instance (MonadError e pr) => MonadError e (DeviceRegionT s pr)