J'essaie d'utiliser libfuse
(cuse) pour créer un périphérique de caractères et jouer dessus comme avec tty régulier, tout va bien jusqu'à ce que j'utilise tcgetattr
.CUSE - retour IOCTL approprié pour termios.tcgetattr()
Malheureusement, termios.tcgetattr()
soulève toujours 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);
}
J'utilise ici le même code (structure) dans le module du noyau, et tout ira bien:
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");
Makefile:
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
Résultat (par exemple en 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']]
Hi Ricardo. Merci pour votre commentaire mais à la fin de la journée, j'ai écrit ce dont j'avais besoin. C'est un pont entre le dispositif de caractères TTY virtuel et n'importe quel point de terminaison TCP. Tous ceux qui en ont besoin peuvent saisir le code sur https://bitbucket.org/hetii/cusetty – MrHetii