diff --git a/05-Interfaces/Makefile b/05-Interfaces/Makefile new file mode 100644 index 0000000..46971c3 --- /dev/null +++ b/05-Interfaces/Makefile @@ -0,0 +1,11 @@ +# +# Homework 5: Building module_procfs and module_sysfs +# + +obj-m :=module_procfs.o module_sysfs.o + +all: + make -C $(BUILD_KERNEL) M=$(PWD) modules + +clean: + make -C $(BUILD_KERNEL) M=$(PWD) clean diff --git a/05-Interfaces/module_procfs.c b/05-Interfaces/module_procfs.c new file mode 100644 index 0000000..f93732c --- /dev/null +++ b/05-Interfaces/module_procfs.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include + + +#define DIR_NAME "my_procfs_dir" +#define ENT1_NAME "uppercase" +#define ENT2_NAME "statistics" + +static char msg[PAGE_SIZE]; +static ssize_t msg_size; +static char stat_msg[PAGE_SIZE]; +static size_t stat_msg_size; +static int call_read; +static int call_write; +static int total_char; + +static ssize_t mywrite(struct file *file, const char __user *pbuf, size_t count, loff_t *ppos) +{ + ssize_t not_copied; + int i; + + call_write++; + + pr_info("mymodule: mywrite: count=%d\n", count); + + not_copied = copy_from_user(msg, pbuf, count); + + msg_size = count - not_copied; + + for (i = 0; i < msg_size; i++) { + total_char++; + msg[i] = toupper(msg[i]); + } + + pr_info("mymodule: mywrite: msg_size=%d\n", msg_size); + return msg_size; +} + +static ssize_t myread(struct file *file, char __user *pbuf, size_t count, loff_t *ppos) +{ + ssize_t num, not_copied; + + call_read++; + + pr_info("mymodule: myread: count=%d\n", count); + + num = min_t(ssize_t, msg_size, count); + + if (num) { + not_copied = copy_to_user(pbuf, msg, num); + num -= not_copied; + } + + msg_size = 0; /* Indicate EOF on next read */ + + pr_info("mymodule: myread: return=%d\n", num); + return num; +} + +static ssize_t stat_read(struct file *file, char __user *pbuf, size_t count, loff_t *ppos) +{ + pr_info("Call Statistics:\ncalled Read %d - called Write" + " %d Total characters %d\n", call_read, call_write, total_char); + + stat_msg_size = sprintf(stat_msg, "Call Statistics:\n called Read %d" + " - called Write %d Total characters %d\n", call_read, call_write, total_char); + + if (count < stat_msg_size) + return -EINVAL; + if (*ppos != 0) + return 0; + if (copy_to_user(pbuf, stat_msg, stat_msg_size)) + return -EINVAL; + + *ppos = stat_msg_size; + + pr_info("mymodule: myread: return=%d\n", stat_msg_size); + return stat_msg_size; +} + + +static const struct file_operations rw_ops = { + .owner = THIS_MODULE, + .read = myread, + .write = mywrite, +}; + +static const struct file_operations r_ops = { + .owner = THIS_MODULE, + .read = stat_read, +}; + + +static struct proc_dir_entry *dir; +static struct proc_dir_entry *ent1, *ent2; + + +static int mymodule_init(void) +{ + dir = proc_mkdir(DIR_NAME, NULL); + if (dir == NULL) { + pr_err("mymodule: error creating: proc/my_procfs_dir\n"); + return -ENOMEM; + } + + ent1 = proc_create(ENT1_NAME, 0666, dir, &rw_ops); + if (ent1 == NULL) { + pr_err("mymodule: error creating: proc/my_procfs_dir/uppercase\n"); + remove_proc_entry(DIR_NAME, NULL); + return -ENOMEM; + } + + ent2 = proc_create(ENT2_NAME, 0444, dir, &r_ops); + if (ent2 == NULL) { + pr_err("mymodule: error creating: /proc/my_procfs_dir/statistics\n"); + remove_proc_entry(ENT1_NAME, dir); + remove_proc_entry(DIR_NAME, NULL); + return -ENOMEM; + } + + pr_info("mymodule: module loaded\nCreate:\n" + "/proc/my_procfs_dir/uppercase\n /proc/my_procfs_dir/statistics\n"); + return 0; +} + +static void mymodule_exit(void) +{ + proc_remove(dir); + + pr_info("mymodule: module exited\nRemove:\n" + "/proc/my_procfs_dir/uppercase\n/proc/my_procfs_dir/statistics\n"); +} + +module_init(mymodule_init); +module_exit(mymodule_exit); + +MODULE_AUTHOR("Dmitriy Ganshin"); +MODULE_DESCRIPTION("Simple Kernel module example procfs: write, read, statistics"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); diff --git a/05-Interfaces/module_sysfs.c b/05-Interfaces/module_sysfs.c new file mode 100644 index 0000000..f75aef7 --- /dev/null +++ b/05-Interfaces/module_sysfs.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include + +static int call_read; +static int call_write; +static int total_char; +static char str[PAGE_SIZE]; + +static ssize_t rw_show(struct class *class, struct class_attribute *attr, char *buf) +{ + call_read++; + + pr_info("mymodule: rw_show: string: %s\n", str); + sprintf(buf, "Output lowercase string:\n%s\n", str); + return strlen(buf); +} + +static ssize_t rw_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) +{ + int i; + int temp; + + call_write++; + + strcpy(str, buf); + temp = strlen(str); + + pr_info("mymodule: rw_store: string: %s\n", str); + + for (i = 0; i < temp; i++) { + total_char++; + str[i] = tolower(str[i]); + } + return count; +} + +static ssize_t statistics_show(struct class *class, struct class_attribute *attr, char *buf) +{ + pr_info("Statistics:\ncall Read %d call Write %d Total characters %d\n", + call_read, call_write, total_char); + sprintf(buf, "Statistics:\ncall Read %d call Write %d Total characters %d\n", + call_read, call_write, total_char); + + return strlen(buf); +} + + +CLASS_ATTR_RW(rw); +CLASS_ATTR_RO(statistics); + +static struct class *attr_class; + + +static int mymodule_init(void) +{ + int ret; + + attr_class = class_create(THIS_MODULE, "lowercase"); + if (attr_class == NULL) { + pr_err("mymodule: error creating sysfs class\n"); + return -ENOMEM; + } + + ret = class_create_file(attr_class, &class_attr_rw); + if (ret) { + pr_err("mymodule: error creating sysfs class attribute\n"); + class_destroy(attr_class); + return ret; + } + + ret = class_create_file(attr_class, &class_attr_statistics); + if (ret) { + pr_err("mymodule: error creating sysfs class attribute\n"); + class_destroy(attr_class); + return ret; + } + + pr_info("mymodule: module loaded\nCreate:\n/sys/class/lowercase/rw\n" + "/sys/class/lowercase/statistics\n"); + return 0; +} + +static void mymodule_exit(void) +{ + class_remove_file(attr_class, &class_attr_statistics); + class_remove_file(attr_class, &class_attr_rw); + class_destroy(attr_class); + + pr_info("mymodule: module exited\nRemove:\n/sys/class/lowercase/rw\n" + "/sys/class/lowercase/statistics\n"); +} + +module_init(mymodule_init); +module_exit(mymodule_exit); + +MODULE_AUTHOR("Dmitriy Ganshin"); +MODULE_DESCRIPTION("Simple Kernel module sysfs (write, read, statistics"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1");