|
System.USB.Safe | Maintainer | Bas van Dijk <v.dijk.bas@gmail.com> |
|
|
|
|
|
Description |
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
|
|
Synopsis |
|
module Control.Monad.Trans.Region | | type DeviceRegionT s pr α = RegionT Device s pr α | | type TopDeviceRegion s α = TopRegion Device s α | | type RegionalDeviceHandle r = RegionalHandle Device r | | getDevice :: RegionalDeviceHandle r -> Device | | class GetDescriptor α desc | α -> desc, desc -> α where | | | resetDevice :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> cr () | | data Config r | | getConfigs :: RegionalDeviceHandle r -> [Config r] | | data ConfigHandle sCfg | | setConfig :: (ParentOf pr cr, MonadCatchIO cr) => Config pr -> (forall sCfg. ConfigHandle sCfg -> cr α) -> cr α | | data SettingAlreadySet | | useActiveConfig :: (ParentOf pr cr, MonadCatchIO cr) => RegionalDeviceHandle pr -> (forall sCfg. ConfigHandle sCfg -> cr α) -> cr α | | data NoActiveConfig | | setConfigWhich :: (ParentOf pr cr, MonadCatchIO cr) => RegionalDeviceHandle pr -> (ConfigDesc -> Bool) -> (forall sCfg. ConfigHandle sCfg -> cr α) -> cr α | | data NotFound | | data Interface sCfg | | getInterfaces :: ConfigHandle sCfg -> [Interface sCfg] | | type IfRegionT sCfg s pr α = RegionT (Interface sCfg) s pr α | | type RegionalIfHandle sCfg r = RegionalHandle (Interface sCfg) r | | claim :: MonadCatchIO pr => Interface sCfg -> IfRegionT sCfg s pr (RegionalIfHandle sCfg (RegionT (Interface sCfg) s pr)) | | withInterfaceWhich :: MonadCatchIO pr => ConfigHandle sCfg -> (Interface -> Bool) -> (forall s. RegionalIfHandle sCfg (RegionT (Interface sCfg) s pr) -> IfRegionT sCfg s pr α) -> pr α | | data Alternate sCfg r | | getAlternates :: RegionalIfHandle sCfg r -> [Alternate sCfg r] | | data AlternateHandle sCfg sAlt r | | setAlternate :: (ParentOf pr cr, MonadCatchIO cr) => Alternate sCfg pr -> (forall sAlt. AlternateHandle sCfg sAlt pr -> cr α) -> cr α | | useActiveAlternate :: (ParentOf pr cr, MonadCatchIO cr) => RegionalIfHandle sCfg pr -> (forall sAlt. AlternateHandle sCfg sAlt pr -> cr α) -> cr α | | setAlternateWhich :: (ParentOf pr cr, MonadCatchIO cr) => RegionalIfHandle sCfg pr -> (InterfaceDesc -> Bool) -> (forall sAlt. AlternateHandle sCfg sAlt pr -> cr α) -> cr α | | data Endpoint transDir transType sAlt r | | getEndpoints :: forall transDir transType sCfg sAlt r. AlternateHandle sCfg sAlt r -> TransferDirection transDir -> TransferType transType -> [Endpoint transDir transType sAlt r] | | clearHalt :: (ParentOf pr cr, MonadIO cr) => Endpoint transDir transType sAlt pr -> cr () | | | | data OUT | | data IN | | | | data CONTROL | | data ISOCHRONOUS | | data BULK | | data INTERRUPT | | type ReadAction r = Timeout -> Size -> r (ByteString, Bool) | | type WriteAction r = Timeout -> ByteString -> r (Size, Bool) | | readEndpoint :: (ReadEndpoint transType, ParentOf pr cr, MonadIO cr) => Endpoint IN transType sAlt pr -> ReadAction cr | | writeEndpoint :: (WriteEndpoint transType, ParentOf pr cr, MonadIO cr) => Endpoint OUT transType sAlt pr -> WriteAction cr | | | | control :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> RequestType -> Recipient -> Word8 -> Word16 -> Word16 -> Timeout -> cr () | | readControl :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> RequestType -> Recipient -> Word8 -> Word16 -> Word16 -> ReadAction cr | | writeControl :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> RequestType -> Recipient -> Word8 -> Word16 -> Word16 -> WriteAction cr | | getLanguages :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> cr [LangId] | | getStrDesc :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> StrIx -> LangId -> Size -> cr String | | getStrDescFirstLang :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> StrIx -> Size -> cr String | | kernelDriverActive :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> InterfaceNumber -> cr Bool | | detachKernelDriver :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> InterfaceNumber -> cr () | | attachKernelDriver :: (ParentOf pr cr, MonadIO cr) => RegionalDeviceHandle pr -> InterfaceNumber -> cr () | | withDetachedKernelDriver :: (ParentOf pr cr, MonadCatchIO cr) => RegionalDeviceHandle pr -> InterfaceNumber -> cr α -> cr α |
|
|
|
Device regions
|
|
module Control.Monad.Trans.Region |
|
|
Handy type synonym for a region in which USB devices can be opened which are
automatically closed when the region terminates.
You can run a device region with runRegionT.
|
|
|
Handy type synonym for a device region which has IO as its parent region
which enables it to be:
|
|
Regional device handles
|
|
|
Handy type synonym for a regional handle to an opened USB device.
A regional handle to an opened USB device can be created by applying open or
with to the USB device you wish to open.
Note that you can also duplicate a regional device handle by applying dup to
it.
|
|
|
Convenience function for retrieving the device from the given regional
handle.
|
|
Getting descriptors
|
|
class GetDescriptor α desc | α -> desc, desc -> α where | Source |
|
| Methods | | Get the descriptor of a given USB entity.
|
| | Instances | |
|
|
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 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:
|
|
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.
| Instances | |
|
|
|
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.
|
|
Setting configurations
|
|
|
|
|
|
:: (ParentOf pr cr, MonadCatchIO cr) | | => Config pr | Continuation function.
| -> forall sCfg. ConfigHandle sCfg -> cr α | | -> cr α | | 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 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:
|
|
|
|
This exception can be thrown in:
to indicate that the device was already configured with a setting.
| Instances | |
|
|
|
:: (ParentOf pr cr, MonadCatchIO cr) | | => RegionalDeviceHandle pr | Continuation function
| -> forall sCfg. ConfigHandle sCfg -> cr α | | -> cr α | | 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.
Exceptions:
|
|
|
|
This exception can be thrown in useActiveConfig to indicate that the
device is currently not configured.
| Instances | |
|
|
|
:: (ParentOf pr cr, MonadCatchIO cr) | | => RegionalDeviceHandle pr | Predicate on the configuration
descriptor.
| -> ConfigDesc -> Bool | Continuation function.
| -> forall sCfg. ConfigHandle sCfg -> cr α | | -> cr α | | 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:
|
|
|
|
|
|
Interfaces
|
|
|
A supported interface of a configuration which you can retrieve using
getInterfaces.
To retrieve the Interface descriptors of an interface use getDesc.
| Instances | |
|
|
|
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.
|
|
Claiming interfaces
|
|
|
Handy type synonym for a region in which interfaces can be claimed which are
automatically released when the region terminates.
You can run an interface region with runRegionT.
|
|
|
Handy type synonym for a regional handle to a claimed interface.
A regional handle to a claimed interface can be created by applying claim or
with to the interface you wish to claim.
|
|
|
:: MonadCatchIO pr | | => Interface sCfg | | -> IfRegionT sCfg s pr (RegionalIfHandle sCfg (RegionT (Interface sCfg) s pr)) | | Claim the given interface in the interface region.
Note that: claim = open which just reads better when applied to an
interface.
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:
- BusyException if the interface is already claimed.
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
|
:: MonadCatchIO pr | | => ConfigHandle sCfg | Predicate on the interface descriptors.
| -> Interface -> Bool | Continuation function.
| -> forall s. RegionalIfHandle sCfg (RegionT (Interface sCfg) s pr) -> IfRegionT sCfg s pr α | | -> pr α | | 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 on the
resulting regional handle.
|
|
|
Alternates
|
|
|
A supported Interface alternate setting.
| Instances | |
|
|
|
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 region in which the interface handle was created.
|
|
Setting alternates
|
|
data AlternateHandle sCfg sAlt r | Source |
|
|
|
|
:: (ParentOf pr cr, MonadCatchIO cr) | | => Alternate sCfg pr | Continuation function.
| -> forall sAlt. AlternateHandle sCfg sAlt pr -> cr α | | -> cr α | | 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
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:
|
|
|
|
:: (ParentOf pr cr, MonadCatchIO cr) | | => RegionalIfHandle sCfg pr | Continuation function.
| -> forall sAlt. AlternateHandle sCfg 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.
Exceptions:
|
|
|
|
:: (ParentOf pr cr, MonadCatchIO cr) | | => RegionalIfHandle sCfg pr | Predicate on the interface
descriptor.
| -> InterfaceDesc -> Bool | Continuation function
| -> forall sAlt. AlternateHandle sCfg sAlt pr -> cr α | | -> cr α | | 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:
|
|
|
Endpoints
|
|
data Endpoint transDir transType sAlt r | Source |
|
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 by using getEndpoints.
| Instances | |
|
|
|
:: forall transDir transType sCfg sAlt r . | | => AlternateHandle sCfg sAlt r | Handle to the alternate from
which you want to retrieve its
endpoints.
| -> TransferDirection transDir | Filter all endpoints which have
this transfer direction.
| -> TransferType transType | Filter all endpoints which have
this transfer type.
| -> [Endpoint transDir transType sAlt r] | | Retrieve all the endpoints from the given alternate handle which are of the
given transfer direction and transfer type.
|
|
|
|
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:
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
Transfer directions
|
|
data TransferDirection transDir where | Source |
|
|
|
|
Out transfer direction (host -> device) used for writing.
|
|
|
|
In transfer direction (device -> host) used for reading.
|
|
|
Transfer types
|
|
data TransferType transType where | Source |
|
|
|
|
|
|
|
|
Instances | |
|
|
|
Instances | |
|
|
Endpoint I/O
|
|
|
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.
|
|
|
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.
|
|
|
|
|
|
:: (WriteEndpoint transType, ParentOf pr cr, MonadIO cr) | | => Endpoint OUT transType sAlt pr | | -> WriteAction cr | | Write bytes to an OUT endpoint with either a BULK or INTERRUPT
transfer type.
Exceptions:
- PipeException if the endpoint halted.
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
Control transfers
|
|
|
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.
| Constructors | |
|
|
|
:: (ParentOf pr cr, MonadIO cr) | | => RegionalDeviceHandle pr | The type of request.
| -> RequestType | The recipient of the request.
| -> Recipient | Request.
| -> Word8 | Value.
| -> Word16 | Index.
| -> Word16 | Timeout (in milliseconds) that this
function should wait before giving up due
to no response being received. For no
timeout, use value 0.
| -> Timeout | | -> cr () | | Perform a USB control request that does not transfer data.
The value and index values should be given in host-endian byte order.
Exceptions:
- TimeoutException if the transfer timed out.
- PipeException if the control request was not supported by the device
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
|
:: (ParentOf pr cr, MonadIO cr) | | => RegionalDeviceHandle pr | The type of request.
| -> RequestType | The recipient of the request.
| -> Recipient | Request.
| -> Word8 | Value.
| -> Word16 | Index.
| -> Word16 | | -> ReadAction cr | | Perform a USB control read.
The value and index values should be given in host-endian byte order.
Exceptions:
- PipeException if the control request was not supported by the device
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
|
:: (ParentOf pr cr, MonadIO cr) | | => RegionalDeviceHandle pr | The type of request.
| -> RequestType | The recipient of the request.
| -> Recipient | Request.
| -> Word8 | Value.
| -> Word16 | Index.
| -> Word16 | | -> WriteAction cr | | Perform a USB control write.
The value and index values should be given in host-endian byte order.
Exceptions:
- PipeException if the control request was not supported by the device
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
String descriptors
|
|
|
Retrieve a list of supported languages.
This function may throw USBExceptions.
|
|
|
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 Handle. In other
words I would like to get a type error when they are some arbitrary number or
come from another device.
|
|
|
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.
|
|
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:
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
Detach a kernel driver from an interface.
If successful, you will then be able to claim the interface and perform I/O.
Exceptions:
- NotFoundException if no kernel driver was active.
- InvalidParamException if the interface does not exist.
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
|
Re-attach an interface's kernel driver, which was previously
detached using detachKernelDriver.
Exceptions:
- NotFoundException if no kernel driver was active.
- InvalidParamException if the interface does not exist.
- NoDeviceException if the device has been disconnected.
- BusyException if the driver cannot be attached because the interface
is claimed by a program or driver.
- Another USBException.
|
|
|
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:
- NoDeviceException if the device has been disconnected.
- Another USBException.
|
|
Produced by Haddock version 2.6.0 |