/*-------Code for the Character driver------*/
/* Header files Include*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
//#include <linux/kdev_t.h>
/* Define parameter used in char driver */
#define LTE_MAJOR 100
#define LTE_MINOR 0
#define LTE_DEVNAME "/dev/lte"
#define DEV_COUNT 1
#define BUFF_LEN 12288
#define DATA_TO_KERNEL 1
#define DATA_TO_USER 2
MODULE_LICENSE("DUAL BSD/GPL");
MODULE_AUTHOR("MILIND");
/* Global Declaration */
dev_t lte_dev;
int device_open =0;
wait_queue_head_t lte_poll_wq;
char *message_ptr = NULL;
unsigned int write_poll_data =0;
/* Structure defination */
struct cdev *lte_cdev;
/*============================================================================================================*/
/* Open and Release Function */
static int lte_dev_open(struct inode *inode ,struct file *file)
{
printk(KERN_INFO"device open (%p,%p)\n", inode, file);
/* we don't want to talk to two processes at the sametime */
if(device_open)
{
printk(KERN_INFO"Please Check device is Open");
return -EBUSY ;
}
device_open++;
return 0;
}
/*============================================ZZZZZZZZZZZZZZZ================================================*/
static int lte_dev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO"device release (%p,%p)\n", inode, file);
device_open--;
return 0;
}
/*============================================ZZZZZZZZZZZZZZZ================================================*/
/* using mmap() function*/
//struct vm_area_struct *vma;
static int lte_dev_mmap(struct file *file, struct vm_area_struct *vma)
{
printk("Hello Neo\n");
#if 1
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys((void*)((unsigned long)message_ptr)) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, PAGE_SHARED)!= 0)
{
return -EAGAIN;
}
#endif
#if 0
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys((void*)((unsigned long)message_ptr)) >> PAGE_SHIFT,
BUFF_LEN, PAGE_SHARED)!= 0)
{
return -EAGAIN;
}
#endif
printk("message_ptr %x\n", message_ptr);
printk("message_ptr physical %x\n", virt_to_phys((void*)((unsigned long)message_ptr)) >> PAGE_SHIFT);
printk("Hello Neo Success\n");
return 0;
}
/*============================================ZZZZZZZZZZZZZZZ================================================*/
static long lte_dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int i;
unsigned char message[BUFF_LEN];
#if 0
for (i=0;i<BUFF_LEN;i++)
{
message[i]= i+3;
printk("%d\t", message[i]);
}
#endif
printk("\ncmd=%d\n",cmd);
switch(cmd)
{
case DATA_TO_KERNEL:
printk(KERN_INFO"Receving Data from User = %p\n", message_ptr);
for (i=0;i<BUFF_LEN;i++)
{
printk("%d\t", message_ptr[i]);
}
printk("\n");
break;
case DATA_TO_USER:
for (i=0;i<BUFF_LEN;i++)
{
message[i]= i+5;
printk("%d\t", message[i]);
}
printk("\n");
memcpy(message_ptr,message,BUFF_LEN);
write_poll_data = 1;
wake_up_poll(<e_poll_wq, 1);
// printk(KERN_INFO"Sending Data to User = %d\n", message[0]);
break;
default :
break;
}
return 0;
}
static unsigned int lte_dev_poll (struct file *fd, poll_table *wait)
{
int mask = 0;
poll_wait(fd, <e_poll_wq, wait);
if (write_poll_data) {
printk(KERN_INFO"\nInvoke User for reading the data\n");
mask = (POLLIN | POLLRDNORM);
write_poll_data = 0; /* This is for taking next data */
}
return mask;
}
/*============================================ZZZZZZZZZZZZZZZ================================================*/
static const struct file_operations lte_dev_fops=
{
.open = lte_dev_open,
.unlocked_ioctl = lte_dev_ioctl,
.mmap = lte_dev_mmap,
.poll = lte_dev_poll,
.release = lte_dev_release
};
/*============================================ZZZZZZZZZZZZZZZ================================================*/
/* Declaration of init and exit function*/
static int lte_dev_init(void)
{
int ret_val;
printk(KERN_INFO"LTE DEVICE REGISTERING\n ");
/* Register the lte_dev*/
if (LTE_MAJOR)
{
lte_dev = MKDEV(LTE_MAJOR, LTE_MINOR);
ret_val= register_chrdev_region(lte_dev, DEV_COUNT, LTE_DEVNAME);
printk("ret_val = %d \n", ret_val);
}
else
{
ret_val = alloc_chrdev_region(<e_dev, LTE_MINOR, DEV_COUNT, LTE_DEVNAME);
printk(KERN_INFO"Hello\n");
printk(KERN_INFO"ret_val = %d \n", ret_val);
}
if (ret_val < 0)
{
printk(KERN_WARNING "lte: Could not get major number %d\n", LTE_MAJOR);
return ret_val;
}
lte_cdev = cdev_alloc();
if(lte_cdev != NULL)
{
lte_cdev->owner = THIS_MODULE;
lte_cdev->ops = <e_dev_fops;
ret_val = cdev_add(lte_cdev, lte_dev,DEV_COUNT);
}
if (ret_val< 0)
{
printk(KERN_ALERT"%s failed with %d\n", "Sorry, registering the char_dev",ret_val);
return 0;
}
init_waitqueue_head(<e_poll_wq);
message_ptr = (char *)kmalloc(BUFF_LEN + 2 * PAGE_SIZE, GFP_KERNEL);
if (!message_ptr) {
printk("kmalloc failed\n");
return 0;
}
printk(KERN_INFO "%s The major device number is %d. \n","Registeration is a success", LTE_MAJOR);
printk(KERN_INFO "If you want to talk to the device driver you'll have to create a device file.\n");
printk(KERN_INFO "We suggest you use: mknod %s c %d 0\n ", LTE_DEVNAME, LTE_MAJOR);
printk(KERN_INFO "The device file name is important, because the ioctl program assumes that's the file you'll use.\n ");
return ret_val;
}
/*============================================ZZZZZZZZZZZZZZZ================================================*/
static void lte_dev_exit(void)
{
//int ret;
printk(KERN_INFO"Device succesfully unregistered");
cdev_del(lte_cdev);
unregister_chrdev_region(lte_dev, DEV_COUNT);
//if (ret<0)
// printk(KERN_ALERT"Error: unregistered_device: %d\n",ret);
//return ret;
}
module_exit(lte_dev_exit);
module_init(lte_dev_init);