Linux driver

Hello, everyone. I need to write my own driver for linux which will communicate with arduino board.

I'm trying to get input/output of the board through ttyACM device. But I can only write to board using it, but cannot read. I cannot understand why. If I use usual cat and echo commands through ttyACM device, everything will be ok.

Here is the code

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>

/* ============== krn space '/dev/ttyN' access ============= */

static struct file *ktty_open(const char *filename, int flags, umode_t mode)
{
    return filp_open(filename, O_RDWR | O_NOCTTY, 0);
}

static ssize_t ktty_write(struct file *f, const char *buf, int count)
{

    int result;
    mm_segment_t oldfs;

    oldfs = get_fs();
    set_fs(KERNEL_DS);
    f->f_pos = 0;
    result = f->f_op->write(f, buf, count, &f->f_pos);
    set_fs(oldfs);

    return result;
}

static ssize_t ktty_read(struct file * f, char * buf, int count)
{
	int result = 0;
	mm_segment_t oldfs;
	oldfs = get_fs();
	set_fs(KERNEL_DS);
	result = f->f_op->read(f, buf, count, &f->f_pos);
	set_fs(oldfs);
	return result;
}

static void ktty_close(struct file *xktty, fl_owner_t id)
{
        filp_close(xktty, id);
}

/* =============== module file operations ===================== */
DEFINE_MUTEX(xmutex);
static struct file *xktty = NULL;
static int xktty_open(struct inode *inode, struct file *filp)
{
    #define XKTTY_MAX_PATH 20
    #define XKTTY_NUM 0
    char filename[XKTTY_MAX_PATH];

    /* only one process at a time */
    if(!(mutex_trylock(&xmutex)))
        return -EBUSY;

    snprintf(filename, XKTTY_MAX_PATH, "/dev/ttyACM%d", XKTTY_NUM);
    xktty = ktty_open(filename, 0, O_RDWR);
    if (PTR_RET(xktty)) {
        mutex_unlock(&xmutex);
        return PTR_RET(xktty);
    }

    return 0;
}

static int xktty_release(struct inode *inode, struct file *file)
{
    if(!IS_ERR_OR_NULL(xktty))
        ktty_close(xktty, 0);
    mutex_unlock(&xmutex);
    return 0;
}

static ssize_t xktty_write(struct file *filp,
                 const char __user * buf, size_t count,
                 loff_t * f_pos)
{

    #define XKTTY_MAX_BUF_LEN 200
    const char kbuf[XKTTY_MAX_BUF_LEN];

    count = count < XKTTY_MAX_BUF_LEN ? count : XKTTY_MAX_BUF_LEN;
    if (copy_from_user((char *)kbuf, (const char __user *)buf, count))
        return -EFAULT;

    if (!IS_ERR_OR_NULL(xktty))
        return ktty_write(xktty, kbuf, count);
    else
        return -EFAULT;
}

static ssize_t xktty_read(struct file * flip, char __user * buf, size_t count,
		loff_t * f_pos)
{
	char kbuf[XKTTY_MAX_BUF_LEN] = { '\0' };
	int result = 0;
	count = count < XKTTY_MAX_BUF_LEN ? count : XKTTY_MAX_BUF_LEN;

	if (!IS_ERR_OR_NULL(xktty))
	{
		printk(KERN_INFO "Tying to read...Count: %d\n", count);
		result = ktty_read(xktty, kbuf, count);
		printk("Buf: %s\nResult: %d\n", kbuf, result);

		if (copy_to_user((char *)buf, (char __user *)kbuf, result))
		{
			printk(KERN_ERR "Cannot copy\n");
			return -EFAULT;
		}
	}
	else
	{
		return -EFAULT;
	}

	return result;
}

static struct file_operations xktty_ops = {
    .owner = THIS_MODULE,
    .open = xktty_open,
    .release = xktty_release,
    .write = xktty_write,
	.read = xktty_read
};

/* =================== init/exit ======================== */

static struct cdev cdev;
static struct class *class;
static int xktty_mjr;

static int xktty_init(void)
{
    #define XKTTY_NAME "xktty"

    dev_t devt = MKDEV(0, 0);
    if (alloc_chrdev_region(&devt, 0, 1, XKTTY_NAME) < 0)
        return -1;
    xktty_mjr = MAJOR(devt);

    cdev_init(&cdev, &xktty_ops);
    cdev.owner = THIS_MODULE;
    devt = MKDEV(xktty_mjr, 0);
    if (cdev_add(&cdev, devt, 1))
        goto exit0;

    class = class_create(THIS_MODULE, XKTTY_NAME);
    if (!class)
        goto exit1;

    devt = MKDEV(xktty_mjr, 0);
    if (!(device_create(class, NULL, devt, NULL, XKTTY_NAME)))
        goto exit2;

    return 0;

exit2:
    class_destroy(class);
exit1:
    cdev_del(&cdev);
exit0:
    unregister_chrdev_region(MKDEV(xktty_mjr, 0), 1);

    return -1;
}

static void xktty_fini(void)
{
    device_destroy(class, MKDEV(xktty_mjr, 0));
    class_destroy(class);
    cdev_del(&cdev);
    unregister_chrdev_region(MKDEV(xktty_mjr, 0), 1);
}

module_init(xktty_init);
module_exit(xktty_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("__ParaPik__");

I'm not familiar with programming Linux using C/C++

Is there any chance that your code is not allowing time for the Arduino to reset when it opens the serial port ?

...R

No. This is no chance for it.

ParaPik:
No. This is no chance for it.

I assume this means "Yes, after my code opens the serial port it does allow time for the Arduino to restart before it tries to send any data"

(Just to avoid misunderstanding).

And I also assume that you realize that the Arduino restarts every time the serial port is opened - so your code should open the port and keep it open.

...R

Yes, I know it.

ParaPik:
Yes, I know it.

I admire the tendency to avoid loquaciousness. :slight_smile:

...R

Also I'm writing a Linux driver that allows you to interface to any card embededd including arduino.
The driver is not complete and has some bugs, but if you want to give us a look:
remoteGpio

I must be missing something ...

To my mind the beauty of Linux is that it includes whatever driver is needed to communicate with an Arduino over a serial connection. I have never needed to install any driver.

Why is it necessary to write drivers?
What will they do that my PC cannot do at the moment?

...R

I start to wonder how you handle the communication when there are more programs that communicate on the same serial naturl you can use semaphores but everything becomes cumbersome and unstable.
Second, actually uses GNU / Linux?
How do you interact simply between an application and the terminal? which adopts IPC?
I prefer to see the pin, spi, eeprom, i2c, etc, under "/sys/class" so you can use commands such as echo and cat.
Finally I prefer to write firmware that becomes plugins my driver without having to change each time the code on the PC.

vbextreme:
I start to wonder how you handle the communication when there are more programs that communicate on the same serial

I have never wanted to use one serial port for one job. I can't conceive of how I would need two PC programs to communicate with one Arduino at the same time.

I prefer to see the pin, spi, eeprom, i2c, etc, under "/sys/class" so you can use commands such as echo and cat.

Those sound to me like Arduino features (but I may well be wrong) and, again, I am not getting my head around why anyone would want to expose them as separate "things" on a PC. To my mind they would be part of an Arduino program that would have a single interface with the PC. Maybe you have a very different concept of a PC program in mind. If so I would be interested to hear about it.

...R

These boards allow you to do many things.
We want you display on an alphanumeric display PC status, case temperature, speed of extra fans maybe the battery voltage of your DIY ups.
You think it's that simple? Needless? What if I want to turn your Arduino into a USB device as you would? How to connect a PSX controller to your PC?
These are simple examples, you know IoT?
Now think of a web server that can control different types of devices, you may have several processes that communicate simultaneously with the board.
Now try to look at the need for a driver not only for PC-> USB-> ARDUINO, but extend your concepts to udoo, ODROID U3 + IO shield, Arduino Yun and all the cards that have a Linux kernel linked in some way a chip embededd.
Sure that if you have to turn yet another Christmas tree can safely be sent via the serial monitor "on", although I would prefer to write "echo 'on'> /sys/class/rgpio/tree/value" of course without writing even a line of code to the PC, or whoever it takes the place, but only a few lines on my beloved chip.

Robin2:
I assume this means "Yes, after my code opens the serial port it does allow time for the Arduino to restart before it tries to send any data"

(Just to avoid misunderstanding).

And I also assume that you realize that the Arduino restarts every time the serial port is opened - so your code should open the port and keep it open.

...R

It actually doesn't work like this.

Restarting the board is a feature of the IDE. Alternatively you can use something like Hterm to control the DTR/RST (or in code), but simply connecting to the COM port does not affect the Arduino.

Try it!

vbextreme:
Now try to look at the need for a driver not only for .....

Clearly you know a lot of stuff that I have never even heard of (which is not difficult).

...R

I confirm

pYro_65:
Restarting the board is a feature of the IDE. Alternatively you can use something like Hterm to control the DTR/RST (or in code), but simply connecting to the COM port does not affect the Arduino.

I am conscious that it can be a bit more complex than I implied in my early Posts - but you must see them in the context of the very minimal information provided by the OP.

I don't recall any case where opening a serial port on my Linux laptop did not restart my Uno. Maybe Windows' defaults are different.

...R

Robin2:
I am conscious that it can be a bit more complex than I implied in my early Posts - but you must see them in the context of the very minimal information provided by the OP.

I don't recall any case where opening a serial port on my Linux laptop did not restart my Uno. Maybe Windows' defaults are different.

...R

I'll have to disagree,

You need to specifically call ioctl() with the TIOCM_DTR flag set to cause a reset on the Arduino, it is not default.

If you used a terminal app, you must have had it selected.

pYro_65:
You need to specifically call ioctl() with the TIOCM_DTR flag set to cause a reset on the Arduino

I guess the problem is that I never called ioctl() myself - I always had someone else do it for me - such as Python.

...R