289 lines
8.2 KiB
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);
|