Accessing the parallel port hardware on linux - c

I wrote a parallel port Linux device driver but I cant access the parallel port hardware
#include <linux/init.h>
#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <linux/ioport.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DRVNAME "parportleds"
#define BASEPORT 0x378
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Ganesh");
MODULE_DESCRIPTION("Parallel port LED driver.");
int parportleds_open(struct inode *inode, struct file *filp);
int parportleds_release(struct inode *inode, struct file *filp);
ssize_t parportleds_read(struct file *filp,char *buf,size_t count, loff_t *f_pos);
ssize_t parportleds_write(struct file *filp, const char *buf,size_t count, loff_t *f_pos);
void parportleds_exit(void);
int parportleds_init(void);
struct file_operations parportleds_fops = {
read: parportleds_read,
write: parportleds_write,
open: parportleds_open,
release: parportleds_release
};
int parportleds_major = 61;
int port;
module_init(parportleds_init);
module_exit(parportleds_exit);
int parportleds_init(void) {
int result;
result = register_chrdev(parportleds_major, DRVNAME,&parportleds_fops);
if (result < 0) {
printk("<1>parport: cannot obtain major number %d\n",parportleds_major);
return result;
}
port = check_region(BASEPORT, 1);
if (port) {
printk("<1>parportleds: cannot reserve 0x378 \n");
result = port;
goto fail;
}
request_region(BASEPORT, 1, DRVNAME);
printk("<1>Inserting parportled module\n");
return 0;
fail:
parportleds_exit();
return result;
}
void parportleds_exit(void) {
unregister_chrdev(parportleds_major, DRVNAME);
if (!port) {
release_region(BASEPORT,1);
}
printk("<1>Removing module parportleds\n");
}
int parportleds_open(struct inode *inode, struct file *filp) {
return 0;
}
int parportleds_release(struct inode *inode, struct file *filp) {
return 0;
}
ssize_t parportleds_read(struct file *filp, char *buf,size_t count, loff_t *f_pos) {
char parportleds_buffer;
parportleds_buffer = inb(BASEPORT);
copy_to_user(buf,&parportleds_buffer,1);
if (*f_pos == 0) {
*f_pos+=1;
return 1;
} else {
return 0;
}
}
ssize_t parportleds_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos) {
char *tmp;
char parportleds_buffer;
tmp=(char *)buf+count-1;
copy_from_user(&parportleds_buffer,tmp,1);
outb(parportleds_buffer,BASEPORT);
printk("<1>parport write: %d\n",parportleds_buffer);
return 1;
}
This is my code when I try to read and write to /dev/parportleds am getting a sucess msg in dmesg
But when i try to make a pin high of parallel port using echo 3 > /dev/parportleds
The voltage of the datapins of paralleport remains same.
can anyone help me to fix this issue.
its my first attempt to write device drivers.

A couple of notes first:
1) Take note of InternetSeriousBusiness' comment: when you echo a character to your char device, the device will receive the ASCII encoded bit pattern. Look at an ASCII table to see which character you need to send to get the bit pattern you want.
2) You mentioned that when you send a bit pattern, the output voltage doesn't change. What about the other direction? Does the value you read accurately reflect voltages applied to the pins, or are those off to? If yes, then you know problem has something to do with writing. If no, then you may not even be connected to the port the way you think you are.
My Answer:
Buffers that interact with user data should have the __user tag attached. For example, the argument list of parportleds_write(), should look like this:
ssize_t parportleds_write(struct file *fp, const char __user *buf, ssize_t count, loff_t *f_pos){
Your call to copy_from_user() may be failing because this is missing. The __user tag should be added to your read function as well.

Related

Registering a Midi Device in Modern Kernel Versions

I am currently writing a Linux Kernel module that will create a virtual Midi device so that a user-space program can send midi signals to any DAW-like software. I found a wonderful function to help me in my task: int register_sound_midi(const struct file_operations * fops, int dev). The problem is that this function ceased to exist after Kernel Version 4.15. From V15 to V16 the register and unregister functions just disappear. I'm now dumbfounded on how to register a fake midi device from the kernel in versions post 4.15. How can I register a midi device in versions of Kernel post 4.15? Is it possible to make the module backwards compatible with older kernel versions?
Current Code:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sound.h>
#include <sound/core.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Module to create dummy midi devices.");
MODULE_VERSION("0.01");
static int dummy_midi_open(struct inode *, struct file *);
static int dummy_midi_release(struct inode *, struct file *);
static ssize_t dummy_midi_read(struct file *, char *, size_t, loff_t *);
static ssize_t dummy_midi_write(struct file *, const char *, size_t, loff_t *);
static struct file_operations dummy_midi_fops = {
.owner = THIS_MODULE,
.read = dummy_midi_read,
.write = dummy_midi_write,
.open = dummy_midi_open,
.release = dummy_midi_release
};
static int minor;
static int dummy_midi_open(struct inode *midi_inode, struct file *midi_file)
{
printk(KERN_INFO"Open\n");
return 0;
}
static int dummy_midi_release(struct inode *midi_inode, struct file *midi_file)
{
printk(KERN_INFO"Release\n");
return 0;
}
static ssize_t dummy_midi_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
printk(KERN_INFO"Read\n");
return 0;
}
static ssize_t dummy_midi_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
printk(KERN_INFO"Write\n");
return 0;
}
static int __init midimodule_init(void)
{
printk(KERN_INFO"Initializing Module\n");
minor = register_sound_midi(&dummy_midi_fops,-1);
printk(KERN_INFO"Registered MIDI Device: %d\n",minor);
return 0;
}
static void __exit midimodule_exit(void)
{
printk(KERN_INFO"Leaving now\n");
unregister_sound_midi(minor);
printk(KERN_INFO"Unregistered MIDI Device: %d\n",minor);
}
module_init(midimodule_init);
module_exit(midimodule_exit);

Character device I/O across users

I'm learning Linux and I have the task that I can't handle. I have to create character device and make possible to read and write across users. For instance, I open first terminal and I use echo 'test' > /tmp/ringdev (where ringdev is my character device). I also open second terminal and I use cat /tmp/ringdev and I expect that output will be test. I have already read some example snippets, but all of them are just about the basics which I already have.
The question is how to read from / write to character device across users?
#define pr_fmt(fmt) "ringdev: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
DECLARE_WAIT_QUEUE_HEAD(head);
/*
* mutex used for access synchronization to buffer (ringdev_buf and ringdev_len)
*/
static struct mutex ringdev_lock;
/*
* buffer and number of written bytes in the buffer
*/
static char ringdev_buf[4096];
static size_t ringdev_len;
static int ringdev_open(struct inode *inode, struct file *filp)
{
printk("device_open called \n");
return 0;
}
static ssize_t ringdev_read(struct file *filp, char __user *buf, size_t count,
loff_t *off)
{
ssize_t ret = 0;
printk("device_read called \n");
/*
* access to ringdev_buf i ringdev_len is protected by ringdev_lock,
* take that lock
*/
//wait_event_interruptible(head,ringdev_len!=0);
mutex_lock(&ringdev_lock);
if (*off > ringdev_len)
count = 0;
else if (count >= ringdev_len - *off)
count = ringdev_len - *off;
/*
* for access to user memory special functions must be used,
* to copy to user memory copy_to_user must be used.
*/
ret = -EFAULT;
if (copy_to_user(buf, ringdev_buf + *off, count))
goto out_unlock;
ret = count;
*off += ret;
out_unlock:
mutex_unlock(&ringdev_lock);
ringdev_len=0;
return ret;
}
static ssize_t ringdev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *off)
{
ssize_t ret=0;
ringdev_len=0;
printk("device_write called \n");
mutex_lock(&ringdev_lock);
ret=-EFAULT;
if(copy_from_user(ringdev_buf+ringdev_len ,buf, count)==0){
ringdev_len= ringdev_len + count;
ret=count;
wake_up_interruptible(&head);
goto out_unlock;
}
else{
ret=-ENOSPC;
}
/*
* not supported yet. Do not forget about copy_from_user().
*/
out_unlock:
mutex_unlock(&ringdev_lock);
return ret;
}
static int ringdev_release(struct inode *inode, struct file *filp)
{
mutex_unlock(&ringdev_lock);
printk("device_release called \n");
return 0;
}
static const struct file_operations ringdev_fops = {
.owner = THIS_MODULE,
.open = ringdev_open,
.read = ringdev_read,
.write = ringdev_write,
.release = ringdev_release,
};
static struct miscdevice ringdev_miscdevice = {
.minor = MISC_DYNAMIC_MINOR,
.name = "ringdev",
.fops = &ringdev_fops
};
static int __init ringdev_init(void)
{
int ret;
mutex_init(&ringdev_lock);
ret = misc_register(&ringdev_miscdevice);
if (ret < 0) {
pr_err("can't register miscdevice.\n");
return ret;
}
pr_info("minor %d\n", ringdev_miscdevice.minor);
return 0;
}
static void __exit ringdev_exit(void)
{
misc_deregister(&ringdev_miscdevice);
mutex_destroy(&ringdev_lock);
}
module_init(ringdev_init);
module_exit(ringdev_exit);
MODULE_DESCRIPTION("Device");
MODULE_AUTHOR("xxx");
MODULE_LICENSE("GPL");

Uneven behavior in different system calls hooking

I am working on a project in which i have hooked the system open call. When a user attempts to open a file i want sys_open to block the action if the current task (pid or tgid that "black listed" ) has potential to leak the file out of the host.
Any ways, the hooking itself worked fine on sys_read and sys_write (I have some printk inside the fake function as an indicator).
but, when i try the hooking on the sys_open function, nothing is printed out - means that the override not succeeded.
I printed out the address of the sys call before and after the override so this might not be the issue.
I am confused about what can cause that uneven behavior when hooking different functions.
will be glad for some input here.
thanks !
dmesg output examples:
when hooked write -
...
[ 2989.500485] in my write ...
[ 2989.500585] in my write ...
when hooked open, noting printed, but here some "debug" output -
[ 890.709696] address found 00000000103d42f6
[ 890.709697] Address before - 0000000006d29c3a
[ 890.709698] Address after - 00000000a5117c6a
[ 948.533339] BYE !!!
using lubuntu vm (kernel v 4.15.0.20).
here is the source code:
#include <linux/init.h> // Macros used to mark up functions e.g., __init __exit
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/page.h>
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
#include <asm/cacheflush.h>
#include <linux/set_memory.h>
#include <linux/cred.h>
#include <linux/user.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ABC");
MODULE_VERSION("0.1");
asmlinkage long (*original_call)( char __user *filename, int flags, umode_t mode); // for read or write: (unsigned int fd, char __user *buf, size_t count);
asmlinkage long my_sys_READ(unsigned int fd, char __user *buf, size_t count);
asmlinkage long my_sys_WRITE(unsigned int fd, char __user *buf, size_t count);
asmlinkage long my_sys_OPEN( char __user *filename, int flags, umode_t mode);
unsigned long* find_sys_call_table(void);
void set_page_rw( unsigned long addr);
void set_page_ro( unsigned long addr);
const struct cred *_cred = NULL ;
struct user_struct *user =NULL ;
unsigned long* sys_call_table = NULL;
void set_page_rw(unsigned long addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
}
void set_page_ro( unsigned long addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
pte->pte = pte->pte &~_PAGE_RW;
}
/*
asmlinkage long my_sys_READ(unsigned int fd, char __user *buf, size_t count)
{
//_cred = current_cred();
user = get_current_user();
if( (int)(*user).uid.val == uid )
{
printk(KERN_ALERT"in my read ... hacked !");
return original_call(fd, buf, count);
}
printk(KERN_ALERT"in my read ... hacked !");
return original_call(fd, buf, count);
}
asmlinkage long my_sys_WRITE(unsigned int fd, char __user *buf, size_t count)
{
//_cred = current_cred();
user = get_current_user();
if( (int)(*user).uid.val == uid )
{
printk(KERN_ALERT"in my write ... hacked !");
return original_call(fd, buf, count);
}
printk(KERN_ALERT"in my write ... hacked !");
return original_call(fd, buf, count);
}
*/
asmlinkage long my_sys_OPEN( char __user *filename, int flags, umode_t mode)
{
printk(KERN_ALERT"in my open ... hacked !");
return original_call(filename, flags, mode);
}
unsigned long* find_sys_call_table(void)
{
return (unsigned long *)kallsyms_lookup_name("sys_call_table");
}
int init_module()
{
printk(KERN_ALERT "I'm dangerous. I hope you did a ");
printk(KERN_ALERT "sync before you insmod'ed me.\n");
sys_call_table = find_sys_call_table();
printk(KERN_INFO"address found %p \n",sys_call_table);
original_call = (void *)sys_call_table[__NR_open];
set_page_rw((unsigned long)sys_call_table);
printk(KERN_INFO" Address before - %p", (void *)sys_call_table[__NR_open]);
sys_call_table[__NR_open] = (unsigned long)my_sys_OPEN;
printk(KERN_INFO" Address after - %p", (void *)sys_call_table[__NR_open]);
return 0;
}
/*
* Cleanup − unregister the appropriate file from /proc
*/
void cleanup_module()
{
/*
* Return the system call back to normal
*/
if (sys_call_table[__NR_open] != (unsigned long)my_sys_OPEN) {
printk(KERN_ALERT "Somebody else also played with the ");
printk(KERN_ALERT "open system call\n");
}
printk(KERN_ALERT "BYE !!!\n");
sys_call_table[__NR_open] = (unsigned long)original_call;
}
Your Ubuntu version is based on glibc 2.27.
glibc version 2.26 switched to implementing open with openat:
commit b41152d716ee9c5ba34495a54e64ea2b732139b5
Author: Adhemerval Zanella <adhemerval.zanella#linaro.org>
Date: Fri Nov 11 15:00:03 2016 -0200
Consolidate Linux open implementation
This patch consolidates the open Linux syscall implementation on
sysdeps/unix/sysv/linux/open{64}.c. The changes are:
1. Remove open{64} from auto-generation syscalls.list.
2. Add a new open{64}.c implementation. For architectures that
define __OFF_T_MATCHES_OFF64_T the default open64 will create
alias to required open symbols.
3. Use __NR_openat as default syscall for open{64}.
You will have to hook openat in addition to open as a result.
Note that the Linux kernel provides a proper interface for this, in the form of the fanotify interface. If you use that, you will not have to worry about such details.

IOCTL call not working with driver

I wrote a IOCTL driver and a corresponding ioctl app with a header file containing commands.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include "myioctl.h"
#include <linux/ioctl.h>
#define NAME MyCharDevice
//Function Prototypes
int NAME_open(struct inode *inode, struct file *filp);
int NAME_release(struct inode *indoe, struct file *filp);
ssize_t NAME_write(struct file *filp, char __user *Ubuff, size_t count, loff_t *offp);
ssize_t NAME_read(struct file *filp, char __user *Ubuff, size_t count, loff_t *offp);
int NAME_flush (struct file *filp);
int NAME_IOCTL (struct inode *inode, struct file *filp, unsigned long cmd, unsigned long val);
//Structure that defines the operations that the driver provides
struct file_operations fops =
{
.owner = THIS_MODULE,
.open = NAME_open,
.read = NAME_read,
.write = NAME_write,
.unlocked_ioctl = NAME_IOCTL,
.release = NAME_release,
.flush = NAME_flush,
};
//Structure for a character driver
struct cdev *my_cdev;
//Init Module
static int __init CharDevice_init(void)
{
int result;
int MAJOR,MINOR;
dev_t Mydev;
Mydev = MKDEV(255,0);//Create a device number
MAJOR=MAJOR(Mydev);
MINOR=MINOR(Mydev);
printk("\nThe Major Number is %d...THe Minor Number is %d\n",MAJOR,MINOR);
result=register_chrdev_region(Mydev,1,"MyCharDevice");//register device region.....
if(result<0)
{
printk(KERN_ALERT "\nThe Region requested for is not obtainable\n");
return(-1);
}
my_cdev = cdev_alloc();//allocate memory to Char Device structure
my_cdev->ops = &fops;//link our file operations to the char device
result=cdev_add(my_cdev,Mydev,1);//Notify the kernel abt the new device
if(result<0)
{
printk(KERN_ALERT "\nThe Char Devide has not been created......\n");
return (-1);
}
return 0;
}
//Cleanup Module
void __exit CharDevice_exit(void)
{
dev_t Mydev;
int MAJOR,MINOR;
Mydev=MKDEV(255,0);
MAJOR=MAJOR(Mydev);
MINOR=MINOR(Mydev);
printk("\nThe Major Number is %d...THe Minor Number is %d\n",MAJOR,MINOR);
unregister_chrdev_region(Mydev,1);//unregister the device numbers and the device created
cdev_del(my_cdev);
printk(KERN_ALERT "\nI have unregistered the stuff that was allocated.....Goodbye for ever.....\n");
return;
}
int NAME_IOCTL (struct inode *inode, struct file *filp, unsigned long cmd, unsigned long val)
{
int BAUD=0, STOP;
char PARITY, CONFIG;
printk ("In IOCTL\n");
printk("command = %d %d val = %d\n", cmd, SET_BAUD, val);
switch (cmd) {
case SET_BAUD:
get_user (BAUD, (int *)val);
printk ("The baud is %d", BAUD);
case SET_PARITY:
case SET_STOP:
case READ_CONFIG:
default:
return -1;
}
return 0;
}
//Open System Call
int NAME_open(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "\nThis is the Kernel....Open Call.....I have nothing to do.....but YOU ALL HAVE....HAHAHAHA...\n");
return 0;
}
//Close System Call
int NAME_release(struct inode *indoe, struct file *filp)
{
printk(KERN_ALERT "\nThis is the release method of my Character Driver......Bye Dudes......\n");
return 0;
}
//Write Functionality
ssize_t NAME_write(struct file *filp, char __user *Ubuff, size_t count, loff_t *offp)
{
char Kbuff[80];
unsigned long result;
ssize_t retval;
//strcpy(Kbuff,Ubuff);
result=copy_from_user((char *)Kbuff,(char *)Ubuff,count); //get user data
if(result==0)
{
printk(KERN_ALERT "\nMessage from the user......\n>>>> %s <<<<\n",Kbuff);
printk(KERN_ALERT "\n Data Successfully Written.....\n");
retval=count;
return retval;
}
else
{
printk(KERN_ALERT "\n Error Writing Data\n");
retval=-EFAULT;
return retval;
}
}
//read Functionality
ssize_t NAME_read(struct file *filp, char __user *Ubuff, size_t count, loff_t *offp)
{
char Kbuff[]="THis is some date from the kernel to the user....User,ENJOY......";
unsigned long result;
ssize_t retval;
//strcpy(Kbuff,Ubuff);
result=copy_to_user((char *)Ubuff,(char *)Kbuff,sizeof(Kbuff)); //copy to user
if(result==0)
{
//printk("\nMessage from the user......\n>>>> %s <<<<\n");
printk(KERN_ALERT "\n Data Successfully read.....\n");
retval=count;
return retval;
}
else
{
printk(KERN_ALERT"\n Error Writing Data to User\n");
retval=-EFAULT;
return retval;
}
}
int NAME_flush (struct file *filp)
{
printk("\n This is the close function of the file....");
return 0;
}
//Module over ride functions
module_init(CharDevice_init);
module_exit(CharDevice_exit);
header file
#define MAGIC 'x'
#define SET_BAUD _IOW(MAGIC,0, int)
#define SET_PARITY _IOW(MAGIC, 1, char)
#define SET_STOP _IOW(MAGIC, 2, int)
#define READ_CONFIG _IOR(MAGIC, 3, int)
c file
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include "myioctl.h"
int main()
{
int FileDesc, Baud=9600;
// char Ubuff[]="THis is the User Buffer......Sending Data to the Kernel....";
// char Kbuff[100];
FileDesc=open("/dev/MyCharDevice",O_RDWR);
if(FileDesc <0)
{
printf("\nError Opening Device\n");
exit(1);
}
ioctl (FileDesc, SET_BAUD, &Baud);
printf("%d %d \n", SET_BAUD, &Baud);
// write(FileDesc,Ubuff,sizeof(Ubuff));
// read(FileDesc,Kbuff,sizeof(Ubuff));
// printf("\n The Data read from the Kernel is\n>>>> %s <<<<\n",Kbuff);
close(FileDesc);
}
I am printing in driver what are the command and value of the argument that print like this
command = 1622004312 1074034688 val = 1622004312
So sent command is equal to the argument I sent. Why this is happening?
I used older IOCTL prototype in my driver.
it should be of this type
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
static int my_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg)
#else
static long my_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
#endif
In my case against my kernel
static long my_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
is the correct type.

endlessly looping when reading from character device

For a homework assignment, I have written a character device driver. It seems to work OK. I can read and write it. The problem is that when I read the device, it endlessly loops, printing out the contents of the message buffer over and over.
This seems like it should be fairly straight forward. Just use copy_to_user(), but it's proven to be very problematic.
Anyway, here is the code. I think the problem is in the gdev_read() function.
The printk's are there to serve as debugging as well as talking points, since I have to present the project in class.
/*
* Implement a generic character pseudo-device driver
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
/* you need these, or the kernel will be tainted */
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple sample character device driver");
/*
* function prototypes
*/
int init_module(void);
void cleanup_module(void);
static ssize_t gdev_read(struct file *, char *, size_t, loff_t *);
static ssize_t gdev_write(struct file *, const char *, size_t, loff_t *);
static int gdev_open(struct inode *, struct file *);
static int gdev_release(struct inode *, struct file *);
/* macros */
#define TRUE 1
#define FALSE 0
#define MAX_MSG_LEN 64
/*
* global variables
*/
static dev_t dev_num; /* device number, for new device */
static char *mesg; /* buffer for message */
/* file operations structure, so my device knows how to act */
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = gdev_read,
.write = gdev_write,
.open = gdev_open,
.release = gdev_release,
};
/* character device struct. Declaired here, but initialized elsewhere */
struct cdev *gdev;
int init_module(void)
{
int err;
printk(KERN_ALERT "in init_module\n");
if(alloc_chrdev_region(&dev_num, 0, 1, "/dev/gdev")){
printk(KERN_INFO "Could not allocate device numbers\n");
printk(KERN_INFO "Module gdev not loaded\n");
return -1;
}
/* now I need to make the device and register it */
gdev = cdev_alloc();
gdev->owner = THIS_MODULE;
gdev->ops = &fops;
err = cdev_add(gdev, dev_num, 1);
if(err){
printk(KERN_NOTICE "Error %d adding gdev", err);
return err;
}
mesg = (char *)vmalloc(MAX_MSG_LEN);
printk(KERN_INFO "Module gdev successfully loaded.\n");
printk(KERN_INFO "gdev Major Number: %d\n", MAJOR(dev_num));
return 0;
}
void cleanup_module(void)
{
printk(KERN_ALERT "in cleanup_module\n");
unregister_chrdev_region(dev_num, 3);
vfree(mesg);
cdev_del( gdev );
printk(KERN_INFO "Module gdev unregistered\n");
}
static ssize_t gdev_read(struct file *filp, char *page,
size_t len, loff_t *offset)
{
ssize_t bytes = len < MAX_MSG_LEN ? len : MAX_MSG_LEN;
printk(KERN_ALERT "in gdev_read\n");
if(copy_to_user(page, mesg, bytes)){
return -EFAULT;
}
return bytes;
}
static ssize_t gdev_write(struct file *filp, const char *page,
size_t len, loff_t *offset)
{
ssize_t bytes = len < MAX_MSG_LEN ? len : MAX_MSG_LEN;
printk(KERN_ALERT "in gdev_write\n");
if(copy_from_user(mesg, page, bytes)){
return -EFAULT;
}
return bytes;
}
static int gdev_open(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "in gdev_open\n");
return 0;
}
static int gdev_release(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "in gdev_release\n");
/* doesn't do anything because it doesn't need too */
return 0;
}
If zero is not returned from read() (in your case gdev_read()), the read function will be called again.
To stop this you use the loff_t *offset parameter. Increment it by how many bytes you have read using (*offset) += bytes; after copy_to_user(). Next time read() is called, offset will be what you have incremented it to. Now just check how many bytes you have previously sent, and only send what you still have remaining. Your function should look like this:
static ssize_t gdev_read(struct file *filp, char *page,
size_t len, loff_t *offset)
{
ssize_t bytes = len < (MAX_MSG_LEN-(*offset)) ? len : (MAX_MSG_LEN-(*offset));
printk(KERN_ALERT "in gdev_read\n");
if(copy_to_user(page, mesg, bytes)){
return -EFAULT;
}
(*offset) += bytes;
return bytes;
}
You could use 'simple_read_from_buffer' function from 'linux/fs.h':
static ssize_t gdev_read(struct file *filep, char __user *buff, size_t count, loff_t *offp)
{
return simple_read_from_buffer(buff, count, offp, my_buffer, buffer_len);
}
'my_buffer' and 'buffer_len' are defined in your module.

Resources