/** * KSudoku subsystem for Linux kernel to solve sudoku with different * algorithms. * Copyright (C) 2020 Sameer Rahmani * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ksudoku.h" #define KSUDOKU_VERSION "0.1" #define KSUDOKU_DESC "Kernel Sudoku Module" /* * The default show callback that is called by sysfs when ever a read * operation happens on any attribute on ksudoku subsystem. */ static ssize_t ksudoku_default_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct ksudoku_attribute *attribute; struct ksudoku *sudoku; attribute = to_ksudoku_attr(attr); sudoku = to_ksudoku(kobj); if (!attribute->show) return -EIO; return attribute->show(sudoku, attribute, buf); } /* * The default show callback that is called by sysfs when ever a write * operation happens on any attribute on ksudoku subsystem. */ static ssize_t ksudoku_default_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { struct ksudoku_attribute *attribute; struct ksudoku *sudoku; attribute = to_ksudoku_attr(attr); sudoku = to_ksudoku(kobj); if (!attribute->store) return -EIO; return attribute->store(sudoku, attribute, buf, len); } /* * Create a `sysfs_ops` structure in order to register * the default IO operation handlers of ksudoku subsystem. */ static const struct sysfs_ops ksudoku_sysfs_ops = { .show = ksudoku_default_attr_show, .store = ksudoku_default_attr_store, }; /* * The release function for ksudoku. This is REQUIRED by the kernel to * have. We free the memory held in our object here. * * We should free the memory here rather than the exit method of the * module. Kernel will call this function as soon as the reference * counter on the kobject embedded inside the ksudoku object hits * zero. So it might happen before or after exit function. * * The release function is per kobject so we're going to free * different ksudoku instances separated from eachother. */ static void ksudoku_release(struct kobject *kobj) { struct ksudoku *s; s = to_ksudoku(kobj); kfree(s); } /* * The show handler of `status` attribute. This callback will * be called via `ksudoku_default_attr_show` which is the * default read handler on ksudoku_type for ksudoku subsystem. * * Since `status` suppose to read only there would not be * a store handler. */ static ssize_t status_show(struct ksudoku *s, struct ksudoku_attribute *attr, char *buf) { return sprintf(buf, "%d", atomic_read(&s->status)); } // __ATTR_RO macro will set the `.show` member of // `status_attribute` to `status_show` (name + _show) static struct ksudoku_attribute status_attribute = __ATTR_RO(status); static ssize_t matrix_show(struct ksudoku *s, struct ksudoku_attribute *attr, char *buf) { if (s->matrix) return sprintf(buf, "NULL"); return sprintf(buf, "%s\n", s->matrix); } static ssize_t matrix_store(struct ksudoku *s, struct ksudoku_attribute *attr, const char *buf, size_t len) { int status = atomic_read(&s->status); char *p = (char *) buf; int i, j, retval; printk(KERN_INFO "len: %ld.\n", len); if (status == KSUDOKU_BUSY) return -EBUSY; if (len != 82) return -EIO; strncpy(s->matrix, buf, len); for (i = 0; i < 9; i++) { for (j = 0; j < 9; j++) { char g = (char) *p; retval = kstrtoint(&g, 10 , &s->matrix_array[i][j]); if (!retval) return -EINVAL; p++; } } return len; } static struct ksudoku_attribute matrix_attribute = __ATTR(matrix, 0664, matrix_show, matrix_store); static struct attribute *ksudoku_default_attrs[] = { &status_attribute.attr, &matrix_attribute.attr, NULL }; ATTRIBUTE_GROUPS(ksudoku_default); static struct kobj_type ksudoku_type = { .sysfs_ops = &ksudoku_sysfs_ops, .release = ksudoku_release, .default_groups = ksudoku_default_groups, }; static struct kset *ksudoku_set; /** * ksudoku_create_sudoku - create new sudoku with under ksudoku subsystem. * @name: name of the sudoku to create. */ struct ksudoku *ksudoku_create_sudoku(const char *name) { struct ksudoku *s; int result; s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) return NULL; s->kobj.kset = ksudoku_set; result = kobject_init_and_add(&s->kobj, &ksudoku_type, NULL, "%s", name); if (result) { kobject_put(&s->kobj); return NULL; } atomic_set(&s->status, KSUDOKU_READY); // Let the world now about the new registered kobj kobject_uevent(&s->kobj, KOBJ_ADD); return s; } EXPORT_SYMBOL_GPL(ksudoku_create_sudoku); /** * ksudoku_destroy_ksudoku - destroys the given ksudoku instance. * @sudoku: Pointer to a ksudoku instance to destroy. */ void ksudoku_destroy_ksudoku(struct ksudoku *sudoku) { kobject_put(&sudoku->kobj); } EXPORT_SYMBOL_GPL(ksudoku_destroy_ksudoku); /** * ksudoku_is_cell_valid - Checks for a valid value in the given cell * @sudoku: Pointer to a ksudoku instance. * @v: The value to check * @row: the row number of the sudoku table * @col: the column number of the sudoku table */ bool ksudoku_is_valid_cell(struct ksudoku *s, int v, int row, int col) { int i=0; int box_row = 3 * (row / 3); int box_col = 3 * (col / 3); int row1 = (row + 2) % 3; int row2 = (row + 4) % 3; int col1 = (col + 2) % 3; int col2 = (col + 4) % 3; /* Check for the value in the given row and column */ for (i = 0; i < 9; i++) { if (s->matrix_array[i][col] == v) return false; if (s->matrix_array[row][i] == v) return false; } /* Check the remaining four spaces in this sector */ if (s->matrix_array[row1 + box_row][col1 + box_col] == v) return false; if (s->matrix_array[row2 + box_row][col1 + box_col] == v) return false; if (s->matrix_array[row1 + box_row][col2 + box_col] == v) return false; if (s->matrix_array[row2 + box_row][col2 + box_col] == v) return false; return true; } EXPORT_SYMBOL_GPL(ksudoku_is_valid_cell); static int ksudoku_init(void) { printk(KERN_INFO "Init ksudoku subsystem.\n"); // kernel_kobj is the kobject related to /sys/kernel ksudoku_set = kset_create_and_add("ksudoku", NULL, kernel_kobj); if (!ksudoku_set) return -ENOMEM; printk(KERN_INFO "Ready to register sudokus.\n"); return 0; } static void ksudoku_exit(void) { kset_unregister(ksudoku_set); printk(KERN_INFO "Exit ksudoku.\n"); } MODULE_AUTHOR("Sameer Rahmani "); MODULE_DESCRIPTION(KSUDOKU_DESC); MODULE_VERSION(KSUDOKU_VERSION); MODULE_LICENSE("GPL"); module_init(ksudoku_init); module_exit(ksudoku_exit);