diff options
Diffstat (limited to 'drivers/staging/fwserial/fwserial.c')
| -rw-r--r-- | drivers/staging/fwserial/fwserial.c | 734 |
1 files changed, 373 insertions, 361 deletions
diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index d03a7f57e8d..384758b11e3 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -12,12 +12,10 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/sched.h> #include <linux/slab.h> #include <linux/device.h> @@ -40,12 +38,10 @@ static int num_ttys = 4; /* # of std ttys to create per fw_card */ /* - doubles as loopback port index */ static bool auto_connect = true; /* try to VIRT_CABLE to every peer */ static bool create_loop_dev = true; /* create a loopback device for each card */ -bool limit_bw; /* limit async bandwidth to 20% of max */ module_param_named(ttys, num_ttys, int, S_IRUGO | S_IWUSR); module_param_named(auto, auto_connect, bool, S_IRUGO | S_IWUSR); module_param_named(loop, create_loop_dev, bool, S_IRUGO | S_IWUSR); -module_param(limit_bw, bool, S_IRUGO | S_IWUSR); /* * Threshold below which the tty is woken for writing @@ -74,12 +70,20 @@ static DEFINE_MUTEX(port_table_lock); static bool port_table_corrupt; #define FWTTY_INVALID_INDEX MAX_TOTAL_PORTS +#define loop_idx(port) (((port)->index) / num_ports) +#define table_idx(loop) ((loop) * num_ports + num_ttys) + /* total # of tty ports created per fw_card */ static int num_ports; /* slab used as pool for struct fwtty_transactions */ static struct kmem_cache *fwtty_txn_cache; +struct tty_driver *fwtty_driver; +static struct tty_driver *fwloop_driver; + +static struct dentry *fwserial_debugfs; + struct fwtty_transaction; typedef void (*fwtty_transaction_cb)(struct fw_card *card, int rcode, void *data, size_t length, @@ -95,13 +99,16 @@ struct fwtty_transaction { }; #define to_device(a, b) (a->b) -#define fwtty_err(p, s, v...) dev_err(to_device(p, device), s, ##v) -#define fwtty_info(p, s, v...) dev_info(to_device(p, device), s, ##v) -#define fwtty_notice(p, s, v...) dev_notice(to_device(p, device), s, ##v) -#define fwtty_dbg(p, s, v...) \ - dev_dbg(to_device(p, device), "%s: " s, __func__, ##v) -#define fwtty_err_ratelimited(p, s, v...) \ - dev_err_ratelimited(to_device(p, device), s, ##v) +#define fwtty_err(p, fmt, ...) \ + dev_err(to_device(p, device), fmt, ##__VA_ARGS__) +#define fwtty_info(p, fmt, ...) \ + dev_info(to_device(p, device), fmt, ##__VA_ARGS__) +#define fwtty_notice(p, fmt, ...) \ + dev_notice(to_device(p, device), fmt, ##__VA_ARGS__) +#define fwtty_dbg(p, fmt, ...) \ + dev_dbg(to_device(p, device), "%s: " fmt, __func__, ##__VA_ARGS__) +#define fwtty_err_ratelimited(p, fmt, ...) \ + dev_err_ratelimited(to_device(p, device), fmt, ##__VA_ARGS__) #ifdef DEBUG static inline void debug_short_write(struct fwtty_port *port, int c, int n) @@ -112,7 +119,7 @@ static inline void debug_short_write(struct fwtty_port *port, int c, int n) spin_lock_bh(&port->lock); avail = dma_fifo_avail(&port->tx_fifo); spin_unlock_bh(&port->lock); - fwtty_dbg(port, "short write: avail:%d req:%d wrote:%d", + fwtty_dbg(port, "short write: avail:%d req:%d wrote:%d\n", avail, c, n); } } @@ -125,14 +132,14 @@ static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card, #ifdef FWTTY_PROFILING -static void profile_fifo_avail(struct fwtty_port *port, unsigned *stat) +static void fwtty_profile_fifo(struct fwtty_port *port, unsigned *stat) { spin_lock_bh(&port->lock); - profile_size_distrib(stat, dma_fifo_avail(&port->tx_fifo)); + fwtty_profile_data(stat, dma_fifo_avail(&port->tx_fifo)); spin_unlock_bh(&port->lock); } -static void dump_profile(struct seq_file *m, struct stats *stats) +static void fwtty_dump_profile(struct seq_file *m, struct stats *stats) { /* for each stat, print sum of 0 to 2^k, then individually */ int k = 4; @@ -172,36 +179,41 @@ static void dump_profile(struct seq_file *m, struct stats *stats) } #else -#define profile_fifo_avail(port, stat) -#define dump_profile(m, stats) +#define fwtty_profile_fifo(port, stat) +#define fwtty_dump_profile(m, stats) #endif -/* Returns the max receive packet size for the given card */ +/* + * Returns the max receive packet size for the given node + * Devices which are OHCI v1.0/ v1.1/ v1.2-draft or RFC 2734 compliant + * are required by specification to support max_rec of 8 (512 bytes) or more. + */ static inline int device_max_receive(struct fw_device *fw_device) { - return 1 << (clamp_t(int, fw_device->max_rec, 8U, 11U) + 1); + /* see IEEE 1394-2008 table 8-8 */ + return min(2 << fw_device->max_rec, 4096); } static void fwtty_log_tx_error(struct fwtty_port *port, int rcode) { switch (rcode) { case RCODE_SEND_ERROR: - fwtty_err_ratelimited(port, "card busy"); + fwtty_err_ratelimited(port, "card busy\n"); break; case RCODE_ADDRESS_ERROR: - fwtty_err_ratelimited(port, "bad unit addr or write length"); + fwtty_err_ratelimited(port, "bad unit addr or write length\n"); break; case RCODE_DATA_ERROR: - fwtty_err_ratelimited(port, "failed rx"); + fwtty_err_ratelimited(port, "failed rx\n"); break; case RCODE_NO_ACK: - fwtty_err_ratelimited(port, "missing ack"); + fwtty_err_ratelimited(port, "missing ack\n"); break; case RCODE_BUSY: - fwtty_err_ratelimited(port, "remote busy"); + fwtty_err_ratelimited(port, "remote busy\n"); break; default: - fwtty_err_ratelimited(port, "failed tx: %d", rcode); + fwtty_err_ratelimited(port, "failed tx: %d\n", rcode); } } @@ -276,7 +288,7 @@ static void __fwtty_restart_tx(struct fwtty_port *port) schedule_delayed_work(&port->drain, 0); avail = dma_fifo_avail(&port->tx_fifo); - fwtty_dbg(port, "fifo len: %d avail: %d", len, avail); + fwtty_dbg(port, "fifo len: %d avail: %d\n", len, avail); } static void fwtty_restart_tx(struct fwtty_port *port) @@ -312,7 +324,7 @@ static void fwtty_update_port_status(struct fwtty_port *port, unsigned status) if (delta & TIOCM_CTS) ++port->icount.cts; - fwtty_dbg(port, "status: %x delta: %x", status, delta); + fwtty_dbg(port, "status: %x delta: %x\n", status, delta); if (delta & TIOCM_CAR) { tty = tty_port_tty_get(&port->port); @@ -440,16 +452,27 @@ static int fwtty_write_port_status(struct fwtty_port *port) return err; } -static void __fwtty_throttle(struct fwtty_port *port, struct tty_struct *tty) +static void fwtty_throttle_port(struct fwtty_port *port) { + struct tty_struct *tty; unsigned old; + tty = tty_port_tty_get(&port->port); + if (!tty) + return; + + spin_lock_bh(&port->lock); + old = port->mctrl; port->mctrl |= OOB_RX_THROTTLE; if (C_CRTSCTS(tty)) port->mctrl &= ~TIOCM_RTS; if (~old & OOB_RX_THROTTLE) __fwtty_write_port_status(port); + + spin_unlock_bh(&port->lock); + + tty_kref_put(tty); } /** @@ -489,116 +512,41 @@ static void fwtty_do_hangup(struct work_struct *work) static void fwtty_emit_breaks(struct work_struct *work) { struct fwtty_port *port = to_port(to_delayed_work(work), emit_breaks); - struct tty_struct *tty; static const char buf[16]; unsigned long now = jiffies; unsigned long elapsed = now - port->break_last; int n, t, c, brk = 0; - tty = tty_port_tty_get(&port->port); - if (!tty) - return; - /* generate breaks at the line rate (but at least 1) */ n = (elapsed * port->cps) / HZ + 1; port->break_last = now; - fwtty_dbg(port, "sending %d brks", n); + fwtty_dbg(port, "sending %d brks\n", n); while (n) { t = min(n, 16); - c = tty_insert_flip_string_fixed_flag(tty, buf, TTY_BREAK, t); + c = tty_insert_flip_string_fixed_flag(&port->port, buf, + TTY_BREAK, t); n -= c; brk += c; if (c < t) break; } - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&port->port); if (port->mstatus & (UART_LSR_BI << 24)) schedule_delayed_work(&port->emit_breaks, FREQ_BREAKS); port->icount.brk += brk; } -static void fwtty_pushrx(struct work_struct *work) -{ - struct fwtty_port *port = to_port(work, push); - struct tty_struct *tty; - struct buffered_rx *buf, *next; - int n, c = 0; - - tty = tty_port_tty_get(&port->port); - if (!tty) - return; - - spin_lock_bh(&port->lock); - list_for_each_entry_safe(buf, next, &port->buf_list, list) { - n = tty_insert_flip_string_fixed_flag(tty, buf->data, - TTY_NORMAL, buf->n); - c += n; - port->buffered -= n; - if (n < buf->n) { - if (n > 0) { - memmove(buf->data, buf->data + n, buf->n - n); - buf->n -= n; - } - __fwtty_throttle(port, tty); - break; - } else { - list_del(&buf->list); - kfree(buf); - } - } - if (c > 0) - tty_flip_buffer_push(tty); - - if (list_empty(&port->buf_list)) - clear_bit(BUFFERING_RX, &port->flags); - spin_unlock_bh(&port->lock); - - tty_kref_put(tty); -} - -static int fwtty_buffer_rx(struct fwtty_port *port, unsigned char *d, size_t n) -{ - struct buffered_rx *buf; - size_t size = (n + sizeof(struct buffered_rx) + 0xFF) & ~0xFF; - - if (port->buffered + n > HIGH_WATERMARK) - return 0; - buf = kmalloc(size, GFP_ATOMIC); - if (!buf) - return 0; - INIT_LIST_HEAD(&buf->list); - buf->n = n; - memcpy(buf->data, d, n); - - spin_lock_bh(&port->lock); - list_add_tail(&buf->list, &port->buf_list); - port->buffered += n; - if (port->buffered > port->stats.watermark) - port->stats.watermark = port->buffered; - set_bit(BUFFERING_RX, &port->flags); - spin_unlock_bh(&port->lock); - - return n; -} - static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) { - struct tty_struct *tty; int c, n = len; unsigned lsr; int err = 0; - tty = tty_port_tty_get(&port->port); - if (!tty) - return -ENOENT; - - fwtty_dbg(port, "%d", n); - profile_size_distrib(port->stats.reads, n); + fwtty_dbg(port, "%d\n", n); + fwtty_profile_data(port->stats.reads, n); if (port->write_only) { n = 0; @@ -616,7 +564,7 @@ static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) lsr &= port->status_mask; if (lsr & ~port->ignore_mask & UART_LSR_OE) { - if (!tty_insert_flip_char(tty, 0, TTY_OVERRUN)) { + if (!tty_insert_flip_char(&port->port, 0, TTY_OVERRUN)) { err = -EIO; goto out; } @@ -629,31 +577,27 @@ static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) goto out; } - if (!test_bit(BUFFERING_RX, &port->flags)) { - c = tty_insert_flip_string_fixed_flag(tty, data, TTY_NORMAL, n); - if (c > 0) - tty_flip_buffer_push(tty); - n -= c; - - if (n) { - /* start buffering and throttling */ - n -= fwtty_buffer_rx(port, &data[c], n); - - spin_lock_bh(&port->lock); - __fwtty_throttle(port, tty); - spin_unlock_bh(&port->lock); - } - } else - n -= fwtty_buffer_rx(port, data, n); + c = tty_insert_flip_string_fixed_flag(&port->port, data, TTY_NORMAL, n); + if (c > 0) + tty_flip_buffer_push(&port->port); + n -= c; if (n) { port->overrun = true; err = -EIO; + fwtty_err_ratelimited(port, "flip buffer overrun\n"); + + } else { + /* throttle the sender if remaining flip buffer space has + * reached high watermark to avoid losing data which may be + * in-flight. Since the AR request context is 32k, that much + * data may have _already_ been acked. + */ + if (tty_buffer_space_avail(&port->port) < HIGH_WATERMARK) + fwtty_throttle_port(port); } out: - tty_kref_put(tty); - port->icount.rx += len; port->stats.lost += n; return err; @@ -684,15 +628,15 @@ static void fwtty_port_handler(struct fw_card *card, rcu_read_unlock(); if (!peer || peer != rcu_access_pointer(port->peer)) { rcode = RCODE_ADDRESS_ERROR; - fwtty_err_ratelimited(port, "ignoring unauthenticated data"); + fwtty_err_ratelimited(port, "ignoring unauthenticated data\n"); goto respond; } switch (tcode) { case TCODE_WRITE_QUADLET_REQUEST: - if (addr != port->rx_handler.offset || len != 4) + if (addr != port->rx_handler.offset || len != 4) { rcode = RCODE_ADDRESS_ERROR; - else { + } else { fwtty_update_port_status(port, *(unsigned *)data); rcode = RCODE_COMPLETE; } @@ -739,10 +683,9 @@ static void fwtty_tx_complete(struct fw_card *card, int rcode, struct fwtty_transaction *txn) { struct fwtty_port *port = txn->port; - struct tty_struct *tty; int len; - fwtty_dbg(port, "rcode: %d", rcode); + fwtty_dbg(port, "rcode: %d\n", rcode); switch (rcode) { case RCODE_COMPLETE: @@ -764,13 +707,8 @@ static void fwtty_tx_complete(struct fw_card *card, int rcode, port->stats.dropped += txn->dma_pended.len; } - if (len < WAKEUP_CHARS) { - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } - } + if (len < WAKEUP_CHARS) + tty_port_tty_wakeup(&port->port); } static int fwtty_tx(struct fwtty_port *port, bool drain) @@ -799,7 +737,7 @@ static int fwtty_tx(struct fwtty_port *port, bool drain) /* try to write as many dma transactions out as possible */ n = -EAGAIN; while (!tty->stopped && !tty->hw_stopped && - !test_bit(STOP_TX, &port->flags)) { + !test_bit(STOP_TX, &port->flags)) { txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC); if (!txn) { n = -ENOMEM; @@ -810,22 +748,23 @@ static int fwtty_tx(struct fwtty_port *port, bool drain) n = dma_fifo_out_pend(&port->tx_fifo, &txn->dma_pended); spin_unlock_bh(&port->lock); - fwtty_dbg(port, "out: %u rem: %d", txn->dma_pended.len, n); + fwtty_dbg(port, "out: %u rem: %d\n", txn->dma_pended.len, n); if (n < 0) { kmem_cache_free(fwtty_txn_cache, txn); - if (n == -EAGAIN) + if (n == -EAGAIN) { ++port->stats.tx_stall; - else if (n == -ENODATA) - profile_size_distrib(port->stats.txns, 0); - else { + } else if (n == -ENODATA) { + fwtty_profile_data(port->stats.txns, 0); + } else { ++port->stats.fifo_errs; - fwtty_err_ratelimited(port, "fifo err: %d", n); + fwtty_err_ratelimited(port, "fifo err: %d\n", + n); } break; } - profile_size_distrib(port->stats.txns, txn->dma_pended.len); + fwtty_profile_data(port->stats.txns, txn->dma_pended.len); fwtty_send_txn_async(peer, txn, TCODE_WRITE_BLOCK_REQUEST, peer->fifo_addr, txn->dma_pended.data, @@ -878,7 +817,7 @@ static void fwtty_write_xchar(struct fwtty_port *port, char ch) ++port->stats.xchars; - fwtty_dbg(port, "%02x", ch); + fwtty_dbg(port, "%02x\n", ch); rcu_read_lock(); peer = rcu_dereference(port->peer); @@ -941,7 +880,7 @@ static void fwserial_destroy(struct kref *kref) for (j = 0; j < num_ports; ++i, ++j) { port_table_corrupt |= port_table[i] != ports[j]; WARN_ONCE(port_table_corrupt, "port_table[%d]: %p != ports[%d]: %p", - i, port_table[i], j, ports[j]); + i, port_table[i], j, ports[j]); port_table[i] = NULL; } @@ -965,7 +904,7 @@ static void fwtty_port_dtr_rts(struct tty_port *tty_port, int on) { struct fwtty_port *port = to_port(tty_port, port); - fwtty_dbg(port, "on/off: %d", on); + fwtty_dbg(port, "on/off: %d\n", on); spin_lock_bh(&port->lock); /* Don't change carrier state if this is a console */ @@ -993,7 +932,7 @@ static int fwtty_port_carrier_raised(struct tty_port *tty_port) rc = (port->mstatus & TIOCM_CAR); - fwtty_dbg(port, "%d", rc); + fwtty_dbg(port, "%d\n", rc); return rc; } @@ -1096,20 +1035,13 @@ static int fwtty_port_activate(struct tty_port *tty_port, static void fwtty_port_shutdown(struct tty_port *tty_port) { struct fwtty_port *port = to_port(tty_port, port); - struct buffered_rx *buf, *next; /* TODO: cancel outstanding transactions */ cancel_delayed_work_sync(&port->emit_breaks); cancel_delayed_work_sync(&port->drain); - cancel_work_sync(&port->push); spin_lock_bh(&port->lock); - list_for_each_entry_safe(buf, next, &port->buf_list, list) { - list_del(&buf->list); - kfree(buf); - } - port->buffered = 0; port->flags = 0; port->break_ctl = 0; port->overrun = 0; @@ -1160,13 +1092,26 @@ static int fwtty_install(struct tty_driver *driver, struct tty_struct *tty) return err; } +static int fwloop_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct fwtty_port *port = fwtty_port_get(table_idx(tty->index)); + int err; + + err = tty_standard_install(driver, tty); + if (!err) + tty->driver_data = port; + else + fwtty_port_put(port); + return err; +} + static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c) { struct fwtty_port *port = tty->driver_data; int n, len; - fwtty_dbg(port, "%d", c); - profile_size_distrib(port->stats.writes, c); + fwtty_dbg(port, "%d\n", c); + fwtty_profile_data(port->stats.writes, c); spin_lock_bh(&port->lock); n = dma_fifo_in(&port->tx_fifo, buf, c); @@ -1192,7 +1137,7 @@ static int fwtty_write_room(struct tty_struct *tty) n = dma_fifo_avail(&port->tx_fifo); spin_unlock_bh(&port->lock); - fwtty_dbg(port, "%d", n); + fwtty_dbg(port, "%d\n", n); return n; } @@ -1206,7 +1151,7 @@ static int fwtty_chars_in_buffer(struct tty_struct *tty) n = dma_fifo_level(&port->tx_fifo); spin_unlock_bh(&port->lock); - fwtty_dbg(port, "%d", n); + fwtty_dbg(port, "%d\n", n); return n; } @@ -1215,7 +1160,7 @@ static void fwtty_send_xchar(struct tty_struct *tty, char ch) { struct fwtty_port *port = tty->driver_data; - fwtty_dbg(port, "%02x", ch); + fwtty_dbg(port, "%02x\n", ch); fwtty_write_xchar(port, ch); } @@ -1242,11 +1187,9 @@ static void fwtty_unthrottle(struct tty_struct *tty) { struct fwtty_port *port = tty->driver_data; - fwtty_dbg(port, "CRTSCTS: %d", (C_CRTSCTS(tty) != 0)); + fwtty_dbg(port, "CRTSCTS: %d\n", (C_CRTSCTS(tty) != 0)); - profile_fifo_avail(port, port->stats.unthrottle); - - schedule_work(&port->push); + fwtty_profile_fifo(port, port->stats.unthrottle); spin_lock_bh(&port->lock); port->mctrl &= ~OOB_RX_THROTTLE; @@ -1310,15 +1253,16 @@ static int set_serial_info(struct fwtty_port *port, return -EFAULT; if (tmp.irq != 0 || tmp.port != 0 || tmp.custom_divisor != 0 || - tmp.baud_base != 400000000) + tmp.baud_base != 400000000) return -EPERM; if (!capable(CAP_SYS_ADMIN)) { if (((tmp.flags & ~ASYNC_USR_MASK) != (port->port.flags & ~ASYNC_USR_MASK))) return -EPERM; - } else + } else { port->port.close_delay = tmp.close_delay * HZ / 100; + } return 0; } @@ -1361,9 +1305,9 @@ static void fwtty_set_termios(struct tty_struct *tty, struct ktermios *old) spin_lock_bh(&port->lock); baud = set_termios(port, tty); - if ((baud == 0) && (old->c_cflag & CBAUD)) + if ((baud == 0) && (old->c_cflag & CBAUD)) { port->mctrl &= ~(TIOCM_DTR | TIOCM_RTS); - else if ((baud != 0) && !(old->c_cflag & CBAUD)) { + } else if ((baud != 0) && !(old->c_cflag & CBAUD)) { if (C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags)) port->mctrl |= TIOCM_DTR | TIOCM_RTS; else @@ -1397,7 +1341,7 @@ static int fwtty_break_ctl(struct tty_struct *tty, int state) struct fwtty_port *port = tty->driver_data; long ret; - fwtty_dbg(port, "%d", state); + fwtty_dbg(port, "%d\n", state); if (state == -1) { set_bit(STOP_TX, &port->flags); @@ -1434,7 +1378,7 @@ static int fwtty_tiocmget(struct tty_struct *tty) tiocm = (port->mctrl & MCTRL_MASK) | (port->mstatus & ~MCTRL_MASK); spin_unlock_bh(&port->lock); - fwtty_dbg(port, "%x", tiocm); + fwtty_dbg(port, "%x\n", tiocm); return tiocm; } @@ -1443,7 +1387,7 @@ static int fwtty_tiocmset(struct tty_struct *tty, unsigned set, unsigned clear) { struct fwtty_port *port = tty->driver_data; - fwtty_dbg(port, "set: %x clear: %x", set, clear); + fwtty_dbg(port, "set: %x clear: %x\n", set, clear); /* TODO: simulate loopback if TIOCM_LOOP set */ @@ -1487,27 +1431,35 @@ static void fwtty_proc_show_port(struct seq_file *m, struct fwtty_port *port) if (port->port.console) (*port->fwcon_ops->stats)(&stats, port->con_data); - seq_printf(m, " tx:%d rx:%d", port->icount.tx + stats.xchars, - port->icount.rx); + seq_printf(m, " addr:%012llx tx:%d rx:%d", port->rx_handler.offset, + port->icount.tx + stats.xchars, port->icount.rx); seq_printf(m, " cts:%d dsr:%d rng:%d dcd:%d", port->icount.cts, port->icount.dsr, port->icount.rng, port->icount.dcd); seq_printf(m, " fe:%d oe:%d pe:%d brk:%d", port->icount.frame, port->icount.overrun, port->icount.parity, port->icount.brk); +} + +static void fwtty_debugfs_show_port(struct seq_file *m, struct fwtty_port *port) +{ + struct stats stats; + + memcpy(&stats, &port->stats, sizeof(stats)); + if (port->port.console) + (*port->fwcon_ops->stats)(&stats, port->con_data); + seq_printf(m, " dr:%d st:%d err:%d lost:%d", stats.dropped, stats.tx_stall, stats.fifo_errs, stats.lost); - seq_printf(m, " pkts:%d thr:%d wtrmk:%d", stats.sent, stats.throttled, - stats.watermark); - seq_printf(m, " addr:%012llx", port->rx_handler.offset); + seq_printf(m, " pkts:%d thr:%d", stats.sent, stats.throttled); if (port->port.console) { - seq_printf(m, "\n "); + seq_puts(m, "\n "); (*port->fwcon_ops->proc_show)(m, port->con_data); } - dump_profile(m, &port->stats); + fwtty_dump_profile(m, &port->stats); } -static void fwtty_proc_show_peer(struct seq_file *m, struct fwtty_peer *peer) +static void fwtty_debugfs_show_peer(struct seq_file *m, struct fwtty_peer *peer) { int generation = peer->generation; @@ -1516,21 +1468,14 @@ static void fwtty_proc_show_peer(struct seq_file *m, struct fwtty_peer *peer) seq_printf(m, " node:%04x gen:%d", peer->node_id, generation); seq_printf(m, " sp:%d max:%d guid:%016llx", peer->speed, peer->max_payload, (unsigned long long) peer->guid); - - if (capable(CAP_SYS_ADMIN)) { - seq_printf(m, " mgmt:%012llx", - (unsigned long long) peer->mgmt_addr); - seq_printf(m, " addr:%012llx", - (unsigned long long) peer->status_addr); - } + seq_printf(m, " mgmt:%012llx", (unsigned long long) peer->mgmt_addr); + seq_printf(m, " addr:%012llx", (unsigned long long) peer->status_addr); seq_putc(m, '\n'); } static int fwtty_proc_show(struct seq_file *m, void *v) { struct fwtty_port *port; - struct fw_serial *serial; - struct fwtty_peer *peer; int i; seq_puts(m, "fwserinfo: 1.0 driver: 1.0\n"); @@ -1539,18 +1484,41 @@ static int fwtty_proc_show(struct seq_file *m, void *v) if (capable(CAP_SYS_ADMIN)) fwtty_proc_show_port(m, port); fwtty_port_put(port); - seq_printf(m, "\n"); + seq_puts(m, "\n"); } - seq_putc(m, '\n'); + return 0; +} - rcu_read_lock(); - list_for_each_entry_rcu(serial, &fwserial_list, list) { - seq_printf(m, "card: %s guid: %016llx\n", - dev_name(serial->card->device), - (unsigned long long) serial->card->guid); - list_for_each_entry_rcu(peer, &serial->peer_list, list) - fwtty_proc_show_peer(m, peer); +static int fwtty_debugfs_stats_show(struct seq_file *m, void *v) +{ + struct fw_serial *serial = m->private; + struct fwtty_port *port; + int i; + + for (i = 0; i < num_ports; ++i) { + port = fwtty_port_get(serial->ports[i]->index); + if (port) { + seq_printf(m, "%2d:", port->index); + fwtty_proc_show_port(m, port); + fwtty_debugfs_show_port(m, port); + fwtty_port_put(port); + seq_puts(m, "\n"); + } } + return 0; +} + +static int fwtty_debugfs_peers_show(struct seq_file *m, void *v) +{ + struct fw_serial *serial = m->private; + struct fwtty_peer *peer; + + rcu_read_lock(); + seq_printf(m, "card: %s guid: %016llx\n", + dev_name(serial->card->device), + (unsigned long long) serial->card->guid); + list_for_each_entry_rcu(peer, &serial->peer_list, list) + fwtty_debugfs_show_peer(m, peer); rcu_read_unlock(); return 0; } @@ -1560,6 +1528,32 @@ static int fwtty_proc_open(struct inode *inode, struct file *fp) return single_open(fp, fwtty_proc_show, NULL); } +static int fwtty_stats_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, fwtty_debugfs_stats_show, inode->i_private); +} + +static int fwtty_peers_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, fwtty_debugfs_peers_show, inode->i_private); +} + +static const struct file_operations fwtty_stats_fops = { + .owner = THIS_MODULE, + .open = fwtty_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations fwtty_peers_fops = { + .owner = THIS_MODULE, + .open = fwtty_peers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static const struct file_operations fwtty_proc_fops = { .owner = THIS_MODULE, .open = fwtty_proc_open, @@ -1596,6 +1590,26 @@ static const struct tty_operations fwtty_ops = { .proc_fops = &fwtty_proc_fops, }; +static const struct tty_operations fwloop_ops = { + .open = fwtty_open, + .close = fwtty_close, + .hangup = fwtty_hangup, + .cleanup = fwtty_cleanup, + .install = fwloop_install, + .write = fwtty_write, + .write_room = fwtty_write_room, + .chars_in_buffer = fwtty_chars_in_buffer, + .send_xchar = fwtty_send_xchar, + .throttle = fwtty_throttle, + .unthrottle = fwtty_unthrottle, + .ioctl = fwtty_ioctl, + .set_termios = fwtty_set_termios, + .break_ctl = fwtty_break_ctl, + .tiocmget = fwtty_tiocmget, + .tiocmset = fwtty_tiocmset, + .get_icount = fwtty_get_icount, +}; + static inline int mgmt_pkt_expected_len(__be16 code) { static const struct fwserial_mgmt_pkt pkt; @@ -1685,15 +1699,14 @@ static void fwserial_virt_plug_complete(struct fwtty_peer *peer, /* reconfigure tx_fifo optimally for this peer */ spin_lock_bh(&port->lock); - port->max_payload = min3(peer->max_payload, peer->fifo_len, - MAX_ASYNC_PAYLOAD); + port->max_payload = min(peer->max_payload, peer->fifo_len); dma_fifo_change_tx_limit(&port->tx_fifo, port->max_payload); spin_unlock_bh(&peer->port->lock); if (port->port.console && port->fwcon_ops->notify != NULL) (*port->fwcon_ops->notify)(FWCON_NOTIFY_ATTACH, port->con_data); - fwtty_info(&peer->unit, "peer (guid:%016llx) connected on %s", + fwtty_info(&peer->unit, "peer (guid:%016llx) connected on %s\n", (unsigned long long)peer->guid, dev_name(port->device)); } @@ -1715,10 +1728,11 @@ static inline int fwserial_send_mgmt_sync(struct fwtty_peer *peer, pkt, be16_to_cpu(pkt->hdr.len)); if (rcode == RCODE_BUSY || rcode == RCODE_SEND_ERROR || rcode == RCODE_GENERATION) { - fwtty_dbg(&peer->unit, "mgmt write error: %d", rcode); + fwtty_dbg(&peer->unit, "mgmt write error: %d\n", rcode); continue; - } else + } else { break; + } } while (--tries > 0); return rcode; } @@ -1781,10 +1795,11 @@ static struct fwtty_port *fwserial_find_port(struct fwtty_peer *peer) return NULL; } -static void fwserial_release_port(struct fwtty_port *port) +static void fwserial_release_port(struct fwtty_port *port, bool reset) { /* drop carrier (and all other line status) */ - fwtty_update_port_status(port, 0); + if (reset) + fwtty_update_port_status(port, 0); spin_lock_bh(&port->lock); @@ -1792,7 +1807,7 @@ static void fwserial_release_port(struct fwtty_port *port) port->max_payload = link_speed_to_max_payload(SCODE_100); dma_fifo_change_tx_limit(&port->tx_fifo, port->max_payload); - rcu_assign_pointer(port->peer, NULL); + RCU_INIT_POINTER(port->peer, NULL); spin_unlock_bh(&port->lock); if (port->port.console && port->fwcon_ops->notify != NULL) @@ -1801,7 +1816,7 @@ static void fwserial_release_port(struct fwtty_port *port) static void fwserial_plug_timeout(unsigned long data) { - struct fwtty_peer *peer = (struct fwtty_peer *) data; + struct fwtty_peer *peer = (struct fwtty_peer *)data; struct fwtty_port *port; spin_lock_bh(&peer->lock); @@ -1814,7 +1829,7 @@ static void fwserial_plug_timeout(unsigned long data) spin_unlock_bh(&peer->lock); if (port) - fwserial_release_port(port); + fwserial_release_port(port, false); } /** @@ -1835,7 +1850,7 @@ static int fwserial_connect_peer(struct fwtty_peer *peer) port = fwserial_find_port(peer); if (!port) { - fwtty_err(&peer->unit, "avail ports in use"); + fwtty_err(&peer->unit, "avail ports in use\n"); err = -EBUSY; goto free_pkt; } @@ -1877,7 +1892,7 @@ cancel_timer: peer_revert_state(peer); release_port: spin_unlock_bh(&peer->lock); - fwserial_release_port(port); + fwserial_release_port(port, false); free_pkt: kfree(pkt); return err; @@ -1892,7 +1907,8 @@ free_pkt: * The port reference is put by fwtty_cleanup (if a reference was * ever taken). */ -static void fwserial_close_port(struct fwtty_port *port) +static void fwserial_close_port(struct tty_driver *driver, + struct fwtty_port *port) { struct tty_struct *tty; @@ -1904,7 +1920,10 @@ static void fwserial_close_port(struct fwtty_port *port) } mutex_unlock(&port->port.mutex); - tty_unregister_device(fwtty_driver, port->index); + if (driver == fwloop_driver) + tty_unregister_device(driver, loop_idx(port)); + else + tty_unregister_device(driver, port->index); } /** @@ -1969,7 +1988,7 @@ static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card, * has created its remote unit device before this driver has * been probed for any unit devices... */ - fwtty_err(card, "unknown card (guid %016llx)", + fwtty_err(card, "unknown card (guid %016llx)\n", (unsigned long long) card->guid); return NULL; } @@ -1997,8 +2016,8 @@ static void __dump_peer_list(struct fw_card *card) list_for_each_entry_rcu(peer, &serial->peer_list, list) { int g = peer->generation; smp_rmb(); - fwtty_dbg(card, "peer(%d:%x) guid: %016llx\n", g, - peer->node_id, (unsigned long long) peer->guid); + fwtty_dbg(card, "peer(%d:%x) guid: %016llx\n", + g, peer->node_id, (unsigned long long) peer->guid); } } #else @@ -2015,6 +2034,13 @@ static void fwserial_auto_connect(struct work_struct *work) schedule_delayed_work(&peer->connect, CONNECT_RETRY_DELAY); } +static void fwserial_peer_workfn(struct work_struct *work) +{ + struct fwtty_peer *peer = to_peer(work, work); + + peer->workfn(work); +} + /** * fwserial_add_peer - add a newly probed 'serial' unit device as a 'peer' * @serial: aggregate representing the specific fw_card to add the peer to @@ -2079,14 +2105,14 @@ static int fwserial_add_peer(struct fw_serial *serial, struct fw_unit *unit) peer->port = NULL; init_timer(&peer->timer); - INIT_WORK(&peer->work, NULL); + INIT_WORK(&peer->work, fwserial_peer_workfn); INIT_DELAYED_WORK(&peer->connect, fwserial_auto_connect); /* associate peer with specific fw_card */ peer->serial = serial; list_add_rcu(&peer->list, &serial->peer_list); - fwtty_info(&peer->unit, "peer added (guid:%016llx)", + fwtty_info(&peer->unit, "peer added (guid:%016llx)\n", (unsigned long long)peer->guid); /* identify the local unit & virt cable to loopback port */ @@ -2149,91 +2175,19 @@ static void fwserial_remove_peer(struct fwtty_peer *peer) list_del_rcu(&peer->list); - fwtty_info(&peer->unit, "peer removed (guid:%016llx)", + fwtty_info(&peer->unit, "peer removed (guid:%016llx)\n", (unsigned long long)peer->guid); spin_unlock_bh(&peer->lock); if (port) - fwserial_release_port(port); + fwserial_release_port(port, true); synchronize_rcu(); kfree(peer); } /** - * create_loop_device - create a loopback tty device - * @tty_driver: tty_driver to own loopback device - * @prototype: ptr to already-assigned 'prototype' tty port - * @index: index to associate this device with the tty port - * @parent: device to child to - * - * HACK - this is basically tty_port_register_device() with an - * alternate naming scheme. Suggest tty_port_register_named_device() - * helper api. - * - * Creates a loopback tty device named 'fwloop<n>' which is attached to - * the local unit in fwserial_add_peer(). Note that <n> in the device - * name advances in increments of port allocation blocks, ie., for port - * indices 0..3, the device name will be 'fwloop0'; for 4..7, 'fwloop1', - * and so on. - * - * Only one loopback device should be created per fw_card. - */ -static void release_loop_device(struct device *dev) -{ - kfree(dev); -} - -static struct device *create_loop_device(struct tty_driver *driver, - struct fwtty_port *prototype, - struct fwtty_port *port, - struct device *parent) -{ - char name[64]; - int index = port->index; - dev_t devt = MKDEV(driver->major, driver->minor_start) + index; - struct device *dev = NULL; - int err; - - if (index >= fwtty_driver->num) - return ERR_PTR(-EINVAL); - - snprintf(name, 64, "%s%d", loop_dev_name, index / num_ports); - - tty_port_link_device(&port->port, driver, index); - - cdev_init(&driver->cdevs[index], driver->cdevs[prototype->index].ops); - driver->cdevs[index].owner = driver->owner; - err = cdev_add(&driver->cdevs[index], devt, 1); - if (err) - return ERR_PTR(err); - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - cdev_del(&driver->cdevs[index]); - return ERR_PTR(-ENOMEM); - } - - dev->devt = devt; - dev->class = prototype->device->class; - dev->parent = parent; - dev->release = release_loop_device; - dev_set_name(dev, "%s", name); - dev->groups = NULL; - dev_set_drvdata(dev, NULL); - - err = device_register(dev); - if (err) { - put_device(dev); - cdev_del(&driver->cdevs[index]); - return ERR_PTR(err); - } - - return dev; -} - -/** * fwserial_create - init everything to create TTYs for a specific fw_card * @unit: fw_unit for first 'serial' unit device probed for this fw_card * @@ -2276,18 +2230,17 @@ static int fwserial_create(struct fw_unit *unit) port->index = FWTTY_INVALID_INDEX; port->port.ops = &fwtty_port_ops; port->serial = serial; + tty_buffer_set_limit(&port->port, 128 * 1024); spin_lock_init(&port->lock); INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx); INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks); INIT_WORK(&port->hangup, fwtty_do_hangup); - INIT_WORK(&port->push, fwtty_pushrx); - INIT_LIST_HEAD(&port->buf_list); init_waitqueue_head(&port->wait_tx); port->max_payload = link_speed_to_max_payload(SCODE_100); dma_fifo_init(&port->tx_fifo); - rcu_assign_pointer(port->peer, NULL); + RCU_INIT_POINTER(port->peer, NULL); serial->ports[i] = port; /* get unique bus addr region for port's status & recv fifo */ @@ -2309,7 +2262,7 @@ static int fwserial_create(struct fw_unit *unit) err = fwtty_ports_add(serial); if (err) { - fwtty_err(&unit, "no space in port table"); + fwtty_err(&unit, "no space in port table\n"); goto free_ports; } @@ -2320,7 +2273,8 @@ static int fwserial_create(struct fw_unit *unit) card->device); if (IS_ERR(tty_dev)) { err = PTR_ERR(tty_dev); - fwtty_err(&unit, "register tty device error (%d)", err); + fwtty_err(&unit, "register tty device error (%d)\n", + err); goto unregister_ttys; } @@ -2331,32 +2285,49 @@ static int fwserial_create(struct fw_unit *unit) if (create_loop_dev) { struct device *loop_dev; - loop_dev = create_loop_device(fwtty_driver, - serial->ports[0], - serial->ports[num_ttys], - card->device); + loop_dev = tty_port_register_device(&serial->ports[j]->port, + fwloop_driver, + loop_idx(serial->ports[j]), + card->device); if (IS_ERR(loop_dev)) { err = PTR_ERR(loop_dev); - fwtty_err(&unit, "create loop device failed (%d)", err); + fwtty_err(&unit, "create loop device failed (%d)\n", + err); goto unregister_ttys; } - serial->ports[num_ttys]->device = loop_dev; - serial->ports[num_ttys]->loopback = true; + serial->ports[j]->device = loop_dev; + serial->ports[j]->loopback = true; + } + + if (!IS_ERR_OR_NULL(fwserial_debugfs)) { + serial->debugfs = debugfs_create_dir(dev_name(&unit->device), + fwserial_debugfs); + if (!IS_ERR_OR_NULL(serial->debugfs)) { + debugfs_create_file("peers", 0444, serial->debugfs, + serial, &fwtty_peers_fops); + debugfs_create_file("stats", 0444, serial->debugfs, + serial, &fwtty_stats_fops); + } } list_add_rcu(&serial->list, &fwserial_list); - fwtty_notice(&unit, "TTY over FireWire on device %s (guid %016llx)", + fwtty_notice(&unit, "TTY over FireWire on device %s (guid %016llx)\n", dev_name(card->device), (unsigned long long) card->guid); err = fwserial_add_peer(serial, unit); if (!err) return 0; - fwtty_err(&unit, "unable to add peer unit device (%d)", err); + fwtty_err(&unit, "unable to add peer unit device (%d)\n", err); /* fall-through to error processing */ + debugfs_remove_recursive(serial->debugfs); + list_del_rcu(&serial->list); + if (create_loop_dev) + tty_unregister_device(fwloop_driver, + loop_idx(serial->ports[j])); unregister_ttys: for (--j; j >= 0; --j) tty_unregister_device(fwtty_driver, serial->ports[j]->index); @@ -2408,9 +2379,9 @@ free_ports: * last peer for a given fw_card triggering the destruction of the same * fw_serial for the same fw_card. */ -static int fwserial_probe(struct device *dev) +static int fwserial_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) { - struct fw_unit *unit = fw_unit(dev); struct fw_serial *serial; int err; @@ -2432,9 +2403,9 @@ static int fwserial_probe(struct device *dev) * specific fw_card). If this is the last peer being removed, then trigger * the destruction of the underlying TTYs. */ -static int fwserial_remove(struct device *dev) +static void fwserial_remove(struct fw_unit *unit) { - struct fwtty_peer *peer = dev_get_drvdata(dev); + struct fwtty_peer *peer = dev_get_drvdata(&unit->device); struct fw_serial *serial = peer->serial; int i; @@ -2445,13 +2416,15 @@ static int fwserial_remove(struct device *dev) /* unlink from the fwserial_list here */ list_del_rcu(&serial->list); - for (i = 0; i < num_ports; ++i) - fwserial_close_port(serial->ports[i]); + debugfs_remove_recursive(serial->debugfs); + + for (i = 0; i < num_ttys; ++i) + fwserial_close_port(fwtty_driver, serial->ports[i]); + if (create_loop_dev) + fwserial_close_port(fwloop_driver, serial->ports[i]); kref_put(&serial->kref, fwserial_destroy); } mutex_unlock(&fwserial_list_mutex); - - return 0; } /** @@ -2496,10 +2469,10 @@ static struct fw_driver fwserial_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = fwserial_probe, - .remove = fwserial_remove, }, + .probe = fwserial_probe, .update = fwserial_update, + .remove = fwserial_remove, .id_table = fwserial_id_table, }; @@ -2510,26 +2483,25 @@ static struct fw_driver fwserial_driver = { /* XXX: config ROM definitons could be improved with semi-automated offset * and length calculation */ +#define FW_ROM_LEN(quads) ((quads) << 16) #define FW_ROM_DESCRIPTOR(ofs) (((CSR_LEAF | CSR_DESCRIPTOR) << 24) | (ofs)) struct fwserial_unit_directory_data { - u16 crc; - u16 len; + u32 len_crc; u32 unit_specifier; u32 unit_sw_version; u32 unit_addr_offset; u32 desc1_ofs; - u16 desc1_crc; - u16 desc1_len; + u32 desc1_len_crc; u32 desc1_data[5]; } __packed; static struct fwserial_unit_directory_data fwserial_unit_directory_data = { - .len = 4, + .len_crc = FW_ROM_LEN(4), .unit_specifier = FW_UNIT_SPECIFIER(LINUX_VENDOR_ID), .unit_sw_version = FW_UNIT_VERSION(FWSERIAL_VERSION), .desc1_ofs = FW_ROM_DESCRIPTOR(1), - .desc1_len = 5, + .desc1_len_crc = FW_ROM_LEN(5), .desc1_data = { 0x00000000, /* type = text */ 0x00000000, /* enc = ASCII, lang EN */ @@ -2549,7 +2521,7 @@ static struct fw_descriptor fwserial_unit_directory = { * The management address is in the unit space region but above other known * address users (to keep wild writes from causing havoc) */ -const struct fw_address_region fwserial_mgmt_addr_region = { +static const struct fw_address_region fwserial_mgmt_addr_region = { .start = CSR_REGISTER_BASE + 0x1e0000ULL, .end = 0x1000000000000ULL, }; @@ -2588,7 +2560,7 @@ static void fwserial_handle_plug_req(struct work_struct *work) switch (peer->state) { case FWPS_NOT_ATTACHED: if (!port) { - fwtty_err(&peer->unit, "no more ports avail"); + fwtty_err(&peer->unit, "no more ports avail\n"); fill_plug_rsp_nack(pkt); } else { peer->port = port; @@ -2615,7 +2587,7 @@ static void fwserial_handle_plug_req(struct work_struct *work) spin_unlock_bh(&peer->lock); if (port) - fwserial_release_port(port); + fwserial_release_port(port, false); rcode = fwserial_send_mgmt_sync(peer, pkt); @@ -2630,14 +2602,14 @@ static void fwserial_handle_plug_req(struct work_struct *work) fwtty_write_port_status(tmp); spin_lock_bh(&peer->lock); } else { - fwtty_err(&peer->unit, "PLUG_RSP error (%d)", rcode); + fwtty_err(&peer->unit, "PLUG_RSP error (%d)\n", rcode); port = peer_revert_state(peer); } } cleanup: spin_unlock_bh(&peer->lock); if (port) - fwserial_release_port(port); + fwserial_release_port(port, false); kfree(pkt); return; } @@ -2681,15 +2653,15 @@ static void fwserial_handle_unplug_req(struct work_struct *work) spin_lock_bh(&peer->lock); if (peer->state == FWPS_UNPLUG_RESPONDING) { - if (rcode == RCODE_COMPLETE) - port = peer_revert_state(peer); - else - fwtty_err(&peer->unit, "UNPLUG_RSP error (%d)", rcode); + if (rcode != RCODE_COMPLETE) + fwtty_err(&peer->unit, "UNPLUG_RSP error (%d)\n", + rcode); + port = peer_revert_state(peer); } cleanup: spin_unlock_bh(&peer->lock); if (port) - fwserial_release_port(port); + fwserial_release_port(port, true); kfree(pkt); return; } @@ -2700,6 +2672,7 @@ static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, size_t len) { struct fwtty_port *port = NULL; + bool reset = false; int rcode; if (addr != fwserial_mgmt_addr_handler.offset || len < sizeof(pkt->hdr)) @@ -2717,24 +2690,24 @@ static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, * already removed from the bus -- and the removal was * processed before we rec'd this transaction */ - fwtty_err(&peer->unit, "peer already removed"); + fwtty_err(&peer->unit, "peer already removed\n"); spin_unlock_bh(&peer->lock); return RCODE_ADDRESS_ERROR; } rcode = RCODE_COMPLETE; - fwtty_dbg(&peer->unit, "mgmt: hdr.code: %04hx", pkt->hdr.code); + fwtty_dbg(&peer->unit, "mgmt: hdr.code: %04hx\n", pkt->hdr.code); switch (be16_to_cpu(pkt->hdr.code) & FWSC_CODE_MASK) { case FWSC_VIRT_CABLE_PLUG: if (work_pending(&peer->work)) { - fwtty_err(&peer->unit, "plug req: busy"); + fwtty_err(&peer->unit, "plug req: busy\n"); rcode = RCODE_CONFLICT_ERROR; } else { peer->work_params.plug_req = pkt->plug_req; - PREPARE_WORK(&peer->work, fwserial_handle_plug_req); + peer->workfn = fwserial_handle_plug_req; queue_work(system_unbound_wq, &peer->work); } break; @@ -2744,7 +2717,7 @@ static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, rcode = RCODE_CONFLICT_ERROR; } else if (be16_to_cpu(pkt->hdr.code) & FWSC_RSP_NACK) { - fwtty_notice(&peer->unit, "NACK plug rsp"); + fwtty_notice(&peer->unit, "NACK plug rsp\n"); port = peer_revert_state(peer); } else { @@ -2760,33 +2733,34 @@ static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, case FWSC_VIRT_CABLE_UNPLUG: if (work_pending(&peer->work)) { - fwtty_err(&peer->unit, "unplug req: busy"); + fwtty_err(&peer->unit, "unplug req: busy\n"); rcode = RCODE_CONFLICT_ERROR; } else { - PREPARE_WORK(&peer->work, fwserial_handle_unplug_req); + peer->workfn = fwserial_handle_unplug_req; queue_work(system_unbound_wq, &peer->work); } break; case FWSC_VIRT_CABLE_UNPLUG_RSP: - if (peer->state != FWPS_UNPLUG_PENDING) + if (peer->state != FWPS_UNPLUG_PENDING) { rcode = RCODE_CONFLICT_ERROR; - else { + } else { if (be16_to_cpu(pkt->hdr.code) & FWSC_RSP_NACK) - fwtty_notice(&peer->unit, "NACK unplug?"); + fwtty_notice(&peer->unit, "NACK unplug?\n"); port = peer_revert_state(peer); + reset = true; } break; default: - fwtty_err(&peer->unit, "unknown mgmt code %d", + fwtty_err(&peer->unit, "unknown mgmt code %d\n", be16_to_cpu(pkt->hdr.code)); rcode = RCODE_DATA_ERROR; } spin_unlock_bh(&peer->lock); if (port) - fwserial_release_port(port); + fwserial_release_port(port, reset); return rcode; } @@ -2813,7 +2787,7 @@ static void fwserial_mgmt_handler(struct fw_card *card, rcu_read_lock(); peer = __fwserial_peer_by_node_id(card, generation, source); if (!peer) { - fwtty_dbg(card, "peer(%d:%x) not found", generation, source); + fwtty_dbg(card, "peer(%d:%x) not found\n", generation, source); __dump_peer_list(card); rcode = RCODE_CONFLICT_ERROR; @@ -2836,14 +2810,18 @@ static int __init fwserial_init(void) { int err, num_loops = !!(create_loop_dev); + /* XXX: placeholder for a "firewire" debugfs node */ + fwserial_debugfs = debugfs_create_dir(KBUILD_MODNAME, NULL); + /* num_ttys/num_ports must not be set above the static alloc avail */ if (num_ttys + num_loops > MAX_CARD_PORTS) num_ttys = MAX_CARD_PORTS - num_loops; num_ports = num_ttys + num_loops; - fwtty_driver = alloc_tty_driver(MAX_TOTAL_PORTS); - if (!fwtty_driver) { - err = -ENOMEM; + fwtty_driver = tty_alloc_driver(MAX_TOTAL_PORTS, TTY_DRIVER_REAL_RAW + | TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(fwtty_driver)) { + err = PTR_ERR(fwtty_driver); return err; } @@ -2853,25 +2831,48 @@ static int __init fwserial_init(void) fwtty_driver->minor_start = 0; fwtty_driver->type = TTY_DRIVER_TYPE_SERIAL; fwtty_driver->subtype = SERIAL_TYPE_NORMAL; - fwtty_driver->flags = TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV; - fwtty_driver->init_termios = tty_std_termios; fwtty_driver->init_termios.c_cflag |= CLOCAL; tty_set_operations(fwtty_driver, &fwtty_ops); err = tty_register_driver(fwtty_driver); if (err) { - driver_err("register tty driver failed (%d)", err); + pr_err("register tty driver failed (%d)\n", err); goto put_tty; } + if (create_loop_dev) { + fwloop_driver = tty_alloc_driver(MAX_TOTAL_PORTS / num_ports, + TTY_DRIVER_REAL_RAW + | TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(fwloop_driver)) { + err = PTR_ERR(fwloop_driver); + goto unregister_driver; + } + + fwloop_driver->driver_name = KBUILD_MODNAME "_loop"; + fwloop_driver->name = loop_dev_name; + fwloop_driver->major = 0; + fwloop_driver->minor_start = 0; + fwloop_driver->type = TTY_DRIVER_TYPE_SERIAL; + fwloop_driver->subtype = SERIAL_TYPE_NORMAL; + fwloop_driver->init_termios = tty_std_termios; + fwloop_driver->init_termios.c_cflag |= CLOCAL; + tty_set_operations(fwloop_driver, &fwloop_ops); + + err = tty_register_driver(fwloop_driver); + if (err) { + pr_err("register loop driver failed (%d)\n", err); + goto put_loop; + } + } + fwtty_txn_cache = kmem_cache_create("fwtty_txn_cache", sizeof(struct fwtty_transaction), 0, 0, fwtty_txn_constructor); if (!fwtty_txn_cache) { err = -ENOMEM; - goto unregister_driver; + goto unregister_loop; } /* @@ -2887,7 +2888,7 @@ static int __init fwserial_init(void) err = fw_core_add_address_handler(&fwserial_mgmt_addr_handler, &fwserial_mgmt_addr_region); if (err) { - driver_err("add management handler failed (%d)", err); + pr_err("add management handler failed (%d)\n", err); goto destroy_cache; } @@ -2895,13 +2896,13 @@ static int __init fwserial_init(void) FW_UNIT_ADDRESS(fwserial_mgmt_addr_handler.offset); err = fw_core_add_descriptor(&fwserial_unit_directory); if (err) { - driver_err("add unit descriptor failed (%d)", err); + pr_err("add unit descriptor failed (%d)\n", err); goto remove_handler; } err = driver_register(&fwserial_driver.driver); if (err) { - driver_err("register fwserial driver failed (%d)", err); + pr_err("register fwserial driver failed (%d)\n", err); goto remove_descriptor; } @@ -2913,10 +2914,17 @@ remove_handler: fw_core_remove_address_handler(&fwserial_mgmt_addr_handler); destroy_cache: kmem_cache_destroy(fwtty_txn_cache); +unregister_loop: + if (create_loop_dev) + tty_unregister_driver(fwloop_driver); +put_loop: + if (create_loop_dev) + put_tty_driver(fwloop_driver); unregister_driver: tty_unregister_driver(fwtty_driver); put_tty: put_tty_driver(fwtty_driver); + debugfs_remove_recursive(fwserial_debugfs); return err; } @@ -2926,8 +2934,13 @@ static void __exit fwserial_exit(void) fw_core_remove_descriptor(&fwserial_unit_directory); fw_core_remove_address_handler(&fwserial_mgmt_addr_handler); kmem_cache_destroy(fwtty_txn_cache); + if (create_loop_dev) { + tty_unregister_driver(fwloop_driver); + put_tty_driver(fwloop_driver); + } tty_unregister_driver(fwtty_driver); put_tty_driver(fwtty_driver); + debugfs_remove_recursive(fwserial_debugfs); } module_init(fwserial_init); @@ -2940,4 +2953,3 @@ MODULE_DEVICE_TABLE(ieee1394, fwserial_id_table); MODULE_PARM_DESC(ttys, "Number of ttys to create for each local firewire node"); MODULE_PARM_DESC(auto, "Auto-connect a tty to each firewire node discovered"); MODULE_PARM_DESC(loop, "Create a loopback device, fwloop<n>, with ttys"); -MODULE_PARM_DESC(limit_bw, "Limit bandwidth utilization to 20%."); |
