Full Code of sysprog21/lkmpg for AI

master e19994dc271b cached
66 files
315.5 KB
82.3k tokens
253 symbols
1 requests
Download .txt
Showing preview only (333K chars total). Download the full file or copy to clipboard to get everything.
Repository: sysprog21/lkmpg
Branch: master
Commit: e19994dc271b
Files: 66
Total size: 315.5 KB

Directory structure:
gitextract_crc5k9ni/

├── .ci/
│   ├── build-n-run.sh
│   ├── check-format.sh
│   ├── check-newline.sh
│   ├── non-working
│   └── static-analysis.sh
├── .github/
│   └── workflows/
│       ├── build-deploy-assets.yaml
│       ├── deploy-github-page.yaml
│       └── status-check.yaml
├── .gitignore
├── .mailmap
├── GPL-2
├── LICENSE
├── Makefile
├── README.md
├── contrib.tex
├── examples/
│   ├── .clang-format
│   ├── Makefile
│   ├── bh_threaded.c
│   ├── bottomhalf.c
│   ├── chardev.c
│   ├── chardev.h
│   ├── chardev2.c
│   ├── completions.c
│   ├── devicemodel.c
│   ├── devicetree.c
│   ├── dht11.c
│   ├── dt-overlay.dts
│   ├── example_atomic.c
│   ├── example_mutex.c
│   ├── example_rwlock.c
│   ├── example_spinlock.c
│   ├── example_tasklet.c
│   ├── hello-1.c
│   ├── hello-2.c
│   ├── hello-3.c
│   ├── hello-4.c
│   ├── hello-5.c
│   ├── hello-sysfs.c
│   ├── intrpt.c
│   ├── ioctl.c
│   ├── kbleds.c
│   ├── led.c
│   ├── other/
│   │   ├── cat_nonblock.c
│   │   └── userspace_ioctl.c
│   ├── print_string.c
│   ├── procfs1.c
│   ├── procfs2.c
│   ├── procfs3.c
│   ├── procfs4.c
│   ├── sched.c
│   ├── sleep.c
│   ├── start.c
│   ├── static_key.c
│   ├── stop.c
│   ├── syscall-steal.c
│   ├── vinput.c
│   ├── vinput.h
│   └── vkbd.c
├── html.cfg
├── lib/
│   ├── codeblock.tex
│   └── kernelsrc.tex
├── lkmpg.tex
└── scripts/
    ├── Contributors
    ├── Exclude
    ├── Include
    └── list-contributors.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .ci/build-n-run.sh
================================================
#!/usr/bin/env bash

function build_example()
{   
    make -C examples || exit 1
}

function list_mod()
{
    # Filter out the modules specified in non-working
    ls examples/*.ko | awk -F "[/|.]" '{print $2}' | grep -vFxf .ci/non-working
}

function run_mod()
{
    # insert/remove twice to ensure resource allocations
    ( sudo insmod "examples/$1.ko" && sudo rmmod "$1" ) || exit 1
    ( sudo insmod "examples/$1.ko" && sudo rmmod "$1" ) || exit 1
}

function run_examples()
{
    for module in $(list_mod); do
        echo "Running $module"
        run_mod "$module"
    done
}

build_example
run_examples


================================================
FILE: .ci/check-format.sh
================================================
#!/usr/bin/env bash

SOURCES=$(find $(git rev-parse --show-toplevel) | grep -E "\.(cpp|cc|c|h)\$")

CLANG_FORMAT=$(which clang-format)
if [ $? -ne 0 ]; then
    CLANG_FORMAT=$(which clang-format)
    if [ $? -ne 0 ]; then
        echo "[!] clang-format not installed. Unable to check source file format policy." >&2
        exit 1
    fi
fi

set -x

for file in ${SOURCES};
do
    $CLANG_FORMAT ${file} > expected-format
    diff -u -p --label="${file}" --label="expected coding style" ${file} expected-format
done
exit $($CLANG_FORMAT --output-replacements-xml ${SOURCES} | grep -E -c "</replacement>")


================================================
FILE: .ci/check-newline.sh
================================================
#!/usr/bin/env bash

set -e -u -o pipefail

ret=0
show=0
# Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e
while IFS= read -rd '' f; do
    if file --mime-encoding "$f" | grep -qv binary; then
        tail -c1 < "$f" | read -r _ || show=1
        if [ $show -eq 1 ]; then
            echo "Warning: No newline at end of file $f"
            ret=1
            show=0
        fi
    fi
done < <(git ls-files -z examples)

exit $ret


================================================
FILE: .ci/non-working
================================================
bottomhalf
bh_threaded
intrpt
vkbd
syscall-steal
led
dht11


================================================
FILE: .ci/static-analysis.sh
================================================
#!/usr/bin/env bash

function do_cppcheck()
{
    local SOURCES=$(find $(git rev-parse --show-toplevel) | grep -E "\.(cpp|cc|c|h)\$")

    local CPPCHECK=$(which cppcheck)
    if [ $? -ne 0 ]; then
        echo "[!] cppcheck not installed. Failed to run static analysis the source code." >&2
        exit 1
    fi

    ## Suppression list ##
    # This list will explain the detail of suppressed warnings.
    # The prototype of the item should be like:
    # "- [{file}] {spec}: {reason}"
    #
    # - [hello-1.c] unusedFunction: False positive of init_module and cleanup_module.
    # - [*.c] missingIncludeSystem: Focus on the example code, not the kernel headers.

    local OPTS="
            --enable=warning,performance,information
            --suppress=unusedFunction:hello-1.c
            --suppress=missingIncludeSystem
            --std=c89 "

    $CPPCHECK $OPTS --xml ${SOURCES} 2> cppcheck.xml
    local ERROR_COUNT=$(cat cppcheck.xml | grep -E -c "</error>" )

    if [ $ERROR_COUNT -gt 0 ]; then
        echo "Cppcheck failed: $ERROR_COUNT error(s)"
        cat cppcheck.xml
        exit 1
    fi
}

function do_sparse()
{
    git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git --depth=1
    if [ $? -ne 0 ]; then
        echo "Failed to download sparse."
        exit 1
    fi
    pushd sparse
    make sparse || exit 1
    sudo make INST_PROGRAMS=sparse PREFIX=/usr install || exit 1
    popd
    local SPARSE=$(which sparse)

    make -C examples C=2 CHECK="$SPARSE" 2> sparse.log

    local WARNING_COUNT=$(cat sparse.log | grep -E -c " warning:" )
    local ERROR_COUNT=$(cat sparse.log | grep -E -c " error:" )
    local COUNT=`expr $WARNING_COUNT + $ERROR_COUNT`
    if [ $COUNT -gt 0 ]; then
        echo "Sparse failed: $WARNING_COUNT warning(s), $ERROR_COUNT error(s)"
        cat sparse.log
        exit 1
    fi
    make -C examples clean
}

function do_gcc()
{
    local GCC=$(which gcc)
    if [ $? -ne 0 ]; then
        echo "[!] gcc is not installed. Failed to run static analysis with GCC." >&2
        exit 1
    fi

    make -C examples CONFIG_STATUS_CHECK_GCC=y STATUS_CHECK_GCC=$GCC 2> gcc.log

    local WARNING_COUNT=$(cat gcc.log | grep -E -c " warning:" )
    local ERROR_COUNT=$(cat gcc.log | grep -E -c " error:" )
    local COUNT=`expr $WARNING_COUNT + $ERROR_COUNT`
    if [ $COUNT -gt 0 ]; then
        echo "gcc failed: $WARNING_COUNT warning(s), $ERROR_COUNT error(s)"
        cat gcc.log
        exit 1
    fi
    make -C examples CONFIG_STATUS_CHECK_GCC=y STATUS_CHECK_GCC=$GCC clean
}

function do_smatch()
{
    git clone https://github.com/error27/smatch.git --depth=1
    if [ $? -ne 0 ]; then
        echo "Failed to download smatch."
        exit 1
    fi
    pushd smatch
    make smatch || exit 1
    local SMATCH=$(pwd)/smatch
    popd

    make -C examples C=2 CHECK="$SMATCH -p=kernel" > smatch.log
    local WARNING_COUNT=$(cat smatch.log | egrep -c " warn:" )
    local ERROR_COUNT=$(cat smatch.log | egrep -c " error:" )
    local COUNT=`expr $WARNING_COUNT + $ERROR_COUNT`
    if [ $COUNT -gt 0 ]; then
        echo "Smatch failed: $WARNING_COUNT warning(s), $ERROR_COUNT error(s)"
        cat smatch.log | grep "warn:\|error:"
        exit 1
    fi
    make -C examples clean
}

do_cppcheck
do_sparse
do_gcc
do_smatch
exit 0


================================================
FILE: .github/workflows/build-deploy-assets.yaml
================================================
name: build-deploy-assets

on:
  push:
    branches: [ master ]

  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    container: twtug/lkmpg

    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: |
          make all
          make html
          tar zcvf lkmpg-html.tar.gz ./html
      - name: Delete old release
        uses: cb80/delrel@latest
        with:
          tag: latest
      - name: Tag
        run: |
          git tag latest
          git push -f --tags
      - name: Release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            lkmpg.pdf
            lkmpg-html.tar.gz
          tag_name: "latest"
          prerelease: true


================================================
FILE: .github/workflows/deploy-github-page.yaml
================================================
name: deploy-github-page

on:
  push:
    branches: [ master ]

  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    container: twtug/lkmpg

    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: |
          make html
      - name: Deploy to gh-pages branch
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./html
          publish_branch: gh-pages


================================================
FILE: .github/workflows/status-check.yaml
================================================
name: status-checks

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

  workflow_dispatch:

jobs:
  validate:
    runs-on: ubuntu-24.04
    steps:
      - name: checkout code
        uses: actions/checkout@v4
      - name: Test changed source files
        id: changed-files
        uses: tj-actions/changed-files@v45
        with:
          files: examples/**
      - name: validate coding style and functionality
        if: ${{ steps.changed-files.outputs.any_changed == 'true' ||
                github.event_name == 'workflow_dispatch' }}
        run: |
            sudo apt install -q -y clang-format cppcheck gcc libsqlite3-dev
            .ci/check-newline.sh
            .ci/check-format.sh
            .ci/static-analysis.sh
            .ci/build-n-run.sh
        shell: bash


================================================
FILE: .gitignore
================================================
# Linux kernel build system
*.o
*.o.d
*.ko
*cmd
*.dwo
*.swp
*.symvers
*.mod
*.mod.c
modules.order

# LaTeX
_minted-lkmpg
_minted
*.aux
*.log
*.out
lkmpg.pdf
*.toc
*.bbl
*.blg
*.fdb_latexmk
*.fls
lkmpg.synctex.gz

# make4ht
*.html
*.svg
*.tmp
*.css
*.4ct
*.4tc
*.dvi
*.lg
*.idv
*.xref
*.ttf
*.png

# format checks
expected-format


================================================
FILE: .mailmap
================================================
Jian-Xing Wu <fdgkhdkgh@gmail.com> 吳建興
Meng-Zong Tsai <hwahwa649@gmail.com> fennecJ   <58484289+fennecJ@users.noreply.github.com>
Meng-Zong Tsai <hwahwa649@gmail.com> fennecJ   <hwahwa649@gmail.com>
Jim Huang   <jserv.tw@gmail.com> Jim Huang  <jserv@ccns.ncku.edu.tw>
Jim Huang   <jserv.tw@gmail.com> Jim Huang  <jserv@biilabs.io>
Chih-En Lin <shiyn.lin@gmail.com> linD026   <shiyn.lin@gmail.com> 
Chih-En Lin <shiyn.lin@gmail.com> linD026   <66012716+linD026@users.noreply.github.com>
Chih-En Lin <shiyn.lin@gmail.com> linD026   <0086d026@email.ntou.edu.tw>
Chih-En Lin <shiyn.lin@gmail.com> linzhien  <0086d026@email.ntou.edu.tw>
mengxinayan <31788564+mengxinayan@users.noreply.github.com> 萌新阿岩
Ethan Chan  <F04066028@gs.ncku.edu.tw> tzuyichan
Peter Lin   <peterlin@qilai.dev> lyctw       <lyctw.ee@gmail.com>
Peter Lin   <peterlin@qilai.dev> Peter Lin   <peterlin.tw@protonmail.com>
Che-Chia Chang <vivahavey@gmail.com> gagachang
Shao-Tse Hung <ccs100203@gmail.com> ccs100203
Yi-Wei Lin <s921975628@gmail.com> RinHizakura
Chih-Hsuan Yang <zxc25077667@gmail.com> 25077667
Yin-Chiuan Chen <leovincentseles@gmail.com> leovincentseles
Xatierlike Lee <xatierlike@gmail.com> xatier
Chin Yik Ming <yikming2222@gmail.com> ChinYikMing
Tse-Wei Lin <20110901eric@outlook.com> 2011eric
Yu-Hsiang Tseng <asas1asas200@gmail.com> asas1asas200
Kuan-Wei Chiu <visitorckw@gmail.com> visitorckw
I-Hsin Cheng <richard120310@gmail.com> vax-r
Wei-Hsin Yeh <weihsinyeh168@gmail.com> weihsinyeh <weihsinyeh168@gmail.com>
Wei-Hsin Yeh <weihsinyeh168@gmail.com> weihsinyeh <90430653+weihsinyeh@users.noreply.github.com>
Cheng-Shian Yeh <yehchanshen@gmail.com> yeh-sudo
Yo-Jung Lin <0xff07@gmail.com> <leo.lin@canonical.com> 0xff07
YYGO <srayuws@users.noreply.github.com> srayuws
Yen-Yu Chen <matt162162162@gmail.com> <69316865+YLowy@users.noreply.github.com> Ylowy
Yan-Jie Chan <51120603+jouae@users.noreply.github.com>
Hung-Jen Pao <ginn0122@gmail.com>
Chung-Han Tsai <jeremy90307@gmail.com>


================================================
FILE: GPL-2
================================================
		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                       51 Franklin St, 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.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, 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 or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program 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".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
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 give any other recipients of the Program a copy of this License
along with the Program.

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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) 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; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an 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.  However, as a
special exception, the source code 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.

If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.

  5. 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 Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.

  7. 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 Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.

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.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.

  9. The Free Software Foundation may publish revised and/or new versions
of the 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 Program
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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	    How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.


================================================
FILE: LICENSE
================================================
OPEN SOFTWARE LICENSE 3.0 (OSL-3.0)

This Open Software License (the "License") applies to any original work
of authorship (the "Original Work") whose owner (the "Licensor") has
placed the following licensing notice adjacent to the copyright notice
for the Original Work:

Licensed under the Open Software License version 3.0

1) Grant of Copyright License. Licensor grants You a worldwide,
royalty-free, non-exclusive, sublicensable license, for the duration of
the copyright, to do the following:

a) to reproduce the Original Work in copies, either alone or as part of
a collective work;

b) to translate, adapt, alter, transform, modify, or arrange the
Original Work, thereby creating derivative works ("Derivative Works")
based upon the Original Work;

c) to distribute or communicate copies of the Original Work and
Derivative Works to the public, with the proviso that copies of Original
Work or Derivative Works that You distribute or communicate shall be
licensed under this Open Software License;

d) to perform the Original Work publicly; and

e) to display the Original Work publicly.

2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, under patent claims owned or
controlled by the Licensor that are embodied in the Original Work as
furnished by the Licensor, for the duration of the patents, to make, use,
sell, offer for sale, have made, and import the Original Work and
Derivative Works.

3) Grant of Source Code License. The term "Source Code" means the preferred
form of the Original Work for making modifications to it and all available
documentation describing how to modify the Original Work. Licensor agrees
to provide a machine-readable copy of the Source Code of the Original Work
along with each copy of the Original Work that Licensor distributes.
Licensor reserves the right to satisfy this obligation by placing a
machine-readable copy of the Source Code in an information repository
reasonably calculated to permit inexpensive and convenient access by You
for as long as Licensor continues to distribute the Original Work.

4) Exclusions From License Grant. Neither the names of Licensor, nor the
names of any contributors to the Original Work, nor any of their
trademarks or service marks, may be used to endorse or promote products
derived from this Original Work without express prior permission of the
Licensor. Except as expressly stated herein, nothing in this License
grants any license to Licensor's trademarks, copyrights, patents, trade
secrets or any other intellectual property. No patent license is granted
to make, use, sell, offer for sale, have made, or import embodiments of
any patent claims other than the licensed claims defined in Section 2.
No license is granted to the trademarks of Licensor even if such marks
are included in the Original Work. Nothing in this License shall be
interpreted to prohibit Licensor from licensing under terms different from
this License any Original Work that Licensor otherwise would have a right
to license.

5) External Deployment. The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in
any way such that the Original Work or Derivative Works may be used by
anyone other than You, whether those works are distributed or communicated
to those persons or made available as an application intended for use over
a network. As an express condition for the grants of license hereunder, You
must treat any External Deployment by You of the Original Work or a
Derivative Work as a distribution under section 1(c).

6) Attribution Rights. You must retain, in the Source Code of any Derivative
Works that You create, all copyright, patent, or trademark notices from the
Source Code of the Original Work, as well as any notices of licensing and
any descriptive text identified therein as an "Attribution Notice." You must
cause the Source Code for any Derivative Works that You create to carry a
prominent Attribution Notice reasonably calculated to inform recipients that
You have modified the Original Work.

7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
the copyright in and to the Original Work and the patent rights granted
herein by Licensor are owned by the Licensor or are sublicensed to You under
the terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an
"AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including,
without limitation, the warranties of non-infringement, merchantability or
fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE
ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an
essential part of this License. No license to the Original Work is granted
by this License except under this disclaimer.

8) Limitation of Liability. Under no circumstances and under no legal theory,
whether in tort (including negligence), contract, or otherwise, shall the
Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses. This limitation of liability shall not
apply to the extent applicable law prohibits such limitation.

9) Acceptance and Termination. If, at any time, You expressly assented to
this License, that assent indicates your clear and irrevocable acceptance of
this License and all of its terms and conditions. If You distribute or
communicate copies of the Original Work or a Derivative Work, You must make
a reasonable effort under the circumstances to obtain the express assent of
recipients to the terms of this License. This License conditions your rights
to undertake the activities listed in Section 1, including your right to
create Derivative Works based upon the Original Work, and doing so without
honoring these terms and conditions is prohibited by copyright law and
international treaty. Nothing in this License is intended to affect copyright
exceptions and limitations (including "fair use" or "fair dealing"). This
License shall terminate immediately and You may no longer exercise any of the
rights granted to You by this License upon your failure to honor the
conditions in Section 1(c).

10) Termination for Patent Action. This License shall terminate automatically
and You may no longer exercise any of the rights granted to You by this
License as of the date You commence an action, including a cross-claim or
counterclaim, against Licensor or any licensee alleging that the Original
Work infringes a patent. This termination provision shall not apply for an
action alleging patent infringement by combinations of the Original Work with
other software or hardware.

11) Jurisdiction, Venue and Governing Law. Any action or suit relating to
this License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and
under the laws of that jurisdiction excluding its conflict-of-law provisions.
The application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded. Any use of the Original
Work outside the scope of this License or after its termination shall be
subject to the requirements and penalties of copyright or patent law in the
appropriate jurisdiction. This section shall survive the termination of this
License.

12) Attorneys' Fees. In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of
this License.

13) Miscellaneous. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable.

14) Definition of "You" in This License. "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this
License. For legal entities, "You" includes any entity that controls,
is controlled by, or is under common control with you. For purposes of
this definition, "control" means (i) the power, direct or indirect, to
cause the direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

15) Right to Use. You may use the Original Work in all ways not
otherwise restricted or conditioned by this License or by law, and
Licensor promises not to interfere with or be responsible for such
uses by You.

16) Modification of This License. This License is Copyright © 2005
Lawrence Rosen. Permission is granted to copy, distribute, or
communicate this License without modification. Nothing in this License
permits You to modify this License as applied to the Original Work or
to Derivative Works. However, You may modify the text of this License
and copy, distribute or communicate your modified version (the "Modified
License") and apply it to other original works of authorship subject to
the following conditions: (i) You may not indicate in any way that your
Modified License is the "Open Software License" or "OSL" and you may not
use those names in the name of your Modified License; (ii) You must replace
the notice specified in the first paragraph above with the notice
"Licensed under <insert your license name here>" or with a notice of your
own that is not confusingly similar to the notice in this License; and
(iii) You may not claim that your original works are open source software
unless your Modified License has been approved by Open Source Initiative
(OSI) and You comply with its license review and certification process.


================================================
FILE: Makefile
================================================
PROJ = lkmpg
all: $(PROJ).pdf

$(PROJ).pdf: lkmpg.tex
	@if ! hash latexmk; then echo "No Latexmk found. See https://mg.readthedocs.io/latexmk.html for installation."; exit 1; fi
	rm -rf _minted-$(PROJ)
	latexmk -shell-escape lkmpg.tex -pdf

html: lkmpg.tex html.cfg assets/Manrope_variable.ttf
	sed $ 's/\t/    /g' lkmpg.tex > lkmpg-for-ht.tex
	make4ht --shell-escape --utf8 --format html5 --config html.cfg --output-dir html  lkmpg-for-ht.tex "fn-in"
	ln -sf lkmpg-for-ht.html html/index.html
	cp assets/Manrope_variable.ttf html/Manrope_variable.ttf
	rm -f  lkmpg-for-ht.tex lkmpg-for-ht.xref lkmpg-for-ht.tmp lkmpg-for-ht.html lkmpg-for-ht.css lkmpg-for-ht.4ct lkmpg-for-ht.4tc lkmpg-for-ht.dvi lkmpg-for-ht.lg lkmpg-for-ht.idv lkmpg*.svg lkmpg-for-ht.log lkmpg-for-ht.aux
	rm -rf _minted-$(PROJ) _minted-lkmpg-for-ht

indent:
	(cd examples; find . -name '*.[ch]' | xargs clang-format -i)

clean:
	rm -f *.dvi *.aux *.log *.ps *.pdf *.out lkmpg.bbl lkmpg.blg lkmpg.lof lkmpg.toc lkmpg.fdb_latexmk lkmpg.fls
	rm -rf html

.PHONY: html


================================================
FILE: README.md
================================================
# The Linux Kernel Module Programming Guide

This project keeps the Linux Kernel Module Programming Guide up to date, with [working examples](examples/) for recent 5.x and 6.x kernel versions.
The guide has been around since 2001 and most copies of it on the web only describe old 2.6.x kernels.

The book can be freely accessed via https://sysprog21.github.io/lkmpg/ or [latest PDF file](https://github.com/sysprog21/lkmpg/releases).
The original guide may be found at [Linux Documentation Project](http://www.tldp.org/LDP/lkmpg/).
You may check other [freely available programming books](https://ebookfoundation.github.io/free-programming-books-search/) listed by The [Free Ebook Foundation](https://ebookfoundation.org/) or [Linux online books](https://onlinebooks.library.upenn.edu/webbin/book/browse?type=lcsubc&key=Linux) collected by [The Online Books Page](https://onlinebooks.library.upenn.edu/).

## Getting Started

### Summary
1. Get the latest source code from the [GitHub page](https://github.com/sysprog21/lkmpg).
2. Install the prerequisites.
3. Generate PDF and/or HTML documents.

### Step 1: Get the latest source code

Make sure you can run `git` with an Internet connection.

```shell
$ git clone https://github.com/sysprog21/lkmpg.git && cd lkmpg
```

### Step 2: Install the prerequisites

To generate the book from source, [TeXLive](https://www.tug.org/texlive/) ([MacTeX](https://www.tug.org/mactex/)) is required.

For Ubuntu Linux, macOS, and other Unix-like systems, run the following command(s):

```bash
# Debian / Ubuntu
$ sudo apt install make texlive-full

# Arch / Manjaro
$ sudo pacman -S make texlive-binextra texlive-bin

# macOS
$ brew install mactex
$ sudo tlmgr update --self
```

Note that `latexmk` is required to generated PDF, and it probably has been installed on your OS already. If not, please follow the [installation guide](https://mg.readthedocs.io/latexmk.html#installation).

In macOS systems, package `Pygments` may not be pre-installed. If not, please refer to the [installation guide](https://pygments.org/download/) before generate documents.

Alternatively, using [Docker](https://docs.docker.com/) is recommended, as it guarantees the same dependencies with our GitHub Actions workflow.
After install [docker engine](https://docs.docker.com/engine/install/) on your machine, pull the docker image [twtug/lkmpg](https://hub.docker.com/r/twtug/lkmpg) and run in isolated containers.

```shell
# pull docker image and run it as container
$ docker pull twtug/lkmpg
$ docker run --rm -it -v $(pwd):/workdir twtug/lkmpg
```

[nerdctl](https://github.com/containerd/nerdctl) is a Docker-compatible command line tool for [containerd](https://containerd.io/), and you can replace the above `docker` commands with `nerdctl` counterparts.

### Step 3: Generate PDF and/or HTML documents

Now we could build document with following commands:

```bash
$ make all              # Generate PDF document
$ make html             # Convert TeX to HTML
$ make clean            # Delete generated files
```

## License

The Linux Kernel Module Programming Guide is a free book; you may reproduce and/or modify it under the terms of the [Open Software License](https://opensource.org/licenses/OSL-3.0).
Use of this work is governed by a copyleft license that can be found in the `LICENSE` file.

The complementary sample code is licensed under GNU GPL version 2, as same as Linux kernel.


================================================
FILE: contrib.tex
================================================
Amit Dhingra,                  % <mechanicalamit@gmail.com>
Andrew Kreimer,                % <algonell@gmail.com>
Andrew Lin,                    % <35786166+classAndrew@users.noreply.github.com>
Andy Shevchenko,               % <andriy.shevchenko@linux.intel.com>
Arush Sharma,                  % <46960231+arushsharma24@users.noreply.github.com>
Aykhan Hagverdili,             % <aykhanhagverdili@gmail.com>
Benno Bielmeier,               % <32938211+bbenno@users.noreply.github.com>
Bob Lee,                       % <defru04002@gmail.com>
Brad Baker,                    % <brad@brdbkr.com>
Che-Chia Chang,                % <vivahavey@gmail.com>
Cheng-Shian Yeh,               % <yehchanshen@gmail.com>
Cheng-Yang Chou,               % <yphbchou0911@gmail.com>
Chih-En Lin,                   % <shiyn.lin@gmail.com>
Chih-Hsuan Yang,               % <zxc25077667@gmail.com>
Chih-Yu Chen,                  % <34228283+chihyu1206@users.noreply.github.com>
Ching-Hua (Vivian) Lin,        % <jkrvivian@gmail.com>
Chin Yik Ming,                 % <yikming2222@gmail.com>
Chung-Han Tsai,                % <jeremy90307@gmail.com>
cvvletter,                     % <cvvletter@pm.me>
Cyril Brulebois,               % <cyril@debamax.com>
Daniele Paolo Scarpazza,       % <>
David Porter,                  % <>
demonsome,                     % <horseradish1208@gmail.com>
Dimo Velev,                    % <>
Ekang Monyet,                  % <ekangmonyet@posteo.net>
Ethan Chan,                    % <F04066028@gs.ncku.edu.tw>
Francois Audeon,               % <>
Gilad Reti,                    % <gilad.reti@gmail.com>
Hao.Dong,                      % <hao.dong.nj@outlook.com>
heartofrain,                   % <heartofrain@outlook.com>
Horst Schirmeier,              % <>
Hsin-Hsiang Peng,              % <hsinspeng@gmail.com>
Hung-Jen Pao,                  % <ginn0122@gmail.com>
Ignacio Martin,                % <>
I-Hsin Cheng,                  % <richard120310@gmail.com>
Integral,                      % <integral@member.fsf.org>
Iûnn Kiàn-îng,                 % <black.yangcr@gmail.com>
Jian-Xing Wu,                  % <fdgkhdkgh@gmail.com>
Jimmy Ma,                      % <jimmatw@gmail.com>
Johan Calle,                   % <43998967+jcallemc@users.noreply.github.com>
keytouch,                      % <qsytech@126.com>
Kohei Otsuka,                  % <13173186+rjhcnf@users.noreply.github.com>
Kuan-Wei Chiu,                 % <visitorckw@gmail.com>
manbing,                       % <manbing3@gmail.com>
Marconi Jiang,                 % <marconi1964@yahoo.com>
mengxinayan,                   % <31788564+mengxinayan@users.noreply.github.com>
Meng-Zong Tsai,                % <hwahwa649@gmail.com>
Peter Lin,                     % <peterlin@qilai.dev>
Roman Lakeev,                  % <>
Sam Erickson,                  % <samuelerickson977@gmail.com>
Shao-Tse Hung,                 % <ccs100203@gmail.com>
Shih-Sheng Yang,               % <james1qaz2wsx12qw@gmail.com>
Stacy Prowell,                 % <sprowell@gmail.com>
Steven Lung,                   % <1030steven@gmail.com>
Tristan Lelong,                % <tristan.lelong@blunderer.org>
Tse-Wei Lin,                   % <20110901eric@outlook.com>
Tucker Polomik,                % <tucker.polomik@inficon.com>
Tyler Fanelli,                 % <tfanelli@redhat.com>
VxTeemo,                       % <tcccvvv123@gmail.com>
Wei-Hsin Yeh,                  % <weihsinyeh168@gmail.com>
Wei-Lun Tsai,                  % <alan23273850@gmail.com>
Xatierlike Lee,                % <xatierlike@gmail.com>
Yan-Jie Chan,                  % <51120603+jouae@users.noreply.github.com>
Yen-Yu Chen,                   % <matt162162162@gmail.com>
Yin-Chiuan Chen,               % <leovincentseles@gmail.com>
Yi-Wei Lin,                    % <s921975628@gmail.com>
Yo-Jung Lin,                   % <0xff07@gmail.com>
Yu-Chun Lin,                   % <eleanor15x@gmail.com>
Yu-Hsiang Tseng,               % <asas1asas200@gmail.com>
YYGO.                          % <srayuws@users.noreply.github.com>


================================================
FILE: examples/.clang-format
================================================
Language: Cpp

AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true

BraceWrapping:
  AfterClass: false
  AfterControlStatement: false
  AfterEnum: false
  AfterFunction: true
  AfterNamespace: true
  AfterObjCDeclaration: false
  AfterStruct: false
  AfterUnion: false
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
  IndentBraces: false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true

BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false

ForEachMacros:
  - 'list_for_each'
  - 'list_for_each_safe'

IncludeBlocks: Preserve
IncludeCategories:
  - Regex: '.*'
    Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None

PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 4
UseTab: Never


================================================
FILE: examples/Makefile
================================================
obj-m += hello-1.o
obj-m += hello-2.o
obj-m += hello-3.o
obj-m += hello-4.o
obj-m += hello-5.o
obj-m += startstop.o
startstop-objs := start.o stop.o
obj-m += chardev.o
obj-m += procfs1.o
obj-m += procfs2.o
obj-m += procfs3.o
obj-m += procfs4.o
obj-m += hello-sysfs.o
obj-m += sleep.o
obj-m += print_string.o
obj-m += kbleds.o
obj-m += sched.o
obj-m += chardev2.o
obj-m += syscall-steal.o
obj-m += intrpt.o
obj-m += completions.o
obj-m += example_tasklet.o
obj-m += devicemodel.o
obj-m += example_spinlock.o
obj-m += example_rwlock.o
obj-m += example_atomic.o
obj-m += example_mutex.o
obj-m += bottomhalf.o
obj-m += bh_threaded.o
obj-m += ioctl.o
obj-m += vinput.o
obj-m += vkbd.o
obj-m += static_key.o
obj-m += led.o
obj-m += dht11.o
obj-m += devicetree.o

KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(CURDIR)

ifeq ($(CONFIG_STATUS_CHECK_GCC),y)
CC=$(STATUS_CHECK_GCC)
ccflags-y += -fanalyzer
endif

all:
	$(MAKE) -C $(KDIR) CC=$(CC) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) CC=$(CC) M=$(PWD) clean
	$(RM) other/cat_nonblock *.plist

indent:
	clang-format -i *.[ch]
	clang-format -i other/*.[ch]


================================================
FILE: examples/bh_threaded.c
================================================
/*
 * bh_thread.c - Top and bottom half interrupt handling
 *
 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de)
 * from:
 *    https://github.com/wendlers/rpi-kmod-samples
 *
 * Press one button to turn on a LED and another to turn it off
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
#define NO_GPIO_REQUEST_ARRAY
#endif

static int button_irqs[] = { -1, -1 };

/* Define GPIOs for LEDs.
 * FIXME: Change the numbers for the GPIO on your board.
 */
static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };

/* Define GPIOs for BUTTONS
 * FIXME: Change the numbers for the GPIO on your board.
 */
static struct gpio buttons[] = {
    { 17, GPIOF_IN, "LED 1 ON BUTTON" },
    { 18, GPIOF_IN, "LED 1 OFF BUTTON" },
};

/* This happens immediately, when the IRQ is triggered */
static irqreturn_t button_top_half(int irq, void *ident)
{
    return IRQ_WAKE_THREAD;
}

/* This can happen at leisure, freeing up IRQs for other high priority task */
static irqreturn_t button_bottom_half(int irq, void *ident)
{
    pr_info("Bottom half task starts\n");
    mdelay(500); /* do something which takes a while */
    pr_info("Bottom half task ends\n");
    return IRQ_HANDLED;
}

static int __init bottomhalf_init(void)
{
    int ret = 0;

    pr_info("%s\n", __func__);

/* register LED gpios */
#ifdef NO_GPIO_REQUEST_ARRAY
    ret = gpio_request(leds[0].gpio, leds[0].label);
#else
    ret = gpio_request_array(leds, ARRAY_SIZE(leds));
#endif

    if (ret) {
        pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
        return ret;
    }

/* register BUTTON gpios */
#ifdef NO_GPIO_REQUEST_ARRAY
    ret = gpio_request(buttons[0].gpio, buttons[0].label);

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail1;
    }

    ret = gpio_request(buttons[1].gpio, buttons[1].label);

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail2;
    }
#else
    ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail1;
    }
#endif

    pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio));

    ret = gpio_to_irq(buttons[0].gpio);

    if (ret < 0) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    button_irqs[0] = ret;

    pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]);

    ret = request_threaded_irq(button_irqs[0], button_top_half,
                               button_bottom_half,
                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                               "gpiomod#button1", &buttons[0]);

    if (ret) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    ret = gpio_to_irq(buttons[1].gpio);

    if (ret < 0) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    button_irqs[1] = ret;

    pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]);

    ret = request_threaded_irq(button_irqs[1], button_top_half,
                               button_bottom_half,
                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                               "gpiomod#button2", &buttons[1]);

    if (ret) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail4;
#else
        goto fail3;
#endif
    }

    return 0;

/* cleanup what has been setup so far */
#ifdef NO_GPIO_REQUEST_ARRAY
fail4:
    free_irq(button_irqs[0], &buttons[0]);

fail3:
    gpio_free(buttons[1].gpio);

fail2:
    gpio_free(buttons[0].gpio);

fail1:
    gpio_free(leds[0].gpio);
#else
fail3:
    free_irq(button_irqs[0], &buttons[0]);

fail2:
    gpio_free_array(buttons, ARRAY_SIZE(buttons));

fail1:
    gpio_free_array(leds, ARRAY_SIZE(leds));
#endif

    return ret;
}

static void __exit bottomhalf_exit(void)
{
    pr_info("%s\n", __func__);

    /* free irqs */
    free_irq(button_irqs[0], &buttons[0]);
    free_irq(button_irqs[1], &buttons[1]);

/* turn all LEDs off */
#ifdef NO_GPIO_REQUEST_ARRAY
    gpio_set_value(leds[0].gpio, 0);
#else
    int i;
    for (i = 0; i < ARRAY_SIZE(leds); i++)
        gpio_set_value(leds[i].gpio, 0);
#endif

/* unregister */
#ifdef NO_GPIO_REQUEST_ARRAY
    gpio_free(leds[0].gpio);
    gpio_free(buttons[0].gpio);
    gpio_free(buttons[1].gpio);
#else
    gpio_free_array(leds, ARRAY_SIZE(leds));
    gpio_free_array(buttons, ARRAY_SIZE(buttons));
#endif
}

module_init(bottomhalf_init);
module_exit(bottomhalf_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Interrupt with top and bottom half");


================================================
FILE: examples/bottomhalf.c
================================================
/*
 * bottomhalf.c - Top and bottom half interrupt handling
 *
 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de)
 * from:
 *    https://github.com/wendlers/rpi-kmod-samples
 *
 * Press one button to turn on an LED and another to turn it off
 */

#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/workqueue.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
#define NO_GPIO_REQUEST_ARRAY
#endif

static int button_irqs[] = { -1, -1 };

/* Define GPIOs for LEDs.
 * TODO: Change the numbers for the GPIO on your board.
 */
static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };

/* Define GPIOs for BUTTONS
 * TODO: Change the numbers for the GPIO on your board.
 */
static struct gpio buttons[] = {
    { 17, GPIOF_IN, "LED 1 ON BUTTON" },
    { 18, GPIOF_IN, "LED 1 OFF BUTTON" },
};

/* Workqueue function containing some non-trivial amount of processing */
static void bottomhalf_work_fn(struct work_struct *work)
{
    pr_info("Bottom half workqueue starts\n");
    /* do something which takes a while */
    msleep(500);

    pr_info("Bottom half workqueue ends\n");
}

static DECLARE_WORK(bottomhalf_work, bottomhalf_work_fn);

/* interrupt function triggered when a button is pressed */
static irqreturn_t button_isr(int irq, void *data)
{
    /* Do something quickly right now */
    if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio))
        gpio_set_value(leds[0].gpio, 1);
    else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio))
        gpio_set_value(leds[0].gpio, 0);

    /* Do the rest at leisure via the scheduler */
    schedule_work(&bottomhalf_work);
    return IRQ_HANDLED;
}

static int __init bottomhalf_init(void)
{
    int ret = 0;

    pr_info("%s\n", __func__);

    /* register LED gpios */
#ifdef NO_GPIO_REQUEST_ARRAY
    ret = gpio_request(leds[0].gpio, leds[0].label);
#else
    ret = gpio_request_array(leds, ARRAY_SIZE(leds));
#endif

    if (ret) {
        pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
        return ret;
    }

    /* register BUTTON gpios */
#ifdef NO_GPIO_REQUEST_ARRAY
    ret = gpio_request(buttons[0].gpio, buttons[0].label);

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail1;
    }

    ret = gpio_request(buttons[1].gpio, buttons[1].label);

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail2;
    }
#else
    ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail1;
    }
#endif

    pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio));

    ret = gpio_to_irq(buttons[0].gpio);

    if (ret < 0) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    button_irqs[0] = ret;

    pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]);

    ret = request_irq(button_irqs[0], button_isr,
                      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                      "gpiomod#button1", NULL);

    if (ret) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    ret = gpio_to_irq(buttons[1].gpio);

    if (ret < 0) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    button_irqs[1] = ret;

    pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]);

    ret = request_irq(button_irqs[1], button_isr,
                      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                      "gpiomod#button2", NULL);

    if (ret) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail4;
#else
        goto fail3;
#endif
    }

    return 0;

/* cleanup what has been setup so far */
#ifdef NO_GPIO_REQUEST_ARRAY
fail4:
    free_irq(button_irqs[0], NULL);

fail3:
    gpio_free(buttons[1].gpio);

fail2:
    gpio_free(buttons[0].gpio);

fail1:
    gpio_free(leds[0].gpio);
#else
fail3:
    free_irq(button_irqs[0], NULL);

fail2:
    gpio_free_array(buttons, ARRAY_SIZE(buttons));

fail1:
    gpio_free_array(leds, ARRAY_SIZE(leds));
#endif

    return ret;
}

static void __exit bottomhalf_exit(void)
{
    pr_info("%s\n", __func__);

    /* free irqs */
    free_irq(button_irqs[0], NULL);
    free_irq(button_irqs[1], NULL);

    /* turn all LEDs off */
#ifdef NO_GPIO_REQUEST_ARRAY
    gpio_set_value(leds[0].gpio, 0);
#else
    int i;
    for (i = 0; i < ARRAY_SIZE(leds); i++)
        gpio_set_value(leds[i].gpio, 0);
#endif

    /* unregister */
#ifdef NO_GPIO_REQUEST_ARRAY
    gpio_free(leds[0].gpio);
    gpio_free(buttons[0].gpio);
    gpio_free(buttons[1].gpio);
#else
    gpio_free_array(leds, ARRAY_SIZE(leds));
    gpio_free_array(buttons, ARRAY_SIZE(buttons));
#endif
}

module_init(bottomhalf_init);
module_exit(bottomhalf_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Interrupt with top and bottom half");


================================================
FILE: examples/chardev.c
================================================
/*
 * chardev.c: Creates a read-only char device that says how many times
 * you have read from the dev file
 */

#include <linux/atomic.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h> /* for sprintf() */
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/uaccess.h> /* for get_user and put_user */
#include <linux/version.h>

#include <asm/errno.h>

/*  Prototypes - this would normally go in a .h file */
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char __user *, size_t,
                            loff_t *);

#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices   */
#define BUF_LEN 80 /* Max length of the message from the device */

/* Global variables are declared as static, so are global within the file. */

static int major; /* major number assigned to our device driver */

enum {
    CDEV_NOT_USED,
    CDEV_EXCLUSIVE_OPEN,
};

/* Is device open? Used to prevent multiple access to device */
static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED);

static char msg[BUF_LEN + 1]; /* The msg the device will give when asked */

static struct class *cls;

static struct file_operations chardev_fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release,
};

static int __init chardev_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &chardev_fops);

    if (major < 0) {
        pr_alert("Registering char device failed with %d\n", major);
        return major;
    }

    pr_info("I was assigned major number %d.\n", major);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
    cls = class_create(DEVICE_NAME);
#else
    cls = class_create(THIS_MODULE, DEVICE_NAME);
#endif
    if (IS_ERR(cls)) {
        pr_err("Failed to create class for device\n");
        unregister_chrdev(major, DEVICE_NAME);
        return PTR_ERR(cls);
    }
    device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);

    pr_info("Device created on /dev/%s\n", DEVICE_NAME);

    return 0;
}

static void __exit chardev_exit(void)
{
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);

    /* Unregister the device */
    unregister_chrdev(major, DEVICE_NAME);
}

/* Methods */

/* Called when a process tries to open the device file, like
 * "sudo cat /dev/chardev"
 */
static int device_open(struct inode *inode, struct file *file)
{
    static int counter = 0;

    if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN))
        return -EBUSY;

    sprintf(msg, "I already told you %d times Hello world!\n", counter++);

    return 0;
}

/* Called when a process closes the device file. */
static int device_release(struct inode *inode, struct file *file)
{
    /* We're now ready for our next caller */
    atomic_set(&already_open, CDEV_NOT_USED);

    return 0;
}

/* Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h   */
                           char __user *buffer, /* buffer to fill with data */
                           size_t length, /* length of the buffer     */
                           loff_t *offset)
{
    /* Number of bytes actually written to the buffer */
    int bytes_read = 0;
    const char *msg_ptr = msg;

    if (!*(msg_ptr + *offset)) { /* we are at the end of message */
        *offset = 0; /* reset the offset */
        return 0; /* signify end of file */
    }

    msg_ptr += *offset;

    /* Actually put the data into the buffer */
    while (length && *msg_ptr) {
        /* The buffer is in the user data segment, not the kernel
         * segment so "*" assignment won't work.  We have to use
         * put_user which copies data from the kernel data segment to
         * the user data segment.
         */
        put_user(*(msg_ptr++), buffer++);
        length--;
        bytes_read++;
    }

    *offset += bytes_read;

    /* Most read functions return the number of bytes put into the buffer. */
    return bytes_read;
}

/* Called when a process writes to dev file: echo "hi" | sudo tee /dev/chardev */
static ssize_t device_write(struct file *filp, const char __user *buff,
                            size_t len, loff_t *off)
{
    pr_alert("Sorry, this operation is not supported.\n");
    return -EINVAL;
}

module_init(chardev_init);
module_exit(chardev_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/chardev.h
================================================
/*
 * chardev.h - the header file with the ioctl definitions.
 *
 * The declarations here have to be in a header file, because they need
 * to be known both to the kernel module (in chardev2.c) and the process
 * calling ioctl() (in userspace_ioctl.c).
 */

#ifndef CHARDEV_H
#define CHARDEV_H

#include <linux/ioctl.h>

/* The major device number. We can not rely on dynamic registration
 * any more, because ioctls need to know it.
 */
#define MAJOR_NUM 100

/* Set the message of the device driver */
#define IOCTL_SET_MSG _IOW(MAJOR_NUM, 0, char *)
/* _IOW means that we are creating an ioctl command number for passing
 * information from a user process to the kernel module.
 *
 * The first arguments, MAJOR_NUM, is the major device number we are using.
 *
 * The second argument is the number of the command (there could be several
 * with different meanings).
 *
 * The third argument is the type we want to get from the process to the
 * kernel.
 */

/* Get the message of the device driver */
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
/* This IOCTL is used for output, to get the message of the device driver.
 * However, we still need the buffer to place the message in to be input,
 * as it is allocated by the process.
 */

/* Get the n'th byte of the message */
#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)
/* The IOCTL is used for both input and output. It receives from the user
 * a number, n, and returns message[n].
 */

/* The name of the device file */
#define DEVICE_FILE_NAME "char_dev"
#define DEVICE_PATH "/dev/char_dev"

#endif


================================================
FILE: examples/chardev2.c
================================================
/*
 * chardev2.c - Create an input/output character device
 */

#include <linux/atomic.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h> /* Specifically, a module */
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/uaccess.h> /* for get_user and put_user */
#include <linux/version.h>

#include <asm/errno.h>

#include "chardev.h"
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80

enum {
    CDEV_NOT_USED,
    CDEV_EXCLUSIVE_OPEN,
};

/* Is the device open right now? Used to prevent concurrent access into
 * the same device
 */
static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED);

/* The message the device will give when asked */
static char message[BUF_LEN + 1];

static struct class *cls;

/* This is called whenever a process attempts to open the device file */
static int device_open(struct inode *inode, struct file *file)
{
    pr_info("device_open(%p)\n", file);

    return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
    pr_info("device_release(%p,%p)\n", inode, file);

    return 0;
}

/* This function is called whenever a process which has already opened the
 * device file attempts to read from it.
 */
static ssize_t device_read(struct file *file, /* see include/linux/fs.h   */
                           char __user *buffer, /* buffer to be filled  */
                           size_t length, /* length of the buffer     */
                           loff_t *offset)
{
    /* Number of bytes actually written to the buffer */
    int bytes_read = 0;
    /* How far did the process reading the message get? Useful if the message
     * is larger than the size of the buffer we get to fill in device_read.
     */
    const char *message_ptr = message;

    if (!*(message_ptr + *offset)) { /* we are at the end of message */
        *offset = 0; /* reset the offset */
        return 0; /* signify end of file */
    }

    message_ptr += *offset;

    /* Actually put the data into the buffer */
    while (length && *message_ptr) {
        /* Because the buffer is in the user data segment, not the kernel
         * data segment, assignment would not work. Instead, we have to
         * use put_user which copies data from the kernel data segment to
         * the user data segment.
         */
        put_user(*(message_ptr++), buffer++);
        length--;
        bytes_read++;
    }

    pr_info("Read %d bytes, %ld left\n", bytes_read, length);

    *offset += bytes_read;

    /* Read functions are supposed to return the number of bytes actually
     * inserted into the buffer.
     */
    return bytes_read;
}

/* called when somebody tries to write into our device file. */
static ssize_t device_write(struct file *file, const char __user *buffer,
                            size_t length, loff_t *offset)
{
    int i;

    pr_info("device_write(%p,%p,%ld)", file, buffer, length);

    for (i = 0; i < length && i < BUF_LEN; i++)
        get_user(message[i], buffer + i);

    /* Again, return the number of input characters used. */
    return i;
}

/* This function is called whenever a process tries to do an ioctl on our
 * device file. We get two extra parameters (additional to the inode and file
 * structures, which all device functions get): the number of the ioctl called
 * and the parameter given to the ioctl function.
 *
 * If the ioctl is write or read/write (meaning output is returned to the
 * calling process), the ioctl call returns the output of this function.
 */
static long
device_ioctl(struct file *file, /* ditto */
             unsigned int ioctl_num, /* number and param for ioctl */
             unsigned long ioctl_param)
{
    int i;
    long ret = 0;

    /* We don't want to talk to two processes at the same time. */
    if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN))
        return -EBUSY;

    /* Switch according to the ioctl called */
    switch (ioctl_num) {
    case IOCTL_SET_MSG: {
        /* Receive a pointer to a message (in user space) and set that to
         * be the device's message. Get the parameter given to ioctl by
         * the process.
         */
        char __user *tmp = (char __user *)ioctl_param;
        char ch;

        /* Find the length of the message */
        get_user(ch, tmp);
        for (i = 0; ch && i < BUF_LEN; i++, tmp++)
            get_user(ch, tmp);

        device_write(file, (char __user *)ioctl_param, i, NULL);
        break;
    }
    case IOCTL_GET_MSG: {
        loff_t offset = 0;

        /* Give the current message to the calling process - the parameter
         * we got is a pointer, fill it.
         */
        i = device_read(file, (char __user *)ioctl_param, 99, &offset);

        /* Put a zero at the end of the buffer, so it will be properly
         * terminated.
         */
        put_user('\0', (char __user *)ioctl_param + i);
        break;
    }
    case IOCTL_GET_NTH_BYTE:
        /* This ioctl is both input (ioctl_param) and output (the return
         * value of this function).
         */
        ret = (long)message[ioctl_param];
        break;
    }

    /* We're now ready for our next caller */
    atomic_set(&already_open, CDEV_NOT_USED);

    return ret;
}

/* Module Declarations */

/* This structure will hold the functions to be called when a process does
 * something to the device we created. Since a pointer to this structure
 * is kept in the devices table, it can't be local to init_module. NULL is
 * for unimplemented functions.
 */
static struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .unlocked_ioctl = device_ioctl,
    .open = device_open,
    .release = device_release, /* a.k.a. close */
};

/* Initialize the module - Register the character device */
static int __init chardev2_init(void)
{
    /* Register the character device (at least try) */
    int ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &fops);

    /* Negative values signify an error */
    if (ret_val < 0) {
        pr_alert("%s failed with %d\n",
                 "Sorry, registering the character device ", ret_val);
        return ret_val;
    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
    cls = class_create(DEVICE_FILE_NAME);
#else
    cls = class_create(THIS_MODULE, DEVICE_FILE_NAME);
#endif
    if (IS_ERR(cls)) {
        pr_err("Failed to create class for device\n");
        unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
        return PTR_ERR(cls);
    }
    device_create(cls, NULL, MKDEV(MAJOR_NUM, 0), NULL, DEVICE_FILE_NAME);

    pr_info("Device created on /dev/%s\n", DEVICE_FILE_NAME);

    return 0;
}

/* Cleanup - unregister the appropriate file from /proc */
static void __exit chardev2_exit(void)
{
    device_destroy(cls, MKDEV(MAJOR_NUM, 0));
    class_destroy(cls);

    /* Unregister the device */
    unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
}

module_init(chardev2_init);
module_exit(chardev2_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/completions.c
================================================
/*
 * completions.c
 */
#include <linux/completion.h>
#include <linux/err.h> /* for IS_ERR() */
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/version.h>

static struct completion crank_comp;
static struct completion flywheel_comp;

static int machine_crank_thread(void *arg)
{
    pr_info("Turn the crank\n");

    complete_all(&crank_comp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
    kthread_complete_and_exit(&crank_comp, 0);
#else
    complete_and_exit(&crank_comp, 0);
#endif
}

static int machine_flywheel_spinup_thread(void *arg)
{
    wait_for_completion(&crank_comp);

    pr_info("Flywheel spins up\n");

    complete_all(&flywheel_comp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
    kthread_complete_and_exit(&flywheel_comp, 0);
#else
    complete_and_exit(&flywheel_comp, 0);
#endif
}

static int __init completions_init(void)
{
    struct task_struct *crank_thread;
    struct task_struct *flywheel_thread;

    pr_info("completions example\n");

    init_completion(&crank_comp);
    init_completion(&flywheel_comp);

    crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank");
    if (IS_ERR(crank_thread))
        goto ERROR_THREAD_1;

    flywheel_thread = kthread_create(machine_flywheel_spinup_thread, NULL,
                                     "KThread Flywheel");
    if (IS_ERR(flywheel_thread))
        goto ERROR_THREAD_2;

    wake_up_process(flywheel_thread);
    wake_up_process(crank_thread);

    return 0;

ERROR_THREAD_2:
    kthread_stop(crank_thread);
ERROR_THREAD_1:

    return -1;
}

static void __exit completions_exit(void)
{
    wait_for_completion(&crank_comp);
    wait_for_completion(&flywheel_comp);

    pr_info("completions exit\n");
}

module_init(completions_init);
module_exit(completions_exit);

MODULE_DESCRIPTION("Completions example");
MODULE_LICENSE("GPL");


================================================
FILE: examples/devicemodel.c
================================================
/*
 * devicemodel.c
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/version.h>

struct devicemodel_data {
    char *greeting;
    int number;
};

static int devicemodel_probe(struct platform_device *dev)
{
    struct devicemodel_data *pd =
        (struct devicemodel_data *)(dev->dev.platform_data);

    pr_info("devicemodel probe\n");
    pr_info("devicemodel greeting: %s; %d\n", pd->greeting, pd->number);

    /* Your device initialization code */

    return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
static void devicemodel_remove(struct platform_device *dev)
{
    pr_info("devicemodel example removed\n");
    /* Your device removal code */
}
#else
static int devicemodel_remove(struct platform_device *dev)
{
    pr_info("devicemodel example removed\n");
    /* Your device removal code */
    return 0;
}
#endif

static int devicemodel_suspend(struct device *dev)
{
    pr_info("devicemodel example suspend\n");

    /* Your device suspend code */

    return 0;
}

static int devicemodel_resume(struct device *dev)
{
    pr_info("devicemodel example resume\n");

    /* Your device resume code */

    return 0;
}

static const struct dev_pm_ops devicemodel_pm_ops = {
    .suspend = devicemodel_suspend,
    .resume = devicemodel_resume,
    .poweroff = devicemodel_suspend,
    .freeze = devicemodel_suspend,
    .thaw = devicemodel_resume,
    .restore = devicemodel_resume,
};

static struct platform_driver devicemodel_driver = {
    .driver =
        {
            .name = "devicemodel_example",
            .pm = &devicemodel_pm_ops,
        },
    .probe = devicemodel_probe,
    .remove = devicemodel_remove,
};

static int __init devicemodel_init(void)
{
    int ret;

    pr_info("devicemodel init\n");

    ret = platform_driver_register(&devicemodel_driver);

    if (ret) {
        pr_err("Unable to register driver\n");
        return ret;
    }

    return 0;
}

static void __exit devicemodel_exit(void)
{
    pr_info("devicemodel exit\n");
    platform_driver_unregister(&devicemodel_driver);
}

module_init(devicemodel_init);
module_exit(devicemodel_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Linux Device Model example");


================================================
FILE: examples/devicetree.c
================================================
/* devicetree.c - Demonstrates device tree interaction with kernel modules */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/version.h>

#define DRIVER_NAME "lkmpg_devicetree"

/* Structure to hold device-specific data */
struct dt_device_data {
    const char *label;
    u32 reg_value;
    u32 custom_value;
    bool has_clock;
};

/* Probe function - called when device tree node matches */
static int dt_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *np = dev->of_node;
    struct dt_device_data *data;
    const char *string_prop;
    u32 value;
    int ret;

    pr_info("%s: Device tree probe called for %s\n", DRIVER_NAME,
            np->full_name);

    /* Allocate memory for device data */
    data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    /* Read a string property */
    ret = of_property_read_string(np, "label", &string_prop);
    if (ret == 0) {
        data->label = string_prop;
        pr_info("%s: Found label property: %s\n", DRIVER_NAME, data->label);
    } else {
        data->label = "unnamed";
        pr_info("%s: No label property found, using default\n", DRIVER_NAME);
    }

    /* Read a u32 property */
    ret = of_property_read_u32(np, "reg", &value);
    if (ret == 0) {
        data->reg_value = value;
        pr_info("%s: Found reg property: 0x%x\n", DRIVER_NAME, data->reg_value);
    }

    /* Read a custom u32 property */
    ret = of_property_read_u32(np, "lkmpg,custom-value", &value);
    if (ret == 0) {
        data->custom_value = value;
        pr_info("%s: Found custom-value property: %u\n", DRIVER_NAME,
                data->custom_value);
    } else {
        data->custom_value = 42; /* Default value */
        pr_info("%s: No custom-value found, using default: %u\n", DRIVER_NAME,
                data->custom_value);
    }

    /* Check for presence of a property */
    data->has_clock = of_property_read_bool(np, "lkmpg,has-clock");
    pr_info("%s: has-clock property: %s\n", DRIVER_NAME,
            data->has_clock ? "present" : "absent");

    /* Store device data for later use */
    platform_set_drvdata(pdev, data);

    pr_info("%s: Device probe successful\n", DRIVER_NAME);
    return 0;
}

/* Remove function - called when device is removed */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
static void dt_remove(struct platform_device *pdev)
{
    struct dt_device_data *data = platform_get_drvdata(pdev);

    pr_info("%s: Removing device %s\n", DRIVER_NAME, data->label);
    /* Cleanup is handled automatically by devm_* functions */
}
#else
static int dt_remove(struct platform_device *pdev)
{
    struct dt_device_data *data = platform_get_drvdata(pdev);

    pr_info("%s: Removing device %s\n", DRIVER_NAME, data->label);
    /* Cleanup is handled automatically by devm_* functions */
    return 0;
}
#endif

/* Device tree match table - defines compatible strings this driver supports */
static const struct of_device_id dt_match_table[] = {
    {
        .compatible = "lkmpg,example-device",
    },
    {
        .compatible = "lkmpg,another-device",
    },
    {} /* Sentinel */
};
MODULE_DEVICE_TABLE(of, dt_match_table);

/* Platform driver structure */
static struct platform_driver dt_driver = {
    .probe = dt_probe,
    .remove = dt_remove,
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = dt_match_table,
    },
};

/* Module initialization */
static int __init dt_init(void)
{
    int ret;

    pr_info("%s: Initializing device tree example module\n", DRIVER_NAME);

    /* Register the platform driver */
    ret = platform_driver_register(&dt_driver);
    if (ret) {
        pr_err("%s: Failed to register platform driver\n", DRIVER_NAME);
        return ret;
    }

    pr_info("%s: Module loaded successfully\n", DRIVER_NAME);
    return 0;
}

/* Module cleanup */
static void __exit dt_exit(void)
{
    pr_info("%s: Cleaning up device tree example module\n", DRIVER_NAME);
    platform_driver_unregister(&dt_driver);
}

module_init(dt_init);
module_exit(dt_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Device tree interaction example for LKMPG");


================================================
FILE: examples/dht11.c
================================================
/*
 *  dht11.c - Using GPIO to read temperature and humidity from DHT11 sensor.
 */

#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/version.h>

#include <asm/errno.h>

#define GPIO_PIN_4 575
#define DEVICE_NAME "dht11"
#define DEVICE_CNT 1

static char msg[64];

struct dht11_dev {
    dev_t dev_num;
    int major_num, minor_num;
    struct cdev cdev;
    struct class *cls;
    struct device *dev;
};

static struct dht11_dev dht11_device;

/* Define GPIOs for LEDs.
 * TODO: According to the requirements, search /sys/kernel/debug/gpio to 
 * find the corresponding GPIO location.
 */
static struct gpio dht11[] = { { GPIO_PIN_4, GPIOF_OUT_INIT_HIGH, "Signal" } };

static int dht11_read_data(void)
{
    int timeout;
    uint8_t sensor_data[5] = { 0 };
    uint8_t i, j;

    gpio_set_value(dht11[0].gpio, 0);
    mdelay(20);
    gpio_set_value(dht11[0].gpio, 1);
    udelay(30);
    gpio_direction_input(dht11[0].gpio);
    udelay(2);

    timeout = 300;
    while (gpio_get_value(dht11[0].gpio) && timeout--)
        udelay(1);

    if (timeout == -1)
        return -ETIMEDOUT;

    timeout = 300;
    while (!gpio_get_value(dht11[0].gpio) && timeout--)
        udelay(1);

    if (timeout == -1)
        return -ETIMEDOUT;

    timeout = 300;
    while (gpio_get_value(dht11[0].gpio) && timeout--)
        udelay(1);

    if (timeout == -1)
        return -ETIMEDOUT;

    for (j = 0; j < 5; j++) {
        uint8_t byte = 0;
        for (i = 0; i < 8; i++) {
            timeout = 300;
            while (gpio_get_value(dht11[0].gpio) && timeout--)
                udelay(1);

            if (timeout == -1)
                return -ETIMEDOUT;

            timeout = 300;
            while (!gpio_get_value(dht11[0].gpio) && timeout--)
                udelay(1);

            if (timeout == -1)
                return -ETIMEDOUT;

            udelay(50);
            byte <<= 1;
            if (gpio_get_value(dht11[0].gpio))
                byte |= 0x01;
        }
        sensor_data[j] = byte;
    }

    if (sensor_data[4] != (uint8_t)(sensor_data[0] + sensor_data[1] +
                                    sensor_data[2] + sensor_data[3]))
        return -EIO;

    gpio_direction_output(dht11[0].gpio, 1);
    sprintf(msg, "Humidity: %d%%\nTemperature: %d deg C\n", sensor_data[0],
            sensor_data[2]);

    return 0;
}

static int device_open(struct inode *inode, struct file *file)
{
    int ret, retry;

    for (retry = 0; retry < 5; ++retry) {
        ret = dht11_read_data();
        if (ret == 0)
            return 0;
        msleep(10);
    }
    gpio_direction_output(dht11[0].gpio, 1);

    return ret;
}

static int device_release(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t device_read(struct file *filp, char __user *buffer,
                           size_t length, loff_t *offset)
{
    int msg_len = strlen(msg);

    if (*offset >= msg_len)
        return 0;

    size_t remain = msg_len - *offset;
    size_t bytes_read = min(length, remain);

    if (copy_to_user(buffer, msg + *offset, bytes_read))
        return -EFAULT;

    *offset += bytes_read;

    return bytes_read;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_release,
    .read = device_read,
};

/* Initialize the module - Register the character device */
static int __init dht11_init(void)
{
    int ret = 0;

    /* Determine whether dynamic allocation of the device number is needed. */
    if (dht11_device.major_num) {
        dht11_device.dev_num =
            MKDEV(dht11_device.major_num, dht11_device.minor_num);
        ret = register_chrdev_region(dht11_device.dev_num, DEVICE_CNT,
                                     DEVICE_NAME);
    } else {
        ret = alloc_chrdev_region(&dht11_device.dev_num, 0, DEVICE_CNT,
                                  DEVICE_NAME);
    }

    /* Negative values signify an error */
    if (ret < 0) {
        pr_alert("Failed to register character device, error: %d\n", ret);
        return ret;
    }

    pr_info("Major = %d, Minor = %d\n", MAJOR(dht11_device.dev_num),
            MINOR(dht11_device.dev_num));

    /* Prevents module unloading while operations are in use */
    dht11_device.cdev.owner = THIS_MODULE;

    cdev_init(&dht11_device.cdev, &fops);
    ret = cdev_add(&dht11_device.cdev, dht11_device.dev_num, 1);
    if (ret) {
        pr_err("Failed to add the device to the system\n");
        goto fail1;
    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
    dht11_device.cls = class_create(DEVICE_NAME);
#else
    dht11_device.cls = class_create(THIS_MODULE, DEVICE_NAME);
#endif
    if (IS_ERR(dht11_device.cls)) {
        pr_err("Failed to create class for device\n");
        ret = PTR_ERR(dht11_device.cls);
        goto fail2;
    }

    dht11_device.dev = device_create(dht11_device.cls, NULL,
                                     dht11_device.dev_num, NULL, DEVICE_NAME);
    if (IS_ERR(dht11_device.dev)) {
        pr_err("Failed to create the device file\n");
        ret = PTR_ERR(dht11_device.dev);
        goto fail3;
    }

    pr_info("Device created on /dev/%s\n", DEVICE_NAME);

    ret = gpio_request(dht11[0].gpio, dht11[0].label);

    if (ret) {
        pr_err("Unable to request GPIOs for dht11: %d\n", ret);
        goto fail4;
    }

    ret = gpio_direction_output(dht11[0].gpio, 1);

    if (ret) {
        pr_err("Failed to set GPIO %d direction\n", dht11[0].gpio);
        goto fail5;
    }

    return 0;

fail5:
    gpio_free(dht11[0].gpio);

fail4:
    device_destroy(dht11_device.cls, dht11_device.dev_num);

fail3:
    class_destroy(dht11_device.cls);

fail2:
    cdev_del(&dht11_device.cdev);

fail1:
    unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);

    return ret;
}

static void __exit dht11_exit(void)
{
    gpio_set_value(dht11[0].gpio, 0);
    gpio_free(dht11[0].gpio);

    device_destroy(dht11_device.cls, dht11_device.dev_num);
    class_destroy(dht11_device.cls);
    cdev_del(&dht11_device.cdev);
    unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
}

module_init(dht11_init);
module_exit(dht11_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/dt-overlay.dts
================================================
/*
 * Device Tree Overlay for LKMPG Device Tree Example
 * 
 * This overlay can be compiled and loaded on systems that support
 * runtime device tree overlays (like Raspberry Pi).
 *
 * Compile with:
 *   dtc -@ -I dts -O dtb -o dt-overlay.dtbo dt-overlay.dts
 *
 * Load with (on Raspberry Pi):
 *   sudo dtoverlay dt-overlay.dtbo
 */

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2835";

    fragment@0 {
        target-path = "/";
        __overlay__ {
            lkmpg_device@0 {
                compatible = "lkmpg,example-device";
                reg = <0x40000000 0x1000>;
                label = "LKMPG Test Device";
                lkmpg,custom-value = <100>;
                lkmpg,has-clock;
                status = "okay";
            };

            lkmpg_device@1 {
                compatible = "lkmpg,another-device";
                reg = <0x40001000 0x1000>;
                label = "LKMPG Secondary Device";
                lkmpg,custom-value = <200>;
                /* no has-clock property for this one */
                status = "okay";
            };
        };
    };
};


================================================
FILE: examples/example_atomic.c
================================================
/*
 * example_atomic.c
 */
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/printk.h>

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)                                                   \
    ((byte & 0x80) ? '1' : '0'), ((byte & 0x40) ? '1' : '0'),                  \
        ((byte & 0x20) ? '1' : '0'), ((byte & 0x10) ? '1' : '0'),              \
        ((byte & 0x08) ? '1' : '0'), ((byte & 0x04) ? '1' : '0'),              \
        ((byte & 0x02) ? '1' : '0'), ((byte & 0x01) ? '1' : '0')

static void atomic_add_subtract(void)
{
    atomic_t debbie;
    atomic_t chris = ATOMIC_INIT(50);

    atomic_set(&debbie, 45);

    /* subtract one */
    atomic_dec(&debbie);

    atomic_add(7, &debbie);

    /* add one */
    atomic_inc(&debbie);

    pr_info("chris: %d, debbie: %d\n", atomic_read(&chris),
            atomic_read(&debbie));
}

static void atomic_bitwise(void)
{
    unsigned long word = 0;

    pr_info("Bits 0: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
    set_bit(3, &word);
    set_bit(5, &word);
    pr_info("Bits 1: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
    clear_bit(5, &word);
    pr_info("Bits 2: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
    change_bit(3, &word);

    pr_info("Bits 3: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));
    if (test_and_set_bit(3, &word))
        pr_info("wrong\n");
    pr_info("Bits 4: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word));

    word = 255;
    pr_info("Bits 5: " BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(word));
}

static int __init example_atomic_init(void)
{
    pr_info("example_atomic started\n");

    atomic_add_subtract();
    atomic_bitwise();

    return 0;
}

static void __exit example_atomic_exit(void)
{
    pr_info("example_atomic exit\n");
}

module_init(example_atomic_init);
module_exit(example_atomic_exit);

MODULE_DESCRIPTION("Atomic operations example");
MODULE_LICENSE("GPL");


================================================
FILE: examples/example_mutex.c
================================================
/*
 * example_mutex.c
 */
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/printk.h>

static DEFINE_MUTEX(mymutex);

static int __init example_mutex_init(void)
{
    int ret;

    pr_info("example_mutex init\n");

    ret = mutex_trylock(&mymutex);
    if (ret != 0) {
        pr_info("mutex is locked\n");

        if (mutex_is_locked(&mymutex) == 0)
            pr_info("The mutex failed to lock!\n");

        mutex_unlock(&mymutex);
        pr_info("mutex is unlocked\n");
    } else
        pr_info("Failed to lock\n");

    return 0;
}

static void __exit example_mutex_exit(void)
{
    pr_info("example_mutex exit\n");
}

module_init(example_mutex_init);
module_exit(example_mutex_exit);

MODULE_DESCRIPTION("Mutex example");
MODULE_LICENSE("GPL");


================================================
FILE: examples/example_rwlock.c
================================================
/*
 * example_rwlock.c
 */
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/rwlock.h>

static DEFINE_RWLOCK(myrwlock);

static void example_read_lock(void)
{
    unsigned long flags;

    read_lock_irqsave(&myrwlock, flags);
    pr_info("Read Locked\n");

    /* Read from something */

    read_unlock_irqrestore(&myrwlock, flags);
    pr_info("Read Unlocked\n");
}

static void example_write_lock(void)
{
    unsigned long flags;

    write_lock_irqsave(&myrwlock, flags);
    pr_info("Write Locked\n");

    /* Write to something */

    write_unlock_irqrestore(&myrwlock, flags);
    pr_info("Write Unlocked\n");
}

static int __init example_rwlock_init(void)
{
    pr_info("example_rwlock started\n");

    example_read_lock();
    example_write_lock();

    return 0;
}

static void __exit example_rwlock_exit(void)
{
    pr_info("example_rwlock exit\n");
}

module_init(example_rwlock_init);
module_exit(example_rwlock_exit);

MODULE_DESCRIPTION("Read/Write locks example");
MODULE_LICENSE("GPL");


================================================
FILE: examples/example_spinlock.c
================================================
/*
 * example_spinlock.c
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/spinlock.h>

static DEFINE_SPINLOCK(sl_static);
static spinlock_t sl_dynamic;

static void example_spinlock_static(void)
{
    unsigned long flags;

    spin_lock_irqsave(&sl_static, flags);
    pr_info("Locked static spinlock\n");

    /* Do something or other safely. Because this uses 100% CPU time, this
     * code should take no more than a few milliseconds to run.
     */

    spin_unlock_irqrestore(&sl_static, flags);
    pr_info("Unlocked static spinlock\n");
}

static void example_spinlock_dynamic(void)
{
    unsigned long flags;

    spin_lock_init(&sl_dynamic);
    spin_lock_irqsave(&sl_dynamic, flags);
    pr_info("Locked dynamic spinlock\n");

    /* Do something or other safely. Because this uses 100% CPU time, this
     * code should take no more than a few milliseconds to run.
     */

    spin_unlock_irqrestore(&sl_dynamic, flags);
    pr_info("Unlocked dynamic spinlock\n");
}

static int __init example_spinlock_init(void)
{
    pr_info("example spinlock started\n");

    example_spinlock_static();
    example_spinlock_dynamic();

    return 0;
}

static void __exit example_spinlock_exit(void)
{
    pr_info("example spinlock exit\n");
}

module_init(example_spinlock_init);
module_exit(example_spinlock_exit);

MODULE_DESCRIPTION("Spinlock example");
MODULE_LICENSE("GPL");


================================================
FILE: examples/example_tasklet.c
================================================
/*
 * example_tasklet.c
 */
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/printk.h>

/* Macro DECLARE_TASKLET_OLD exists for compatibility.
 * See https://lwn.net/Articles/830964/
 */
#ifndef DECLARE_TASKLET_OLD
#define DECLARE_TASKLET_OLD(arg1, arg2) DECLARE_TASKLET(arg1, arg2, 0L)
#endif

static void tasklet_fn(unsigned long data)
{
    pr_info("Example tasklet starts\n");
    mdelay(5000);
    pr_info("Example tasklet ends\n");
}

static DECLARE_TASKLET_OLD(mytask, tasklet_fn);

static int __init example_tasklet_init(void)
{
    pr_info("tasklet example init\n");
    tasklet_schedule(&mytask);
    mdelay(200);
    pr_info("Example tasklet init continues...\n");
    return 0;
}

static void __exit example_tasklet_exit(void)
{
    pr_info("tasklet example exit\n");
    tasklet_kill(&mytask);
}

module_init(example_tasklet_init);
module_exit(example_tasklet_exit);

MODULE_DESCRIPTION("Tasklet example");
MODULE_LICENSE("GPL");


================================================
FILE: examples/hello-1.c
================================================
/*
 * hello-1.c - The simplest kernel module.
 */
#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */

int init_module(void)
{
    pr_info("Hello world 1.\n");

    /* A nonzero return means init_module failed; module can't be loaded. */
    return 0;
}

void cleanup_module(void)
{
    pr_info("Goodbye world 1.\n");
}

MODULE_LICENSE("GPL");


================================================
FILE: examples/hello-2.c
================================================
/*
 * hello-2.c - Demonstrating the module_init() and module_exit() macros.
 * This is preferred over using init_module() and cleanup_module().
 */
#include <linux/init.h> /* Needed for the macros */
#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */

static int __init hello_2_init(void)
{
    pr_info("Hello, world 2\n");
    return 0;
}

static void __exit hello_2_exit(void)
{
    pr_info("Goodbye, world 2\n");
}

module_init(hello_2_init);
module_exit(hello_2_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/hello-3.c
================================================
/*
 * hello-3.c - Illustrating the __init, __initdata and __exit macros.
 */
#include <linux/init.h> /* Needed for the macros */
#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */

static int hello3_data __initdata = 3;

static int __init hello_3_init(void)
{
    pr_info("Hello, world %d\n", hello3_data);
    return 0;
}

static void __exit hello_3_exit(void)
{
    pr_info("Goodbye, world 3\n");
}

module_init(hello_3_init);
module_exit(hello_3_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/hello-4.c
================================================
/*
 * hello-4.c - Demonstrates module documentation.
 */
#include <linux/init.h> /* Needed for the macros */
#include <linux/module.h> /* Needed by all modules */
#include <linux/printk.h> /* Needed for pr_info() */

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LKMPG");
MODULE_DESCRIPTION("A sample driver");

static int __init init_hello_4(void)
{
    pr_info("Hello, world 4\n");
    return 0;
}

static void __exit cleanup_hello_4(void)
{
    pr_info("Goodbye, world 4\n");
}

module_init(init_hello_4);
module_exit(cleanup_hello_4);


================================================
FILE: examples/hello-5.c
================================================
/*
 * hello-5.c - Demonstrates command line argument passing to a module.
 */
#include <linux/init.h>
#include <linux/kernel.h> /* for ARRAY_SIZE() */
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/printk.h>
#include <linux/stat.h>

MODULE_LICENSE("GPL");

static short int myshort = 1;
static int myint = 420;
static long int mylong = 9999;
static char *mystring = "blah";
static int myintarray[2] = { 420, 420 };
static int arr_argc = 0;

/* module_param(foo, int, 0000)
 * The first param is the parameter's name.
 * The second param is its data type.
 * The final argument is the permissions bits,
 * for exposing parameters in sysfs (if non-zero) at a later stage.
 */
module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(myshort, "A short integer");
module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(myint, "An integer");
module_param(mylong, long, S_IRUSR);
MODULE_PARM_DESC(mylong, "A long integer");
module_param(mystring, charp, 0000);
MODULE_PARM_DESC(mystring, "A character string");

/* module_param_array(name, type, num, perm);
 * The first param is the parameter's (in this case the array's) name.
 * The second param is the data type of the elements of the array.
 * The third argument is a pointer to the variable that will store the number
 * of elements of the array initialized by the user at module loading time.
 * The fourth argument is the permission bits.
 */
module_param_array(myintarray, int, &arr_argc, 0000);
MODULE_PARM_DESC(myintarray, "An array of integers");

static int __init hello_5_init(void)
{
    int i;

    pr_info("Hello, world 5\n=============\n");
    pr_info("myshort is a short integer: %hd\n", myshort);
    pr_info("myint is an integer: %d\n", myint);
    pr_info("mylong is a long integer: %ld\n", mylong);
    pr_info("mystring is a string: %s\n", mystring);

    for (i = 0; i < ARRAY_SIZE(myintarray); i++)
        pr_info("myintarray[%d] = %d\n", i, myintarray[i]);

    pr_info("got %d arguments for myintarray.\n", arr_argc);
    return 0;
}

static void __exit hello_5_exit(void)
{
    pr_info("Goodbye, world 5\n");
}

module_init(hello_5_init);
module_exit(hello_5_exit);


================================================
FILE: examples/hello-sysfs.c
================================================
/*
 * hello-sysfs.c sysfs example
 */
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/sysfs.h>

static struct kobject *mymodule;

/* the variable you want to be able to change */
static int myvariable = 0;

static ssize_t myvariable_show(struct kobject *kobj,
                               struct kobj_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", myvariable);
}

static ssize_t myvariable_store(struct kobject *kobj,
                                struct kobj_attribute *attr, const char *buf,
                                size_t count)
{
    sscanf(buf, "%d", &myvariable);
    return count;
}

static struct kobj_attribute myvariable_attribute =
    __ATTR(myvariable, 0660, myvariable_show, myvariable_store);

static int __init mymodule_init(void)
{
    int error = 0;

    pr_info("mymodule: initialized\n");

    mymodule = kobject_create_and_add("mymodule", kernel_kobj);
    if (!mymodule)
        return -ENOMEM;

    error = sysfs_create_file(mymodule, &myvariable_attribute.attr);
    if (error) {
        kobject_put(mymodule);
        pr_info("failed to create the myvariable file "
                "in /sys/kernel/mymodule\n");
    }

    return error;
}

static void __exit mymodule_exit(void)
{
    pr_info("mymodule: Exit success\n");
    kobject_put(mymodule);
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/intrpt.c
================================================
/*
 * intrpt.c - Handling GPIO with interrupts
 *
 * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de)
 * from:
 *   https://github.com/wendlers/rpi-kmod-samples
 *
 * Press one button to turn on a LED and another to turn it off.
 */

#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h> /* for ARRAY_SIZE() */
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
#define NO_GPIO_REQUEST_ARRAY
#endif

static int button_irqs[] = { -1, -1 };

/* Define GPIOs for LEDs.
 * TODO: Change the numbers for the GPIO on your board.
 */
static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };

/* Define GPIOs for BUTTONS
 * TODO: Change the numbers for the GPIO on your board.
 */
static struct gpio buttons[] = { { 17, GPIOF_IN, "LED 1 ON BUTTON" },
                                 { 18, GPIOF_IN, "LED 1 OFF BUTTON" } };

/* interrupt function triggered when a button is pressed. */
static irqreturn_t button_isr(int irq, void *data)
{
    /* first button */
    if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio))
        gpio_set_value(leds[0].gpio, 1);
    /* second button */
    else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio))
        gpio_set_value(leds[0].gpio, 0);

    return IRQ_HANDLED;
}

static int __init intrpt_init(void)
{
    int ret = 0;

    pr_info("%s\n", __func__);

    /* register LED gpios */
#ifdef NO_GPIO_REQUEST_ARRAY
    ret = gpio_request(leds[0].gpio, leds[0].label);
#else
    ret = gpio_request_array(leds, ARRAY_SIZE(leds));
#endif

    if (ret) {
        pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
        return ret;
    }

    /* register BUTTON gpios */
#ifdef NO_GPIO_REQUEST_ARRAY
    ret = gpio_request(buttons[0].gpio, buttons[0].label);

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail1;
    }

    ret = gpio_request(buttons[1].gpio, buttons[1].label);

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail2;
    }
#else
    ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));

    if (ret) {
        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
        goto fail1;
    }
#endif

    pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio));

    ret = gpio_to_irq(buttons[0].gpio);

    if (ret < 0) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    button_irqs[0] = ret;

    pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]);

    ret = request_irq(button_irqs[0], button_isr,
                      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                      "gpiomod#button1", NULL);

    if (ret) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    ret = gpio_to_irq(buttons[1].gpio);

    if (ret < 0) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail3;
#else
        goto fail2;
#endif
    }

    button_irqs[1] = ret;

    pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]);

    ret = request_irq(button_irqs[1], button_isr,
                      IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                      "gpiomod#button2", NULL);

    if (ret) {
        pr_err("Unable to request IRQ: %d\n", ret);
#ifdef NO_GPIO_REQUEST_ARRAY
        goto fail4;
#else
        goto fail3;
#endif
    }

    return 0;

/* cleanup what has been setup so far */
#ifdef NO_GPIO_REQUEST_ARRAY
fail4:
    free_irq(button_irqs[0], NULL);

fail3:
    gpio_free(buttons[1].gpio);

fail2:
    gpio_free(buttons[0].gpio);

fail1:
    gpio_free(leds[0].gpio);
#else
fail3:
    free_irq(button_irqs[0], NULL);

fail2:
    gpio_free_array(buttons, ARRAY_SIZE(buttons));

fail1:
    gpio_free_array(leds, ARRAY_SIZE(leds));
#endif

    return ret;
}

static void __exit intrpt_exit(void)
{
    pr_info("%s\n", __func__);

    /* free irqs */
    free_irq(button_irqs[0], NULL);
    free_irq(button_irqs[1], NULL);

    /* turn all LEDs off */
#ifdef NO_GPIO_REQUEST_ARRAY
    gpio_set_value(leds[0].gpio, 0);
#else
    int i;
    for (i = 0; i < ARRAY_SIZE(leds); i++)
        gpio_set_value(leds[i].gpio, 0);
#endif

    /* unregister */
#ifdef NO_GPIO_REQUEST_ARRAY
    gpio_free(leds[0].gpio);
    gpio_free(buttons[0].gpio);
    gpio_free(buttons[1].gpio);
#else
    gpio_free_array(leds, ARRAY_SIZE(leds));
    gpio_free_array(buttons, ARRAY_SIZE(buttons));
#endif
}

module_init(intrpt_init);
module_exit(intrpt_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Handle some GPIO interrupts");


================================================
FILE: examples/ioctl.c
================================================
/*
 * ioctl.c
 */
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>

struct ioctl_arg {
    unsigned int val;
};

/* Documentation/userspace-api/ioctl/ioctl-number.rst */
#define IOC_MAGIC '\x66'

#define IOCTL_VALSET _IOW(IOC_MAGIC, 0, struct ioctl_arg)
#define IOCTL_VALGET _IOR(IOC_MAGIC, 1, struct ioctl_arg)
#define IOCTL_VALGET_NUM _IOR(IOC_MAGIC, 2, int)
#define IOCTL_VALSET_NUM _IO(IOC_MAGIC, 3)

#define IOCTL_VAL_MAXNR 3
#define DRIVER_NAME "ioctltest"

static unsigned int test_ioctl_major = 0;
static unsigned int num_of_dev = 1;
static struct cdev test_ioctl_cdev;
static int ioctl_num = 0;

struct test_ioctl_data {
    unsigned char val;
    rwlock_t lock;
};

static long test_ioctl_ioctl(struct file *filp, unsigned int cmd,
                             unsigned long arg)
{
    struct test_ioctl_data *ioctl_data = filp->private_data;
    int retval = 0;
    unsigned char val;
    struct ioctl_arg data;
    memset(&data, 0, sizeof(data));

    switch (cmd) {
    case IOCTL_VALSET:
        if (copy_from_user(&data, (int __user *)arg, sizeof(data))) {
            retval = -EFAULT;
            goto done;
        }

        pr_alert("IOCTL set val:%x .\n", data.val);
        write_lock(&ioctl_data->lock);
        ioctl_data->val = data.val;
        write_unlock(&ioctl_data->lock);
        break;

    case IOCTL_VALGET:
        read_lock(&ioctl_data->lock);
        val = ioctl_data->val;
        read_unlock(&ioctl_data->lock);
        data.val = val;

        if (copy_to_user((int __user *)arg, &data, sizeof(data))) {
            retval = -EFAULT;
            goto done;
        }

        break;

    case IOCTL_VALGET_NUM:
        retval = __put_user(ioctl_num, (int __user *)arg);
        break;

    case IOCTL_VALSET_NUM:
        ioctl_num = arg;
        break;

    default:
        retval = -ENOTTY;
    }

done:
    return retval;
}

static ssize_t test_ioctl_read(struct file *filp, char __user *buf,
                               size_t count, loff_t *f_pos)
{
    struct test_ioctl_data *ioctl_data = filp->private_data;
    unsigned char val;
    int retval;
    int i = 0;

    read_lock(&ioctl_data->lock);
    val = ioctl_data->val;
    read_unlock(&ioctl_data->lock);

    for (; i < count; i++) {
        if (copy_to_user(&buf[i], &val, 1)) {
            retval = -EFAULT;
            goto out;
        }
    }

    retval = count;
out:
    return retval;
}

static int test_ioctl_close(struct inode *inode, struct file *filp)
{
    pr_alert("%s call.\n", __func__);

    if (filp->private_data) {
        kfree(filp->private_data);
        filp->private_data = NULL;
    }

    return 0;
}

static int test_ioctl_open(struct inode *inode, struct file *filp)
{
    struct test_ioctl_data *ioctl_data;

    pr_alert("%s call.\n", __func__);
    ioctl_data = kmalloc(sizeof(struct test_ioctl_data), GFP_KERNEL);

    if (ioctl_data == NULL)
        return -ENOMEM;

    rwlock_init(&ioctl_data->lock);
    ioctl_data->val = 0xFF;
    filp->private_data = ioctl_data;

    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = test_ioctl_open,
    .release = test_ioctl_close,
    .read = test_ioctl_read,
    .unlocked_ioctl = test_ioctl_ioctl,
};

static int __init ioctl_init(void)
{
    dev_t dev;
    int ret;

    ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME);

    if (ret)
        return ret;

    test_ioctl_major = MAJOR(dev);
    cdev_init(&test_ioctl_cdev, &fops);
    ret = cdev_add(&test_ioctl_cdev, dev, num_of_dev);

    if (ret) {
        unregister_chrdev_region(dev, num_of_dev);
        return ret;
    }

    pr_alert("%s driver(major: %d) installed.\n", DRIVER_NAME,
             test_ioctl_major);
    return 0;
}

static void __exit ioctl_exit(void)
{
    dev_t dev = MKDEV(test_ioctl_major, 0);

    cdev_del(&test_ioctl_cdev);
    unregister_chrdev_region(dev, num_of_dev);
    pr_alert("%s driver removed.\n", DRIVER_NAME);
}

module_init(ioctl_init);
module_exit(ioctl_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("This is test_ioctl module");


================================================
FILE: examples/kbleds.c
================================================
/*
 * kbleds.c - Blink keyboard leds until the module is unloaded.
 */

#include <linux/init.h>
#include <linux/kd.h> /* For KDSETLED */
#include <linux/module.h>
#include <linux/tty.h> /* For tty_struct */
#include <linux/vt.h> /* For MAX_NR_CONSOLES */
#include <linux/vt_kern.h> /* for fg_console */
#include <linux/console_struct.h> /* For vc_cons */

MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs.");

static struct timer_list my_timer;
static struct tty_driver *my_driver;
static unsigned long kbledstatus = 0;

#define BLINK_DELAY HZ / 5
#define ALL_LEDS_ON 0x07
#define RESTORE_LEDS 0xFF

/* Function my_timer_func blinks the keyboard LEDs periodically by invoking
 * command KDSETLED of ioctl() on the keyboard driver. To learn more on virtual
 * terminal ioctl operations, please see file:
 *   drivers/tty/vt/vt_ioctl.c, function vt_ioctl().
 *
 * The argument to KDSETLED is alternatively set to 7 (thus causing the led
 * mode to be set to LED_SHOW_IOCTL, and all the leds are lit) and to 0xFF
 * (any value above 7 switches back the led mode to LED_SHOW_FLAGS, thus
 * the LEDs reflect the actual keyboard status).  To learn more on this,
 * please see file: drivers/tty/vt/keyboard.c, function setledstate().
 */
static void my_timer_func(struct timer_list *unused)
{
    struct tty_struct *t = vc_cons[fg_console].d->port.tty;

    if (kbledstatus == ALL_LEDS_ON)
        kbledstatus = RESTORE_LEDS;
    else
        kbledstatus = ALL_LEDS_ON;

    (my_driver->ops->ioctl)(t, KDSETLED, kbledstatus);

    my_timer.expires = jiffies + BLINK_DELAY;
    add_timer(&my_timer);
}

static int __init kbleds_init(void)
{
    int i;

    pr_info("kbleds: loading\n");
    pr_info("kbleds: fgconsole is %x\n", fg_console);
    for (i = 0; i < MAX_NR_CONSOLES; i++) {
        if (!vc_cons[i].d)
            break;
        pr_info("poet_atkm: console[%i/%i] #%i, tty %p\n", i, MAX_NR_CONSOLES,
                vc_cons[i].d->vc_num, (void *)vc_cons[i].d->port.tty);
    }
    pr_info("kbleds: finished scanning consoles\n");

    my_driver = vc_cons[fg_console].d->port.tty->driver;
    pr_info("kbleds: tty driver name %s\n", my_driver->driver_name);

    /* Set up the LED blink timer the first time. */
    timer_setup(&my_timer, my_timer_func, 0);
    my_timer.expires = jiffies + BLINK_DELAY;
    add_timer(&my_timer);

    return 0;
}

static void __exit kbleds_cleanup(void)
{
    pr_info("kbleds: unloading...\n");
    del_timer(&my_timer);
    (my_driver->ops->ioctl)(vc_cons[fg_console].d->port.tty, KDSETLED,
                            RESTORE_LEDS);
}

module_init(kbleds_init);
module_exit(kbleds_cleanup);

MODULE_LICENSE("GPL");


================================================
FILE: examples/led.c
================================================
/*
 * led.c - Using GPIO to control the LED on/off
 */

#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/version.h>

#include <asm/errno.h>

#define DEVICE_NAME "gpio_led"
#define DEVICE_CNT 1
#define BUF_LEN 2

static char control_signal[BUF_LEN];
static unsigned long device_buffer_size = 0;

struct LED_dev {
    dev_t dev_num;
    int major_num, minor_num;
    struct cdev cdev;
    struct class *cls;
    struct device *dev;
};

static struct LED_dev led_device;

/* Define GPIOs for LEDs.
 * TODO: According to the requirements, search /sys/kernel/debug/gpio to 
 * find the corresponding GPIO location.
 */
static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };

/* This is called whenever a process attempts to open the device file */
static int device_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t device_write(struct file *file, const char __user *buffer,
                            size_t length, loff_t *offset)
{
    device_buffer_size = min(BUF_LEN, length);

    if (copy_from_user(control_signal, buffer, device_buffer_size)) {
        return -EFAULT;
    }

    /* Determine the received signal to decide the LED on/off state. */
    switch (control_signal[0]) {
    case '0':
        gpio_set_value(leds[0].gpio, 0);
        pr_info("LED OFF");
        break;
    case '1':
        gpio_set_value(leds[0].gpio, 1);
        pr_info("LED ON");
        break;
    default:
        pr_warn("Invalid value!\n");
        break;
    }

    *offset += device_buffer_size;

    /* Again, return the number of input characters used. */
    return device_buffer_size;
}

static struct file_operations fops = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
    .owner = THIS_MODULE,
#endif
    .write = device_write,
    .open = device_open,
    .release = device_release,
};

/* Initialize the module - Register the character device */
static int __init led_init(void)
{
    int ret = 0;

    /* Determine whether dynamic allocation of the device number is needed. */
    if (led_device.major_num) {
        led_device.dev_num = MKDEV(led_device.major_num, led_device.minor_num);
        ret =
            register_chrdev_region(led_device.dev_num, DEVICE_CNT, DEVICE_NAME);
    } else {
        ret = alloc_chrdev_region(&led_device.dev_num, 0, DEVICE_CNT,
                                  DEVICE_NAME);
    }

    /* Negative values signify an error */
    if (ret < 0) {
        pr_alert("Failed to register character device, error: %d\n", ret);
        return ret;
    }

    pr_info("Major = %d, Minor = %d\n", MAJOR(led_device.dev_num),
            MINOR(led_device.dev_num));

    /* Prevents module unloading while operations are in use */
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
    led_device.cdev.owner = THIS_MODULE;
#endif

    cdev_init(&led_device.cdev, &fops);
    ret = cdev_add(&led_device.cdev, led_device.dev_num, 1);
    if (ret) {
        pr_err("Failed to add the device to the system\n");
        goto fail1;
    }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
    led_device.cls = class_create(DEVICE_NAME);
#else
    led_device.cls = class_create(THIS_MODULE, DEVICE_NAME);
#endif
    if (IS_ERR(led_device.cls)) {
        pr_err("Failed to create class for device\n");
        ret = PTR_ERR(led_device.cls);
        goto fail2;
    }

    led_device.dev = device_create(led_device.cls, NULL, led_device.dev_num,
                                   NULL, DEVICE_NAME);
    if (IS_ERR(led_device.dev)) {
        pr_err("Failed to create the device file\n");
        ret = PTR_ERR(led_device.dev);
        goto fail3;
    }

    pr_info("Device created on /dev/%s\n", DEVICE_NAME);

    ret = gpio_request(leds[0].gpio, leds[0].label);

    if (ret) {
        pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
        goto fail4;
    }

    ret = gpio_direction_output(leds[0].gpio, leds[0].flags);

    if (ret) {
        pr_err("Failed to set GPIO %d direction\n", leds[0].gpio);
        goto fail5;
    }

    return 0;

fail5:
    gpio_free(leds[0].gpio);

fail4:
    device_destroy(led_device.cls, led_device.dev_num);

fail3:
    class_destroy(led_device.cls);

fail2:
    cdev_del(&led_device.cdev);

fail1:
    unregister_chrdev_region(led_device.dev_num, DEVICE_CNT);

    return ret;
}

static void __exit led_exit(void)
{
    gpio_set_value(leds[0].gpio, 0);
    gpio_free(leds[0].gpio);

    device_destroy(led_device.cls, led_device.dev_num);
    class_destroy(led_device.cls);
    cdev_del(&led_device.cdev);
    unregister_chrdev_region(led_device.dev_num, DEVICE_CNT);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/other/cat_nonblock.c
================================================
/*
 *  cat_nonblock.c - open a file and display its contents, but exit rather than
 *  wait for input.
 */
#include <errno.h> /* for errno */
#include <fcntl.h> /* for open */
#include <stdio.h> /* standard I/O */
#include <stdlib.h> /* for exit */
#include <unistd.h> /* for read */

#define MAX_BYTES 1024 * 4

int main(int argc, char *argv[])
{
    int fd; /* The file descriptor for the file to read */
    size_t bytes; /* The number of bytes read */
    char buffer[MAX_BYTES]; /* The buffer for the bytes */

    /* Usage */
    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        puts("Reads the content of a file, but doesn't wait for input");
        exit(EXIT_FAILURE);
    }

    /* Open the file for reading in non blocking mode */
    fd = open(argv[1], O_RDONLY | O_NONBLOCK);

    /* If open failed */
    if (fd == -1) {
        puts(errno == EAGAIN ? "Open would block" : "Open failed");
        exit(EXIT_FAILURE);
    }

    /* Read the file and output its contents */
    do {
        /* Read characters from the file */
        bytes = read(fd, buffer, MAX_BYTES);

        /* If there's an error, report it and die */
        if (bytes == -1) {
            if (errno == EAGAIN)
                puts("Normally I'd block, but you told me not to");
            else
                puts("Another read error");
            exit(EXIT_FAILURE);
        }

        /* Print the characters */
        if (bytes > 0) {
            for (int i = 0; i < bytes; i++)
                putchar(buffer[i]);
        }

        /* While there are no errors and the file isn't over */
    } while (bytes > 0);

    close(fd);
    return 0;
}


================================================
FILE: examples/other/userspace_ioctl.c
================================================

/*  userspace_ioctl.c - the process to use ioctl's to control the kernel module
 *
 *  Until now we could have used cat for input and output.  But now
 *  we need to do ioctl's, which require writing our own process. 
 */

/* device specifics, such as ioctl numbers and the 
 * major device file. */
#include "../chardev.h"

#include <stdio.h> /* standard I/O */
#include <fcntl.h> /* open */
#include <unistd.h> /* close */
#include <stdlib.h> /* exit */
#include <sys/ioctl.h> /* ioctl */

/* Functions for the ioctl calls */

int ioctl_set_msg(int file_desc, char *message)
{
    int ret_val;

    ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);

    if (ret_val < 0) {
        printf("ioctl_set_msg failed:%d\n", ret_val);
    }

    return ret_val;
}

int ioctl_get_msg(int file_desc)
{
    int ret_val;
    char message[100] = { 0 };

    /* Warning - this is dangerous because we don't tell 
   * the kernel how far it's allowed to write, so it 
   * might overflow the buffer. In a real production 
   * program, we would have used two ioctls - one to tell
   * the kernel the buffer length and another to give 
   * it the buffer to fill
   */
    ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);

    if (ret_val < 0) {
        printf("ioctl_get_msg failed:%d\n", ret_val);
    }
    printf("get_msg message:%s", message);

    return ret_val;
}

int ioctl_get_nth_byte(int file_desc)
{
    int i, c;

    printf("get_nth_byte message:");

    i = 0;
    do {
        c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);

        if (c < 0) {
            printf("\nioctl_get_nth_byte failed at the %d'th byte:\n", i);
            return c;
        }

        putchar(c);
    } while (c != 0);

    return 0;
}

/* Main - Call the ioctl functions */
int main(void)
{
    int file_desc, ret_val;
    char *msg = "Message passed by ioctl\n";

    file_desc = open(DEVICE_PATH, O_RDWR);
    if (file_desc < 0) {
        printf("Can't open device file: %s, error:%d\n", DEVICE_PATH,
               file_desc);
        exit(EXIT_FAILURE);
    }

    ret_val = ioctl_set_msg(file_desc, msg);
    if (ret_val)
        goto error;
    ret_val = ioctl_get_nth_byte(file_desc);
    if (ret_val)
        goto error;
    ret_val = ioctl_get_msg(file_desc);
    if (ret_val)
        goto error;

    close(file_desc);
    return 0;
error:
    close(file_desc);
    exit(EXIT_FAILURE);
}


================================================
FILE: examples/print_string.c
================================================
/*
 * print_string.c - Send output to the tty we're running on, regardless if
 * it is through X11, telnet, etc.  We do this by printing the string to the
 * tty associated with the current task.
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h> /* For current */
#include <linux/tty.h> /* For the tty declarations */

static void print_string(char *str)
{
    /* The tty for the current task */
    struct tty_struct *my_tty = get_current_tty();

    /* If my_tty is NULL, the current task has no tty you can print to (i.e.,
     * if it is a daemon). If so, there is nothing we can do.
     */
    if (my_tty) {
        const struct tty_operations *ttyops = my_tty->driver->ops;
        /* my_tty->driver is a struct which holds the tty's functions,
         * one of which (write) is used to write strings to the tty.
         * It can be used to take a string either from the user's or
         * kernel's memory segment.
         *
         * The function's 1st parameter is the tty to write to, because the
         * same function would normally be used for all tty's of a certain
         * type.
         * The 2nd parameter is a pointer to a string.
         * The 3rd parameter is the length of the string.
         *
         * As you will see below, sometimes it's necessary to use
         * preprocessor stuff to create code that works for different
         * kernel versions. The (naive) approach we've taken here does not
         * scale well. The right way to deal with this is described in
         * section 2 of
         * linux/Documentation/SubmittingPatches
         */
        (ttyops->write)(my_tty, /* The tty itself */
                        str, /* String */
                        strlen(str)); /* Length */

        /* ttys were originally hardware devices, which (usually) strictly
         * followed the ASCII standard. In ASCII, to move to a new line you
         * need two characters, a carriage return and a line feed. On Unix,
         * the ASCII line feed is used for both purposes - so we can not
         * just use \n, because it would not have a carriage return and the
         * next line will start at the column right after the line feed.
         *
         * This is why text files are different between Unix and MS Windows.
         * In CP/M and derivatives, like MS-DOS and MS Windows, the ASCII
         * standard was strictly adhered to, and therefore a newline requires
         * both a LF and a CR.
         */
        (ttyops->write)(my_tty, "\015\012", 2);
    }
}

static int __init print_string_init(void)
{
    print_string("The module has been inserted.  Hello world!");
    return 0;
}

static void __exit print_string_exit(void)
{
    print_string("The module has been removed.  Farewell world!");
}

module_init(print_string_init);
module_exit(print_string_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/procfs1.c
================================================
/*
 * procfs1.c
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#define HAVE_PROC_OPS
#endif

#define procfs_name "helloworld"

static struct proc_dir_entry *our_proc_file;

static ssize_t procfile_read(struct file *file_pointer, char __user *buffer,
                             size_t buffer_length, loff_t *offset)
{
    char s[13] = "HelloWorld!\n";
    int len = sizeof(s);
    ssize_t ret = len;

    if (*offset >= len || copy_to_user(buffer, s, len)) {
        pr_info("copy_to_user failed\n");
        ret = 0;
    } else {
        pr_info("procfile read %s\n", file_pointer->f_path.dentry->d_name.name);
        *offset += len;
    }

    return ret;
}

#ifdef HAVE_PROC_OPS
static const struct proc_ops proc_file_fops = {
    .proc_read = procfile_read,
};
#else
static const struct file_operations proc_file_fops = {
    .read = procfile_read,
};
#endif

static int __init procfs1_init(void)
{
    our_proc_file = proc_create(procfs_name, 0644, NULL, &proc_file_fops);
    if (NULL == our_proc_file) {
        pr_alert("Error:Could not initialize /proc/%s\n", procfs_name);
        return -ENOMEM;
    }

    pr_info("/proc/%s created\n", procfs_name);
    return 0;
}

static void __exit procfs1_exit(void)
{
    proc_remove(our_proc_file);
    pr_info("/proc/%s removed\n", procfs_name);
}

module_init(procfs1_init);
module_exit(procfs1_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/procfs2.c
================================================
/*
 * procfs2.c -  create a "file" in /proc
 */

#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
#include <linux/uaccess.h> /* for copy_from_user */
#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#define HAVE_PROC_OPS
#endif

#define PROCFS_MAX_SIZE 1024
#define PROCFS_NAME "buffer1k"

/* This structure hold information about the /proc file */
static struct proc_dir_entry *our_proc_file;

/* The buffer used to store character for this module */
static char procfs_buffer[PROCFS_MAX_SIZE];

/* The size of the buffer */
static unsigned long procfs_buffer_size = 0;

/* This function is called then the /proc file is read */
static ssize_t procfile_read(struct file *file_pointer, char __user *buffer,
                             size_t buffer_length, loff_t *offset)
{
    char s[13] = "HelloWorld!\n";
    int len = sizeof(s);
    ssize_t ret = len;

    if (*offset >= len || copy_to_user(buffer, s, len)) {
        pr_info("copy_to_user failed\n");
        ret = 0;
    } else {
        pr_info("procfile read %s\n", file_pointer->f_path.dentry->d_name.name);
        *offset += len;
    }

    return ret;
}

/* This function is called with the /proc file is written. */
static ssize_t procfile_write(struct file *file, const char __user *buff,
                              size_t len, loff_t *off)
{
    procfs_buffer_size = len;
    if (procfs_buffer_size >= PROCFS_MAX_SIZE)
        procfs_buffer_size = PROCFS_MAX_SIZE - 1;

    if (copy_from_user(procfs_buffer, buff, procfs_buffer_size))
        return -EFAULT;

    procfs_buffer[procfs_buffer_size] = '\0';
    *off += procfs_buffer_size;
    pr_info("procfile write %s\n", procfs_buffer);

    return procfs_buffer_size;
}

#ifdef HAVE_PROC_OPS
static const struct proc_ops proc_file_fops = {
    .proc_read = procfile_read,
    .proc_write = procfile_write,
};
#else
static const struct file_operations proc_file_fops = {
    .read = procfile_read,
    .write = procfile_write,
};
#endif

static int __init procfs2_init(void)
{
    our_proc_file = proc_create(PROCFS_NAME, 0644, NULL, &proc_file_fops);
    if (NULL == our_proc_file) {
        pr_alert("Error:Could not initialize /proc/%s\n", PROCFS_NAME);
        return -ENOMEM;
    }

    pr_info("/proc/%s created\n", PROCFS_NAME);
    return 0;
}

static void __exit procfs2_exit(void)
{
    proc_remove(our_proc_file);
    pr_info("/proc/%s removed\n", PROCFS_NAME);
}

module_init(procfs2_init);
module_exit(procfs2_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/procfs3.c
================================================
/*
 * procfs3.c
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#include <linux/minmax.h>
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#define HAVE_PROC_OPS
#endif

#define PROCFS_MAX_SIZE 2048UL
#define PROCFS_ENTRY_FILENAME "buffer2k"

static struct proc_dir_entry *our_proc_file;
static char procfs_buffer[PROCFS_MAX_SIZE];
static unsigned long procfs_buffer_size = 0;

static ssize_t procfs_read(struct file *filp, char __user *buffer,
                           size_t length, loff_t *offset)
{
    if (*offset || procfs_buffer_size == 0) {
        pr_debug("procfs_read: END\n");
        *offset = 0;
        return 0;
    }
    procfs_buffer_size = min(procfs_buffer_size, length);
    if (copy_to_user(buffer, procfs_buffer, procfs_buffer_size))
        return -EFAULT;
    *offset += procfs_buffer_size;

    pr_debug("procfs_read: read %lu bytes\n", procfs_buffer_size);
    return procfs_buffer_size;
}
static ssize_t procfs_write(struct file *file, const char __user *buffer,
                            size_t len, loff_t *off)
{
    procfs_buffer_size = min(PROCFS_MAX_SIZE, len);
    if (copy_from_user(procfs_buffer, buffer, procfs_buffer_size))
        return -EFAULT;
    *off += procfs_buffer_size;

    pr_debug("procfs_write: write %lu bytes\n", procfs_buffer_size);
    return procfs_buffer_size;
}
static int procfs_open(struct inode *inode, struct file *file)
{
    return 0;
}
static int procfs_close(struct inode *inode, struct file *file)
{
    return 0;
}

#ifdef HAVE_PROC_OPS
static struct proc_ops file_ops_4_our_proc_file = {
    .proc_read = procfs_read,
    .proc_write = procfs_write,
    .proc_open = procfs_open,
    .proc_release = procfs_close,
};
#else
static const struct file_operations file_ops_4_our_proc_file = {
    .read = procfs_read,
    .write = procfs_write,
    .open = procfs_open,
    .release = procfs_close,
};
#endif

static int __init procfs3_init(void)
{
    our_proc_file = proc_create(PROCFS_ENTRY_FILENAME, 0644, NULL,
                                &file_ops_4_our_proc_file);
    if (our_proc_file == NULL) {
        pr_debug("Error: Could not initialize /proc/%s\n",
                 PROCFS_ENTRY_FILENAME);
        return -ENOMEM;
    }
    proc_set_size(our_proc_file, 80);
    proc_set_user(our_proc_file, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID);

    pr_debug("/proc/%s created\n", PROCFS_ENTRY_FILENAME);
    return 0;
}

static void __exit procfs3_exit(void)
{
    remove_proc_entry(PROCFS_ENTRY_FILENAME, NULL);
    pr_debug("/proc/%s removed\n", PROCFS_ENTRY_FILENAME);
}

module_init(procfs3_init);
module_exit(procfs3_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/procfs4.c
================================================
/*
 * procfs4.c -  create a "file" in /proc
 * This program uses the seq_file library to manage the /proc file.
 */

#include <linux/kernel.h> /* We are doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/proc_fs.h> /* Necessary because we use proc fs */
#include <linux/seq_file.h> /* for seq_file */
#include <linux/version.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#define HAVE_PROC_OPS
#endif

#define PROC_NAME "iter"

/* This function is called at the beginning of a sequence.
 * ie, when:
 *   - the /proc file is read (first time)
 *   - after the function stop (end of sequence)
 */
static void *my_seq_start(struct seq_file *s, loff_t *pos)
{
    static unsigned long counter = 0;

    /* beginning a new sequence? */
    if (*pos == 0) {
        /* yes => return a non null value to begin the sequence */
        return &counter;
    }

    /* no => it is the end of the sequence, return end to stop reading */
    *pos = 0;
    return NULL;
}

/* This function is called after the beginning of a sequence.
 * It is called until the return is NULL (this ends the sequence).
 */
static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
    unsigned long *tmp_v = (unsigned long *)v;
    (*tmp_v)++;
    (*pos)++;
    return NULL;
}

/* This function is called at the end of a sequence. */
static void my_seq_stop(struct seq_file *s, void *v)
{
    /* nothing to do, we use a static value in start() */
}

/* This function is called for each "step" of a sequence. */
static int my_seq_show(struct seq_file *s, void *v)
{
    loff_t *spos = (loff_t *)v;

    seq_printf(s, "%Ld\n", *spos);
    return 0;
}

/* This structure gather "function" to manage the sequence */
static struct seq_operations my_seq_ops = {
    .start = my_seq_start,
    .next = my_seq_next,
    .stop = my_seq_stop,
    .show = my_seq_show,
};

/* This function is called when the /proc file is open. */
static int my_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &my_seq_ops);
};

/* This structure gather "function" that manage the /proc file */
#ifdef HAVE_PROC_OPS
static const struct proc_ops my_file_ops = {
    .proc_open = my_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = seq_release,
};
#else
static const struct file_operations my_file_ops = {
    .open = my_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release,
};
#endif

static int __init procfs4_init(void)
{
    struct proc_dir_entry *entry;

    entry = proc_create(PROC_NAME, 0, NULL, &my_file_ops);
    if (entry == NULL) {
        pr_debug("Error: Could not initialize /proc/%s\n", PROC_NAME);
        return -ENOMEM;
    }

    return 0;
}

static void __exit procfs4_exit(void)
{
    remove_proc_entry(PROC_NAME, NULL);
    pr_debug("/proc/%s removed\n", PROC_NAME);
}

module_init(procfs4_init);
module_exit(procfs4_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/sched.c
================================================
/*
 * sched.c
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>

static struct workqueue_struct *queue = NULL;
static struct work_struct work;

static void work_handler(struct work_struct *data)
{
    pr_info("work handler function.\n");
}

static int __init sched_init(void)
{
    queue = alloc_workqueue("HELLOWORLD", WQ_UNBOUND, 1);
    if (!queue) {
        pr_err("Failed to allocate workqueue\n");
        return -ENOMEM;
    }
    INIT_WORK(&work, work_handler);
    queue_work(queue, &work);
    return 0;
}

static void __exit sched_exit(void)
{
    flush_workqueue(queue);
    destroy_workqueue(queue);
}

module_init(sched_init);
module_exit(sched_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Workqueue example");


================================================
FILE: examples/sleep.c
================================================
/*
 * sleep.c - create a /proc file, and if several processes try to open it
 * at the same time, put all but one to sleep.
 */

#include <linux/atomic.h>
#include <linux/fs.h>
#include <linux/kernel.h> /* for sprintf() */
#include <linux/module.h> /* Specifically, a module */
#include <linux/printk.h>
#include <linux/proc_fs.h> /* Necessary because we use proc fs */
#include <linux/types.h>
#include <linux/uaccess.h> /* for get_user and put_user */
#include <linux/version.h>
#include <linux/wait.h> /* For putting processes to sleep and
                                   waking them up */

#include <asm/current.h>
#include <asm/errno.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
#define HAVE_PROC_OPS
#endif

/* Here we keep the last message received, to prove that we can process our
 * input.
 */
#define MESSAGE_LENGTH 80
static char message[MESSAGE_LENGTH];

static struct proc_dir_entry *our_proc_file;
#define PROC_ENTRY_FILENAME "sleep"

/* Since we use the file operations struct, we can't use the special proc
 * output provisions - we have to use a standard read function, which is this
 * function.
 */
static ssize_t module_output(struct file *file, /* see include/linux/fs.h   */
                             char __user *buf, /* The buffer to put data to
                                                   (in the user segment)    */
                             size_t len, /* The length of the buffer */
                             loff_t *offset)
{
    static int finished = 0;
    int i;
    char output_msg[MESSAGE_LENGTH + 30];

    /* Return 0 to signify end of file - that we have nothing more to say
     * at this point.
     */
    if (finished) {
        finished = 0;
        return 0;
    }

    sprintf(output_msg, "Last input:%s\n", message);
    for (i = 0; i < len && output_msg[i]; i++)
        put_user(output_msg[i], buf + i);

    finished = 1;
    return i; /* Return the number of bytes "read" */
}

/* This function receives input from the user when the user writes to the
 * /proc file.
 */
static ssize_t module_input(struct file *file, /* The file itself */
                            const char __user *buf, /* The buffer with input */
                            size_t length, /* The buffer's length */
                            loff_t *offset) /* offset to file - ignore */
{
    int i;

    /* Put the input into message, where module_output will later be able
     * to use it.
     */
    for (i = 0; i < MESSAGE_LENGTH - 1 && i < length; i++)
        get_user(message[i], buf + i);
    /* we want a standard, zero terminated string */
    message[i] = '\0';

    /* We need to return the number of input characters used */
    return i;
}

/* 1 if the file is currently open by somebody */
static atomic_t already_open = ATOMIC_INIT(0);

/* Queue of processes who want our file */
static DECLARE_WAIT_QUEUE_HEAD(waitq);

/* Called when the /proc file is opened */
static int module_open(struct inode *inode, struct file *file)
{
    /* Try to get without blocking  */
    if (!atomic_cmpxchg(&already_open, 0, 1)) {
        /* Success without blocking, allow the access */
        return 0;
    }
    /* If the file's flags include O_NONBLOCK, it means the process does not
     * want to wait for the file. In this case, because the file is already open,
     * we should fail with -EAGAIN, meaning "you will have to try again",
     * instead of blocking a process which would rather stay awake.
     */
    if (file->f_flags & O_NONBLOCK)
        return -EAGAIN;

    while (atomic_cmpxchg(&already_open, 0, 1)) {
        int i, is_sig = 0;

        /* This function puts the current process, including any system
         * calls, such as us, to sleep.  Execution will be resumed right
         * after the function call, either because somebody called
         * wake_up(&waitq) (only module_close does that, when the file
         * is closed) or when a signal, such as Ctrl-C, is sent
         * to the process
         */
        wait_event_interruptible(waitq, !atomic_read(&already_open));

        /* If we woke up because we got a signal we're not blocking,
         * return -EINTR (fail the system call).  This allows processes
         * to be killed or stopped.
         */
        for (i = 0; i < _NSIG_WORDS && !is_sig; i++)
            is_sig = current->pending.signal.sig[i] & ~current->blocked.sig[i];

        if (is_sig) {
            /* Return -EINTR if we got a signal */
            return -EINTR;
        }
    }

    return 0; /* Allow the access */
}

/* Called when the /proc file is closed */
static int module_close(struct inode *inode, struct file *file)
{
    /* Set already_open to zero, so one of the processes in the waitq will
     * be able to set already_open back to one and to open the file. All
     * the other processes will be called when already_open is back to one,
     * so they'll go back to sleep.
     */
    atomic_set(&already_open, 0);

    /* Wake up all the processes in waitq, so if anybody is waiting for the
     * file, they can have it.
     */
    wake_up(&waitq);

    return 0; /* success */
}

/* Structures to register as the /proc file, with pointers to all the relevant
 * functions.
 */

/* File operations for our /proc file. This is where we place pointers to all
 * the functions called when somebody tries to do something to our file. NULL
 * means we don't want to deal with something.
 */
#ifdef HAVE_PROC_OPS
static const struct proc_ops file_ops_4_our_proc_file = {
    .proc_read = module_output, /* "read" from the file */
    .proc_write = module_input, /* "write" to the file */
    .proc_open = module_open, /* called when the /proc file is opened */
    .proc_release = module_close, /* called when it's closed */
    .proc_lseek = noop_llseek, /* return file->f_pos */
};
#else
static const struct file_operations file_ops_4_our_proc_file = {
    .read = module_output,
    .write = module_input,
    .open = module_open,
    .release = module_close,
    .llseek = noop_llseek,
};
#endif

/* Initialize the module - register the /proc file */
static int __init sleep_init(void)
{
    our_proc_file =
        proc_create(PROC_ENTRY_FILENAME, 0644, NULL, &file_ops_4_our_proc_file);
    if (our_proc_file == NULL) {
        pr_debug("Error: Could not initialize /proc/%s\n", PROC_ENTRY_FILENAME);
        return -ENOMEM;
    }
    proc_set_size(our_proc_file, 80);
    proc_set_user(our_proc_file, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID);

    pr_info("/proc/%s created\n", PROC_ENTRY_FILENAME);

    return 0;
}

/* Cleanup - unregister our file from /proc.  This could get dangerous if
 * there are still processes waiting in waitq, because they are inside our
 * open function, which will get unloaded. I'll explain how to avoid removal
 * of a kernel module in such a case in chapter 10.
 */
static void __exit sleep_exit(void)
{
    remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
    pr_debug("/proc/%s removed\n", PROC_ENTRY_FILENAME);
}

module_init(sleep_init);
module_exit(sleep_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/start.c
================================================
/*
 * start.c - Illustration of multi filed modules
 */

#include <linux/kernel.h> /* We are doing kernel work */
#include <linux/module.h> /* Specifically, a module */

int init_module(void)
{
    pr_info("Hello, world - this is the kernel speaking\n");
    return 0;
}

MODULE_LICENSE("GPL");


================================================
FILE: examples/static_key.c
================================================
/*
 * static_key.c
 */

#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h> /* for sprintf() */
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/uaccess.h> /* for get_user and put_user */
#include <linux/jump_label.h> /* for static key macros */
#include <linux/version.h>

#include <asm/errno.h>

static int device_open(struct inode *inode, struct file *file);
static int device_release(struct inode *inode, struct file *file);
static ssize_t device_read(struct file *file, char __user *buf, size_t count,
                           loff_t *ppos);
static ssize_t device_write(struct file *file, const char __user *buf,
                            size_t count, loff_t *ppos);

#define DEVICE_NAME "key_state"
#define BUF_LEN 10

static int major;

enum {
    CDEV_NOT_USED,
    CDEV_EXCLUSIVE_OPEN,
};

static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED);

static char msg[BUF_LEN + 1];

static struct class *cls;

static DEFINE_STATIC_KEY_FALSE(fkey);

static struct file_operations chardev_fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_release,
    .read = device_read,
    .write = device_write,
};

static int __init chardev_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &chardev_fops);
    if (major < 0) {
        pr_alert("Registering char device failed with %d\n", major);
        return major;
    }

    pr_info("I was assigned major number %d\n", major);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
    cls = class_create(DEVICE_NAME);
#else
    cls = class_create(THIS_MODULE, DEVICE_NAME);
#endif
    if (IS_ERR(cls)) {
        pr_err("Failed to create class for device\n");
        unregister_chrdev(major, DEVICE_NAME);
        return PTR_ERR(cls);
    }
    device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);

    pr_info("Device created on /dev/%s\n", DEVICE_NAME);

    return 0;
}

static void __exit chardev_exit(void)
{
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);

    /* Unregister the device */
    unregister_chrdev(major, DEVICE_NAME);
}

/* Methods */

/**
 * Called when a process tried to open the device file, like
 * cat /dev/key_state
 */
static int device_open(struct inode *inode, struct file *file)
{
    if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN))
        return -EBUSY;

    sprintf(msg, static_key_enabled(&fkey) ? "enabled\n" : "disabled\n");

    pr_info("fastpath 1\n");
    if (static_branch_unlikely(&fkey))
        pr_alert("do unlikely thing\n");
    pr_info("fastpath 2\n");

    return 0;
}

/**
 * Called when a process closes the device file
 */
static int device_release(struct inode *inode, struct file *file)
{
    /* We are now ready for our next caller. */
    atomic_set(&already_open, CDEV_NOT_USED);

    return 0;
}

/**
 * Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
                           char __user *buffer, /* buffer to fill with data */
                           size_t length, /* length of the buffer */
                           loff_t *offset)
{
    /* Number of the bytes actually written to the buffer */
    int bytes_read = 0;
    const char *msg_ptr = msg;

    if (!*(msg_ptr + *offset)) { /* We are at the end of the message */
        *offset = 0; /* reset the offset */
        return 0; /* signify end of file */
    }

    msg_ptr += *offset;

    /* Actually put the data into the buffer */
    while (length && *msg_ptr) {
        /**
         * The buffer is in the user data segment, not the kernel
         * segment so "*" assignment won't work. We have to use
         * put_user which copies data from the kernel data segment to
         * the user data segment.
         */
        put_user(*(msg_ptr++), buffer++);
        length--;
        bytes_read++;
    }

    *offset += bytes_read;

    /* Most read functions return the number of bytes put into the buffer. */
    return bytes_read;
}

/* Called when a process writes to dev file; echo "enable" > /dev/key_state */
static ssize_t device_write(struct file *filp, const char __user *buffer,
                            size_t length, loff_t *offset)
{
    char command[10];

    if (length > 10) {
        pr_err("command exceeded 10 char\n");
        return -EINVAL;
    }

    if (copy_from_user(command, buffer, length))
        return -EFAULT;

    if (strncmp(command, "enable", strlen("enable")) == 0)
        static_branch_enable(&fkey);
    else if (strncmp(command, "disable", strlen("disable")) == 0)
        static_branch_disable(&fkey);
    else {
        pr_err("Invalid command: %s\n", command);
        return -EINVAL;
    }

    /* Again, return the number of input characters used. */
    return length;
}

module_init(chardev_init);
module_exit(chardev_exit);

MODULE_LICENSE("GPL");


================================================
FILE: examples/stop.c
================================================
/*
 * stop.c - Illustration of multi filed modules
 */

#include <linux/kernel.h> /* We are doing kernel work */
#include <linux/module.h> /* Specifically, a module  */

void cleanup_module(void)
{
    pr_info("Short is the life of a kernel module\n");
}

MODULE_LICENSE("GPL");


================================================
FILE: examples/syscall-steal.c
================================================
/*
 * syscall-steal.c
 *
 * System call "stealing" sample.
 *
 * Disables page protection at a processor level by changing the 16th bit
 * in the cr0 register (could be Intel specific).
 */

#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h> /* which will have params */
#include <linux/unistd.h> /* The list of system calls */
#include <linux/cred.h> /* For current_uid() */
#include <linux/uidgid.h> /* For __kuid_val() */
#include <linux/version.h>

/* For the current (process) structure, we need this to know who the
 * current user is.
 */
#include <linux/sched.h>
#include <linux/uaccess.h>

/* The way we access "sys_call_table" varies as kernel internal changes.
 * - Prior to v5.4 : manual symbol lookup
 * - v5.5 to v5.6  : use kallsyms_lookup_name()
 * - v5.7+         : Kprobes or specific kernel module parameter
 */

/* The in-kernel calls to the ksys_close() syscall were removed in Linux v5.11+.
 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0))

#if defined(CONFIG_KPROBES)
#define HAVE_KPROBES 1
#if defined(CONFIG_X86_64)
/* If you have tried to use the syscall table to intercept syscalls and it 
 * doesn't work, you can try to use Kprobes to intercept syscalls.
 * Set USE_KPROBES_PRE_HANDLER_BEFORE_SYSCALL to 1 to register a pre-handler
 * before the syscall.
 */
#define USE_KPROBES_PRE_HANDLER_BEFORE_SYSCALL 0
#endif
#include <linux/kprobes.h>
#else
#define HAVE_PARAM 1
#include <linux/kallsyms.h> /* For sprint_symbol */
/* The address of the sys_call_table, which can be obtained with looking up
 * "/boot/System.map" or "/proc/kallsyms". When the kernel version is v5.7+,
 * without CONFIG_KPROBES, you can input the parameter or the module will look
 * up all the memory.
 */
static unsigned long sym = 0;
module_param(sym, ulong, 0644);
#endif /* CONFIG_KPROBES */

#else

#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0)
#define HAVE_KSYS_CLOSE 1
#include <linux/syscalls.h> /* For ksys_close() */
#else
#include <linux/kallsyms.h> /* For kallsyms_lookup_name */
#endif

#endif /* Version >= v5.7 */

/* UID we want to spy on - will be filled from the command line. */
static uid_t uid = -1;
module_param(uid, int, 0644);

#if USE_KPROBES_PRE_HANDLER_BEFORE_SYSCALL

/* syscall_sym is the symbol name of the syscall to spy on. The default is
 * "__x64_sys_openat", which can be changed by the module parameter. You can 
 * look up the symbol name of a syscall in /proc/kallsyms.
 */
static char *syscall_sym = "__x64_sys_openat";
module_param(syscall_sym, charp, 0644);

static int sys_call_kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
    if (__kuid_val(current_uid()) != uid) {
        return 0;
    }

    pr_info("%s called by %d\n", syscall_sym, uid);
    return 0;
}

static struct kprobe syscall_kprobe = {
    .symbol_name = "__x64_sys_openat",
    .pre_handler = sys_call_kprobe_pre_handler,
};

#else

static unsigned long **sys_call_table_stolen;

/* A pointer to the original system call. The reason we keep this, rather
 * than call the original function (sys_openat), is because somebody else
 * might have replaced the system call before us. Note that this is not
 * 100% safe, because if another module replaced sys_openat before us,
 * then when we are inserted, we will call the function in that module -
 * and it might be removed before we are.
 *
 * Another reason for this is that we can not get sys_openat.
 * It is a static variable, so it is not exported.
 */
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
static asmlinkage long (*original_call)(const struct pt_regs *);
#else
static asmlinkage long (*original_call)(int, const char __user *, int, umode_t);
#endif

/* The function we will replace sys_openat (the function called when you
 * call the open system call) with. To find the exact prototype, with
 * the number and type of arguments, we find the original function first
 * (it is at fs/open.c).
 *
 * In theory, this means that we are tied to the current version of the
 * kernel. In practice, the system calls almost never change (it would
 * wreck havoc and require programs to be recompiled, since the system
 * calls are the interface between the kernel and the processes).
 */
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
static asmlinkage long our_sys_openat(const struct pt_regs *regs)
#else
static asmlinkage long our_sys_openat(int dfd, const char __user *filename,
                                      int flags, umode_t mode)
#endif
{
    int i = 0;
    char ch;

    if (__kuid_val(current_uid()) != uid)
        goto orig_call;

    /* Report the file, if relevant */
    pr_info("Opened file by %d: ", uid);
    do {
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
        get_user(ch, (char __user *)regs->si + i);
#else
        get_user(ch, (char __user *)filename + i);
#endif
        i++;
        pr_info("%c", ch);
    } while (ch != 0);
    pr_info("\n");

orig_call:
    /* Call the original sys_openat - otherwise, we lose the ability to
     * open files.
     */
#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER
    return original_call(regs);
#else
    return original_call(dfd, filename, flags, mode);
#endif
}

static unsigned long **acquire_sys_call_table(void)
{
#ifdef HAVE_KSYS_CLOSE
    unsigned long int offset = PAGE_OFFSET;
    unsigned long **sct;

    while (offset < ULLONG_MAX) {
        sct = (unsigned long **)offset;

        if (sct[__NR_close] == (unsigned long *)ksys_close)
            return sct;

        offset += sizeof(void *);
    }

    return NULL;
#endif

#ifdef HAVE_PARAM
    const char sct_name[15] = "sys_call_table";
    char symbol[40] = { 0 };

    if (sym == 0) {
        pr_alert("For Linux v5.7+, Kprobes is the preferable way to get "
                 "symbol.\n");
        pr_info("If Kprobes is absent, you have to specify the address of "
                "sys_call_table symbol\n");
        pr_info("by /boot/System.map or /proc/kallsyms, which contains all the "
                "symbol addresses, into sym parameter.\n");
        return NULL;
    }
    sprint_symbol(symbol, sym);
    if (!strncmp(sct_name, symbol, sizeof(sct_name) - 1))
        return (unsigned long **)sym;

    return NULL;
#endif

#ifdef HAVE_KPROBES
    unsigned long (*kallsyms_lookup_name)(const char *name);
    struct kprobe kp = {
        .symbol_name = "kallsyms_lookup_name",
    };

    if (register_kprobe(&kp) < 0)
        return NULL;
    kallsyms_lookup_name = (unsigned long (*)(const char *name))kp.addr;
    unregister_kprobe(&kp);
#endif

    return (unsigned long **)kallsyms_lookup_name("sys_call_table");
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
static inline void __write_cr0(unsigned long cr0)
{
    asm volatile("mov %0,%%cr0" : "+r"(cr0) : : "memory");
}
#else
#define __write_cr0 write_cr0
#endif

static void enable_write_protection(void)
{
    unsigned long cr0 = read_cr0();
    set_bit(16, &cr0);
    __write_cr0(cr0);
}

static void disable_write_protection(void)
{
    unsigned long cr0 = read_cr0();
    clear_bit(16, &cr0);
    __write_cr0(cr0);
}
#endif

static int __init syscall_steal_start(void)
{
#if USE_KPROBES_PRE_HANDLER_BEFORE_SYSCALL
    int err;
    /* use symbol name from the module parameter */
    syscall_kprobe.symbol_name = syscall_sym;
    err = register_kprobe(&syscall_kprobe);
    if (err) {
        pr_err("register_kprobe() on %s failed: %d\n", syscall_sym, err);
        pr_err("Please check the symbol name from 'syscall_sym' parameter.\n");
        return err;
    }
#else
    if (!(sys_call_table_stolen = acquire_sys_call_table()))
        return -1;

    disable_write_protection();

    /* keep track of the original open function */
    original_call = (void *)sys_call_table_stolen[__NR_openat];

    /* use our openat function instead */
    sys_call_table_stolen[__NR_openat] = (unsigned long *)our_sys_openat;

    enable_write_protection();
#endif

    pr_info("Spying on UID:%d\n", uid);
    return 0;
}

static void __exit syscall_steal_end(void)
{
#if USE_KPROBES_PRE_HANDLER_BEFORE_SYSCALL
    unregister_kprobe(&syscall_kprobe);
#else
    if (!sys_call_table_stolen)
        return;

    /* Return the system call back to normal */
    if (sys_call_table_stolen[__NR_openat] != (unsigned long *)our_sys_openat) {
        pr_alert("Somebody else also played with the ");
        pr_alert("open system call\n");
        pr_alert("The system may be left in ");
        pr_alert("an unstable state.\n");
    }

    disable_write_protection();
    sys_call_table_stolen[__NR_openat] = (unsigned long *)original_call;
    enable_write_protection();
#endif

    msleep(2000);
}

module_init(syscall_steal_start);
module_exit(syscall_steal_end);

MODULE_LICENSE("GPL");


================================================
FILE: examples/vinput.c
================================================
/*
 * vinput.c
 */

#include <linux/cdev.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/version.h>

#include <asm/uaccess.h>

#include "vinput.h"

#define DRIVER_NAME "vinput"

#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)

static DECLARE_BITMAP(vinput_ids, VINPUT_MINORS);

static LIST_HEAD(vinput_devices);
static LIST_HEAD(vinput_vdevices);

static int vinput_dev;
static struct spinlock vinput_lock;
static struct class vinput_class;

/* Search the name of vinput device in the vinput_devices linked list,
 * which added at vinput_register().
 */
static struct vinput_device *vinput_get_device_by_type(const char *type)
{
    int found = 0;
    struct vinput_device *vinput;
    struct list_head *curr;

    spin_lock(&vinput_lock);
    list_for_each (curr, &vinput_devices) {
        vinput = list_entry(curr, struct vinput_device, list);
        if (vinput && strncmp(type, vinput->name, strlen(vinput->name)) == 0) {
            found = 1;
            break;
        }
    }
    spin_unlock(&vinput_lock);

    if (found)
        return vinput;
    return ERR_PTR(-ENODEV);
}

/* Search the id of virtual device in the vinput_vdevices linked list,
 * which added at vinput_alloc_vdevice().
 */
static struct vinput *vinput_get_vdevice_by_id(long id)
{
    struct vinput *vinput = NULL;
    struct list_head *curr;

    spin_lock(&vinput_lock);
    list_for_each (curr, &vinput_vdevices) {
        vinput = list_entry(curr, struct vinput, list);
        if (vinput && vinput->id == id)
            break;
    }
    spin_unlock(&vinput_lock);

    if (vinput && vinput->id == id)
        return vinput;
    return ERR_PTR(-ENODEV);
}

static int vinput_open(struct inode *inode, struct file *file)
{
    int err = 0;
    struct vinput *vinput = NULL;

    vinput = vinput_get_vdevice_by_id(iminor(inode));

    if (IS_ERR(vinput))
        err = PTR_ERR(vinput);
    else
        file->private_data = vinput;

    return err;
}

static int vinput_release(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t vinput_read(struct file *file, char __user *buffer, size_t count,
                           loff_t *offset)
{
    int len;
    char buff[VINPUT_MAX_LEN + 1];
    struct vinput *vinput = file->private_data;

    len = vinput->type->ops->read(vinput, buff, count);

    if (*offset > len)
        count = 0;
    else if (count + *offset > VINPUT_MAX_LEN)
        count = len - *offset;

    if (raw_copy_to_user(buffer, buff + *offset, count))
        return -EFAULT;

    *offset += count;

    return count;
}

static ssize_t vinput_write(struct file *file, const char __user *buffer,
                            size_t count, loff_t *offset)
{
    char buff[VINPUT_MAX_LEN + 1];
    struct vinput *vinput = file->private_data;

    memset(buff, 0, sizeof(char) * (VINPUT_MAX_LEN + 1));

    if (count > VINPUT_MAX_LEN) {
        dev_warn(&vinput->dev, "Too long. %d bytes allowed\n", VINPUT_MAX_LEN);
        return -EINVAL;
    }

    if (raw_copy_from_user(buff, buffer, count))
        return -EFAULT;

    return vinput->type->ops->send(vinput, buff, count);
}

static const struct file_operations vinput_fops = {
    .owner = THIS_MODULE,
    .open = vinput_open,
    .release = vinput_release,
    .read = vinput_read,
    .write = vinput_write,
};

static void vinput_unregister_vdevice(struct vinput *vinput)
{
    input_unregister_device(vinput->input);
    if (vinput->type->ops->kill)
        vinput->type->ops->kill(vinput);
}

static void vinput_destroy_vdevice(struct vinput *vinput)
{
    /* Remove from the list first */
    spin_lock(&vinput_lock);
    list_del(&vinput->list);
    clear_bit(vinput->id, vinput_ids);
    spin_unlock(&vinput_lock);

    kfree(vinput);
}

static void vinput_release_dev(struct device *dev)
{
    struct vinput *vinput = dev_to_vinput(dev);
    int id = vinput->id;

    vinput_destroy_vdevice(vinput);

    pr_debug("released vinput%d.\n", id);
}

static struct vinput *vinput_alloc_vdevice(void)
{
    int err;
    struct vinput *vinput = kzalloc(sizeof(struct vinput), GFP_KERNEL);

    if (!vinput) {
        pr_err("vinput: Cannot allocate vinput input device\n");
        return ERR_PTR(-ENOMEM);
    }

    spin_lock_init(&vinput->lock);

    spin_lock(&vinput_lock);
    vinput->id = find_first_zero_bit(vinput_ids, VINPUT_MINORS);
    if (vinput->id >= VINPUT_MINORS) {
        err = -ENOBUFS;
        goto fail_id;
    }
    set_bit(vinput->id, vinput_ids);
    list_add(&vinput->list, &vinput_vdevices);
    spin_unlock(&vinput_lock);

    /* allocate the input device */
    vinput->input = input_allocate_device();
    if (vinput->input == NULL) {
        pr_err("vinput: Cannot allocate vinput input device\n");
        err = -ENOMEM;
        goto fail_input_dev;
    }

    /* initialize device */
    vinput->dev.class = &vinput_class;
    vinput->dev.release = vinput_release_dev;
    vinput->dev.devt = MKDEV(vinput_dev, vinput->id);
    dev_set_name(&vinput->dev, DRIVER_NAME "%lu", vinput->id);

    return vinput;

fail_input_dev:
    spin_lock(&vinput_lock);
    list_del(&vinput->list);
fail_id:
    spin_unlock(&vinput_lock);
    kfree(vinput);

    return ERR_PTR(err);
}

static int vinput_register_vdevice(struct vinput *vinput)
{
    int err = 0;

    /* register the input device */
    vinput->input->name = vinput->type->name;
    vinput->input->phys = "vinput";
    vinput->input->dev.parent = &vinput->dev;

    vinput->input->id.bustype = BUS_VIRTUAL;
    vinput->input->id.product = 0x0000;
    vinput->input->id.vendor = 0x0000;
    vinput->input->id.version = 0x0000;

    err = vinput->type->ops->init(vinput);

    if (err == 0)
        dev_info(&vinput->dev, "Registered virtual input %s %ld\n",
                 vinput->type->name, vinput->id);

    return err;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
static ssize_t export_store(const struct class *class,
                            const struct class_attribute *attr,
#else
static ssize_t export_store(struct class *class, struct class_attribute *attr,
#endif
                            const char *buf, size_t len)
{
    int err;
    struct vinput *vinput;
    struct vinput_device *device;

    device = vinput_get_device_by_type(buf);
    if (IS_ERR(device)) {
        pr_info("vinput: This virtual device isn't registered\n");
        err = PTR_ERR(device);
        goto fail;
    }

    vinput = vinput_alloc_vdevice();
    if (IS_ERR(vinput)) {
        err = PTR_ERR(vinput);
        goto fail;
    }

    vinput->type = device;
    err = device_register(&vinput->dev);
    if (err < 0)
        goto fail_register;

    err = vinput_register_vdevice(vinput);
    if (err < 0)
        goto fail_register_vinput;

    return len;

fail_register_vinput:
    input_free_device(vinput->input);
    device_unregister(&vinput->dev);
    /* avoid calling vinput_destroy_vdevice() twice */
    return err;
fail_register:
    input_free_device(vinput->input);
    vinput_destroy_vdevice(vinput);
fail:
    return err;
}
/* This macro generates class_attr_export structure and export_store() */
static CLASS_ATTR_WO(export);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
static ssize_t unexport_store(const struct class *class,
                              const struct class_attribute *attr,
#else
static ssize_t unexport_store(struct class *class, struct class_attribute *attr,
#endif
                              const char *buf, size_t len)
{
    int err;
    unsigned long id;
    struct vinput *vinput;

    err = kstrtol(buf, 10, &id);
    if (err) {
        err = -EINVAL;
        goto failed;
    }

    vinput = vinput_get_vdevice_by_id(id);
    if (IS_ERR(vinput)) {
        pr_err("vinput: No such vinput device %ld\n", id);
        err = PTR_ERR(vinput);
        goto failed;
    }

    vinput_unregister_vdevice(vinput);
    device_unregister(&vinput->dev);

    return len;
failed:
    return err;
}
/* This macro generates class_attr_unexport structure and unexport_store() */
static CLASS_ATTR_WO(unexport);

static struct attribute *vinput_class_attrs[] = {
    &class_attr_export.attr,
    &class_attr_unexport.attr,
    NULL,
};

/* This macro generates vinput_class_groups structure */
ATTRIBUTE_GROUPS(vinput_class);

static struct class vinput_class = {
    .name = "vinput",
/* .owner was removed in Linux v6.4 via upstream commit 6e30a66433af ("driver core: class: remove
 * struct module owner out of struct class")
 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
    .owner = THIS_MODULE,
#endif
    .class_groups = vinput_class_groups,
};

int vinput_register(struct vinput_device *dev)
{
    spin_lock(&vinput_lock);
    list_add(&dev->list, &vinput_devices);
    spin_unlock(&vinput_lock);

    pr_info("vinput: registered new virtual input device '%s'\n", dev->name);

    return 0;
}
EXPORT_SYMBOL(vinput_register);

void vinput_unregister(struct vinput_device *dev)
{
    struct list_head *curr, *next;

    /* Remove from the list first */
    spin_lock(&vinput_lock);
    list_del(&dev->list);
    spin_unlock(&vinput_lock);

    /* unregister all devices of this type */
    list_for_each_safe (curr, next, &vinput_vdevices) {
        struct vinput *vinput = list_entry(curr, struct vinput, list);
        if (vinput && vinput->type == dev) {
            vinput_unregister_vdevice(vinput);
            device_unregister(&vinput->dev);
        }
    }

    pr_info("vinput: unregistered virtual input device '%s'\n", dev->name);
}
EXPORT_SYMBOL(vinput_unregister);

static int __init vinput_init(void)
{
    int err = 0;

    pr_info("vinput: Loading virtual input driver\n");

    vinput_dev = register_chrdev(0, DRIVER_NAME, &vinput_fops);
    if (vinput_dev < 0) {
        pr_err("vinput: Unable to allocate char dev region\n");
        err = vinput_dev;
        goto failed_alloc;
    }

    spin_lock_init(&vinput_lock);

    err = class_register(&vinput_class);
    if (err < 0) {
        pr_err("vinput: Unable to register vinput class\n");
        goto failed_class;
    }

    return 0;
failed_class:
    unregister_chrdev(vinput_dev, DRIVER_NAME);
failed_alloc:
    return err;
}

static void __exit vinput_end(void)
{
    pr_info("vinput: Unloading virtual input driver\n");

    unregister_chrdev(vinput_dev, DRIVER_NAME);
    class_unregister(&vinput_class);
}

module_init(vinput_init);
module_exit(vinput_end);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Emulate input events");


================================================
FILE: examples/vinput.h
================================================
/*
 * vinput.h
 */

#ifndef VINPUT_H
#define VINPUT_H

#include <linux/input.h>
#include <linux/spinlock.h>

#define VINPUT_MAX_LEN 128
#define MAX_VINPUT 32
#define VINPUT_MINORS MAX_VINPUT

#define dev_to_vinput(dev) container_of(dev, struct vinput, dev)

struct vinput_device;

struct vinput {
    long id;
    long devno;
    long last_entry;
    spinlock_t lock;

    void *priv_data;

    struct device dev;
    struct list_head list;
    struct input_dev *input;
    struct vinput_device *type;
};

struct vinput_ops {
    int (*init)(struct vinput *);
    int (*kill)(struct vinput *);
    int (*send)(struct vinput *, char *, int);
    int (*read)(struct vinput *, char *, int);
};

struct vinput_device {
    char name[16];
    struct list_head list;
    struct vinput_ops *ops;
};

int vinput_register(struct vinput_device *dev);
void vinput_unregister(struct vinput_device *dev);

#endif


================================================
FILE: examples/vkbd.c
================================================
/*
 * vkbd.c
 */

#include <linux/init.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/spinlock.h>

#include "vinput.h"

#define VINPUT_KBD "vkbd"
#define VINPUT_RELEASE 0
#define VINPUT_PRESS 1

static unsigned short vkeymap[KEY_MAX];

static int vinput_vkbd_init(struct vinput *vinput)
{
    int i;

    /* Set up the input bitfield */
    vinput->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
    vinput->input->keycodesize = sizeof(unsigned short);
    vinput->input->keycodemax = KEY_MAX;
    vinput->input->keycode = vkeymap;

    for (i = 0; i < KEY_MAX; i++)
        set_bit(vkeymap[i], vinput->input->keybit);

    /* vinput will help us allocate new input device structure via
     * input_allocate_device(). So, we can register it straightforwardly.
     */
    return input_register_device(vinput->input);
}

static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
{
    spin_lock(&vinput->lock);
    len = snprintf(buff, len, "%+ld\n", vinput->last_entry);
    spin_unlock(&vinput->lock);

    return len;
}

static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
{
    int ret;
    long key = 0;
    short type = VINPUT_PRESS;

    /* Determine which event was received (press or release)
     * and store the state.
     */
    if (buff[0] == '+')
        ret = kstrtol(buff + 1, 10, &key);
    else
        ret = kstrtol(buff, 10, &key);
    if (ret)
        dev_err(&vinput->dev, "error during kstrtol: -%d\n", ret);
    spin_lock(&vinput->lock);
    vinput->last_entry = key;
    spin_unlock(&vinput->lock);

    if (key < 0) {
        type = VINPUT_RELEASE;
        key = -key;
    }

    dev_info(&vinput->dev, "Event %s code %ld\n",
             (type == VINPUT_RELEASE) ? "VINPUT_RELEASE" : "VINPUT_PRESS", key);

    /* Report the state received to input subsystem. */
    input_report_key(vinput->input, key, type);
    /* Tell input subsystem that it finished the report. */
    input_sync(vinput->input);

    return len;
}

static struct vinput_ops vkbd_ops = {
    .init = vinput_vkbd_init,
    .send = vinput_vkbd_send,
    .read = vinput_vkbd_read,
};

static struct vinput_device vkbd_dev = {
    .name = VINPUT_KBD,
    .ops = &vkbd_ops,
};

static int __init vkbd_init(void)
{
    int i;

    for (i = 0; i < KEY_MAX; i++)
        vkeymap[i] = i;
    return vinput_register(&vkbd_dev);
}

static void __exit vkbd_end(void)
{
    vinput_unregister(&vkbd_dev);
}

module_init(vkbd_init);
module_exit(vkbd_end);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Emulate keyboard input events through /dev/vinput");


================================================
FILE: html.cfg
================================================
\Preamble{xhtml}

\Configure{tableofcontents*}{chapter,section,subsection}

\Css{html {
    width: 100vw;
    overflow-x: hidden;
}}

\Css{@font-face {
  font-family: Manrope;
  src: url(Manrope_variable.ttf);
}}

\Css{body {
    max-width: 55rem;
    box-sizing: border-box;
    padding: 1rem;
    margin: 0 auto;
    overflow-x: hidden;
    background-color: \#F4ECD8;
    color: \#5B464B;
    line-height: 1.5;
}}

\Css{a {
color: \#0060DF;
}}

\Css{p, a {
font-size: 1.2rem;
font-family: Manrope;
}}

\Css{p + pre {
font-size: 1.1em;
}}

\Css{div.author {
    white-space: normal;
}}

\Css{img.math {
    height: 1rem;
    vertical-align: top;
}}

\Css{pre.fancyvrb, {
    white-space: pre;
}}

\Css{figure, .fancyvrb, .verbatim {
    margin-inline: 0;
    overflow-x: auto;
}}

\Css{.ecrm-0500 {
    font-size: 70\%;
    font-style: italic;
    color: gray;
    width: 1.5rem;
    display: inline-block;
    -webkit-user-select: none;
    -moz-user-select: none;
    -o-user-select: none;
    user-select: none;
}}

\Css{.flushright:first-child {
    position:absolute;
    top: 10px;
    right: 50px;
}}

\Css{.right {
    text-align: right;
}}

\AtBeginDocument{%
\Configure{@HEAD}{\HCode{
    <script async defer src="https://buttons.github.io/buttons.js"></script>
    <div class="right">
        <a class="github-button" href="https://github.com/sysprog21/lkmpg" data-size="large" aria-label="View on GitHub">View on GitHub</a>
        <a class="github-button" href="https://github.com/sysprog21/lkmpg/releases/download/latest/lkmpg.pdf" data-icon="octicon-download" data-size="large" aria-label="Download PDF document">Download PDF document</a>
        <br><br>
    </div>
    \Hnewline}}
}

\begin{document}
\EndPreamble


================================================
FILE: lib/codeblock.tex
================================================
\newminted[code]{c}{frame=single,
  framesep=2mm,
  baselinestretch=1,
  fontsize=\footnotesize,
  breaklines,
  breakafter=d,
  linenos
}

\usemintedstyle{vs}

\NewDocumentCommand{\samplec}{oom}{%
  \IfNoValueTF{#1}%
  {%
    \inputminted[frame=single, framesep=2mm, baselinestretch=1, fontsize=\footnotesize, breaklines, breakafter=d, linenos]{c}{#3}%
  }%
  {%
    \IfNoValueTF{#2}%
    {%
      \inputminted[frame=single, framesep=2mm, baselinestretch=1, fontsize=\footnotesize, breaklines, breakafter=d, firstline=#1, linenos]{c}{#3}%
    }%
    {%
      \inputminted[frame=single, framesep=2mm, baselinestretch=1, fontsize=\footnotesize, breaklines, breakafter=d, firstline=#1, lastline=#2, linenos]{c}{#3}%
    }%
  }%
}

\newminted[codebash]{bash}{frame=single,
  framesep=2mm,
  baselinestretch=1.2,
  numbersep=8pt,
  breaklines,
  linenos
}

\newmintinline[sh]{bash}{}
\newmintinline[cpp]{c}{}


================================================
FILE: lib/kernelsrc.tex
================================================
\newcommand*{\src}[2][]{\href{https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/#2}%
                             {\ifthenelse{\equal{#1}{}}{#2}{#1}}}


================================================
FILE: lkmpg.tex
================================================
\documentclass[10pt, oneside]{book}
\usepackage[Bjornstrup]{fncychap}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{graphicx}
\usepackage{fancyhdr}
\usepackage{xparse}
\usepackage{ifthen}
\usepackage{pdfpages}

% tikz settings
\usepackage{tikz}
\usetikzlibrary{shapes.geometric, arrows, shadows, decorations.text}
\tikzstyle{startstop} = [rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30, drop shadow]
\tikzstyle{io} = [trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30]
\tikzstyle{process} = [rectangle, minimum width=3cm, minimum height=1cm, text centered, text width=3cm, draw=black, fill=orange!30]
\tikzstyle{decision} = [diamond, minimum width=1cm, minimum height=1cm, text centered, draw=black, fill=green!30]
\tikzstyle{arrow} = [thick,->,>=stealth]
\tikzstyle{line} = [draw, -latex']

% packages for code
\usepackage{verbatim}
\usepackage{minted}

% citation
\usepackage{cite}
\usepackage[colorlinks,citecolor=green]{hyperref}
\usepackage{cleveref}

\usepackage{xcolor}
\hypersetup{
  colorlinks,
  linkcolor = {red!50!black},
  citecolor = {blue!50!black},
  urlcolor = {blue!80!black}
}

\input{lib/codeblock}
\input{lib/kernelsrc}

% FIXME: classify with chapters instead of sections
\renewcommand{\thesection}{\arabic{section}}

\author{Peter Jay Salzman, Michael Burian, Ori Pomerantz, Bob Mottram, Jim Huang}
\date{\today}
\title{The Linux Kernel Module Programming Guide}
\begin{document}

\maketitle
\ifdefined\HCode
\includegraphics{assets/cover-with-names.png}
% turn off TOC
\else
\pagestyle{empty}
\begin{tikzpicture}[remember picture, overlay]
  \node at (current page.center) {\includegraphics[width=\paperwidth, height=\paperheight]{assets/cover.png}}; \\
  \node at (11, -9.5) {\Large \textbf{Peter Jay Salzman, Michael Burian,}}; \\
  \node at (11, -10.5) {\Large \textbf{Ori Pomerantz, Bob Mottram,}}; \\
  \node at (11, -11.5) {\Large \textbf{Jim Huang}};
\end{tikzpicture}
\tableofcontents
\fi

\section{Introduction}
\label{sec:introduction}
The Linux Kernel Module Programming Guide is a free book; you may reproduce or modify it under the terms of the \href{https://opensource.org/licenses/OSL-3.0}{Open Software License}, version 3.0.

This book is distributed in the hope that it would be useful, but without any warranty,
without even the implied warranty of merchantability or fitness for a particular purpose.

The author encourages wide distribution of this book for personal or commercial use,
provided the above copyright notice remains intact and the method adheres to the provisions of the \href{https://opensource.org/licenses/OSL-3.0}{Open Software License}.
In summary, you may copy and distribute this book free of charge or for a profit.
No explicit permission is required from the author for reproduction of this book in any medium, physical or electronic.

Derivative works and translations of this document must be placed under the Open Software License, and the original copyright notice must remain intact.
If you have contributed new material to this book, you must make the material and source code available for your revisions.
Please make revisions and updates available directly to the document maintainer, Jim Huang <jserv@ccns.ncku.edu.tw>.
This will allow for the merging of updates and provide consistent revisions to the Linux community.

If you publish or distribute this book commercially, donations, royalties,
or printed copies are greatly appreciated by the author and the \href{https://tldp.org/}{Linux Documentation Project} (LDP).
Contributing in this way shows your support for free software and the LDP.
If you have questions or comments, please contact the address above.

\subsection{Authorship}
\label{sec:authorship}

The Linux Kernel Module Programming Guide was initially authored by Ori Pomerantz for Linux v2.2.
As the Linux kernel evolved, Ori's availability to maintain the document diminished.
Consequently, Peter Jay Salzman assumed the role of maintainer and updated the guide for Linux v2.4.
Similar constraints arose for Peter when tracking developments in Linux v2.6,
leading to Michael Burian joining as a co-maintainer to bring the guide up to speed with Linux v2.6.
Bob Mottram contributed to the guide by updating examples for Linux v3.8 and later.
Jim Huang then undertook the task of updating the guide for recent Linux versions (v5.0 and beyond),
along with revising the LaTeX document.
The guide continues to be maintained for compatibility with modern kernels (v6.x series) while ensuring examples work with older LTS kernels.

\subsection{Acknowledgements}
\label{sec:acknowledgements}

The following people have contributed corrections or good suggestions:

\begin{flushleft}
\input{contrib}
\end{flushleft}

\subsection{What Is A Kernel Module?}
\label{sec:kernelmod}

Involvement in the development of Linux kernel modules requires a foundation in the C programming language and a track record of creating conventional programs intended for process execution.
This pursuit delves into a domain where an unregulated pointer, if disregarded,
may potentially trigger the total elimination of an entire filesystem,
resulting in a scenario that necessitates a complete system reboot.

A Linux kernel module is precisely defined as a code segment capable of dynamic loading and unloading within the kernel as needed.
These modules enhance kernel capabilities without necessitating a system reboot.
A notable example is seen in the device driver module, which facilitates kernel interaction with hardware components linked to the system.
In the absence of modules, the prevailing approach leans toward monolithic kernels,
requiring direct integration of new functionalities into the kernel image.
This approach leads to larger kernels and necessitates kernel rebuilding and subsequent system rebooting when new functionalities are desired.

\subsection{Kernel module package}
\label{sec:packages}

Linux distributions provide the commands \sh|modprobe|, \sh|insmod| and \sh|depmod| within a package.

On Ubuntu/Debian GNU/Linux:
\begin{codebash}
sudo apt-get install build-essential kmod
\end{codebash}

On Arch Linux:
\begin{codebash}
sudo pacman -S gcc kmod
\end{codebash}

\subsection{What Modules are in my Kernel?}
\label{sec:modutils}

To discover what modules are already loaded within your current kernel, use the command \sh|lsmod|.
\begin{codebash}
lsmod
\end{codebash}

Modules are stored within the file \verb|/proc/modules|, so you can also see them with:
\begin{codebash}
cat /proc/modules
\end{codebash}

This can be a long list, and you might prefer to search for something particular.
To search for the \verb|fat| module:
\begin{codebash}
lsmod | grep fat
\end{codebash}

\subsection{Is there a need to download and compile the kernel?}
\label{sec:buildkernel}
To effectively follow this guide, there is no obligatory requirement for performing such actions.
Nonetheless, a prudent approach involves executing the examples within a test distribution on a virtual machine,
thus mitigating any potential risk of disrupting the system.

\subsection{Before We Begin}
\label{sec:preparation}
Before delving into code, certain matters require attention.
Variances exist among individuals' systems, and distinct personal approaches are evident.
The achievement of successful compilation and loading of the inaugural ``hello world'' program may,
at times, present challenges.
It is reassuring to note that overcoming the initial obstacle on the first attempt paves the way for subsequent endeavors to proceed seamlessly.

\begin{enumerate}
  \item Modversioning.
        A module compiled for one kernel will not load if a different kernel is booted,
        unless \cpp|CONFIG_MODVERSIONS| is enabled in the kernel.
        Module versioning will be discussed later in this guide.
        Until module versioning is covered, the examples in this guide may not work correctly if running a kernel with modversioning turned on.
        However, most stock Linux distribution kernels come with modversioning enabled.
        If difficulties arise when loading the modules due to versioning errors, consider compiling a kernel with modversioning turned off.

  \item Using the X Window System.
        It is highly recommended to extract, compile, and load all the examples discussed in this guide from a console.
        Working on these tasks within the X Window System is discouraged.

        Modules cannot directly print to the screen like \cpp|printf()| can,
        but they can log information and warnings to the kernel's log ring buffer.
        This output is \emph{not} automatically displayed on any console or terminal.
        To view kernel module messages, you must use \sh|dmesg| to read the kernel log ring buffer,
        or check the systemd journal with \sh|journalctl -k| for kernel messages.
        Refer to \Cref{sec:helloworld} for more information.
        The terminal or environment from which you load the module does not affect where the output goes—it always goes to the kernel log.
  \item SecureBoot.
        Numerous modern computers arrive pre-configured with UEFI SecureBoot enabled—an essential security standard ensuring booting exclusively through trusted software endorsed by the original equipment manufacturer.
        Certain Linux distributions even ship with the default Linux kernel configured to support SecureBoot.
        In these cases, the kernel module necessitates a signed security key.

        Failing that, an attempt to insert your first ``hello world'' module would result in the message: ``\emph{ERROR: could not insert module}''.
        If this message ``\emph{Lockdown: insmod: unsigned module loading is restricted;
        see man kernel lockdown.7}'' appears in the \sh|dmesg| output,
        the simplest approach involves disabling UEFI SecureBoot from the boot menu of your PC or laptop,
        allowing the successful insertion of the ``hello world'' module.
        Naturally, an alternative involves undergoing intricate procedures such as generating keys, system key installation,
        and module signing to achieve functionality.
        However, this intricate process is less appropriate for beginners. If interested,
        more detailed steps for \href{https://wiki.debian.org/SecureBoot}{SecureBoot} can be explored and followed.
\end{enumerate}

\section{Headers}
\label{sec:headers}
Before building anything, it is necessary to install the header files for the kernel.

On Ubuntu/Debian GNU/Linux:
\begin{codebash}
sudo apt-get update
apt-cache search linux-headers-`uname -r`
\end{codebash}

The following command provides information about the available kernel header files.
Then, for example:
\begin{codebash}
sudo apt-get install linux-headers-`uname -r`
\end{codebash}

On Arch Linux:
\begin{codebash}
sudo pacman -S linux-headers
\end{codebash}

On Fedora:
\begin{codebash}
sudo dnf install kernel-devel kernel-headers
\end{codebash}

\section{Examples}
\label{sec:examples}
All the examples from this document are available within the \verb|examples| subdirectory.

Should compile errors occur, it may be due to a more recent kernel version being in use,
or there might be a need to install the corresponding kernel header files.

\section{Hello World}
\label{sec:helloworld}
\subsection{The Simplest Module}
\label{sec:org2d3e245}
Most individuals beginning their programming journey typically start with some variant of a \emph{hello world} example.
It is unclear what the outcomes are for those who deviate from this tradition, but it seems prudent to adhere to it.
The learning process will begin with a series of hello world programs that illustrate various fundamental aspects of writing a kernel module.

Presented next is the simplest possible module.

Make a test directory:
\begin{codebash}
mkdir -p ~/develop/kernel/hello-1
cd ~/develop/kernel/hello-1
\end{codebash}

Paste this into your favorite editor and save it as \verb|hello-1.c|:

\samplec{examples/hello-1.c}

Now you will need a \verb|Makefile|. If you copy and paste this, change the indentation to use \textit{tabs}, not spaces.

\begin{code}
obj-m += hello-1.o

PWD := $(CURDIR)

all:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
\end{code}

In \verb|Makefile|, \verb|$(CURDIR)| can be set to the absolute pathname of the current working directory (after all \verb|-C| options are processed, if any).
See more about \verb|CURDIR| in \href{https://www.gnu.org/software/make/manual/make.html}{GNU make manual}.

And finally, just run \verb|make| directly.

\begin{codebash}
make
\end{codebash}

If there is no \verb|PWD := $(CURDIR)| statement in the Makefile, then it may not compile correctly with \verb|sudo make|.
This is because some environment variables are specified by the security policy and cannot be inherited.
The default security policy is \verb|sudoers|.
In the \verb|sudoers| security policy, \verb|env_reset| is enabled by default, which restricts environment variables.
Specifically, path variables are not retained from the user environment;
they are set to default values (for more information, see: \href{https://www.sudo.ws/docs/man/sudoers.man/}{sudoers manual}).
You can see the environment variable settings by:

\begin{verbatim}
$ sudo -s
# sudo -V
\end{verbatim}

Here is a simple Makefile as an example to demonstrate the problem mentioned above.

\begin{code}
all:
	echo $(PWD)
\end{code}

Then, we can use the \verb|-p| flag to print out the environment variable values from the Makefile.

\begin{verbatim}
$ make -p | grep PWD
PWD = /home/ubuntu/temp
OLDPWD = /home/ubuntu
	echo $(PWD)
\end{verbatim}

The \verb|PWD| variable will not be inherited with \verb|sudo|.

\begin{verbatim}
$ sudo make -p | grep PWD
	echo $(PWD)
\end{verbatim}

However, there are three ways to solve this problem.

\begin{enumerate}
  \item {
  You can use the \verb|-E| flag to temporarily preserve them.

  \begin{codebash}
    $ sudo -E make -p | grep PWD
    PWD = /home/ubuntu/temp
    OLDPWD = /home/ubuntu
    echo $(PWD)
  \end{codebash}
  }

  \item {
  You can disable \verb|env_reset| by editing \verb|/etc/sudoers| as root using \verb|visudo|.

  \begin{code}
  ## sudoers file.
  ##
  ...
  Defaults env_reset
  ## Change env_reset to !env_reset in previous line to keep all environment variables
  \end{code}

  Then execute \verb|env| and \verb|sudo env| individually.

  \begin{codebash}
    # disable the env_reset
    echo "user:" > non-env_reset.log; env >> non-env_reset.log
    echo "root:" >> non-env_reset.log; sudo env >> non-env_reset.log
    # enable the env_reset
    echo "user:" > env_reset.log; env >> env_reset.log
    echo "root:" >> env_reset.log; sudo env >> env_reset.log
  \end{codebash}

  You can view and compare these logs to find differences between \verb|env_reset| and \verb|!env_reset|.
  }

  \item {You can preserve environment variables by appending them to \verb|env_keep| in \verb|/etc/sudoers|.

  \begin{code}
  Defaults env_keep += "PWD"
  \end{code}

  After applying the above change, you can check the environment variable settings by:

  \begin{verbatim}
    $ sudo -s
    # sudo -V
  \end{verbatim}
  }
\end{enumerate}

If all goes smoothly you should then find that you have a compiled \verb|hello-1.ko| module.
You can find info on it with the command:
\begin{codebash}
modinfo hello-1.ko
\end{codebash}

At this point the command:
\begin{codebash}
lsmod | grep hello
\end{codebash}

should return nothing.
You can try loading your new module with:
\begin{codebash}
sudo insmod hello-1.ko
\end{codebash}

The dash character will get converted to an underscore, so when you again try:
\begin{codebash}
lsmod | grep hello
\end{codebash}

You should now see your loaded module. It can be removed again with:
\begin{codebash}
sudo rmmod hello_1
\end{codebash}

Notice that the dash was replaced by an underscore.
To see the module's output messages, use \sh|dmesg| to view the kernel log ring buffer:
\begin{codebash}
sudo dmesg | tail -10
\end{codebash}

You should see messages like ``Hello world 1.'' and ``Goodbye world 1.'' from your module.
Alternatively, you can check the systemd journal for kernel messages:
\begin{codebash}
journalctl --since "1 hour ago" | grep kernel
\end{codebash}

You now know the basics of creating, compiling, installing and removing modules.
Now for more of a description of how this module works.

Kernel modules must have at least two functions: a "start" (initialization) function called \cpp|init_module()| which is called when the module is \sh|insmod|ed into the kernel,
and an "end" (cleanup) function called \cpp|cleanup_module()| which is called just before it is removed from the kernel.
Actually, things have changed starting with kernel 2.3.13.
% TODO: adjust the section anchor
You can now use whatever name you like for the start and end functions of a module,
and you will learn how to do this in \Cref{sec:hello_n_goodbye}.
In fact, the new method is the preferred method.
The old \cpp|init_module()| and \cpp|cleanup_module()| have been deprecated for x86 systems with indirect branch tracking (IBT) enabled starting from \href{https://github.com/torvalds/linux/commit/4fab2d76}{commit 4fab2d76} in kernel 6.15, causing build failures.
However, many existing examples still use these names for their start and end functions.

Typically, \cpp|init_module()| either registers a handler for something with the kernel, 
or it replaces one of the kernel functions with its own code (usually code to do something and then call the original function).
The \cpp|cleanup_module()| function is supposed to undo whatever \cpp|init_module()| did, so the module can be unloaded safely.

Lastly, every kernel module needs to include \verb|<linux/module.h>|.
% TODO: adjust the section anchor
We needed to include \verb|<linux/printk.h>| only for the macro expansion for the \cpp|pr_alert()| log level, which you'll learn about in \Cref{sec:printk}.

\begin{enumerate}
  \item A point about coding style.
        Another thing that may not be immediately obvi
Download .txt
gitextract_crc5k9ni/

├── .ci/
│   ├── build-n-run.sh
│   ├── check-format.sh
│   ├── check-newline.sh
│   ├── non-working
│   └── static-analysis.sh
├── .github/
│   └── workflows/
│       ├── build-deploy-assets.yaml
│       ├── deploy-github-page.yaml
│       └── status-check.yaml
├── .gitignore
├── .mailmap
├── GPL-2
├── LICENSE
├── Makefile
├── README.md
├── contrib.tex
├── examples/
│   ├── .clang-format
│   ├── Makefile
│   ├── bh_threaded.c
│   ├── bottomhalf.c
│   ├── chardev.c
│   ├── chardev.h
│   ├── chardev2.c
│   ├── completions.c
│   ├── devicemodel.c
│   ├── devicetree.c
│   ├── dht11.c
│   ├── dt-overlay.dts
│   ├── example_atomic.c
│   ├── example_mutex.c
│   ├── example_rwlock.c
│   ├── example_spinlock.c
│   ├── example_tasklet.c
│   ├── hello-1.c
│   ├── hello-2.c
│   ├── hello-3.c
│   ├── hello-4.c
│   ├── hello-5.c
│   ├── hello-sysfs.c
│   ├── intrpt.c
│   ├── ioctl.c
│   ├── kbleds.c
│   ├── led.c
│   ├── other/
│   │   ├── cat_nonblock.c
│   │   └── userspace_ioctl.c
│   ├── print_string.c
│   ├── procfs1.c
│   ├── procfs2.c
│   ├── procfs3.c
│   ├── procfs4.c
│   ├── sched.c
│   ├── sleep.c
│   ├── start.c
│   ├── static_key.c
│   ├── stop.c
│   ├── syscall-steal.c
│   ├── vinput.c
│   ├── vinput.h
│   └── vkbd.c
├── html.cfg
├── lib/
│   ├── codeblock.tex
│   └── kernelsrc.tex
├── lkmpg.tex
└── scripts/
    ├── Contributors
    ├── Exclude
    ├── Include
    └── list-contributors.sh
Download .txt
SYMBOL INDEX (253 symbols across 39 files)

FILE: examples/bh_threaded.c
  type gpio (line 27) | struct gpio
  type gpio (line 32) | struct gpio
  function irqreturn_t (line 38) | static irqreturn_t button_top_half(int irq, void *ident)
  function irqreturn_t (line 44) | static irqreturn_t button_bottom_half(int irq, void *ident)
  function bottomhalf_init (line 52) | static int __init bottomhalf_init(void)
  function bottomhalf_exit (line 183) | static void __exit bottomhalf_exit(void)

FILE: examples/bottomhalf.c
  type gpio (line 29) | struct gpio
  type gpio (line 34) | struct gpio
  function bottomhalf_work_fn (line 40) | static void bottomhalf_work_fn(struct work_struct *work)
  function irqreturn_t (line 52) | static irqreturn_t button_isr(int irq, void *data)
  function bottomhalf_init (line 65) | static int __init bottomhalf_init(void)
  function bottomhalf_exit (line 194) | static void __exit bottomhalf_exit(void)

FILE: examples/chardev.c
  type inode (line 22) | struct inode
  type file (line 22) | struct file
  type inode (line 23) | struct inode
  type file (line 23) | struct file
  type file (line 24) | struct file
  type file (line 25) | struct file
  type class (line 45) | struct class
  type file_operations (line 47) | struct file_operations
  function chardev_init (line 54) | static int __init chardev_init(void)
  function chardev_exit (line 82) | static void __exit chardev_exit(void)
  function device_open (line 96) | static int device_open(struct inode *inode, struct file *file)
  function device_release (line 109) | static int device_release(struct inode *inode, struct file *file)
  function device_read (line 120) | static ssize_t device_read(struct file *filp, /* see include/linux/fs.h ...
  function device_write (line 155) | static ssize_t device_write(struct file *filp, const char __user *buff,

FILE: examples/chardev2.c
  type class (line 36) | struct class
  function device_open (line 39) | static int device_open(struct inode *inode, struct file *file)
  function device_release (line 46) | static int device_release(struct inode *inode, struct file *file)
  function device_read (line 56) | static ssize_t device_read(struct file *file, /* see include/linux/fs.h ...
  function device_write (line 98) | static ssize_t device_write(struct file *file, const char __user *buffer,
  function device_ioctl (line 120) | static long
  type file_operations (line 185) | struct file_operations
  function chardev2_init (line 194) | static int __init chardev2_init(void)
  function chardev2_exit (line 224) | static void __exit chardev2_exit(void)

FILE: examples/completions.c
  type completion (line 12) | struct completion
  type completion (line 13) | struct completion
  function machine_crank_thread (line 15) | static int machine_crank_thread(void *arg)
  function machine_flywheel_spinup_thread (line 27) | static int machine_flywheel_spinup_thread(void *arg)
  function completions_init (line 41) | static int __init completions_init(void)
  function completions_exit (line 72) | static void __exit completions_exit(void)

FILE: examples/devicemodel.c
  type devicemodel_data (line 9) | struct devicemodel_data {
  function devicemodel_probe (line 14) | static int devicemodel_probe(struct platform_device *dev)
  function devicemodel_remove (line 28) | static void devicemodel_remove(struct platform_device *dev)
  function devicemodel_remove (line 34) | static int devicemodel_remove(struct platform_device *dev)
  function devicemodel_suspend (line 42) | static int devicemodel_suspend(struct device *dev)
  function devicemodel_resume (line 51) | static int devicemodel_resume(struct device *dev)
  type dev_pm_ops (line 60) | struct dev_pm_ops
  type platform_driver (line 69) | struct platform_driver
  function devicemodel_init (line 79) | static int __init devicemodel_init(void)
  function devicemodel_exit (line 95) | static void __exit devicemodel_exit(void)

FILE: examples/devicetree.c
  type dt_device_data (line 14) | struct dt_device_data {
  function dt_probe (line 22) | static int dt_probe(struct platform_device *pdev)
  function dt_remove (line 82) | static void dt_remove(struct platform_device *pdev)
  function dt_remove (line 90) | static int dt_remove(struct platform_device *pdev)
  type of_device_id (line 101) | struct of_device_id
  type platform_driver (line 113) | struct platform_driver
  function dt_init (line 123) | static int __init dt_init(void)
  function dt_exit (line 141) | static void __exit dt_exit(void)

FILE: examples/dht11.c
  type dht11_dev (line 25) | struct dht11_dev {
  type dht11_dev (line 33) | struct dht11_dev
  type gpio (line 39) | struct gpio
  function dht11_read_data (line 41) | static int dht11_read_data(void)
  function device_open (line 111) | static int device_open(struct inode *inode, struct file *file)
  function device_release (line 126) | static int device_release(struct inode *inode, struct file *file)
  function device_read (line 131) | static ssize_t device_read(struct file *filp, char __user *buffer,
  type file_operations (line 150) | struct file_operations
  function dht11_init (line 158) | static int __init dht11_init(void)
  function dht11_exit (line 247) | static void __exit dht11_exit(void)

FILE: examples/example_atomic.c
  function atomic_add_subtract (line 16) | static void atomic_add_subtract(void)
  function atomic_bitwise (line 35) | static void atomic_bitwise(void)
  function example_atomic_init (line 56) | static int __init example_atomic_init(void)
  function example_atomic_exit (line 66) | static void __exit example_atomic_exit(void)

FILE: examples/example_mutex.c
  function example_mutex_init (line 10) | static int __init example_mutex_init(void)
  function example_mutex_exit (line 31) | static void __exit example_mutex_exit(void)

FILE: examples/example_rwlock.c
  function example_read_lock (line 10) | static void example_read_lock(void)
  function example_write_lock (line 23) | static void example_write_lock(void)
  function example_rwlock_init (line 36) | static int __init example_rwlock_init(void)
  function example_rwlock_exit (line 46) | static void __exit example_rwlock_exit(void)

FILE: examples/example_spinlock.c
  function example_spinlock_static (line 12) | static void example_spinlock_static(void)
  function example_spinlock_dynamic (line 27) | static void example_spinlock_dynamic(void)
  function example_spinlock_init (line 43) | static int __init example_spinlock_init(void)
  function example_spinlock_exit (line 53) | static void __exit example_spinlock_exit(void)

FILE: examples/example_tasklet.c
  function tasklet_fn (line 16) | static void tasklet_fn(unsigned long data)
  function example_tasklet_init (line 25) | static int __init example_tasklet_init(void)
  function example_tasklet_exit (line 34) | static void __exit example_tasklet_exit(void)

FILE: examples/hello-1.c
  function init_module (line 7) | int init_module(void)
  function cleanup_module (line 15) | void cleanup_module(void)

FILE: examples/hello-2.c
  function hello_2_init (line 9) | static int __init hello_2_init(void)
  function hello_2_exit (line 15) | static void __exit hello_2_exit(void)

FILE: examples/hello-3.c
  function hello_3_init (line 10) | static int __init hello_3_init(void)
  function hello_3_exit (line 16) | static void __exit hello_3_exit(void)

FILE: examples/hello-4.c
  function init_hello_4 (line 12) | static int __init init_hello_4(void)
  function cleanup_hello_4 (line 18) | static void __exit cleanup_hello_4(void)

FILE: examples/hello-5.c
  function hello_5_init (line 45) | static int __init hello_5_init(void)
  function hello_5_exit (line 62) | static void __exit hello_5_exit(void)

FILE: examples/hello-sysfs.c
  type kobject (line 11) | struct kobject
  function myvariable_show (line 16) | static ssize_t myvariable_show(struct kobject *kobj,
  function myvariable_store (line 22) | static ssize_t myvariable_store(struct kobject *kobj,
  type kobj_attribute (line 30) | struct kobj_attribute
  function mymodule_init (line 33) | static int __init mymodule_init(void)
  function mymodule_exit (line 53) | static void __exit mymodule_exit(void)

FILE: examples/intrpt.c
  type gpio (line 27) | struct gpio
  type gpio (line 32) | struct gpio
  function irqreturn_t (line 36) | static irqreturn_t button_isr(int irq, void *data)
  function intrpt_init (line 48) | static int __init intrpt_init(void)
  function intrpt_exit (line 177) | static void __exit intrpt_exit(void)

FILE: examples/ioctl.c
  type ioctl_arg (line 13) | struct ioctl_arg {
  type cdev (line 30) | struct cdev
  type test_ioctl_data (line 33) | struct test_ioctl_data {
  function test_ioctl_ioctl (line 38) | static long test_ioctl_ioctl(struct file *filp, unsigned int cmd,
  function test_ioctl_read (line 89) | static ssize_t test_ioctl_read(struct file *filp, char __user *buf,
  function test_ioctl_close (line 113) | static int test_ioctl_close(struct inode *inode, struct file *filp)
  function test_ioctl_open (line 125) | static int test_ioctl_open(struct inode *inode, struct file *filp)
  type file_operations (line 142) | struct file_operations
  function ioctl_init (line 150) | static int __init ioctl_init(void)
  function ioctl_exit (line 174) | static void __exit ioctl_exit(void)

FILE: examples/kbleds.c
  type timer_list (line 15) | struct timer_list
  type tty_driver (line 16) | struct tty_driver
  function my_timer_func (line 34) | static void my_timer_func(struct timer_list *unused)
  function kbleds_init (line 49) | static int __init kbleds_init(void)
  function kbleds_cleanup (line 74) | static void __exit kbleds_cleanup(void)

FILE: examples/led.c
  type LED_dev (line 26) | struct LED_dev {
  type LED_dev (line 34) | struct LED_dev
  type gpio (line 40) | struct gpio
  function device_open (line 43) | static int device_open(struct inode *inode, struct file *file)
  function device_release (line 48) | static int device_release(struct inode *inode, struct file *file)
  function device_write (line 53) | static ssize_t device_write(struct file *file, const char __user *buffer,
  type file_operations (line 83) | struct file_operations
  function led_init (line 93) | static int __init led_init(void)
  function led_exit (line 183) | static void __exit led_exit(void)

FILE: examples/other/cat_nonblock.c
  function main (line 13) | int main(int argc, char *argv[])

FILE: examples/other/userspace_ioctl.c
  function ioctl_set_msg (line 20) | int ioctl_set_msg(int file_desc, char *message)
  function ioctl_get_msg (line 33) | int ioctl_get_msg(int file_desc)
  function ioctl_get_nth_byte (line 55) | int ioctl_get_nth_byte(int file_desc)
  function main (line 77) | int main(void)

FILE: examples/print_string.c
  function print_string (line 12) | static void print_string(char *str)
  function print_string_init (line 60) | static int __init print_string_init(void)
  function print_string_exit (line 66) | static void __exit print_string_exit(void)

FILE: examples/procfs1.c
  type proc_dir_entry (line 17) | struct proc_dir_entry
  function procfile_read (line 19) | static ssize_t procfile_read(struct file *file_pointer, char __user *buf...
  type proc_ops (line 38) | struct proc_ops
  type file_operations (line 42) | struct file_operations
  function procfs1_init (line 47) | static int __init procfs1_init(void)
  function procfs1_exit (line 59) | static void __exit procfs1_exit(void)

FILE: examples/procfs2.c
  type proc_dir_entry (line 19) | struct proc_dir_entry
  function procfile_read (line 28) | static ssize_t procfile_read(struct file *file_pointer, char __user *buf...
  function procfile_write (line 47) | static ssize_t procfile_write(struct file *file, const char __user *buff,
  type proc_ops (line 65) | struct proc_ops
  type file_operations (line 70) | struct file_operations
  function procfs2_init (line 76) | static int __init procfs2_init(void)
  function procfs2_exit (line 88) | static void __exit procfs2_exit(void)

FILE: examples/procfs3.c
  type proc_dir_entry (line 22) | struct proc_dir_entry
  function procfs_read (line 26) | static ssize_t procfs_read(struct file *filp, char __user *buffer,
  function procfs_write (line 42) | static ssize_t procfs_write(struct file *file, const char __user *buffer,
  function procfs_open (line 53) | static int procfs_open(struct inode *inode, struct file *file)
  function procfs_close (line 57) | static int procfs_close(struct inode *inode, struct file *file)
  type proc_ops (line 63) | struct proc_ops
  type file_operations (line 70) | struct file_operations
  function procfs3_init (line 78) | static int __init procfs3_init(void)
  function procfs3_exit (line 94) | static void __exit procfs3_exit(void)

FILE: examples/procfs4.c
  type seq_file (line 23) | struct seq_file
  type seq_file (line 41) | struct seq_file
  function my_seq_stop (line 50) | static void my_seq_stop(struct seq_file *s, void *v)
  function my_seq_show (line 56) | static int my_seq_show(struct seq_file *s, void *v)
  type seq_operations (line 65) | struct seq_operations
  function my_open (line 73) | static int my_open(struct inode *inode, struct file *file)
  type proc_ops (line 80) | struct proc_ops
  type file_operations (line 87) | struct file_operations
  function procfs4_init (line 95) | static int __init procfs4_init(void)
  function procfs4_exit (line 108) | static void __exit procfs4_exit(void)

FILE: examples/sched.c
  type workqueue_struct (line 8) | struct workqueue_struct
  type work_struct (line 9) | struct work_struct
  function work_handler (line 11) | static void work_handler(struct work_struct *data)
  function sched_init (line 16) | static int __init sched_init(void)
  function sched_exit (line 28) | static void __exit sched_exit(void)

FILE: examples/sleep.c
  type proc_dir_entry (line 31) | struct proc_dir_entry
  function module_output (line 38) | static ssize_t module_output(struct file *file, /* see include/linux/fs....
  function module_input (line 67) | static ssize_t module_input(struct file *file, /* The file itself */
  function module_open (line 93) | static int module_open(struct inode *inode, struct file *file)
  function module_close (line 137) | static int module_close(struct inode *inode, struct file *file)
  type proc_ops (line 163) | struct proc_ops
  type file_operations (line 171) | struct file_operations
  function sleep_init (line 181) | static int __init sleep_init(void)
  function sleep_exit (line 202) | static void __exit sleep_exit(void)

FILE: examples/start.c
  function init_module (line 8) | int init_module(void)

FILE: examples/static_key.c
  type inode (line 18) | struct inode
  type file (line 18) | struct file
  type inode (line 19) | struct inode
  type file (line 19) | struct file
  type file (line 20) | struct file
  type file (line 22) | struct file
  type class (line 39) | struct class
  type file_operations (line 43) | struct file_operations
  function chardev_init (line 51) | static int __init chardev_init(void)
  function chardev_exit (line 78) | static void __exit chardev_exit(void)
  function device_open (line 93) | static int device_open(struct inode *inode, struct file *file)
  function device_release (line 111) | static int device_release(struct inode *inode, struct file *file)
  function device_read (line 123) | static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
  function device_write (line 159) | static ssize_t device_write(struct file *filp, const char __user *buffer,

FILE: examples/stop.c
  function cleanup_module (line 8) | void cleanup_module(void)

FILE: examples/syscall-steal.c
  function sys_call_kprobe_pre_handler (line 82) | static int sys_call_kprobe_pre_handler(struct kprobe *p, struct pt_regs ...
  type kprobe (line 92) | struct kprobe
  type pt_regs (line 112) | struct pt_regs
  type pt_regs (line 128) | struct pt_regs
  function our_sys_openat (line 130) | static asmlinkage long our_sys_openat(int dfd, const char __user *filename,
  type kprobe (line 204) | struct kprobe
  function __write_cr0 (line 218) | static inline void __write_cr0(unsigned long cr0)
  function enable_write_protection (line 226) | static void enable_write_protection(void)
  function disable_write_protection (line 233) | static void disable_write_protection(void)
  function syscall_steal_start (line 241) | static int __init syscall_steal_start(void)
  function syscall_steal_end (line 272) | static void __exit syscall_steal_end(void)

FILE: examples/vinput.c
  type spinlock (line 26) | struct spinlock
  type class (line 27) | struct class
  type vinput_device (line 32) | struct vinput_device
  type vinput_device (line 35) | struct vinput_device
  type list_head (line 36) | struct list_head
  type vinput (line 56) | struct vinput
  type vinput (line 58) | struct vinput
  type list_head (line 59) | struct list_head
  function vinput_open (line 74) | static int vinput_open(struct inode *inode, struct file *file)
  function vinput_release (line 89) | static int vinput_release(struct inode *inode, struct file *file)
  function vinput_read (line 94) | static ssize_t vinput_read(struct file *file, char __user *buffer, size_...
  function vinput_write (line 116) | static ssize_t vinput_write(struct file *file, const char __user *buffer,
  type file_operations (line 135) | struct file_operations
  function vinput_unregister_vdevice (line 143) | static void vinput_unregister_vdevice(struct vinput *vinput)
  function vinput_destroy_vdevice (line 150) | static void vinput_destroy_vdevice(struct vinput *vinput)
  function vinput_release_dev (line 161) | static void vinput_release_dev(struct device *dev)
  type vinput (line 171) | struct vinput
  type vinput (line 174) | struct vinput
  type vinput (line 174) | struct vinput
  function vinput_register_vdevice (line 219) | static int vinput_register_vdevice(struct vinput *vinput)
  function export_store (line 243) | static ssize_t export_store(const struct class *class,
  function unexport_store (line 293) | static ssize_t unexport_store(const struct class *class,
  type attribute (line 327) | struct attribute
  type class (line 336) | struct class
  function vinput_register (line 347) | int vinput_register(struct vinput_device *dev)
  function vinput_unregister (line 359) | void vinput_unregister(struct vinput_device *dev)
  function vinput_init (line 381) | static int __init vinput_init(void)
  function vinput_end (line 409) | static void __exit vinput_end(void)

FILE: examples/vinput.h
  type vinput_device (line 17) | struct vinput_device
  type vinput (line 19) | struct vinput {
  type vinput_ops (line 33) | struct vinput_ops {
  type vinput_device (line 40) | struct vinput_device {
  type vinput_device (line 46) | struct vinput_device
  type vinput_device (line 47) | struct vinput_device

FILE: examples/vkbd.c
  function vinput_vkbd_init (line 18) | static int vinput_vkbd_init(struct vinput *vinput)
  function vinput_vkbd_read (line 37) | static int vinput_vkbd_read(struct vinput *vinput, char *buff, int len)
  function vinput_vkbd_send (line 46) | static int vinput_vkbd_send(struct vinput *vinput, char *buff, int len)
  type vinput_ops (line 81) | struct vinput_ops
  type vinput_device (line 87) | struct vinput_device
  function vkbd_init (line 92) | static int __init vkbd_init(void)
  function vkbd_end (line 101) | static void __exit vkbd_end(void)
Condensed preview — 66 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (338K chars).
[
  {
    "path": ".ci/build-n-run.sh",
    "chars": 613,
    "preview": "#!/usr/bin/env bash\n\nfunction build_example()\n{   \n    make -C examples || exit 1\n}\n\nfunction list_mod()\n{\n    # Filter "
  },
  {
    "path": ".ci/check-format.sh",
    "chars": 604,
    "preview": "#!/usr/bin/env bash\n\nSOURCES=$(find $(git rev-parse --show-toplevel) | grep -E \"\\.(cpp|cc|c|h)\\$\")\n\nCLANG_FORMAT=$(which"
  },
  {
    "path": ".ci/check-newline.sh",
    "chars": 501,
    "preview": "#!/usr/bin/env bash\n\nset -e -u -o pipefail\n\nret=0\nshow=0\n# Reference: https://medium.com/@alexey.inkin/how-to-force-newl"
  },
  {
    "path": ".ci/non-working",
    "chars": 59,
    "preview": "bottomhalf\nbh_threaded\nintrpt\nvkbd\nsyscall-steal\nled\ndht11\n"
  },
  {
    "path": ".ci/static-analysis.sh",
    "chars": 3305,
    "preview": "#!/usr/bin/env bash\n\nfunction do_cppcheck()\n{\n    local SOURCES=$(find $(git rev-parse --show-toplevel) | grep -E \"\\.(cp"
  },
  {
    "path": ".github/workflows/build-deploy-assets.yaml",
    "chars": 717,
    "preview": "name: build-deploy-assets\n\non:\n  push:\n    branches: [ master ]\n\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubunt"
  },
  {
    "path": ".github/workflows/deploy-github-page.yaml",
    "chars": 471,
    "preview": "name: deploy-github-page\n\non:\n  push:\n    branches: [ master ]\n\n  workflow_dispatch:\n\njobs:\n  build:\n    runs-on: ubuntu"
  },
  {
    "path": ".github/workflows/status-check.yaml",
    "chars": 816,
    "preview": "name: status-checks\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\n  workflow_dispatch:"
  },
  {
    "path": ".gitignore",
    "chars": 329,
    "preview": "# Linux kernel build system\n*.o\n*.o.d\n*.ko\n*cmd\n*.dwo\n*.swp\n*.symvers\n*.mod\n*.mod.c\nmodules.order\n\n# LaTeX\n_minted-lkmpg"
  },
  {
    "path": ".mailmap",
    "chars": 1970,
    "preview": "Jian-Xing Wu <fdgkhdkgh@gmail.com> 吳建興\nMeng-Zong Tsai <hwahwa649@gmail.com> fennecJ   <58484289+fennecJ@users.noreply.gi"
  },
  {
    "path": "GPL-2",
    "chars": 18011,
    "preview": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc."
  },
  {
    "path": "LICENSE",
    "chars": 10274,
    "preview": "OPEN SOFTWARE LICENSE 3.0 (OSL-3.0)\n\nThis Open Software License (the \"License\") applies to any original work\nof authorsh"
  },
  {
    "path": "Makefile",
    "chars": 1037,
    "preview": "PROJ = lkmpg\nall: $(PROJ).pdf\n\n$(PROJ).pdf: lkmpg.tex\n\t@if ! hash latexmk; then echo \"No Latexmk found. See https://mg.r"
  },
  {
    "path": "README.md",
    "chars": 3424,
    "preview": "# The Linux Kernel Module Programming Guide\n\nThis project keeps the Linux Kernel Module Programming Guide up to date, wi"
  },
  {
    "path": "contrib.tex",
    "chars": 4033,
    "preview": "Amit Dhingra,                  % <mechanicalamit@gmail.com>\nAndrew Kreimer,                % <algonell@gmail.com>\nAndrew"
  },
  {
    "path": "examples/.clang-format",
    "chars": 2621,
    "preview": "Language: Cpp\n\nAccessModifierOffset: -4\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutive"
  },
  {
    "path": "examples/Makefile",
    "chars": 1110,
    "preview": "obj-m += hello-1.o\nobj-m += hello-2.o\nobj-m += hello-3.o\nobj-m += hello-4.o\nobj-m += hello-5.o\nobj-m += startstop.o\nstar"
  },
  {
    "path": "examples/bh_threaded.c",
    "chars": 5043,
    "preview": "/*\n * bh_thread.c - Top and bottom half interrupt handling\n *\n * Based upon the RPi example by Stefan Wendler (devnull@k"
  },
  {
    "path": "examples/bottomhalf.c",
    "chars": 5282,
    "preview": "/*\n * bottomhalf.c - Top and bottom half interrupt handling\n *\n * Based upon the RPi example by Stefan Wendler (devnull@"
  },
  {
    "path": "examples/chardev.c",
    "chars": 4711,
    "preview": "/*\n * chardev.c: Creates a read-only char device that says how many times\n * you have read from the dev file\n */\n\n#inclu"
  },
  {
    "path": "examples/chardev.h",
    "chars": 1568,
    "preview": "/*\n * chardev.h - the header file with the ioctl definitions.\n *\n * The declarations here have to be in a header file, b"
  },
  {
    "path": "examples/chardev2.c",
    "chars": 7037,
    "preview": "/*\n * chardev2.c - Create an input/output character device\n */\n\n#include <linux/atomic.h>\n#include <linux/cdev.h>\n#inclu"
  },
  {
    "path": "examples/completions.c",
    "chars": 1925,
    "preview": "/*\n * completions.c\n */\n#include <linux/completion.h>\n#include <linux/err.h> /* for IS_ERR() */\n#include <linux/init.h>\n"
  },
  {
    "path": "examples/devicemodel.c",
    "chars": 2237,
    "preview": "/*\n * devicemodel.c\n */\n#include <linux/kernel.h>\n#include <linux/module.h>\n#include <linux/platform_device.h>\n#include "
  },
  {
    "path": "examples/devicetree.c",
    "chars": 4311,
    "preview": "/* devicetree.c - Demonstrates device tree interaction with kernel modules */\n\n#include <linux/init.h>\n#include <linux/k"
  },
  {
    "path": "examples/dht11.c",
    "chars": 6391,
    "preview": "/*\n *  dht11.c - Using GPIO to read temperature and humidity from DHT11 sensor.\n */\n\n#include <linux/cdev.h>\n#include <l"
  },
  {
    "path": "examples/dt-overlay.dts",
    "chars": 1102,
    "preview": "/*\n * Device Tree Overlay for LKMPG Device Tree Example\n * \n * This overlay can be compiled and loaded on systems that s"
  },
  {
    "path": "examples/example_atomic.c",
    "chars": 1977,
    "preview": "/*\n * example_atomic.c\n */\n#include <linux/atomic.h>\n#include <linux/bitops.h>\n#include <linux/module.h>\n#include <linux"
  },
  {
    "path": "examples/example_mutex.c",
    "chars": 774,
    "preview": "/*\n * example_mutex.c\n */\n#include <linux/module.h>\n#include <linux/mutex.h>\n#include <linux/printk.h>\n\nstatic DEFINE_MU"
  },
  {
    "path": "examples/example_rwlock.c",
    "chars": 1024,
    "preview": "/*\n * example_rwlock.c\n */\n#include <linux/module.h>\n#include <linux/printk.h>\n#include <linux/rwlock.h>\n\nstatic DEFINE_"
  },
  {
    "path": "examples/example_spinlock.c",
    "chars": 1429,
    "preview": "/*\n * example_spinlock.c\n */\n#include <linux/init.h>\n#include <linux/module.h>\n#include <linux/printk.h>\n#include <linux"
  },
  {
    "path": "examples/example_tasklet.c",
    "chars": 990,
    "preview": "/*\n * example_tasklet.c\n */\n#include <linux/delay.h>\n#include <linux/interrupt.h>\n#include <linux/module.h>\n#include <li"
  },
  {
    "path": "examples/hello-1.c",
    "chars": 399,
    "preview": "/*\n * hello-1.c - The simplest kernel module.\n */\n#include <linux/module.h> /* Needed by all modules */\n#include <linux/"
  },
  {
    "path": "examples/hello-2.c",
    "chars": 553,
    "preview": "/*\n * hello-2.c - Demonstrating the module_init() and module_exit() macros.\n * This is preferred over using init_module("
  },
  {
    "path": "examples/hello-3.c",
    "chars": 536,
    "preview": "/*\n * hello-3.c - Illustrating the __init, __initdata and __exit macros.\n */\n#include <linux/init.h> /* Needed for the m"
  },
  {
    "path": "examples/hello-4.c",
    "chars": 531,
    "preview": "/*\n * hello-4.c - Demonstrates module documentation.\n */\n#include <linux/init.h> /* Needed for the macros */\n#include <l"
  },
  {
    "path": "examples/hello-5.c",
    "chars": 2223,
    "preview": "/*\n * hello-5.c - Demonstrates command line argument passing to a module.\n */\n#include <linux/init.h>\n#include <linux/ke"
  },
  {
    "path": "examples/hello-sysfs.c",
    "chars": 1477,
    "preview": "/*\n * hello-sysfs.c sysfs example\n */\n#include <linux/fs.h>\n#include <linux/init.h>\n#include <linux/kobject.h>\n#include "
  },
  {
    "path": "examples/intrpt.c",
    "chars": 4811,
    "preview": "/*\n * intrpt.c - Handling GPIO with interrupts\n *\n * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de)\n"
  },
  {
    "path": "examples/ioctl.c",
    "chars": 4219,
    "preview": "/*\n * ioctl.c\n */\n#include <linux/cdev.h>\n#include <linux/fs.h>\n#include <linux/init.h>\n#include <linux/ioctl.h>\n#includ"
  },
  {
    "path": "examples/kbleds.c",
    "chars": 2676,
    "preview": "/*\n * kbleds.c - Blink keyboard leds until the module is unloaded.\n */\n\n#include <linux/init.h>\n#include <linux/kd.h> /*"
  },
  {
    "path": "examples/led.c",
    "chars": 4959,
    "preview": "/*\n * led.c - Using GPIO to control the LED on/off\n */\n\n#include <linux/cdev.h>\n#include <linux/delay.h>\n#include <linux"
  },
  {
    "path": "examples/other/cat_nonblock.c",
    "chars": 1669,
    "preview": "/*\n *  cat_nonblock.c - open a file and display its contents, but exit rather than\n *  wait for input.\n */\n#include <err"
  },
  {
    "path": "examples/other/userspace_ioctl.c",
    "chars": 2385,
    "preview": "\n/*  userspace_ioctl.c - the process to use ioctl's to control the kernel module\n *\n *  Until now we could have used cat"
  },
  {
    "path": "examples/print_string.c",
    "chars": 2916,
    "preview": "/*\n * print_string.c - Send output to the tty we're running on, regardless if\n * it is through X11, telnet, etc.  We do "
  },
  {
    "path": "examples/procfs1.c",
    "chars": 1524,
    "preview": "/*\n * procfs1.c\n */\n\n#include <linux/kernel.h>\n#include <linux/module.h>\n#include <linux/proc_fs.h>\n#include <linux/uacc"
  },
  {
    "path": "examples/procfs2.c",
    "chars": 2638,
    "preview": "/*\n * procfs2.c -  create a \"file\" in /proc\n */\n\n#include <linux/kernel.h> /* We're doing kernel work */\n#include <linux"
  },
  {
    "path": "examples/procfs3.c",
    "chars": 2798,
    "preview": "/*\n * procfs3.c\n */\n\n#include <linux/kernel.h>\n#include <linux/module.h>\n#include <linux/proc_fs.h>\n#include <linux/sche"
  },
  {
    "path": "examples/procfs4.c",
    "chars": 2955,
    "preview": "/*\n * procfs4.c -  create a \"file\" in /proc\n * This program uses the seq_file library to manage the /proc file.\n */\n\n#in"
  },
  {
    "path": "examples/sched.c",
    "chars": 764,
    "preview": "/*\n * sched.c\n */\n#include <linux/init.h>\n#include <linux/module.h>\n#include <linux/workqueue.h>\n\nstatic struct workqueu"
  },
  {
    "path": "examples/sleep.c",
    "chars": 7067,
    "preview": "/*\n * sleep.c - create a /proc file, and if several processes try to open it\n * at the same time, put all but one to sle"
  },
  {
    "path": "examples/start.c",
    "chars": 295,
    "preview": "/*\n * start.c - Illustration of multi filed modules\n */\n\n#include <linux/kernel.h> /* We are doing kernel work */\n#inclu"
  },
  {
    "path": "examples/static_key.c",
    "chars": 4986,
    "preview": "/*\n * static_key.c\n */\n\n#include <linux/atomic.h>\n#include <linux/device.h>\n#include <linux/fs.h>\n#include <linux/kernel"
  },
  {
    "path": "examples/stop.c",
    "chars": 279,
    "preview": "/*\n * stop.c - Illustration of multi filed modules\n */\n\n#include <linux/kernel.h> /* We are doing kernel work */\n#includ"
  },
  {
    "path": "examples/syscall-steal.c",
    "chars": 8714,
    "preview": "/*\n * syscall-steal.c\n *\n * System call \"stealing\" sample.\n *\n * Disables page protection at a processor level by changi"
  },
  {
    "path": "examples/vinput.c",
    "chars": 10521,
    "preview": "/*\n * vinput.c\n */\n\n#include <linux/cdev.h>\n#include <linux/input.h>\n#include <linux/module.h>\n#include <linux/slab.h>\n#"
  },
  {
    "path": "examples/vinput.h",
    "chars": 900,
    "preview": "/*\n * vinput.h\n */\n\n#ifndef VINPUT_H\n#define VINPUT_H\n\n#include <linux/input.h>\n#include <linux/spinlock.h>\n\n#define VIN"
  },
  {
    "path": "examples/vkbd.c",
    "chars": 2603,
    "preview": "/*\n * vkbd.c\n */\n\n#include <linux/init.h>\n#include <linux/input.h>\n#include <linux/module.h>\n#include <linux/spinlock.h>"
  },
  {
    "path": "html.cfg",
    "chars": 1733,
    "preview": "\\Preamble{xhtml}\n\n\\Configure{tableofcontents*}{chapter,section,subsection}\n\n\\Css{html {\n    width: 100vw;\n    overflow-x"
  },
  {
    "path": "lib/codeblock.tex",
    "chars": 905,
    "preview": "\\newminted[code]{c}{frame=single,\n  framesep=2mm,\n  baselinestretch=1,\n  fontsize=\\footnotesize,\n  breaklines,\n  breakaf"
  },
  {
    "path": "lib/kernelsrc.tex",
    "chars": 171,
    "preview": "\\newcommand*{\\src}[2][]{\\href{https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/#2}%\n               "
  },
  {
    "path": "lkmpg.tex",
    "chars": 144189,
    "preview": "\\documentclass[10pt, oneside]{book}\n\\usepackage[Bjornstrup]{fncychap}\n\\usepackage[utf8]{inputenc}\n\\usepackage[T1]{fonten"
  },
  {
    "path": "scripts/Contributors",
    "chars": 2931,
    "preview": "Amit Dhingra,<mechanicalamit@gmail.com>\nAndrew Kreimer,<algonell@gmail.com>\nAndrew Lin,<35786166+classAndrew@users.norep"
  },
  {
    "path": "scripts/Exclude",
    "chars": 43,
    "preview": "Jim Huang,              One of main author\n"
  },
  {
    "path": "scripts/Include",
    "chars": 176,
    "preview": "Daniele Paolo Scarpazza,<>\nDavid Porter,<>\nDimo Velev,<>\nFrancois Audeon,<>\nHorst Schirmeier,<>\nIgnacio Martin,<>\nRoman "
  },
  {
    "path": "scripts/list-contributors.sh",
    "chars": 1839,
    "preview": "#!/usr/bin/env bash\nFORMAT=\"%aN,<%aE>\"                   #Set git output format in \"Name,<Email>\" //Capital in aN and aE"
  }
]

About this extraction

This page contains the full source code of the sysprog21/lkmpg GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 66 files (315.5 KB), approximately 82.3k tokens, and a symbol index with 253 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!