├── .gitignore ├── 01_simple_LKM ├── Makefile └── mymodule.c ├── 02_dev_nr ├── Makefile ├── README.md ├── dev_nr.c ├── test └── test.c ├── 03_read_write ├── Makefile └── read_write.c ├── 04_gpio_driver ├── Makefile └── gpio_driver.c ├── 05_lcd_driver ├── Makefile └── lcd_driver.c ├── 06_pwm_driver ├── Makefile └── pwm_driver.c ├── 07_BMP280 ├── Makefile └── bmp280.c ├── 08_timer ├── Makefile └── mytimer.c ├── 09_hrtimer ├── Makefile └── my_hrtimer.c ├── 10_spi_bmp280 ├── Makefile ├── README.md ├── spi_bmp280.c └── spidev_disabler.dts ├── 11_gpio_irq ├── Makefile └── gpio_irq.c ├── 12_parameters ├── Makefile └── my_params.c ├── 13_ioctl ├── Makefile ├── README.md ├── ioctl_example.c ├── ioctl_test.h └── test.c ├── 14_Kernel_Threads ├── Makefile └── kthread.c ├── 15_Sending_Signals ├── Makefile ├── gpio_irq_signal.c └── testapp.c ├── 16_Poll ├── Makefile ├── gpio_irq_poll.c └── testapp.c ├── 17_waitqueue ├── Makefile └── waitqueue.c ├── 18_procfs ├── Makefile └── procfs_test.c ├── 19_sysfs ├── Makefile └── sysfs_test.c ├── 20_dt_probe ├── Makefile ├── dt_probe.c └── testoverlay.dts ├── 21_dt_gpio ├── Makefile ├── dt_gpio.c └── testoverlay.dts ├── 22_dt_i2c ├── Atmega_I2C_ADC │ ├── I2CSlave.c │ ├── I2CSlave.h │ ├── LICENSE │ ├── README.md │ ├── main.c │ └── makefile ├── Makefile ├── dt_i2c.c └── testoverlay.dts ├── 23_malloc ├── Makefile └── alloc_test.c ├── 24_serdev ├── Makefile ├── README.md ├── serdev_echo.c └── serdev_overlay.dts ├── 25_dt_iio ├── Atmega_I2C_ADC │ ├── I2CSlave.c │ ├── I2CSlave.h │ ├── LICENSE │ ├── README.md │ ├── main.c │ └── makefile ├── Makefile ├── README.md ├── dt_iio.c └── testoverlay.dts ├── 26_dt_spi ├── Atmega_SPI_ADC │ ├── Makefile │ └── main.c ├── Makefile ├── README.md ├── dt_spi.c └── testoverlay.dts ├── 27_misc_device ├── Makefile └── misc_test.c ├── 28_mutex ├── Makefile └── mymutex.c ├── 29_completion ├── Makefile └── mycompletion.c ├── 30_dma_memcpy ├── Makefile └── my_dma_memcpy.c ├── 31_file_access ├── Makefile └── file_access.c ├── 32_mmap ├── Makefile ├── my_mmap.c └── test.c ├── 33_list ├── Makefile └── mylist.c ├── 34_my_cdev ├── Makefile └── my_cdev.c ├── 35_priv_data ├── Makefile ├── a.out ├── priv_data.c └── test.c ├── 36_i2c_driver ├── Makefile └── my_i2c_driver.c ├── 38_log_levels ├── Makefile └── log_levels.c ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # NOTE! Don't add files that are generated in specific 3 | # subdirectories here. Add them in the ".gitignore" file 4 | # in that subdirectory instead. 5 | # 6 | # NOTE! Please use 'git ls-files -i --exclude-standard' 7 | # command after changing this file, to see if there are 8 | # any tracked files which get ignored after the change. 9 | # 10 | # Normal rules 11 | # 12 | .* 13 | *.o 14 | *.o.* 15 | *.a 16 | *.s 17 | *.ko 18 | *.so 19 | *.so.dbg 20 | *.mod.c 21 | *.mod 22 | *.i 23 | *.lst 24 | *.symtypes 25 | *.order 26 | modules.builtin 27 | *.elf 28 | *.bin 29 | *.gz 30 | *.bz2 31 | *.lzma 32 | *.xz 33 | *.lzo 34 | *.patch 35 | *.gcno 36 | *.json 37 | 38 | # 39 | # Top-level generic files 40 | # 41 | /tags 42 | /TAGS 43 | /linux 44 | /vmlinux 45 | /vmlinuz 46 | /System.map 47 | /Module.markers 48 | /Module.symvers 49 | *.symvers 50 | 51 | # 52 | # git files that we don't want to ignore even it they are dot-files 53 | # 54 | !.gitignore 55 | !.mailmap 56 | 57 | # 58 | # Generated include files 59 | # 60 | include/config 61 | include/linux/version.h 62 | include/generated 63 | arch/*/include/generated 64 | 65 | # stgit generated dirs 66 | patches-* 67 | 68 | # quilt's files 69 | patches 70 | series 71 | 72 | # cscope files 73 | cscope.* 74 | ncscope.* 75 | 76 | # gnu global files 77 | GPATH 78 | GRTAGS 79 | GSYMS 80 | GTAGS 81 | 82 | *.orig 83 | *~ 84 | \#*# 85 | >>>>>>> 7c03a7af5a9636b27a19ebd9eef8be0b622e0150 86 | -------------------------------------------------------------------------------- /01_simple_LKM/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += mymodule.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /01_simple_LKM/mymodule.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* Meta Information */ 5 | MODULE_LICENSE("GPL"); 6 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 7 | MODULE_DESCRIPTION("A hello world LKM"); 8 | 9 | /** 10 | * @brief This function is called, when the module is loaded into the kernel 11 | */ 12 | static int __init my_init(void) { 13 | printk("Hello, Kernel!\n"); 14 | return 0; 15 | } 16 | 17 | /** 18 | * @brief This function is called, when the module is removed from the kernel 19 | */ 20 | static void __exit my_exit(void) { 21 | printk("Goodbye, Kernel\n"); 22 | } 23 | 24 | module_init(my_init); 25 | module_exit(my_exit); 26 | 27 | 28 | -------------------------------------------------------------------------------- /02_dev_nr/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dev_nr.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /02_dev_nr/README.md: -------------------------------------------------------------------------------- 1 | To run this example create a device file with: 2 | 3 | 4 | ~~~~~ 5 | sudo mknod /dev/mydevice c 64 0 6 | 7 | ~~~~~ 8 | 9 | -------------------------------------------------------------------------------- /02_dev_nr/dev_nr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Meta Information */ 6 | MODULE_LICENSE("GPL"); 7 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 8 | MODULE_DESCRIPTION("Registers a device nr. and implement some callback functions"); 9 | 10 | 11 | /** 12 | * @brief This function is called, when the device file is opened 13 | */ 14 | static int driver_open(struct inode *device_file, struct file *instance) { 15 | printk("dev_nr - open was called!\n"); 16 | return 0; 17 | } 18 | 19 | /** 20 | * @brief This function is called, when the device file is closed 21 | */ 22 | static int driver_close(struct inode *device_file, struct file *instance) { 23 | printk("dev_nr - close was called!\n"); 24 | return 0; 25 | } 26 | 27 | static struct file_operations fops = { 28 | .owner = THIS_MODULE, 29 | .open = driver_open, 30 | .release = driver_close 31 | }; 32 | 33 | #define MYMAJOR 64 34 | 35 | /** 36 | * @brief This function is called, when the module is loaded into the kernel 37 | */ 38 | static int __init ModuleInit(void) { 39 | int retval; 40 | printk("Hello, Kernel!\n"); 41 | /* register device nr. */ 42 | retval = register_chrdev(MYMAJOR, "my_dev_nr", &fops); 43 | if(retval == 0) { 44 | printk("dev_nr - registered Device number Major: %d, Minor: %d\n", MYMAJOR, 0); 45 | } 46 | else if(retval > 0) { 47 | printk("dev_nr - registered Device number Major: %d, Minor: %d\n", retval>>20, retval&0xfffff); 48 | } 49 | else { 50 | printk("Could not register device number!\n"); 51 | return -1; 52 | } 53 | return 0; 54 | } 55 | 56 | /** 57 | * @brief This function is called, when the module is removed from the kernel 58 | */ 59 | static void __exit ModuleExit(void) { 60 | unregister_chrdev(MYMAJOR, "my_dev_nr"); 61 | printk("Goodbye, Kernel\n"); 62 | } 63 | 64 | module_init(ModuleInit); 65 | module_exit(ModuleExit); 66 | 67 | 68 | -------------------------------------------------------------------------------- /02_dev_nr/test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Johannes4Linux/Linux_Driver_Tutorial_legacy/8eab78e38580bbb63fe2466ee755709e5a813b16/02_dev_nr/test -------------------------------------------------------------------------------- /02_dev_nr/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | int dev = open("/dev/mydevice", O_RDONLY); 8 | if(dev == -1) { 9 | printf("Opening was not possible!\n"); 10 | return -1; 11 | } 12 | printf("Opening was successfull!\n"); 13 | close(dev); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /03_read_write/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += read_write.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /03_read_write/read_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Meta Information */ 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 10 | MODULE_DESCRIPTION("Registers a device nr. and implement some callback functions"); 11 | 12 | /* Buffer for data */ 13 | static char buffer[255]; 14 | static int buffer_pointer = 0; 15 | 16 | /* Variables for device and device class */ 17 | static dev_t my_device_nr; 18 | static struct class *my_class; 19 | static struct cdev my_device; 20 | 21 | #define DRIVER_NAME "dummydriver" 22 | #define DRIVER_CLASS "MyModuleClass" 23 | 24 | /** 25 | * @brief Read data out of the buffer 26 | */ 27 | static ssize_t driver_read(struct file *File, char *user_buffer, size_t count, loff_t *offs) { 28 | int to_copy, not_copied, delta; 29 | 30 | /* Get amount of data to copy */ 31 | to_copy = min(count, buffer_pointer); 32 | 33 | /* Copy data to user */ 34 | not_copied = copy_to_user(user_buffer, buffer, to_copy); 35 | 36 | /* Calculate data */ 37 | delta = to_copy - not_copied; 38 | 39 | return delta; 40 | } 41 | 42 | /** 43 | * @brief Write data to buffer 44 | */ 45 | static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 46 | int to_copy, not_copied, delta; 47 | 48 | /* Get amount of data to copy */ 49 | to_copy = min(count, sizeof(buffer)); 50 | 51 | /* Copy data to user */ 52 | not_copied = copy_from_user(buffer, user_buffer, to_copy); 53 | buffer_pointer = to_copy; 54 | 55 | /* Calculate data */ 56 | delta = to_copy - not_copied; 57 | 58 | return delta; 59 | } 60 | 61 | /** 62 | * @brief This function is called, when the device file is opened 63 | */ 64 | static int driver_open(struct inode *device_file, struct file *instance) { 65 | printk("dev_nr - open was called!\n"); 66 | return 0; 67 | } 68 | 69 | /** 70 | * @brief This function is called, when the device file is opened 71 | */ 72 | static int driver_close(struct inode *device_file, struct file *instance) { 73 | printk("dev_nr - close was called!\n"); 74 | return 0; 75 | } 76 | 77 | static struct file_operations fops = { 78 | .owner = THIS_MODULE, 79 | .open = driver_open, 80 | .release = driver_close, 81 | .read = driver_read, 82 | .write = driver_write 83 | }; 84 | 85 | /** 86 | * @brief This function is called, when the module is loaded into the kernel 87 | */ 88 | static int __init ModuleInit(void) { 89 | int retval; 90 | printk("Hello, Kernel!\n"); 91 | 92 | /* Allocate a device nr */ 93 | if( alloc_chrdev_region(&my_device_nr, 0, 1, DRIVER_NAME) < 0) { 94 | printk("Device Nr. could not be allocated!\n"); 95 | return -1; 96 | } 97 | printk("read_write - Device Nr. Major: %d, Minor: %d was registered!\n", my_device_nr >> 20, my_device_nr & 0xfffff); 98 | 99 | /* Create device class */ 100 | if((my_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) { 101 | printk("Device class can not be created!\n"); 102 | goto ClassError; 103 | } 104 | 105 | /* create device file */ 106 | if(device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL) { 107 | printk("Can not create device file!\n"); 108 | goto FileError; 109 | } 110 | 111 | /* Initialize device file */ 112 | cdev_init(&my_device, &fops); 113 | 114 | /* Regisering device to kernel */ 115 | if(cdev_add(&my_device, my_device_nr, 1) == -1) { 116 | printk("Registering of device to kernel failed!\n"); 117 | goto AddError; 118 | } 119 | 120 | return 0; 121 | AddError: 122 | device_destroy(my_class, my_device_nr); 123 | FileError: 124 | class_destroy(my_class); 125 | ClassError: 126 | unregister_chrdev_region(my_device_nr, 1); 127 | return -1; 128 | } 129 | 130 | /** 131 | * @brief This function is called, when the module is removed from the kernel 132 | */ 133 | static void __exit ModuleExit(void) { 134 | cdev_del(&my_device); 135 | device_destroy(my_class, my_device_nr); 136 | class_destroy(my_class); 137 | unregister_chrdev_region(my_device_nr, 1); 138 | printk("Goodbye, Kernel\n"); 139 | } 140 | 141 | module_init(ModuleInit); 142 | module_exit(ModuleExit); 143 | 144 | 145 | -------------------------------------------------------------------------------- /04_gpio_driver/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += gpio_driver.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /04_gpio_driver/gpio_driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Meta Information */ 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 11 | MODULE_DESCRIPTION("A simple gpio driver for setting a LED and reading a button"); 12 | 13 | /* Variables for device and device class */ 14 | static dev_t my_device_nr; 15 | static struct class *my_class; 16 | static struct cdev my_device; 17 | 18 | #define DRIVER_NAME "my_gpio_driver" 19 | #define DRIVER_CLASS "MyModuleClass" 20 | 21 | /** 22 | * @brief Read data out of the buffer 23 | */ 24 | static ssize_t driver_read(struct file *File, char *user_buffer, size_t count, loff_t *offs) { 25 | int to_copy, not_copied, delta; 26 | char tmp[3] = " \n"; 27 | 28 | /* Get amount of data to copy */ 29 | to_copy = min(count, sizeof(tmp)); 30 | 31 | /* Read value of button */ 32 | printk("Value of button: %d\n", gpio_get_value(17)); 33 | tmp[0] = gpio_get_value(17) + '0'; 34 | 35 | /* Copy data to user */ 36 | not_copied = copy_to_user(user_buffer, &tmp, to_copy); 37 | 38 | /* Calculate data */ 39 | delta = to_copy - not_copied; 40 | 41 | return delta; 42 | } 43 | 44 | /** 45 | * @brief Write data to buffer 46 | */ 47 | static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 48 | int to_copy, not_copied, delta; 49 | char value; 50 | 51 | /* Get amount of data to copy */ 52 | to_copy = min(count, sizeof(value)); 53 | 54 | /* Copy data to user */ 55 | not_copied = copy_from_user(&value, user_buffer, to_copy); 56 | 57 | /* Setting the LED */ 58 | switch(value) { 59 | case '0': 60 | gpio_set_value(4, 0); 61 | break; 62 | case '1': 63 | gpio_set_value(4, 1); 64 | break; 65 | default: 66 | printk("Invalid Input!\n"); 67 | break; 68 | } 69 | 70 | /* Calculate data */ 71 | delta = to_copy - not_copied; 72 | 73 | return delta; 74 | } 75 | 76 | /** 77 | * @brief This function is called, when the device file is opened 78 | */ 79 | static int driver_open(struct inode *device_file, struct file *instance) { 80 | printk("dev_nr - open was called!\n"); 81 | return 0; 82 | } 83 | 84 | /** 85 | * @brief This function is called, when the device file is opened 86 | */ 87 | static int driver_close(struct inode *device_file, struct file *instance) { 88 | printk("dev_nr - close was called!\n"); 89 | return 0; 90 | } 91 | 92 | static struct file_operations fops = { 93 | .owner = THIS_MODULE, 94 | .open = driver_open, 95 | .release = driver_close, 96 | .read = driver_read, 97 | .write = driver_write 98 | }; 99 | 100 | /** 101 | * @brief This function is called, when the module is loaded into the kernel 102 | */ 103 | static int __init ModuleInit(void) { 104 | printk("Hello, Kernel!\n"); 105 | 106 | /* Allocate a device nr */ 107 | if( alloc_chrdev_region(&my_device_nr, 0, 1, DRIVER_NAME) < 0) { 108 | printk("Device Nr. could not be allocated!\n"); 109 | return -1; 110 | } 111 | printk("read_write - Device Nr. Major: %d, Minor: %d was registered!\n", my_device_nr >> 20, my_device_nr && 0xfffff); 112 | 113 | /* Create device class */ 114 | if((my_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) { 115 | printk("Device class can not be created!\n"); 116 | goto ClassError; 117 | } 118 | 119 | /* create device file */ 120 | if(device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL) { 121 | printk("Can not create device file!\n"); 122 | goto FileError; 123 | } 124 | 125 | /* Initialize device file */ 126 | cdev_init(&my_device, &fops); 127 | 128 | /* Regisering device to kernel */ 129 | if(cdev_add(&my_device, my_device_nr, 1) == -1) { 130 | printk("Registering of device to kernel failed!\n"); 131 | goto AddError; 132 | } 133 | 134 | /* GPIO 4 init */ 135 | if(gpio_request(4, "rpi-gpio-4")) { 136 | printk("Can not allocate GPIO 4\n"); 137 | goto AddError; 138 | } 139 | 140 | /* Set GPIO 4 direction */ 141 | if(gpio_direction_output(4, 0)) { 142 | printk("Can not set GPIO 4 to output!\n"); 143 | goto Gpio4Error; 144 | } 145 | 146 | /* GPIO 17 init */ 147 | if(gpio_request(17, "rpi-gpio-17")) { 148 | printk("Can not allocate GPIO 17\n"); 149 | goto Gpio4Error; 150 | } 151 | 152 | /* Set GPIO 17 direction */ 153 | if(gpio_direction_input(17)) { 154 | printk("Can not set GPIO 17 to input!\n"); 155 | goto Gpio17Error; 156 | } 157 | 158 | 159 | return 0; 160 | Gpio17Error: 161 | gpio_free(17); 162 | Gpio4Error: 163 | gpio_free(4); 164 | AddError: 165 | device_destroy(my_class, my_device_nr); 166 | FileError: 167 | class_destroy(my_class); 168 | ClassError: 169 | unregister_chrdev_region(my_device_nr, 1); 170 | return -1; 171 | } 172 | 173 | /** 174 | * @brief This function is called, when the module is removed from the kernel 175 | */ 176 | static void __exit ModuleExit(void) { 177 | gpio_set_value(4, 0); 178 | gpio_free(17); 179 | gpio_free(4); 180 | cdev_del(&my_device); 181 | device_destroy(my_class, my_device_nr); 182 | class_destroy(my_class); 183 | unregister_chrdev_region(my_device_nr, 1); 184 | printk("Goodbye, Kernel\n"); 185 | } 186 | 187 | module_init(ModuleInit); 188 | module_exit(ModuleExit); 189 | 190 | 191 | -------------------------------------------------------------------------------- /05_lcd_driver/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += lcd_driver.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /05_lcd_driver/lcd_driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Meta Information */ 11 | MODULE_LICENSE("GPL"); 12 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 13 | MODULE_DESCRIPTION("A driver to write to a LCD text display"); 14 | 15 | /* Variables for device and device class */ 16 | static dev_t my_device_nr; 17 | static struct class *my_class; 18 | static struct cdev my_device; 19 | 20 | #define DRIVER_NAME "lcd" 21 | #define DRIVER_CLASS "MyModuleClass" 22 | 23 | /* LCD char buffer */ 24 | static char lcd_buffer[17]; 25 | 26 | /* Pinout for LCD Display */ 27 | unsigned int gpios[] = { 28 | 3, /* Enable Pin */ 29 | 2, /* Register Select Pin */ 30 | 4, /* Data Pin 0*/ 31 | 17, /* Data Pin 1*/ 32 | 27, /* Data Pin 2*/ 33 | 22, /* Data Pin 3*/ 34 | 10, /* Data Pin 4*/ 35 | 9, /* Data Pin 5*/ 36 | 11, /* Data Pin 6*/ 37 | 5, /* Data Pin 7*/ 38 | }; 39 | 40 | #define REGISTER_SELECT gpios[1] 41 | 42 | /** 43 | * @brief generates a pulse on the enable signal 44 | */ 45 | void lcd_enable(void) { 46 | gpio_set_value(gpios[0], 1); 47 | msleep(5); 48 | gpio_set_value(gpios[0], 0); 49 | } 50 | 51 | /** 52 | * @brief set the 8 bit data bus 53 | * @param data: Data to set 54 | */ 55 | void lcd_send_byte(char data) { 56 | int i; 57 | for(i=0; i<8; i++) 58 | gpio_set_value(gpios[i+2], ((data) & (1<> i); 59 | lcd_enable(); 60 | msleep(5); 61 | } 62 | 63 | /** 64 | * @brief send a command to the LCD 65 | * 66 | * @param data: command to send 67 | */ 68 | void lcd_command(uint8_t data) { 69 | gpio_set_value(REGISTER_SELECT, 0); /* RS to Instruction */ 70 | lcd_send_byte(data); 71 | } 72 | 73 | /** 74 | * @brief send a data to the LCD 75 | * 76 | * @param data: command to send 77 | */ 78 | void lcd_data(uint8_t data) { 79 | gpio_set_value(REGISTER_SELECT, 1); /* RS to data */ 80 | lcd_send_byte(data); 81 | } 82 | 83 | 84 | /** 85 | * @brief Write data to buffer 86 | */ 87 | static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 88 | int to_copy, not_copied, delta, i; 89 | 90 | /* Get amount of data to copy */ 91 | to_copy = min(count, sizeof(lcd_buffer)); 92 | 93 | /* Copy data to user */ 94 | not_copied = copy_from_user(lcd_buffer, user_buffer, to_copy); 95 | 96 | /* Calculate data */ 97 | delta = to_copy - not_copied; 98 | 99 | /* Set the new data to the display */ 100 | lcd_command(0x1); 101 | 102 | for(i=0; i> 20, my_device_nr && 0xfffff); 145 | 146 | /* Create device class */ 147 | if((my_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) { 148 | printk("Device class can not be created!\n"); 149 | goto ClassError; 150 | } 151 | 152 | /* create device file */ 153 | if(device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL) { 154 | printk("Can not create device file!\n"); 155 | goto FileError; 156 | } 157 | 158 | /* Initialize device file */ 159 | cdev_init(&my_device, &fops); 160 | 161 | /* Regisering device to kernel */ 162 | if(cdev_add(&my_device, my_device_nr, 1) == -1) { 163 | printk("lcd-driver - Registering of device to kernel failed!\n"); 164 | goto AddError; 165 | } 166 | 167 | /* Initialize GPIOs */ 168 | printk("lcd-driver - GPIO Init\n"); 169 | for(i=0; i<10; i++) { 170 | if(gpio_request(gpios[i], names[i])) { 171 | printk("lcd-driver - Error Init GPIO %d\n", gpios[i]); 172 | goto GpioInitError; 173 | } 174 | } 175 | 176 | printk("lcd-driver - Set GPIOs to output\n"); 177 | for(i=0; i<10; i++) { 178 | if(gpio_direction_output(gpios[i], 0)) { 179 | printk("lcd-driver - Error setting GPIO %d to output\n", i); 180 | goto GpioDirectionError; 181 | } 182 | } 183 | 184 | /* Init the display */ 185 | lcd_command(0x30); /* Set the display for 8 bit data interface */ 186 | 187 | lcd_command(0xf); /* Turn display on, turn cursor on, set cursor blinking */ 188 | 189 | lcd_command(0x1); 190 | 191 | char text[] = "Hello World!"; 192 | for(i=0; i=0; i--) 200 | gpio_free(gpios[i]); 201 | AddError: 202 | device_destroy(my_class, my_device_nr); 203 | FileError: 204 | class_destroy(my_class); 205 | ClassError: 206 | unregister_chrdev_region(my_device_nr, 1); 207 | return -1; 208 | } 209 | 210 | /** 211 | * @brief This function is called, when the module is removed from the kernel 212 | */ 213 | static void __exit ModuleExit(void) { 214 | int i; 215 | lcd_command(0x1); /* Clear the display */ 216 | for(i=0; i<10; i++){ 217 | gpio_set_value(gpios[i], 0); 218 | gpio_free(gpios[i]); 219 | } 220 | cdev_del(&my_device); 221 | device_destroy(my_class, my_device_nr); 222 | class_destroy(my_class); 223 | unregister_chrdev_region(my_device_nr, 1); 224 | printk("Goodbye, Kernel\n"); 225 | } 226 | 227 | module_init(ModuleInit); 228 | module_exit(ModuleExit); 229 | 230 | 231 | -------------------------------------------------------------------------------- /06_pwm_driver/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += pwm_driver.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /06_pwm_driver/pwm_driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Meta Information */ 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 11 | MODULE_DESCRIPTION("A simple driver to access the Hardware PWM IP"); 12 | 13 | /* Variables for device and device class */ 14 | static dev_t my_device_nr; 15 | static struct class *my_class; 16 | static struct cdev my_device; 17 | 18 | #define DRIVER_NAME "my_pwm_driver" 19 | #define DRIVER_CLASS "MyModuleClass" 20 | 21 | /* Variables for pwm */ 22 | struct pwm_device *pwm0 = NULL; 23 | u32 pwm_on_time = 500000000; 24 | 25 | /** 26 | * @brief Write data to buffer 27 | */ 28 | static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 29 | int to_copy, not_copied, delta; 30 | char value; 31 | 32 | /* Get amount of data to copy */ 33 | to_copy = min(count, sizeof(value)); 34 | 35 | /* Copy data to user */ 36 | not_copied = copy_from_user(&value, user_buffer, to_copy); 37 | 38 | /* Set PWM on time */ 39 | if(value < 'a' || value > 'j') 40 | printk("Invalid Value\n"); 41 | else 42 | pwm_config(pwm0, 100000000 * (value - 'a'), 1000000000); 43 | 44 | /* Calculate data */ 45 | delta = to_copy - not_copied; 46 | 47 | return delta; 48 | } 49 | 50 | /** 51 | * @brief This function is called, when the device file is opened 52 | */ 53 | static int driver_open(struct inode *device_file, struct file *instance) { 54 | printk("dev_nr - open was called!\n"); 55 | return 0; 56 | } 57 | 58 | /** 59 | * @brief This function is called, when the device file is opened 60 | */ 61 | static int driver_close(struct inode *device_file, struct file *instance) { 62 | printk("dev_nr - close was called!\n"); 63 | return 0; 64 | } 65 | 66 | static struct file_operations fops = { 67 | .owner = THIS_MODULE, 68 | .open = driver_open, 69 | .release = driver_close, 70 | .write = driver_write 71 | }; 72 | 73 | /** 74 | * @brief This function is called, when the module is loaded into the kernel 75 | */ 76 | static int __init ModuleInit(void) { 77 | printk("Hello, Kernel!\n"); 78 | 79 | /* Allocate a device nr */ 80 | if( alloc_chrdev_region(&my_device_nr, 0, 1, DRIVER_NAME) < 0) { 81 | printk("Device Nr. could not be allocated!\n"); 82 | return -1; 83 | } 84 | printk("read_write - Device Nr. Major: %d, Minor: %d was registered!\n", my_device_nr >> 20, my_device_nr && 0xfffff); 85 | 86 | /* Create device class */ 87 | if((my_class = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) { 88 | printk("Device class can not be created!\n"); 89 | goto ClassError; 90 | } 91 | 92 | /* create device file */ 93 | if(device_create(my_class, NULL, my_device_nr, NULL, DRIVER_NAME) == NULL) { 94 | printk("Can not create device file!\n"); 95 | goto FileError; 96 | } 97 | 98 | /* Initialize device file */ 99 | cdev_init(&my_device, &fops); 100 | 101 | /* Regisering device to kernel */ 102 | if(cdev_add(&my_device, my_device_nr, 1) == -1) { 103 | printk("Registering of device to kernel failed!\n"); 104 | goto AddError; 105 | } 106 | 107 | pwm0 = pwm_request(0, "my-pwm"); 108 | if(pwm0 == NULL) { 109 | printk("Could not get PWM0!\n"); 110 | goto AddError; 111 | } 112 | 113 | pwm_config(pwm0, pwm_on_time, 1000000000); 114 | pwm_enable(pwm0); 115 | 116 | return 0; 117 | AddError: 118 | device_destroy(my_class, my_device_nr); 119 | FileError: 120 | class_destroy(my_class); 121 | ClassError: 122 | unregister_chrdev_region(my_device_nr, 1); 123 | return -1; 124 | } 125 | 126 | /** 127 | * @brief This function is called, when the module is removed from the kernel 128 | */ 129 | static void __exit ModuleExit(void) { 130 | pwm_disable(pwm0); 131 | pwm_free(pwm0); 132 | cdev_del(&my_device); 133 | device_destroy(my_class, my_device_nr); 134 | class_destroy(my_class); 135 | unregister_chrdev_region(my_device_nr, 1); 136 | printk("Goodbye, Kernel\n"); 137 | } 138 | 139 | module_init(ModuleInit); 140 | module_exit(ModuleExit); 141 | 142 | 143 | -------------------------------------------------------------------------------- /07_BMP280/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += bmp280.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /07_BMP280/bmp280.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define DRIVER_NAME "bmp280" 12 | #define DRIVER_CLASS "bmp280Class" 13 | 14 | static struct i2c_adapter * bmp_i2c_adapter = NULL; 15 | static struct i2c_client * bmp280_i2c_client = NULL; 16 | 17 | /* Meta Information */ 18 | MODULE_AUTHOR("Johannes 4Linux"); 19 | MODULE_LICENSE("GPL"); 20 | MODULE_DESCRIPTION("A driver for reading out a BMP280 temperature sensor"); 21 | MODULE_SUPPORTED_DEVICE("NONE"); 22 | 23 | /* Defines for device identification */ 24 | #define I2C_BUS_AVAILABLE 1 /* The I2C Bus available on the raspberry */ 25 | #define SLAVE_DEVICE_NAME "BMP280" /* Device and Driver Name */ 26 | #define BMP280_SLAVE_ADDRESS 0x76 /* BMP280 I2C address */ 27 | 28 | static const struct i2c_device_id bmp_id[] = { 29 | { SLAVE_DEVICE_NAME, 0 }, 30 | { } 31 | }; 32 | 33 | static struct i2c_driver bmp_driver = { 34 | .driver = { 35 | .name = SLAVE_DEVICE_NAME, 36 | .owner = THIS_MODULE 37 | } 38 | }; 39 | 40 | static struct i2c_board_info bmp_i2c_board_info = { 41 | I2C_BOARD_INFO(SLAVE_DEVICE_NAME, BMP280_SLAVE_ADDRESS) 42 | }; 43 | 44 | /* Variables for Device and Deviceclass*/ 45 | static dev_t myDeviceNr; 46 | static struct class *myClass; 47 | static struct cdev myDevice; 48 | 49 | /* Variables for temperature calculation */ 50 | s32 dig_T1, dig_T2, dig_T3; 51 | 52 | /** 53 | * @brief Read current temperature from BMP280 sensor 54 | * 55 | * @return temperature in degree 56 | */ 57 | s32 read_temperature(void) { 58 | int var1, var2; 59 | s32 raw_temp; 60 | s32 d1, d2, d3; 61 | 62 | /* Read Temperature */ 63 | d1 = i2c_smbus_read_byte_data(bmp280_i2c_client, 0xFA); 64 | d2 = i2c_smbus_read_byte_data(bmp280_i2c_client, 0xFB); 65 | d3 = i2c_smbus_read_byte_data(bmp280_i2c_client, 0xFC); 66 | raw_temp = ((d1<<16) | (d2<<8) | d3) >> 4; 67 | 68 | /* Calculate temperature in degree */ 69 | var1 = ((((raw_temp >> 3) - (dig_T1 << 1))) * (dig_T2)) >> 11; 70 | 71 | var2 = (((((raw_temp >> 4) - (dig_T1)) * ((raw_temp >> 4) - (dig_T1))) >> 12) * (dig_T3)) >> 14; 72 | return ((var1 + var2) *5 +128) >> 8; 73 | } 74 | 75 | /** 76 | * @brief Get data out of buffer 77 | */ 78 | static ssize_t driver_read(struct file *File, char *user_buffer, size_t count, loff_t *offs) { 79 | int to_copy, not_copied, delta; 80 | char out_string[20]; 81 | int temperature; 82 | 83 | /* Get amount of bytes to copy */ 84 | to_copy = min(sizeof(out_string), count); 85 | 86 | /* Get temperature */ 87 | temperature = read_temperature(); 88 | snprintf(out_string, sizeof(out_string), "%d.%d\n", temperature/100, temperature%100); 89 | 90 | /* Copy Data to user */ 91 | not_copied = copy_to_user(user_buffer, out_string, to_copy); 92 | 93 | /* Calculate delta */ 94 | delta = to_copy - not_copied; 95 | 96 | return delta; 97 | } 98 | 99 | /** 100 | * @brief This function is called, when the device file is opened 101 | */ 102 | static int driver_open(struct inode *deviceFile, struct file *instance) { 103 | printk("MyDeviceDriver - Open was called\n"); 104 | return 0; 105 | } 106 | 107 | /** 108 | * @brief This function is called, when the device file is close 109 | */ 110 | static int driver_close(struct inode *deviceFile, struct file *instance) { 111 | printk("MyDeviceDriver - Close was called\n"); 112 | return 0; 113 | } 114 | 115 | /* Map the file operations */ 116 | static struct file_operations fops = { 117 | .owner = THIS_MODULE, 118 | .open = driver_open, 119 | .release = driver_close, 120 | .read = driver_read, 121 | }; 122 | 123 | 124 | /** 125 | * @brief function, which is called after loading module to kernel, do initialization there 126 | */ 127 | static int __init ModuleInit(void) { 128 | int ret = -1; 129 | u8 id; 130 | printk("MyDeviceDriver - Hello Kernel\n"); 131 | 132 | /* Allocate Device Nr */ 133 | if ( alloc_chrdev_region(&myDeviceNr, 0, 1, DRIVER_NAME) < 0) { 134 | printk("Device Nr. could not be allocated!\n"); 135 | } 136 | printk("MyDeviceDriver - Device Nr %d was registered\n", myDeviceNr); 137 | 138 | /* Create Device Class */ 139 | if ((myClass = class_create(THIS_MODULE, DRIVER_CLASS)) == NULL) { 140 | printk("Device Class can not be created!\n"); 141 | goto ClassError; 142 | } 143 | 144 | /* Create Device file */ 145 | if (device_create(myClass, NULL, myDeviceNr, NULL, DRIVER_NAME) == NULL) { 146 | printk("Can not create device file!\n"); 147 | goto FileError; 148 | } 149 | 150 | /* Initialize Device file */ 151 | cdev_init(&myDevice, &fops); 152 | 153 | /* register device to kernel */ 154 | if (cdev_add(&myDevice, myDeviceNr, 1) == -1) { 155 | printk("Registering of device to kernel failed!\n"); 156 | goto KernelError; 157 | } 158 | 159 | bmp_i2c_adapter = i2c_get_adapter(I2C_BUS_AVAILABLE); 160 | 161 | if(bmp_i2c_adapter != NULL) { 162 | bmp280_i2c_client = i2c_acpi_new_device(bmp_i2c_adapter, &bmp_i2c_board_info); 163 | if(bmp280_i2c_client != NULL) { 164 | if(i2c_add_driver(&bmp_driver) != -1) { 165 | ret = 0; 166 | } 167 | else 168 | printk("Can't add driver...\n"); 169 | } 170 | i2c_put_adapter(bmp_i2c_adapter); 171 | } 172 | printk("BMP280 Driver added!\n"); 173 | 174 | /* Read Chip ID */ 175 | id = i2c_smbus_read_byte_data(bmp280_i2c_client, 0xD0); 176 | printk("ID: 0x%x\n", id); 177 | 178 | /* Read Calibration Values */ 179 | dig_T1 = i2c_smbus_read_word_data(bmp280_i2c_client, 0x88); 180 | dig_T2 = i2c_smbus_read_word_data(bmp280_i2c_client, 0x8a); 181 | dig_T3 = i2c_smbus_read_word_data(bmp280_i2c_client, 0x8c); 182 | 183 | if(dig_T2 > 32767) 184 | dig_T2 -= 65536; 185 | 186 | if(dig_T3 > 32767) 187 | dig_T3 -= 65536; 188 | 189 | /* Initialice the sensor */ 190 | i2c_smbus_write_byte_data(bmp280_i2c_client, 0xf5, 5<<5); 191 | i2c_smbus_write_byte_data(bmp280_i2c_client, 0xf4, ((5<<5) | (5<<2) | (3<<0))); 192 | return ret; 193 | 194 | KernelError: 195 | device_destroy(myClass, myDeviceNr); 196 | FileError: 197 | class_destroy(myClass); 198 | ClassError: 199 | unregister_chrdev(myDeviceNr, DRIVER_NAME); 200 | return (-1); 201 | } 202 | 203 | /** 204 | * @brief function, which is called when removing module from kernel 205 | * free alocated resources 206 | */ 207 | static void __exit ModuleExit(void) { 208 | printk("MyDeviceDriver - Goodbye, Kernel!\n"); 209 | i2c_unregister_device(bmp280_i2c_client); 210 | i2c_del_driver(&bmp_driver); 211 | cdev_del(&myDevice); 212 | device_destroy(myClass, myDeviceNr); 213 | class_destroy(myClass); 214 | unregister_chrdev_region(myDeviceNr, 1); 215 | } 216 | 217 | module_init(ModuleInit); 218 | module_exit(ModuleExit); 219 | -------------------------------------------------------------------------------- /08_timer/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += mytimer.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /08_timer/mytimer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Meta Information */ 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 10 | MODULE_DESCRIPTION("A simple test for LKM's timer"); 11 | 12 | /** variable for timer */ 13 | static struct timer_list my_timer; 14 | 15 | void timer_callback(struct timer_list * data) { 16 | gpio_set_value(4, 0); /* Turn LED off */ 17 | } 18 | 19 | /** 20 | * @brief This function is called, when the module is loaded into the kernel 21 | */ 22 | static int __init ModuleInit(void) { 23 | printk("Hello, Kernel!\n"); 24 | /* GPIO 4 init */ 25 | if(gpio_request(4, "rpi-gpio-4")) { 26 | printk("Can not allocate GPIO 4\n"); 27 | return -1; 28 | } 29 | 30 | /* Set GPIO 4 direction */ 31 | if(gpio_direction_output(4, 0)) { 32 | printk("Can not set GPIO 4 to output!\n"); 33 | gpio_free(4); 34 | return -1; 35 | } 36 | 37 | /* Turn LED on */ 38 | gpio_set_value(4, 1); 39 | 40 | /* Initialize timer */ 41 | timer_setup(&my_timer, timer_callback, 0); 42 | mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); 43 | 44 | 45 | return 0; 46 | } 47 | 48 | /** 49 | * @brief This function is called, when the module is removed from the kernel 50 | */ 51 | static void __exit ModuleExit(void) { 52 | printk("Goodbye, Kernel\n"); 53 | gpio_free(4); 54 | del_timer(&my_timer); 55 | } 56 | 57 | module_init(ModuleInit); 58 | module_exit(ModuleExit); 59 | 60 | 61 | -------------------------------------------------------------------------------- /09_hrtimer/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += my_hrtimer.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /09_hrtimer/my_hrtimer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("A simple LKM using High Resulution Timers"); 10 | 11 | /* hr timer */ 12 | static struct hrtimer my_hrtimer; 13 | u64 start_t; 14 | 15 | static enum hrtimer_restart test_hrtimer_handler(struct hrtimer *timer) { 16 | /* Get current time */ 17 | u64 now_t = jiffies; 18 | printk("start_t - now_t = %u\n", jiffies_to_msecs(now_t - start_t)); 19 | return HRTIMER_NORESTART; 20 | } 21 | 22 | /** 23 | * @brief This function is called, when the module is loaded into the kernel 24 | */ 25 | static int __init ModuleInit(void) { 26 | printk("Hello, Kernel!\n"); 27 | 28 | /* Init of hrtimer */ 29 | hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 30 | my_hrtimer.function = &test_hrtimer_handler; 31 | start_t = jiffies; 32 | hrtimer_start(&my_hrtimer, ms_to_ktime(100), HRTIMER_MODE_REL); 33 | return 0; 34 | } 35 | 36 | /** 37 | * @brief This function is called, when the module is removed from the kernel 38 | */ 39 | static void __exit ModuleExit(void) { 40 | hrtimer_cancel(&my_hrtimer); 41 | printk("Goodbye, Kernel\n"); 42 | } 43 | 44 | module_init(ModuleInit); 45 | module_exit(ModuleExit); 46 | 47 | 48 | -------------------------------------------------------------------------------- /10_spi_bmp280/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += spi_bmp280.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /10_spi_bmp280/README.md: -------------------------------------------------------------------------------- 1 | # For Raspberry Pi 3 2 | 3 | A Linux Driver Module is already using the Chip Select 0. To use it, the overlay *spidev_disabler.dts* is needed. 4 | 5 | It can be compiled with: 6 | 7 | ```sh 8 | dtc spidev_disabler.dts -O dtb >spidev_disabler.dtbo 9 | ``` 10 | 11 | and can be loaded with 12 | ```sh 13 | sudo dtoverlay -d . spidev_disabler 14 | ``` 15 | 16 | Now the LKM can be loaded and the module should work! 17 | 18 | For more information, see [this forum post](https://www.raspberrypi.org/forums/viewtopic.php?t=151423) 19 | 20 | -------------------------------------------------------------------------------- /10_spi_bmp280/spi_bmp280.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Meta Information */ 6 | MODULE_LICENSE("GPL"); 7 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 8 | MODULE_DESCRIPTION("A simple LKM to read and write some registers of a BMP280 sensor"); 9 | 10 | 11 | #define MY_BUS_NUM 0 12 | static struct spi_device *bmp280_dev; 13 | 14 | /** 15 | * @brief This function is called, when the module is loaded into the kernel 16 | */ 17 | static int __init ModuleInit(void) { 18 | struct spi_master *master; 19 | u8 id; 20 | u8 val[] = {0x75, 0x40}; 21 | 22 | /* Parameters for SPI device */ 23 | struct spi_board_info spi_device_info = { 24 | .modalias = "bmp280", 25 | .max_speed_hz = 1000000, 26 | .bus_num = MY_BUS_NUM, 27 | .chip_select = 0, 28 | .mode = 3, 29 | }; 30 | 31 | /* Get access to spi bus */ 32 | master = spi_busnum_to_master(MY_BUS_NUM); 33 | /* Check if we could get the master */ 34 | if(!master) { 35 | printk("There is no spi bus with Nr. %d\n", MY_BUS_NUM); 36 | return -1; 37 | } 38 | 39 | /* Create new SPI device */ 40 | bmp280_dev = spi_new_device(master, &spi_device_info); 41 | if(!bmp280_dev) { 42 | printk("Could not create device!\n"); 43 | return -1; 44 | } 45 | 46 | bmp280_dev -> bits_per_word = 8; 47 | 48 | /* Setup the bus for device's parameters */ 49 | if(spi_setup(bmp280_dev) != 0){ 50 | printk("Could not change bus setup!\n"); 51 | spi_unregister_device(bmp280_dev); 52 | return -1; 53 | } 54 | 55 | /* Read Chip ID */ 56 | id = spi_w8r8(bmp280_dev, 0xD0); 57 | printk("Chip ID: 0x%x\n", id); 58 | 59 | /* Write to config reg */ 60 | spi_write(bmp280_dev, val, sizeof(8)); 61 | id = spi_w8r8(bmp280_dev, 0xF5); 62 | printk("Config Reg. value: 0x%x\n", id); 63 | 64 | printk("Hello, Kernel!\n"); 65 | 66 | 67 | return 0; 68 | } 69 | 70 | /** 71 | * @brief This function is called, when the module is removed from the kernel 72 | */ 73 | static void __exit ModuleExit(void) { 74 | if(bmp280_dev) 75 | spi_unregister_device(bmp280_dev); 76 | 77 | printk("Goodbye, Kernel\n"); 78 | } 79 | 80 | module_init(ModuleInit); 81 | module_exit(ModuleExit); 82 | 83 | 84 | -------------------------------------------------------------------------------- /10_spi_bmp280/spidev_disabler.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | 4 | / { 5 | compatible = "brcm,bcm2708"; 6 | 7 | fragment@0 { 8 | target = <&spidev0>; 9 | 10 | __overlay__ { 11 | status = "disabled"; 12 | }; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /11_gpio_irq/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += gpio_irq.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /11_gpio_irq/gpio_irq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("A simple LKM for a gpio interrupt"); 10 | 11 | /** variable contains pin number o interrupt controller to which GPIO 17 is mapped to */ 12 | unsigned int irq_number; 13 | 14 | /** 15 | * @brief Interrupt service routine is called, when interrupt is triggered 16 | */ 17 | static irqreturn_t gpio_irq_handler(int irq, void *dev_id) 18 | { 19 | printk("gpio_irq: Interrupt was triggered and ISR was called!\n"); 20 | return IRQ_HANDLED; 21 | } 22 | 23 | /** 24 | * @brief This function is called, when the module is loaded into the kernel 25 | */ 26 | static int __init ModuleInit(void) { 27 | printk("qpio_irq: Loading module... "); 28 | 29 | /* Setup the gpio */ 30 | if(gpio_request(17, "rpi-gpio-17")) { 31 | printk("Error!\nCan not allocate GPIO 17\n"); 32 | return -1; 33 | } 34 | 35 | /* Set GPIO 17 direction */ 36 | if(gpio_direction_input(17)) { 37 | printk("Error!\nCan not set GPIO 17 to input!\n"); 38 | gpio_free(17); 39 | return -1; 40 | } 41 | 42 | /* Setup the interrupt */ 43 | irq_number = gpio_to_irq(17); 44 | 45 | if(request_irq(irq_number, gpio_irq_handler, IRQF_TRIGGER_RISING, "my_gpio_irq", NULL) != 0){ 46 | printk("Error!\nCan not request interrupt nr.: %d\n", irq_number); 47 | gpio_free(17); 48 | return -1; 49 | } 50 | 51 | printk("Done!\n"); 52 | printk("GPIO 17 is mapped to IRQ Nr.: %d\n", irq_number); 53 | return 0; 54 | } 55 | 56 | /** 57 | * @brief This function is called, when the module is removed from the kernel 58 | */ 59 | static void __exit ModuleExit(void) { 60 | printk("gpio_irq: Unloading module... "); 61 | free_irq(irq_number, NULL); 62 | gpio_free(17); 63 | 64 | } 65 | 66 | module_init(ModuleInit); 67 | module_exit(ModuleExit); 68 | 69 | 70 | -------------------------------------------------------------------------------- /12_parameters/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += my_params.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /12_parameters/my_params.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* Meta Information */ 5 | MODULE_LICENSE("GPL"); 6 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 7 | MODULE_DESCRIPTION("A simple LKM to demonstrate the use of parameters"); 8 | 9 | /* Kernel Module's Paramerters*/ 10 | static unsigned int gpio_nr = 12; 11 | static char *device_name = "testdevice"; 12 | 13 | module_param(gpio_nr, uint, S_IRUGO); 14 | module_param(device_name, charp, S_IRUGO); 15 | 16 | MODULE_PARM_DESC(gpio_nr, "Nr. of GPIO to use in this LKM"); 17 | MODULE_PARM_DESC(device_name, "Device name to use in this LKM"); 18 | 19 | /** 20 | * @brief This function is called, when the module is loaded into the kernel 21 | */ 22 | static int __init ModuleInit(void) { 23 | printk("gpio_nr = %u\n", gpio_nr); 24 | printk("device_name = %s\n", device_name); 25 | printk("Hello, Kernel!\n"); 26 | return 0; 27 | } 28 | 29 | /** 30 | * @brief This function is called, when the module is removed from the kernel 31 | */ 32 | static void __exit ModuleExit(void) { 33 | printk("Goodbye, Kernel\n"); 34 | } 35 | 36 | module_init(ModuleInit); 37 | module_exit(ModuleExit); 38 | 39 | 40 | -------------------------------------------------------------------------------- /13_ioctl/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += ioctl_example.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /13_ioctl/README.md: -------------------------------------------------------------------------------- 1 | To run this example create a device file with: 2 | 3 | ~~~~~ 4 | sudo mknod /dev/dummy c 64 0 5 | ~~~~~ 6 | 7 | -------------------------------------------------------------------------------- /13_ioctl/ioctl_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ioctl_test.h" 8 | 9 | /* Meta Information */ 10 | MODULE_LICENSE("GPL"); 11 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 12 | MODULE_DESCRIPTION("A simple example for ioctl in a LKM"); 13 | 14 | 15 | /** 16 | * @brief This function is called, when the device file is opened 17 | */ 18 | static int driver_open(struct inode *device_file, struct file *instance) { 19 | printk("ioctl_example - open was called!\n"); 20 | return 0; 21 | } 22 | 23 | /** 24 | * @brief This function is called, when the device file is opened 25 | */ 26 | static int driver_close(struct inode *device_file, struct file *instance) { 27 | printk("ioctl_example - close was called!\n"); 28 | return 0; 29 | } 30 | 31 | /* Global Variable for reading and writing */ 32 | int32_t answer = 42; 33 | 34 | static long int my_ioctl(struct file *file, unsigned cmd, unsigned long arg){ 35 | struct mystruct test; 36 | switch(cmd){ 37 | case WR_VALUE: 38 | if(copy_from_user(&answer, (int32_t *) arg, sizeof(answer))) 39 | printk("ioctl_example - Error copying data from user!\n"); 40 | else 41 | printk("ioctl_example - Update the answer to %d\n", answer); 42 | break; 43 | case RD_VALUE: 44 | if(copy_to_user((int32_t *) arg, &answer, sizeof(answer))) 45 | printk("ioctl_example - Error copying data to user!\n"); 46 | else 47 | printk("ioctl_example - The answer was copied!\n"); 48 | break; 49 | case GREETER: 50 | if(copy_from_user(&test, (struct mystruct *) arg, sizeof(test))) 51 | printk("ioctl_example - Error copying data from user!\n"); 52 | else 53 | printk("ioctl_example - %d greets to %s\n", test.repeat, test.name); 54 | break; 55 | } 56 | return 0; 57 | } 58 | 59 | static struct file_operations fops = { 60 | .owner = THIS_MODULE, 61 | .open = driver_open, 62 | .release = driver_close, 63 | .unlocked_ioctl = my_ioctl 64 | }; 65 | 66 | #define MYMAJOR 64 67 | 68 | /** 69 | * @brief This function is called, when the module is loaded into the kernel 70 | */ 71 | static int __init ModuleInit(void) { 72 | int retval; 73 | printk("Hello, Kernel!\n"); 74 | /* register device nr. */ 75 | retval = register_chrdev(MYMAJOR, "my_ioctl_example", &fops); 76 | if(retval == 0) { 77 | printk("ioctl_example - registered Device number Major: %d, Minor: %d\n", MYMAJOR, 0); 78 | } 79 | else if(retval > 0) { 80 | printk("ioctl_example - registered Device number Major: %d, Minor: %d\n", retval>>20, retval&0xfffff); 81 | } 82 | else { 83 | printk("Could not register device number!\n"); 84 | return -1; 85 | } 86 | return 0; 87 | } 88 | 89 | /** 90 | * @brief This function is called, when the module is removed from the kernel 91 | */ 92 | static void __exit ModuleExit(void) { 93 | unregister_chrdev(MYMAJOR, "my_ioctl_example"); 94 | printk("Goodbye, Kernel\n"); 95 | } 96 | 97 | module_init(ModuleInit); 98 | module_exit(ModuleExit); 99 | 100 | 101 | -------------------------------------------------------------------------------- /13_ioctl/ioctl_test.h: -------------------------------------------------------------------------------- 1 | #ifndef IOCTL_TEST_H 2 | #define IOCTL_TEST_H 3 | 4 | struct mystruct{ 5 | int repeat; 6 | char name[64]; 7 | }; 8 | 9 | #define WR_VALUE _IOW('a', 'a', int32_t *) 10 | #define RD_VALUE _IOR('a', 'b', int32_t *) 11 | #define GREETER _IOW('a', 'c', struct mystruct *) 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /13_ioctl/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ioctl_test.h" 8 | 9 | int main() { 10 | int answer; 11 | struct mystruct test = {4, "Johannes"}; 12 | int dev = open("/dev/dummy", O_WRONLY); 13 | if(dev == -1) { 14 | printf("Opening was not possible!\n"); 15 | return -1; 16 | } 17 | 18 | ioctl(dev, RD_VALUE, &answer); 19 | printf("The answer is %d\n", answer); 20 | 21 | answer = 123; 22 | 23 | ioctl(dev, WR_VALUE, &answer); 24 | ioctl(dev, RD_VALUE, &answer); 25 | printf("The answer is now %d\n", answer); 26 | 27 | ioctl(dev, GREETER, &test); 28 | 29 | printf("Opening was successfull!\n"); 30 | close(dev); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /14_Kernel_Threads/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += kthread.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /14_Kernel_Threads/kthread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Global variables for the threads */ 8 | static struct task_struct *kthread_1; 9 | static struct task_struct *kthread_2; 10 | static int t1 = 1, t2 = 2; 11 | 12 | /** 13 | * @brief Function which will be executed by the threads 14 | * 15 | * @param thread_nr Pointer to number of the thread 16 | */ 17 | int thread_function(void * thread_nr) { 18 | unsigned int i = 0; 19 | int t_nr = *(int *) thread_nr; 20 | 21 | /* Working loop */ 22 | while(!kthread_should_stop()){ 23 | printk("kthread - Thread %d is executed! Counter val: %d\n", t_nr, i++); 24 | msleep(t_nr * 1000); 25 | } 26 | 27 | printk("kthread - Thread %d finished execution!\n", t_nr); 28 | return 0; 29 | } 30 | 31 | /* Meta Information */ 32 | MODULE_LICENSE("GPL"); 33 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 34 | MODULE_DESCRIPTION("A simple example for threads in a LKM"); 35 | 36 | /** 37 | * @brief This function is called, when the module is loaded into the kernel 38 | */ 39 | static int __init ModuleInit(void) { 40 | printk("kthread - Init threads\n"); 41 | 42 | kthread_1 = kthread_create(thread_function, &t1, "kthread_1"); 43 | if(kthread_1 != NULL){ 44 | /* Let's start the thread */ 45 | wake_up_process(kthread_1); 46 | printk("kthread - Thread 1 was created and is running now!\n"); 47 | } 48 | else { 49 | printk("kthread - Thread 1 could not be created!\n"); 50 | return -1; 51 | } 52 | kthread_2 = kthread_run(thread_function, &t2, "kthread_2"); 53 | if(kthread_2 != NULL) 54 | printk("kthread - Thread 2 was created and is running now!\n"); 55 | else { 56 | printk("kthread - Thread 2 could not be created!\n"); 57 | kthread_stop(kthread_1); 58 | return -1; 59 | } 60 | printk("kthread - Both threads are running now!\n"); 61 | 62 | return 0; 63 | } 64 | 65 | /** 66 | * @brief This function is called, when the module is removed from the kernel 67 | */ 68 | static void __exit ModuleExit(void) { 69 | printk("kthread - Stop both threads\n"); 70 | kthread_stop(kthread_1); 71 | kthread_stop(kthread_2); 72 | } 73 | 74 | module_init(ModuleInit); 75 | module_exit(ModuleExit); 76 | 77 | 78 | -------------------------------------------------------------------------------- /15_Sending_Signals/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += gpio_irq_signal.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /15_Sending_Signals/gpio_irq_signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MYMAJOR 64 11 | 12 | /* Meta Information */ 13 | MODULE_LICENSE("GPL"); 14 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 15 | MODULE_DESCRIPTION("Kernel Module which sends a signal to an userspace app when GPIO 17 has a rising edge"); 16 | 17 | /** variable contains pin number o interrupt controller to which GPIO 17 is mapped to */ 18 | unsigned int irq_number; 19 | 20 | /** Global variables and defines for userspace app registration */ 21 | #define REGISTER_UAPP _IO('R', 'g') 22 | static struct task_struct *task = NULL; 23 | 24 | /* define for Signal sending */ 25 | #define SIGNR 44 26 | 27 | /** 28 | * @brief Interrupt service routine is called, when interrupt is triggered 29 | */ 30 | static irqreturn_t gpio_irq_signal_handler(int irq, void *dev_id) 31 | { 32 | struct siginfo info; 33 | printk("gpio_irq_signal: Interrupt was triggered and ISR was called!\n"); 34 | 35 | if(task != NULL) { 36 | memset(&info, 0, sizeof(info)); 37 | info.si_signo = SIGNR; 38 | info.si_code = SI_QUEUE; 39 | 40 | /* Send the signal */ 41 | if(send_sig_info(SIGNR, (struct kernel_siginfo *) &info, task) < 0) 42 | printk("gpio_irq_signal: Error sending signal\n"); 43 | } 44 | return IRQ_HANDLED; 45 | } 46 | 47 | /** 48 | * @brief This function is called, when the device file is opened 49 | */ 50 | static int my_close(struct inode *device_file, struct file *instance) { 51 | if(task != NULL) 52 | task = NULL; 53 | return 0; 54 | } 55 | 56 | /** 57 | * @brief IOCTL for registering the Userspace app to the kernel module 58 | */ 59 | static long int my_ioctl(struct file *file, unsigned cmd, unsigned long arg) { 60 | if(cmd == REGISTER_UAPP) { 61 | task = get_current(); 62 | printk("gpio_irq_signal: Userspace app with PID %d is registered\n", task->pid); 63 | } 64 | return 0; 65 | } 66 | 67 | static struct file_operations fops = { 68 | .owner = THIS_MODULE, 69 | .release = my_close, 70 | .unlocked_ioctl = my_ioctl, 71 | }; 72 | 73 | /** 74 | * @brief This function is called, when the module is loaded into the kernel 75 | */ 76 | static int __init ModuleInit(void) { 77 | printk("qpio_irq: Loading module... "); 78 | 79 | /* Setup the gpio */ 80 | if(gpio_request(17, "rpi-gpio-17")) { 81 | printk("Error!\nCan not allocate GPIO 17\n"); 82 | return -1; 83 | } 84 | 85 | /* Set GPIO 17 direction */ 86 | if(gpio_direction_input(17)) { 87 | printk("Error!\nCan not set GPIO 17 to input!\n"); 88 | gpio_free(17); 89 | return -1; 90 | } 91 | 92 | gpio_set_debounce(17, 300); 93 | 94 | /* Setup the interrupt */ 95 | irq_number = gpio_to_irq(17); 96 | 97 | if(request_irq(irq_number, gpio_irq_signal_handler, IRQF_TRIGGER_RISING, "my_gpio_irq_signal", NULL) != 0){ 98 | printk("Error!\nCan not request interrupt nr.: %d\n", irq_number); 99 | gpio_free(17); 100 | return -1; 101 | } 102 | 103 | if(register_chrdev(MYMAJOR, "gpio_irq_signal", &fops) < 0) { 104 | printk("Error!\n Can't register device Number!\n"); 105 | gpio_free(17); 106 | free_irq(irq_number, NULL); 107 | } 108 | 109 | printk("Done!\n"); 110 | printk("GPIO 17 is mapped to IRQ Nr.: %d\n", irq_number); 111 | return 0; 112 | } 113 | 114 | /** 115 | * @brief This function is called, when the module is removed from the kernel 116 | */ 117 | static void __exit ModuleExit(void) { 118 | printk("gpio_irq_signal: Unloading module... "); 119 | free_irq(irq_number, NULL); 120 | gpio_free(17); 121 | unregister_chrdev(MYMAJOR, "gpio_irq_signal"); 122 | printk("Done!\n"); 123 | } 124 | 125 | module_init(ModuleInit); 126 | module_exit(ModuleExit); 127 | -------------------------------------------------------------------------------- /15_Sending_Signals/testapp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define SIGTX 44 9 | #define REGISTER_UAPP _IO('R', 'g') 10 | 11 | void signalhandler(int sig) { 12 | printf("Button was pressed!\n"); 13 | } 14 | 15 | int main() { 16 | int fd; 17 | signal(SIGTX, signalhandler); 18 | 19 | printf("PID: %d\n", getpid()); 20 | 21 | /* Open the device file */ 22 | fd = open("/dev/irq_signal", O_RDONLY); 23 | if(fd < 0) { 24 | perror("Could not open device file"); 25 | return -1; 26 | } 27 | 28 | /* Register app to KM */ 29 | if(ioctl(fd, REGISTER_UAPP, NULL)) { 30 | perror("Error registering app"); 31 | close(fd); 32 | return -1; 33 | } 34 | 35 | 36 | /* Wait for Signal */ 37 | printf("Wait for signal...\n"); 38 | while(1) 39 | sleep(1); 40 | 41 | return 0; 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /16_Poll/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += gpio_irq_poll.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /16_Poll/gpio_irq_poll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #define MYMAJOR 64 13 | 14 | /* Meta Information */ 15 | MODULE_LICENSE("GPL"); 16 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 17 | MODULE_DESCRIPTION("Kernel Module which use poll to notify an userspace app when GPIO 17 has a rising edge"); 18 | 19 | /** variable contains pin number o interrupt controller to which GPIO 17 is mapped to */ 20 | unsigned int irq_number; 21 | static int irq_ready = 0; 22 | static wait_queue_head_t waitqueue; 23 | 24 | /** 25 | * @brief Interrupt service routine is called, when interrupt is triggered 26 | */ 27 | static irqreturn_t gpio_irq_poll_handler(int irq, void *dev_id) { 28 | printk("gpio_irq_poll: Interrupt was triggered and ISR was called!\n"); 29 | irq_ready = 1; 30 | wake_up(&waitqueue); 31 | return IRQ_HANDLED; 32 | } 33 | 34 | /** 35 | * @brief Poll callback to allow userspace app to poll for button being pressed 36 | */ 37 | static unsigned int my_poll(struct file *file, poll_table *wait) { 38 | poll_wait(file, &waitqueue, wait); 39 | if(irq_ready == 1) { 40 | irq_ready = 0; 41 | return POLLIN; 42 | } 43 | return 0; 44 | } 45 | 46 | static struct file_operations fops = { 47 | .owner = THIS_MODULE, 48 | .poll = my_poll 49 | }; 50 | 51 | /** 52 | * @brief This function is called, when the module is loaded into the kernel 53 | */ 54 | static int __init ModuleInit(void) { 55 | printk("qpio_irq: Loading module... "); 56 | 57 | /* Init qaitqueue */ 58 | init_waitqueue_head(&waitqueue); 59 | 60 | /* Setup the gpio */ 61 | if(gpio_request(17, "rpi-gpio-17")) { 62 | printk("Error!\nCan not allocate GPIO 17\n"); 63 | return -1; 64 | } 65 | 66 | /* Set GPIO 17 direction */ 67 | if(gpio_direction_input(17)) { 68 | printk("Error!\nCan not set GPIO 17 to input!\n"); 69 | gpio_free(17); 70 | return -1; 71 | } 72 | 73 | gpio_set_debounce(17, 300); 74 | 75 | /* Setup the interrupt */ 76 | irq_number = gpio_to_irq(17); 77 | 78 | if(request_irq(irq_number, gpio_irq_poll_handler, IRQF_TRIGGER_RISING, "my_gpio_irq_poll", NULL) != 0){ 79 | printk("Error!\nCan not request interrupt nr.: %d\n", irq_number); 80 | gpio_free(17); 81 | return -1; 82 | } 83 | 84 | if(register_chrdev(MYMAJOR, "gpio_irq_poll", &fops) < 0) { 85 | printk("Error!\n Can't register device Number!\n"); 86 | gpio_free(17); 87 | free_irq(irq_number, NULL); 88 | } 89 | 90 | printk("Done!\n"); 91 | printk("GPIO 17 is mapped to IRQ Nr.: %d\n", irq_number); 92 | 93 | /* Init waitqueue */ 94 | return 0; 95 | } 96 | 97 | /** 98 | * @brief This function is called, when the module is removed from the kernel 99 | */ 100 | static void __exit ModuleExit(void) { 101 | printk("gpio_irq_poll: Unloading module... "); 102 | free_irq(irq_number, NULL); 103 | gpio_free(17); 104 | unregister_chrdev(MYMAJOR, "gpio_irq_poll"); 105 | printk("Done!\n"); 106 | } 107 | 108 | module_init(ModuleInit); 109 | module_exit(ModuleExit); 110 | -------------------------------------------------------------------------------- /16_Poll/testapp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() { 10 | int fd; 11 | int test; 12 | 13 | struct pollfd my_poll; 14 | 15 | /* Open the device file */ 16 | fd = open("/dev/irq_poll", O_RDONLY); 17 | if(fd < 0) { 18 | perror("Could not open device file"); 19 | return -1; 20 | } 21 | 22 | memset(&my_poll, 0, sizeof(my_poll)); 23 | my_poll.fd = fd; 24 | my_poll.events = POLLIN; 25 | 26 | /* Wait for Signal */ 27 | printf("Wait for signal...\n"); 28 | poll(&my_poll, 1, -1); 29 | printf("Button was pressed!\n"); 30 | 31 | return 0; 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /17_waitqueue/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += waitqueue.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /17_waitqueue/waitqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | /* Meta Information */ 12 | MODULE_LICENSE("GPL"); 13 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 14 | MODULE_DESCRIPTION("A simple example for threads in a LKM"); 15 | 16 | /* Define for device number */ 17 | #define MYMAJOR 64 18 | 19 | /* Global variables for the threads */ 20 | static struct task_struct *kthread_1; 21 | static struct task_struct *kthread_2; 22 | 23 | /* watch_Variable to monitor with the waitqueues */ 24 | static long int watch_var = 0; 25 | 26 | /* Static declaration of waitqueue */ 27 | DECLARE_WAIT_QUEUE_HEAD(wq1); 28 | 29 | /* Dynamic declaration of waitqueue */ 30 | static wait_queue_head_t wq2; 31 | 32 | /** 33 | * @brief Function which will be executed by the threads 34 | * 35 | * @param wait_sel selection of wait function 36 | */ 37 | int thread_function(void * wait_sel) { 38 | int selection = *(int *) wait_sel; 39 | 40 | switch(selection) { 41 | case 1: 42 | wait_event(wq1, watch_var == 11); 43 | printk("waitqueue - watch_var is now 11!\n"); 44 | break; 45 | case 2: 46 | while(wait_event_timeout(wq2, watch_var == 22, msecs_to_jiffies(5000)) == 0) 47 | printk("waitqueue - watch_var is still not 22, but timeout elapsed!\n"); 48 | printk("waitqueue - watch_var is now 22!\n"); 49 | break; 50 | default: 51 | break; 52 | } 53 | printk("waitqueue - Thread monitoring wq%d finished execution!\n", selection); 54 | return 0; 55 | } 56 | 57 | /** 58 | * @brief Write watch_var over device file 59 | */ 60 | static ssize_t my_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 61 | int to_copy, not_copied, delta; 62 | char buffer[16]; 63 | printk("waitqueue - write callback called\n"); 64 | 65 | memset(buffer, 0, sizeof(buffer)); 66 | /* Get amount of data to copy */ 67 | to_copy = min(count, sizeof(buffer)); 68 | 69 | /* Copy data to user */ 70 | not_copied = copy_from_user(buffer, user_buffer, to_copy); 71 | 72 | /* Calculate data */ 73 | delta = to_copy - not_copied; 74 | 75 | /* Convert string to long int */ 76 | if(kstrtol(buffer, 10, &watch_var) == -EINVAL) 77 | printk("waitqueue - Error converting input!\n"); 78 | printk("waitqueue - watch_var is now %ld\n", watch_var); 79 | 80 | wake_up(&wq1); 81 | wake_up(&wq2); 82 | 83 | return delta; 84 | } 85 | 86 | 87 | static struct file_operations fops = { 88 | .owner = THIS_MODULE, 89 | .write = my_write 90 | }; 91 | 92 | /** 93 | * @brief This function is called, when the module is loaded into the kernel 94 | */ 95 | static int __init my_module_init(void) { 96 | int t1 = 1, t2 = 2; 97 | 98 | /* Init dynamically created waitqueue */ 99 | init_waitqueue_head(&wq2); 100 | 101 | printk("waitqueue - Init threads\n"); 102 | 103 | /* Register device number */ 104 | if(register_chrdev(MYMAJOR, "my_dev_nr", &fops) != 0) { 105 | printk("waitqueue - Could not register device number!\n"); 106 | return -1; 107 | } 108 | printk("waitqueue - Device Number registered!\n"); 109 | 110 | kthread_1 = kthread_run(thread_function, &t1, "kthread_1"); 111 | if(kthread_1 != NULL){ 112 | printk("waitqueue - Thread 1 was created and is running now!\n"); 113 | } 114 | else { 115 | printk("waitqueue - Thread 1 could not be created!\n"); 116 | unregister_chrdev(MYMAJOR, "my_dev_nr"); 117 | return -1; 118 | } 119 | kthread_2 = kthread_run(thread_function, &t2, "kthread_2"); 120 | if(kthread_2 != NULL) 121 | printk("waitqueue - Thread 2 was created and is running now!\n"); 122 | else { 123 | printk("waitqueue - Thread 2 could not be created!\n"); 124 | watch_var = 11; 125 | wake_up(&wq1); 126 | mdelay(10); 127 | unregister_chrdev(MYMAJOR, "my_dev_nr"); 128 | return -1; 129 | } 130 | printk("waitqueue - Both threads are running now!\n"); 131 | 132 | return 0; 133 | } 134 | 135 | /** 136 | * @brief This function is called, when the module is removed from the kernel 137 | */ 138 | static void __exit my_module_exit(void) { 139 | printk("waitqueue - Stop both threads\n"); 140 | watch_var = 11; 141 | wake_up(&wq1); 142 | mdelay(10); 143 | watch_var = 22; 144 | wake_up(&wq2); 145 | mdelay(10); 146 | unregister_chrdev(MYMAJOR, "my_dev_nr"); 147 | } 148 | 149 | module_init(my_module_init); 150 | module_exit(my_module_exit); 151 | -------------------------------------------------------------------------------- /18_procfs/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += procfs_test.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /18_procfs/procfs_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Meta Information */ 6 | MODULE_LICENSE("GPL"); 7 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 8 | MODULE_DESCRIPTION("Module creates a folder and file in procfs and implements read and write callbacks"); 9 | 10 | /* global variables for procfs folder and file */ 11 | static struct proc_dir_entry *proc_folder; 12 | static struct proc_dir_entry *proc_file; 13 | 14 | /** 15 | * @brief Read data out of the buffer 16 | */ 17 | static ssize_t my_read(struct file *File, char *user_buffer, size_t count, loff_t *offs) { 18 | char text[] = "Hello from a procfs file!\n"; 19 | int to_copy, not_copied, delta; 20 | 21 | /* Get amount of data to copy */ 22 | to_copy = min(count, sizeof(text)); 23 | 24 | /* Copy data to user */ 25 | not_copied = copy_to_user(user_buffer, text, to_copy); 26 | 27 | /* Calculate data */ 28 | delta = to_copy - not_copied; 29 | 30 | return delta; 31 | } 32 | 33 | /** 34 | * @brief Write data to buffer 35 | */ 36 | static ssize_t my_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 37 | char text[255]; 38 | int to_copy, not_copied, delta; 39 | 40 | /* Clear text */ 41 | memset(text, 0, sizeof(text)); 42 | 43 | /* Get amount of data to copy */ 44 | to_copy = min(count, sizeof(text)); 45 | 46 | /* Copy data to user */ 47 | not_copied = copy_from_user(text, user_buffer, to_copy); 48 | printk("procfs_test - You have written %s to me\n", text); 49 | 50 | /* Calculate data */ 51 | delta = to_copy - not_copied; 52 | 53 | return delta; 54 | } 55 | 56 | static struct proc_ops fops = { 57 | .proc_read = my_read, 58 | .proc_write = my_write, 59 | }; 60 | 61 | /** 62 | * @brief This function is called, when the module is loaded into the kernel 63 | */ 64 | static int __init my_init(void) { 65 | /* /proc/hello/dummy */ 66 | 67 | proc_folder = proc_mkdir("hello", NULL); 68 | if(proc_folder == NULL) { 69 | printk("procfs_test - Error creating /proc/hello\n"); 70 | return -ENOMEM; 71 | } 72 | 73 | proc_file = proc_create("dummy", 0666, proc_folder, &fops); 74 | if(proc_file == NULL) { 75 | printk("procfs_test - Error creating /proc/hello/dummy\n"); 76 | proc_remove(proc_folder); 77 | return -ENOMEM; 78 | } 79 | 80 | printk("procfs_test - Created /proc/hello/dummy\n"); 81 | return 0; 82 | } 83 | 84 | /** 85 | * @brief This function is called, when the module is removed from the kernel 86 | */ 87 | static void __exit my_exit(void) { 88 | printk("procfs_test - Removing /proc/hello/dummy\n"); 89 | proc_remove(proc_file); 90 | proc_remove(proc_folder); 91 | } 92 | 93 | module_init(my_init); 94 | module_exit(my_exit); 95 | -------------------------------------------------------------------------------- /19_sysfs/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += sysfs_test.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /19_sysfs/sysfs_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("Creating a folder and a file in sysfs"); 10 | 11 | /* global variable for sysfs folder hello */ 12 | static struct kobject *dummy_kobj; 13 | 14 | /** 15 | * @brief Read callback for hello/dummy 16 | */ 17 | static ssize_t dummy_show(struct kobject *kobj, struct kobj_attribute *attr, char *buffer) { 18 | return sprintf(buffer, "You have read from /sys/kernel/%s/%s\n", kobj->name, attr->attr.name); 19 | } 20 | 21 | /** 22 | * @brief Read callback for hello/dummy 23 | */ 24 | static ssize_t dummy_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buffer, size_t count) { 25 | printk("sysfs_test - You wrote '%s' to /sys/kernel/%s/%s\n", buffer, kobj->name, attr->attr.name); 26 | return count; 27 | } 28 | 29 | static struct kobj_attribute dummy_attr = __ATTR(dummy, 0660, dummy_show, dummy_store); 30 | 31 | /** 32 | * @brief This function is called, when the module is loaded into the kernel 33 | */ 34 | static int __init my_init(void) { 35 | printk("sysfs_test - Creating /sys/kernel/hello/dummy\n"); 36 | 37 | /* Creating the folder hello */ 38 | dummy_kobj = kobject_create_and_add("hello", kernel_kobj); 39 | if(!dummy_kobj) { 40 | printk("sysfs_test - Error creating /sys/kernel/hello\n"); 41 | return -ENOMEM; 42 | } 43 | 44 | /* Create the sysfs file dummy */ 45 | if(sysfs_create_file(dummy_kobj, &dummy_attr.attr)) { 46 | printk("sysfs_test - Error creating /sys/kernel/hello/dummy\n"); 47 | kobject_put(dummy_kobj); 48 | return -ENOMEM; 49 | } 50 | return 0; 51 | } 52 | 53 | /** 54 | * @brief This function is called, when the module is removed from the kernel 55 | */ 56 | static void __exit my_exit(void) { 57 | printk("sysfs_test - Deleting /sys/kernel/hello/dummy\n"); 58 | sysfs_remove_file(dummy_kobj, &dummy_attr.attr); 59 | kobject_put(dummy_kobj); 60 | } 61 | 62 | module_init(my_init); 63 | module_exit(my_exit); 64 | 65 | 66 | -------------------------------------------------------------------------------- /20_dt_probe/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dt_probe.o 2 | 3 | all: module dt 4 | echo Builded Device Tree Overlay and kernel module 5 | 6 | module: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 8 | dt: testoverlay.dts 9 | dtc -@ -I dts -O dtb -o testoverlay.dtbo testoverlay.dts 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | rm -rf testoverlay.dtbo 13 | 14 | -------------------------------------------------------------------------------- /20_dt_probe/dt_probe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Meta Information */ 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 11 | MODULE_DESCRIPTION("A simple LKM to parse the device tree for a specific device and its properties"); 12 | 13 | /* Declate the probe and remove functions */ 14 | static int dt_probe(struct platform_device *pdev); 15 | static int dt_remove(struct platform_device *pdev); 16 | 17 | static struct of_device_id my_driver_ids[] = { 18 | { 19 | .compatible = "brightlight,mydev", 20 | }, { /* sentinel */ } 21 | }; 22 | MODULE_DEVICE_TABLE(of, my_driver_ids); 23 | 24 | static struct platform_driver my_driver = { 25 | .probe = dt_probe, 26 | .remove = dt_remove, 27 | .driver = { 28 | .name = "my_device_driver", 29 | .of_match_table = my_driver_ids, 30 | }, 31 | }; 32 | 33 | /** 34 | * @brief This function is called on loading the driver 35 | */ 36 | static int dt_probe(struct platform_device *pdev) { 37 | struct device *dev = &pdev->dev; 38 | const char *label; 39 | int my_value, ret; 40 | 41 | printk("dt_probe - Now I am in the probe function!\n"); 42 | 43 | /* Check for device properties */ 44 | if(!device_property_present(dev, "label")) { 45 | printk("dt_probe - Error! Device property 'label' not found!\n"); 46 | return -1; 47 | } 48 | if(!device_property_present(dev, "my_value")) { 49 | printk("dt_probe - Error! Device property 'my_value' not found!\n"); 50 | return -1; 51 | } 52 | 53 | /* Read device properties */ 54 | ret = device_property_read_string(dev, "label", &label); 55 | if(ret) { 56 | printk("dt_probe - Error! Could not read 'label'\n"); 57 | return -1; 58 | } 59 | printk("dt_probe - label: %s\n", label); 60 | ret = device_property_read_u32(dev, "my_value", &my_value); 61 | if(ret) { 62 | printk("dt_probe - Error! Could not read 'my_value'\n"); 63 | return -1; 64 | } 65 | printk("dt_probe - my_value: %d\n", my_value); 66 | 67 | return 0; 68 | } 69 | 70 | /** 71 | * @brief This function is called on unloading the driver 72 | */ 73 | static int dt_remove(struct platform_device *pdev) { 74 | printk("dt_probe - Now I am in the remove function\n"); 75 | return 0; 76 | } 77 | 78 | /** 79 | * @brief This function is called, when the module is loaded into the kernel 80 | */ 81 | static int __init my_init(void) { 82 | printk("dt_probe - Loading the driver...\n"); 83 | if(platform_driver_register(&my_driver)) { 84 | printk("dt_probe - Error! Could not load driver\n"); 85 | return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | /** 91 | * @brief This function is called, when the module is removed from the kernel 92 | */ 93 | static void __exit my_exit(void) { 94 | printk("dt_probe - Unload driver"); 95 | platform_driver_unregister(&my_driver); 96 | } 97 | 98 | module_init(my_init); 99 | module_exit(my_exit); 100 | 101 | 102 | -------------------------------------------------------------------------------- /20_dt_probe/testoverlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | / { 4 | compatible = "brcm,bcm2835"; 5 | fragment@0 { 6 | target-path = "/"; 7 | __overlay__ { 8 | my_device { 9 | compatible = "brightlight,mydev"; 10 | status = "okay"; 11 | label = "Test"; 12 | my_value = <12>; 13 | }; 14 | }; 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /21_dt_gpio/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dt_gpio.o 2 | 3 | all: module dt 4 | echo Builded Device Tree Overlay and kernel module 5 | 6 | module: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 8 | dt: testoverlay.dts 9 | dtc -@ -I dts -O dtb -o testoverlay.dtbo testoverlay.dts 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | rm -rf testoverlay.dtbo 13 | 14 | -------------------------------------------------------------------------------- /21_dt_gpio/dt_gpio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Meta Information */ 11 | MODULE_LICENSE("GPL"); 12 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 13 | MODULE_DESCRIPTION("A simple LKM to parse the device tree for a specific device and its properties"); 14 | 15 | /* Declate the probe and remove functions */ 16 | static int dt_probe(struct platform_device *pdev); 17 | static int dt_remove(struct platform_device *pdev); 18 | 19 | static struct of_device_id my_driver_ids[] = { 20 | { 21 | .compatible = "brightlight,mydev", 22 | }, { /* sentinel */ } 23 | }; 24 | MODULE_DEVICE_TABLE(of, my_driver_ids); 25 | 26 | static struct platform_driver my_driver = { 27 | .probe = dt_probe, 28 | .remove = dt_remove, 29 | .driver = { 30 | .name = "my_device_driver", 31 | .of_match_table = my_driver_ids, 32 | }, 33 | }; 34 | 35 | /* GPIO variable */ 36 | static struct gpio_desc *my_led = NULL; 37 | 38 | static struct proc_dir_entry *proc_file; 39 | 40 | /** 41 | * @brief Write data to buffer 42 | */ 43 | static ssize_t my_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 44 | switch (user_buffer[0]) { 45 | case '0': 46 | case '1': 47 | gpiod_set_value(my_led, user_buffer[0] - '0'); 48 | default: 49 | break; 50 | } 51 | return count; 52 | } 53 | 54 | static struct proc_ops fops = { 55 | .proc_write = my_write, 56 | }; 57 | 58 | /** 59 | * @brief This function is called on loading the driver 60 | */ 61 | static int dt_probe(struct platform_device *pdev) { 62 | struct device *dev = &pdev->dev; 63 | const char *label; 64 | int my_value, ret; 65 | 66 | printk("dt_gpio - Now I am in the probe function!\n"); 67 | 68 | /* Check for device properties */ 69 | if(!device_property_present(dev, "label")) { 70 | printk("dt_gpio - Error! Device property 'label' not found!\n"); 71 | return -1; 72 | } 73 | if(!device_property_present(dev, "my_value")) { 74 | printk("dt_gpio - Error! Device property 'my_value' not found!\n"); 75 | return -1; 76 | } 77 | if(!device_property_present(dev, "green-led-gpio")) { 78 | printk("dt_gpio - Error! Device property 'green-led-gpio' not found!\n"); 79 | return -1; 80 | } 81 | 82 | /* Read device properties */ 83 | ret = device_property_read_string(dev, "label", &label); 84 | if(ret) { 85 | printk("dt_gpio - Error! Could not read 'label'\n"); 86 | return -1; 87 | } 88 | printk("dt_gpio - label: %s\n", label); 89 | ret = device_property_read_u32(dev, "my_value", &my_value); 90 | if(ret) { 91 | printk("dt_gpio - Error! Could not read 'my_value'\n"); 92 | return -1; 93 | } 94 | printk("dt_gpio - my_value: %d\n", my_value); 95 | 96 | /* Init GPIO */ 97 | my_led = gpiod_get(dev, "green-led", GPIOD_OUT_LOW); 98 | if(IS_ERR(my_led)) { 99 | printk("dt_gpio - Error! Could not setup the GPIO\n"); 100 | return -1 * IS_ERR(my_led); 101 | } 102 | 103 | /* Creating procfs file */ 104 | proc_file = proc_create("my-led", 0666, NULL, &fops); 105 | if(proc_file == NULL) { 106 | printk("procfs_test - Error creating /proc/my-led\n"); 107 | gpiod_put(my_led); 108 | return -ENOMEM; 109 | } 110 | 111 | 112 | return 0; 113 | } 114 | 115 | /** 116 | * @brief This function is called on unloading the driver 117 | */ 118 | static int dt_remove(struct platform_device *pdev) { 119 | printk("dt_gpio - Now I am in the remove function\n"); 120 | gpiod_put(my_led); 121 | proc_remove(proc_file); 122 | return 0; 123 | } 124 | 125 | /** 126 | * @brief This function is called, when the module is loaded into the kernel 127 | */ 128 | static int __init my_init(void) { 129 | printk("dt_gpio - Loading the driver...\n"); 130 | if(platform_driver_register(&my_driver)) { 131 | printk("dt_gpio - Error! Could not load driver\n"); 132 | return -1; 133 | } 134 | return 0; 135 | } 136 | 137 | /** 138 | * @brief This function is called, when the module is removed from the kernel 139 | */ 140 | static void __exit my_exit(void) { 141 | printk("dt_gpio - Unload driver"); 142 | platform_driver_unregister(&my_driver); 143 | } 144 | 145 | module_init(my_init); 146 | module_exit(my_exit); 147 | 148 | 149 | -------------------------------------------------------------------------------- /21_dt_gpio/testoverlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | / { 4 | compatible = "brcm,bcm2835"; 5 | fragment@0 { 6 | target-path = "/"; 7 | __overlay__ { 8 | my_device { 9 | compatible = "brightlight,mydev"; 10 | status = "okay"; 11 | label = "Test"; 12 | my_value = <12>; 13 | green-led-gpio = <&gpio 21 0>; 14 | }; 15 | }; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /22_dt_i2c/Atmega_I2C_ADC/I2CSlave.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "I2CSlave.h" 5 | 6 | static void (*I2C_recv)(uint8_t); 7 | static void (*I2C_req)(); 8 | 9 | void I2C_setCallbacks(void (*recv)(uint8_t), void (*req)()) 10 | { 11 | I2C_recv = recv; 12 | I2C_req = req; 13 | } 14 | 15 | void I2C_init(uint8_t address) 16 | { 17 | cli(); 18 | // load address into TWI address register 19 | TWAR = address << 1; 20 | // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt 21 | TWCR = (1< 5 | #include 6 | 7 | void I2C_init(uint8_t address); 8 | void I2C_stop(void); 9 | void I2C_setCallbacks(void (*recv)(uint8_t), void (*req)()); 10 | 11 | inline void __attribute__((always_inline)) I2C_transmitByte(uint8_t data) 12 | { 13 | TWDR = data; 14 | } 15 | 16 | ISR(TWI_vect); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /22_dt_i2c/Atmega_I2C_ADC/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Parth Dave 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /22_dt_i2c/Atmega_I2C_ADC/README.md: -------------------------------------------------------------------------------- 1 | # AVR I2C Slave Library 2 | 3 | This library provides interrupt-based I2C slave functionality for Atmel 8-bit microcontrollers equipped with a TWI peripheral. 4 | 5 | It is somewhat based on an existing library found here: https://github.com/devthrash/I2C-slave-lib 6 | 7 | Files 8 | ----- 9 | * `I2CSlave.c` -- Implements init/stop, and interrupt-based receive and request logic 10 | * `I2CSlave.h` -- Function prototypes and a convience method for transmitting data 11 | 12 | Usage 13 | ----- 14 | **Provide callbacks for receiving and handling requests in your application code.** 15 | eg: 16 | ```c 17 | void I2C_received(uint8_t data); 18 | void I2C_requested(); 19 | 20 | I2C_setCallbacks(I2C_received, I2C_requested); 21 | ``` 22 | 23 | The library calls the received callback *for each byte* the master transmits to the slave. 24 | The library calls the requested callback *for each byte* the master attempts to read from the slave. 25 | 26 | **Init the I2C slave with the slave address** 27 | ```c 28 | I2C_init(I2C_ADDRESS); 29 | ``` 30 | 31 | **Transmitting data to the master when requested** 32 | ```c 33 | I2C_transmitByte(data); 34 | ``` 35 | 36 | Example 37 | ------- 38 | 39 | The example in `main.c` implements a sample application in which the slave can receive a byte, 40 | and then will echo the byte when requested. 41 | 42 | To compile: 43 | `make` 44 | -------------------------------------------------------------------------------- /22_dt_i2c/Atmega_I2C_ADC/main.c: -------------------------------------------------------------------------------- 1 | /* I2C Echo Example */ 2 | #include "I2CSlave.h" 3 | #include 4 | #include 5 | 6 | #define I2C_ADDR 0x10 7 | 8 | volatile uint8_t update_interval = 100; /* Update Interval in 10ms */ 9 | volatile uint8_t adc_value = 0; /* ADC value*/ 10 | 11 | void I2C_received(uint8_t received_data) 12 | { 13 | update_interval = received_data; 14 | } 15 | 16 | void I2C_requested() 17 | { 18 | I2C_transmitByte(adc_value); 19 | } 20 | 21 | void setup() 22 | { 23 | // set received/requested callbacks 24 | I2C_setCallbacks(I2C_received, I2C_requested); 25 | 26 | // init I2C 27 | I2C_init(I2C_ADDR); 28 | 29 | // Init ADC 30 | ADMUX = (1<>2)); 58 | PORTB ^= 0x1; 59 | 60 | 61 | 62 | my_delay_ms_10ms_steps(update_interval); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /22_dt_i2c/Atmega_I2C_ADC/makefile: -------------------------------------------------------------------------------- 1 | DEVICE = atmega88pa 2 | MCU = atmega88pa 3 | F_CPU = 8000000 4 | TARGET = adc 5 | 6 | CC = avr-gcc 7 | OBJCOPY = avr-objcopy 8 | 9 | INCLUDES = -I./ -I/usr/lib/avr/include 10 | CFLAGS = -std=c99 -g -Wall -mmcu=$(MCU) -DF_CPU=$(F_CPU) $(INCLUDES) -Os 11 | LDFLAGS = -Wl,-gc-sections -Wl,-relax 12 | 13 | OBJECT_FILES = main.o I2CSlave.o 14 | 15 | all: $(TARGET).hex size 16 | 17 | size: 18 | @if [ -f $(TARGET).obj ]; then avr-size -C --mcu=$(MCU) $(TARGET).obj; fi 19 | 20 | clean: 21 | rm -rf *.o *.hex *.obj 22 | 23 | %.hex: %.obj 24 | $(OBJCOPY) -R .eeprom -O ihex $< $@ 25 | 26 | %.obj: $(OBJECT_FILES) 27 | $(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@ 28 | -------------------------------------------------------------------------------- /22_dt_i2c/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dt_i2c.o 2 | 3 | all: module dt 4 | echo Builded Device Tree Overlay and kernel module 5 | 6 | module: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 8 | dt: testoverlay.dts 9 | dtc -@ -I dts -O dtb -o testoverlay.dtbo testoverlay.dts 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | rm -rf testoverlay.dtbo 13 | 14 | -------------------------------------------------------------------------------- /22_dt_i2c/dt_i2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("A driver for my simple AMTEGA I2C ADC"); 10 | 11 | static struct i2c_client *adc_client; 12 | 13 | /* Declate the probe and remove functions */ 14 | static int my_adc_probe(struct i2c_client *client, const struct i2c_device_id *id); 15 | static int my_adc_remove(struct i2c_client *client); 16 | 17 | static struct of_device_id my_driver_ids[] = { 18 | { 19 | .compatible = "brightlight,myadc", 20 | }, { /* sentinel */ } 21 | }; 22 | MODULE_DEVICE_TABLE(of, my_driver_ids); 23 | 24 | static struct i2c_device_id my_adc[] = { 25 | {"my_adc", 0}, 26 | { }, 27 | }; 28 | MODULE_DEVICE_TABLE(i2c, my_adc); 29 | 30 | static struct i2c_driver my_driver = { 31 | .probe = my_adc_probe, 32 | .remove = my_adc_remove, 33 | .id_table = my_adc, 34 | .driver = { 35 | .name = "my_adc", 36 | .of_match_table = my_driver_ids, 37 | }, 38 | }; 39 | 40 | static struct proc_dir_entry *proc_file; 41 | 42 | /** 43 | * @brief Update timing between to ADC reads 44 | */ 45 | static ssize_t my_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { 46 | long val; 47 | if(0 == kstrtol(user_buffer, 0, &val)) 48 | i2c_smbus_write_byte(adc_client, (u8) val); 49 | return count; 50 | } 51 | 52 | /** 53 | * @brief Read ADC value 54 | */ 55 | static ssize_t my_read(struct file *File, char *user_buffer, size_t count, loff_t *offs) { 56 | u8 adc; 57 | adc = i2c_smbus_read_byte(adc_client); 58 | return sprintf(user_buffer, "%d\n", adc); 59 | } 60 | 61 | static struct proc_ops fops = { 62 | .proc_write = my_write, 63 | .proc_read = my_read, 64 | }; 65 | 66 | /** 67 | * @brief This function is called on loading the driver 68 | */ 69 | static int my_adc_probe(struct i2c_client *client, const struct i2c_device_id *id) { 70 | printk("dt_i2c - Now I am in the Probe function!\n"); 71 | 72 | if(client->addr != 0x10) { 73 | printk("dt_i2c - Wrong I2C address!\n"); 74 | return -1; 75 | } 76 | 77 | adc_client = client; 78 | 79 | /* Creating procfs file */ 80 | proc_file = proc_create("myadc", 0666, NULL, &fops); 81 | if(proc_file == NULL) { 82 | printk("dt_i2c - Error creating /proc/myadc\n"); 83 | return -ENOMEM; 84 | } 85 | 86 | return 0; 87 | } 88 | 89 | /** 90 | * @brief This function is called on unloading the driver 91 | */ 92 | static int my_adc_remove(struct i2c_client *client) { 93 | printk("dt_i2c - Now I am in the Remove function!\n"); 94 | proc_remove(proc_file); 95 | return 0; 96 | } 97 | 98 | /* This will create the init and exit function automatically */ 99 | module_i2c_driver(my_driver); 100 | 101 | -------------------------------------------------------------------------------- /22_dt_i2c/testoverlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | / { 4 | compatible = "brcm,bcm2835"; 5 | fragment@0 { 6 | target = <&i2c1>; 7 | __overlay__ { 8 | #address-cells = <1>; 9 | #size-cells = <0>; 10 | 11 | my_adc: my_adc@10 { 12 | compatible = "brightlight,myadc"; 13 | reg = <0x10>; 14 | status = "okay"; 15 | }; 16 | }; 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /23_malloc/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += alloc_test.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /23_malloc/alloc_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("Demonstartion for dynamic memory management in a Linux Kernel Module"); 10 | 11 | struct driver_data { 12 | u8 version; 13 | char data[64]; 14 | }; 15 | 16 | u32 *ptr1; 17 | struct driver_data *ptr2; 18 | 19 | /** 20 | * @brief This function is called, when the module is loaded into the kernel 21 | */ 22 | static int __init my_init(void) { 23 | ptr1 = kmalloc(sizeof(u32), GFP_KERNEL); 24 | if(ptr1 == NULL) { 25 | printk("alloc_test - Out of memory!\n"); 26 | return -1; 27 | } 28 | printk("alloc_test - *ptr1: 0x%x\n", *ptr1); 29 | *ptr1 = 0xc001c0de; 30 | printk("alloc_test - *ptr1: 0x%x\n", *ptr1); 31 | kfree(ptr1); 32 | 33 | ptr1 = kzalloc(sizeof(u32), GFP_KERNEL); 34 | if(ptr1 == NULL) { 35 | printk("alloc_test - Out of memory!\n"); 36 | return -1; 37 | } 38 | printk("alloc_test - *ptr1: 0x%x\n", *ptr1); 39 | *ptr1 = 0xc001c0de; 40 | printk("alloc_test - *ptr1: 0x%x\n", *ptr1); 41 | kfree(ptr1); 42 | 43 | ptr2 = kzalloc(sizeof(struct driver_data), GFP_KERNEL); 44 | if(ptr2 == NULL) { 45 | printk("alloc_test - Out of memory!\n"); 46 | return -1; 47 | } 48 | 49 | ptr2->version = 123; 50 | strcpy(ptr2->data, "This is a test string for my linux kernel module"); 51 | 52 | 53 | printk("alloc_test - ptr2->version: %d\n", ptr2->version); 54 | printk("alloc_test - ptr2->data: %s\n", ptr2->data); 55 | 56 | return 0; 57 | } 58 | 59 | /** 60 | * @brief This function is called, when the module is removed from the kernel 61 | */ 62 | static void __exit my_exit(void) { 63 | printk("alloc_test - ptr2->version: %d\n", ptr2->version); 64 | printk("alloc_test - ptr2->data: %s\n", ptr2->data); 65 | kfree(ptr2); 66 | } 67 | 68 | module_init(my_init); 69 | module_exit(my_exit); 70 | 71 | 72 | -------------------------------------------------------------------------------- /24_serdev/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += serdev_echo.o 2 | 3 | all: module dt 4 | echo Builded Device Tree Overlay and kernel module 5 | 6 | module: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 8 | dt: serdev_overlay.dts 9 | dtc -@ -I dts -O dtb -o serdev_overlay.dtbo serdev_overlay.dts 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | rm -rf serdev_overlay.dtbo 13 | 14 | -------------------------------------------------------------------------------- /24_serdev/README.md: -------------------------------------------------------------------------------- 1 | # Serial (UART) Driver 2 | 3 | For this driver you need an UART-to-USB Adpter to receive and send characters to the Linux Driver. 4 | 5 | Run `sudo raspi-config`, go to *Interfaces* -> *Serial* and Disable *Login Shell over SSH* but enable *Serial Port*. 6 | 7 | 8 | -------------------------------------------------------------------------------- /24_serdev/serdev_echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Meta Information */ 10 | MODULE_LICENSE("GPL"); 11 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 12 | MODULE_DESCRIPTION("A simple loopback driver for an UART port"); 13 | 14 | /* Declate the probe and remove functions */ 15 | static int serdev_echo_probe(struct serdev_device *serdev); 16 | static void serdev_echo_remove(struct serdev_device *serdev); 17 | 18 | static struct of_device_id serdev_echo_ids[] = { 19 | { 20 | .compatible = "brightlight,echodev", 21 | }, { /* sentinel */ } 22 | }; 23 | MODULE_DEVICE_TABLE(of, serdev_echo_ids); 24 | 25 | static struct serdev_device_driver serdev_echo_driver = { 26 | .probe = serdev_echo_probe, 27 | .remove = serdev_echo_remove, 28 | .driver = { 29 | .name = "serdev-echo", 30 | .of_match_table = serdev_echo_ids, 31 | }, 32 | }; 33 | 34 | /** 35 | * @brief Callback is called whenever a character is received 36 | */ 37 | static int serdev_echo_recv(struct serdev_device *serdev, const unsigned char *buffer, size_t size) { 38 | printk("serdev_echo - Received %ld bytes with \"%s\"\n", size, buffer); 39 | return serdev_device_write_buf(serdev, buffer, size); 40 | } 41 | 42 | static const struct serdev_device_ops serdev_echo_ops = { 43 | .receive_buf = serdev_echo_recv, 44 | }; 45 | 46 | /** 47 | * @brief This function is called on loading the driver 48 | */ 49 | static int serdev_echo_probe(struct serdev_device *serdev) { 50 | int status; 51 | printk("serdev_echo - Now I am in the probe function!\n"); 52 | 53 | serdev_device_set_client_ops(serdev, &serdev_echo_ops); 54 | status = serdev_device_open(serdev); 55 | if(status) { 56 | printk("serdev_echo - Error opening serial port!\n"); 57 | return -status; 58 | } 59 | 60 | serdev_device_set_baudrate(serdev, 9600); 61 | serdev_device_set_flow_control(serdev, false); 62 | serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); 63 | 64 | status = serdev_device_write_buf(serdev, "Type something: ", sizeof("Type something: ")); 65 | printk("serdev_echo - Wrote %d bytes.\n", status); 66 | 67 | return 0; 68 | } 69 | 70 | /** 71 | * @brief This function is called on unloading the driver 72 | */ 73 | static void serdev_echo_remove(struct serdev_device *serdev) { 74 | printk("serdev_echo - Now I am in the remove function\n"); 75 | serdev_device_close(serdev); 76 | } 77 | 78 | /** 79 | * @brief This function is called, when the module is loaded into the kernel 80 | */ 81 | static int __init my_init(void) { 82 | printk("serdev_echo - Loading the driver...\n"); 83 | if(serdev_device_driver_register(&serdev_echo_driver)) { 84 | printk("serdev_echo - Error! Could not load driver\n"); 85 | return -1; 86 | } 87 | return 0; 88 | } 89 | 90 | /** 91 | * @brief This function is called, when the module is removed from the kernel 92 | */ 93 | static void __exit my_exit(void) { 94 | printk("serdev_echo - Unload driver"); 95 | serdev_device_driver_unregister(&serdev_echo_driver); 96 | } 97 | 98 | module_init(my_init); 99 | module_exit(my_exit); 100 | 101 | 102 | -------------------------------------------------------------------------------- /24_serdev/serdev_overlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | / { 4 | compatible = "brcm,bcm2835"; 5 | fragment@0 { 6 | target = <&uart1>; 7 | __overlay__ { 8 | echodev { 9 | compatible = "brightlight,echodev"; 10 | status = "okay"; 11 | }; 12 | }; 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /25_dt_iio/Atmega_I2C_ADC/I2CSlave.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "I2CSlave.h" 5 | 6 | static void (*I2C_recv)(uint8_t); 7 | static void (*I2C_req)(); 8 | 9 | void I2C_setCallbacks(void (*recv)(uint8_t), void (*req)()) 10 | { 11 | I2C_recv = recv; 12 | I2C_req = req; 13 | } 14 | 15 | void I2C_init(uint8_t address) 16 | { 17 | cli(); 18 | // load address into TWI address register 19 | TWAR = address << 1; 20 | // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt 21 | TWCR = (1< 5 | #include 6 | 7 | void I2C_init(uint8_t address); 8 | void I2C_stop(void); 9 | void I2C_setCallbacks(void (*recv)(uint8_t), void (*req)()); 10 | 11 | inline void __attribute__((always_inline)) I2C_transmitByte(uint8_t data) 12 | { 13 | TWDR = data; 14 | } 15 | 16 | ISR(TWI_vect); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /25_dt_iio/Atmega_I2C_ADC/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Parth Dave 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /25_dt_iio/Atmega_I2C_ADC/README.md: -------------------------------------------------------------------------------- 1 | # AVR I2C Slave Library 2 | 3 | This library provides interrupt-based I2C slave functionality for Atmel 8-bit microcontrollers equipped with a TWI peripheral. 4 | 5 | It is somewhat based on an existing library found here: https://github.com/devthrash/I2C-slave-lib 6 | 7 | Files 8 | ----- 9 | * `I2CSlave.c` -- Implements init/stop, and interrupt-based receive and request logic 10 | * `I2CSlave.h` -- Function prototypes and a convience method for transmitting data 11 | 12 | Usage 13 | ----- 14 | **Provide callbacks for receiving and handling requests in your application code.** 15 | eg: 16 | ```c 17 | void I2C_received(uint8_t data); 18 | void I2C_requested(); 19 | 20 | I2C_setCallbacks(I2C_received, I2C_requested); 21 | ``` 22 | 23 | The library calls the received callback *for each byte* the master transmits to the slave. 24 | The library calls the requested callback *for each byte* the master attempts to read from the slave. 25 | 26 | **Init the I2C slave with the slave address** 27 | ```c 28 | I2C_init(I2C_ADDRESS); 29 | ``` 30 | 31 | **Transmitting data to the master when requested** 32 | ```c 33 | I2C_transmitByte(data); 34 | ``` 35 | 36 | Example 37 | ------- 38 | 39 | The example in `main.c` implements a sample application in which the slave can receive a byte, 40 | and then will echo the byte when requested. 41 | 42 | To compile: 43 | `make` 44 | -------------------------------------------------------------------------------- /25_dt_iio/Atmega_I2C_ADC/main.c: -------------------------------------------------------------------------------- 1 | /* I2C Echo Example */ 2 | #include "I2CSlave.h" 3 | #include 4 | #include 5 | 6 | #define I2C_ADDR 0x10 7 | 8 | #define CMD_IDLE 0x00 9 | #define CMD_GET_STATE 0x11 10 | #define CMD_SET_STATE 0x22 11 | #define CMD_GET_CHANNEL 0x33 12 | #define CMD_SET_CHANNEL 0x44 13 | #define CMD_GET_ADC_VAL 0x55 14 | 15 | #define STATE_OFF 0x0 16 | #define STATE_ON 0x1 17 | 18 | 19 | 20 | volatile uint8_t adc_value = 0; /* ADC value*/ 21 | volatile uint8_t sel_adc = 0; 22 | volatile uint8_t state = STATE_OFF; 23 | volatile uint8_t cmd = CMD_IDLE; 24 | 25 | void I2C_received(uint8_t received_data) 26 | { 27 | switch(cmd) { 28 | case CMD_IDLE: 29 | switch(received_data) { 30 | case CMD_GET_STATE: 31 | case CMD_SET_STATE: 32 | case CMD_GET_CHANNEL: 33 | case CMD_SET_CHANNEL: 34 | case CMD_GET_ADC_VAL: 35 | cmd = received_data; 36 | break; 37 | default: 38 | break; 39 | } 40 | break; 41 | case CMD_SET_STATE: 42 | state = received_data; 43 | cmd = CMD_IDLE; 44 | break; 45 | case CMD_SET_CHANNEL: 46 | sel_adc = received_data; 47 | cmd = CMD_IDLE; 48 | break; 49 | default: 50 | break; 51 | } 52 | } 53 | 54 | void I2C_requested() 55 | { 56 | switch(cmd) { 57 | case CMD_GET_STATE: 58 | I2C_transmitByte(state); 59 | cmd = CMD_IDLE; 60 | break; 61 | case CMD_GET_CHANNEL: 62 | I2C_transmitByte(sel_adc); 63 | cmd = CMD_IDLE; 64 | break; 65 | case CMD_GET_ADC_VAL: 66 | I2C_transmitByte(adc_value); 67 | cmd = CMD_IDLE; 68 | break; 69 | default: 70 | I2C_transmitByte(0x51); 71 | break; 72 | } 73 | } 74 | 75 | void setup() 76 | { 77 | // set received/requested callbacks 78 | I2C_setCallbacks(I2C_received, I2C_requested); 79 | 80 | // init I2C 81 | I2C_init(I2C_ADDR); 82 | } 83 | 84 | int main() 85 | { 86 | setup(); 87 | DDRB = 0x1; 88 | 89 | // Main program loop 90 | while(1) { 91 | if(state == STATE_ON) { 92 | /* Select ADC */ 93 | ADMUX = ((1<>2)); 103 | PORTB |= 0x1; 104 | } 105 | else 106 | PORTB &= 0xfe; 107 | 108 | _delay_ms(50); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /25_dt_iio/Atmega_I2C_ADC/makefile: -------------------------------------------------------------------------------- 1 | DEVICE = atmega88pa 2 | MCU = atmega88pa 3 | F_CPU = 8000000 4 | TARGET = adc 5 | 6 | CC = avr-gcc 7 | OBJCOPY = avr-objcopy 8 | 9 | INCLUDES = -I./ -I/usr/lib/avr/include 10 | CFLAGS = -std=c99 -g -Wall -mmcu=$(MCU) -DF_CPU=$(F_CPU) $(INCLUDES) -Os 11 | LDFLAGS = -Wl,-gc-sections -Wl,-relax 12 | 13 | OBJECT_FILES = main.o I2CSlave.o 14 | 15 | all: $(TARGET).hex size 16 | 17 | size: 18 | @if [ -f $(TARGET).obj ]; then avr-size -C --mcu=$(MCU) $(TARGET).obj; fi 19 | 20 | clean: 21 | rm -rf *.o *.hex *.obj 22 | 23 | %.hex: %.obj 24 | $(OBJCOPY) -R .eeprom -O ihex $< $@ 25 | 26 | %.obj: $(OBJECT_FILES) 27 | $(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@ 28 | -------------------------------------------------------------------------------- /25_dt_iio/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dt_iio.o 2 | 3 | all: module dt 4 | echo Builded Device Tree Overlay and kernel module 5 | 6 | module: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 8 | dt: testoverlay.dts 9 | dtc -@ -I dts -O dtb -o testoverlay.dtbo testoverlay.dts 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | rm -rf testoverlay.dtbo 13 | 14 | -------------------------------------------------------------------------------- /25_dt_iio/README.md: -------------------------------------------------------------------------------- 1 | # Using the Atmega I2C ADC 2 | 3 | I recommend to decrease the I2C frequency so that the I2C bus is not to fast for my slow Atmega I2C ADC (I use an 8MHz oscillator). 4 | 5 | To decrease the frequency, add the following at the end of /boot/config.txt 6 | 7 | ~~~ 8 | dtparam i2c_baudrate=10000 9 | ~~~ 10 | 11 | 12 | And also don't forget to enable I2C over raspi-config! 13 | -------------------------------------------------------------------------------- /25_dt_iio/dt_iio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define CMD_GET_STATE 0x11 9 | #define CMD_SET_STATE 0x22 10 | #define CMD_GET_ADC_VAL 0x55 11 | 12 | /* Meta Information */ 13 | MODULE_LICENSE("GPL"); 14 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 15 | MODULE_DESCRIPTION("A driver for my simple AMTEGA I2C ADC"); 16 | 17 | struct my_adc { 18 | struct i2c_client *client; 19 | }; 20 | 21 | static int my_adc_read_raw(struct iio_dev * indio_dev, struct iio_chan_spec const * chan, int *val, int *val2, long mask) { 22 | struct my_adc *adc = iio_priv(indio_dev); 23 | int ret; 24 | 25 | if(mask == IIO_CHAN_INFO_RAW) { 26 | ret = i2c_smbus_read_byte_data(adc->client, CMD_GET_ADC_VAL); 27 | if(ret < 0) { 28 | printk("dt_iio - Error reading ADC value!\n"); 29 | return ret; 30 | } 31 | *val = ret; 32 | } 33 | else 34 | return -EINVAL; 35 | return IIO_VAL_INT; 36 | } 37 | 38 | static const struct iio_chan_spec my_adc_channels[] = { 39 | { 40 | .type = IIO_VOLTAGE, 41 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 42 | } 43 | }; 44 | 45 | static const struct iio_info my_adc_info = { 46 | .read_raw = my_adc_read_raw, 47 | }; 48 | 49 | /* Declate the probe and remove functions */ 50 | static int my_adc_probe(struct i2c_client *client, const struct i2c_device_id *id); 51 | static int my_adc_remove(struct i2c_client *client); 52 | 53 | static struct of_device_id my_driver_ids[] = { 54 | { 55 | .compatible = "brightlight,myadc", 56 | }, { /* sentinel */ } 57 | }; 58 | MODULE_DEVICE_TABLE(of, my_driver_ids); 59 | 60 | static struct i2c_device_id my_adc[] = { 61 | {"my_adc", 0}, 62 | { }, 63 | }; 64 | MODULE_DEVICE_TABLE(i2c, my_adc); 65 | 66 | static struct i2c_driver my_driver = { 67 | .probe = my_adc_probe, 68 | .remove = my_adc_remove, 69 | .id_table = my_adc, 70 | .driver = { 71 | .name = "my_adc", 72 | .of_match_table = my_driver_ids, 73 | }, 74 | }; 75 | 76 | /** 77 | * @brief This function is called on loading the driver 78 | */ 79 | static int my_adc_probe(struct i2c_client *client, const struct i2c_device_id *id) { 80 | struct iio_dev *indio_dev; 81 | struct my_adc *adc; 82 | int ret; 83 | 84 | printk("dt_iio - Now I am in the Probe function!\n"); 85 | 86 | if(client->addr != 0x10) { 87 | printk("dt_iio - Wrong I2C address!\n"); 88 | return -1; 89 | } 90 | 91 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(struct iio_dev)); 92 | if(!indio_dev) { 93 | printk("dt_iio - Error! Out of memory\n"); 94 | return -ENOMEM; 95 | } 96 | 97 | adc = iio_priv(indio_dev); 98 | adc->client = client; 99 | 100 | indio_dev->name = id->name; 101 | indio_dev->info = &my_adc_info; 102 | indio_dev->modes = INDIO_DIRECT_MODE; 103 | indio_dev->channels = my_adc_channels; 104 | indio_dev->num_channels = ARRAY_SIZE(my_adc_channels); 105 | 106 | /* Set the ADC to power up mode */ 107 | ret = i2c_smbus_write_byte_data(adc->client, CMD_SET_STATE, 0x1); 108 | if(ret < 0) { 109 | printk("dt_iio - Error! Could not power ADC up\n"); 110 | return -1; 111 | } 112 | 113 | /* Not shown in video, but useful! */ 114 | /* Save indio_dev as i2c device data */ 115 | i2c_set_clientdata(client, indio_dev); 116 | 117 | return devm_iio_device_register(&client->dev, indio_dev); 118 | } 119 | 120 | /** 121 | * @brief This function is called on unloading the driver 122 | */ 123 | static int my_adc_remove(struct i2c_client *client) { 124 | struct iio_dev * indio_dev; 125 | struct my_adc *adc; 126 | 127 | printk("dt_iio - Now I am in the Remove function!\n"); 128 | /* Not shown in video, but useful! */ 129 | /* Set device to power down mode */ 130 | indio_dev = i2c_get_clientdata(client); 131 | adc = iio_priv(indio_dev); 132 | i2c_smbus_write_byte_data(adc->client, CMD_SET_STATE, 0x0); 133 | 134 | return 0; 135 | } 136 | 137 | /* This will create the init and exit function automatically */ 138 | module_i2c_driver(my_driver); 139 | 140 | -------------------------------------------------------------------------------- /25_dt_iio/testoverlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | / { 4 | compatible = "brcm,bcm2835"; 5 | fragment@0 { 6 | target = <&i2c1>; 7 | __overlay__ { 8 | #address-cells = <1>; 9 | #size-cells = <0>; 10 | 11 | my_adc: my_adc@10 { 12 | compatible = "brightlight,myadc"; 13 | reg = <0x10>; 14 | status = "okay"; 15 | }; 16 | }; 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /26_dt_spi/Atmega_SPI_ADC/Makefile: -------------------------------------------------------------------------------- 1 | MCU = atmega88pa 2 | FREQ = 8000000UL 3 | SOURCES = main.c 4 | TARGET = spi_adc 5 | 6 | all: 7 | avr-gcc -Wall -mmcu=$(MCU) -Os -Iinc/ -DF_CPU=$(FREQ) -o $(TARGET).elf $(SOURCES) 8 | avr-objcopy -O ihex $(TARGET).elf $(TARGET).hex 9 | 10 | flash: 11 | sudo avrdude -c usbasp -p m168p -U flash:w:$(TARGET).hex 12 | 13 | clean: 14 | rm -rf $(TARGET).elf $(TARGET).hex 15 | 16 | -------------------------------------------------------------------------------- /26_dt_spi/Atmega_SPI_ADC/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define SPI_DDR DDRB 6 | #define MISO_GPIO 4 7 | 8 | #define CMD_IDLE 0x00 9 | #define CMD_GET_STATE 0x11 10 | #define CMD_SET_STATE 0x22 11 | #define CMD_GET_CHANNEL 0x33 12 | #define CMD_SET_CHANNEL 0x44 13 | #define CMD_GET_ADC_VAL 0x55 14 | #define CMD_ECHO 0x66 15 | 16 | #define STATE_OFF 0x0 17 | #define STATE_ON 0x1 18 | 19 | 20 | volatile uint8_t adc_value = 0; /* ADC value*/ 21 | volatile uint8_t sel_adc = 0; 22 | volatile uint8_t state = STATE_OFF; 23 | volatile uint8_t cmd = CMD_IDLE; 24 | 25 | int main() { 26 | /* All Pins Input except MISO Pin */ 27 | SPI_DDR |= 1 | (1<>2)); 49 | PORTB |= 0x1; 50 | } 51 | else 52 | PORTB &= 0xfe; 53 | 54 | _delay_ms(50); 55 | } 56 | return 0; 57 | } 58 | 59 | ISR(SPI_STC_vect) { 60 | uint8_t data; 61 | /* Get current received byte */ 62 | data = SPDR; 63 | /* State machine */ 64 | switch(cmd) { 65 | case CMD_IDLE: 66 | switch(data) { 67 | case CMD_GET_STATE: 68 | SPDR = state; 69 | cmd = data; 70 | break; 71 | case CMD_SET_STATE: 72 | cmd = data; 73 | break; 74 | case CMD_GET_CHANNEL: 75 | SPDR = sel_adc; 76 | cmd = data; 77 | break; 78 | case CMD_SET_CHANNEL: 79 | cmd = data; 80 | break; 81 | case CMD_GET_ADC_VAL: 82 | SPDR = adc_value; 83 | cmd = data; 84 | break; 85 | case CMD_ECHO: 86 | SPDR = data; 87 | cmd = data; 88 | break; 89 | default: 90 | break; 91 | } 92 | break; 93 | case CMD_GET_STATE: 94 | cmd = CMD_IDLE; 95 | break; 96 | case CMD_SET_STATE: 97 | state = data; 98 | cmd = CMD_IDLE; 99 | break; 100 | case CMD_GET_CHANNEL: 101 | cmd = CMD_IDLE; 102 | break; 103 | case CMD_SET_CHANNEL: 104 | sel_adc = data; 105 | cmd = CMD_IDLE; 106 | break; 107 | case CMD_GET_ADC_VAL: 108 | cmd = CMD_IDLE; 109 | break; 110 | default: 111 | cmd = CMD_IDLE; 112 | break; 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /26_dt_spi/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += dt_spi.o 2 | 3 | all: module dt 4 | echo Builded Device Tree Overlay and kernel module 5 | 6 | module: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 8 | dt: testoverlay.dts 9 | dtc -@ -I dts -O dtb -o testoverlay.dtbo testoverlay.dts 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | rm -rf testoverlay.dtbo 13 | 14 | -------------------------------------------------------------------------------- /26_dt_spi/README.md: -------------------------------------------------------------------------------- 1 | # Using the Atmega I2C ADC 2 | 3 | I recommend to decrease the I2C frequency so that the I2C bus is not to fast for my slow Atmega I2C ADC (I use an 8MHz oscillator). 4 | 5 | To decrease the frequency, add the following at the end of /boot/config.txt 6 | 7 | ~~~ 8 | dtparam i2c_baudrate=10000 9 | ~~~ 10 | 11 | 12 | And also don't forget to enable I2C over raspi-config! 13 | -------------------------------------------------------------------------------- /26_dt_spi/dt_spi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define CMD_GET_STATE 0x11 8 | #define CMD_SET_STATE 0x22 9 | #define CMD_GET_ADC_VAL 0x55 10 | 11 | /* Meta Information */ 12 | MODULE_LICENSE("GPL"); 13 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 14 | MODULE_DESCRIPTION("A driver for my simple AMTEGA SPI ADC"); 15 | 16 | struct my_adc { 17 | struct spi_device *client; 18 | }; 19 | 20 | static int my_adc_read_raw(struct iio_dev * indio_dev, struct iio_chan_spec const * chan, int *val, int *val2, long mask) { 21 | struct my_adc *adc = iio_priv(indio_dev); 22 | int ret; 23 | 24 | if(mask == IIO_CHAN_INFO_RAW) { 25 | ret = spi_w8r8(adc->client, CMD_GET_ADC_VAL); 26 | if(ret < 0) { 27 | printk("dt_iio - Error reading ADC value!\n"); 28 | return ret; 29 | } 30 | *val = ret; 31 | } 32 | else 33 | return -EINVAL; 34 | return IIO_VAL_INT; 35 | } 36 | 37 | static const struct iio_chan_spec my_adc_channels[] = { 38 | { 39 | .type = IIO_VOLTAGE, 40 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 41 | } 42 | }; 43 | 44 | static const struct iio_info my_adc_info = { 45 | .read_raw = my_adc_read_raw, 46 | }; 47 | 48 | /* Declate the probe and remove functions */ 49 | static int my_adc_probe(struct spi_device *client); 50 | static int my_adc_remove(struct spi_device *client); 51 | 52 | static struct of_device_id my_driver_ids[] = { 53 | { 54 | .compatible = "brightlight,myadc", 55 | }, { /* sentinel */ } 56 | }; 57 | MODULE_DEVICE_TABLE(of, my_driver_ids); 58 | 59 | static struct spi_device_id my_adc[] = { 60 | {"my_adc", 0}, 61 | { }, 62 | }; 63 | MODULE_DEVICE_TABLE(spi, my_adc); 64 | 65 | static struct spi_driver my_driver = { 66 | .probe = my_adc_probe, 67 | .remove = my_adc_remove, 68 | .id_table = my_adc, 69 | .driver = { 70 | .name = "my_adc", 71 | .of_match_table = my_driver_ids, 72 | }, 73 | }; 74 | 75 | /** 76 | * @brief This function is called on loading the driver 77 | */ 78 | static int my_adc_probe(struct spi_device *client) { 79 | struct iio_dev *indio_dev; 80 | struct my_adc *adc; 81 | int ret; 82 | u8 buffer[2]; 83 | 84 | printk("dt_iio - Now I am in the Probe function!\n"); 85 | 86 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(struct iio_dev)); 87 | if(!indio_dev) { 88 | printk("dt_iio - Error! Out of memory\n"); 89 | return -ENOMEM; 90 | } 91 | 92 | adc = iio_priv(indio_dev); 93 | adc->client = client; 94 | 95 | indio_dev->name = "myadc"; 96 | indio_dev->info = &my_adc_info; 97 | indio_dev->modes = INDIO_DIRECT_MODE; 98 | indio_dev->channels = my_adc_channels; 99 | indio_dev->num_channels = ARRAY_SIZE(my_adc_channels); 100 | 101 | ret = spi_setup(client); 102 | if(ret < 0) { 103 | printk("dt_iio - Error! Failed to set up the SPI Bus\n"); 104 | return ret; 105 | } 106 | 107 | /* Set the ADC to power up mode */ 108 | buffer[0] = CMD_SET_STATE; 109 | buffer[1] = 0x1; 110 | 111 | ret = spi_write(adc->client, buffer, 2); 112 | if(ret < 0) { 113 | printk("dt_iio - Error! Could not power ADC up\n"); 114 | return -1; 115 | } 116 | 117 | spi_set_drvdata(client, indio_dev); 118 | 119 | return devm_iio_device_register(&client->dev, indio_dev); 120 | } 121 | 122 | /** 123 | * @brief This function is called on unloading the driver 124 | */ 125 | static int my_adc_remove(struct spi_device *client) { 126 | struct iio_dev * indio_dev; 127 | struct my_adc *adc; 128 | u8 buffer[2]; 129 | 130 | printk("dt_iio - Now I am in the Remove function!\n"); 131 | /* Set device to power down mode */ 132 | buffer[0] = CMD_SET_STATE; 133 | buffer[1] = 0x0; 134 | indio_dev = spi_get_drvdata(client); 135 | adc = iio_priv(indio_dev); 136 | 137 | spi_write(adc->client, buffer, 2); 138 | 139 | return 0; 140 | } 141 | 142 | /* This will create the init and exit function automatically */ 143 | module_spi_driver(my_driver); 144 | 145 | -------------------------------------------------------------------------------- /26_dt_spi/testoverlay.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /plugin/; 3 | / { 4 | compatible = "brcm,bcm2835"; 5 | fragment@0 { 6 | target = <&spidev0>; 7 | __overlay__ { 8 | status = "disabled"; 9 | }; 10 | }; 11 | 12 | fragment@1 { 13 | target = <&spi0>; 14 | __overlay__ { 15 | status = "okay"; 16 | #address-cells = <1>; 17 | #size-cells = <0>; 18 | 19 | my_adc: my_adc@0 { 20 | compatible = "brightlight,myadc"; 21 | reg = <0x0>; 22 | spi-max-frequency = <4000>; 23 | spi-bits-per-word = <8>; 24 | status = "okay"; 25 | }; 26 | }; 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /27_misc_device/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += misc_test.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /27_misc_device/misc_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("A simple demonstration for a misc device"); 10 | 11 | #define MAX_SIZE 256 12 | static char data[MAX_SIZE]; 13 | static size_t data_len; 14 | 15 | static int my_open(struct inode *inode, struct file *file) { 16 | printk("misc_test - Open called\n"); 17 | printk("misc_test - Device Numbers: %d %d\n", imajor(inode), iminor(inode)); 18 | if(file->f_mode & FMODE_READ) 19 | printk("misc_test - Open called with read permissions\n"); 20 | if(file->f_mode & FMODE_WRITE) 21 | printk("misc_test - Open called with write permissions\n"); 22 | return 0; 23 | } 24 | 25 | static int my_close(struct inode *inode, struct file *file) { 26 | printk("misc_test - Close called\n"); 27 | return 0; 28 | } 29 | 30 | static ssize_t my_write(struct file *file, const char __user *user_buffer, size_t user_len, loff_t *ppos) { 31 | int status; 32 | if(user_len > data_len) 33 | data_len = MAX_SIZE; 34 | else 35 | data_len = user_len; 36 | printk("misc_test - Write called\n"); 37 | status = copy_from_user(data, user_buffer, data_len); 38 | if(status) { 39 | printk("misc_test - Error during copy_from_user\n"); 40 | return -status; 41 | } 42 | return data_len; 43 | } 44 | 45 | static ssize_t my_read(struct file *file, char __user *user_buffer, size_t user_len, loff_t *ppos) { 46 | int status; 47 | size_t len; 48 | if(user_len < data_len) 49 | len = user_len; 50 | else 51 | len = data_len; 52 | printk("misc_test - Read called\n"); 53 | status = copy_to_user(user_buffer, data, len); 54 | if(status) { 55 | printk("misc_test - Error during copy_to_user\n"); 56 | return -status; 57 | } 58 | return len; 59 | } 60 | 61 | 62 | static const struct file_operations fops = { 63 | .owner = THIS_MODULE, 64 | .read = my_read, 65 | .write = my_write, 66 | .open = my_open, 67 | .release = my_close, 68 | }; 69 | 70 | static struct miscdevice my_device = { 71 | .name = "testdev", 72 | .minor = MISC_DYNAMIC_MINOR, 73 | .fops = &fops, 74 | }; 75 | 76 | /** 77 | * @brief This function is called, when the module is loaded into the kernel 78 | */ 79 | static int __init my_init(void) { 80 | int status; 81 | printk("misc_test - Register misc device\n"); 82 | status = misc_register(&my_device); 83 | if(status) { 84 | printk("misc_test - Error during Register misc device\n"); 85 | return -status; 86 | } 87 | return 0; 88 | } 89 | 90 | /** 91 | * @brief This function is called, when the module is removed from the kernel 92 | */ 93 | static void __exit my_exit(void) { 94 | printk("misc_test - Deregister misc device\n"); 95 | misc_deregister(&my_device); 96 | } 97 | 98 | module_init(my_init); 99 | module_exit(my_exit); 100 | 101 | 102 | -------------------------------------------------------------------------------- /28_mutex/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += mymutex.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /28_mutex/mymutex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Global variables for the threads */ 9 | static struct task_struct *kthread_1; 10 | static struct task_struct *kthread_2; 11 | static int t1 = 1, t2 = 2; 12 | static struct mutex lock; 13 | 14 | /** 15 | * @brief Function which will be executed by the threads 16 | * 17 | * @param thread_nr Pointer to number of the thread 18 | */ 19 | int thread_function(void * thread_nr) { 20 | int delay[] = { 0, 1000, 500}; 21 | int t_nr = *(int *) thread_nr; 22 | 23 | /* Working loop */ 24 | printk("mymutex - Thread %d is executed!\n", t_nr); 25 | 26 | mutex_lock(&lock); 27 | printk("mymutex - Thread %d is in critical section!\n", t_nr); 28 | msleep(delay[t_nr]); 29 | printk("mymutex - Thread %d is leaving the critical section!\n", t_nr); 30 | mutex_unlock(&lock); 31 | 32 | printk("mymutex - Thread %d finished execution!\n", t_nr); 33 | return 0; 34 | } 35 | 36 | /* Meta Information */ 37 | MODULE_LICENSE("GPL"); 38 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 39 | MODULE_DESCRIPTION("A simple example for threads in a LKM"); 40 | 41 | /** 42 | * @brief This function is called, when the module is loaded into the kernel 43 | */ 44 | static int __init ModuleInit(void) { 45 | printk("mymutex - Init threads\n"); 46 | 47 | mutex_init(&lock); 48 | 49 | kthread_1 = kthread_create(thread_function, &t1, "kthread_1"); 50 | if(kthread_1 != NULL){ 51 | /* Let's start the thread */ 52 | wake_up_process(kthread_1); 53 | printk("mymutex - Thread 1 was created and is running now!\n"); 54 | } 55 | else { 56 | printk("mymutex - Thread 1 could not be created!\n"); 57 | return -1; 58 | } 59 | kthread_2 = kthread_run(thread_function, &t2, "kthread_2"); 60 | if(kthread_2 != NULL) 61 | printk("mymutex - Thread 2 was created and is running now!\n"); 62 | else { 63 | printk("mymutex - Thread 2 could not be created!\n"); 64 | kthread_stop(kthread_1); 65 | return -1; 66 | } 67 | printk("mymutex - Both threads are running now!\n"); 68 | 69 | return 0; 70 | } 71 | 72 | /** 73 | * @brief This function is called, when the module is removed from the kernel 74 | */ 75 | static void __exit ModuleExit(void) { 76 | printk("mymutex - Stop both threads\n"); 77 | } 78 | 79 | module_init(ModuleInit); 80 | module_exit(ModuleExit); 81 | 82 | 83 | -------------------------------------------------------------------------------- /29_completion/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += mycompletion.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /29_completion/mycompletion.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Meta Information */ 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 10 | MODULE_DESCRIPTION("A simple test for completions"); 11 | 12 | /** variable for timer */ 13 | static struct timer_list my_timer; 14 | static struct completion comp; 15 | 16 | void timer_callback(struct timer_list * data) { 17 | printk("mycompletion - timer expired\n"); 18 | complete(&comp); 19 | } 20 | 21 | /** 22 | * @brief This function is called, when the module is loaded into the kernel 23 | */ 24 | static int __init ModuleInit(void) { 25 | int status; 26 | 27 | /* Initialize timer */ 28 | timer_setup(&my_timer, timer_callback, 0); 29 | /* Initialize completion */ 30 | init_completion(&comp); 31 | 32 | printk("mycompletion - Start the timer the first time. Time: 40ms\n"); 33 | mod_timer(&my_timer, jiffies + msecs_to_jiffies(40)); 34 | status = wait_for_completion_timeout(&comp, msecs_to_jiffies(100)); 35 | if(!status) 36 | printk("mycompletion - Completion timed out!\n"); 37 | 38 | reinit_completion(&comp); 39 | printk("mycompletion - Start the timer the second time. Time: 400ms\n"); 40 | mod_timer(&my_timer, jiffies + msecs_to_jiffies(400)); 41 | status = wait_for_completion_timeout(&comp, msecs_to_jiffies(100)); 42 | if(!status) 43 | printk("mycompletion - Completion timed out!\n"); 44 | 45 | return 0; 46 | } 47 | 48 | /** 49 | * @brief This function is called, when the module is removed from the kernel 50 | */ 51 | static void __exit ModuleExit(void) { 52 | del_timer(&my_timer); 53 | } 54 | 55 | module_init(ModuleInit); 56 | module_exit(ModuleExit); 57 | 58 | 59 | -------------------------------------------------------------------------------- /30_dma_memcpy/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += my_dma_memcpy.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /30_dma_memcpy/my_dma_memcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Meta Information */ 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 11 | MODULE_DESCRIPTION("A simple DMA example for copying data from RAM to RAM"); 12 | 13 | void my_dma_transfer_completed(void *param) 14 | { 15 | struct completion *cmp = (struct completion *) param; 16 | complete(cmp); 17 | } 18 | 19 | /** 20 | * @brief This function is called, when the module is loaded into the kernel 21 | */ 22 | static int __init my_init(void) { 23 | dma_cap_mask_t mask; 24 | struct dma_chan *chan; 25 | struct dma_async_tx_descriptor *chan_desc; 26 | dma_cookie_t cookie; 27 | dma_addr_t src_addr, dst_addr; 28 | u8 *src_buf, *dst_buf; 29 | struct completion cmp; 30 | int status; 31 | 32 | printk("my_dma_memcpy - Init\n"); 33 | 34 | dma_cap_zero(mask); 35 | dma_cap_set(DMA_SLAVE | DMA_PRIVATE, mask); 36 | chan = dma_request_channel(mask, NULL, NULL); 37 | if(!chan) { 38 | printk("my_dma_memcpy - Error requesting dma channel\n"); 39 | return -ENODEV; 40 | } 41 | 42 | src_buf = dma_alloc_coherent(chan->device->dev, 1024, &src_addr, GFP_KERNEL); 43 | dst_buf = dma_alloc_coherent(chan->device->dev, 1024, &dst_addr, GFP_KERNEL); 44 | 45 | memset(src_buf, 0x12, 1024); 46 | memset(dst_buf, 0x0, 1024); 47 | 48 | printk("my_dma_memcpy - Before DMA Transfer: src_buf[0] = %x\n", src_buf[0]); 49 | printk("my_dma_memcpy - Before DMA Transfer: dst_buf[0] = %x\n", dst_buf[0]); 50 | 51 | chan_desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, 1024, DMA_MEM_TO_MEM); 52 | if(!chan_desc) { 53 | printk("my_dma_memcpy - Error requesting dma channel\n"); 54 | status = -1; 55 | goto free; 56 | } 57 | 58 | init_completion(&cmp); 59 | 60 | chan_desc->callback = my_dma_transfer_completed; 61 | chan_desc->callback_param = &cmp; 62 | 63 | cookie = dmaengine_submit(chan_desc); 64 | 65 | /* Fire the DMA transfer */ 66 | dma_async_issue_pending(chan); 67 | 68 | if(wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) <= 0) { 69 | printk("my_dma_memcpy - Timeout!\n"); 70 | status = -1; 71 | } 72 | 73 | status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); 74 | if(status == DMA_COMPLETE) { 75 | printk("my_dma_memcpy - DMA transfer has completed!\n"); 76 | status = 0; 77 | printk("my_dma_memcpy - After DMA Transfer: src_buf[0] = %x\n", src_buf[0]); 78 | printk("my_dma_memcpy - After DMA Transfer: dst_buf[0] = %x\n", dst_buf[0]); 79 | } else { 80 | printk("my_dma_memcpy - Error on DMA transfer\n"); 81 | } 82 | 83 | dmaengine_terminate_all(chan); 84 | free: 85 | dma_free_coherent(chan->device->dev, 1024, src_buf, src_addr); 86 | dma_free_coherent(chan->device->dev, 1024, dst_buf, dst_addr); 87 | 88 | dma_release_channel(chan); 89 | return 0; 90 | } 91 | 92 | /** 93 | * @brief This function is called, when the module is removed from the kernel 94 | */ 95 | static void __exit my_exit(void) { 96 | printk("Goodbye, Kernel\n"); 97 | } 98 | 99 | module_init(my_init); 100 | module_exit(my_exit); 101 | 102 | 103 | -------------------------------------------------------------------------------- /31_file_access/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += file_access.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /31_file_access/file_access.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Meta Information */ 6 | MODULE_LICENSE("GPL"); 7 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 8 | MODULE_DESCRIPTION("An example for reading and writing to a file in your filesystem from a driver"); 9 | 10 | /** 11 | * @brief This function is called, when the module is loaded into the kernel 12 | */ 13 | static int __init my_init(void) { 14 | struct file *file; 15 | char data[128] = "\"Hey, careful, man, there's beverage here!\" - Dude\n"; 16 | ssize_t len; 17 | 18 | /* Open the file */ 19 | file = filp_open("/tmp/dude", O_RDWR | O_CREAT, 0644); 20 | if(!file) { 21 | printk("file_access - Error opening file\n"); 22 | return -1; 23 | } 24 | 25 | /* Write to the file */ 26 | len = kernel_write(file, data, sizeof(data), &file->f_pos); 27 | if(len < 0) { 28 | printk("file_access - Error writing to file: %d\n", len); 29 | filp_close(file, NULL); 30 | } 31 | printk("file_access - Wrote %d bytes to file\n", len); 32 | 33 | /* Read it back */ 34 | memset(data, 0, sizeof(data)); 35 | file->f_pos = 0; 36 | 37 | len = kernel_read(file, data, sizeof(data), &file->f_pos); 38 | if(len < 0) { 39 | printk("file_access - Error reading the file: %d\n", len); 40 | filp_close(file, NULL); 41 | } 42 | 43 | printk("file_access - Read %d bytes: '%s'\n", len, data); 44 | filp_close(file, NULL); 45 | 46 | return 0; 47 | } 48 | 49 | /** 50 | * @brief This function is called, when the module is removed from the kernel 51 | */ 52 | static void __exit my_exit(void) { 53 | printk("file_access - Unloading driver\n"); 54 | } 55 | 56 | module_init(my_init); 57 | module_exit(my_exit); 58 | 59 | 60 | -------------------------------------------------------------------------------- /32_mmap/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += my_mmap.o 2 | 3 | all: module testapp 4 | 5 | module: 6 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 7 | 8 | clean: 9 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 10 | rm a.out 11 | 12 | testapp: test.c 13 | gcc test.c 14 | 15 | 16 | -------------------------------------------------------------------------------- /32_mmap/my_mmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("Johannes 4GNU_Linux"); 10 | MODULE_DESCRIPTION("A simple implementation for the mmap syscall"); 11 | 12 | #define MYMAJOR 64 13 | #define DEVNAME "mydev" 14 | 15 | static void *my_data; 16 | 17 | static ssize_t my_read(struct file *file, char __user *user_buffer, size_t len, loff_t *offs) 18 | { 19 | int not_copied, to_copy = (len > PAGE_SIZE) ? PAGE_SIZE : len; 20 | not_copied = copy_to_user(user_buffer, my_data, to_copy); 21 | return to_copy - not_copied; 22 | } 23 | 24 | static ssize_t my_write(struct file *file, const char __user *user_buffer, size_t len, loff_t *offs) 25 | { 26 | int not_copied, to_copy = (len > PAGE_SIZE) ? PAGE_SIZE : len; 27 | not_copied = copy_from_user(my_data, user_buffer, to_copy); 28 | return to_copy - not_copied; 29 | } 30 | 31 | static int my_mmap(struct file *file, struct vm_area_struct *vma) 32 | { 33 | int status; 34 | 35 | vma->vm_pgoff = virt_to_phys(my_data) >> PAGE_SHIFT; 36 | 37 | status = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 38 | vma->vm_end - vma->vm_start, vma->vm_page_prot); 39 | if(status) { 40 | printk("my_mmap - Error remap_pfn_range: %d\n", status); 41 | return -EAGAIN; 42 | } 43 | return 0; 44 | } 45 | 46 | static struct file_operations fops = { 47 | .owner = THIS_MODULE, 48 | .read = my_read, 49 | .write = my_write, 50 | .mmap = my_mmap, 51 | }; 52 | 53 | int __init my_init(void) 54 | { 55 | int status; 56 | 57 | printk("my_mmap - Hello!\n"); 58 | my_data = kzalloc(PAGE_SIZE, GFP_DMA); 59 | if(!my_data) 60 | return -ENOMEM; 61 | 62 | printk("my_mmap - I have allocated a page (%ld Bytes)\n", PAGE_SIZE); 63 | printk("my_mmap - PAGESHIFT: %d\n", PAGE_SHIFT); 64 | 65 | status = register_chrdev(MYMAJOR, DEVNAME, &fops); 66 | if(status < 0) { 67 | printk("my_mmap - Error registering device number!\n"); 68 | kfree(my_data); 69 | return status; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | void __exit my_exit(void) 76 | { 77 | if(my_data) 78 | kfree(my_data); 79 | unregister_chrdev(MYMAJOR, DEVNAME); 80 | printk("my_mmap - Goodbye\n"); 81 | } 82 | 83 | module_init(my_init); 84 | module_exit(my_exit); 85 | 86 | -------------------------------------------------------------------------------- /32_mmap/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define DEVNAME "/dev/mydev" 9 | 10 | int main(int argc, char **argv) 11 | { 12 | int fd, status, offset; 13 | char text[4096]; 14 | void *ptr; 15 | 16 | if(argc < 2) { 17 | printf("Usage: %s [m,r,w,p] {data}\n", argv[0]); 18 | return 0; 19 | } 20 | 21 | fd = open(DEVNAME, O_RDWR); 22 | if(fd < 0) { 23 | perror("open"); 24 | return 1; 25 | } 26 | 27 | switch(argv[1][0]) { 28 | case 'r': 29 | status = read(fd, text, 4096); 30 | printf("READ: I got %d bytes with '%s'\n", status, text); 31 | break; 32 | case 'w': 33 | if(argc < 3) { 34 | printf("Usage: %s w [data]\n", argv[0]); 35 | break; 36 | } 37 | 38 | memset(text, 0, 4096); 39 | strcpy(text, argv[2]); 40 | 41 | status = write(fd, text, 4096); 42 | printf("Wrote %d bytes\n", status); 43 | break; 44 | case 'm': 45 | ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 46 | if(ptr == MAP_FAILED) { 47 | perror("mmap"); 48 | break; 49 | } 50 | 51 | if(argc > 2) { 52 | memset(text, 0, 4096); 53 | strcpy(text, argv[2]); 54 | memcpy(ptr, text, 4096); 55 | } 56 | 57 | memset(text, 0, 4096); 58 | memcpy(text, ptr, 4096); 59 | printf("MMAP: I got '%s'\n", text); 60 | munmap(ptr, 4096); 61 | break; 62 | case 'p': 63 | if(argc < 3) { 64 | printf("Usage: %s p [offset]\n", argv[0]); 65 | break; 66 | } 67 | offset = atoi(argv[2]); 68 | 69 | ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 70 | if(ptr == MAP_FAILED) { 71 | perror("mmap"); 72 | break; 73 | } 74 | 75 | printf("MMAP: Byte on Offset %d: *(ptr + %d) = '%c'\n", offset, offset, 76 | *((char *) (ptr) + offset)); 77 | 78 | munmap(ptr, 4096); 79 | break; 80 | default: 81 | printf("'%c' is invalid.\n", argv[1][0]); 82 | break; 83 | } 84 | 85 | close(fd); 86 | } 87 | -------------------------------------------------------------------------------- /33_list/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += mylist.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /33_list/mylist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* Meta Information */ 7 | MODULE_LICENSE("GPL"); 8 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 9 | MODULE_DESCRIPTION("Demonstration for lists in the kernel"); 10 | 11 | struct my_data { 12 | struct list_head list; 13 | char text[64]; 14 | }; 15 | 16 | LIST_HEAD(my_list); 17 | 18 | /** 19 | * @brief This function is called, when the module is loaded into the kernel 20 | */ 21 | static int __init my_init(void) { 22 | struct my_data *tmp, *next; 23 | struct list_head *ptr; 24 | 25 | printk("mylist - Hello, Kernel!\n"); 26 | 27 | /* Let's create a list with three elements */ 28 | tmp = kmalloc(sizeof(struct my_data), GFP_KERNEL); 29 | strcpy(tmp->text, "Hello World"); 30 | list_add_tail(&tmp->list, &my_list); 31 | 32 | tmp = kmalloc(sizeof(struct my_data), GFP_KERNEL); 33 | strcpy(tmp->text, "Second Element"); 34 | list_add_tail(&tmp->list, &my_list); 35 | 36 | tmp = kmalloc(sizeof(struct my_data), GFP_KERNEL); 37 | strcpy(tmp->text, "and the last element"); 38 | list_add_tail(&tmp->list, &my_list); 39 | 40 | list_for_each_prev(ptr, &my_list) { 41 | tmp = list_entry(ptr, struct my_data, list); 42 | printk("mylist - Element text: %s\n", tmp->text); 43 | } 44 | 45 | /* Free memory */ 46 | list_for_each_entry_safe(tmp, next, &my_list, list) { 47 | list_del(&tmp->list); 48 | kfree(tmp); 49 | } 50 | printk("mylist - Freeing memory done!\n"); 51 | 52 | return 0; 53 | } 54 | 55 | /** 56 | * @brief This function is called, when the module is removed from the kernel 57 | */ 58 | static void __exit my_exit(void) { 59 | printk("mylist - Goodbye, Kernel\n"); 60 | } 61 | 62 | module_init(my_init); 63 | module_exit(my_exit); 64 | 65 | 66 | -------------------------------------------------------------------------------- /34_my_cdev/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += my_cdev.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /34_my_cdev/my_cdev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* Meta Information */ 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 10 | MODULE_DESCRIPTION("An example for registering device numbers for character devices"); 11 | 12 | #define DEVNR 64 13 | #define DEVNRNAME "my_cdev" 14 | 15 | static struct cdev my_cdev; 16 | static char buffer[256]; 17 | 18 | static ssize_t my_read(struct file *file, char __user *user_buffer, size_t len, loff_t *off) 19 | { 20 | int not_copied, to_copy = (len < 256) ? len : 256; 21 | 22 | printk("my_cdev - my_Read called, *off: %lld\n", *off); 23 | 24 | if(*off >= to_copy) 25 | return 0; 26 | 27 | not_copied = copy_to_user(user_buffer, buffer, to_copy); 28 | 29 | *off += to_copy - not_copied; 30 | 31 | return to_copy - not_copied; 32 | } 33 | 34 | static ssize_t my_write(struct file *file, const char __user *user_buffer, size_t len, loff_t *off) 35 | { 36 | int not_copied, to_copy = (len < 256) ? len : 256; 37 | 38 | printk("my_cdev - my_write called\n"); 39 | 40 | 41 | not_copied = copy_from_user(buffer, user_buffer, to_copy); 42 | 43 | 44 | return to_copy - not_copied; 45 | } 46 | 47 | struct file_operations fops = { 48 | .read = my_read, 49 | .write = my_write, 50 | }; 51 | 52 | /** 53 | * @brief This function is called, when the module is loaded into the kernel 54 | */ 55 | static int __init my_init(void) { 56 | int status; 57 | dev_t devnr = MKDEV(DEVNR, 0); 58 | 59 | status = register_chrdev_region(devnr, 1, DEVNRNAME); 60 | if(status < 0) { 61 | printk("my_cdev - Error regiserting device number!\n"); 62 | return status; 63 | } 64 | 65 | cdev_init(&my_cdev, &fops); 66 | my_cdev.owner = THIS_MODULE; 67 | 68 | status = cdev_add(&my_cdev, devnr, 1); 69 | if(status < 0) { 70 | printk("my_cdev - Error adding cdev\n"); 71 | unregister_chrdev_region(devnr, 1); 72 | return status; 73 | } 74 | printk("my_cdev - Registered device number 64 0, created cdev\n"); 75 | 76 | return 0; 77 | } 78 | 79 | /** 80 | * @brief This function is called, when the module is removed from the kernel 81 | */ 82 | static void __exit my_exit(void) { 83 | dev_t devnr = MKDEV(DEVNR, 0); 84 | unregister_chrdev_region(devnr, 1); 85 | cdev_del(&my_cdev); 86 | printk("my_cdev - Registered device number 64 0, created cdev\n"); 87 | } 88 | 89 | module_init(my_init); 90 | module_exit(my_exit); 91 | 92 | 93 | -------------------------------------------------------------------------------- /35_priv_data/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += priv_data.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /35_priv_data/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Johannes4Linux/Linux_Driver_Tutorial_legacy/8eab78e38580bbb63fe2466ee755709e5a813b16/35_priv_data/a.out -------------------------------------------------------------------------------- /35_priv_data/priv_data.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* Meta Information */ 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 11 | MODULE_DESCRIPTION("An example for using private data for file descriptors"); 12 | 13 | #define DEVNR 64 14 | #define DEVNRNAME "my_cdev" 15 | 16 | static struct cdev my_cdev; 17 | 18 | static int my_open(struct inode *inode, struct file *file) 19 | { 20 | char *ptr; 21 | ptr = kzalloc(256, GFP_KERNEL); 22 | if(!ptr) 23 | return -ENOMEM; 24 | printk("priv_data - Allocated 256 bytes of memory\n"); 25 | file->private_data = ptr; 26 | return 0; 27 | } 28 | 29 | static ssize_t my_read(struct file *file, char __user *user_buffer, size_t len, loff_t *off) 30 | { 31 | char *buffer; 32 | int not_copied, to_copy = (len < 256) ? len : 256; 33 | 34 | buffer = (char *) file->private_data; 35 | if(!buffer) 36 | return -1; 37 | 38 | printk("priv_data - my_Read called, *off: %lld\n", *off); 39 | 40 | if(*off >= to_copy) 41 | return 0; 42 | 43 | not_copied = copy_to_user(user_buffer, buffer, to_copy); 44 | 45 | *off += to_copy - not_copied; 46 | 47 | return to_copy - not_copied; 48 | } 49 | 50 | static ssize_t my_write(struct file *file, const char __user *user_buffer, size_t len, loff_t *off) 51 | { 52 | char *buffer; 53 | int not_copied, to_copy = (len < 256) ? len : 256; 54 | 55 | buffer = (char *) file->private_data; 56 | if(!buffer) 57 | return -1; 58 | 59 | printk("priv_data - my_write called\n"); 60 | 61 | 62 | not_copied = copy_from_user(buffer, user_buffer, to_copy); 63 | 64 | 65 | return to_copy - not_copied; 66 | } 67 | 68 | static int my_close(struct inode *inode, struct file *file) 69 | { 70 | char *ptr; 71 | ptr = (char *) file->private_data; 72 | if(ptr) 73 | kfree(ptr); 74 | printk("priv_data - freed 256 bytes\n"); 75 | return 0; 76 | } 77 | 78 | struct file_operations fops = { 79 | .open = my_open, 80 | .release = my_close, 81 | .read = my_read, 82 | .write = my_write, 83 | }; 84 | 85 | /** 86 | * @brief This function is called, when the module is loaded into the kernel 87 | */ 88 | static int __init my_init(void) { 89 | int status; 90 | dev_t devnr = MKDEV(DEVNR, 0); 91 | 92 | status = register_chrdev_region(devnr, 1, DEVNRNAME); 93 | if(status < 0) { 94 | printk("priv_data - Error regiserting device number!\n"); 95 | return status; 96 | } 97 | 98 | cdev_init(&my_cdev, &fops); 99 | my_cdev.owner = THIS_MODULE; 100 | 101 | status = cdev_add(&my_cdev, devnr, 1); 102 | if(status < 0) { 103 | printk("priv_data - Error adding cdev\n"); 104 | unregister_chrdev_region(devnr, 1); 105 | return status; 106 | } 107 | printk("priv_data - Registered device number 64 0, created cdev\n"); 108 | 109 | return 0; 110 | } 111 | 112 | /** 113 | * @brief This function is called, when the module is removed from the kernel 114 | */ 115 | static void __exit my_exit(void) { 116 | dev_t devnr = MKDEV(DEVNR, 0); 117 | unregister_chrdev_region(devnr, 1); 118 | cdev_del(&my_cdev); 119 | printk("priv_data - Registered device number 64 0, created cdev\n"); 120 | } 121 | 122 | module_init(my_init); 123 | module_exit(my_exit); 124 | 125 | 126 | -------------------------------------------------------------------------------- /35_priv_data/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char **argv) 8 | { 9 | int fd, len; 10 | char buffer[64]; 11 | 12 | if(argc < 2) { 13 | printf("Usage: %s \n", argv[0]); 14 | return 0; 15 | } 16 | 17 | fd = open("/dev/priv_data", O_RDWR); 18 | if(fd < 0) { 19 | perror("open"); 20 | return fd; 21 | } 22 | 23 | write(fd, argv[1], strlen(argv[1])); 24 | 25 | printf("Press enter to continue..."); 26 | getchar(); 27 | 28 | len = read(fd, buffer, 64); 29 | printf("I got %d bytes %s\n", len, buffer); 30 | 31 | close(fd); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /36_i2c_driver/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += my_i2c_driver.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /36_i2c_driver/my_i2c_driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL"); 6 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 7 | MODULE_DESCRIPTION("A simple I2C Device driver with driver_data"); 8 | 9 | struct my_data { 10 | char name[32]; 11 | int i; 12 | }; 13 | 14 | static struct my_data a = { 15 | "Device A", 16 | 42, 17 | }; 18 | 19 | static struct my_data b = { 20 | "Device B", 21 | 123, 22 | }; 23 | 24 | static struct i2c_device_id my_ids[] = { 25 | {"a-dev", (long unsigned int) &a}, 26 | {"b-dev", (long unsigned int) &b}, 27 | {}, 28 | }; 29 | MODULE_DEVICE_TABLE(i2c, my_ids); 30 | 31 | static int my_probe(struct i2c_client *client, const struct i2c_device_id *id) 32 | { 33 | struct my_data *data = (struct my_data*) id->driver_data; 34 | printk("my_i2c_driver - %s data->i=%d\n", data->name, data->i); 35 | printk("my_i2c_driver - ID: 0x%x\n", i2c_smbus_read_byte_data(client, 0xd0)); 36 | return 0; 37 | } 38 | 39 | static void my_remove(struct i2c_client *client) 40 | { 41 | printk("my_i2c_driver - Removing device\n"); 42 | } 43 | 44 | static struct i2c_driver my_driver= { 45 | .probe = my_probe, 46 | .remove = my_remove, 47 | .id_table = my_ids, 48 | .driver = { 49 | .name = "my-i2c-driver", 50 | } 51 | }; 52 | 53 | module_i2c_driver(my_driver); 54 | -------------------------------------------------------------------------------- /38_log_levels/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += log_levels.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | -------------------------------------------------------------------------------- /38_log_levels/log_levels.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* Meta Information */ 5 | MODULE_LICENSE("GPL"); 6 | MODULE_AUTHOR("Johannes 4 GNU/Linux"); 7 | MODULE_DESCRIPTION("A hello world LKM"); 8 | 9 | /** 10 | * @brief This function is called, when the module is loaded into the kernel 11 | */ 12 | static int __init my_init(void) { 13 | printk(KERN_DEBUG "This is a debug msg\n"); 14 | printk(KERN_INFO "Info: %d\n", 42); 15 | printk(KERN_ERR "ERROR!!!\n"); 16 | printk(KERN_ALERT "ALERT!!!\n"); 17 | return 0; 18 | } 19 | 20 | /** 21 | * @brief This function is called, when the module is removed from the kernel 22 | */ 23 | static void __exit my_exit(void) { 24 | pr_info("Another info\n"); 25 | pr_crit("Critical info: %d\n", 123); 26 | pr_err("Second Error\n"); 27 | } 28 | 29 | module_init(my_init); 30 | module_exit(my_exit); 31 | 32 | 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux Driver Tutorial 2 | 3 | Here you can find examples for simple Linux Kernel Modules and Linux Drivers. 4 | 5 | ## Preparation 6 | 7 | I used a Raspberry Pi 3 to develop and test my modules and drivers. To compile them, you need to install the Kernel headers on your Pi. On Raspbian you can do this with the following command: 8 | 9 | ```bash 10 | sudo apt update 11 | sudo apt install raspberrypi-kernel-headers 12 | ``` 13 | 14 | Raspberry Pi OS is only installs the latest kernel headers. So, make sure, you are running the latest kernel. You can do this by running: 15 | 16 | ```bash 17 | sudo apt upgrade 18 | ``` 19 | 20 | You also need the build utils (make, gcc, ...) but they come preinstalled on Raspbian. 21 | 22 | ## Content 23 | 24 | In this repo you can find examples for: 25 | 1. Simple Kernel Module 26 | 2. Device Numbers and Device Files 27 | 3. Create device file in driver and callbacks 28 | 4. GPIO Driver 29 | 5. Text LCD Driver 30 | 6. PWM Module 31 | 7. Temperature Sensor (I2C) 32 | 8. Timer in Linux Kernel Modules 33 | 9. High Resolution Timer in Linux Kernel Modules 34 | 10. Accessing SPI with a Linux Kernel Module (BMP280 sensor again) 35 | 11. Using a GPIO Interrupt in a Linux Kernel Module 36 | 12. Using Parameters in a Linux Kernel Module 37 | 13. IOCTL in a Linux Kernel Module 38 | 14. Threads in a Linux Kernel Module 39 | 15. Sending a signal from a Linux Kernel Module to an userspace application 40 | 16. The poll callback 41 | 17. Waitqueues in a Linux Kernel Module 42 | 18. Create procfs entries from a Linux Kernel Module 43 | 19. Create sysfs entries from a Linux Kernel Module 44 | 20. Parse the device tree from a Linux Kernel Module to get the deivce properties of a specific device 45 | 21. Device Tree GPIO Driver 46 | 22. Device Tree Driver for I2C Device 47 | 23. Dynamical memory management in a Linux Kernel module 48 | 24. Serial (UART) Driver 49 | 25. Industrial IO compatible driver for an ATMEGA I2C ADC 50 | 26. Device Tree SPI Driver (IIO compatible driver for Atmega SPI ADC) 51 | 27. Misc Device 52 | 28. Mutex for exclusive access to shared resource 53 | 29. Completions for synchronisation 54 | 30. Direct Memory Access (DMA) memcopy example 55 | 31. Accessing files form a Linux Driver 56 | 32. The mmap callback 57 | 33. Linked Lists 58 | 34. Registering device numbers, read and write callback in character devices Take 2 59 | 35. Private Data in struct file 60 | 36. I2C Device Driver without Device Tree 61 | 37. Sysfs Class 62 | 38. Kernel Log levels 63 | 64 | 65 | ## More Information 66 | 67 | For more information about my Linux Driver examples check out my [videos and my playlist](https://www.youtube.com/watch?v=x1Y203vH-Dc&list=PLCGpd0Do5-I3b5TtyqeF1UdyD4C-S-dMa) 68 | 69 | ## Support me 70 | 71 | If you want to support me, you can buy me a coffee [buymeacoffee.com/johannes4linux](https://www.buymeacoffee.com/johannes4linux). 72 | --------------------------------------------------------------------------------