162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Edgeport USB Serial Converter driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2000 Inside Out Networks, All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Supports the following devices:
962306a36Sopenharmony_ci *	Edgeport/4
1062306a36Sopenharmony_ci *	Edgeport/4t
1162306a36Sopenharmony_ci *	Edgeport/2
1262306a36Sopenharmony_ci *	Edgeport/4i
1362306a36Sopenharmony_ci *	Edgeport/2i
1462306a36Sopenharmony_ci *	Edgeport/421
1562306a36Sopenharmony_ci *	Edgeport/21
1662306a36Sopenharmony_ci *	Rapidport/4
1762306a36Sopenharmony_ci *	Edgeport/8
1862306a36Sopenharmony_ci *	Edgeport/2D8
1962306a36Sopenharmony_ci *	Edgeport/4D8
2062306a36Sopenharmony_ci *	Edgeport/8i
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * For questions or problems with this driver, contact Inside Out
2362306a36Sopenharmony_ci * Networks technical support, or Peter Berger <pberger@brimson.com>,
2462306a36Sopenharmony_ci * or Al Borchers <alborchers@steinerpoint.com>.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/kernel.h>
2962306a36Sopenharmony_ci#include <linux/jiffies.h>
3062306a36Sopenharmony_ci#include <linux/errno.h>
3162306a36Sopenharmony_ci#include <linux/slab.h>
3262306a36Sopenharmony_ci#include <linux/tty.h>
3362306a36Sopenharmony_ci#include <linux/tty_driver.h>
3462306a36Sopenharmony_ci#include <linux/tty_flip.h>
3562306a36Sopenharmony_ci#include <linux/module.h>
3662306a36Sopenharmony_ci#include <linux/spinlock.h>
3762306a36Sopenharmony_ci#include <linux/serial.h>
3862306a36Sopenharmony_ci#include <linux/ioctl.h>
3962306a36Sopenharmony_ci#include <linux/wait.h>
4062306a36Sopenharmony_ci#include <linux/firmware.h>
4162306a36Sopenharmony_ci#include <linux/ihex.h>
4262306a36Sopenharmony_ci#include <linux/uaccess.h>
4362306a36Sopenharmony_ci#include <linux/usb.h>
4462306a36Sopenharmony_ci#include <linux/usb/serial.h>
4562306a36Sopenharmony_ci#include "io_edgeport.h"
4662306a36Sopenharmony_ci#include "io_ionsp.h"		/* info for the iosp messages */
4762306a36Sopenharmony_ci#include "io_16654.h"		/* 16654 UART defines */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
5062306a36Sopenharmony_ci#define DRIVER_DESC "Edgeport USB Serial Driver"
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MAX_NAME_LEN		64
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define OPEN_TIMEOUT		(5*HZ)		/* 5 seconds */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct usb_device_id edgeport_2port_id_table[] = {
5762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
5862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
5962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
6062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
6162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
6262306a36Sopenharmony_ci	{ }
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const struct usb_device_id edgeport_4port_id_table[] = {
6662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
6762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
6862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
6962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
7062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
7162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
7262306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
7362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
7462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
7562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
7662306a36Sopenharmony_ci	{ }
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const struct usb_device_id edgeport_8port_id_table[] = {
8062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
8162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
8262306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
8362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
8462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
8562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
8662306a36Sopenharmony_ci	{ }
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const struct usb_device_id Epic_port_id_table[] = {
9062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
9162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
9262306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
9362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
9462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
9562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
9662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
9762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
9862306a36Sopenharmony_ci	{ }
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/* Devices that this driver supports */
10262306a36Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = {
10362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4) },
10462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_RAPIDPORT_4) },
10562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4T) },
10662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
10762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2) },
10862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4I) },
10962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2I) },
11062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_421) },
11162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_21) },
11262306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
11362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8) },
11462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2_DIN) },
11562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4_DIN) },
11662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
11762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
11862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
11962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
12062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8I) },
12162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
12262306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
12362306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
12462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
12562306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
12662306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
12762306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
12862306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
12962306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
13062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
13162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
13262306a36Sopenharmony_ci	{ } /* Terminating entry */
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* receive port state */
13962306a36Sopenharmony_cienum RXSTATE {
14062306a36Sopenharmony_ci	EXPECT_HDR1 = 0,    /* Expect header byte 1 */
14162306a36Sopenharmony_ci	EXPECT_HDR2 = 1,    /* Expect header byte 2 */
14262306a36Sopenharmony_ci	EXPECT_DATA = 2,    /* Expect 'RxBytesRemaining' data */
14362306a36Sopenharmony_ci	EXPECT_HDR3 = 3,    /* Expect header byte 3 (for status hdrs only) */
14462306a36Sopenharmony_ci};
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* Transmit Fifo
14862306a36Sopenharmony_ci * This Transmit queue is an extension of the edgeport Rx buffer.
14962306a36Sopenharmony_ci * The maximum amount of data buffered in both the edgeport
15062306a36Sopenharmony_ci * Rx buffer (maxTxCredits) and this buffer will never exceed maxTxCredits.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistruct TxFifo {
15362306a36Sopenharmony_ci	unsigned int	head;	/* index to head pointer (write) */
15462306a36Sopenharmony_ci	unsigned int	tail;	/* index to tail pointer (read)  */
15562306a36Sopenharmony_ci	unsigned int	count;	/* Bytes in queue */
15662306a36Sopenharmony_ci	unsigned int	size;	/* Max size of queue (equal to Max number of TxCredits) */
15762306a36Sopenharmony_ci	unsigned char	*fifo;	/* allocated Buffer */
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/* This structure holds all of the local port information */
16162306a36Sopenharmony_cistruct edgeport_port {
16262306a36Sopenharmony_ci	__u16			txCredits;		/* our current credits for this port */
16362306a36Sopenharmony_ci	__u16			maxTxCredits;		/* the max size of the port */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	struct TxFifo		txfifo;			/* transmit fifo -- size will be maxTxCredits */
16662306a36Sopenharmony_ci	struct urb		*write_urb;		/* write URB for this port */
16762306a36Sopenharmony_ci	bool			write_in_progress;	/* 'true' while a write URB is outstanding */
16862306a36Sopenharmony_ci	spinlock_t		ep_lock;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	__u8			shadowLCR;		/* last LCR value received */
17162306a36Sopenharmony_ci	__u8			shadowMCR;		/* last MCR value received */
17262306a36Sopenharmony_ci	__u8			shadowMSR;		/* last MSR value received */
17362306a36Sopenharmony_ci	__u8			shadowLSR;		/* last LSR value received */
17462306a36Sopenharmony_ci	__u8			shadowXonChar;		/* last value set as XON char in Edgeport */
17562306a36Sopenharmony_ci	__u8			shadowXoffChar;		/* last value set as XOFF char in Edgeport */
17662306a36Sopenharmony_ci	__u8			validDataMask;
17762306a36Sopenharmony_ci	__u32			baudRate;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	bool			open;
18062306a36Sopenharmony_ci	bool			openPending;
18162306a36Sopenharmony_ci	bool			commandPending;
18262306a36Sopenharmony_ci	bool			closePending;
18362306a36Sopenharmony_ci	bool			chaseResponsePending;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	wait_queue_head_t	wait_chase;		/* for handling sleeping while waiting for chase to finish */
18662306a36Sopenharmony_ci	wait_queue_head_t	wait_open;		/* for handling sleeping while waiting for open to finish */
18762306a36Sopenharmony_ci	wait_queue_head_t	wait_command;		/* for handling sleeping while waiting for command to finish */
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	struct usb_serial_port	*port;			/* loop back to the owner of this object */
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/* This structure holds all of the individual device information */
19462306a36Sopenharmony_cistruct edgeport_serial {
19562306a36Sopenharmony_ci	char			name[MAX_NAME_LEN+2];		/* string name of this device */
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	struct edge_manuf_descriptor	manuf_descriptor;	/* the manufacturer descriptor */
19862306a36Sopenharmony_ci	struct edge_boot_descriptor	boot_descriptor;	/* the boot firmware descriptor */
19962306a36Sopenharmony_ci	struct edgeport_product_info	product_info;		/* Product Info */
20062306a36Sopenharmony_ci	struct edge_compatibility_descriptor epic_descriptor;	/* Edgeport compatible descriptor */
20162306a36Sopenharmony_ci	int			is_epic;			/* flag if EPiC device or not */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	__u8			interrupt_in_endpoint;		/* the interrupt endpoint handle */
20462306a36Sopenharmony_ci	unsigned char		*interrupt_in_buffer;		/* the buffer we use for the interrupt endpoint */
20562306a36Sopenharmony_ci	struct urb		*interrupt_read_urb;		/* our interrupt urb */
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	__u8			bulk_in_endpoint;		/* the bulk in endpoint handle */
20862306a36Sopenharmony_ci	unsigned char		*bulk_in_buffer;		/* the buffer we use for the bulk in endpoint */
20962306a36Sopenharmony_ci	struct urb		*read_urb;			/* our bulk read urb */
21062306a36Sopenharmony_ci	bool			read_in_progress;
21162306a36Sopenharmony_ci	spinlock_t		es_lock;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	__u8			bulk_out_endpoint;		/* the bulk out endpoint handle */
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	__s16			rxBytesAvail;			/* the number of bytes that we need to read from this device */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	enum RXSTATE		rxState;			/* the current state of the bulk receive processor */
21862306a36Sopenharmony_ci	__u8			rxHeader1;			/* receive header byte 1 */
21962306a36Sopenharmony_ci	__u8			rxHeader2;			/* receive header byte 2 */
22062306a36Sopenharmony_ci	__u8			rxHeader3;			/* receive header byte 3 */
22162306a36Sopenharmony_ci	__u8			rxPort;				/* the port that we are currently receiving data for */
22262306a36Sopenharmony_ci	__u8			rxStatusCode;			/* the receive status code */
22362306a36Sopenharmony_ci	__u8			rxStatusParam;			/* the receive status parameter */
22462306a36Sopenharmony_ci	__s16			rxBytesRemaining;		/* the number of port bytes left to read */
22562306a36Sopenharmony_ci	struct usb_serial	*serial;			/* loop back to the owner of this object */
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/* baud rate information */
22962306a36Sopenharmony_cistruct divisor_table_entry {
23062306a36Sopenharmony_ci	__u32   BaudRate;
23162306a36Sopenharmony_ci	__u16  Divisor;
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * Define table of divisors for Rev A EdgePort/4 hardware
23662306a36Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and
23762306a36Sopenharmony_ci * MCR.7 = 0.
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = {
24162306a36Sopenharmony_ci	{   50,		4608},
24262306a36Sopenharmony_ci	{   75,		3072},
24362306a36Sopenharmony_ci	{   110,	2095},	/* 2094.545455 => 230450   => .0217 % over */
24462306a36Sopenharmony_ci	{   134,	1713},	/* 1713.011152 => 230398.5 => .00065% under */
24562306a36Sopenharmony_ci	{   150,	1536},
24662306a36Sopenharmony_ci	{   300,	768},
24762306a36Sopenharmony_ci	{   600,	384},
24862306a36Sopenharmony_ci	{   1200,	192},
24962306a36Sopenharmony_ci	{   1800,	128},
25062306a36Sopenharmony_ci	{   2400,	96},
25162306a36Sopenharmony_ci	{   4800,	48},
25262306a36Sopenharmony_ci	{   7200,	32},
25362306a36Sopenharmony_ci	{   9600,	24},
25462306a36Sopenharmony_ci	{   14400,	16},
25562306a36Sopenharmony_ci	{   19200,	12},
25662306a36Sopenharmony_ci	{   38400,	6},
25762306a36Sopenharmony_ci	{   57600,	4},
25862306a36Sopenharmony_ci	{   115200,	2},
25962306a36Sopenharmony_ci	{   230400,	1},
26062306a36Sopenharmony_ci};
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/* Number of outstanding Command Write Urbs */
26362306a36Sopenharmony_cistatic atomic_t CmdUrbs = ATOMIC_INIT(0);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* function prototypes */
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic void edge_close(struct usb_serial_port *port);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void  process_rcvd_data(struct edgeport_serial *edge_serial,
27162306a36Sopenharmony_ci				unsigned char *buffer, __u16 bufferLength);
27262306a36Sopenharmony_cistatic void process_rcvd_status(struct edgeport_serial *edge_serial,
27362306a36Sopenharmony_ci				__u8 byte2, __u8 byte3);
27462306a36Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
27562306a36Sopenharmony_ci		int length);
27662306a36Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr);
27762306a36Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
27862306a36Sopenharmony_ci				__u8 lsr, __u8 data);
27962306a36Sopenharmony_cistatic int  send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command,
28062306a36Sopenharmony_ci				__u8 param);
28162306a36Sopenharmony_cistatic int  calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor);
28262306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty,
28362306a36Sopenharmony_ci				struct edgeport_port *edge_port,
28462306a36Sopenharmony_ci				const struct ktermios *old_termios);
28562306a36Sopenharmony_cistatic int  send_cmd_write_uart_register(struct edgeport_port *edge_port,
28662306a36Sopenharmony_ci				__u8 regNum, __u8 regValue);
28762306a36Sopenharmony_cistatic int  write_cmd_usb(struct edgeport_port *edge_port,
28862306a36Sopenharmony_ci				unsigned char *buffer, int writeLength);
28962306a36Sopenharmony_cistatic void send_more_port_data(struct edgeport_serial *edge_serial,
29062306a36Sopenharmony_ci				struct edgeport_port *edge_port);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
29362306a36Sopenharmony_ci					__u16 length, const __u8 *data);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/* ************************************************************************ */
29662306a36Sopenharmony_ci/* ************************************************************************ */
29762306a36Sopenharmony_ci/* ************************************************************************ */
29862306a36Sopenharmony_ci/* ************************************************************************ */
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/************************************************************************
30162306a36Sopenharmony_ci *									*
30262306a36Sopenharmony_ci * update_edgeport_E2PROM()	Compare current versions of		*
30362306a36Sopenharmony_ci *				Boot ROM and Manufacture 		*
30462306a36Sopenharmony_ci *				Descriptors with versions		*
30562306a36Sopenharmony_ci *				embedded in this driver			*
30662306a36Sopenharmony_ci *									*
30762306a36Sopenharmony_ci ************************************************************************/
30862306a36Sopenharmony_cistatic void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
31162306a36Sopenharmony_ci	__u32 BootCurVer;
31262306a36Sopenharmony_ci	__u32 BootNewVer;
31362306a36Sopenharmony_ci	__u8 BootMajorVersion;
31462306a36Sopenharmony_ci	__u8 BootMinorVersion;
31562306a36Sopenharmony_ci	__u16 BootBuildNumber;
31662306a36Sopenharmony_ci	__u32 Bootaddr;
31762306a36Sopenharmony_ci	const struct ihex_binrec *rec;
31862306a36Sopenharmony_ci	const struct firmware *fw;
31962306a36Sopenharmony_ci	const char *fw_name;
32062306a36Sopenharmony_ci	int response;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	switch (edge_serial->product_info.iDownloadFile) {
32362306a36Sopenharmony_ci	case EDGE_DOWNLOAD_FILE_I930:
32462306a36Sopenharmony_ci		fw_name	= "edgeport/boot.fw";
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case EDGE_DOWNLOAD_FILE_80251:
32762306a36Sopenharmony_ci		fw_name	= "edgeport/boot2.fw";
32862306a36Sopenharmony_ci		break;
32962306a36Sopenharmony_ci	default:
33062306a36Sopenharmony_ci		return;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	response = request_ihex_firmware(&fw, fw_name,
33462306a36Sopenharmony_ci					 &edge_serial->serial->dev->dev);
33562306a36Sopenharmony_ci	if (response) {
33662306a36Sopenharmony_ci		dev_err(dev, "Failed to load image \"%s\" err %d\n",
33762306a36Sopenharmony_ci		       fw_name, response);
33862306a36Sopenharmony_ci		return;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	rec = (const struct ihex_binrec *)fw->data;
34262306a36Sopenharmony_ci	BootMajorVersion = rec->data[0];
34362306a36Sopenharmony_ci	BootMinorVersion = rec->data[1];
34462306a36Sopenharmony_ci	BootBuildNumber = (rec->data[2] << 8) | rec->data[3];
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* Check Boot Image Version */
34762306a36Sopenharmony_ci	BootCurVer = (edge_serial->boot_descriptor.MajorVersion << 24) +
34862306a36Sopenharmony_ci		     (edge_serial->boot_descriptor.MinorVersion << 16) +
34962306a36Sopenharmony_ci		      le16_to_cpu(edge_serial->boot_descriptor.BuildNumber);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	BootNewVer = (BootMajorVersion << 24) +
35262306a36Sopenharmony_ci		     (BootMinorVersion << 16) +
35362306a36Sopenharmony_ci		      BootBuildNumber;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	dev_dbg(dev, "Current Boot Image version %d.%d.%d\n",
35662306a36Sopenharmony_ci	    edge_serial->boot_descriptor.MajorVersion,
35762306a36Sopenharmony_ci	    edge_serial->boot_descriptor.MinorVersion,
35862306a36Sopenharmony_ci	    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (BootNewVer > BootCurVer) {
36262306a36Sopenharmony_ci		dev_dbg(dev, "**Update Boot Image from %d.%d.%d to %d.%d.%d\n",
36362306a36Sopenharmony_ci		    edge_serial->boot_descriptor.MajorVersion,
36462306a36Sopenharmony_ci		    edge_serial->boot_descriptor.MinorVersion,
36562306a36Sopenharmony_ci		    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber),
36662306a36Sopenharmony_ci		    BootMajorVersion, BootMinorVersion, BootBuildNumber);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		dev_dbg(dev, "Downloading new Boot Image\n");
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		for (rec = ihex_next_binrec(rec); rec;
37162306a36Sopenharmony_ci		     rec = ihex_next_binrec(rec)) {
37262306a36Sopenharmony_ci			Bootaddr = be32_to_cpu(rec->addr);
37362306a36Sopenharmony_ci			response = rom_write(edge_serial->serial,
37462306a36Sopenharmony_ci					     Bootaddr >> 16,
37562306a36Sopenharmony_ci					     Bootaddr & 0xFFFF,
37662306a36Sopenharmony_ci					     be16_to_cpu(rec->len),
37762306a36Sopenharmony_ci					     &rec->data[0]);
37862306a36Sopenharmony_ci			if (response < 0) {
37962306a36Sopenharmony_ci				dev_err(&edge_serial->serial->dev->dev,
38062306a36Sopenharmony_ci					"rom_write failed (%x, %x, %d)\n",
38162306a36Sopenharmony_ci					Bootaddr >> 16, Bootaddr & 0xFFFF,
38262306a36Sopenharmony_ci					be16_to_cpu(rec->len));
38362306a36Sopenharmony_ci				break;
38462306a36Sopenharmony_ci			}
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	} else {
38762306a36Sopenharmony_ci		dev_dbg(dev, "Boot Image -- already up to date\n");
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	release_firmware(fw);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void dump_product_info(struct edgeport_serial *edge_serial,
39362306a36Sopenharmony_ci			      struct edgeport_product_info *product_info)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* Dump Product Info structure */
39862306a36Sopenharmony_ci	dev_dbg(dev, "**Product Information:\n");
39962306a36Sopenharmony_ci	dev_dbg(dev, "  ProductId             %x\n", product_info->ProductId);
40062306a36Sopenharmony_ci	dev_dbg(dev, "  NumPorts              %d\n", product_info->NumPorts);
40162306a36Sopenharmony_ci	dev_dbg(dev, "  ProdInfoVer           %d\n", product_info->ProdInfoVer);
40262306a36Sopenharmony_ci	dev_dbg(dev, "  IsServer              %d\n", product_info->IsServer);
40362306a36Sopenharmony_ci	dev_dbg(dev, "  IsRS232               %d\n", product_info->IsRS232);
40462306a36Sopenharmony_ci	dev_dbg(dev, "  IsRS422               %d\n", product_info->IsRS422);
40562306a36Sopenharmony_ci	dev_dbg(dev, "  IsRS485               %d\n", product_info->IsRS485);
40662306a36Sopenharmony_ci	dev_dbg(dev, "  RomSize               %d\n", product_info->RomSize);
40762306a36Sopenharmony_ci	dev_dbg(dev, "  RamSize               %d\n", product_info->RamSize);
40862306a36Sopenharmony_ci	dev_dbg(dev, "  CpuRev                %x\n", product_info->CpuRev);
40962306a36Sopenharmony_ci	dev_dbg(dev, "  BoardRev              %x\n", product_info->BoardRev);
41062306a36Sopenharmony_ci	dev_dbg(dev, "  BootMajorVersion      %d.%d.%d\n",
41162306a36Sopenharmony_ci		product_info->BootMajorVersion,
41262306a36Sopenharmony_ci		product_info->BootMinorVersion,
41362306a36Sopenharmony_ci		le16_to_cpu(product_info->BootBuildNumber));
41462306a36Sopenharmony_ci	dev_dbg(dev, "  FirmwareMajorVersion  %d.%d.%d\n",
41562306a36Sopenharmony_ci		product_info->FirmwareMajorVersion,
41662306a36Sopenharmony_ci		product_info->FirmwareMinorVersion,
41762306a36Sopenharmony_ci		le16_to_cpu(product_info->FirmwareBuildNumber));
41862306a36Sopenharmony_ci	dev_dbg(dev, "  ManufactureDescDate   %d/%d/%d\n",
41962306a36Sopenharmony_ci		product_info->ManufactureDescDate[0],
42062306a36Sopenharmony_ci		product_info->ManufactureDescDate[1],
42162306a36Sopenharmony_ci		product_info->ManufactureDescDate[2]+1900);
42262306a36Sopenharmony_ci	dev_dbg(dev, "  iDownloadFile         0x%x\n",
42362306a36Sopenharmony_ci		product_info->iDownloadFile);
42462306a36Sopenharmony_ci	dev_dbg(dev, "  EpicVer               %d\n", product_info->EpicVer);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic void get_product_info(struct edgeport_serial *edge_serial)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct edgeport_product_info *product_info = &edge_serial->product_info;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	memset(product_info, 0, sizeof(struct edgeport_product_info));
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	product_info->ProductId = (__u16)(le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ~ION_DEVICE_ID_80251_NETCHIP);
43462306a36Sopenharmony_ci	product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts;
43562306a36Sopenharmony_ci	product_info->ProdInfoVer = 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	product_info->RomSize = edge_serial->manuf_descriptor.RomSize;
43862306a36Sopenharmony_ci	product_info->RamSize = edge_serial->manuf_descriptor.RamSize;
43962306a36Sopenharmony_ci	product_info->CpuRev = edge_serial->manuf_descriptor.CpuRev;
44062306a36Sopenharmony_ci	product_info->BoardRev = edge_serial->manuf_descriptor.BoardRev;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	product_info->BootMajorVersion =
44362306a36Sopenharmony_ci				edge_serial->boot_descriptor.MajorVersion;
44462306a36Sopenharmony_ci	product_info->BootMinorVersion =
44562306a36Sopenharmony_ci				edge_serial->boot_descriptor.MinorVersion;
44662306a36Sopenharmony_ci	product_info->BootBuildNumber =
44762306a36Sopenharmony_ci				edge_serial->boot_descriptor.BuildNumber;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	memcpy(product_info->ManufactureDescDate,
45062306a36Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate,
45162306a36Sopenharmony_ci			sizeof(edge_serial->manuf_descriptor.DescDate));
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* check if this is 2nd generation hardware */
45462306a36Sopenharmony_ci	if (le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct)
45562306a36Sopenharmony_ci					    & ION_DEVICE_ID_80251_NETCHIP)
45662306a36Sopenharmony_ci		product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251;
45762306a36Sopenharmony_ci	else
45862306a36Sopenharmony_ci		product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Determine Product type and set appropriate flags */
46162306a36Sopenharmony_ci	switch (DEVICE_ID_FROM_USB_PRODUCT_ID(product_info->ProductId)) {
46262306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_COMPATIBLE:
46362306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4T:
46462306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4:
46562306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_2:
46662306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU:
46762306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_8:
46862306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_421:
46962306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_21:
47062306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_2_DIN:
47162306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4_DIN:
47262306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU:
47362306a36Sopenharmony_ci		product_info->IsRS232 = 1;
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_2I:	/* Edgeport/2 RS422/RS485 */
47762306a36Sopenharmony_ci		product_info->IsRS422 = 1;
47862306a36Sopenharmony_ci		product_info->IsRS485 = 1;
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_8I:	/* Edgeport/4 RS422 */
48262306a36Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4I:	/* Edgeport/4 RS422 */
48362306a36Sopenharmony_ci		product_info->IsRS422 = 1;
48462306a36Sopenharmony_ci		break;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	dump_product_info(edge_serial, product_info);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int get_epic_descriptor(struct edgeport_serial *ep)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	int result;
49362306a36Sopenharmony_ci	struct usb_serial *serial = ep->serial;
49462306a36Sopenharmony_ci	struct edgeport_product_info *product_info = &ep->product_info;
49562306a36Sopenharmony_ci	struct edge_compatibility_descriptor *epic;
49662306a36Sopenharmony_ci	struct edge_compatibility_bits *bits;
49762306a36Sopenharmony_ci	struct device *dev = &serial->dev->dev;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ep->is_epic = 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	epic = kmalloc(sizeof(*epic), GFP_KERNEL);
50262306a36Sopenharmony_ci	if (!epic)
50362306a36Sopenharmony_ci		return -ENOMEM;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
50662306a36Sopenharmony_ci				 USB_REQUEST_ION_GET_EPIC_DESC,
50762306a36Sopenharmony_ci				 0xC0, 0x00, 0x00,
50862306a36Sopenharmony_ci				 epic, sizeof(*epic),
50962306a36Sopenharmony_ci				 300);
51062306a36Sopenharmony_ci	if (result == sizeof(*epic)) {
51162306a36Sopenharmony_ci		ep->is_epic = 1;
51262306a36Sopenharmony_ci		memcpy(&ep->epic_descriptor, epic, sizeof(*epic));
51362306a36Sopenharmony_ci		memset(product_info, 0, sizeof(struct edgeport_product_info));
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		product_info->NumPorts = epic->NumPorts;
51662306a36Sopenharmony_ci		product_info->ProdInfoVer = 0;
51762306a36Sopenharmony_ci		product_info->FirmwareMajorVersion = epic->MajorVersion;
51862306a36Sopenharmony_ci		product_info->FirmwareMinorVersion = epic->MinorVersion;
51962306a36Sopenharmony_ci		product_info->FirmwareBuildNumber = epic->BuildNumber;
52062306a36Sopenharmony_ci		product_info->iDownloadFile = epic->iDownloadFile;
52162306a36Sopenharmony_ci		product_info->EpicVer = epic->EpicVer;
52262306a36Sopenharmony_ci		product_info->Epic = epic->Supports;
52362306a36Sopenharmony_ci		product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE;
52462306a36Sopenharmony_ci		dump_product_info(ep, product_info);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		bits = &ep->epic_descriptor.Supports;
52762306a36Sopenharmony_ci		dev_dbg(dev, "**EPIC descriptor:\n");
52862306a36Sopenharmony_ci		dev_dbg(dev, "  VendEnableSuspend: %s\n", bits->VendEnableSuspend ? "TRUE": "FALSE");
52962306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPOpen         : %s\n", bits->IOSPOpen	? "TRUE": "FALSE");
53062306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPClose        : %s\n", bits->IOSPClose	? "TRUE": "FALSE");
53162306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPChase        : %s\n", bits->IOSPChase	? "TRUE": "FALSE");
53262306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPSetRxFlow    : %s\n", bits->IOSPSetRxFlow	? "TRUE": "FALSE");
53362306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPSetTxFlow    : %s\n", bits->IOSPSetTxFlow	? "TRUE": "FALSE");
53462306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPSetXChar     : %s\n", bits->IOSPSetXChar	? "TRUE": "FALSE");
53562306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPRxCheck      : %s\n", bits->IOSPRxCheck	? "TRUE": "FALSE");
53662306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPSetClrBreak  : %s\n", bits->IOSPSetClrBreak	? "TRUE": "FALSE");
53762306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPWriteMCR     : %s\n", bits->IOSPWriteMCR	? "TRUE": "FALSE");
53862306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPWriteLCR     : %s\n", bits->IOSPWriteLCR	? "TRUE": "FALSE");
53962306a36Sopenharmony_ci		dev_dbg(dev, "  IOSPSetBaudRate  : %s\n", bits->IOSPSetBaudRate	? "TRUE": "FALSE");
54062306a36Sopenharmony_ci		dev_dbg(dev, "  TrueEdgeport     : %s\n", bits->TrueEdgeport	? "TRUE": "FALSE");
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		result = 0;
54362306a36Sopenharmony_ci	} else if (result >= 0) {
54462306a36Sopenharmony_ci		dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n",
54562306a36Sopenharmony_ci			 result);
54662306a36Sopenharmony_ci		result = -EIO;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	kfree(epic);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return result;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci/************************************************************************/
55662306a36Sopenharmony_ci/************************************************************************/
55762306a36Sopenharmony_ci/*            U S B  C A L L B A C K   F U N C T I O N S                */
55862306a36Sopenharmony_ci/*            U S B  C A L L B A C K   F U N C T I O N S                */
55962306a36Sopenharmony_ci/************************************************************************/
56062306a36Sopenharmony_ci/************************************************************************/
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci/*****************************************************************************
56362306a36Sopenharmony_ci * edge_interrupt_callback
56462306a36Sopenharmony_ci *	this is the callback function for when we have received data on the
56562306a36Sopenharmony_ci *	interrupt endpoint.
56662306a36Sopenharmony_ci *****************************************************************************/
56762306a36Sopenharmony_cistatic void edge_interrupt_callback(struct urb *urb)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct edgeport_serial *edge_serial = urb->context;
57062306a36Sopenharmony_ci	struct device *dev;
57162306a36Sopenharmony_ci	struct edgeport_port *edge_port;
57262306a36Sopenharmony_ci	struct usb_serial_port *port;
57362306a36Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
57462306a36Sopenharmony_ci	int length = urb->actual_length;
57562306a36Sopenharmony_ci	unsigned long flags;
57662306a36Sopenharmony_ci	int bytes_avail;
57762306a36Sopenharmony_ci	int position;
57862306a36Sopenharmony_ci	int txCredits;
57962306a36Sopenharmony_ci	int portNumber;
58062306a36Sopenharmony_ci	int result;
58162306a36Sopenharmony_ci	int status = urb->status;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	switch (status) {
58462306a36Sopenharmony_ci	case 0:
58562306a36Sopenharmony_ci		/* success */
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	case -ECONNRESET:
58862306a36Sopenharmony_ci	case -ENOENT:
58962306a36Sopenharmony_ci	case -ESHUTDOWN:
59062306a36Sopenharmony_ci		/* this urb is terminated, clean up */
59162306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
59262306a36Sopenharmony_ci		return;
59362306a36Sopenharmony_ci	default:
59462306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
59562306a36Sopenharmony_ci		goto exit;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	dev = &edge_serial->serial->dev->dev;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* process this interrupt-read even if there are no ports open */
60162306a36Sopenharmony_ci	if (length) {
60262306a36Sopenharmony_ci		usb_serial_debug_data(dev, __func__, length, data);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		if (length > 1) {
60562306a36Sopenharmony_ci			bytes_avail = data[0] | (data[1] << 8);
60662306a36Sopenharmony_ci			if (bytes_avail) {
60762306a36Sopenharmony_ci				spin_lock_irqsave(&edge_serial->es_lock, flags);
60862306a36Sopenharmony_ci				edge_serial->rxBytesAvail += bytes_avail;
60962306a36Sopenharmony_ci				dev_dbg(dev,
61062306a36Sopenharmony_ci					"%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n",
61162306a36Sopenharmony_ci					__func__, bytes_avail,
61262306a36Sopenharmony_ci					edge_serial->rxBytesAvail,
61362306a36Sopenharmony_ci					edge_serial->read_in_progress);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci				if (edge_serial->rxBytesAvail > 0 &&
61662306a36Sopenharmony_ci				    !edge_serial->read_in_progress) {
61762306a36Sopenharmony_ci					dev_dbg(dev, "%s - posting a read\n", __func__);
61862306a36Sopenharmony_ci					edge_serial->read_in_progress = true;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci					/* we have pending bytes on the
62162306a36Sopenharmony_ci					   bulk in pipe, send a request */
62262306a36Sopenharmony_ci					result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
62362306a36Sopenharmony_ci					if (result) {
62462306a36Sopenharmony_ci						dev_err(dev,
62562306a36Sopenharmony_ci							"%s - usb_submit_urb(read bulk) failed with result = %d\n",
62662306a36Sopenharmony_ci							__func__, result);
62762306a36Sopenharmony_ci						edge_serial->read_in_progress = false;
62862306a36Sopenharmony_ci					}
62962306a36Sopenharmony_ci				}
63062306a36Sopenharmony_ci				spin_unlock_irqrestore(&edge_serial->es_lock,
63162306a36Sopenharmony_ci						       flags);
63262306a36Sopenharmony_ci			}
63362306a36Sopenharmony_ci		}
63462306a36Sopenharmony_ci		/* grab the txcredits for the ports if available */
63562306a36Sopenharmony_ci		position = 2;
63662306a36Sopenharmony_ci		portNumber = 0;
63762306a36Sopenharmony_ci		while ((position < length - 1) &&
63862306a36Sopenharmony_ci				(portNumber < edge_serial->serial->num_ports)) {
63962306a36Sopenharmony_ci			txCredits = data[position] | (data[position+1] << 8);
64062306a36Sopenharmony_ci			if (txCredits) {
64162306a36Sopenharmony_ci				port = edge_serial->serial->port[portNumber];
64262306a36Sopenharmony_ci				edge_port = usb_get_serial_port_data(port);
64362306a36Sopenharmony_ci				if (edge_port && edge_port->open) {
64462306a36Sopenharmony_ci					spin_lock_irqsave(&edge_port->ep_lock,
64562306a36Sopenharmony_ci							  flags);
64662306a36Sopenharmony_ci					edge_port->txCredits += txCredits;
64762306a36Sopenharmony_ci					spin_unlock_irqrestore(&edge_port->ep_lock,
64862306a36Sopenharmony_ci							       flags);
64962306a36Sopenharmony_ci					dev_dbg(dev, "%s - txcredits for port%d = %d\n",
65062306a36Sopenharmony_ci						__func__, portNumber,
65162306a36Sopenharmony_ci						edge_port->txCredits);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci					/* tell the tty driver that something
65462306a36Sopenharmony_ci					   has changed */
65562306a36Sopenharmony_ci					tty_port_tty_wakeup(&edge_port->port->port);
65662306a36Sopenharmony_ci					/* Since we have more credit, check
65762306a36Sopenharmony_ci					   if more data can be sent */
65862306a36Sopenharmony_ci					send_more_port_data(edge_serial,
65962306a36Sopenharmony_ci								edge_port);
66062306a36Sopenharmony_ci				}
66162306a36Sopenharmony_ci			}
66262306a36Sopenharmony_ci			position += 2;
66362306a36Sopenharmony_ci			++portNumber;
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ciexit:
66862306a36Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
66962306a36Sopenharmony_ci	if (result)
67062306a36Sopenharmony_ci		dev_err(&urb->dev->dev,
67162306a36Sopenharmony_ci			"%s - Error %d submitting control urb\n",
67262306a36Sopenharmony_ci						__func__, result);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci/*****************************************************************************
67762306a36Sopenharmony_ci * edge_bulk_in_callback
67862306a36Sopenharmony_ci *	this is the callback function for when we have received data on the
67962306a36Sopenharmony_ci *	bulk in endpoint.
68062306a36Sopenharmony_ci *****************************************************************************/
68162306a36Sopenharmony_cistatic void edge_bulk_in_callback(struct urb *urb)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct edgeport_serial	*edge_serial = urb->context;
68462306a36Sopenharmony_ci	struct device *dev;
68562306a36Sopenharmony_ci	unsigned char		*data = urb->transfer_buffer;
68662306a36Sopenharmony_ci	int			retval;
68762306a36Sopenharmony_ci	__u16			raw_data_length;
68862306a36Sopenharmony_ci	int status = urb->status;
68962306a36Sopenharmony_ci	unsigned long flags;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (status) {
69262306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
69362306a36Sopenharmony_ci			__func__, status);
69462306a36Sopenharmony_ci		edge_serial->read_in_progress = false;
69562306a36Sopenharmony_ci		return;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (urb->actual_length == 0) {
69962306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - read bulk callback with no data\n", __func__);
70062306a36Sopenharmony_ci		edge_serial->read_in_progress = false;
70162306a36Sopenharmony_ci		return;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	dev = &edge_serial->serial->dev->dev;
70562306a36Sopenharmony_ci	raw_data_length = urb->actual_length;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	usb_serial_debug_data(dev, __func__, raw_data_length, data);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	spin_lock_irqsave(&edge_serial->es_lock, flags);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* decrement our rxBytes available by the number that we just got */
71262306a36Sopenharmony_ci	edge_serial->rxBytesAvail -= raw_data_length;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	dev_dbg(dev, "%s - Received = %d, rxBytesAvail %d\n", __func__,
71562306a36Sopenharmony_ci		raw_data_length, edge_serial->rxBytesAvail);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	process_rcvd_data(edge_serial, data, urb->actual_length);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* check to see if there's any more data for us to read */
72062306a36Sopenharmony_ci	if (edge_serial->rxBytesAvail > 0) {
72162306a36Sopenharmony_ci		dev_dbg(dev, "%s - posting a read\n", __func__);
72262306a36Sopenharmony_ci		retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
72362306a36Sopenharmony_ci		if (retval) {
72462306a36Sopenharmony_ci			dev_err(dev,
72562306a36Sopenharmony_ci				"%s - usb_submit_urb(read bulk) failed, retval = %d\n",
72662306a36Sopenharmony_ci				__func__, retval);
72762306a36Sopenharmony_ci			edge_serial->read_in_progress = false;
72862306a36Sopenharmony_ci		}
72962306a36Sopenharmony_ci	} else {
73062306a36Sopenharmony_ci		edge_serial->read_in_progress = false;
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	spin_unlock_irqrestore(&edge_serial->es_lock, flags);
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci/*****************************************************************************
73862306a36Sopenharmony_ci * edge_bulk_out_data_callback
73962306a36Sopenharmony_ci *	this is the callback function for when we have finished sending
74062306a36Sopenharmony_ci *	serial data on the bulk out endpoint.
74162306a36Sopenharmony_ci *****************************************************************************/
74262306a36Sopenharmony_cistatic void edge_bulk_out_data_callback(struct urb *urb)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct edgeport_port *edge_port = urb->context;
74562306a36Sopenharmony_ci	int status = urb->status;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (status) {
74862306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev,
74962306a36Sopenharmony_ci			"%s - nonzero write bulk status received: %d\n",
75062306a36Sopenharmony_ci			__func__, status);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if (edge_port->open)
75462306a36Sopenharmony_ci		tty_port_tty_wakeup(&edge_port->port->port);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/* Release the Write URB */
75762306a36Sopenharmony_ci	edge_port->write_in_progress = false;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* Check if more data needs to be sent */
76062306a36Sopenharmony_ci	send_more_port_data((struct edgeport_serial *)
76162306a36Sopenharmony_ci		(usb_get_serial_data(edge_port->port->serial)), edge_port);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/*****************************************************************************
76662306a36Sopenharmony_ci * BulkOutCmdCallback
76762306a36Sopenharmony_ci *	this is the callback function for when we have finished sending a
76862306a36Sopenharmony_ci *	command	on the bulk out endpoint.
76962306a36Sopenharmony_ci *****************************************************************************/
77062306a36Sopenharmony_cistatic void edge_bulk_out_cmd_callback(struct urb *urb)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct edgeport_port *edge_port = urb->context;
77362306a36Sopenharmony_ci	int status = urb->status;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	atomic_dec(&CmdUrbs);
77662306a36Sopenharmony_ci	dev_dbg(&urb->dev->dev, "%s - FREE URB %p (outstanding %d)\n",
77762306a36Sopenharmony_ci		__func__, urb, atomic_read(&CmdUrbs));
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* clean up the transfer buffer */
78162306a36Sopenharmony_ci	kfree(urb->transfer_buffer);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* Free the command urb */
78462306a36Sopenharmony_ci	usb_free_urb(urb);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (status) {
78762306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev,
78862306a36Sopenharmony_ci			"%s - nonzero write bulk status received: %d\n",
78962306a36Sopenharmony_ci			__func__, status);
79062306a36Sopenharmony_ci		return;
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	/* tell the tty driver that something has changed */
79462306a36Sopenharmony_ci	if (edge_port->open)
79562306a36Sopenharmony_ci		tty_port_tty_wakeup(&edge_port->port->port);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* we have completed the command */
79862306a36Sopenharmony_ci	edge_port->commandPending = false;
79962306a36Sopenharmony_ci	wake_up(&edge_port->wait_command);
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci/*****************************************************************************
80462306a36Sopenharmony_ci * Driver tty interface functions
80562306a36Sopenharmony_ci *****************************************************************************/
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci/*****************************************************************************
80862306a36Sopenharmony_ci * SerialOpen
80962306a36Sopenharmony_ci *	this function is called by the tty driver when a port is opened
81062306a36Sopenharmony_ci *	If successful, we return 0
81162306a36Sopenharmony_ci *	Otherwise we return a negative error number.
81262306a36Sopenharmony_ci *****************************************************************************/
81362306a36Sopenharmony_cistatic int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
81662306a36Sopenharmony_ci	struct device *dev = &port->dev;
81762306a36Sopenharmony_ci	struct usb_serial *serial;
81862306a36Sopenharmony_ci	struct edgeport_serial *edge_serial;
81962306a36Sopenharmony_ci	int response;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (edge_port == NULL)
82262306a36Sopenharmony_ci		return -ENODEV;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* see if we've set up our endpoint info yet (can't set it up
82562306a36Sopenharmony_ci	   in edge_startup as the structures were not set up at that time.) */
82662306a36Sopenharmony_ci	serial = port->serial;
82762306a36Sopenharmony_ci	edge_serial = usb_get_serial_data(serial);
82862306a36Sopenharmony_ci	if (edge_serial == NULL)
82962306a36Sopenharmony_ci		return -ENODEV;
83062306a36Sopenharmony_ci	if (edge_serial->interrupt_in_buffer == NULL) {
83162306a36Sopenharmony_ci		struct usb_serial_port *port0 = serial->port[0];
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		/* not set up yet, so do it now */
83462306a36Sopenharmony_ci		edge_serial->interrupt_in_buffer =
83562306a36Sopenharmony_ci					port0->interrupt_in_buffer;
83662306a36Sopenharmony_ci		edge_serial->interrupt_in_endpoint =
83762306a36Sopenharmony_ci					port0->interrupt_in_endpointAddress;
83862306a36Sopenharmony_ci		edge_serial->interrupt_read_urb = port0->interrupt_in_urb;
83962306a36Sopenharmony_ci		edge_serial->bulk_in_buffer = port0->bulk_in_buffer;
84062306a36Sopenharmony_ci		edge_serial->bulk_in_endpoint =
84162306a36Sopenharmony_ci					port0->bulk_in_endpointAddress;
84262306a36Sopenharmony_ci		edge_serial->read_urb = port0->read_urb;
84362306a36Sopenharmony_ci		edge_serial->bulk_out_endpoint =
84462306a36Sopenharmony_ci					port0->bulk_out_endpointAddress;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		/* set up our interrupt urb */
84762306a36Sopenharmony_ci		usb_fill_int_urb(edge_serial->interrupt_read_urb,
84862306a36Sopenharmony_ci		      serial->dev,
84962306a36Sopenharmony_ci		      usb_rcvintpipe(serial->dev,
85062306a36Sopenharmony_ci				port0->interrupt_in_endpointAddress),
85162306a36Sopenharmony_ci		      port0->interrupt_in_buffer,
85262306a36Sopenharmony_ci		      edge_serial->interrupt_read_urb->transfer_buffer_length,
85362306a36Sopenharmony_ci		      edge_interrupt_callback, edge_serial,
85462306a36Sopenharmony_ci		      edge_serial->interrupt_read_urb->interval);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		/* set up our bulk in urb */
85762306a36Sopenharmony_ci		usb_fill_bulk_urb(edge_serial->read_urb, serial->dev,
85862306a36Sopenharmony_ci			usb_rcvbulkpipe(serial->dev,
85962306a36Sopenharmony_ci				port0->bulk_in_endpointAddress),
86062306a36Sopenharmony_ci			port0->bulk_in_buffer,
86162306a36Sopenharmony_ci			edge_serial->read_urb->transfer_buffer_length,
86262306a36Sopenharmony_ci			edge_bulk_in_callback, edge_serial);
86362306a36Sopenharmony_ci		edge_serial->read_in_progress = false;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		/* start interrupt read for this edgeport
86662306a36Sopenharmony_ci		 * this interrupt will continue as long
86762306a36Sopenharmony_ci		 * as the edgeport is connected */
86862306a36Sopenharmony_ci		response = usb_submit_urb(edge_serial->interrupt_read_urb,
86962306a36Sopenharmony_ci								GFP_KERNEL);
87062306a36Sopenharmony_ci		if (response) {
87162306a36Sopenharmony_ci			dev_err(dev, "%s - Error %d submitting control urb\n",
87262306a36Sopenharmony_ci				__func__, response);
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/* initialize our wait queues */
87762306a36Sopenharmony_ci	init_waitqueue_head(&edge_port->wait_open);
87862306a36Sopenharmony_ci	init_waitqueue_head(&edge_port->wait_chase);
87962306a36Sopenharmony_ci	init_waitqueue_head(&edge_port->wait_command);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* initialize our port settings */
88262306a36Sopenharmony_ci	edge_port->txCredits = 0;	/* Can't send any data yet */
88362306a36Sopenharmony_ci	/* Must always set this bit to enable ints! */
88462306a36Sopenharmony_ci	edge_port->shadowMCR = MCR_MASTER_IE;
88562306a36Sopenharmony_ci	edge_port->chaseResponsePending = false;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	/* send a open port command */
88862306a36Sopenharmony_ci	edge_port->openPending = true;
88962306a36Sopenharmony_ci	edge_port->open        = false;
89062306a36Sopenharmony_ci	response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (response < 0) {
89362306a36Sopenharmony_ci		dev_err(dev, "%s - error sending open port command\n", __func__);
89462306a36Sopenharmony_ci		edge_port->openPending = false;
89562306a36Sopenharmony_ci		return -ENODEV;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	/* now wait for the port to be completely opened */
89962306a36Sopenharmony_ci	wait_event_timeout(edge_port->wait_open, !edge_port->openPending,
90062306a36Sopenharmony_ci								OPEN_TIMEOUT);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (!edge_port->open) {
90362306a36Sopenharmony_ci		/* open timed out */
90462306a36Sopenharmony_ci		dev_dbg(dev, "%s - open timeout\n", __func__);
90562306a36Sopenharmony_ci		edge_port->openPending = false;
90662306a36Sopenharmony_ci		return -ENODEV;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* create the txfifo */
91062306a36Sopenharmony_ci	edge_port->txfifo.head	= 0;
91162306a36Sopenharmony_ci	edge_port->txfifo.tail	= 0;
91262306a36Sopenharmony_ci	edge_port->txfifo.count	= 0;
91362306a36Sopenharmony_ci	edge_port->txfifo.size	= edge_port->maxTxCredits;
91462306a36Sopenharmony_ci	edge_port->txfifo.fifo	= kmalloc(edge_port->maxTxCredits, GFP_KERNEL);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (!edge_port->txfifo.fifo) {
91762306a36Sopenharmony_ci		edge_close(port);
91862306a36Sopenharmony_ci		return -ENOMEM;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* Allocate a URB for the write */
92262306a36Sopenharmony_ci	edge_port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
92362306a36Sopenharmony_ci	edge_port->write_in_progress = false;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (!edge_port->write_urb) {
92662306a36Sopenharmony_ci		edge_close(port);
92762306a36Sopenharmony_ci		return -ENOMEM;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	dev_dbg(dev, "%s - Initialize TX fifo to %d bytes\n",
93162306a36Sopenharmony_ci		__func__, edge_port->maxTxCredits);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	return 0;
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/************************************************************************
93862306a36Sopenharmony_ci *
93962306a36Sopenharmony_ci * block_until_chase_response
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci *	This function will block the close until one of the following:
94262306a36Sopenharmony_ci *		1. Response to our Chase comes from Edgeport
94362306a36Sopenharmony_ci *		2. A timeout of 10 seconds without activity has expired
94462306a36Sopenharmony_ci *		   (1K of Edgeport data @ 2400 baud ==> 4 sec to empty)
94562306a36Sopenharmony_ci *
94662306a36Sopenharmony_ci ************************************************************************/
94762306a36Sopenharmony_cistatic void block_until_chase_response(struct edgeport_port *edge_port)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
95062306a36Sopenharmony_ci	DEFINE_WAIT(wait);
95162306a36Sopenharmony_ci	__u16 lastCredits;
95262306a36Sopenharmony_ci	int timeout = 1*HZ;
95362306a36Sopenharmony_ci	int loop = 10;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	while (1) {
95662306a36Sopenharmony_ci		/* Save Last credits */
95762306a36Sopenharmony_ci		lastCredits = edge_port->txCredits;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci		/* Did we get our Chase response */
96062306a36Sopenharmony_ci		if (!edge_port->chaseResponsePending) {
96162306a36Sopenharmony_ci			dev_dbg(dev, "%s - Got Chase Response\n", __func__);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci			/* did we get all of our credit back? */
96462306a36Sopenharmony_ci			if (edge_port->txCredits == edge_port->maxTxCredits) {
96562306a36Sopenharmony_ci				dev_dbg(dev, "%s - Got all credits\n", __func__);
96662306a36Sopenharmony_ci				return;
96762306a36Sopenharmony_ci			}
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/* Block the thread for a while */
97162306a36Sopenharmony_ci		prepare_to_wait(&edge_port->wait_chase, &wait,
97262306a36Sopenharmony_ci						TASK_UNINTERRUPTIBLE);
97362306a36Sopenharmony_ci		schedule_timeout(timeout);
97462306a36Sopenharmony_ci		finish_wait(&edge_port->wait_chase, &wait);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		if (lastCredits == edge_port->txCredits) {
97762306a36Sopenharmony_ci			/* No activity.. count down. */
97862306a36Sopenharmony_ci			loop--;
97962306a36Sopenharmony_ci			if (loop == 0) {
98062306a36Sopenharmony_ci				edge_port->chaseResponsePending = false;
98162306a36Sopenharmony_ci				dev_dbg(dev, "%s - Chase TIMEOUT\n", __func__);
98262306a36Sopenharmony_ci				return;
98362306a36Sopenharmony_ci			}
98462306a36Sopenharmony_ci		} else {
98562306a36Sopenharmony_ci			/* Reset timeout value back to 10 seconds */
98662306a36Sopenharmony_ci			dev_dbg(dev, "%s - Last %d, Current %d\n", __func__,
98762306a36Sopenharmony_ci					lastCredits, edge_port->txCredits);
98862306a36Sopenharmony_ci			loop = 10;
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci/************************************************************************
99562306a36Sopenharmony_ci *
99662306a36Sopenharmony_ci * block_until_tx_empty
99762306a36Sopenharmony_ci *
99862306a36Sopenharmony_ci *	This function will block the close until one of the following:
99962306a36Sopenharmony_ci *		1. TX count are 0
100062306a36Sopenharmony_ci *		2. The edgeport has stopped
100162306a36Sopenharmony_ci *		3. A timeout of 3 seconds without activity has expired
100262306a36Sopenharmony_ci *
100362306a36Sopenharmony_ci ************************************************************************/
100462306a36Sopenharmony_cistatic void block_until_tx_empty(struct edgeport_port *edge_port)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
100762306a36Sopenharmony_ci	DEFINE_WAIT(wait);
100862306a36Sopenharmony_ci	struct TxFifo *fifo = &edge_port->txfifo;
100962306a36Sopenharmony_ci	__u32 lastCount;
101062306a36Sopenharmony_ci	int timeout = HZ/10;
101162306a36Sopenharmony_ci	int loop = 30;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	while (1) {
101462306a36Sopenharmony_ci		/* Save Last count */
101562306a36Sopenharmony_ci		lastCount = fifo->count;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		/* Is the Edgeport Buffer empty? */
101862306a36Sopenharmony_ci		if (lastCount == 0) {
101962306a36Sopenharmony_ci			dev_dbg(dev, "%s - TX Buffer Empty\n", __func__);
102062306a36Sopenharmony_ci			return;
102162306a36Sopenharmony_ci		}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		/* Block the thread for a while */
102462306a36Sopenharmony_ci		prepare_to_wait(&edge_port->wait_chase, &wait,
102562306a36Sopenharmony_ci						TASK_UNINTERRUPTIBLE);
102662306a36Sopenharmony_ci		schedule_timeout(timeout);
102762306a36Sopenharmony_ci		finish_wait(&edge_port->wait_chase, &wait);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci		dev_dbg(dev, "%s wait\n", __func__);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		if (lastCount == fifo->count) {
103262306a36Sopenharmony_ci			/* No activity.. count down. */
103362306a36Sopenharmony_ci			loop--;
103462306a36Sopenharmony_ci			if (loop == 0) {
103562306a36Sopenharmony_ci				dev_dbg(dev, "%s - TIMEOUT\n", __func__);
103662306a36Sopenharmony_ci				return;
103762306a36Sopenharmony_ci			}
103862306a36Sopenharmony_ci		} else {
103962306a36Sopenharmony_ci			/* Reset timeout value back to seconds */
104062306a36Sopenharmony_ci			loop = 30;
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci/*****************************************************************************
104762306a36Sopenharmony_ci * edge_close
104862306a36Sopenharmony_ci *	this function is called by the tty driver when a port is closed
104962306a36Sopenharmony_ci *****************************************************************************/
105062306a36Sopenharmony_cistatic void edge_close(struct usb_serial_port *port)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	struct edgeport_serial *edge_serial;
105362306a36Sopenharmony_ci	struct edgeport_port *edge_port;
105462306a36Sopenharmony_ci	int status;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	edge_serial = usb_get_serial_data(port->serial);
105762306a36Sopenharmony_ci	edge_port = usb_get_serial_port_data(port);
105862306a36Sopenharmony_ci	if (edge_serial == NULL || edge_port == NULL)
105962306a36Sopenharmony_ci		return;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	/* block until tx is empty */
106262306a36Sopenharmony_ci	block_until_tx_empty(edge_port);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	edge_port->closePending = true;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (!edge_serial->is_epic ||
106762306a36Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPChase) {
106862306a36Sopenharmony_ci		/* flush and chase */
106962306a36Sopenharmony_ci		edge_port->chaseResponsePending = true;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
107262306a36Sopenharmony_ci		status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
107362306a36Sopenharmony_ci		if (status == 0)
107462306a36Sopenharmony_ci			/* block until chase finished */
107562306a36Sopenharmony_ci			block_until_chase_response(edge_port);
107662306a36Sopenharmony_ci		else
107762306a36Sopenharmony_ci			edge_port->chaseResponsePending = false;
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (!edge_serial->is_epic ||
108162306a36Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPClose) {
108262306a36Sopenharmony_ci	       /* close the port */
108362306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__);
108462306a36Sopenharmony_ci		send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0);
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	/* port->close = true; */
108862306a36Sopenharmony_ci	edge_port->closePending = false;
108962306a36Sopenharmony_ci	edge_port->open = false;
109062306a36Sopenharmony_ci	edge_port->openPending = false;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	usb_kill_urb(edge_port->write_urb);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (edge_port->write_urb) {
109562306a36Sopenharmony_ci		/* if this urb had a transfer buffer already
109662306a36Sopenharmony_ci				(old transfer) free it */
109762306a36Sopenharmony_ci		kfree(edge_port->write_urb->transfer_buffer);
109862306a36Sopenharmony_ci		usb_free_urb(edge_port->write_urb);
109962306a36Sopenharmony_ci		edge_port->write_urb = NULL;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci	kfree(edge_port->txfifo.fifo);
110262306a36Sopenharmony_ci	edge_port->txfifo.fifo = NULL;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci/*****************************************************************************
110662306a36Sopenharmony_ci * SerialWrite
110762306a36Sopenharmony_ci *	this function is called by the tty driver when data should be written
110862306a36Sopenharmony_ci *	to the port.
110962306a36Sopenharmony_ci *	If successful, we return the number of bytes written, otherwise we
111062306a36Sopenharmony_ci *	return a negative error number.
111162306a36Sopenharmony_ci *****************************************************************************/
111262306a36Sopenharmony_cistatic int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
111362306a36Sopenharmony_ci					const unsigned char *data, int count)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
111662306a36Sopenharmony_ci	struct TxFifo *fifo;
111762306a36Sopenharmony_ci	int copySize;
111862306a36Sopenharmony_ci	int bytesleft;
111962306a36Sopenharmony_ci	int firsthalf;
112062306a36Sopenharmony_ci	int secondhalf;
112162306a36Sopenharmony_ci	unsigned long flags;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (edge_port == NULL)
112462306a36Sopenharmony_ci		return -ENODEV;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* get a pointer to the Tx fifo */
112762306a36Sopenharmony_ci	fifo = &edge_port->txfifo;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* calculate number of bytes to put in fifo */
113262306a36Sopenharmony_ci	copySize = min((unsigned int)count,
113362306a36Sopenharmony_ci				(edge_port->txCredits - fifo->count));
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s of %d byte(s) Fifo room  %d -- will copy %d bytes\n",
113662306a36Sopenharmony_ci		__func__, count, edge_port->txCredits - fifo->count, copySize);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/* catch writes of 0 bytes which the tty driver likes to give us,
113962306a36Sopenharmony_ci	   and when txCredits is empty */
114062306a36Sopenharmony_ci	if (copySize == 0) {
114162306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - copySize = Zero\n", __func__);
114262306a36Sopenharmony_ci		goto finish_write;
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* queue the data
114662306a36Sopenharmony_ci	 * since we can never overflow the buffer we do not have to check for a
114762306a36Sopenharmony_ci	 * full condition
114862306a36Sopenharmony_ci	 *
114962306a36Sopenharmony_ci	 * the copy is done is two parts -- first fill to the end of the buffer
115062306a36Sopenharmony_ci	 * then copy the reset from the start of the buffer
115162306a36Sopenharmony_ci	 */
115262306a36Sopenharmony_ci	bytesleft = fifo->size - fifo->head;
115362306a36Sopenharmony_ci	firsthalf = min(bytesleft, copySize);
115462306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - copy %d bytes of %d into fifo \n", __func__,
115562306a36Sopenharmony_ci		firsthalf, bytesleft);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* now copy our data */
115862306a36Sopenharmony_ci	memcpy(&fifo->fifo[fifo->head], data, firsthalf);
115962306a36Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, firsthalf, &fifo->fifo[fifo->head]);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* update the index and size */
116262306a36Sopenharmony_ci	fifo->head  += firsthalf;
116362306a36Sopenharmony_ci	fifo->count += firsthalf;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* wrap the index */
116662306a36Sopenharmony_ci	if (fifo->head == fifo->size)
116762306a36Sopenharmony_ci		fifo->head = 0;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	secondhalf = copySize-firsthalf;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (secondhalf) {
117262306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - copy rest of data %d\n", __func__, secondhalf);
117362306a36Sopenharmony_ci		memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
117462306a36Sopenharmony_ci		usb_serial_debug_data(&port->dev, __func__, secondhalf, &fifo->fifo[fifo->head]);
117562306a36Sopenharmony_ci		/* update the index and size */
117662306a36Sopenharmony_ci		fifo->count += secondhalf;
117762306a36Sopenharmony_ci		fifo->head  += secondhalf;
117862306a36Sopenharmony_ci		/* No need to check for wrap since we can not get to end of
117962306a36Sopenharmony_ci		 * the fifo in this part
118062306a36Sopenharmony_ci		 */
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cifinish_write:
118462306a36Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	send_more_port_data((struct edgeport_serial *)
118762306a36Sopenharmony_ci			usb_get_serial_data(port->serial), edge_port);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s wrote %d byte(s) TxCredits %d, Fifo %d\n",
119062306a36Sopenharmony_ci		__func__, copySize, edge_port->txCredits, fifo->count);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	return copySize;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci/************************************************************************
119762306a36Sopenharmony_ci *
119862306a36Sopenharmony_ci * send_more_port_data()
119962306a36Sopenharmony_ci *
120062306a36Sopenharmony_ci *	This routine attempts to write additional UART transmit data
120162306a36Sopenharmony_ci *	to a port over the USB bulk pipe. It is called (1) when new
120262306a36Sopenharmony_ci *	data has been written to a port's TxBuffer from higher layers
120362306a36Sopenharmony_ci *	(2) when the peripheral sends us additional TxCredits indicating
120462306a36Sopenharmony_ci *	that it can accept more	Tx data for a given port; and (3) when
120562306a36Sopenharmony_ci *	a bulk write completes successfully and we want to see if we
120662306a36Sopenharmony_ci *	can transmit more.
120762306a36Sopenharmony_ci *
120862306a36Sopenharmony_ci ************************************************************************/
120962306a36Sopenharmony_cistatic void send_more_port_data(struct edgeport_serial *edge_serial,
121062306a36Sopenharmony_ci					struct edgeport_port *edge_port)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct TxFifo	*fifo = &edge_port->txfifo;
121362306a36Sopenharmony_ci	struct device	*dev = &edge_port->port->dev;
121462306a36Sopenharmony_ci	struct urb	*urb;
121562306a36Sopenharmony_ci	unsigned char	*buffer;
121662306a36Sopenharmony_ci	int		status;
121762306a36Sopenharmony_ci	int		count;
121862306a36Sopenharmony_ci	int		bytesleft;
121962306a36Sopenharmony_ci	int		firsthalf;
122062306a36Sopenharmony_ci	int		secondhalf;
122162306a36Sopenharmony_ci	unsigned long	flags;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (edge_port->write_in_progress ||
122662306a36Sopenharmony_ci	    !edge_port->open             ||
122762306a36Sopenharmony_ci	    (fifo->count == 0)) {
122862306a36Sopenharmony_ci		dev_dbg(dev, "%s EXIT - fifo %d, PendingWrite = %d\n",
122962306a36Sopenharmony_ci			__func__, fifo->count, edge_port->write_in_progress);
123062306a36Sopenharmony_ci		goto exit_send;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	/* since the amount of data in the fifo will always fit into the
123462306a36Sopenharmony_ci	 * edgeport buffer we do not need to check the write length
123562306a36Sopenharmony_ci	 *
123662306a36Sopenharmony_ci	 * Do we have enough credits for this port to make it worthwhile
123762306a36Sopenharmony_ci	 * to bother queueing a write. If it's too small, say a few bytes,
123862306a36Sopenharmony_ci	 * it's better to wait for more credits so we can do a larger write.
123962306a36Sopenharmony_ci	 */
124062306a36Sopenharmony_ci	if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits, EDGE_FW_BULK_MAX_PACKET_SIZE)) {
124162306a36Sopenharmony_ci		dev_dbg(dev, "%s Not enough credit - fifo %d TxCredit %d\n",
124262306a36Sopenharmony_ci			__func__, fifo->count, edge_port->txCredits);
124362306a36Sopenharmony_ci		goto exit_send;
124462306a36Sopenharmony_ci	}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	/* lock this write */
124762306a36Sopenharmony_ci	edge_port->write_in_progress = true;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	/* get a pointer to the write_urb */
125062306a36Sopenharmony_ci	urb = edge_port->write_urb;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	/* make sure transfer buffer is freed */
125362306a36Sopenharmony_ci	kfree(urb->transfer_buffer);
125462306a36Sopenharmony_ci	urb->transfer_buffer = NULL;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/* build the data header for the buffer and port that we are about
125762306a36Sopenharmony_ci	   to send out */
125862306a36Sopenharmony_ci	count = fifo->count;
125962306a36Sopenharmony_ci	buffer = kmalloc(count+2, GFP_ATOMIC);
126062306a36Sopenharmony_ci	if (!buffer) {
126162306a36Sopenharmony_ci		edge_port->write_in_progress = false;
126262306a36Sopenharmony_ci		goto exit_send;
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci	buffer[0] = IOSP_BUILD_DATA_HDR1(edge_port->port->port_number, count);
126562306a36Sopenharmony_ci	buffer[1] = IOSP_BUILD_DATA_HDR2(edge_port->port->port_number, count);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/* now copy our data */
126862306a36Sopenharmony_ci	bytesleft =  fifo->size - fifo->tail;
126962306a36Sopenharmony_ci	firsthalf = min(bytesleft, count);
127062306a36Sopenharmony_ci	memcpy(&buffer[2], &fifo->fifo[fifo->tail], firsthalf);
127162306a36Sopenharmony_ci	fifo->tail  += firsthalf;
127262306a36Sopenharmony_ci	fifo->count -= firsthalf;
127362306a36Sopenharmony_ci	if (fifo->tail == fifo->size)
127462306a36Sopenharmony_ci		fifo->tail = 0;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	secondhalf = count-firsthalf;
127762306a36Sopenharmony_ci	if (secondhalf) {
127862306a36Sopenharmony_ci		memcpy(&buffer[2+firsthalf], &fifo->fifo[fifo->tail],
127962306a36Sopenharmony_ci								secondhalf);
128062306a36Sopenharmony_ci		fifo->tail  += secondhalf;
128162306a36Sopenharmony_ci		fifo->count -= secondhalf;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	if (count)
128562306a36Sopenharmony_ci		usb_serial_debug_data(&edge_port->port->dev, __func__, count, &buffer[2]);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	/* fill up the urb with all of our data and submit it */
128862306a36Sopenharmony_ci	usb_fill_bulk_urb(urb, edge_serial->serial->dev,
128962306a36Sopenharmony_ci			usb_sndbulkpipe(edge_serial->serial->dev,
129062306a36Sopenharmony_ci					edge_serial->bulk_out_endpoint),
129162306a36Sopenharmony_ci			buffer, count+2,
129262306a36Sopenharmony_ci			edge_bulk_out_data_callback, edge_port);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/* decrement the number of credits we have by the number we just sent */
129562306a36Sopenharmony_ci	edge_port->txCredits -= count;
129662306a36Sopenharmony_ci	edge_port->port->icount.tx += count;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
129962306a36Sopenharmony_ci	if (status) {
130062306a36Sopenharmony_ci		/* something went wrong */
130162306a36Sopenharmony_ci		dev_err_console(edge_port->port,
130262306a36Sopenharmony_ci			"%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n",
130362306a36Sopenharmony_ci				__func__, status);
130462306a36Sopenharmony_ci		edge_port->write_in_progress = false;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci		/* revert the credits as something bad happened. */
130762306a36Sopenharmony_ci		edge_port->txCredits += count;
130862306a36Sopenharmony_ci		edge_port->port->icount.tx -= count;
130962306a36Sopenharmony_ci	}
131062306a36Sopenharmony_ci	dev_dbg(dev, "%s wrote %d byte(s) TxCredit %d, Fifo %d\n",
131162306a36Sopenharmony_ci		__func__, count, edge_port->txCredits, fifo->count);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ciexit_send:
131462306a36Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci/*****************************************************************************
131962306a36Sopenharmony_ci * edge_write_room
132062306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to know how
132162306a36Sopenharmony_ci *	many bytes of data we can accept for a specific port.
132262306a36Sopenharmony_ci *****************************************************************************/
132362306a36Sopenharmony_cistatic unsigned int edge_write_room(struct tty_struct *tty)
132462306a36Sopenharmony_ci{
132562306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
132662306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
132762306a36Sopenharmony_ci	unsigned int room;
132862306a36Sopenharmony_ci	unsigned long flags;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* total of both buffers is still txCredit */
133162306a36Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
133262306a36Sopenharmony_ci	room = edge_port->txCredits - edge_port->txfifo.count;
133362306a36Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
133662306a36Sopenharmony_ci	return room;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci/*****************************************************************************
134162306a36Sopenharmony_ci * edge_chars_in_buffer
134262306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to know how
134362306a36Sopenharmony_ci *	many bytes of data we currently have outstanding in the port (data that
134462306a36Sopenharmony_ci *	has been written, but hasn't made it out the port yet)
134562306a36Sopenharmony_ci *****************************************************************************/
134662306a36Sopenharmony_cistatic unsigned int edge_chars_in_buffer(struct tty_struct *tty)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
134962306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
135062306a36Sopenharmony_ci	unsigned int num_chars;
135162306a36Sopenharmony_ci	unsigned long flags;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
135462306a36Sopenharmony_ci	num_chars = edge_port->maxTxCredits - edge_port->txCredits +
135562306a36Sopenharmony_ci						edge_port->txfifo.count;
135662306a36Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
135762306a36Sopenharmony_ci	if (num_chars) {
135862306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - returns %u\n", __func__, num_chars);
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	return num_chars;
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci/*****************************************************************************
136662306a36Sopenharmony_ci * SerialThrottle
136762306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to stop the data
136862306a36Sopenharmony_ci *	being read from the port.
136962306a36Sopenharmony_ci *****************************************************************************/
137062306a36Sopenharmony_cistatic void edge_throttle(struct tty_struct *tty)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
137362306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
137462306a36Sopenharmony_ci	int status;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (edge_port == NULL)
137762306a36Sopenharmony_ci		return;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (!edge_port->open) {
138062306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
138162306a36Sopenharmony_ci		return;
138262306a36Sopenharmony_ci	}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, send the stop character */
138562306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
138662306a36Sopenharmony_ci		unsigned char stop_char = STOP_CHAR(tty);
138762306a36Sopenharmony_ci		status = edge_write(tty, port, &stop_char, 1);
138862306a36Sopenharmony_ci		if (status <= 0)
138962306a36Sopenharmony_ci			return;
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
139362306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
139462306a36Sopenharmony_ci		edge_port->shadowMCR &= ~MCR_RTS;
139562306a36Sopenharmony_ci		status = send_cmd_write_uart_register(edge_port, MCR,
139662306a36Sopenharmony_ci							edge_port->shadowMCR);
139762306a36Sopenharmony_ci		if (status != 0)
139862306a36Sopenharmony_ci			return;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci/*****************************************************************************
140462306a36Sopenharmony_ci * edge_unthrottle
140562306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to resume the
140662306a36Sopenharmony_ci *	data being read from the port (called after SerialThrottle is called)
140762306a36Sopenharmony_ci *****************************************************************************/
140862306a36Sopenharmony_cistatic void edge_unthrottle(struct tty_struct *tty)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
141162306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
141262306a36Sopenharmony_ci	int status;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	if (edge_port == NULL)
141562306a36Sopenharmony_ci		return;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (!edge_port->open) {
141862306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
141962306a36Sopenharmony_ci		return;
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, send the start character */
142362306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
142462306a36Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
142562306a36Sopenharmony_ci		status = edge_write(tty, port, &start_char, 1);
142662306a36Sopenharmony_ci		if (status <= 0)
142762306a36Sopenharmony_ci			return;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
143062306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
143162306a36Sopenharmony_ci		edge_port->shadowMCR |= MCR_RTS;
143262306a36Sopenharmony_ci		send_cmd_write_uart_register(edge_port, MCR,
143362306a36Sopenharmony_ci						edge_port->shadowMCR);
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci/*****************************************************************************
143962306a36Sopenharmony_ci * SerialSetTermios
144062306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to change
144162306a36Sopenharmony_ci * the termios structure
144262306a36Sopenharmony_ci *****************************************************************************/
144362306a36Sopenharmony_cistatic void edge_set_termios(struct tty_struct *tty,
144462306a36Sopenharmony_ci			     struct usb_serial_port *port,
144562306a36Sopenharmony_ci			     const struct ktermios *old_termios)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	if (edge_port == NULL)
145062306a36Sopenharmony_ci		return;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (!edge_port->open) {
145362306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
145462306a36Sopenharmony_ci		return;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	/* change the port settings to the new ones specified */
145862306a36Sopenharmony_ci	change_port_settings(tty, edge_port, old_termios);
145962306a36Sopenharmony_ci}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci/*****************************************************************************
146362306a36Sopenharmony_ci * get_lsr_info - get line status register info
146462306a36Sopenharmony_ci *
146562306a36Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically
146662306a36Sopenharmony_ci * 	    is emptied.  On bus types like RS485, the transmitter must
146762306a36Sopenharmony_ci * 	    release the bus after transmitting. This must be done when
146862306a36Sopenharmony_ci * 	    the transmit shift register is empty, not be done when the
146962306a36Sopenharmony_ci * 	    transmit holding register is empty.  This functionality
147062306a36Sopenharmony_ci * 	    allows an RS485 driver to be written in user space.
147162306a36Sopenharmony_ci *****************************************************************************/
147262306a36Sopenharmony_cistatic int get_lsr_info(struct edgeport_port *edge_port,
147362306a36Sopenharmony_ci						unsigned int __user *value)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	unsigned int result = 0;
147662306a36Sopenharmony_ci	unsigned long flags;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
147962306a36Sopenharmony_ci	if (edge_port->maxTxCredits == edge_port->txCredits &&
148062306a36Sopenharmony_ci	    edge_port->txfifo.count == 0) {
148162306a36Sopenharmony_ci		dev_dbg(&edge_port->port->dev, "%s -- Empty\n", __func__);
148262306a36Sopenharmony_ci		result = TIOCSER_TEMT;
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	if (copy_to_user(value, &result, sizeof(int)))
148762306a36Sopenharmony_ci		return -EFAULT;
148862306a36Sopenharmony_ci	return 0;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_cistatic int edge_tiocmset(struct tty_struct *tty,
149262306a36Sopenharmony_ci					unsigned int set, unsigned int clear)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
149562306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
149662306a36Sopenharmony_ci	unsigned int mcr;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	mcr = edge_port->shadowMCR;
149962306a36Sopenharmony_ci	if (set & TIOCM_RTS)
150062306a36Sopenharmony_ci		mcr |= MCR_RTS;
150162306a36Sopenharmony_ci	if (set & TIOCM_DTR)
150262306a36Sopenharmony_ci		mcr |= MCR_DTR;
150362306a36Sopenharmony_ci	if (set & TIOCM_LOOP)
150462306a36Sopenharmony_ci		mcr |= MCR_LOOPBACK;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (clear & TIOCM_RTS)
150762306a36Sopenharmony_ci		mcr &= ~MCR_RTS;
150862306a36Sopenharmony_ci	if (clear & TIOCM_DTR)
150962306a36Sopenharmony_ci		mcr &= ~MCR_DTR;
151062306a36Sopenharmony_ci	if (clear & TIOCM_LOOP)
151162306a36Sopenharmony_ci		mcr &= ~MCR_LOOPBACK;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	edge_port->shadowMCR = mcr;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	return 0;
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic int edge_tiocmget(struct tty_struct *tty)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
152362306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
152462306a36Sopenharmony_ci	unsigned int result = 0;
152562306a36Sopenharmony_ci	unsigned int msr;
152662306a36Sopenharmony_ci	unsigned int mcr;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	msr = edge_port->shadowMSR;
152962306a36Sopenharmony_ci	mcr = edge_port->shadowMCR;
153062306a36Sopenharmony_ci	result = ((mcr & MCR_DTR)	? TIOCM_DTR: 0)	  /* 0x002 */
153162306a36Sopenharmony_ci		  | ((mcr & MCR_RTS)	? TIOCM_RTS: 0)   /* 0x004 */
153262306a36Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_CTS)	? TIOCM_CTS: 0)   /* 0x020 */
153362306a36Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_CD)	? TIOCM_CAR: 0)   /* 0x040 */
153462306a36Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_RI)	? TIOCM_RI:  0)   /* 0x080 */
153562306a36Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_DSR)	? TIOCM_DSR: 0);  /* 0x100 */
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	return result;
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci/*****************************************************************************
154162306a36Sopenharmony_ci * SerialIoctl
154262306a36Sopenharmony_ci *	this function handles any ioctl calls to the driver
154362306a36Sopenharmony_ci *****************************************************************************/
154462306a36Sopenharmony_cistatic int edge_ioctl(struct tty_struct *tty,
154562306a36Sopenharmony_ci					unsigned int cmd, unsigned long arg)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
154862306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	switch (cmd) {
155162306a36Sopenharmony_ci	case TIOCSERGETLSR:
155262306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
155362306a36Sopenharmony_ci		return get_lsr_info(edge_port, (unsigned int __user *) arg);
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci	return -ENOIOCTLCMD;
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci/*****************************************************************************
156062306a36Sopenharmony_ci * SerialBreak
156162306a36Sopenharmony_ci *	this function sends a break to the port
156262306a36Sopenharmony_ci *****************************************************************************/
156362306a36Sopenharmony_cistatic int edge_break(struct tty_struct *tty, int break_state)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
156662306a36Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
156762306a36Sopenharmony_ci	struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
156862306a36Sopenharmony_ci	int status = 0;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	if (!edge_serial->is_epic ||
157162306a36Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPChase) {
157262306a36Sopenharmony_ci		/* flush and chase */
157362306a36Sopenharmony_ci		edge_port->chaseResponsePending = true;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
157662306a36Sopenharmony_ci		status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
157762306a36Sopenharmony_ci		if (status == 0) {
157862306a36Sopenharmony_ci			/* block until chase finished */
157962306a36Sopenharmony_ci			block_until_chase_response(edge_port);
158062306a36Sopenharmony_ci		} else {
158162306a36Sopenharmony_ci			edge_port->chaseResponsePending = false;
158262306a36Sopenharmony_ci		}
158362306a36Sopenharmony_ci	}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	if (!edge_serial->is_epic ||
158662306a36Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPSetClrBreak) {
158762306a36Sopenharmony_ci		if (break_state == -1) {
158862306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__);
158962306a36Sopenharmony_ci			status = send_iosp_ext_cmd(edge_port,
159062306a36Sopenharmony_ci						IOSP_CMD_SET_BREAK, 0);
159162306a36Sopenharmony_ci		} else {
159262306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLEAR_BREAK\n", __func__);
159362306a36Sopenharmony_ci			status = send_iosp_ext_cmd(edge_port,
159462306a36Sopenharmony_ci						IOSP_CMD_CLEAR_BREAK, 0);
159562306a36Sopenharmony_ci		}
159662306a36Sopenharmony_ci		if (status)
159762306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - error sending break set/clear command.\n",
159862306a36Sopenharmony_ci				__func__);
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	return status;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci/*****************************************************************************
160662306a36Sopenharmony_ci * process_rcvd_data
160762306a36Sopenharmony_ci *	this function handles the data received on the bulk in pipe.
160862306a36Sopenharmony_ci *****************************************************************************/
160962306a36Sopenharmony_cistatic void process_rcvd_data(struct edgeport_serial *edge_serial,
161062306a36Sopenharmony_ci				unsigned char *buffer, __u16 bufferLength)
161162306a36Sopenharmony_ci{
161262306a36Sopenharmony_ci	struct usb_serial *serial = edge_serial->serial;
161362306a36Sopenharmony_ci	struct device *dev = &serial->dev->dev;
161462306a36Sopenharmony_ci	struct usb_serial_port *port;
161562306a36Sopenharmony_ci	struct edgeport_port *edge_port;
161662306a36Sopenharmony_ci	__u16 lastBufferLength;
161762306a36Sopenharmony_ci	__u16 rxLen;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	lastBufferLength = bufferLength + 1;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	while (bufferLength > 0) {
162262306a36Sopenharmony_ci		/* failsafe incase we get a message that we don't understand */
162362306a36Sopenharmony_ci		if (lastBufferLength == bufferLength) {
162462306a36Sopenharmony_ci			dev_dbg(dev, "%s - stuck in loop, exiting it.\n", __func__);
162562306a36Sopenharmony_ci			break;
162662306a36Sopenharmony_ci		}
162762306a36Sopenharmony_ci		lastBufferLength = bufferLength;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci		switch (edge_serial->rxState) {
163062306a36Sopenharmony_ci		case EXPECT_HDR1:
163162306a36Sopenharmony_ci			edge_serial->rxHeader1 = *buffer;
163262306a36Sopenharmony_ci			++buffer;
163362306a36Sopenharmony_ci			--bufferLength;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci			if (bufferLength == 0) {
163662306a36Sopenharmony_ci				edge_serial->rxState = EXPECT_HDR2;
163762306a36Sopenharmony_ci				break;
163862306a36Sopenharmony_ci			}
163962306a36Sopenharmony_ci			fallthrough;
164062306a36Sopenharmony_ci		case EXPECT_HDR2:
164162306a36Sopenharmony_ci			edge_serial->rxHeader2 = *buffer;
164262306a36Sopenharmony_ci			++buffer;
164362306a36Sopenharmony_ci			--bufferLength;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci			dev_dbg(dev, "%s - Hdr1=%02X Hdr2=%02X\n", __func__,
164662306a36Sopenharmony_ci				edge_serial->rxHeader1, edge_serial->rxHeader2);
164762306a36Sopenharmony_ci			/* Process depending on whether this header is
164862306a36Sopenharmony_ci			 * data or status */
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci			if (IS_CMD_STAT_HDR(edge_serial->rxHeader1)) {
165162306a36Sopenharmony_ci				/* Decode this status header and go to
165262306a36Sopenharmony_ci				 * EXPECT_HDR1 (if we can process the status
165362306a36Sopenharmony_ci				 * with only 2 bytes), or go to EXPECT_HDR3 to
165462306a36Sopenharmony_ci				 * get the third byte. */
165562306a36Sopenharmony_ci				edge_serial->rxPort =
165662306a36Sopenharmony_ci				    IOSP_GET_HDR_PORT(edge_serial->rxHeader1);
165762306a36Sopenharmony_ci				edge_serial->rxStatusCode =
165862306a36Sopenharmony_ci				    IOSP_GET_STATUS_CODE(
165962306a36Sopenharmony_ci						edge_serial->rxHeader1);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci				if (!IOSP_STATUS_IS_2BYTE(
166262306a36Sopenharmony_ci						edge_serial->rxStatusCode)) {
166362306a36Sopenharmony_ci					/* This status needs additional bytes.
166462306a36Sopenharmony_ci					 * Save what we have and then wait for
166562306a36Sopenharmony_ci					 * more data.
166662306a36Sopenharmony_ci					 */
166762306a36Sopenharmony_ci					edge_serial->rxStatusParam
166862306a36Sopenharmony_ci						= edge_serial->rxHeader2;
166962306a36Sopenharmony_ci					edge_serial->rxState = EXPECT_HDR3;
167062306a36Sopenharmony_ci					break;
167162306a36Sopenharmony_ci				}
167262306a36Sopenharmony_ci				/* We have all the header bytes, process the
167362306a36Sopenharmony_ci				   status now */
167462306a36Sopenharmony_ci				process_rcvd_status(edge_serial,
167562306a36Sopenharmony_ci						edge_serial->rxHeader2, 0);
167662306a36Sopenharmony_ci				edge_serial->rxState = EXPECT_HDR1;
167762306a36Sopenharmony_ci				break;
167862306a36Sopenharmony_ci			}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci			edge_serial->rxPort = IOSP_GET_HDR_PORT(edge_serial->rxHeader1);
168162306a36Sopenharmony_ci			edge_serial->rxBytesRemaining = IOSP_GET_HDR_DATA_LEN(edge_serial->rxHeader1,
168262306a36Sopenharmony_ci									      edge_serial->rxHeader2);
168362306a36Sopenharmony_ci			dev_dbg(dev, "%s - Data for Port %u Len %u\n", __func__,
168462306a36Sopenharmony_ci				edge_serial->rxPort,
168562306a36Sopenharmony_ci				edge_serial->rxBytesRemaining);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci			if (bufferLength == 0) {
168862306a36Sopenharmony_ci				edge_serial->rxState = EXPECT_DATA;
168962306a36Sopenharmony_ci				break;
169062306a36Sopenharmony_ci			}
169162306a36Sopenharmony_ci			fallthrough;
169262306a36Sopenharmony_ci		case EXPECT_DATA: /* Expect data */
169362306a36Sopenharmony_ci			if (bufferLength < edge_serial->rxBytesRemaining) {
169462306a36Sopenharmony_ci				rxLen = bufferLength;
169562306a36Sopenharmony_ci				/* Expect data to start next buffer */
169662306a36Sopenharmony_ci				edge_serial->rxState = EXPECT_DATA;
169762306a36Sopenharmony_ci			} else {
169862306a36Sopenharmony_ci				/* BufLen >= RxBytesRemaining */
169962306a36Sopenharmony_ci				rxLen = edge_serial->rxBytesRemaining;
170062306a36Sopenharmony_ci				/* Start another header next time */
170162306a36Sopenharmony_ci				edge_serial->rxState = EXPECT_HDR1;
170262306a36Sopenharmony_ci			}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci			bufferLength -= rxLen;
170562306a36Sopenharmony_ci			edge_serial->rxBytesRemaining -= rxLen;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci			/* spit this data back into the tty driver if this
170862306a36Sopenharmony_ci			   port is open */
170962306a36Sopenharmony_ci			if (rxLen && edge_serial->rxPort < serial->num_ports) {
171062306a36Sopenharmony_ci				port = serial->port[edge_serial->rxPort];
171162306a36Sopenharmony_ci				edge_port = usb_get_serial_port_data(port);
171262306a36Sopenharmony_ci				if (edge_port && edge_port->open) {
171362306a36Sopenharmony_ci					dev_dbg(dev, "%s - Sending %d bytes to TTY for port %d\n",
171462306a36Sopenharmony_ci						__func__, rxLen,
171562306a36Sopenharmony_ci						edge_serial->rxPort);
171662306a36Sopenharmony_ci					edge_tty_recv(edge_port->port, buffer,
171762306a36Sopenharmony_ci							rxLen);
171862306a36Sopenharmony_ci					edge_port->port->icount.rx += rxLen;
171962306a36Sopenharmony_ci				}
172062306a36Sopenharmony_ci			}
172162306a36Sopenharmony_ci			buffer += rxLen;
172262306a36Sopenharmony_ci			break;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci		case EXPECT_HDR3:	/* Expect 3rd byte of status header */
172562306a36Sopenharmony_ci			edge_serial->rxHeader3 = *buffer;
172662306a36Sopenharmony_ci			++buffer;
172762306a36Sopenharmony_ci			--bufferLength;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci			/* We have all the header bytes, process the
173062306a36Sopenharmony_ci			   status now */
173162306a36Sopenharmony_ci			process_rcvd_status(edge_serial,
173262306a36Sopenharmony_ci				edge_serial->rxStatusParam,
173362306a36Sopenharmony_ci				edge_serial->rxHeader3);
173462306a36Sopenharmony_ci			edge_serial->rxState = EXPECT_HDR1;
173562306a36Sopenharmony_ci			break;
173662306a36Sopenharmony_ci		}
173762306a36Sopenharmony_ci	}
173862306a36Sopenharmony_ci}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci/*****************************************************************************
174262306a36Sopenharmony_ci * process_rcvd_status
174362306a36Sopenharmony_ci *	this function handles the any status messages received on the
174462306a36Sopenharmony_ci *	bulk in pipe.
174562306a36Sopenharmony_ci *****************************************************************************/
174662306a36Sopenharmony_cistatic void process_rcvd_status(struct edgeport_serial *edge_serial,
174762306a36Sopenharmony_ci						__u8 byte2, __u8 byte3)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	struct usb_serial_port *port;
175062306a36Sopenharmony_ci	struct edgeport_port *edge_port;
175162306a36Sopenharmony_ci	struct tty_struct *tty;
175262306a36Sopenharmony_ci	struct device *dev;
175362306a36Sopenharmony_ci	__u8 code = edge_serial->rxStatusCode;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	/* switch the port pointer to the one being currently talked about */
175662306a36Sopenharmony_ci	if (edge_serial->rxPort >= edge_serial->serial->num_ports)
175762306a36Sopenharmony_ci		return;
175862306a36Sopenharmony_ci	port = edge_serial->serial->port[edge_serial->rxPort];
175962306a36Sopenharmony_ci	edge_port = usb_get_serial_port_data(port);
176062306a36Sopenharmony_ci	if (edge_port == NULL) {
176162306a36Sopenharmony_ci		dev_err(&edge_serial->serial->dev->dev,
176262306a36Sopenharmony_ci			"%s - edge_port == NULL for port %d\n",
176362306a36Sopenharmony_ci					__func__, edge_serial->rxPort);
176462306a36Sopenharmony_ci		return;
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci	dev = &port->dev;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	if (code == IOSP_EXT_STATUS) {
176962306a36Sopenharmony_ci		switch (byte2) {
177062306a36Sopenharmony_ci		case IOSP_EXT_STATUS_CHASE_RSP:
177162306a36Sopenharmony_ci			/* we want to do EXT status regardless of port
177262306a36Sopenharmony_ci			 * open/closed */
177362306a36Sopenharmony_ci			dev_dbg(dev, "%s - Port %u EXT CHASE_RSP Data = %02x\n",
177462306a36Sopenharmony_ci				__func__, edge_serial->rxPort, byte3);
177562306a36Sopenharmony_ci			/* Currently, the only EXT_STATUS is Chase, so process
177662306a36Sopenharmony_ci			 * here instead of one more call to one more subroutine
177762306a36Sopenharmony_ci			 * If/when more EXT_STATUS, there'll be more work to do
177862306a36Sopenharmony_ci			 * Also, we currently clear flag and close the port
177962306a36Sopenharmony_ci			 * regardless of content of above's Byte3.
178062306a36Sopenharmony_ci			 * We could choose to do something else when Byte3 says
178162306a36Sopenharmony_ci			 * Timeout on Chase from Edgeport, like wait longer in
178262306a36Sopenharmony_ci			 * block_until_chase_response, but for now we don't.
178362306a36Sopenharmony_ci			 */
178462306a36Sopenharmony_ci			edge_port->chaseResponsePending = false;
178562306a36Sopenharmony_ci			wake_up(&edge_port->wait_chase);
178662306a36Sopenharmony_ci			return;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci		case IOSP_EXT_STATUS_RX_CHECK_RSP:
178962306a36Sopenharmony_ci			dev_dbg(dev, "%s ========== Port %u CHECK_RSP Sequence = %02x =============\n",
179062306a36Sopenharmony_ci				__func__, edge_serial->rxPort, byte3);
179162306a36Sopenharmony_ci			/* Port->RxCheckRsp = true; */
179262306a36Sopenharmony_ci			return;
179362306a36Sopenharmony_ci		}
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	if (code == IOSP_STATUS_OPEN_RSP) {
179762306a36Sopenharmony_ci		edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3);
179862306a36Sopenharmony_ci		edge_port->maxTxCredits = edge_port->txCredits;
179962306a36Sopenharmony_ci		dev_dbg(dev, "%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d\n",
180062306a36Sopenharmony_ci			__func__, edge_serial->rxPort, byte2, edge_port->txCredits);
180162306a36Sopenharmony_ci		handle_new_msr(edge_port, byte2);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci		/* send the current line settings to the port so we are
180462306a36Sopenharmony_ci		   in sync with any further termios calls */
180562306a36Sopenharmony_ci		tty = tty_port_tty_get(&edge_port->port->port);
180662306a36Sopenharmony_ci		if (tty) {
180762306a36Sopenharmony_ci			change_port_settings(tty,
180862306a36Sopenharmony_ci				edge_port, &tty->termios);
180962306a36Sopenharmony_ci			tty_kref_put(tty);
181062306a36Sopenharmony_ci		}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci		/* we have completed the open */
181362306a36Sopenharmony_ci		edge_port->openPending = false;
181462306a36Sopenharmony_ci		edge_port->open = true;
181562306a36Sopenharmony_ci		wake_up(&edge_port->wait_open);
181662306a36Sopenharmony_ci		return;
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	/* If port is closed, silently discard all rcvd status. We can
182062306a36Sopenharmony_ci	 * have cases where buffered status is received AFTER the close
182162306a36Sopenharmony_ci	 * port command is sent to the Edgeport.
182262306a36Sopenharmony_ci	 */
182362306a36Sopenharmony_ci	if (!edge_port->open || edge_port->closePending)
182462306a36Sopenharmony_ci		return;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	switch (code) {
182762306a36Sopenharmony_ci	/* Not currently sent by Edgeport */
182862306a36Sopenharmony_ci	case IOSP_STATUS_LSR:
182962306a36Sopenharmony_ci		dev_dbg(dev, "%s - Port %u LSR Status = %02x\n",
183062306a36Sopenharmony_ci			__func__, edge_serial->rxPort, byte2);
183162306a36Sopenharmony_ci		handle_new_lsr(edge_port, false, byte2, 0);
183262306a36Sopenharmony_ci		break;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	case IOSP_STATUS_LSR_DATA:
183562306a36Sopenharmony_ci		dev_dbg(dev, "%s - Port %u LSR Status = %02x, Data = %02x\n",
183662306a36Sopenharmony_ci			__func__, edge_serial->rxPort, byte2, byte3);
183762306a36Sopenharmony_ci		/* byte2 is LSR Register */
183862306a36Sopenharmony_ci		/* byte3 is broken data byte */
183962306a36Sopenharmony_ci		handle_new_lsr(edge_port, true, byte2, byte3);
184062306a36Sopenharmony_ci		break;
184162306a36Sopenharmony_ci	/*
184262306a36Sopenharmony_ci	 *	case IOSP_EXT_4_STATUS:
184362306a36Sopenharmony_ci	 *		dev_dbg(dev, "%s - Port %u LSR Status = %02x Data = %02x\n",
184462306a36Sopenharmony_ci	 *			__func__, edge_serial->rxPort, byte2, byte3);
184562306a36Sopenharmony_ci	 *		break;
184662306a36Sopenharmony_ci	 */
184762306a36Sopenharmony_ci	case IOSP_STATUS_MSR:
184862306a36Sopenharmony_ci		dev_dbg(dev, "%s - Port %u MSR Status = %02x\n",
184962306a36Sopenharmony_ci			__func__, edge_serial->rxPort, byte2);
185062306a36Sopenharmony_ci		/*
185162306a36Sopenharmony_ci		 * Process this new modem status and generate appropriate
185262306a36Sopenharmony_ci		 * events, etc, based on the new status. This routine
185362306a36Sopenharmony_ci		 * also saves the MSR in Port->ShadowMsr.
185462306a36Sopenharmony_ci		 */
185562306a36Sopenharmony_ci		handle_new_msr(edge_port, byte2);
185662306a36Sopenharmony_ci		break;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	default:
185962306a36Sopenharmony_ci		dev_dbg(dev, "%s - Unrecognized IOSP status code %u\n", __func__, code);
186062306a36Sopenharmony_ci		break;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci/*****************************************************************************
186662306a36Sopenharmony_ci * edge_tty_recv
186762306a36Sopenharmony_ci *	this function passes data on to the tty flip buffer
186862306a36Sopenharmony_ci *****************************************************************************/
186962306a36Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
187062306a36Sopenharmony_ci		int length)
187162306a36Sopenharmony_ci{
187262306a36Sopenharmony_ci	int cnt;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	cnt = tty_insert_flip_string(&port->port, data, length);
187562306a36Sopenharmony_ci	if (cnt < length) {
187662306a36Sopenharmony_ci		dev_err(&port->dev, "%s - dropping data, %d bytes lost\n",
187762306a36Sopenharmony_ci				__func__, length - cnt);
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci	data += cnt;
188062306a36Sopenharmony_ci	length -= cnt;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	tty_flip_buffer_push(&port->port);
188362306a36Sopenharmony_ci}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci/*****************************************************************************
188762306a36Sopenharmony_ci * handle_new_msr
188862306a36Sopenharmony_ci *	this function handles any change to the msr register for a port.
188962306a36Sopenharmony_ci *****************************************************************************/
189062306a36Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	struct  async_icount *icount;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
189562306a36Sopenharmony_ci			EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
189662306a36Sopenharmony_ci		icount = &edge_port->port->icount;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci		/* update input line counters */
189962306a36Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_CTS)
190062306a36Sopenharmony_ci			icount->cts++;
190162306a36Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_DSR)
190262306a36Sopenharmony_ci			icount->dsr++;
190362306a36Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_CD)
190462306a36Sopenharmony_ci			icount->dcd++;
190562306a36Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_RI)
190662306a36Sopenharmony_ci			icount->rng++;
190762306a36Sopenharmony_ci		wake_up_interruptible(&edge_port->port->port.delta_msr_wait);
190862306a36Sopenharmony_ci	}
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	/* Save the new modem status */
191162306a36Sopenharmony_ci	edge_port->shadowMSR = newMsr & 0xf0;
191262306a36Sopenharmony_ci}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci/*****************************************************************************
191662306a36Sopenharmony_ci * handle_new_lsr
191762306a36Sopenharmony_ci *	this function handles any change to the lsr register for a port.
191862306a36Sopenharmony_ci *****************************************************************************/
191962306a36Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
192062306a36Sopenharmony_ci							__u8 lsr, __u8 data)
192162306a36Sopenharmony_ci{
192262306a36Sopenharmony_ci	__u8 newLsr = (__u8) (lsr & (__u8)
192362306a36Sopenharmony_ci		(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK));
192462306a36Sopenharmony_ci	struct async_icount *icount;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	edge_port->shadowLSR = lsr;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (newLsr & LSR_BREAK) {
192962306a36Sopenharmony_ci		/*
193062306a36Sopenharmony_ci		 * Parity and Framing errors only count if they
193162306a36Sopenharmony_ci		 * occur exclusive of a break being
193262306a36Sopenharmony_ci		 * received.
193362306a36Sopenharmony_ci		 */
193462306a36Sopenharmony_ci		newLsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
193562306a36Sopenharmony_ci	}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	/* Place LSR data byte into Rx buffer */
193862306a36Sopenharmony_ci	if (lsrData)
193962306a36Sopenharmony_ci		edge_tty_recv(edge_port->port, &data, 1);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/* update input line counters */
194262306a36Sopenharmony_ci	icount = &edge_port->port->icount;
194362306a36Sopenharmony_ci	if (newLsr & LSR_BREAK)
194462306a36Sopenharmony_ci		icount->brk++;
194562306a36Sopenharmony_ci	if (newLsr & LSR_OVER_ERR)
194662306a36Sopenharmony_ci		icount->overrun++;
194762306a36Sopenharmony_ci	if (newLsr & LSR_PAR_ERR)
194862306a36Sopenharmony_ci		icount->parity++;
194962306a36Sopenharmony_ci	if (newLsr & LSR_FRM_ERR)
195062306a36Sopenharmony_ci		icount->frame++;
195162306a36Sopenharmony_ci}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci/****************************************************************************
195562306a36Sopenharmony_ci * sram_write
195662306a36Sopenharmony_ci *	writes a number of bytes to the Edgeport device's sram starting at the
195762306a36Sopenharmony_ci *	given address.
195862306a36Sopenharmony_ci *	If successful returns the number of bytes written, otherwise it returns
195962306a36Sopenharmony_ci *	a negative error number of the problem.
196062306a36Sopenharmony_ci ****************************************************************************/
196162306a36Sopenharmony_cistatic int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
196262306a36Sopenharmony_ci					__u16 length, const __u8 *data)
196362306a36Sopenharmony_ci{
196462306a36Sopenharmony_ci	int result;
196562306a36Sopenharmony_ci	__u16 current_length;
196662306a36Sopenharmony_ci	unsigned char *transfer_buffer;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	dev_dbg(&serial->dev->dev, "%s - %x, %x, %d\n", __func__, extAddr, addr, length);
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	transfer_buffer =  kmalloc(64, GFP_KERNEL);
197162306a36Sopenharmony_ci	if (!transfer_buffer)
197262306a36Sopenharmony_ci		return -ENOMEM;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	/* need to split these writes up into 64 byte chunks */
197562306a36Sopenharmony_ci	result = 0;
197662306a36Sopenharmony_ci	while (length > 0) {
197762306a36Sopenharmony_ci		if (length > 64)
197862306a36Sopenharmony_ci			current_length = 64;
197962306a36Sopenharmony_ci		else
198062306a36Sopenharmony_ci			current_length = length;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci/*		dev_dbg(&serial->dev->dev, "%s - writing %x, %x, %d\n", __func__, extAddr, addr, current_length); */
198362306a36Sopenharmony_ci		memcpy(transfer_buffer, data, current_length);
198462306a36Sopenharmony_ci		result = usb_control_msg(serial->dev,
198562306a36Sopenharmony_ci					usb_sndctrlpipe(serial->dev, 0),
198662306a36Sopenharmony_ci					USB_REQUEST_ION_WRITE_RAM,
198762306a36Sopenharmony_ci					0x40, addr, extAddr, transfer_buffer,
198862306a36Sopenharmony_ci					current_length, 300);
198962306a36Sopenharmony_ci		if (result < 0)
199062306a36Sopenharmony_ci			break;
199162306a36Sopenharmony_ci		length -= current_length;
199262306a36Sopenharmony_ci		addr += current_length;
199362306a36Sopenharmony_ci		data += current_length;
199462306a36Sopenharmony_ci	}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	kfree(transfer_buffer);
199762306a36Sopenharmony_ci	return result;
199862306a36Sopenharmony_ci}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci/****************************************************************************
200262306a36Sopenharmony_ci * rom_write
200362306a36Sopenharmony_ci *	writes a number of bytes to the Edgeport device's ROM starting at the
200462306a36Sopenharmony_ci *	given address.
200562306a36Sopenharmony_ci *	If successful returns the number of bytes written, otherwise it returns
200662306a36Sopenharmony_ci *	a negative error number of the problem.
200762306a36Sopenharmony_ci ****************************************************************************/
200862306a36Sopenharmony_cistatic int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
200962306a36Sopenharmony_ci					__u16 length, const __u8 *data)
201062306a36Sopenharmony_ci{
201162306a36Sopenharmony_ci	int result;
201262306a36Sopenharmony_ci	__u16 current_length;
201362306a36Sopenharmony_ci	unsigned char *transfer_buffer;
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	transfer_buffer =  kmalloc(64, GFP_KERNEL);
201662306a36Sopenharmony_ci	if (!transfer_buffer)
201762306a36Sopenharmony_ci		return -ENOMEM;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	/* need to split these writes up into 64 byte chunks */
202062306a36Sopenharmony_ci	result = 0;
202162306a36Sopenharmony_ci	while (length > 0) {
202262306a36Sopenharmony_ci		if (length > 64)
202362306a36Sopenharmony_ci			current_length = 64;
202462306a36Sopenharmony_ci		else
202562306a36Sopenharmony_ci			current_length = length;
202662306a36Sopenharmony_ci		memcpy(transfer_buffer, data, current_length);
202762306a36Sopenharmony_ci		result = usb_control_msg(serial->dev,
202862306a36Sopenharmony_ci					usb_sndctrlpipe(serial->dev, 0),
202962306a36Sopenharmony_ci					USB_REQUEST_ION_WRITE_ROM, 0x40,
203062306a36Sopenharmony_ci					addr, extAddr,
203162306a36Sopenharmony_ci					transfer_buffer, current_length, 300);
203262306a36Sopenharmony_ci		if (result < 0)
203362306a36Sopenharmony_ci			break;
203462306a36Sopenharmony_ci		length -= current_length;
203562306a36Sopenharmony_ci		addr += current_length;
203662306a36Sopenharmony_ci		data += current_length;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	kfree(transfer_buffer);
204062306a36Sopenharmony_ci	return result;
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci/****************************************************************************
204562306a36Sopenharmony_ci * rom_read
204662306a36Sopenharmony_ci *	reads a number of bytes from the Edgeport device starting at the given
204762306a36Sopenharmony_ci *	address.
204862306a36Sopenharmony_ci *	Returns zero on success or a negative error number.
204962306a36Sopenharmony_ci ****************************************************************************/
205062306a36Sopenharmony_cistatic int rom_read(struct usb_serial *serial, __u16 extAddr,
205162306a36Sopenharmony_ci					__u16 addr, __u16 length, __u8 *data)
205262306a36Sopenharmony_ci{
205362306a36Sopenharmony_ci	int result;
205462306a36Sopenharmony_ci	__u16 current_length;
205562306a36Sopenharmony_ci	unsigned char *transfer_buffer;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	transfer_buffer =  kmalloc(64, GFP_KERNEL);
205862306a36Sopenharmony_ci	if (!transfer_buffer)
205962306a36Sopenharmony_ci		return -ENOMEM;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	/* need to split these reads up into 64 byte chunks */
206262306a36Sopenharmony_ci	result = 0;
206362306a36Sopenharmony_ci	while (length > 0) {
206462306a36Sopenharmony_ci		if (length > 64)
206562306a36Sopenharmony_ci			current_length = 64;
206662306a36Sopenharmony_ci		else
206762306a36Sopenharmony_ci			current_length = length;
206862306a36Sopenharmony_ci		result = usb_control_msg(serial->dev,
206962306a36Sopenharmony_ci					usb_rcvctrlpipe(serial->dev, 0),
207062306a36Sopenharmony_ci					USB_REQUEST_ION_READ_ROM,
207162306a36Sopenharmony_ci					0xC0, addr, extAddr, transfer_buffer,
207262306a36Sopenharmony_ci					current_length, 300);
207362306a36Sopenharmony_ci		if (result < current_length) {
207462306a36Sopenharmony_ci			if (result >= 0)
207562306a36Sopenharmony_ci				result = -EIO;
207662306a36Sopenharmony_ci			break;
207762306a36Sopenharmony_ci		}
207862306a36Sopenharmony_ci		memcpy(data, transfer_buffer, current_length);
207962306a36Sopenharmony_ci		length -= current_length;
208062306a36Sopenharmony_ci		addr += current_length;
208162306a36Sopenharmony_ci		data += current_length;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci		result = 0;
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	kfree(transfer_buffer);
208762306a36Sopenharmony_ci	return result;
208862306a36Sopenharmony_ci}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci/****************************************************************************
209262306a36Sopenharmony_ci * send_iosp_ext_cmd
209362306a36Sopenharmony_ci *	Is used to send a IOSP message to the Edgeport device
209462306a36Sopenharmony_ci ****************************************************************************/
209562306a36Sopenharmony_cistatic int send_iosp_ext_cmd(struct edgeport_port *edge_port,
209662306a36Sopenharmony_ci						__u8 command, __u8 param)
209762306a36Sopenharmony_ci{
209862306a36Sopenharmony_ci	unsigned char   *buffer;
209962306a36Sopenharmony_ci	unsigned char   *currentCommand;
210062306a36Sopenharmony_ci	int             length = 0;
210162306a36Sopenharmony_ci	int             status = 0;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	buffer = kmalloc(10, GFP_ATOMIC);
210462306a36Sopenharmony_ci	if (!buffer)
210562306a36Sopenharmony_ci		return -ENOMEM;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	currentCommand = buffer;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	MAKE_CMD_EXT_CMD(&currentCommand, &length, edge_port->port->port_number,
211062306a36Sopenharmony_ci			 command, param);
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	status = write_cmd_usb(edge_port, buffer, length);
211362306a36Sopenharmony_ci	if (status) {
211462306a36Sopenharmony_ci		/* something bad happened, let's free up the memory */
211562306a36Sopenharmony_ci		kfree(buffer);
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	return status;
211962306a36Sopenharmony_ci}
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci/*****************************************************************************
212362306a36Sopenharmony_ci * write_cmd_usb
212462306a36Sopenharmony_ci *	this function writes the given buffer out to the bulk write endpoint.
212562306a36Sopenharmony_ci *****************************************************************************/
212662306a36Sopenharmony_cistatic int write_cmd_usb(struct edgeport_port *edge_port,
212762306a36Sopenharmony_ci					unsigned char *buffer, int length)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	struct edgeport_serial *edge_serial =
213062306a36Sopenharmony_ci				usb_get_serial_data(edge_port->port->serial);
213162306a36Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
213262306a36Sopenharmony_ci	int status = 0;
213362306a36Sopenharmony_ci	struct urb *urb;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	usb_serial_debug_data(dev, __func__, length, buffer);
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	/* Allocate our next urb */
213862306a36Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_ATOMIC);
213962306a36Sopenharmony_ci	if (!urb)
214062306a36Sopenharmony_ci		return -ENOMEM;
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	atomic_inc(&CmdUrbs);
214362306a36Sopenharmony_ci	dev_dbg(dev, "%s - ALLOCATE URB %p (outstanding %d)\n",
214462306a36Sopenharmony_ci		__func__, urb, atomic_read(&CmdUrbs));
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	usb_fill_bulk_urb(urb, edge_serial->serial->dev,
214762306a36Sopenharmony_ci			usb_sndbulkpipe(edge_serial->serial->dev,
214862306a36Sopenharmony_ci					edge_serial->bulk_out_endpoint),
214962306a36Sopenharmony_ci			buffer, length, edge_bulk_out_cmd_callback, edge_port);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	edge_port->commandPending = true;
215262306a36Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	if (status) {
215562306a36Sopenharmony_ci		/* something went wrong */
215662306a36Sopenharmony_ci		dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
215762306a36Sopenharmony_ci			__func__, status);
215862306a36Sopenharmony_ci		usb_free_urb(urb);
215962306a36Sopenharmony_ci		atomic_dec(&CmdUrbs);
216062306a36Sopenharmony_ci		return status;
216162306a36Sopenharmony_ci	}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci#if 0
216462306a36Sopenharmony_ci	wait_event(&edge_port->wait_command, !edge_port->commandPending);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	if (edge_port->commandPending) {
216762306a36Sopenharmony_ci		/* command timed out */
216862306a36Sopenharmony_ci		dev_dbg(dev, "%s - command timed out\n", __func__);
216962306a36Sopenharmony_ci		status = -EINVAL;
217062306a36Sopenharmony_ci	}
217162306a36Sopenharmony_ci#endif
217262306a36Sopenharmony_ci	return status;
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci/*****************************************************************************
217762306a36Sopenharmony_ci * send_cmd_write_baud_rate
217862306a36Sopenharmony_ci *	this function sends the proper command to change the baud rate of the
217962306a36Sopenharmony_ci *	specified port.
218062306a36Sopenharmony_ci *****************************************************************************/
218162306a36Sopenharmony_cistatic int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
218262306a36Sopenharmony_ci								int baudRate)
218362306a36Sopenharmony_ci{
218462306a36Sopenharmony_ci	struct edgeport_serial *edge_serial =
218562306a36Sopenharmony_ci				usb_get_serial_data(edge_port->port->serial);
218662306a36Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
218762306a36Sopenharmony_ci	unsigned char *cmdBuffer;
218862306a36Sopenharmony_ci	unsigned char *currCmd;
218962306a36Sopenharmony_ci	int cmdLen = 0;
219062306a36Sopenharmony_ci	int divisor;
219162306a36Sopenharmony_ci	int status;
219262306a36Sopenharmony_ci	u32 number = edge_port->port->port_number;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	if (edge_serial->is_epic &&
219562306a36Sopenharmony_ci	    !edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) {
219662306a36Sopenharmony_ci		dev_dbg(dev, "SendCmdWriteBaudRate - NOT Setting baud rate for port, baud = %d\n",
219762306a36Sopenharmony_ci			baudRate);
219862306a36Sopenharmony_ci		return 0;
219962306a36Sopenharmony_ci	}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	dev_dbg(dev, "%s - baud = %d\n", __func__, baudRate);
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	status = calc_baud_rate_divisor(dev, baudRate, &divisor);
220462306a36Sopenharmony_ci	if (status) {
220562306a36Sopenharmony_ci		dev_err(dev, "%s - bad baud rate\n", __func__);
220662306a36Sopenharmony_ci		return status;
220762306a36Sopenharmony_ci	}
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	/* Alloc memory for the string of commands. */
221062306a36Sopenharmony_ci	cmdBuffer =  kmalloc(0x100, GFP_ATOMIC);
221162306a36Sopenharmony_ci	if (!cmdBuffer)
221262306a36Sopenharmony_ci		return -ENOMEM;
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	currCmd = cmdBuffer;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	/* Enable access to divisor latch */
221762306a36Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, LCR_DL_ENABLE);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	/* Write the divisor itself */
222062306a36Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLL, LOW8(divisor));
222162306a36Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLM, HIGH8(divisor));
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	/* Restore original value to disable access to divisor latch */
222462306a36Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR,
222562306a36Sopenharmony_ci						edge_port->shadowLCR);
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
222862306a36Sopenharmony_ci	if (status) {
222962306a36Sopenharmony_ci		/* something bad happened, let's free up the memory */
223062306a36Sopenharmony_ci		kfree(cmdBuffer);
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	return status;
223462306a36Sopenharmony_ci}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci/*****************************************************************************
223862306a36Sopenharmony_ci * calc_baud_rate_divisor
223962306a36Sopenharmony_ci *	this function calculates the proper baud rate divisor for the specified
224062306a36Sopenharmony_ci *	baud rate.
224162306a36Sopenharmony_ci *****************************************************************************/
224262306a36Sopenharmony_cistatic int calc_baud_rate_divisor(struct device *dev, int baudrate, int *divisor)
224362306a36Sopenharmony_ci{
224462306a36Sopenharmony_ci	int i;
224562306a36Sopenharmony_ci	__u16 custom;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
224862306a36Sopenharmony_ci		if (divisor_table[i].BaudRate == baudrate) {
224962306a36Sopenharmony_ci			*divisor = divisor_table[i].Divisor;
225062306a36Sopenharmony_ci			return 0;
225162306a36Sopenharmony_ci		}
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	/* We have tried all of the standard baud rates
225562306a36Sopenharmony_ci	 * lets try to calculate the divisor for this baud rate
225662306a36Sopenharmony_ci	 * Make sure the baud rate is reasonable */
225762306a36Sopenharmony_ci	if (baudrate > 50 && baudrate < 230400) {
225862306a36Sopenharmony_ci		/* get divisor */
225962306a36Sopenharmony_ci		custom = (__u16)((230400L + baudrate/2) / baudrate);
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci		*divisor = custom;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci		dev_dbg(dev, "%s - Baud %d = %d\n", __func__, baudrate, custom);
226462306a36Sopenharmony_ci		return 0;
226562306a36Sopenharmony_ci	}
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	return -1;
226862306a36Sopenharmony_ci}
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci/*****************************************************************************
227262306a36Sopenharmony_ci * send_cmd_write_uart_register
227362306a36Sopenharmony_ci *  this function builds up a uart register message and sends to the device.
227462306a36Sopenharmony_ci *****************************************************************************/
227562306a36Sopenharmony_cistatic int send_cmd_write_uart_register(struct edgeport_port *edge_port,
227662306a36Sopenharmony_ci						__u8 regNum, __u8 regValue)
227762306a36Sopenharmony_ci{
227862306a36Sopenharmony_ci	struct edgeport_serial *edge_serial =
227962306a36Sopenharmony_ci				usb_get_serial_data(edge_port->port->serial);
228062306a36Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
228162306a36Sopenharmony_ci	unsigned char *cmdBuffer;
228262306a36Sopenharmony_ci	unsigned char *currCmd;
228362306a36Sopenharmony_ci	unsigned long cmdLen = 0;
228462306a36Sopenharmony_ci	int status;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	dev_dbg(dev, "%s - write to %s register 0x%02x\n",
228762306a36Sopenharmony_ci		(regNum == MCR) ? "MCR" : "LCR", __func__, regValue);
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	if (edge_serial->is_epic &&
229062306a36Sopenharmony_ci	    !edge_serial->epic_descriptor.Supports.IOSPWriteMCR &&
229162306a36Sopenharmony_ci	    regNum == MCR) {
229262306a36Sopenharmony_ci		dev_dbg(dev, "SendCmdWriteUartReg - Not writing to MCR Register\n");
229362306a36Sopenharmony_ci		return 0;
229462306a36Sopenharmony_ci	}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	if (edge_serial->is_epic &&
229762306a36Sopenharmony_ci	    !edge_serial->epic_descriptor.Supports.IOSPWriteLCR &&
229862306a36Sopenharmony_ci	    regNum == LCR) {
229962306a36Sopenharmony_ci		dev_dbg(dev, "SendCmdWriteUartReg - Not writing to LCR Register\n");
230062306a36Sopenharmony_ci		return 0;
230162306a36Sopenharmony_ci	}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	/* Alloc memory for the string of commands. */
230462306a36Sopenharmony_ci	cmdBuffer = kmalloc(0x10, GFP_ATOMIC);
230562306a36Sopenharmony_ci	if (cmdBuffer == NULL)
230662306a36Sopenharmony_ci		return -ENOMEM;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	currCmd = cmdBuffer;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	/* Build a cmd in the buffer to write the given register */
231162306a36Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->port_number,
231262306a36Sopenharmony_ci			   regNum, regValue);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
231562306a36Sopenharmony_ci	if (status) {
231662306a36Sopenharmony_ci		/* something bad happened, let's free up the memory */
231762306a36Sopenharmony_ci		kfree(cmdBuffer);
231862306a36Sopenharmony_ci	}
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	return status;
232162306a36Sopenharmony_ci}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci/*****************************************************************************
232562306a36Sopenharmony_ci * change_port_settings
232662306a36Sopenharmony_ci *	This routine is called to set the UART on the device to match the
232762306a36Sopenharmony_ci *	specified new settings.
232862306a36Sopenharmony_ci *****************************************************************************/
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty,
233162306a36Sopenharmony_ci	struct edgeport_port *edge_port, const struct ktermios *old_termios)
233262306a36Sopenharmony_ci{
233362306a36Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
233462306a36Sopenharmony_ci	struct edgeport_serial *edge_serial =
233562306a36Sopenharmony_ci			usb_get_serial_data(edge_port->port->serial);
233662306a36Sopenharmony_ci	int baud;
233762306a36Sopenharmony_ci	unsigned cflag;
233862306a36Sopenharmony_ci	__u8 mask = 0xff;
233962306a36Sopenharmony_ci	__u8 lData;
234062306a36Sopenharmony_ci	__u8 lParity;
234162306a36Sopenharmony_ci	__u8 lStop;
234262306a36Sopenharmony_ci	__u8 rxFlow;
234362306a36Sopenharmony_ci	__u8 txFlow;
234462306a36Sopenharmony_ci	int status;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	if (!edge_port->open &&
234762306a36Sopenharmony_ci	    !edge_port->openPending) {
234862306a36Sopenharmony_ci		dev_dbg(dev, "%s - port not opened\n", __func__);
234962306a36Sopenharmony_ci		return;
235062306a36Sopenharmony_ci	}
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	cflag = tty->termios.c_cflag;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	switch (cflag & CSIZE) {
235562306a36Sopenharmony_ci	case CS5:
235662306a36Sopenharmony_ci		lData = LCR_BITS_5; mask = 0x1f;
235762306a36Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 5\n", __func__);
235862306a36Sopenharmony_ci		break;
235962306a36Sopenharmony_ci	case CS6:
236062306a36Sopenharmony_ci		lData = LCR_BITS_6; mask = 0x3f;
236162306a36Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 6\n", __func__);
236262306a36Sopenharmony_ci		break;
236362306a36Sopenharmony_ci	case CS7:
236462306a36Sopenharmony_ci		lData = LCR_BITS_7; mask = 0x7f;
236562306a36Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 7\n", __func__);
236662306a36Sopenharmony_ci		break;
236762306a36Sopenharmony_ci	default:
236862306a36Sopenharmony_ci	case CS8:
236962306a36Sopenharmony_ci		lData = LCR_BITS_8;
237062306a36Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 8\n", __func__);
237162306a36Sopenharmony_ci		break;
237262306a36Sopenharmony_ci	}
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	lParity = LCR_PAR_NONE;
237562306a36Sopenharmony_ci	if (cflag & PARENB) {
237662306a36Sopenharmony_ci		if (cflag & CMSPAR) {
237762306a36Sopenharmony_ci			if (cflag & PARODD) {
237862306a36Sopenharmony_ci				lParity = LCR_PAR_MARK;
237962306a36Sopenharmony_ci				dev_dbg(dev, "%s - parity = mark\n", __func__);
238062306a36Sopenharmony_ci			} else {
238162306a36Sopenharmony_ci				lParity = LCR_PAR_SPACE;
238262306a36Sopenharmony_ci				dev_dbg(dev, "%s - parity = space\n", __func__);
238362306a36Sopenharmony_ci			}
238462306a36Sopenharmony_ci		} else if (cflag & PARODD) {
238562306a36Sopenharmony_ci			lParity = LCR_PAR_ODD;
238662306a36Sopenharmony_ci			dev_dbg(dev, "%s - parity = odd\n", __func__);
238762306a36Sopenharmony_ci		} else {
238862306a36Sopenharmony_ci			lParity = LCR_PAR_EVEN;
238962306a36Sopenharmony_ci			dev_dbg(dev, "%s - parity = even\n", __func__);
239062306a36Sopenharmony_ci		}
239162306a36Sopenharmony_ci	} else {
239262306a36Sopenharmony_ci		dev_dbg(dev, "%s - parity = none\n", __func__);
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	if (cflag & CSTOPB) {
239662306a36Sopenharmony_ci		lStop = LCR_STOP_2;
239762306a36Sopenharmony_ci		dev_dbg(dev, "%s - stop bits = 2\n", __func__);
239862306a36Sopenharmony_ci	} else {
239962306a36Sopenharmony_ci		lStop = LCR_STOP_1;
240062306a36Sopenharmony_ci		dev_dbg(dev, "%s - stop bits = 1\n", __func__);
240162306a36Sopenharmony_ci	}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	/* figure out the flow control settings */
240462306a36Sopenharmony_ci	rxFlow = txFlow = 0x00;
240562306a36Sopenharmony_ci	if (cflag & CRTSCTS) {
240662306a36Sopenharmony_ci		rxFlow |= IOSP_RX_FLOW_RTS;
240762306a36Sopenharmony_ci		txFlow |= IOSP_TX_FLOW_CTS;
240862306a36Sopenharmony_ci		dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__);
240962306a36Sopenharmony_ci	} else {
241062306a36Sopenharmony_ci		dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__);
241162306a36Sopenharmony_ci	}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, set the start and stop character
241462306a36Sopenharmony_ci	   in the device */
241562306a36Sopenharmony_ci	if (I_IXOFF(tty) || I_IXON(tty)) {
241662306a36Sopenharmony_ci		unsigned char stop_char  = STOP_CHAR(tty);
241762306a36Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci		if (!edge_serial->is_epic ||
242062306a36Sopenharmony_ci		    edge_serial->epic_descriptor.Supports.IOSPSetXChar) {
242162306a36Sopenharmony_ci			send_iosp_ext_cmd(edge_port,
242262306a36Sopenharmony_ci					IOSP_CMD_SET_XON_CHAR, start_char);
242362306a36Sopenharmony_ci			send_iosp_ext_cmd(edge_port,
242462306a36Sopenharmony_ci					IOSP_CMD_SET_XOFF_CHAR, stop_char);
242562306a36Sopenharmony_ci		}
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci		/* if we are implementing INBOUND XON/XOFF */
242862306a36Sopenharmony_ci		if (I_IXOFF(tty)) {
242962306a36Sopenharmony_ci			rxFlow |= IOSP_RX_FLOW_XON_XOFF;
243062306a36Sopenharmony_ci			dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
243162306a36Sopenharmony_ci				__func__, start_char, stop_char);
243262306a36Sopenharmony_ci		} else {
243362306a36Sopenharmony_ci			dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__);
243462306a36Sopenharmony_ci		}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_ci		/* if we are implementing OUTBOUND XON/XOFF */
243762306a36Sopenharmony_ci		if (I_IXON(tty)) {
243862306a36Sopenharmony_ci			txFlow |= IOSP_TX_FLOW_XON_XOFF;
243962306a36Sopenharmony_ci			dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
244062306a36Sopenharmony_ci				__func__, start_char, stop_char);
244162306a36Sopenharmony_ci		} else {
244262306a36Sopenharmony_ci			dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__);
244362306a36Sopenharmony_ci		}
244462306a36Sopenharmony_ci	}
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	/* Set flow control to the configured value */
244762306a36Sopenharmony_ci	if (!edge_serial->is_epic ||
244862306a36Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)
244962306a36Sopenharmony_ci		send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
245062306a36Sopenharmony_ci	if (!edge_serial->is_epic ||
245162306a36Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)
245262306a36Sopenharmony_ci		send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
245662306a36Sopenharmony_ci	edge_port->shadowLCR |= (lData | lParity | lStop);
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	edge_port->validDataMask = mask;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	/* Send the updated LCR value to the EdgePort */
246162306a36Sopenharmony_ci	status = send_cmd_write_uart_register(edge_port, LCR,
246262306a36Sopenharmony_ci							edge_port->shadowLCR);
246362306a36Sopenharmony_ci	if (status != 0)
246462306a36Sopenharmony_ci		return;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	/* set up the MCR register and send it to the EdgePort */
246762306a36Sopenharmony_ci	edge_port->shadowMCR = MCR_MASTER_IE;
246862306a36Sopenharmony_ci	if (cflag & CBAUD)
246962306a36Sopenharmony_ci		edge_port->shadowMCR |= (MCR_DTR | MCR_RTS);
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	status = send_cmd_write_uart_register(edge_port, MCR,
247262306a36Sopenharmony_ci						edge_port->shadowMCR);
247362306a36Sopenharmony_ci	if (status != 0)
247462306a36Sopenharmony_ci		return;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	/* Determine divisor based on baud rate */
247762306a36Sopenharmony_ci	baud = tty_get_baud_rate(tty);
247862306a36Sopenharmony_ci	if (!baud) {
247962306a36Sopenharmony_ci		/* pick a default, any default... */
248062306a36Sopenharmony_ci		baud = 9600;
248162306a36Sopenharmony_ci	}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
248462306a36Sopenharmony_ci	status = send_cmd_write_baud_rate(edge_port, baud);
248562306a36Sopenharmony_ci	if (status == -1) {
248662306a36Sopenharmony_ci		/* Speed change was not possible - put back the old speed */
248762306a36Sopenharmony_ci		baud = tty_termios_baud_rate(old_termios);
248862306a36Sopenharmony_ci		tty_encode_baud_rate(tty, baud, baud);
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ci}
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci/****************************************************************************
249462306a36Sopenharmony_ci * unicode_to_ascii
249562306a36Sopenharmony_ci *	Turns a string from Unicode into ASCII.
249662306a36Sopenharmony_ci *	Doesn't do a good job with any characters that are outside the normal
249762306a36Sopenharmony_ci *	ASCII range, but it's only for debugging...
249862306a36Sopenharmony_ci *	NOTE: expects the unicode in LE format
249962306a36Sopenharmony_ci ****************************************************************************/
250062306a36Sopenharmony_cistatic void unicode_to_ascii(char *string, int buflen,
250162306a36Sopenharmony_ci					__le16 *unicode, int unicode_size)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	int i;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	if (buflen <= 0)	/* never happens, but... */
250662306a36Sopenharmony_ci		return;
250762306a36Sopenharmony_ci	--buflen;		/* space for nul */
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	for (i = 0; i < unicode_size; i++) {
251062306a36Sopenharmony_ci		if (i >= buflen)
251162306a36Sopenharmony_ci			break;
251262306a36Sopenharmony_ci		string[i] = (char)(le16_to_cpu(unicode[i]));
251362306a36Sopenharmony_ci	}
251462306a36Sopenharmony_ci	string[i] = 0x00;
251562306a36Sopenharmony_ci}
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci/****************************************************************************
251962306a36Sopenharmony_ci * get_manufacturing_desc
252062306a36Sopenharmony_ci *	reads in the manufacturing descriptor and stores it into the serial
252162306a36Sopenharmony_ci *	structure.
252262306a36Sopenharmony_ci ****************************************************************************/
252362306a36Sopenharmony_cistatic void get_manufacturing_desc(struct edgeport_serial *edge_serial)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
252662306a36Sopenharmony_ci	int response;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	dev_dbg(dev, "getting manufacturer descriptor\n");
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	response = rom_read(edge_serial->serial,
253162306a36Sopenharmony_ci				(EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16,
253262306a36Sopenharmony_ci				(__u16)(EDGE_MANUF_DESC_ADDR & 0x0000ffff),
253362306a36Sopenharmony_ci				EDGE_MANUF_DESC_LEN,
253462306a36Sopenharmony_ci				(__u8 *)(&edge_serial->manuf_descriptor));
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	if (response < 0) {
253762306a36Sopenharmony_ci		dev_err(dev, "error in getting manufacturer descriptor: %d\n",
253862306a36Sopenharmony_ci				response);
253962306a36Sopenharmony_ci	} else {
254062306a36Sopenharmony_ci		char string[30];
254162306a36Sopenharmony_ci		dev_dbg(dev, "**Manufacturer Descriptor\n");
254262306a36Sopenharmony_ci		dev_dbg(dev, "  RomSize:        %dK\n",
254362306a36Sopenharmony_ci			edge_serial->manuf_descriptor.RomSize);
254462306a36Sopenharmony_ci		dev_dbg(dev, "  RamSize:        %dK\n",
254562306a36Sopenharmony_ci			edge_serial->manuf_descriptor.RamSize);
254662306a36Sopenharmony_ci		dev_dbg(dev, "  CpuRev:         %d\n",
254762306a36Sopenharmony_ci			edge_serial->manuf_descriptor.CpuRev);
254862306a36Sopenharmony_ci		dev_dbg(dev, "  BoardRev:       %d\n",
254962306a36Sopenharmony_ci			edge_serial->manuf_descriptor.BoardRev);
255062306a36Sopenharmony_ci		dev_dbg(dev, "  NumPorts:       %d\n",
255162306a36Sopenharmony_ci			edge_serial->manuf_descriptor.NumPorts);
255262306a36Sopenharmony_ci		dev_dbg(dev, "  DescDate:       %d/%d/%d\n",
255362306a36Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate[0],
255462306a36Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate[1],
255562306a36Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate[2]+1900);
255662306a36Sopenharmony_ci		unicode_to_ascii(string, sizeof(string),
255762306a36Sopenharmony_ci			edge_serial->manuf_descriptor.SerialNumber,
255862306a36Sopenharmony_ci			edge_serial->manuf_descriptor.SerNumLength/2);
255962306a36Sopenharmony_ci		dev_dbg(dev, "  SerialNumber: %s\n", string);
256062306a36Sopenharmony_ci		unicode_to_ascii(string, sizeof(string),
256162306a36Sopenharmony_ci			edge_serial->manuf_descriptor.AssemblyNumber,
256262306a36Sopenharmony_ci			edge_serial->manuf_descriptor.AssemblyNumLength/2);
256362306a36Sopenharmony_ci		dev_dbg(dev, "  AssemblyNumber: %s\n", string);
256462306a36Sopenharmony_ci		unicode_to_ascii(string, sizeof(string),
256562306a36Sopenharmony_ci		    edge_serial->manuf_descriptor.OemAssyNumber,
256662306a36Sopenharmony_ci		    edge_serial->manuf_descriptor.OemAssyNumLength/2);
256762306a36Sopenharmony_ci		dev_dbg(dev, "  OemAssyNumber:  %s\n", string);
256862306a36Sopenharmony_ci		dev_dbg(dev, "  UartType:       %d\n",
256962306a36Sopenharmony_ci			edge_serial->manuf_descriptor.UartType);
257062306a36Sopenharmony_ci		dev_dbg(dev, "  IonPid:         %d\n",
257162306a36Sopenharmony_ci			edge_serial->manuf_descriptor.IonPid);
257262306a36Sopenharmony_ci		dev_dbg(dev, "  IonConfig:      %d\n",
257362306a36Sopenharmony_ci			edge_serial->manuf_descriptor.IonConfig);
257462306a36Sopenharmony_ci	}
257562306a36Sopenharmony_ci}
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci/****************************************************************************
257962306a36Sopenharmony_ci * get_boot_desc
258062306a36Sopenharmony_ci *	reads in the bootloader descriptor and stores it into the serial
258162306a36Sopenharmony_ci *	structure.
258262306a36Sopenharmony_ci ****************************************************************************/
258362306a36Sopenharmony_cistatic void get_boot_desc(struct edgeport_serial *edge_serial)
258462306a36Sopenharmony_ci{
258562306a36Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
258662306a36Sopenharmony_ci	int response;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	dev_dbg(dev, "getting boot descriptor\n");
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	response = rom_read(edge_serial->serial,
259162306a36Sopenharmony_ci				(EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16,
259262306a36Sopenharmony_ci				(__u16)(EDGE_BOOT_DESC_ADDR & 0x0000ffff),
259362306a36Sopenharmony_ci				EDGE_BOOT_DESC_LEN,
259462306a36Sopenharmony_ci				(__u8 *)(&edge_serial->boot_descriptor));
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	if (response < 0) {
259762306a36Sopenharmony_ci		dev_err(dev, "error in getting boot descriptor: %d\n",
259862306a36Sopenharmony_ci				response);
259962306a36Sopenharmony_ci	} else {
260062306a36Sopenharmony_ci		dev_dbg(dev, "**Boot Descriptor:\n");
260162306a36Sopenharmony_ci		dev_dbg(dev, "  BootCodeLength: %d\n",
260262306a36Sopenharmony_ci			le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
260362306a36Sopenharmony_ci		dev_dbg(dev, "  MajorVersion:   %d\n",
260462306a36Sopenharmony_ci			edge_serial->boot_descriptor.MajorVersion);
260562306a36Sopenharmony_ci		dev_dbg(dev, "  MinorVersion:   %d\n",
260662306a36Sopenharmony_ci			edge_serial->boot_descriptor.MinorVersion);
260762306a36Sopenharmony_ci		dev_dbg(dev, "  BuildNumber:    %d\n",
260862306a36Sopenharmony_ci			le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
260962306a36Sopenharmony_ci		dev_dbg(dev, "  Capabilities:   0x%x\n",
261062306a36Sopenharmony_ci		      le16_to_cpu(edge_serial->boot_descriptor.Capabilities));
261162306a36Sopenharmony_ci		dev_dbg(dev, "  UConfig0:       %d\n",
261262306a36Sopenharmony_ci			edge_serial->boot_descriptor.UConfig0);
261362306a36Sopenharmony_ci		dev_dbg(dev, "  UConfig1:       %d\n",
261462306a36Sopenharmony_ci			edge_serial->boot_descriptor.UConfig1);
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci}
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci/****************************************************************************
262062306a36Sopenharmony_ci * load_application_firmware
262162306a36Sopenharmony_ci *	This is called to load the application firmware to the device
262262306a36Sopenharmony_ci ****************************************************************************/
262362306a36Sopenharmony_cistatic void load_application_firmware(struct edgeport_serial *edge_serial)
262462306a36Sopenharmony_ci{
262562306a36Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
262662306a36Sopenharmony_ci	const struct ihex_binrec *rec;
262762306a36Sopenharmony_ci	const struct firmware *fw;
262862306a36Sopenharmony_ci	const char *fw_name;
262962306a36Sopenharmony_ci	const char *fw_info;
263062306a36Sopenharmony_ci	int response;
263162306a36Sopenharmony_ci	__u32 Operaddr;
263262306a36Sopenharmony_ci	__u16 build;
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	switch (edge_serial->product_info.iDownloadFile) {
263562306a36Sopenharmony_ci		case EDGE_DOWNLOAD_FILE_I930:
263662306a36Sopenharmony_ci			fw_info = "downloading firmware version (930)";
263762306a36Sopenharmony_ci			fw_name	= "edgeport/down.fw";
263862306a36Sopenharmony_ci			break;
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_ci		case EDGE_DOWNLOAD_FILE_80251:
264162306a36Sopenharmony_ci			fw_info = "downloading firmware version (80251)";
264262306a36Sopenharmony_ci			fw_name	= "edgeport/down2.fw";
264362306a36Sopenharmony_ci			break;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci		case EDGE_DOWNLOAD_FILE_NONE:
264662306a36Sopenharmony_ci			dev_dbg(dev, "No download file specified, skipping download\n");
264762306a36Sopenharmony_ci			return;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci		default:
265062306a36Sopenharmony_ci			return;
265162306a36Sopenharmony_ci	}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	response = request_ihex_firmware(&fw, fw_name,
265462306a36Sopenharmony_ci				    &edge_serial->serial->dev->dev);
265562306a36Sopenharmony_ci	if (response) {
265662306a36Sopenharmony_ci		dev_err(dev, "Failed to load image \"%s\" err %d\n",
265762306a36Sopenharmony_ci		       fw_name, response);
265862306a36Sopenharmony_ci		return;
265962306a36Sopenharmony_ci	}
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	rec = (const struct ihex_binrec *)fw->data;
266262306a36Sopenharmony_ci	build = (rec->data[2] << 8) | rec->data[3];
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	dev_dbg(dev, "%s %d.%d.%d\n", fw_info, rec->data[0], rec->data[1], build);
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci	edge_serial->product_info.FirmwareMajorVersion = rec->data[0];
266762306a36Sopenharmony_ci	edge_serial->product_info.FirmwareMinorVersion = rec->data[1];
266862306a36Sopenharmony_ci	edge_serial->product_info.FirmwareBuildNumber = cpu_to_le16(build);
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci	for (rec = ihex_next_binrec(rec); rec;
267162306a36Sopenharmony_ci	     rec = ihex_next_binrec(rec)) {
267262306a36Sopenharmony_ci		Operaddr = be32_to_cpu(rec->addr);
267362306a36Sopenharmony_ci		response = sram_write(edge_serial->serial,
267462306a36Sopenharmony_ci				     Operaddr >> 16,
267562306a36Sopenharmony_ci				     Operaddr & 0xFFFF,
267662306a36Sopenharmony_ci				     be16_to_cpu(rec->len),
267762306a36Sopenharmony_ci				     &rec->data[0]);
267862306a36Sopenharmony_ci		if (response < 0) {
267962306a36Sopenharmony_ci			dev_err(&edge_serial->serial->dev->dev,
268062306a36Sopenharmony_ci				"sram_write failed (%x, %x, %d)\n",
268162306a36Sopenharmony_ci				Operaddr >> 16, Operaddr & 0xFFFF,
268262306a36Sopenharmony_ci				be16_to_cpu(rec->len));
268362306a36Sopenharmony_ci			break;
268462306a36Sopenharmony_ci		}
268562306a36Sopenharmony_ci	}
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	dev_dbg(dev, "sending exec_dl_code\n");
268862306a36Sopenharmony_ci	response = usb_control_msg (edge_serial->serial->dev,
268962306a36Sopenharmony_ci				    usb_sndctrlpipe(edge_serial->serial->dev, 0),
269062306a36Sopenharmony_ci				    USB_REQUEST_ION_EXEC_DL_CODE,
269162306a36Sopenharmony_ci				    0x40, 0x4000, 0x0001, NULL, 0, 3000);
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	release_firmware(fw);
269462306a36Sopenharmony_ci}
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci/****************************************************************************
269862306a36Sopenharmony_ci * edge_startup
269962306a36Sopenharmony_ci ****************************************************************************/
270062306a36Sopenharmony_cistatic int edge_startup(struct usb_serial *serial)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	struct edgeport_serial *edge_serial;
270362306a36Sopenharmony_ci	struct usb_device *dev;
270462306a36Sopenharmony_ci	struct device *ddev = &serial->dev->dev;
270562306a36Sopenharmony_ci	int i;
270662306a36Sopenharmony_ci	int response;
270762306a36Sopenharmony_ci	bool interrupt_in_found;
270862306a36Sopenharmony_ci	bool bulk_in_found;
270962306a36Sopenharmony_ci	bool bulk_out_found;
271062306a36Sopenharmony_ci	static const __u32 descriptor[3] = {	EDGE_COMPATIBILITY_MASK0,
271162306a36Sopenharmony_ci						EDGE_COMPATIBILITY_MASK1,
271262306a36Sopenharmony_ci						EDGE_COMPATIBILITY_MASK2 };
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	dev = serial->dev;
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	/* create our private serial structure */
271762306a36Sopenharmony_ci	edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
271862306a36Sopenharmony_ci	if (!edge_serial)
271962306a36Sopenharmony_ci		return -ENOMEM;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	spin_lock_init(&edge_serial->es_lock);
272262306a36Sopenharmony_ci	edge_serial->serial = serial;
272362306a36Sopenharmony_ci	usb_set_serial_data(serial, edge_serial);
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	/* get the name for the device from the device */
272662306a36Sopenharmony_ci	i = usb_string(dev, dev->descriptor.iManufacturer,
272762306a36Sopenharmony_ci	    &edge_serial->name[0], MAX_NAME_LEN+1);
272862306a36Sopenharmony_ci	if (i < 0)
272962306a36Sopenharmony_ci		i = 0;
273062306a36Sopenharmony_ci	edge_serial->name[i++] = ' ';
273162306a36Sopenharmony_ci	usb_string(dev, dev->descriptor.iProduct,
273262306a36Sopenharmony_ci	    &edge_serial->name[i], MAX_NAME_LEN+2 - i);
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	/* Read the epic descriptor */
273762306a36Sopenharmony_ci	if (get_epic_descriptor(edge_serial) < 0) {
273862306a36Sopenharmony_ci		/* memcpy descriptor to Supports structures */
273962306a36Sopenharmony_ci		memcpy(&edge_serial->epic_descriptor.Supports, descriptor,
274062306a36Sopenharmony_ci		       sizeof(struct edge_compatibility_bits));
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci		/* get the manufacturing descriptor for this device */
274362306a36Sopenharmony_ci		get_manufacturing_desc(edge_serial);
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci		/* get the boot descriptor */
274662306a36Sopenharmony_ci		get_boot_desc(edge_serial);
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci		get_product_info(edge_serial);
274962306a36Sopenharmony_ci	}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	/* set the number of ports from the manufacturing description */
275262306a36Sopenharmony_ci	/* serial->num_ports = serial->product_info.NumPorts; */
275362306a36Sopenharmony_ci	if ((!edge_serial->is_epic) &&
275462306a36Sopenharmony_ci	    (edge_serial->product_info.NumPorts != serial->num_ports)) {
275562306a36Sopenharmony_ci		dev_warn(ddev,
275662306a36Sopenharmony_ci			"Device Reported %d serial ports vs. core thinking we have %d ports, email greg@kroah.com this information.\n",
275762306a36Sopenharmony_ci			 edge_serial->product_info.NumPorts,
275862306a36Sopenharmony_ci			 serial->num_ports);
275962306a36Sopenharmony_ci	}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	dev_dbg(ddev, "%s - time 1 %ld\n", __func__, jiffies);
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_ci	/* If not an EPiC device */
276462306a36Sopenharmony_ci	if (!edge_serial->is_epic) {
276562306a36Sopenharmony_ci		/* now load the application firmware into this device */
276662306a36Sopenharmony_ci		load_application_firmware(edge_serial);
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci		dev_dbg(ddev, "%s - time 2 %ld\n", __func__, jiffies);
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci		/* Check current Edgeport EEPROM and update if necessary */
277162306a36Sopenharmony_ci		update_edgeport_E2PROM(edge_serial);
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci		dev_dbg(ddev, "%s - time 3 %ld\n", __func__, jiffies);
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci		/* set the configuration to use #1 */
277662306a36Sopenharmony_ci/*		dev_dbg(ddev, "set_configuration 1\n"); */
277762306a36Sopenharmony_ci/*		usb_set_configuration (dev, 1); */
277862306a36Sopenharmony_ci	}
277962306a36Sopenharmony_ci	dev_dbg(ddev, "  FirmwareMajorVersion  %d.%d.%d\n",
278062306a36Sopenharmony_ci	    edge_serial->product_info.FirmwareMajorVersion,
278162306a36Sopenharmony_ci	    edge_serial->product_info.FirmwareMinorVersion,
278262306a36Sopenharmony_ci	    le16_to_cpu(edge_serial->product_info.FirmwareBuildNumber));
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	/* we set up the pointers to the endpoints in the edge_open function,
278562306a36Sopenharmony_ci	 * as the structures aren't created yet. */
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	response = 0;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	if (edge_serial->is_epic) {
279062306a36Sopenharmony_ci		struct usb_host_interface *alt;
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci		alt = serial->interface->cur_altsetting;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci		/* EPIC thing, set up our interrupt polling now and our read
279562306a36Sopenharmony_ci		 * urb, so that the device knows it really is connected. */
279662306a36Sopenharmony_ci		interrupt_in_found = bulk_in_found = bulk_out_found = false;
279762306a36Sopenharmony_ci		for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
279862306a36Sopenharmony_ci			struct usb_endpoint_descriptor *endpoint;
279962306a36Sopenharmony_ci			int buffer_size;
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci			endpoint = &alt->endpoint[i].desc;
280262306a36Sopenharmony_ci			buffer_size = usb_endpoint_maxp(endpoint);
280362306a36Sopenharmony_ci			if (!interrupt_in_found &&
280462306a36Sopenharmony_ci			    (usb_endpoint_is_int_in(endpoint))) {
280562306a36Sopenharmony_ci				/* we found a interrupt in endpoint */
280662306a36Sopenharmony_ci				dev_dbg(ddev, "found interrupt in\n");
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci				/* not set up yet, so do it now */
280962306a36Sopenharmony_ci				edge_serial->interrupt_read_urb =
281062306a36Sopenharmony_ci						usb_alloc_urb(0, GFP_KERNEL);
281162306a36Sopenharmony_ci				if (!edge_serial->interrupt_read_urb) {
281262306a36Sopenharmony_ci					response = -ENOMEM;
281362306a36Sopenharmony_ci					break;
281462306a36Sopenharmony_ci				}
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci				edge_serial->interrupt_in_buffer =
281762306a36Sopenharmony_ci					kmalloc(buffer_size, GFP_KERNEL);
281862306a36Sopenharmony_ci				if (!edge_serial->interrupt_in_buffer) {
281962306a36Sopenharmony_ci					response = -ENOMEM;
282062306a36Sopenharmony_ci					break;
282162306a36Sopenharmony_ci				}
282262306a36Sopenharmony_ci				edge_serial->interrupt_in_endpoint =
282362306a36Sopenharmony_ci						endpoint->bEndpointAddress;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci				/* set up our interrupt urb */
282662306a36Sopenharmony_ci				usb_fill_int_urb(
282762306a36Sopenharmony_ci					edge_serial->interrupt_read_urb,
282862306a36Sopenharmony_ci					dev,
282962306a36Sopenharmony_ci					usb_rcvintpipe(dev,
283062306a36Sopenharmony_ci						endpoint->bEndpointAddress),
283162306a36Sopenharmony_ci					edge_serial->interrupt_in_buffer,
283262306a36Sopenharmony_ci					buffer_size,
283362306a36Sopenharmony_ci					edge_interrupt_callback,
283462306a36Sopenharmony_ci					edge_serial,
283562306a36Sopenharmony_ci					endpoint->bInterval);
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci				interrupt_in_found = true;
283862306a36Sopenharmony_ci			}
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci			if (!bulk_in_found &&
284162306a36Sopenharmony_ci				(usb_endpoint_is_bulk_in(endpoint))) {
284262306a36Sopenharmony_ci				/* we found a bulk in endpoint */
284362306a36Sopenharmony_ci				dev_dbg(ddev, "found bulk in\n");
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci				/* not set up yet, so do it now */
284662306a36Sopenharmony_ci				edge_serial->read_urb =
284762306a36Sopenharmony_ci						usb_alloc_urb(0, GFP_KERNEL);
284862306a36Sopenharmony_ci				if (!edge_serial->read_urb) {
284962306a36Sopenharmony_ci					response = -ENOMEM;
285062306a36Sopenharmony_ci					break;
285162306a36Sopenharmony_ci				}
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci				edge_serial->bulk_in_buffer =
285462306a36Sopenharmony_ci					kmalloc(buffer_size, GFP_KERNEL);
285562306a36Sopenharmony_ci				if (!edge_serial->bulk_in_buffer) {
285662306a36Sopenharmony_ci					response = -ENOMEM;
285762306a36Sopenharmony_ci					break;
285862306a36Sopenharmony_ci				}
285962306a36Sopenharmony_ci				edge_serial->bulk_in_endpoint =
286062306a36Sopenharmony_ci						endpoint->bEndpointAddress;
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci				/* set up our bulk in urb */
286362306a36Sopenharmony_ci				usb_fill_bulk_urb(edge_serial->read_urb, dev,
286462306a36Sopenharmony_ci					usb_rcvbulkpipe(dev,
286562306a36Sopenharmony_ci						endpoint->bEndpointAddress),
286662306a36Sopenharmony_ci					edge_serial->bulk_in_buffer,
286762306a36Sopenharmony_ci					usb_endpoint_maxp(endpoint),
286862306a36Sopenharmony_ci					edge_bulk_in_callback,
286962306a36Sopenharmony_ci					edge_serial);
287062306a36Sopenharmony_ci				bulk_in_found = true;
287162306a36Sopenharmony_ci			}
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci			if (!bulk_out_found &&
287462306a36Sopenharmony_ci			    (usb_endpoint_is_bulk_out(endpoint))) {
287562306a36Sopenharmony_ci				/* we found a bulk out endpoint */
287662306a36Sopenharmony_ci				dev_dbg(ddev, "found bulk out\n");
287762306a36Sopenharmony_ci				edge_serial->bulk_out_endpoint =
287862306a36Sopenharmony_ci						endpoint->bEndpointAddress;
287962306a36Sopenharmony_ci				bulk_out_found = true;
288062306a36Sopenharmony_ci			}
288162306a36Sopenharmony_ci		}
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci		if (response || !interrupt_in_found || !bulk_in_found ||
288462306a36Sopenharmony_ci							!bulk_out_found) {
288562306a36Sopenharmony_ci			if (!response) {
288662306a36Sopenharmony_ci				dev_err(ddev, "expected endpoints not found\n");
288762306a36Sopenharmony_ci				response = -ENODEV;
288862306a36Sopenharmony_ci			}
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci			goto error;
289162306a36Sopenharmony_ci		}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci		/* start interrupt read for this edgeport this interrupt will
289462306a36Sopenharmony_ci		 * continue as long as the edgeport is connected */
289562306a36Sopenharmony_ci		response = usb_submit_urb(edge_serial->interrupt_read_urb,
289662306a36Sopenharmony_ci								GFP_KERNEL);
289762306a36Sopenharmony_ci		if (response) {
289862306a36Sopenharmony_ci			dev_err(ddev, "%s - Error %d submitting control urb\n",
289962306a36Sopenharmony_ci				__func__, response);
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci			goto error;
290262306a36Sopenharmony_ci		}
290362306a36Sopenharmony_ci	}
290462306a36Sopenharmony_ci	return response;
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_cierror:
290762306a36Sopenharmony_ci	usb_free_urb(edge_serial->interrupt_read_urb);
290862306a36Sopenharmony_ci	kfree(edge_serial->interrupt_in_buffer);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	usb_free_urb(edge_serial->read_urb);
291162306a36Sopenharmony_ci	kfree(edge_serial->bulk_in_buffer);
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	kfree(edge_serial);
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	return response;
291662306a36Sopenharmony_ci}
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci/****************************************************************************
292062306a36Sopenharmony_ci * edge_disconnect
292162306a36Sopenharmony_ci *	This function is called whenever the device is removed from the usb bus.
292262306a36Sopenharmony_ci ****************************************************************************/
292362306a36Sopenharmony_cistatic void edge_disconnect(struct usb_serial *serial)
292462306a36Sopenharmony_ci{
292562306a36Sopenharmony_ci	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	if (edge_serial->is_epic) {
292862306a36Sopenharmony_ci		usb_kill_urb(edge_serial->interrupt_read_urb);
292962306a36Sopenharmony_ci		usb_kill_urb(edge_serial->read_urb);
293062306a36Sopenharmony_ci	}
293162306a36Sopenharmony_ci}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci/****************************************************************************
293562306a36Sopenharmony_ci * edge_release
293662306a36Sopenharmony_ci *	This function is called when the device structure is deallocated.
293762306a36Sopenharmony_ci ****************************************************************************/
293862306a36Sopenharmony_cistatic void edge_release(struct usb_serial *serial)
293962306a36Sopenharmony_ci{
294062306a36Sopenharmony_ci	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	if (edge_serial->is_epic) {
294362306a36Sopenharmony_ci		usb_kill_urb(edge_serial->interrupt_read_urb);
294462306a36Sopenharmony_ci		usb_free_urb(edge_serial->interrupt_read_urb);
294562306a36Sopenharmony_ci		kfree(edge_serial->interrupt_in_buffer);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci		usb_kill_urb(edge_serial->read_urb);
294862306a36Sopenharmony_ci		usb_free_urb(edge_serial->read_urb);
294962306a36Sopenharmony_ci		kfree(edge_serial->bulk_in_buffer);
295062306a36Sopenharmony_ci	}
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	kfree(edge_serial);
295362306a36Sopenharmony_ci}
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_cistatic int edge_port_probe(struct usb_serial_port *port)
295662306a36Sopenharmony_ci{
295762306a36Sopenharmony_ci	struct edgeport_port *edge_port;
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL);
296062306a36Sopenharmony_ci	if (!edge_port)
296162306a36Sopenharmony_ci		return -ENOMEM;
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_ci	spin_lock_init(&edge_port->ep_lock);
296462306a36Sopenharmony_ci	edge_port->port = port;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	usb_set_serial_port_data(port, edge_port);
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	return 0;
296962306a36Sopenharmony_ci}
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_cistatic void edge_port_remove(struct usb_serial_port *port)
297262306a36Sopenharmony_ci{
297362306a36Sopenharmony_ci	struct edgeport_port *edge_port;
297462306a36Sopenharmony_ci
297562306a36Sopenharmony_ci	edge_port = usb_get_serial_port_data(port);
297662306a36Sopenharmony_ci	kfree(edge_port);
297762306a36Sopenharmony_ci}
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_2port_device = {
298062306a36Sopenharmony_ci	.driver = {
298162306a36Sopenharmony_ci		.owner		= THIS_MODULE,
298262306a36Sopenharmony_ci		.name		= "edgeport_2",
298362306a36Sopenharmony_ci	},
298462306a36Sopenharmony_ci	.description		= "Edgeport 2 port adapter",
298562306a36Sopenharmony_ci	.id_table		= edgeport_2port_id_table,
298662306a36Sopenharmony_ci	.num_ports		= 2,
298762306a36Sopenharmony_ci	.num_bulk_in		= 1,
298862306a36Sopenharmony_ci	.num_bulk_out		= 1,
298962306a36Sopenharmony_ci	.num_interrupt_in	= 1,
299062306a36Sopenharmony_ci	.open			= edge_open,
299162306a36Sopenharmony_ci	.close			= edge_close,
299262306a36Sopenharmony_ci	.throttle		= edge_throttle,
299362306a36Sopenharmony_ci	.unthrottle		= edge_unthrottle,
299462306a36Sopenharmony_ci	.attach			= edge_startup,
299562306a36Sopenharmony_ci	.disconnect		= edge_disconnect,
299662306a36Sopenharmony_ci	.release		= edge_release,
299762306a36Sopenharmony_ci	.port_probe		= edge_port_probe,
299862306a36Sopenharmony_ci	.port_remove		= edge_port_remove,
299962306a36Sopenharmony_ci	.ioctl			= edge_ioctl,
300062306a36Sopenharmony_ci	.set_termios		= edge_set_termios,
300162306a36Sopenharmony_ci	.tiocmget		= edge_tiocmget,
300262306a36Sopenharmony_ci	.tiocmset		= edge_tiocmset,
300362306a36Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
300462306a36Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
300562306a36Sopenharmony_ci	.write			= edge_write,
300662306a36Sopenharmony_ci	.write_room		= edge_write_room,
300762306a36Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
300862306a36Sopenharmony_ci	.break_ctl		= edge_break,
300962306a36Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
301062306a36Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
301162306a36Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
301262306a36Sopenharmony_ci};
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_4port_device = {
301562306a36Sopenharmony_ci	.driver = {
301662306a36Sopenharmony_ci		.owner		= THIS_MODULE,
301762306a36Sopenharmony_ci		.name		= "edgeport_4",
301862306a36Sopenharmony_ci	},
301962306a36Sopenharmony_ci	.description		= "Edgeport 4 port adapter",
302062306a36Sopenharmony_ci	.id_table		= edgeport_4port_id_table,
302162306a36Sopenharmony_ci	.num_ports		= 4,
302262306a36Sopenharmony_ci	.num_bulk_in		= 1,
302362306a36Sopenharmony_ci	.num_bulk_out		= 1,
302462306a36Sopenharmony_ci	.num_interrupt_in	= 1,
302562306a36Sopenharmony_ci	.open			= edge_open,
302662306a36Sopenharmony_ci	.close			= edge_close,
302762306a36Sopenharmony_ci	.throttle		= edge_throttle,
302862306a36Sopenharmony_ci	.unthrottle		= edge_unthrottle,
302962306a36Sopenharmony_ci	.attach			= edge_startup,
303062306a36Sopenharmony_ci	.disconnect		= edge_disconnect,
303162306a36Sopenharmony_ci	.release		= edge_release,
303262306a36Sopenharmony_ci	.port_probe		= edge_port_probe,
303362306a36Sopenharmony_ci	.port_remove		= edge_port_remove,
303462306a36Sopenharmony_ci	.ioctl			= edge_ioctl,
303562306a36Sopenharmony_ci	.set_termios		= edge_set_termios,
303662306a36Sopenharmony_ci	.tiocmget		= edge_tiocmget,
303762306a36Sopenharmony_ci	.tiocmset		= edge_tiocmset,
303862306a36Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
303962306a36Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
304062306a36Sopenharmony_ci	.write			= edge_write,
304162306a36Sopenharmony_ci	.write_room		= edge_write_room,
304262306a36Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
304362306a36Sopenharmony_ci	.break_ctl		= edge_break,
304462306a36Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
304562306a36Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
304662306a36Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
304762306a36Sopenharmony_ci};
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_cistatic struct usb_serial_driver edgeport_8port_device = {
305062306a36Sopenharmony_ci	.driver = {
305162306a36Sopenharmony_ci		.owner		= THIS_MODULE,
305262306a36Sopenharmony_ci		.name		= "edgeport_8",
305362306a36Sopenharmony_ci	},
305462306a36Sopenharmony_ci	.description		= "Edgeport 8 port adapter",
305562306a36Sopenharmony_ci	.id_table		= edgeport_8port_id_table,
305662306a36Sopenharmony_ci	.num_ports		= 8,
305762306a36Sopenharmony_ci	.num_bulk_in		= 1,
305862306a36Sopenharmony_ci	.num_bulk_out		= 1,
305962306a36Sopenharmony_ci	.num_interrupt_in	= 1,
306062306a36Sopenharmony_ci	.open			= edge_open,
306162306a36Sopenharmony_ci	.close			= edge_close,
306262306a36Sopenharmony_ci	.throttle		= edge_throttle,
306362306a36Sopenharmony_ci	.unthrottle		= edge_unthrottle,
306462306a36Sopenharmony_ci	.attach			= edge_startup,
306562306a36Sopenharmony_ci	.disconnect		= edge_disconnect,
306662306a36Sopenharmony_ci	.release		= edge_release,
306762306a36Sopenharmony_ci	.port_probe		= edge_port_probe,
306862306a36Sopenharmony_ci	.port_remove		= edge_port_remove,
306962306a36Sopenharmony_ci	.ioctl			= edge_ioctl,
307062306a36Sopenharmony_ci	.set_termios		= edge_set_termios,
307162306a36Sopenharmony_ci	.tiocmget		= edge_tiocmget,
307262306a36Sopenharmony_ci	.tiocmset		= edge_tiocmset,
307362306a36Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
307462306a36Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
307562306a36Sopenharmony_ci	.write			= edge_write,
307662306a36Sopenharmony_ci	.write_room		= edge_write_room,
307762306a36Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
307862306a36Sopenharmony_ci	.break_ctl		= edge_break,
307962306a36Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
308062306a36Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
308162306a36Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
308262306a36Sopenharmony_ci};
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_cistatic struct usb_serial_driver epic_device = {
308562306a36Sopenharmony_ci	.driver = {
308662306a36Sopenharmony_ci		.owner		= THIS_MODULE,
308762306a36Sopenharmony_ci		.name		= "epic",
308862306a36Sopenharmony_ci	},
308962306a36Sopenharmony_ci	.description		= "EPiC device",
309062306a36Sopenharmony_ci	.id_table		= Epic_port_id_table,
309162306a36Sopenharmony_ci	.num_ports		= 1,
309262306a36Sopenharmony_ci	.num_bulk_in		= 1,
309362306a36Sopenharmony_ci	.num_bulk_out		= 1,
309462306a36Sopenharmony_ci	.num_interrupt_in	= 1,
309562306a36Sopenharmony_ci	.open			= edge_open,
309662306a36Sopenharmony_ci	.close			= edge_close,
309762306a36Sopenharmony_ci	.throttle		= edge_throttle,
309862306a36Sopenharmony_ci	.unthrottle		= edge_unthrottle,
309962306a36Sopenharmony_ci	.attach			= edge_startup,
310062306a36Sopenharmony_ci	.disconnect		= edge_disconnect,
310162306a36Sopenharmony_ci	.release		= edge_release,
310262306a36Sopenharmony_ci	.port_probe		= edge_port_probe,
310362306a36Sopenharmony_ci	.port_remove		= edge_port_remove,
310462306a36Sopenharmony_ci	.ioctl			= edge_ioctl,
310562306a36Sopenharmony_ci	.set_termios		= edge_set_termios,
310662306a36Sopenharmony_ci	.tiocmget		= edge_tiocmget,
310762306a36Sopenharmony_ci	.tiocmset		= edge_tiocmset,
310862306a36Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
310962306a36Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
311062306a36Sopenharmony_ci	.write			= edge_write,
311162306a36Sopenharmony_ci	.write_room		= edge_write_room,
311262306a36Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
311362306a36Sopenharmony_ci	.break_ctl		= edge_break,
311462306a36Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
311562306a36Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
311662306a36Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
311762306a36Sopenharmony_ci};
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
312062306a36Sopenharmony_ci	&edgeport_2port_device, &edgeport_4port_device,
312162306a36Sopenharmony_ci	&edgeport_8port_device, &epic_device, NULL
312262306a36Sopenharmony_ci};
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined);
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
312762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
312862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
312962306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/boot.fw");
313062306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/boot2.fw");
313162306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/down.fw");
313262306a36Sopenharmony_ciMODULE_FIRMWARE("edgeport/down2.fw");
3133