diff --git a/07_procfs/Makefile b/07_procfs/Makefile new file mode 100644 index 0000000..95e1c76 --- /dev/null +++ b/07_procfs/Makefile @@ -0,0 +1,9 @@ +KERNELDIR ?= ../../buildroot/buildroot-2021.02.7/output/build/linux-5.10.10/ #WARNING relative path + +obj-m := kmodule_proc_fs.o + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean diff --git a/07_procfs/README.md b/07_procfs/README.md new file mode 100644 index 0000000..7fa3528 --- /dev/null +++ b/07_procfs/README.md @@ -0,0 +1,10 @@ +## Lesson 07 - ProcFS interface and orange pi bootup + +* Update your existing sysfs kernel module with procfs API: +* Create folder in procfs file system; +* Create entry that returns module author name; +* Create entry that returns amount of "store" callback calls; +* Create entry that returns amount of "show" callback calls. +* Build image for orange pi zero +* Attach console output from your development board + diff --git a/07_procfs/console.txt b/07_procfs/console.txt new file mode 100644 index 0000000..9ac88e3 --- /dev/null +++ b/07_procfs/console.txt @@ -0,0 +1,21 @@ +# insmod /tmp/kmodule_proc_fs.ko +# echo "store1" > /sys/kernel/MyObject/list +# echo "store2" > /sys/kernel/MyObject/list +# echo "store3" > /sys/kernel/MyObject/list +# cat /sys/kernel/MyObject/list +store3 + +store2 + +store1 + +# cat /proc/MyObject/author +Nazarii Kurylko # +# cat /proc/MyObject/storecount +storecount = 3 +# cat /proc/MyObject/s +showcount storecount +# cat /proc/MyObject/showcount +showcount = 1 +# + diff --git a/07_procfs/kmodule_proc_fs.c b/07_procfs/kmodule_proc_fs.c new file mode 100644 index 0000000..16e6448 --- /dev/null +++ b/07_procfs/kmodule_proc_fs.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + *Module implements the following task: + *Update your existing sysfs kernel module with procfs API: + *Create folder in procfs file system; + *Create entry that returns module author name; + *Create entry that returns amount of "store" callback calls; + *Create entry that returns amount of "show" callback calls. + *Build image for orange pi zero + *Attach console output from your development board + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULEAUTHOR "Nazarii Kurylko " + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION("Basic data structures module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +#define MODULE_NAME "MyObject" +#define FILE_AUTHOR "author" +#define FILE_SHOWCOUNT "showcount" +#define FILE_STORECOUNT "storecount" +#define MAX_NODESS_IN_LIST (100) + +static unsigned int nodes_in_list; +static unsigned int showcount; +static unsigned int storecount; + +LIST_HEAD(list_head); + +struct str_node { + struct list_head list; + char *str_ptr; +}; + +static ssize_t myobject_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int bytes = 0; + int res = 0; + struct str_node *node = NULL; + struct str_node *node_temp = NULL; + + // print all string to output + list_for_each_entry_safe(node, node_temp, &list_head, list) { + res = scnprintf(&buf[bytes], (PAGE_SIZE - bytes), "%s\n", + node->str_ptr); + if (res <= 0) { + pr_debug("myobject: %s->scnprintf returned %d\n", + __func__, res); + return -EIO; + } + bytes += res; + } + + showcount++; + return bytes + 1; // +1 - for 0 termination +} + +static ssize_t myobject_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + //allocate memory for the struct. + struct str_node *new = NULL; + + if (nodes_in_list > MAX_NODESS_IN_LIST) { + pr_debug("myobject: maximum number of strings reached %u\n", + nodes_in_list); + return -ECANCELED; + } + + // if no string return; + if (count < 1) + return count; + + //alocate the link list node + new = kmalloc(sizeof(struct str_node), GFP_KERNEL); + if (new == NULL) + return -ENOMEM; + + new->str_ptr = NULL; //initialize pointer + + //allocate the memory for string. + new->str_ptr = kzalloc(count + 1, GFP_KERNEL); + if (new->str_ptr == NULL) { + kfree(new); + pr_debug("myobject: kzalloc returned error\n"); + return -ENOMEM; + } + + //copy string to allocated buffer + memcpy(new->str_ptr, buf, count + 1); + + //next prev pointer initalisation + INIT_LIST_HEAD(&new->list); + + //add new node between head and previous node + list_add(&(new->list), &list_head); + + nodes_in_list += 1; + + storecount++; + + return count; +} + +static struct kobj_attribute list_attribute = + __ATTR(list, 0664, myobject_show, myobject_store); + +static struct kobject *myobject_kobj; + +static struct proc_dir_entry *proc_dir, *author_file, *showcount_file, + *storecount_file; + +static ssize_t read_author(struct file *file, char __user *pbuf, size_t count, + loff_t *ppos) +{ + int not_copied = copy_to_user(pbuf, MODULEAUTHOR, sizeof(MODULEAUTHOR)); + *ppos += sizeof(MODULEAUTHOR) - not_copied; + + if (*ppos > sizeof(MODULEAUTHOR)) + return 0; + else + return sizeof(MODULEAUTHOR); +} + +static ssize_t read_showcount(struct file *file, char __user *pbuf, + size_t count, loff_t *ppos) +{ + char tmp_buff[255]; + int bytes = scnprintf(tmp_buff, sizeof(tmp_buff), "showcount = %u\n", + showcount); + + int not_copied = copy_to_user(pbuf, tmp_buff, bytes); + *ppos += bytes - not_copied; + + if (*ppos > bytes) + return 0; + else + return bytes; +} + +static ssize_t read_storecount(struct file *file, char __user *pbuf, + size_t count, loff_t *ppos) +{ + char tmp_buff[255]; + int bytes = scnprintf(tmp_buff, sizeof(tmp_buff), "storecount = %u\n", + storecount); + + int not_copied = copy_to_user(pbuf, tmp_buff, bytes); + *ppos += bytes - not_copied; + + if (*ppos > bytes) + return 0; + else + return bytes; +} + +static struct proc_ops proc_ops_author = { + .proc_read = read_author, +}; + +static struct proc_ops proc_ops_showcount = { + .proc_read = read_showcount, +}; + +static struct proc_ops proc_ops_storecount = { + .proc_read = read_storecount, +}; + +static int myobject_init(void) +{ + int res = 0; + + myobject_kobj = kobject_create_and_add(MODULE_NAME, kernel_kobj); + if (!myobject_kobj) { + pr_debug("myobject: kobject_create_and_add returned error\n"); + return -ENOMEM; + } + res = sysfs_create_file(myobject_kobj, &list_attribute.attr); + if (res) { + pr_debug("myobject: sysfs_create_file returned error\n"); + kobject_put(myobject_kobj); + return -ENOMEM; + } + + /* create directory */ + proc_dir = proc_mkdir(MODULE_NAME, NULL); + if (proc_dir == NULL) { + pr_debug("myobject: proc_mkdir returned error\n"); + kobject_put(myobject_kobj); + return -ENOMEM; + } + + /* create author file */ + author_file = + proc_create(FILE_AUTHOR, 0444, proc_dir, &proc_ops_author); + if (author_file == NULL) { + pr_debug("myobject: proc_create1 returned error\n"); + kobject_put(myobject_kobj); + return -ENOMEM; + } + + /* create showcount file */ + showcount_file = proc_create(FILE_SHOWCOUNT, 0444, proc_dir, + &proc_ops_showcount); + if (showcount_file == NULL) { + pr_debug("myobject: proc_create2 returned error\n"); + kobject_put(myobject_kobj); + return -ENOMEM; + } + + /* create storecount file */ + storecount_file = proc_create(FILE_STORECOUNT, 0444, proc_dir, + &proc_ops_storecount); + if (storecount_file == NULL) { + pr_debug("myobject: proc_create3 returned error\n"); + kobject_put(myobject_kobj); + return -ENOMEM; + } + return res; +} + +static void myobject_exit(void) +{ + struct str_node *node = NULL; + struct str_node *node_temp = NULL; + + kobject_put(myobject_kobj); + + //free resources + list_for_each_entry_safe(node, node_temp, &list_head, list) { + list_del(&node->list); + kfree(node->str_ptr); + kfree(node); + } + + proc_remove(proc_dir); + proc_remove(author_file); + proc_remove(showcount_file); + proc_remove(storecount_file); +} + +module_init(myobject_init); +module_exit(myobject_exit);