diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for fuyu-gpio-direct
+
+## 0.1.0.0 -- 2026-07-29
+
+* Initial release. Low-level FFI bindings for libgpiod v2 in Haskell.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,459 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+individually obtain patent licenses, in effect transforming the
+program into proprietary software.  To prevent this, we have made it
+clear that any patent must be licensed for everyone's free use or not
+licensed at all.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently combined with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+into another language.  (Hereinafter, translation is included without
+limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a fully made executable work, complete
+source code means all the source code for all modules it contains, plus
+any associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of work is made by offering access to copy from
+    a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may
+add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among countries
+not thus excluded.  In such case, this License incorporates the
+limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/fuyu-gpio-direct.cabal b/fuyu-gpio-direct.cabal
new file mode 100644
--- /dev/null
+++ b/fuyu-gpio-direct.cabal
@@ -0,0 +1,35 @@
+cabal-version:      3.0
+name:               fuyu-gpio-direct 
+version:            0.1.0.0
+synopsis:           Direct Haskell bindings for Linux libgpiod v2
+description:
+    Mid-level and low-level Haskell bindings for interacting with Linux GPIO
+    character devices using libgpiod v2. Provides type-safe abstractions for
+    line requests, chip management, edge event monitoring, and settings.
+license:            LGPL-2.1-or-later
+license-file:       LICENSE
+author:             BassGT
+maintainer:         sebastian11medrano@gmail.com
+category:           System, Hardware
+build-type:         Simple
+extra-doc-files:    CHANGELOG.md
+
+source-repository head
+    type:     git
+    location: https://github.com/BassGT/fuyu-gpio-direct.git
+
+common warnings
+    ghc-options: -Wall
+
+library
+    import:           warnings
+    exposed-modules:  Fuyu.GPIO.Direct,
+                      Fuyu.GPIO.Direct.Types,
+                      Fuyu.GPIO.Direct.Bindings
+    build-depends:    base >= 4.18 && < 5,
+                      bytestring >= 0.10 && < 0.13,
+                      unix >= 2.7 && < 2.9,
+                      vector >= 0.12 && < 0.14
+    extra-libraries:  gpiod
+    hs-source-dirs:   src
+    default-language: Haskell2010
diff --git a/src/Fuyu/GPIO/Direct.hs b/src/Fuyu/GPIO/Direct.hs
new file mode 100644
--- /dev/null
+++ b/src/Fuyu/GPIO/Direct.hs
@@ -0,0 +1,873 @@
+{-# LANGUAGE PatternSynonyms #-}
+-- |
+-- Module      : Fuyu.GPIO.Direct
+-- Description : Mid-level Haskell bindings for Linux GPIO character device interface (libgpiod v2).
+-- Maintainer  : BassGT
+-- Portability : POSIX (Linux GPIO character device interface)
+--
+-- High-level direct Haskell interface for interacting with Linux GPIO character devices
+-- using @libgpiod v2@.
+module Fuyu.GPIO.Direct
+  ( -- * Module Overview
+    -- $overview
+
+    -- * High-Level Opaque Handles (Abstract Types)
+    Chip
+  , ChipInfo
+  , InfoEvent
+  , LineInfo
+  , LineSettings
+  , LineConfig
+  , LineRequest
+  , RequestConfig
+  , EventBuffer
+  , RawEdgeEvent
+
+    -- * Safety & Index Wrappers
+  , LineOffset(..)
+
+    -- * Line Definitions & Patterns
+  , LineValue(..)
+  , pattern LineActive
+  , pattern LineInactive
+  , pattern LineError
+
+  , LineDirection(..)
+  , pattern DirAsIs
+  , pattern DirInput
+  , pattern DirOutput
+
+  , LineEdge(..)
+  , pattern EdgeNone
+  , pattern EdgeRising
+  , pattern EdgeFalling
+  , pattern EdgeBoth
+
+  , LineBias(..)
+  , pattern BiasAsIs
+  , pattern BiasUnknown
+  , pattern BiasDisabled
+  , pattern BiasPullUp
+  , pattern BiasPullDown
+
+  , LineDrive(..)
+  , pattern PushPull
+  , pattern OpenDrain
+  , pattern OpenSource
+
+  , LineClock(..)
+  , pattern Monotonic
+  , pattern Realtime
+  , pattern Hardware
+
+  , InfoEventType(..)
+  , pattern LineRequested
+  , pattern LineReleased
+  , pattern LineConfigChanged
+
+  , EdgeEventType(..)
+  , pattern Rising
+  , pattern Falling
+
+  , WaitResult(..)
+  , TimeoutNs(..)
+  , TimestampNs
+  , EdgeEvent(..)
+
+    -- * Chip Management
+  , chipOpen
+  , chipClose
+  , chipInfo
+  , chipPath
+  , chipLineInfo
+  , chipWatchLineInfo
+  , chipUnwatchLineInfo
+  , chipFd
+  , chipWaitInfoEvent
+  , chipReadInfoEvent
+  , chipLineOffsetFromName
+  , chipRequestLines
+
+    -- * Chip Info
+  , chipInfoFree
+  , chipInfoName
+  , chipInfoLabel
+  , chipInfoNumLines
+
+    -- * Line Information
+  , lineInfoFree
+  , lineInfoCopy
+  , lineInfoOffset
+  , lineInfoName
+  , lineInfoIsUsed
+  , lineInfoConsumer
+  , lineInfoDirection
+  , lineInfoEdgeDetection
+  , lineInfoBias
+  , lineInfoDrive
+  , lineInfoIsActiveLow
+  , lineInfoIsDebounced
+  , lineInfoDebouncePeriod
+  , lineInfoEventClock
+
+    -- * Line Watch (Info Event)
+  , infoEventFree
+  , infoEventType
+  , infoEventTimestamp
+  , infoEventLineInfo
+
+    -- * Line Settings
+  , lineSettingsNew
+  , lineSettingsFree
+  , lineSettingsReset
+  , lineSettingsCopy
+  , lineSettingsSetDirection
+  , lineSettingsDirection
+  , lineSettingsSetEdgeDetection
+  , lineSettingsEdgeDetection
+  , lineSettingsSetBias
+  , lineSettingsBias
+  , lineSettingsSetDrive
+  , lineSettingsDrive
+  , lineSettingsSetEventClock
+  , lineSettingsEventClock
+  , lineSettingsSetActiveLow
+  , lineSettingsActiveLow
+  , lineSettingsSetDebouncePeriodUs
+  , lineSettingsDebouncePeriodUs
+  , lineSettingsSetOutputValue
+  , lineSettingsOutputValue
+
+    -- * Line Configuration
+  , lineConfigNew
+  , lineConfigFree
+  , lineConfigReset
+  , lineConfigAddLineSettings
+  , lineConfigLineSettings
+  , lineConfigSetOutputValues
+  , lineConfigNumOffsets
+  , lineConfigConfiguredOffsets
+
+    -- * Request Configuration
+  , requestConfigNew
+  , requestConfigFree
+  , requestConfigSetConsumer
+  , requestConfigConsumer
+  , requestConfigSetEventBufferSize
+  , requestConfigEventBufferSize
+
+    -- * Line Request
+  , lineRequestRelease
+  , lineRequestChipName
+  , lineRequestNumLines
+  , lineRequestRequestedOffsets
+  , lineRequestValue
+  , lineRequestSubsetValues
+  , lineRequestValues
+  , lineRequestSetValue
+  , lineRequestSetValuesSubset
+  , lineRequestSetValues
+  , lineRequestReconfigure
+  , lineRequestFd
+  , lineRequestWaitEdgeEvents
+  , lineRequestReadEdgeEvents
+
+    -- * Edge Events & Event Buffer
+  , rawEdgeEventFree
+  , rawEdgeEventCopy
+  , rawEdgeEventType
+  , rawEdgeEventTimestampNs
+  , rawEdgeEventLineOffset
+  , rawEdgeEventGlobalSeqNo
+  , rawEdgeEventLineSeqNo
+  , eventBufferNew
+  , eventBufferCapacity
+  , eventBufferFree
+  , eventBufferGetEvent
+  , eventBufferNumEvents
+
+    -- * Utilities
+  , isGPIOChip
+  , gpiodAPIVersion
+  ) where
+
+import System.Posix.Types (Fd)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import Foreign.C.Error (Errno, getErrno, eINVAL)
+import Foreign.C.Types (CInt, CLong)
+import Foreign.Marshal.Utils (toBool)
+import Foreign.Ptr (Ptr, nullPtr)
+import Data.Vector.Storable (Vector)
+import qualified Data.Vector.Storable as V
+import qualified Data.Vector.Storable.Mutable as MV
+import Data.Word (Word64)
+
+import Fuyu.GPIO.Direct.Bindings
+import Fuyu.GPIO.Direct.Types
+
+-- $overview
+-- High-level direct Haskell interface for interacting with Linux GPIO character devices
+-- using @libgpiod v2@.
+--
+-- This module wraps lower-level FFI calls into type-safe Haskell operations using 'ByteString',
+-- 'Vector', and 'Either' 'Foreign.C.Error.Errno' error handling.
+--
+-- All resources wrapping C pointers (such as t'Chip, t'LineInfo, t'LineSettings, t'LineConfig,
+-- t'LineRequest, etc.) must be explicitly freed or released when no longer needed.
+
+--------------------------------------------------------------------------------
+-- INTERNAL ERROR HELPERS
+--------------------------------------------------------------------------------
+
+checkNull :: IO (Ptr a) -> IO (Either Errno (Ptr a))
+checkNull action = do
+  ptr <- action
+  if ptr == nullPtr
+    then Left <$> getErrno
+    else return $ Right ptr
+
+checkMinusOne :: IO CInt -> IO (Either Errno ())
+checkMinusOne action = do
+  res <- action
+  if res == -1
+    then Left <$> getErrno
+    else return $ Right ()
+
+--------------------------------------------------------------------------------
+-- INTERNAL HELPERS
+--------------------------------------------------------------------------------
+ 
+timeoutNsToC :: TimeoutNs -> CLong
+timeoutNsToC Immediate        = 0
+timeoutNsToC Infinite         = -1
+timeoutNsToC (Nanoseconds ns) = fromIntegral ns
+
+--------------------------------------------------------------------------------
+-- 1. CHIP MANAGEMENT
+--------------------------------------------------------------------------------
+
+-- | Open a GPIO chip device node at the given filesystem path (e.g. @"\/dev\/gpiochip0"@).
+--
+-- Returns 'Right' t'Chip on success, or 'Left' 'Errno' on failure (such as 'Foreign.C.Error.eACCES'
+-- if permissions are missing or 'Foreign.C.Error.eNOENT' if the path does not exist).
+-- The returned handle must be closed with 'chipClose' when finished to free file descriptors and memory.
+chipOpen :: ByteString -> IO (Either Errno Chip)
+chipOpen bs = BS.useAsCString bs $ \cStr -> do
+  res <- checkNull (c_gpiod_chip_open cStr)
+  return $ Chip <$> res
+
+-- | Close an open GPIO chip device handle and release associated kernel and C resources.
+chipClose :: Chip -> IO ()
+chipClose (Chip ptr) = c_gpiod_chip_close ptr
+
+-- | Query static information (device name, label, line count) about an open GPIO chip.
+--
+-- Returns 'Right' t'ChipInfo on success. The caller must explicitly free the returned
+-- object using 'chipInfoFree'.
+chipInfo :: Chip -> IO (Either Errno ChipInfo)
+chipInfo (Chip ptr) = do
+  res <- checkNull (c_gpiod_chip_get_info ptr)
+  return $ ChipInfo <$> res
+
+-- | Retrieve the filesystem device path used when opening the chip.
+--
+-- Returns 'Right' 'ByteString' containing the path (e.g. @"\/dev\/gpiochip0"@).
+chipPath :: Chip -> IO (Either Errno ByteString)
+chipPath (Chip ptr) = do
+  res <- checkNull (c_gpiod_chip_get_path ptr)
+  case res of
+    Left err -> return (Left err)
+    Right cStr -> Right <$> BS.packCString cStr
+
+-- | Query a snapshot of status and configuration information for a specific line offset.
+--
+-- Returns 'Right' t'LineInfo on success. The caller must explicitly free the returned
+-- object using 'lineInfoFree'.
+chipLineInfo :: Chip -> LineOffset -> IO (Either Errno LineInfo)
+chipLineInfo (Chip ptr) offset = do
+  res <- checkNull $ c_gpiod_chip_get_line_info ptr offset
+  return $ LineInfo <$> res
+
+-- | Query line status snapshot and subscribe to status change events for the specified line offset.
+--
+-- Future status change events (such as line requested, released, or reconfigured) can be waited for
+-- via 'chipWaitInfoEvent' and read via 'chipReadInfoEvent'.
+-- The caller must explicitly free the returned t'LineInfo object using 'lineInfoFree'.
+chipWatchLineInfo :: Chip -> LineOffset -> IO (Either Errno LineInfo)
+chipWatchLineInfo (Chip ptr) offset = do
+  res <- checkNull $ c_gpiod_chip_watch_line_info ptr offset
+  return $ LineInfo <$> res   
+
+-- | Stop watching a line offset for status change events.
+--
+-- Disables notifications previously initiated via 'chipWatchLineInfo'.
+chipUnwatchLineInfo :: Chip -> LineOffset -> IO (Either Errno ())
+chipUnwatchLineInfo (Chip ptr) offset = do
+  checkMinusOne $ c_gpiod_chip_unwatch_line_info ptr offset
+
+-- | Retrieve the underlying Linux file descriptor ('Fd') associated with an open t'Chip.
+--
+-- Can be passed to event loops ('GHC.Event', @epoll@, @select@, etc.) to monitor line watch events.
+chipFd :: Chip -> IO Fd
+chipFd (Chip ptr) = c_gpiod_chip_get_fd ptr
+
+-- | Wait for status change events on any of the watched lines on the chip.
+--
+-- Accepts a t'TimeoutNs parameter ('Immediate', 'Infinite', or 'Nanoseconds').
+-- Returns 'Right' 'EventReady' if an event is available to read, or 'Right' 'Timeout' if timed out.
+chipWaitInfoEvent :: Chip -> TimeoutNs -> IO (Either Errno WaitResult)
+chipWaitInfoEvent (Chip ptr) ns = do
+  res <- c_gpiod_chip_wait_info_event ptr (fromIntegral $ timeoutNsToC ns)
+  if res == -1
+    then Left <$> getErrno
+    else case res of
+      1 -> return $ Right EventReady
+      0 -> return $ Right Timeout
+      _ -> return $ Right Timeout
+  
+-- | Read a single line status change event (t'InfoEvent) from the chip.
+--
+-- Should be called when 'chipWaitInfoEvent' indicates 'EventReady'.
+-- The caller must free the returned t'InfoEvent with 'infoEventFree'.
+chipReadInfoEvent :: Chip -> IO (Either Errno InfoEvent)
+chipReadInfoEvent (Chip ptr) = do
+  res <- checkNull $ c_gpiod_chip_read_info_event ptr 
+  return $ InfoEvent <$> res   
+
+-- | Look up the zero-based line offset index on the chip given a pin name string.
+--
+-- Returns 'Right' t'LineOffset if the pin name exists, or 'Left' 'Errno' (e.g. 'Foreign.C.Error.eENOENT') if not found.
+chipLineOffsetFromName :: Chip -> ByteString -> IO (Either Errno LineOffset)
+chipLineOffsetFromName (Chip ptr) name = do
+  res <- BS.useAsCString name $ \cStr -> c_gpiod_chip_get_line_offset_from_name ptr cStr
+  if res == -1 
+    then Left <$> getErrno 
+    else return $ Right $ LineOffset (fromIntegral res)
+
+-- | Request exclusive kernel control over a collection of lines on the chip.
+--
+-- Takes optional t'RequestConfig (consumer label, event buffer size) and mandatory t'LineConfig (settings mapped to offsets).
+-- Returns 'Right' t'LineRequest on success. The handle must be released with 'lineRequestRelease'.
+chipRequestLines :: Chip -> Maybe RequestConfig -> LineConfig -> IO (Either Errno LineRequest)
+chipRequestLines (Chip chipPtr) maybeReqConf (LineConfig lineConfPtr) = do
+  let reqConfPtr = case maybeReqConf of
+                     Just (RequestConfig ptr) -> ptr
+                     Nothing                  -> nullPtr
+  res <- checkNull (c_gpiod_chip_request_lines chipPtr reqConfPtr lineConfPtr)
+  return $ LineRequest <$> res
+
+--------------------------------------------------------------------------------
+-- 2. CHIP INFO
+--------------------------------------------------------------------------------
+
+-- | Free a t'ChipInfo structure allocated by 'chipInfo'.
+chipInfoFree :: ChipInfo -> IO ()
+chipInfoFree (ChipInfo info) = c_gpiod_chip_info_free info 
+
+-- | Retrieve the kernel device name string of the chip (e.g. @"gpiochip4"@).
+chipInfoName :: ChipInfo -> IO ByteString
+chipInfoName (ChipInfo info) = do
+  res <- c_gpiod_chip_info_get_name info
+  if res == nullPtr
+    then return BS.empty
+    else BS.packCString res
+
+-- | Retrieve the hardware controller label string of the chip (e.g. @"pinctrl-bcm2835"@).
+chipInfoLabel :: ChipInfo -> IO ByteString
+chipInfoLabel (ChipInfo info) = do
+  res <- c_gpiod_chip_info_get_label info
+  if res == nullPtr
+    then return BS.empty
+    else BS.packCString res
+
+-- | Retrieve the total number of GPIO lines exposed by the chip.
+chipInfoNumLines :: ChipInfo -> IO Word 
+chipInfoNumLines (ChipInfo info) = do
+  res <- c_gpiod_chip_info_get_num_lines info
+  return $ fromIntegral res
+
+--------------------------------------------------------------------------------
+-- 4. LINE INFORMATION
+--------------------------------------------------------------------------------
+
+-- | Free a t'LineInfo snapshot structure allocated by 'chipLineInfo', 'chipWatchLineInfo', or 'infoEventLineInfo'.
+lineInfoFree :: LineInfo -> IO ()
+lineInfoFree (LineInfo info) = c_gpiod_line_info_free info
+
+-- | Duplicate a t'LineInfo object. The copy must be freed separately with 'lineInfoFree'.
+lineInfoCopy :: LineInfo -> IO (Either Errno LineInfo)
+lineInfoCopy (LineInfo info) = do
+  res <- checkNull (c_gpiod_line_info_copy info)
+  return $ LineInfo <$> res
+
+-- | Retrieve the zero-based line offset index from a t'LineInfo snapshot.
+lineInfoOffset :: LineInfo -> IO LineOffset
+lineInfoOffset (LineInfo info) = c_gpiod_line_info_get_offset info
+
+-- | Retrieve the name of the GPIO line, or 'Nothing' if unnamed in device tree / specs.
+lineInfoName :: LineInfo -> IO (Maybe ByteString)
+lineInfoName (LineInfo info) = do
+  res <- c_gpiod_line_info_get_name info
+  if res == nullPtr
+    then return Nothing
+    else Just <$> BS.packCString res 
+
+-- | Check whether the line is currently in use / claimed by a driver or consumer.
+lineInfoIsUsed :: LineInfo -> IO Bool
+lineInfoIsUsed (LineInfo info) = do
+  res <- c_gpiod_line_info_is_used info
+  return $ toBool res
+
+-- | Retrieve the consumer identification string of the entity using the line, or 'Nothing' if unclaimed.
+lineInfoConsumer :: LineInfo -> IO (Maybe ByteString)
+lineInfoConsumer (LineInfo info) = do
+  name <- c_gpiod_line_info_get_consumer info
+  if name == nullPtr
+    then return Nothing
+    else Just <$> BS.packCString name
+
+-- | Query the line direction setting ('DirInput' or 'DirOutput').
+lineInfoDirection :: LineInfo -> IO LineDirection
+lineInfoDirection (LineInfo info) = c_gpiod_line_info_get_direction info
+
+-- | Query the line edge detection mode ('EdgeNone', 'EdgeRising', 'EdgeFalling', 'EdgeBoth').
+lineInfoEdgeDetection :: LineInfo -> IO LineEdge
+lineInfoEdgeDetection (LineInfo info) = c_gpiod_line_info_get_edge_detection info
+
+-- | Query internal pull resistor bias ('BiasDisabled', 'BiasPullUp', 'BiasPullDown', etc.).
+lineInfoBias :: LineInfo -> IO LineBias
+lineInfoBias (LineInfo info) = c_gpiod_line_info_get_bias info
+
+-- | Query output driver mode ('PushPull', 'OpenDrain', 'OpenSource').
+lineInfoDrive :: LineInfo -> IO LineDrive
+lineInfoDrive (LineInfo info) = c_gpiod_line_info_get_drive info
+
+-- | Check if active-low signal inversion is enabled on this line.
+lineInfoIsActiveLow :: LineInfo -> IO Bool
+lineInfoIsActiveLow (LineInfo info) = do
+  res <- c_gpiod_line_info_is_active_low info
+  return $ toBool res
+
+-- | Check if hardware/kernel input debounce filtering is enabled for this line.
+lineInfoIsDebounced :: LineInfo -> IO Bool
+lineInfoIsDebounced (LineInfo info) = do
+  res <- c_gpiod_line_info_is_debounced info
+  return $ toBool res
+
+-- | Query the input debounce period in microseconds (0 if debouncing is disabled).
+lineInfoDebouncePeriod :: LineInfo -> IO Word  
+lineInfoDebouncePeriod (LineInfo info) = do
+  res <- c_gpiod_line_info_get_debounce_period_us info
+  return $ fromIntegral res
+
+-- | Query the timestamp clock source used for line edge events ('Monotonic', 'Realtime', 'Hardware').
+lineInfoEventClock :: LineInfo -> IO LineClock
+lineInfoEventClock (LineInfo info) = c_gpiod_line_info_get_event_clock info
+
+--------------------------------------------------------------------------------
+-- 5. LINE WATCH (INFO EVENT) 
+--------------------------------------------------------------------------------
+
+-- | Free an t'InfoEvent structure allocated by 'chipReadInfoEvent'.
+infoEventFree :: InfoEvent -> IO ()
+infoEventFree (InfoEvent event) = c_gpiod_info_event_free event 
+
+-- | Query event classification ('LineRequested', 'LineReleased', or 'LineConfigChanged').
+infoEventType :: InfoEvent -> IO InfoEventType
+infoEventType (InfoEvent event) = c_gpiod_info_event_get_event_type event 
+
+-- | Query timestamp (in nanoseconds) when the line status event occurred.
+infoEventTimestamp :: InfoEvent -> IO TimestampNs 
+infoEventTimestamp (InfoEvent event) = c_gpiod_info_event_get_timestamp_ns event
+
+-- | Extract a line info snapshot (t'LineInfo) representing updated state at event time.
+infoEventLineInfo :: InfoEvent -> IO LineInfo
+infoEventLineInfo (InfoEvent event) = LineInfo <$> c_gpiod_info_event_get_line_info event
+  
+--------------------------------------------------------------------------------
+-- 6. LINE SETTINGS
+--------------------------------------------------------------------------------
+
+-- | Allocate a new t'LineSettings object with default attributes.
+--
+-- Must be freed with 'lineSettingsFree' when finished.
+lineSettingsNew :: IO (Either Errno LineSettings)
+lineSettingsNew = do 
+  res <- checkNull c_gpiod_line_settings_new
+  return $ LineSettings <$> res 
+
+-- | Free a t'LineSettings object and release associated resources.
+lineSettingsFree :: LineSettings -> IO ()
+lineSettingsFree (LineSettings settings) = c_gpiod_line_settings_free settings 
+
+-- | Reset a t'LineSettings object back to default values.
+lineSettingsReset :: LineSettings -> IO ()
+lineSettingsReset (LineSettings settings) = c_gpiod_line_settings_reset settings 
+
+-- | Duplicate a t'LineSettings object. The copy must be freed with 'lineSettingsFree'.
+lineSettingsCopy :: LineSettings -> IO (Either Errno LineSettings)
+lineSettingsCopy (LineSettings settings) = do
+  res <- checkNull (c_gpiod_line_settings_copy settings)
+  return $ LineSettings <$> res
+
+-- | Set the line pin direction ('DirInput', 'DirOutput', 'DirAsIs') in settings.
+lineSettingsSetDirection :: LineSettings -> LineDirection -> IO (Either Errno ())
+lineSettingsSetDirection (LineSettings settings) dir =
+  checkMinusOne $ c_gpiod_line_settings_set_direction settings dir
+  
+-- | Get the line pin direction in settings.
+lineSettingsDirection :: LineSettings -> IO LineDirection
+lineSettingsDirection (LineSettings settings) = c_gpiod_line_settings_get_direction settings
+
+-- | Set edge detection mode ('EdgeNone', 'EdgeRising', 'EdgeFalling', 'EdgeBoth') in settings.
+lineSettingsSetEdgeDetection :: LineSettings -> LineEdge -> IO (Either Errno ())
+lineSettingsSetEdgeDetection (LineSettings settings) edge =
+  checkMinusOne $ c_gpiod_line_settings_set_edge_detection settings edge
+
+-- | Get edge detection mode from settings.
+lineSettingsEdgeDetection :: LineSettings -> IO LineEdge
+lineSettingsEdgeDetection (LineSettings settings) = c_gpiod_line_settings_get_edge_detection settings
+
+-- | Set internal pull bias ('BiasDisabled', 'BiasPullUp', 'BiasPullDown', 'BiasAsIs') in settings.
+lineSettingsSetBias :: LineSettings -> LineBias -> IO (Either Errno ())
+lineSettingsSetBias (LineSettings settings) bias =
+  checkMinusOne $ c_gpiod_line_settings_set_bias settings bias
+
+-- | Get internal pull bias from settings.
+lineSettingsBias :: LineSettings -> IO LineBias
+lineSettingsBias (LineSettings settings) = c_gpiod_line_settings_get_bias settings
+
+-- | Set output driver mode ('PushPull', 'OpenDrain', 'OpenSource') in settings.
+lineSettingsSetDrive :: LineSettings -> LineDrive -> IO (Either Errno ())
+lineSettingsSetDrive (LineSettings settings) drive =
+  checkMinusOne $ c_gpiod_line_settings_set_drive settings drive
+
+-- | Get output driver mode from settings.
+lineSettingsDrive :: LineSettings -> IO LineDrive
+lineSettingsDrive (LineSettings settings) = c_gpiod_line_settings_get_drive settings
+
+-- | Set event timestamp clock source ('Monotonic', 'Realtime', 'Hardware') in settings.
+lineSettingsSetEventClock :: LineSettings -> LineClock -> IO (Either Errno ())
+lineSettingsSetEventClock (LineSettings settings) clock =
+  checkMinusOne $ c_gpiod_line_settings_set_event_clock settings clock
+
+-- | Get event timestamp clock source from settings.
+lineSettingsEventClock :: LineSettings -> IO LineClock
+lineSettingsEventClock (LineSettings settings) = c_gpiod_line_settings_get_event_clock settings
+
+-- | Set active-low signal inversion flag in settings.
+lineSettingsSetActiveLow :: LineSettings -> Bool -> IO ()
+lineSettingsSetActiveLow (LineSettings settings) activeLow =
+  c_gpiod_line_settings_set_active_low settings (if activeLow then 1 else 0)
+
+-- | Get active-low signal inversion flag from settings.
+lineSettingsActiveLow :: LineSettings -> IO Bool
+lineSettingsActiveLow (LineSettings settings) = do
+  res <- c_gpiod_line_settings_get_active_low settings
+  return $ toBool res
+
+-- | Set input debounce period in microseconds in settings.
+lineSettingsSetDebouncePeriodUs :: LineSettings -> Word -> IO ()
+lineSettingsSetDebouncePeriodUs (LineSettings settings) us =
+  c_gpiod_line_settings_set_debounce_period_us settings (fromIntegral us)
+
+-- | Get input debounce period in microseconds from settings.
+lineSettingsDebouncePeriodUs :: LineSettings -> IO Word
+lineSettingsDebouncePeriodUs (LineSettings settings) = do
+  res <- c_gpiod_line_settings_get_debounce_period_us settings
+  return $ fromIntegral res
+
+-- | Set initial output value ('LineActive' or 'LineInactive') in settings.
+lineSettingsSetOutputValue :: LineSettings -> LineValue -> IO (Either Errno ())
+lineSettingsSetOutputValue (LineSettings settings) val =
+  checkMinusOne $ c_gpiod_line_settings_set_output_value settings val
+
+-- | Get output value from settings.
+lineSettingsOutputValue :: LineSettings -> IO LineValue
+lineSettingsOutputValue (LineSettings settings) = c_gpiod_line_settings_get_output_value settings
+
+--------------------------------------------------------------------------------
+-- 7. LINE CONFIGURATION
+--------------------------------------------------------------------------------
+
+-- | Allocate a new t'LineConfig accumulator object.
+--
+-- Must be freed with 'lineConfigFree' when finished.
+lineConfigNew :: IO (Either Errno LineConfig)
+lineConfigNew = do  
+  res <- checkNull c_gpiod_line_config_new 
+  return $ LineConfig <$> res
+
+-- | Free a t'LineConfig object.
+lineConfigFree :: LineConfig -> IO ()
+lineConfigFree (LineConfig config) = c_gpiod_line_config_free config
+
+-- | Reset a t'LineConfig object, clearing all associated line settings.
+lineConfigReset :: LineConfig -> IO ()
+lineConfigReset (LineConfig config) = c_gpiod_line_config_reset config
+
+-- | Associate a set of t'LineOffset's (as a Storable 'Vector') with a t'LineSettings configuration.
+lineConfigAddLineSettings :: LineConfig -> Vector LineOffset -> LineSettings -> IO (Either Errno ())
+lineConfigAddLineSettings (LineConfig config) offsets (LineSettings settings) = do
+  let size = fromIntegral (V.length offsets)
+  checkMinusOne $ V.unsafeWith offsets $ \ptr -> 
+    c_gpiod_line_config_add_line_settings config ptr size settings
+
+-- | Fetch a copy of the t'LineSettings configured for a specific line offset.
+lineConfigLineSettings :: LineConfig -> LineOffset -> IO (Either Errno LineSettings)
+lineConfigLineSettings (LineConfig config) offset = do
+  res <- checkNull $ c_gpiod_line_config_get_line_settings config offset
+  return $ LineSettings <$> res 
+
+-- | Override output values for lines configured in t'LineConfig using a Storable 'Vector' of t'LineValue's.
+lineConfigSetOutputValues :: LineConfig -> Vector LineValue -> IO (Either Errno ())
+lineConfigSetOutputValues (LineConfig config) values = do
+  let size = fromIntegral (V.length values)
+  checkMinusOne $ V.unsafeWith values $ \ptr ->
+    c_gpiod_line_config_set_output_values config ptr size
+
+-- | Query total count of distinct line offsets configured within a t'LineConfig object.
+lineConfigNumOffsets :: LineConfig -> IO Word  
+lineConfigNumOffsets (LineConfig config) =
+  fromIntegral <$> c_gpiod_line_config_get_num_configured_offsets config 
+
+-- | Retrieve all configured line offsets stored in t'LineConfig as a Storable 'Vector'.
+lineConfigConfiguredOffsets :: LineConfig -> IO (Vector LineOffset)
+lineConfigConfiguredOffsets (LineConfig config) = do
+  num <- c_gpiod_line_config_get_num_configured_offsets config
+  vec <- MV.new (fromIntegral num)
+  _ <- MV.unsafeWith vec $ \ptr ->
+    c_gpiod_line_config_get_configured_offsets config ptr num
+  V.unsafeFreeze vec
+
+--------------------------------------------------------------------------------
+-- 8. REQUESTS CONFIG
+--------------------------------------------------------------------------------
+
+-- | Allocate a new t'RequestConfig object.
+--
+-- Must be freed with 'requestConfigFree' when finished.
+requestConfigNew :: IO (Either Errno RequestConfig)
+requestConfigNew = do  
+  res <- checkNull c_gpiod_request_config_new  
+  return $ RequestConfig <$> res 
+
+-- | Free a t'RequestConfig object.
+requestConfigFree :: RequestConfig -> IO()
+requestConfigFree (RequestConfig config) = c_gpiod_request_config_free config
+
+-- | Set the consumer identification string (e.g. @"my-app"@) in a t'RequestConfig.
+requestConfigSetConsumer :: RequestConfig -> ByteString -> IO()
+requestConfigSetConsumer (RequestConfig config) name =
+  BS.useAsCString name $ \cStr -> do 
+    c_gpiod_request_config_set_consumer config cStr
+  
+-- | Get the consumer identification string from a t'RequestConfig.
+requestConfigConsumer :: RequestConfig -> IO ByteString
+requestConfigConsumer (RequestConfig config) = do
+  res <- c_gpiod_request_config_get_consumer config
+  BS.packCString res 
+    
+-- | Set the size of kernel event buffer (in number of events) in a t'RequestConfig.
+requestConfigSetEventBufferSize :: RequestConfig -> Word -> IO ()
+requestConfigSetEventBufferSize (RequestConfig config) size =
+  c_gpiod_request_config_set_event_buffer_size config (fromIntegral size)
+
+-- | Get the size of kernel event buffer (in number of events) from a t'RequestConfig.
+requestConfigEventBufferSize :: RequestConfig -> IO Word
+requestConfigEventBufferSize (RequestConfig config) =
+  fromIntegral <$> c_gpiod_request_config_get_event_buffer_size config
+
+--------------------------------------------------------------------------------
+-- 9. LINE REQUEST
+--------------------------------------------------------------------------------
+
+-- | Release claimed GPIO lines and close the line request object.
+lineRequestRelease :: LineRequest -> IO ()
+lineRequestRelease (LineRequest request) = c_gpiod_line_request_release request
+
+-- | Retrieve the name of the GPIO chip on which this request was created.
+lineRequestChipName :: LineRequest -> IO ByteString
+lineRequestChipName (LineRequest request) = do
+  res <- c_gpiod_line_request_get_chip_name request
+  BS.packCString res  
+  
+-- | Query total count of lines claimed in this request.
+lineRequestNumLines :: LineRequest -> IO Word 
+lineRequestNumLines (LineRequest request) = 
+  fromIntegral <$> c_gpiod_line_request_get_num_requested_lines request 
+
+-- | Retrieve all line offsets claimed by this request as a Storable 'Vector'.
+lineRequestRequestedOffsets :: LineRequest -> IO (Vector LineOffset)
+lineRequestRequestedOffsets (LineRequest request) = do
+  num <- c_gpiod_line_request_get_num_requested_lines request
+  vec <- MV.new (fromIntegral num)
+  _ <- MV.unsafeWith vec $ \ptr ->
+    c_gpiod_line_request_get_requested_offsets request ptr num
+  V.unsafeFreeze vec
+
+-- | Read the logical value ('LineActive' or 'LineInactive') of a single requested line offset.
+lineRequestValue :: LineRequest -> LineOffset -> IO (Either Errno LineValue)
+lineRequestValue (LineRequest request) offset = do
+  lineValue <- c_gpiod_line_request_get_value request offset
+  if lineValue == LineError
+    then Left <$> getErrno
+    else return $ Right lineValue
+
+-- | Read logical values for a subset of requested lines into a Storable 'Vector'.
+lineRequestSubsetValues :: LineRequest -> Vector LineOffset -> IO (Either Errno (Vector LineValue))
+lineRequestSubsetValues (LineRequest request) offsets = do
+  let numValues = V.length offsets
+  mutValues <- MV.new numValues
+  res <- V.unsafeWith offsets $ \offsetsPtr ->
+    MV.unsafeWith mutValues $ \valuesPtr ->
+      c_gpiod_line_request_get_values_subset request (fromIntegral numValues) offsetsPtr valuesPtr
+  if res == -1
+    then Left <$> getErrno
+    else Right <$> V.unsafeFreeze mutValues
+
+-- | Read logical values for all requested lines into a Storable 'Vector'.
+lineRequestValues :: LineRequest -> IO (Either Errno (Vector LineValue))
+lineRequestValues (LineRequest request) = do
+  numLines <- c_gpiod_line_request_get_num_requested_lines request
+  mutValues <- MV.new (fromIntegral numLines)
+  res <- MV.unsafeWith mutValues $ \valuesPtr ->
+    c_gpiod_line_request_get_values request valuesPtr
+  if res == -1
+    then Left <$> getErrno
+    else Right <$> V.unsafeFreeze mutValues
+
+-- | Set the logical value ('LineActive' or 'LineInactive') of a single requested line offset.
+lineRequestSetValue :: LineRequest -> LineOffset -> LineValue -> IO (Either Errno ())
+lineRequestSetValue _ _ LineError = return (Left eINVAL)
+lineRequestSetValue (LineRequest request) offset value =
+  checkMinusOne $ c_gpiod_line_request_set_value request offset value
+
+-- | Set logical values for a subset of requested lines from Storable 'Vector's of offsets and values.
+lineRequestSetValuesSubset :: LineRequest -> Vector LineOffset -> Vector LineValue -> IO (Either Errno ())
+lineRequestSetValuesSubset (LineRequest request) offsets values
+  | V.length offsets /= V.length values = return (Left eINVAL)
+  | otherwise =
+      let numValues = V.length offsets
+      in V.unsafeWith offsets $ \offsetsPtr ->
+           V.unsafeWith values $ \valuesPtr ->
+             checkMinusOne $ c_gpiod_line_request_set_values_subset request (fromIntegral numValues) offsetsPtr valuesPtr
+
+-- | Set logical values for all requested lines from a Storable 'Vector'.
+lineRequestSetValues :: LineRequest -> Vector LineValue -> IO (Either Errno ())
+lineRequestSetValues (LineRequest request) values =
+  V.unsafeWith values $ \valuesPtr ->
+    checkMinusOne $ c_gpiod_line_request_set_values request valuesPtr
+
+-- | Update line configurations (direction, edge detection, output values) of existing requested lines.
+lineRequestReconfigure :: LineRequest -> LineConfig -> IO (Either Errno ())
+lineRequestReconfigure (LineRequest request) (LineConfig config) =
+  checkMinusOne $ c_gpiod_line_request_reconfigure_lines request config  
+
+-- | Retrieve file descriptor ('Fd') associated with the active line request for event looping.
+lineRequestFd :: LineRequest -> IO Fd
+lineRequestFd (LineRequest request) = c_gpiod_line_request_get_fd request
+
+-- | Wait for hardware edge events to occur on requested input lines.
+--
+-- Takes a t'TimeoutNs parameter. Returns 'Right' 'EventReady' if events are ready to read.
+lineRequestWaitEdgeEvents :: LineRequest -> TimeoutNs -> IO (Either Errno WaitResult)
+lineRequestWaitEdgeEvents (LineRequest request) timeoutNs = do
+  res <- c_gpiod_line_request_wait_edge_events request (timeoutNsToC timeoutNs)
+  if res == -1
+    then Left <$> getErrno
+    else case res of
+      1 -> return $ Right EventReady
+      0 -> return $ Right Timeout
+      _ -> return $ Right Timeout
+
+-- | Read up to @maxEvents@ edge events from a line request into an t'EventBuffer.
+--
+-- Returns 'Right' 'Int' containing the number of events read into the buffer.
+lineRequestReadEdgeEvents :: LineRequest -> EventBuffer -> Word -> IO (Either Errno Int)
+lineRequestReadEdgeEvents (LineRequest reqPtr) (EventBuffer bufPtr) maxEvents = do
+  count <- c_gpiod_line_request_read_edge_events reqPtr bufPtr (fromIntegral maxEvents)
+  if count == -1
+    then Left <$> getErrno
+    else return $ Right (fromIntegral count)
+
+--------------------------------------------------------------------------------
+-- 10. EDGE EVENTS & EVENT BUFFER
+--------------------------------------------------------------------------------
+
+-- | Free a t'RawEdgeEvent object.
+rawEdgeEventFree :: RawEdgeEvent -> IO ()
+rawEdgeEventFree (RawEdgeEvent event) = c_gpiod_edge_event_free event
+
+-- | Duplicate a t'RawEdgeEvent. The copy must be freed with 'rawEdgeEventFree'.
+rawEdgeEventCopy :: RawEdgeEvent -> IO (Either Errno RawEdgeEvent)
+rawEdgeEventCopy (RawEdgeEvent event) = do
+  res <- checkNull (c_gpiod_edge_event_copy event)
+  return $ RawEdgeEvent <$> res
+
+-- | Extract event edge type ('Rising' or 'Falling') from a raw edge event pointer.
+rawEdgeEventType :: RawEdgeEvent -> IO EdgeEventType
+rawEdgeEventType (RawEdgeEvent event) = c_gpiod_edge_event_get_event_type event
+
+-- | Extract event timestamp (in nanoseconds) from a raw edge event pointer.
+rawEdgeEventTimestampNs :: RawEdgeEvent -> IO TimestampNs
+rawEdgeEventTimestampNs (RawEdgeEvent event) = c_gpiod_edge_event_get_timestamp_ns event
+
+-- | Retrieve the zero-based line offset index that triggered the edge event.
+rawEdgeEventLineOffset :: RawEdgeEvent -> IO LineOffset
+rawEdgeEventLineOffset (RawEdgeEvent event) = c_gpiod_edge_event_get_line_offset event 
+
+-- | Retrieve the global event sequence number across all lines on the GPIO chip.
+rawEdgeEventGlobalSeqNo :: RawEdgeEvent -> IO Word64 
+rawEdgeEventGlobalSeqNo (RawEdgeEvent event) =
+  fromIntegral <$> c_gpiod_edge_event_get_global_seqno event  
+
+-- | Retrieve the line-specific event sequence number.
+rawEdgeEventLineSeqNo :: RawEdgeEvent -> IO LineOffset
+rawEdgeEventLineSeqNo (RawEdgeEvent event) = c_gpiod_edge_event_get_line_seqno event  
+
+-- | Allocate a new t'EventBuffer with the specified capacity.
+--
+-- Must be freed with 'eventBufferFree'.
+eventBufferNew :: Word -> IO (Either Errno EventBuffer)
+eventBufferNew cap = do
+  res <- checkNull $ c_gpiod_edge_event_buffer_new (fromIntegral cap)
+  return $ EventBuffer <$> res
+
+-- | Query the maximum capacity of an t'EventBuffer.
+eventBufferCapacity :: EventBuffer -> IO Word
+eventBufferCapacity (EventBuffer buffer) =
+  fromIntegral <$> c_gpiod_edge_event_buffer_get_capacity buffer 
+
+-- | Free an t'EventBuffer and release associated memory.
+eventBufferFree :: EventBuffer -> IO ()
+eventBufferFree (EventBuffer buffer) = c_gpiod_edge_event_buffer_free buffer 
+
+-- | Retrieve a raw edge event pointer from the buffer by zero-based index.
+eventBufferGetEvent :: EventBuffer -> Word -> IO (Either Errno RawEdgeEvent)
+eventBufferGetEvent (EventBuffer buffer) idx = do
+  res <- checkNull (c_gpiod_edge_event_buffer_get_event buffer (fromIntegral idx))
+  return $ RawEdgeEvent <$> res
+
+-- | Query the number of edge events currently populated in the buffer.
+eventBufferNumEvents :: EventBuffer -> IO Word
+eventBufferNumEvents (EventBuffer buffer) =
+  fromIntegral <$> c_gpiod_edge_event_buffer_get_num_events buffer  
+
+--------------------------------------------------------------------------------
+-- 11. UTILITIES
+--------------------------------------------------------------------------------
+
+-- | Check whether the filesystem path (e.g. @"\/dev\/gpiochip0"@) refers to a valid GPIO chip character device node.
+isGPIOChip :: ByteString -> IO Bool
+isGPIOChip path = do
+  BS.useAsCString path (fmap toBool . c_gpiod_is_gpiochip_device)
+
+-- | Retrieve the version string of the underlying C @libgpiod@ library (e.g. @"2.1"@).
+gpiodAPIVersion :: IO ByteString
+gpiodAPIVersion = do
+  ver <- c_gpiod_api_version
+  if ver == nullPtr
+    then return BS.empty
+    else BS.packCString ver
diff --git a/src/Fuyu/GPIO/Direct/Bindings.hs b/src/Fuyu/GPIO/Direct/Bindings.hs
new file mode 100644
--- /dev/null
+++ b/src/Fuyu/GPIO/Direct/Bindings.hs
@@ -0,0 +1,457 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+-- |
+-- Module      : Fuyu.GPIO.Direct.Bindings
+-- Description : Low-level FFI bindings to C libgpiod v2 symbols.
+-- Maintainer  : BassGT
+-- Portability : POSIX (Linux GPIO character device interface)
+--
+-- Raw Foreign Function Interface (FFI) bindings for @libgpiod v2@ C API functions.
+-- Pure in-memory setters, getters, allocators, and deallocators use @unsafe@ imports
+-- for zero-overhead performance, while kernel ioctl and I/O calls use default safe imports.
+module Fuyu.GPIO.Direct.Bindings where
+
+import Foreign.C.String (CString)
+import Foreign.C.Types (CInt(..), CLong(..), CSize(..), CUInt(..), CULong(..), CBool(..))
+import Foreign.Ptr (Ptr)
+import Fuyu.GPIO.Direct.Types
+import Data.Int (Int64)
+import System.Posix.Types (Fd(..))
+
+--------------------------------------------------------------------------------
+-- 1. CHIP MANAGEMENT
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_chip_open@. Opens a GPIO chip at the given filesystem path.
+foreign import ccall "gpiod_chip_open"
+  c_gpiod_chip_open :: CString -> IO (Ptr CGpiodChip)
+
+-- | Raw C call to @gpiod_chip_close@. Closes the chip and frees associated C resources.
+foreign import ccall "gpiod_chip_close"
+  c_gpiod_chip_close :: Ptr CGpiodChip -> IO ()
+
+-- | Raw C call to @gpiod_chip_get_info@. Retrieves static chip information via kernel ioctl.
+foreign import ccall "gpiod_chip_get_info"
+  c_gpiod_chip_get_info :: Ptr CGpiodChip -> IO (Ptr CGpiodChipInfo) 
+
+-- | Raw C call to @gpiod_chip_get_path@. Returns cached filesystem path string from RAM.
+foreign import ccall unsafe "gpiod_chip_get_path"
+  c_gpiod_chip_get_path :: Ptr CGpiodChip -> IO CString  
+
+-- | Raw C call to @gpiod_chip_get_line_info@. Fetches line info snapshot via kernel ioctl.
+foreign import ccall "gpiod_chip_get_line_info"
+  c_gpiod_chip_get_line_info :: Ptr CGpiodChip -> LineOffset -> IO (Ptr CGpiodLineInfo) 
+
+-- | Raw C call to @gpiod_chip_watch_line_info@. Enables line status watching via kernel ioctl.
+foreign import ccall "gpiod_chip_watch_line_info"
+  c_gpiod_chip_watch_line_info :: Ptr CGpiodChip -> LineOffset -> IO (Ptr CGpiodLineInfo) 
+
+-- | Raw C call to @gpiod_chip_unwatch_line_info@. Disables status watching via kernel ioctl.
+foreign import ccall "gpiod_chip_unwatch_line_info"
+  c_gpiod_chip_unwatch_line_info :: Ptr CGpiodChip -> LineOffset -> IO CInt
+
+-- | Raw C call to @gpiod_chip_get_fd@. Returns cached file descriptor from RAM.
+foreign import ccall unsafe "gpiod_chip_get_fd"
+  c_gpiod_chip_get_fd :: Ptr CGpiodChip -> IO Fd
+
+-- | Raw C call to @gpiod_chip_wait_info_event@. Waits for status events via poll/epoll syscall.
+foreign import ccall "gpiod_chip_wait_info_event"
+  c_gpiod_chip_wait_info_event :: Ptr CGpiodChip -> Int64 -> IO CInt 
+
+-- | Raw C call to @gpiod_chip_read_info_event@. Reads info event via read syscall.
+foreign import ccall "gpiod_chip_read_info_event"
+  c_gpiod_chip_read_info_event :: Ptr CGpiodChip -> IO (Ptr CGpiodInfoEvent)
+
+-- | Raw C call to @gpiod_chip_request_lines@. Requests lines via kernel ioctl.
+foreign import ccall "gpiod_chip_request_lines"
+  c_gpiod_chip_request_lines :: Ptr CGpiodChip 
+                             -> Ptr CGpiodRequestConfig
+                             -> Ptr CGpiodLineConfig
+                             -> IO (Ptr CGpiodLineRequest)
+
+-- | Raw C call to @gpiod_chip_get_line_offset_from_name@. Looks up line offset by pin name.
+foreign import ccall "gpiod_chip_get_line_offset_from_name"
+  c_gpiod_chip_get_line_offset_from_name :: Ptr CGpiodChip -> CString -> IO CInt
+
+--------------------------------------------------------------------------------
+-- 2. CHIP INFO
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_chip_info_free@. Frees a chip info object in C heap.
+foreign import ccall unsafe "gpiod_chip_info_free"
+  c_gpiod_chip_info_free :: Ptr CGpiodChipInfo -> IO ()
+
+-- | Raw C call to @gpiod_chip_info_get_name@. Gets chip device name from RAM.
+foreign import ccall unsafe "gpiod_chip_info_get_name"
+  c_gpiod_chip_info_get_name :: Ptr CGpiodChipInfo -> IO CString
+
+-- | Raw C call to @gpiod_chip_info_get_label@. Gets hardware controller label from RAM.
+foreign import ccall unsafe "gpiod_chip_info_get_label"
+  c_gpiod_chip_info_get_label :: Ptr CGpiodChipInfo -> IO CString  
+
+-- | Raw C call to @gpiod_chip_info_get_num_lines@. Gets total count of lines from RAM.
+foreign import ccall unsafe "gpiod_chip_info_get_num_lines"
+  c_gpiod_chip_info_get_num_lines :: Ptr CGpiodChipInfo -> IO CSize 
+
+--------------------------------------------------------------------------------
+-- 4. LINE INFORMATION
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_line_info_free@. Frees a line info object in C heap.
+foreign import ccall unsafe "gpiod_line_info_free"
+  c_gpiod_line_info_free :: Ptr CGpiodLineInfo -> IO ()
+
+-- | Raw C call to @gpiod_line_info_copy@. Duplicates line info object in RAM.
+foreign import ccall unsafe "gpiod_line_info_copy"
+  c_gpiod_line_info_copy :: Ptr CGpiodLineInfo -> IO (Ptr CGpiodLineInfo)
+
+-- | Raw C call to @gpiod_line_info_get_offset@. Returns line offset index from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_offset"
+  c_gpiod_line_info_get_offset :: Ptr CGpiodLineInfo -> IO LineOffset
+
+-- | Raw C call to @gpiod_line_info_get_name@. Returns pin name string from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_name"
+  c_gpiod_line_info_get_name :: Ptr CGpiodLineInfo -> IO CString
+
+-- | Raw C call to @gpiod_line_info_is_used@. Checks if line is used from RAM.
+foreign import ccall unsafe "gpiod_line_info_is_used"
+  c_gpiod_line_info_is_used :: Ptr CGpiodLineInfo -> IO CBool
+
+-- | Raw C call to @gpiod_line_info_get_consumer@. Gets consumer label from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_consumer"
+  c_gpiod_line_info_get_consumer :: Ptr CGpiodLineInfo -> IO CString
+
+-- | Raw C call to @gpiod_line_info_get_direction@. Gets direction setting from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_direction"
+  c_gpiod_line_info_get_direction :: Ptr CGpiodLineInfo -> IO LineDirection
+
+-- | Raw C call to @gpiod_line_info_get_edge_detection@. Gets edge mode from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_edge_detection"
+  c_gpiod_line_info_get_edge_detection :: Ptr CGpiodLineInfo -> IO LineEdge
+
+-- | Raw C call to @gpiod_line_info_get_bias@. Gets internal pull bias setting from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_bias"
+  c_gpiod_line_info_get_bias :: Ptr CGpiodLineInfo -> IO LineBias
+
+-- | Raw C call to @gpiod_line_info_get_drive@. Gets output drive configuration from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_drive"
+  c_gpiod_line_info_get_drive :: Ptr CGpiodLineInfo -> IO LineDrive
+
+-- | Raw C call to @gpiod_line_info_is_active_low@. Checks active-low flag from RAM.
+foreign import ccall unsafe "gpiod_line_info_is_active_low"
+  c_gpiod_line_info_is_active_low :: Ptr CGpiodLineInfo -> IO CBool  
+
+-- | Raw C call to @gpiod_line_info_is_debounced@. Checks debounce filter flag from RAM.
+foreign import ccall unsafe "gpiod_line_info_is_debounced"
+  c_gpiod_line_info_is_debounced :: Ptr CGpiodLineInfo -> IO CBool  
+
+-- | Raw C call to @gpiod_line_info_get_debounce_period_us@. Gets debounce period from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_debounce_period_us"
+  c_gpiod_line_info_get_debounce_period_us :: Ptr CGpiodLineInfo -> IO CULong
+
+-- | Raw C call to @gpiod_line_info_get_event_clock@. Gets event timestamp clock source from RAM.
+foreign import ccall unsafe "gpiod_line_info_get_event_clock"
+  c_gpiod_line_info_get_event_clock :: Ptr CGpiodLineInfo -> IO LineClock
+
+--------------------------------------------------------------------------------
+-- 5. LINE WATCH (INFO EVENT)
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_info_event_free@. Frees an info event structure in C heap.
+foreign import ccall unsafe "gpiod_info_event_free"
+  c_gpiod_info_event_free :: Ptr CGpiodInfoEvent -> IO ()
+
+-- | Raw C call to @gpiod_info_event_get_event_type@. Gets event type from RAM.
+foreign import ccall unsafe "gpiod_info_event_get_event_type"
+  c_gpiod_info_event_get_event_type :: Ptr CGpiodInfoEvent -> IO InfoEventType
+
+-- | Raw C call to @gpiod_info_event_get_timestamp_ns@. Gets event timestamp from RAM.
+foreign import ccall unsafe "gpiod_info_event_get_timestamp_ns"
+  c_gpiod_info_event_get_timestamp_ns :: Ptr CGpiodInfoEvent -> IO TimestampNs
+
+-- | Raw C call to @gpiod_info_event_get_line_info@. Reads line info pointer from RAM.
+foreign import ccall unsafe "gpiod_info_event_get_line_info"
+  c_gpiod_info_event_get_line_info :: Ptr CGpiodInfoEvent -> IO (Ptr CGpiodLineInfo)
+
+--------------------------------------------------------------------------------
+-- 6. LINE SETTINGS
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_line_settings_new@. Allocates line settings object in RAM.
+foreign import ccall unsafe "gpiod_line_settings_new"
+  c_gpiod_line_settings_new :: IO (Ptr CGpiodLineSettings)
+
+-- | Raw C call to @gpiod_line_settings_free@. Frees line settings object in RAM.
+foreign import ccall unsafe "gpiod_line_settings_free"
+  c_gpiod_line_settings_free :: Ptr CGpiodLineSettings -> IO ()
+
+-- | Raw C call to @gpiod_line_settings_reset@. Resets settings object in RAM.
+foreign import ccall unsafe "gpiod_line_settings_reset"
+  c_gpiod_line_settings_reset :: Ptr CGpiodLineSettings -> IO ()
+
+-- | Raw C call to @gpiod_line_settings_copy@. Duplicates line settings object in RAM.
+foreign import ccall unsafe "gpiod_line_settings_copy"
+  c_gpiod_line_settings_copy :: Ptr CGpiodLineSettings -> IO (Ptr CGpiodLineSettings)
+
+-- | Raw C call to @gpiod_line_settings_set_direction@. Sets line direction setting in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_direction"
+  c_gpiod_line_settings_set_direction :: Ptr CGpiodLineSettings -> LineDirection -> IO CInt      
+
+-- | Raw C call to @gpiod_line_settings_get_direction@. Gets line direction setting from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_direction"
+  c_gpiod_line_settings_get_direction :: Ptr CGpiodLineSettings -> IO LineDirection
+
+-- | Raw C call to @gpiod_line_settings_set_edge_detection@. Sets edge detection setting in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_edge_detection"
+  c_gpiod_line_settings_set_edge_detection :: Ptr CGpiodLineSettings -> LineEdge -> IO CInt 
+
+-- | Raw C call to @gpiod_line_settings_get_edge_detection@. Gets edge detection setting from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_edge_detection"
+  c_gpiod_line_settings_get_edge_detection :: Ptr CGpiodLineSettings -> IO LineEdge 
+
+-- | Raw C call to @gpiod_line_settings_set_bias@. Sets internal pull bias setting in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_bias"
+  c_gpiod_line_settings_set_bias :: Ptr CGpiodLineSettings -> LineBias -> IO CInt
+
+-- | Raw C call to @gpiod_line_settings_get_bias@. Gets internal pull bias setting from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_bias"
+  c_gpiod_line_settings_get_bias :: Ptr CGpiodLineSettings -> IO LineBias
+
+-- | Raw C call to @gpiod_line_settings_set_drive@. Sets driver output mode in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_drive"
+  c_gpiod_line_settings_set_drive :: Ptr CGpiodLineSettings -> LineDrive -> IO CInt
+
+-- | Raw C call to @gpiod_line_settings_get_drive@. Gets driver output mode from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_drive"
+  c_gpiod_line_settings_get_drive :: Ptr CGpiodLineSettings -> IO LineDrive
+
+-- | Raw C call to @gpiod_line_settings_set_active_low@. Sets active-low property flag in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_active_low"
+  c_gpiod_line_settings_set_active_low :: Ptr CGpiodLineSettings -> CBool -> IO ()  
+
+-- | Raw C call to @gpiod_line_settings_get_active_low@. Gets active-low property flag from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_active_low"
+  c_gpiod_line_settings_get_active_low :: Ptr CGpiodLineSettings -> IO CBool
+
+-- | Raw C call to @gpiod_line_settings_set_debounce_period_us@. Sets input debounce period in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_debounce_period_us"
+  c_gpiod_line_settings_set_debounce_period_us :: Ptr CGpiodLineSettings -> CLong -> IO ()  
+
+-- | Raw C call to @gpiod_line_settings_get_debounce_period_us@. Gets input debounce period from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_debounce_period_us"
+  c_gpiod_line_settings_get_debounce_period_us :: Ptr CGpiodLineSettings -> IO CLong
+
+-- | Raw C call to @gpiod_line_settings_set_event_clock@. Sets event clock source in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_event_clock"
+  c_gpiod_line_settings_set_event_clock :: Ptr CGpiodLineSettings -> LineClock -> IO CInt 
+
+-- | Raw C call to @gpiod_line_settings_get_event_clock@. Gets event clock source from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_event_clock"
+  c_gpiod_line_settings_get_event_clock :: Ptr CGpiodLineSettings -> IO LineClock
+
+-- | Raw C call to @gpiod_line_settings_set_output_value@. Sets output value setting in RAM.
+foreign import ccall unsafe "gpiod_line_settings_set_output_value"
+  c_gpiod_line_settings_set_output_value :: Ptr CGpiodLineSettings -> LineValue -> IO CInt 
+
+-- | Raw C call to @gpiod_line_settings_get_output_value@. Gets output value setting from RAM.
+foreign import ccall unsafe "gpiod_line_settings_get_output_value"
+  c_gpiod_line_settings_get_output_value :: Ptr CGpiodLineSettings -> IO LineValue
+
+--------------------------------------------------------------------------------
+-- 7. LINE CONFIGURATION
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_line_config_new@. Allocates line config accumulator in RAM.
+foreign import ccall unsafe "gpiod_line_config_new"
+  c_gpiod_line_config_new :: IO (Ptr CGpiodLineConfig)
+
+-- | Raw C call to @gpiod_line_config_free@. Frees line config object in RAM.
+foreign import ccall unsafe "gpiod_line_config_free"
+  c_gpiod_line_config_free :: Ptr CGpiodLineConfig -> IO ()
+
+-- | Raw C call to @gpiod_line_config_reset@. Clears line settings mapping in RAM.
+foreign import ccall unsafe "gpiod_line_config_reset"
+  c_gpiod_line_config_reset :: Ptr CGpiodLineConfig -> IO ()
+
+-- | Raw C call to @gpiod_line_config_add_line_settings@. Maps array of offsets to settings in RAM.
+foreign import ccall unsafe "gpiod_line_config_add_line_settings"
+  c_gpiod_line_config_add_line_settings :: Ptr CGpiodLineConfig
+                                        -> Ptr LineOffset
+                                        -> CSize
+                                        -> Ptr CGpiodLineSettings 
+                                        -> IO CInt
+
+-- | Raw C call to @gpiod_line_config_get_line_settings@. Gets line settings associated with offset in RAM.
+foreign import ccall unsafe "gpiod_line_config_get_line_settings"
+  c_gpiod_line_config_get_line_settings :: Ptr CGpiodLineConfig -> LineOffset -> IO (Ptr CGpiodLineSettings)
+
+-- | Raw C call to @gpiod_line_config_set_output_values@. Overrides output values in RAM.
+foreign import ccall unsafe "gpiod_line_config_set_output_values"
+  c_gpiod_line_config_set_output_values :: Ptr CGpiodLineConfig -> Ptr LineValue -> CSize -> IO CInt
+
+-- | Raw C call to @gpiod_line_config_get_num_configured_offsets@. Gets count of offsets from RAM.
+foreign import ccall unsafe "gpiod_line_config_get_num_configured_offsets"
+  c_gpiod_line_config_get_num_configured_offsets :: Ptr CGpiodLineConfig -> IO CSize
+
+-- | Raw C call to @gpiod_line_config_get_configured_offsets@. Copies configured offsets array in RAM.
+foreign import ccall unsafe "gpiod_line_config_get_configured_offsets"
+  c_gpiod_line_config_get_configured_offsets :: Ptr CGpiodLineConfig -> Ptr LineOffset -> CSize -> IO CSize
+
+--------------------------------------------------------------------------------
+-- 8. REQUESTS CONFIG
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_request_config_new@. Allocates request configuration in RAM.
+foreign import ccall unsafe "gpiod_request_config_new"
+  c_gpiod_request_config_new :: IO (Ptr CGpiodRequestConfig)
+
+-- | Raw C call to @gpiod_request_config_free@. Frees request config object in RAM.
+foreign import ccall unsafe "gpiod_request_config_free"
+  c_gpiod_request_config_free :: Ptr CGpiodRequestConfig -> IO()
+
+-- | Raw C call to @gpiod_request_config_set_consumer@. Sets consumer string in RAM.
+foreign import ccall unsafe "gpiod_request_config_set_consumer"
+  c_gpiod_request_config_set_consumer :: Ptr CGpiodRequestConfig -> CString -> IO()
+
+-- | Raw C call to @gpiod_request_config_get_consumer@. Reads consumer string from RAM.
+foreign import ccall unsafe "gpiod_request_config_get_consumer"
+  c_gpiod_request_config_get_consumer :: Ptr CGpiodRequestConfig -> IO CString
+
+-- | Raw C call to @gpiod_request_config_set_event_buffer_size@. Sets kernel event buffer size in RAM.
+foreign import ccall unsafe "gpiod_request_config_set_event_buffer_size"
+  c_gpiod_request_config_set_event_buffer_size :: Ptr CGpiodRequestConfig -> CSize -> IO()   
+
+-- | Raw C call to @gpiod_request_config_get_event_buffer_size@. Gets kernel event buffer size from RAM.
+foreign import ccall unsafe "gpiod_request_config_get_event_buffer_size"
+  c_gpiod_request_config_get_event_buffer_size :: Ptr CGpiodRequestConfig -> IO CSize  
+
+--------------------------------------------------------------------------------
+-- 9. LINE REQUEST
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_line_request_release@. Releases lines and closes line request file descriptor.
+foreign import ccall "gpiod_line_request_release"
+  c_gpiod_line_request_release :: Ptr CGpiodLineRequest -> IO()
+
+-- | Raw C call to @gpiod_line_request_get_chip_name@. Gets chip device name from RAM.
+foreign import ccall unsafe "gpiod_line_request_get_chip_name"
+  c_gpiod_line_request_get_chip_name :: Ptr CGpiodLineRequest -> IO CString
+
+-- | Raw C call to @gpiod_line_request_get_num_requested_lines@. Gets line count from RAM.
+foreign import ccall unsafe "gpiod_line_request_get_num_requested_lines"
+  c_gpiod_line_request_get_num_requested_lines :: Ptr CGpiodLineRequest -> IO CSize
+
+-- | Raw C call to @gpiod_line_request_get_requested_offsets@. Copies requested line offsets from RAM.
+foreign import ccall unsafe "gpiod_line_request_get_requested_offsets"
+  c_gpiod_line_request_get_requested_offsets :: Ptr CGpiodLineRequest -> Ptr LineOffset -> CSize -> IO CSize
+
+-- | Raw C call to @gpiod_line_request_get_value@. Reads logical value via kernel ioctl.
+foreign import ccall "gpiod_line_request_get_value"
+  c_gpiod_line_request_get_value :: Ptr CGpiodLineRequest -> LineOffset -> IO LineValue 
+
+-- | Raw C call to @gpiod_line_request_get_values_subset@. Reads logical values subset via kernel ioctl.
+foreign import ccall "gpiod_line_request_get_values_subset"
+  c_gpiod_line_request_get_values_subset :: Ptr CGpiodLineRequest
+                                         -> CSize
+                                         -> Ptr LineOffset
+                                         -> Ptr LineValue
+                                         -> IO CInt
+
+-- | Raw C call to @gpiod_line_request_get_values@. Reads logical values via kernel ioctl.
+foreign import ccall "gpiod_line_request_get_values"
+  c_gpiod_line_request_get_values :: Ptr CGpiodLineRequest -> Ptr LineValue -> IO CInt 
+
+-- | Raw C call to @gpiod_line_request_set_value@. Sets logical value via kernel ioctl.
+foreign import ccall "gpiod_line_request_set_value"
+  c_gpiod_line_request_set_value :: Ptr CGpiodLineRequest -> LineOffset -> LineValue -> IO CInt 
+
+-- | Raw C call to @gpiod_line_request_set_values_subset@. Sets logical values subset via kernel ioctl.
+foreign import ccall "gpiod_line_request_set_values_subset"
+  c_gpiod_line_request_set_values_subset :: Ptr CGpiodLineRequest
+                                          -> CSize
+                                          -> Ptr LineOffset
+                                          -> Ptr LineValue
+                                          -> IO CInt
+
+-- | Raw C call to @gpiod_line_request_set_values@. Sets logical values via kernel ioctl.
+foreign import ccall "gpiod_line_request_set_values"
+  c_gpiod_line_request_set_values :: Ptr CGpiodLineRequest -> Ptr LineValue -> IO CInt 
+
+-- | Raw C call to @gpiod_line_request_reconfigure_lines@. Applies new configuration via kernel ioctl.
+foreign import ccall "gpiod_line_request_reconfigure_lines"
+  c_gpiod_line_request_reconfigure_lines :: Ptr CGpiodLineRequest -> Ptr CGpiodLineConfig -> IO CInt  
+
+-- | Raw C call to @gpiod_line_request_get_fd@. Returns cached file descriptor from RAM.
+foreign import ccall unsafe "gpiod_line_request_get_fd"
+  c_gpiod_line_request_get_fd :: Ptr CGpiodLineRequest -> IO Fd 
+
+-- | Raw C call to @gpiod_line_request_wait_edge_events@. Waits for edge events via poll/epoll syscall.
+foreign import ccall "gpiod_line_request_wait_edge_events"
+  c_gpiod_line_request_wait_edge_events :: Ptr CGpiodLineRequest -> CLong -> IO CInt
+
+-- | Raw C call to @gpiod_line_request_read_edge_events@. Reads edge events via read syscall.
+foreign import ccall "gpiod_line_request_read_edge_events" 
+  c_gpiod_line_request_read_edge_events :: Ptr CGpiodLineRequest -> Ptr CGpiodEdgeEventBuffer -> CSize -> IO CInt
+
+--------------------------------------------------------------------------------
+-- 10. EDGE EVENTS & EVENT BUFFER
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_edge_event_free@. Frees an edge event structure in RAM.
+foreign import ccall unsafe "gpiod_edge_event_free"
+  c_gpiod_edge_event_free :: Ptr CGpiodEdgeEvent -> IO ()
+
+-- | Raw C call to @gpiod_edge_event_copy@. Duplicates an edge event in RAM.
+foreign import ccall unsafe "gpiod_edge_event_copy"
+  c_gpiod_edge_event_copy :: Ptr CGpiodEdgeEvent -> IO (Ptr CGpiodEdgeEvent)
+
+-- | Raw C call to @gpiod_edge_event_get_event_type@. Gets edge type from RAM.
+foreign import ccall unsafe "gpiod_edge_event_get_event_type"
+  c_gpiod_edge_event_get_event_type :: Ptr CGpiodEdgeEvent -> IO EdgeEventType
+
+-- | Raw C call to @gpiod_edge_event_get_timestamp_ns@. Gets edge event timestamp from RAM.
+foreign import ccall unsafe "gpiod_edge_event_get_timestamp_ns" 
+  c_gpiod_edge_event_get_timestamp_ns :: Ptr CGpiodEdgeEvent -> IO TimestampNs 
+
+-- | Raw C call to @gpiod_edge_event_get_line_offset@. Gets triggering line offset from RAM.
+foreign import ccall unsafe "gpiod_edge_event_get_line_offset"
+  c_gpiod_edge_event_get_line_offset :: Ptr CGpiodEdgeEvent -> IO LineOffset
+
+-- | Raw C call to @gpiod_edge_event_get_global_seqno@. Gets global sequence number from RAM.
+foreign import ccall unsafe "gpiod_edge_event_get_global_seqno"
+  c_gpiod_edge_event_get_global_seqno :: Ptr CGpiodEdgeEvent -> IO CULong 
+
+-- | Raw C call to @gpiod_edge_event_get_line_seqno@. Gets line sequence number from RAM.
+foreign import ccall unsafe "gpiod_edge_event_get_line_seqno"
+  c_gpiod_edge_event_get_line_seqno :: Ptr CGpiodEdgeEvent -> IO LineOffset 
+
+-- | Raw C call to @gpiod_edge_event_buffer_new@. Allocates edge event buffer in RAM.
+foreign import ccall unsafe "gpiod_edge_event_buffer_new"
+  c_gpiod_edge_event_buffer_new :: CSize -> IO (Ptr CGpiodEdgeEventBuffer)
+
+-- | Raw C call to @gpiod_edge_event_buffer_get_capacity@. Gets buffer capacity from RAM.
+foreign import ccall unsafe "gpiod_edge_event_buffer_get_capacity"
+  c_gpiod_edge_event_buffer_get_capacity :: Ptr CGpiodEdgeEventBuffer -> IO CSize
+
+-- | Raw C call to @gpiod_edge_event_buffer_free@. Frees edge event buffer in RAM.
+foreign import ccall unsafe "gpiod_edge_event_buffer_free"
+  c_gpiod_edge_event_buffer_free :: Ptr CGpiodEdgeEventBuffer -> IO ()
+
+-- | Raw C call to @gpiod_edge_event_buffer_get_event@. Fetches event pointer by index from RAM array.
+foreign import ccall unsafe "gpiod_edge_event_buffer_get_event"
+  c_gpiod_edge_event_buffer_get_event :: Ptr CGpiodEdgeEventBuffer -> CULong -> IO (Ptr CGpiodEdgeEvent)
+
+-- | Raw C call to @gpiod_edge_event_buffer_get_num_events@. Gets count of events from RAM.
+foreign import ccall unsafe "gpiod_edge_event_buffer_get_num_events"
+  c_gpiod_edge_event_buffer_get_num_events :: Ptr CGpiodEdgeEventBuffer -> IO CSize 
+
+--------------------------------------------------------------------------------
+-- 11. MISCELLANEOUS UTILITIES
+--------------------------------------------------------------------------------
+
+-- | Raw C call to @gpiod_is_gpiochip_device@. Performs stat/open filesystem syscall on dev path.
+foreign import ccall "gpiod_is_gpiochip_device"
+  c_gpiod_is_gpiochip_device :: CString -> IO CBool
+
+-- | Raw C call to @gpiod_api_version@. Returns static version string from RAM.
+foreign import ccall unsafe "gpiod_api_version"
+  c_gpiod_api_version :: IO CString
diff --git a/src/Fuyu/GPIO/Direct/Types.hs b/src/Fuyu/GPIO/Direct/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Fuyu/GPIO/Direct/Types.hs
@@ -0,0 +1,363 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE PatternSynonyms #-}
+-- |
+-- Module      : Fuyu.GPIO.Direct.Types
+-- Description : Core data types, newtype wrappers, and C enum pattern synonyms for libgpiod v2 bindings.
+-- Maintainer  : BassGT
+-- Portability : POSIX (Linux GPIO character device interface)
+--
+-- This module defines all data types, opaque handles, safety wrappers, pattern
+-- synonyms for C enums, and native helper structures used throughout the
+-- @fuyu-gpio-direct@ library.
+--
+-- Resource handles wrap raw foreign pointers to C structures managed by @libgpiod v2@.
+-- Callers are responsible for explicitly freeing handles created via allocations or getters
+-- using their corresponding cleanup operations (e.g., 'Fuyu.GPIO.Direct.chipClose',
+-- 'Fuyu.GPIO.Direct.lineSettingsFree', etc.).
+module Fuyu.GPIO.Direct.Types where
+
+import Foreign.Ptr (Ptr)
+import Foreign.Storable (Storable)
+import Data.Word (Word64)
+import Foreign.C.Types (CInt(..), CUInt(..), CULong(..))
+
+--------------------------------------------------------------------------------
+-- CORE TYPES & WRAPPERS
+--------------------------------------------------------------------------------
+
+-- | Opaque ghost type representing the C structure @struct gpiod_chip@.
+data CGpiodChip 
+
+-- | Opaque ghost type representing the C structure @struct gpiod_chip_info@.
+data CGpiodChipInfo
+
+-- | Opaque ghost type representing the C structure @struct gpiod_info_event@.
+data CGpiodInfoEvent
+
+-- | Opaque ghost type representing the C structure @struct gpiod_line_info@.
+data CGpiodLineInfo 
+
+-- | Opaque ghost type representing the C structure @struct gpiod_line_settings@.
+data CGpiodLineSettings  
+
+-- | Opaque ghost type representing the C structure @struct gpiod_line_config@.
+data CGpiodLineConfig  
+
+-- | Opaque ghost type representing the C structure @struct gpiod_line_request@.
+data CGpiodLineRequest    
+
+-- | Opaque ghost type representing the C structure @struct gpiod_request_config@.
+data CGpiodRequestConfig
+
+-- | Opaque ghost type representing the C structure @struct gpiod_edge_event_buffer@.
+data CGpiodEdgeEventBuffer
+
+-- | Opaque ghost type representing the C structure @struct gpiod_edge_event@.
+data CGpiodEdgeEvent
+
+-- | Opaque handle representing an open GPIO chip device controller.
+--
+-- Must be explicitly closed with 'Fuyu.GPIO.Direct.chipClose' when no longer needed.
+newtype Chip = Chip (Ptr CGpiodChip)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque snapshot of static GPIO chip information (name, label, line count).
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.chipInfoFree'.
+newtype ChipInfo = ChipInfo (Ptr CGpiodChipInfo)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque event object emitted when a line watched on a chip changes status.
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.infoEventFree'.
+newtype InfoEvent = InfoEvent (Ptr CGpiodInfoEvent)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque snapshot of a single GPIO line\'s status and configuration attributes.
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.lineInfoFree'.
+newtype LineInfo = LineInfo (Ptr CGpiodLineInfo)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque accumulator for GPIO line configuration settings (direction, drive, bias, etc.).
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.lineSettingsFree'.
+newtype LineSettings = LineSettings (Ptr CGpiodLineSettings)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque map associating line offsets with their respective t'LineSettings.
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.lineConfigFree'.
+newtype LineConfig = LineConfig (Ptr CGpiodLineConfig)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque handle representing requested (claimed) GPIO lines under active kernel control.
+--
+-- Must be explicitly released with 'Fuyu.GPIO.Direct.lineRequestRelease'.
+newtype LineRequest = LineRequest (Ptr CGpiodLineRequest)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque request configuration object (consumer name, kernel event buffer size).
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.requestConfigFree'.
+newtype RequestConfig = RequestConfig (Ptr CGpiodRequestConfig)
+  deriving (Eq, Ord, Show)
+
+-- | Opaque fixed-capacity buffer storing edge detection events read from the kernel.
+--
+-- Must be explicitly freed with 'Fuyu.GPIO.Direct.eventBufferFree'.
+newtype EventBuffer = EventBuffer (Ptr CGpiodEdgeEventBuffer) 
+  deriving (Eq, Ord, Show)
+
+-- | Opaque reference to an individual edge detection event stored within an t'EventBuffer.
+--
+-- Can be copied using 'Fuyu.GPIO.Direct.rawEdgeEventCopy', which returned copy must be freed
+-- via 'Fuyu.GPIO.Direct.rawEdgeEventFree'.
+newtype RawEdgeEvent = RawEdgeEvent (Ptr CGpiodEdgeEvent)
+  deriving (Eq, Ord, Show)
+
+-- | Type-safe wrapper for zero-based GPIO line offset indices on a chip.
+newtype LineOffset = LineOffset CUInt
+  deriving (Eq, Ord, Show, Read, Storable)
+
+
+
+--------------------------------------------------------------------------------
+-- LINE DEFINITIONS TYPES
+--------------------------------------------------------------------------------
+
+-- | Logical line state representation.
+--
+-- Note that logical values account for active-low inversion: an active-low line set to
+-- 'LineActive' corresponds to a physical logic low voltage level on hardware.
+newtype LineValue = LineValue CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show LineValue where
+  show LineActive   = "Active"
+  show LineInactive = "Inactive"
+  show LineError    = "Error"
+  show (LineValue n) = "LineValue " ++ show n
+
+-- | Logical active state (1).
+pattern LineActive :: LineValue
+pattern LineActive = LineValue 1
+
+-- | Logical inactive state (0).
+pattern LineInactive :: LineValue
+pattern LineInactive = LineValue 0
+
+-- | Error sentinel returned by low-level functions on failure (-1).
+pattern LineError :: LineValue
+pattern LineError = LineValue (-1)
+
+{-# COMPLETE LineActive, LineInactive, LineError, LineValue #-}
+
+-- | GPIO line pin direction configuration.
+newtype LineDirection = LineDirection CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show LineDirection where
+  show DirAsIs   = "DirAsIs"
+  show DirInput  = "DirInput"
+  show DirOutput = "DirOutput"
+  show (LineDirection n) = "LineDirection " ++ show n
+
+-- | Leave line direction unchanged during configuration re-application.
+pattern DirAsIs :: LineDirection
+pattern DirAsIs = LineDirection 1
+
+-- | Configure line as input.
+pattern DirInput :: LineDirection
+pattern DirInput = LineDirection 2
+
+-- | Configure line as output.
+pattern DirOutput :: LineDirection
+pattern DirOutput = LineDirection 3
+
+{-# COMPLETE DirAsIs, DirInput, DirOutput, LineDirection #-}
+
+-- | Edge detection mode for monitoring input signal transitions.
+newtype LineEdge = LineEdge CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show LineEdge where
+  show EdgeNone    = "EdgeNone"
+  show EdgeRising  = "EdgeRising"
+  show EdgeFalling = "EdgeFalling"
+  show EdgeBoth    = "EdgeBoth"
+  show (LineEdge n) = "LineEdge " ++ show n
+
+-- | Disable edge detection events on this line.
+pattern EdgeNone :: LineEdge
+pattern EdgeNone = LineEdge 1
+
+-- | Trigger edge events on low-to-high (rising) signal transitions.
+pattern EdgeRising :: LineEdge
+pattern EdgeRising = LineEdge 2
+
+-- | Trigger edge events on high-to-low (falling) signal transitions.
+pattern EdgeFalling :: LineEdge
+pattern EdgeFalling = LineEdge 3
+
+-- | Trigger edge events on both rising and falling signal transitions.
+pattern EdgeBoth :: LineEdge
+pattern EdgeBoth = LineEdge 4
+
+{-# COMPLETE EdgeNone, EdgeRising, EdgeFalling, EdgeBoth, LineEdge #-}
+
+-- | Internal pull resistor bias configuration for a line.
+newtype LineBias = LineBias CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show LineBias where
+  show BiasAsIs     = "BiasAsIs"
+  show BiasUnknown  = "BiasUnknown"
+  show BiasDisabled = "BiasDisabled"
+  show BiasPullUp   = "BiasPullUp"
+  show BiasPullDown = "BiasPullDown"
+  show (LineBias n) = "LineBias " ++ show n
+
+-- | Keep current line bias settings unchanged.
+pattern BiasAsIs :: LineBias
+pattern BiasAsIs = LineBias 1
+
+-- | Line bias is unknown or non-standard.
+pattern BiasUnknown :: LineBias
+pattern BiasUnknown = LineBias 2
+
+-- | Disable internal pull-up and pull-down resistors (floating input / tri-stated).
+pattern BiasDisabled :: LineBias
+pattern BiasDisabled = LineBias 3
+
+-- | Enable internal pull-up resistor.
+pattern BiasPullUp :: LineBias
+pattern BiasPullUp = LineBias 4
+
+-- | Enable internal pull-down resistor.
+pattern BiasPullDown :: LineBias
+pattern BiasPullDown = LineBias 5
+
+{-# COMPLETE BiasAsIs, BiasUnknown, BiasDisabled, BiasPullUp, BiasPullDown, LineBias #-}
+
+-- | Output pin driver mode configuration.
+newtype LineDrive = LineDrive CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show LineDrive where
+  show PushPull   = "PushPull"
+  show OpenDrain  = "OpenDrain"
+  show OpenSource = "OpenSource"
+  show (LineDrive n) = "LineDrive " ++ show n
+
+-- | Standard push-pull driver actively driving high and low output logic levels.
+pattern PushPull :: LineDrive
+pattern PushPull = LineDrive 1
+
+-- | Open-drain driver mode (sinks current when active, open/high-impedance when inactive).
+pattern OpenDrain :: LineDrive
+pattern OpenDrain = LineDrive 2
+
+-- | Open-source driver mode (sources current when active, open/high-impedance when inactive).
+pattern OpenSource :: LineDrive
+pattern OpenSource = LineDrive 3
+
+{-# COMPLETE PushPull, OpenDrain, OpenSource, LineDrive #-}
+
+-- | Timestamp clock source used when recording line edge events in the kernel.
+newtype LineClock = LineClock CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show LineClock where
+  show Monotonic = "Monotonic"
+  show Realtime  = "Realtime"
+  show Hardware  = "Hardware"
+  show (LineClock n) = "LineClock " ++ show n
+
+-- | Kernel @CLOCK_MONOTONIC@ clock source (default).
+pattern Monotonic :: LineClock
+pattern Monotonic = LineClock 1
+
+-- | Kernel @CLOCK_REALTIME@ wall-clock source.
+pattern Realtime :: LineClock
+pattern Realtime = LineClock 2
+
+-- | Hardware SoC timestamp clock source (if supported by hardware/kernel).
+pattern Hardware :: LineClock
+pattern Hardware = LineClock 3
+
+{-# COMPLETE Monotonic, Realtime, Hardware, LineClock #-}
+
+--------------------------------------------------------------------------------
+-- OTHER LIBGPIOD DEFINITIONS TYPES
+--------------------------------------------------------------------------------
+
+-- | Type of status change event emitted when watching line status on a GPIO chip.
+newtype InfoEventType = InfoEventType CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show InfoEventType where
+  show LineRequested     = "LineRequested"
+  show LineReleased      = "LineReleased"
+  show LineConfigChanged = "LineConfigChanged"
+  show (InfoEventType n) = "InfoEventType " ++ show n
+
+-- | Line was claimed / requested by a process consumer.
+pattern LineRequested :: InfoEventType
+pattern LineRequested = InfoEventType 1
+
+-- | Line was released by its consumer process.
+pattern LineReleased :: InfoEventType
+pattern LineReleased = InfoEventType 2
+
+-- | Line configuration attributes were modified.
+pattern LineConfigChanged :: InfoEventType
+pattern LineConfigChanged = InfoEventType 3
+
+{-# COMPLETE LineRequested, LineReleased, LineConfigChanged, InfoEventType #-}
+
+-- | Direction of transition detected on an input edge event.
+newtype EdgeEventType = EdgeEventType CInt
+  deriving (Eq, Ord, Read, Storable)
+
+instance Show EdgeEventType where
+  show Rising  = "Rising"
+  show Falling = "Falling"
+  show (EdgeEventType n) = "EdgeEventType " ++ show n
+
+-- | Low-to-high transition detected.
+pattern Rising :: EdgeEventType
+pattern Rising = EdgeEventType 1
+
+-- | High-to-low transition detected.
+pattern Falling :: EdgeEventType
+pattern Falling = EdgeEventType 2
+
+{-# COMPLETE Rising, Falling, EdgeEventType #-}
+
+--------------------------------------------------------------------------------
+-- NATIVE HELPER TYPES 
+--------------------------------------------------------------------------------
+
+-- | Result status of waiting on GPIO chip or line request events.
+data WaitResult 
+  = Timeout    -- ^ The specified timeout expired before any event arrived.
+  | EventReady -- ^ One or more events are ready to be read.
+  deriving (Eq, Ord, Show, Read)
+
+-- | Timeout duration configuration for event waiting calls.
+data TimeoutNs
+  = Nanoseconds CULong -- ^ Wait for up to the specified duration in nanoseconds.
+  | Immediate          -- ^ Non-blocking poll; return status immediately.
+  | Infinite           -- ^ Block indefinitely until an event arrives (-1 in C API).
+  deriving (Eq, Ord, Show, Read)
+
+-- | Absolute timestamp represented in nanoseconds.
+type TimestampNs = Word64
+
+-- | Pure Haskell structure containing parsed edge event information.
+data EdgeEvent = EdgeEvent 
+  { eventLineOffset :: LineOffset   -- ^ Offset index of the line that generated the edge event.
+  , edgeType        :: EdgeEventType -- ^ Type of edge transition ('Rising' or 'Falling').
+  , timestamp       :: TimestampNs   -- ^ Event timestamp in nanoseconds.
+  } deriving (Eq, Ord, Show, Read)
