-
Notifications
You must be signed in to change notification settings - Fork 13
/
StandardDeviceRequests.hs
286 lines (250 loc) · 11 KB
/
StandardDeviceRequests.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
{-# LANGUAGE CPP, NoImplicitPrelude, UnicodeSyntax, DeriveDataTypeable #-}
#if __GLASGOW_HASKELL__ >= 704
{-# LANGUAGE Trustworthy #-}
#endif
--------------------------------------------------------------------------------
-- |
-- Module : System.USB.IO.StandardDeviceRequests
-- Copyright : (c) 2009–2012 Bas van Dijk
-- License : BSD3 (see the file LICENSE)
-- Maintainer : Bas van Dijk <v.dijk.bas@gmail.com>
--
-- This module provides functions for performing standard device requests.
-- The functions are primarily used for testing USB devices.
--
-- To avoid name clashes with functions from @System.USB@ it is advised to use
-- an explicit import list or a qualified import.
--
--------------------------------------------------------------------------------
module System.USB.IO.StandardDeviceRequests
( setHalt
, setConfig, getConfig
, clearRemoteWakeup
, setRemoteWakeup
, setStandardTestMode, TestMode(..)
, getInterfaceAltSetting
, getDeviceStatus
, getEndpointStatus
, setDeviceAddress
, synchFrame, FrameNumber
) where
--------------------------------------------------------------------------------
-- Imports
--------------------------------------------------------------------------------
-- from base:
import Data.Bits ( testBit, shiftL )
import Data.Bool ( Bool )
import Data.Data ( Data )
import Data.Eq ( Eq, (==) )
import Data.Function ( ($), (.) )
import Data.Functor ( fmap )
import Data.Maybe ( Maybe(Nothing, Just), maybe )
import Data.Typeable ( Typeable )
import Data.Word ( Word8, Word16 )
import Prelude ( (+), (*), fromIntegral, Enum )
import System.IO ( IO )
import Text.Read ( Read )
import Text.Show ( Show )
#if __GLASGOW_HASKELL__ < 700
import Prelude ( fromInteger )
import Data.Eq ( (==) )
#endif
-- from bytestring:
import qualified Data.ByteString as B ( ByteString, head, unpack )
-- from bindings-libusb:
import Bindings.Libusb ( c'LIBUSB_REQUEST_SET_FEATURE
, c'LIBUSB_REQUEST_SET_CONFIGURATION
, c'LIBUSB_REQUEST_GET_CONFIGURATION
, c'LIBUSB_REQUEST_CLEAR_FEATURE
, c'LIBUSB_REQUEST_GET_INTERFACE
, c'LIBUSB_REQUEST_GET_STATUS
, c'LIBUSB_REQUEST_SET_ADDRESS
, c'LIBUSB_REQUEST_SYNCH_FRAME
)
-- from usb:
import System.USB.DeviceHandling ( DeviceHandle
, ConfigValue
, InterfaceNumber
, InterfaceAltSetting
)
#if __HADDOCK__
import qualified System.USB.DeviceHandling as USB ( setConfig, getConfig )
#endif
import System.USB.Descriptors ( EndpointAddress
, DeviceStatus(..)
)
import System.USB.IO ( Timeout
, RequestType(Standard)
, Recipient( ToDevice
, ToInterface
, ToEndpoint
)
, Value
, control, readControlExact
)
import System.USB.Internal ( marshalEndpointAddress )
import Utils ( genFromEnum )
--------------------------------------------------------------------------------
-- Standard Device Requests
-------------------------------------------------------------------------------
-- See: USB 2.0 Spec. section 9.4
-- Standard Feature Selectors:
-- See: USB 2.0 Spec. table 9-6
haltFeature, remoteWakeupFeature, testModeFeature ∷ Value
haltFeature = 0
remoteWakeupFeature = 1
testModeFeature = 2
-- | See: USB 2.0 Spec. section 9.4.9
setHalt ∷ DeviceHandle → EndpointAddress → (Timeout → IO ())
setHalt devHndl endpointAddr = control devHndl
Standard
ToEndpoint
c'LIBUSB_REQUEST_SET_FEATURE
haltFeature
(marshalEndpointAddress endpointAddr)
-- | See: USB 2.0 Spec. section 9.4.7
--
-- /This function is for testing purposes only!/
--
-- You should normally use @System.USB.DeviceHandling.'USB.setConfig'@ because
-- that function notifies the underlying operating system about the changed
-- configuration.
setConfig ∷ DeviceHandle → Maybe ConfigValue → (Timeout → IO ())
setConfig devHndl mbConfigValue = control devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_SET_CONFIGURATION
(marshal mbConfigValue)
0
where
marshal ∷ Maybe ConfigValue → Value
marshal = maybe 0 fromIntegral
-- | See: USB 2.0 Spec. section 9.4.2
--
-- /This function is for testing purposes only!/
--
-- You should normally use @System.USB.DeviceHandling.'USB.getConfig'@ because
-- that functon may exploit operating system caches (no I/O involved).
getConfig ∷ DeviceHandle → (Timeout → IO (Maybe ConfigValue))
getConfig devHndl = fmap (unmarshal . B.head)
. readControlExact devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_GET_CONFIGURATION
0
0
1
where
unmarshal ∷ Word8 → Maybe ConfigValue
unmarshal 0 = Nothing
unmarshal n = Just $ fromIntegral n
-- | See: USB 2.0 Spec. section 9.4.1
clearRemoteWakeup ∷ DeviceHandle → (Timeout → IO ())
clearRemoteWakeup devHndl =
control devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_CLEAR_FEATURE
remoteWakeupFeature
0
-- | See: USB 2.0 Spec. section 9.4.9
setRemoteWakeup ∷ DeviceHandle → (Timeout → IO ())
setRemoteWakeup devHndl =
control devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_SET_FEATURE
remoteWakeupFeature
0
-- | See: USB 2.0 Spec. section 9.4.9
-- TODO: What about vendor-specific test modes?
setStandardTestMode ∷ DeviceHandle → TestMode → (Timeout → IO ())
setStandardTestMode devHndl testMode =
control devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_SET_FEATURE
testModeFeature
((genFromEnum testMode + 1) `shiftL` 8)
-- | See: USB 2.0 Spec. table 9-7
data TestMode = Test_J
| Test_K
| Test_SE0_NAK
| Test_Packet
| Test_Force_Enable
deriving (Eq, Show, Read, Enum, Data, Typeable)
-- | See: USB 2.0 Spec. section 9.4.4
getInterfaceAltSetting ∷ DeviceHandle → InterfaceNumber → (Timeout → IO InterfaceAltSetting)
getInterfaceAltSetting devHndl ifNum =
fmap B.head . readControlExact devHndl
Standard
ToInterface
c'LIBUSB_REQUEST_GET_INTERFACE
0
(fromIntegral ifNum)
1
-- | See: USB 2.0 Spec. section 9.4.5
getDeviceStatus ∷ DeviceHandle → (Timeout → IO DeviceStatus)
getDeviceStatus devHndl =
fmap (unmarshal . B.head) . readControlExact devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_GET_STATUS
0
0
2
where
unmarshal ∷ Word8 → DeviceStatus
unmarshal a = DeviceStatus { remoteWakeup = testBit a 1
, selfPowered = testBit a 0
}
-- | See: USB 2.0 Spec. section 9.4.5
getEndpointStatus ∷ DeviceHandle → EndpointAddress → (Timeout → IO Bool)
getEndpointStatus devHndl endpointAddr =
fmap ((1 ==) . B.head) . readControlExact devHndl
Standard
ToEndpoint
c'LIBUSB_REQUEST_GET_STATUS
0
(marshalEndpointAddress endpointAddr)
2
-- | See: USB 2.0 Spec. section 9.4.6
setDeviceAddress ∷ DeviceHandle → Word16 → (Timeout → IO ())
setDeviceAddress devHndl deviceAddr = control devHndl
Standard
ToDevice
c'LIBUSB_REQUEST_SET_ADDRESS
deviceAddr
0
-- TODO: setDescriptor See: USB 2.0 Spec. section 9.4.8
-- TODO Sync Frame klopt niet helemaal, je kunt met deze request ook het frame number setten!
{-|
This request is used to set and then report an endpoint's synchronization frame.
When an endpoint supports isochronous transfers, the endpoint may also require
per-frame transfers to vary in size according to a specific pattern. The host
and the endpoint must agree on which frame the repeating pattern begins. The
number of the frame in which the pattern began is returned to the host.
If a high-speed device supports the Synch Frame request, it must internally
synchronize itself to the zeroth microframe and have a time notion of classic
frame. Only the frame number is used to synchronize and reported by the device
endpoint (i.e., no microframe number). The endpoint must synchronize to the
zeroth microframe.
This value is only used for isochronous data transfers using implicit pattern
synchronization. If the specified endpoint does not support this request, then
the device will respond with a Request Error.
See: USB 2.0 Spec. section 9.4.11
-}
synchFrame ∷ DeviceHandle → EndpointAddress → (Timeout → IO FrameNumber)
synchFrame devHndl endpointAddr =
fmap unmarshal . readControlExact devHndl
Standard
ToEndpoint
c'LIBUSB_REQUEST_SYNCH_FRAME
0
(marshalEndpointAddress endpointAddr)
2
where
unmarshal ∷ B.ByteString → FrameNumber
unmarshal bs = let [h, l] = B.unpack bs
in fromIntegral h * 256 + fromIntegral l
type FrameNumber = Word16