|  | /* SPDX-License-Identifier: GPL-2.0 */ | 
|  | #ifndef _FIREWIRE_FWSERIAL_H | 
|  | #define _FIREWIRE_FWSERIAL_H | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/tty.h> | 
|  | #include <linux/tty_driver.h> | 
|  | #include <linux/tty_flip.h> | 
|  | #include <linux/list.h> | 
|  | #include <linux/firewire.h> | 
|  | #include <linux/firewire-constants.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include <linux/rcupdate.h> | 
|  | #include <linux/mutex.h> | 
|  | #include <linux/serial.h> | 
|  | #include <linux/serial_reg.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/seq_file.h> | 
|  | #include <linux/debugfs.h> | 
|  |  | 
|  | #include "dma_fifo.h" | 
|  |  | 
|  | #ifdef FWTTY_PROFILING | 
|  | #define DISTRIBUTION_MAX_SIZE     8192 | 
|  | #define DISTRIBUTION_MAX_INDEX    (ilog2(DISTRIBUTION_MAX_SIZE) + 1) | 
|  | static inline void fwtty_profile_data(unsigned int stat[], unsigned int val) | 
|  | { | 
|  | int n = (val) ? min(ilog2(val) + 1, DISTRIBUTION_MAX_INDEX) : 0; | 
|  | ++stat[n]; | 
|  | } | 
|  | #else | 
|  | #define DISTRIBUTION_MAX_INDEX    0 | 
|  | #define fwtty_profile_data(st, n) | 
|  | #endif | 
|  |  | 
|  | /* Parameters for both VIRT_CABLE_PLUG & VIRT_CABLE_PLUG_RSP mgmt codes */ | 
|  | struct virt_plug_params { | 
|  | __be32  status_hi; | 
|  | __be32  status_lo; | 
|  | __be32	fifo_hi; | 
|  | __be32	fifo_lo; | 
|  | __be32	fifo_len; | 
|  | }; | 
|  |  | 
|  | struct peer_work_params { | 
|  | union { | 
|  | struct virt_plug_params plug_req; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * fwtty_peer: structure representing local & remote unit devices | 
|  | * @unit: unit child device of fw_device node | 
|  | * @serial: back pointer to associated fw_serial aggregate | 
|  | * @guid: unique 64-bit guid for this unit device | 
|  | * @generation: most recent bus generation | 
|  | * @node_id: most recent node_id | 
|  | * @speed: link speed of peer (0 = S100, 2 = S400, ... 5 = S3200) | 
|  | * @mgmt_addr: bus addr region to write mgmt packets to | 
|  | * @status_addr: bus addr register to write line status to | 
|  | * @fifo_addr: bus addr region to write serial output to | 
|  | * @fifo_len:  max length for single write to fifo_addr | 
|  | * @list: link for insertion into fw_serial's peer_list | 
|  | * @rcu: for deferring peer reclamation | 
|  | * @lock: spinlock to synchonize changes to state & port fields | 
|  | * @work: only one work item can be queued at any one time | 
|  | *        Note: pending work is canceled prior to removal, so this | 
|  | *        peer is valid for at least the lifetime of the work function | 
|  | * @work_params: parameter block for work functions | 
|  | * @timer: timer for resetting peer state if remote request times out | 
|  | * @state: current state | 
|  | * @connect: work item for auto-connecting | 
|  | * @connect_retries: # of connections already attempted | 
|  | * @port: associated tty_port (usable if state == FWSC_ATTACHED) | 
|  | */ | 
|  | struct fwtty_peer { | 
|  | struct fw_unit		*unit; | 
|  | struct fw_serial	*serial; | 
|  | u64			guid; | 
|  | int			generation; | 
|  | int			node_id; | 
|  | unsigned int		speed; | 
|  | int			max_payload; | 
|  | u64			mgmt_addr; | 
|  |  | 
|  | /* these are usable only if state == FWSC_ATTACHED */ | 
|  | u64			status_addr; | 
|  | u64			fifo_addr; | 
|  | int			fifo_len; | 
|  |  | 
|  | struct list_head	list; | 
|  | struct rcu_head		rcu; | 
|  |  | 
|  | spinlock_t		lock; | 
|  | work_func_t		workfn; | 
|  | struct work_struct	work; | 
|  | struct peer_work_params work_params; | 
|  | struct timer_list	timer; | 
|  | int			state; | 
|  | struct delayed_work	connect; | 
|  | int			connect_retries; | 
|  |  | 
|  | struct fwtty_port	*port; | 
|  | }; | 
|  |  | 
|  | #define to_peer(ptr, field)	(container_of(ptr, struct fwtty_peer, field)) | 
|  |  | 
|  | /* state values for fwtty_peer.state field */ | 
|  | enum fwtty_peer_state { | 
|  | FWPS_GONE, | 
|  | FWPS_NOT_ATTACHED, | 
|  | FWPS_ATTACHED, | 
|  | FWPS_PLUG_PENDING, | 
|  | FWPS_PLUG_RESPONDING, | 
|  | FWPS_UNPLUG_PENDING, | 
|  | FWPS_UNPLUG_RESPONDING, | 
|  |  | 
|  | FWPS_NO_MGMT_ADDR = -1, | 
|  | }; | 
|  |  | 
|  | #define CONNECT_RETRY_DELAY	HZ | 
|  | #define MAX_CONNECT_RETRIES	10 | 
|  |  | 
|  | /* must be holding peer lock for these state funclets */ | 
|  | static inline void peer_set_state(struct fwtty_peer *peer, int new) | 
|  | { | 
|  | peer->state = new; | 
|  | } | 
|  |  | 
|  | static inline struct fwtty_port *peer_revert_state(struct fwtty_peer *peer) | 
|  | { | 
|  | struct fwtty_port *port = peer->port; | 
|  |  | 
|  | peer->port = NULL; | 
|  | peer_set_state(peer, FWPS_NOT_ATTACHED); | 
|  | return port; | 
|  | } | 
|  |  | 
|  | struct fwserial_mgmt_pkt { | 
|  | struct { | 
|  | __be16		len; | 
|  | __be16		code; | 
|  | } hdr; | 
|  | union { | 
|  | struct virt_plug_params plug_req; | 
|  | struct virt_plug_params plug_rsp; | 
|  | }; | 
|  | } __packed; | 
|  |  | 
|  | /* fwserial_mgmt_packet codes */ | 
|  | #define FWSC_RSP_OK			0x0000 | 
|  | #define FWSC_RSP_NACK			0x8000 | 
|  | #define FWSC_CODE_MASK			0x0fff | 
|  |  | 
|  | #define FWSC_VIRT_CABLE_PLUG		1 | 
|  | #define FWSC_VIRT_CABLE_UNPLUG		2 | 
|  | #define FWSC_VIRT_CABLE_PLUG_RSP	3 | 
|  | #define FWSC_VIRT_CABLE_UNPLUG_RSP	4 | 
|  |  | 
|  | /* 1 min. plug timeout -- suitable for userland authorization */ | 
|  | #define VIRT_CABLE_PLUG_TIMEOUT		(60 * HZ) | 
|  |  | 
|  | struct stats { | 
|  | unsigned int	xchars; | 
|  | unsigned int	dropped; | 
|  | unsigned int	tx_stall; | 
|  | unsigned int	fifo_errs; | 
|  | unsigned int	sent; | 
|  | unsigned int	lost; | 
|  | unsigned int	throttled; | 
|  | unsigned int	reads[DISTRIBUTION_MAX_INDEX + 1]; | 
|  | unsigned int	writes[DISTRIBUTION_MAX_INDEX + 1]; | 
|  | unsigned int	txns[DISTRIBUTION_MAX_INDEX + 1]; | 
|  | unsigned int	unthrottle[DISTRIBUTION_MAX_INDEX + 1]; | 
|  | }; | 
|  |  | 
|  | struct fwconsole_ops { | 
|  | void (*notify)(int code, void *data); | 
|  | void (*stats)(struct stats *stats, void *data); | 
|  | void (*proc_show)(struct seq_file *m, void *data); | 
|  | }; | 
|  |  | 
|  | /* codes for console ops notify */ | 
|  | #define FWCON_NOTIFY_ATTACH		1 | 
|  | #define FWCON_NOTIFY_DETACH		2 | 
|  |  | 
|  | /** | 
|  | * fwtty_port: structure used to track/represent underlying tty_port | 
|  | * @port: underlying tty_port | 
|  | * @device: tty device | 
|  | * @index: index into port_table for this particular port | 
|  | *    note: minor = index + minor_start assigned by tty_alloc_driver() | 
|  | * @serial: back pointer to the containing fw_serial | 
|  | * @rx_handler: bus address handler for unique addr region used by remotes | 
|  | *              to communicate with this port. Every port uses | 
|  | *		fwtty_port_handler() for per port transactions. | 
|  | * @fwcon_ops: ops for attached fw_console (if any) | 
|  | * @con_data: private data for fw_console | 
|  | * @wait_tx: waitqueue for sleeping until writer/drain completes tx | 
|  | * @emit_breaks: delayed work responsible for generating breaks when the | 
|  | *               break line status is active | 
|  | * @cps : characters per second computed from the termios settings | 
|  | * @break_last: timestamp in jiffies from last emit_breaks | 
|  | * @hangup: work responsible for HUPing when carrier is dropped/lost | 
|  | * @mstatus: loose virtualization of LSR/MSR | 
|  | *         bits 15..0  correspond to TIOCM_* bits | 
|  | *         bits 19..16 reserved for mctrl | 
|  | *         bit 20      OOB_TX_THROTTLE | 
|  | *	   bits 23..21 reserved | 
|  | *         bits 31..24 correspond to UART_LSR_* bits | 
|  | * @lock: spinlock for protecting concurrent access to fields below it | 
|  | * @mctrl: loose virtualization of MCR | 
|  | *         bits 15..0  correspond to TIOCM_* bits | 
|  | *         bit 16      OOB_RX_THROTTLE | 
|  | *         bits 19..17 reserved | 
|  | *	   bits 31..20 reserved for mstatus | 
|  | * @drain: delayed work scheduled to ensure that writes are flushed. | 
|  | *         The work can race with the writer but concurrent sending is | 
|  | *         prevented with the IN_TX flag. Scheduled under lock to | 
|  | *         limit scheduling when fifo has just been drained. | 
|  | * @tx_fifo: fifo used to store & block-up writes for dma to remote | 
|  | * @max_payload: max bytes transmissible per dma (based on peer's max_payload) | 
|  | * @status_mask: UART_LSR_* bitmask significant to rx (based on termios) | 
|  | * @ignore_mask: UART_LSR_* bitmask of states to ignore (also based on termios) | 
|  | * @break_ctl: if set, port is 'sending break' to remote | 
|  | * @write_only: self-explanatory | 
|  | * @overrun: previous rx was lost (partially or completely) | 
|  | * @loopback: if set, port is in loopback mode | 
|  | * @flags: atomic bit flags | 
|  | *         bit 0: IN_TX - gate to allow only one cpu to send from the dma fifo | 
|  | *                        at a time. | 
|  | *         bit 1: STOP_TX - force tx to exit while sending | 
|  | * @peer: rcu-pointer to associated fwtty_peer (if attached) | 
|  | *        NULL if no peer attached | 
|  | * @icount: predefined statistics reported by the TIOCGICOUNT ioctl | 
|  | * @stats: additional statistics reported in /proc/tty/driver/firewire_serial | 
|  | */ | 
|  | struct fwtty_port { | 
|  | struct tty_port		   port; | 
|  | struct device		   *device; | 
|  | unsigned int		   index; | 
|  | struct fw_serial	   *serial; | 
|  | struct fw_address_handler  rx_handler; | 
|  |  | 
|  | struct fwconsole_ops	   *fwcon_ops; | 
|  | void			   *con_data; | 
|  |  | 
|  | wait_queue_head_t	   wait_tx; | 
|  | struct delayed_work	   emit_breaks; | 
|  | unsigned int		   cps; | 
|  | unsigned long		   break_last; | 
|  |  | 
|  | struct work_struct	   hangup; | 
|  |  | 
|  | unsigned int		   mstatus; | 
|  |  | 
|  | spinlock_t		   lock; | 
|  | unsigned int		   mctrl; | 
|  | struct delayed_work	   drain; | 
|  | struct dma_fifo		   tx_fifo; | 
|  | int			   max_payload; | 
|  | unsigned int		   status_mask; | 
|  | unsigned int		   ignore_mask; | 
|  | unsigned int		   break_ctl:1, | 
|  | write_only:1, | 
|  | overrun:1, | 
|  | loopback:1; | 
|  | unsigned long		   flags; | 
|  |  | 
|  | struct fwtty_peer __rcu	   *peer; | 
|  |  | 
|  | struct async_icount	   icount; | 
|  | struct stats		   stats; | 
|  | }; | 
|  |  | 
|  | #define to_port(ptr, field)	(container_of(ptr, struct fwtty_port, field)) | 
|  |  | 
|  | /* bit #s for flags field */ | 
|  | #define IN_TX                      0 | 
|  | #define STOP_TX                    1 | 
|  |  | 
|  | /* bitmasks for special mctrl/mstatus bits */ | 
|  | #define OOB_RX_THROTTLE   0x00010000 | 
|  | #define MCTRL_RSRVD       0x000e0000 | 
|  | #define OOB_TX_THROTTLE   0x00100000 | 
|  | #define MSTATUS_RSRVD     0x00e00000 | 
|  |  | 
|  | #define MCTRL_MASK        (TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | TIOCM_OUT2 | \ | 
|  | TIOCM_LOOP | OOB_RX_THROTTLE | MCTRL_RSRVD) | 
|  |  | 
|  | /* XXX even every 1/50th secs. may be unnecessarily accurate */ | 
|  | /* delay in jiffies between brk emits */ | 
|  | #define FREQ_BREAKS        (HZ / 50) | 
|  |  | 
|  | /* Ports are allocated in blocks of num_ports for each fw_card */ | 
|  | #define MAX_CARD_PORTS           CONFIG_FWTTY_MAX_CARD_PORTS | 
|  | #define MAX_TOTAL_PORTS          CONFIG_FWTTY_MAX_TOTAL_PORTS | 
|  |  | 
|  | /* tuning parameters */ | 
|  | #define FWTTY_PORT_TXFIFO_LEN	4096 | 
|  | #define FWTTY_PORT_MAX_PEND_DMA    8    /* costs a cache line per pend */ | 
|  | #define DRAIN_THRESHOLD         1024 | 
|  | #define MAX_ASYNC_PAYLOAD       4096    /* ohci-defined limit          */ | 
|  | #define WRITER_MINIMUM           128 | 
|  | /* TODO: how to set watermark to AR context size? see fwtty_rx() */ | 
|  | #define HIGH_WATERMARK         32768	/* AR context is 32K	       */ | 
|  |  | 
|  | /* | 
|  | * Size of bus addr region above 4GB used per port as the recv addr | 
|  | * - must be at least as big as the MAX_ASYNC_PAYLOAD | 
|  | */ | 
|  | #define FWTTY_PORT_RXFIFO_LEN	MAX_ASYNC_PAYLOAD | 
|  |  | 
|  | /** | 
|  | * fw_serial: aggregate used to associate tty ports with specific fw_card | 
|  | * @card: fw_card associated with this fw_serial device (1:1 association) | 
|  | * @kref: reference-counted multi-port management allows delayed destroy | 
|  | * @self: local unit device as 'peer'. Not valid until local unit device | 
|  | *         is enumerated. | 
|  | * @list: link for insertion into fwserial_list | 
|  | * @peer_list: list of local & remote unit devices attached to this card | 
|  | * @ports: fixed array of tty_ports provided by this serial device | 
|  | */ | 
|  | struct fw_serial { | 
|  | struct fw_card	  *card; | 
|  | struct kref	  kref; | 
|  |  | 
|  | struct dentry	  *debugfs; | 
|  | struct fwtty_peer *self; | 
|  |  | 
|  | struct list_head  list; | 
|  | struct list_head  peer_list; | 
|  |  | 
|  | struct fwtty_port *ports[MAX_CARD_PORTS]; | 
|  | }; | 
|  |  | 
|  | #define to_serial(ptr, field)	(container_of(ptr, struct fw_serial, field)) | 
|  |  | 
|  | #define TTY_DEV_NAME		    "fwtty"	/* ttyFW was taken           */ | 
|  | static const char tty_dev_name[] =  TTY_DEV_NAME; | 
|  | static const char loop_dev_name[] = "fwloop"; | 
|  |  | 
|  | extern struct tty_driver *fwtty_driver; | 
|  |  | 
|  | /* | 
|  | * Returns the max send async payload size in bytes based on the unit device | 
|  | * link speed. Self-limiting asynchronous bandwidth (via reducing the payload) | 
|  | * is not necessary and does not work, because | 
|  | *   1) asynchronous traffic will absorb all available bandwidth (less that | 
|  | *	being used for isochronous traffic) | 
|  | *   2) isochronous arbitration always wins. | 
|  | */ | 
|  | static inline int link_speed_to_max_payload(unsigned int speed) | 
|  | { | 
|  | /* Max async payload is 4096 - see IEEE 1394-2008 tables 6-4, 16-18 */ | 
|  | return min(512 << speed, 4096); | 
|  | } | 
|  |  | 
|  | #endif /* _FIREWIRE_FWSERIAL_H */ |