+static void sllin_receive_buf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct sllin *sl = (struct sllin *) tty->disc_data;
+ netdev_dbg(sl->dev, "sllin_receive_buf invoked, count = %u\n", count);
+
+ if (!sl || sl->magic != SLLIN_MAGIC || !netif_running(sl->dev))
+ return;
+
+ if (sl->lin_master)
+ sllin_master_receive_buf(tty, cp, fp, count);
+ else
+ sllin_slave_receive_buf(tty, cp, fp, count);
+
+}
+
+static int sllin_send_tx_buff(struct sllin *sl)
+{
+ struct tty_struct *tty = sl->tty;
+ int remains;
+ int res;
+
+ set_bit(SLF_TXBUFF_RQ, &sl->flags);
+ do {
+ if (unlikely(test_and_set_bit(SLF_TXBUFF_INPR, &sl->flags)))
+ return 0; /* ongoing concurrent processing */
+
+ clear_bit(SLF_TXBUFF_RQ, &sl->flags);
+ smp_mb__after_clear_bit();
+
+#ifdef BREAK_BY_BAUD
+ if (sl->lin_state != SLSTATE_BREAK_SENT)
+ remains = sl->tx_lim - sl->tx_cnt;
+ else
+ remains = 1;
+#else
+ remains = sl->tx_lim - sl->tx_cnt;
+#endif
+
+ res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
+ if (res < 0)
+ goto error_in_write;
+
+ remains -= res;
+ sl->tx_cnt += res;
+
+ if (remains > 0) {
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains);
+ if (res < 0) {
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ goto error_in_write;
+ }
+
+ remains -= res;
+ sl->tx_cnt += res;
+ }
+
+ netdev_dbg(sl->dev, "sllin_send_tx_buff sent %d, remains %d\n",
+ sl->tx_cnt, remains);
+
+ clear_bit(SLF_TXBUFF_INPR, &sl->flags);
+ smp_mb__after_clear_bit();
+
+ } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags)));
+
+ return 0;
+
+error_in_write:
+ clear_bit(SLF_TXBUFF_INPR, &sl->flags);
+ return -1;
+
+}
+
+#ifdef BREAK_BY_BAUD
+static int sllin_send_break(struct sllin *sl)
+{
+ struct tty_struct *tty = sl->tty;
+ unsigned long break_baud;
+ int res;
+
+ break_baud = ((sl->lin_baud * 2) / 3);
+ sltty_change_speed(tty, break_baud);
+
+ tty->ops->flush_buffer(tty);
+ sl->rx_cnt = SLLIN_BUFF_BREAK;
+
+ sl->rx_expect = SLLIN_BUFF_BREAK + 1;
+ sl->lin_state = SLSTATE_BREAK_SENT;
+
+ res = sllin_send_tx_buff(sl);
+ if (res < 0) {
+ sl->lin_state = SLSTATE_IDLE;
+ return res;
+ }
+
+ return 0;
+}
+#else /* BREAK_BY_BAUD */
+
+static int sllin_send_break(struct sllin *sl)
+{
+ struct tty_struct *tty = sl->tty;
+ int retval;
+ unsigned long break_baud;
+ unsigned long usleep_range_min;
+ unsigned long usleep_range_max;
+
+ break_baud = ((sl->lin_baud * 2) / 3);
+ sl->rx_cnt = SLLIN_BUFF_BREAK;
+ sl->rx_expect = SLLIN_BUFF_BREAK + 1;
+ sl->lin_state = SLSTATE_BREAK_SENT;
+
+ /* Do the break ourselves; Inspired by
+ http://lxr.linux.no/#linux+v3.1.2/drivers/tty/tty_io.c#L2452 */
+ retval = tty->ops->break_ctl(tty, -1);
+ if (retval)
+ return retval;
+
+ /* udelay(712); */
+ usleep_range_min = (1000000l * SLLIN_SAMPLES_PER_CHAR) / break_baud;
+ usleep_range_max = usleep_range_min + 50;
+ usleep_range(usleep_range_min, usleep_range_max);
+
+ retval = tty->ops->break_ctl(tty, 0);
+ usleep_range_min = (1000000l * 1 /* 1 bit */) / break_baud;
+ usleep_range_max = usleep_range_min + 30;
+ usleep_range(usleep_range_min, usleep_range_max);
+
+ tty->ops->flush_buffer(tty);
+
+ sl->tx_cnt = SLLIN_BUFF_SYNC;
+
+ netdev_dbg(sl->dev, "Break sent.\n");
+ set_bit(SLF_RXEVENT, &sl->flags);
+ wake_up(&sl->kwt_wq);
+
+ return 0;
+}
+#endif /* BREAK_BY_BAUD */
+
+
+static enum hrtimer_restart sllin_rx_timeout_handler(struct hrtimer *hrtimer)
+{
+ struct sllin *sl = container_of(hrtimer, struct sllin, rx_timer);
+
+ sllin_report_error(sl, LIN_ERR_RX_TIMEOUT);
+ set_bit(SLF_TMOUTEVENT, &sl->flags);
+ wake_up(&sl->kwt_wq);
+
+ return HRTIMER_NORESTART;
+}
+