/* my_time.c
 * 
 * author : marco corvi <marco_corvi@geocities.com>
 * date   : feb 2001
 *
 * proc time-module
 * "Kernel Projects for Linux" G. Nutt - Assignment nr. 4
 * See also Pomerantz article for a lot of details.
 *
 * This module provides the local system time through the proc
 * file system. The file "/proc/my_time" contains the local time. 
 *
 * Compile
 *           gcc -Wall -DMODULE -D__KERNEL__ -DLINUX -o my_time.o -c my_time.c
 *           ld -m elf_i386 -r -o my_time my_time.o
 * Install
 *           sync; insmod ./my_time
 * or
 *           sync; insmod ./my_time.o
 * Uninstall
 *           sync; rmmod my_time
 */
#include <linux/kernel.h>
#include <linux/module.h>

// #if CONFIG_MODVERSIONS==1
// #  define MODVERSIONS
// #  include <linux/modversions.h>
// #endif

#include <linux/proc_fs.h>

#include <linux/mm.h>
#include <linux/unistd.h>

extern struct timeval xtime;

#define LENGTH 128
#define STR_LEN 17

/*
 * Define the symbol DEBUG if you want info printouts
 */
// #define DEBUG

/*
 * Define the symbol LOCAL_BUFFER if you want to use a
 * buffer local to the get_info() routine, otherwise a
 * system provided buffer (1024 B) is used.
 */
#define LOCAL_BUFFER


int my_time_info( char *  kbuf, 
               char ** buf_loc,
               off_t   offset,
               int     length,
               int     zero)
{
  int len=0;
# ifdef LOCAL_BUFFER
    static char my_time_buffer[ LENGTH ];
# endif

# ifdef DEBUG
    printk("my_time_info : kbuf %p buf_loc %p offset %d length %d\n",
      kbuf, buf_loc, (int)offset, length);
# endif

# ifdef LOCAL_BUFFER
    if (offset > LENGTH - STR_LEN) {
#     ifdef DEBUG
        printk("my_time_info returns EOF\n");
#     endif
      return 0;          // EOF
    }
    len = sprintf(my_time_buffer+offset, "%ld %ld\n",
      (unsigned long)xtime.tv_sec,
      (unsigned long)xtime.tv_usec );
    *buf_loc = my_time_buffer+offset;
#   ifdef DEBUG
      printk("returning %s\n", my_time_buffer+offset);
#   endif
# else
    if (offset > length - STR_LEN) {
#     ifdef DEBUG
        printk("my_time_info returns EOF\n");
#     endif
      return 0;          // EOF
    }
    len = sprintf(kbuf+offset, "%ld %ld\n",
      (unsigned long)xtime.tv_sec,
      (unsigned long)xtime.tv_usec );
    *buf_loc = kbuf+offset;
#endif // LOCAL_BUFFER


# ifdef DEBUG
    printk("my_time_info return %d \n", len);
# endif
  return len;
}

struct proc_dir_entry my_time_file = {
  0,                   // inode: kernel assigned dynamically
  7,                   // name length
  "my_time",           // name
  S_IFREG | S_IRUGO,   // file mode: regular, u-g-o readable
  1,                   // number of links
  0, 0,                // uid, gid (root)
  LENGTH,              // ls size
  NULL,                // time iops
  my_time_info,        // time get_info()
  NULL                 // time inode fill
};

int init_module() 
{ 
  int res;
  res = proc_register( &proc_root, &my_time_file );
  printk("proc register my_time: low_ino %d \n", my_time_file.low_ino);
  return res;
}

void cleanup_module()
{
  proc_unregister( &proc_root, my_time_file.low_ino );
  printk("proc unregister my_time.\n");
}

