I wrote a small hello world type of character device driver.
When I type echo -n "abcdef" > /dev/bond
and do a cat /dev/bond
then only last "f" of above input abcdef is displayed rest nothing is displayed.

I have experimented many things but I am unable to catch the error.

Can some one point out the error?
Here is the code

/* Necessary includes for device drivers */
#include <linux/init.h>
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#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/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */

MODULE_LICENSE("Dual BSD/GPL");

/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);

/* Structure that declares the usual file */
/* access functions */
struct file_operations memory_fops = {
  read: memory_read,
  write: memory_write,
  open: memory_open,
  release: memory_release
};
/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);

/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;

int memory_init(void) {
  int result;

  /* Registering device */
  result = register_chrdev(memory_major, "bond", &memory_fops);
  if (result < 0) {
    printk(KERN_ALERT  "memory: cannot obtain major number %d\n", memory_major);
    return result;
  }

  /* Allocating memory for the buffer */
  memory_buffer = kmalloc(1, GFP_KERNEL); 
  if (!memory_buffer) { 
    result = -ENOMEM;
    goto fail; 
  } 
  memset(memory_buffer, 0, 10);

  printk(KERN_ALERT "Inserting bond module\n"); 
  return 0;

  fail: 
    memory_exit(); 
    return result;
}


void memory_exit(void) {
  /* Freeing the major number */
  unregister_chrdev(memory_major, "bond");

  /* Freeing buffer memory */
  if (memory_buffer) {
    kfree(memory_buffer);
  }

  printk( KERN_ALERT "Removing bond module\n");

}


int memory_open(struct inode *inode, struct file *filp) {

  /* Success */
  return 0;
}


int memory_release(struct inode *inode, struct file *filp) {
 
  /* Success */
  return 0;
}


ssize_t memory_read(struct file *filp, char *buf, 
                    size_t count, loff_t *f_pos) { 
 
  /* Transfering data to user space */ 
  copy_to_user(buf,memory_buffer,10);

  /* Changing reading position as best suits */ 
  if (*f_pos == 0) { 
    *f_pos+=1; 
    return 1; 
  } else { 
    return 0; 
  }
}

ssize_t memory_write( struct file *filp, char *buf,
                      size_t count, loff_t *f_pos) {

  char *tmp;

  tmp=buf+count-1;
  copy_from_user(memory_buffer,tmp,10);
  return 1;
}

Recommended Answers

All 4 Replies

I haven't worked with linux driver programming at this level just yet, so some of the functions and conventions are unfamiliar to me. So with that, I have an observation which you may correct me on if I'm wrong.

In memory_write(), you have tmp=buf+count-1;, and please correct me if im wrong, but isn't this command taking the string tmp and pushing it count bytes ahead of buf which means only the last byte will be seen by the function copy_from_user? Perhaps its intentional, but unless count is zero, I don't see what the point is of that command.

Ok I have got every thing perfectly working as I wanted.If some one wants to try I am posting my code

/* Necessary includes for device drivers */
#include <linux/init.h>
//#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#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/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_from/to_user */

MODULE_LICENSE("Dual BSD/GPL");

/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);

/* Structure that declares the usual file */
/* access functions */
struct file_operations memory_fops = {
	read: memory_read,
   	write: memory_write,
	open: memory_open,
	release: memory_release
};
/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);

/* Buffer to store data */
char *memory_buffer;
int result;

int memory_init(void) {

	/* Registering device */
	result = register_chrdev(0, "bond", &memory_fops);
	if (result < 0) {
		printk(KERN_ALERT  "memory: cannot obtain major number \n");
		return result;
	}

	/* Allocating memory for the buffer */
	memory_buffer = kmalloc(10, GFP_KERNEL); 
	if (!memory_buffer) { 
		result = -ENOMEM;
		goto fail; 
	} 
	memset(memory_buffer, 0, 10);

	printk(KERN_ALERT "Inserting bond module\n"); 
	return 0;

fail: 
	memory_exit(); 
	return result;
}


void memory_exit(void) {
	/* Freeing the major number */
	unregister_chrdev(result, "bond");

	/* Freeing buffer memory */
	if (memory_buffer) {
		kfree(memory_buffer);
	}

	printk( KERN_ALERT "Removing bond module\n");

}


int memory_open(struct inode *inode, struct file *filp) {

	/* Success */
	return 0;
}


int memory_release(struct inode *inode, struct file *filp) {

	/* Success */
	return 0;
}


ssize_t memory_read(struct file *filp, char __user *buf, 
		size_t count, loff_t *f_pos) 
{
	if (*f_pos > 0)
		return 0;
	if (count > strlen(memory_buffer))
		count = strlen(memory_buffer);
	copy_to_user(buf,memory_buffer,count);
//	*f_pos = *f_pos + count;

	return count; 
}

ssize_t memory_write( struct file *filp, const char __user *buf,
		size_t count, loff_t *f_pos) 
{
	copy_from_user(memory_buffer, buf, count);
	return count;
}

and here is the corresponding Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
.PHONY: build clean
build:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
	rm -rf modules.order Module.symvers
else
$(info Building with KERNELRELEASE =${KERNELRELEASE})
obj-m := bond.o

endif

If you install kernel-headers on your system the above code will work.
The above has a problem of buffer overflow since I have not made any checks on the amount of data user is transferring inside similarly any mutex or semaphore has not
been defined in above but you can try the above code
and

echo -n "something" > /dev/device_u_made
cat /dev/device_u_made

will show you "something"
Thanks for your help.

I may need this. Very useful. Thanks.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.