?? ircomm_tty.c
字號(hào):
IRDA_DEBUG(2, "%s(), returning after block_til_ready with %d\n", __FUNCTION__ , ret); return ret; } return 0;}/* * Function ircomm_tty_close (tty, filp) * * This routine is called when a particular tty device is closed. * */static void ircomm_tty_close(struct tty_struct *tty, struct file *filp){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned long flags; IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); if (!tty) return; ASSERT(self != NULL, return;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); spin_lock_irqsave(&self->spinlock, flags); if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&self->spinlock, flags); IRDA_DEBUG(0, "%s(), returning 1\n", __FUNCTION__ ); return; } if ((tty->count == 1) && (self->open_count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ IRDA_DEBUG(0, "%s(), bad serial port count; " "tty->count is 1, state->count is %d\n", __FUNCTION__ , self->open_count); self->open_count = 1; } if (--self->open_count < 0) { ERROR("%s(), bad serial port count for ttys%d: %d\n", __FUNCTION__, self->line, self->open_count); self->open_count = 0; } if (self->open_count) { spin_unlock_irqrestore(&self->spinlock, flags); IRDA_DEBUG(0, "%s(), open count > 0\n", __FUNCTION__ ); return; } /* Hum... Should be test_and_set_bit ??? - Jean II */ set_bit(ASYNC_B_CLOSING, &self->flags); /* We need to unlock here (we were unlocking at the end of this * function), because tty_wait_until_sent() may schedule. * I don't know if the rest should be protected somehow, * so someone should check. - Jean II */ spin_unlock_irqrestore(&self->spinlock, flags); /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if (self->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, self->closing_wait); ircomm_tty_shutdown(self); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; self->tty = NULL; if (self->blocked_open) { if (self->close_delay) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(self->close_delay); } wake_up_interruptible(&self->open_wait); } self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&self->close_wait);}/* * Function ircomm_tty_flush_buffer (tty) * * * */static void ircomm_tty_flush_buffer(struct tty_struct *tty){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; ASSERT(self != NULL, return;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); /* * Let do_softint() do this to avoid race condition with * do_softint() ;-) */ schedule_work(&self->tqueue);}/* * Function ircomm_tty_do_softint (private_) * * We use this routine to give the write wakeup to the user at at a * safe time (as fast as possible after write have completed). This * can be compared to the Tx interrupt. */static void ircomm_tty_do_softint(void *private_){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) private_; struct tty_struct *tty; unsigned long flags; struct sk_buff *skb, *ctrl_skb; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); if (!self || self->magic != IRCOMM_TTY_MAGIC) return; tty = self->tty; if (!tty) return; /* Unlink control buffer */ spin_lock_irqsave(&self->spinlock, flags); ctrl_skb = self->ctrl_skb; self->ctrl_skb = NULL; spin_unlock_irqrestore(&self->spinlock, flags); /* Flush control buffer if any */ if(ctrl_skb) { if(self->flow == FLOW_START) ircomm_control_request(self->ircomm, ctrl_skb); /* Drop reference count - see ircomm_ttp_data_request(). */ dev_kfree_skb(ctrl_skb); } if (tty->hw_stopped) return; /* Unlink transmit buffer */ spin_lock_irqsave(&self->spinlock, flags); skb = self->tx_skb; self->tx_skb = NULL; spin_unlock_irqrestore(&self->spinlock, flags); /* Flush transmit buffer if any */ if (skb) { ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL); /* Drop reference count - see ircomm_ttp_data_request(). */ dev_kfree_skb(skb); } /* Check if user (still) wants to be waken up */ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { (tty->ldisc.write_wakeup)(tty); } wake_up_interruptible(&tty->write_wait);}/* * Function ircomm_tty_write (tty, from_user, buf, count) * * This routine is called by the kernel to write a series of characters * to the tty device. The characters may come from user space or kernel * space. This routine will return the number of characters actually * accepted for writing. This routine is mandatory. */static int ircomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *ubuf, int count){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned char *kbuf; /* Buffer in kernel space */ unsigned long flags; struct sk_buff *skb; int tailroom = 0; int len = 0; int size; IRDA_DEBUG(2, "%s(), count=%d, hw_stopped=%d\n", __FUNCTION__ , count, tty->hw_stopped); ASSERT(self != NULL, return -1;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); /* We may receive packets from the TTY even before we have finished * our setup. Not cool. * The problem is that we don't know the final header and data size * to create the proper skb, so any skb we would create would have * bogus header and data size, so need care. * We use a bogus header size to safely detect this condition. * Another problem is that hw_stopped was set to 0 way before it * should be, so we would drop this skb. It should now be fixed. * One option is to not accept data until we are properly setup. * But, I suspect that when it happens, the ppp line discipline * just "drops" the data, which might screw up connect scripts. * The second option is to create a "safe skb", with large header * and small size (see ircomm_tty_open() for values). * We just need to make sure that when the real values get filled, * we don't mess up the original "safe skb" (see tx_data_size). * Jean II */ if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) { IRDA_DEBUG(1, "%s() : not initialised\n", __FUNCTION__);#ifdef IRCOMM_NO_TX_BEFORE_INIT /* We didn't consume anything, TTY will retry */ return 0;#endif } if (count < 1) return 0; /* Additional copy to avoid copy_from_user() under spinlock. * We tradeoff this extra copy to allow to pack more the * IrCOMM frames. This is advantageous because the IrDA link * is the bottleneck. */ if (from_user) { kbuf = kmalloc(count, GFP_KERNEL); if (kbuf == NULL) return -ENOMEM; if (copy_from_user(kbuf, ubuf, count)) { kfree(kbuf); return -EFAULT; } } else /* The buffer is already in kernel space */ kbuf = (unsigned char *) ubuf; /* Protect our manipulation of self->tx_skb and related */ spin_lock_irqsave(&self->spinlock, flags); /* Fetch current transmit buffer */ skb = self->tx_skb; /* * Send out all the data we get, possibly as multiple fragmented * frames, but this will only happen if the data is larger than the * max data size. The normal case however is just the opposite, and * this function may be called multiple times, and will then actually * defragment the data and send it out as one packet as soon as * possible, but at a safer point in time */ while (count) { size = count; /* Adjust data size to the max data size */ if (size > self->max_data_size) size = self->max_data_size; /* * Do we already have a buffer ready for transmit, or do * we need to allocate a new frame */ if (skb) { /* * Any room for more data at the end of the current * transmit buffer? Cannot use skb_tailroom, since * dev_alloc_skb gives us a larger skb than we * requested * Note : use tx_data_size, because max_data_size * may have changed and we don't want to overwrite * the skb. - Jean II */ if ((tailroom = (self->tx_data_size - skb->len)) > 0) { /* Adjust data to tailroom */ if (size > tailroom) size = tailroom; } else { /* * Current transmit frame is full, so break * out, so we can send it as soon as possible */ break; } } else { /* Prepare a full sized frame */ skb = dev_alloc_skb(self->max_data_size+ self->max_header_size); if (!skb) { spin_unlock_irqrestore(&self->spinlock, flags); if (from_user) kfree(kbuf); return -ENOBUFS; } skb_reserve(skb, self->max_header_size); self->tx_skb = skb; /* Remember skb size because max_data_size may * change later on - Jean II */ self->tx_data_size = self->max_data_size; } /* Copy data */ memcpy(skb_put(skb,size), kbuf + len, size); count -= size; len += size; } spin_unlock_irqrestore(&self->spinlock, flags); if (from_user) kfree(kbuf); /* * Schedule a new thread which will transmit the frame as soon * as possible, but at a safe point in time. We do this so the * "user" can give us data multiple times, as PPP does (because of * its 256 byte tx buffer). We will then defragment and send out * all this data as one single packet. */ schedule_work(&self->tqueue); return len;}/* * Function ircomm_tty_write_room (tty) * * This routine returns the numbers of characters the tty driver will * accept for queuing to be written. This number is subject to change as * output buffers get emptied, or if the output flow control is acted. */static int ircomm_tty_write_room(struct tty_struct *tty){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned long flags; int ret; ASSERT(self != NULL, return -1;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);#ifdef IRCOMM_NO_TX_BEFORE_INIT /* max_header_size tells us if the channel is initialised or not. */ if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) /* Don't bother us yet */ return 0;#endif /* Check if we are allowed to transmit any data. * hw_stopped is the regular flow control. * Jean II */ if (tty->hw_stopped) ret = 0; else { spin_lock_irqsave(&self->spinlock, flags); if (self->tx_skb) ret = self->tx_data_size - self->tx_skb->len; else ret = self->max_data_size; spin_unlock_irqrestore(&self->spinlock, flags); } IRDA_DEBUG(2, "%s(), ret=%d\n", __FUNCTION__ , ret); return ret;}/* * Function ircomm_tty_wait_until_sent (tty, timeout) * * This routine waits until the device has written out all of the * characters in its transmitter FIFO. */static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned long orig_jiffies, poll_time; unsigned long flags; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); ASSERT(self != NULL, return;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); orig_jiffies = jiffies; /* Set poll time to 200 ms */ poll_time = IRDA_MIN(timeout, msecs_to_jiffies(200)); spin_lock_irqsave(&self->spinlock, flags); while (self->tx_skb && self->tx_skb->len) { spin_unlock_irqrestore(&self->spinlock, flags); current->state = TASK_INTERRUPTIBLE; schedule_timeout(poll_time); spin_lock_irqsave(&self->spinlock, flags); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } spin_unlock_irqrestore(&self->spinlock, flags); current->state = TASK_RUNNING;}/* * Function ircomm_tty_throttle (tty) * * This routine notifies the tty driver that input buffers for the line * discipline are close to full, and it should somehow signal that no * more characters should be sent to the tty. */static void ircomm_tty_throttle(struct tty_struct *tty){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); ASSERT(self != NULL, return;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); /* Software flow control? */ if (I_IXOFF(tty)) ircomm_tty_send_xchar(tty, STOP_CHAR(tty)); /* Hardware flow control? */ if (tty->termios->c_cflag & CRTSCTS) { self->settings.dte &= ~IRCOMM_RTS; self->settings.dte |= IRCOMM_DELTA_RTS; ircomm_param_request(self, IRCOMM_DTE, TRUE); } ircomm_flow_request(self->ircomm, FLOW_STOP);}/* * Function ircomm_tty_unthrottle (tty) * * This routine notifies the tty drivers that it should signals that * characters can now be sent to the tty without fear of overrunning the * input buffers of the line disciplines. */static void ircomm_tty_unthrottle(struct tty_struct *tty){ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); ASSERT(self != NULL, return;); ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); /* Using software flow control? */ if (I_IXOFF(tty)) { ircomm_tty_send_xchar(tty, START_CHAR(tty)); } /* Using hardware flow control? */ if (tty->termios->c_cflag & CRTSCTS) {
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -