fuyu-gpio 0.0.9.0 → 0.1.0.0
raw patch · 28 files changed
+1286/−1268 lines, 28 filesdep +filepathdep +manageddep ~fuyu-gpio-directnew-component:exe:03-monitornew-component:exe:04-led-and-buttonPVP ok
version bump matches the API change (PVP)
Dependencies added: filepath, managed
Dependency ranges changed: fuyu-gpio-direct
API changes (from Hackage documentation)
- Fuyu.GPIO: ChipInfoFailed :: Errno -> GpioException
- Fuyu.GPIO: ChipOpenFailed :: FilePath -> Errno -> GpioException
- Fuyu.GPIO: CustomGpioError :: String -> Errno -> GpioException
- Fuyu.GPIO: EventBufferNewFailed :: Errno -> GpioException
- Fuyu.GPIO: EventReady :: !a -> WaitResult a
- Fuyu.GPIO: InvalidArgument :: String -> GpioException
- Fuyu.GPIO: LineConfigNewFailed :: Errno -> GpioException
- Fuyu.GPIO: LineInfoCopyFailed :: Errno -> GpioException
- Fuyu.GPIO: LineInfoFailed :: Errno -> GpioException
- Fuyu.GPIO: LineReconfigureFailed :: Errno -> GpioException
- Fuyu.GPIO: LineRequestFailed :: Errno -> GpioException
- Fuyu.GPIO: LineSettingsNewFailed :: Errno -> GpioException
- Fuyu.GPIO: LineSettingsSetFailed :: Errno -> GpioException
- Fuyu.GPIO: LineValueReadFailed :: Errno -> GpioException
- Fuyu.GPIO: LineValueWriteFailed :: Errno -> GpioException
- Fuyu.GPIO: RawEdgeEventCopyFailed :: Errno -> GpioException
- Fuyu.GPIO: ReadEdgeEventsFailed :: Errno -> GpioException
- Fuyu.GPIO: ReadInfoEventFailed :: Errno -> GpioException
- Fuyu.GPIO: ReadyChip :: Chip -> ReadyChip
- Fuyu.GPIO: RequestConfigNewFailed :: Errno -> GpioException
- Fuyu.GPIO: TimeoutResult :: WaitResult a
- Fuyu.GPIO: WaitEdgeEventsFailed :: Errno -> GpioException
- Fuyu.GPIO: WaitInfoEventFailed :: Errno -> GpioException
- Fuyu.GPIO: data GpioException
- Fuyu.GPIO: data WaitResult a
- Fuyu.GPIO: lineInfo :: InfoEvent -> IO LineInfo
- Fuyu.GPIO: newtype ReadyChip
- Fuyu.GPIO: offsetFromName :: Chip -> ByteString -> IO Offset
- Fuyu.GPIO: path :: Chip -> IO ByteString
- Fuyu.GPIO: pattern ConfigChanged :: InfoEventType
- Fuyu.GPIO: pattern Immediate :: Timeout
- Fuyu.GPIO: pattern Infinite :: Timeout
- Fuyu.GPIO: pattern Nanoseconds :: CULong -> Timeout
- Fuyu.GPIO: pattern Released :: InfoEventType
- Fuyu.GPIO: pattern Requested :: InfoEventType
- Fuyu.GPIO: readyToChip :: ReadyChip -> Chip
- Fuyu.GPIO: setValue :: Request -> Offset -> Value -> IO ()
- Fuyu.GPIO: setValues :: Request -> Vector Value -> IO ()
- Fuyu.GPIO: setValuesSubset :: Request -> Vector Offset -> Vector Value -> IO ()
- Fuyu.GPIO: settings :: Config -> Offset -> IO Settings
- Fuyu.GPIO: type InfoEvent = InfoEvent
- Fuyu.GPIO: type InfoEventType = InfoEventType
- Fuyu.GPIO: type LineInfo = LineInfo
- Fuyu.GPIO: type Timeout = TimeoutNs
- Fuyu.GPIO: type Timestamp = TimestampNs
- Fuyu.GPIO: unwatchLine :: Chip -> Offset -> IO ()
- Fuyu.GPIO: value :: Request -> Offset -> IO Value
- Fuyu.GPIO: values :: Request -> IO (Vector Value)
- Fuyu.GPIO: valuesSubset :: Request -> Vector Offset -> IO (Vector Value)
- Fuyu.GPIO: waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
- Fuyu.GPIO: watchLine :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
- Fuyu.GPIO: withEvent :: ReadyChip -> (InfoEvent -> IO a) -> IO a
- Fuyu.GPIO: withGpioApp :: IO a -> IO ()
- Fuyu.GPIO: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO: withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip: fd :: Chip -> IO Fd
- Fuyu.GPIO.Chip: gpiodAPIVersion :: IO ByteString
- Fuyu.GPIO.Chip: isGPIOChip :: FilePath -> IO Bool
- Fuyu.GPIO.Chip: offsetFromName :: Chip -> ByteString -> IO Offset
- Fuyu.GPIO.Chip: path :: Chip -> IO ByteString
- Fuyu.GPIO.Chip: withChip :: FilePath -> (Chip -> IO a) -> IO a
- Fuyu.GPIO.Chip: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip.Info: label :: ChipInfo -> IO ByteString
- Fuyu.GPIO.Chip.Info: name :: ChipInfo -> IO ByteString
- Fuyu.GPIO.Chip.Info: numLines :: ChipInfo -> IO Word
- Fuyu.GPIO.Chip.Info: type ChipInfo = ChipInfo
- Fuyu.GPIO.Chip.Info: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip.Info.Unsafe: chipInfo :: Chip -> IO ChipInfo
- Fuyu.GPIO.Chip.Info.Unsafe: freeChipInfo :: ChipInfo -> IO ()
- Fuyu.GPIO.Chip.Info.Unsafe: type ChipInfo = ChipInfo
- Fuyu.GPIO.Chip.Unsafe: closeChip :: Chip -> IO ()
- Fuyu.GPIO.Chip.Unsafe: freeInfoEvent :: InfoEvent -> IO ()
- Fuyu.GPIO.Chip.Unsafe: openChip :: FilePath -> IO Chip
- Fuyu.GPIO.Chip.Unsafe: readInfoEvent :: ReadyChip -> IO InfoEvent
- Fuyu.GPIO.Chip.Unsafe: type Chip = Chip
- Fuyu.GPIO.Chip.Unsafe: type InfoEvent = InfoEvent
- Fuyu.GPIO.Chip.Watch: EventReady :: !a -> WaitResult a
- Fuyu.GPIO.Chip.Watch: ReadyChip :: Chip -> ReadyChip
- Fuyu.GPIO.Chip.Watch: TimeoutResult :: WaitResult a
- Fuyu.GPIO.Chip.Watch: data WaitResult a
- Fuyu.GPIO.Chip.Watch: eventType :: InfoEvent -> IO InfoEventType
- Fuyu.GPIO.Chip.Watch: lineInfo :: InfoEvent -> IO LineInfo
- Fuyu.GPIO.Chip.Watch: newtype ReadyChip
- Fuyu.GPIO.Chip.Watch: pattern ConfigChanged :: InfoEventType
- Fuyu.GPIO.Chip.Watch: pattern Immediate :: Timeout
- Fuyu.GPIO.Chip.Watch: pattern Infinite :: Timeout
- Fuyu.GPIO.Chip.Watch: pattern Nanoseconds :: CULong -> Timeout
- Fuyu.GPIO.Chip.Watch: pattern Offset :: CUInt -> Offset
- Fuyu.GPIO.Chip.Watch: pattern Released :: InfoEventType
- Fuyu.GPIO.Chip.Watch: pattern Requested :: InfoEventType
- Fuyu.GPIO.Chip.Watch: readyToChip :: ReadyChip -> Chip
- Fuyu.GPIO.Chip.Watch: timestampNs :: InfoEvent -> IO Timestamp
- Fuyu.GPIO.Chip.Watch: type Chip = Chip
- Fuyu.GPIO.Chip.Watch: type InfoEvent = InfoEvent
- Fuyu.GPIO.Chip.Watch: type InfoEventType = InfoEventType
- Fuyu.GPIO.Chip.Watch: type LineInfo = LineInfo
- Fuyu.GPIO.Chip.Watch: type Offset = LineOffset
- Fuyu.GPIO.Chip.Watch: type Timeout = TimeoutNs
- Fuyu.GPIO.Chip.Watch: type Timestamp = TimestampNs
- Fuyu.GPIO.Chip.Watch: unwatchLine :: Chip -> Offset -> IO ()
- Fuyu.GPIO.Chip.Watch: waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
- Fuyu.GPIO.Chip.Watch: watchLine :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO.Chip.Watch: withEvent :: ReadyChip -> (InfoEvent -> IO a) -> IO a
- Fuyu.GPIO.Chip.Watch: withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Chip.Watch.Unsafe: ReadyChip :: Chip -> ReadyChip
- Fuyu.GPIO.Chip.Watch.Unsafe: freeInfoEvent :: InfoEvent -> IO ()
- Fuyu.GPIO.Chip.Watch.Unsafe: newtype ReadyChip
- Fuyu.GPIO.Chip.Watch.Unsafe: readInfoEvent :: ReadyChip -> IO InfoEvent
- Fuyu.GPIO.Chip.Watch.Unsafe: readyToChip :: ReadyChip -> Chip
- Fuyu.GPIO.Chip.Watch.Unsafe: type Chip = Chip
- Fuyu.GPIO.Chip.Watch.Unsafe: type InfoEvent = InfoEvent
- Fuyu.GPIO.EdgeEvent: bufferEvent :: Buffer -> Word -> IO Event
- Fuyu.GPIO.EdgeEvent: parseEvent :: Event -> IO EdgeEvent
- Fuyu.GPIO.EdgeEvent: withRawEvents :: ReadyRequest -> Buffer -> (Event -> IO a) -> IO (NonEmpty a)
- Fuyu.GPIO.EdgeEvent.Unsafe: ReadyRequest :: Request -> ReadyRequest
- Fuyu.GPIO.EdgeEvent.Unsafe: capacity :: Capacity -> Word
- Fuyu.GPIO.EdgeEvent.Unsafe: data Capacity
- Fuyu.GPIO.EdgeEvent.Unsafe: freeEventBuffer :: Buffer -> IO ()
- Fuyu.GPIO.EdgeEvent.Unsafe: newEventBuffer :: Capacity -> IO Buffer
- Fuyu.GPIO.EdgeEvent.Unsafe: newtype ReadyRequest
- Fuyu.GPIO.EdgeEvent.Unsafe: readEventsRaw :: ReadyRequest -> Buffer -> IO Int
- Fuyu.GPIO.EdgeEvent.Unsafe: type Buffer = EventBuffer
- Fuyu.GPIO.EdgeEvent.Unsafe: userBufferCapacity :: Word -> Capacity
- Fuyu.GPIO.Exception: instance GHC.Exception.Type.Exception Fuyu.GPIO.Exception.GpioException
- Fuyu.GPIO.Exception: instance GHC.Show.Show Fuyu.GPIO.Exception.GpioException
- Fuyu.GPIO.Exception: withGpioApp :: IO a -> IO ()
- Fuyu.GPIO.Line: activeLow :: Settings -> IO Bool
- Fuyu.GPIO.Line: addSettings :: Config -> Vector Offset -> Settings -> IO ()
- Fuyu.GPIO.Line: bias :: Settings -> IO Bias
- Fuyu.GPIO.Line: chipName :: Request -> IO ByteString
- Fuyu.GPIO.Line: configuredOffsets :: Config -> IO (Vector Offset)
- Fuyu.GPIO.Line: debouncePeriodUs :: Settings -> IO Word
- Fuyu.GPIO.Line: direction :: Settings -> IO Direction
- Fuyu.GPIO.Line: drive :: Settings -> IO Drive
- Fuyu.GPIO.Line: edgeDetection :: Settings -> IO Edge
- Fuyu.GPIO.Line: eventClock :: Settings -> IO Clock
- Fuyu.GPIO.Line: fd :: Request -> IO Fd
- Fuyu.GPIO.Line: numLines :: Request -> IO Word
- Fuyu.GPIO.Line: numOffsets :: Config -> IO Word
- Fuyu.GPIO.Line: outputValue :: Settings -> IO Value
- Fuyu.GPIO.Line: pattern Active :: Value
- Fuyu.GPIO.Line: pattern BiasAsIs :: Bias
- Fuyu.GPIO.Line: pattern BiasDisabled :: Bias
- Fuyu.GPIO.Line: pattern BiasPullDown :: Bias
- Fuyu.GPIO.Line: pattern BiasPullUp :: Bias
- Fuyu.GPIO.Line: pattern BiasUnknown :: Bias
- Fuyu.GPIO.Line: pattern DirAsIs :: Direction
- Fuyu.GPIO.Line: pattern DirInput :: Direction
- Fuyu.GPIO.Line: pattern DirOutput :: Direction
- Fuyu.GPIO.Line: pattern EdgeBoth :: Edge
- Fuyu.GPIO.Line: pattern EdgeFalling :: Edge
- Fuyu.GPIO.Line: pattern EdgeNone :: Edge
- Fuyu.GPIO.Line: pattern EdgeRising :: Edge
- Fuyu.GPIO.Line: pattern Hardware :: Clock
- Fuyu.GPIO.Line: pattern Inactive :: Value
- Fuyu.GPIO.Line: pattern Monotonic :: Clock
- Fuyu.GPIO.Line: pattern Offset :: CUInt -> Offset
- Fuyu.GPIO.Line: pattern OpenDrain :: Drive
- Fuyu.GPIO.Line: pattern OpenSource :: Drive
- Fuyu.GPIO.Line: pattern PushPull :: Drive
- Fuyu.GPIO.Line: pattern Realtime :: Clock
- Fuyu.GPIO.Line: pattern ValueError :: Value
- Fuyu.GPIO.Line: reconfigureLines :: Request -> Config -> IO ()
- Fuyu.GPIO.Line: requestedOffsets :: Request -> IO (Vector Offset)
- Fuyu.GPIO.Line: resetConfig :: Config -> IO ()
- Fuyu.GPIO.Line: resetSettings :: Settings -> IO ()
- Fuyu.GPIO.Line: setActiveLow :: Settings -> Bool -> IO ()
- Fuyu.GPIO.Line: setBias :: Settings -> Bias -> IO ()
- Fuyu.GPIO.Line: setDebouncePeriodUs :: Settings -> Word -> IO ()
- Fuyu.GPIO.Line: setDirection :: Settings -> Direction -> IO ()
- Fuyu.GPIO.Line: setDrive :: Settings -> Drive -> IO ()
- Fuyu.GPIO.Line: setEdgeDetection :: Settings -> Edge -> IO ()
- Fuyu.GPIO.Line: setEventClock :: Settings -> Clock -> IO ()
- Fuyu.GPIO.Line: setOutputValue :: Settings -> Value -> IO ()
- Fuyu.GPIO.Line: setOutputValues :: Config -> Vector Value -> IO ()
- Fuyu.GPIO.Line: setValue :: Request -> Offset -> Value -> IO ()
- Fuyu.GPIO.Line: setValues :: Request -> Vector Value -> IO ()
- Fuyu.GPIO.Line: setValuesSubset :: Request -> Vector Offset -> Vector Value -> IO ()
- Fuyu.GPIO.Line: settings :: Config -> Offset -> IO Settings
- Fuyu.GPIO.Line: type Bias = LineBias
- Fuyu.GPIO.Line: type Clock = LineClock
- Fuyu.GPIO.Line: type Config = LineConfig
- Fuyu.GPIO.Line: type Direction = LineDirection
- Fuyu.GPIO.Line: type Drive = LineDrive
- Fuyu.GPIO.Line: type Edge = LineEdge
- Fuyu.GPIO.Line: type Offset = LineOffset
- Fuyu.GPIO.Line: type Request = LineRequest
- Fuyu.GPIO.Line: type RequestConfig = RequestConfig
- Fuyu.GPIO.Line: type Settings = LineSettings
- Fuyu.GPIO.Line: type Value = LineValue
- Fuyu.GPIO.Line: value :: Request -> Offset -> IO Value
- Fuyu.GPIO.Line: values :: Request -> IO (Vector Value)
- Fuyu.GPIO.Line: valuesSubset :: Request -> Vector Offset -> IO (Vector Value)
- Fuyu.GPIO.Line: withConfig :: (Config -> IO a) -> IO a
- Fuyu.GPIO.Line: withRequest :: Chip -> Maybe RequestConfig -> Config -> (Request -> IO a) -> IO a
- Fuyu.GPIO.Line: withSettings :: (Settings -> IO a) -> IO a
- Fuyu.GPIO.Line.Info: bias :: LineInfo -> IO Bias
- Fuyu.GPIO.Line.Info: consumer :: LineInfo -> IO (Maybe ByteString)
- Fuyu.GPIO.Line.Info: debouncePeriod :: LineInfo -> IO Word
- Fuyu.GPIO.Line.Info: direction :: LineInfo -> IO Direction
- Fuyu.GPIO.Line.Info: drive :: LineInfo -> IO Drive
- Fuyu.GPIO.Line.Info: edgeDetection :: LineInfo -> IO Edge
- Fuyu.GPIO.Line.Info: eventClock :: LineInfo -> IO Clock
- Fuyu.GPIO.Line.Info: isActiveLow :: LineInfo -> IO Bool
- Fuyu.GPIO.Line.Info: isDebounced :: LineInfo -> IO Bool
- Fuyu.GPIO.Line.Info: isUsed :: LineInfo -> IO Bool
- Fuyu.GPIO.Line.Info: name :: LineInfo -> IO (Maybe ByteString)
- Fuyu.GPIO.Line.Info: offset :: LineInfo -> IO Offset
- Fuyu.GPIO.Line.Info: type LineInfo = LineInfo
- Fuyu.GPIO.Line.Info: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
- Fuyu.GPIO.Line.Info.Unsafe: copyLineInfo :: LineInfo -> IO LineInfo
- Fuyu.GPIO.Line.Info.Unsafe: freeLineInfo :: LineInfo -> IO ()
- Fuyu.GPIO.Line.Info.Unsafe: lineInfo :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO.Line.Info.Unsafe: type LineInfo = LineInfo
- Fuyu.GPIO.Line.Unsafe: freeConfig :: Config -> IO ()
- Fuyu.GPIO.Line.Unsafe: freeSettings :: Settings -> IO ()
- Fuyu.GPIO.Line.Unsafe: newConfig :: IO Config
- Fuyu.GPIO.Line.Unsafe: newSettings :: IO Settings
- Fuyu.GPIO.Line.Unsafe: releaseRequest :: Request -> IO ()
- Fuyu.GPIO.Line.Unsafe: requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request
- Fuyu.GPIO.Line.Unsafe: type Chip = Chip
- Fuyu.GPIO.Line.Unsafe: type Config = LineConfig
- Fuyu.GPIO.Line.Unsafe: type Request = LineRequest
- Fuyu.GPIO.Line.Unsafe: type RequestConfig = RequestConfig
- Fuyu.GPIO.Line.Unsafe: type Settings = LineSettings
- Fuyu.GPIO.RequestConfig.Unsafe: freeRequestConfig :: RequestConfig -> IO ()
- Fuyu.GPIO.RequestConfig.Unsafe: newRequestConfig :: IO RequestConfig
- Fuyu.GPIO.RequestConfig.Unsafe: type RequestConfig = RequestConfig
+ Fuyu.GPIO: chipFd :: Chip -> IO Fd
+ Fuyu.GPIO: chipPath :: Chip -> IO FilePath
+ Fuyu.GPIO: lineFd :: Request -> IO Fd
+ Fuyu.GPIO: lineOffsetFromName :: Chip -> String -> IO Offset
+ Fuyu.GPIO: lineSettings :: Config -> Offset -> IO Settings
+ Fuyu.GPIO: lineValue :: Request -> Offset -> IO Value
+ Fuyu.GPIO: lineValues :: Request -> IO [Value]
+ Fuyu.GPIO: lineValuesSubset :: Request -> [Offset] -> IO (Vector Value)
+ Fuyu.GPIO: setLineValue :: Request -> Offset -> Value -> IO ()
+ Fuyu.GPIO: setLineValues :: Request -> [Value] -> IO ()
+ Fuyu.GPIO: setLineValuesSubset :: Request -> [Offset] -> [Value] -> IO ()
+ Fuyu.GPIO.EdgeEvent: bufferRawEvent :: Buffer -> Word -> IO (Maybe RawEvent)
+ Fuyu.GPIO.EdgeEvent: forRawEvents :: ReadyRequest -> Buffer -> (RawEvent -> IO a) -> IO (NonEmpty a)
+ Fuyu.GPIO.EdgeEvent: forRawEvents_ :: ReadyRequest -> Buffer -> (RawEvent -> IO b) -> IO ()
+ Fuyu.GPIO.EdgeEvent: parseRawEvent :: RawEvent -> IO Event
+ Fuyu.GPIO.EdgeEvent: type EventType = EdgeEventType
+ Fuyu.GPIO.EdgeEvent: type RawEvent = RawEdgeEvent
+ Fuyu.GPIO.Exception: instance GHC.Internal.Exception.Type.Exception Fuyu.GPIO.Exception.GpioException
+ Fuyu.GPIO.Exception: instance GHC.Internal.Show.Show Fuyu.GPIO.Exception.GpioException
+ Fuyu.GPIO.Monitor: EventReady :: !a -> WaitResult a
+ Fuyu.GPIO.Monitor: ReadyChip :: Chip -> ReadyChip
+ Fuyu.GPIO.Monitor: TimeoutResult :: WaitResult a
+ Fuyu.GPIO.Monitor: bias :: LineInfo -> IO Bias
+ Fuyu.GPIO.Monitor: chipName :: ChipInfo -> IO String
+ Fuyu.GPIO.Monitor: consumer :: LineInfo -> IO (Maybe String)
+ Fuyu.GPIO.Monitor: data WaitResult a
+ Fuyu.GPIO.Monitor: debouncePeriod :: LineInfo -> IO Word
+ Fuyu.GPIO.Monitor: direction :: LineInfo -> IO Direction
+ Fuyu.GPIO.Monitor: drive :: LineInfo -> IO Drive
+ Fuyu.GPIO.Monitor: edgeDetection :: LineInfo -> IO Edge
+ Fuyu.GPIO.Monitor: eventClock :: LineInfo -> IO Clock
+ Fuyu.GPIO.Monitor: eventType :: Event -> IO EventType
+ Fuyu.GPIO.Monitor: isActiveLow :: LineInfo -> IO Bool
+ Fuyu.GPIO.Monitor: isDebounced :: LineInfo -> IO Bool
+ Fuyu.GPIO.Monitor: isUsed :: LineInfo -> IO Bool
+ Fuyu.GPIO.Monitor: label :: ChipInfo -> IO String
+ Fuyu.GPIO.Monitor: lineInfo :: Event -> IO LineInfo
+ Fuyu.GPIO.Monitor: lineName :: LineInfo -> IO (Maybe String)
+ Fuyu.GPIO.Monitor: newtype ReadyChip
+ Fuyu.GPIO.Monitor: numLines :: ChipInfo -> IO Word
+ Fuyu.GPIO.Monitor: offset :: LineInfo -> IO Offset
+ Fuyu.GPIO.Monitor: pattern BiasAsIs :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasDisabled :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasPullDown :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasPullUp :: Bias
+ Fuyu.GPIO.Monitor: pattern BiasUnknown :: Bias
+ Fuyu.GPIO.Monitor: pattern ConfigChanged :: InfoEventType
+ Fuyu.GPIO.Monitor: pattern DirAsIs :: Direction
+ Fuyu.GPIO.Monitor: pattern DirInput :: Direction
+ Fuyu.GPIO.Monitor: pattern DirOutput :: Direction
+ Fuyu.GPIO.Monitor: pattern EdgeBoth :: Edge
+ Fuyu.GPIO.Monitor: pattern EdgeFalling :: Edge
+ Fuyu.GPIO.Monitor: pattern EdgeNone :: Edge
+ Fuyu.GPIO.Monitor: pattern EdgeRising :: Edge
+ Fuyu.GPIO.Monitor: pattern Hardware :: Clock
+ Fuyu.GPIO.Monitor: pattern Immediate :: Timeout
+ Fuyu.GPIO.Monitor: pattern Infinite :: Timeout
+ Fuyu.GPIO.Monitor: pattern Monotonic :: Clock
+ Fuyu.GPIO.Monitor: pattern Nanoseconds :: CULong -> Timeout
+ Fuyu.GPIO.Monitor: pattern Offset :: CUInt -> Offset
+ Fuyu.GPIO.Monitor: pattern OpenDrain :: Drive
+ Fuyu.GPIO.Monitor: pattern OpenSource :: Drive
+ Fuyu.GPIO.Monitor: pattern PushPull :: Drive
+ Fuyu.GPIO.Monitor: pattern Realtime :: Clock
+ Fuyu.GPIO.Monitor: pattern Released :: InfoEventType
+ Fuyu.GPIO.Monitor: pattern Requested :: InfoEventType
+ Fuyu.GPIO.Monitor: readyToChip :: ReadyChip -> Chip
+ Fuyu.GPIO.Monitor: timestampNs :: Event -> IO Timestamp
+ Fuyu.GPIO.Monitor: type Bias = LineBias
+ Fuyu.GPIO.Monitor: type Chip = Chip
+ Fuyu.GPIO.Monitor: type ChipInfo = ChipInfo
+ Fuyu.GPIO.Monitor: type Clock = LineClock
+ Fuyu.GPIO.Monitor: type Direction = LineDirection
+ Fuyu.GPIO.Monitor: type Drive = LineDrive
+ Fuyu.GPIO.Monitor: type Edge = LineEdge
+ Fuyu.GPIO.Monitor: type Event = InfoEvent
+ Fuyu.GPIO.Monitor: type EventType = InfoEventType
+ Fuyu.GPIO.Monitor: type LineInfo = LineInfo
+ Fuyu.GPIO.Monitor: type Offset = LineOffset
+ Fuyu.GPIO.Monitor: type Timeout = TimeoutNs
+ Fuyu.GPIO.Monitor: type Timestamp = TimestampNs
+ Fuyu.GPIO.Monitor: unwatchLine :: Chip -> Offset -> IO ()
+ Fuyu.GPIO.Monitor: waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)
+ Fuyu.GPIO.Monitor: watchLine :: Chip -> Offset -> IO LineInfo
+ Fuyu.GPIO.Monitor: withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a
+ Fuyu.GPIO.Monitor: withEvent :: ReadyChip -> (Event -> IO a) -> IO a
+ Fuyu.GPIO.Monitor: withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
+ Fuyu.GPIO.Monitor: withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a
+ Fuyu.GPIO.Unsafe: ReadyChip :: Chip -> ReadyChip
+ Fuyu.GPIO.Unsafe: ReadyRequest :: Request -> ReadyRequest
+ Fuyu.GPIO.Unsafe: capacity :: Capacity -> Word
+ Fuyu.GPIO.Unsafe: chipInfo :: Chip -> IO ChipInfo
+ Fuyu.GPIO.Unsafe: closeChip :: Chip -> IO ()
+ Fuyu.GPIO.Unsafe: copyLineInfo :: LineInfo -> IO LineInfo
+ Fuyu.GPIO.Unsafe: data Capacity
+ Fuyu.GPIO.Unsafe: freeChipInfo :: ChipInfo -> IO ()
+ Fuyu.GPIO.Unsafe: freeEventBuffer :: Buffer -> IO ()
+ Fuyu.GPIO.Unsafe: freeInfoEvent :: InfoEvent -> IO ()
+ Fuyu.GPIO.Unsafe: freeLineConfig :: Config -> IO ()
+ Fuyu.GPIO.Unsafe: freeLineInfo :: LineInfo -> IO ()
+ Fuyu.GPIO.Unsafe: freeLineSettings :: Settings -> IO ()
+ Fuyu.GPIO.Unsafe: freeRequestConfig :: RequestConfig -> IO ()
+ Fuyu.GPIO.Unsafe: lineInfo :: Chip -> Offset -> IO LineInfo
+ Fuyu.GPIO.Unsafe: newEventBuffer :: Capacity -> IO Buffer
+ Fuyu.GPIO.Unsafe: newLineConfig :: IO Config
+ Fuyu.GPIO.Unsafe: newLineSettings :: IO Settings
+ Fuyu.GPIO.Unsafe: newRequestConfig :: IO RequestConfig
+ Fuyu.GPIO.Unsafe: newtype ReadyChip
+ Fuyu.GPIO.Unsafe: newtype ReadyRequest
+ Fuyu.GPIO.Unsafe: openChip :: PosixPath -> IO Chip
+ Fuyu.GPIO.Unsafe: readEventsRaw :: ReadyRequest -> Buffer -> IO Int
+ Fuyu.GPIO.Unsafe: readInfoEvent :: ReadyChip -> IO InfoEvent
+ Fuyu.GPIO.Unsafe: readyToChip :: ReadyChip -> Chip
+ Fuyu.GPIO.Unsafe: readyToRequest :: ReadyRequest -> Request
+ Fuyu.GPIO.Unsafe: releaseLineRequest :: Request -> IO ()
+ Fuyu.GPIO.Unsafe: requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request
+ Fuyu.GPIO.Unsafe: type Buffer = EventBuffer
+ Fuyu.GPIO.Unsafe: type Chip = Chip
+ Fuyu.GPIO.Unsafe: type ChipInfo = ChipInfo
+ Fuyu.GPIO.Unsafe: type Config = LineConfig
+ Fuyu.GPIO.Unsafe: type InfoEvent = InfoEvent
+ Fuyu.GPIO.Unsafe: type LineInfo = LineInfo
+ Fuyu.GPIO.Unsafe: type Offset = LineOffset
+ Fuyu.GPIO.Unsafe: type RawEvent = RawEdgeEvent
+ Fuyu.GPIO.Unsafe: type Request = LineRequest
+ Fuyu.GPIO.Unsafe: type RequestConfig = RequestConfig
+ Fuyu.GPIO.Unsafe: type Settings = LineSettings
+ Fuyu.GPIO.Unsafe: unwatchLine :: Chip -> Offset -> IO ()
+ Fuyu.GPIO.Unsafe: userBufferCapacity :: Word -> Capacity
+ Fuyu.GPIO.Unsafe: watchLine :: Chip -> Offset -> IO LineInfo
- Fuyu.GPIO: addSettings :: Config -> Vector Offset -> Settings -> IO ()
+ Fuyu.GPIO: addSettings :: Config -> [Offset] -> Settings -> IO ()
- Fuyu.GPIO: chipName :: Request -> IO ByteString
+ Fuyu.GPIO: chipName :: Request -> IO String
- Fuyu.GPIO: configuredOffsets :: Config -> IO (Vector Offset)
+ Fuyu.GPIO: configuredOffsets :: Config -> IO [Offset]
- Fuyu.GPIO: gpiodAPIVersion :: IO ByteString
+ Fuyu.GPIO: gpiodAPIVersion :: IO String
- Fuyu.GPIO: requestedOffsets :: Request -> IO (Vector Offset)
+ Fuyu.GPIO: requestedOffsets :: Request -> IO [Offset]
- Fuyu.GPIO: setOutputValues :: Config -> Vector Value -> IO ()
+ Fuyu.GPIO: setOutputValues :: Config -> [Value] -> IO ()
- Fuyu.GPIO.EdgeEvent: copyEvent :: Event -> IO Event
+ Fuyu.GPIO.EdgeEvent: copyEvent :: RawEvent -> IO RawEvent
- Fuyu.GPIO.EdgeEvent: eventType :: Event -> IO EdgeEventType
+ Fuyu.GPIO.EdgeEvent: eventType :: RawEvent -> IO EventType
- Fuyu.GPIO.EdgeEvent: globalSeqNo :: Event -> IO Word64
+ Fuyu.GPIO.EdgeEvent: globalSeqNo :: RawEvent -> IO Word64
- Fuyu.GPIO.EdgeEvent: lineOffset :: Event -> IO Offset
+ Fuyu.GPIO.EdgeEvent: lineOffset :: RawEvent -> IO Offset
- Fuyu.GPIO.EdgeEvent: lineSeqNo :: Event -> IO Offset
+ Fuyu.GPIO.EdgeEvent: lineSeqNo :: RawEvent -> IO Offset
- Fuyu.GPIO.EdgeEvent: readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty EdgeEvent)
+ Fuyu.GPIO.EdgeEvent: readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty Event)
- Fuyu.GPIO.EdgeEvent: timestampNs :: Event -> IO Timestamp
+ Fuyu.GPIO.EdgeEvent: timestampNs :: RawEvent -> IO Timestamp
- Fuyu.GPIO.EdgeEvent: type Event = RawEdgeEvent
+ Fuyu.GPIO.EdgeEvent: type Event = EdgeEvent
- Fuyu.GPIO.Exception: ChipOpenFailed :: FilePath -> Errno -> GpioException
+ Fuyu.GPIO.Exception: ChipOpenFailed :: PosixPath -> Errno -> GpioException
Files
- CHANGELOG.md +15/−0
- examples/01-blink.hs +19/−18
- examples/02-button.hs +32/−32
- examples/03-led-and-button.hs +0/−112
- examples/03-monitor.hs +71/−0
- examples/04-led-and-button.hs +159/−0
- examples/04-line-watch.hs +0/−69
- examples/05-request-config.hs +61/−66
- fuyu-gpio.cabal +14/−22
- src/Fuyu/GPIO.hs +356/−18
- src/Fuyu/GPIO/Chip.hs +0/−66
- src/Fuyu/GPIO/Chip/Info.hs +0/−47
- src/Fuyu/GPIO/Chip/Info/Unsafe.hs +0/−29
- src/Fuyu/GPIO/Chip/Unsafe.hs +0/−36
- src/Fuyu/GPIO/Chip/Watch.hs +0/−99
- src/Fuyu/GPIO/Chip/Watch/Unsafe.hs +0/−33
- src/Fuyu/GPIO/EdgeEvent.hs +69/−34
- src/Fuyu/GPIO/EdgeEvent/Unsafe.hs +0/−42
- src/Fuyu/GPIO/Exception.hs +7/−50
- src/Fuyu/GPIO/Line.hs +0/−279
- src/Fuyu/GPIO/Line/Info.hs +0/−92
- src/Fuyu/GPIO/Line/Info/Unsafe.hs +0/−35
- src/Fuyu/GPIO/Line/Unsafe.hs +0/−57
- src/Fuyu/GPIO/Monitor.hs +239/−0
- src/Fuyu/GPIO/RequestConfig.hs +1/−1
- src/Fuyu/GPIO/RequestConfig/Unsafe.hs +0/−29
- src/Fuyu/GPIO/Types.hs +2/−2
- src/Fuyu/GPIO/Unsafe.hs +241/−0
CHANGELOG.md view
@@ -1,5 +1,20 @@ # Revision history for fuyu-gpio +## 0.1.0.0 -- 2026-09-06++* **API Simplification & Architectural Consolidation**:+ * Merged `Fuyu.GPIO.Chip` and `Fuyu.GPIO.Line` into a unified `Fuyu.GPIO` core module.+ * Merged `Chip.Info`, `Line.Info`, and `Chip.Watch` into `Fuyu.GPIO.Monitor`.+ * Consolidated all manual and low-level FFI operations into a single `Fuyu.GPIO.Unsafe` module.+ * Explicit naming for unsafe allocators (`newLineSettings`, `freeLineSettings`, `newLineConfig`, `freeLineConfig`, `releaseLineRequest`) to eliminate domain ambiguity.+* **Dependencies & Compatibility**:+ * Updated dependency on `fuyu-gpio-direct` to `^>= 0.2.0.0`.+ * Added upper bounds for `filepath (< 1.6)` and `managed (< 1.1)` compliant with PVP.+* **Documentation & Examples**:+ * Reached 100% Haddock documentation coverage across all library modules.+ * Added structured progressive examples (01 to 05) with `Control.Monad.Managed` and `transformers` (`ContT` / `StateT`).+ * Updated README with detailed module architecture and design concepts.+ ## 0.0.9.0 -- 2026-08-13 * Initial release. High-level managed abstraction layer for libgpiod built on top of fuyu-gpio-direct.
examples/01-blink.hs view
@@ -3,47 +3,48 @@ -- using nested resource allocation brackets ('withChip', 'withSettings', 'withConfig', 'withRequest'). module Main where --- High-level resource brackets & utility functions-import Fuyu.GPIO.Chip (withChip, isGPIOChip)-import Fuyu.GPIO.Line (withSettings, withConfig, withRequest)-import qualified Fuyu.GPIO.Line as Line+-- High-level resource brackets & utility functions.+-- This import libgpiod Core API functions for chip and line (definitions, settings, configuration, request).+-- Also this module contains the miscellaneous functions for libgpiod ('isGPIOChip' & 'gpiodAPIVersion')+import Fuyu.GPIO --- Base & third-party libraries+-- Base & third-party libraries.+-- Usually we are going to use these modules and their functions. import Control.Concurrent (threadDelay) import Control.Monad (replicateM_)-import Data.Vector.Storable (singleton) --- We could check if this file is a GPIO Chip with 'isGPIOChip' function-chipPath :: FilePath-chipPath = "/dev/gpiochip0" +-- We could check if this file is a GPIO Chip with 'isGPIOChip' function.+myChipPath :: FilePath+myChipPath = "/dev/gpiochip0" -- In Orange Pi devices we could find this information with 'gpio readall' command (or in docs) -- In this case the line offset 269 corresponds to physical pin 7 -ledOffset :: Line.Offset -ledOffset = Line.Offset 269 +ledOffset :: Offset +ledOffset = Offset 269 + main :: IO () main = do- isChip <- isGPIOChip chipPath+ isChip <- isGPIOChip myChipPath if isChip then do putStrLn "Example started: LED blinking" runApp- else putStrLn $ chipPath ++ " does not correspond to a valid GPIO Chip"+ else putStrLn $ myChipPath ++ " does not correspond to a valid GPIO Chip" runApp :: IO () runApp = do- withChip chipPath $ \chip -> do+ withChip myChipPath $ \chip -> do withSettings $ \settings -> do- Line.setDirection settings Line.DirOutput+ setDirection settings DirOutput withConfig $ \config -> do- Line.addSettings config (singleton ledOffset) settings+ addSettings config [ledOffset] settings -- The use of Nothing instead of a 'RequestConfig' means that we are using a NULL request configuration object. -- (Do not confuse Fuyu.GPIO.RequestConfig with Fuyu.GPIO.Line.Config, the first one is used for kernel options -- and the second one is used for line config). withRequest chip Nothing config $ \request -> do replicateM_ 10 $ do - Line.setValue request ledOffset Line.Active+ setLineValue request ledOffset Active threadDelay 500000 -- 0.5 seconds pause - Line.setValue request ledOffset Line.Inactive+ setLineValue request ledOffset Inactive threadDelay 500000 -- 0.5 seconds pause
examples/02-button.hs view
@@ -3,64 +3,64 @@ -- using a user-space event buffer with 'userBufferCapacity 1' to read button press events. module Main where --- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (withChip)-import Fuyu.GPIO.Exception (withGpioApp)-import Fuyu.GPIO.Line (withSettings, withConfig, withRequest)-import qualified Fuyu.GPIO.Line as Line-import Fuyu.GPIO.EdgeEvent (withBuffer)-import qualified Fuyu.GPIO.EdgeEvent as Event+-- High-level resource brackets & exception handling.+-- I recommend a qualified import of 'Fuyu.GPIO.EdgeEvent', for example as Edge.+import Fuyu.GPIO +import qualified Fuyu.GPIO.EdgeEvent as Edge --- Base & third-party libraries+-- Base & third-party libraries. +-- To have a clean output when we press Ctrl+c. +import Control.Exception (catch, throwIO, AsyncException(UserInterrupt)) import Control.Monad (forever)-import Data.Vector.Storable (singleton) -chipPath :: FilePath-chipPath = "/dev/gpiochip0" +myChipPath :: FilePath+myChipPath = "/dev/gpiochip0" -- This constant defines the maximum duration 'waitEvents' will wait for an event. -- (This timeout can also be configured as infinite or immediate).-fiveSecondsNs :: Event.Timeout-fiveSecondsNs = Event.Nanoseconds 5000000000 +fiveSecondsNs :: Edge.Timeout+fiveSecondsNs = Edge.Nanoseconds 5000000000 -- Do not confuse this with kernel ring buffer capacity. 'Capacity' refers to the user-space event buffer. -- It is clamped between 1 and 1024, and must be constructed via 'userBufferCapacity' -- (passing 0 defaults to 64).-bufferCapacity :: Event.Capacity-bufferCapacity = Event.userBufferCapacity 1 +myBufferCapacity :: Edge.Capacity+myBufferCapacity = Edge.userBufferCapacity 1 -buttonOffset :: Line.Offset-buttonOffset = Line.Offset 257 +buttonOffset :: Offset+buttonOffset = Offset 257 -buttonSettings :: Line.Settings -> IO ()+buttonSettings :: Settings -> IO () buttonSettings stgs = do- Line.setDirection stgs Line.DirInput -- Configure line as input mode- Line.setBias stgs Line.BiasPullUp -- Enable internal pull-up resistor- -- (the physical button connects GND when pressed, driving the line to Inactive)- Line.setDebouncePeriodUs stgs 20000 -- 20ms debounce period to filter out mechanical contact bounce without threadDelay- Line.setEdgeDetection stgs Line.EdgeBoth -- Listen for both Rising and Falling edge transitions+ setDirection stgs DirInput -- Configure line as input mode+ setBias stgs BiasPullUp -- Enable internal pull-up resistor+ -- (the physical button connects GND when pressed, driving the line to Inactive)+ setDebouncePeriodUs stgs 20000 -- 20ms debounce period to filter out mechanical contact bounce without threadDelay+ setEdgeDetection stgs EdgeBoth -- Listen for both Rising and Falling edge transitions -buttonWorker :: Line.Request -> Event.Buffer -> IO ()+buttonWorker :: Request -> Edge.Buffer -> IO () buttonWorker req buf = do- res <- Event.waitEvents req fiveSecondsNs+ res <- Edge.waitEvents req fiveSecondsNs case res of - Event.EventReady readyReq -> do- events <- Event.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1)+ Edge.EventReady readyReq -> do+ events <- Edge.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1) print events - Event.TimeoutResult -> putStrLn "Timeout: No event was read" -- Printed after the 5-second wait timeout expires+ Edge.TimeoutResult -> putStrLn "Timeout: No event was read" -- Printed after the 5-second wait timeout expires main :: IO ()-main = withGpioApp runApp+main = runApp `catch` \exc -> case exc of+ UserInterrupt -> putStrLn "\nLoop terminated successfully!"+ other -> throwIO other runApp :: IO () runApp = do- withChip chipPath $ \chip -> do+ withChip myChipPath $ \chip -> do withSettings $ \settings -> do buttonSettings settings withConfig $ \config -> do- Line.addSettings config (singleton buttonOffset) settings+ addSettings config [buttonOffset] settings withRequest chip Nothing config $ \request -> do- withBuffer bufferCapacity $ \buffer -> do+ Edge.withBuffer myBufferCapacity $ \buffer -> do putStrLn "Loop started: Press the button to generate events or Ctrl+C to exit" forever (buttonWorker request buffer)
− examples/03-led-and-button.hs
@@ -1,112 +0,0 @@--- In this example we will learn how to coordinate GPIO output (LED blinking) and input (button press)--- concurrently. We launch two worker threads with 'forkIO' and use an 'MVar' to dynamically--- control the LED blinking speed upon detecting button press edge events.-module Main where---- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (withChip)-import Fuyu.GPIO.Exception (withGpioApp)-import Fuyu.GPIO.Line (withSettings, withConfig, withRequest)-import qualified Fuyu.GPIO.Line as Line-import Fuyu.GPIO.EdgeEvent (withBuffer)-import qualified Fuyu.GPIO.EdgeEvent as Event---- Base & third-party libraries-import Control.Concurrent (MVar, forkIO, killThread, modifyMVar_, newMVar, readMVar, threadDelay)-import Control.Exception (finally)-import Control.Monad (forever)-import Data.Vector.Storable (singleton)-import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)--chipPath :: FilePath-chipPath = "/dev/gpiochip0"---- This constant defines the maximum duration 'waitEvents' will wait for an event.--- A short 100ms timeout yields execution back to the RTS so worker threads run smoothly.-waitTimeoutNs :: Event.Timeout-waitTimeoutNs = Event.Nanoseconds 100000000 ---- Do not confuse this with kernel ring buffer capacity. 'Capacity' refers to the user-space event buffer.--- It is clamped between 1 and 1024, and must be constructed via 'userBufferCapacity'--- (passing 0 defaults to 64).-bufferCapacity :: Event.Capacity-bufferCapacity = Event.userBufferCapacity 1--ledOffset :: Line.Offset-ledOffset = Line.Offset 256 --buttonOffset :: Line.Offset-buttonOffset = Line.Offset 271--type Microseconds = Int---- Available blinking speed states-data LooptimeState = OneSec | HalfSec | FifthOfSec | TenthOfSec- deriving (Eq, Show)---- Convert LooptimeState into delay duration in microseconds-stateToMicroseconds :: LooptimeState -> Microseconds-stateToMicroseconds OneSec = 1000000 -- 1.0s delay-stateToMicroseconds HalfSec = 500000 -- 0.5s delay-stateToMicroseconds FifthOfSec = 200000 -- 0.2s delay-stateToMicroseconds TenthOfSec = 100000 -- 0.1s delay---- Cycle to the next blinking speed state-nextSpeed :: LooptimeState -> LooptimeState -nextSpeed OneSec = HalfSec-nextSpeed HalfSec = FifthOfSec-nextSpeed FifthOfSec = TenthOfSec-nextSpeed TenthOfSec = OneSec--ledSettings :: Line.Settings -> IO ()-ledSettings stgs = Line.setDirection stgs Line.DirOutput--buttonSettings :: Line.Settings -> IO ()-buttonSettings stgs = do- Line.setDirection stgs Line.DirInput -- Configure line as input mode- Line.setBias stgs Line.BiasPullUp -- Enable internal pull-up resistor- -- (the physical button connects GND when pressed, driving the line to Inactive)- Line.setDebouncePeriodUs stgs 80000 -- 80ms native kernel debounce period to filter out mechanical contact bounce without threadDelay- Line.setEdgeDetection stgs Line.EdgeFalling -- Listen for Falling edge transitions (button press to GND)---- Blinks the LED continuously using the delay duration read from the MVar-ledWorker :: Line.Request -> MVar LooptimeState -> IO ()-ledWorker req speedMVar = forever $ do- lts <- readMVar speedMVar- let delayUs = stateToMicroseconds lts- Line.setValue req ledOffset Line.Active- threadDelay delayUs- Line.setValue req ledOffset Line.Inactive- threadDelay delayUs---- Listens for button edge events and cycles the blinking speed state-buttonWorker :: Line.Request -> Event.Buffer -> MVar LooptimeState -> IO ()-buttonWorker req buf speedMVar = do- res <- Event.waitEvents req waitTimeoutNs- case res of- Event.EventReady readyReq -> do- _events <- Event.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1)- modifyMVar_ speedMVar (return . nextSpeed)- Event.TimeoutResult -> threadDelay 20000 -- 20ms pause to yield file descriptor to LED worker thread--main :: IO ()-main = withGpioApp runApp--runApp :: IO ()-runApp = do- hSetBuffering stdout NoBuffering- initialSpeedMVar <- newMVar OneSec- withChip chipPath $ \chip -> do - withSettings $ \buttonStgs -> do- buttonSettings buttonStgs- withSettings $ \ledStgs -> do - ledSettings ledStgs- withConfig $ \config -> do- Line.addSettings config (singleton ledOffset) ledStgs- Line.addSettings config (singleton buttonOffset) buttonStgs- withRequest chip Nothing config $ \request -> do- withBuffer bufferCapacity $ \buffer -> do- Line.setValue request ledOffset Line.Inactive- putStrLn "Loop started: LED blinking concurrently. Press the button to change speed, or Ctrl+C to exit"- tid <- forkIO (forever $ ledWorker request initialSpeedMVar)- forever (buttonWorker request buffer initialSpeedMVar) `finally` killThread tid
+ examples/03-monitor.hs view
@@ -0,0 +1,71 @@+-- In this example we will learn how to monitor GPIO line status changes across processes using chip watching.+-- This example structures the application into modular helper blocks ('runApp', 'monitorApp', 'lineApp'),+-- serving as an intermediate modular phase before introducing resource management with 'Control.Monad.Managed'+-- in the next example (04-led-and-button.hs) to eliminate deeply nested brackets.+module Main where++-- High-level resource brackets & exception handling.+import Fuyu.GPIO++-- Qualified Domain Modules.+-- Same as Fuyu.GPIO.EdgeEvent I sugest an qualified import. +import qualified Fuyu.GPIO.Monitor as Monitor ++-- Base & third-party libraries.+import Control.Concurrent (forkIO, threadDelay)++myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"++targetOffset :: Offset+targetOffset = Offset 257++waitTimeoutNs :: Monitor.Timeout+waitTimeoutNs = Monitor.Nanoseconds 5000000000 -- 5 seconds++main :: IO ()+main = do+ putStrLn "Starting line status event monitor..."+ runApp + putStrLn "Line status event monitor completed successfully."++-- We could use some modular application blocks as the next ones to avoid+-- "super nested" functions. Personally I sugest to use this kind of organization+-- or use managed package (next example) for medium size programs.++-- 'runApp' centralizes worker threads and watchers organized in structured blocks.+runApp :: IO ()+runApp = do+ putStrLn "Opening GPIO chip and starting line status watching..."+ withChip myChipPath $ \chip -> do+ -- Register the line watch in the kernel before any interaction+ -- (required so the kernel starts queueing status events for targetOffset)+ Monitor.withWatchLine chip targetOffset $ \_lineInfo -> do+ _ <- forkIO $ lineApp chip+ monitorApp chip ++-- 'monitorApp' waits for status change events using 'waitEvent' and security token 'ReadyChip'+monitorApp :: Chip -> IO ()+monitorApp chip = do + putStrLn "Waiting for line status change event (timeout: 5s)..."+ res <- Monitor.waitEvent chip waitTimeoutNs+ case res of+ -- Same pattern as 'Fuyu.GPIO.EdgeEvent.waitEvents'+ Monitor.EventReady readyChip -> do+ Monitor.withEvent readyChip $ \infoEvent -> do+ -- In this case, we expect a 'Requested' info event type+ eventType <- Monitor.eventType infoEvent + putStrLn ("Event received! " ++ show eventType)+ Monitor.TimeoutResult -> putStrLn "Wait timed out (timeout)."++-- 'lineApp' simulates line interactions (requesting access to targetOffset) in a concurrent thread+lineApp :: Chip -> IO () +lineApp chip = do + withSettings $ \settings -> do+ setDirection settings DirAsIs + withConfig $ \config -> do+ addSettings config [targetOffset] settings + withRequest chip Nothing config $ \request -> do+ name <- chipName request+ putStrLn ("Line request created successfully on chip: " ++ show name)+ threadDelay 500000 -- Hold requested line briefly
+ examples/04-led-and-button.hs view
@@ -0,0 +1,159 @@+-- In this example we will learn how to coordinate GPIO output (LED blinking) and input (button press)+-- concurrently using 'forkIO' and an 'MVar' to dynamically control the LED blinking speed upon+-- detecting button press edge events.+--+-- We introduce 'Control.Monad.Managed' ('managed', 'runManaged') to acquire and compose nested resources+-- in a clean, linear 'do' block. This effectively eliminates the "Pyramid of Doom" (deeply nested 'with*'+-- brackets) in an accessible, lightweight manner before introducing more advanced abstractions like+-- monad transformers ('ContT' / 'StateT') in example 05.+module Main where++-- High-level resource brackets & exception handling.+import Fuyu.GPIO +import qualified Fuyu.GPIO.EdgeEvent as Edge ++-- Base & third-party libraries.+import Control.Monad.Managed (managed, runManaged, liftIO)+-- For concurrent medium size programs, 'Control.Concurrent' is awesome. +import Control.Concurrent (MVar, forkIO, killThread, modifyMVar_, newMVar, readMVar, threadDelay)+-- Besides the functions and types we used before (to manage Ctrl+c exit), we alse need 'finally' to ensure+-- the cleanup of the concurrent thread when the main loop ends or is interrupted.+import Control.Exception (finally, catch, throwIO, AsyncException(UserInterrupt))+import Control.Monad (forever)+import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)++myChipPath :: FilePath+myChipPath = "/dev/gpiochip0"++-- This constant defines the maximum duration 'waitEvents' will wait for an event.+-- A short 100ms timeout yields execution back to the RTS so worker threads run smoothly.+waitTimeoutNs :: Edge.Timeout+waitTimeoutNs = Edge.Nanoseconds 100000000 ++-- Do not confuse this with kernel ring buffer capacity. 'Capacity' refers to the user-space event buffer.+-- It is clamped between 1 and 1024, and must be constructed via 'userBufferCapacity'+-- (passing 0 defaults to 64).+bufferCapacity :: Edge.Capacity+bufferCapacity = Edge.userBufferCapacity 1++ledOffset :: Offset+ledOffset = Offset 256 ++buttonOffset :: Offset+buttonOffset = Offset 271++type Microseconds = Int++-- Available blinking speed states.+data LooptimeState = OneSec | HalfSec | FifthOfSec | TenthOfSec+ deriving (Eq, Show)++-- Convert LooptimeState into delay duration in microseconds.+stateToMicroseconds :: LooptimeState -> Microseconds+stateToMicroseconds OneSec = 1000000 -- 1.0s delay+stateToMicroseconds HalfSec = 500000 -- 0.5s delay+stateToMicroseconds FifthOfSec = 200000 -- 0.2s delay+stateToMicroseconds TenthOfSec = 100000 -- 0.1s delay++-- Cycle to the next blinking speed state.+nextSpeed :: LooptimeState -> LooptimeState +nextSpeed OneSec = HalfSec+nextSpeed HalfSec = FifthOfSec+nextSpeed FifthOfSec = TenthOfSec+nextSpeed TenthOfSec = OneSec++myLedSettings :: Settings -> IO ()+myLedSettings stgs = setDirection stgs DirOutput++myButtonSettings :: Settings -> IO ()+myButtonSettings stgs = do+ setDirection stgs DirInput -- Configure line as input mode.+ setBias stgs BiasPullUp -- Enable internal pull-up resistor.+ -- (the physical button connects GND when pressed, driving the line to Inactive).+ setDebouncePeriodUs stgs 80000 -- 80ms native kernel debounce period to filter out mechanical contact bounce without threadDelay.+ setEdgeDetection stgs EdgeFalling -- Listen for Falling edge transitions (button press to GND).++-- Blinks the LED continuously using the delay duration read from the MVar.+ledWorker :: Request -> MVar LooptimeState -> IO ()+ledWorker req speedMVar = forever $ do+ lts <- readMVar speedMVar+ let delayUs = stateToMicroseconds lts+ setLineValue req ledOffset Active+ threadDelay delayUs+ setLineValue req ledOffset Inactive+ threadDelay delayUs++-- Listens for button edge events and cycles the blinking speed state.+buttonWorker :: Request -> Edge.Buffer -> MVar LooptimeState -> IO ()+buttonWorker req buf speedMVar = do+ res <- Edge.waitEvents req waitTimeoutNs+ case res of+ Edge.EventReady readyReq -> do+ _events <- Edge.readEvents readyReq buf -- Read events from user buffer (configured with capacity 1).+ modifyMVar_ speedMVar (return . nextSpeed)+ Edge.TimeoutResult -> threadDelay 20000 -- 20ms pause to yield file descriptor to LED worker thread.++withAppConfig :: Settings -> Settings -> (Config -> IO r) -> IO r+withAppConfig ledStgs btnStgs action =+ withConfig $ \config -> do + addSettings config [ledOffset] ledStgs+ addSettings config [buttonOffset] btnStgs+ action config+ +withAppRequest :: Chip -> Config -> (Request -> IO r) -> IO r+withAppRequest chip = withRequest chip Nothing ++main :: IO ()+main = runApp `catch` \exc -> case exc of+ UserInterrupt -> putStrLn "\nLoop terminated seccessfully!"+ other -> throwIO other ++-- The next lines shows us how to manage libgpiod resources with managed package +-- (avoiding the Pyramid of Doom).+ +runApp :: IO ()+runApp = do+ hSetBuffering stdout NoBuffering+ initialSpeedMVar <- newMVar OneSec++ -- Instead of nesting 6 levels of 'with*' brackets, 'runManaged' flattens+ -- resource acquisition sequentially while guaranteeing safe cleanup on exit.+ runManaged $ do+ chip <- managed (withChip myChipPath)+ ledSettings <- managed withSettings+ btnSettings <- managed withSettings+ liftIO $ do+ myLedSettings ledSettings+ myButtonSettings btnSettings+ config <- managed (withAppConfig ledSettings btnSettings)+ request <- managed (withAppRequest chip config)+ buffer <- managed (Edge.withBuffer bufferCapacity)+ liftIO (appLoop request initialSpeedMVar buffer)+ +appLoop :: Request -> MVar LooptimeState -> Edge.Buffer -> IO ()+appLoop request speed buffer = do+ setLineValue request ledOffset Inactive+ putStrLn "Loop started: LED blinking concurrently. Press the button to change speed, or Ctrl+C to exit"+ tid <- forkIO (forever $ ledWorker request speed)+ forever (buttonWorker request buffer speed) `finally` killThread tid++{-+-- For comparison, here is how 'runApp' would look without 'Control.Monad.Managed'+-- (demonstrating the "Pyramid of Doom" caused by multiple nested brackets):++runAppPyramid :: IO ()+runAppPyramid = do+ hSetBuffering stdout NoBuffering+ initialSpeedMVar <- newMVar OneSec+ withChip chipPath $ \chip -> do + withSettings $ \btnStgs -> do+ myButtonSettings btnStgs+ withSettings $ \ledStgs -> do + myLedSettings ledStgs+ withConfig $ \config -> do+ addSettings config (singleton ledOffset) ledStgs+ addSettings config (singleton buttonOffset) btnStgs+ withRequest chip Nothing config $ \request -> do+ withBuffer bufferCapacity $ \buffer -> do+ appLoop request initialSpeedMVar buffer+-}
− examples/04-line-watch.hs
@@ -1,69 +0,0 @@--- In this example we will learn how to monitor GPIO line status changes across processes using chip watching.--- This example structures the application into modular helper functions ('runApp', 'monitorApp', 'lineApp'),--- serving as an intermediate modular phase before refactoring nested brackets with monadic continuation ('ContT').-module Main where---- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (Chip, withChip)-import Fuyu.GPIO.Line (withRequest, withConfig, withSettings)-import Fuyu.GPIO.Exception (withGpioApp)---- Qualified Domain Modules-import qualified Fuyu.GPIO.Chip.Watch as Watch-import qualified Fuyu.GPIO.Line as Line---- Base & third-party libraries-import Control.Concurrent (forkIO, threadDelay)-import Data.Vector.Storable (singleton)--chipPath :: FilePath-chipPath = "/dev/gpiochip0"--targetOffset :: Line.Offset-targetOffset = Line.Offset 257--waitTimeoutNs :: Watch.Timeout-waitTimeoutNs = Watch.Nanoseconds 5000000000 -- 5 seconds--main :: IO ()-main = withGpioApp $ do- putStrLn "Starting line status event monitor..."- runApp - putStrLn "Line status event monitor completed successfully."---- 'runApp' centralizes worker threads and watchers to avoid pyramid of doom-runApp :: IO ()-runApp = do- putStrLn "Opening GPIO chip and starting line status watching..."- withChip chipPath $ \chip -> do- -- Register the line watch in the kernel before any interaction- -- (required so the kernel starts queueing status events for targetOffset)- Watch.withWatchLine chip targetOffset $ \_lineInfo -> do- _ <- forkIO $ lineApp chip- monitorApp chip ---- 'monitorApp' waits for status change events using 'waitEvent' and security token 'ReadyChip'-monitorApp :: Chip -> IO ()-monitorApp chip = do - putStrLn "Waiting for line status change event (timeout: 5s)..."- res <- Watch.waitEvent chip waitTimeoutNs- case res of- -- Same pattern as 'Fuyu.GPIO.EdgeEvent.waitEvents'- Watch.EventReady readyChip -> do- Watch.withEvent readyChip $ \infoEvent -> do- -- In this case, we expect a 'Requested' info event type- eventType <- Watch.eventType infoEvent - putStrLn ("Event received! " ++ show eventType)- Watch.TimeoutResult -> putStrLn "Wait timed out (timeout)."---- 'lineApp' simulates line interactions (requesting access to targetOffset) in a concurrent thread-lineApp :: Chip -> IO () -lineApp chip = do - withSettings $ \settings -> do- Line.setDirection settings Line.DirAsIs - withConfig $ \config -> do- Line.addSettings config (singleton targetOffset) settings - withRequest chip Nothing config $ \request -> do- name <- Line.chipName request- putStrLn ("Line request created successfully on chip: " ++ show name)- threadDelay 500000 -- Hold requested line briefly
examples/05-request-config.hs view
@@ -5,42 +5,42 @@ module Main where -- High-level resource brackets & exception handling-import Fuyu.GPIO.Chip (withChip)-import qualified Fuyu.GPIO.Line as Line-import qualified Fuyu.GPIO.EdgeEvent as EdgeEvent +import Fuyu.GPIO +import qualified Fuyu.GPIO.EdgeEvent as Edge import qualified Fuyu.GPIO.RequestConfig as ReqConf -import Fuyu.GPIO.Exception (withGpioApp) --- Base & third-party libraries+-- Base & core libraries ('transformers' is a core GHC boot package with zero external dependencies). import Control.Monad.Trans.Cont (evalContT, ContT(..))+-- We import 'Control.Monad.Trans.State.Strict' directly from 'transformers' rather than+-- 'Control.Monad.State' from 'mtl' to keep external dependencies minimal.+-- We explicitly choose the strict variant ('.Strict') and use 'modify'' to force evaluation of state+-- updates eagerly. This prevents space leaks (accumulation of unevaluated thunks in memory)+-- as encoder tick counts change continuously. import Control.Monad.Trans.State.Strict (StateT, evalStateT, gets, modify') import Control.Monad.IO.Class (liftIO) import Control.Monad (forever, when)-import qualified Data.Vector.Storable as V (fromList)+-- To have a clean exit when Ctrl+C is pressed.+import Control.Exception (catch, throwIO, AsyncException(UserInterrupt)) import Data.List.NonEmpty (NonEmpty(..)) -chipPath :: FilePath-chipPath = "/dev/gpiochip0"+myChipPath :: FilePath+myChipPath = "/dev/gpiochip0" -- Line offsets for the rotary encoder signals-offsetCLK :: Line.Offset-offsetCLK = Line.Offset 256+offsetCLK :: Offset+offsetCLK = Offset 256 -offsetDT :: Line.Offset-offsetDT = Line.Offset 271+offsetDT :: Offset+offsetDT = Offset 271 -- Timeout for waiting on edge events (5 seconds)-fiveSecondsNs :: EdgeEvent.Timeout-fiveSecondsNs = EdgeEvent.Nanoseconds 5000000000+fiveSecondsNs :: Edge.Timeout+fiveSecondsNs = Edge.Nanoseconds 5000000000 -- Setting user buffer capacity to 1 guarantees that 'readEvents' returns exactly 1 event at a time. -- This simplifies pattern matching to '(ev :| _)' without losing any events in the kernel queue.-capacity :: EdgeEvent.Capacity-capacity = EdgeEvent.userBufferCapacity 1------------------------------------------------------------------------------------- Encoder State Definition---------------------------------------------------------------------------------+capacity :: Edge.Capacity+capacity = Edge.userBufferCapacity 1 -- Clean pure Haskell record representing the quadrature state and step count. data EncoderState = EncoderState@@ -54,71 +54,66 @@ initialState = EncoderState { clkPin = 1, dtPin = 1, position = 0 } main :: IO ()-main = withGpioApp $ do- putStrLn "Starting request config example..."- runApp- putStrLn "Request config example completed successfully."------------------------------------------------------------------------------------- Helper Configurator Brackets---------------------------------------------------------------------------------+main = do + putStrLn "Starting request config example (Press Ctrl+C to exit)..."+ runApp `catch` \exc -> case exc of+ UserInterrupt -> putStrLn "\nLoop terminated successfully!"+ other -> throwIO other --- Encapsulates the creation and configuration of RequestConfig (consumer label & buffer size).+-- We need some helpers configurator brackets. +-- This one encapsulates the creation and configuration of RequestConfig (consumer label & buffer size). withAppRequestConfig :: (ReqConf.RequestConfig -> IO r) -> IO r withAppRequestConfig action = ReqConf.withRequestConfig $ \reqconf -> do ReqConf.setConsumer reqconf "encoder-app" ReqConf.setBufferSize reqconf 256 action reqconf --- Encapsulates line settings configuration (input mode, 1ms debounce, edge detection).-withAppLineSettings :: (Line.Settings -> IO r) -> IO r-withAppLineSettings action = Line.withSettings $ \settings -> do- Line.setDirection settings Line.DirInput- Line.setDebouncePeriodUs settings 1000 -- 1ms debounce suitable for rotary encoder hardware- Line.setEdgeDetection settings Line.EdgeBoth +-- To encapsulates line settings configuration (input mode, 1ms debounce, edge detection).+withAppLineSettings :: (Settings -> IO r) -> IO r+withAppLineSettings action = withSettings $ \settings -> do+ setDirection settings DirInput+ setDebouncePeriodUs settings 1000 -- 1ms debounce suitable for rotary encoder hardware+ setEdgeDetection settings EdgeBoth action settings --- Encapsulates building line configuration for target pin offsets (CLK & DT).-withAppLineConfig :: Line.Settings -> (Line.Config -> IO r) -> IO r-withAppLineConfig settings action = Line.withConfig $ \config -> do- Line.addSettings config (V.fromList [offsetCLK, offsetDT]) settings+-- And to encapsulates building line configuration for target pin offsets (CLK & DT).+withAppLineConfig :: Settings -> (Config -> IO r) -> IO r+withAppLineConfig settings action = withConfig $ \config -> do+ addSettings config [offsetCLK, offsetDT] settings action config ------------------------------------------------------------------------------------ Resource Setup using ContT and Execution with StateT------------------------------------------------------------------------------------- Monadic resource setup using 'ContT' flattens nested 'with...' brackets into a linear 'do' block.--- 'evalStateT' then runs the application loop with managed pure state ('EncoderState').+-- Monadic resource setup using 'ContT' (from 'transformers') flattens nested 'with...' brackets+-- into a linear 'do' block. This achieves the same goal as 'runManaged' in example 04, but using+-- standard core transformers without external dependencies.+-- 'evalStateT' then runs the stateful application loop initialized with 'initialState'. runApp :: IO () runApp = evalContT $ do- chip <- ContT $ withChip chipPath+ chip <- ContT $ withChip myChipPath reqconf <- ContT withAppRequestConfig settings <- ContT withAppLineSettings config <- ContT $ withAppLineConfig settings- request <- ContT $ Line.withRequest chip (Just reqconf) config- buffer <- ContT $ EdgeEvent.withBuffer capacity+ request <- ContT $ withRequest chip (Just reqconf) config+ buffer <- ContT $ Edge.withBuffer capacity - -- Run stateful application loop starting with 'initialState'+ -- Run stateful application loop starting with 'initialState'. liftIO $ evalStateT (appLoop request buffer) initialState ------------------------------------------------------------------------------------ Encoder Application Loop using MonadState (StateT)---------------------------------------------------------------------------------- -- Application loop running in 'StateT EncoderState IO ()'.-appLoop :: Line.Request -> EdgeEvent.Buffer -> StateT EncoderState IO ()+-- Unlike example 04 which used an 'MVar' to share mutable state across concurrent threads ('forkIO'),+-- this example is a single-threaded sequential event loop. 'StateT' provides pure, structured state+-- transitions without the synchronization overhead or locking primitives of MVars.+appLoop :: Request -> Edge.Buffer -> StateT EncoderState IO () appLoop request buffer = forever $ do- result <- liftIO $ EdgeEvent.waitEvents request fiveSecondsNs+ result <- liftIO $ Edge.waitEvents request fiveSecondsNs case result of- EdgeEvent.TimeoutResult -> + Edge.TimeoutResult -> liftIO $ putStrLn "No edge event was read (timeout)." - EdgeEvent.EventReady req -> do- (ev :| _) <- liftIO $ EdgeEvent.readEvents req buffer+ Edge.EventReady req -> do+ (ev :| _) <- liftIO $ Edge.readEvents req buffer oldPos <- gets position - -- Update pure state cleanly using strict 'modify''+ -- Update pure state cleanly using strict 'modify'. modify' (updateEncoderState ev) newPos <- gets position@@ -129,13 +124,13 @@ -- When CLK transitions to LOW (Falling edge), we inspect the current state of DT: -- - DT == 1 (HIGH) -> Clockwise rotation (+1) -- - DT == 0 (LOW) -> Counter-Clockwise rotation (-1)-updateEncoderState :: EdgeEvent.EdgeEvent -> EncoderState -> EncoderState-updateEncoderState (EdgeEvent.EdgeEvent offset evType _) st = case (offset, evType) of- (Line.Offset 256, EdgeEvent.Falling) ->+updateEncoderState :: Edge.Event -> EncoderState -> EncoderState+updateEncoderState (Edge.EdgeEvent offset evType _) st = case (offset, evType) of+ (Offset 256, Edge.Falling) -> let delta = if dtPin st == 1 then 1 else (-1) in st { clkPin = 0, position = position st + delta } - (Line.Offset 256, EdgeEvent.Rising) -> st { clkPin = 1 }- (Line.Offset 271, EdgeEvent.Falling) -> st { dtPin = 0 }- (Line.Offset 271, EdgeEvent.Rising) -> st { dtPin = 1 }- _ -> st+ (Offset 256, Edge.Rising) -> st { clkPin = 1 }+ (Offset 271, Edge.Falling) -> st { dtPin = 0 }+ (Offset 271, Edge.Rising) -> st { dtPin = 1 }+ _ -> st
fuyu-gpio.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: fuyu-gpio-version: 0.0.9.0+version: 0.1.0.0 synopsis: High-level, type-safe interface for Linux GPIO (libgpiod v2). description: High-level, type-safe, and resource-managed Haskell interface for Linux GPIO character devices using libgpiod v2. Built on top of fuyu-gpio-direct, fuyu-gpio provides automatic memory management (bracket / with* style), typed exception handling, metadata snapshots, and zero-copy vector operations for high-performance GPIO I/O. license: LGPL-2.1-or-later@@ -22,29 +22,20 @@ library import: warnings exposed-modules: Fuyu.GPIO,- Fuyu.GPIO.Chip,- Fuyu.GPIO.Chip.Info,- Fuyu.GPIO.Chip.Info.Unsafe,- Fuyu.GPIO.Chip.Unsafe,- Fuyu.GPIO.Line,- Fuyu.GPIO.Line.Info,- Fuyu.GPIO.Line.Info.Unsafe,- Fuyu.GPIO.Line.Unsafe,+ Fuyu.GPIO.Unsafe, + Fuyu.GPIO.Monitor, Fuyu.GPIO.RequestConfig,- Fuyu.GPIO.RequestConfig.Unsafe, Fuyu.GPIO.EdgeEvent,- Fuyu.GPIO.EdgeEvent.Unsafe,- Fuyu.GPIO.Chip.Watch,- Fuyu.GPIO.Chip.Watch.Unsafe, Fuyu.GPIO.Exception other-modules: Fuyu.GPIO.Types -- other-extensions: build-depends: base >= 4.18 && < 5,- fuyu-gpio-direct ^>=0.1.0.0,+ fuyu-gpio-direct ^>=0.2.0.0, bytestring ^>=0.12.1.0,- vector ^>=0.13.1.0+ vector ^>=0.13.1.0,+ filepath >= 1.5.5.0 && < 1.6 hs-source-dirs: src@@ -68,6 +59,7 @@ base >= 4.18 && < 5, fuyu-gpio, vector ^>= 0.13.1.0,+ managed >= 1.0.10 && < 1.1, transformers >= 0.5 && < 0.7, bytestring ^>= 0.12.1.0 @@ -80,16 +72,16 @@ import: example-config main-is: 02-button.hs -executable 03-led-and-button- import: example-config- main-is: 03-led-and-button.hs- ghc-options: -threaded --executable 04-line-watch+executable 03-monitor import: example-config- main-is: 04-line-watch.hs+ main-is: 03-monitor.hs ghc-options: -threaded -rtsopts +executable 04-led-and-button+ import: example-config+ main-is: 04-led-and-button.hs+ ghc-options: -threaded + executable 05-request-config import: example-config main-is: 05-request-config.hs
src/Fuyu/GPIO.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} -- | -- Module : Fuyu.GPIO -- Description : High-level, type-safe Haskell interface for Linux GPIO (libgpiod v2).@@ -5,26 +6,363 @@ -- Stability : experimental -- Portability : POSIX (Linux GPIO character device interface) ----- This is the main umbrella module for @fuyu-gpio@, providing high-level,--- managed resource wrappers ('withChip', 'withSettings', 'withConfig', 'withRequest')--- and exception handling ('GpioException', 'withGpioApp') for Linux GPIO character devices.+-- This module groups functions related to GPIO chip and line handling from the+-- <https://libgpiod.readthedocs.io/en/master/core_api.html libgpiod Core API>.+-- Specifically, these functions cover GPIO Chip, GPIO line settings,+-- GPIO line configuration, GPIO line requests, and libgpiod miscellaneous interfaces. ----- For detailed metadata inspection, import "Fuyu.GPIO.Chip.Info" or "Fuyu.GPIO.Line.Info" qualified.--- For manual/unmanaged FFI resource lifecycle, import the corresponding @.Unsafe@ submodules. module Fuyu.GPIO- ( -- * Domain Modules- module Fuyu.GPIO.Chip- , module Fuyu.GPIO.Line- , module Fuyu.GPIO.RequestConfig- , module Fuyu.GPIO.EdgeEvent+ ( -- * GPIO Chip+ Chip+ , withChip+ , chipPath+ , lineOffsetFromName+ , chipFd - -- * Exception & App Runner- , GpioException(..)- , withGpioApp+ -- * GPIO line definitions+ , Offset+ , pattern Offset+ , Direction+ , pattern DirAsIs+ , pattern DirInput+ , pattern DirOutput+ , Edge+ , pattern EdgeNone+ , pattern EdgeRising+ , pattern EdgeFalling+ , pattern EdgeBoth+ , Bias+ , pattern BiasAsIs+ , pattern BiasUnknown+ , pattern BiasDisabled+ , pattern BiasPullUp+ , pattern BiasPullDown+ , Drive+ , pattern PushPull+ , pattern OpenDrain+ , pattern OpenSource+ , Clock+ , pattern Monotonic+ , pattern Realtime+ , pattern Hardware+ , Value+ , pattern Active+ , pattern Inactive+ , pattern ValueError++ -- * GPIO Line Settings+ , Settings+ , withSettings+ , setDirection+ , direction+ , setEdgeDetection+ , edgeDetection+ , setBias+ , bias+ , setDrive+ , drive+ , setEventClock+ , eventClock+ , setActiveLow+ , activeLow+ , setDebouncePeriodUs+ , debouncePeriodUs+ , setOutputValue+ , outputValue+ , resetSettings++ -- * GPIO Line Config+ , Config+ , withConfig+ , addSettings+ , lineSettings+ , setOutputValues+ , numOffsets+ , configuredOffsets+ , resetConfig++ -- * GPIO Line Request+ , Request+ , RequestConfig+ , withRequest+ , lineValue+ , lineValues+ , lineValuesSubset+ , setLineValue+ , setLineValues+ , setLineValuesSubset+ , chipName+ , numLines+ , requestedOffsets+ , lineFd+ , reconfigureLines++ -- * General Utilities+ , isGPIOChip+ , gpiodAPIVersion ) where -import Fuyu.GPIO.Chip hiding (fd, eventType, timestampNs)-import Fuyu.GPIO.Line hiding (fd)-import Fuyu.GPIO.RequestConfig-import Fuyu.GPIO.EdgeEvent-import Fuyu.GPIO.Exception (GpioException(..), withGpioApp)+import Control.Exception (bracket, throwIO)+import System.OsPath.Posix (encodeFS, decodeFS)+import qualified Data.ByteString.Char8 as C8+import qualified Data.Vector.Storable as V+import System.Posix.Types (Fd)+import qualified Fuyu.GPIO.Direct as D+import Fuyu.GPIO.Unsafe+ ( openChip+ , closeChip+ , newLineSettings+ , freeLineSettings+ , newLineConfig+ , freeLineConfig+ , requestLines+ , releaseLineRequest+ )+import Fuyu.GPIO.Exception+import Fuyu.GPIO.Types++--------------------------------------------------------------------------------+-- GPIO Chip+--------------------------------------------------------------------------------++-- | Open a GPIO chip by filesystem path (e.g. @"\/dev\/gpiochip0"@) and automatically close it when finished.+--+-- @+-- main :: IO ()+-- main = do+-- withChip "\/dev\/gpiochip0" $ \chip -> do+-- someFunc chip+-- @+withChip :: FilePath -> (Chip -> IO a) -> IO a+withChip str f = do+ path' <- encodeFS str+ bracket (openChip path') closeChip f++-- | Retrieve chip filesystem path as a 'FilePath'.+chipPath :: Chip -> IO FilePath+chipPath chip = do+ path' <- unwrapOrThrow ChipInfoFailed (D.chipPath chip)+ decodeFS path'++-- | Map a GPIO line name (e.g. "GPIO17") to its numeric 'Offset' on the chip.+lineOffsetFromName :: Chip -> String -> IO Offset+lineOffsetFromName chip name =+ unwrapOrThrow LineInfoFailed (D.chipLineOffsetFromName chip (C8.pack name))++-- | Get the underlying Linux file descriptor associated with the GPIO chip handle.+chipFd :: Chip -> IO Fd+chipFd = D.chipFd++--------------------------------------------------------------------------------+-- GPIO Line Settings+--------------------------------------------------------------------------------++-- | Allocate a new line settings object and free it automatically afterwards.+withSettings :: (Settings -> IO a) -> IO a+withSettings = bracket newLineSettings freeLineSettings++-- | Set the line direction in the settings.+setDirection :: Settings -> Direction -> IO ()+setDirection set dir = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDirection set dir)++-- | Get the line direction from the settings.+direction :: Settings -> IO Direction+direction = D.lineSettingsDirection++-- | Set edge detection in the settings.+setEdgeDetection :: Settings -> Edge -> IO ()+setEdgeDetection set edge = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEdgeDetection set edge)++-- | Get edge detection from the settings.+edgeDetection :: Settings -> IO Edge+edgeDetection = D.lineSettingsEdgeDetection++-- | Set electrical bias in the settings.+setBias :: Settings -> Bias -> IO ()+setBias _ BiasUnknown = throwIO $ InvalidArgument "setBias: BiasUnknown is a read-only state and cannot be set as a bias configuration."+setBias set biasVal = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetBias set biasVal)++-- | Get electrical bias from the settings.+bias :: Settings -> IO Bias+bias = D.lineSettingsBias++-- | Set drive mode in the settings.+setDrive :: Settings -> Drive -> IO ()+setDrive set driveMode = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDrive set driveMode)++-- | Get drive mode from the settings.+drive :: Settings -> IO Drive+drive = D.lineSettingsDrive++-- | Set event clock source in the settings.+setEventClock :: Settings -> Clock -> IO ()+setEventClock set clk = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEventClock set clk)++-- | Get event clock source from the settings.+eventClock :: Settings -> IO Clock+eventClock = D.lineSettingsEventClock++-- | Set active-low in the settings.+setActiveLow :: Settings -> Bool -> IO ()+setActiveLow = D.lineSettingsSetActiveLow++-- | Get active-low setting.+activeLow :: Settings -> IO Bool+activeLow = D.lineSettingsActiveLow++-- | Set debounce period in microseconds.+setDebouncePeriodUs :: Settings -> Word -> IO ()+setDebouncePeriodUs = D.lineSettingsSetDebouncePeriodUs++-- | Get debounce period in microseconds.+debouncePeriodUs :: Settings -> IO Word+debouncePeriodUs = D.lineSettingsDebouncePeriodUs++-- | Set default output value in the settings.+setOutputValue :: Settings -> Value -> IO ()+setOutputValue _ ValueError = throwIO $ InvalidArgument "setOutputValue: ValueError pattern is a read-only error state and cannot be set as an output value."+setOutputValue set val = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetOutputValue set val)++-- | Get default output value from the settings.+outputValue :: Settings -> IO Value+outputValue = D.lineSettingsOutputValue++-- | Reset line settings object to default values.+resetSettings :: Settings -> IO ()+resetSettings = D.lineSettingsReset++--------------------------------------------------------------------------------+-- GPIO Line Config+--------------------------------------------------------------------------------++-- | Allocate a new line configuration object and free it automatically afterwards.+-- +withConfig :: (Config -> IO a) -> IO a+withConfig = bracket newLineConfig freeLineConfig++-- | Add settings for a list of line offsets in the configuration.+addSettings :: Config -> [Offset] -> Settings -> IO ()+addSettings config offsets stgs =+ unwrapOrThrow LineConfigNewFailed (D.lineConfigAddLineSettings config (V.fromList offsets) stgs)++-- | Get settings for a specific line offset from configuration.+lineSettings :: Config -> Offset -> IO Settings+lineSettings config offset' = unwrapOrThrow LineConfigNewFailed (D.lineConfigLineSettings config offset')++-- | Set output values for lines in configuration.+setOutputValues :: Config -> [Value] -> IO ()+setOutputValues config vals+ | V.elem ValueError (V.fromList vals) =+ throwIO $ InvalidArgument "setOutputValues: Vector contains ValueError pattern, which cannot be set as an output value."+ | otherwise =+ unwrapOrThrow LineConfigNewFailed (D.lineConfigSetOutputValues config (V.fromList vals))++-- | Get the number of configured offsets in the line configuration.+numOffsets :: Config -> IO Word+numOffsets = D.lineConfigNumOffsets++-- | Get all configured line offsets in the configuration as a 'Offset' list.+configuredOffsets :: Config -> IO [Offset]+configuredOffsets config = do+ vec <- D.lineConfigConfiguredOffsets config + return (V.toList vec)+ ++-- | Reset line configuration object to empty state.+resetConfig :: Config -> IO ()+resetConfig = D.lineConfigReset++--------------------------------------------------------------------------------+-- GPIO Line Request+--------------------------------------------------------------------------------++-- | Request GPIO lines from a chip and automatically release them afterwards.+--+-- Since kernel configuration options ('RequestConfig') are optional,+-- passing 'Nothing' is equivalent to passing a NULL pointer in C.+--+-- @+-- someFunc :: IO ()+-- someFunc = do+-- withRequest chip Nothing config $ \request -> do+-- setLineValue request (Offset 271) Active +-- @+--+-- Assuming offset 271 was configured as output ('DirOutput') with 'setDirection'+-- and added to the configuration with 'addSettings', this drives the physical pin active.+withRequest :: Chip -> Maybe RequestConfig -> Config -> (Request -> IO a) -> IO a+withRequest chip maybeReqConf lineConf = bracket (requestLines chip maybeReqConf lineConf) releaseLineRequest++-- | Get the logical value of a requested GPIO line at the given offset.+lineValue :: Request -> Offset -> IO Value+lineValue req offset' = unwrapOrThrow LineValueReadFailed (D.lineRequestValue req offset')++-- | Get the logical values of all requested lines as a 'Value' list.+lineValues :: Request -> IO [Value]+lineValues req = do+ vec <- unwrapOrThrow LineValueReadFailed (D.lineRequestValues req)+ return (V.toList vec)++-- | Get the logical values of a subset of requested lines specified by offsets.+lineValuesSubset :: Request -> [Offset] -> IO (V.Vector Value)+lineValuesSubset req offsets =+ unwrapOrThrow LineValueReadFailed (D.lineRequestSubsetValues req (V.fromList offsets))++-- | Set the logical value of a requested GPIO line at the given offset.+setLineValue :: Request -> Offset -> Value -> IO ()+setLineValue _ _ ValueError = throwIO $ InvalidArgument "setValue: ValueError pattern is a read-only error state and cannot be written to a GPIO line."+setLineValue req offset' val = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValue req offset' val)++-- | Set the logical values of all requested lines from a 'Value' list.+setLineValues :: Request -> [Value] -> IO ()+setLineValues req vals+ | V.elem ValueError (V.fromList vals) =+ throwIO $ InvalidArgument "setValues: Vector contains ValueError pattern, which cannot be written to GPIO lines."+ | otherwise =+ unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValues req (V.fromList vals))++-- | Set the logical values of a subset of requested lines from lists of 'Offset' and 'Value'.+setLineValuesSubset :: Request -> [Offset] -> [Value] -> IO ()+setLineValuesSubset req offsets vals+ | V.elem ValueError (V.fromList vals) =+ throwIO $ InvalidArgument "setValuesSubset: Vector contains ValueError pattern, which cannot be written to GPIO lines."+ | otherwise =+ unwrapOrThrow LineValueWriteFailed $ D.lineRequestSetValuesSubset req (V.fromList offsets) (V.fromList vals)++-- | Get the name as a 'String' of the chip this request was made on.+chipName :: Request -> IO String+chipName request = do+ name <- D.lineRequestChipName request + return (C8.unpack name)++-- | Get the number of lines in the request.+numLines :: Request -> IO Word+numLines = D.lineRequestNumLines++-- | Get all requested line offsets as a 'Offset' list.+requestedOffsets :: Request -> IO [Offset]+requestedOffsets request = do+ vec <- D.lineRequestRequestedOffsets request+ return (V.toList vec)++-- | Get the file descriptor associated with the line request handle.+lineFd :: Request -> IO Fd+lineFd = D.lineRequestFd++-- | Update the configuration of lines associated with an active line request.+reconfigureLines :: Request -> Config -> IO ()+reconfigureLines req config = unwrapOrThrow LineReconfigureFailed (D.lineRequestReconfigure req config)++--------------------------------------------------------------------------------+-- General Utilities+--------------------------------------------------------------------------------++-- | Check if the given filesystem path is a valid GPIO chip character device.+isGPIOChip :: FilePath -> IO Bool+isGPIOChip str = do+ path' <- encodeFS str + D.isGPIOChip path' ++-- | Retrieve the underlying libgpiod C API version string (e.g. "2.1").+gpiodAPIVersion :: IO String +gpiodAPIVersion = C8.unpack <$> D.gpiodAPIVersion
− src/Fuyu/GPIO/Chip.hs
@@ -1,66 +0,0 @@--- |--- Module : Fuyu.GPIO.Chip--- Description : High-level operations for GPIO chips and line watching.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides managed resource brackets ('withChip') for opening and closing--- Linux GPIO chips safely, as well as functions for watching line status changes.-module Fuyu.GPIO.Chip- ( -- * Operations & Brackets- withChip- , withChipInfo- , withLineInfo- , path- , offsetFromName- , fd-- -- * Line Watch & Info Events- , module Fuyu.GPIO.Chip.Watch-- -- * General Utilities- , isGPIOChip- , gpiodAPIVersion- ) where--import Control.Exception (bracket)-import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as BS8-import System.Posix.Types (Fd)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Info (withChipInfo)-import Fuyu.GPIO.Line.Info (withLineInfo)-import Fuyu.GPIO.Chip.Unsafe (openChip, closeChip)-import Fuyu.GPIO.Chip.Watch-import Fuyu.GPIO.Exception---- | Open a GPIO chip by filesystem path (e.g. "/dev/gpiochip0") and automatically close it when finished.-withChip :: FilePath -> (Chip -> IO a) -> IO a-withChip path' = bracket (openChip path') closeChip---- | Retrieve chip filesystem path as a 'ByteString'.-path :: Chip -> IO ByteString-path chip = unwrapOrThrow ChipInfoFailed (D.chipPath chip)---- | Map a GPIO line name (e.g. "GPIO17") to its numeric 'Offset' on the chip.-offsetFromName :: Chip -> ByteString -> IO Offset-offsetFromName chip name = unwrapOrThrow LineInfoFailed (D.chipLineOffsetFromName chip name)---- | Get the underlying Linux file descriptor associated with the GPIO chip handle.-fd :: Chip -> IO Fd-fd = D.chipFd------------------------------------------------------------------------------------- General Utilities------------------------------------------------------------------------------------ General Utilities------------------------------------------------------------------------------------- | Check if the given filesystem path is a valid GPIO chip character device.-isGPIOChip :: FilePath -> IO Bool-isGPIOChip = D.isGPIOChip . BS8.pack---- | Retrieve the underlying libgpiod C API version string (e.g. "2.1").-gpiodAPIVersion :: IO ByteString-gpiodAPIVersion = D.gpiodAPIVersion
− src/Fuyu/GPIO/Chip/Info.hs
@@ -1,47 +0,0 @@--- |--- Module : Fuyu.GPIO.Chip.Info--- Description : Read-only metadata query functions for ChipInfo.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides functions to inspect 'ChipInfo' snapshots.--- It is designed to be imported qualified:------ @--- import qualified Fuyu.GPIO.Chip.Info as ChipInfo--- @-module Fuyu.GPIO.Chip.Info- ( -- * Types- ChipInfo-- -- * Managed Resource Allocation- , withChipInfo-- -- * Metadata Accessors- , name- , label- , numLines- ) where--import Control.Exception (bracket)-import Data.ByteString (ByteString)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Info.Unsafe (chipInfo, freeChipInfo)-import Fuyu.GPIO.Types---- | Retrieve information about a GPIO chip and free it automatically afterwards.-withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a-withChipInfo chip = bracket (chipInfo chip) freeChipInfo---- | Get the name of the GPIO chip (e.g. "gpiochip4").-name :: ChipInfo -> IO ByteString-name = D.chipInfoName---- | Get the label of the GPIO chip.-label :: ChipInfo -> IO ByteString-label = D.chipInfoLabel---- | Get the total number of lines exposed by the GPIO chip.-numLines :: ChipInfo -> IO Word-numLines = D.chipInfoNumLines
− src/Fuyu/GPIO/Chip/Info/Unsafe.hs
@@ -1,29 +0,0 @@--- |--- Module : Fuyu.GPIO.Chip.Info.Unsafe--- Description : Unsafe manual resource allocation for ChipInfo.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('chipInfo', 'freeChipInfo') for 'ChipInfo' handles.-module Fuyu.GPIO.Chip.Info.Unsafe- ( -- * Types- ChipInfo-- -- * Unsafe Manual Resource Allocation- , chipInfo- , freeChipInfo- ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Retrieve chip info directly.--- Must be manually freed using 'freeChipInfo'.-chipInfo :: Chip -> IO ChipInfo-chipInfo chip = unwrapOrThrow ChipInfoFailed (D.chipInfo chip)---- | Free a 'ChipInfo' handle.-freeChipInfo :: ChipInfo -> IO ()-freeChipInfo = D.chipInfoFree
− src/Fuyu/GPIO/Chip/Unsafe.hs
@@ -1,36 +0,0 @@--- |--- Module : Fuyu.GPIO.Chip.Unsafe--- Description : Unsafe manual resource allocation for GPIO chips and info events.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('openChip', 'closeChip', 'readInfoEvent', 'freeInfoEvent')--- for applications that cannot use managed bracket functions.-module Fuyu.GPIO.Chip.Unsafe- ( -- * Types- Chip- , InfoEvent-- -- * Unsafe Manual Resource Allocation- , openChip- , closeChip- , readInfoEvent- , freeInfoEvent- ) where--import qualified Data.ByteString.Char8 as BS8-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Watch.Unsafe (readInfoEvent, freeInfoEvent)-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Open a GPIO chip by its filesystem path (e.g. "/dev/gpiochip4").--- Must be manually closed using 'closeChip'.-openChip :: FilePath -> IO Chip-openChip path = unwrapOrThrow (ChipOpenFailed path) (D.chipOpen (BS8.pack path))---- | Close a GPIO chip handle.-closeChip :: Chip -> IO ()-closeChip = D.chipClose-
− src/Fuyu/GPIO/Chip/Watch.hs
@@ -1,99 +0,0 @@-{-# LANGUAGE PatternSynonyms #-}---- |--- Module : Fuyu.GPIO.Chip.Watch--- Description : Operations for watching GPIO line status events.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides managed resource brackets ('withWatchLine', 'withEvent')--- and functions for watching line status changes (e.g. requested, released, reconfigured).------ It is designed to be imported qualified or used via top-level "Fuyu.GPIO":------ @--- import qualified Fuyu.GPIO.Chip.Watch as Watch--- @-module Fuyu.GPIO.Chip.Watch- ( -- * Types & Patterns- Chip- , ReadyChip(..)- , readyToChip- , WaitResult(..)- , LineInfo- , Offset- , pattern Offset- , Timeout- , pattern Nanoseconds- , pattern Immediate- , pattern Infinite- , Timestamp- , InfoEvent- , InfoEventType- , pattern Requested- , pattern Released- , pattern ConfigChanged-- -- * Managed Brackets- , withWatchLine- , withEvent-- -- * Line Watch Operations- , watchLine- , unwatchLine- , waitEvent-- -- * InfoEvent Accessors- , eventType- , timestampNs- , lineInfo- ) where--import Control.Exception (bracket)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Chip.Watch.Unsafe (readInfoEvent, freeInfoEvent)-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types hiding (eventType)---- | Start watching a line for status change events (e.g. requested, released, reconfigured)--- within a bracket, automatically unwatching the line when finished.------ Passes the initial 'LineInfo' snapshot of the line to the callback.-withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a-withWatchLine chip offset' = bracket (watchLine chip offset') (\_ -> unwatchLine chip offset')---- | Start watching a line for status change events (e.g. requested, released, reconfigured).--- Returns the initial 'LineInfo' snapshot of the line.-watchLine :: Chip -> Offset -> IO LineInfo-watchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipWatchLineInfo chip offset')---- | Stop watching a line for status change events.-unwatchLine :: Chip -> Offset -> IO ()-unwatchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipUnwatchLineInfo chip offset')---- | Wait for status change info events on any of the watched lines on the chip until the specified timeout.--- Throws 'WaitInfoEventFailed' on error.-waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)-waitEvent chip timeout = do- res <- unwrapOrThrow WaitInfoEventFailed (D.chipWaitInfoEvent chip timeout)- pure $ case res of- D.EventReady -> EventReady (ReadyChip chip)- D.Timeout -> TimeoutResult---- | Read a status change info event from a chip once 'waitEvent' indicates it is ready,--- and automatically free it afterwards.-withEvent :: ReadyChip -> (InfoEvent -> IO a) -> IO a-withEvent readyChip = bracket (readInfoEvent readyChip) freeInfoEvent---- | Get the event type of an 'InfoEvent' ('Requested', 'Released', 'ConfigChanged').-eventType :: InfoEvent -> IO InfoEventType-eventType = D.infoEventType---- | Get the timestamp in nanoseconds of an 'InfoEvent'.-timestampNs :: InfoEvent -> IO Timestamp-timestampNs = D.infoEventTimestamp---- | Get the line info snapshot associated with an 'InfoEvent'.-lineInfo :: InfoEvent -> IO LineInfo-lineInfo = D.infoEventLineInfo
− src/Fuyu/GPIO/Chip/Watch/Unsafe.hs
@@ -1,33 +0,0 @@--- |--- Module : Fuyu.GPIO.Chip.Watch.Unsafe--- Description : Unsafe manual resource allocation for line info events.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('readInfoEvent', 'freeInfoEvent')--- for applications that cannot use managed bracket functions.-module Fuyu.GPIO.Chip.Watch.Unsafe- ( -- * Types & Security Token- Chip- , ReadyChip(..)- , readyToChip- , InfoEvent-- -- * Unsafe Manual Resource Allocation- , readInfoEvent- , freeInfoEvent- ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Read a line info event from a chip after 'Fuyu.GPIO.Chip.Watch.waitEvent' confirms it is ready.--- Must be manually freed using 'freeInfoEvent'.-readInfoEvent :: ReadyChip -> IO InfoEvent-readInfoEvent (ReadyChip chip) = unwrapOrThrow ReadInfoEventFailed (D.chipReadInfoEvent chip)---- | Free an info event object.-freeInfoEvent :: InfoEvent -> IO ()-freeInfoEvent = D.infoEventFree
src/Fuyu/GPIO/EdgeEvent.hs view
@@ -10,8 +10,14 @@ -- This module provides managed resource brackets ('withBuffer') and functions for waiting -- on edge events ('waitEvents') and reading them ('readEvents') securely using the -- 'ReadyRequest' capability token.+--+-- It is designed to be imported qualified:+--+-- @+-- import qualified Fuyu.GPIO.EdgeEvent as Edge+-- @ module Fuyu.GPIO.EdgeEvent- ( -- * Security Token & Wait Result+ ( -- * Security Tokens & Wait Result WaitResult(..) , ReadyRequest(..) , readyToRequest@@ -21,8 +27,10 @@ , Capacity , userBufferCapacity , capacity- , Event+ , RawEvent , Timeout+ , Event+ , EventType , pattern Nanoseconds , pattern Immediate , pattern Infinite@@ -34,18 +42,19 @@ -- * Event Data Type & Parser , NonEmpty(..) , EdgeEvent(..)- , parseEvent+ , parseRawEvent - -- * Event Buffer Operations (Managed)+ -- * Event Buffer Operations , withBuffer , bufferCapacity , bufferNumEvents- , bufferEvent+ , bufferRawEvent -- * Waiting & Reading Events , waitEvents , readEvents- , withRawEvents+ , forRawEvents+ , forRawEvents_ -- * RawEdgeEvent Metadata Accessors , eventType@@ -56,16 +65,30 @@ , copyEvent ) where -import Control.Exception (bracket)-import Control.Monad (forM)+import Control.Exception (bracket, throwIO)+import Foreign.C.Error (Errno(..))+import Control.Monad (forM, forM_) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NE import Data.Word (Word64) import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.EdgeEvent.Unsafe (newEventBuffer, freeEventBuffer, readEventsRaw)+import Fuyu.GPIO.Unsafe (newEventBuffer, freeEventBuffer, readEventsRaw) import Fuyu.GPIO.Exception import Fuyu.GPIO.Types hiding (eventType) +--------------------------------------------------------------------------------+-- Domain Type Aliases+--------------------------------------------------------------------------------+-- | Type alias for 'EdgeEvent' designed for qualified use (e.g. @Edge.Event@).+type Event = EdgeEvent++-- | Type alias for 'EdgeEventType' designed for qualified use (e.g. @Edge.EventType@).+type EventType = EdgeEventType ++--------------------------------------------------------------------------------+-- Core Edge Event Functions +--------------------------------------------------------------------------------+ -- | Allocate an edge event buffer of the specified capacity and free it automatically afterwards. withBuffer :: Capacity -> (Buffer -> IO a) -> IO a withBuffer capacity' = bracket (newEventBuffer capacity') freeEventBuffer@@ -78,67 +101,79 @@ bufferNumEvents :: Buffer -> IO Word bufferNumEvents = D.eventBufferNumEvents --- | Get a specific edge event from the buffer by index.-bufferEvent :: Buffer -> Word -> IO Event-bufferEvent buf idx = unwrapOrThrow ReadEdgeEventsFailed (D.eventBufferGetEvent buf idx)+-- | Get a specific edge event from the buffer by index, returning 'Nothing' if the index is out of bounds.+bufferRawEvent :: Buffer -> Word -> IO (Maybe RawEvent)+bufferRawEvent buf idx = do+ res <- D.eventBufferGetEvent buf idx+ pure $ case res of+ Right ev -> Just ev+ Left _ -> Nothing -- | Wait for edge events to occur on requested lines until the specified timeout. -- Throws 'WaitEdgeEventsFailed' on error. waitEvents :: Request -> Timeout -> IO (WaitResult ReadyRequest) waitEvents req timeout = do- res <- unwrapOrThrow WaitEdgeEventsFailed (D.lineRequestWaitEdgeEvents req timeout)- pure $ case res of- D.EventReady -> EventReady (ReadyRequest req)- D.Timeout -> TimeoutResult+ res <- D.lineRequestWaitEdgeEvents req timeout+ case res of+ Left (Errno 4) -> waitEvents req timeout -- Retry on EINTR so GHC RTS can deliver UserInterrupt.+ Left err -> throwIO (WaitEdgeEventsFailed err)+ Right D.EventReady -> pure (EventReady (ReadyRequest req))+ Right D.Timeout -> pure TimeoutResult -- | Parse a raw edge event pointer into a pure Haskell 'EdgeEvent' structure.-parseEvent :: Event -> IO EdgeEvent-parseEvent ev = EdgeEvent+parseRawEvent :: RawEvent -> IO Event+parseRawEvent ev = EdgeEvent <$> D.rawEdgeEventLineOffset ev <*> D.rawEdgeEventType ev <*> D.rawEdgeEventTimestampNs ev -- | Read buffered edge events once 'waitEvents' indicates they are ready, -- parsing them into a non-empty list of pure 'EdgeEvent' structures.-readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty EdgeEvent)-readEvents readyReq buf = withRawEvents readyReq buf parseEvent+readEvents :: ReadyRequest -> Buffer -> IO (NonEmpty Event)+readEvents readyReq buf = forRawEvents readyReq buf parseRawEvent -- | Process raw edge events directly in the buffer using a callback without intermediate allocations, -- returning a non-empty list of results.-withRawEvents :: ReadyRequest -> Buffer -> (Event -> IO a) -> IO (NonEmpty a)-withRawEvents readyReq buf action = do+forRawEvents :: ReadyRequest -> Buffer -> (RawEvent -> IO a) -> IO (NonEmpty a)+forRawEvents readyReq buf action = do count <- readEventsRaw readyReq buf results <- forM [0 .. count - 1] $ \idx -> do- ev <- bufferEvent buf (fromIntegral idx)+ Just ev <- bufferRawEvent buf (fromIntegral idx)+ action ev + return $ NE.fromList results+ +-- | Same as 'forRawEvents' but ignore the results +forRawEvents_ :: ReadyRequest -> Buffer -> (RawEvent -> IO b) -> IO ()+forRawEvents_ readyReq buf action = do+ count <- readEventsRaw readyReq buf+ forM_ [0 .. count - 1] $ \idx -> do+ Just ev <- bufferRawEvent buf (fromIntegral idx) action ev- case NE.nonEmpty results of- Just ne -> pure ne- Nothing -> ioError (userError "readEvents: expected at least one event from ReadyRequest but got none")-+ ----------------------------------------------------------------------------------- RawEdgeEvent Metadata Accessors+-- RawEvent Metadata Accessors -------------------------------------------------------------------------------- -- | Get the type of event ('Rising' or 'Falling').-eventType :: Event -> IO EdgeEventType+eventType :: RawEvent -> IO EventType eventType = D.rawEdgeEventType -- | Get the event timestamp in nanoseconds.-timestampNs :: Event -> IO Timestamp+timestampNs :: RawEvent -> IO Timestamp timestampNs = D.rawEdgeEventTimestampNs -- | Get the offset of the line that triggered the event.-lineOffset :: Event -> IO Offset+lineOffset :: RawEvent -> IO Offset lineOffset = D.rawEdgeEventLineOffset -- | Get the global sequence number of the event.-globalSeqNo :: Event -> IO Word64+globalSeqNo :: RawEvent -> IO Word64 globalSeqNo = D.rawEdgeEventGlobalSeqNo -- | Get the line-specific sequence number of the event.-lineSeqNo :: Event -> IO Offset+lineSeqNo :: RawEvent -> IO Offset lineSeqNo = D.rawEdgeEventLineSeqNo -- | Make a copy of a raw edge event object.-copyEvent :: Event -> IO Event+copyEvent :: RawEvent -> IO RawEvent copyEvent ev = unwrapOrThrow RawEdgeEventCopyFailed (D.rawEdgeEventCopy ev)
− src/Fuyu/GPIO/EdgeEvent/Unsafe.hs
@@ -1,42 +0,0 @@--- |--- Module : Fuyu.GPIO.EdgeEvent.Unsafe--- Description : Unsafe manual resource allocation and raw buffer reading for event buffers.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('newEventBuffer', 'freeEventBuffer') and raw reading ('readEventsRaw') for edge 'Buffer' handles.-module Fuyu.GPIO.EdgeEvent.Unsafe- ( -- * Types & Security Token- Buffer- , Capacity- , userBufferCapacity- , capacity- , ReadyRequest(..)-- -- * Unsafe Manual Resource Allocation & Reading- , newEventBuffer- , freeEventBuffer- , readEventsRaw- ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Allocate an edge event buffer of the specified capacity.--- Must be manually freed with 'freeEventBuffer'.-newEventBuffer :: Capacity -> IO Buffer-newEventBuffer cap = unwrapOrThrow EventBufferNewFailed (D.eventBufferNew (capacity cap))---- | Free an edge event buffer object.-freeEventBuffer :: Buffer -> IO ()-freeEventBuffer = D.eventBufferFree---- | Read raw edge events into the buffer and return the number of events read.--- Automatically uses the buffer's full capacity.--- Throws 'ReadEdgeEventsFailed' on error.-readEventsRaw :: ReadyRequest -> Buffer -> IO Int-readEventsRaw (ReadyRequest req) buf = do- cap <- D.eventBufferCapacity buf- unwrapOrThrow ReadEdgeEventsFailed (D.lineRequestReadEdgeEvents req buf cap)
src/Fuyu/GPIO/Exception.hs view
@@ -1,28 +1,25 @@ {-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE ScopedTypeVariables #-} -- | -- Module : Fuyu.GPIO.Exception--- Description : Exception types and high-level application handler for fuyu-gpio operations.+-- Description : Exception types for fuyu-gpio operations. -- Maintainer : BassGT -- Stability : experimental -- Portability : POSIX ----- High-level exception type 'GpioException' thrown by fuyu-gpio operations,--- and managed application runner 'withGpioApp' for graceful signal handling.+-- High-level exception type 'GpioException' thrown by fuyu-gpio operations. module Fuyu.GPIO.Exception ( GpioException(..) , unwrapOrThrow- , withGpioApp ) where -import Control.Exception (Exception, SomeException, catch, fromException, throwIO, AsyncException(UserInterrupt))+import Control.Exception (Exception, throwIO)+import System.OsPath.Posix (PosixPath) import Foreign.C.Error (Errno(..))-import Control.Monad (void) -- | High-level exceptions thrown by fuyu-gpio operations. data GpioException- = ChipOpenFailed FilePath Errno+ = ChipOpenFailed PosixPath Errno | ChipInfoFailed Errno | LineInfoFailed Errno | LineSettingsNewFailed Errno@@ -42,33 +39,10 @@ | LineInfoCopyFailed Errno | CustomGpioError String Errno | InvalidArgument String- deriving (Exception)--instance Eq GpioException where- ChipOpenFailed p1 e1 == ChipOpenFailed p2 e2 = p1 == p2 && e1 == e2- ChipInfoFailed e1 == ChipInfoFailed e2 = e1 == e2- LineInfoFailed e1 == LineInfoFailed e2 = e1 == e2- LineSettingsNewFailed e1 == LineSettingsNewFailed e2 = e1 == e2- LineSettingsSetFailed e1 == LineSettingsSetFailed e2 = e1 == e2- LineConfigNewFailed e1 == LineConfigNewFailed e2 = e1 == e2- RequestConfigNewFailed e1 == RequestConfigNewFailed e2 = e1 == e2- LineRequestFailed e1 == LineRequestFailed e2 = e1 == e2- EventBufferNewFailed e1 == EventBufferNewFailed e2 = e1 == e2- LineValueReadFailed e1 == LineValueReadFailed e2 = e1 == e2- LineValueWriteFailed e1 == LineValueWriteFailed e2 = e1 == e2- LineReconfigureFailed e1 == LineReconfigureFailed e2 = e1 == e2- WaitEdgeEventsFailed e1 == WaitEdgeEventsFailed e2 = e1 == e2- ReadEdgeEventsFailed e1 == ReadEdgeEventsFailed e2 = e1 == e2- WaitInfoEventFailed e1 == WaitInfoEventFailed e2 = e1 == e2- ReadInfoEventFailed e1 == ReadInfoEventFailed e2 = e1 == e2- RawEdgeEventCopyFailed e1 == RawEdgeEventCopyFailed e2 = e1 == e2- LineInfoCopyFailed e1 == LineInfoCopyFailed e2 = e1 == e2- CustomGpioError s1 e1 == CustomGpioError s2 e2 = s1 == s2 && e1 == e2- InvalidArgument msg1 == InvalidArgument msg2 = msg1 == msg2- _ == _ = False+ deriving (Eq, Exception) instance Show GpioException where- show (ChipOpenFailed path (Errno e)) = "ChipOpenFailed: Failed to open chip at '" ++ path ++ "' (errno " ++ show e ++ ")"+ show (ChipOpenFailed path (Errno e)) = "ChipOpenFailed: Failed to open chip at " ++ show path ++ " (errno " ++ show e ++ ")" show (ChipInfoFailed (Errno e)) = "ChipInfoFailed (errno " ++ show e ++ ")" show (LineInfoFailed (Errno e)) = "LineInfoFailed (errno " ++ show e ++ ")" show (LineSettingsNewFailed (Errno e)) = "LineSettingsNewFailed (errno " ++ show e ++ ")"@@ -97,20 +71,3 @@ Left errno -> throwIO (mkExc errno) Right val -> pure val --- | High-level managed application runner.--- Automatically handles 'Ctrl+C' ('UserInterrupt'), interrupted system calls ('EINTR' / 'WaitEdgeEventsFailed'),--- and prints formatted 'GpioException' messages cleanly without uncaught backtraces.-withGpioApp :: IO a -> IO ()-withGpioApp action = void action `catch` handleAppException- where- handleAppException :: SomeException -> IO ()- handleAppException exc- | isUserInterrupt exc = putStrLn "\nLoop terminated successfully!"- | Just (WaitEdgeEventsFailed (Errno 4)) <- fromException exc = putStrLn "\nLoop terminated successfully!"- | Just (gpioErr :: GpioException) <- fromException exc = putStrLn $ "\n[GPIO Exception]: " ++ show gpioErr- | otherwise = throwIO exc-- isUserInterrupt :: SomeException -> Bool- isUserInterrupt e = case fromException e of- Just UserInterrupt -> True- _ -> False
− src/Fuyu/GPIO/Line.hs
@@ -1,279 +0,0 @@--- |--- Module : Fuyu.GPIO.Line--- Description : High-level operations for GPIO line settings, requests, and value I/O.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides managed resource brackets ('withSettings', 'withConfig', 'withRequest')--- for configuring GPIO line properties (direction, bias, drive mode, active-low, debounce)--- and requesting access to read or write logical values to GPIO lines.-module Fuyu.GPIO.Line- ( -- * Types & Patterns- Settings- , Config- , Request- , RequestConfig- , Offset- , pattern Offset- , Value- , pattern Active- , pattern Inactive- , pattern ValueError- , Direction- , pattern DirAsIs- , pattern DirInput- , pattern DirOutput- , Edge- , pattern EdgeNone- , pattern EdgeRising- , pattern EdgeFalling- , pattern EdgeBoth- , Bias- , pattern BiasAsIs- , pattern BiasUnknown- , pattern BiasDisabled- , pattern BiasPullUp- , pattern BiasPullDown- , Drive- , pattern PushPull- , pattern OpenDrain- , pattern OpenSource- , Clock- , pattern Monotonic- , pattern Realtime- , pattern Hardware-- -- * Managed Resource Allocation (with*)- , withSettings- , withConfig- , withRequest-- -- * Line Settings Operations- , setDirection- , direction- , setEdgeDetection- , edgeDetection- , setBias- , bias- , setDrive- , drive- , setEventClock- , eventClock- , setActiveLow- , activeLow- , setDebouncePeriodUs- , debouncePeriodUs- , setOutputValue- , outputValue- , resetSettings-- -- * Line Configuration Operations- , addSettings- , settings- , setOutputValues- , numOffsets- , configuredOffsets- , resetConfig-- -- * Line Value Operations (Read / Write)- , value- , values- , valuesSubset- , setValue- , setValues- , setValuesSubset-- -- * Line Request Operations & Metadata- , chipName- , numLines- , requestedOffsets- , fd- , reconfigureLines- ) where--import Control.Exception (bracket, throwIO)-import Data.ByteString (ByteString)-import qualified Data.Vector.Storable as V-import System.Posix.Types (Fd)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Line.Unsafe (newSettings, freeSettings, newConfig, freeConfig, requestLines, releaseRequest)-import Fuyu.GPIO.Types------------------------------------------------------------------------------------- Resource Bracket Management------------------------------------------------------------------------------------- | Allocate a new line settings object and free it automatically afterwards.-withSettings :: (Settings -> IO a) -> IO a-withSettings = bracket newSettings freeSettings---- | Allocate a new line configuration object and free it automatically afterwards.-withConfig :: (Config -> IO a) -> IO a-withConfig = bracket newConfig freeConfig---- | Request GPIO lines from a chip and automatically release them afterwards.-withRequest :: Chip -> Maybe RequestConfig -> Config -> (Request -> IO a) -> IO a-withRequest chip maybeReqConf lineConf = bracket (requestLines chip maybeReqConf lineConf) releaseRequest------------------------------------------------------------------------------------- Line Settings Setters & Getters------------------------------------------------------------------------------------- | Set the line direction in the settings.-setDirection :: Settings -> Direction -> IO ()-setDirection set dir = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDirection set dir)---- | Get the line direction from the settings.-direction :: Settings -> IO Direction-direction = D.lineSettingsDirection---- | Set edge detection in the settings.-setEdgeDetection :: Settings -> Edge -> IO ()-setEdgeDetection set edge = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEdgeDetection set edge)---- | Get edge detection from the settings.-edgeDetection :: Settings -> IO Edge-edgeDetection = D.lineSettingsEdgeDetection---- | Set electrical bias in the settings.-setBias :: Settings -> Bias -> IO ()-setBias _ BiasUnknown = throwIO $ InvalidArgument "setBias: BiasUnknown is a read-only state and cannot be set as a bias configuration."-setBias set biasVal = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetBias set biasVal)---- | Get electrical bias from the settings.-bias :: Settings -> IO Bias-bias = D.lineSettingsBias---- | Set drive mode in the settings.-setDrive :: Settings -> Drive -> IO ()-setDrive set driveMode = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetDrive set driveMode)---- | Get drive mode from the settings.-drive :: Settings -> IO Drive-drive = D.lineSettingsDrive---- | Set event clock source in the settings.-setEventClock :: Settings -> Clock -> IO ()-setEventClock set clk = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetEventClock set clk)---- | Get event clock source from the settings.-eventClock :: Settings -> IO Clock-eventClock = D.lineSettingsEventClock---- | Set active-low in the settings.-setActiveLow :: Settings -> Bool -> IO ()-setActiveLow = D.lineSettingsSetActiveLow---- | Get active-low setting.-activeLow :: Settings -> IO Bool-activeLow = D.lineSettingsActiveLow---- | Set debounce period in microseconds.-setDebouncePeriodUs :: Settings -> Word -> IO ()-setDebouncePeriodUs = D.lineSettingsSetDebouncePeriodUs---- | Get debounce period in microseconds.-debouncePeriodUs :: Settings -> IO Word-debouncePeriodUs = D.lineSettingsDebouncePeriodUs---- | Set default output value in the settings.-setOutputValue :: Settings -> Value -> IO ()-setOutputValue _ ValueError = throwIO $ InvalidArgument "setOutputValue: ValueError pattern is a read-only error state and cannot be set as an output value."-setOutputValue set val = unwrapOrThrow LineSettingsSetFailed (D.lineSettingsSetOutputValue set val)---- | Get default output value from the settings.-outputValue :: Settings -> IO Value-outputValue = D.lineSettingsOutputValue---- | Reset line settings object to default values.-resetSettings :: Settings -> IO ()-resetSettings = D.lineSettingsReset------------------------------------------------------------------------------------- Line Configuration Operations------------------------------------------------------------------------------------- | Add settings for a vector of line offsets in the configuration.-addSettings :: Config -> V.Vector Offset -> Settings -> IO ()-addSettings config offsets stgs = unwrapOrThrow LineConfigNewFailed (D.lineConfigAddLineSettings config offsets stgs)---- | Get settings for a specific line offset from configuration.-settings :: Config -> Offset -> IO Settings-settings config offset' = unwrapOrThrow LineConfigNewFailed (D.lineConfigLineSettings config offset')---- | Set output values for lines in configuration.-setOutputValues :: Config -> V.Vector Value -> IO ()-setOutputValues config vals- | V.elem ValueError vals = throwIO $ InvalidArgument "setOutputValues: Vector contains ValueError pattern, which cannot be set as an output value."- | otherwise = unwrapOrThrow LineConfigNewFailed (D.lineConfigSetOutputValues config vals)---- | Get the number of configured offsets in the line configuration.-numOffsets :: Config -> IO Word-numOffsets = D.lineConfigNumOffsets---- | Get all configured line offsets in the configuration as a Storable 'V.Vector'.-configuredOffsets :: Config -> IO (V.Vector Offset)-configuredOffsets = D.lineConfigConfiguredOffsets---- | Reset line configuration object to empty state.-resetConfig :: Config -> IO ()-resetConfig = D.lineConfigReset------------------------------------------------------------------------------------- Line Reading & Writing------------------------------------------------------------------------------------- | Get the logical value of a requested GPIO line at the given offset.-value :: Request -> Offset -> IO Value-value req offset' = unwrapOrThrow LineValueReadFailed (D.lineRequestValue req offset')---- | Get the logical values of all requested lines as a Storable 'V.Vector'.-values :: Request -> IO (V.Vector Value)-values req = unwrapOrThrow LineValueReadFailed (D.lineRequestValues req)---- | Get the logical values of a subset of requested lines specified by offsets.-valuesSubset :: Request -> V.Vector Offset -> IO (V.Vector Value)-valuesSubset req offsets = unwrapOrThrow LineValueReadFailed (D.lineRequestSubsetValues req offsets)---- | Set the logical value of a requested GPIO line at the given offset.-setValue :: Request -> Offset -> Value -> IO ()-setValue _ _ ValueError = throwIO $ InvalidArgument "setValue: ValueError pattern is a read-only error state and cannot be written to a GPIO line."-setValue req offset' val = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValue req offset' val)---- | Set the logical values of all requested lines from a Storable 'V.Vector'.-setValues :: Request -> V.Vector Value -> IO ()-setValues req vals- | V.elem ValueError vals = throwIO $ InvalidArgument "setValues: Vector contains ValueError pattern, which cannot be written to GPIO lines."- | otherwise = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValues req vals)---- | Set the logical values of a subset of requested lines from vectors of offsets and values.-setValuesSubset :: Request -> V.Vector Offset -> V.Vector Value -> IO ()-setValuesSubset req offsets vals- | V.elem ValueError vals = throwIO $ InvalidArgument "setValuesSubset: Vector contains ValueError pattern, which cannot be written to GPIO lines."- | otherwise = unwrapOrThrow LineValueWriteFailed (D.lineRequestSetValuesSubset req offsets vals)------------------------------------------------------------------------------------- Line Request Operations & Metadata------------------------------------------------------------------------------------- | Get the name of the chip this request was made on.-chipName :: Request -> IO ByteString-chipName = D.lineRequestChipName---- | Get the number of lines in the request.-numLines :: Request -> IO Word-numLines = D.lineRequestNumLines---- | Get all requested line offsets as a Storable 'V.Vector'.-requestedOffsets :: Request -> IO (V.Vector Offset)-requestedOffsets = D.lineRequestRequestedOffsets---- | Get the file descriptor associated with the line request handle.-fd :: Request -> IO Fd-fd = D.lineRequestFd---- | Update the configuration of lines associated with an active line request.-reconfigureLines :: Request -> Config -> IO ()-reconfigureLines req config = unwrapOrThrow LineReconfigureFailed (D.lineRequestReconfigure req config)
− src/Fuyu/GPIO/Line/Info.hs
@@ -1,92 +0,0 @@--- |--- Module : Fuyu.GPIO.Line.Info--- Description : Read-only metadata query functions for LineInfo snapshots.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ This module provides functions to inspect 'LineInfo' snapshots.--- It is designed to be imported qualified:------ @--- import qualified Fuyu.GPIO.Line.Info as LineInfo--- @-module Fuyu.GPIO.Line.Info- ( -- * Types- LineInfo-- -- * Managed Resource Allocation- , withLineInfo-- -- * Metadata Accessors- , offset- , name- , isUsed- , consumer- , direction- , edgeDetection- , bias- , drive- , isActiveLow- , isDebounced- , debouncePeriod- , eventClock- ) where--import Control.Exception (bracket)-import Data.ByteString (ByteString)-import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Line.Info.Unsafe (lineInfo, freeLineInfo)-import Fuyu.GPIO.Types---- | Retrieve information about a specific line on a chip and free it automatically afterwards.-withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a-withLineInfo chip offset' = bracket (lineInfo chip offset') freeLineInfo---- | Get the numeric 'Offset' of the line from a 'LineInfo' snapshot.-offset :: LineInfo -> IO Offset-offset = D.lineInfoOffset---- | Get the name of the line (e.g. "GPIO17"), if set.-name :: LineInfo -> IO (Maybe ByteString)-name = D.lineInfoName---- | Check if the line is currently in use by a consumer kernel driver or user process.-isUsed :: LineInfo -> IO Bool-isUsed = D.lineInfoIsUsed---- | Get the consumer name string of the line, if in use.-consumer :: LineInfo -> IO (Maybe ByteString)-consumer = D.lineInfoConsumer---- | Get the configured direction of the line ('DirInput', 'DirOutput', 'DirAsIs').-direction :: LineInfo -> IO Direction-direction = D.lineInfoDirection---- | Get the configured edge detection of the line ('EdgeNone', 'EdgeRising', 'EdgeFalling', 'EdgeBoth').-edgeDetection :: LineInfo -> IO Edge-edgeDetection = D.lineInfoEdgeDetection---- | Get the configured electrical bias ('BiasDisabled', 'BiasPullUp', 'BiasPullDown', etc.).-bias :: LineInfo -> IO Bias-bias = D.lineInfoBias---- | Get the configured drive mode ('PushPull', 'OpenDrain', 'OpenSource').-drive :: LineInfo -> IO Drive-drive = D.lineInfoDrive---- | Check if active-low logic is configured for the line.-isActiveLow :: LineInfo -> IO Bool-isActiveLow = D.lineInfoIsActiveLow---- | Check if hardware debounce is configured for the line.-isDebounced :: LineInfo -> IO Bool-isDebounced = D.lineInfoIsDebounced---- | Get the debounce period in microseconds for the line.-debouncePeriod :: LineInfo -> IO Word-debouncePeriod = D.lineInfoDebouncePeriod---- | Get the event clock source configured for the line ('Monotonic', 'Realtime', 'Hardware').-eventClock :: LineInfo -> IO Clock-eventClock = D.lineInfoEventClock
− src/Fuyu/GPIO/Line/Info/Unsafe.hs
@@ -1,35 +0,0 @@--- |--- Module : Fuyu.GPIO.Line.Info.Unsafe--- Description : Unsafe manual resource allocation for LineInfo snapshots.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('lineInfo', 'freeLineInfo', 'copyLineInfo') for 'LineInfo' handles.-module Fuyu.GPIO.Line.Info.Unsafe- ( -- * Types- LineInfo-- -- * Unsafe Manual Resource Allocation- , lineInfo- , freeLineInfo- , copyLineInfo- ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Retrieve information about a specific line on a chip.--- Must be manually freed using 'freeLineInfo'.-lineInfo :: Chip -> Offset -> IO LineInfo-lineInfo chip offset' = unwrapOrThrow LineInfoFailed (D.chipLineInfo chip offset')---- | Free a 'LineInfo' handle.-freeLineInfo :: LineInfo -> IO ()-freeLineInfo = D.lineInfoFree---- | Make a copy of a 'LineInfo' snapshot.--- Must be manually freed using 'freeLineInfo'.-copyLineInfo :: LineInfo -> IO LineInfo-copyLineInfo info = unwrapOrThrow LineInfoCopyFailed (D.lineInfoCopy info)
− src/Fuyu/GPIO/Line/Unsafe.hs
@@ -1,57 +0,0 @@--- |--- Module : Fuyu.GPIO.Line.Unsafe--- Description : Unsafe manual resource allocation for line settings, config, and requests.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('newSettings', 'freeSettings', 'newConfig', 'freeConfig',--- 'requestLines', 'releaseRequest') for line settings, configs, and requests.-module Fuyu.GPIO.Line.Unsafe- ( -- * Types- Chip- , Settings- , Config- , Request- , RequestConfig-- -- * Unsafe Manual Resource Allocation- , newSettings- , freeSettings- , newConfig- , freeConfig- , requestLines- , releaseRequest- ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Allocate a new line settings object.--- Must be manually freed with 'freeSettings'.-newSettings :: IO Settings-newSettings = unwrapOrThrow LineSettingsNewFailed D.lineSettingsNew---- | Free a line settings object.-freeSettings :: Settings -> IO ()-freeSettings = D.lineSettingsFree---- | Allocate a new line configuration object.--- Must be manually freed with 'freeConfig'.-newConfig :: IO Config-newConfig = unwrapOrThrow LineConfigNewFailed D.lineConfigNew---- | Free a line configuration object.-freeConfig :: Config -> IO ()-freeConfig = D.lineConfigFree---- | Request GPIO lines from a chip.--- Must be manually released with 'releaseRequest'.-requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request-requestLines chip maybeReqConf lineConf =- unwrapOrThrow LineRequestFailed (D.chipRequestLines chip maybeReqConf lineConf)---- | Release a line request handle.-releaseRequest :: Request -> IO ()-releaseRequest = D.lineRequestRelease
+ src/Fuyu/GPIO/Monitor.hs view
@@ -0,0 +1,239 @@+{-# LANGUAGE PatternSynonyms #-}++-- |+-- Module : Fuyu.GPIO.Monitor+-- Description : Read-only metadata query functions for ChipInfo and LineInfo, +-- and operations for watching GPIO line status events.+-- Maintainer : BassGT+-- Stability : experimental+-- Portability : POSIX (Linux gpiod v2)+--+-- This module unifies metadata query functions for 'ChipInfo' and 'LineInfo'+-- snapshots, as well as real-time line status change event monitoring ('InfoEvent').+--+-- It is designed to be imported qualified:+--+-- @+-- import qualified Fuyu.GPIO.Monitor as Monitor+-- @+module Fuyu.GPIO.Monitor+ ( -- * Chip Metadata Snapshot & Inspection+ ChipInfo+ , withChipInfo+ , chipName+ , label+ , numLines++ -- * Line Metadata Snapshot & Inspection+ , LineInfo+ , withLineInfo+ , offset+ , lineName+ , isUsed+ , consumer+ , direction+ , edgeDetection+ , bias+ , drive+ , isActiveLow+ , isDebounced+ , debouncePeriod+ , eventClock++ -- * Line Status Watching & Events+ , ReadyChip(..)+ , readyToChip+ , WaitResult(..)+ , Event+ , EventType+ , pattern Requested+ , pattern Released+ , pattern ConfigChanged+ , withWatchLine+ , watchLine+ , unwatchLine+ , waitEvent+ , withEvent+ , eventType+ , timestampNs+ , lineInfo++ -- * Types & Patterns+ , Chip+ , Offset+ , pattern Offset+ , Direction+ , pattern DirAsIs+ , pattern DirInput+ , pattern DirOutput+ , Edge+ , pattern EdgeNone+ , pattern EdgeRising+ , pattern EdgeFalling+ , pattern EdgeBoth+ , Bias+ , pattern BiasAsIs+ , pattern BiasUnknown+ , pattern BiasDisabled+ , pattern BiasPullUp+ , pattern BiasPullDown+ , Drive+ , pattern PushPull+ , pattern OpenDrain+ , pattern OpenSource+ , Clock+ , pattern Monotonic+ , pattern Realtime+ , pattern Hardware+ , Timeout+ , pattern Nanoseconds+ , pattern Immediate+ , pattern Infinite+ , Timestamp+ ) where++import Control.Exception (bracket, throwIO)+import Foreign.C.Error (Errno(..))+import qualified Data.ByteString.Char8 as C8+import qualified Fuyu.GPIO.Direct as D+import qualified Fuyu.GPIO.Unsafe as Unsafe+import Fuyu.GPIO.Exception+import Fuyu.GPIO.Types hiding (eventType)++--------------------------------------------------------------------------------+-- Domain Type Aliases+--------------------------------------------------------------------------------++-- | Type alias for 'InfoEvent' designed for qualified use (e.g. @Monitor.Event@).+type Event = InfoEvent ++-- | Type alias for 'InfoEventType' designed for qualified use (e.g. @Monitor.EventType@).+type EventType = InfoEventType++--------------------------------------------------------------------------------+-- Chip Metadata Snapshot & Inspection+--------------------------------------------------------------------------------++-- | Retrieve information about a GPIO chip and free it automatically afterwards.+withChipInfo :: Chip -> (ChipInfo -> IO a) -> IO a+withChipInfo chip = bracket (Unsafe.chipInfo chip) Unsafe.freeChipInfo++-- | Get the name of the GPIO chip (e.g. "gpiochip0").+chipName :: ChipInfo -> IO String+chipName info = C8.unpack <$> D.chipInfoName info++-- | Get the label of the GPIO chip.+label :: ChipInfo -> IO String+label info = C8.unpack <$> D.chipInfoLabel info++-- | Get the total number of lines exposed by the GPIO chip.+numLines :: ChipInfo -> IO Word+numLines = D.chipInfoNumLines++--------------------------------------------------------------------------------+-- Line Metadata Snapshot & Inspection+--------------------------------------------------------------------------------++-- | Retrieve information about a specific line on a chip and free it automatically afterwards.+withLineInfo :: Chip -> Offset -> (LineInfo -> IO a) -> IO a+withLineInfo chip offset' = bracket (Unsafe.lineInfo chip offset') Unsafe.freeLineInfo++-- | Get the numeric 'Offset' of the line from a 'LineInfo' snapshot.+offset :: LineInfo -> IO Offset+offset = D.lineInfoOffset++-- | Get the name of the line (e.g. "GPIO17"), if set.+lineName :: LineInfo -> IO (Maybe String)+lineName info = fmap C8.unpack <$> D.lineInfoName info++-- | Check if the line is currently in use by a consumer kernel driver or user process.+isUsed :: LineInfo -> IO Bool+isUsed = D.lineInfoIsUsed++-- | Get the consumer name string of the line, if in use.+consumer :: LineInfo -> IO (Maybe String)+consumer info = fmap C8.unpack <$> D.lineInfoConsumer info++-- | Get the configured direction of the line ('DirInput', 'DirOutput', 'DirAsIs').+direction :: LineInfo -> IO Direction+direction = D.lineInfoDirection++-- | Get the configured edge detection of the line ('EdgeNone', 'EdgeRising', 'EdgeFalling', 'EdgeBoth').+edgeDetection :: LineInfo -> IO Edge+edgeDetection = D.lineInfoEdgeDetection++-- | Get the configured electrical bias ('BiasDisabled', 'BiasPullUp', 'BiasPullDown', etc.).+bias :: LineInfo -> IO Bias+bias = D.lineInfoBias++-- | Get the configured drive mode ('PushPull', 'OpenDrain', 'OpenSource').+drive :: LineInfo -> IO Drive+drive = D.lineInfoDrive++-- | Check if active-low logic is configured for the line.+isActiveLow :: LineInfo -> IO Bool+isActiveLow = D.lineInfoIsActiveLow++-- | Check if hardware debounce is configured for the line.+isDebounced :: LineInfo -> IO Bool+isDebounced = D.lineInfoIsDebounced++-- | Get the debounce period in microseconds for the line.+debouncePeriod :: LineInfo -> IO Word+debouncePeriod = D.lineInfoDebouncePeriod++-- | Get the event clock source configured for the line ('Monotonic', 'Realtime', 'Hardware').+eventClock :: LineInfo -> IO Clock+eventClock = D.lineInfoEventClock++--------------------------------------------------------------------------------+-- Line Status Watching & Events+--------------------------------------------------------------------------------++-- | Start watching a line for status change events (e.g. requested, released, reconfigured)+-- within a bracket, automatically unwatching the line and freeing the initial snapshot when finished.+-- Passes the initial 'LineInfo' snapshot of the line to the callback.+withWatchLine :: Chip -> Offset -> (LineInfo -> IO a) -> IO a+withWatchLine chip offset' =+ bracket+ (watchLine chip offset')+ (\info -> do+ Unsafe.freeLineInfo info+ unwatchLine chip offset')++-- | Start watching a line for status change events (e.g. requested, released, reconfigured).+-- Returns the initial 'LineInfo' snapshot of the line.+watchLine :: Chip -> Offset -> IO LineInfo+watchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipWatchLineInfo chip offset')++-- | Stop watching a line for status change events.+unwatchLine :: Chip -> Offset -> IO ()+unwatchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipUnwatchLineInfo chip offset')++-- | Wait for status change info events on any of the watched lines on the chip until the specified timeout.+-- Throws 'WaitInfoEventFailed' on error.+waitEvent :: Chip -> Timeout -> IO (WaitResult ReadyChip)+waitEvent chip timeout = do+ res <- D.chipWaitInfoEvent chip timeout+ case res of+ Left (Errno 4) -> waitEvent chip timeout -- Retry on EINTR so GHC RTS can deliver UserInterrupt.+ Left err -> throwIO (WaitInfoEventFailed err)+ Right D.EventReady -> pure (EventReady (ReadyChip chip))+ Right D.Timeout -> pure TimeoutResult+ +-- | Read a status change info event from a chip once 'waitEvent' indicates it is ready,+-- and automatically free it afterwards.+withEvent :: ReadyChip -> (Event -> IO a) -> IO a+withEvent readyChip = bracket (Unsafe.readInfoEvent readyChip) Unsafe.freeInfoEvent++-- | Get the event type of an 'InfoEvent' ('Requested', 'Released', 'ConfigChanged').+eventType :: Event -> IO EventType+eventType = D.infoEventType++-- | Get the timestamp in nanoseconds of an 'InfoEvent'.+timestampNs :: Event -> IO Timestamp+timestampNs = D.infoEventTimestamp++-- | Get the line info snapshot associated with an 'InfoEvent'.+lineInfo :: Event -> IO LineInfo+lineInfo = D.infoEventLineInfo
src/Fuyu/GPIO/RequestConfig.hs view
@@ -19,7 +19,7 @@ import Control.Exception (bracket) import Data.ByteString (ByteString) import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.RequestConfig.Unsafe (newRequestConfig, freeRequestConfig)+import Fuyu.GPIO.Unsafe (newRequestConfig, freeRequestConfig) import Fuyu.GPIO.Types -- | Allocate a new request configuration object and free it automatically afterwards.
− src/Fuyu/GPIO/RequestConfig/Unsafe.hs
@@ -1,29 +0,0 @@--- |--- Module : Fuyu.GPIO.RequestConfig.Unsafe--- Description : Unsafe manual resource allocation for RequestConfig.--- Maintainer : BassGT--- Stability : experimental--- Portability : POSIX (Linux gpiod v2)------ Manual resource allocation ('newRequestConfig', 'freeRequestConfig') for 'RequestConfig' handles.-module Fuyu.GPIO.RequestConfig.Unsafe- ( -- * Types- RequestConfig-- -- * Unsafe Manual Resource Allocation- , newRequestConfig- , freeRequestConfig- ) where--import qualified Fuyu.GPIO.Direct as D-import Fuyu.GPIO.Exception-import Fuyu.GPIO.Types---- | Allocate a new request configuration object.--- Must be manually freed with 'freeRequestConfig'.-newRequestConfig :: IO RequestConfig-newRequestConfig = unwrapOrThrow RequestConfigNewFailed D.requestConfigNew---- | Free a request configuration object.-freeRequestConfig :: RequestConfig -> IO ()-freeRequestConfig = D.requestConfigFree
src/Fuyu/GPIO/Types.hs view
@@ -9,7 +9,7 @@ , Request , RequestConfig , Buffer- , Event+ , RawEvent , ReadyRequest(..) , readyToRequest , ReadyChip(..)@@ -102,7 +102,7 @@ -- | Alias for 'D.EventBuffer' type Buffer = D.EventBuffer -- | Alias for 'D.RawEdgeEvent'-type Event = D.RawEdgeEvent+type RawEvent = D.RawEdgeEvent -- | Alias for 'D.LineOffset' type Offset = D.LineOffset
+ src/Fuyu/GPIO/Unsafe.hs view
@@ -0,0 +1,241 @@+-- |+-- Module : Fuyu.GPIO.Unsafe+-- Description : Unsafe manual resource allocation and low-level FFI operations.+-- Maintainer : BassGT+-- Stability : experimental+-- Portability : POSIX (Linux gpiod v2)+--+-- Manual resource allocation ('openChip', 'closeChip', 'newLineSettings', 'freeLineSettings', etc.)+-- and raw FFI operations for applications that cannot use managed bracket functions.+module Fuyu.GPIO.Unsafe+ ( -- * Types & Security Tokens+ Chip+ , ChipInfo+ , LineInfo+ , Settings+ , Config+ , Request+ , RequestConfig+ , Buffer+ , RawEvent+ , Offset+ , Capacity+ , userBufferCapacity+ , capacity+ , ReadyRequest(..)+ , readyToRequest+ , ReadyChip(..)+ , readyToChip+ , InfoEvent++ -- * Chip Manual Resource Allocation+ , openChip+ , closeChip++ -- * Chip Info Manual Resource Allocation+ , chipInfo+ , freeChipInfo++ -- * Chip Watch / Line Info Event Manual Resource Allocation+ , watchLine+ , unwatchLine+ , readInfoEvent+ , freeInfoEvent++ -- * Line Settings Manual Resource Allocation+ , newLineSettings+ , freeLineSettings++ -- * Line Configuration Manual Resource Allocation+ , newLineConfig+ , freeLineConfig++ -- * Line Request Manual Resource Allocation+ , requestLines+ , releaseLineRequest++ -- * Line Info Manual Resource Allocation & Copy+ , lineInfo+ , freeLineInfo+ , copyLineInfo++ -- * Request Configuration Manual Resource Allocation+ , newRequestConfig+ , freeRequestConfig++ -- * Edge Event Buffer Manual Allocation & Raw Reading+ , newEventBuffer+ , freeEventBuffer+ , readEventsRaw+ ) where++import System.OsPath.Posix (PosixPath)+import qualified Fuyu.GPIO.Direct as D+import Fuyu.GPIO.Exception+import Fuyu.GPIO.Types++--------------------------------------------------------------------------------+-- Chip Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Open a GPIO chip by its filesystem path (e.g. @"\/dev\/gpiochip0"@).+-- Must be manually closed using 'closeChip'.+openChip :: PosixPath -> IO Chip+openChip path = unwrapOrThrow (ChipOpenFailed path) (D.chipOpen path)++-- | Close a GPIO chip handle.+closeChip :: Chip -> IO ()+closeChip = D.chipClose++--------------------------------------------------------------------------------+-- Chip Info Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Retrieve chip info directly.+-- Must be manually freed using 'freeChipInfo'.+chipInfo :: Chip -> IO ChipInfo+chipInfo chip = unwrapOrThrow ChipInfoFailed (D.chipInfo chip)++-- | Free a 'ChipInfo' handle.+freeChipInfo :: ChipInfo -> IO ()+freeChipInfo = D.chipInfoFree++--------------------------------------------------------------------------------+-- Chip Watch / Line Info Event Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Start watching a line for status change events (e.g. requested, released, reconfigured).+--+-- Returns the initial 'LineInfo' snapshot of the line at the moment watching starts.+--+-- * __Memory Management__: The returned 'LineInfo' is allocated in C heap memory and+-- must be manually freed using 'freeLineInfo'.+-- * __Kernel Watch State__: Freeing the 'LineInfo' snapshot does /not/ stop the kernel watch;+-- the kernel continues watching the line until 'unwatchLine' is explicitly called.+--+-- @+-- monitorApp :: IO ()+-- monitorApp = do+-- chip <- openChip "\/dev\/gpiochip0"+--+-- -- Start watching line in the kernel and get initial snapshot+-- info <- watchLine chip (Offset 271)+--+-- -- Inspect initial metadata and free the snapshot immediately+-- dir <- Monitor.direction info+-- freeLineInfo info+--+-- -- ... wait for events with 'Monitor.waitEvent' and read with 'readInfoEvent' ...+--+-- -- Stop watching the line when finished+-- unwatchLine chip (Offset 271)+-- closeChip chip+-- @+watchLine :: Chip -> Offset -> IO LineInfo+watchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipWatchLineInfo chip offset')++-- | Stop watching a line for status change events.+-- Disables kernel notifications previously initiated with 'watchLine'.+unwatchLine :: Chip -> Offset -> IO ()+unwatchLine chip offset' = unwrapOrThrow LineInfoFailed (D.chipUnwatchLineInfo chip offset')++-- | Read a line info event from a chip after 'Fuyu.GPIO.Monitor.waitEvent' confirms it is ready.+-- Must be manually freed using 'freeInfoEvent'.+readInfoEvent :: ReadyChip -> IO InfoEvent+readInfoEvent (ReadyChip chip) = unwrapOrThrow ReadInfoEventFailed (D.chipReadInfoEvent chip)++-- | Free an info event object.+freeInfoEvent :: InfoEvent -> IO ()+freeInfoEvent = D.infoEventFree++--------------------------------------------------------------------------------+-- Line Settings Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Allocate a new line settings object.+-- Must be manually freed with 'freeLineSettings'.+newLineSettings :: IO Settings+newLineSettings = unwrapOrThrow LineSettingsNewFailed D.lineSettingsNew++-- | Free a line settings object.+freeLineSettings :: Settings -> IO ()+freeLineSettings = D.lineSettingsFree++--------------------------------------------------------------------------------+-- Line Configuration Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Allocate a new line configuration object.+-- Must be manually freed with 'freeLineConfig'.+newLineConfig :: IO Config+newLineConfig = unwrapOrThrow LineConfigNewFailed D.lineConfigNew++-- | Free a line configuration object.+freeLineConfig :: Config -> IO ()+freeLineConfig = D.lineConfigFree++--------------------------------------------------------------------------------+-- Line Request Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Request GPIO lines from a chip.+-- Must be manually released with 'releaseLineRequest'.+requestLines :: Chip -> Maybe RequestConfig -> Config -> IO Request+requestLines chip maybeReqConf lineConf =+ unwrapOrThrow LineRequestFailed (D.chipRequestLines chip maybeReqConf lineConf)++-- | Release a line request handle.+releaseLineRequest :: Request -> IO ()+releaseLineRequest = D.lineRequestRelease++--------------------------------------------------------------------------------+-- Line Info Manual Resource Allocation & Copy+--------------------------------------------------------------------------------++-- | Retrieve information about a specific line on a chip.+-- Must be manually freed using 'freeLineInfo'.+lineInfo :: Chip -> Offset -> IO LineInfo+lineInfo chip offset' = unwrapOrThrow LineInfoFailed (D.chipLineInfo chip offset')++-- | Free a 'LineInfo' handle.+freeLineInfo :: LineInfo -> IO ()+freeLineInfo = D.lineInfoFree++-- | Make a copy of a 'LineInfo' snapshot.+-- Must be manually freed using 'freeLineInfo'.+copyLineInfo :: LineInfo -> IO LineInfo+copyLineInfo info = unwrapOrThrow LineInfoCopyFailed (D.lineInfoCopy info)++--------------------------------------------------------------------------------+-- Request Configuration Manual Resource Allocation+--------------------------------------------------------------------------------++-- | Allocate a new request configuration object.+-- Must be manually freed with 'freeRequestConfig'.+newRequestConfig :: IO RequestConfig+newRequestConfig = unwrapOrThrow RequestConfigNewFailed D.requestConfigNew++-- | Free a request configuration object.+freeRequestConfig :: RequestConfig -> IO ()+freeRequestConfig = D.requestConfigFree++--------------------------------------------------------------------------------+-- Edge Event Buffer Manual Allocation & Raw Reading+--------------------------------------------------------------------------------++-- | Allocate an edge event buffer of the specified capacity.+-- Must be manually freed with 'freeEventBuffer'.+newEventBuffer :: Capacity -> IO Buffer+newEventBuffer cap = unwrapOrThrow EventBufferNewFailed (D.eventBufferNew (capacity cap))++-- | Free an edge event buffer object.+freeEventBuffer :: Buffer -> IO ()+freeEventBuffer = D.eventBufferFree++-- | Read raw edge events into the buffer and return the number of events read.+-- Automatically uses the buffer's full capacity.+-- Throws 'ReadEdgeEventsFailed' on error.+readEventsRaw :: ReadyRequest -> Buffer -> IO Int+readEventsRaw (ReadyRequest req) buf = do+ cap <- D.eventBufferCapacity buf+ unwrapOrThrow ReadEdgeEventsFailed (D.lineRequestReadEdgeEvents req buf cap)