ksudoku/ksudoku.c

249 lines
6.6 KiB
C

#include <linux/init.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <asm/atomic.h>
#include <linux/slab.h>
#define KSUDOKU_VERSION "0.1"
#define KSUDOKU_DESC "Kernel Sudoku Module"
#define KSUDOKU_READY 0
#define KSUDOKU_BUSY 1
struct ksudoku {
struct kobject kobj;
const struct attribute_group *attr_group;
char *matrix;
atomic_t status;
};
#define to_ksudoku(obj) container_of(obj, struct ksudoku, kobj)
struct ksudoku_attribute {
struct attribute attr;
ssize_t (*show)(struct ksudoku *s, struct ksudoku_attribute *attr,
char *buf);
ssize_t (*store)(struct ksudoku *s, struct ksudoku_attribute *attr,
const char *buf, size_t count);
bool value;
};
#define to_ksudoku_attr(_attr) container_of(_attr, struct ksudoku_attribute, attr)
/*
* 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)
{
return sprintf(buf, "%s", 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);
if (status == KSUDOKU_BUSY)
return -EBUSY;
if (len != 81)
return -EIO;
strncpy(s->matrix, buf, len);
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;
static struct ksudoku *create_ksudoku(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;
}
static void destroy_ksudoku(struct ksudoku *s)
{
kobject_put(&s->kobj);
}
static int ksudoku_init(void)
{
printk(KERN_ALERT "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_ALERT "Ready to register sudokus.\n");
/* sudoku = kmalloc(sizeof (struct ksudoku), GFP_KERNEL); */
/* atomic_set(&sudoku->status, 0); */
/* sudoku->attr_group = &attr_group; */
/* if (!sudoku) */
/* return -ENOMEM; */
/* sudoku->kobj = kobject_create_and_add("ksudoku", kernel_kobj); */
/* if (!&sudoku->kobj) */
/* return -ENOMEM; */
/* sysfs_result = sysfs_create_group(sudoku->kobj, sudoku->attr_group); */
/* if (sysfs_result) */
/* kobject_put(sudoku->kobj); */
return 0;
}
static void ksudoku_exit(void)
{
//kobject_put(sudoku->kobj);
kset_unregister(ksudoku_set);
printk(KERN_ALERT "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);