-1

I have written SPI character device driver in kernel space. I can now able to communicate through the following function from user space.

1.open("/dev/rfk_spi", O_RDWR);    
2.write(fd,buf,sizeof(buf)/sizeof(buf[0]));
3.read(fd,tab,sizeof(tab)/sizeof(tab[0]));  

But, I want to implement some more function which have some argument and return type. And I want to access this function from user space program.Suppose

 1.unsigned char function1(unsigned int,unsigned char*);
 2.void function2(struct student record);

Then how to write the code in kernel space/user space to exchange data.

These are my kernel function:

  1.static int spi_open(struct inode *inode,struct file *filp) {}  
  2.static int spi_release(struct inode *inode,struct file *filp){}
  3.static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops){}
  4.static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops){}  
  static const struct file_operations spi_fops =  
  {  
     .owner=THIS_MODULE,  
     .open=spi_open,  
     .read=spi_read,  
     .release=spi_release,  
     .write=spi_write,  
  };  
  static struct miscdevice misc = 
  {  
     .minor = MISC_DYNAMIC_MINOR,  
     .name  = DEVICE_NAME,  
     .fops  = &spi_fops,  
  };  

  6.static int __init spi_init(void){}  
  7.static void __exit spi_exit(void){}   
  module_init(spi_init);  
  module_exit(spi_exit);  

  MODULE_LICENSE("GPL");  

Please guide me to solve my problem!

UPDATE- As working on suggestions of @jjm, Now I get the following reply:

I have cross compiled the chardev.c ,chardev.h and make chardev.ko

root@rfk-desktop:# ls
chardev.c  chardev.h  chardev.ko  chardev.mod.c  chardev.mod.o  chardev.o ioctl  ioctl.c  Makefile  modules.order  Module.symvers

root@rfk-desktop:# file chardev.ko 
chardev.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped

root@rfk-desktop:# file ioctl
ioctl: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped

I copied the file to my development board and register

[root@FriendlyARM 2.6.32.2-FriendlyARM]# modprob chardev
[root@FriendlyARM 2.6.32.2-FriendlyARM]# mknod char_dev c 100 0

But when I am running the test program :)

[root@FriendlyARM /mnt]# ./ioctl
Can't open device file: char_dev

Please reply where I am missing?

UPDATE- Every thing works fine no problem!!!& I shall post the updated source code with detailed description very shortly!!

Please,any one could tell me why I am getting -Ve marks!! Is there any information wrong ? or missing? anything else?

RFK
  • 83
  • 11
  • 1
    RFK, to make any suggestions, send us log of **ls -l** in **/dev/** and **dmesg** log after **insmod chardev.ko** – jjm Apr 23 '15 at 03:54

3 Answers3

1

You can use IOCTLs for this purpose. You can register ioctl function also to your file ops. eg.

static const struct file_operations spi_fops =  
{  
 .owner=THIS_MODULE,  
 .open=spi_open,  
 .read=spi_read,  
 .release=spi_release,  
 .write=spi_write,  
 .unlocked_ioctl=spi_ioctl
};

Then you can use

 copy_from_user 

function to receive arguments from user space and

copy_to_user

function to pass arguments to user space. Use these functions in your IOCTLs for implementing functionalities other than read and write.

jjm
  • 431
  • 3
  • 19
  • Thanks jjm.I have read the topic IOCLT but not able to get the idea properly what piece of code I have to add. Could you please send any example link of of the same? – RFK Apr 21 '15 at 13:22
  • 1
    RFK, Please read this link [link](http://linux.die.net/lkmpg/x892.html) .This is explaining about ioctls, and [link](http://www.fsl.cs.sunysb.edu/kernel-api/re256.html) explains copy_to_user,and link to copy_from_user. You can pass address of your structure(with parameters from user) as ioctl parm. – jjm Apr 22 '15 at 00:10
  • Hi, jjm. Sorry for the updated post. My driver was registering to the current directory , that is why I was not able to access. Now my initial requirement fulfilled. I can send mgs to kernel and can get msg from kernel. Thank you very much. – RFK Apr 22 '15 at 14:14
0

I would go against the popular vote here on Stackoverflow and suggest using sysfs for a couple of reasons:

  1. Everything is visible and coherent - you can ls /sys/your-driver and see all operations that can be run on this driver (take for example /sys/device/system/cpu) - this is the main point IMO. ioctls have no equivalent for this fantastic feature
  2. You can structure your operations - everything related to, say, baud rates can reside under /sys/your-driver/baud
  3. You can use streams, as an entry is created for each function
Ishay Peled
  • 2,783
  • 1
  • 23
  • 37
0

My source code is properly working please have a look.If any one get benefited or see helpful for others please give some +Ve marks.

Also please, inform me why I am getting -Ve marks for this question!!

1."chardev.c"

#include <linux/kernel.h>   
#include <linux/module.h>   
#include <linux/fs.h>
#include <asm/uaccess.h>    
#include "./chardev.h"
#define SUCCESS 0
#define DEVICE_NAME "char_dev_rfk"
#define BUF_LEN 80
static int Device_Open = 0;
static char Message[BUF_LEN];
static char *Message_Ptr;
static int device_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "\n\rdevice_open(%p)\n", file);

    if (Device_Open) return -EBUSY;

    Device_Open++;
    Message_Ptr = Message;
    try_module_get(THIS_MODULE);
    return SUCCESS;
}

static int device_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "\n\rdevice_release(%p,%p)\n", inode, file);
    Device_Open--;
    module_put(THIS_MODULE);
    return SUCCESS;
}
static ssize_t device_read(struct file *file,char __user * buffer,size_t length, loff_t * offset)
{

    int bytes_read = 0;
    printk(KERN_INFO "\n\rdevice_read(%p,%p,%d)\n", file, buffer, length);
    if (*Message_Ptr == 0)  return 0;
    while (length && *Message_Ptr_k)
    {
        put_user(*(Message_Ptr_k++), buffer++);
        length--;
        bytes_read++;
    }
    printk(KERN_INFO "Read %d bytes, %d left\n", bytes_read, length);
    return bytes_read;
}
static ssize_t device_write(struct file *file,const char __user * buffer, size_t length, loff_t * offset)
{
    int i;
    printk(KERN_INFO "\n\rdevice_write(%p,%s,%d)", file, buffer, length);
    for (i = 0; i < length && i < BUF_LEN; i++)
        get_user(Message[i], buffer + i);

    Message_Ptr = Message;
    return i;
}
int device_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
    int i;
    char *temp;
    char ch;

    switch (ioctl_num) 
    {
        case IOCTL_SET_MSG:     
            temp = (char *)ioctl_param;
            get_user(ch, temp);
            for (i = 0; ch && i < BUF_LEN; i++, temp++)
                get_user(ch, temp);

            device_write(file, (char *)ioctl_param, i, 0);
        break;

        case IOCTL_GET_MSG:     
            i = device_read(file, (char *)ioctl_param, 99, 0);
            put_user('\0', (char *)ioctl_param + i);
        break;

        case IOCTL_GET_NTH_BYTE:

            return Message[ioctl_param];
        break;
    }
    return SUCCESS;
}
struct file_operations Fops = 
{
    .read = device_read,
    .write = device_write,
    .ioctl = device_ioctl,
    .open = device_open,
    .release = device_release,  
};
int init_module()
{
    int ret_val;    
    ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);
    if (ret_val < 0) 
    {
        printk(KERN_ALERT "%s failed with %d\n","Sorry, registering the character device ", ret_val);
        return ret_val;
    }
    printk(KERN_INFO "%s The major device number is %d.\n","Registeration is a success", MAJOR_NUM);        

    return 0;
}
void cleanup_module()
{

}       
MODULE_LICENSE("GPL");  

2."chardev.h"

#ifndef CHARDEV_H
#define CHARDEV_H

#include <linux/ioctl.h>
#define MAJOR_NUM 100
#define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *)
#define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *)
#define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int)
#define DEVICE_FILE_NAME "char_dev_rfk"

#endif

3."ioctl.c"

#include "chardev.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>      
#include <unistd.h>     
#include <sys/ioctl.h>
ioctl_set_msg(int file_desc, char *message)
{
    int ret_val;
    printf("\n\rioctl_set_msg():Called\n");
    ret_val = ioctl(file_desc, IOCTL_SET_MSG, message);

    if (ret_val < 0) 
    {
        printf("ioctl_set_msg failed:%d\n", ret_val);
        exit(-1);
    }

}
ioctl_get_msg(int file_desc)
{
    int ret_val;
    char message[100];
    printf("\n\rioctl_get_msg():Called\n");
    ret_val = ioctl(file_desc, IOCTL_GET_MSG, message);

    if (ret_val < 0) 
        {
        printf("ioctl_get_msg failed:%d\n", ret_val);
        exit(-1);
    }
    printf("\n\rget_msg message:%s\n", message);
}

ioctl_get_nth_byte(int file_desc)
{
    int i;
    char c;
    printf("\n\rioctl_get_nth_byte():Called\n");
    printf("\n\rget_nth_byte message:");
    i = 0;
    do {
        c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);
        if (c < 0) 
        {
            printf("ioctl_get_nth_byte failed at the %d'th byte:\n",i);
            exit(-1);
        }
        putchar(c);
    } while (c != 0);
    putchar('\n');
}
main()
{
    int file_desc, ret_val;
    char *msg = "Message passed from Uesr:Rofique\n";       
        file_desc = open("./char_dev_rfk", O_RDWR); 
    if (file_desc < 0) 
        {
        printf("Can't open device file: %s\n", DEVICE_FILE_NAME);
        exit(-1);
    }   
    ioctl_set_msg(file_desc, msg);  
    ioctl_get_msg(file_desc);       
    close(file_desc);
}

I have cross compile it for ARM9 board. So, make file as follows.

4."Makefile"

ifneq ($(KERNELRELEASE),)
obj-m:= chardev.o
else
CROSS=arm-linux-
KDIR := /opt/linux-2.6.32.2/
all:ioctl
    make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- 
ioctl:ioctl.c
    $(CROSS)gcc -o ioctl ioctl.c
    $(CROSS)strip ioctl
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *symvers modul* ioctl
endif
RFK
  • 83
  • 11