/*
 *  devcabal.c
 *  The /dev/cabal kernel Module.
 *  Copyright (c) 2004 Mohammed Sameer <uniball@gmx.net>, All rights reserved.
 *  GPL, If it breaks, Keep the 2 pieces.
 *  We are everywhere!
 *  Greetings to all people in Omraneia!
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>/* for put_user */

/*  
 *  Prototypes - this would normally go in a .h file
 */
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

#define SUCCESS 0
#define DEVICE_NAME "cabal"/* Dev name as it appears in /proc/devices   */
#define BUF_LEN 80/* Max length of the message from the device */

/* 
 * Global variables are declared as static, so are global within the file. 
 */

static int Major;/* Major number assigned to our device driver */
static int Device_Open = 0;/* Is device open?  
 * Used to prevent multiple access to device */
static char *msg_Ptr = ""
"3%3%3%33%33%3%3%3%3%%%%%%%3%%%3%%%%%%%%%x%xxxx)-:%%]%,=|=:;:=||%]x>-::=:;:==;x%%\n"
"3%3%33%333%33%3%3]!3%3%3%3%%3%3%3%3%%%%xxxxx]x---~~~:::|%xx3]|%++|;.;;;;;;;;|x%%\n"
"3%33%33%3%33%33%)..+3%%%3%3%3%%3%%%%%%%x%xxxxx.......:........~---`:;;:;:;=;|%%%\n"
"33%33%33%33%3%]`....:%%3%%3%3%%%]~-..-\"x%xxxx].... . -|_;;|_,...-.-:;;;;:==;x%%3\n"
"mm%3%33%33%3%+......:.!%%3%3%%%+:::....:!%xx]..:__;;..?33%333%nx_a.:;;;;;=;=x%3%\n"
"3%%333%33%33|x|a_a_a_a;3%%3%%%%:|x_aa;]:|%%x]:x]%O%|;_jx%uu3%%3>%~.:;;;=;=;|%%3%\n"
"3%3%3%33%33%:%%3333%3%x%3%3%33%;|]3%%%+|x%%xx;|!x|x%]++]uu3%;++`;;:;;;=::;:|3%%3\n"
"%3%3%%%%%%%%++x3%333]%%%%3%3%%3nvx|%xx|x%%%%%)-]]%%]||:.:|q-:::-:::::|;:=;:x%3%3\n"
"3x%%%%%]+=+++]|u%3%3v|]]=+|?!mWZG+]%%xdm&g%%x) :-xx|xx...:=|;;__..::=;;;;:;%%3%3\n"
"%%%x%||=++++]+]|%3%%x]=++]+]+=3UZqguuOXWUWUm>:---..-.....x++|+x||:::;:;;:=:%3%%3\n"
"q3%|=++]=++]+]+]++%3]=+]+]+++++?443XXXXWWUW&).:.:.:.....-|%3%%%+:.:=:;::;:|3%3%%\n"
"]%c|++++=+++++=+=+|x|uv+++]++x=]|3XXu3u3d4ZQ:.--....-..-.JZ+~+|:.::;=:;;;;xx%%%3\n"
"|+|]v==|=++]+]xxxxx|x|++++|x||+]|=3XXdx]~........-.:.-.-.?C) ::.aamyaya|v:xx%%3%\n"
"|||=;!n=:=++|+x]]]+]++++====||]=]=+?4X^..::.............-|3||.:|xn4QO]:+p|x%%%%%\n"
"|z||]=x?xxax>|x%%x]]]]++++==|+++||=+|~..:|xx;;.:......-.:|3>.;:x+|~?|]|xx;:+%%%%\n"
"|||++||:]]!m|vx3333%xx]]]+++=]||==++;..:%33%]ux||::....:||3]xu%;|v?%|xuO%%||%%%%\n"
"jC=||||||ku%4GxXXX3O3uuuxuudx|]]+=||:...:!!3%33]xx:.:.:%]v|+3+%|u]u]3x%3%~:+%%%%\n"
"|x||x||x|!!!||+ZO**33mOmOO3!?%];+|aaa.-..:+%%]2n%|;x|xonc]%!O?Cxpnx?Cx|]%OO|%xxx\n"
"mmyd#quxd##000W2>x|%!337+-:-!!!!XH*]|~:.. .+~~+%%:|3]j%|nox|3+g|]p|*|u%!ux|xx]xx\n"
"HNW3#mmyx3**]++|=|;-.....:. :..:+>|||x::...:_;;|ex+x|4*xxouv?v]|%|*|j]?3|a+v%||x\n"
"AKmp33Oqgquxxaa||xx_|:\"\"~.:..__||;:n|>||:_;_|v]+|x|]34]|xxxn|+|;]?!|]x7||+x+|?]|\n"
"X3S3x23XdOHQHWKb|uugu%2Ox%udXUXmu;:?=]|xuxnn;=?+|u|v+]?]]xoxo+3+]!|%|]_x:|vnx3%n\n"
"OOX]]33X4WXGXH&XG3XX4Xmx%%dX4UUUX|xnx||xdXOOOu?]3|=]?]?C?;xvxv+]?+?x>||v+x|u|x|%\n"
"3WW#ZRZ#WmQQWWWUXOd4XXXG23OOXXXX3%uXu!|333!!\"^~++]|||3+?]]|n|n|!|n||x|||+]|%=x|:\n"
"dQ#0&XU4ZQZXQWmZ&4ZG23d3X%3XX3XOCx]XO|u%3,....__undnunx3%%%%3x%3%%xo]]n|%+a||+]+\n"
"";

static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};

/*
 * Functions
 */

int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);

if (Major < 0) {
printk("Registering the character device failed with %d\n",
       Major);
return Major;
}

printk("<1>I was assigned major number %d.  To talk to\n", Major);
printk("<1>the driver, create a dev file with\n");
printk("'mknod /dev/cabal c %d 0'.\n", Major);
printk("<1>Try various minor numbers.  Try to cat and echo to\n");
printk("the device file.\n");
printk("<1>Remove the device file and module when done.\n");

return 0;
}

void cleanup_module(void)
{
/* 
 * Unregister the device 
 */
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0)
printk("Error in unregister_chrdev: %d\n", ret);
}

/*
 * Methods
 */

/* 
 * Called when a process tries to open the device file, like
 * "cat /dev/mycharfile"
 */
static int device_open(struct inode *inode, struct file *file)
{
if (Device_Open)
  {
return -EBUSY;
  }
Device_Open++;
try_module_get(THIS_MODULE);

return SUCCESS;
}

/* 
 * Called when a process closes the device file.
 */
static int device_release(struct inode *inode, struct file *file)
{
Device_Open--;/* We're now ready for our next caller */

/* 
 * Decrement the usage count, or else once you opened the file, you'll
 * never get get rid of the module. 
 */
module_put(THIS_MODULE);

return 0;
}

/* 
 * Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp,/* see include/linux/fs.h   */
   char *buffer,/* buffer to fill with data */
   size_t length,/* length of the buffer     */
   loff_t * offset)
{
/*
 * Number of bytes actually written to the buffer 
 */
int bytes_read = 0;

/*
 * If we're at the end of the message, 
 * return 0 signifying end of file 
 */
if (*msg_Ptr == 0)
return 0;

/* 
 * Actually put the data into the buffer 
 */
while (length && *msg_Ptr) {

/* 
 * The buffer is in the user data segment, not the kernel 
 * segment so "*" assignment won't work.  We have to use 
 * put_user which copies data from the kernel data segment to
 * the user data segment. 
 */
put_user(*(msg_Ptr++), buffer++);

length--;
bytes_read++;
}

/* 
 * Most read functions return the number of bytes put into the buffer
 */
return bytes_read;
}

/*  
 * Called when a process writes to dev file: echo "hi" > /dev/hello 
 */
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
printk("<1>/dev/cabal is read only, You can only be absorbed, but not force yourself!.\n");
return -EINVAL;
}
