/* * Main code for btty Babylon driver class * * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: ldisc.c,v 1.3 2004/04/08 05:59:00 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include "bab_module.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE > 0x020100 #include #endif #include "ldisc.h" #include "port.h" #include "aps_if.h" #include "msgq.h" #include "version.h" #define DEVCLASS_NAME "btty" int devclass_id; static unsigned char port_ids[MAXPORTNUM]; static unsigned char port_ids_inuse[MAXPORTNUM]; int count ; /* * Ports */ static port_t *register_port(void); static port_t *port_list = NULL; /* * the daemon message queue */ static struct MsgQ q; /* * Function declarations */ static int l_open(struct tty_struct *tty); static int l_ioctl (struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg); static void l_close(struct tty_struct *tty); static void l_receive_buf(struct tty_struct *tty, const unsigned char *cp,char *fp, int count); static int l_receive_room(struct tty_struct *tty); static void l_write_wakeup(struct tty_struct *tty); static void l_flush_buffer(struct tty_struct *tty); static ssize_t l_chars_in_buffer(struct tty_struct *tty); /* 16bit FCS Table */ __u16 fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; /* FCS Calculation */ #define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) /* * Line dicipline struct */ static struct tty_ldisc adc_ldisc = { TTY_LDISC_MAGIC, /* Magic number */ #if LINUX_VERSION_CODE >= 0x20100 "btty", #endif 0, /* Number */ 0, /* flags */ l_open, /* open */ l_close, /* close */ l_flush_buffer, /* void flush_buffer */ l_chars_in_buffer, /* int chars_in_buffer */ 0, /* int read(tty, file, unsigned char buf, unsigned int nr */ 0, /* int write(...) */ l_ioctl, /* int ioctl */ 0, /* void set_termios(tty, struct termios *old) */ 0, /* int select(...) */ l_receive_buf, l_receive_room, l_write_wakeup }; /* * Debug variable */ #ifdef DEBUG static int debug=1; #else static int debug=0; #endif /* * Babylon wants a port connected */ static int b_connect(channel_t *ch, const char *phone, __u32 type) { port_t *p = ch->dev; Req msg; if (debug) { printk("btty: b_connect\n"); } if (!port_is_valid(p)) { printk("btty: b_connect: port (%p) not valid\n", p); ch->ConnectComplete(ch, 1); return(-1); } if (debug) printk("btty: Connected requested on %d: %p: u32: %d\n",p->channel.index,p,type); ch->state = CS_DIALING; msg.cmd=REQ_DIAL; msg.port=p->channel.index; strncpy(msg.phone_num, phone, sizeof(msg.phone_num)); msg.pid = -1; queueMsg(&q,&msg,sizeof(Req)); return(0); } /* * Babylon wants a port hungup */ static int b_hangup(channel_t *ch) { port_t *p = ch->dev; Req msg; if (debug) { printk("btty: b_hangup\n"); } if (!port_is_valid(p)) { printk("btty: b_hangup: port (%p) not valid\n", p); return(-1); } if (debug) printk("btty: Hangup requested on %d: %p\n",p->channel.index,p); ch->state = CS_DISCONNECTING; msg.cmd=REQ_HANGUP; msg.port=p->channel.index; msg.phone_num[0]=(char)0; msg.pid = -1; queueMsg(&q,&msg,sizeof(Req)); return(0); } static int b_output(channel_t *ch, struct sk_buff *skb) { port_t *p = ch->dev; unsigned char *buf, c; const unsigned char *d = skb->data; unsigned short fcs; int l = skb->len; if (debug>1) printk("btty: b_output(%d)\n", l); if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return -EIO; } if (l > 2000) return -EIO; if (test_and_set_busy(&p->channel)) { printk("%s: b_output: already busy!!!\n", p->channel.device_name); return -EBUSY; } if (test_and_set_bit(0, &p->xbuf.lock)) { if (debug>1) printk("btty: b_output: busy\n"); return -EBUSY; } p->xskb = skb; buf = p->xbuf.base; *buf++ = 0x7e; fcs = PPP_INITFCS; for (; l > 0; l--) { unsigned char c = *d++; fcs = PPP_FCS(fcs, c); if (0x7e == c || 0x7d == c || in_xmap(p, c)) { *buf++ = 0x7d; c ^= 0x20; } *buf++ = c; } fcs ^= 0xffff; c = (fcs & 0xff); if (0x7e == c || 0x7d == c || in_xmap(p, c)) { *buf++ = 0x7d; c ^= 0x20; } *buf++ = c; c = ((fcs >> 8) & 0xff); if (0x7e == c || 0x7d == c || in_xmap(p, c)) { *buf++ = 0x7d; c ^= 0x20; } *buf++ = c; *buf++ = 0x7e; /*memcpy(p->xbuf.head, d, l);*/ p->xbuf.head = p->xbuf.base; p->xbuf.count = buf - p->xbuf.head; if (debug>1) printk("btty: b_output: writing\n"); p->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); queue_task(&p->bh, &tq_immediate); mark_bh(IMMEDIATE_BH); #if 0 l_write_wakeup(p->tty); #endif return 0; } static void b_output_bh(void *data) { port_t *p = data; if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return; } l_write_wakeup(p->tty); } /* * Take a port out-of-service */ static void unregister_port(port_t *port) { unsigned long flags; port_t **p; int port_id = -1; if (debug) { printk("btty: unregister_port\n"); } if(!port_is_valid(port)) { printk("btty: %s: port not valid\n", __FUNCTION__); return; } if ((CS_IDLE != port->channel.state) && (CS_UNAVAIL != port->channel.state)) { printk("btty: unregister_port: calling ConnectComplete(%s, %lu)\n", port->channel.device_name, port->callId); port->channel.state = CS_UNAVAIL; port->channel.ConnectComplete(&port->channel, port->callId); } /* Save out port number before trashing it */ port_id=port->channel.index; UnregisterChannel(&port->channel); /* remove port from port_list */ save_flags(flags); cli(); for (p=&port_list; *p && *p!=port; p=&(*p)->next) ; if (*p) *p = (*p)->next; restore_flags(flags); if (port->rbuf.base) free_page((unsigned long)port->rbuf.base); if (port->xbuf.base) free_page((unsigned long)port->xbuf.base); kfree(port); /* Set this port number as available as the last step */ port_ids[port_id]=(char)0; port_ids_inuse[port_id]=(char)0; return; } static void btty_use(channel_t *ch) { MOD_INC_USE_COUNT; } static void btty_unuse(channel_t *ch) { MOD_DEC_USE_COUNT; } /* * Put a port in-service */ static port_t *register_port(void) { port_t *p; int port_id,i; port_id = -1; /* Find the first free port number */ for (i=0;imagic = PORT_MAGIC; p->channel.index = port_id; sprintf(p->channel.device_name, "btty%u", p->channel.index); strcpy(p->channel.dev_class, "btty"); p->channel.dev = p; p->channel.use = btty_use; p->channel.unuse = btty_unuse; p->channel.Output = b_output; p->channel.Connect = b_connect; p->channel.Hangup = b_hangup; p->channel.state = CS_UNAVAIL; p->tty = NULL; /* Allocate frame buffers */ p->rbuf.base = (__u8 *) __get_free_page(GFP_KERNEL); p->xbuf.base = (__u8 *) __get_free_page(GFP_KERNEL); if(!p->rbuf.base || !p->xbuf.base) goto fail; p->rbuf.head = p->rbuf.base; p->rbuf.count = 0; p->rbuf.size = PAGE_SIZE; p->rbuf.fcs = PPP_INITFCS; p->rbuf.lock = 0; p->xbuf.head = p->xbuf.base; p->xbuf.count = 0; p->xbuf.size = PAGE_SIZE; p->xbuf.fcs = PPP_INITFCS; p->xbuf.lock = 0; p->xskb = NULL; p->channel.x_accm[0] = ~0L; p->channel.x_accm[1] = 0L; p->channel.x_accm[2] = 0L; p->channel.x_accm[3] = 0x60000000L; p->channel.r_accm = ~0L; p->bh.sync = 0; p->bh.routine = b_output_bh; p->bh.data = p; p->in_tx = 0; p->next = port_list; port_list = p; if(RegisterChannel(&p->channel)) { printk("btty: Channel registration failed\n"); goto fail; } else { printk("btty: Registered port btty%d\n",p->channel.index); } return p; fail: if (p->rbuf.base) free_page((unsigned long)p->rbuf.base); if (p->xbuf.base) free_page((unsigned long)p->xbuf.base); kfree(p); return(NULL); } /* * The port has been opened and the line discipline switched to Babylon * Construct, initialize and register a channel. Create and initialize * the control and state stucture, attach it to the tty device. * Setup buffers. */ static int l_open(struct tty_struct *tty) { port_t *p = (port_t *) tty->disc_data; if (debug) { printk("btty: l_open\n"); } /* Make sure we're not already connected */ if(p) return -EEXIST; MOD_INC_USE_COUNT; return 0; } static port_t *find_port (unsigned long num) { port_t *p=port_list; for (p=port_list; p; p=p->next) if (p->channel.index == num) return p; return NULL; } static int l_ioctl (struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) { port_t *p; /* Check we are not over our port limit */ if (arg>MAXPORTNUM) { printk("btty: l_ioctrl called with bad port number (%ld)\n",arg); return(-EINVAL); } /* Find the port number */ if ((p = find_port(arg))==NULL) { printk("btty: attach to port %lu -> not found\n", arg); return(-EINVAL); } /* Check it really does exist for fun */ if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return -EIO; } if (debug>1) { printk("l_ioctl: arg %ld: %p\n",arg,p); } switch (cmd) { case BIOC_TTY_ATTACH: if (debug) { printk("l_ioctl: tty_attach called with arg: %ld (%p)\n",arg,p); } /* Check that we are not already attached to this port */ if (p->tty) { printk("btty: attach to port %lu -> busy\n", arg); return(-EBUSY); } /* Crosslink the structures */ p->tty = tty; tty->disc_data = p; /* Flush any pending buffers in the driver or line discipline */ if(tty->ldisc.flush_buffer) { tty->ldisc.flush_buffer(tty); if (debug) printk("btty: ldisc.flush_buffer\n"); } if(tty->driver.flush_buffer) { tty->driver.flush_buffer(tty); if (debug) printk("btty: driver.flush_buffer\n"); } if (debug) printk("btty: attach on state %d\n",p->channel.state); if (CS_DIALING == p->channel.state) { /* outgoing connection */ if (debug) printk("btty: attach for outgoing connection\n"); p->channel.state = CS_CONNECTED; p->channel.ConnectComplete(&p->channel, 0); p->channel.Up(&p->channel); } else { /* incoming connection */ if (debug) printk("btty: attach for incoming connection\n"); p->channel.state = CS_CONNECTED; p->channel.Open(&p->channel); p->channel.Up(&p->channel); } return(0); case BIOC_TTY_SETSTATUS: if (debug) { printk("btty: l_iotcl: tty_setstatus called with arg: %ld",arg); } /* Check for a valid status */ if ((arg<0) || (arg>CS_UNAVAIL)) { return(-EINVAL); } /* Set the status in babylon */ p->channel.state = arg; return(0); case BIOC_TTY_GETSTATUS: if (debug>1) { printk("btty: l_iotcl: tty_getstatus called with arg: %ld, state %d",arg,p->channel.state); } /* Return the status in babylon */ return(p->channel.state); } return -ENOIOCTLCMD; } /* * The port has been closed and/or the line discipline changed * Unregister and free the channel, buffers and the control structure */ static void l_close(struct tty_struct *tty) { port_t *p = (port_t *) tty->disc_data; if (debug) { printk("btty: l_close\n"); } if(!p) { return; } if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return; } p->channel.state = CS_IDLE; p->channel.ConnectComplete(&p->channel, 0x100); p->channel.Down(&p->channel); tty->disc_data = NULL; p->tty = NULL; MOD_DEC_USE_COUNT; } /* * Flush the read/RX buffer for the port */ static void l_flush_buffer(struct tty_struct *tty) { port_t *p = (port_t *) tty->disc_data; if (debug>1) { printk("btty: l_flush_buffer\n"); } if(!p) return; if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return; } /* Flush the RX buffer */ memset(p->rbuf.base, 0, p->rbuf.size); p->rbuf.count = 0; p->rbuf.head = p->rbuf.base; } /* * Return the number of characters waiting in the RX buffer */ static ssize_t l_chars_in_buffer(struct tty_struct *tty) { port_t *p = (port_t *) tty->disc_data; if(!p) return -ENODEV; if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return 0; } if (debug>2) { printk("btty: l_chars_in_buffer(%d)\n", p->rbuf.count); } return p->rbuf.count; } /* * Validate a received frame and pass to the protocol stack */ int l_do_frame(port_t *p) { struct sk_buff *skb; if (debug>1) printk("btty: l_do_frame\n"); if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return -1; } /* Check for runts */ if(p->rbuf.count < 2) goto runt; /* Check the FCS */ if(p->rbuf.fcs != PPP_GOODFCS) goto bad_crc; /* Chop off the FCS */ p->rbuf.count -= 2; if (CS_CONNECTED != p->channel.state) goto drop; skb = dev_alloc_skb(p->rbuf.count); if (!skb) goto drop; if (debug>1) printk("btty: l_do_frame: copying packet\n"); memcpy(skb_put(skb, p->rbuf.count), p->rbuf.base, p->rbuf.count); /* Pass the packet to Babylon */ cli(); ch_Input(&p->channel, skb); sti(); return 0; drop: if (debug) printk("btty: l_do_frame: dropping packet\n"); return 0; bad_crc: if (debug) printk("btty: Received packet with bad CRC (%04x)\n", p->rbuf.fcs); ++p->rx_crc_errors; return -1; runt: if (debug) printk("btty: Received runt packet\n"); return -1; } /* * Data is available at the tty driver */ static void l_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { port_t *p = (port_t *) tty->disc_data; buf_t *buf = NULL; unsigned char c; if(!p) return; if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return; } buf = &p->rbuf; /* Collect the characters and flags */ while(count-- > 0) { p->rx_bytes++; c = *cp++; if(fp) { if(*fp && p->error == 0) { p->error = *fp; switch(p->error) { case TTY_OVERRUN: ++p->rx_fifo_errors; break; case TTY_FRAME: case TTY_BREAK: ++p->rx_frame_errors; break; } } ++fp; } /* End of Frame */ if(c == PPP_FLAG) { if(p->escape) p->error |= 0x80; if(buf->count && l_do_frame(p)) p->rx_errors++; /* Prep the buffer for the next frame */ buf->count = 0; buf->fcs = PPP_INITFCS; p->escape = 0; p->error = 0; continue; } /* if the packet is in error, look no further */ if(p->error != 0) continue; /* Ignore mapped chars */ if(in_rmap(p, c)) { continue; } /* Deal with escapes and escaped chars */ if(p->escape) { c ^= p->escape; p->escape = 0; } else if(c == PPP_ESCAPE) { p->escape = PPP_TRANS; continue; } /* Store the character if there is room */ if(buf->count < buf->size) { buf->base[buf->count++] = c; buf->fcs = PPP_FCS(buf->fcs, c); continue; } /* No room, were toast */ if (!p->error) { p->rx_length_errors++; p->error |= 0xC0; } } } static int l_receive_room(struct tty_struct *tty) { return 65536; /* We don't restrict incoming data */ } /* * Called by the driver when it has more room. If we have more to * send, we do it here. */ static void l_write_wakeup(struct tty_struct *tty) { int actual; port_t *p = (port_t *)tty->disc_data; if (debug>1) printk("btty: l_write_wakeup\n"); /* Make sure we're connected */ if(!p) return; if (!port_is_valid(p)) { printk("btty: %s: port not valid\n", __FUNCTION__); return; } if (test_and_set_bit(0, &p->in_tx)) return; if (debug>1) printk("btty: xbuf.count: %d\n", p->xbuf.count); if(p->xbuf.count <= 0) { /* * The serial buffer is almost empty so we should record * the previous packet as sent and prep for a new buffer. */ done: p->tx_packets++; tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); if (p->xskb) { b_dev_kfree_skb(p->xskb); p->xskb = NULL; clear_busy(&p->channel); } clear_bit(0, &p->xbuf.lock); cli(); clear_bit(0, &p->in_tx); p->channel.OutputComplete(&p->channel); sti(); return; } actual = tty->driver.write(tty, 0, p->xbuf.head, p->xbuf.count); if (debug>1) printk("btty: actual: %d\n", actual); if (actual > 0) { p->xbuf.head += actual; p->xbuf.count -= actual; p->tx_bytes += actual; if (p->xbuf.count <= 0) goto done; } clear_bit(0, &p->in_tx); } /*----------------------------------------------------------------------------*/ #if LINUX_VERSION_CODE < 0x20100 static int call_read(struct inode *inode, struct file *filp, char *buf, int len) #else static ssize_t call_read(struct file *filp, char *buf, size_t len, loff_t *off) #endif { struct qMsg *msg; int ret; if (debug) printk("btty: call_read\n"); if (len < 0) return -EINVAL; ret = verify_area(VERIFY_WRITE, buf, len); if (ret) return ret; msg = waitMsg(&q); if (!msg) return -EAGAIN; len = msg->len < len ? msg->len : len; if (copy_to_user(buf, msg->data, len)) len = -EFAULT; kfree(msg); return len; } /* * Routine to accept data from the proc file and stuff it on the message queue */ #if LINUX_VERSION_CODE < 0x20100 int call_write(struct inode *i, struct file *f, const char *buf, int len) #else ssize_t call_write(struct file *f, const char *buf, size_t len, loff_t *off) #endif { Req msg; int ret; if (debug) printk("btty: call_write\n"); /* Make sure the data size is correct */ if (len!=sizeof(Req)) { printk("btty: write: data wrong size\n"); return(-EINVAL); } if((ret = verify_area(VERIFY_READ, buf, sizeof(Req))) == 0) { if (copy_from_user(&msg, buf, sizeof(Req))) return -EFAULT; queueMsg(&q,&msg,sizeof(Req)); if (debug) printk("btty: recieved req on btty%d for pid %d\n",msg.port,msg.pid); return(sizeof(Req)); } return(-EIO); } /* * Routine to open the proc file */ static int call_open(struct inode *inode, struct file *filp) { MOD_INC_USE_COUNT; if (debug) printk("btty: call_open\n"); filp->private_data = NULL; return 0; } /* * Routine to release the proc file */ #if LINUX_VERSION_CODE < 0x20100 static void call_release(struct inode *inode, struct file *filp) #else static int call_release(struct inode *inode, struct file *filp) #endif { filp->private_data = NULL; if (debug) printk("btty: call_release\n"); MOD_DEC_USE_COUNT; #if LINUX_VERSION_CODE > 0x20100 return 0; #endif } /* * Routine to get ioctl's on the proc file. */ static int call_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) { port_t *p; int count; if (debug) printk("btty: call_ioctl\n"); /* Process commands not requiring a port number */ switch(cmd) { case BIOCF_TTY_RESET: /* * Reset the driver: unregister * all ports - basically return the driver to initial sane state */ for (count=0;countchannel.state = CS_IDLE; if (debug) printk("btty: created port (%p)\n",p); return(p->channel.index); } else { return(-EIO); } case BIOCF_TTY_FIND_PORT: /* Find the first free port number */ count = -1; for (count=0;countMAXPORTNUM)) { if (debug) printk("btty: passed invalid port num (%ld)\n", arg); return(-EINVAL); } /* Convert the port number to a port pointer*/ if ((p=find_port(arg))==NULL) { return(-EINVAL); } /* Double check this is valid in case the programmer is a moron */ if (!port_is_valid(p)) { printk("btty: passed invalid port (%p)\n", p); return -EIO; } if (debug) { printk("btty: call_iotcl: called with cmd: %d and arg: %ld (%p)\n",cmd,arg,p); } /* Process the command */ switch (cmd) { case BIOCF_TTY_STAT_IDLE: p->channel.state = CS_IDLE; return(0); case BIOCF_TTY_STAT_DIALING: p->channel.state = CS_DIALING; return(0); case BIOCF_TTY_STAT_RINGING: p->channel.state = CS_RINGING; return(0); case BIOCF_TTY_STAT_CONNECTING: p->channel.state = CS_CONNECTING; return(0); case BIOCF_TTY_STAT_CONNECTED: p->channel.state = CS_CONNECTED; return(0); case BIOCF_TTY_STAT_DISCONNECTING: p->channel.state = CS_DISCONNECTING; return(0); case BIOCF_TTY_STAT_STALLED: p->channel.state = CS_STALLED; return(0); case BIOCF_TTY_STAT_UNAVAIL: p->channel.state = CS_UNAVAIL; return(0); case BIOCF_TTY_CONN_FAILED: /* * Special function to set the status to idle and pass along * that we are failed the connection */ p->channel.state = CS_IDLE; p->channel.ConnectComplete(&p->channel, -2); return(0); case BIOCF_TTY_UNREGISTER_PORT: unregister_port(p); return(0); } return(-ENOIOCTLCMD); } /* * Routine to see if there is any data waiting for the proc file */ #if LINUX_VERSION_CODE < 0x20100 static int call_select(struct inode *inode, struct file *filp, int sel_type, select_table *wait) { int ret; if (debug) printk("btty: call_select\n"); switch (sel_type) { case SEL_IN: /* read */ select_wait(&q.wait, wait); ret = !!q.msgQ; break; case SEL_OUT: /* write */ ret = 1; break; default: ret = 0; break; } return ret; } #else static unsigned call_poll(struct file *filp, poll_table *wait) { int ret; poll_wait(filp, &q.wait, wait); ret = POLLOUT | POLLWRNORM; if (!!q.msgQ) ret |= POLLIN | POLLRDNORM; return ret; } #endif static struct file_operations call_file_operations = { .read = call_read, .write = call_write, #if LINUX_VERSION_CODE < 0x20100 .select = call_select, /* select */ #else .poll = call_poll, #endif .ioctl = call_ioctl, /* ioctl */ .open = call_open, .release = call_release, }; static struct inode_operations call_inode_operations = { &call_file_operations, 0, }; static struct proc_dir_entry proc_ttyctrl = { 0, 8, "bttyctrl", S_IFREG | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, 1, 0, 0, 0, &call_inode_operations, 0, 0, 0, 0, 0 }; /*----------------------------------------------------------------------------*/ /* * This device class is alive! */ int init_module() { pr_info("SpellCaster Babylon TTY Device Class v%s Loaded\n", version); pr_info("Copyright (C) 1996 - 2000 SpellCaster Telecommunications Inc.\n"); initMsgQ(&q); devclass_id = RegisterDeviceClass(DEVCLASS_NAME); if(devclass_id < 0) { pr_info("btty: TTY Device class already registered!\n"); return -1; } if (tty_register_ldisc(N_BAB, &adc_ldisc)) { printk(KERN_INFO "btty: tty_register_ldisc failed\n"); goto fail_notty; } if (proc_register(&proc_root, &proc_ttyctrl)) { printk(KERN_INFO "btty: proc_register failed\n"); goto fail_noproc; } memset(&port_ids,(char)0,MAXPORTNUM); memset(&port_ids_inuse,(char)0,MAXPORTNUM); return 0; /*fail: proc_unregister(&proc_root, proc_ttyctrl.low_ino); */ fail_noproc: tty_register_ldisc(N_BAB, NULL); fail_notty: UnregisterDeviceClass(devclass_id); return -1; } /* * This device class is toast */ void cleanup_module() { cli(); while (port_list) { unregister_port(port_list); } sti(); proc_unregister(&proc_root, proc_ttyctrl.low_ino); tty_register_ldisc(N_BAB, NULL); UnregisterDeviceClass(devclass_id); if (debug) printk("btty: Cleanup done\n"); }