I would like to create and use a tty device from Userspace. I found pty which does this as far as creating such a device and give the ability to write and read from this device. How ever i also would like to interact with ioctls sent to the tty Device.
I built this little demo:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
char buffer[2048];
char slaveDevPath[64];
void printTermiosSettings(const struct termios* termiosPtr) {
unsigned int baud = cfgetispeed(termiosPtr);
switch(baud) {
case B0:
printf("Baud Rate: <hang up>\n");
break;
case B50:
printf("Baud Rate: %d\n", 50);
break;
case B75:
printf("Baud Rate: %d\n", 75);
break;
case B110:
printf("Baud Rate: %d\n", 110);
break;
case B134:
printf("Baud Rate: %d\n", 134);
break;
case B150:
printf("Baud Rate: %d\n", 150);
break;
case B200:
printf("Baud Rate: %d\n", 200);
break;
case B300:
printf("Baud Rate: %d\n", 300);
break;
case B600:
printf("Baud Rate: %d\n", 600);
break;
case B1200:
printf("Baud Rate: %d\n", 1200);
break;
case B1800:
printf("Baud Rate: %d\n", 1800);
break;
case B2400:
printf("Baud Rate: %d\n", 2400);
break;
case B4800:
printf("Baud Rate: %d\n", 4800);
break;
case B9600:
printf("Baud Rate: %d\n", 9600);
break;
case B19200:
printf("Baud Rate: %d\n", 19200);
break;
case B38400:
printf("Baud Rate: %d\n", 38400);
break;
/* non posix bauds: */
case B115200:
printf("Baud Rate: %d\n", 115200);
break;
case B230400:
printf("Baud Rate: %d\n", 38400);
break;
case B460800:
printf("Baud Rate: %d\n", 460800);
break;
case B500000:
printf("Baud Rate: %d\n", 500000);
break;
case B576000:
printf("Baud Rate: %d\n", 576000);
break;
case B921600:
printf("Baud Rate: %d\n", 921600);
break;
case B1000000:
printf("Baud Rate: %d\n", 1000000);
break;
case B1152000:
printf("Baud Rate: %d\n", 1152000);
break;
case B1500000:
printf("Baud Rate: %d\n", 38400);
break;
case B2000000:
printf("Baud Rate: %d\n", 38400);
break;
case B2500000:
printf("Baud Rate: %d\n", 2500000);
break;
case B3000000:
printf("Baud Rate: %d\n", 3000000);
break;
case B3500000:
printf("Baud Rate: %d\n", 3500000);
break;
case B4000000:
printf("Baud Rate: %d\n", 4000000);
break;
default:
printf("Baud Rate: <unknown baudId:%d>", baud);
}
switch (termiosPtr->c_cflag & CSIZE) {
case CS5:
printf("Data Bits: 5\n");
break;
case CS6:
printf("Data Bits: 6\n");
break;
case CS7:
printf("Data Bits: 7\n");
break;
case CS8:
printf("Data Bits: 8\n");
break;
default:
printf("Data Bits: Unknown\n");
break;
}
if (termiosPtr->c_cflag & CSTOPB) {
printf("Stop Bits: 2\n");
} else {
printf("Stop Bits: 1\n");
}
if (termiosPtr->c_cflag & PARENB) {
if (termiosPtr->c_cflag & PARODD) {
printf("Parity: Odd\n");
} else {
printf("Parity: Even\n");
}
} else {
printf("Parity: None\n");
}
if (termiosPtr->c_cflag & CRTSCTS) {
printf("Flow Control: Hardware\n");
} else if (termiosPtr->c_iflag & (IXON | IXOFF)) {
printf("Flow Control: Software (XON/XOFF)\n");
} else {
printf("Flow Control: None\n");
}
}
int main(int argc, char** argv) {
int master = -1, slave = -1;
struct termios slave_termios;
// Open PTY master
master = posix_openpt(O_RDWR);
if(master < 0) {
perror("Couldn't create pty");
return -1;
}
grantpt(master);
unlockpt(master);
ptsname_r(master, slaveDevPath, sizeof(slaveDevPath));
printf("Slave Device: %s\n", slaveDevPath);
slave = open(slaveDevPath, O_RDONLY);
if(slave < 0) {
perror("Couldn't open slave tty");
return -1;
}
while(1) {
read(master, buffer, sizeof(buffer));
tcgetattr(slave, &slave_termios);
printTermiosSettings(&slave_termios);
printf("received: %s\n", buffer);
usleep(10000);
write(master, ">", 2);
write(master, buffer, sizeof(buffer));
}
return 0;
}
As seen in the code I poll the settings of the created slave Device. However I would like to get a callback when some one changes some thing like baudrate, ... and probably also reject some settings.
I also thought about using fuse but I never used it. So it just is an idea jet.
"Userspace device driver" is a misnomer. Device drivers run in the kernel by definition. A userspace device driver is not actually a device driver at all.
Pseudoterminals are supported by a bona fide device driver that runs in the kernel. You can certainly write a program that obtains a pty and mediates access to it. This is along the lines of what people usually mean when they say "userspace device driver". But again, it is not a device driver in the sense you want. The only device here is the underlying pty, and its driver runs in the kernel.
ioctls cannot be sent to your userspace program. It is not a device driver (or a device). They can be sent to the underlying pty device, and perhaps you can give your program a custom mechanism for receiving ioctls -- a userspace interface, that is -- but the userspace side cannot be the target of an
ioctl()syscall.To respond in to ioctls, you need a hook in the kernel. That would ordinarily take the form of an actual device driver. You can put most of your device service in userspace, and perhaps you can use an existing, generic driver such as UIO, but you cannot get away without engaging the kernel.