ksudoku/ksudoku.c

289 lines
8.2 KiB
C

/**
* KSudoku subsystem for Linux kernel to solve sudoku with different
* algorithms.
* Copyright (C) 2020 Sameer Rahmani <lxsameer@gnu.org>
*
* 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 <lxsameer@gnu.org>");
MODULE_DESCRIPTION(KSUDOKU_DESC);
MODULE_VERSION(KSUDOKU_VERSION);
MODULE_LICENSE("GPL");
module_init(ksudoku_init);
module_exit(ksudoku_exit);