├── Chapter02 ├── Makefile ├── README.md ├── helloworld-params.c └── helloworld.c ├── Chapter03 ├── Makefile ├── cmwq.c ├── dedicated-workqueue.c ├── hr-timer.c ├── shared-workqueue.c ├── std-timer.c ├── tasklet.c ├── user-invoke.c └── waitqueue.c ├── Chapter04 ├── Makefile ├── README.md └── dummy-char.c ├── Chapter07 ├── Makefile ├── README.md ├── platform-dummy-char.c └── platform-dummy-ins.c ├── Chapter08 ├── Makefile └── ee24lc512.c ├── Chapter09 ├── Makefile ├── eeprom_93xx46.c └── eeprom_93xx46.h ├── Chapter10 ├── Makefile ├── kmalloc.c ├── vma_list.c └── vmalloc.c ├── Chapter11 ├── Makefile ├── README.md └── dma-single-buffer.c ├── Chapter14 ├── sysfs-kobj-and-group │ ├── Makefile │ └── sysfs-group.c └── sysfs-poll │ ├── Makefile │ ├── README.md │ ├── sysfs-poll-user.c │ ├── sysfs-poll.c │ └── sysfs-select-user.c ├── Chapter15 ├── Makefile ├── README.md ├── iio-dummy-random.c └── iio-ins.c ├── Chapter16 ├── Makefile ├── README.md ├── fake-gpio-chip.c ├── fake-gpio-ins.c ├── gpio │ ├── Makefile │ ├── gpio-descriptor-module.c │ ├── gpio-legacy-dt-module.c │ └── gpio-legacy-module.c └── mcp23016.c ├── Chapter17 ├── Makefile ├── README.md ├── input-button.c ├── input-polled-button.c └── polled-ins.c ├── LICENSE └── README.md /Chapter02/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := helloworld-params.o helloworld.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter02/README.md: -------------------------------------------------------------------------------- 1 | # Helloworld drivers 2 | 3 | After running `make` command, there will be two modules: 4 | 5 | * helloworld.ko 6 | * helloworld-params.ko 7 | 8 | The fist module is basic helloworld driver, the second one is the same, but 9 | accepts some parameters, and print these in the kernel debug messages. 10 | After loading the first module, two entries will be added in kernel debug message: 11 | 12 | ```bash 13 | # insmod ./helloworld.ko 14 | #dmesg 15 | [...] 16 | [38535.487568] Hello world! 17 | [38542.391099] End of the world 18 | ``` 19 | 20 | For the second module, one loads it with: 21 | 22 | ```bash 23 | # insmod ./helloworld-params.ko 24 | ``` 25 | 26 | If no parameters are provided, the defaults will be used: 27 | 28 | ```bash 29 | $ dmesg 30 | [...] 31 | [37858.595126] Hello world with parameters! 32 | [37858.595129] The *mystr* parameter: hello 33 | [37858.595130] The *myint* parameter: 1 34 | [37858.595131] The *myarr* parameter: 0, 1, 2 35 | [37887.232643] End of the world 36 | ``` 37 | 38 | When parameters are provided, the will be printed 39 | 40 | 41 | ```bash 42 | # insmod ./helloworld-params.ko mystr="packtpub" myint=255 myarr=23,4,7 43 | # dmesg 44 | [...] 45 | [37892.417968] Hello world with parameters! 46 | [37892.417970] The *mystr* parameter: packtpub 47 | [37892.417971] The *myint* parameter: 255 48 | [37892.417972] The *myarr* parameter: 23, 4, 7 49 | [37895.222808] End of the world 50 | 51 | ``` 52 | -------------------------------------------------------------------------------- /Chapter02/helloworld-params.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static char *mystr = "hello"; 7 | static int myint = 1; 8 | static int myarr[3] = {0, 1, 2}; 9 | 10 | 11 | module_param(myint, int, S_IRUGO); 12 | module_param(mystr, charp, S_IRUGO); 13 | module_param_array(myarr, int,NULL, S_IWUSR|S_IRUSR); 14 | 15 | MODULE_PARM_DESC(myint,"this is my int variable"); 16 | MODULE_PARM_DESC(mystr,"this is my char pointer variable"); 17 | MODULE_PARM_DESC(myarr,"this is my array of int"); 18 | MODULE_INFO(my_field_name, "What eeasy value"); 19 | 20 | static int __init hellowolrd_init(void) { 21 | pr_info("Hello world with parameters!\n"); 22 | pr_info("The *mystr* parameter: %s\n", mystr); 23 | pr_info("The *myint* parameter: %d\n", myint); 24 | pr_info("The *myarr* parameter: %d, %d, %d\n", myarr[0], myarr[1], myarr[2]); 25 | return 0; 26 | } 27 | 28 | static void __exit hellowolrd_exit(void) { 29 | pr_info("End of the world\n"); 30 | } 31 | 32 | module_init(hellowolrd_init); 33 | module_exit(hellowolrd_exit); 34 | MODULE_AUTHOR("John Madieu "); 35 | MODULE_LICENSE("GPL"); 36 | -------------------------------------------------------------------------------- /Chapter02/helloworld.c: -------------------------------------------------------------------------------- 1 | /* you can prefix the module output messages by uncommenting the following line */ 2 | //#define pr_fmt(fmt) "PACKT: " fmt 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static int __init hellowolrd_init(void) { 9 | pr_info("Hello world!\n"); 10 | return 0; 11 | } 12 | 13 | static void __exit hellowolrd_exit(void) { 14 | pr_info("End of the world\n"); 15 | } 16 | 17 | module_init(hellowolrd_init); 18 | module_exit(hellowolrd_exit); 19 | MODULE_AUTHOR("John Madieu "); 20 | MODULE_LICENSE("GPL"); 21 | -------------------------------------------------------------------------------- /Chapter03/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := cmwq.o \ 2 | tasklet.o \ 3 | hr-timer.o \ 4 | std-timer.o \ 5 | waitqueue.o \ 6 | shared-workqueue.o \ 7 | dedicated-workqueue.o \ 8 | user-invoke.o 9 | 10 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 11 | 12 | all default: modules 13 | install: modules_install 14 | 15 | modules modules_install help clean: 16 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 17 | -------------------------------------------------------------------------------- /Chapter03/cmwq.c: -------------------------------------------------------------------------------- 1 | #define pr_fmt(fmt) "PACKT-03-cmwq: " fmt 2 | 3 | #include 4 | #include 5 | #include /* for work queue */ 6 | #include /* for kmalloc() */ 7 | 8 | static struct workqueue_struct *wq; 9 | 10 | struct work_data { 11 | struct work_struct my_work; 12 | int the_data; 13 | }; 14 | 15 | static void work_handler(struct work_struct *work) 16 | { 17 | struct work_data * my_data = container_of(work, struct work_data, my_work); 18 | pr_info("CMWQ module handler: %s, data is %d\n", 19 | __func__, my_data->the_data); 20 | kfree(my_data); 21 | } 22 | 23 | static int __init my_init(void) 24 | { 25 | struct work_data * my_data; 26 | 27 | pr_info("CMWQ module init: %s %d\n", __func__, __LINE__); 28 | wq = alloc_workqueue("cmwp-example", 29 | /* we want it to be unbound and high priority */ 30 | WQ_UNBOUND | WQ_HIGHPRI, 31 | 0); 32 | my_data = kmalloc(sizeof(struct work_data), GFP_KERNEL); 33 | 34 | my_data->the_data = 34; 35 | 36 | INIT_WORK(&my_data->my_work, work_handler); 37 | queue_work(wq, &my_data->my_work); 38 | 39 | return 0; 40 | } 41 | 42 | static void __exit my_exit(void) 43 | { 44 | flush_workqueue(wq); 45 | destroy_workqueue(wq); 46 | pr_info("Work queue module exit: %s %d\n", __func__, __LINE__); 47 | } 48 | 49 | module_init(my_init); 50 | module_exit(my_exit); 51 | MODULE_LICENSE("GPL"); 52 | MODULE_AUTHOR("John Madieu "); 53 | MODULE_DESCRIPTION("Dedicated workqueue example"); 54 | -------------------------------------------------------------------------------- /Chapter03/dedicated-workqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include /* for work queue */ 4 | #include /* for kmalloc() */ 5 | 6 | static struct workqueue_struct *wq; 7 | 8 | struct work_data { 9 | struct work_struct my_work; 10 | int the_data; 11 | }; 12 | 13 | static void work_handler(struct work_struct *work) 14 | { 15 | struct work_data * my_data = container_of(work, struct work_data, my_work); 16 | pr_info("Work queue module handler: %s, data is %d\n", 17 | __func__, my_data->the_data); 18 | kfree(my_data); 19 | } 20 | 21 | static int __init my_init(void) 22 | { 23 | struct work_data * my_data; 24 | 25 | pr_info("Work queue module init: %s %d\n", __func__, __LINE__); 26 | wq = create_singlethread_workqueue("my_single_thread"); 27 | my_data = kmalloc(sizeof(struct work_data), GFP_KERNEL); 28 | 29 | my_data->the_data = 34; 30 | 31 | INIT_WORK(&my_data->my_work, work_handler); 32 | queue_work(wq, &my_data->my_work); 33 | 34 | return 0; 35 | } 36 | 37 | static void __exit my_exit(void) 38 | { 39 | flush_workqueue(wq); 40 | destroy_workqueue(wq); 41 | pr_info("Work queue module exit: %s %d\n", __func__, __LINE__); 42 | } 43 | 44 | module_init(my_init); 45 | module_exit(my_exit); 46 | MODULE_LICENSE("GPL"); 47 | MODULE_AUTHOR("John Madieu "); 48 | MODULE_DESCRIPTION("Dedicated workqueue example"); 49 | -------------------------------------------------------------------------------- /Chapter03/hr-timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MS_TO_NS(x) (x * 1E6L) 8 | 9 | static struct hrtimer hr_timer; 10 | 11 | enum hrtimer_restart my_hrtimer_callback(struct hrtimer *timer) 12 | { 13 | pr_info("%s called (%ld).\n", __func__, jiffies); 14 | return HRTIMER_NORESTART; 15 | } 16 | 17 | static int hrt_init_module(void) 18 | { 19 | ktime_t ktime; 20 | unsigned long delay_in_ms = 200L; 21 | 22 | pr_info("hrtimer module installing\n"); 23 | 24 | /* 25 | * ktime = ktime_set(0, 200 * 1000 * 1000); 26 | * 200 ms = 10 * 1000 * 1000 ns 27 | */ 28 | ktime = ktime_set(0, MS_TO_NS(delay_in_ms)); 29 | 30 | hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 31 | hr_timer.function = &my_hrtimer_callback; 32 | pr_info( "Starting timer to fire in %ldms (%ld)\n", \ 33 | delay_in_ms, jiffies ); 34 | 35 | hrtimer_start(&hr_timer, ktime, HRTIMER_MODE_REL); 36 | return 0; 37 | } 38 | 39 | static void hrt_cleanup_module(void) 40 | { 41 | int ret; 42 | ret = hrtimer_cancel(&hr_timer); 43 | if (ret) 44 | pr_info("The timer was still in use...\n"); 45 | 46 | pr_info("hrtimer module uninstalling\n"); 47 | return; 48 | } 49 | 50 | module_init(hrt_init_module); 51 | module_exit(hrt_cleanup_module); 52 | MODULE_LICENSE("GPL"); 53 | MODULE_AUTHOR("John Madieu "); 54 | MODULE_DESCRIPTION("Standard timer example"); 55 | -------------------------------------------------------------------------------- /Chapter03/shared-workqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include /* for sleep */ 4 | #include /* for wait queue */ 5 | #include 6 | #include 7 | #include /* for kmalloc() */ 8 | #include 9 | 10 | //static DECLARE_WAIT_QUEUE_HEAD(my_wq); 11 | static int sleep = 0; 12 | 13 | struct work_data { 14 | struct work_struct my_work; 15 | wait_queue_head_t my_wq; 16 | int the_data; 17 | }; 18 | 19 | static void work_handler(struct work_struct *work) 20 | { 21 | struct work_data *my_data = container_of(work, \ 22 | struct work_data, my_work); 23 | pr_info("Work queue module handler: %s, data is %d\n", __FUNCTION__, my_data->the_data); 24 | msleep(3000); 25 | wake_up_interruptible(&my_data->my_wq); 26 | kfree(my_data); 27 | } 28 | 29 | static int __init my_init(void) 30 | { 31 | struct work_data * my_data; 32 | 33 | my_data = kmalloc(sizeof(struct work_data), GFP_KERNEL); 34 | my_data->the_data = 34; 35 | 36 | INIT_WORK(&my_data->my_work, work_handler); 37 | init_waitqueue_head(&my_data->my_wq); 38 | 39 | schedule_work(&my_data->my_work); 40 | pr_info("I'm goint to sleep ...\n"); 41 | wait_event_interruptible(my_data->my_wq, sleep != 0); 42 | pr_info("I am Waked up...\n"); 43 | return 0; 44 | } 45 | 46 | static void __exit my_exit(void) 47 | { 48 | pr_info("Work queue module exit: %s %d\n", __FUNCTION__, __LINE__); 49 | } 50 | 51 | module_init(my_init); 52 | module_exit(my_exit); 53 | MODULE_LICENSE("GPL"); 54 | MODULE_AUTHOR("John Madieu "); 55 | MODULE_DESCRIPTION("Shared workqueue"); 56 | -------------------------------------------------------------------------------- /Chapter03/std-timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static struct timer_list my_timer; 7 | 8 | void my_timer_callback(struct timer_list *t) 9 | { 10 | pr_info("%s called (%ld).\n", __FUNCTION__, jiffies); 11 | } 12 | 13 | static int __init my_init(void) 14 | { 15 | int retval; 16 | pr_info("Timer module loaded\n"); 17 | 18 | timer_setup(&my_timer, my_timer_callback, 0); 19 | pr_info( "Setup timer to fire in 500ms (%ld)\n", jiffies); 20 | 21 | retval = mod_timer(&my_timer, jiffies + msecs_to_jiffies(500)); 22 | if (retval) 23 | pr_info("Timer firing failed\n"); 24 | 25 | return 0; 26 | } 27 | 28 | static void my_exit(void) 29 | { 30 | int retval; 31 | retval = del_timer(&my_timer); 32 | if (retval) 33 | pr_info("The timer is still in use...\n"); 34 | 35 | pr_info("Timer module unloaded\n"); 36 | return; 37 | } 38 | 39 | module_init(my_init); 40 | module_exit(my_exit); 41 | MODULE_AUTHOR("John Madieu "); 42 | MODULE_DESCRIPTION("Standard timer example"); 43 | MODULE_LICENSE("GPL"); 44 | -------------------------------------------------------------------------------- /Chapter03/tasklet.c: -------------------------------------------------------------------------------- 1 | #define pr_fmt(fmt) "PACKT-03: " fmt 2 | 3 | #include 4 | #include 5 | #include 6 | #include /* for tasklets api */ 7 | 8 | char tasklet_data[]="We use a string; but it could be pointer to a structure"; 9 | 10 | /* Tasklet handler, that just print the data */ 11 | void tasklet_function(struct tasklet_struct *t) 12 | { 13 | pr_info("running %s\n", __func__); 14 | return; 15 | } 16 | 17 | DECLARE_TASKLET(my_tasklet, tasklet_function); 18 | 19 | static int __init my_init(void) 20 | { 21 | /* Schedule the handler */ 22 | tasklet_schedule(&my_tasklet); 23 | pr_info("tasklet example\n"); 24 | return 0; 25 | } 26 | 27 | void my_exit( void ) 28 | { 29 | /* Stop the tasklet before we exit */ 30 | tasklet_kill(&my_tasklet); 31 | pr_info("tasklet example cleanup\n"); 32 | return; 33 | } 34 | 35 | module_init(my_init); 36 | module_exit(my_exit); 37 | MODULE_AUTHOR("John Madieu "); 38 | MODULE_LICENSE("GPL"); 39 | -------------------------------------------------------------------------------- /Chapter03/user-invoke.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include /* for work queue */ 4 | #include 5 | 6 | static struct delayed_work initiate_shutdown_work; 7 | 8 | static void delayed_shutdown(struct work_struct *work) 9 | { 10 | char *cmd = "/sbin/shutdown"; 11 | char *argv[] = { 12 | cmd, 13 | "-h", 14 | "now", 15 | NULL, 16 | }; 17 | char *envp[] = { 18 | "HOME=/", 19 | "PATH=/sbin:/bin:/usr/sbin:/usr/bin", 20 | NULL, 21 | }; 22 | 23 | call_usermodehelper(cmd, argv, envp, 0); 24 | } 25 | 26 | static int __init my_shutdown_init( void ) 27 | { 28 | INIT_DELAYED_WORK(&initiate_shutdown_work, delayed_shutdown); 29 | schedule_delayed_work(&initiate_shutdown_work, msecs_to_jiffies(200)); 30 | return 0; 31 | } 32 | 33 | static void __exit my_shutdown_exit( void ) 34 | { 35 | return; 36 | } 37 | 38 | module_init( my_shutdown_init ); 39 | module_exit( my_shutdown_exit ); 40 | MODULE_LICENSE("GPL"); 41 | MODULE_AUTHOR("John Madieu "); 42 | MODULE_DESCRIPTION("Simple module that trigger a delayed shut down"); 43 | -------------------------------------------------------------------------------- /Chapter03/waitqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static DECLARE_WAIT_QUEUE_HEAD(my_wq); 9 | static int condition = 0; 10 | 11 | /* declare a work queue*/ 12 | static struct work_struct wrk; 13 | 14 | static void work_handler(struct work_struct *work) 15 | { 16 | pr_info("Waitqueue module handler %s\n", __FUNCTION__); 17 | msleep(3000); 18 | pr_info("Wake up the sleeping module\n"); 19 | condition = 1; 20 | wake_up_interruptible(&my_wq); 21 | } 22 | 23 | static int __init my_init(void) 24 | { 25 | pr_info("Wait queue example\n"); 26 | 27 | INIT_WORK(&wrk, work_handler); 28 | schedule_work(&wrk); 29 | 30 | pr_info("Going to sleep %s\n", __FUNCTION__); 31 | wait_event_interruptible(my_wq, condition != 0); 32 | 33 | pr_info("woken up by the work job\n"); 34 | return 0; 35 | } 36 | 37 | void my_exit(void) 38 | { 39 | pr_info("waitqueue example cleanup\n"); 40 | } 41 | 42 | module_init(my_init); 43 | module_exit(my_exit); 44 | MODULE_AUTHOR("John Madieu "); 45 | MODULE_LICENSE("GPL"); 46 | -------------------------------------------------------------------------------- /Chapter04/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := dummy-char.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter04/README.md: -------------------------------------------------------------------------------- 1 | # Character device driver 2 | 3 | After running `make` command, there will be one module: 4 | 5 | * dummy-char.ko 6 | 7 | After loading the module, there will be a `/dev/dummy_char` char device. There 8 | is also a class created for this device, `/sys/class/dummy_char_class/`. One can 9 | actually print info on the device using `udevadm info` command: 10 | 11 | 12 | ```bash 13 | # insmod dummy-char.ko 14 | # udevadm info /dev/dummy_char 15 | P: /devices/virtual/dummy_char_class/dummy_char 16 | N: dummy_char 17 | E: DEVNAME=/dev/dummy_char 18 | E: DEVPATH=/devices/virtual/dummy_char_class/dummy_char 19 | E: MAJOR=241 20 | E: MINOR=0 21 | E: SUBSYSTEM=dummy_char_class 22 | 23 | 24 | $ ls -l /sys/class/dummy_char_class/ 25 | total 0 26 | lrwxrwxrwx 1 root root 0 oct. 12 16:05 dummy_char -> ../../devices/virtual/dummy_char_class/dummy_char 27 | $ cat /sys/class/dummy_char_class/dummy_char/dev 28 | 241:0 29 | ``` 30 | 31 | For testing purpose, one can use `cat` and `read` commands: 32 | 33 | ```bash 34 | # cat /dev/dummy_char 35 | # echo "blabla" > /dev/dummy_char 36 | # rmmod dummy-char.ko 37 | 38 | $ dmesg 39 | [...] 40 | [31444.392114] dummy_char major number = 241 41 | [31444.392217] dummy char module loaded 42 | [31452.575938] Someone tried to open me 43 | [31452.575945] Nothing to read guy 44 | [31452.575950] Someone closed me 45 | [31483.210527] Someone tried to open me 46 | [31483.210570] Can't accept any data guy 47 | [31483.210578] Someone closed me 48 | [31498.998185] dummy char module Unloaded 49 | ``` 50 | -------------------------------------------------------------------------------- /Chapter04/dummy-char.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static unsigned int major; /* major number for device */ 9 | static struct class *dummy_class; 10 | static struct cdev dummy_cdev; 11 | 12 | 13 | int dummy_open(struct inode * inode, struct file * filp) 14 | { 15 | pr_info("Someone tried to open me\n"); 16 | return 0; 17 | } 18 | 19 | int dummy_release(struct inode * inode, struct file * filp) 20 | { 21 | pr_info("Someone closed me\n"); 22 | return 0; 23 | } 24 | 25 | ssize_t dummy_read (struct file *filp, char __user * buf, size_t count, 26 | loff_t * offset) 27 | { 28 | pr_info("Nothing to read\n"); 29 | return 0; 30 | } 31 | 32 | 33 | ssize_t dummy_write(struct file * filp, const char __user * buf, size_t count, 34 | loff_t * offset) 35 | { 36 | pr_info("Can't accept any data\n"); 37 | return count; 38 | } 39 | 40 | struct file_operations dummy_fops = { 41 | open: dummy_open, 42 | release: dummy_release, 43 | read: dummy_read, 44 | write: dummy_write, 45 | }; 46 | 47 | static int __init dummy_char_init_module(void) 48 | { 49 | struct device *dummy_device; 50 | int error; 51 | dev_t devt = 0; 52 | 53 | /* Get a range of minor numbers (starting with 0) to work with */ 54 | error = alloc_chrdev_region(&devt, 0, 1, "dummy_char"); 55 | if (error < 0) { 56 | pr_err("Can't get major number\n"); 57 | return error; 58 | } 59 | major = MAJOR(devt); 60 | pr_info("dummy_char major number = %d\n",major); 61 | 62 | /* Create device class, visible in /sys/class */ 63 | dummy_class = class_create(THIS_MODULE, "dummy_char_class"); 64 | if (IS_ERR(dummy_class)) { 65 | pr_err("Error creating dummy char class.\n"); 66 | unregister_chrdev_region(MKDEV(major, 0), 1); 67 | return PTR_ERR(dummy_class); 68 | } 69 | 70 | /* Initialize the char device and tie a file_operations to it */ 71 | cdev_init(&dummy_cdev, &dummy_fops); 72 | dummy_cdev.owner = THIS_MODULE; 73 | /* Now make the device live for the users to access */ 74 | cdev_add(&dummy_cdev, devt, 1); 75 | 76 | dummy_device = device_create(dummy_class, 77 | NULL, /* no parent device */ 78 | devt, /* associated dev_t */ 79 | NULL, /* no additional data */ 80 | "dummy_char"); /* device name */ 81 | 82 | if (IS_ERR(dummy_device)) { 83 | pr_err("Error creating dummy char device.\n"); 84 | class_destroy(dummy_class); 85 | unregister_chrdev_region(devt, 1); 86 | return -1; 87 | } 88 | 89 | pr_info("dummy char module loaded\n"); 90 | return 0; 91 | } 92 | 93 | static void __exit dummy_char_cleanup_module(void) 94 | { 95 | unregister_chrdev_region(MKDEV(major, 0), 1); 96 | device_destroy(dummy_class, MKDEV(major, 0)); 97 | cdev_del(&dummy_cdev); 98 | class_destroy(dummy_class); 99 | 100 | pr_info("dummy char module Unloaded\n"); 101 | } 102 | 103 | module_init(dummy_char_init_module); 104 | module_exit(dummy_char_cleanup_module); 105 | 106 | MODULE_AUTHOR("John Madieu "); 107 | MODULE_DESCRIPTION("Dummy character driver"); 108 | MODULE_LICENSE("GPL"); 109 | -------------------------------------------------------------------------------- /Chapter07/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := platform-dummy-char.o platform-dummy-ins.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter07/README.md: -------------------------------------------------------------------------------- 1 | # Platform device and driver 2 | 3 | This code consists of converting the driver in chapter 4 into a platform 4 | driver. After running `make` command, there will be two modules: 5 | 6 | * platform-dummy-char.ko 7 | * platform-dummy-ins.ko 8 | 9 | The fist module is our platform driver. The second one is a basic module whose 10 | aim is to create a platform device that will match the 11 | `platform-dummy-char` driver. 12 | 13 | 14 | Prior to testing our driver, one should load the following modules: 15 | 16 | ```bash 17 | # insmod platform-dummy-char.ko 18 | # insmod platform-dummy-ins.ko 19 | ``` 20 | 21 | Once the modules loaded, one can see below message in debug output: 22 | 23 | ```bash 24 | $ dmesg 25 | [...] 26 | [33117.715597] dummy_char major number = 241 27 | [33117.715662] dummy char module loaded 28 | [33117.715670] platform-dummy-char device added 29 | ``` 30 | 31 | One can print additional information by listing the sysfs content of the device: 32 | 33 | ```bash 34 | $ ls -l /sys/devices/platform/platform-dummy-char.0/ 35 | total 0 36 | lrwxrwxrwx 1 root root 0 oct. 12 16:40 driver -> ../../../bus/platform/drivers/platform-dummy-char 37 | -rw-r--r-- 1 root root 4096 oct. 12 16:42 driver_override 38 | -r--r--r-- 1 root root 4096 oct. 12 16:42 modalias 39 | drwxr-xr-x 2 root root 0 oct. 12 16:42 power 40 | lrwxrwxrwx 1 root root 0 oct. 12 16:42 subsystem -> ../../../bus/platform 41 | -rw-r--r-- 1 root root 4096 oct. 12 16:40 uevent 42 | ``` 43 | 44 | Or by using `udevadm` tool: 45 | 46 | ```bash 47 | $ udevadm info /dev/dummy_char 48 | P: /devices/platform/platform-dummy-char.0/dummy_char_class/dummy_char 49 | N: dummy_char 50 | E: DEVNAME=/dev/dummy_char 51 | E: DEVPATH=/devices/platform/platform-dummy-char.0/dummy_char_class/dummy_char 52 | E: MAJOR=241 53 | E: MINOR=0 54 | E: SUBSYSTEM=dummy_char_class 55 | ``` 56 | 57 | Of course, the behaviour remains the same as the char device tested on chapter : 58 | 59 | ```bash 60 | # cat /dev/dummy_char 61 | # echo "blabla" > /dev/dummy_char 62 | # rmmod dummy-char.ko 63 | 64 | $ dmesg 65 | [...] 66 | [ 6753.573560] dummy_char major number = 241 67 | [ 6753.573611] dummy char module loaded 68 | [ 6753.573622] platform-dummy-char device added 69 | [ 7081.034607] Someone tried to open me 70 | [ 7081.034641] Can't accept any data guy 71 | [ 7081.034649] Someone closed me 72 | [ 7084.861861] Someone tried to open me 73 | [ 7084.861887] Nothing to read guy 74 | [ 7084.861906] Someone closed me 75 | ``` 76 | -------------------------------------------------------------------------------- /Chapter07/platform-dummy-char.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For platform devices */ 5 | #include 6 | #include 7 | 8 | static unsigned int major; /* major number for device */ 9 | static struct class *dummy_class; 10 | static struct cdev dummy_cdev; 11 | 12 | int dummy_open(struct inode * inode, struct file * filp) 13 | { 14 | pr_info("Someone tried to open me\n"); 15 | return 0; 16 | } 17 | 18 | int dummy_release(struct inode * inode, struct file * filp) 19 | { 20 | pr_info("Someone closed me\n"); 21 | return 0; 22 | } 23 | 24 | ssize_t dummy_read (struct file *filp, char __user * buf, size_t count, 25 | loff_t * offset) 26 | { 27 | pr_info("Nothing to read guy\n"); 28 | return 0; 29 | } 30 | 31 | ssize_t dummy_write(struct file * filp, const char __user * buf, size_t count, 32 | loff_t * offset) 33 | { 34 | pr_info("Can't accept any data guy\n"); 35 | return count; 36 | } 37 | 38 | struct file_operations dummy_fops = { 39 | open: dummy_open, 40 | release: dummy_release, 41 | read: dummy_read, 42 | write: dummy_write, 43 | }; 44 | 45 | static int my_pdrv_probe (struct platform_device *pdev) 46 | { 47 | struct device *dummy_device; 48 | int error; 49 | dev_t devt = 0; 50 | 51 | /* Get a range of minor numbers (starting with 0) to work with */ 52 | error = alloc_chrdev_region(&devt, 0, 1, "dummy_char"); 53 | if (error < 0) { 54 | pr_err("Can't get major number\n"); 55 | return error; 56 | } 57 | major = MAJOR(devt); 58 | pr_info("dummy_char major number = %d\n",major); 59 | 60 | /* Create device class, visible in /sys/class */ 61 | dummy_class = class_create(THIS_MODULE, "dummy_char_class"); 62 | if (IS_ERR(dummy_class)) { 63 | pr_err("Error creating sdma test module class.\n"); 64 | unregister_chrdev_region(MKDEV(major, 0), 1); 65 | return PTR_ERR(dummy_class); 66 | } 67 | 68 | /* Initialize the char device and tie a file_operations to it */ 69 | cdev_init(&dummy_cdev, &dummy_fops); 70 | dummy_cdev.owner = THIS_MODULE; 71 | /* Now make the device live for the users to access */ 72 | cdev_add(&dummy_cdev, devt, 1); 73 | 74 | dummy_device = device_create(dummy_class, 75 | &pdev->dev, /* no parent device */ 76 | devt, /* associated dev_t */ 77 | NULL, /* no additional data */ 78 | "dummy_char"); /* device name */ 79 | 80 | if (IS_ERR(dummy_device)) { 81 | pr_err("Error creating sdma test class device.\n"); 82 | class_destroy(dummy_class); 83 | unregister_chrdev_region(devt, 1); 84 | return -1; 85 | } 86 | 87 | pr_info("dummy char module loaded\n"); 88 | return 0; 89 | } 90 | 91 | static int my_pdrv_remove(struct platform_device *pdev) 92 | { 93 | unregister_chrdev_region(MKDEV(major, 0), 1); 94 | device_destroy(dummy_class, MKDEV(major, 0)); 95 | cdev_del(&dummy_cdev); 96 | class_destroy(dummy_class); 97 | 98 | pr_info("dummy char module Unloaded\n"); 99 | return 0; 100 | } 101 | 102 | static struct platform_driver mypdrv = { 103 | .probe = my_pdrv_probe, 104 | .remove = my_pdrv_remove, 105 | .driver = { 106 | .name = "platform-dummy-char", 107 | .owner = THIS_MODULE, 108 | }, 109 | }; 110 | module_platform_driver(mypdrv); 111 | MODULE_AUTHOR("John Madieu "); 112 | MODULE_LICENSE("GPL"); 113 | -------------------------------------------------------------------------------- /Chapter07/platform-dummy-ins.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static struct platform_device *pdev; 6 | 7 | static int __init platform_dummy_char_add(void) 8 | { 9 | int inst_id = 0; /* instance unique ID: base address would be a good choice */ 10 | pdev = platform_device_alloc("platform-dummy-char", inst_id); 11 | platform_device_add(pdev); 12 | pr_info("platform-dummy-char device added\n"); 13 | return 0; 14 | } 15 | 16 | static void __exit fplatform_dummy_char_put(void) 17 | { 18 | pr_info("platform-dummy-char device removed\n"); 19 | platform_device_put(pdev); 20 | } 21 | 22 | module_init(platform_dummy_char_add); 23 | module_exit(fplatform_dummy_char_put); 24 | MODULE_LICENSE("GPL"); 25 | MODULE_AUTHOR("John Madieu "); 26 | -------------------------------------------------------------------------------- /Chapter08/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := ee24lc512.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter08/ee24lc512.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | /* 18 | * The structure to represent 'eep_dev' devices. 19 | * data - data buffer; 20 | * block_size - maximum number of bytes that can be read or written 21 | * in one call; 22 | * eep_mutex - a mutex to protect this device; 23 | * users: the number of time this device is being opened 24 | */ 25 | struct eep_dev { 26 | unsigned char *data; 27 | struct i2c_client *client; 28 | struct mutex eep_mutex; 29 | struct list_head device_entry; 30 | dev_t devt; 31 | unsigned users; 32 | }; 33 | 34 | static LIST_HEAD(device_list); 35 | static DEFINE_MUTEX(device_list_lock); 36 | 37 | #define EEP_DEVICE_NAME "packt-mem" 38 | #define EEP_PAGE_SIZE 128 39 | #define EEP_SIZE 1024*64 /* 24LC512 is 64KB sized */ 40 | 41 | static struct class *eep_class = NULL; 42 | static int probed_ndevices = 0; 43 | 44 | int eep_open(struct inode *inode, struct file *filp) 45 | { 46 | struct eep_dev *eeprom = NULL; 47 | int err = -ENXIO; 48 | 49 | mutex_lock(&device_list_lock); 50 | list_for_each_entry(eeprom, &device_list, device_entry) { 51 | if (eeprom->devt == inode->i_rdev) { 52 | err = 0; 53 | break; 54 | } 55 | } 56 | 57 | if (err) { 58 | pr_warn("eeprom: nothing for major %d\n", imajor(inode)); 59 | goto err_find_dev; 60 | } 61 | 62 | /* if opened the 1st time, allocate the buffer */ 63 | if (eeprom->data == NULL) { 64 | eeprom->data = kzalloc(EEP_SIZE, GFP_KERNEL); 65 | if (!eeprom->data) { 66 | pr_err("[target] open(): out of memory\n"); 67 | err = -ENOMEM; 68 | goto err_alloc_data_buf; 69 | } 70 | } 71 | 72 | eeprom->users++; 73 | /* store a pointer to struct eep_dev here for other methods */ 74 | filp->private_data = eeprom; 75 | mutex_unlock(&device_list_lock); 76 | return 0; 77 | 78 | err_alloc_data_buf: 79 | kfree(eeprom->data); 80 | eeprom->data = NULL; 81 | err_find_dev: 82 | mutex_unlock(&device_list_lock); 83 | return err; 84 | } 85 | 86 | /* 87 | * Release is called when device node is closed 88 | */ 89 | int eep_release(struct inode *inode, struct file *filp) 90 | { 91 | struct eep_dev *eeprom; 92 | 93 | mutex_lock(&device_list_lock); 94 | eeprom = filp->private_data; 95 | 96 | /* last close? */ 97 | eeprom->users--; 98 | if (!eeprom->users) { 99 | kfree(eeprom->data); 100 | eeprom->data = NULL ; /* Never forget to set to 'NULL' after freeing */ 101 | } 102 | 103 | mutex_unlock(&device_list_lock); 104 | return 0; 105 | } 106 | 107 | ssize_t eep_read(struct file *filp, char __user *buf, 108 | size_t count, loff_t *f_pos) 109 | { 110 | struct eep_dev *eeprom = filp->private_data; 111 | struct i2c_msg msg[2]; 112 | ssize_t retval = 0; 113 | int _reg_addr; 114 | u8 reg_addr[2]; 115 | 116 | if (mutex_lock_killable(&eeprom->eep_mutex)) 117 | return -EINTR; 118 | 119 | if (*f_pos >= EEP_SIZE) /* EOF */ 120 | goto end_read; 121 | 122 | if (*f_pos + count > EEP_SIZE) 123 | count = EEP_SIZE - *f_pos; 124 | 125 | if (count > EEP_SIZE) 126 | count = EEP_SIZE; 127 | 128 | _reg_addr = (int)(*f_pos); 129 | reg_addr[0] = (u8)(_reg_addr >> 8); 130 | reg_addr[1] = (u8)(_reg_addr & 0xFF); 131 | 132 | msg[0].addr = eeprom->client->addr; 133 | msg[0].flags = 0; /* Write */ 134 | msg[0].len = 2; /* Address is 2byte coded */ 135 | msg[0].buf = reg_addr; 136 | 137 | msg[1].addr = eeprom->client->addr; 138 | msg[1].flags = I2C_M_RD; /* We need to read */ 139 | msg[1].len = count; 140 | msg[1].buf = eeprom->data; 141 | 142 | if (i2c_transfer(eeprom->client->adapter, msg, 2) < 0) 143 | pr_err("ee24lc512: i2c_transfer failed\n"); 144 | 145 | if (copy_to_user(buf, eeprom->data, count) != 0) { 146 | retval = -EIO; 147 | goto end_read; 148 | } 149 | 150 | retval = count; 151 | *f_pos += count ; 152 | 153 | end_read: 154 | mutex_unlock(&eeprom->eep_mutex); 155 | return retval; 156 | } 157 | 158 | int transacWrite(struct eep_dev *eeprom, 159 | int _reg_addr, unsigned char *data, 160 | int offset, unsigned int len) 161 | { 162 | unsigned char tmp[len + 2]; 163 | struct i2c_msg msg[1]; 164 | 165 | tmp[0] = (u8)(_reg_addr >> 8); 166 | tmp[1] = (u8)(_reg_addr & 0xFF); 167 | memcpy (tmp + 2, &(data[offset]), len); 168 | 169 | msg[0].addr = eeprom->client->addr; 170 | msg[0].flags = 0; /* Write */ 171 | msg[0].len = len + 2; /* Address is 2 bytes coded */ 172 | msg[0].buf = tmp; 173 | 174 | if (i2c_transfer(eeprom->client->adapter, msg, 1) < 0){ 175 | pr_err("ee24lc512: i2c_transfer failed\n"); 176 | return -1; 177 | } 178 | return len; 179 | } 180 | 181 | ssize_t eep_write(struct file *filp, const char __user *buf, 182 | size_t count, loff_t *f_pos) 183 | { 184 | int i, _reg_addr, offset, remain_in_page, nb_page, last_remain; 185 | struct eep_dev *eeprom = filp->private_data; 186 | ssize_t retval = 0; 187 | 188 | if (mutex_lock_killable(&eeprom->eep_mutex)) 189 | return -EINTR; 190 | 191 | if (*f_pos >= EEP_SIZE) { 192 | retval = -EINVAL; 193 | goto end_write; 194 | } 195 | 196 | if (*f_pos >= EEP_SIZE) { 197 | /* Writing beyond the end of the buffer is not allowed. */ 198 | retval = -EINVAL; 199 | goto end_write; 200 | } 201 | 202 | if (*f_pos + count > EEP_SIZE) 203 | count = EEP_SIZE - *f_pos; 204 | 205 | if (count > EEP_SIZE) 206 | count = EEP_SIZE; 207 | 208 | if (copy_from_user(eeprom->data, buf, count) != 0) { 209 | retval = -EFAULT; 210 | goto end_write; 211 | } 212 | 213 | _reg_addr = (int)(*f_pos); 214 | offset = 0; 215 | remain_in_page = (EEP_PAGE_SIZE - (_reg_addr % EEP_PAGE_SIZE)) % EEP_PAGE_SIZE; 216 | nb_page = (count - remain_in_page) / EEP_PAGE_SIZE; 217 | last_remain = (count - remain_in_page) % EEP_PAGE_SIZE ; 218 | 219 | if (remain_in_page > 0){ 220 | retval = transacWrite(eeprom, _reg_addr, eeprom->data, offset, remain_in_page); 221 | if (retval < 0) 222 | goto end_write; 223 | offset += remain_in_page; 224 | _reg_addr += remain_in_page; 225 | retval = offset; 226 | mdelay(10); 227 | } 228 | 229 | if (nb_page) { 230 | for (i=0; i < nb_page; i++){ 231 | retval = transacWrite(eeprom, _reg_addr, eeprom->data, offset, EEP_PAGE_SIZE); 232 | if (retval < 0) 233 | goto end_write; 234 | offset += EEP_PAGE_SIZE; 235 | _reg_addr += EEP_PAGE_SIZE; 236 | retval = offset; 237 | mdelay(10); 238 | } 239 | } 240 | 241 | if (last_remain > 0){ 242 | retval = transacWrite(eeprom, _reg_addr, eeprom->data, offset, last_remain); 243 | if (retval < 0) 244 | goto end_write; 245 | offset += last_remain; 246 | _reg_addr += last_remain; 247 | retval = offset; 248 | mdelay(10); 249 | } 250 | 251 | *f_pos += count; 252 | 253 | end_write: 254 | mutex_unlock(&eeprom->eep_mutex); 255 | return retval; 256 | } 257 | 258 | loff_t eep_llseek(struct file *filp, loff_t off, int whence) 259 | { 260 | loff_t newpos = 0; 261 | 262 | switch (whence) { 263 | case 0: /* SEEK_SET */ 264 | newpos = off; 265 | break; 266 | 267 | case 1: /* SEEK_CUR */ 268 | newpos = filp->f_pos + off; 269 | break; 270 | 271 | case 2: /* SEEK_END - Not supported */ 272 | return -EINVAL; 273 | 274 | default: /* can't happen */ 275 | return -EINVAL; 276 | } 277 | if (newpos < 0 || newpos > EEP_SIZE) 278 | return -EINVAL; 279 | 280 | filp->f_pos = newpos; 281 | return newpos; 282 | } 283 | 284 | struct file_operations eep_fops = { 285 | .owner = THIS_MODULE, 286 | .read = eep_read, 287 | .write = eep_write, 288 | .open = eep_open, 289 | .release = eep_release, 290 | .llseek = eep_llseek, 291 | }; 292 | 293 | #ifdef CONFIG_OF 294 | static const struct of_device_id eeprom_dt_ids[] = { 295 | { .compatible = "microchip,ee24lc512" }, 296 | {}, 297 | }; 298 | MODULE_DEVICE_TABLE(of, eeprom_dt_ids); 299 | #endif 300 | 301 | static int ee24lc512_probe(struct i2c_client *client, 302 | const struct i2c_device_id *id) 303 | { 304 | int major; 305 | unsigned char data[5]; 306 | u8 reg_addr[2]; 307 | struct i2c_msg msg[2]; 308 | 309 | int err = 0; 310 | struct eep_dev *eeprom = NULL; 311 | struct device *device = NULL; 312 | 313 | if (!i2c_check_functionality(client->adapter, 314 | I2C_FUNC_SMBUS_BYTE_DATA)) 315 | return -EIO; 316 | 317 | /* 318 | * We send a simple i2c transaction. If it fails, 319 | * it means there is no eeprom 320 | */ 321 | reg_addr[0] = 0x00; 322 | reg_addr[1] = 0x00; 323 | 324 | msg[0].addr = client->addr; 325 | msg[0].flags = 0; /* Write */ 326 | msg[0].len = 2; /* Address is 2byte coded */ 327 | msg[0].buf = reg_addr; 328 | 329 | msg[1].addr = client->addr; 330 | msg[1].flags = I2C_M_RD; /* We need to read */ 331 | msg[1].len = 5; //count; 332 | msg[1].buf = data; 333 | 334 | if (i2c_transfer(client->adapter, msg, 2) < 0) { 335 | pr_err("ee24lc512: i2c_transfer failed\n"); 336 | return -ENODEV; 337 | } 338 | 339 | eeprom = devm_kzalloc(&client->dev, sizeof(*eeprom), GFP_KERNEL); 340 | if (!eeprom) 341 | return -ENOMEM; 342 | 343 | major = register_chrdev(0, "eeprom", &eep_fops); 344 | if (major < 0) { 345 | pr_err("[target] register_chrdev() failed\n"); 346 | return major; 347 | } 348 | 349 | /* Construct devices */ 350 | eeprom->data = NULL; 351 | eeprom->client = client; 352 | mutex_init(&eeprom->eep_mutex); 353 | INIT_LIST_HEAD(&eeprom->device_entry); 354 | 355 | eeprom->devt = MKDEV(major, 0); 356 | device = device_create(eep_class, NULL, /* no parent device */ 357 | eeprom->devt, NULL, /* no additional data */ 358 | EEP_DEVICE_NAME "_%d", probed_ndevices++); 359 | 360 | if (IS_ERR(device)) { 361 | err = PTR_ERR(device); 362 | pr_err("[target] Error %d while trying to create %s%d", 363 | err, EEP_DEVICE_NAME, --probed_ndevices); 364 | goto fail; 365 | } 366 | 367 | mutex_lock(&device_list_lock); 368 | list_add(&eeprom->device_entry, &device_list); 369 | i2c_set_clientdata(client, eeprom); 370 | mutex_unlock(&device_list_lock); 371 | 372 | return 0; /* success */ 373 | 374 | fail: 375 | if (eeprom) 376 | kfree(eeprom); 377 | return err; 378 | } 379 | 380 | static int ee24lc512_remove(struct i2c_client *client) 381 | { 382 | struct eep_dev *eeprom = i2c_get_clientdata(client); 383 | 384 | /* prevent new opens */ 385 | mutex_lock(&device_list_lock); 386 | list_del(&eeprom->device_entry); 387 | device_destroy(eep_class, eeprom->devt); 388 | if (eeprom->users == 0) 389 | kfree(eeprom); 390 | mutex_unlock(&device_list_lock); 391 | 392 | return 0; 393 | } 394 | 395 | static const struct i2c_device_id ee24lc512_id[] = { 396 | {"ee24lc512", 0}, 397 | {}, 398 | }; 399 | MODULE_DEVICE_TABLE(i2c, ee24lc512_id); 400 | 401 | static struct i2c_driver ee24lc512_i2c_driver = { 402 | .driver = { 403 | .owner = THIS_MODULE, 404 | .name = "ee24lc512", 405 | .of_match_table = of_match_ptr(eeprom_dt_ids), 406 | }, 407 | .probe = ee24lc512_probe, 408 | .remove = ee24lc512_remove, 409 | .id_table = ee24lc512_id, 410 | }; 411 | 412 | static int __init eeprom_drv_init(void) 413 | { 414 | int status; 415 | 416 | /* Claim our 256 reserved device numbers. Then register a class 417 | * that will key udev/mdev to add/remove /dev nodes. Last, register 418 | * the driver which manages those device numbers. 419 | */ 420 | eep_class = class_create(THIS_MODULE, "eeprom"); 421 | if (IS_ERR(eep_class)) 422 | return PTR_ERR(eep_class); 423 | 424 | status = i2c_register_driver(THIS_MODULE, &ee24lc512_i2c_driver); 425 | if (status < 0) 426 | class_destroy(eep_class); 427 | 428 | return status; 429 | } 430 | module_init(eeprom_drv_init); 431 | 432 | static void __exit eeprom_drv_exit(void) 433 | { 434 | i2c_del_driver(&ee24lc512_i2c_driver); 435 | class_destroy(eep_class); 436 | } 437 | module_exit(eeprom_drv_exit); 438 | 439 | MODULE_AUTHOR("John Madieu "); 440 | MODULE_LICENSE("GPL"); 441 | -------------------------------------------------------------------------------- /Chapter09/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := eeprom_93xx46.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter09/eeprom_93xx46.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Driver for 93xx46 EEPROMs 4 | * 5 | * (C) 2011 DENX Software Engineering, Anatolij Gustschin 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "eeprom_93xx46.h" 21 | 22 | #define OP_START 0x4 23 | #define OP_WRITE (OP_START | 0x1) 24 | #define OP_READ (OP_START | 0x2) 25 | #define ADDR_EWDS 0x00 26 | #define ADDR_ERAL 0x20 27 | #define ADDR_EWEN 0x30 28 | 29 | struct eeprom_93xx46_devtype_data { 30 | unsigned int quirks; 31 | }; 32 | 33 | static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = { 34 | .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ | 35 | EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH, 36 | }; 37 | 38 | struct eeprom_93xx46_dev { 39 | struct spi_device *spi; 40 | struct eeprom_93xx46_platform_data *pdata; 41 | struct mutex lock; 42 | struct nvmem_config nvmem_config; 43 | struct nvmem_device *nvmem; 44 | int addrlen; 45 | int size; 46 | }; 47 | 48 | static inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev) 49 | { 50 | return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ; 51 | } 52 | 53 | static inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev) 54 | { 55 | return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH; 56 | } 57 | 58 | static int eeprom_93xx46_read(void *priv, unsigned int off, 59 | void *val, size_t count) 60 | { 61 | struct eeprom_93xx46_dev *edev = priv; 62 | char *buf = val; 63 | int err = 0; 64 | 65 | if (unlikely(off >= edev->size)) 66 | return 0; 67 | if ((off + count) > edev->size) 68 | count = edev->size - off; 69 | if (unlikely(!count)) 70 | return count; 71 | 72 | mutex_lock(&edev->lock); 73 | 74 | if (edev->pdata->prepare) 75 | edev->pdata->prepare(edev); 76 | 77 | while (count) { 78 | struct spi_message m; 79 | struct spi_transfer t[2] = { { 0 } }; 80 | u16 cmd_addr = OP_READ << edev->addrlen; 81 | size_t nbytes = count; 82 | int bits; 83 | 84 | if (edev->addrlen == 7) { 85 | cmd_addr |= off & 0x7f; 86 | bits = 10; 87 | if (has_quirk_single_word_read(edev)) 88 | nbytes = 1; 89 | } else { 90 | cmd_addr |= (off >> 1) & 0x3f; 91 | bits = 9; 92 | if (has_quirk_single_word_read(edev)) 93 | nbytes = 2; 94 | } 95 | 96 | dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", 97 | cmd_addr, edev->spi->max_speed_hz); 98 | 99 | spi_message_init(&m); 100 | 101 | t[0].tx_buf = (char *)&cmd_addr; 102 | t[0].len = 2; 103 | t[0].bits_per_word = bits; 104 | spi_message_add_tail(&t[0], &m); 105 | 106 | t[1].rx_buf = buf; 107 | t[1].len = count; 108 | t[1].bits_per_word = 8; 109 | spi_message_add_tail(&t[1], &m); 110 | 111 | err = spi_sync(edev->spi, &m); 112 | /* have to wait at least Tcsl ns */ 113 | ndelay(250); 114 | 115 | if (err) { 116 | dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", 117 | nbytes, (int)off, err); 118 | break; 119 | } 120 | 121 | buf += nbytes; 122 | off += nbytes; 123 | count -= nbytes; 124 | } 125 | 126 | if (edev->pdata->finish) 127 | edev->pdata->finish(edev); 128 | 129 | mutex_unlock(&edev->lock); 130 | 131 | return err; 132 | } 133 | 134 | static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) 135 | { 136 | struct spi_message m; 137 | struct spi_transfer t; 138 | int bits, ret; 139 | u16 cmd_addr; 140 | 141 | cmd_addr = OP_START << edev->addrlen; 142 | if (edev->addrlen == 7) { 143 | cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1; 144 | bits = 10; 145 | } else { 146 | cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS); 147 | bits = 9; 148 | } 149 | 150 | if (has_quirk_instruction_length(edev)) { 151 | cmd_addr <<= 2; 152 | bits += 2; 153 | } 154 | 155 | dev_dbg(&edev->spi->dev, "ew%s cmd 0x%04x, %d bits\n", 156 | is_on ? "en" : "ds", cmd_addr, bits); 157 | 158 | spi_message_init(&m); 159 | memset(&t, 0, sizeof(t)); 160 | 161 | t.tx_buf = &cmd_addr; 162 | t.len = 2; 163 | t.bits_per_word = bits; 164 | spi_message_add_tail(&t, &m); 165 | 166 | mutex_lock(&edev->lock); 167 | 168 | if (edev->pdata->prepare) 169 | edev->pdata->prepare(edev); 170 | 171 | ret = spi_sync(edev->spi, &m); 172 | /* have to wait at least Tcsl ns */ 173 | ndelay(250); 174 | if (ret) 175 | dev_err(&edev->spi->dev, "erase/write %sable error %d\n", 176 | is_on ? "en" : "dis", ret); 177 | 178 | if (edev->pdata->finish) 179 | edev->pdata->finish(edev); 180 | 181 | mutex_unlock(&edev->lock); 182 | return ret; 183 | } 184 | 185 | static ssize_t 186 | eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, 187 | const char *buf, unsigned off) 188 | { 189 | struct spi_message m; 190 | struct spi_transfer t[2]; 191 | int bits, data_len, ret; 192 | u16 cmd_addr; 193 | 194 | cmd_addr = OP_WRITE << edev->addrlen; 195 | 196 | if (edev->addrlen == 7) { 197 | cmd_addr |= off & 0x7f; 198 | bits = 10; 199 | data_len = 1; 200 | } else { 201 | cmd_addr |= (off >> 1) & 0x3f; 202 | bits = 9; 203 | data_len = 2; 204 | } 205 | 206 | dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr); 207 | 208 | spi_message_init(&m); 209 | memset(t, 0, sizeof(t)); 210 | 211 | t[0].tx_buf = (char *)&cmd_addr; 212 | t[0].len = 2; 213 | t[0].bits_per_word = bits; 214 | spi_message_add_tail(&t[0], &m); 215 | 216 | t[1].tx_buf = buf; 217 | t[1].len = data_len; 218 | t[1].bits_per_word = 8; 219 | spi_message_add_tail(&t[1], &m); 220 | 221 | ret = spi_sync(edev->spi, &m); 222 | /* have to wait program cycle time Twc ms */ 223 | mdelay(6); 224 | return ret; 225 | } 226 | 227 | static int eeprom_93xx46_write(void *priv, unsigned int off, 228 | void *val, size_t count) 229 | { 230 | struct eeprom_93xx46_dev *edev = priv; 231 | char *buf = val; 232 | int i, ret, step = 1; 233 | 234 | if (unlikely(off >= edev->size)) 235 | return -EFBIG; 236 | if ((off + count) > edev->size) 237 | count = edev->size - off; 238 | if (unlikely(!count)) 239 | return count; 240 | 241 | /* only write even number of bytes on 16-bit devices */ 242 | if (edev->addrlen == 6) { 243 | step = 2; 244 | count &= ~1; 245 | } 246 | 247 | /* erase/write enable */ 248 | ret = eeprom_93xx46_ew(edev, 1); 249 | if (ret) 250 | return ret; 251 | 252 | mutex_lock(&edev->lock); 253 | 254 | if (edev->pdata->prepare) 255 | edev->pdata->prepare(edev); 256 | 257 | for (i = 0; i < count; i += step) { 258 | ret = eeprom_93xx46_write_word(edev, &buf[i], off + i); 259 | if (ret) { 260 | dev_err(&edev->spi->dev, "write failed at %d: %d\n", 261 | (int)off + i, ret); 262 | break; 263 | } 264 | } 265 | 266 | if (edev->pdata->finish) 267 | edev->pdata->finish(edev); 268 | 269 | mutex_unlock(&edev->lock); 270 | 271 | /* erase/write disable */ 272 | eeprom_93xx46_ew(edev, 0); 273 | return ret; 274 | } 275 | 276 | static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) 277 | { 278 | struct eeprom_93xx46_platform_data *pd = edev->pdata; 279 | struct spi_message m; 280 | struct spi_transfer t; 281 | int bits, ret; 282 | u16 cmd_addr; 283 | 284 | cmd_addr = OP_START << edev->addrlen; 285 | if (edev->addrlen == 7) { 286 | cmd_addr |= ADDR_ERAL << 1; 287 | bits = 10; 288 | } else { 289 | cmd_addr |= ADDR_ERAL; 290 | bits = 9; 291 | } 292 | 293 | if (has_quirk_instruction_length(edev)) { 294 | cmd_addr <<= 2; 295 | bits += 2; 296 | } 297 | 298 | dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits); 299 | 300 | spi_message_init(&m); 301 | memset(&t, 0, sizeof(t)); 302 | 303 | t.tx_buf = &cmd_addr; 304 | t.len = 2; 305 | t.bits_per_word = bits; 306 | spi_message_add_tail(&t, &m); 307 | 308 | mutex_lock(&edev->lock); 309 | 310 | if (edev->pdata->prepare) 311 | edev->pdata->prepare(edev); 312 | 313 | ret = spi_sync(edev->spi, &m); 314 | if (ret) 315 | dev_err(&edev->spi->dev, "erase error %d\n", ret); 316 | /* have to wait erase cycle time Tec ms */ 317 | mdelay(6); 318 | 319 | if (pd->finish) 320 | pd->finish(edev); 321 | 322 | mutex_unlock(&edev->lock); 323 | return ret; 324 | } 325 | 326 | static ssize_t eeprom_93xx46_store_erase(struct device *dev, 327 | struct device_attribute *attr, 328 | const char *buf, size_t count) 329 | { 330 | struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev); 331 | int erase = 0, ret; 332 | 333 | sscanf(buf, "%d", &erase); 334 | if (erase) { 335 | ret = eeprom_93xx46_ew(edev, 1); 336 | if (ret) 337 | return ret; 338 | ret = eeprom_93xx46_eral(edev); 339 | if (ret) 340 | return ret; 341 | ret = eeprom_93xx46_ew(edev, 0); 342 | if (ret) 343 | return ret; 344 | } 345 | return count; 346 | } 347 | static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); 348 | 349 | static void select_assert(void *context) 350 | { 351 | struct eeprom_93xx46_dev *edev = context; 352 | 353 | gpiod_set_value_cansleep(edev->pdata->select, 1); 354 | } 355 | 356 | static void select_deassert(void *context) 357 | { 358 | struct eeprom_93xx46_dev *edev = context; 359 | 360 | gpiod_set_value_cansleep(edev->pdata->select, 0); 361 | } 362 | 363 | static const struct of_device_id eeprom_93xx46_of_table[] = { 364 | { .compatible = "eeprom-93xx46", }, 365 | { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, 366 | {} 367 | }; 368 | MODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); 369 | 370 | static int eeprom_93xx46_probe_dt(struct spi_device *spi) 371 | { 372 | const struct of_device_id *of_id = 373 | of_match_device(eeprom_93xx46_of_table, &spi->dev); 374 | struct device_node *np = spi->dev.of_node; 375 | struct eeprom_93xx46_platform_data *pd; 376 | u32 tmp; 377 | int ret; 378 | 379 | pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); 380 | if (!pd) 381 | return -ENOMEM; 382 | 383 | ret = of_property_read_u32(np, "data-size", &tmp); 384 | if (ret < 0) { 385 | dev_err(&spi->dev, "data-size property not found\n"); 386 | return ret; 387 | } 388 | 389 | if (tmp == 8) { 390 | pd->flags |= EE_ADDR8; 391 | } else if (tmp == 16) { 392 | pd->flags |= EE_ADDR16; 393 | } else { 394 | dev_err(&spi->dev, "invalid data-size (%d)\n", tmp); 395 | return -EINVAL; 396 | } 397 | 398 | if (of_property_read_bool(np, "read-only")) 399 | pd->flags |= EE_READONLY; 400 | 401 | pd->select = devm_gpiod_get_optional(&spi->dev, "select", 402 | GPIOD_OUT_LOW); 403 | if (IS_ERR(pd->select)) 404 | return PTR_ERR(pd->select); 405 | 406 | pd->prepare = select_assert; 407 | pd->finish = select_deassert; 408 | gpiod_direction_output(pd->select, 0); 409 | 410 | if (of_id->data) { 411 | const struct eeprom_93xx46_devtype_data *data = of_id->data; 412 | 413 | pd->quirks = data->quirks; 414 | } 415 | 416 | spi->dev.platform_data = pd; 417 | 418 | return 0; 419 | } 420 | 421 | static int eeprom_93xx46_probe(struct spi_device *spi) 422 | { 423 | struct eeprom_93xx46_platform_data *pd; 424 | struct eeprom_93xx46_dev *edev; 425 | int err; 426 | 427 | if (spi->dev.of_node) { 428 | err = eeprom_93xx46_probe_dt(spi); 429 | if (err < 0) 430 | return err; 431 | } 432 | 433 | pd = spi->dev.platform_data; 434 | if (!pd) { 435 | dev_err(&spi->dev, "missing platform data\n"); 436 | return -ENODEV; 437 | } 438 | 439 | edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL); 440 | if (!edev) 441 | return -ENOMEM; 442 | 443 | if (pd->flags & EE_ADDR8) 444 | edev->addrlen = 7; 445 | else if (pd->flags & EE_ADDR16) 446 | edev->addrlen = 6; 447 | else { 448 | dev_err(&spi->dev, "unspecified address type\n"); 449 | return -EINVAL; 450 | } 451 | 452 | mutex_init(&edev->lock); 453 | 454 | edev->spi = spi; 455 | edev->pdata = pd; 456 | 457 | edev->size = 128; 458 | edev->nvmem_config.type = NVMEM_TYPE_EEPROM; 459 | edev->nvmem_config.name = dev_name(&spi->dev); 460 | edev->nvmem_config.dev = &spi->dev; 461 | edev->nvmem_config.read_only = pd->flags & EE_READONLY; 462 | edev->nvmem_config.root_only = true; 463 | edev->nvmem_config.owner = THIS_MODULE; 464 | edev->nvmem_config.compat = true; 465 | edev->nvmem_config.base_dev = &spi->dev; 466 | edev->nvmem_config.reg_read = eeprom_93xx46_read; 467 | edev->nvmem_config.reg_write = eeprom_93xx46_write; 468 | edev->nvmem_config.priv = edev; 469 | edev->nvmem_config.stride = 4; 470 | edev->nvmem_config.word_size = 1; 471 | edev->nvmem_config.size = edev->size; 472 | 473 | edev->nvmem = devm_nvmem_register(&spi->dev, &edev->nvmem_config); 474 | if (IS_ERR(edev->nvmem)) 475 | return PTR_ERR(edev->nvmem); 476 | 477 | dev_info(&spi->dev, "%d-bit eeprom %s\n", 478 | (pd->flags & EE_ADDR8) ? 8 : 16, 479 | (pd->flags & EE_READONLY) ? "(readonly)" : ""); 480 | 481 | if (!(pd->flags & EE_READONLY)) { 482 | if (device_create_file(&spi->dev, &dev_attr_erase)) 483 | dev_err(&spi->dev, "can't create erase interface\n"); 484 | } 485 | 486 | spi_set_drvdata(spi, edev); 487 | return 0; 488 | } 489 | 490 | static int eeprom_93xx46_remove(struct spi_device *spi) 491 | { 492 | struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi); 493 | 494 | if (!(edev->pdata->flags & EE_READONLY)) 495 | device_remove_file(&spi->dev, &dev_attr_erase); 496 | 497 | return 0; 498 | } 499 | 500 | static struct spi_driver eeprom_93xx46_driver = { 501 | .driver = { 502 | .name = "93xx46", 503 | .of_match_table = of_match_ptr(eeprom_93xx46_of_table), 504 | }, 505 | .probe = eeprom_93xx46_probe, 506 | .remove = eeprom_93xx46_remove, 507 | }; 508 | 509 | module_spi_driver(eeprom_93xx46_driver); 510 | 511 | MODULE_LICENSE("GPL"); 512 | MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs"); 513 | MODULE_AUTHOR("Anatolij Gustschin "); 514 | MODULE_ALIAS("spi:93xx46"); 515 | -------------------------------------------------------------------------------- /Chapter09/eeprom_93xx46.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Module: eeprom_93xx46 4 | * platform description for 93xx46 EEPROMs. 5 | */ 6 | #include 7 | 8 | struct eeprom_93xx46_platform_data { 9 | unsigned char flags; 10 | #define EE_ADDR8 0x01 /* 8 bit addr. cfg */ 11 | #define EE_ADDR16 0x02 /* 16 bit addr. cfg */ 12 | #define EE_READONLY 0x08 /* forbid writing */ 13 | 14 | unsigned int quirks; 15 | /* Single word read transfers only; no sequential read. */ 16 | #define EEPROM_93XX46_QUIRK_SINGLE_WORD_READ (1 << 0) 17 | /* Instructions such as EWEN are (addrlen + 2) in length. */ 18 | #define EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH (1 << 1) 19 | 20 | /* 21 | * optional hooks to control additional logic 22 | * before and after spi transfer. 23 | */ 24 | void (*prepare)(void *); 25 | void (*finish)(void *); 26 | struct gpio_desc *select; 27 | }; 28 | -------------------------------------------------------------------------------- /Chapter10/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := kmalloc.o vmalloc.o vma_list.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter10/kmalloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void *ptr; 7 | 8 | static int alloc_init(void) 9 | { 10 | size_t size = 1024; /* allocate 1024 bytes */ 11 | ptr = kmalloc(size,GFP_KERNEL); 12 | 13 | if(!ptr) { 14 | /* handle error */ 15 | pr_err("memory allocation failed\n"); 16 | return -ENOMEM; 17 | } else { 18 | pr_info("Memory allocated successfully\n"); 19 | } 20 | 21 | return 0; 22 | } 23 | 24 | static void alloc_exit(void) 25 | { 26 | kfree(ptr); 27 | pr_info("Memory freed\n"); 28 | } 29 | 30 | module_init(alloc_init); 31 | module_exit(alloc_exit); 32 | MODULE_LICENSE("GPL"); 33 | MODULE_AUTHOR("John Madieu "); 34 | -------------------------------------------------------------------------------- /Chapter10/vma_list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static int pid_mem = 1; 9 | module_param(pid_mem, int, S_IRUGO); 10 | MODULE_PARM_DESC(pid_mem, "PID of the process for which one need to list VMAs"); 11 | 12 | static void print_mem(struct task_struct *task) 13 | { 14 | struct mm_struct *mm; 15 | struct vm_area_struct *vma; 16 | int count = 0; 17 | mm = task->mm; 18 | pr_info("\nThis mm_struct has %d vmas.\n", mm->map_count); 19 | for (vma = mm->mmap ; vma ; vma = vma->vm_next) { 20 | printk ("\nVma number %d: \n", ++count); 21 | printk(" Starts at 0x%lx, Ends at 0x%lx\n", 22 | vma->vm_start, vma->vm_end); 23 | } 24 | pr_info("\nCode Segment start = 0x%lx, end = 0x%lx \n" 25 | "Data Segment start = 0x%lx, end = 0x%lx\n" 26 | "Stack Segment start = 0x%lx\n", 27 | mm->start_code, mm->end_code, 28 | mm->start_data, mm->end_data, 29 | mm->start_stack); 30 | } 31 | 32 | static int mm_exp_load(void){ 33 | struct task_struct *task; 34 | pr_info("\nGot the process id to look up as %d.\n", pid_mem); 35 | for_each_process(task) { 36 | if ( task->pid == pid_mem) { 37 | printk("%s[%d]\n", task->comm, task->pid); 38 | print_mem(task); 39 | } 40 | } 41 | return 0; 42 | } 43 | 44 | static void mm_exp_unload(void) 45 | { 46 | printk("\nPrint segment information module exiting.\n"); 47 | } 48 | 49 | module_init(mm_exp_load); 50 | module_exit(mm_exp_unload); 51 | 52 | MODULE_AUTHOR ("John Madieu "); 53 | MODULE_DESCRIPTION ("Print segment information"); 54 | MODULE_LICENSE("GPL"); 55 | -------------------------------------------------------------------------------- /Chapter10/vmalloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void *ptr; 6 | 7 | static int my_vmalloc_init(void) 8 | { 9 | unsigned long size = 8192; 10 | ptr = vmalloc(size); 11 | if (!ptr) { 12 | /* handle error */ 13 | pr_err("memory allocation failed\n"); 14 | return -ENOMEM; 15 | } else { 16 | pr_info("Memory allocated successfully\n"); 17 | } 18 | return 0; 19 | } 20 | 21 | static void my_vmalloc_exit(void) 22 | { 23 | vfree(ptr); //free the allocated memory 24 | pr_info("Memory freed\n"); 25 | } 26 | 27 | module_init(my_vmalloc_init); 28 | module_exit(my_vmalloc_exit); 29 | MODULE_LICENSE("GPL"); 30 | MODULE_AUTHOR("john Madieu "); 31 | -------------------------------------------------------------------------------- /Chapter11/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := dma-single-buffer.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter11/README.md: -------------------------------------------------------------------------------- 1 | # DMA driver test 2 | 3 | There are two drivers example in this chapter. After running `make` command, 4 | there will be two modules: 5 | 6 | * imx-sdma-scatter-gather.ko 7 | * imx-sdma-single.ko 8 | 9 | The fist module implements the scatter/gather DMA, and the second one implement 10 | a single buffer mapping. Both rely on i.MX6 from NXP. 11 | These two modules are mutally excluse. These can't be load at the same time. 12 | Prior to load one module, make sure the other one is not loaded. 13 | 14 | Whatever module is loaded, it will create a character device, `/dev/sdma_test`. 15 | 16 | ```bash 17 | # udevadm info /dev/dma_test 18 | P: /devices/virtual/dma_test/sdma_test 19 | N: sdma_test 20 | E: DEVNAME=/dev/dma_test 21 | E: DEVPATH=/devices/virtual/dma_test/dma_test 22 | E: MAJOR=244 23 | E: MINOR=0 24 | E: SUBSYSTEM=sdma_test 25 | ``` 26 | 27 | For testing purpose, one just has to write a dummy string into it, and then 28 | read anything from it. 29 | 30 | ## Single mapping DMA 31 | 32 | Below is what one can do for testing the single buffer mapping module: 33 | 34 | ```bash 35 | # insmod dma-single-buffer.ko 36 | [ 315.517196] DMA-TEST: DMA test major number = 234 37 | [ 315.524313] DMA-TEST: DMA test Driver Module loaded 38 | # echo "" > /dev/dma_test 39 | SDMA test major number = 244 40 | SDMA test Driver Module loaded 41 | [ 317.928507] DMA-TEST: Initializing buffer 42 | [ 317.932618] DMA-TEST: Dumping WBUF initialized buffer 43 | [ 317.937780] DMA-TEST: [0000] 56 56 56 56 56 56 56 56 44 | [ 317.942850] DMA-TEST: [0008] 56 56 56 56 56 56 56 56 45 | [...] 46 | [ 323.106945] DMA-TEST: [8176] 56 56 56 56 56 56 56 56 47 | [ 323.111999] DMA-TEST: [8184] 56 56 56 56 56 56 56 56 48 | [ 323.117055] DMA-TEST: Buffer initialized 49 | [ 323.121141] DMA-TEST: Got DMA channel 3 50 | [ 323.125058] DMA-TEST: DMA channel configured 51 | [ 323.129412] DMA-TEST: DMA mappings created 52 | [ 323.133634] DMA-TEST: Got this cookie: 2 53 | [ 323.137634] DMA-TEST: waiting for DMA transaction... 54 | [ 323.137781] DMA-TEST: in dma_m2m_callback 55 | [ 323.146783] DMA-TEST: Checking if DMA succeed ... 56 | [ 323.151600] DMA-TEST: buffer copy passed! 57 | [ 323.155706] DMA-TEST: Dumping RBUF DMA buffer 58 | [ 323.160148] DMA-TEST: [0000] 56 56 56 56 56 56 56 56 59 | [ 323.165207] DMA-TEST: [0008] 56 56 56 56 56 56 56 56 60 | [ 323.170262] DMA-TEST: [0016] 56 56 56 56 56 56 56 56 61 | [...] 62 | buffer copy passed! 63 | 64 | # rmmod dma-single-buffer.ko 65 | ``` 66 | -------------------------------------------------------------------------------- /Chapter11/dma-single-buffer.c: -------------------------------------------------------------------------------- 1 | #define pr_fmt(fmt) "DMA-TEST: " fmt 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static u32 *wbuf; 15 | static u32 *rbuf; 16 | static int dma_result; 17 | static int gMajor; /* major number of device */ 18 | 19 | static struct class *dma_test_class; 20 | static struct completion dma_m2m_ok; 21 | static struct dma_chan *dma_m2m_chan; 22 | 23 | static void dev_release(struct device *dev) 24 | { 25 | pr_info( "releasing dma capable device\n"); 26 | } 27 | 28 | // static u64 _dma_mask = DMA_BIT_MASK(64); 29 | static struct device dev = { 30 | .release = dev_release, 31 | .coherent_dma_mask = ~0, /* dma_alloc_coherent(): allow any address */ 32 | .dma_mask = &dev.coherent_dma_mask, /* other APIs: use the same mask as coherent */ 33 | }; 34 | 35 | /* could have been st with dma_set_mask_and_coherent() */ 36 | 37 | /* we need page aligned buffers */ 38 | #define DMA_BUF_SIZE 2 * PAGE_SIZE 39 | 40 | int dma_open(struct inode * inode, struct file * filp) 41 | { 42 | init_completion(&dma_m2m_ok); 43 | 44 | wbuf = kzalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA); 45 | if(!wbuf) { 46 | pr_err("Failed to allocate wbuf!\n"); 47 | return -ENOMEM; 48 | } 49 | 50 | rbuf = kzalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA); 51 | if(!rbuf) { 52 | kfree(wbuf); 53 | pr_err("Failed to allocate rbuf!\n"); 54 | return -ENOMEM; 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | int dma_release(struct inode * inode, struct file * filp) 61 | { 62 | kfree(wbuf); 63 | kfree(rbuf); 64 | return 0; 65 | } 66 | 67 | /* remove the #if ... #endif to have data dumped in the dmesg buffer */ 68 | __maybe_unused static void data_dump(const char *prefix, u8 *d, size_t count) 69 | { 70 | #if 0 71 | size_t i; 72 | 73 | pr_info("Dumping %s\n", prefix); 74 | for (i = 0; i < count; i += 8) 75 | pr_info( 76 | "[%04d] %02x %02x %02x %02x %02x %02x %02x %02x\n", 77 | i, d[i], d[i + 1], d[i + 2], d[i + 3], 78 | d[i + 4], d[i + 5], d[i + 6], d[i + 7]); 79 | #endif 80 | } 81 | 82 | ssize_t dma_read (struct file *filp, char __user * buf, size_t count, 83 | loff_t * offset) 84 | { 85 | pr_info("DMA result: %d!\n", dma_result); 86 | return 0; 87 | } 88 | 89 | static void dma_m2m_callback(void *data) 90 | { 91 | pr_info("in %s\n",__func__); 92 | 93 | complete(&dma_m2m_ok); 94 | } 95 | 96 | ssize_t dma_write(struct file * filp, const char __user * buf, size_t count, 97 | loff_t * offset) 98 | { 99 | u32 *index, i; 100 | size_t err = count; 101 | dma_cookie_t cookie; 102 | dma_cap_mask_t dma_m2m_mask; 103 | dma_addr_t dma_src, dma_dst; 104 | struct dma_slave_config dma_m2m_config = {0}; 105 | struct dma_async_tx_descriptor *dma_m2m_desc; 106 | 107 | pr_info("Initializing buffer\n"); 108 | index = wbuf; 109 | for (i = 0; i < DMA_BUF_SIZE/4; i++) { 110 | *(index + i) = 0x56565656; 111 | } 112 | data_dump("WBUF initialized buffer", (u8*)wbuf, DMA_BUF_SIZE); 113 | pr_info("Buffer initialized\n"); 114 | 115 | /* 1- Initialize capabilities and request a DMA channel */ 116 | dma_cap_zero(dma_m2m_mask); 117 | dma_cap_set(DMA_MEMCPY, dma_m2m_mask); 118 | /* dma_m2m_chan = dma_request_channel(dma_m2m_mask, NULL, NULL); */ 119 | dma_m2m_chan = dma_request_chan_by_mask(&dma_m2m_mask); 120 | if (!dma_m2m_chan) { 121 | pr_err("Error requesting the DMA memory to memory channel\n"); 122 | return -EINVAL; 123 | } else { 124 | pr_info("Got DMA channel %d\n", dma_m2m_chan->chan_id); 125 | } 126 | 127 | /* 2- Set slave and controller specific parameters */ 128 | dma_m2m_config.direction = DMA_MEM_TO_MEM; 129 | dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 130 | dmaengine_slave_config(dma_m2m_chan, &dma_m2m_config); 131 | pr_info("DMA channel configured\n"); 132 | 133 | dma_src = dma_map_single(&dev, wbuf, DMA_BUF_SIZE, DMA_TO_DEVICE); 134 | if (dma_mapping_error(&dev, dma_src)) { 135 | pr_err("Could not map src buffer\n"); 136 | err = -ENOMEM; 137 | goto channel_release; 138 | } 139 | dma_dst = dma_map_single(&dev, rbuf, DMA_BUF_SIZE, DMA_FROM_DEVICE); 140 | if (dma_mapping_error(&dev, dma_dst)) { 141 | pr_err("Could not map dst buffer\n"); 142 | dma_unmap_single(&dev, dma_src, DMA_BUF_SIZE, DMA_TO_DEVICE); 143 | err = -ENOMEM; 144 | goto channel_release; 145 | } 146 | pr_info("DMA mappings created\n"); 147 | 148 | /* 3- Get a descriptor for the transaction. */ 149 | dma_m2m_desc = 150 | dma_m2m_chan->device->device_prep_dma_memcpy( 151 | dma_m2m_chan, dma_dst, dma_src, DMA_BUF_SIZE, 0); 152 | if (!dma_m2m_desc) { 153 | pr_err("error in prep_dma_sg\n"); 154 | err = -EINVAL; 155 | goto dma_unmap; 156 | } 157 | dma_m2m_desc->callback = dma_m2m_callback; 158 | 159 | /* 4- Submit the transaction */ 160 | cookie = dmaengine_submit(dma_m2m_desc); 161 | if (dma_submit_error(cookie)) { 162 | pr_err("Unable to submit the DMA coockie\n"); 163 | err = -EINVAL; 164 | goto dma_unmap; 165 | } 166 | pr_info("Got this cookie: %d\n", cookie); 167 | 168 | /* 5- Issue pending DMA requests and wait for callback notification */ 169 | dma_async_issue_pending(dma_m2m_chan); 170 | pr_info("waiting for DMA transaction...\n"); 171 | 172 | /* One can use wait_for_completion_timeout() also */ 173 | wait_for_completion(&dma_m2m_ok); 174 | 175 | dma_unmap: 176 | /* we do not care about the source anymore */ 177 | dma_unmap_single(&dev, dma_src, DMA_BUF_SIZE, DMA_TO_DEVICE); 178 | /* unmap the DMA memory destination for CPU access. This will sync the buffer */ 179 | dma_unmap_single(&dev, dma_dst, DMA_BUF_SIZE, DMA_FROM_DEVICE); 180 | 181 | /* 182 | * if no error occured, then we are safe to access the buffer. 183 | * remember, the buffer must be synced first, and dma_unmap_single() did it. 184 | */ 185 | if (err >= 0) { 186 | pr_info("Checking if DMA succeed ...\n"); 187 | 188 | for (i = 0; i < DMA_BUF_SIZE/4; i++) { 189 | if (*(rbuf+i) != *(wbuf+i)) { 190 | pr_err("Single DMA buffer copy falled!,r=%x,w=%x,%d\n", 191 | *(rbuf+i), *(wbuf+i), i); 192 | return err; 193 | } 194 | } 195 | 196 | pr_info("buffer copy passed!\n"); 197 | dma_result = 1; 198 | data_dump("RBUF DMA buffer", (u8*)rbuf, DMA_BUF_SIZE); 199 | } 200 | 201 | channel_release: 202 | dma_release_channel(dma_m2m_chan); 203 | dma_m2m_chan = NULL; 204 | 205 | return err; 206 | } 207 | 208 | struct file_operations dma_fops = { 209 | .open = dma_open, 210 | .read = dma_read, 211 | .write = dma_write, 212 | .release = dma_release, 213 | }; 214 | 215 | int __init dma_init_module(void) 216 | { 217 | int error; 218 | struct device *dma_test_dev; 219 | 220 | /* register a character device */ 221 | error = register_chrdev(0, "dma_test", &dma_fops); 222 | if (error < 0) { 223 | pr_err("DMA test driver can't get major number\n"); 224 | return error; 225 | } 226 | gMajor = error; 227 | pr_info("DMA test major number = %d\n",gMajor); 228 | 229 | dma_test_class = class_create(THIS_MODULE, "dma_test"); 230 | if (IS_ERR(dma_test_class)) { 231 | pr_err("Error creating dma test module class.\n"); 232 | unregister_chrdev(gMajor, "dma_test"); 233 | return PTR_ERR(dma_test_class); 234 | } 235 | 236 | dma_test_dev = device_create(dma_test_class, NULL, 237 | MKDEV(gMajor, 0), NULL, "dma_test"); 238 | 239 | if (IS_ERR(dma_test_dev)) { 240 | pr_err("Error creating dma test class device.\n"); 241 | class_destroy(dma_test_class); 242 | unregister_chrdev(gMajor, "dma_test"); 243 | return -1; 244 | } 245 | 246 | dev_set_name(&dev, "dma-test-dev"); 247 | device_register(&dev); 248 | 249 | pr_info("DMA test Driver Module loaded\n"); 250 | return 0; 251 | } 252 | 253 | static void dma_cleanup_module(void) 254 | { 255 | unregister_chrdev(gMajor, "dma_test"); 256 | device_destroy(dma_test_class, MKDEV(gMajor, 0)); 257 | class_destroy(dma_test_class); 258 | device_unregister(&dev); 259 | 260 | pr_info("DMA test Driver Module Unloaded\n"); 261 | } 262 | 263 | module_init(dma_init_module); 264 | module_exit(dma_cleanup_module); 265 | 266 | MODULE_AUTHOR("John Madieu "); 267 | MODULE_DESCRIPTION("DMA test driver"); 268 | MODULE_LICENSE("GPL"); 269 | -------------------------------------------------------------------------------- /Chapter14/sysfs-kobj-and-group/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := sysfs-group.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter14/sysfs-kobj-and-group/sysfs-group.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static int foo = 0; 9 | static int bar = 0; 10 | 11 | static ssize_t demo_show(struct kobject *kobj, struct kobj_attribute *attr, 12 | char *buf) 13 | { 14 | size_t len = 0; 15 | int val = 0; 16 | 17 | len = scnprintf(buf, PAGE_SIZE, 18 | "%s:\t", attr->attr.name); 19 | 20 | if (strcmp(attr->attr.name, "foo") == 0) 21 | val = foo; 22 | else 23 | val = bar; 24 | 25 | len += scnprintf(buf + len, PAGE_SIZE - len, "%d\n", val); 26 | 27 | return len; 28 | } 29 | 30 | static ssize_t demo_store(struct kobject *kobj, struct kobj_attribute *attr, 31 | const char *buf, size_t len) 32 | { 33 | int val; 34 | 35 | if (!len || !sscanf(buf, "%d", &val)) 36 | return -EINVAL; 37 | 38 | if (strcmp(attr->attr.name, "foo") == 0) 39 | foo = val; 40 | else 41 | bar = val; 42 | 43 | return len; 44 | } 45 | 46 | static struct kobj_attribute foo_attr = 47 | __ATTR(foo, 0660, demo_show, demo_store); 48 | static struct kobj_attribute bar_attr = 49 | __ATTR(bar, 0660, demo_show, demo_store); 50 | 51 | /* attrs is an array of pointers to attributes */ 52 | static struct attribute *demo_attrs[] = { 53 | &foo_attr.attr, 54 | &bar_attr.attr, 55 | NULL,}; 56 | 57 | static struct attribute_group my_attr_group = { 58 | .attrs = demo_attrs, 59 | /*.bin_attrs = demo_bin_attrs,*/ 60 | }; 61 | 62 | static struct kobject *demo_kobj; 63 | 64 | static int __init hello_module_init(void) 65 | { 66 | int err = -1; 67 | pr_info("sysfs group and kobject test: init\n"); 68 | 69 | /* 70 | * Create a simple kobject with the name of "packt", 71 | * located under /sys/kernel/ 72 | */ 73 | demo_kobj = kobject_create_and_add("packt", kernel_kobj); 74 | if (!demo_kobj) 75 | return -ENOMEM; 76 | 77 | err = sysfs_create_group(demo_kobj, &my_attr_group); 78 | if (err) 79 | kobject_put(demo_kobj); 80 | 81 | return err; 82 | } 83 | 84 | static void __exit hello_module_exit(void) 85 | { 86 | if (demo_kobj) 87 | kobject_put(demo_kobj); 88 | 89 | pr_info("sysfs group and kobject test: exit\n"); 90 | } 91 | 92 | module_init(hello_module_init); 93 | module_exit(hello_module_exit); 94 | MODULE_LICENSE("GPL"); 95 | MODULE_AUTHOR("John Madieu "); 96 | -------------------------------------------------------------------------------- /Chapter14/sysfs-poll/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := sysfs-poll.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter14/sysfs-poll/README.md: -------------------------------------------------------------------------------- 1 | # Pollable sysfs attributes 2 | 3 | After running `make` command, there will be one module: 4 | 5 | * sysfs-poll.ko 6 | 7 | After loading the module, this will create two sysfs attributes in `/sys/hello`: 8 | 9 | * /sys/hello/trigger 10 | * /sys/hello/notify 11 | 12 | ```bash 13 | # insmod ./sysfs-poll.ko 14 | 15 | # ls -l /sys/hello/ 16 | total 0 17 | -rw-r--r-- 1 root root 4096 oct. 13 11:54 notify 18 | -rw-r--r-- 1 root root 4096 oct. 13 11:54 trigger 19 | ``` 20 | 21 | For testing, one can build either compile either the file `sysfs-select-user.c` 22 | or `sysfs-poll-user.c`. 23 | 24 | ```bash 25 | $ gcc sysfs-select-user.c -o sysfs-select 26 | ``` 27 | Now one should execute sysfs-select binary as sudo: 28 | 29 | ```bash 30 | # sudo ./sysfs-select 31 | ``` 32 | 33 | Open another console, and then write anything into either `/sys/hello/trigger` 34 | or `/sys/hello/notify`. 35 | 36 | ```bash 37 | # echo "john" > /sys/hello/trigger 38 | ``` 39 | The app wainting will then print something like: 40 | 41 | ```bash 42 | # sudo ./sysfs-select 43 | Change detected in /sys/hello/trigger 44 | ``` 45 | Same for writing in `/sys/hello/notify` (in console 1): 46 | 47 | ```bash 48 | # echo "john" > /sys/hello/notify 49 | ``` 50 | which will produce below output (in console 2): 51 | 52 | ```bash 53 | # ./sysfs-select 54 | Change detected in /sys/hello/notify 55 | ``` 56 | 57 | Additionally, one can use `dmesg` command for debug messages. 58 | -------------------------------------------------------------------------------- /Chapter14/sysfs-poll/sysfs-poll-user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define TEST_SYSFS_TRIGGER "/sys/hello/trigger" 12 | #define TEST_SYSFS_NOTIFY "/sys/hello/notify" 13 | 14 | int main(int argc, char **argv) 15 | { 16 | int cnt, notify_fd, trigger_fd, rv; 17 | char attrData[100]; 18 | struct pollfd ufds[2]; 19 | 20 | if ((notify_fd = open(TEST_SYSFS_NOTIFY, O_RDWR)) < 0){ 21 | perror("Unable to open notify"); 22 | exit(1); 23 | } 24 | 25 | if ((trigger_fd = open(TEST_SYSFS_TRIGGER, O_RDWR)) < 0) { 26 | perror("Unable to open trigger"); 27 | exit(1); 28 | } 29 | 30 | ufds[0].fd = notify_fd; 31 | ufds[0].events = POLLPRI|POLLERR; 32 | ufds[1].fd = trigger_fd; 33 | ufds[1].events = POLLPRI|POLLERR; 34 | 35 | cnt = read( notify_fd, attrData, 100 ); 36 | cnt = read( trigger_fd, attrData, 100 ); 37 | ufds[0].revents = 0; 38 | ufds[1].revents = 0; 39 | 40 | if (( rv = poll( ufds, 2, 1000000)) < 0 ) { 41 | perror("poll error"); 42 | else if (rv == 0) 43 | printf("Timeout occurred!\n"); 44 | else 45 | printf("triggered\n"); 46 | 47 | printf( "revents[0]: %08X\n", ufds[0].revents ); 48 | printf( "revents[1]: %08X\n", ufds[1].revents ); 49 | close( trigger_fd ); 50 | close( notify_fd ); 51 | } 52 | -------------------------------------------------------------------------------- /Chapter14/sysfs-poll/sysfs-poll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct d_attr { 9 | struct attribute attr; 10 | int value; /* This is our data */ 11 | }; 12 | 13 | static struct d_attr notify = { 14 | .attr.name="notify", 15 | .attr.mode = 0644, 16 | .value = 0, 17 | }; 18 | 19 | static struct d_attr trigger = { 20 | .attr.name="trigger", 21 | .attr.mode = 0644, 22 | .value = 0, 23 | }; 24 | 25 | static struct attribute * d_attrs[] = { 26 | ¬ify.attr, 27 | &trigger.attr, 28 | NULL 29 | }; 30 | 31 | static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) 32 | { 33 | struct d_attr *da = container_of(attr, struct d_attr, attr); 34 | pr_info( "hello: show called (%s)\n", da->attr.name ); 35 | return scnprintf(buf, PAGE_SIZE, "%s: %d\n", da->attr.name, da->value); 36 | } 37 | static struct kobject *mykobj; 38 | 39 | static ssize_t store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) 40 | { 41 | struct d_attr *da = container_of(attr, struct d_attr, attr); 42 | 43 | sscanf(buf, "%d", &da->value); 44 | pr_info("sysfs_notify store %s = %d\n", da->attr.name, da->value); 45 | 46 | if (strcmp(da->attr.name, "notify") == 0){ 47 | notify.value = da->value; 48 | sysfs_notify(mykobj, NULL, "notify"); 49 | } 50 | else if(strcmp(da->attr.name, "trigger") == 0){ 51 | trigger.value = da->value; 52 | sysfs_notify(mykobj, NULL, "trigger"); 53 | } 54 | return sizeof(int); 55 | } 56 | 57 | static struct sysfs_ops s_ops = { 58 | .show = show, 59 | .store = store, 60 | }; 61 | 62 | static struct kobj_type k_type = { 63 | .sysfs_ops = &s_ops, 64 | .default_attrs = d_attrs, 65 | }; 66 | 67 | static struct kobject *mykobj; 68 | static int __init hello_module_init(void) 69 | { 70 | int err = -1; 71 | pr_info("Pollable sysfs hello: init\n"); 72 | mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL); 73 | /* mykobj = kobject_create() is not exported */ 74 | if (mykobj) { 75 | kobject_init(mykobj, &k_type); 76 | if (kobject_add(mykobj, NULL, "%s", "hello")) { 77 | err = -1; 78 | pr_info("Hello: kobject_add() failed\n"); 79 | kobject_put(mykobj); 80 | mykobj = NULL; 81 | } 82 | err = 0; 83 | } 84 | return err; 85 | } 86 | 87 | static void __exit hello_module_exit(void) 88 | { 89 | if (mykobj) { 90 | kobject_put(mykobj); 91 | kfree(mykobj); 92 | } 93 | pr_info("Pollable sysfs hello: exit\n"); 94 | } 95 | 96 | module_init(hello_module_init); 97 | module_exit(hello_module_exit); 98 | MODULE_LICENSE("GPL"); 99 | MODULE_AUTHOR("John Madieu "); 100 | -------------------------------------------------------------------------------- /Chapter14/sysfs-poll/sysfs-select-user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define TEST_SYSFS_TRIGGER "/sys/hello/trigger" 12 | #define TEST_SYSFS_NOTIFY "/sys/hello/notify" 13 | 14 | int main (void) 15 | { 16 | 17 | fd_set read_fds; 18 | int i, max_fd, ret, cnt; 19 | int notify_fd, trigger_fd; 20 | char attrData[100]; 21 | FD_ZERO (&read_fds); 22 | 23 | if ((notify_fd = open(TEST_SYSFS_NOTIFY, O_RDWR)) < 0){ 24 | perror("Unable to open notify"); 25 | exit(1); 26 | } 27 | 28 | if ((trigger_fd = open(TEST_SYSFS_TRIGGER, O_RDWR)) < 0) { 29 | perror("Unable to open trigger"); 30 | exit(1); 31 | } 32 | 33 | FD_SET (notify_fd, &read_fds); 34 | FD_SET (trigger_fd, &read_fds); 35 | max_fd = notify_fd > trigger_fd ? notify_fd : trigger_fd; 36 | 37 | /* We first need to read data until the end of the file */ 38 | cnt = read( notify_fd, attrData, 100 ); 39 | cnt = read( trigger_fd, attrData, 100 ); 40 | 41 | ret = select(max_fd + 1, NULL, NULL, &read_fds, NULL); 42 | if (ret < 0) 43 | perror("pselect events"); 44 | if (FD_ISSET(notify_fd, &read_fds)) 45 | printf("Change detected in /sys/hello/notify\n"); 46 | if (FD_ISSET(trigger_fd, &read_fds)) 47 | printf("Change detected in /sys/hello/trigger\n"); 48 | 49 | close( trigger_fd ); 50 | close( notify_fd ); 51 | return 0; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /Chapter15/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := iio-ins.o iio-dummy-random.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter15/README.md: -------------------------------------------------------------------------------- 1 | # IIO driver testing 2 | 3 | 4 | After running `make` command, there will be two modules: 5 | 6 | * iio-dummy-random.ko 7 | * iio-ins.ko 8 | 9 | The fist module is our dummy IIO driver, for a fake device, which exposes four 10 | channels. The second one is a basic module whose aim is to create a platform 11 | device that will match the `iio-dummy-random` driver. That, one can also declre 12 | the device in the device tree. 13 | 14 | First, one should load the following modules: 15 | 16 | ```bash 17 | # iio-dummy-random.ko 18 | # iio-ins.ko 19 | ``` 20 | 21 | Since the board on which I tested this driver had lready three IIO devices 22 | (device0, device1, and device2), after loading our test drivers, another 23 | IIO device (device3) will be added to the system: 24 | 25 | On can check this through the sysfs interface: 26 | 27 | ```bash 28 | ~# ls -l /sys/bus/iio/devices/ 29 | [...] 30 | lrwxrwxrwx 1 root root 0 Jul 31 20:26 iio:device3 -> ../../../devices/platform/iio-dummy-random.0/iio:device3 31 | lrwxrwxrwx 1 root root 0 Jul 31 20:23 iio_sysfs_trigger -> ../../../devices/iio_sysfs_trigger 32 | ``` 33 | Now one knows the device exists, one can check or list the available channels 34 | like below: 35 | 36 | ```bach 37 | # ls /sys/bus/iio/devices/iio\:device3/ 38 | dev in_voltage2_raw name uevent 39 | in_voltage0_raw in_voltage3_raw power 40 | in_voltage1_raw in_voltage_scale subsystem 41 | 42 | # cat /sys/bus/iio/devices/iio:device0/name 43 | iio_dummy_random 44 | ``` 45 | 46 | In case one need to print more information, one can use `udevadm` command as 47 | following: 48 | 49 | ```bash 50 | # udevadm info /dev/iio\:device3 51 | P: /devices/platform/iio-dummy-random.0/iio:device3 52 | N: iio:device3 53 | E: DEVNAME=/dev/iio:device3 54 | E: DEVPATH=/devices/platform/iio-dummy-random.0/iio:device3 55 | E: DEVTYPE=iio_device 56 | E: MAJOR=250 57 | E: MINOR=3 58 | E: SUBSYSTEM=iio 59 | ``` 60 | -------------------------------------------------------------------------------- /Chapter15/iio-dummy-random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For platform devices */ 5 | #include /* For IRQ */ 6 | #include /* For DT*/ 7 | #include /* mandatory */ 8 | #include /* mandatory since sysfs are used */ 9 | #include /* For advanced users, to manage iio events */ 10 | #include /* mandatory to use triggered buffers */ 11 | 12 | 13 | #define FAKE_VOLTAGE_CHANNEL(num) \ 14 | { \ 15 | .type = IIO_VOLTAGE, \ 16 | .indexed = 1, \ 17 | .channel = (num), \ 18 | .address = (num), \ 19 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 20 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ 21 | } 22 | 23 | struct my_private_data { 24 | int foo; 25 | int bar; 26 | struct mutex lock; 27 | }; 28 | 29 | static int fake_read_raw(struct iio_dev *indio_dev, 30 | struct iio_chan_spec const *channel, int *val, 31 | int *val2, long mask) 32 | { 33 | return 0; 34 | } 35 | 36 | static int fake_write_raw(struct iio_dev *indio_dev, 37 | struct iio_chan_spec const *chan, 38 | int val, int val2, long mask) 39 | { 40 | return 0; 41 | } 42 | 43 | static const struct iio_chan_spec fake_channels[] = { 44 | FAKE_VOLTAGE_CHANNEL(0), 45 | FAKE_VOLTAGE_CHANNEL(1), 46 | FAKE_VOLTAGE_CHANNEL(2), 47 | FAKE_VOLTAGE_CHANNEL(3), 48 | }; 49 | 50 | static const struct of_device_id iio_dummy_ids[] = { 51 | { .compatible = "packt,iio-dummy-random", }, 52 | { /* sentinel */ } 53 | }; 54 | 55 | static const struct iio_info fake_iio_info = { 56 | .read_raw = fake_read_raw, 57 | .write_raw = fake_write_raw, 58 | }; 59 | 60 | static int my_pdrv_probe (struct platform_device *pdev) 61 | { 62 | struct iio_dev *indio_dev; 63 | struct my_private_data *data; 64 | 65 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); 66 | if (!indio_dev) { 67 | dev_err(&pdev->dev, "iio allocation failed!\n"); 68 | return -ENOMEM; 69 | } 70 | 71 | data = iio_priv(indio_dev); 72 | mutex_init(&data->lock); 73 | indio_dev->dev.parent = &pdev->dev; 74 | indio_dev->info = &fake_iio_info; 75 | indio_dev->name = KBUILD_MODNAME; 76 | indio_dev->modes = INDIO_DIRECT_MODE; 77 | indio_dev->channels = fake_channels; 78 | indio_dev->num_channels = ARRAY_SIZE(fake_channels); 79 | indio_dev->available_scan_masks = 0xF; 80 | iio_device_register(indio_dev); 81 | 82 | platform_set_drvdata(pdev, indio_dev); 83 | return 0; 84 | } 85 | 86 | static int my_pdrv_remove(struct platform_device *pdev) 87 | { 88 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); 89 | iio_device_unregister(indio_dev); 90 | return 0; 91 | } 92 | 93 | static struct platform_driver mypdrv = { 94 | .probe = my_pdrv_probe, 95 | .remove = my_pdrv_remove, 96 | .driver = { 97 | .name = "iio-dummy-random", 98 | .of_match_table = of_match_ptr(iio_dummy_ids), 99 | .owner = THIS_MODULE, 100 | }, 101 | }; 102 | module_platform_driver(mypdrv); 103 | MODULE_AUTHOR("John Madieu "); 104 | MODULE_LICENSE("GPL"); 105 | -------------------------------------------------------------------------------- /Chapter15/iio-ins.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static struct platform_device *pdev; 6 | 7 | static int __init fake_iio_add(void) 8 | { 9 | int inst_id = 0; /* instance unique ID: base address would be a good choice */ 10 | pdev = platform_device_alloc("iio-dummy-random", inst_id); 11 | platform_device_add(pdev); 12 | pr_info("iio-dummy-random added"); 13 | return 0; 14 | } 15 | 16 | static void __exit fake_iio_put(void) 17 | { 18 | pr_info("iio-dummy-random removed"); 19 | platform_device_put(pdev); 20 | } 21 | 22 | module_init(fake_iio_add); 23 | module_exit(fake_iio_put); 24 | MODULE_LICENSE("GPL"); 25 | MODULE_AUTHOR("John Madieu "); 26 | -------------------------------------------------------------------------------- /Chapter16/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := mcp23016.o fake-gpio-chip.o fake-gpio-ins.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter16/README.md: -------------------------------------------------------------------------------- 1 | # GPIO controller drivers test 2 | 3 | After running `make` command, there will be three modules: 4 | 5 | * mcp23016.ko 6 | * fake-gpio-chip.ko 7 | * fake-gpio-ins.ko 8 | 9 | The fist module is the driver for the MCP23016, which is an I2C expander. The 10 | second one is a fake gpiochip driver, for people not having such MCP chip. 11 | The goal of the third module is just to create a platform device that matches 12 | our fake-gpio-chip module. 13 | 14 | ## Fake gpiochip driver 15 | 16 | Prior to testing the fake gpio driver, one should load the following modules: 17 | 18 | ```bash 19 | # insmod fake-gpio-chip.ko 20 | # insmod fake-gpio-ins.ko 21 | ``` 22 | 23 | For testing purpose, one can list available gpiochip on the system 24 | 25 | ```bash 26 | # ls -l /sys/class/gpio/gpiochip* 27 | [...] 28 | lrwxrwxrwx 1 root root 0 Aug 13 17:30 /sys/class/gpio/gpiochip448 -> ../../devices/platform/fake-gpio-chip.0/gpio/gpiochip448 29 | [...] 30 | ``` 31 | 32 | Additionally, `udevadm` may print more informations: 33 | 34 | ```bash 35 | # udevadm info /sys/class/gpio/gpiochip448 36 | P: /devices/platform/fake-gpio-chip.0/gpio/gpiochip448 37 | E: DEVPATH=/devices/platform/fake-gpio-chip.0/gpio/gpiochip448 38 | E: SUBSYSTEM=gpio 39 | ``` 40 | 41 | Parameters defined in the drivers may be checked as below: 42 | 43 | ```bash 44 | # cat /sys/class/gpio/gpiochip448/ 45 | base device/ label ngpio power/ subsystem/ uevent 46 | # cat /sys/class/gpio/gpiochip448/ngpio 47 | 16 48 | # cat /sys/class/gpio/gpiochip448/base 49 | 448 50 | # cat /sys/class/gpio/gpiochip448/label 51 | fake-gpio-chip 52 | ``` 53 | 54 | Feel free to improve this driver, adding for example debug message when one 55 | exports GPIOs, change their directions or their values. 56 | 57 | 58 | ## MCP23016 driver test 59 | 60 | Prior to loading this module utomatically, one should declare the device 61 | in the devicetree, as the child of i2c controller to which the expander is 62 | connected. The node should look like: 63 | 64 | ```json 65 | &i2c3 { 66 | [...] 67 | 68 | expander_0: mcp23016@20 { 69 | compatible = "microchip,mcp23016"; 70 | gpio-controller; 71 | #gpio-cells = <2>; 72 | reg = <0x20>; 73 | }; 74 | }; 75 | ``` 76 | If the system is not aware of the module, one can manually load it as below: 77 | 78 | ```bash 79 | # insmod mcp23016.ko 80 | ``` 81 | 82 | After this module loaded, it creates a `/sys/class/gpio/gpiochip464` entry 83 | on my system (this may be different on yours). 84 | 85 | ```bash 86 | # udevadm info /sys/class/gpio/gpiochip464 87 | P: /devices/soc0/soc.0/2100000.aips-bus/21a8000.i2c/i2c-2/2-0026/gpio/gpiochip464 88 | E: DEVPATH=/devices/soc0/soc.0/2100000.aips-bus/21a8000.i2c/i2c-2/2-0026/gpio/gpiochip464 89 | E: SUBSYSTEM=gpio 90 | ``` 91 | Some properties of the chip can be checked as below: 92 | 93 | ```bash 94 | # ls /sys/class/gpio/gpiochip464/ 95 | base device/ label ngpio power/ subsystem/ uevent 96 | # cat /sys/class/gpio/gpiochip464/base 97 | 464 98 | # cat /sys/class/gpio/gpiochip464/label 99 | mcp23016 100 | # cat /sys/class/gpio/gpiochip464/ngpio 101 | 16 102 | */ 103 | ``` 104 | 105 | The base of this gpio controller is 464, which is global to the system. 106 | It means that, in one need to export the first (gpio0) and third (gpio2) gpios 107 | of this ontroller, on should write 464 and 464 into `/sys/class/gpio/export` 108 | file. 109 | -------------------------------------------------------------------------------- /Chapter16/fake-gpio-chip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include /* For platform devices */ 6 | #include /* For DT*/ 7 | 8 | #define GPIO_NUM 16 9 | static struct gpio_chip chip; 10 | 11 | static int fake_get_value(struct gpio_chip *gc, unsigned offset) 12 | { 13 | return 0; 14 | } 15 | 16 | static void fake_set_value(struct gpio_chip *gc, unsigned offset, int val) 17 | { 18 | } 19 | 20 | static int fake_direction_output(struct gpio_chip *gc, 21 | unsigned offset, int val) 22 | { 23 | return 0; 24 | } 25 | 26 | static int fake_direction_input(struct gpio_chip *gc, 27 | unsigned offset) 28 | { 29 | return 0; 30 | } 31 | 32 | static const struct of_device_id fake_gpiochip_ids[] = { 33 | { .compatible = "packt,fake-gpio-chip", }, 34 | { /* sentinel */ } 35 | }; 36 | 37 | static int my_pdrv_probe (struct platform_device *pdev) 38 | { 39 | chip.label = pdev->name; 40 | chip.base = -1; 41 | chip.owner = THIS_MODULE; 42 | chip.ngpio = GPIO_NUM; 43 | chip.can_sleep = 1; 44 | chip.get = fake_get_value; 45 | chip.set = fake_set_value; 46 | chip.direction_output = fake_direction_output; 47 | chip.direction_input = fake_direction_input; 48 | 49 | return gpiochip_add(&chip); 50 | } 51 | 52 | static int my_pdrv_remove(struct platform_device *pdev) 53 | { 54 | gpiochip_remove(&chip); 55 | return 0; 56 | } 57 | 58 | static struct platform_driver mypdrv = { 59 | .probe = my_pdrv_probe, 60 | .remove = my_pdrv_remove, 61 | .driver = { 62 | .name = "fake-gpio-chip", 63 | .of_match_table = of_match_ptr(fake_gpiochip_ids), 64 | .owner = THIS_MODULE, 65 | }, 66 | }; 67 | module_platform_driver(mypdrv); 68 | MODULE_AUTHOR("John Madieu "); 69 | MODULE_LICENSE("GPL"); 70 | -------------------------------------------------------------------------------- /Chapter16/fake-gpio-ins.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static struct platform_device *pdev; 6 | 7 | static int __init platform_dummy_char_add(void) 8 | { 9 | int inst_id = 0; /* instance unique ID: base address would be a good choice */ 10 | pdev = platform_device_alloc("fake-gpio-chip", inst_id); 11 | platform_device_add(pdev); 12 | pr_info("fake-gpio-chip added\n"); 13 | return 0; 14 | } 15 | 16 | static void __exit fplatform_dummy_char_put(void) 17 | { 18 | pr_info("fake-gpio-chip removed\n"); 19 | platform_device_put(pdev); 20 | } 21 | 22 | module_init(platform_dummy_char_add); 23 | module_exit(fplatform_dummy_char_put); 24 | MODULE_LICENSE("GPL"); 25 | MODULE_AUTHOR("John Madieu "); 26 | -------------------------------------------------------------------------------- /Chapter16/gpio/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := gpio-legacy-dt-module.o gpio-descriptor-module.o gpio-legacy-module.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter16/gpio/gpio-descriptor-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For platform devices */ 5 | #include /* For GPIO Descriptor interface */ 6 | #include /* For IRQ */ 7 | #include /* For DT*/ 8 | 9 | /* 10 | * Let us consider the bellow mapping 11 | * 12 | * foo_device { 13 | * compatible = "packt,gpio-descriptor-sample"; 14 | * led-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>, // red 15 | * <&gpio2 16 GPIO_ACTIVE_HIGH>, // green 16 | * 17 | * btn1-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; 18 | * btn2-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; 19 | * }; 20 | */ 21 | 22 | static struct gpio_desc *red, *green, *btn1, *btn2; 23 | static int irq; 24 | 25 | static irqreturn_t btn1_pushed_irq_handler(int irq, void *dev_id) 26 | { 27 | int state; 28 | 29 | /* read the button value and change the led state */ 30 | state = gpiod_get_value(btn2); 31 | gpiod_set_value(red, state); 32 | gpiod_set_value(green, state); 33 | 34 | pr_info("btn1 interrupt: Interrupt! btn2 state is %d)\n", state); 35 | return IRQ_HANDLED; 36 | } 37 | 38 | static const struct of_device_id gpiod_dt_ids[] = { 39 | { .compatible = "packt,gpio-descriptor-sample", }, 40 | { /* sentinel */ } 41 | }; 42 | 43 | 44 | static int my_pdrv_probe (struct platform_device *pdev) 45 | { 46 | int retval; 47 | struct device *dev = &pdev->dev; 48 | 49 | /* 50 | * We use gpiod_get/gpiod_get_index() along with the flags 51 | * in order to configure the GPIO direction and an initial 52 | * value in a single function call. 53 | * 54 | * One could have used: 55 | * red = gpiod_get_index(dev, "led", 0); 56 | * gpiod_direction_output(red, 0); 57 | */ 58 | red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_LOW); 59 | green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_LOW); 60 | 61 | /* 62 | * Configure Button GPIOs as input 63 | * 64 | * After this, one can call gpiod_set_debounce() 65 | * only if the controller has the feature 66 | * For example, to debounce a button with a delay of 200ms 67 | * gpiod_set_debounce(btn1, 200); 68 | */ 69 | btn1 = gpiod_get(dev, "btn1", GPIOD_IN); 70 | btn2 = gpiod_get(dev, "btn2", GPIOD_IN); 71 | 72 | irq = gpiod_to_irq(btn1); 73 | retval = request_threaded_irq(irq, NULL, 74 | btn1_pushed_irq_handler, 75 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, 76 | "gpio-descriptor-sample", NULL); 77 | pr_info("Hello! device probed!\n"); 78 | return 0; 79 | } 80 | 81 | static int my_pdrv_remove(struct platform_device *pdev) 82 | { 83 | free_irq(irq, NULL); 84 | gpiod_put(red); 85 | gpiod_put(green); 86 | gpiod_put(btn1); 87 | gpiod_put(btn2); 88 | pr_info("good bye reader!\n"); 89 | return 0; 90 | } 91 | 92 | static struct platform_driver mypdrv = { 93 | .probe = my_pdrv_probe, 94 | .remove = my_pdrv_remove, 95 | .driver = { 96 | .name = "gpio_descriptor_sample", 97 | .of_match_table = of_match_ptr(gpiod_dt_ids), 98 | .owner = THIS_MODULE, 99 | }, 100 | }; 101 | module_platform_driver(mypdrv); 102 | 103 | MODULE_AUTHOR("John Madieu "); 104 | MODULE_LICENSE("GPL"); 105 | -------------------------------------------------------------------------------- /Chapter16/gpio/gpio-legacy-dt-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For platform devices */ 5 | #include /* For IRQ */ 6 | #include /* For Legacy integer based GPIO */ 7 | #include /* For of_gpio* functions */ 8 | #include /* For DT*/ 9 | 10 | 11 | /* 12 | * Let us consider the node bellow 13 | * 14 | * foo_device { 15 | * compatible = "packt,gpio-legacy-sample"; 16 | * led-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>, // red 17 | * <&gpio2 16 GPIO_ACTIVE_HIGH>, // green 18 | * 19 | * btn1-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; 20 | * btn2-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; 21 | * }; 22 | */ 23 | 24 | static unsigned int gpio_red, gpio_green, gpio_btn1, gpio_btn2; 25 | static int irq; 26 | 27 | static irqreturn_t btn1_pushed_irq_handler(int irq, void *dev_id) 28 | { 29 | int state; 30 | 31 | /* read the button value and change the led state */ 32 | state = gpio_get_value(gpio_btn2); 33 | gpio_set_value(gpio_red, state); 34 | gpio_set_value(gpio_green, state); 35 | 36 | pr_info("gpio_btn1 interrupt: Interrupt! gpio_btn2 state is %d)\n", state); 37 | return IRQ_HANDLED; 38 | } 39 | 40 | static const struct of_device_id gpio_dt_ids[] = { 41 | { .compatible = "packt,gpio-legacy-sample", }, 42 | { /* sentinel */ } 43 | }; 44 | 45 | static int my_pdrv_probe (struct platform_device *pdev) 46 | { 47 | int retval; 48 | struct device_node *np = pdev->dev.of_node; 49 | 50 | if (!np) 51 | return -ENOENT; 52 | 53 | gpio_red = of_get_named_gpio(np, "led", 0); 54 | gpio_green = of_get_named_gpio(np, "led", 1); 55 | gpio_btn1 = of_get_named_gpio(np, "btn1", 0); 56 | gpio_btn2 = of_get_named_gpio(np, "btn2", 0); 57 | 58 | gpio_request(gpio_green, "green-led"); 59 | gpio_request(gpio_red, "red-led"); 60 | gpio_request(gpio_btn1, "button-1"); 61 | gpio_request(gpio_btn2, "button-2"); 62 | 63 | /* 64 | * Configure Button GPIOs as input 65 | * 66 | * After this, one can call gpio_set_debounce() 67 | * only if the controller has the feature 68 | * 69 | * For example, to debounce a button with a delay of 200ms 70 | * gpio_set_debounce(gpio_btn1, 200); 71 | */ 72 | gpio_direction_input(gpio_btn1); 73 | gpio_direction_input(gpio_btn2); 74 | 75 | /* 76 | * Set LED GPIOs as output, with their initial values set to 0 77 | */ 78 | gpio_direction_output(gpio_red, 0); 79 | gpio_direction_output(gpio_green, 0); 80 | 81 | irq = gpio_to_irq(gpio_btn1); 82 | retval = request_threaded_irq(irq, NULL, 83 | btn1_pushed_irq_handler, 84 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, 85 | "gpio-legacy-sample", NULL); 86 | 87 | pr_info("Hello world!\n"); 88 | return 0; 89 | } 90 | 91 | static int my_pdrv_remove(struct platform_device *pdev) 92 | { 93 | free_irq(irq, NULL); 94 | gpio_free(gpio_red); 95 | gpio_free(gpio_green); 96 | gpio_free(gpio_btn1); 97 | gpio_free(gpio_btn2); 98 | 99 | pr_info("End of the world\n"); 100 | return 0; 101 | } 102 | 103 | 104 | static struct platform_driver mypdrv = { 105 | .probe = my_pdrv_probe, 106 | .remove = my_pdrv_remove, 107 | .driver = { 108 | .name = "gpio_legacy_sample", 109 | .of_match_table = of_match_ptr(gpio_dt_ids), 110 | .owner = THIS_MODULE, 111 | }, 112 | }; 113 | module_platform_driver(mypdrv); 114 | 115 | MODULE_AUTHOR("John Madieu "); 116 | MODULE_LICENSE("GPL"); 117 | -------------------------------------------------------------------------------- /Chapter16/gpio/gpio-legacy-module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For Legacy integer based GPIO */ 5 | #include /* For IRQ */ 6 | 7 | /* 8 | * Please choose values that are free on your system 9 | */ 10 | static unsigned int GPIO_LED_RED = 49; 11 | static unsigned int GPIO_BTN1 = 115; 12 | static unsigned int GPIO_BTN2 = 116; 13 | static unsigned int GPIO_LED_GREEN = 120; 14 | static int irq; 15 | 16 | static irqreturn_t btn1_pushed_irq_handler(int irq, void *dev_id) 17 | { 18 | int state; 19 | 20 | /* read the button value and change the led state */ 21 | state = gpio_get_value(GPIO_BTN2); 22 | gpio_set_value(GPIO_LED_RED, state); 23 | gpio_set_value(GPIO_LED_GREEN, state); 24 | 25 | pr_info("GPIO_BTN1 interrupt: Interrupt! GPIO_BTN2 state is %d)\n", state); 26 | return IRQ_HANDLED; 27 | } 28 | 29 | static int __init hellowolrd_init(void) 30 | { 31 | int retval; 32 | 33 | /* 34 | * One could have checked whether the GPIO is valid on the controller or not, 35 | * using gpio_is_valid() function. 36 | * Ex: 37 | * if (!gpio_is_valid(GPIO_LED_RED)) { 38 | * pr_infor("Invalid Red LED\n"); 39 | * return -ENODEV; 40 | * } 41 | */ 42 | gpio_request(GPIO_LED_GREEN, "green-led"); 43 | gpio_request(GPIO_LED_RED, "red-led"); 44 | gpio_request(GPIO_BTN1, "button-1"); 45 | gpio_request(GPIO_BTN2, "button-2"); 46 | 47 | /* 48 | * Configure Button GPIOs as input 49 | * 50 | * After this, one can call gpio_set_debounce() 51 | * only if the controller has the feature 52 | * 53 | * For example, to debounce a button with a delay of 200ms 54 | * gpio_set_debounce(GPIO_BTN1, 200); 55 | */ 56 | gpio_direction_input(GPIO_BTN1); 57 | gpio_direction_input(GPIO_BTN2); 58 | 59 | /* 60 | * Set LED GPIOs as output, with their initial values set to 0 61 | */ 62 | gpio_direction_output(GPIO_LED_RED, 0); 63 | gpio_direction_output(GPIO_LED_GREEN, 0); 64 | 65 | irq = gpio_to_irq(GPIO_BTN1); 66 | retval = request_threaded_irq(irq, NULL,\ 67 | btn1_pushed_irq_handler, \ 68 | IRQF_TRIGGER_LOW | IRQF_ONESHOT, \ 69 | "device-name", NULL); 70 | 71 | pr_info("Hello world!\n"); 72 | return 0; 73 | } 74 | 75 | static void __exit hellowolrd_exit(void) 76 | { 77 | free_irq(irq, NULL); 78 | gpio_free(GPIO_LED_RED); 79 | gpio_free(GPIO_LED_GREEN); 80 | gpio_free(GPIO_BTN1); 81 | gpio_free(GPIO_BTN2); 82 | 83 | pr_info("End of the world\n"); 84 | } 85 | 86 | 87 | module_init(hellowolrd_init); 88 | module_exit(hellowolrd_exit); 89 | MODULE_AUTHOR("John Madieu "); 90 | MODULE_LICENSE("GPL"); 91 | -------------------------------------------------------------------------------- /Chapter16/mcp23016.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define GPIO_NUM 16 8 | #define INPUT 1 9 | #define OUTPUT 0 10 | 11 | /* Chip Control registers */ 12 | 13 | /* 14 | * GP0 ang GP1 provides access to GPIO ports 15 | * A read from these register provide status of pin of these ports 16 | * A write to these register will modify the output latch register (OLAT0, OLAT1) 17 | * and data register. 18 | */ 19 | #define GP0 0x0 20 | #define GP1 0x1 21 | 22 | /* 23 | * IOLAT0 and IOLAT1 control the output value of GP0 and GP1 24 | * Write into one of these register will result in affecting 25 | */ 26 | #define OLAT0 0x2 27 | #define OLAT1 0x3 28 | #define IOPOL0 0x4 29 | #define IOPOL1 0x5 30 | 31 | /* 32 | * IODIR0 and IODIR1 registers control GP0 and GP1 IOs direction 33 | * 1 => input; 0 => output 34 | * default value are 0xFF in each reg. 35 | */ 36 | #define IODIR0 0x6 37 | #define IODIR1 0x7 38 | 39 | /* 40 | * INTCAP0 and INTCAP1 register contain value of the port that generated the interupt 41 | * INTCAP0 contains value of GPO at time of GPO change interrupt 42 | * INTCAP1 contains value of GP1 at time of GP1 change interrupt 43 | */ 44 | #define INTCAP0 0x8 45 | #define INTCAP1 0x9 46 | 47 | struct mcp23016 { 48 | struct i2c_client *client; 49 | struct gpio_chip chip; 50 | }; 51 | 52 | static inline struct mcp23016 *to_mcp23016(struct gpio_chip *gc) 53 | { 54 | return container_of(gc, struct mcp23016, chip); 55 | } 56 | 57 | static int mcp23016_get_value(struct gpio_chip *gc, unsigned offset) 58 | { 59 | s32 value; 60 | struct mcp23016 *mcp = to_mcp23016(gc); 61 | unsigned bank = offset / 8 ; 62 | unsigned bit = offset % 8 ; 63 | 64 | u8 reg_intcap = (bank == 0) ? INTCAP0 : INTCAP1; 65 | value = i2c_smbus_read_byte_data(mcp->client, reg_intcap); 66 | return (value >= 0) ? (value >> bit) & 0x1 : 0; 67 | } 68 | 69 | static int mcp23016_set(struct mcp23016 *mcp, unsigned offset, int val) 70 | { 71 | s32 value; 72 | 73 | unsigned bank = offset / 8 ; 74 | u8 reg_gpio = (bank == 0) ? GP0 : GP1; 75 | unsigned bit = offset % 8 ; 76 | 77 | value = i2c_smbus_read_byte_data(mcp->client, reg_gpio); 78 | if (value >= 0) { 79 | if (val) 80 | value |= 1 << bit; 81 | else 82 | value &= ~(1 << bit); 83 | 84 | return i2c_smbus_write_byte_data(mcp->client, reg_gpio, value); 85 | } 86 | 87 | return value; 88 | } 89 | 90 | 91 | static void mcp23016_set_value(struct gpio_chip *gc, unsigned offset, int val) 92 | { 93 | struct mcp23016 *mcp = to_mcp23016(gc); 94 | mcp23016_set(mcp, offset, val); 95 | } 96 | 97 | /* 98 | * direction = 1 => input 99 | * direction = 0 => output 100 | */ 101 | static int mcp23016_direction(struct gpio_chip *gc, unsigned offset, 102 | unsigned direction, int val) 103 | { 104 | struct mcp23016 *mcp = to_mcp23016(gc); 105 | unsigned bank = offset / 8 ; 106 | unsigned bit = offset % 8 ; 107 | u8 reg_iodir = (bank == 0) ? IODIR0 : IODIR1; 108 | s32 iodirval = i2c_smbus_read_byte_data(mcp->client, reg_iodir); 109 | 110 | if (direction) 111 | iodirval |= 1 << bit; 112 | else 113 | iodirval &= ~(1 << bit); 114 | 115 | i2c_smbus_write_byte_data(mcp->client, reg_iodir, iodirval); 116 | if (direction) 117 | return iodirval ; 118 | else 119 | return mcp23016_set(mcp, offset, val); 120 | } 121 | 122 | static int mcp23016_direction_output(struct gpio_chip *gc, 123 | unsigned offset, int val) 124 | { 125 | return mcp23016_direction(gc, offset, OUTPUT, val); 126 | } 127 | 128 | static int mcp23016_direction_input(struct gpio_chip *gc, 129 | unsigned offset) 130 | { 131 | return mcp23016_direction(gc, offset, INPUT, 0); 132 | } 133 | 134 | #ifdef CONFIG_OF 135 | static const struct of_device_id mcp23016_ids[] = { 136 | { .compatible = "microchip,mcp23016", }, 137 | { /* sentinel */ } 138 | }; 139 | MODULE_DEVICE_TABLE(of, mcp23016_ids); 140 | #endif 141 | 142 | static int mcp23016_probe(struct i2c_client *client, 143 | const struct i2c_device_id *id) 144 | { 145 | struct mcp23016 *mcp; 146 | 147 | if (!i2c_check_functionality(client->adapter, 148 | I2C_FUNC_SMBUS_BYTE_DATA)) 149 | return -EIO; 150 | 151 | mcp = devm_kzalloc(&client->dev, sizeof(*mcp), GFP_KERNEL); 152 | if (!mcp) 153 | return -ENOMEM; 154 | 155 | mcp->chip.label = client->name; 156 | mcp->chip.base = -1; 157 | mcp->chip.owner = THIS_MODULE; 158 | mcp->chip.ngpio = GPIO_NUM; 159 | mcp->chip.can_sleep = 1; 160 | mcp->chip.get = mcp23016_get_value; 161 | mcp->chip.set = mcp23016_set_value; 162 | mcp->chip.direction_output = mcp23016_direction_output; 163 | mcp->chip.direction_input = mcp23016_direction_input; 164 | mcp->client = client; 165 | i2c_set_clientdata(client, mcp); 166 | 167 | return gpiochip_add(&mcp->chip); 168 | } 169 | 170 | static int mcp23016_remove(struct i2c_client *client) 171 | { 172 | struct mcp23016 *mcp; 173 | mcp = i2c_get_clientdata(client); 174 | gpiochip_remove(&mcp->chip); 175 | return 0; 176 | } 177 | 178 | static const struct i2c_device_id mcp23016_id[] = { 179 | {"mcp23016", 0}, 180 | {}, 181 | }; 182 | 183 | MODULE_DEVICE_TABLE(i2c, mcp23016_id); 184 | 185 | static struct i2c_driver mcp23016_i2c_driver = { 186 | .driver = { 187 | .owner = THIS_MODULE, 188 | .name = "mcp23016", 189 | .of_match_table = of_match_ptr(mcp23016_ids), 190 | }, 191 | .probe = mcp23016_probe, 192 | .remove = mcp23016_remove, 193 | .id_table = mcp23016_id, 194 | }; 195 | 196 | module_i2c_driver(mcp23016_i2c_driver); 197 | MODULE_AUTHOR("John Madieu "); 198 | MODULE_LICENSE("GPL"); 199 | -------------------------------------------------------------------------------- /Chapter17/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := input-button.o input-polled-button.o polled-ins.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | 5 | all default: modules 6 | install: modules_install 7 | 8 | modules modules_install help clean: 9 | $(MAKE) -C $(KERNELDIR) M=$(shell pwd) $@ 10 | -------------------------------------------------------------------------------- /Chapter17/README.md: -------------------------------------------------------------------------------- 1 | # Input device drivers test 2 | 3 | There are three drivers example in this chapter. After running `make` command, 4 | there will be two modules: 5 | 6 | * input-button.ko 7 | * input-polled-button.ko 8 | * polled-ins.ko 9 | 10 | The fist module implements the IRQ based input device, and the second one 11 | implements a polled input device. Both rely on i.MX6 from NXP. The third one 12 | should be used only on x86 platform, and will create a platform device that 13 | matches the polled input module. 14 | 15 | The polled device can be partially tested on a PC. Prior to (partially) test 16 | this module on a PC, one should comment each line below in the code 17 | (`input-polled-button.c`): 18 | 19 | ```c 20 | input_report_key(poll_dev->input, BTN_0, gpiod_get_value(priv->btn_gpiod) & 1); 21 | input_sync(poll_dev->input); 22 | gpiod_put(priv->btn_gpiod); 23 | ``` 24 | 25 | ## Polled input driver 26 | 27 | On i.MX6 platforms, one can use the devicetree node below: 28 | 29 | ```json 30 | input_button_device { 31 | compatible = "packt,input-polled-button"; 32 | button-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; 33 | }; 34 | ``` 35 | The module can then be loaded: 36 | 37 | ```bash 38 | # insmod input-polled-button.ko 39 | ``` 40 | On x86 platform, one need to load polled-ins.ko too. 41 | 42 | ```bash 43 | # insmod polled-ins.ko 44 | ``` 45 | 46 | after the module is loded, one can see the following debug message: 47 | 48 | ```bash 49 | $ dmesg 50 | [...] 51 | [ 335.044171] input-polled-button added 52 | [ 342.053497] input: Packt input polled Btn as /devices/platform/input-polled-button.0/input/input14 53 | ``` 54 | 55 | Now let's print some informations about the device using `udevadm` command: 56 | 57 | ```bash 58 | $ udevadm info /dev/input/event14 59 | P: /devices/platform/input-polled-button.0/input/input14/event14 60 | N: input/event14 61 | S: input/by-path/platform-input-polled-button.0-event 62 | E: DEVLINKS=/dev/input/by-path/platform-input-polled-button.0-event 63 | E: DEVNAME=/dev/input/event14 64 | E: DEVPATH=/devices/platform/input-polled-button.0/input/input14/event14 65 | E: ID_INPUT=1 66 | E: ID_PATH=platform-input-polled-button.0 67 | E: ID_PATH_TAG=platform-input-polled-button_0 68 | E: MAJOR=13 69 | E: MINOR=78 70 | E: SUBSYSTEM=input 71 | E: USEC_INITIALIZED=342093232 72 | ``` 73 | 74 | since the polled input driver can be partially tested on PC, the module loading 75 | may fail with below error in debug: 76 | 77 | ```bash 78 | $ dmesg 79 | [...] 80 | [ 277.561986] input_polled_button: Unknown symbol input_allocate_polled_device (err 0) 81 | [ 277.562012] input_polled_button: Unknown symbol input_free_polled_device (err 0) 82 | [ 277.562033] input_polled_button: Unknown symbol input_register_polled_device (err 0) 83 | [ 277.562050] input_polled_button: Unknown symbol input_unregister_polled_device (err 0) 84 | ``` 85 | 86 | This means our module tries to use non exported symbols. By using `depmod`, 87 | one can see our module dependencies, and load these prior to load our polled 88 | input module 89 | 90 | ```bash 91 | $ modinfo ./input-polled-button.ko 92 | filename: /home/ldd/sources/chapter-17/./input-polled-button.ko 93 | description: Polled input device 94 | author: John Madieu 95 | license: GPL 96 | srcversion: 9B24B13C64ECB6B10C912B3 97 | depends: input-polldev 98 | vermagic: 4.4.0-97-generic SMP mod_unload modversions 99 | 100 | $ sudo modprobe input-polldev 101 | ``` 102 | That is all for x86 based platform. Now on our i.MX6 based board, one can check 103 | the states of the gpio associated with this device as following: 104 | 105 | ```bash 106 | # cat /sys/kernel/debug/gpio | grep button 107 | gpio-193 (button-gpio ) in hi 108 | # cat /sys/kernel/debug/gpio | grep button 109 | gpio-193 (button-gpio ) in lo 110 | ``` 111 | 112 | One should run this command with the button pushed first, and then with the 113 | button released. The tool `evtest` cant be used to show the keycode 114 | corresponding to the button states: 115 | 116 | ```bash 117 | # evtest /dev/input/event14 118 | Input driver version is 1.0.1 119 | Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0 120 | Input device name: "Packt input polled Btn" 121 | Supported events: 122 | Event type 0 (EV_SYN) 123 | Event type 1 (EV_KEY) 124 | Event code 256 (BTN_0) 125 | Properties: 126 | Testing ... (interrupt to exit) 127 | 128 | ``` 129 | 130 | ## IRQ based input driver 131 | 132 | One can use the below node in the devicetree: 133 | 134 | ```json 135 | input_button_device { 136 | compatible = "packt,input-button"; 137 | button-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; 138 | }; 139 | ``` 140 | You should make sure the GPIO is actually free. The command below will load the 141 | module: 142 | 143 | ```bash 144 | # insmod input-polled-button.ko 145 | ``` 146 | 147 | using `udevadm` on the device will print the following informations: 148 | 149 | ```bash 150 | # udevadm info /dev/input/event0 151 | P: /devices/platform/input-button.0/input/input0/event0 152 | N: input/event0 153 | S: input/by-path/platform-input-button.0-event 154 | E: DEVLINKS=/dev/input/by-path/platform-input-button.0-event 155 | E: DEVNAME=/dev/input/event0 156 | E: DEVPATH=/devices/platform/input-button.0/input/input0/event0 157 | E: ID_INPUT=1 158 | E: ID_PATH=platform-input-button.0 159 | E: ID_PATH_TAG=platform-input-button_0 160 | E: MAJOR=13 161 | E: MINOR=64 162 | E: SUBSYSTEM=input 163 | E: USEC_INITIALIZED=74842430 164 | ``` 165 | 166 | One can check whether the IRQ line has been acquired by the driver using the 167 | below command: 168 | 169 | ```bash 170 | # cat /proc/interrupts 171 | 147: 0 0 0 0 GIC 147 20e0000.hdmi_video 172 | 150: 481 0 0 0 GIC 150 2188000.ethernet 173 | 151: 0 0 0 0 GIC 151 2188000.ethernet 174 | 152: 75 0 0 0 GIC 152 mx6-pcie-msi 175 | 160: 0 0 0 0 gpio-mxc 0 packt-input-button 176 | 161: 0 0 0 0 gpio-mxc 1 2198000.usdhc cd 177 | 250: 0 0 0 0 gpio-mxc 26 2-0026 178 | 286: 0 0 0 0 gpio-mxc 30 ad7606, ad7606, ad7606 179 | 351: 0 0 0 0 gpio-mxc 31 2-0027 180 | 352: 0 0 0 0 gpio-mxc 0 2194000.usdhc cd 181 | ``` 182 | 183 | the line with `packt-input-button` correspond to the IRQ line for this input device 184 | -------------------------------------------------------------------------------- /Chapter17/input-button.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For DT*/ 5 | #include /* For platform devices */ 6 | #include /* For GPIO Descriptor interface */ 7 | #include 8 | #include 9 | 10 | 11 | struct btn_data { 12 | struct gpio_desc *btn_gpiod; 13 | struct input_dev *i_dev; 14 | struct platform_device *pdev; 15 | int irq; 16 | }; 17 | 18 | static int btn_open(struct input_dev *i_dev) 19 | { 20 | pr_info("input device opened()\n"); 21 | return 0; 22 | } 23 | 24 | static void btn_close(struct input_dev *i_dev) 25 | { 26 | pr_info("input device closed()\n"); 27 | } 28 | 29 | static irqreturn_t packt_btn_interrupt(int irq, void *dev_id) 30 | { 31 | struct btn_data *priv = dev_id; 32 | 33 | input_report_key(priv->i_dev, BTN_0, gpiod_get_value(priv->btn_gpiod) & 1); 34 | input_sync(priv->i_dev); 35 | return IRQ_HANDLED; 36 | } 37 | 38 | static const struct of_device_id btn_dt_ids[] = { 39 | { .compatible = "packt,input-button", }, 40 | { /* sentinel */ } 41 | }; 42 | 43 | static int btn_probe(struct platform_device *pdev) 44 | { 45 | struct btn_data *priv; 46 | struct gpio_desc *gpiod; 47 | struct input_dev *i_dev; 48 | int ret; 49 | 50 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 51 | if (!priv) 52 | return -ENOMEM; 53 | 54 | i_dev = input_allocate_device(); 55 | if (!i_dev) 56 | return -ENOMEM; 57 | 58 | i_dev->open = btn_open; 59 | i_dev->close = btn_close; 60 | i_dev->name = "Packt Btn"; 61 | i_dev->dev.parent = &pdev->dev; 62 | priv->i_dev = i_dev; 63 | priv->pdev = pdev; 64 | 65 | /* Declare the events generated by this driver */ 66 | set_bit(EV_KEY, i_dev->evbit); 67 | set_bit(BTN_0, i_dev->keybit); /* buttons */ 68 | 69 | /* We assume this GPIO is active high */ 70 | gpiod = gpiod_get(&pdev->dev, "button", GPIOD_IN); 71 | if (IS_ERR(gpiod)) 72 | return -ENODEV; 73 | 74 | priv->irq = gpiod_to_irq(priv->btn_gpiod); 75 | priv->btn_gpiod = gpiod; 76 | 77 | ret = input_register_device(priv->i_dev); 78 | if (ret) { 79 | pr_err("Failed to register input device\n"); 80 | goto err_input; 81 | } 82 | 83 | ret = request_any_context_irq(priv->irq, 84 | packt_btn_interrupt, 85 | (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), 86 | "packt-input-button", priv); 87 | if (ret < 0) { 88 | dev_err(&pdev->dev, 89 | "Unable to acquire interrupt for GPIO line\n"); 90 | goto err_btn; 91 | } 92 | 93 | platform_set_drvdata(pdev, priv); 94 | return 0; 95 | 96 | err_btn: 97 | gpiod_put(priv->btn_gpiod); 98 | err_input: 99 | input_free_device(i_dev); 100 | return ret; 101 | } 102 | 103 | static int btn_remove(struct platform_device *pdev) 104 | { 105 | struct btn_data *priv; 106 | priv = platform_get_drvdata(pdev); 107 | input_unregister_device(priv->i_dev); 108 | input_free_device(priv->i_dev); 109 | free_irq(priv->irq, priv); 110 | gpiod_put(priv->btn_gpiod); 111 | return 0; 112 | } 113 | 114 | static struct platform_driver mypdrv = { 115 | .probe = btn_probe, 116 | .remove = btn_remove, 117 | .driver = { 118 | .name = "input-button", 119 | .of_match_table = of_match_ptr(btn_dt_ids), 120 | .owner = THIS_MODULE, 121 | }, 122 | }; 123 | module_platform_driver(mypdrv); 124 | 125 | MODULE_LICENSE("GPL"); 126 | MODULE_AUTHOR("John Madieu "); 127 | MODULE_DESCRIPTION("Input device (IRQ based)"); 128 | -------------------------------------------------------------------------------- /Chapter17/input-polled-button.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include /* For DT*/ 5 | #include /* For platform devices */ 6 | #include /* For GPIO Descriptor interface */ 7 | #include 8 | #include 9 | 10 | 11 | struct poll_btn_data { 12 | struct gpio_desc *btn_gpiod; 13 | struct input_polled_dev *poll_dev; 14 | }; 15 | 16 | static void polled_btn_open(struct input_polled_dev *poll_dev) 17 | { 18 | /* struct poll_btn_data *priv = poll_dev->private; */ 19 | pr_info("polled device opened()\n"); 20 | } 21 | 22 | static void polled_btn_close(struct input_polled_dev *poll_dev) 23 | { 24 | /* struct poll_btn_data *priv = poll_dev->private; */ 25 | pr_info("polled device closed()\n"); 26 | } 27 | 28 | static void polled_btn_poll(struct input_polled_dev *poll_dev) 29 | { 30 | struct poll_btn_data *priv = poll_dev->private; 31 | 32 | /* One can comment these two line for testing purpose on a PC */ 33 | input_report_key(poll_dev->input, BTN_0, gpiod_get_value(priv->btn_gpiod) & 1); 34 | input_sync(poll_dev->input); 35 | } 36 | 37 | static const struct of_device_id btn_dt_ids[] = { 38 | { .compatible = "packt,input-polled-button", }, 39 | { /* sentinel */ } 40 | }; 41 | 42 | static int polled_btn_probe(struct platform_device *pdev) 43 | { 44 | struct poll_btn_data *priv; 45 | struct input_polled_dev *poll_dev; 46 | struct input_dev *input_dev; 47 | int ret; 48 | 49 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 50 | if (!priv) 51 | return -ENOMEM; 52 | 53 | poll_dev = input_allocate_polled_device(); 54 | if (!poll_dev) 55 | return -ENOMEM; 56 | 57 | /* We assume this GPIO is active high */ 58 | priv->btn_gpiod = gpiod_get(&pdev->dev, "button", GPIOD_IN); 59 | 60 | poll_dev->private = priv ; 61 | poll_dev->poll_interval = 200; /* Poll every 200ms */ 62 | poll_dev->poll = polled_btn_poll; 63 | poll_dev->open = polled_btn_open; 64 | poll_dev->close = polled_btn_close; 65 | priv->poll_dev = poll_dev; 66 | 67 | input_dev = poll_dev->input; 68 | input_dev->name = "Packt input polled Btn"; 69 | input_dev->dev.parent = &pdev->dev; 70 | 71 | /* Declare the events generated by this driver */ 72 | set_bit(EV_KEY, input_dev->evbit); 73 | set_bit(BTN_0, input_dev->keybit); /* buttons */ 74 | 75 | ret = input_register_polled_device(priv->poll_dev); 76 | if (ret) { 77 | pr_err("Failed to register input polled device\n"); 78 | input_free_polled_device(poll_dev); 79 | devm_kfree(&pdev->dev, priv); 80 | return ret; 81 | } 82 | 83 | platform_set_drvdata(pdev, priv); 84 | return 0; 85 | } 86 | 87 | static int polled_btn_remove(struct platform_device *pdev) 88 | { 89 | struct poll_btn_data *priv = platform_get_drvdata(pdev); 90 | input_unregister_polled_device(priv->poll_dev); 91 | input_free_polled_device(priv->poll_dev); 92 | gpiod_put(priv->btn_gpiod); 93 | return 0; 94 | } 95 | 96 | static struct platform_driver mypdrv = { 97 | .probe = polled_btn_probe, 98 | .remove = polled_btn_remove, 99 | .driver = { 100 | .name = "input-polled-button", 101 | .of_match_table = of_match_ptr(btn_dt_ids), 102 | .owner = THIS_MODULE, 103 | }, 104 | }; 105 | module_platform_driver(mypdrv); 106 | 107 | MODULE_LICENSE("GPL"); 108 | MODULE_AUTHOR("John Madieu "); 109 | MODULE_DESCRIPTION("Polled input device"); 110 | -------------------------------------------------------------------------------- /Chapter17/polled-ins.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static struct platform_device *pdev; 6 | 7 | static int __init fake_poll_dev_add(void) 8 | { 9 | int inst_id = 0; 10 | pdev = platform_device_alloc("input-polled-button", inst_id); 11 | platform_device_add(pdev); 12 | pr_info("input-polled-button added"); 13 | return 0; 14 | } 15 | 16 | static void __exit fake_poll_dev_put(void) 17 | { 18 | pr_info("input-polled-button removed"); 19 | platform_device_put(pdev); 20 | } 21 | 22 | module_init(fake_poll_dev_add); 23 | module_exit(fake_poll_dev_put); 24 | MODULE_LICENSE("GPL"); 25 | MODULE_AUTHOR("John Madieu "); 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Packt 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Linux Device Driver Development-Second Edition 5 | 6 | Understanding and installing toolchains 7 | 8 | This is the code repository for [Linux Device Driver Development-Second Edition](https://www.packtpub.com/product/linux-device-driver-development-second-edition/9781803240060?utm_source=github&utm_medium=repository&utm_campaign=9781803240060), published by Packt. 9 | 10 | **Everything you need to start with device driver development for Linux kernel and embedded Linux** 11 | 12 | ## What is this book about? 13 | Linux is by far the most-used kernel on embedded systems. Thanks to its subsystems, the Linux kernel supports almost all of the application fields in the industrial world. This updated second edition of Linux Device Driver Development is a comprehensive introduction to the Linux kernel world and the different subsystems that it is made of, and will be useful for embedded developers from any discipline. 14 | 15 | This book covers the following exciting features: 16 | * Download, configure, build, and tailor the Linux kernel 17 | * Describe the hardware using a device tree 18 | * Write feature-rich platform drivers and leverage I2C and SPI buses 19 | * Get the most out of the new concurrency managed workqueue infrastructure 20 | * Understand the Linux kernel timekeeping mechanism and use time-related APIs 21 | * Use the regmap framework to factor the code and make it generic 22 | * Offload CPU for memory copies using DMA 23 | * Interact with the real world using GPIO, IIO, and input subsystems 24 | 25 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1803240067) today! 26 | 27 | https://www.packtpub.com/ 29 | 30 | ## Instructions and Navigations 31 | All of the code is organized into folders. For example, Chapter02. 32 | 33 | The code will look like the following: 34 | ``` 35 | struct mutex { 36 | atomic_long_t owner; 37 | spinlock_t wait_lock; 38 | #ifdef CONFIG_MUTEX_SPIN_ON_OWNER 39 | struct optimistic_spin_queue osq; /* Spinner MCS lock */ 40 | ``` 41 | 42 | **Following is what you need for this book:** 43 | This Linux OS book is for embedded system and embedded Linux enthusiasts/developers who want to get started with Linux kernel development and leverage its subsystems. Electronic hackers and hobbyists interested in Linux kernel development as well as anyone looking to interact with the platform using GPIO, IIO, and input subsystems will also find this book useful. 44 | 45 | With the following software and hardware list you can run all code files present in the book (Chapter 1-17). 46 | ### Software and Hardware List 47 | | Chapter | Software required | OS required | 48 | | -------- | ------------------------------------ | ----------------------------------- | 49 | | 1-17 | A computer with good network bandwidth and enough space and RAM to download and build Linux kernel | Preferably any Debian distribution | 50 | | 1-17 | Any cortex -A embedded board available on market (for example UDOO quad, Jetson nano, Rasberry Pi, Beagle bone | Either a yocto/Buildroot ditribution or any embedded or vendor-specific OS (for example Rasbian for Rasberry Pi) | 51 | 52 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://static.packt-cdn.com/downloads/9781803240060_ColorImages.pdf). 53 | 54 | ## Errata 55 | 56 | * Page 45 (Paragraph 5, line 2,3): `modeprob -r` _should be_ `modprob -r` 57 | * Page 48 (Paragraph 2, line 8): `if(wite(fd, buf, 1) < 0) {` _should be_ `if(write(fd, buf, 1) < 0) {` 58 | * page 189 (Paragraph 1, line1): `foo-verlay.dts` _should be_ `foo-overlay.dts` 59 | * Page 30, (second last paragraph): `$(CORSS_COMPILE) objdump` _should be_ `$(CROSS_COMPILE) objdump` 60 | * Page 69, (code snippet): `#include` _should be_ `#include ` 61 | 62 | ### Related products 63 | * Mastering Linux Device Driver Development [[Packt]](https://www.packtpub.com/product/mastering-linux-device-driver-development/9781789342048?utm_source=github&utm_medium=repository&utm_campaign=9781789342048) [[Amazon]](https://www.amazon.com/dp/1785280007) 64 | 65 | * Linux Kernel Programming [[Packt]](https://www.packtpub.com/product/linux-kernel-programming/9781789953435?utm_source=github&utm_medium=repository&utm_campaign=9781789953435) [[Amazon]](https://www.amazon.com/dp/178995343X) 66 | 67 | ## Get to Know the Author 68 | **John Madieu** 69 | is an embedded Linux and kernel engineer living in Paris, France. His main activity consists of developing device drivers and Board Support Packages (BSPs) for companies in domains including IoT, automation, transport, healthcare, energy, and the military. John is the founder and chief consultant of LABCSMART, a company that provides training and services for embedded Linux and Linux kernel engineering. He is an open source and embedded systems enthusiast, convinced that it is only by sharing knowledge that we can learn more. He is passionate about boxing, which he practiced for 6 years professionally, and continues to channel this passion through training sessions that he provides voluntarily. 70 | 71 | ## Other books by the author 72 | * [Mastering Linux Device Driver Development](https://www.packtpub.com/product/mastering-linux-device-driver-development/9781789342048?utm_source=github&utm_medium=repository&utm_campaign=9781789342048) 73 | ### Download a free PDF 74 | 75 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
76 |

https://packt.link/free-ebook/9781803240060

77 | --------------------------------------------------------------------------------