0

I want to use serial port in kernel space, I have found some code which was in user space, I tried to convert the codes to work in kernel space...

This is my code

#include <linux/termios.h>
#include <linux/unistd.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
struct file * fp;

...

struct termios termAttr;
struct sigaction saio;

oldfs = get_fs();
set_fs(KERNEL_DS);
fp = filp_open("/dev/ttymxc0", O_RDWR | O_NOCTTY | O_NDELAY,0);
if(fp == NULL)
    printk(KERN_ALERT "Serial openning error!!.\n");
else{
    saio.sa_handler = signal_handler_IO;
    saio.sa_flags = 0;
    saio.sa_restorer = NULL;
    sigaction(SIGIO,&saio,NULL);
    fcntl(fp, F_SETFL, O_NDELAY|FASYNC);
    fcntl(fp, F_SETOWN, THIS_MODULE);

    tcgetattr(fp,&termAttr);
    cfsetispeed(&termAttr,B115200);
    cfsetospeed(&termAttr,B115200);
    termAttr.c_cflag &= ~PARENB;
    termAttr.c_cflag &= ~CSTOPB;
    termAttr.c_cflag &= ~CSIZE;
    termAttr.c_cflag |= CS8;
    termAttr.c_cflag |= (CLOCAL | CREAD);
    termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
    termAttr.c_oflag &= ~OPOST;
    tcsetattr(fp,TCSANOW,&termAttr);
    printk(KERN_ALERT "Serial configured....\n");
    vfs_write(fp, "HI",2, NULL);
    filp_close(fp, NULL);
    set_fs(oldfs);
}

while compiling, I got following errors:

note: each undeclared identifier is reported only once for each function it appears in
error: implicit declaration of function 'sigaction' [-Werror=implicit-function-declaration]
                  sigaction(SIGIO,&saio,NULL);
                  ^
error: implicit declaration of function 'fcntl' [-Werror=implicit-function-declaration]
                          fcntl(fp, F_SETFL, O_NDELAY|FASYNC);
                          ^
error: implicit declaration of function 'tcgetattr' [-Werror=implicit-function-declaration]
                  tcgetattr(fp,&termAttr);
                  ^
error: implicit declaration of function 'cfsetispeed' [-Werror=implicit-function-declaration]
                  cfsetispeed(&termAttr,B115200);
                  ^
error: implicit declaration of function 'cfsetospeed' [-Werror=implicit-function-declaration]
                  cfsetospeed(&termAttr,B115200);
                  ^
error: implicit declaration of function 'tcsetattr' [-Werror=implicit-function-declaration]

I am cross compiling this driver and I already compiled Linux source, I have searched for this functions in my Linux source code but I did not find any of this functions! what should I use instead of this functions?

Edit 1:

I have changed my code into this:

    //serial
   struct ktermios termAttr;
   struct sigaction saio;
   loff_t pos =0;
   struct tty_struct *tty;
           serialfp = file_open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY,0);
           if(serialfp == NULL)
                   printk(KERN_ALERT "ARIO RMG Serial openning error!!.\n");
           else{


                tty = (struct tty_struct *)serialfp->private_data;
                tty_termios_encode_baud_rate(&tty->termios,B115200,B115200 );
                    printk(KERN_ALERT "ARIO RMG Serial configured....\n");
                    pos = serialfp->f_pos;
                    file_write(serialfp, "\n\n\n\n\nThis is first test of sending serial data from kernel module\n\n\n\n\n",70,&pos);
                    serialfp->f_pos=pos;

                    serial_thread_condition = 1;
                    mutex_init(&serial_mutex);
                    task1 = kthread_create(&thread_function, (void *)&pid1, "pradeep");
                    wake_up_process(task1);

                    printk(KERN_ALERT "data received:%s\n\n\n\n\n\n\n\n",rmg_drvstruct[0].RxSerial);


           }

I am able to send data into serial port now, I also created a thread to read data from serial port. with following code:

static int thread_function(void *data){
     loff_t pos;
     while(serial_thread_condition){
     mutex_lock(&serial_mutex);
     if (IS_ERR(serialfp)) {
          mutex_unlock(&serial_mutex);
          serial_thread_condition=0;
          return 0;

     }
     pos = serialfp->f_pos;
     printk(KERN_INFO "try to read from serial\r\n");
     if(file_read(serialfp, rmg_drvstruct[0].RxSerial, 100, &pos)>0)
     {

          printk(KERN_INFO "Data: %s\r\n", rmg_drvstruct[0].RxSerial);
             serialfp->f_pos = pos;
             serial_thread_condition = 0;
             mutex_unlock(&serial_mutex);
             break;
     }
     mutex_unlock(&serial_mutex);
     }

}

int file_read(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_read(file, data, size, &offset);

    set_fs(oldfs);
    return ret;
}

But I got nothing in serial port in my thread, I wanted to use interrupts for new received bytes, but irq_request() function makes kernel panic and computer freezes out, so what should I do to properly receive data with interrupt or a thread?

0andriy
  • 4,183
  • 1
  • 24
  • 37
Ahmad Afkande
  • 137
  • 2
  • 14
  • It is a completely wrong approach. Look at how GPS drivers are utilizing serdev framework. – 0andriy Apr 26 '20 at 13:38
  • @0andriy I could not find a solution, I got really confused! can you please show me an example link or something? – Ahmad Afkande Apr 29 '20 at 10:06
  • https://elixir.bootlin.com/linux/latest/source/drivers/gnss/serial.c – 0andriy Apr 29 '20 at 10:30
  • @0andriy I don't understand this code, How is this code gets input serial data interrupts and passes it to user space application? I did not find anything to be registered as interrupt handler or something... – Ahmad Afkande Apr 29 '20 at 11:23

1 Answers1

0

So after a while searching and testing I used this code to communicate with serial port, But I have changed a bit of it.

Please read my edit before using this code

I used a thread to read from serial port, cause using interrupts (request_irq) makes my kernel to shoot himself in the head!

So this is my code for anyone who might want to use serial port in kernel module.

#include <linux/tty.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include<linux/mutex.h>

MODULE_LICENSE("GPL");

struct file * serialfp;
struct task_struct *task1;
int pid1 = 1;
....
static struct file *file_open(const char *path, int flags, int rights)
{
    struct file *filp = NULL;
    mm_segment_t oldfs;
    int err = 0;

    oldfs = get_fs();
    set_fs(get_ds());
    filp = filp_open(path, flags, rights);
    set_fs(oldfs);
    if (IS_ERR(filp)) {
        err = PTR_ERR(filp);
        return NULL;
    }
    return filp;
}
static void file_close(struct file *file)
{
    filp_close(file, NULL);
}

static int file_read(struct file *file, unsigned long long *offset, unsigned char *data, unsigned int size)
{
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_read(file, data, size, offset);

    set_fs(oldfs);
    return ret;
}

static int file_write(struct file *file, unsigned long long *offset, unsigned char *data, unsigned int size)
{
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_write(file, data, size, offset);
    set_fs(oldfs);
    return ret;
}
int file_sync(struct file *file)
{
    vfs_fsync(file, 0);
    return 0;
}

static int thread_function(void *data){
     loff_t pos;
     int len;
     while(serial_thread_condition){
     mutex_lock(&serial_mutex);
     if (IS_ERR(serialfp)|| serial_thread_condition==0) {
          printk(KERN_INFO "serial reading thread has been terminated.\r\n");
          mutex_unlock(&serial_mutex);
          serial_thread_condition=0;
          return 0;

     }
     pos = serialfp->f_pos;    
     if((len=file_read(serialfp,&pos, rmg_drvstruct[0].RxSerial, 100))>0){
          printk(KERN_INFO "Received data : %s\r\n", rmg_drvstruct[0].RxSerial);
             serialfp->f_pos = pos;
          file_write(serialfp,&pos,"I have received:",16);
          file_write(serialfp,&pos, rmg_drvstruct[0].RxSerial,len);
          serialfp->f_pos = pos;
     }
      file_sync(serialfp);
     mutex_unlock(&serial_mutex);
     mdelay(5);
     }
}

so my device opening function is like:

static int device_open(struct inode *inode, struct file *file){
   loff_t pos =0;
   struct tty_struct *tty;

   ...

   serialfp = file_open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY    ,0);
   if(serialfp == NULL)
       printk(KERN_ALERT "ARIO RMG Serial openning error!!.\n");
   else{

       tty = (struct tty_struct *)serialfp->private_data;
       tty_termios_encode_baud_rate(&tty->termios,B115200,B115200 );
       printk(KERN_ALERT "Serial configured....\n");
       pos = serialfp->f_pos;
       file_write(serialfp,&pos,"\n\n\n\n\nThis is first test of sending serial data from kernel module\n\n\n\n\n",70);

       serialfp->f_pos=pos;
       file_sync(serialfp);

       serial_thread_condition = 1;
       mutex_init(&serial_mutex);
       task1 = kthread_create(&thread_function, (void *)&pid1, "pradeep");
       wake_up_process(task1);
   }

...

}

and closing module function:

static int device_release(struct inode *inode, struct file *file){
...

 if (!IS_ERR(serialfp)) {

      mutex_lock(&serial_mutex);
      printk(KERN_INFO " Trying to realease serail thread");
       if(serial_thread_condition==1){
           int i=0;
           while(i++<256)serial_thread_condition =0;
       }
       printk(KERN_INFO " serial thread released.");
       file_close(serialfp);
       mutex_unlock(&serial_mutex);
  }

....

}

Thanks to 0andriy for helping and dmeister for his answer in that link.

EDIT:

So I did what I wanted in kernel, opening a file in kernel space regardless of any suggestions not to do so.

But I needed to open a file in kernel module...

So why every body said don't open user space files in kernel? In these two articles there are some reasons to not using files in kernel , this link and this link.

there are some reasons like: 1- kernel module may lose CPU at any time and the file which is opened by kernel may close.

2- I am not sure really about this, But they said files needs to have a process to stay opened but kernel module itself is not a process(maybe I am wrong!).

3- If any error happens while working with files(open/close/read/write), kernel module can not handle it and causes kernel panic...

I have experienced a lot of kernel panics only while opening and closing the file I wanted to work with. These are some of reasons why you should not use files in kernel modules, So as every body said before "If you need to use files in kernel module you probably did something wrong in your code!"

Ahmad Afkande
  • 137
  • 2
  • 14