//------------------------------------------------------------------- // nicasync.cpp // // This demo illustrates the use of asynchronous notification // to process packets received by the RealTek 8139 interface. // It relies upon a new version of our RealTek device-driver, // named 'nicasync.c', which implements the necessary support // for the asynchronous notification capability, as discussed // in the "Linux Device Drivers (3rd Ed)" book, in Chapter 6. // You can run our 'trychat.cpp' program from another station // on the local network in order to test this demo. // // compile with: $ g++ nicasync.cpp -o nicasync // // programmer: ALLAN CRUSE // written on: 27 APR 2005 // revised on: 29 APR 2005 -- added transmit for keyboard-input //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for read(), write(), close() #include // for strlen() #include // for sigaction() #include // for tcgetattr(), tcsetattr() #include // for ioctl() #include // for fcntl() #include // for wait() #include // for POLL_IN #include // for htons() #define CHAT_ID 0x0635 // identifies our packet-protocol int kbd, nic, done = 0; unsigned char nicaddr[6]; char devname[] = "/dev/nic"; void my_sigaction( int signo, siginfo_t *info, void *data ) { // function prototypes void handle_nic_input( int nic ); void handle_kbd_input( int kbd, int nic ); if ( signo == SIGINT ) done = 1; // -C pressed if ( signo != SIGUSR1 ) return; // only handle SIGUSR1 // ignore notifications of network-packet transmissions if ( (info->si_band & POLL_IN) == 0 ) return; // process input now available on the signaling device else if ( info->si_fd == kbd ) handle_kbd_input( kbd, nic ); else if ( info->si_fd == nic ) handle_nic_input( nic ); } int main( void ) { // open network device-file and get its hardware-address nic = open( devname, O_RDWR ); if ( nic < 0 ) { perror( devname ); exit(1); } if ( ioctl( nic, 0, &nicaddr ) < 0 ) { perror( "ioctl" ); exit(1); } // install our signal-handler struct sigaction oa, sa = {0}; sigemptyset( &sa.sa_mask ); // no blocked signals sa.sa_flags = SA_SIGINFO; // provide extra info sa.sa_sigaction = my_sigaction; // our signal-handler sigaction( SIGUSR1, &sa, &oa ); // for SIGUSR1 events sigaction( SIGINT, &sa, NULL ); // (and for -C) // enable noncanonical input-mode at the terminal kbd = STDIN_FILENO; struct termios orig_term; tcgetattr( kbd, &orig_term ); struct termios work_term = orig_term; work_term.c_lflag &= ~( ICANON | ECHO ); work_term.c_cc[ VMIN ] = 1; work_term.c_cc[ VTIME ] = 0; tcsetattr( kbd, TCSAFLUSH, &work_term ); // enable asynchronous notification for the terminal device fcntl( kbd, F_SETOWN, getpid() ); fcntl( kbd, F_SETSIG, SIGUSR1 ); fcntl( kbd, F_SETFL, fcntl( kbd, F_GETFL )|FASYNC ); // enable asynchronous notification for the network interface fcntl( nic, F_SETOWN, getpid() ); fcntl( nic, F_SETSIG, SIGUSR1 ); fcntl( nic, F_SETFL, fcntl( nic, F_GETFL )|FASYNC ); // main loop to await -C or typed by user while ( !done ) pause(); // restore the original input-mode for the terminal tcsetattr( kbd, TCSAFLUSH, &orig_term ); // restore the original signal-handler for SIGUSR1 sigaction( SIGUSR1, &oa, NULL ); // close the device-file and terminate this program close( nic ); printf( "\n" ); exit( 0 ); } void handle_nic_input( int nic ) { // read the new data from the network device unsigned char msg[ 1536 ] = {0}; int nbytes = read( nic, msg, sizeof( msg ) ); if ( nbytes < 0 ) return; // if it's a 'netchat' packet, then display its character if ( *(short*)(msg+12) == htons( CHAT_ID ) ) { nbytes = *(short*)(msg+14); write( STDOUT_FILENO, msg+16, nbytes ); } } void handle_kbd_input( int kbd, int nic ) { // read the new data from the keyboard unsigned char inch[ 64 ] = {0}; int nbytes = read( kbd, inch, sizeof( inch ) ); if ( nbytes < 0 ) return; // set 'done' for -key; otherwise echo input to screen if ( *(int*)inch == '\e' ) done = 1; else write( STDOUT_FILENO, inch, nbytes ); // also transmit the keyboard-input in a protocol frame memcpy( inch+16, inch, nbytes ); // keyboard data *(short*)(inch+14) = nbytes; // data's length *(short*)(inch+12) = htons( CHAT_ID ); // protocol-type memcpy( inch+6, nicaddr, 6 ); // source-address memset( inch+0, 0xFF, 6 ); // dest'n-address write( nic, inch, sizeof( inch ) ); // xmit the frame }