/**
* A demo driver for simple Character device."
*
* simple_chrdev.c
*
* Author Tianze Sun
*
* E-mail [email protected]
*
* Fri Jun 2th 20:32:43 GMT 2006
*
* Current version: 1.1.0
*
* All rights reserved. Licensed under dual BSD/GPL license.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/signal.h>
#define SIMPLE_PHYS_ADDR 0x1FFFFF7C //Address
#define SIZE_OF_SIMPLE 2 //2 Bytes
#define SIMPLE_TIMER_DELAY 1 // 1 ms
#define SIMPLE_IOCTL_MAGIC 'K' //Magic
#define CMD1 _IOW(SIMPLE_IOCTL_MAGIC,3,unsigned long)
#define CMD2 _IOW(SIMPLE_IOCTL_MAGIC,4,unsigned long)
#define CMDn _IOW(SIMPLE_IOCTL_MAGIC,5,unsigned long)
#define SIMPLE_NAME "Simple"
int SIMPLE_MAJOR = 0;
int SIMPLE_MINOR = 0;
int SIMPLE_NUMBER = 1;
void __iomem *simple_addr_virt = NULL;
struct cdev *simple_cdev;
dev_t simple_dev;
int simple_open(struct inode *inode, struct file *filp);
int simple_release(struct inode *inode, struct file *filp);
int simple_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static ssize_t simple_read(struct file * file, char __user *buf, size_t count, loff_t *ppos);
static ssize_t simple_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos);
loff_t simple_llseek(struct file *file,loff_t offset, int origin);
static struct timer_list simple_timer;
static void simple_timer_handler(unsigned long data);
extern struct file_operations simple_fops;
struct file_operations simple_fops =
{
.owner = THIS_MODULE,
.llseek = simple_llseek,
.open = simple_open,
.read = simple_read,
.write = simple_write,
.ioctl = simple_ioctl,
.release = simple_release,
};
/* Open */
int simple_open(struct inode *inode, struct file *filp)
{
printk("Simple Device is opened\n");
try_module_get(THIS_MODULE) ;
return 0 ;
}
/* Release */
int simple_release(struct inode *inode, struct file *filp)
{
printk("Simple Device is released!\n");
module_put(THIS_MODULE) ;
return 0 ;
}
static ssize_t simple_read(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{
int rv = 0; /* Return Value */
if (count <= 0)
return count;
printk("Reading Data now...\n");
if (file->f_flags & O_NONBLOCK) {
rv = -EAGAIN;
return rv;
}
//Get data from Kernel space.
return rv;
}
/* Write */
static ssize_t simple_write(struct file *file,const char __user *buf,size_t len,loff_t *ppos)
{
int rv = 0;
printk("Writing Data now...\n");
//Get data from User space.
return rv;
}
/* Llseek */
loff_t simple_llseek(struct file *file, loff_t offset, int origin)
{
long long retval = -EINVAL;
switch (origin) {
case 1:
offset += file->f_pos;
retval = offset;
case 0:
if (offset < 0)
break;
retval = offset;
}
return retval;
}
/* Ioctl */
int simple_ioctl( struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{
switch( cmd ) {
case CMD1:
printk("First Command.\n");
//do something...
break;
case CMD2:
printk("Second Command.\n");
//do something...
break;
case CMDn:
printk("Still a Command.\n");
//do something...
break;
default:
return -ENOTTY;
}
return -EINVAL;
}
static void simple_timer_handler( unsigned long data)
{
int Condition = 1;
simple_timer.expires = jiffies + SIMPLE_TIMER_DELAY;
simple_timer.function = &simple_timer_handler;
add_timer (&simple_timer);
if(Condition)
printk("Everything is fine.\n");
else
{
printk("We don't need timer anymore...\n");
del_timer(&simple_timer);
}
return ;
}
static int __init simple_init(void)
{
int ret;
/* register major and minor */
if(SIMPLE_MAJOR)
{
simple_dev = MKDEV(SIMPLE_MAJOR, SIMPLE_MINOR);
ret = register_chrdev_region(simple_dev,SIMPLE_NUMBER,SIMPLE_NAME);
}
else
{
/* Dynamic assign major */
ret = alloc_chrdev_region(&simple_dev, SIMPLE_MINOR,SIMPLE_NUMBER,SIMPLE_NAME);
SIMPLE_MAJOR = MAJOR(simple_dev);
}
if(ret<0)
{
printk(KERN_ERR "Cannot get major %d!\n",SIMPLE_MAJOR) ;
return ret;
}
/* Register Character Device Driver */
simple_cdev = cdev_alloc() ;
if(simple_cdev != NULL)
{
cdev_init(simple_cdev, &simple_fops) ;
simple_cdev->ops = &simple_fops ;
simple_cdev->owner = THIS_MODULE ;
if(cdev_add(simple_cdev, simple_dev, SIMPLE_NUMBER))
printk(KERN_NOTICE "Someting wrong when adding simple_cdev!\n");
else
printk("Success adding simple_cdev!\n");
}
else
{
printk(KERN_ERR "Register simple_dev error!\n") ;
return -1 ;
}
/* Request region */
if(!request_mem_region(SIMPLE_PHYS_ADDR,SIZE_OF_SIMPLE,SIMPLE_NAME))
{
printk(KERN_ERR "simple: IO %X is not free.\n", SIMPLE_PHYS_ADDR);
return -EBUSY;
}
/* Memory map */
simple_addr_virt = ioremap(SIMPLE_PHYS_ADDR, SIZE_OF_SIMPLE );
if (!simple_addr_virt) {
printk (KERN_ERR "simple: ioremap failed.\n");
iounmap(simple_addr_virt);
return -EINVAL;
}
/* Register timer */
init_timer(&simple_timer);
simple_timer.function = &simple_timer_handler;
simple_timer.expires = jiffies + SIMPLE_TIMER_DELAY;
return 0;
}
static void __exit simple_exit(void)
{
dev_t devno = MKDEV(SIMPLE_MAJOR,SIMPLE_MINOR);
printk("Unloading simple_cdev now...\n");
unregister_chrdev_region(devno,SIMPLE_NUMBER);
release_mem_region(SIMPLE_PHYS_ADDR,SIZE_OF_SIMPLE);
iounmap(simple_addr_virt);
cdev_del(simple_cdev);
}
module_init(simple_init);
module_exit(simple_exit);
MODULE_AUTHOR("Tianze SUN");
MODULE_DESCRIPTION("SIMPLE Character");
MODULE_LICENSE("dual BSD/GPL");