diff options
Diffstat (limited to 'drivers/staging/fwserial/fwserial.c')
| -rw-r--r-- | drivers/staging/fwserial/fwserial.c | 2891 |
1 files changed, 0 insertions, 2891 deletions
diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c deleted file mode 100644 index 81b06d88ed0d..000000000000 --- a/drivers/staging/fwserial/fwserial.c +++ /dev/null @@ -1,2891 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * FireWire Serial driver - * - * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/mod_devicetable.h> -#include <linux/rculist.h> -#include <linux/workqueue.h> -#include <linux/ratelimit.h> -#include <linux/bug.h> -#include <linux/uaccess.h> - -#include "fwserial.h" - -inline u64 be32_to_u64(__be32 hi, __be32 lo) -{ - return ((u64)be32_to_cpu(hi) << 32 | be32_to_cpu(lo)); -} - -#define LINUX_VENDOR_ID 0xd00d1eU /* same id used in card root directory */ -#define FWSERIAL_VERSION 0x00e81cU /* must be unique within LINUX_VENDOR_ID */ - -/* configurable options */ -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 */ - -module_param_named(ttys, num_ttys, int, 0644); -module_param_named(auto, auto_connect, bool, 0644); -module_param_named(loop, create_loop_dev, bool, 0644); - -/* - * Threshold below which the tty is woken for writing - * - should be equal to WAKEUP_CHARS in drivers/tty/n_tty.c because - * even if the writer is woken, n_tty_poll() won't set EPOLLOUT until - * our fifo is below this level - */ -#define WAKEUP_CHARS 256 - -/* - * fwserial_list: list of every fw_serial created for each fw_card - * See discussion in fwserial_probe. - */ -static LIST_HEAD(fwserial_list); -static DEFINE_MUTEX(fwserial_list_mutex); - -/* - * port_table: array of tty ports allocated to each fw_card - * - * tty ports are allocated during probe when an fw_serial is first - * created for a given fw_card. Ports are allocated in a contiguous block, - * each block consisting of 'num_ports' ports. - */ -static struct fwtty_port *port_table[MAX_TOTAL_PORTS]; -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, - struct fwtty_transaction *txn); - -struct fwtty_transaction { - struct fw_transaction fw_txn; - fwtty_transaction_cb callback; - struct fwtty_port *port; - union { - struct dma_pending dma_pended; - }; -}; - -#define to_device(a, b) (a->b) -#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) -{ - int avail; - - if (n < c) { - 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\n", - avail, c, n); - } -} -#else -#define debug_short_write(port, c, n) -#endif - -static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card, - int generation, int id); - -#ifdef FWTTY_PROFILING - -static void fwtty_profile_fifo(struct fwtty_port *port, unsigned int *stat) -{ - spin_lock_bh(&port->lock); - fwtty_profile_data(stat, dma_fifo_avail(&port->tx_fifo)); - spin_unlock_bh(&port->lock); -} - -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; - unsigned int sum; - int j; - char t[10]; - - snprintf(t, 10, "< %d", 1 << k); - seq_printf(m, "\n%14s %6s", " ", t); - for (j = k + 1; j < DISTRIBUTION_MAX_INDEX; ++j) - seq_printf(m, "%6d", 1 << j); - - ++k; - for (j = 0, sum = 0; j <= k; ++j) - sum += stats->reads[j]; - seq_printf(m, "\n%14s: %6d", "reads", sum); - for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) - seq_printf(m, "%6d", stats->reads[j]); - - for (j = 0, sum = 0; j <= k; ++j) - sum += stats->writes[j]; - seq_printf(m, "\n%14s: %6d", "writes", sum); - for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) - seq_printf(m, "%6d", stats->writes[j]); - - for (j = 0, sum = 0; j <= k; ++j) - sum += stats->txns[j]; - seq_printf(m, "\n%14s: %6d", "txns", sum); - for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) - seq_printf(m, "%6d", stats->txns[j]); - - for (j = 0, sum = 0; j <= k; ++j) - sum += stats->unthrottle[j]; - seq_printf(m, "\n%14s: %6d", "avail @ unthr", sum); - for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j) - seq_printf(m, "%6d", stats->unthrottle[j]); -} - -#else -#define fwtty_profile_fifo(port, stat) -#define fwtty_dump_profile(m, stats) -#endif - -/* - * 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) -{ - /* 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\n"); - break; - case RCODE_ADDRESS_ERROR: - fwtty_err_ratelimited(port, "bad unit addr or write length\n"); - break; - case RCODE_DATA_ERROR: - fwtty_err_ratelimited(port, "failed rx\n"); - break; - case RCODE_NO_ACK: - fwtty_err_ratelimited(port, "missing ack\n"); - break; - case RCODE_BUSY: - fwtty_err_ratelimited(port, "remote busy\n"); - break; - default: - fwtty_err_ratelimited(port, "failed tx: %d\n", rcode); - } -} - -static void fwtty_common_callback(struct fw_card *card, int rcode, - void *payload, size_t len, void *cb_data) -{ - struct fwtty_transaction *txn = cb_data; - struct fwtty_port *port = txn->port; - - if (port && rcode != RCODE_COMPLETE) - fwtty_log_tx_error(port, rcode); - if (txn->callback) - txn->callback(card, rcode, payload, len, txn); - kmem_cache_free(fwtty_txn_cache, txn); -} - -static int fwtty_send_data_async(struct fwtty_peer *peer, int tcode, - unsigned long long addr, void *payload, - size_t len, fwtty_transaction_cb callback, - struct fwtty_port *port) -{ - struct fwtty_transaction *txn; - int generation; - - txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC); - if (!txn) - return -ENOMEM; - - txn->callback = callback; - txn->port = port; - - generation = peer->generation; - smp_rmb(); - fw_send_request(peer->serial->card, &txn->fw_txn, tcode, - peer->node_id, generation, peer->speed, addr, payload, - len, fwtty_common_callback, txn); - return 0; -} - -static void fwtty_send_txn_async(struct fwtty_peer *peer, - struct fwtty_transaction *txn, int tcode, - unsigned long long addr, void *payload, - size_t len, fwtty_transaction_cb callback, - struct fwtty_port *port) -{ - int generation; - - txn->callback = callback; - txn->port = port; - - generation = peer->generation; - smp_rmb(); - fw_send_request(peer->serial->card, &txn->fw_txn, tcode, - peer->node_id, generation, peer->speed, addr, payload, - len, fwtty_common_callback, txn); -} - -static void __fwtty_restart_tx(struct fwtty_port *port) -{ - int len, avail; - - len = dma_fifo_out_level(&port->tx_fifo); - if (len) - schedule_delayed_work(&port->drain, 0); - avail = dma_fifo_avail(&port->tx_fifo); - - fwtty_dbg(port, "fifo len: %d avail: %d\n", len, avail); -} - -static void fwtty_restart_tx(struct fwtty_port *port) -{ - spin_lock_bh(&port->lock); - __fwtty_restart_tx(port); - spin_unlock_bh(&port->lock); -} - -/* - * fwtty_update_port_status - decodes & dispatches line status changes - * - * Note: in loopback, the port->lock is being held. Only use functions that - * don't attempt to reclaim the port->lock. - */ -static void fwtty_update_port_status(struct fwtty_port *port, - unsigned int status) -{ - unsigned int delta; - struct tty_struct *tty; - - /* simulated LSR/MSR status from remote */ - status &= ~MCTRL_MASK; - delta = (port->mstatus ^ status) & ~MCTRL_MASK; - delta &= ~(status & TIOCM_RNG); - port->mstatus = status; - - if (delta & TIOCM_RNG) - ++port->icount.rng; - if (delta & TIOCM_DSR) - ++port->icount.dsr; - if (delta & TIOCM_CAR) - ++port->icount.dcd; - if (delta & TIOCM_CTS) - ++port->icount.cts; - - fwtty_dbg(port, "status: %x delta: %x\n", status, delta); - - if (delta & TIOCM_CAR) { - tty = tty_port_tty_get(&port->port); - if (tty && !C_CLOCAL(tty)) { - if (status & TIOCM_CAR) - wake_up_interruptible(&port->port.open_wait); - else - schedule_work(&port->hangup); - } - tty_kref_put(tty); - } - - if (delta & TIOCM_CTS) { - tty = tty_port_tty_get(&port->port); - if (tty && C_CRTSCTS(tty)) { - if (tty->hw_stopped) { - if (status & TIOCM_CTS) { - tty->hw_stopped = 0; - if (port->loopback) - __fwtty_restart_tx(port); - else - fwtty_restart_tx(port); - } - } else { - if (~status & TIOCM_CTS) - tty->hw_stopped = 1; - } - } - tty_kref_put(tty); - - } else if (delta & OOB_TX_THROTTLE) { - tty = tty_port_tty_get(&port->port); - if (tty) { - if (tty->hw_stopped) { - if (~status & OOB_TX_THROTTLE) { - tty->hw_stopped = 0; - if (port->loopback) - __fwtty_restart_tx(port); - else - fwtty_restart_tx(port); - } - } else { - if (status & OOB_TX_THROTTLE) - tty->hw_stopped = 1; - } - } - tty_kref_put(tty); - } - - if (delta & (UART_LSR_BI << 24)) { - if (status & (UART_LSR_BI << 24)) { - port->break_last = jiffies; - schedule_delayed_work(&port->emit_breaks, 0); - } else { - /* run emit_breaks one last time (if pending) */ - mod_delayed_work(system_wq, &port->emit_breaks, 0); - } - } - - if (delta & (TIOCM_DSR | TIOCM_CAR | TIOCM_CTS | TIOCM_RNG)) - wake_up_interruptible(&port->port.delta_msr_wait); -} - -/* - * __fwtty_port_line_status - generate 'line status' for indicated port - * - * This function returns a remote 'MSR' state based on the local 'MCR' state, - * as if a null modem cable was attached. The actual status is a mangling - * of TIOCM_* bits suitable for sending to a peer's status_addr. - * - * Note: caller must be holding port lock - */ -static unsigned int __fwtty_port_line_status(struct fwtty_port *port) -{ - unsigned int status = 0; - - /* TODO: add module param to tie RNG to DTR as well */ - - if (port->mctrl & TIOCM_DTR) - status |= TIOCM_DSR | TIOCM_CAR; - if (port->mctrl & TIOCM_RTS) - status |= TIOCM_CTS; - if (port->mctrl & OOB_RX_THROTTLE) - status |= OOB_TX_THROTTLE; - /* emulate BRK as add'l line status */ - if (port->break_ctl) - status |= UART_LSR_BI << 24; - - return status; -} - -/* - * __fwtty_write_port_status - send the port line status to peer - * - * Note: caller must be holding the port lock. - */ -static int __fwtty_write_port_status(struct fwtty_port *port) -{ - struct fwtty_peer *peer; - int err = -ENOENT; - unsigned int status = __fwtty_port_line_status(port); - - rcu_read_lock(); - peer = rcu_dereference(port->peer); - if (peer) { - err = fwtty_send_data_async(peer, TCODE_WRITE_QUADLET_REQUEST, - peer->status_addr, &status, - sizeof(status), NULL, port); - } - rcu_read_unlock(); - - return err; -} - -/* - * fwtty_write_port_status - same as above but locked by port lock - */ -static int fwtty_write_port_status(struct fwtty_port *port) -{ - int err; - - spin_lock_bh(&port->lock); - err = __fwtty_write_port_status(port); - spin_unlock_bh(&port->lock); - return err; -} - -static void fwtty_throttle_port(struct fwtty_port *port) -{ - struct tty_struct *tty; - unsigned int 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); -} - -/* - * fwtty_do_hangup - wait for ldisc to deliver all pending rx; only then hangup - * - * When the remote has finished tx, and all in-flight rx has been received and - * pushed to the flip buffer, the remote may close its device. This will - * drop DTR on the remote which will drop carrier here. Typically, the tty is - * hung up when carrier is dropped or lost. - * - * However, there is a race between the hang up and the line discipline - * delivering its data to the reader. A hangup will cause the ldisc to flush - * (ie., clear) the read buffer and flip buffer. Because of firewire's - * relatively high throughput, the ldisc frequently lags well behind the driver, - * resulting in lost data (which has already been received and written to - * the flip buffer) when the remote closes its end. - * - * Unfortunately, since the flip buffer offers no direct method for determining - * if it holds data, ensuring the ldisc has delivered all data is problematic. - */ - -/* FIXME: drop this workaround when __tty_hangup waits for ldisc completion */ -static void fwtty_do_hangup(struct work_struct *work) -{ - struct fwtty_port *port = to_port(work, hangup); - struct tty_struct *tty; - - schedule_timeout_uninterruptible(msecs_to_jiffies(50)); - - tty = tty_port_tty_get(&port->port); - if (tty) - tty_vhangup(tty); - tty_kref_put(tty); -} - -static void fwtty_emit_breaks(struct work_struct *work) -{ - struct fwtty_port *port = to_port(to_delayed_work(work), emit_breaks); - static const char buf[16]; - unsigned long now = jiffies; - unsigned long elapsed = now - port->break_last; - int n, t, c, brk = 0; - - /* 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", n); - - while (n) { - t = min(n, 16); - 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(&port->port); - - if (port->mstatus & (UART_LSR_BI << 24)) - schedule_delayed_work(&port->emit_breaks, FREQ_BREAKS); - port->icount.brk += brk; -} - -static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len) -{ - int c, n = len; - unsigned int lsr; - int err = 0; - - fwtty_dbg(port, "%d\n", n); - fwtty_profile_data(port->stats.reads, n); - - if (port->write_only) { - n = 0; - goto out; - } - - /* disregard break status; breaks are generated by emit_breaks work */ - lsr = (port->mstatus >> 24) & ~UART_LSR_BI; - - if (port->overrun) - lsr |= UART_LSR_OE; - - if (lsr & UART_LSR_OE) - ++port->icount.overrun; - - lsr &= port->status_mask; - if (lsr & ~port->ignore_mask & UART_LSR_OE) { - if (!tty_insert_flip_char(&port->port, 0, TTY_OVERRUN)) { - err = -EIO; - goto out; - } - } - port->overrun = false; - - if (lsr & port->ignore_mask & ~UART_LSR_OE) { - /* TODO: don't drop SAK and Magic SysRq here */ - n = 0; - goto out; - } - - 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: - port->icount.rx += len; - port->stats.lost += n; - return err; -} - -/* - * fwtty_port_handler - bus address handler for port reads/writes - * - * This handler is responsible for handling inbound read/write dma from remotes. - */ -static void fwtty_port_handler(struct fw_card *card, - struct fw_request *request, - int tcode, int destination, int source, - int generation, - unsigned long long addr, - void *data, size_t len, - void *callback_data) -{ - struct fwtty_port *port = callback_data; - struct fwtty_peer *peer; - int err; - int rcode; - - /* Only accept rx from the peer virtual-cabled to this port */ - rcu_read_lock(); - peer = __fwserial_peer_by_node_id(card, generation, source); - rcu_read_unlock(); - if (!peer || peer != rcu_access_pointer(port->peer)) { - rcode = RCODE_ADDRESS_ERROR; - 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) { - rcode = RCODE_ADDRESS_ERROR; - } else { - fwtty_update_port_status(port, *(unsigned int *)data); - rcode = RCODE_COMPLETE; - } - break; - - case TCODE_WRITE_BLOCK_REQUEST: - if (addr != port->rx_handler.offset + 4 || - len > port->rx_handler.length - 4) { - rcode = RCODE_ADDRESS_ERROR; - } else { - err = fwtty_rx(port, data, len); - switch (err) { - case 0: - rcode = RCODE_COMPLETE; - break; - case -EIO: - rcode = RCODE_DATA_ERROR; - break; - default: - rcode = RCODE_CONFLICT_ERROR; - break; - } - } - break; - - default: - rcode = RCODE_TYPE_ERROR; - } - -respond: - fw_send_response(card, request, rcode); -} - -/* - * fwtty_tx_complete - callback for tx dma - * @data: ignored, has no meaning for write txns - * @length: ignored, has no meaning for write txns - * - * The writer must be woken here if the fifo has been emptied because it - * may have slept if chars_in_buffer was != 0 - */ -static void fwtty_tx_complete(struct fw_card *card, int rcode, - void *data, size_t length, - struct fwtty_transaction *txn) -{ - struct fwtty_port *port = txn->port; - int len; - - fwtty_dbg(port, "rcode: %d\n", rcode); - - switch (rcode) { - case RCODE_COMPLETE: - spin_lock_bh(&port->lock); - dma_fifo_out_complete(&port->tx_fifo, &txn->dma_pended); - len = dma_fifo_level(&port->tx_fifo); - spin_unlock_bh(&port->lock); - - port->icount.tx += txn->dma_pended.len; - break; - - default: - /* TODO: implement retries */ - spin_lock_bh(&port->lock); - dma_fifo_out_complete(&port->tx_fifo, &txn->dma_pended); - len = dma_fifo_level(&port->tx_fifo); - spin_unlock_bh(&port->lock); - - port->stats.dropped += txn->dma_pended.len; - } - - if (len < WAKEUP_CHARS) - tty_port_tty_wakeup(&port->port); -} - -static int fwtty_tx(struct fwtty_port *port, bool drain) -{ - struct fwtty_peer *peer; - struct fwtty_transaction *txn; - struct tty_struct *tty; - int n, len; - - tty = tty_port_tty_get(&port->port); - if (!tty) - return -ENOENT; - - rcu_read_lock(); - peer = rcu_dereference(port->peer); - if (!peer) { - n = -EIO; - goto out; - } - - if (test_and_set_bit(IN_TX, &port->flags)) { - n = -EALREADY; - goto out; - } - - /* try to write as many dma transactions out as possible */ - n = -EAGAIN; - while (!tty->flow.stopped && !tty->hw_stopped && - !test_bit(STOP_TX, &port->flags)) { - txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC); - if (!txn) { - n = -ENOMEM; - break; - } - - spin_lock_bh(&port->lock); - n = dma_fifo_out_pend(&port->tx_fifo, &txn->dma_pended); - spin_unlock_bh(&port->lock); - - 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) { - ++port->stats.tx_stall; - } else if (n == -ENODATA) { - fwtty_profile_data(port->stats.txns, 0); - } else { - ++port->stats.fifo_errs; - fwtty_err_ratelimited(port, "fifo err: %d\n", - n); - } - break; - } - - 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, - txn->dma_pended.len, fwtty_tx_complete, - port); - ++port->stats.sent; - - /* - * Stop tx if the 'last view' of the fifo is empty or if - * this is the writer and there's not enough data to bother - */ - if (n == 0 || (!drain && n < WRITER_MINIMUM)) - break; - } - - if (n >= 0 || n == -EAGAIN || n == -ENOMEM || n == -ENODATA) { - spin_lock_bh(&port->lock); - len = dma_fifo_out_level(&port->tx_fifo); - if (len) { - unsigned long delay = (n == -ENOMEM) ? HZ : 1; - - schedule_delayed_work(&port->drain, delay); - } - len = dma_fifo_level(&port->tx_fifo); - spin_unlock_bh(&port->lock); - - /* wakeup the writer */ - if (drain && len < WAKEUP_CHARS) - tty_wakeup(tty); - } - - clear_bit(IN_TX, &port->flags); - wake_up_interruptible(&port->wait_tx); - -out: - rcu_read_unlock(); - tty_kref_put(tty); - return n; -} - -static void fwtty_drain_tx(struct work_struct *work) -{ - struct fwtty_port *port = to_port(to_delayed_work(work), drain); - - fwtty_tx(port, true); -} - -static void fwtty_write_xchar(struct fwtty_port *port, char ch) -{ - struct fwtty_peer *peer; - - ++port->stats.xchars; - - fwtty_dbg(port, "%02x\n", ch); - - rcu_read_lock(); - peer = rcu_dereference(port->peer); - if (peer) { - fwtty_send_data_async(peer, TCODE_WRITE_BLOCK_REQUEST, - peer->fifo_addr, &ch, sizeof(ch), - NULL, port); - } - rcu_read_unlock(); -} - -static struct fwtty_port *fwtty_port_get(unsigned int index) -{ - struct fwtty_port *port; - - if (index >= MAX_TOTAL_PORTS) - return NULL; - - mutex_lock(&port_table_lock); - port = port_table[index]; - if (port) - kref_get(&port->serial->kref); - mutex_unlock(&port_table_lock); - return port; -} - -static int fwtty_ports_add(struct fw_serial *serial) -{ - int err = -EBUSY; - int i, j; - - if (port_table_corrupt) - return err; - - mutex_lock(&port_table_lock); - for (i = 0; i + num_ports <= MAX_TOTAL_PORTS; i += num_ports) { - if (!port_table[i]) { - for (j = 0; j < num_ports; ++i, ++j) { - serial->ports[j]->index = i; - port_table[i] = serial->ports[j]; - } - err = 0; - break; - } - } - mutex_unlock(&port_table_lock); - return err; -} - -static void fwserial_destroy(struct kref *kref) -{ - struct fw_serial *serial = to_serial(kref, kref); - struct fwtty_port **ports = serial->ports; - int j, i = ports[0]->index; - - synchronize_rcu(); - - mutex_lock(&port_table_lock); - 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]); - - port_table[i] = NULL; - } - mutex_unlock(&port_table_lock); - - for (j = 0; j < num_ports; ++j) { - fw_core_remove_address_handler(&ports[j]->rx_handler); - tty_port_destroy(&ports[j]->port); - kfree(ports[j]); - } - kfree(serial); -} - -static void fwtty_port_put(struct fwtty_port *port) -{ - kref_put(&port->serial->kref, fwserial_destroy); -} - -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\n", on); - - spin_lock_bh(&port->lock); - /* Don't change carrier state if this is a console */ - if (!port->port.console) { - if (on) - port->mctrl |= TIOCM_DTR | TIOCM_RTS; - else - port->mctrl &= ~(TIOCM_DTR | TIOCM_RTS); - } - - __fwtty_write_port_status(port); - spin_unlock_bh(&port->lock); -} - -/* - * fwtty_port_carrier_raised: required tty_port operation - * - * This port operation is polled after a tty has been opened and is waiting for - * carrier detect -- see drivers/tty/tty_port:tty_port_block_til_ready(). - */ -static int fwtty_port_carrier_raised(struct tty_port *tty_port) -{ - struct fwtty_port *port = to_port(tty_port, port); - int rc; - - rc = (port->mstatus & TIOCM_CAR); - - fwtty_dbg(port, "%d\n", rc); - - return rc; -} - -static unsigned int set_termios(struct fwtty_port *port, struct tty_struct *tty) -{ - unsigned int baud, frame; - - baud = tty_termios_baud_rate(&tty->termios); - tty_termios_encode_baud_rate(&tty->termios, baud, baud); - - /* compute bit count of 2 frames */ - frame = 12 + ((C_CSTOPB(tty)) ? 4 : 2) + ((C_PARENB(tty)) ? 2 : 0); - - switch (C_CSIZE(tty)) { - case CS5: - frame -= (C_CSTOPB(tty)) ? 1 : 0; - break; - case CS6: - frame += 2; - break; - case CS7: - frame += 4; - break; - case CS8: - frame += 6; - break; - } - - port->cps = (baud << 1) / frame; - - port->status_mask = UART_LSR_OE; - if (_I_FLAG(tty, BRKINT | PARMRK)) - port->status_mask |= UART_LSR_BI; - - port->ignore_mask = 0; - if (I_IGNBRK(tty)) { - port->ignore_mask |= UART_LSR_BI; - if (I_IGNPAR(tty)) - port->ignore_mask |= UART_LSR_OE; - } - - port->write_only = !C_CREAD(tty); - - /* turn off echo and newline xlat if loopback */ - if (port->loopback) { - tty->termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOKE | - ECHONL | ECHOPRT | ECHOCTL); - tty->termios.c_oflag &= ~ONLCR; - } - - return baud; -} - -static int fwtty_port_activate(struct tty_port *tty_port, - struct tty_struct *tty) -{ - struct fwtty_port *port = to_port(tty_port, port); - unsigned int baud; - int err; - - set_bit(TTY_IO_ERROR, &tty->flags); - - err = dma_fifo_alloc(&port->tx_fifo, FWTTY_PORT_TXFIFO_LEN, - cache_line_size(), - port->max_payload, - FWTTY_PORT_MAX_PEND_DMA, - GFP_KERNEL); - if (err) - return err; - - spin_lock_bh(&port->lock); - - baud = set_termios(port, tty); - - /* if console, don't change carrier state */ - if (!port->port.console) { - port->mctrl = 0; - if (baud != 0) - port->mctrl = TIOCM_DTR | TIOCM_RTS; - } - - if (C_CRTSCTS(tty) && ~port->mstatus & TIOCM_CTS) - tty->hw_stopped = 1; - - __fwtty_write_port_status(port); - spin_unlock_bh(&port->lock); - - clear_bit(TTY_IO_ERROR, &tty->flags); - - return 0; -} - -/* - * fwtty_port_shutdown - * - * Note: the tty port core ensures this is not the console and - * manages TTY_IO_ERROR properly - */ -static void fwtty_port_shutdown(struct tty_port *tty_port) -{ - struct fwtty_port *port = to_port(tty_port, port); - - /* TODO: cancel outstanding transactions */ - - cancel_delayed_work_sync(&port->emit_breaks); - cancel_delayed_work_sync(&port->drain); - - spin_lock_bh(&port->lock); - port->flags = 0; - port->break_ctl = 0; - port->overrun = 0; - __fwtty_write_port_status(port); - dma_fifo_free(&port->tx_fifo); - spin_unlock_bh(&port->lock); -} - -static int fwtty_open(struct tty_struct *tty, struct file *fp) -{ - struct fwtty_port *port = tty->driver_data; - - return tty_port_open(&port->port, tty, fp); -} - -static void fwtty_close(struct tty_struct *tty, struct file *fp) -{ - struct fwtty_port *port = tty->driver_data; - - tty_port_close(&port->port, tty, fp); -} - -static void fwtty_hangup(struct tty_struct *tty) -{ - struct fwtty_port *port = tty->driver_data; - - tty_port_hangup(&port->port); -} - -static void fwtty_cleanup(struct tty_struct *tty) -{ - struct fwtty_port *port = tty->driver_data; - - tty->driver_data = NULL; - fwtty_port_put(port); -} - -static int fwtty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct fwtty_port *port = fwtty_port_get(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 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\n", c); - fwtty_profile_data(port->stats.writes, c); - - spin_lock_bh(&port->lock); - n = dma_fifo_in(&port->tx_fifo, buf, c); - len = dma_fifo_out_level(&port->tx_fifo); - if (len < DRAIN_THRESHOLD) - schedule_delayed_work(&port->drain, 1); - spin_unlock_bh(&port->lock); - - if (len >= DRAIN_THRESHOLD) - fwtty_tx(port, false); - - debug_short_write(port, c, n); - - return (n < 0) ? 0 : n; -} - -static unsigned int fwtty_write_room(struct tty_struct *tty) -{ - struct fwtty_port *port = tty->driver_data; - unsigned int n; - - spin_lock_bh(&port->lock); - n = dma_fifo_avail(&port->tx_fifo); - spin_unlock_bh(&port->lock); - - fwtty_dbg(port, "%u\n", n); - - return n; -} - -static unsigned int fwtty_chars_in_buffer(struct tty_struct *tty) -{ - struct fwtty_port *port = tty->driver_data; - unsigned int n; - - spin_lock_bh(&port->lock); - n = dma_fifo_level(&port->tx_fifo); - spin_unlock_bh(&port->lock); - - fwtty_dbg(port, "%u\n", n); - - return n; -} - -static void fwtty_send_xchar(struct tty_struct *tty, char ch) -{ - struct fwtty_port *port = tty->driver_data; - - fwtty_dbg(port, "%02x\n", ch); - - fwtty_write_xchar(port, ch); -} - -static void fwtty_throttle(struct tty_struct *tty) -{ - struct fwtty_port *port = tty->driver_data; - - /* - * Ignore throttling (but not unthrottling). - * It only makes sense to throttle when data will no longer be - * accepted by the tty flip buffer. For example, it is - * possible for received data to overflow the tty buffer long - * before the line discipline ever has a chance to throttle the driver. - * Additionally, the driver may have already completed the I/O - * but the tty buffer is still emptying, so the line discipline is - * throttling and unthrottling nothing. - */ - - ++port->stats.throttled; -} - -static void fwtty_unthrottle(struct tty_struct *tty) -{ - struct fwtty_port *port = tty->driver_data; - - fwtty_dbg(port, "CRTSCTS: %d\n", C_CRTSCTS(tty) != 0); - - fwtty_profile_fifo(port, port->stats.unthrottle); - - spin_lock_bh(&port->lock); - port->mctrl &= ~OOB_RX_THROTTLE; - if (C_CRTSCTS(tty)) - port->mctrl |= TIOCM_RTS; - __fwtty_write_port_status(port); - spin_unlock_bh(&port->lock); -} - -static int check_msr_delta(struct fwtty_port *port, unsigned long mask, - struct async_icount *prev) -{ - struct async_icount now; - int delta; - - now = port->icount; |
