/* Modulo de kernel que emula puertos serie Author: Roberto Munoz Gomez Date: 19-03-08 Version: 1.1 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_AUTHOR "Roberto Munoz Gomez " #define DRIVER_DESC "Emulador de puertor serie" /* Module information */ MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); #define ETTY_MAJOR_IN 245 /* Major para los dispositivos que estaran "dentro" de la tpv */ #define ETTY_MAJOR_OUT 246 /* Major para los de "fuera" */ #define ETTY_MINOR 0 /* starting minor number*/ #define VIRTUAL_SERIAL_PORTS 8 #define ETTY_NAME "etty" #define MY_NAME ETTY_NAME static int numberofports=VIRTUAL_SERIAL_PORTS; static int etty_major_in=ETTY_MAJOR_IN; static int etty_major_out=ETTY_MAJOR_OUT; static int etty_minor=ETTY_MINOR; static int debug=0; static int nullmodem=1; module_param(numberofports, int,S_IRUGO); module_param(etty_major_in, int,S_IRUGO); module_param(etty_major_out, int,S_IRUGO); module_param(etty_minor, int,S_IRUGO); module_param(debug, int,S_IRUGO); module_param(nullmodem, int,S_IRUGO); MODULE_PARM_DESC(numberofports,"Numero puertos serie virtuales a crear.(default: 8)"); MODULE_PARM_DESC(etty_major_in,"Major para los puertos in. (default: 240)"); MODULE_PARM_DESC(etty_major_out,"Major para los puertos out. (default: 241)"); MODULE_PARM_DESC(etty_minor,"Minor a partir del cual comienzan los dispositivos. (default: 0)"); MODULE_PARM_DESC(debug,"Un valor distinto de 0 activa la salida de debug, a mayor valor más prolijo («verbose»)"); MODULE_PARM_DESC(nullmodem,"La conexion virtual entre ambos tty se hace mediante un cable NULL-MODEM (con las lineas CTS/RTS y DTR/DSR cruzadas)"); struct etty_dev_t { //char * buffer; struct tty_struct *tty; struct serial_struct * serial; //int top; struct semaphore sem; /* mutual exclusion semaphore */ int opens; /* Variables que guardan el estado de las lineas*/ int mcr; int msr; /*Datos transmitidos y recibidos*/ long long int tx; long long int rx; /*Veces que se han cambiado los valores de estas lineas*/ long long int cts; long long int dsr; }; struct etty_dev{ struct etty_dev_t in; struct etty_dev_t out; //struct tty_struct *tty; int opens; //int major; int minor; }; struct tty_driver * etty_dev_in; struct tty_driver * etty_dev_out; struct etty_dev * dev; static void printdebug(int level,int major,int minor,char *c,...){ va_list lp; if(debug && debug >= level){ va_start(lp,c); if(major == etty_major_out) printk(KERN_DEBUG "ETTY OUT:%d: ",minor); else if(major == etty_major_in) printk(KERN_DEBUG "ETTY IN:%d: ",minor); else printk(KERN_DEBUG "ETTY NOSE: "); vprintk(c,lp); va_end(lp); } } static void etty_close(struct tty_struct *tty, struct file *file) { int minor=iminor(file->f_dentry->d_inode)-etty_minor; int major=imajor(file->f_dentry->d_inode); if(major==etty_major_in){ dev[minor].in.opens--; printdebug(2,major,minor,"Cierro de %s_in%d, y se queda %d veces abierta\n",ETTY_NAME,minor,dev[minor].in.opens); }else{ dev[minor].out.opens--; printdebug(2,major,minor,"Cierro de %s_out%d, y se queda %d veces abierta\n",ETTY_NAME,minor,dev[minor].out.opens); } dev[minor].opens--; } static int etty_open(struct tty_struct *tty, struct file *file) { int minor=iminor(file->f_dentry->d_inode)-etty_minor; int major=imajor(file->f_dentry->d_inode); printdebug(2,major,minor,"Comenzando la apertura de %s_%s%d\n",ETTY_NAME,major==etty_major_in?"in":"out",minor); tty->driver_data = &dev[minor]; if(major==etty_major_in){ etty_dev_in->ttys[minor]=tty; dev[minor].in.tty = tty; dev[minor].in.opens++; }else{ etty_dev_out->ttys[minor]=tty; dev[minor].out.tty = tty; dev[minor].out.opens++; } dev[minor].opens++; printdebug(2,major,minor,"Apertura de %s_%s%d terminada, y van %d veces abierta\n",ETTY_NAME,major==etty_major_in?"in":"out",minor,dev[minor].opens); return 0; } static int etty_write(struct tty_struct *tty,const unsigned char *buffer, int count) { int minor=((struct etty_dev *)(tty->driver_data))->minor; int i; int major=0; struct etty_dev_t * dev2=NULL; struct tty_struct * tty2; if (tty == dev[minor].out.tty) major=etty_major_out; if (tty == dev[minor].in.tty) major=etty_major_in; printdebug(2,major,minor,"Comenzando transmision de %d bytes\n",count); /* * * ESTO ES QUE OUT ESCRIBE EN IN Y QUE IN ESCRIBE EN OUT * */ if (tty == dev[minor].out.tty) dev2=&(dev[minor].in); if (tty == dev[minor].in.tty) dev2=&(dev[minor].out); if(dev2==NULL){ printdebug(2,major,minor,"O la tty_in o la tty_out ha petado y es NULL\n"); printk(KERN_ERR "ETTY: ERROR DE LECTURA O ESCRITURA\n"); return -1; } tty2=dev2->tty; //struct tty_struct * tty2= etty_dev_out->ttys[minor]; if(tty2==NULL || dev2->opens <= 0 || dev[minor].opens <= 0){ printdebug(2,major,minor,"Tienes que abrir la tty de destino antes de poder escribir en esta\n"); printk(KERN_ERR "ETTY: Antes de escribir, abre el dispositivo de salida\n"); return -1; } //printk(KERN_NOTICE "EMPIEZO A ESCRIBIR\n"); //printk(KERN_NOTICE "ESCRIBO %s\n",buffer); printdebug(3,major,minor,"Bloqueando semaforo\n"); down(&dev2->sem); printdebug(3,major,minor,"Semaforo bloqueado\n"); //for(i=0;ibuf.lock)); printdebug(4,major,minor,"Comienza la insercion en el buffer de salida\n"); tty2->low_latency=1; //Para que funcione con escrituras mayores que 512, realiza la escritura en el momento sin esperar al siguiente tick #if KERNEL_VERSION (2,6,18) == LINUX_VERSION_CODE i=tty_insert_flip_string(tty2, buffer,count); #else for (i = 0; i < count; ++i) { if (tty2->flip.count >= TTY_FLIPBUF_SIZE) tty_flip_buffer_push(tty2); tty_insert_flip_char(tty2, buffer[i], TTY_NORMAL); } tty_flip_buffer_push(tty2); #endif printdebug(4,major,minor,"Insercion en el buffer de salida terminada\n"); tty_flip_buffer_push(tty2); printdebug(4,major,minor,"Acabo de hacer un push para que la otra tty vea los datos\n"); //spin_unlock(&(tty2->buf.lock)); if (i != count){ printk(KERN_ERR "ETTY: LOS CANTIDAD LEIDA NO ES IGUAL A LA ENVIADA.LEIDA=%d ENVIADA=%d\n",i,count); } dev2->rx+=i; if (tty == dev[minor].out.tty) dev[minor].out.tx+=i; if (tty == dev[minor].in.tty) dev[minor].in.tx+=i; printdebug(3,major,minor,"Liberando semaforo\n"); up(&dev2->sem); printdebug(3,major,minor,"Semaforo liberado\n"); printdebug(2,major,minor,"Transmision de %d bytes terminada\n",i); return i; } /* Devuelve cuantos caracteres hay dentro del buffer y faltan por enviar al dispositivo, siempre es 0 */ static int etty_chars_in_buffer(struct tty_struct *tty) { //int minor=((struct etty_dev *)(tty->driver_data))->minor; //return buffersize - dev[minor].out.top; return 0; } /* Devuelve el espacio que queda libre para poder escribir en el buffer, como el buffer es directo, es siempre 1 */ static int etty_write_room(struct tty_struct *tty) { //int minor=((struct etty_dev *)(tty->driver_data))->minor; //return buffersize - dev[minor].out.top; return 1; } static void etty_set_termios(struct tty_struct *tty, struct termios * old){ printdebug(2,0,-1,"Soy set_termios y no hago nada de nada\n"); } static int etty_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data,int major) { int ret=0; int i; char stat_buf[32]; struct etty_dev_t *etty; ret+=sprintf(page,"Emulated TTY driver.\nNumber of virtual ports:%d\n",numberofports); #define INFOBIT(info,bit,str) \ if ((info) & (bit)) \ strncat(stat_buf, (str), sizeof(stat_buf) - \ strlen(stat_buf) - 2) for (i=0;iopens,etty->tx,etty->rx); stat_buf[0] = '\0'; stat_buf[1] = '\0'; INFOBIT(etty->mcr,TIOCM_RTS, "|RTS"); INFOBIT(etty->msr,TIOCM_CTS, "|CTS"); INFOBIT(etty->mcr,TIOCM_DTR, "|DTR"); INFOBIT(etty->msr,TIOCM_DSR, "|DSR"); INFOBIT(etty->msr,TIOCM_CAR, "|CD"); INFOBIT(etty->msr,TIOCM_RNG, "|RI"); if (stat_buf[0]) stat_buf[0] = ' '; strcat(stat_buf, "\n"); ret += sprintf(page+ret, stat_buf); } *eof=1; return ret; } static int etty_read_proc_in(char *page, char **start, off_t off, int count, int *eof, void *data) { return etty_read_proc(page,start,off,count,eof,data,etty_major_in); } static int etty_read_proc_out(char *page, char **start, off_t off, int count, int *eof, void *data) { return etty_read_proc(page,start,off,count,eof,data,etty_major_out); } static int etty_tiocmget(struct tty_struct *tty, struct file *file) { int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); struct etty_dev_t *etty ; int result = 0; unsigned int msr; unsigned int mcr; printdebug(3,major,minor,"Comenzamos TIOCM GET\n"); if (major==etty_major_in) etty=&((struct etty_dev *)(tty->driver_data))->in; else etty=&((struct etty_dev *)(tty->driver_data))->out; mcr=etty->mcr; msr=etty->msr; result = ((mcr & TIOCM_DTR) ? TIOCM_DTR : 0) | /* DTR is set */ ((mcr & TIOCM_RTS) ? TIOCM_RTS : 0) | /* RTS is set */ ((mcr & TIOCM_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */ ((mcr & TIOCM_OUT1) ? TIOCM_OUT1 : 0) | ((mcr & TIOCM_OUT2) ? TIOCM_OUT2 : 0) | ((msr & TIOCM_CTS) ? TIOCM_CTS : 0) | /* CTS is set */ ((msr & TIOCM_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/ ((msr & TIOCM_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */ ((msr & TIOCM_DSR) ? TIOCM_DSR : 0); /* DSR is set */ printdebug(3,major,minor,"Finalizamos TIOCM GET\n"); return result; } static int etty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); struct etty_dev_t *etty ; struct etty_dev_t *etty2 ; unsigned int mcr; unsigned int mcr2; unsigned int msr2; long long int cts2; long long int dsr2; printdebug(3,major,minor,"Comenzamos TIOCM SET\n"); if (major==etty_major_in){ etty=&((struct etty_dev *)(tty->driver_data))->in; etty2=&((struct etty_dev *)(tty->driver_data))->out; }else{ etty=&((struct etty_dev *)(tty->driver_data))->out; etty2=&((struct etty_dev *)(tty->driver_data))->in; } mcr=etty->mcr; mcr2=etty2->mcr; msr2=etty2->msr; cts2=etty2->cts; dsr2=etty2->dsr; /* CRUZAMOS LAS LINEAS CTS Y RTS como en los cables NULL-MODEM si la variable esta activa */ if (set & TIOCM_RTS){ printdebug(4,major,minor,"Activamos RTS\n"); mcr |= TIOCM_RTS; mcr2 |= TIOCM_RTS; msr2 |= TIOCM_CTS; cts2++; } if (set & TIOCM_DTR){ printdebug(4,major,minor,"Activamos DTR\n"); mcr |= TIOCM_DTR; mcr2 |= TIOCM_DTR; msr2 |= TIOCM_DSR; dsr2++; } if (set & TIOCM_OUT1){ printdebug(4,major,minor,"Activamos OUT1\n"); mcr |= TIOCM_OUT1; } if (set & TIOCM_OUT2){ printdebug(4,major,minor,"Activamos OUT2\n"); mcr |= TIOCM_OUT1; } if (clear & TIOCM_OUT1){ printdebug(4,major,minor,"Quitamos OUT1\n"); mcr &= ~TIOCM_OUT1; } if (clear & TIOCM_OUT2){ printdebug(4,major,minor,"Quitamos OUT2\n"); mcr &= ~TIOCM_OUT2; } if (clear & TIOCM_RTS){ printdebug(4,major,minor,"Quitamos RTS\n"); mcr &= ~TIOCM_RTS; mcr2 &= ~TIOCM_RTS; msr2 &= ~TIOCM_CTS; cts2++; } if (clear & TIOCM_DTR){ printdebug(4,major,minor,"Quitamos DTR\n"); mcr &= ~TIOCM_DTR; mcr2 &= ~TIOCM_DTR; msr2 &= ~TIOCM_DSR; dsr2++; } /* set the new TIOCM value in the device */ etty->mcr = mcr; if(nullmodem){ printdebug(4,major,minor,"Lo cruzamos!!! NULL MODEM\n"); etty2->msr = msr2; etty2->cts=cts2; etty2->dsr=dsr2; }else{ printdebug(4,major,minor,"Cable directo, sin cruzar\n"); etty2->mcr = mcr2; } printdebug(3,major,minor,"Finalizamos TIOCM SET\n"); return 0; } int etty_ioctl_tiocmget(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ printdebug(3,0,-1,"Comenzamos IOCTL TIOCMGET\n"); put_user(etty_tiocmget(tty,file),(int *)arg); printdebug(3,0,-1,"Terminamos IOCTL TIOCMGET\n"); return 0; } int etty_ioctl_tiocsserial(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); struct etty_dev_t * disp; //struct serial_struct *tmp; printdebug(3,major,minor,"Comenzamos TIOCSSERIAL\n"); if (major==etty_major_in) disp=&((struct etty_dev *)(tty->driver_data))->in; else disp=&((struct etty_dev *)(tty->driver_data))->out; if (!arg) return -EFAULT; if (copy_from_user(disp->serial,(void __user *)arg, sizeof(struct serial_struct))) return -EFAULT; printdebug(3,major,minor,"Terminamos TIOCSSERIAL\n"); return 0; } int etty_ioctl_tiocgserial(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); struct etty_dev_t * disp; //struct tty_driver * etty; struct serial_struct tmp; printdebug(3,major,minor,"Comenzamos TIOCGSERIAL\n"); if (major==etty_major_in) disp=&((struct etty_dev *)(tty->driver_data))->in; else disp=&((struct etty_dev *)(tty->driver_data))->out; if (!arg) return -EFAULT; if (disp->serial){ printdebug(4,major,minor,"Devuelvo lo que tengo\n"); if (copy_to_user((void __user *)arg, &(disp->serial), sizeof(struct serial_struct))) return -EFAULT; }else{ printdebug(4,major,minor,"Me invento los datos\n"); //if(major==etty_major_in) etty=etty_dev_in; //else etty=etty_dev_out; memset(&tmp, 0, sizeof(struct serial_struct)); //tmp.type = etty->type; //tmp.line = etty->line; //tmp.port = etty->port; //tmp.irq = etty->irq; tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; //tmp.xmit_fifo_size = etty->xmit_fifo_size; //tmp.baud_base = etty->baud_base; tmp.close_delay = 5*HZ; tmp.closing_wait = 30*HZ; //tmp.custom_divisor = etty->custom_divisor; //tmp.hub6 = etty->hub6; //tmp.io_type = etty->io_type; if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct))) return -EFAULT; } printdebug(3,major,minor,"Terminamos TIOCGSERIAL\n"); return 0; } int etty_ioctl_tcgets(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); struct etty_dev_t * disp; printdebug(3,major,minor,"Comenzamos TCGETS\n"); if (major==etty_major_in) disp=&((struct etty_dev *)(tty->driver_data))->in; else disp=&((struct etty_dev *)(tty->driver_data))->out; if (kernel_termios_to_user_termios((void __user *)arg,disp->tty->termios)) return -EFAULT; printdebug(3,major,minor,"Terminamos TCGETS\n"); return 0; } void pintacflag(int f){ switch (f & CSIZE){ case CS5:printdebug(4,0,-1,"Tengo CS5 \n");break; case CS6:printdebug(4,0,-1,"Tengo CS6 \n");break; case CS7:printdebug(4,0,-1,"Tengo CS7 \n");break; case CS8:printdebug(4,0,-1,"Tengo CS8 \n");break; } if (f & CSTOPB ) printdebug(4,0,-1,"Tengo CSTOPB \n"); if (f & CREAD ) printdebug(4,0,-1,"Tengo CREAD \n"); if (f & PARENB ) printdebug(4,0,-1,"Tengo PARENB \n"); if (f & PARODD ) printdebug(4,0,-1,"Tengo PARODD \n"); if (f & HUPCL ) printdebug(4,0,-1,"Tengo HUPCL \n"); if (f & CLOCAL ) printdebug(4,0,-1,"Tengo CLOCAL \n"); switch (f & CBAUD){ case B0 :printdebug(4,0,-1,"Tengo B0 \n");break; case B50 :printdebug(4,0,-1,"Tengo B50 \n");break; case B75 :printdebug(4,0,-1,"Tengo B75 \n");break; case B110 :printdebug(4,0,-1,"Tengo B110 \n");break; case B134 :printdebug(4,0,-1,"Tengo B134 \n");break; case B150 :printdebug(4,0,-1,"Tengo B150 \n");break; case B200 :printdebug(4,0,-1,"Tengo B200 \n");break; case B300 :printdebug(4,0,-1,"Tengo B300 \n");break; case B600 :printdebug(4,0,-1,"Tengo B600 \n");break; case B1200 :printdebug(4,0,-1,"Tengo B1200 \n");break; case B1800 :printdebug(4,0,-1,"Tengo B1800 \n");break; case B2400 :printdebug(4,0,-1,"Tengo B2400 \n");break; case B4800 :printdebug(4,0,-1,"Tengo B4800 \n");break; case B9600 :printdebug(4,0,-1,"Tengo B9600 \n");break; case B19200 :printdebug(4,0,-1,"Tengo B19200 \n");break; case B38400 :printdebug(4,0,-1,"Tengo B38400 \n");break; } } void pintaoflag(int f){ if (f & OPOST )printdebug(4,0,-1,"Tengo OPOST \n"); if (f & OLCUC )printdebug(4,0,-1,"Tengo OLCUC \n"); if (f & ONLCR )printdebug(4,0,-1,"Tengo ONLCR \n"); if (f & OCRNL )printdebug(4,0,-1,"Tengo OCRNL \n"); if (f & ONOCR )printdebug(4,0,-1,"Tengo ONOCR \n"); if (f & ONLRET)printdebug(4,0,-1,"Tengo ONLRET\n"); if (f & OFILL )printdebug(4,0,-1,"Tengo OFILL \n"); if (f & OFDEL )printdebug(4,0,-1,"Tengo OFDEL \n"); if (f & NLDLY )printdebug(4,0,-1,"Tengo NLDLY \n"); switch (f & NLDLY ){ case NL0: printdebug(4,0,-1,"Tengo NL0 \n");break; case NL1: printdebug(4,0,-1,"Tengo NL1 \n");break; } if (f & CRDLY )printdebug(4,0,-1,"Tengo CRDLY \n"); switch (f & CRDLY ){ case CR0:printdebug(4,0,-1,"Tengo CR0 \n");break; case CR1:printdebug(4,0,-1,"Tengo CR1 \n");break; case CR2:printdebug(4,0,-1,"Tengo CR2 \n");break; case CR3:printdebug(4,0,-1,"Tengo CR3 \n");break; } if (f & TABDLY)printdebug(4,0,-1,"Tengo TABDLY\n"); switch (f & TABDLY){ case TAB0:printdebug(4,0,-1,"Tengo TAB0\n");break; case TAB1:printdebug(4,0,-1,"Tengo TAB1\n");break; case TAB2:printdebug(4,0,-1,"Tengo TAB2\n");break; case TAB3:printdebug(4,0,-1,"Tengo TAB3\n");break; } if (f & BSDLY )printdebug(4,0,-1,"Tengo BSDLY \n"); switch (f & BSDLY ){ case BS0:printdebug(4,0,-1,"Tengo BS0 \n");break; case BS1:printdebug(4,0,-1,"Tengo BS1 \n");break; } if (f & VTDLY )printdebug(4,0,-1,"Tengo VTDLY \n"); switch (f & VTDLY ){ case VT0:printdebug(4,0,-1,"Tengo VT0 \n");break; case VT1:printdebug(4,0,-1,"Tengo VT1 \n");break; } if (f & FFDLY )printdebug(4,0,-1,"Tengo FFDLY \n"); switch (f & FFDLY ){ case FF0:printdebug(4,0,-1,"Tengo FF0 \n");break; case FF1:printdebug(4,0,-1,"Tengo FF1 \n");break; } } int etty_ioctl_tcsets(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct termios temp; struct etty_dev_t * disp; int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); struct tty_ldisc * ld = tty_ldisc_ref(tty); //speed_t ospeedi; //speed_t ospeedo; //speed_t nspeedi; //speed_t nspeedo; printdebug(3,major,minor,"Comenzamos TCSETS\n"); if (major==etty_major_in){ disp=&((struct etty_dev *)(tty->driver_data))->in; }else{ disp=&((struct etty_dev *)(tty->driver_data))->out; } if (user_termios_to_kernel_termios(&temp,(void __user *)arg)) return -EFAULT; printdebug(4,major,minor,"speed: Tengo %u en tty y %u en termios y me ponen %u de termios\n",tty_get_baud_rate(disp->tty),tty_termios_baud_rate(disp->tty->termios),tty_termios_baud_rate(&temp)); printdebug(4,major,minor,"c_iflag: Tengo %x y me ponen %x\n",disp->tty->termios->c_iflag,temp.c_iflag); disp->tty->termios->c_iflag = temp.c_iflag; printdebug(4,major,minor,"c_oflag: Tengo %x y me ponen %x\n",disp->tty->termios->c_oflag,temp.c_oflag); disp->tty->termios->c_oflag = temp.c_oflag; printdebug(4,major,minor,"c_cflag: Tengo %x y me ponen %x\n",disp->tty->termios->c_cflag,temp.c_cflag); disp->tty->termios->c_cflag = temp.c_cflag; printdebug(4,major,minor,"c_lflag: Tengo %x y me ponen %x\n",disp->tty->termios->c_lflag,temp.c_lflag); disp->tty->termios->c_lflag = temp.c_lflag; //printdebug(4,major,"speed: Tengo %d y me ponen %d\n",tty_get_baud_rate(disp->tty),tty_get_baud_rate(temp)); /* Esto es para que funcione bien la nd77 y los caracteres de control se reciban donde deben */ if (ld != NULL) { if (ld->flush_buffer){ printdebug(2,major,minor,"HAGOOO FLUSH!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); ld->flush_buffer(tty); } tty_ldisc_deref(ld); } printdebug(3,major,minor,"Terminamos TCSETS\n"); return 0; } int etty_ioctl_serconfig(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); printdebug(2,major,minor,"Soy SERCONFIG (a.k.a autoconfig) y no hago nada\n"); return 0; } int etty_ioctl_setflags(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int syc[2]; if (copy_from_user(syc,(void __user *)arg, sizeof(int)*2)) return -EFAULT; return etty_tiocmset(tty,file,syc[0],syc[1]); } int etty_ioctl_tiocgicount(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct etty_dev_t * disp; int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ void __user *icnt = (void __user *)arg; struct serial_icounter_struct icount; //struct uart_state *state = tty->driver_data; //struct uart_icount cnow; //struct uart_port *port = state->port; //spin_lock_irq(&port->lock); //memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); //spin_unlock_irq(&port->lock); //icount.cts = cnow.cts; //icount.dsr = cnow.dsr; //icount.rng = cnow.rng; //icount.dcd = cnow.dcd; //icount.rx = cnow.rx; //icount.tx = cnow.tx; //icount.frame = cnow.frame; //icount.overrun = cnow.overrun; //icount.parity = cnow.parity; //icount.brk = cnow.brk; //icount.buf_overrun = cnow.buf_overrun; printdebug(3,major,minor,"Comenzamos TIOCGICOUNT\n"); if (major==etty_major_in) disp=&((struct etty_dev *)(tty->driver_data))->in; else disp=&((struct etty_dev *)(tty->driver_data))->out; icount.cts = 0; icount.dsr = 0; icount.rng = 0; icount.dcd = 0; icount.rx = disp->rx; icount.tx = disp->tx; icount.frame = 0; icount.overrun = 0; icount.parity = 0; icount.brk = 0; icount.buf_overrun = 0; printdebug(3,major,minor,"Finalizamos TIOCGICOUNT\n"); return copy_to_user(icnt, &icount, sizeof(struct serial_icounter_struct)); } static int etty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { int major=imajor(file->f_dentry->d_inode); int minor=iminor(file->f_dentry->d_inode); printdebug(2,major,minor,"Ejecuto IOCTL\n"); switch (cmd){ case TIOCGSERIAL: return etty_ioctl_tiocgserial(tty,file,cmd,arg);break; case TIOCSSERIAL: return etty_ioctl_tiocsserial(tty,file,cmd,arg);break; case TIOCMGET: return etty_ioctl_tiocmget(tty,file,cmd,arg);break; case TCGETS: return etty_ioctl_tcgets(tty,file,cmd,arg); break; case TIOCSERCONFIG: return etty_ioctl_serconfig(tty,file,cmd,arg); break; case TCSETS: //Si se descomenta dan errores muy raros. case TCSETSF: case TCSETSW: return etty_ioctl_tcsets(tty,file,cmd,arg); break; case TIOCGICOUNT: return etty_ioctl_tiocgicount(tty,file,cmd,arg);break; case 0x6969: return etty_ioctl_setflags(tty,file,cmd,arg); break; default: printdebug(2,major,minor,"La IOCTL %x no existe\n",cmd); return -ENOIOCTLCMD; } return -ENOIOCTLCMD; } static void etty_flush_buffer(struct tty_struct *tty){ printdebug(2,0,-1,"Soy FLUSH_BUFFER y no hago nada\n"); } static struct tty_operations serial_ops_in = { .open = etty_open, .close = etty_close, .write = etty_write, .write_room = etty_write_room, .set_termios = etty_set_termios, .tiocmset = etty_tiocmset, .tiocmget = etty_tiocmget, .ioctl = etty_ioctl, .read_proc=etty_read_proc_in, .chars_in_buffer=etty_chars_in_buffer, .flush_buffer=etty_flush_buffer, }; static struct tty_operations serial_ops_out = { .open = etty_open, .close = etty_close, .write = etty_write, .write_room = etty_write_room, .set_termios = etty_set_termios, .tiocmset = etty_tiocmset, .tiocmget = etty_tiocmget, .ioctl = etty_ioctl, .read_proc=etty_read_proc_out, .chars_in_buffer=etty_chars_in_buffer, .flush_buffer=etty_flush_buffer, }; static struct tty_driver * initialize_etty(int major){ int retval; struct tty_driver *d; /* initialize the tty driver */ printdebug(2,major,-1,"Comenzando alloc_tty_driver\n"); d=alloc_tty_driver(numberofports); if (!d) { printdebug(2,major,-1,"fallo en alloc_tty_driver\n"); printk(KERN_ERR "ETTY: failed to alloc etty driver\n"); return NULL; } printdebug(2,major,-1,"alloc_tty_driver terminado\n"); d->owner = THIS_MODULE; if(major==ETTY_MAJOR_IN){ d->driver_name = "emulated_tty_in"; d->name = "etty_in"; //d->devfs_name = "tts/etty_in%d"; d->major = ETTY_MAJOR_IN; }else{ d->driver_name = "emulated_tty_out"; d->name = "etty_out"; //d->devfs_name = "tts/etty_out%d"; d->major = ETTY_MAJOR_OUT; } d->type = TTY_DRIVER_TYPE_SERIAL; d->minor_start=etty_minor; d->minor_num=numberofports; d->subtype = SERIAL_TYPE_NORMAL; //d->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; d->flags = TTY_DRIVER_REAL_RAW ; d->init_termios = tty_std_termios; d->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; d->init_termios.c_oflag = ~OPOST; d->init_termios.c_lflag = ~(ECHO | ECHOE|ICANON); if(major==ETTY_MAJOR_IN){ printdebug(2,major,-1,"Establezco operaciones in\n"); tty_set_operations(d, &serial_ops_in); }else{ printdebug(2,major,-1,"Establezco operaciones out\n"); tty_set_operations(d, &serial_ops_out); } printdebug(2,major,-1,"Registrando driver\n"); retval = tty_register_driver(d); if (retval) { printdebug(2,major,-1,"Fallo en el registro del driver\n"); printk(KERN_INFO "ETTY: failed to register etty driver\n"); put_tty_driver(d); return NULL; } //printdebug(2,"Registrando devices\n"); //for (i = 0; i < numberofports; ++i){ // printdebug(3,"Registrando device %d\n",i); // tty_register_device(d, i, NULL); // printdebug(3,"Device %d registrado\n",i); //} //printdebug(2,"Registro de devices terminado\n"); printdebug(2,major,-1,"Driver registrado\n"); return d; } static int finalize_etty(struct tty_driver *d){ //int i; //printdebug(2,"Desregistrando devices\n"); //for (i = 0; i < numberofports; ++i){ // printdebug(3,"Desregistrando device %d\n",i); // tty_unregister_device(d, i); // printdebug(3,"Device %d desregistrado\n",i); //} //printdebug(2,"Desregistro de devices terminado\n"); tty_unregister_driver(d); return 0; } static int __init etty_init(void) { int i; printdebug(1,0,-1,"Comenzando carga del emulador de puertos serie\n"); if (numberofports < 1 //|| buffersize < 1 ) return -1; printdebug(2,0,-1, "Inicializando etty_in.\n"); etty_dev_in=initialize_etty(etty_major_in); printdebug(2,0,-1, "etty_in inicializado.\n"); printdebug(2,0,-1, "Inicializando etty_out.\n"); etty_dev_out=initialize_etty(etty_major_out); printdebug(2,0,-1, "etty_out inicializado.\n"); if(NULL == etty_dev_in){ printdebug(2,0,-1, "La inicializacion de etty_in devolvio NULL.\n"); return -1; } if(NULL == etty_dev_out){ printdebug(2,0,-1, "La inicializacion de etty_out devolvio NULL.\n"); return -1; } dev=(struct etty_dev *)kmalloc(numberofports*(sizeof(struct etty_dev)),GFP_KERNEL); printdebug(2,0,-1, "Comenzamos inicializacion de los puertos.\n"); for (i=0;i