CUSE - 为termios.tcgetattr()返回正确的IOCTL

时间:2016-11-06 04:17:40

标签: c ioctl termios

我尝试使用libfuse(cuse)创建角色设备并像使用普通tty一样玩它,一切都很好,直到我使用tcgetattr

不幸的是,termios.tcgetattr()总是举起I/O error

cusetest.c

#define FUSE_USE_VERSION 29
#define _FILE_OFFSET_BITS 64
#include <fuse/cuse_lowlevel.h>
#include <fuse/fuse_opt.h>
#include <string.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <termios.h>
#include <linux/termios.h>

#include <unistd.h>  
#include <stdlib.h>
#include <strings.h>
#include <errno.h>


#define LOG(...) do { fprintf(stderr, "DEBUG: "__VA_ARGS__); puts(""); } while (0)

static void cusetest_open(fuse_req_t req, struct fuse_file_info *fi) {
    LOG("cusetest_open called\n");
    fuse_reply_open(req, fi);
}

static void cusetest_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info *fi) {
    LOG("cusetest_read called\n");
    fuse_reply_buf(req, "Hello", size > 5 ? 5 : size);
}

static void cusetest_write(fuse_req_t req, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) {
    LOG("cusetest_write called, size: %lu bytes\n", size);
    fuse_reply_write(req, size);
}

static void cusetest_ioctl(fuse_req_t req, int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) {
    LOG("cusetest_ioctl called, cmd: %d insize: %lu outsize: %lu\n", cmd, in_bufsz, out_bufsz);
    struct termios oldtio;
    int  i;


    oldtio.c_iflag = 1;
    oldtio.c_oflag = 2;
    oldtio.c_cflag = 3;
    oldtio.c_lflag = 4;
    for (i = 0; i < NCCS; ++i)
    {
      oldtio.c_cc[i] = i;
    }

    printf("NCCS:%ud\n\n", NCCS);
    printf("c_iflag:%ud \n", oldtio.c_iflag);
    printf("c_oflag:%ud\n", oldtio.c_oflag);
    printf("c_cflag:%ud\n", oldtio.c_cflag);
    printf("c_lflag:%ud\n", oldtio.c_lflag);
//    printk("c_ispeed:%ud\n", oldtio.ispeed);
//    printk("c_ospeed:%ud\n\n", oldtio.c_ospeed);

    for (i = 0; i < NCCS; ++i)
    {
      printf("CC: %d\n", oldtio.c_cc[i]);
    }
    printf("\n");

    fuse_reply_ioctl(req, 21506, &oldtio, sizeof(oldtio));
}

static const struct cuse_lowlevel_ops cusetest_clop = {
        .open           = cusetest_open,
        .read           = cusetest_read,
        .write          = cusetest_write,
        .ioctl          = cusetest_ioctl,
};

struct cuse_info2 {
        unsigned int dev_major;
        unsigned int dev_minor;
        unsigned int dev_info_argc;
        char ** dev_info_argv;
        unsigned int flags;
};

// char * argv[] == char ** argv
int main(int argc, char** argv) {
    // -f: run in foreground, -d: debug ouput
    // Compile official example and use -h
    const char* cusearg[] = {"test", "-f", "-d"};
    const char* devarg[]  = {"DEVNAME=ttyCUSE0" };

    struct cuse_info ci;
    memset(&ci, 0x00, sizeof(ci));
    ci.flags = CUSE_UNRESTRICTED_IOCTL;
    ci.dev_info_argc=1;
    ci.dev_info_argv = devarg;

    //int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, void *userdata);
    return cuse_lowlevel_main(3, (char**) &cusearg, &ci, &cusetest_clop, NULL);
}

这里我在内核模块中使用相同的代码(结构),一切都很好:

ttymodule.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/termios.h>
#include <linux/errno.h>

#define  DEVICE_NAME "ttytest"
#define  CLASS_NAME  "ttytest"

static int    major;
static struct class*  tty_class  = NULL;
static struct device* tty_device = NULL;

static long fake_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg){
    struct termios oldtio;
    int  i;


    oldtio.c_iflag = 1;
    oldtio.c_oflag = 2;
    oldtio.c_cflag = 3;
    oldtio.c_lflag = 4;
    for (i = 0; i < NCCS; ++i)
    {
      oldtio.c_cc[i] = i;
    }

    printk(KERN_ALERT "ttytest: ioctl called: %d -> %ld \n", cmd_in, arg);
    printk(KERN_ALERT "NCCS:%ud\n\n", NCCS);
    printk(KERN_ALERT "c_iflag:%ud \n", oldtio.c_iflag);
    printk(KERN_ALERT "c_oflag:%ud\n", oldtio.c_oflag);
    printk(KERN_ALERT "c_cflag:%ud\n", oldtio.c_cflag);
    printk(KERN_ALERT "c_lflag:%ud\n", oldtio.c_lflag);
    //printk(KERN_ALERT "c_ispeed:%ud\n", oldtio.ispeed);
    //printk(KERN_ALERT "c_ospeed:%ud\n\n", oldtio.c_ospeed);

    for (i = 0; i < NCCS; ++i)
    {
      printk(KERN_ALERT "CC: %d\n", oldtio.c_cc[i]);
    }
    printk(KERN_ALERT "\n");

    return cmd_in+1;
}

static struct file_operations fops =
{
   .owner = THIS_MODULE,
   .unlocked_ioctl = fake_ioctl
};

static int __init tty_init(void){
   printk(KERN_INFO "ttytest: Initializing ...\n");

   major = register_chrdev(0, DEVICE_NAME, &fops);
   if (major<0){
      printk(KERN_ALERT "ttytest failed to register a major number\n");
      return major;
   }

   tty_class = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(tty_class)){
      unregister_chrdev(major, DEVICE_NAME);
      printk(KERN_ALERT "Failed to register device class\n");
      return PTR_ERR(tty_class);
   }

   tty_device = device_create(tty_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
   if (IS_ERR(tty_device)){
      class_destroy(tty_class);
      unregister_chrdev(major, DEVICE_NAME);
      printk(KERN_ALERT "Failed to create the device\n");
      return PTR_ERR(tty_device);
   }

   return 0;
}

static void __exit tty_exit(void){
   device_destroy(tty_class, MKDEV(major, 0));
   class_unregister(tty_class);                          // unregister the device class
   class_destroy(tty_class);                             // remove the device class
   unregister_chrdev(major, DEVICE_NAME);             // unregister the major number
   printk(KERN_INFO "ttytest: Goodbye ...\n");
}

module_init(tty_init);
module_exit(tty_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Grzegorz Hetman");
MODULE_DESCRIPTION("A simple tty module to test cuse implementation.");
MODULE_VERSION("0.1");

生成文件:

obj-m   := ttymodule.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)


all: unload clean
    $(MAKE) -C $(KERNELDIR) M=$(PWD)
    @make load
    @make cuse
    @sudo ./cusetest

clean:
    @rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c *.order *.symvers cusetest

load:
    @sudo insmod ttymodule.ko
    @sudo chmod 777 /dev/ttytest
    @sudo lsmod |grep ttymodule

unload:
    @sudo rmmod ttymodule || true
    @sudo rm -f /dev/ttymodule

cuse:
    @gcc -Wall -g cusetest.c -lfuse -o cusetest

结果(例如在python中):

import termios
termios.tcgetattr(open('/dev/ttyCUSE0','rw+')) # error: (5, 'Input/output error')

termios.tcgetattr(open('/dev/ttytest','rw+')) # [5523920, 0, 1576586344, 32702, 8, 8, ['\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '@', 'r', '\x90', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'g', '\x82', '\x01', '\x00', '\x00', '\x00', '\x00', ',', 'h', 'K', '\x00', '\x00', '\x00', '\x00', '\x00', '\x90']]

1 个答案:

答案 0 :(得分:0)

据我所知,cuse司机并不喜欢普通的司机,因为他们不是终端司机。它们是面向用户空间字符的文件系统驱动程序。

为了根据需要使用它,它应该在below image (Taken from LDD3)之类的tty组件下。与图像相同的源描述了如何创建终端派生程序。

顺便说一下,我知道没有用户空间的驱动程序。

tty core overview