mirror of
https://github.com/sysprog21/lkmpg.git
synced 2024-11-22 04:09:18 +08:00
Add LaTeX script and sample code
This commit is contained in:
parent
826b45b3d9
commit
2246e20809
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
*.o
|
||||
*.ko
|
||||
*cmd
|
||||
*.swp
|
||||
*.symvers
|
||||
|
||||
# LaTeX
|
||||
_minted-lkmpg
|
||||
*.aux
|
||||
*.log
|
||||
*.out
|
||||
lkmpg.pdf
|
||||
*.toc
|
6
Makefile
Normal file
6
Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
all: lkmpg.tex
|
||||
pdflatex -shell-escap lkmpg.tex
|
||||
|
||||
clean:
|
||||
rm -f *.dvi *.aux *.log *.ps *.pdf *.out lkmpg.bbl lkmpg.blg lkmpg.lof lkmpg.toc
|
||||
rm -rf _minted-lkmpg
|
38
examples/Makefile
Normal file
38
examples/Makefile
Normal file
|
@ -0,0 +1,38 @@
|
|||
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 += start.o
|
||||
obj-m += 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.o
|
||||
obj-m += intrpt.o
|
||||
obj-m += cryptosha256.o
|
||||
obj-m += cryptosk.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 += ioctl.o
|
||||
|
||||
all:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||
rm -f other/ioctl other/cat_noblock *.plist
|
165
examples/bottomhalf.c
Normal file
165
examples/bottomhalf.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* bottomhalf.c - Top and bottom half interrupt handling
|
||||
*
|
||||
* Copyright (C) 2017 by Bob Mottram
|
||||
* 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>
|
||||
|
||||
static int button_irqs[] = { -1, -1 };
|
||||
|
||||
/* Define GPIOs for LEDs.
|
||||
Change the numbers for the GPIO on your board. */
|
||||
static struct gpio leds[] = {
|
||||
{ 4, GPIOF_OUT_INIT_LOW, "LED 1" }
|
||||
};
|
||||
|
||||
/* Define GPIOs for BUTTONS
|
||||
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" }
|
||||
};
|
||||
|
||||
/* Tasklet containing some non-trivial amount of processing */
|
||||
static void bottomhalf_tasklet_fn(unsigned long data)
|
||||
{
|
||||
pr_info("Bottom half tasklet starts\n");
|
||||
/* do something which takes a while */
|
||||
mdelay(500);
|
||||
pr_info("Bottom half tasklet ends\n");
|
||||
}
|
||||
|
||||
DECLARE_TASKLET(buttontask, bottomhalf_tasklet_fn, 0L);
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
tasklet_schedule(&buttontask);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int init_module()
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
/* register LED gpios */
|
||||
ret = gpio_request_array(leds, ARRAY_SIZE(leds));
|
||||
|
||||
if (ret) {
|
||||
pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register BUTTON gpios */
|
||||
ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));
|
||||
|
||||
if (ret) {
|
||||
pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
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);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
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);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
|
||||
ret = gpio_to_irq(buttons[1].gpio);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Unable to request IRQ: %d\n", ret);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
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);
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* cleanup what has been setup so far */
|
||||
fail3:
|
||||
free_irq(button_irqs[0], NULL);
|
||||
|
||||
fail2:
|
||||
gpio_free_array(buttons, ARRAY_SIZE(leds));
|
||||
|
||||
fail1:
|
||||
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cleanup_module()
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
/* free irqs */
|
||||
free_irq(button_irqs[0], NULL);
|
||||
free_irq(button_irqs[1], NULL);
|
||||
|
||||
/* turn all LEDs off */
|
||||
for (i = 0; i < ARRAY_SIZE(leds); i++)
|
||||
gpio_set_value(leds[i].gpio, 0);
|
||||
|
||||
/* unregister */
|
||||
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||
gpio_free_array(buttons, ARRAY_SIZE(buttons));
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Interrupt with top and bottom half");
|
183
examples/chardev.c
Normal file
183
examples/chardev.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* chardev.c: Creates a read-only char device that says how many times
|
||||
* you've read from the dev file
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
/*
|
||||
* Prototypes - this would normally go in a .h file
|
||||
*/
|
||||
int init_module(void);
|
||||
void cleanup_module(void);
|
||||
static int device_open(struct inode *, struct file *);
|
||||
static int device_release(struct inode *, struct file *);
|
||||
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
|
||||
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
|
||||
|
||||
#define SUCCESS 0
|
||||
#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 */
|
||||
static int Device_Open = 0; /* Is device open?
|
||||
* Used to prevent multiple access to device */
|
||||
static char msg[BUF_LEN]; /* The msg the device will give when asked */
|
||||
static char *msg_Ptr;
|
||||
|
||||
static struct class *cls;
|
||||
|
||||
static struct file_operations chardev_fops = {
|
||||
.read = device_read,
|
||||
.write = device_write,
|
||||
.open = device_open,
|
||||
.release = device_release
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is called when the module is loaded
|
||||
*/
|
||||
int init_module(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);
|
||||
|
||||
cls = class_create(THIS_MODULE, DEVICE_NAME);
|
||||
device_create(cls, NULL, MKDEV(Major, 0), NULL, DEVICE_NAME);
|
||||
|
||||
pr_info("Device created on /dev/%s\n", DEVICE_NAME);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called when the module is unloaded
|
||||
*/
|
||||
void cleanup_module(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
|
||||
* "cat /dev/mycharfile"
|
||||
*/
|
||||
static int device_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
static int counter = 0;
|
||||
|
||||
if (Device_Open)
|
||||
return -EBUSY;
|
||||
|
||||
Device_Open++;
|
||||
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
|
||||
msg_Ptr = msg;
|
||||
try_module_get(THIS_MODULE);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a process closes the device file.
|
||||
*/
|
||||
static int device_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
Device_Open--; /* We're now ready for our next caller */
|
||||
|
||||
/*
|
||||
* Decrement the usage count, or else once you opened the file, you'll
|
||||
* never get get rid of the module.
|
||||
*/
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *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;
|
||||
|
||||
/*
|
||||
* If we're at the end of the message,
|
||||
* return 0 signifying end of file
|
||||
*/
|
||||
if (*msg_Ptr == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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++;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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" > /dev/hello
|
||||
*/
|
||||
static ssize_t device_write(struct file *filp,
|
||||
const char *buff,
|
||||
size_t len,
|
||||
loff_t * off)
|
||||
{
|
||||
pr_alert("Sorry, this operation isn't supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
66
examples/chardev.h
Normal file
66
examples/chardev.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* chardev2.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 chardev.c) and the process calling ioctl (ioctl.c)
|
||||
*/
|
||||
|
||||
#ifndef CHARDEV_H
|
||||
#define CHARDEV_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/*
|
||||
* The major device number. We can't 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're 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're 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"
|
||||
|
||||
#endif
|
289
examples/chardev2.c
Normal file
289
examples/chardev2.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* chardev2.c - Create an input/output character device
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/module.h> /* Specifically, a module */
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
#include "chardev.h"
|
||||
#define SUCCESS 0
|
||||
#define DEVICE_NAME "char_dev"
|
||||
#define BUF_LEN 80
|
||||
|
||||
/*
|
||||
* Is the device open right now? Used to prevent
|
||||
* concurent access into the same device
|
||||
*/
|
||||
static int Device_Open = 0;
|
||||
|
||||
/*
|
||||
* The message the device will give when asked
|
||||
*/
|
||||
static char Message[BUF_LEN];
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static char *Message_Ptr;
|
||||
|
||||
static int Major; /* Major number assigned to our device driver */
|
||||
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)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
pr_info("device_open(%p)\n", file);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We don't want to talk to two processes at the same time
|
||||
*/
|
||||
if (Device_Open)
|
||||
return -EBUSY;
|
||||
|
||||
Device_Open++;
|
||||
/*
|
||||
* Initialize the message
|
||||
*/
|
||||
Message_Ptr = Message;
|
||||
try_module_get(THIS_MODULE);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int device_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
pr_info("device_release(%p,%p)\n", inode, file);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We're now ready for our next caller
|
||||
*/
|
||||
Device_Open--;
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 with data */
|
||||
size_t length, /* length of the buffer */
|
||||
loff_t * offset)
|
||||
{
|
||||
/*
|
||||
* Number of bytes actually written to the buffer
|
||||
*/
|
||||
int bytes_read = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
pr_info("device_read(%p,%p,%d)\n", file, buffer, length);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If we're at the end of the message, return 0
|
||||
* (which signifies end of file)
|
||||
*/
|
||||
if (*Message_Ptr == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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 wouldn't
|
||||
* 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++;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
pr_info("Read %d bytes, %d left\n", bytes_read, length);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read functions are supposed to return the number
|
||||
* of bytes actually inserted into the buffer
|
||||
*/
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is 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;
|
||||
|
||||
#ifdef DEBUG
|
||||
pr_info("device_write(%p,%s,%d)", file, buffer, length);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < length && i < BUF_LEN; i++)
|
||||
get_user(Message[i], buffer + i);
|
||||
|
||||
Message_Ptr = Message;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
long device_ioctl(struct file *file, /* ditto */
|
||||
unsigned int ioctl_num, /* number and param for ioctl */
|
||||
unsigned long ioctl_param)
|
||||
{
|
||||
int i;
|
||||
char *temp;
|
||||
char ch;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
temp = (char *)ioctl_param;
|
||||
|
||||
/*
|
||||
* Find the length of the message
|
||||
*/
|
||||
get_user(ch, temp);
|
||||
for (i = 0; ch && i < BUF_LEN; i++, temp++)
|
||||
get_user(ch, temp);
|
||||
|
||||
device_write(file, (char *)ioctl_param, i, 0);
|
||||
break;
|
||||
|
||||
case IOCTL_GET_MSG:
|
||||
/*
|
||||
* Give the current message to the calling process -
|
||||
* the parameter we got is a pointer, fill it.
|
||||
*/
|
||||
i = device_read(file, (char *)ioctl_param, 99, 0);
|
||||
|
||||
/*
|
||||
* Put a zero at the end of the buffer, so it will be
|
||||
* properly terminated
|
||||
*/
|
||||
put_user('\0', (char *)ioctl_param + i);
|
||||
break;
|
||||
|
||||
case IOCTL_GET_NTH_BYTE:
|
||||
/*
|
||||
* This ioctl is both input (ioctl_param) and
|
||||
* output (the return value of this function)
|
||||
*/
|
||||
return Message[ioctl_param];
|
||||
break;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
int init_module()
|
||||
{
|
||||
int ret_val;
|
||||
/*
|
||||
* Register the character device (atleast try)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
Major = ret_val;
|
||||
|
||||
cls = class_create(THIS_MODULE, DEVICE_FILE_NAME);
|
||||
device_create(cls, NULL, MKDEV(Major, MAJOR_NUM), NULL, DEVICE_FILE_NAME);
|
||||
|
||||
pr_info("Device created on /dev/%s\n", DEVICE_FILE_NAME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup - unregister the appropriate file from /proc
|
||||
*/
|
||||
void cleanup_module()
|
||||
{
|
||||
device_destroy(cls, MKDEV(Major, 0));
|
||||
class_destroy(cls);
|
||||
|
||||
/*
|
||||
* Unregister the device
|
||||
*/
|
||||
unregister_chrdev(Major, DEVICE_NAME);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
77
examples/completions.c
Normal file
77
examples/completions.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
static struct {
|
||||
struct completion crank_comp;
|
||||
struct completion flywheel_comp;
|
||||
} machine;
|
||||
|
||||
static int machine_crank_thread(void* arg)
|
||||
{
|
||||
pr_info("Turn the crank\n");
|
||||
|
||||
complete_all(&machine.crank_comp);
|
||||
complete_and_exit(&machine.crank_comp, 0);
|
||||
}
|
||||
|
||||
static int machine_flywheel_spinup_thread(void* arg)
|
||||
{
|
||||
wait_for_completion(&machine.crank_comp);
|
||||
|
||||
pr_info("Flywheel spins up\n");
|
||||
|
||||
complete_all(&machine.flywheel_comp);
|
||||
complete_and_exit(&machine.flywheel_comp, 0);
|
||||
}
|
||||
|
||||
static int completions_init(void)
|
||||
{
|
||||
struct task_struct* crank_thread;
|
||||
struct task_struct* flywheel_thread;
|
||||
|
||||
pr_info("completions example\n");
|
||||
|
||||
init_completion(&machine.crank_comp);
|
||||
init_completion(&machine.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;
|
||||
}
|
||||
|
||||
void completions_exit(void)
|
||||
{
|
||||
wait_for_completion(&machine.crank_comp);
|
||||
wait_for_completion(&machine.flywheel_comp);
|
||||
|
||||
pr_info("completions exit\n");
|
||||
}
|
||||
|
||||
module_init(completions_init);
|
||||
module_exit(completions_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Completions example");
|
||||
MODULE_LICENSE("GPL");
|
63
examples/cryptosha256.c
Normal file
63
examples/cryptosha256.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include <linux/module.h>
|
||||
#include <crypto/internal/hash.h>
|
||||
|
||||
#define SHA256_LENGTH 32
|
||||
|
||||
static void show_hash_result(char * plaintext, char * hash_sha256)
|
||||
{
|
||||
int i;
|
||||
char str[SHA256_LENGTH*2 + 1];
|
||||
|
||||
pr_info("sha256 test for string: \"%s\"\n", plaintext);
|
||||
for (i = 0; i < SHA256_LENGTH ; i++)
|
||||
sprintf(&str[i*2],"%02x", (unsigned char)hash_sha256[i]);
|
||||
str[i*2] = 0;
|
||||
pr_info("%s\n", str);
|
||||
}
|
||||
|
||||
int cryptosha256_init(void)
|
||||
{
|
||||
char * plaintext = "This is a test";
|
||||
char hash_sha256[SHA256_LENGTH];
|
||||
struct crypto_shash *sha256;
|
||||
struct shash_desc *shash;
|
||||
|
||||
sha256 = crypto_alloc_shash("sha256", 0, 0);
|
||||
if (IS_ERR(sha256))
|
||||
return -1;
|
||||
|
||||
shash =
|
||||
kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(sha256),
|
||||
GFP_KERNEL);
|
||||
if (!shash)
|
||||
return -ENOMEM;
|
||||
|
||||
shash->tfm = sha256;
|
||||
|
||||
if (crypto_shash_init(shash))
|
||||
return -1;
|
||||
|
||||
if (crypto_shash_update(shash, plaintext, strlen(plaintext)))
|
||||
return -1;
|
||||
|
||||
if (crypto_shash_final(shash, hash_sha256))
|
||||
return -1;
|
||||
|
||||
kfree(shash);
|
||||
crypto_free_shash(sha256);
|
||||
|
||||
show_hash_result(plaintext, hash_sha256);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cryptosha256_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(cryptosha256_init);
|
||||
module_exit(cryptosha256_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("sha256 hash test");
|
||||
MODULE_LICENSE("GPL");
|
197
examples/cryptosk.c
Normal file
197
examples/cryptosk.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include <crypto/internal/skcipher.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#define SYMMETRIC_KEY_LENGTH 32
|
||||
#define CIPHER_BLOCK_SIZE 16
|
||||
|
||||
struct tcrypt_result {
|
||||
struct completion completion;
|
||||
int err;
|
||||
};
|
||||
|
||||
struct skcipher_def {
|
||||
struct scatterlist sg;
|
||||
struct crypto_skcipher * tfm;
|
||||
struct skcipher_request * req;
|
||||
struct tcrypt_result result;
|
||||
char * scratchpad;
|
||||
char * ciphertext;
|
||||
char * ivdata;
|
||||
};
|
||||
|
||||
static struct skcipher_def sk;
|
||||
|
||||
static void test_skcipher_finish(struct skcipher_def * sk)
|
||||
{
|
||||
if (sk->tfm)
|
||||
crypto_free_skcipher(sk->tfm);
|
||||
if (sk->req)
|
||||
skcipher_request_free(sk->req);
|
||||
if (sk->ivdata)
|
||||
kfree(sk->ivdata);
|
||||
if (sk->scratchpad)
|
||||
kfree(sk->scratchpad);
|
||||
if (sk->ciphertext)
|
||||
kfree(sk->ciphertext);
|
||||
}
|
||||
|
||||
static int test_skcipher_result(struct skcipher_def * sk, int rc)
|
||||
{
|
||||
switch (rc) {
|
||||
case 0:
|
||||
break;
|
||||
case -EINPROGRESS || -EBUSY:
|
||||
rc = wait_for_completion_interruptible(
|
||||
&sk->result.completion);
|
||||
if (!rc && !sk->result.err) {
|
||||
reinit_completion(&sk->result.completion);
|
||||
break;
|
||||
}
|
||||
pr_info("skcipher encrypt returned with %d result %d\n",
|
||||
rc, sk->result.err);
|
||||
break;
|
||||
default:
|
||||
pr_info("skcipher encrypt returned with %d result %d\n",
|
||||
rc, sk->result.err);
|
||||
break;
|
||||
}
|
||||
|
||||
init_completion(&sk->result.completion);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void test_skcipher_callback(struct crypto_async_request *req, int error)
|
||||
{
|
||||
struct tcrypt_result *result = req->data;
|
||||
/* int ret; */
|
||||
|
||||
if (error == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
result->err = error;
|
||||
complete(&result->completion);
|
||||
pr_info("Encryption finished successfully\n");
|
||||
|
||||
/* decrypt data */
|
||||
/*
|
||||
memset((void*)sk.scratchpad, '-', CIPHER_BLOCK_SIZE);
|
||||
ret = crypto_skcipher_decrypt(sk.req);
|
||||
ret = test_skcipher_result(&sk, ret);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
sg_copy_from_buffer(&sk.sg, 1, sk.scratchpad, CIPHER_BLOCK_SIZE);
|
||||
sk.scratchpad[CIPHER_BLOCK_SIZE-1] = 0;
|
||||
|
||||
pr_info("Decryption request successful\n");
|
||||
pr_info("Decrypted: %s\n", sk.scratchpad);
|
||||
*/
|
||||
}
|
||||
|
||||
static int test_skcipher_encrypt(char * plaintext, char * password,
|
||||
struct skcipher_def * sk)
|
||||
{
|
||||
int ret = -EFAULT;
|
||||
unsigned char key[SYMMETRIC_KEY_LENGTH];
|
||||
|
||||
if (!sk->tfm) {
|
||||
sk->tfm = crypto_alloc_skcipher("cbc-aes-aesni", 0, 0);
|
||||
if (IS_ERR(sk->tfm)) {
|
||||
pr_info("could not allocate skcipher handle\n");
|
||||
return PTR_ERR(sk->tfm);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sk->req) {
|
||||
sk->req = skcipher_request_alloc(sk->tfm, GFP_KERNEL);
|
||||
if (!sk->req) {
|
||||
pr_info("could not allocate skcipher request\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
skcipher_request_set_callback(sk->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
|
||||
test_skcipher_callback,
|
||||
&sk->result);
|
||||
|
||||
/* clear the key */
|
||||
memset((void*)key,'\0',SYMMETRIC_KEY_LENGTH);
|
||||
|
||||
/* Use the world's favourite password */
|
||||
sprintf((char*)key,"%s",password);
|
||||
|
||||
/* AES 256 with given symmetric key */
|
||||
if (crypto_skcipher_setkey(sk->tfm, key, SYMMETRIC_KEY_LENGTH)) {
|
||||
pr_info("key could not be set\n");
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
pr_info("Symmetric key: %s\n", key);
|
||||
pr_info("Plaintext: %s\n", plaintext);
|
||||
|
||||
if (!sk->ivdata) {
|
||||
/* see https://en.wikipedia.org/wiki/Initialization_vector */
|
||||
sk->ivdata = kmalloc(CIPHER_BLOCK_SIZE, GFP_KERNEL);
|
||||
if (!sk->ivdata) {
|
||||
pr_info("could not allocate ivdata\n");
|
||||
goto out;
|
||||
}
|
||||
get_random_bytes(sk->ivdata, CIPHER_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (!sk->scratchpad) {
|
||||
/* The text to be encrypted */
|
||||
sk->scratchpad = kmalloc(CIPHER_BLOCK_SIZE, GFP_KERNEL);
|
||||
if (!sk->scratchpad) {
|
||||
pr_info("could not allocate scratchpad\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
sprintf((char*)sk->scratchpad,"%s",plaintext);
|
||||
|
||||
sg_init_one(&sk->sg, sk->scratchpad, CIPHER_BLOCK_SIZE);
|
||||
skcipher_request_set_crypt(sk->req, &sk->sg, &sk->sg,
|
||||
CIPHER_BLOCK_SIZE, sk->ivdata);
|
||||
init_completion(&sk->result.completion);
|
||||
|
||||
/* encrypt data */
|
||||
ret = crypto_skcipher_encrypt(sk->req);
|
||||
ret = test_skcipher_result(sk, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
pr_info("Encryption request successful\n");
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cryptoapi_init(void)
|
||||
{
|
||||
/* The world's favourite password */
|
||||
char * password = "password123";
|
||||
|
||||
sk.tfm = NULL;
|
||||
sk.req = NULL;
|
||||
sk.scratchpad = NULL;
|
||||
sk.ciphertext = NULL;
|
||||
sk.ivdata = NULL;
|
||||
|
||||
test_skcipher_encrypt("Testing", password, &sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cryptoapi_exit(void)
|
||||
{
|
||||
test_skcipher_finish(&sk);
|
||||
}
|
||||
|
||||
module_init(cryptoapi_init);
|
||||
module_exit(cryptoapi_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Symmetric key encryption example");
|
||||
MODULE_LICENSE("GPL");
|
96
examples/devicemodel.c
Normal file
96
examples/devicemodel.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.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 initialisation code */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devicemodel_remove(struct platform_device *dev)
|
||||
{
|
||||
pr_info("devicemodel example removed\n");
|
||||
|
||||
/* Your device removal code */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &devicemodel_pm_ops,
|
||||
},
|
||||
.probe = devicemodel_probe,
|
||||
.remove = devicemodel_remove,
|
||||
};
|
||||
|
||||
static int 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 devicemodel_exit(void)
|
||||
{
|
||||
pr_info("devicemodel exit\n");
|
||||
platform_driver_unregister(&devicemodel_driver);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Linux Device Model example");
|
||||
|
||||
module_init(devicemodel_init);
|
||||
module_exit(devicemodel_exit);
|
76
examples/example_atomic.c
Normal file
76
examples/example_atomic.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.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, BYTE_TO_BINARY(word));
|
||||
}
|
||||
|
||||
static int example_atomic_init(void)
|
||||
{
|
||||
pr_info("example_atomic started\n");
|
||||
|
||||
atomic_add_subtract();
|
||||
atomic_bitwise();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_atomic_exit(void)
|
||||
{
|
||||
pr_info("example_atomic exit\n");
|
||||
}
|
||||
|
||||
module_init(example_atomic_init);
|
||||
module_exit(example_atomic_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Atomic operations example");
|
||||
MODULE_LICENSE("GPL");
|
40
examples/example_mutex.c
Normal file
40
examples/example_mutex.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
DEFINE_MUTEX(mymutex);
|
||||
|
||||
static int 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 example_mutex_exit(void)
|
||||
{
|
||||
pr_info("example_mutex exit\n");
|
||||
}
|
||||
|
||||
module_init(example_mutex_init);
|
||||
module_exit(example_mutex_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Mutex example");
|
||||
MODULE_LICENSE("GPL");
|
53
examples/example_rwlock.c
Normal file
53
examples/example_rwlock.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
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 example_rwlock_init(void)
|
||||
{
|
||||
pr_info("example_rwlock started\n");
|
||||
|
||||
example_read_lock();
|
||||
example_write_lock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_rwlock_exit(void)
|
||||
{
|
||||
pr_info("example_rwlock exit\n");
|
||||
}
|
||||
|
||||
module_init(example_rwlock_init);
|
||||
module_exit(example_rwlock_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Read/Write locks example");
|
||||
MODULE_LICENSE("GPL");
|
63
examples/example_spinlock.c
Normal file
63
examples/example_spinlock.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
DEFINE_SPINLOCK(sl_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 example_spinlock_init(void)
|
||||
{
|
||||
pr_info("example spinlock started\n");
|
||||
|
||||
example_spinlock_static();
|
||||
example_spinlock_dynamic();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_spinlock_exit(void)
|
||||
{
|
||||
pr_info("example spinlock exit\n");
|
||||
}
|
||||
|
||||
module_init(example_spinlock_init);
|
||||
module_exit(example_spinlock_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Spinlock example");
|
||||
MODULE_LICENSE("GPL");
|
35
examples/example_tasklet.c
Normal file
35
examples/example_tasklet.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
static void tasklet_fn(unsigned long data)
|
||||
{
|
||||
pr_info("Example tasklet starts\n");
|
||||
mdelay(5000);
|
||||
pr_info("Example tasklet ends\n");
|
||||
}
|
||||
|
||||
DECLARE_TASKLET(mytask, tasklet_fn, 0L);
|
||||
|
||||
static int 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 example_tasklet_exit(void)
|
||||
{
|
||||
pr_info("tasklet example exit\n");
|
||||
tasklet_kill(&mytask);
|
||||
}
|
||||
|
||||
module_init(example_tasklet_init);
|
||||
module_exit(example_tasklet_exit);
|
||||
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Tasklet example");
|
||||
MODULE_LICENSE("GPL");
|
22
examples/hello-1.c
Normal file
22
examples/hello-1.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* hello-1.c - The simplest kernel module.
|
||||
*/
|
||||
#include <linux/module.h> /* Needed by all modules */
|
||||
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
pr_info("Hello world 1.\n");
|
||||
|
||||
/*
|
||||
* A non 0 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");
|
23
examples/hello-2.c
Normal file
23
examples/hello-2.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* hello-2.c - Demonstrating the module_init() and module_exit() macros.
|
||||
* This is preferred over using init_module() and cleanup_module().
|
||||
*/
|
||||
#include <linux/module.h> /* Needed by all modules */
|
||||
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||
#include <linux/init.h> /* Needed for the macros */
|
||||
|
||||
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");
|
24
examples/hello-3.c
Normal file
24
examples/hello-3.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* hello-3.c - Illustrating the __init, __initdata and __exit macros.
|
||||
*/
|
||||
#include <linux/module.h> /* Needed by all modules */
|
||||
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||
#include <linux/init.h> /* Needed for the macros */
|
||||
|
||||
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");
|
25
examples/hello-4.c
Normal file
25
examples/hello-4.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* hello-4.c - Demonstrates module documentation.
|
||||
*/
|
||||
#include <linux/module.h> /* Needed by all modules */
|
||||
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||
#include <linux/init.h> /* Needed for the macros */
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("A sample driver");
|
||||
MODULE_SUPPORTED_DEVICE("testdevice");
|
||||
|
||||
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);
|
70
examples/hello-5.c
Normal file
70
examples/hello-5.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* hello-5.c - Demonstrates command line argument passing to a module.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/stat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Peter Jay Salzman");
|
||||
|
||||
static short int myshort = 1;
|
||||
static int myint = 420;
|
||||
static long int mylong = 9999;
|
||||
static char *mystring = "blah";
|
||||
static int myintArray[2] = { -1, -1 };
|
||||
static int arr_argc = 0;
|
||||
|
||||
/*
|
||||
* module_param(foo, int, 0000)
|
||||
* The first param is the parameters name
|
||||
* The second param is it's 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 < (sizeof myintArray / sizeof (int)); 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);
|
67
examples/hello-sysfs.c
Normal file
67
examples/hello-sysfs.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* hello-sysfs.c sysfs example
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
|
||||
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,
|
||||
char *buf, size_t count)
|
||||
{
|
||||
sscanf(buf, "%du", &myvariable);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static struct kobj_attribute myvariable_attribute =
|
||||
__ATTR(myvariable, 0660, myvariable_show,
|
||||
(void*)myvariable_store);
|
||||
|
||||
static int __init mymodule_init (void)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
pr_info("mymodule: initialised\n");
|
||||
|
||||
mymodule =
|
||||
kobject_create_and_add("mymodule", kernel_kobj);
|
||||
if (!mymodule)
|
||||
return -ENOMEM;
|
||||
|
||||
error = sysfs_create_file(mymodule, &myvariable_attribute.attr);
|
||||
if (error) {
|
||||
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);
|
151
examples/intrpt.c
Normal file
151
examples/intrpt.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* intrpt.c - Handling GPIO with interrupts
|
||||
*
|
||||
* Copyright (C) 2017 by Bob Mottram
|
||||
* 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/interrupt.h>
|
||||
|
||||
static int button_irqs[] = { -1, -1 };
|
||||
|
||||
/* Define GPIOs for LEDs.
|
||||
Change the numbers for the GPIO on your board. */
|
||||
static struct gpio leds[] = {
|
||||
{ 4, GPIOF_OUT_INIT_LOW, "LED 1" }
|
||||
};
|
||||
|
||||
/* Define GPIOs for BUTTONS
|
||||
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;
|
||||
}
|
||||
|
||||
int init_module()
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
/* register LED gpios */
|
||||
ret = gpio_request_array(leds, ARRAY_SIZE(leds));
|
||||
|
||||
if (ret) {
|
||||
pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register BUTTON gpios */
|
||||
ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));
|
||||
|
||||
if (ret) {
|
||||
pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
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);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
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);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
|
||||
ret = gpio_to_irq(buttons[1].gpio);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Unable to request IRQ: %d\n", ret);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
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);
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* cleanup what has been setup so far */
|
||||
fail3:
|
||||
free_irq(button_irqs[0], NULL);
|
||||
|
||||
fail2:
|
||||
gpio_free_array(buttons, ARRAY_SIZE(leds));
|
||||
|
||||
fail1:
|
||||
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cleanup_module()
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
/* free irqs */
|
||||
free_irq(button_irqs[0], NULL);
|
||||
free_irq(button_irqs[1], NULL);
|
||||
|
||||
/* turn all LEDs off */
|
||||
for (i = 0; i < ARRAY_SIZE(leds); i++)
|
||||
gpio_set_value(leds[i].gpio, 0);
|
||||
|
||||
/* unregister */
|
||||
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||
gpio_free_array(buttons, ARRAY_SIZE(buttons));
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Handle some GPIO interrupts");
|
205
examples/ioctl.c
Normal file
205
examples/ioctl.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
struct ioctl_arg {
|
||||
unsigned int reg;
|
||||
unsigned int val;
|
||||
};
|
||||
|
||||
/* Documentation/ioctl/ioctl-number.txt */
|
||||
#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 _IOW(IOC_MAGIC, 3, int)
|
||||
|
||||
#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 (!capable(CAP_SYS_ADMIN)) {
|
||||
retval = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
if (!access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
*/
|
||||
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:
|
||||
/*
|
||||
if (!access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd))) {
|
||||
retval = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
*/
|
||||
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:
|
||||
/*
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
*/
|
||||
ioctl_num = arg;
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = -ENOTTY;
|
||||
}
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 ioctl_init(void) {
|
||||
dev_t dev = MKDEV(test_ioctl_major, 0);
|
||||
int alloc_ret = 0;
|
||||
int cdev_ret = 0;
|
||||
alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME);
|
||||
|
||||
if (alloc_ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
test_ioctl_major = MAJOR(dev);
|
||||
cdev_init(&test_ioctl_cdev, &fops);
|
||||
cdev_ret = cdev_add(&test_ioctl_cdev, dev, num_of_dev);
|
||||
|
||||
if (cdev_ret) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
pr_alert("%s driver(major: %d) installed.\n", DRIVER_NAME, test_ioctl_major);
|
||||
return 0;
|
||||
error:
|
||||
|
||||
if (cdev_ret == 0) {
|
||||
cdev_del(&test_ioctl_cdev);
|
||||
}
|
||||
|
||||
if (alloc_ret == 0) {
|
||||
unregister_chrdev_region(dev, num_of_dev);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void 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_AUTHOR("Wang Chen Shu");
|
||||
MODULE_DESCRIPTION("This is test_ioctl module");
|
93
examples/kbleds.c
Normal file
93
examples/kbleds.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* kbleds.c - Blink keyboard leds until the module is unloaded.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/vt_kern.h> /* for fg_console */
|
||||
#include <linux/tty.h> /* For fg_console, MAX_NR_CONSOLES */
|
||||
#include <linux/kd.h> /* For KDSETLED */
|
||||
#include <linux/vt.h>
|
||||
#include <linux/console_struct.h> /* For vc_cons */
|
||||
|
||||
MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs.");
|
||||
MODULE_AUTHOR("Daniele Paolo Scarpazza");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct timer_list my_timer;
|
||||
struct tty_driver *my_driver;
|
||||
char 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:
|
||||
* /usr/src/linux/drivers/char/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:
|
||||
* /usr/src/linux/drivers/char/keyboard.c, function setledstate().
|
||||
*
|
||||
*/
|
||||
|
||||
static void my_timer_func(unsigned long ptr)
|
||||
{
|
||||
unsigned long *pstatus = (unsigned long *)ptr;
|
||||
struct tty_struct* t = vc_cons[fg_console].d->port.tty;
|
||||
|
||||
if (*pstatus == ALL_LEDS_ON)
|
||||
*pstatus = RESTORE_LEDS;
|
||||
else
|
||||
*pstatus = ALL_LEDS_ON;
|
||||
|
||||
(my_driver->ops->ioctl) (t, KDSETLED, *pstatus);
|
||||
|
||||
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 %lx\n", i,
|
||||
MAX_NR_CONSOLES, vc_cons[i].d->vc_num,
|
||||
(unsigned long)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 magic %x\n", my_driver->magic);
|
||||
|
||||
/*
|
||||
* Set up the LED blink timer the first time
|
||||
*/
|
||||
timer_setup(&my_timer, (void*)&my_timer_func, (unsigned long)&kbledstatus);
|
||||
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);
|
65
examples/other/cat_noblock.c
Normal file
65
examples/other/cat_noblock.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
/* cat_noblock.c - open a file and display its contents, but exit rather than
|
||||
* wait for input */
|
||||
/* Copyright (C) 1998 by Ori Pomerantz */
|
||||
|
||||
#include <stdio.h> /* standard I/O */
|
||||
#include <fcntl.h> /* for open */
|
||||
#include <unistd.h> /* for read */
|
||||
#include <stdlib.h> /* for exit */
|
||||
#include <errno.h> /* for errno */
|
||||
|
||||
#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(-1);
|
||||
}
|
||||
|
||||
/* Open the file for reading in non blocking mode */
|
||||
fd = open(argv[1], O_RDONLY | O_NONBLOCK);
|
||||
|
||||
/* If open failed */
|
||||
if (fd == -1) {
|
||||
if (errno = EAGAIN)
|
||||
puts("Open would block");
|
||||
else
|
||||
puts("Open failed");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Read the file and output its contents */
|
||||
do {
|
||||
int i;
|
||||
|
||||
/* 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(-1);
|
||||
}
|
||||
|
||||
/* Print the characters */
|
||||
if (bytes > 0) {
|
||||
for(i=0; i<bytes; i++)
|
||||
putchar(buffer[i]);
|
||||
}
|
||||
|
||||
/* While there are no errors and the file isn't over */
|
||||
} while (bytes > 0);
|
||||
return 0;
|
||||
}
|
108
examples/print_string.c
Normal file
108
examples/print_string.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* print_string.c - Send output to the tty we're running on, regardless if it's
|
||||
* through X11, telnet, etc. We do this by printing the string to the tty
|
||||
* associated with the current task.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h> /* For current */
|
||||
#include <linux/tty.h> /* For the tty declarations */
|
||||
#include <linux/version.h> /* For LINUX_VERSION_CODE */
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Peter Jay Salzman");
|
||||
|
||||
static void print_string(char *str)
|
||||
{
|
||||
struct tty_struct *my_tty;
|
||||
const struct tty_operations *ttyops;
|
||||
|
||||
/*
|
||||
* tty struct went into signal struct in 2.6.6
|
||||
*/
|
||||
#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,5) )
|
||||
/*
|
||||
* The tty for the current task
|
||||
*/
|
||||
my_tty = current->tty;
|
||||
#else
|
||||
/*
|
||||
* The tty for the current task, for 2.6.6+ kernels
|
||||
*/
|
||||
my_tty = get_current_tty();
|
||||
#endif
|
||||
ttyops = my_tty->driver->ops;
|
||||
|
||||
/*
|
||||
* If my_tty is NULL, the current task has no tty you can print to
|
||||
* (ie, if it's a daemon). If so, there's nothing we can do.
|
||||
*/
|
||||
if (my_tty != NULL) {
|
||||
|
||||
/*
|
||||
* 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 controls
|
||||
* whether the function receives a string from kernel
|
||||
* memory (false, 0) or from user memory (true, non zero).
|
||||
* BTW: this param has been removed in Kernels > 2.6.9
|
||||
* The (2nd) 3rd parameter is a pointer to a string.
|
||||
* The (3rd) 4th 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 */
|
||||
#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9) )
|
||||
0, /* Don't take the string
|
||||
from user space */
|
||||
#endif
|
||||
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't just use \n, because it wouldn't 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 requirs both a LF and a CR.
|
||||
*/
|
||||
|
||||
#if ( LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9) )
|
||||
(ttyops->write) (my_tty, 0, "\015\012", 2);
|
||||
#else
|
||||
(ttyops->write) (my_tty, "\015\012", 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
51
examples/procfs1.c
Normal file
51
examples/procfs1.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
procfs1.c
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define procfs_name "helloworld"
|
||||
|
||||
struct proc_dir_entry *Our_Proc_File;
|
||||
|
||||
|
||||
ssize_t procfile_read(struct file *filePointer,char *buffer,
|
||||
size_t buffer_length, loff_t * offset)
|
||||
{
|
||||
int ret=0;
|
||||
if(strlen(buffer) ==0) {
|
||||
pr_info("procfile read %s\n",filePointer->f_path.dentry->d_name.name);
|
||||
ret=copy_to_user(buffer,"HelloWorld!\n",sizeof("HelloWorld!\n"));
|
||||
ret=sizeof("HelloWorld!\n");
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_file_fops = {
|
||||
.proc_read = procfile_read,
|
||||
};
|
||||
|
||||
int init_module()
|
||||
{
|
||||
Our_Proc_File = proc_create(procfs_name,0644,NULL,&proc_file_fops);
|
||||
if(NULL==Our_Proc_File) {
|
||||
proc_remove(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;
|
||||
}
|
||||
|
||||
void cleanup_module()
|
||||
{
|
||||
proc_remove(Our_Proc_File);
|
||||
pr_info("/proc/%s removed\n", procfs_name);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
99
examples/procfs2.c
Normal file
99
examples/procfs2.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* procfs2.c - create a "file" in /proc
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h> /* Specifically, a module */
|
||||
#include <linux/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
|
||||
#include <linux/uaccess.h> /* for copy_from_user */
|
||||
|
||||
#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
|
||||
*
|
||||
*/
|
||||
ssize_t procfile_read(struct file *filePointer,char *buffer,
|
||||
size_t buffer_length, loff_t * offset)
|
||||
{
|
||||
int ret=0;
|
||||
if(strlen(buffer) ==0) {
|
||||
pr_info("procfile read %s\n",filePointer->f_path.dentry->d_name.name);
|
||||
ret=copy_to_user(buffer,"HelloWorld!\n",sizeof("HelloWorld!\n"));
|
||||
ret=sizeof("HelloWorld!\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is called with the /proc file is written
|
||||
*
|
||||
*/
|
||||
static ssize_t procfile_write(struct file *file, const char *buff,
|
||||
size_t len, loff_t *off)
|
||||
{
|
||||
procfs_buffer_size = len;
|
||||
if (procfs_buffer_size > PROCFS_MAX_SIZE)
|
||||
procfs_buffer_size = PROCFS_MAX_SIZE;
|
||||
|
||||
if (copy_from_user(procfs_buffer, buff, procfs_buffer_size))
|
||||
return -EFAULT;
|
||||
|
||||
procfs_buffer[procfs_buffer_size] = '\0';
|
||||
return procfs_buffer_size;
|
||||
}
|
||||
|
||||
static const struct proc_ops proc_file_fops = {
|
||||
.proc_read = procfile_read,
|
||||
.proc_write = procfile_write,
|
||||
};
|
||||
|
||||
/**
|
||||
*This function is called when the module is loaded
|
||||
*
|
||||
*/
|
||||
int init_module()
|
||||
{
|
||||
Our_Proc_File = proc_create(PROCFS_NAME,0644,NULL,&proc_file_fops);
|
||||
if(NULL==Our_Proc_File) {
|
||||
proc_remove(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;
|
||||
}
|
||||
|
||||
/**
|
||||
*This function is called when the module is unloaded
|
||||
*
|
||||
*/
|
||||
void cleanup_module()
|
||||
{
|
||||
proc_remove(Our_Proc_File);
|
||||
pr_info("/proc/%s removed\n", PROCFS_NAME);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
85
examples/procfs3.c
Normal file
85
examples/procfs3.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
procfs3.c
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define PROCFS_MAX_SIZE 2048
|
||||
#define PROCFS_ENTRY_FILENAME "buffer2k"
|
||||
|
||||
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 *buffer,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
static int finished = 0;
|
||||
if(finished)
|
||||
{
|
||||
pr_debug("procfs_read: END\n");
|
||||
finished = 0;
|
||||
return 0;
|
||||
}
|
||||
finished = 1;
|
||||
if(copy_to_user(buffer, procfs_buffer, procfs_buffer_size))
|
||||
return -EFAULT;
|
||||
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 *buffer,
|
||||
size_t len, loff_t *off)
|
||||
{
|
||||
if(len>PROCFS_MAX_SIZE)
|
||||
procfs_buffer_size = PROCFS_MAX_SIZE;
|
||||
else
|
||||
procfs_buffer_size = len;
|
||||
if(copy_from_user(procfs_buffer, buffer, procfs_buffer_size))
|
||||
return -EFAULT;
|
||||
pr_debug("procfs_write: write %lu bytes\n", procfs_buffer_size);
|
||||
return procfs_buffer_size;
|
||||
}
|
||||
int procfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
try_module_get(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
int procfs_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
module_put(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
int init_module()
|
||||
{
|
||||
Our_Proc_File = proc_create(PROCFS_ENTRY_FILENAME, 0644, NULL,&File_Ops_4_Our_Proc_File);
|
||||
if(Our_Proc_File == NULL)
|
||||
{
|
||||
remove_proc_entry(PROCFS_ENTRY_FILENAME, 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;
|
||||
}
|
||||
void cleanup_module()
|
||||
{
|
||||
remove_proc_entry(PROCFS_ENTRY_FILENAME, NULL);
|
||||
pr_debug("/proc/%s removed\n", PROCFS_ENTRY_FILENAME);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
133
examples/procfs4.c
Normal file
133
examples/procfs4.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* procfs4.c - create a "file" in /proc
|
||||
* This program uses the seq_file library to manage the /proc file.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 proc fs */
|
||||
#include <linux/seq_file.h> /* for seq_file */
|
||||
|
||||
#define PROC_NAME "iter"
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
else {
|
||||
/* no => it's 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's called untill 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
|
||||
*
|
||||
*/
|
||||
static struct proc_ops my_file_ops = {
|
||||
.proc_open = my_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function is called when the module is loaded
|
||||
*
|
||||
*/
|
||||
int init_module(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
entry = proc_create(PROC_NAME, 0, NULL, &my_file_ops);
|
||||
if(entry == NULL)
|
||||
{
|
||||
remove_proc_entry(PROC_NAME, NULL);
|
||||
pr_debug("Error: Could not initialize /proc/%s\n", PROC_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called when the module is unloaded.
|
||||
*
|
||||
*/
|
||||
void cleanup_module(void)
|
||||
{
|
||||
remove_proc_entry(PROC_NAME, NULL);
|
||||
pr_debug("/proc/%s removed\n", PROC_NAME);
|
||||
}
|
29
examples/sched.c
Normal file
29
examples/sched.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.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");
|
||||
}
|
||||
|
||||
int init_module()
|
||||
{
|
||||
queue = alloc_workqueue("HELLOWORLD", WQ_UNBOUND, 1);
|
||||
INIT_WORK(&work, work_handler);
|
||||
schedule_work(&work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cleanup_module()
|
||||
{
|
||||
destroy_workqueue(queue);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bob Mottram");
|
||||
MODULE_DESCRIPTION("Workqueue example");
|
267
examples/sleep.c
Normal file
267
examples/sleep.c
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/module.h> /* Specifically, a module */
|
||||
#include <linux/proc_fs.h> /* Necessary because we use proc fs */
|
||||
#include <linux/sched.h> /* For putting processes to sleep and
|
||||
waking them up */
|
||||
#include <linux/uaccess.h> /* for get_user and put_user */
|
||||
|
||||
/*
|
||||
* The module's file functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 *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 message[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;
|
||||
}
|
||||
|
||||
/*
|
||||
* If you don't understand this by now, you're hopeless as a kernel
|
||||
* programmer.
|
||||
*/
|
||||
sprintf(message, "Last input:%s\n", Message);
|
||||
for (i = 0; i < len && message[i]; i++)
|
||||
put_user(message[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 *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
|
||||
*/
|
||||
int Already_Open = 0;
|
||||
|
||||
/*
|
||||
* Queue of processes who want our file
|
||||
*/
|
||||
DECLARE_WAIT_QUEUE_HEAD(WaitQ);
|
||||
/*
|
||||
* Called when the /proc file is opened
|
||||
*/
|
||||
static int module_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/*
|
||||
* If the file's flags include O_NONBLOCK, it means the process doesn't
|
||||
* want to wait for the file. In this case, if the file is already
|
||||
* open, we should fail with -EAGAIN, meaning "you'll have to try
|
||||
* again", instead of blocking a process which would rather stay awake.
|
||||
*/
|
||||
if ((file->f_flags & O_NONBLOCK) && Already_Open)
|
||||
return -EAGAIN;
|
||||
|
||||
/*
|
||||
* This is the correct place for try_module_get(THIS_MODULE) because
|
||||
* if a process is in the loop, which is within the kernel module,
|
||||
* the kernel module must not be removed.
|
||||
*/
|
||||
try_module_get(THIS_MODULE);
|
||||
|
||||
/*
|
||||
* If the file is already open, wait until it isn't
|
||||
*/
|
||||
|
||||
while (Already_Open) {
|
||||
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, !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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Emmanuel Papirakis:
|
||||
*
|
||||
* This is a little update to work with 2.2.*. Signals now are contained in
|
||||
* two words (64 bits) and are stored in a structure that contains an array of
|
||||
* two unsigned longs. We now have to make 2 checks in our if.
|
||||
*
|
||||
* Ori Pomerantz:
|
||||
*
|
||||
* Nobody promised me they'll never use more than 64 bits, or that this book
|
||||
* won't be used for a version of Linux with a word size of 16 bits. This code
|
||||
* would work in any case.
|
||||
*/
|
||||
for (i = 0; i < _NSIG_WORDS && !is_sig; i++)
|
||||
is_sig =
|
||||
current->pending.signal.sig[i] & ~current->
|
||||
blocked.sig[i];
|
||||
|
||||
if (is_sig) {
|
||||
/*
|
||||
* It's important to put module_put(THIS_MODULE) here,
|
||||
* because for processes where the open is interrupted
|
||||
* there will never be a corresponding close. If we
|
||||
* don't decrement the usage count here, we will be
|
||||
* left with a positive usage count which we'll have no
|
||||
* way to bring down to zero, giving us an immortal
|
||||
* module, which can only be killed by rebooting
|
||||
* the machine.
|
||||
*/
|
||||
module_put(THIS_MODULE);
|
||||
return -EINTR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got here, Already_Open must be zero
|
||||
*/
|
||||
|
||||
/*
|
||||
* Open the file
|
||||
*/
|
||||
Already_Open = 1;
|
||||
return 0; /* Allow the access */
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the /proc file is closed
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
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.
|
||||
*/
|
||||
static 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 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Module initialization and cleanup
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initialize the module - register the proc file
|
||||
*/
|
||||
|
||||
int init_module()
|
||||
{
|
||||
Our_Proc_File = proc_create(PROC_ENTRY_FILENAME, 0644, NULL, &File_Ops_4_Our_Proc_File);
|
||||
if(Our_Proc_File == NULL)
|
||||
{
|
||||
remove_proc_entry(PROC_ENTRY_FILENAME, 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/test created\n");
|
||||
|
||||
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.
|
||||
*/
|
||||
void cleanup_module()
|
||||
{
|
||||
remove_proc_entry(PROC_ENTRY_FILENAME, NULL);
|
||||
pr_debug("/proc/%s removed\n", PROC_ENTRY_FILENAME);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
14
examples/start.c
Normal file
14
examples/start.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* start.c - Illustration of multi filed modules
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h> /* We're 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");
|
13
examples/stop.c
Normal file
13
examples/stop.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* stop.c - Illustration of multi filed modules
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/module.h> /* Specifically, a module */
|
||||
|
||||
void cleanup_module()
|
||||
{
|
||||
pr_info("Short is the life of a kernel module\n");
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
155
examples/syscall.c
Normal file
155
examples/syscall.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* syscall.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)
|
||||
*
|
||||
* Based on example by Peter Jay Salzman and
|
||||
* https://bbs.archlinux.org/viewtopic.php?id=139406
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/paravirt.h>
|
||||
#include <linux/moduleparam.h> /* which will have params */
|
||||
#include <linux/unistd.h> /* The list of system calls */
|
||||
|
||||
/*
|
||||
* For the current (process) structure, we need
|
||||
* this to know who the current user is.
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
unsigned long **sys_call_table;
|
||||
unsigned long original_cr0;
|
||||
|
||||
/*
|
||||
* UID we want to spy on - will be filled from the
|
||||
* command line
|
||||
*/
|
||||
static int uid;
|
||||
module_param(uid, int, 0644);
|
||||
|
||||
/*
|
||||
* A pointer to the original system call. The reason
|
||||
* we keep this, rather than call the original function
|
||||
* (sys_open), 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_open before us, then when we're inserted
|
||||
* we'll call the function in that module - and it
|
||||
* might be removed before we are.
|
||||
*
|
||||
* Another reason for this is that we can't get sys_open.
|
||||
* It's a static variable, so it is not exported.
|
||||
*/
|
||||
asmlinkage int (*original_call) (const char *, int, int);
|
||||
|
||||
/*
|
||||
* The function we'll replace sys_open (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's at fs/open.c).
|
||||
*
|
||||
* In theory, this means that we're 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).
|
||||
*/
|
||||
asmlinkage int our_sys_open(const char *filename, int flags, int mode)
|
||||
{
|
||||
int i = 0;
|
||||
char ch;
|
||||
|
||||
/*
|
||||
* Report the file, if relevant
|
||||
*/
|
||||
pr_info("Opened file by %d: ", uid);
|
||||
do {
|
||||
get_user(ch, filename + i);
|
||||
i++;
|
||||
pr_info("%c", ch);
|
||||
} while (ch != 0);
|
||||
pr_info("\n");
|
||||
|
||||
/*
|
||||
* Call the original sys_open - otherwise, we lose
|
||||
* the ability to open files
|
||||
*/
|
||||
return original_call(filename, flags, mode);
|
||||
}
|
||||
|
||||
static unsigned long **aquire_sys_call_table(void)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static int __init syscall_start(void)
|
||||
{
|
||||
if(!(sys_call_table = aquire_sys_call_table()))
|
||||
return -1;
|
||||
|
||||
original_cr0 = read_cr0();
|
||||
|
||||
write_cr0(original_cr0 & ~0x00010000);
|
||||
|
||||
/* keep track of the original open function */
|
||||
original_call = (void*)sys_call_table[__NR_open];
|
||||
|
||||
/* use our open function instead */
|
||||
sys_call_table[__NR_open] = (unsigned long *)our_sys_open;
|
||||
|
||||
write_cr0(original_cr0);
|
||||
|
||||
pr_info("Spying on UID:%d\n", uid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit syscall_end(void)
|
||||
{
|
||||
if(!sys_call_table) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the system call back to normal
|
||||
*/
|
||||
if (sys_call_table[__NR_open] != (unsigned long *)our_sys_open) {
|
||||
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");
|
||||
}
|
||||
|
||||
write_cr0(original_cr0 & ~0x00010000);
|
||||
sys_call_table[__NR_open] = (unsigned long *)original_call;
|
||||
write_cr0(original_cr0);
|
||||
|
||||
msleep(2000);
|
||||
}
|
||||
|
||||
module_init(syscall_start);
|
||||
module_exit(syscall_end);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
BIN
img/seq_file.png
Normal file
BIN
img/seq_file.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
34
lib/codeblock.tex
Normal file
34
lib/codeblock.tex
Normal file
|
@ -0,0 +1,34 @@
|
|||
\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,
|
||||
breaklines,
|
||||
breakafter=d,
|
||||
linenos
|
||||
}
|
Loading…
Reference in New Issue
Block a user