#if __GLASGOW_HASKELL__ >= 704
#endif
#ifdef HAS_EVENT_MANAGER
#endif
module System.USB.IO.Iteratee
( enumReadBulk
, enumReadInterrupt
#ifdef HAS_EVENT_MANAGER
, enumReadIsochronous
#endif
) where
import Prelude ( fromIntegral )
import Data.Function ( ($) )
import Data.Word ( Word8 )
import Data.Maybe ( Maybe(Nothing, Just) )
import Control.Exception ( toException )
import Control.Monad ( (>>=), return )
import Foreign.Storable ( peek )
import Foreign.Ptr ( castPtr )
import Foreign.Marshal.Alloc ( alloca, allocaBytes )
#if __GLASGOW_HASKELL__ < 700
import Prelude ( fromInteger )
import Control.Monad ( fail )
#endif
import Data.Eq.Unicode ( (≡), (≢) )
import Data.Function.Unicode ( (∘) )
import Data.Bool.Unicode ( (∧) )
import Bindings.Libusb
import Control.Monad.Trans.Control ( MonadBaseControl, StM, control )
import System.USB.DeviceHandling ( DeviceHandle )
import System.USB.Descriptors ( EndpointAddress )
import System.USB.IO ( Timeout, Size )
import System.USB.Internal ( C'TransferFunc
, withDevHndlPtr
, marshalEndpointAddress
, convertUSBException
)
#ifdef __HADDOCK__
import System.USB.Descriptors ( maxPacketSize, endpointMaxPacketSize
, TransferDirection(In)
, TransferType(Bulk, Interrupt)
)
#endif
import Data.Iteratee.Base ( Stream(EOF, Chunk), runIter, idoneM )
import Data.Iteratee.Iteratee ( Enumerator, throwErr )
import Data.Iteratee.Base.ReadableChunk ( ReadableChunk(readFromPtr) )
import Data.NullPoint ( NullPoint(empty) )
#ifdef HAS_EVENT_MANAGER
import Data.Bool ( otherwise )
import Data.List ( (++) )
import Data.Ord ( (<) )
import Foreign.Ptr ( Ptr, plusPtr )
import Foreign.Storable ( poke )
import Foreign.Marshal.Array ( advancePtr )
import System.IO ( IO )
import Text.Show ( show )
import Prelude ( (+), error, String )
import Control.Exception ( SomeException, mask )
import Data.Iteratee.Base ( Iteratee )
import Data.Vector ( Vector )
import qualified Data.Vector.Unboxed as Unboxed ( Vector )
import qualified Data.Vector.Storable as Storable ( Vector )
import qualified Data.Vector.Generic as VG ( empty, length, map, sum
, convert, unsafeFreeze
)
import qualified Data.Vector.Generic.Mutable as VGM ( unsafeNew, unsafeWrite )
import System.USB.Exceptions ( USBException(..), ioException )
#ifdef __HADDOCK__
import System.USB.Descriptors ( TransferType(Isochronous), maxIsoPacketSize )
#endif
import System.USB.Internal ( getWait, Wait
, C'TransferType
, allocaTransfer, withCallback
, newLock, release
, pokeVector
, initIsoPacketDesc
)
#endif
enumReadBulk ∷ (ReadableChunk s Word8, NullPoint s, MonadBaseControl IO m)
⇒ DeviceHandle
→ EndpointAddress
→ Size
→ Timeout
→ Enumerator s m α
enumReadBulk devHndl
#ifdef HAS_EVENT_MANAGER
| Just wait ← getWait devHndl =
enumReadAsync wait c'LIBUSB_TRANSFER_TYPE_BULK devHndl
#endif
| otherwise = enumReadSync c'libusb_bulk_transfer devHndl
enumReadInterrupt ∷ (ReadableChunk s Word8, NullPoint s, MonadBaseControl IO m)
⇒ DeviceHandle
→ EndpointAddress
→ Size
→ Timeout
→ Enumerator s m α
enumReadInterrupt devHndl
#ifdef HAS_EVENT_MANAGER
| Just wait ← getWait devHndl =
enumReadAsync wait c'LIBUSB_TRANSFER_TYPE_INTERRUPT devHndl
#endif
| otherwise = enumReadSync c'libusb_interrupt_transfer devHndl
type Run s m α = Stream s → IO (StM m (Iteratee s m α))
#ifdef HAS_EVENT_MANAGER
enumReadAsync ∷ ∀ s m α
. (ReadableChunk s Word8, NullPoint s, MonadBaseControl IO m)
⇒ Wait
→ C'TransferType
→ DeviceHandle → EndpointAddress → Size → Timeout
→ Enumerator s m α
enumReadAsync wait transType = \devHndl endpointAddr chunkSize timeout →
enum wait
transType
VG.empty
devHndl endpointAddr
timeout
chunkSize
withResult withResult
where
withResult ∷ WithResult s m α
withResult transPtr bufferPtr cont stop = do
n ← peek $ p'libusb_transfer'actual_length transPtr
if n ≡ 0
then stop $ Chunk empty
else readFromPtr bufferPtr (fromIntegral n) >>= cont ∘ Chunk
type WithResult s m α = Ptr C'libusb_transfer → Ptr Word8
→ Run s m α
→ Run s m α
→ IO (StM m (Iteratee s m α))
enum ∷ ∀ m s α
. MonadBaseControl IO m
⇒ Wait
→ C'TransferType
→ Storable.Vector C'libusb_iso_packet_descriptor
→ DeviceHandle → EndpointAddress
→ Timeout
→ Size
→ WithResult s m α → WithResult s m α
→ Enumerator s m α
enum wait
transType
isos
devHndl endpointAddr
timeout
chunkSize
onCompletion onTimeout = \iter →
control $ \runInIO →
withDevHndlPtr devHndl $ \devHndlPtr →
allocaBytes chunkSize $ \bufferPtr → do
let nrOfIsos = VG.length isos
allocaTransfer nrOfIsos $ \transPtr → do
lock ← newLock
withCallback (\_ → release lock) $ \cbPtr → do
poke (p'libusb_transfer'dev_handle transPtr) devHndlPtr
poke (p'libusb_transfer'endpoint transPtr) (marshalEndpointAddress endpointAddr)
poke (p'libusb_transfer'type transPtr) transType
poke (p'libusb_transfer'timeout transPtr) (fromIntegral timeout)
poke (p'libusb_transfer'length transPtr) (fromIntegral chunkSize)
poke (p'libusb_transfer'callback transPtr) cbPtr
poke (p'libusb_transfer'buffer transPtr) (castPtr bufferPtr)
poke (p'libusb_transfer'num_iso_packets transPtr) (fromIntegral nrOfIsos)
pokeVector (p'libusb_transfer'iso_packet_desc transPtr) isos
let waitForTermination ∷ IO ()
waitForTermination = wait timeout lock transPtr
go ∷ Enumerator s m α
go i = runIter i idoneM on_cont
on_cont ∷ (Stream s → Iteratee s m α)
→ Maybe SomeException
→ m (Iteratee s m α)
on_cont _ (Just e) = return $ throwErr e
on_cont k Nothing =
control $ \runInIO' →
mask $ \restore → do
let stop, cont ∷ Run s m α
stop = runInIO' ∘ return ∘ k
cont = runInIO' ∘ go ∘ k
ex ∷ USBException → IO (StM m (Iteratee s m α))
ex = stop ∘ EOF ∘ Just ∘ toException
err ← c'libusb_submit_transfer transPtr
if err ≢ c'LIBUSB_SUCCESS
then ex $ convertUSBException err
else do
waitForTermination
restore $ do
status ← peek $ p'libusb_transfer'status transPtr
let run withResult = withResult transPtr bufferPtr cont stop
case status of
ts | ts ≡ c'LIBUSB_TRANSFER_COMPLETED → run onCompletion
| ts ≡ c'LIBUSB_TRANSFER_TIMED_OUT → run onTimeout
| ts ≡ c'LIBUSB_TRANSFER_ERROR → ex ioException
| ts ≡ c'LIBUSB_TRANSFER_NO_DEVICE → ex NoDeviceException
| ts ≡ c'LIBUSB_TRANSFER_OVERFLOW → ex OverflowException
| ts ≡ c'LIBUSB_TRANSFER_STALL → ex PipeException
| ts ≡ c'LIBUSB_TRANSFER_CANCELLED →
moduleError "transfer status can't be Cancelled!"
| otherwise → moduleError $ "Unknown transfer status: "
++ show ts ++ "!"
runInIO $ go iter
moduleError ∷ String → error
moduleError msg = error $ thisModule ++ ": " ++ msg
thisModule ∷ String
thisModule = "System.USB.IO.Iteratee"
needThreadedRTSError ∷ String → error
needThreadedRTSError msg = moduleError $ msg ++
" is only supported when using the threaded runtime. " ++
"Please build your program with -threaded."
enumReadIsochronous ∷ ∀ s m α
. (ReadableChunk s Word8, MonadBaseControl IO m)
⇒ DeviceHandle
→ EndpointAddress
→ Unboxed.Vector Size
→ Timeout
→ Enumerator (Vector s) m α
enumReadIsochronous devHndl endpointAddr sizes timeout
| Just wait ← getWait devHndl =
enum wait
c'LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
(VG.map initIsoPacketDesc $ VG.convert sizes)
devHndl endpointAddr
timeout
totalSize
onCompletion onTimeout
| otherwise = needThreadedRTSError "enumReadIsochronous"
where
totalSize = VG.sum sizes
nrOfIsos = VG.length sizes
onCompletion, onTimeout ∷ WithResult (Vector s) m α
onCompletion transPtr bufferPtr cont _ = do
mv ← VGM.unsafeNew nrOfIsos
let isoArrayPtr = p'libusb_transfer'iso_packet_desc transPtr
go ix ptr
| ix < nrOfIsos = do
let isoPtr = advancePtr isoArrayPtr ix
l ← peek (p'libusb_iso_packet_descriptor'length isoPtr)
a ← peek (p'libusb_iso_packet_descriptor'actual_length isoPtr)
let transferred = fromIntegral a
s ← readFromPtr ptr transferred
VGM.unsafeWrite mv ix s
go (ix+1) (ptr `plusPtr` fromIntegral l)
| otherwise = VG.unsafeFreeze mv >>= cont ∘ Chunk
go 0 bufferPtr
onTimeout _ _ _ stop = stop ∘ EOF ∘ Just ∘ toException $ TimeoutException
#endif
enumReadSync ∷ ∀ s m α
. (ReadableChunk s Word8, NullPoint s, MonadBaseControl IO m)
⇒ C'TransferFunc → ( DeviceHandle
→ EndpointAddress
→ Size
→ Timeout
→ Enumerator s m α
)
enumReadSync c'transfer = \devHndl
endpoint
chunkSize
timeout → \iter →
control $ \runInIO →
withDevHndlPtr devHndl $ \devHndlPtr →
alloca $ \transferredPtr →
allocaBytes chunkSize $ \dataPtr →
let go ∷ Enumerator s m α
go i = runIter i idoneM on_cont
on_cont ∷ (Stream s → Iteratee s m α) → Maybe SomeException → m (Iteratee s m α)
on_cont _ (Just e) = return $ throwErr e
on_cont k Nothing =
control $ \runInIO' → do
let stop, cont ∷ Run s m α
stop = runInIO' ∘ return ∘ k
cont = runInIO' ∘ go ∘ k
err ← c'transfer devHndlPtr
(marshalEndpointAddress endpoint)
(castPtr dataPtr)
(fromIntegral chunkSize)
transferredPtr
(fromIntegral timeout)
if err ≢ c'LIBUSB_SUCCESS ∧
err ≢ c'LIBUSB_ERROR_TIMEOUT
then stop ∘ EOF ∘ Just ∘ toException $ convertUSBException err
else do
t ← peek transferredPtr
if t ≡ 0
then stop $ Chunk empty
else readFromPtr dataPtr (fromIntegral t) >>= cont ∘ Chunk
in runInIO $ go iter