/* 
gcc -D__KERNEL__ -DMODULE -Wall -O2 -DASSEMBLY -I/usr/include \
 -c -o create_proc_entry.o create_proc_entry.c
gcc -D__KERNEL__ -DMODULE -Wall -O2 -DASSEMBLY -I/usr/include \
 -D__SMP__ -c -o create_proc_entry.o create_proc_entry.c
*/

#include <linux/autoconf.h>

#if defined(CONFIG_MODVERSIONS)
#include <linux/modversions.h>
#endif

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>

static int read_hoge(char * buf, char * * start,
 off_t offset, int length, int * eof, void * data ) {
  int len;
    /* You do not need to mind about "start", "offset", "length"
       and "eof" if the data is smaller than the size of a page.
       If the value of "start" is kept 0, the caller
       regards the data is smaller than the page. */
    len = 0;
    len += sprintf(buf + len, "hoge\n");
    return (len);
}

/* always null terminated */
static char sz_[65536] = "fuga\n";

static int read_fuga(char * buf, char * * start,
 off_t offset, int buf_len, int * eof, void * data ) {
    strncpy(buf, & (sz_[offset]), buf_len);
    (* start) = buf;
    if (((int) (strlen(sz_) - offset)) <= buf_len) {
        (* eof) = 1;
        buf_len = strlen(buf);
    }
    return (buf_len);
}

static int write_fuga(struct file * pfile, const char * buf,
 unsigned long input_length, void * data ) {
  static struct file * pfilePrev = (void *) 0;
  static int offset;
    if (pfile != pfilePrev) {
        offset = 0;
    }
    /* Oops! Though this works, why? */
    /* memcpy(& (sz_[offset]), buf, input_length); */
    copy_from_user(& (sz_[offset]), buf, input_length);
    offset += input_length;
    sz_[offset] = '\0';
    pfilePrev = pfile;
    return (input_length);
}

struct proc_dir_entry * pentry_dir_;

int init_module() {
  struct proc_dir_entry * pentry_file;
    pentry_dir_ = create_proc_entry(
     "hoge_dir", /* const char * name */
     S_IFDIR,    /* mode_t mode (/usr/include/linux/stat.h) */
     0 );        /* struct proc_dir_entry * parent */
    pentry_file = create_proc_entry("hoge_file", 0, pentry_dir_);
    if (pentry_file) {
        pentry_file->read_proc = read_hoge;
    }
    /* You can specify the name of the entry in the relative
       pathname from the root of procfs. */
    pentry_file = create_proc_entry("hoge_dir/fuga_file", 0, 0);
    if (pentry_file) {
        pentry_file->read_proc = read_fuga;
        pentry_file->write_proc = write_fuga;
    }
    return (0);
}

void cleanup_module() {
    remove_proc_entry(
     "hoge_file",   /* const char * name */
     pentry_dir_ ); /* proc_dir_entry * parent */
    remove_proc_entry("hoge_dir/fuga_file", 0);
    remove_proc_entry("hoge_dir", 0);
}

