úÎ -ó* @      !"#$%&'()*+,-./0123456789:;<=>?#Bas van Dijk <v.dijk.bas@gmail.com>D0Control transfers can have three request types: Standard, Class and Vendor. We disallow Standard, requests however because with them you can 9destroy the safety guarantees that this module provides. -Class of transfer types that support writing (Only   and   transfer types are supported.) Write bytes to an  endpoint. (Handy type synonym for write transfers. A  WriteAction; is a function which takes a timeout and the bytestring to Owrite. The function returns an action which, when exectued, returns the number Nof bytes that were actually written paired with an indication if the transfer  timed out. .Class of transfer types that support reading. (Only   and   transfer types are supported.) Read bytes from an   endpoint. 'Handy type synonym for read transfers. A  ReadActionB is a function which takes a timeout and a size which defines how Imany bytes to read. The function returns an action which, when executed, Qperforms the actual read and returns the bytestring that was read paired with an &indication if the transfer timed out.  Interrupt. endpoints support read and write operations. Bulk. endpoints support read and write operations.  Isochronous endpoints don'%t support read and write operations. Control endpoints don'%t support read and write operations. 9In transfer direction (device -> host) used for reading. :Out transfer direction (host -> device) used for writing. I/CO operations on endpoints are type-safe. You can only read from an endpoint with an  : transfer direction and you can only write to an endpoint with an  transfer direction. JReading and writing also have different implementations for the different endpoint transfer types like:   and  . I/O with endpoints of other transfer types like   and   is not possible. LThis type lifts the transfer direction and transfer type information to the type-level so that I/7O operations can specify which endpoints they support. A supported endpoint from an . (A handle to a setted alternate setting.  A supported  alternate setting. A handle to a claimed . A supported interface of a .  This exception can be thrown in * to indicate that the $device is currently not configured. !This exception can be thrown in:  %  )  *  0  1 Cto indicate that the device was already configured with a setting. A handle to an active . A supported configuration of a @. AThe ParentOf class declares the parent/$child relationship between regions.  A region p is the parent of region c if they're either equivalent like:   DeviceRegionT sP mP A DeviceRegionT sP mP or if p0 is the parent of the parent of the child like:  DeviceRegionT sP mP A DeviceRegionT sC''' 4 (DeviceRegionT sC'' * (... 8 (DeviceRegionT sC' B (DeviceRegionT sP mP)))) A handle to an opened @. LA region which does not have parent regions and can be directly executed in B by / or concurrently executed in another region by . CMVar8 which keeps track of wheter an alternate has been set.  See: 0. DMVar; which keeps track of wheter a configuration has been set.  See: ). EJBecause regions can be nested and device handles can be duplicated from a Mchild region to a parent region we need to keep a reference count per opened device. The reference count is: . initialized at 1 when a device is created in !, 2 incremented when we duplicate a device handle in ", & decremented on exit from a region in F. GOnly when the reference count reaches 0 the device is actually closed. Note that the reference count IORef, is shared between threads. I make sure the 'modifications happen atomically using: G. A monad transformer in which @s can be opened wich are .automatically closed on exit from the region. HFRegions need to know the list of devices that have been opened in and Pduplicated to the region. Regions also need to be able to update that list when new devices are registered. 3There are to logical choices for representing this  [OpenedDevice] state:  StateT [OpenedDevice] ReaderT (IORef [OpenedDevice])LThe former has the advantage of being able to be run in any monad while the #latter can only be executed in the IO monad because of the IORef . The latter 5however may be more efficient because the state doesn't need to be passed around. 5Because eventually all regions have to be run in the IO monad anyway I have .choosen the latter for its better efficiency. Execute a region. All @3s which have been opened in the given region using !, and which haven't been duplicated using ", will be closed on Qexit from this function wether by normal termination or by raising an exception. OAlso 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 s7 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.) @s are parameterised with the region in which they were created. (So device handles which were created by  openDevice in the given region have this sG 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. 9TODO: Say something more about this nesting of regions... "Convenience funtion for running a  top-level region in B.  Note that: runTopDeviceRegion =  )Return a region which executes the given  top-level region in a new thread. 7Note that the forked region has the same type variable s as the resulting region. This means that all !s which can be referenced in the >resulting region can also be referenced in the forked region. +Transform the computation inside a region. Lift a I operation to the new monad. !Open a device in a region. ONote that the returned device handle is parameterised with the region in which Mit was created. This is to ensure that device handles can never escape their Lregion and to support operations on device handles that are used in a child 6region of the region in which the device was created. DThis is a non-blocking function; no requests are sent over the bus.  Exceptions:  NoMemException* if there is a memory allocation failure.  AccessException+ if the user has insufficient permissions.  NoDeviceException& if the device has been disconnected.  Another  USBException. "0Duplicate a device handle in the parent region. 3For 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 9d1h :: DeviceHandle (DeviceRegion sC (DeviceRegion sP m)) where sC is bound by the inner runDeviceRegionT and sP is bound by the outer runDeviceRegionT. 4Suppose 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 sC, escapes the inner region. FHowever, if you duplicate the device handle you can safely return it.    d1hDup <- dupDeviceHandle d1h  return d1hDup  Note that +d1hDup :: DeviceHandle (DeviceRegionT sP m) 4Back in the parent region you can safely operate on d1hDup. #GA convenience function which opens the given device, applies the given Gfunction to the resulting device handle and runs the resulting region.  Note that: withDevice dev f =  $ ! dev >>= f $,Retrieve the device from the device handle. %3Perform a USB port reset to reinitialize a device. LThe system will attempt to restore the previous configuration and alternate (settings after the reset has completed. <You can only reset a device when all computations passed to ) or *# have been terminated. If you call  resetDevice and such a computation is still running a  exception is thrown. LIf the reset fails, the descriptors change, or the previous state cannot be Prestored, the device will appear to be disconnected and reconnected. This means Othat 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. OTODO: Think about how to handle the implications of the the previous paragraph! EThis is a blocking function which usually incurs a noticeable delay.  Exceptions:  NotFoundException* if re-enumeration is required, or if the ! device has been disconnected.  ' if a configuration has been set using ) or  *.  Another  USBException. &>Retrieve the supported configurations from the device handle. MNote that the configuration is parameterised by the same region in which the Jdevice handle was created. This ensures you can never use a configuration outside that region. 'DRetrieve the configuration descriptor from the given configuration. (0Duplicate a configuration in the parent region.  Also see: ". )LSet the active configuration for a device and then apply the given function 'to the resulting configuration handle. OUSB devices support multiple configurations of which only one can be active at 2any given time. When a configuration is set using ) or *> no threads can set a new configuration until the computation Apassed to these functions terminates. If you do try to set one a  exception will be thrown. PThe operating system may or may not have already set an active configuration on Pthe device. It is up to your application to ensure the correct configuration is Qselected before you attempt to claim interfaces and perform other operations. If 5you want to use the current active configuration use *. KIf you call this function on a device already configured with the selected Mconfiguration, then this function will act as a lightweight device reset: it Pwill issue a SET_CONFIGURATION request using the current configuration, causing Nmost 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:   BusyException& if interfaces are currently claimed.  NoDeviceException% if the device has been disconnected  / if a configuration has already been set using  ) or *.  Another  USBException. *MApply the given function to the configuration handle of the currently active *configuration of the given device handle. HThis function needs to determine the current active configuration. This <information may be cached by the operating system. If it isn't cached this Jfunction will block while a control transfer is submitted to retrieve the  information. VTODO: I'm not yet sure if this is the best way of handling already configured devices. !So this may change in the future!  Exceptions:  NoDeviceException& if the device has been disconnected.  / if a configuration has already been set using  ) or *.  " if the device is not configured.  Aanother  USBException. +ARetrieve the supported interfaces from the configuration handle. KNote that the interface is parameterised by the same type variables as the Nconfiguration handle. This ensures you can never use an interface outside the  scope of the function passed to ) or *. ,?Retrieve the alternate interface descriptors of the interface. -JClaim the given interface, then apply the given function to the resulting Minterface handle and finally release the interface on exit from the function 9wether by normal termination or by raising an exception. ?Note that it is allowed to claim an already-claimed interface. LClaiming of interfaces is a purely logical operation; it does not cause any Mrequests to be sent over the bus. Interface claiming is used to instruct the Nunderlying 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. .ERetrieve the supported alternate settings from the interface handle. ONote that the alternate setting is parameterised by the same type variables as Jthe interface handle. This ensures you can never use an alternate setting ,outside the scope of the function passed to -. /=Retrieve the interface descriptor of this alternate setting. 0HActivate an alternate setting for an interface and then apply the given ,function to the resulting alternate handle. OSimillary to configurations, interfaces support multiple alternate settings of Owhich only one can be active at any given time. When an alternate is set using 0 or 1$ no threads can set a new alternate Quntil the computation passed to these functions terminates. If you do try to set one a  exception will be thrown. QThe operating system may already have set an alternate for the interface. If you 0want to use this currently active alternate use 1. This is a blocking function.  Exceptions:  NoDeviceException& if the device has been disconnected.  , if an alternate has already been set using  0 or 1.  Another  USBException. 1IApply the given function to the alternate handle of the currently active (alternate of the give interface handle. KTo determine the current active alternate this function will block while a ;control transfer is submitted to retrieve the information. VTODO: I'm not yet sure if this is the best way of handling already configured devices. !So this may change in the future!  Exceptions:  NoDeviceException& if the device has been disconnected.  , if an alternate has already been set using  0 or 1.  Aanother  USBException. 2<Retrieve the supported endpoints from the alternate handle. JNote that the endpoint is parameterised by the same type variables as the Oalternate handle. This ensures you can never use an endpoint outside the scope of the function passed to 0 or 1. 3The > type is not rich enough to encode the transfer direction and Qtransfer type. In order to introduce this type information we have to filter the Ilist of endpoints and get back a list of endpoint handles which have the Mspecified transfer direction and transfer type and also expres this in their type. 4ARetrieve the endpoint descriptor from the given endpoint handle. 5Clear the halt/!stall condition for an endpoint. QEndpoints with halt status are unable to receive or transmit data until the halt condition is stalled. LYou 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. 6Perform a USB control& request that does not transfer data. The value and index3 values should be given in host-endian byte order.  Exceptions:  TimeoutException if the transfer timed out.   PipeException8 if the control request was not supported by the device  NoDeviceException& if the device has been disconnected.  Another  USBException. 7Perform a USB control read. The value and index3 values should be given in host-endian byte order.  Exceptions:   PipeException8 if the control request was not supported by the device  NoDeviceException& if the device has been disconnected.  Another  USBException. 8Perform a USB control write. The value and index3 values should be given in host-endian byte order.  Exceptions:   PipeException8 if the control request was not supported by the device  NoDeviceException& if the device has been disconnected.  Another  USBException. 9(Retrieve a list of supported languages. This function may throw  USBExceptions. :,Retrieve a string descriptor from a device. PThis is a convenience function which formulates the appropriate control message Oto 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 :4 I would like the type system to guarantee that the given StrIx and LangId actually belong to the given  DeviceHandle . In other Nwords I would like to get a type error when they are some arbitrary number or come from another device. ;ERetrieve a string descriptor from a device using the first supported  language. PThis is a convenience function which formulates the appropriate control message Oto retrieve the descriptor. The string returned is Unicode, as detailed in the USB specifications. This function may throw  USBExceptions. <8Determine if a kernel driver is active on an interface. QIf 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. IIf 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 =.  Exceptions:  NotFoundException! if no kernel driver was active.  InvalidParamException" if the interface does not exist.  NoDeviceException& if the device has been disconnected.   BusyException8 if the driver cannot be attached because the interface & is claimed by a program or driver.  Another  USBException. ?FIf a kernel driver is active on the specified interface the driver is Pdetached and the given action is executed. If the action terminates, whether by Mnormal termination or by raising an exception, the kernel driver is attached Qagain. 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. JMonadic if ... then ... else ... @  !"#$%&'()*+,-./0123456789:;<=>?@ !"#$%&'()*+,-./012345  6789:;<=>?@  !"#$%&'()*+,-./0123456789:;<=>?K      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV usb-safe-0.2System.USB.Safeusb-0.3System.USB.Enumerationbase System.IO Data.IORefmonads-fd-0.0.0.1Control.Monad.Error.Class RequestTypeVendorClass WriteEndpoint writeEndpoint WriteAction ReadEndpoint readEndpoint ReadAction InterruptBulk IsochronousControlInOutEndpointHandleEndpointAlternateHandle AlternateInterfaceHandle InterfaceNoActiveConfigSettingAlreadySet ConfigHandleConfig DeviceHandleTopDeviceRegion DeviceRegionTrunDeviceRegionTrunTopDeviceRegionforkTopDeviceRegionmapDeviceRegionT liftCatch openDevicedupDeviceHandle withDevice getDevice resetDevice getConfigs getConfigDesc dupConfig withConfigwithActiveConfig getInterfacesgetInterfaceDescs withInterface getAlternatesgetInterfaceDesc withAlternatewithActiveAlternate getEndpointsfilterEndpointsgetEndpointDesc clearHaltcontrol readControl writeControl getLanguages getStrDescgetStrDescFirstLangkernelDriverActivedetachKernelDriverattachKernelDriverwithDetachedKernelDriverSystem.USB.InternalDeviceParentOf GHC.IOBaseIOAlternateAlreadySetMVarConfigAlreadySetMVar RefCntIORefrunWithatomicModifyIORefunDeviceRegionT catchErrorifM