aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/fwserial/fwserial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/fwserial/fwserial.c')
-rw-r--r--drivers/staging/fwserial/fwserial.c734
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%.");