18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Edgeport USB Serial Converter driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2000 Inside Out Networks, All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Supports the following devices:
98c2ecf20Sopenharmony_ci *	Edgeport/4
108c2ecf20Sopenharmony_ci *	Edgeport/4t
118c2ecf20Sopenharmony_ci *	Edgeport/2
128c2ecf20Sopenharmony_ci *	Edgeport/4i
138c2ecf20Sopenharmony_ci *	Edgeport/2i
148c2ecf20Sopenharmony_ci *	Edgeport/421
158c2ecf20Sopenharmony_ci *	Edgeport/21
168c2ecf20Sopenharmony_ci *	Rapidport/4
178c2ecf20Sopenharmony_ci *	Edgeport/8
188c2ecf20Sopenharmony_ci *	Edgeport/2D8
198c2ecf20Sopenharmony_ci *	Edgeport/4D8
208c2ecf20Sopenharmony_ci *	Edgeport/8i
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * For questions or problems with this driver, contact Inside Out
238c2ecf20Sopenharmony_ci * Networks technical support, or Peter Berger <pberger@brimson.com>,
248c2ecf20Sopenharmony_ci * or Al Borchers <alborchers@steinerpoint.com>.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
308c2ecf20Sopenharmony_ci#include <linux/errno.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/tty.h>
338c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
348c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
358c2ecf20Sopenharmony_ci#include <linux/module.h>
368c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
378c2ecf20Sopenharmony_ci#include <linux/serial.h>
388c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
398c2ecf20Sopenharmony_ci#include <linux/wait.h>
408c2ecf20Sopenharmony_ci#include <linux/firmware.h>
418c2ecf20Sopenharmony_ci#include <linux/ihex.h>
428c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
438c2ecf20Sopenharmony_ci#include <linux/usb.h>
448c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
458c2ecf20Sopenharmony_ci#include "io_edgeport.h"
468c2ecf20Sopenharmony_ci#include "io_ionsp.h"		/* info for the iosp messages */
478c2ecf20Sopenharmony_ci#include "io_16654.h"		/* 16654 UART defines */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli"
508c2ecf20Sopenharmony_ci#define DRIVER_DESC "Edgeport USB Serial Driver"
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define MAX_NAME_LEN		64
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define OPEN_TIMEOUT		(5*HZ)		/* 5 seconds */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic const struct usb_device_id edgeport_2port_id_table[] = {
578c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) },
588c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) },
598c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) },
608c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) },
618c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) },
628c2ecf20Sopenharmony_ci	{ }
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct usb_device_id edgeport_4port_id_table[] = {
668c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) },
678c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_RAPIDPORT_4) },
688c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4T) },
698c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
708c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) },
718c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
728c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) },
738c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
748c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
758c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
768c2ecf20Sopenharmony_ci	{ }
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic const struct usb_device_id edgeport_8port_id_table[] = {
808c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) },
818c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
828c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) },
838c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
848c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
858c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
868c2ecf20Sopenharmony_ci	{ }
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic const struct usb_device_id Epic_port_id_table[] = {
908c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
918c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
928c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
938c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
948c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
958c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
968c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
978c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
988c2ecf20Sopenharmony_ci	{ }
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* Devices that this driver supports */
1028c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table_combined[] = {
1038c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4) },
1048c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_RAPIDPORT_4) },
1058c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4T) },
1068c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_MT4X56USB) },
1078c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2) },
1088c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4I) },
1098c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2I) },
1108c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_421) },
1118c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_21) },
1128c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) },
1138c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8) },
1148c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_2_DIN) },
1158c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_4_DIN) },
1168c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) },
1178c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) },
1188c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) },
1198c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_COMPATIBLE) },
1208c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION,	ION_DEVICE_ID_EDGEPORT_8I) },
1218c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) },
1228c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) },
1238c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) },
1248c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) },
1258c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) },
1268c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) },
1278c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) },
1288c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) },
1298c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) },
1308c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) },
1318c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) },
1328c2ecf20Sopenharmony_ci	{ } /* Terminating entry */
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table_combined);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* receive port state */
1398c2ecf20Sopenharmony_cienum RXSTATE {
1408c2ecf20Sopenharmony_ci	EXPECT_HDR1 = 0,    /* Expect header byte 1 */
1418c2ecf20Sopenharmony_ci	EXPECT_HDR2 = 1,    /* Expect header byte 2 */
1428c2ecf20Sopenharmony_ci	EXPECT_DATA = 2,    /* Expect 'RxBytesRemaining' data */
1438c2ecf20Sopenharmony_ci	EXPECT_HDR3 = 3,    /* Expect header byte 3 (for status hdrs only) */
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/* Transmit Fifo
1488c2ecf20Sopenharmony_ci * This Transmit queue is an extension of the edgeport Rx buffer.
1498c2ecf20Sopenharmony_ci * The maximum amount of data buffered in both the edgeport
1508c2ecf20Sopenharmony_ci * Rx buffer (maxTxCredits) and this buffer will never exceed maxTxCredits.
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistruct TxFifo {
1538c2ecf20Sopenharmony_ci	unsigned int	head;	/* index to head pointer (write) */
1548c2ecf20Sopenharmony_ci	unsigned int	tail;	/* index to tail pointer (read)  */
1558c2ecf20Sopenharmony_ci	unsigned int	count;	/* Bytes in queue */
1568c2ecf20Sopenharmony_ci	unsigned int	size;	/* Max size of queue (equal to Max number of TxCredits) */
1578c2ecf20Sopenharmony_ci	unsigned char	*fifo;	/* allocated Buffer */
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* This structure holds all of the local port information */
1618c2ecf20Sopenharmony_cistruct edgeport_port {
1628c2ecf20Sopenharmony_ci	__u16			txCredits;		/* our current credits for this port */
1638c2ecf20Sopenharmony_ci	__u16			maxTxCredits;		/* the max size of the port */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	struct TxFifo		txfifo;			/* transmit fifo -- size will be maxTxCredits */
1668c2ecf20Sopenharmony_ci	struct urb		*write_urb;		/* write URB for this port */
1678c2ecf20Sopenharmony_ci	bool			write_in_progress;	/* 'true' while a write URB is outstanding */
1688c2ecf20Sopenharmony_ci	spinlock_t		ep_lock;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	__u8			shadowLCR;		/* last LCR value received */
1718c2ecf20Sopenharmony_ci	__u8			shadowMCR;		/* last MCR value received */
1728c2ecf20Sopenharmony_ci	__u8			shadowMSR;		/* last MSR value received */
1738c2ecf20Sopenharmony_ci	__u8			shadowLSR;		/* last LSR value received */
1748c2ecf20Sopenharmony_ci	__u8			shadowXonChar;		/* last value set as XON char in Edgeport */
1758c2ecf20Sopenharmony_ci	__u8			shadowXoffChar;		/* last value set as XOFF char in Edgeport */
1768c2ecf20Sopenharmony_ci	__u8			validDataMask;
1778c2ecf20Sopenharmony_ci	__u32			baudRate;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	bool			open;
1808c2ecf20Sopenharmony_ci	bool			openPending;
1818c2ecf20Sopenharmony_ci	bool			commandPending;
1828c2ecf20Sopenharmony_ci	bool			closePending;
1838c2ecf20Sopenharmony_ci	bool			chaseResponsePending;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	wait_queue_head_t	wait_chase;		/* for handling sleeping while waiting for chase to finish */
1868c2ecf20Sopenharmony_ci	wait_queue_head_t	wait_open;		/* for handling sleeping while waiting for open to finish */
1878c2ecf20Sopenharmony_ci	wait_queue_head_t	wait_command;		/* for handling sleeping while waiting for command to finish */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	struct usb_serial_port	*port;			/* loop back to the owner of this object */
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/* This structure holds all of the individual device information */
1948c2ecf20Sopenharmony_cistruct edgeport_serial {
1958c2ecf20Sopenharmony_ci	char			name[MAX_NAME_LEN+2];		/* string name of this device */
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	struct edge_manuf_descriptor	manuf_descriptor;	/* the manufacturer descriptor */
1988c2ecf20Sopenharmony_ci	struct edge_boot_descriptor	boot_descriptor;	/* the boot firmware descriptor */
1998c2ecf20Sopenharmony_ci	struct edgeport_product_info	product_info;		/* Product Info */
2008c2ecf20Sopenharmony_ci	struct edge_compatibility_descriptor epic_descriptor;	/* Edgeport compatible descriptor */
2018c2ecf20Sopenharmony_ci	int			is_epic;			/* flag if EPiC device or not */
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	__u8			interrupt_in_endpoint;		/* the interrupt endpoint handle */
2048c2ecf20Sopenharmony_ci	unsigned char		*interrupt_in_buffer;		/* the buffer we use for the interrupt endpoint */
2058c2ecf20Sopenharmony_ci	struct urb		*interrupt_read_urb;		/* our interrupt urb */
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	__u8			bulk_in_endpoint;		/* the bulk in endpoint handle */
2088c2ecf20Sopenharmony_ci	unsigned char		*bulk_in_buffer;		/* the buffer we use for the bulk in endpoint */
2098c2ecf20Sopenharmony_ci	struct urb		*read_urb;			/* our bulk read urb */
2108c2ecf20Sopenharmony_ci	bool			read_in_progress;
2118c2ecf20Sopenharmony_ci	spinlock_t		es_lock;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	__u8			bulk_out_endpoint;		/* the bulk out endpoint handle */
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	__s16			rxBytesAvail;			/* the number of bytes that we need to read from this device */
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	enum RXSTATE		rxState;			/* the current state of the bulk receive processor */
2188c2ecf20Sopenharmony_ci	__u8			rxHeader1;			/* receive header byte 1 */
2198c2ecf20Sopenharmony_ci	__u8			rxHeader2;			/* receive header byte 2 */
2208c2ecf20Sopenharmony_ci	__u8			rxHeader3;			/* receive header byte 3 */
2218c2ecf20Sopenharmony_ci	__u8			rxPort;				/* the port that we are currently receiving data for */
2228c2ecf20Sopenharmony_ci	__u8			rxStatusCode;			/* the receive status code */
2238c2ecf20Sopenharmony_ci	__u8			rxStatusParam;			/* the receive status paramater */
2248c2ecf20Sopenharmony_ci	__s16			rxBytesRemaining;		/* the number of port bytes left to read */
2258c2ecf20Sopenharmony_ci	struct usb_serial	*serial;			/* loop back to the owner of this object */
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/* baud rate information */
2298c2ecf20Sopenharmony_cistruct divisor_table_entry {
2308c2ecf20Sopenharmony_ci	__u32   BaudRate;
2318c2ecf20Sopenharmony_ci	__u16  Divisor;
2328c2ecf20Sopenharmony_ci};
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci/*
2358c2ecf20Sopenharmony_ci * Define table of divisors for Rev A EdgePort/4 hardware
2368c2ecf20Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and
2378c2ecf20Sopenharmony_ci * MCR.7 = 0.
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = {
2418c2ecf20Sopenharmony_ci	{   50,		4608},
2428c2ecf20Sopenharmony_ci	{   75,		3072},
2438c2ecf20Sopenharmony_ci	{   110,	2095},	/* 2094.545455 => 230450   => .0217 % over */
2448c2ecf20Sopenharmony_ci	{   134,	1713},	/* 1713.011152 => 230398.5 => .00065% under */
2458c2ecf20Sopenharmony_ci	{   150,	1536},
2468c2ecf20Sopenharmony_ci	{   300,	768},
2478c2ecf20Sopenharmony_ci	{   600,	384},
2488c2ecf20Sopenharmony_ci	{   1200,	192},
2498c2ecf20Sopenharmony_ci	{   1800,	128},
2508c2ecf20Sopenharmony_ci	{   2400,	96},
2518c2ecf20Sopenharmony_ci	{   4800,	48},
2528c2ecf20Sopenharmony_ci	{   7200,	32},
2538c2ecf20Sopenharmony_ci	{   9600,	24},
2548c2ecf20Sopenharmony_ci	{   14400,	16},
2558c2ecf20Sopenharmony_ci	{   19200,	12},
2568c2ecf20Sopenharmony_ci	{   38400,	6},
2578c2ecf20Sopenharmony_ci	{   57600,	4},
2588c2ecf20Sopenharmony_ci	{   115200,	2},
2598c2ecf20Sopenharmony_ci	{   230400,	1},
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/* Number of outstanding Command Write Urbs */
2638c2ecf20Sopenharmony_cistatic atomic_t CmdUrbs = ATOMIC_INIT(0);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/* local function prototypes */
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/* function prototypes for all URB callbacks */
2698c2ecf20Sopenharmony_cistatic void edge_interrupt_callback(struct urb *urb);
2708c2ecf20Sopenharmony_cistatic void edge_bulk_in_callback(struct urb *urb);
2718c2ecf20Sopenharmony_cistatic void edge_bulk_out_data_callback(struct urb *urb);
2728c2ecf20Sopenharmony_cistatic void edge_bulk_out_cmd_callback(struct urb *urb);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/* function prototypes for the usbserial callbacks */
2758c2ecf20Sopenharmony_cistatic int edge_open(struct tty_struct *tty, struct usb_serial_port *port);
2768c2ecf20Sopenharmony_cistatic void edge_close(struct usb_serial_port *port);
2778c2ecf20Sopenharmony_cistatic int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
2788c2ecf20Sopenharmony_ci					const unsigned char *buf, int count);
2798c2ecf20Sopenharmony_cistatic int edge_write_room(struct tty_struct *tty);
2808c2ecf20Sopenharmony_cistatic int edge_chars_in_buffer(struct tty_struct *tty);
2818c2ecf20Sopenharmony_cistatic void edge_throttle(struct tty_struct *tty);
2828c2ecf20Sopenharmony_cistatic void edge_unthrottle(struct tty_struct *tty);
2838c2ecf20Sopenharmony_cistatic void edge_set_termios(struct tty_struct *tty,
2848c2ecf20Sopenharmony_ci					struct usb_serial_port *port,
2858c2ecf20Sopenharmony_ci					struct ktermios *old_termios);
2868c2ecf20Sopenharmony_cistatic int  edge_ioctl(struct tty_struct *tty,
2878c2ecf20Sopenharmony_ci					unsigned int cmd, unsigned long arg);
2888c2ecf20Sopenharmony_cistatic void edge_break(struct tty_struct *tty, int break_state);
2898c2ecf20Sopenharmony_cistatic int  edge_tiocmget(struct tty_struct *tty);
2908c2ecf20Sopenharmony_cistatic int  edge_tiocmset(struct tty_struct *tty,
2918c2ecf20Sopenharmony_ci					unsigned int set, unsigned int clear);
2928c2ecf20Sopenharmony_cistatic int  edge_startup(struct usb_serial *serial);
2938c2ecf20Sopenharmony_cistatic void edge_disconnect(struct usb_serial *serial);
2948c2ecf20Sopenharmony_cistatic void edge_release(struct usb_serial *serial);
2958c2ecf20Sopenharmony_cistatic int edge_port_probe(struct usb_serial_port *port);
2968c2ecf20Sopenharmony_cistatic int edge_port_remove(struct usb_serial_port *port);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/* function prototypes for all of our local functions */
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void  process_rcvd_data(struct edgeport_serial *edge_serial,
3018c2ecf20Sopenharmony_ci				unsigned char *buffer, __u16 bufferLength);
3028c2ecf20Sopenharmony_cistatic void process_rcvd_status(struct edgeport_serial *edge_serial,
3038c2ecf20Sopenharmony_ci				__u8 byte2, __u8 byte3);
3048c2ecf20Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
3058c2ecf20Sopenharmony_ci		int length);
3068c2ecf20Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr);
3078c2ecf20Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
3088c2ecf20Sopenharmony_ci				__u8 lsr, __u8 data);
3098c2ecf20Sopenharmony_cistatic int  send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command,
3108c2ecf20Sopenharmony_ci				__u8 param);
3118c2ecf20Sopenharmony_cistatic int  calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor);
3128c2ecf20Sopenharmony_cistatic int  send_cmd_write_baud_rate(struct edgeport_port *edge_port,
3138c2ecf20Sopenharmony_ci				int baudRate);
3148c2ecf20Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty,
3158c2ecf20Sopenharmony_ci				struct edgeport_port *edge_port,
3168c2ecf20Sopenharmony_ci				struct ktermios *old_termios);
3178c2ecf20Sopenharmony_cistatic int  send_cmd_write_uart_register(struct edgeport_port *edge_port,
3188c2ecf20Sopenharmony_ci				__u8 regNum, __u8 regValue);
3198c2ecf20Sopenharmony_cistatic int  write_cmd_usb(struct edgeport_port *edge_port,
3208c2ecf20Sopenharmony_ci				unsigned char *buffer, int writeLength);
3218c2ecf20Sopenharmony_cistatic void send_more_port_data(struct edgeport_serial *edge_serial,
3228c2ecf20Sopenharmony_ci				struct edgeport_port *edge_port);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
3258c2ecf20Sopenharmony_ci					__u16 length, const __u8 *data);
3268c2ecf20Sopenharmony_cistatic int rom_read(struct usb_serial *serial, __u16 extAddr, __u16 addr,
3278c2ecf20Sopenharmony_ci						__u16 length, __u8 *data);
3288c2ecf20Sopenharmony_cistatic int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
3298c2ecf20Sopenharmony_ci					__u16 length, const __u8 *data);
3308c2ecf20Sopenharmony_cistatic void get_manufacturing_desc(struct edgeport_serial *edge_serial);
3318c2ecf20Sopenharmony_cistatic void get_boot_desc(struct edgeport_serial *edge_serial);
3328c2ecf20Sopenharmony_cistatic void load_application_firmware(struct edgeport_serial *edge_serial);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic void unicode_to_ascii(char *string, int buflen,
3358c2ecf20Sopenharmony_ci				__le16 *unicode, int unicode_size);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci/* ************************************************************************ */
3398c2ecf20Sopenharmony_ci/* ************************************************************************ */
3408c2ecf20Sopenharmony_ci/* ************************************************************************ */
3418c2ecf20Sopenharmony_ci/* ************************************************************************ */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/************************************************************************
3448c2ecf20Sopenharmony_ci *									*
3458c2ecf20Sopenharmony_ci * update_edgeport_E2PROM()	Compare current versions of		*
3468c2ecf20Sopenharmony_ci *				Boot ROM and Manufacture 		*
3478c2ecf20Sopenharmony_ci *				Descriptors with versions		*
3488c2ecf20Sopenharmony_ci *				embedded in this driver			*
3498c2ecf20Sopenharmony_ci *									*
3508c2ecf20Sopenharmony_ci ************************************************************************/
3518c2ecf20Sopenharmony_cistatic void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
3548c2ecf20Sopenharmony_ci	__u32 BootCurVer;
3558c2ecf20Sopenharmony_ci	__u32 BootNewVer;
3568c2ecf20Sopenharmony_ci	__u8 BootMajorVersion;
3578c2ecf20Sopenharmony_ci	__u8 BootMinorVersion;
3588c2ecf20Sopenharmony_ci	__u16 BootBuildNumber;
3598c2ecf20Sopenharmony_ci	__u32 Bootaddr;
3608c2ecf20Sopenharmony_ci	const struct ihex_binrec *rec;
3618c2ecf20Sopenharmony_ci	const struct firmware *fw;
3628c2ecf20Sopenharmony_ci	const char *fw_name;
3638c2ecf20Sopenharmony_ci	int response;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	switch (edge_serial->product_info.iDownloadFile) {
3668c2ecf20Sopenharmony_ci	case EDGE_DOWNLOAD_FILE_I930:
3678c2ecf20Sopenharmony_ci		fw_name	= "edgeport/boot.fw";
3688c2ecf20Sopenharmony_ci		break;
3698c2ecf20Sopenharmony_ci	case EDGE_DOWNLOAD_FILE_80251:
3708c2ecf20Sopenharmony_ci		fw_name	= "edgeport/boot2.fw";
3718c2ecf20Sopenharmony_ci		break;
3728c2ecf20Sopenharmony_ci	default:
3738c2ecf20Sopenharmony_ci		return;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	response = request_ihex_firmware(&fw, fw_name,
3778c2ecf20Sopenharmony_ci					 &edge_serial->serial->dev->dev);
3788c2ecf20Sopenharmony_ci	if (response) {
3798c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to load image \"%s\" err %d\n",
3808c2ecf20Sopenharmony_ci		       fw_name, response);
3818c2ecf20Sopenharmony_ci		return;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	rec = (const struct ihex_binrec *)fw->data;
3858c2ecf20Sopenharmony_ci	BootMajorVersion = rec->data[0];
3868c2ecf20Sopenharmony_ci	BootMinorVersion = rec->data[1];
3878c2ecf20Sopenharmony_ci	BootBuildNumber = (rec->data[2] << 8) | rec->data[3];
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Check Boot Image Version */
3908c2ecf20Sopenharmony_ci	BootCurVer = (edge_serial->boot_descriptor.MajorVersion << 24) +
3918c2ecf20Sopenharmony_ci		     (edge_serial->boot_descriptor.MinorVersion << 16) +
3928c2ecf20Sopenharmony_ci		      le16_to_cpu(edge_serial->boot_descriptor.BuildNumber);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	BootNewVer = (BootMajorVersion << 24) +
3958c2ecf20Sopenharmony_ci		     (BootMinorVersion << 16) +
3968c2ecf20Sopenharmony_ci		      BootBuildNumber;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	dev_dbg(dev, "Current Boot Image version %d.%d.%d\n",
3998c2ecf20Sopenharmony_ci	    edge_serial->boot_descriptor.MajorVersion,
4008c2ecf20Sopenharmony_ci	    edge_serial->boot_descriptor.MinorVersion,
4018c2ecf20Sopenharmony_ci	    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (BootNewVer > BootCurVer) {
4058c2ecf20Sopenharmony_ci		dev_dbg(dev, "**Update Boot Image from %d.%d.%d to %d.%d.%d\n",
4068c2ecf20Sopenharmony_ci		    edge_serial->boot_descriptor.MajorVersion,
4078c2ecf20Sopenharmony_ci		    edge_serial->boot_descriptor.MinorVersion,
4088c2ecf20Sopenharmony_ci		    le16_to_cpu(edge_serial->boot_descriptor.BuildNumber),
4098c2ecf20Sopenharmony_ci		    BootMajorVersion, BootMinorVersion, BootBuildNumber);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		dev_dbg(dev, "Downloading new Boot Image\n");
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		for (rec = ihex_next_binrec(rec); rec;
4148c2ecf20Sopenharmony_ci		     rec = ihex_next_binrec(rec)) {
4158c2ecf20Sopenharmony_ci			Bootaddr = be32_to_cpu(rec->addr);
4168c2ecf20Sopenharmony_ci			response = rom_write(edge_serial->serial,
4178c2ecf20Sopenharmony_ci					     Bootaddr >> 16,
4188c2ecf20Sopenharmony_ci					     Bootaddr & 0xFFFF,
4198c2ecf20Sopenharmony_ci					     be16_to_cpu(rec->len),
4208c2ecf20Sopenharmony_ci					     &rec->data[0]);
4218c2ecf20Sopenharmony_ci			if (response < 0) {
4228c2ecf20Sopenharmony_ci				dev_err(&edge_serial->serial->dev->dev,
4238c2ecf20Sopenharmony_ci					"rom_write failed (%x, %x, %d)\n",
4248c2ecf20Sopenharmony_ci					Bootaddr >> 16, Bootaddr & 0xFFFF,
4258c2ecf20Sopenharmony_ci					be16_to_cpu(rec->len));
4268c2ecf20Sopenharmony_ci				break;
4278c2ecf20Sopenharmony_ci			}
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci	} else {
4308c2ecf20Sopenharmony_ci		dev_dbg(dev, "Boot Image -- already up to date\n");
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	release_firmware(fw);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci#if 0
4368c2ecf20Sopenharmony_ci/************************************************************************
4378c2ecf20Sopenharmony_ci *
4388c2ecf20Sopenharmony_ci *  Get string descriptor from device
4398c2ecf20Sopenharmony_ci *
4408c2ecf20Sopenharmony_ci ************************************************************************/
4418c2ecf20Sopenharmony_cistatic int get_string_desc(struct usb_device *dev, int Id,
4428c2ecf20Sopenharmony_ci				struct usb_string_descriptor **pRetDesc)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct usb_string_descriptor StringDesc;
4458c2ecf20Sopenharmony_ci	struct usb_string_descriptor *pStringDesc;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "%s - USB String ID = %d\n", __func__, Id);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (!usb_get_descriptor(dev, USB_DT_STRING, Id, &StringDesc,
4508c2ecf20Sopenharmony_ci						sizeof(StringDesc)))
4518c2ecf20Sopenharmony_ci		return 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	pStringDesc = kmalloc(StringDesc.bLength, GFP_KERNEL);
4548c2ecf20Sopenharmony_ci	if (!pStringDesc)
4558c2ecf20Sopenharmony_ci		return -1;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (!usb_get_descriptor(dev, USB_DT_STRING, Id, pStringDesc,
4588c2ecf20Sopenharmony_ci							StringDesc.bLength)) {
4598c2ecf20Sopenharmony_ci		kfree(pStringDesc);
4608c2ecf20Sopenharmony_ci		return -1;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	*pRetDesc = pStringDesc;
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci#endif
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic void dump_product_info(struct edgeport_serial *edge_serial,
4698c2ecf20Sopenharmony_ci			      struct edgeport_product_info *product_info)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Dump Product Info structure */
4748c2ecf20Sopenharmony_ci	dev_dbg(dev, "**Product Information:\n");
4758c2ecf20Sopenharmony_ci	dev_dbg(dev, "  ProductId             %x\n", product_info->ProductId);
4768c2ecf20Sopenharmony_ci	dev_dbg(dev, "  NumPorts              %d\n", product_info->NumPorts);
4778c2ecf20Sopenharmony_ci	dev_dbg(dev, "  ProdInfoVer           %d\n", product_info->ProdInfoVer);
4788c2ecf20Sopenharmony_ci	dev_dbg(dev, "  IsServer              %d\n", product_info->IsServer);
4798c2ecf20Sopenharmony_ci	dev_dbg(dev, "  IsRS232               %d\n", product_info->IsRS232);
4808c2ecf20Sopenharmony_ci	dev_dbg(dev, "  IsRS422               %d\n", product_info->IsRS422);
4818c2ecf20Sopenharmony_ci	dev_dbg(dev, "  IsRS485               %d\n", product_info->IsRS485);
4828c2ecf20Sopenharmony_ci	dev_dbg(dev, "  RomSize               %d\n", product_info->RomSize);
4838c2ecf20Sopenharmony_ci	dev_dbg(dev, "  RamSize               %d\n", product_info->RamSize);
4848c2ecf20Sopenharmony_ci	dev_dbg(dev, "  CpuRev                %x\n", product_info->CpuRev);
4858c2ecf20Sopenharmony_ci	dev_dbg(dev, "  BoardRev              %x\n", product_info->BoardRev);
4868c2ecf20Sopenharmony_ci	dev_dbg(dev, "  BootMajorVersion      %d.%d.%d\n",
4878c2ecf20Sopenharmony_ci		product_info->BootMajorVersion,
4888c2ecf20Sopenharmony_ci		product_info->BootMinorVersion,
4898c2ecf20Sopenharmony_ci		le16_to_cpu(product_info->BootBuildNumber));
4908c2ecf20Sopenharmony_ci	dev_dbg(dev, "  FirmwareMajorVersion  %d.%d.%d\n",
4918c2ecf20Sopenharmony_ci		product_info->FirmwareMajorVersion,
4928c2ecf20Sopenharmony_ci		product_info->FirmwareMinorVersion,
4938c2ecf20Sopenharmony_ci		le16_to_cpu(product_info->FirmwareBuildNumber));
4948c2ecf20Sopenharmony_ci	dev_dbg(dev, "  ManufactureDescDate   %d/%d/%d\n",
4958c2ecf20Sopenharmony_ci		product_info->ManufactureDescDate[0],
4968c2ecf20Sopenharmony_ci		product_info->ManufactureDescDate[1],
4978c2ecf20Sopenharmony_ci		product_info->ManufactureDescDate[2]+1900);
4988c2ecf20Sopenharmony_ci	dev_dbg(dev, "  iDownloadFile         0x%x\n",
4998c2ecf20Sopenharmony_ci		product_info->iDownloadFile);
5008c2ecf20Sopenharmony_ci	dev_dbg(dev, "  EpicVer               %d\n", product_info->EpicVer);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic void get_product_info(struct edgeport_serial *edge_serial)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct edgeport_product_info *product_info = &edge_serial->product_info;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	memset(product_info, 0, sizeof(struct edgeport_product_info));
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	product_info->ProductId = (__u16)(le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct) & ~ION_DEVICE_ID_80251_NETCHIP);
5108c2ecf20Sopenharmony_ci	product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts;
5118c2ecf20Sopenharmony_ci	product_info->ProdInfoVer = 0;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	product_info->RomSize = edge_serial->manuf_descriptor.RomSize;
5148c2ecf20Sopenharmony_ci	product_info->RamSize = edge_serial->manuf_descriptor.RamSize;
5158c2ecf20Sopenharmony_ci	product_info->CpuRev = edge_serial->manuf_descriptor.CpuRev;
5168c2ecf20Sopenharmony_ci	product_info->BoardRev = edge_serial->manuf_descriptor.BoardRev;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	product_info->BootMajorVersion =
5198c2ecf20Sopenharmony_ci				edge_serial->boot_descriptor.MajorVersion;
5208c2ecf20Sopenharmony_ci	product_info->BootMinorVersion =
5218c2ecf20Sopenharmony_ci				edge_serial->boot_descriptor.MinorVersion;
5228c2ecf20Sopenharmony_ci	product_info->BootBuildNumber =
5238c2ecf20Sopenharmony_ci				edge_serial->boot_descriptor.BuildNumber;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	memcpy(product_info->ManufactureDescDate,
5268c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate,
5278c2ecf20Sopenharmony_ci			sizeof(edge_serial->manuf_descriptor.DescDate));
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* check if this is 2nd generation hardware */
5308c2ecf20Sopenharmony_ci	if (le16_to_cpu(edge_serial->serial->dev->descriptor.idProduct)
5318c2ecf20Sopenharmony_ci					    & ION_DEVICE_ID_80251_NETCHIP)
5328c2ecf20Sopenharmony_ci		product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251;
5338c2ecf20Sopenharmony_ci	else
5348c2ecf20Sopenharmony_ci		product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* Determine Product type and set appropriate flags */
5378c2ecf20Sopenharmony_ci	switch (DEVICE_ID_FROM_USB_PRODUCT_ID(product_info->ProductId)) {
5388c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_COMPATIBLE:
5398c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4T:
5408c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4:
5418c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_2:
5428c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU:
5438c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_8:
5448c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_421:
5458c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_21:
5468c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_2_DIN:
5478c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4_DIN:
5488c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU:
5498c2ecf20Sopenharmony_ci		product_info->IsRS232 = 1;
5508c2ecf20Sopenharmony_ci		break;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_2I:	/* Edgeport/2 RS422/RS485 */
5538c2ecf20Sopenharmony_ci		product_info->IsRS422 = 1;
5548c2ecf20Sopenharmony_ci		product_info->IsRS485 = 1;
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_8I:	/* Edgeport/4 RS422 */
5588c2ecf20Sopenharmony_ci	case ION_DEVICE_ID_EDGEPORT_4I:	/* Edgeport/4 RS422 */
5598c2ecf20Sopenharmony_ci		product_info->IsRS422 = 1;
5608c2ecf20Sopenharmony_ci		break;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	dump_product_info(edge_serial, product_info);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int get_epic_descriptor(struct edgeport_serial *ep)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	int result;
5698c2ecf20Sopenharmony_ci	struct usb_serial *serial = ep->serial;
5708c2ecf20Sopenharmony_ci	struct edgeport_product_info *product_info = &ep->product_info;
5718c2ecf20Sopenharmony_ci	struct edge_compatibility_descriptor *epic;
5728c2ecf20Sopenharmony_ci	struct edge_compatibility_bits *bits;
5738c2ecf20Sopenharmony_ci	struct device *dev = &serial->dev->dev;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	ep->is_epic = 0;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	epic = kmalloc(sizeof(*epic), GFP_KERNEL);
5788c2ecf20Sopenharmony_ci	if (!epic)
5798c2ecf20Sopenharmony_ci		return -ENOMEM;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
5828c2ecf20Sopenharmony_ci				 USB_REQUEST_ION_GET_EPIC_DESC,
5838c2ecf20Sopenharmony_ci				 0xC0, 0x00, 0x00,
5848c2ecf20Sopenharmony_ci				 epic, sizeof(*epic),
5858c2ecf20Sopenharmony_ci				 300);
5868c2ecf20Sopenharmony_ci	if (result == sizeof(*epic)) {
5878c2ecf20Sopenharmony_ci		ep->is_epic = 1;
5888c2ecf20Sopenharmony_ci		memcpy(&ep->epic_descriptor, epic, sizeof(*epic));
5898c2ecf20Sopenharmony_ci		memset(product_info, 0, sizeof(struct edgeport_product_info));
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		product_info->NumPorts = epic->NumPorts;
5928c2ecf20Sopenharmony_ci		product_info->ProdInfoVer = 0;
5938c2ecf20Sopenharmony_ci		product_info->FirmwareMajorVersion = epic->MajorVersion;
5948c2ecf20Sopenharmony_ci		product_info->FirmwareMinorVersion = epic->MinorVersion;
5958c2ecf20Sopenharmony_ci		product_info->FirmwareBuildNumber = epic->BuildNumber;
5968c2ecf20Sopenharmony_ci		product_info->iDownloadFile = epic->iDownloadFile;
5978c2ecf20Sopenharmony_ci		product_info->EpicVer = epic->EpicVer;
5988c2ecf20Sopenharmony_ci		product_info->Epic = epic->Supports;
5998c2ecf20Sopenharmony_ci		product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE;
6008c2ecf20Sopenharmony_ci		dump_product_info(ep, product_info);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		bits = &ep->epic_descriptor.Supports;
6038c2ecf20Sopenharmony_ci		dev_dbg(dev, "**EPIC descriptor:\n");
6048c2ecf20Sopenharmony_ci		dev_dbg(dev, "  VendEnableSuspend: %s\n", bits->VendEnableSuspend ? "TRUE": "FALSE");
6058c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPOpen         : %s\n", bits->IOSPOpen	? "TRUE": "FALSE");
6068c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPClose        : %s\n", bits->IOSPClose	? "TRUE": "FALSE");
6078c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPChase        : %s\n", bits->IOSPChase	? "TRUE": "FALSE");
6088c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPSetRxFlow    : %s\n", bits->IOSPSetRxFlow	? "TRUE": "FALSE");
6098c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPSetTxFlow    : %s\n", bits->IOSPSetTxFlow	? "TRUE": "FALSE");
6108c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPSetXChar     : %s\n", bits->IOSPSetXChar	? "TRUE": "FALSE");
6118c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPRxCheck      : %s\n", bits->IOSPRxCheck	? "TRUE": "FALSE");
6128c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPSetClrBreak  : %s\n", bits->IOSPSetClrBreak	? "TRUE": "FALSE");
6138c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPWriteMCR     : %s\n", bits->IOSPWriteMCR	? "TRUE": "FALSE");
6148c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPWriteLCR     : %s\n", bits->IOSPWriteLCR	? "TRUE": "FALSE");
6158c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IOSPSetBaudRate  : %s\n", bits->IOSPSetBaudRate	? "TRUE": "FALSE");
6168c2ecf20Sopenharmony_ci		dev_dbg(dev, "  TrueEdgeport     : %s\n", bits->TrueEdgeport	? "TRUE": "FALSE");
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		result = 0;
6198c2ecf20Sopenharmony_ci	} else if (result >= 0) {
6208c2ecf20Sopenharmony_ci		dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n",
6218c2ecf20Sopenharmony_ci			 result);
6228c2ecf20Sopenharmony_ci		result = -EIO;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	kfree(epic);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return result;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/************************************************************************/
6328c2ecf20Sopenharmony_ci/************************************************************************/
6338c2ecf20Sopenharmony_ci/*            U S B  C A L L B A C K   F U N C T I O N S                */
6348c2ecf20Sopenharmony_ci/*            U S B  C A L L B A C K   F U N C T I O N S                */
6358c2ecf20Sopenharmony_ci/************************************************************************/
6368c2ecf20Sopenharmony_ci/************************************************************************/
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci/*****************************************************************************
6398c2ecf20Sopenharmony_ci * edge_interrupt_callback
6408c2ecf20Sopenharmony_ci *	this is the callback function for when we have received data on the
6418c2ecf20Sopenharmony_ci *	interrupt endpoint.
6428c2ecf20Sopenharmony_ci *****************************************************************************/
6438c2ecf20Sopenharmony_cistatic void edge_interrupt_callback(struct urb *urb)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial = urb->context;
6468c2ecf20Sopenharmony_ci	struct device *dev;
6478c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port;
6488c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
6498c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
6508c2ecf20Sopenharmony_ci	int length = urb->actual_length;
6518c2ecf20Sopenharmony_ci	unsigned long flags;
6528c2ecf20Sopenharmony_ci	int bytes_avail;
6538c2ecf20Sopenharmony_ci	int position;
6548c2ecf20Sopenharmony_ci	int txCredits;
6558c2ecf20Sopenharmony_ci	int portNumber;
6568c2ecf20Sopenharmony_ci	int result;
6578c2ecf20Sopenharmony_ci	int status = urb->status;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	switch (status) {
6608c2ecf20Sopenharmony_ci	case 0:
6618c2ecf20Sopenharmony_ci		/* success */
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	case -ECONNRESET:
6648c2ecf20Sopenharmony_ci	case -ENOENT:
6658c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
6668c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
6678c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
6688c2ecf20Sopenharmony_ci		return;
6698c2ecf20Sopenharmony_ci	default:
6708c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
6718c2ecf20Sopenharmony_ci		goto exit;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	dev = &edge_serial->serial->dev->dev;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* process this interrupt-read even if there are no ports open */
6778c2ecf20Sopenharmony_ci	if (length) {
6788c2ecf20Sopenharmony_ci		usb_serial_debug_data(dev, __func__, length, data);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		if (length > 1) {
6818c2ecf20Sopenharmony_ci			bytes_avail = data[0] | (data[1] << 8);
6828c2ecf20Sopenharmony_ci			if (bytes_avail) {
6838c2ecf20Sopenharmony_ci				spin_lock_irqsave(&edge_serial->es_lock, flags);
6848c2ecf20Sopenharmony_ci				edge_serial->rxBytesAvail += bytes_avail;
6858c2ecf20Sopenharmony_ci				dev_dbg(dev,
6868c2ecf20Sopenharmony_ci					"%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n",
6878c2ecf20Sopenharmony_ci					__func__, bytes_avail,
6888c2ecf20Sopenharmony_ci					edge_serial->rxBytesAvail,
6898c2ecf20Sopenharmony_ci					edge_serial->read_in_progress);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci				if (edge_serial->rxBytesAvail > 0 &&
6928c2ecf20Sopenharmony_ci				    !edge_serial->read_in_progress) {
6938c2ecf20Sopenharmony_ci					dev_dbg(dev, "%s - posting a read\n", __func__);
6948c2ecf20Sopenharmony_ci					edge_serial->read_in_progress = true;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci					/* we have pending bytes on the
6978c2ecf20Sopenharmony_ci					   bulk in pipe, send a request */
6988c2ecf20Sopenharmony_ci					result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
6998c2ecf20Sopenharmony_ci					if (result) {
7008c2ecf20Sopenharmony_ci						dev_err(dev,
7018c2ecf20Sopenharmony_ci							"%s - usb_submit_urb(read bulk) failed with result = %d\n",
7028c2ecf20Sopenharmony_ci							__func__, result);
7038c2ecf20Sopenharmony_ci						edge_serial->read_in_progress = false;
7048c2ecf20Sopenharmony_ci					}
7058c2ecf20Sopenharmony_ci				}
7068c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&edge_serial->es_lock,
7078c2ecf20Sopenharmony_ci						       flags);
7088c2ecf20Sopenharmony_ci			}
7098c2ecf20Sopenharmony_ci		}
7108c2ecf20Sopenharmony_ci		/* grab the txcredits for the ports if available */
7118c2ecf20Sopenharmony_ci		position = 2;
7128c2ecf20Sopenharmony_ci		portNumber = 0;
7138c2ecf20Sopenharmony_ci		while ((position < length - 1) &&
7148c2ecf20Sopenharmony_ci				(portNumber < edge_serial->serial->num_ports)) {
7158c2ecf20Sopenharmony_ci			txCredits = data[position] | (data[position+1] << 8);
7168c2ecf20Sopenharmony_ci			if (txCredits) {
7178c2ecf20Sopenharmony_ci				port = edge_serial->serial->port[portNumber];
7188c2ecf20Sopenharmony_ci				edge_port = usb_get_serial_port_data(port);
7198c2ecf20Sopenharmony_ci				if (edge_port && edge_port->open) {
7208c2ecf20Sopenharmony_ci					spin_lock_irqsave(&edge_port->ep_lock,
7218c2ecf20Sopenharmony_ci							  flags);
7228c2ecf20Sopenharmony_ci					edge_port->txCredits += txCredits;
7238c2ecf20Sopenharmony_ci					spin_unlock_irqrestore(&edge_port->ep_lock,
7248c2ecf20Sopenharmony_ci							       flags);
7258c2ecf20Sopenharmony_ci					dev_dbg(dev, "%s - txcredits for port%d = %d\n",
7268c2ecf20Sopenharmony_ci						__func__, portNumber,
7278c2ecf20Sopenharmony_ci						edge_port->txCredits);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci					/* tell the tty driver that something
7308c2ecf20Sopenharmony_ci					   has changed */
7318c2ecf20Sopenharmony_ci					tty_port_tty_wakeup(&edge_port->port->port);
7328c2ecf20Sopenharmony_ci					/* Since we have more credit, check
7338c2ecf20Sopenharmony_ci					   if more data can be sent */
7348c2ecf20Sopenharmony_ci					send_more_port_data(edge_serial,
7358c2ecf20Sopenharmony_ci								edge_port);
7368c2ecf20Sopenharmony_ci				}
7378c2ecf20Sopenharmony_ci			}
7388c2ecf20Sopenharmony_ci			position += 2;
7398c2ecf20Sopenharmony_ci			++portNumber;
7408c2ecf20Sopenharmony_ci		}
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ciexit:
7448c2ecf20Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
7458c2ecf20Sopenharmony_ci	if (result)
7468c2ecf20Sopenharmony_ci		dev_err(&urb->dev->dev,
7478c2ecf20Sopenharmony_ci			"%s - Error %d submitting control urb\n",
7488c2ecf20Sopenharmony_ci						__func__, result);
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci/*****************************************************************************
7538c2ecf20Sopenharmony_ci * edge_bulk_in_callback
7548c2ecf20Sopenharmony_ci *	this is the callback function for when we have received data on the
7558c2ecf20Sopenharmony_ci *	bulk in endpoint.
7568c2ecf20Sopenharmony_ci *****************************************************************************/
7578c2ecf20Sopenharmony_cistatic void edge_bulk_in_callback(struct urb *urb)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct edgeport_serial	*edge_serial = urb->context;
7608c2ecf20Sopenharmony_ci	struct device *dev;
7618c2ecf20Sopenharmony_ci	unsigned char		*data = urb->transfer_buffer;
7628c2ecf20Sopenharmony_ci	int			retval;
7638c2ecf20Sopenharmony_ci	__u16			raw_data_length;
7648c2ecf20Sopenharmony_ci	int status = urb->status;
7658c2ecf20Sopenharmony_ci	unsigned long flags;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (status) {
7688c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
7698c2ecf20Sopenharmony_ci			__func__, status);
7708c2ecf20Sopenharmony_ci		edge_serial->read_in_progress = false;
7718c2ecf20Sopenharmony_ci		return;
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (urb->actual_length == 0) {
7758c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - read bulk callback with no data\n", __func__);
7768c2ecf20Sopenharmony_ci		edge_serial->read_in_progress = false;
7778c2ecf20Sopenharmony_ci		return;
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	dev = &edge_serial->serial->dev->dev;
7818c2ecf20Sopenharmony_ci	raw_data_length = urb->actual_length;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, raw_data_length, data);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&edge_serial->es_lock, flags);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* decrement our rxBytes available by the number that we just got */
7888c2ecf20Sopenharmony_ci	edge_serial->rxBytesAvail -= raw_data_length;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - Received = %d, rxBytesAvail %d\n", __func__,
7918c2ecf20Sopenharmony_ci		raw_data_length, edge_serial->rxBytesAvail);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	process_rcvd_data(edge_serial, data, urb->actual_length);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	/* check to see if there's any more data for us to read */
7968c2ecf20Sopenharmony_ci	if (edge_serial->rxBytesAvail > 0) {
7978c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - posting a read\n", __func__);
7988c2ecf20Sopenharmony_ci		retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
7998c2ecf20Sopenharmony_ci		if (retval) {
8008c2ecf20Sopenharmony_ci			dev_err(dev,
8018c2ecf20Sopenharmony_ci				"%s - usb_submit_urb(read bulk) failed, retval = %d\n",
8028c2ecf20Sopenharmony_ci				__func__, retval);
8038c2ecf20Sopenharmony_ci			edge_serial->read_in_progress = false;
8048c2ecf20Sopenharmony_ci		}
8058c2ecf20Sopenharmony_ci	} else {
8068c2ecf20Sopenharmony_ci		edge_serial->read_in_progress = false;
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&edge_serial->es_lock, flags);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci/*****************************************************************************
8148c2ecf20Sopenharmony_ci * edge_bulk_out_data_callback
8158c2ecf20Sopenharmony_ci *	this is the callback function for when we have finished sending
8168c2ecf20Sopenharmony_ci *	serial data on the bulk out endpoint.
8178c2ecf20Sopenharmony_ci *****************************************************************************/
8188c2ecf20Sopenharmony_cistatic void edge_bulk_out_data_callback(struct urb *urb)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = urb->context;
8218c2ecf20Sopenharmony_ci	int status = urb->status;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (status) {
8248c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev,
8258c2ecf20Sopenharmony_ci			"%s - nonzero write bulk status received: %d\n",
8268c2ecf20Sopenharmony_ci			__func__, status);
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (edge_port->open)
8308c2ecf20Sopenharmony_ci		tty_port_tty_wakeup(&edge_port->port->port);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	/* Release the Write URB */
8338c2ecf20Sopenharmony_ci	edge_port->write_in_progress = false;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	/* Check if more data needs to be sent */
8368c2ecf20Sopenharmony_ci	send_more_port_data((struct edgeport_serial *)
8378c2ecf20Sopenharmony_ci		(usb_get_serial_data(edge_port->port->serial)), edge_port);
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci/*****************************************************************************
8428c2ecf20Sopenharmony_ci * BulkOutCmdCallback
8438c2ecf20Sopenharmony_ci *	this is the callback function for when we have finished sending a
8448c2ecf20Sopenharmony_ci *	command	on the bulk out endpoint.
8458c2ecf20Sopenharmony_ci *****************************************************************************/
8468c2ecf20Sopenharmony_cistatic void edge_bulk_out_cmd_callback(struct urb *urb)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = urb->context;
8498c2ecf20Sopenharmony_ci	int status = urb->status;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	atomic_dec(&CmdUrbs);
8528c2ecf20Sopenharmony_ci	dev_dbg(&urb->dev->dev, "%s - FREE URB %p (outstanding %d)\n",
8538c2ecf20Sopenharmony_ci		__func__, urb, atomic_read(&CmdUrbs));
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* clean up the transfer buffer */
8578c2ecf20Sopenharmony_ci	kfree(urb->transfer_buffer);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* Free the command urb */
8608c2ecf20Sopenharmony_ci	usb_free_urb(urb);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (status) {
8638c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev,
8648c2ecf20Sopenharmony_ci			"%s - nonzero write bulk status received: %d\n",
8658c2ecf20Sopenharmony_ci			__func__, status);
8668c2ecf20Sopenharmony_ci		return;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* tell the tty driver that something has changed */
8708c2ecf20Sopenharmony_ci	if (edge_port->open)
8718c2ecf20Sopenharmony_ci		tty_port_tty_wakeup(&edge_port->port->port);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* we have completed the command */
8748c2ecf20Sopenharmony_ci	edge_port->commandPending = false;
8758c2ecf20Sopenharmony_ci	wake_up(&edge_port->wait_command);
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci/*****************************************************************************
8808c2ecf20Sopenharmony_ci * Driver tty interface functions
8818c2ecf20Sopenharmony_ci *****************************************************************************/
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci/*****************************************************************************
8848c2ecf20Sopenharmony_ci * SerialOpen
8858c2ecf20Sopenharmony_ci *	this function is called by the tty driver when a port is opened
8868c2ecf20Sopenharmony_ci *	If successful, we return 0
8878c2ecf20Sopenharmony_ci *	Otherwise we return a negative error number.
8888c2ecf20Sopenharmony_ci *****************************************************************************/
8898c2ecf20Sopenharmony_cistatic int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
8928c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
8938c2ecf20Sopenharmony_ci	struct usb_serial *serial;
8948c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial;
8958c2ecf20Sopenharmony_ci	int response;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if (edge_port == NULL)
8988c2ecf20Sopenharmony_ci		return -ENODEV;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* see if we've set up our endpoint info yet (can't set it up
9018c2ecf20Sopenharmony_ci	   in edge_startup as the structures were not set up at that time.) */
9028c2ecf20Sopenharmony_ci	serial = port->serial;
9038c2ecf20Sopenharmony_ci	edge_serial = usb_get_serial_data(serial);
9048c2ecf20Sopenharmony_ci	if (edge_serial == NULL)
9058c2ecf20Sopenharmony_ci		return -ENODEV;
9068c2ecf20Sopenharmony_ci	if (edge_serial->interrupt_in_buffer == NULL) {
9078c2ecf20Sopenharmony_ci		struct usb_serial_port *port0 = serial->port[0];
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci		/* not set up yet, so do it now */
9108c2ecf20Sopenharmony_ci		edge_serial->interrupt_in_buffer =
9118c2ecf20Sopenharmony_ci					port0->interrupt_in_buffer;
9128c2ecf20Sopenharmony_ci		edge_serial->interrupt_in_endpoint =
9138c2ecf20Sopenharmony_ci					port0->interrupt_in_endpointAddress;
9148c2ecf20Sopenharmony_ci		edge_serial->interrupt_read_urb = port0->interrupt_in_urb;
9158c2ecf20Sopenharmony_ci		edge_serial->bulk_in_buffer = port0->bulk_in_buffer;
9168c2ecf20Sopenharmony_ci		edge_serial->bulk_in_endpoint =
9178c2ecf20Sopenharmony_ci					port0->bulk_in_endpointAddress;
9188c2ecf20Sopenharmony_ci		edge_serial->read_urb = port0->read_urb;
9198c2ecf20Sopenharmony_ci		edge_serial->bulk_out_endpoint =
9208c2ecf20Sopenharmony_ci					port0->bulk_out_endpointAddress;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		/* set up our interrupt urb */
9238c2ecf20Sopenharmony_ci		usb_fill_int_urb(edge_serial->interrupt_read_urb,
9248c2ecf20Sopenharmony_ci		      serial->dev,
9258c2ecf20Sopenharmony_ci		      usb_rcvintpipe(serial->dev,
9268c2ecf20Sopenharmony_ci				port0->interrupt_in_endpointAddress),
9278c2ecf20Sopenharmony_ci		      port0->interrupt_in_buffer,
9288c2ecf20Sopenharmony_ci		      edge_serial->interrupt_read_urb->transfer_buffer_length,
9298c2ecf20Sopenharmony_ci		      edge_interrupt_callback, edge_serial,
9308c2ecf20Sopenharmony_ci		      edge_serial->interrupt_read_urb->interval);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		/* set up our bulk in urb */
9338c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(edge_serial->read_urb, serial->dev,
9348c2ecf20Sopenharmony_ci			usb_rcvbulkpipe(serial->dev,
9358c2ecf20Sopenharmony_ci				port0->bulk_in_endpointAddress),
9368c2ecf20Sopenharmony_ci			port0->bulk_in_buffer,
9378c2ecf20Sopenharmony_ci			edge_serial->read_urb->transfer_buffer_length,
9388c2ecf20Sopenharmony_ci			edge_bulk_in_callback, edge_serial);
9398c2ecf20Sopenharmony_ci		edge_serial->read_in_progress = false;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		/* start interrupt read for this edgeport
9428c2ecf20Sopenharmony_ci		 * this interrupt will continue as long
9438c2ecf20Sopenharmony_ci		 * as the edgeport is connected */
9448c2ecf20Sopenharmony_ci		response = usb_submit_urb(edge_serial->interrupt_read_urb,
9458c2ecf20Sopenharmony_ci								GFP_KERNEL);
9468c2ecf20Sopenharmony_ci		if (response) {
9478c2ecf20Sopenharmony_ci			dev_err(dev, "%s - Error %d submitting control urb\n",
9488c2ecf20Sopenharmony_ci				__func__, response);
9498c2ecf20Sopenharmony_ci		}
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	/* initialize our wait queues */
9538c2ecf20Sopenharmony_ci	init_waitqueue_head(&edge_port->wait_open);
9548c2ecf20Sopenharmony_ci	init_waitqueue_head(&edge_port->wait_chase);
9558c2ecf20Sopenharmony_ci	init_waitqueue_head(&edge_port->wait_command);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	/* initialize our port settings */
9588c2ecf20Sopenharmony_ci	edge_port->txCredits = 0;	/* Can't send any data yet */
9598c2ecf20Sopenharmony_ci	/* Must always set this bit to enable ints! */
9608c2ecf20Sopenharmony_ci	edge_port->shadowMCR = MCR_MASTER_IE;
9618c2ecf20Sopenharmony_ci	edge_port->chaseResponsePending = false;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* send a open port command */
9648c2ecf20Sopenharmony_ci	edge_port->openPending = true;
9658c2ecf20Sopenharmony_ci	edge_port->open        = false;
9668c2ecf20Sopenharmony_ci	response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (response < 0) {
9698c2ecf20Sopenharmony_ci		dev_err(dev, "%s - error sending open port command\n", __func__);
9708c2ecf20Sopenharmony_ci		edge_port->openPending = false;
9718c2ecf20Sopenharmony_ci		return -ENODEV;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* now wait for the port to be completely opened */
9758c2ecf20Sopenharmony_ci	wait_event_timeout(edge_port->wait_open, !edge_port->openPending,
9768c2ecf20Sopenharmony_ci								OPEN_TIMEOUT);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	if (!edge_port->open) {
9798c2ecf20Sopenharmony_ci		/* open timed out */
9808c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - open timedout\n", __func__);
9818c2ecf20Sopenharmony_ci		edge_port->openPending = false;
9828c2ecf20Sopenharmony_ci		return -ENODEV;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* create the txfifo */
9868c2ecf20Sopenharmony_ci	edge_port->txfifo.head	= 0;
9878c2ecf20Sopenharmony_ci	edge_port->txfifo.tail	= 0;
9888c2ecf20Sopenharmony_ci	edge_port->txfifo.count	= 0;
9898c2ecf20Sopenharmony_ci	edge_port->txfifo.size	= edge_port->maxTxCredits;
9908c2ecf20Sopenharmony_ci	edge_port->txfifo.fifo	= kmalloc(edge_port->maxTxCredits, GFP_KERNEL);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	if (!edge_port->txfifo.fifo) {
9938c2ecf20Sopenharmony_ci		edge_close(port);
9948c2ecf20Sopenharmony_ci		return -ENOMEM;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* Allocate a URB for the write */
9988c2ecf20Sopenharmony_ci	edge_port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
9998c2ecf20Sopenharmony_ci	edge_port->write_in_progress = false;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (!edge_port->write_urb) {
10028c2ecf20Sopenharmony_ci		edge_close(port);
10038c2ecf20Sopenharmony_ci		return -ENOMEM;
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - Initialize TX fifo to %d bytes\n",
10078c2ecf20Sopenharmony_ci		__func__, edge_port->maxTxCredits);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	return 0;
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci/************************************************************************
10148c2ecf20Sopenharmony_ci *
10158c2ecf20Sopenharmony_ci * block_until_chase_response
10168c2ecf20Sopenharmony_ci *
10178c2ecf20Sopenharmony_ci *	This function will block the close until one of the following:
10188c2ecf20Sopenharmony_ci *		1. Response to our Chase comes from Edgeport
10198c2ecf20Sopenharmony_ci *		2. A timeout of 10 seconds without activity has expired
10208c2ecf20Sopenharmony_ci *		   (1K of Edgeport data @ 2400 baud ==> 4 sec to empty)
10218c2ecf20Sopenharmony_ci *
10228c2ecf20Sopenharmony_ci ************************************************************************/
10238c2ecf20Sopenharmony_cistatic void block_until_chase_response(struct edgeport_port *edge_port)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
10268c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
10278c2ecf20Sopenharmony_ci	__u16 lastCredits;
10288c2ecf20Sopenharmony_ci	int timeout = 1*HZ;
10298c2ecf20Sopenharmony_ci	int loop = 10;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	while (1) {
10328c2ecf20Sopenharmony_ci		/* Save Last credits */
10338c2ecf20Sopenharmony_ci		lastCredits = edge_port->txCredits;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci		/* Did we get our Chase response */
10368c2ecf20Sopenharmony_ci		if (!edge_port->chaseResponsePending) {
10378c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - Got Chase Response\n", __func__);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci			/* did we get all of our credit back? */
10408c2ecf20Sopenharmony_ci			if (edge_port->txCredits == edge_port->maxTxCredits) {
10418c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - Got all credits\n", __func__);
10428c2ecf20Sopenharmony_ci				return;
10438c2ecf20Sopenharmony_ci			}
10448c2ecf20Sopenharmony_ci		}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci		/* Block the thread for a while */
10478c2ecf20Sopenharmony_ci		prepare_to_wait(&edge_port->wait_chase, &wait,
10488c2ecf20Sopenharmony_ci						TASK_UNINTERRUPTIBLE);
10498c2ecf20Sopenharmony_ci		schedule_timeout(timeout);
10508c2ecf20Sopenharmony_ci		finish_wait(&edge_port->wait_chase, &wait);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci		if (lastCredits == edge_port->txCredits) {
10538c2ecf20Sopenharmony_ci			/* No activity.. count down. */
10548c2ecf20Sopenharmony_ci			loop--;
10558c2ecf20Sopenharmony_ci			if (loop == 0) {
10568c2ecf20Sopenharmony_ci				edge_port->chaseResponsePending = false;
10578c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - Chase TIMEOUT\n", __func__);
10588c2ecf20Sopenharmony_ci				return;
10598c2ecf20Sopenharmony_ci			}
10608c2ecf20Sopenharmony_ci		} else {
10618c2ecf20Sopenharmony_ci			/* Reset timeout value back to 10 seconds */
10628c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - Last %d, Current %d\n", __func__,
10638c2ecf20Sopenharmony_ci					lastCredits, edge_port->txCredits);
10648c2ecf20Sopenharmony_ci			loop = 10;
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci/************************************************************************
10718c2ecf20Sopenharmony_ci *
10728c2ecf20Sopenharmony_ci * block_until_tx_empty
10738c2ecf20Sopenharmony_ci *
10748c2ecf20Sopenharmony_ci *	This function will block the close until one of the following:
10758c2ecf20Sopenharmony_ci *		1. TX count are 0
10768c2ecf20Sopenharmony_ci *		2. The edgeport has stopped
10778c2ecf20Sopenharmony_ci *		3. A timeout of 3 seconds without activity has expired
10788c2ecf20Sopenharmony_ci *
10798c2ecf20Sopenharmony_ci ************************************************************************/
10808c2ecf20Sopenharmony_cistatic void block_until_tx_empty(struct edgeport_port *edge_port)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
10838c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
10848c2ecf20Sopenharmony_ci	struct TxFifo *fifo = &edge_port->txfifo;
10858c2ecf20Sopenharmony_ci	__u32 lastCount;
10868c2ecf20Sopenharmony_ci	int timeout = HZ/10;
10878c2ecf20Sopenharmony_ci	int loop = 30;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	while (1) {
10908c2ecf20Sopenharmony_ci		/* Save Last count */
10918c2ecf20Sopenharmony_ci		lastCount = fifo->count;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		/* Is the Edgeport Buffer empty? */
10948c2ecf20Sopenharmony_ci		if (lastCount == 0) {
10958c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - TX Buffer Empty\n", __func__);
10968c2ecf20Sopenharmony_ci			return;
10978c2ecf20Sopenharmony_ci		}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		/* Block the thread for a while */
11008c2ecf20Sopenharmony_ci		prepare_to_wait(&edge_port->wait_chase, &wait,
11018c2ecf20Sopenharmony_ci						TASK_UNINTERRUPTIBLE);
11028c2ecf20Sopenharmony_ci		schedule_timeout(timeout);
11038c2ecf20Sopenharmony_ci		finish_wait(&edge_port->wait_chase, &wait);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s wait\n", __func__);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		if (lastCount == fifo->count) {
11088c2ecf20Sopenharmony_ci			/* No activity.. count down. */
11098c2ecf20Sopenharmony_ci			loop--;
11108c2ecf20Sopenharmony_ci			if (loop == 0) {
11118c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - TIMEOUT\n", __func__);
11128c2ecf20Sopenharmony_ci				return;
11138c2ecf20Sopenharmony_ci			}
11148c2ecf20Sopenharmony_ci		} else {
11158c2ecf20Sopenharmony_ci			/* Reset timeout value back to seconds */
11168c2ecf20Sopenharmony_ci			loop = 30;
11178c2ecf20Sopenharmony_ci		}
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/*****************************************************************************
11238c2ecf20Sopenharmony_ci * edge_close
11248c2ecf20Sopenharmony_ci *	this function is called by the tty driver when a port is closed
11258c2ecf20Sopenharmony_ci *****************************************************************************/
11268c2ecf20Sopenharmony_cistatic void edge_close(struct usb_serial_port *port)
11278c2ecf20Sopenharmony_ci{
11288c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial;
11298c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port;
11308c2ecf20Sopenharmony_ci	int status;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	edge_serial = usb_get_serial_data(port->serial);
11338c2ecf20Sopenharmony_ci	edge_port = usb_get_serial_port_data(port);
11348c2ecf20Sopenharmony_ci	if (edge_serial == NULL || edge_port == NULL)
11358c2ecf20Sopenharmony_ci		return;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	/* block until tx is empty */
11388c2ecf20Sopenharmony_ci	block_until_tx_empty(edge_port);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	edge_port->closePending = true;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic ||
11438c2ecf20Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPChase) {
11448c2ecf20Sopenharmony_ci		/* flush and chase */
11458c2ecf20Sopenharmony_ci		edge_port->chaseResponsePending = true;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
11488c2ecf20Sopenharmony_ci		status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
11498c2ecf20Sopenharmony_ci		if (status == 0)
11508c2ecf20Sopenharmony_ci			/* block until chase finished */
11518c2ecf20Sopenharmony_ci			block_until_chase_response(edge_port);
11528c2ecf20Sopenharmony_ci		else
11538c2ecf20Sopenharmony_ci			edge_port->chaseResponsePending = false;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic ||
11578c2ecf20Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPClose) {
11588c2ecf20Sopenharmony_ci	       /* close the port */
11598c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__);
11608c2ecf20Sopenharmony_ci		send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0);
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/* port->close = true; */
11648c2ecf20Sopenharmony_ci	edge_port->closePending = false;
11658c2ecf20Sopenharmony_ci	edge_port->open = false;
11668c2ecf20Sopenharmony_ci	edge_port->openPending = false;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	usb_kill_urb(edge_port->write_urb);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	if (edge_port->write_urb) {
11718c2ecf20Sopenharmony_ci		/* if this urb had a transfer buffer already
11728c2ecf20Sopenharmony_ci				(old transfer) free it */
11738c2ecf20Sopenharmony_ci		kfree(edge_port->write_urb->transfer_buffer);
11748c2ecf20Sopenharmony_ci		usb_free_urb(edge_port->write_urb);
11758c2ecf20Sopenharmony_ci		edge_port->write_urb = NULL;
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci	kfree(edge_port->txfifo.fifo);
11788c2ecf20Sopenharmony_ci	edge_port->txfifo.fifo = NULL;
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci/*****************************************************************************
11828c2ecf20Sopenharmony_ci * SerialWrite
11838c2ecf20Sopenharmony_ci *	this function is called by the tty driver when data should be written
11848c2ecf20Sopenharmony_ci *	to the port.
11858c2ecf20Sopenharmony_ci *	If successful, we return the number of bytes written, otherwise we
11868c2ecf20Sopenharmony_ci *	return a negative error number.
11878c2ecf20Sopenharmony_ci *****************************************************************************/
11888c2ecf20Sopenharmony_cistatic int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
11898c2ecf20Sopenharmony_ci					const unsigned char *data, int count)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
11928c2ecf20Sopenharmony_ci	struct TxFifo *fifo;
11938c2ecf20Sopenharmony_ci	int copySize;
11948c2ecf20Sopenharmony_ci	int bytesleft;
11958c2ecf20Sopenharmony_ci	int firsthalf;
11968c2ecf20Sopenharmony_ci	int secondhalf;
11978c2ecf20Sopenharmony_ci	unsigned long flags;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (edge_port == NULL)
12008c2ecf20Sopenharmony_ci		return -ENODEV;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	/* get a pointer to the Tx fifo */
12038c2ecf20Sopenharmony_ci	fifo = &edge_port->txfifo;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	/* calculate number of bytes to put in fifo */
12088c2ecf20Sopenharmony_ci	copySize = min((unsigned int)count,
12098c2ecf20Sopenharmony_ci				(edge_port->txCredits - fifo->count));
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s of %d byte(s) Fifo room  %d -- will copy %d bytes\n",
12128c2ecf20Sopenharmony_ci		__func__, count, edge_port->txCredits - fifo->count, copySize);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/* catch writes of 0 bytes which the tty driver likes to give us,
12158c2ecf20Sopenharmony_ci	   and when txCredits is empty */
12168c2ecf20Sopenharmony_ci	if (copySize == 0) {
12178c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - copySize = Zero\n", __func__);
12188c2ecf20Sopenharmony_ci		goto finish_write;
12198c2ecf20Sopenharmony_ci	}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	/* queue the data
12228c2ecf20Sopenharmony_ci	 * since we can never overflow the buffer we do not have to check for a
12238c2ecf20Sopenharmony_ci	 * full condition
12248c2ecf20Sopenharmony_ci	 *
12258c2ecf20Sopenharmony_ci	 * the copy is done is two parts -- first fill to the end of the buffer
12268c2ecf20Sopenharmony_ci	 * then copy the reset from the start of the buffer
12278c2ecf20Sopenharmony_ci	 */
12288c2ecf20Sopenharmony_ci	bytesleft = fifo->size - fifo->head;
12298c2ecf20Sopenharmony_ci	firsthalf = min(bytesleft, copySize);
12308c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - copy %d bytes of %d into fifo \n", __func__,
12318c2ecf20Sopenharmony_ci		firsthalf, bytesleft);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	/* now copy our data */
12348c2ecf20Sopenharmony_ci	memcpy(&fifo->fifo[fifo->head], data, firsthalf);
12358c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, firsthalf, &fifo->fifo[fifo->head]);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* update the index and size */
12388c2ecf20Sopenharmony_ci	fifo->head  += firsthalf;
12398c2ecf20Sopenharmony_ci	fifo->count += firsthalf;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/* wrap the index */
12428c2ecf20Sopenharmony_ci	if (fifo->head == fifo->size)
12438c2ecf20Sopenharmony_ci		fifo->head = 0;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	secondhalf = copySize-firsthalf;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (secondhalf) {
12488c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - copy rest of data %d\n", __func__, secondhalf);
12498c2ecf20Sopenharmony_ci		memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
12508c2ecf20Sopenharmony_ci		usb_serial_debug_data(&port->dev, __func__, secondhalf, &fifo->fifo[fifo->head]);
12518c2ecf20Sopenharmony_ci		/* update the index and size */
12528c2ecf20Sopenharmony_ci		fifo->count += secondhalf;
12538c2ecf20Sopenharmony_ci		fifo->head  += secondhalf;
12548c2ecf20Sopenharmony_ci		/* No need to check for wrap since we can not get to end of
12558c2ecf20Sopenharmony_ci		 * the fifo in this part
12568c2ecf20Sopenharmony_ci		 */
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_cifinish_write:
12608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	send_more_port_data((struct edgeport_serial *)
12638c2ecf20Sopenharmony_ci			usb_get_serial_data(port->serial), edge_port);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s wrote %d byte(s) TxCredits %d, Fifo %d\n",
12668c2ecf20Sopenharmony_ci		__func__, copySize, edge_port->txCredits, fifo->count);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	return copySize;
12698c2ecf20Sopenharmony_ci}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci/************************************************************************
12738c2ecf20Sopenharmony_ci *
12748c2ecf20Sopenharmony_ci * send_more_port_data()
12758c2ecf20Sopenharmony_ci *
12768c2ecf20Sopenharmony_ci *	This routine attempts to write additional UART transmit data
12778c2ecf20Sopenharmony_ci *	to a port over the USB bulk pipe. It is called (1) when new
12788c2ecf20Sopenharmony_ci *	data has been written to a port's TxBuffer from higher layers
12798c2ecf20Sopenharmony_ci *	(2) when the peripheral sends us additional TxCredits indicating
12808c2ecf20Sopenharmony_ci *	that it can accept more	Tx data for a given port; and (3) when
12818c2ecf20Sopenharmony_ci *	a bulk write completes successfully and we want to see if we
12828c2ecf20Sopenharmony_ci *	can transmit more.
12838c2ecf20Sopenharmony_ci *
12848c2ecf20Sopenharmony_ci ************************************************************************/
12858c2ecf20Sopenharmony_cistatic void send_more_port_data(struct edgeport_serial *edge_serial,
12868c2ecf20Sopenharmony_ci					struct edgeport_port *edge_port)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	struct TxFifo	*fifo = &edge_port->txfifo;
12898c2ecf20Sopenharmony_ci	struct device	*dev = &edge_port->port->dev;
12908c2ecf20Sopenharmony_ci	struct urb	*urb;
12918c2ecf20Sopenharmony_ci	unsigned char	*buffer;
12928c2ecf20Sopenharmony_ci	int		status;
12938c2ecf20Sopenharmony_ci	int		count;
12948c2ecf20Sopenharmony_ci	int		bytesleft;
12958c2ecf20Sopenharmony_ci	int		firsthalf;
12968c2ecf20Sopenharmony_ci	int		secondhalf;
12978c2ecf20Sopenharmony_ci	unsigned long	flags;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (edge_port->write_in_progress ||
13028c2ecf20Sopenharmony_ci	    !edge_port->open             ||
13038c2ecf20Sopenharmony_ci	    (fifo->count == 0)) {
13048c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s EXIT - fifo %d, PendingWrite = %d\n",
13058c2ecf20Sopenharmony_ci			__func__, fifo->count, edge_port->write_in_progress);
13068c2ecf20Sopenharmony_ci		goto exit_send;
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	/* since the amount of data in the fifo will always fit into the
13108c2ecf20Sopenharmony_ci	 * edgeport buffer we do not need to check the write length
13118c2ecf20Sopenharmony_ci	 *
13128c2ecf20Sopenharmony_ci	 * Do we have enough credits for this port to make it worthwhile
13138c2ecf20Sopenharmony_ci	 * to bother queueing a write. If it's too small, say a few bytes,
13148c2ecf20Sopenharmony_ci	 * it's better to wait for more credits so we can do a larger write.
13158c2ecf20Sopenharmony_ci	 */
13168c2ecf20Sopenharmony_ci	if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits, EDGE_FW_BULK_MAX_PACKET_SIZE)) {
13178c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s Not enough credit - fifo %d TxCredit %d\n",
13188c2ecf20Sopenharmony_ci			__func__, fifo->count, edge_port->txCredits);
13198c2ecf20Sopenharmony_ci		goto exit_send;
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	/* lock this write */
13238c2ecf20Sopenharmony_ci	edge_port->write_in_progress = true;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/* get a pointer to the write_urb */
13268c2ecf20Sopenharmony_ci	urb = edge_port->write_urb;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	/* make sure transfer buffer is freed */
13298c2ecf20Sopenharmony_ci	kfree(urb->transfer_buffer);
13308c2ecf20Sopenharmony_ci	urb->transfer_buffer = NULL;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	/* build the data header for the buffer and port that we are about
13338c2ecf20Sopenharmony_ci	   to send out */
13348c2ecf20Sopenharmony_ci	count = fifo->count;
13358c2ecf20Sopenharmony_ci	buffer = kmalloc(count+2, GFP_ATOMIC);
13368c2ecf20Sopenharmony_ci	if (!buffer) {
13378c2ecf20Sopenharmony_ci		edge_port->write_in_progress = false;
13388c2ecf20Sopenharmony_ci		goto exit_send;
13398c2ecf20Sopenharmony_ci	}
13408c2ecf20Sopenharmony_ci	buffer[0] = IOSP_BUILD_DATA_HDR1(edge_port->port->port_number, count);
13418c2ecf20Sopenharmony_ci	buffer[1] = IOSP_BUILD_DATA_HDR2(edge_port->port->port_number, count);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	/* now copy our data */
13448c2ecf20Sopenharmony_ci	bytesleft =  fifo->size - fifo->tail;
13458c2ecf20Sopenharmony_ci	firsthalf = min(bytesleft, count);
13468c2ecf20Sopenharmony_ci	memcpy(&buffer[2], &fifo->fifo[fifo->tail], firsthalf);
13478c2ecf20Sopenharmony_ci	fifo->tail  += firsthalf;
13488c2ecf20Sopenharmony_ci	fifo->count -= firsthalf;
13498c2ecf20Sopenharmony_ci	if (fifo->tail == fifo->size)
13508c2ecf20Sopenharmony_ci		fifo->tail = 0;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	secondhalf = count-firsthalf;
13538c2ecf20Sopenharmony_ci	if (secondhalf) {
13548c2ecf20Sopenharmony_ci		memcpy(&buffer[2+firsthalf], &fifo->fifo[fifo->tail],
13558c2ecf20Sopenharmony_ci								secondhalf);
13568c2ecf20Sopenharmony_ci		fifo->tail  += secondhalf;
13578c2ecf20Sopenharmony_ci		fifo->count -= secondhalf;
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	if (count)
13618c2ecf20Sopenharmony_ci		usb_serial_debug_data(&edge_port->port->dev, __func__, count, &buffer[2]);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	/* fill up the urb with all of our data and submit it */
13648c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, edge_serial->serial->dev,
13658c2ecf20Sopenharmony_ci			usb_sndbulkpipe(edge_serial->serial->dev,
13668c2ecf20Sopenharmony_ci					edge_serial->bulk_out_endpoint),
13678c2ecf20Sopenharmony_ci			buffer, count+2,
13688c2ecf20Sopenharmony_ci			edge_bulk_out_data_callback, edge_port);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	/* decrement the number of credits we have by the number we just sent */
13718c2ecf20Sopenharmony_ci	edge_port->txCredits -= count;
13728c2ecf20Sopenharmony_ci	edge_port->port->icount.tx += count;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
13758c2ecf20Sopenharmony_ci	if (status) {
13768c2ecf20Sopenharmony_ci		/* something went wrong */
13778c2ecf20Sopenharmony_ci		dev_err_console(edge_port->port,
13788c2ecf20Sopenharmony_ci			"%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n",
13798c2ecf20Sopenharmony_ci				__func__, status);
13808c2ecf20Sopenharmony_ci		edge_port->write_in_progress = false;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci		/* revert the credits as something bad happened. */
13838c2ecf20Sopenharmony_ci		edge_port->txCredits += count;
13848c2ecf20Sopenharmony_ci		edge_port->port->icount.tx -= count;
13858c2ecf20Sopenharmony_ci	}
13868c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s wrote %d byte(s) TxCredit %d, Fifo %d\n",
13878c2ecf20Sopenharmony_ci		__func__, count, edge_port->txCredits, fifo->count);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ciexit_send:
13908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci/*****************************************************************************
13958c2ecf20Sopenharmony_ci * edge_write_room
13968c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to know how
13978c2ecf20Sopenharmony_ci *	many bytes of data we can accept for a specific port. If successful,
13988c2ecf20Sopenharmony_ci *	we return the amount of room that we have for this port	(the txCredits)
13998c2ecf20Sopenharmony_ci *	otherwise we return a negative error number.
14008c2ecf20Sopenharmony_ci *****************************************************************************/
14018c2ecf20Sopenharmony_cistatic int edge_write_room(struct tty_struct *tty)
14028c2ecf20Sopenharmony_ci{
14038c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
14048c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
14058c2ecf20Sopenharmony_ci	int room;
14068c2ecf20Sopenharmony_ci	unsigned long flags;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	if (edge_port == NULL)
14098c2ecf20Sopenharmony_ci		return 0;
14108c2ecf20Sopenharmony_ci	if (edge_port->closePending)
14118c2ecf20Sopenharmony_ci		return 0;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	if (!edge_port->open) {
14148c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
14158c2ecf20Sopenharmony_ci		return 0;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	/* total of both buffers is still txCredit */
14198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
14208c2ecf20Sopenharmony_ci	room = edge_port->txCredits - edge_port->txfifo.count;
14218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
14248c2ecf20Sopenharmony_ci	return room;
14258c2ecf20Sopenharmony_ci}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci/*****************************************************************************
14298c2ecf20Sopenharmony_ci * edge_chars_in_buffer
14308c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to know how
14318c2ecf20Sopenharmony_ci *	many bytes of data we currently have outstanding in the port (data that
14328c2ecf20Sopenharmony_ci *	has been written, but hasn't made it out the port yet)
14338c2ecf20Sopenharmony_ci *	If successful, we return the number of bytes left to be written in the
14348c2ecf20Sopenharmony_ci *	system,
14358c2ecf20Sopenharmony_ci *	Otherwise we return a negative error number.
14368c2ecf20Sopenharmony_ci *****************************************************************************/
14378c2ecf20Sopenharmony_cistatic int edge_chars_in_buffer(struct tty_struct *tty)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
14408c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
14418c2ecf20Sopenharmony_ci	int num_chars;
14428c2ecf20Sopenharmony_ci	unsigned long flags;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	if (edge_port == NULL)
14458c2ecf20Sopenharmony_ci		return 0;
14468c2ecf20Sopenharmony_ci	if (edge_port->closePending)
14478c2ecf20Sopenharmony_ci		return 0;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	if (!edge_port->open) {
14508c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
14518c2ecf20Sopenharmony_ci		return 0;
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
14558c2ecf20Sopenharmony_ci	num_chars = edge_port->maxTxCredits - edge_port->txCredits +
14568c2ecf20Sopenharmony_ci						edge_port->txfifo.count;
14578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
14588c2ecf20Sopenharmony_ci	if (num_chars) {
14598c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - returns %d\n", __func__, num_chars);
14608c2ecf20Sopenharmony_ci	}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	return num_chars;
14638c2ecf20Sopenharmony_ci}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci/*****************************************************************************
14678c2ecf20Sopenharmony_ci * SerialThrottle
14688c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to stop the data
14698c2ecf20Sopenharmony_ci *	being read from the port.
14708c2ecf20Sopenharmony_ci *****************************************************************************/
14718c2ecf20Sopenharmony_cistatic void edge_throttle(struct tty_struct *tty)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
14748c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
14758c2ecf20Sopenharmony_ci	int status;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	if (edge_port == NULL)
14788c2ecf20Sopenharmony_ci		return;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	if (!edge_port->open) {
14818c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
14828c2ecf20Sopenharmony_ci		return;
14838c2ecf20Sopenharmony_ci	}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	/* if we are implementing XON/XOFF, send the stop character */
14868c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
14878c2ecf20Sopenharmony_ci		unsigned char stop_char = STOP_CHAR(tty);
14888c2ecf20Sopenharmony_ci		status = edge_write(tty, port, &stop_char, 1);
14898c2ecf20Sopenharmony_ci		if (status <= 0)
14908c2ecf20Sopenharmony_ci			return;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
14948c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
14958c2ecf20Sopenharmony_ci		edge_port->shadowMCR &= ~MCR_RTS;
14968c2ecf20Sopenharmony_ci		status = send_cmd_write_uart_register(edge_port, MCR,
14978c2ecf20Sopenharmony_ci							edge_port->shadowMCR);
14988c2ecf20Sopenharmony_ci		if (status != 0)
14998c2ecf20Sopenharmony_ci			return;
15008c2ecf20Sopenharmony_ci	}
15018c2ecf20Sopenharmony_ci}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci/*****************************************************************************
15058c2ecf20Sopenharmony_ci * edge_unthrottle
15068c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to resume the
15078c2ecf20Sopenharmony_ci *	data being read from the port (called after SerialThrottle is called)
15088c2ecf20Sopenharmony_ci *****************************************************************************/
15098c2ecf20Sopenharmony_cistatic void edge_unthrottle(struct tty_struct *tty)
15108c2ecf20Sopenharmony_ci{
15118c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
15128c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
15138c2ecf20Sopenharmony_ci	int status;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (edge_port == NULL)
15168c2ecf20Sopenharmony_ci		return;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	if (!edge_port->open) {
15198c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
15208c2ecf20Sopenharmony_ci		return;
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	/* if we are implementing XON/XOFF, send the start character */
15248c2ecf20Sopenharmony_ci	if (I_IXOFF(tty)) {
15258c2ecf20Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
15268c2ecf20Sopenharmony_ci		status = edge_write(tty, port, &start_char, 1);
15278c2ecf20Sopenharmony_ci		if (status <= 0)
15288c2ecf20Sopenharmony_ci			return;
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
15318c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
15328c2ecf20Sopenharmony_ci		edge_port->shadowMCR |= MCR_RTS;
15338c2ecf20Sopenharmony_ci		send_cmd_write_uart_register(edge_port, MCR,
15348c2ecf20Sopenharmony_ci						edge_port->shadowMCR);
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci/*****************************************************************************
15408c2ecf20Sopenharmony_ci * SerialSetTermios
15418c2ecf20Sopenharmony_ci *	this function is called by the tty driver when it wants to change
15428c2ecf20Sopenharmony_ci * the termios structure
15438c2ecf20Sopenharmony_ci *****************************************************************************/
15448c2ecf20Sopenharmony_cistatic void edge_set_termios(struct tty_struct *tty,
15458c2ecf20Sopenharmony_ci	struct usb_serial_port *port, struct ktermios *old_termios)
15468c2ecf20Sopenharmony_ci{
15478c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	if (edge_port == NULL)
15508c2ecf20Sopenharmony_ci		return;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (!edge_port->open) {
15538c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
15548c2ecf20Sopenharmony_ci		return;
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	/* change the port settings to the new ones specified */
15588c2ecf20Sopenharmony_ci	change_port_settings(tty, edge_port, old_termios);
15598c2ecf20Sopenharmony_ci}
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci/*****************************************************************************
15638c2ecf20Sopenharmony_ci * get_lsr_info - get line status register info
15648c2ecf20Sopenharmony_ci *
15658c2ecf20Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically
15668c2ecf20Sopenharmony_ci * 	    is emptied.  On bus types like RS485, the transmitter must
15678c2ecf20Sopenharmony_ci * 	    release the bus after transmitting. This must be done when
15688c2ecf20Sopenharmony_ci * 	    the transmit shift register is empty, not be done when the
15698c2ecf20Sopenharmony_ci * 	    transmit holding register is empty.  This functionality
15708c2ecf20Sopenharmony_ci * 	    allows an RS485 driver to be written in user space.
15718c2ecf20Sopenharmony_ci *****************************************************************************/
15728c2ecf20Sopenharmony_cistatic int get_lsr_info(struct edgeport_port *edge_port,
15738c2ecf20Sopenharmony_ci						unsigned int __user *value)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	unsigned int result = 0;
15768c2ecf20Sopenharmony_ci	unsigned long flags;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&edge_port->ep_lock, flags);
15798c2ecf20Sopenharmony_ci	if (edge_port->maxTxCredits == edge_port->txCredits &&
15808c2ecf20Sopenharmony_ci	    edge_port->txfifo.count == 0) {
15818c2ecf20Sopenharmony_ci		dev_dbg(&edge_port->port->dev, "%s -- Empty\n", __func__);
15828c2ecf20Sopenharmony_ci		result = TIOCSER_TEMT;
15838c2ecf20Sopenharmony_ci	}
15848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&edge_port->ep_lock, flags);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	if (copy_to_user(value, &result, sizeof(int)))
15878c2ecf20Sopenharmony_ci		return -EFAULT;
15888c2ecf20Sopenharmony_ci	return 0;
15898c2ecf20Sopenharmony_ci}
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_cistatic int edge_tiocmset(struct tty_struct *tty,
15928c2ecf20Sopenharmony_ci					unsigned int set, unsigned int clear)
15938c2ecf20Sopenharmony_ci{
15948c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
15958c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
15968c2ecf20Sopenharmony_ci	unsigned int mcr;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	mcr = edge_port->shadowMCR;
15998c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
16008c2ecf20Sopenharmony_ci		mcr |= MCR_RTS;
16018c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
16028c2ecf20Sopenharmony_ci		mcr |= MCR_DTR;
16038c2ecf20Sopenharmony_ci	if (set & TIOCM_LOOP)
16048c2ecf20Sopenharmony_ci		mcr |= MCR_LOOPBACK;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
16078c2ecf20Sopenharmony_ci		mcr &= ~MCR_RTS;
16088c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
16098c2ecf20Sopenharmony_ci		mcr &= ~MCR_DTR;
16108c2ecf20Sopenharmony_ci	if (clear & TIOCM_LOOP)
16118c2ecf20Sopenharmony_ci		mcr &= ~MCR_LOOPBACK;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	edge_port->shadowMCR = mcr;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	send_cmd_write_uart_register(edge_port, MCR, edge_port->shadowMCR);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	return 0;
16188c2ecf20Sopenharmony_ci}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_cistatic int edge_tiocmget(struct tty_struct *tty)
16218c2ecf20Sopenharmony_ci{
16228c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
16238c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
16248c2ecf20Sopenharmony_ci	unsigned int result = 0;
16258c2ecf20Sopenharmony_ci	unsigned int msr;
16268c2ecf20Sopenharmony_ci	unsigned int mcr;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	msr = edge_port->shadowMSR;
16298c2ecf20Sopenharmony_ci	mcr = edge_port->shadowMCR;
16308c2ecf20Sopenharmony_ci	result = ((mcr & MCR_DTR)	? TIOCM_DTR: 0)	  /* 0x002 */
16318c2ecf20Sopenharmony_ci		  | ((mcr & MCR_RTS)	? TIOCM_RTS: 0)   /* 0x004 */
16328c2ecf20Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_CTS)	? TIOCM_CTS: 0)   /* 0x020 */
16338c2ecf20Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_CD)	? TIOCM_CAR: 0)   /* 0x040 */
16348c2ecf20Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_RI)	? TIOCM_RI:  0)   /* 0x080 */
16358c2ecf20Sopenharmony_ci		  | ((msr & EDGEPORT_MSR_DSR)	? TIOCM_DSR: 0);  /* 0x100 */
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	return result;
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic int get_serial_info(struct tty_struct *tty,
16418c2ecf20Sopenharmony_ci				struct serial_struct *ss)
16428c2ecf20Sopenharmony_ci{
16438c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
16448c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	ss->type		= PORT_16550A;
16478c2ecf20Sopenharmony_ci	ss->line		= edge_port->port->minor;
16488c2ecf20Sopenharmony_ci	ss->port		= edge_port->port->port_number;
16498c2ecf20Sopenharmony_ci	ss->irq			= 0;
16508c2ecf20Sopenharmony_ci	ss->xmit_fifo_size	= edge_port->maxTxCredits;
16518c2ecf20Sopenharmony_ci	ss->baud_base		= 9600;
16528c2ecf20Sopenharmony_ci	ss->close_delay		= 5*HZ;
16538c2ecf20Sopenharmony_ci	ss->closing_wait	= 30*HZ;
16548c2ecf20Sopenharmony_ci	return 0;
16558c2ecf20Sopenharmony_ci}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci/*****************************************************************************
16598c2ecf20Sopenharmony_ci * SerialIoctl
16608c2ecf20Sopenharmony_ci *	this function handles any ioctl calls to the driver
16618c2ecf20Sopenharmony_ci *****************************************************************************/
16628c2ecf20Sopenharmony_cistatic int edge_ioctl(struct tty_struct *tty,
16638c2ecf20Sopenharmony_ci					unsigned int cmd, unsigned long arg)
16648c2ecf20Sopenharmony_ci{
16658c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
16668c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	switch (cmd) {
16698c2ecf20Sopenharmony_ci	case TIOCSERGETLSR:
16708c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
16718c2ecf20Sopenharmony_ci		return get_lsr_info(edge_port, (unsigned int __user *) arg);
16728c2ecf20Sopenharmony_ci	}
16738c2ecf20Sopenharmony_ci	return -ENOIOCTLCMD;
16748c2ecf20Sopenharmony_ci}
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci/*****************************************************************************
16788c2ecf20Sopenharmony_ci * SerialBreak
16798c2ecf20Sopenharmony_ci *	this function sends a break to the port
16808c2ecf20Sopenharmony_ci *****************************************************************************/
16818c2ecf20Sopenharmony_cistatic void edge_break(struct tty_struct *tty, int break_state)
16828c2ecf20Sopenharmony_ci{
16838c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
16848c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port = usb_get_serial_port_data(port);
16858c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
16868c2ecf20Sopenharmony_ci	int status;
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic ||
16898c2ecf20Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPChase) {
16908c2ecf20Sopenharmony_ci		/* flush and chase */
16918c2ecf20Sopenharmony_ci		edge_port->chaseResponsePending = true;
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
16948c2ecf20Sopenharmony_ci		status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
16958c2ecf20Sopenharmony_ci		if (status == 0) {
16968c2ecf20Sopenharmony_ci			/* block until chase finished */
16978c2ecf20Sopenharmony_ci			block_until_chase_response(edge_port);
16988c2ecf20Sopenharmony_ci		} else {
16998c2ecf20Sopenharmony_ci			edge_port->chaseResponsePending = false;
17008c2ecf20Sopenharmony_ci		}
17018c2ecf20Sopenharmony_ci	}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic ||
17048c2ecf20Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPSetClrBreak) {
17058c2ecf20Sopenharmony_ci		if (break_state == -1) {
17068c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__);
17078c2ecf20Sopenharmony_ci			status = send_iosp_ext_cmd(edge_port,
17088c2ecf20Sopenharmony_ci						IOSP_CMD_SET_BREAK, 0);
17098c2ecf20Sopenharmony_ci		} else {
17108c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLEAR_BREAK\n", __func__);
17118c2ecf20Sopenharmony_ci			status = send_iosp_ext_cmd(edge_port,
17128c2ecf20Sopenharmony_ci						IOSP_CMD_CLEAR_BREAK, 0);
17138c2ecf20Sopenharmony_ci		}
17148c2ecf20Sopenharmony_ci		if (status)
17158c2ecf20Sopenharmony_ci			dev_dbg(&port->dev, "%s - error sending break set/clear command.\n",
17168c2ecf20Sopenharmony_ci				__func__);
17178c2ecf20Sopenharmony_ci	}
17188c2ecf20Sopenharmony_ci}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci/*****************************************************************************
17228c2ecf20Sopenharmony_ci * process_rcvd_data
17238c2ecf20Sopenharmony_ci *	this function handles the data received on the bulk in pipe.
17248c2ecf20Sopenharmony_ci *****************************************************************************/
17258c2ecf20Sopenharmony_cistatic void process_rcvd_data(struct edgeport_serial *edge_serial,
17268c2ecf20Sopenharmony_ci				unsigned char *buffer, __u16 bufferLength)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	struct usb_serial *serial = edge_serial->serial;
17298c2ecf20Sopenharmony_ci	struct device *dev = &serial->dev->dev;
17308c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
17318c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port;
17328c2ecf20Sopenharmony_ci	__u16 lastBufferLength;
17338c2ecf20Sopenharmony_ci	__u16 rxLen;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	lastBufferLength = bufferLength + 1;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	while (bufferLength > 0) {
17388c2ecf20Sopenharmony_ci		/* failsafe incase we get a message that we don't understand */
17398c2ecf20Sopenharmony_ci		if (lastBufferLength == bufferLength) {
17408c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - stuck in loop, exiting it.\n", __func__);
17418c2ecf20Sopenharmony_ci			break;
17428c2ecf20Sopenharmony_ci		}
17438c2ecf20Sopenharmony_ci		lastBufferLength = bufferLength;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci		switch (edge_serial->rxState) {
17468c2ecf20Sopenharmony_ci		case EXPECT_HDR1:
17478c2ecf20Sopenharmony_ci			edge_serial->rxHeader1 = *buffer;
17488c2ecf20Sopenharmony_ci			++buffer;
17498c2ecf20Sopenharmony_ci			--bufferLength;
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci			if (bufferLength == 0) {
17528c2ecf20Sopenharmony_ci				edge_serial->rxState = EXPECT_HDR2;
17538c2ecf20Sopenharmony_ci				break;
17548c2ecf20Sopenharmony_ci			}
17558c2ecf20Sopenharmony_ci			fallthrough;
17568c2ecf20Sopenharmony_ci		case EXPECT_HDR2:
17578c2ecf20Sopenharmony_ci			edge_serial->rxHeader2 = *buffer;
17588c2ecf20Sopenharmony_ci			++buffer;
17598c2ecf20Sopenharmony_ci			--bufferLength;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - Hdr1=%02X Hdr2=%02X\n", __func__,
17628c2ecf20Sopenharmony_ci				edge_serial->rxHeader1, edge_serial->rxHeader2);
17638c2ecf20Sopenharmony_ci			/* Process depending on whether this header is
17648c2ecf20Sopenharmony_ci			 * data or status */
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci			if (IS_CMD_STAT_HDR(edge_serial->rxHeader1)) {
17678c2ecf20Sopenharmony_ci				/* Decode this status header and go to
17688c2ecf20Sopenharmony_ci				 * EXPECT_HDR1 (if we can process the status
17698c2ecf20Sopenharmony_ci				 * with only 2 bytes), or go to EXPECT_HDR3 to
17708c2ecf20Sopenharmony_ci				 * get the third byte. */
17718c2ecf20Sopenharmony_ci				edge_serial->rxPort =
17728c2ecf20Sopenharmony_ci				    IOSP_GET_HDR_PORT(edge_serial->rxHeader1);
17738c2ecf20Sopenharmony_ci				edge_serial->rxStatusCode =
17748c2ecf20Sopenharmony_ci				    IOSP_GET_STATUS_CODE(
17758c2ecf20Sopenharmony_ci						edge_serial->rxHeader1);
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci				if (!IOSP_STATUS_IS_2BYTE(
17788c2ecf20Sopenharmony_ci						edge_serial->rxStatusCode)) {
17798c2ecf20Sopenharmony_ci					/* This status needs additional bytes.
17808c2ecf20Sopenharmony_ci					 * Save what we have and then wait for
17818c2ecf20Sopenharmony_ci					 * more data.
17828c2ecf20Sopenharmony_ci					 */
17838c2ecf20Sopenharmony_ci					edge_serial->rxStatusParam
17848c2ecf20Sopenharmony_ci						= edge_serial->rxHeader2;
17858c2ecf20Sopenharmony_ci					edge_serial->rxState = EXPECT_HDR3;
17868c2ecf20Sopenharmony_ci					break;
17878c2ecf20Sopenharmony_ci				}
17888c2ecf20Sopenharmony_ci				/* We have all the header bytes, process the
17898c2ecf20Sopenharmony_ci				   status now */
17908c2ecf20Sopenharmony_ci				process_rcvd_status(edge_serial,
17918c2ecf20Sopenharmony_ci						edge_serial->rxHeader2, 0);
17928c2ecf20Sopenharmony_ci				edge_serial->rxState = EXPECT_HDR1;
17938c2ecf20Sopenharmony_ci				break;
17948c2ecf20Sopenharmony_ci			}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci			edge_serial->rxPort = IOSP_GET_HDR_PORT(edge_serial->rxHeader1);
17978c2ecf20Sopenharmony_ci			edge_serial->rxBytesRemaining = IOSP_GET_HDR_DATA_LEN(edge_serial->rxHeader1,
17988c2ecf20Sopenharmony_ci									      edge_serial->rxHeader2);
17998c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - Data for Port %u Len %u\n", __func__,
18008c2ecf20Sopenharmony_ci				edge_serial->rxPort,
18018c2ecf20Sopenharmony_ci				edge_serial->rxBytesRemaining);
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci			if (bufferLength == 0) {
18048c2ecf20Sopenharmony_ci				edge_serial->rxState = EXPECT_DATA;
18058c2ecf20Sopenharmony_ci				break;
18068c2ecf20Sopenharmony_ci			}
18078c2ecf20Sopenharmony_ci			fallthrough;
18088c2ecf20Sopenharmony_ci		case EXPECT_DATA: /* Expect data */
18098c2ecf20Sopenharmony_ci			if (bufferLength < edge_serial->rxBytesRemaining) {
18108c2ecf20Sopenharmony_ci				rxLen = bufferLength;
18118c2ecf20Sopenharmony_ci				/* Expect data to start next buffer */
18128c2ecf20Sopenharmony_ci				edge_serial->rxState = EXPECT_DATA;
18138c2ecf20Sopenharmony_ci			} else {
18148c2ecf20Sopenharmony_ci				/* BufLen >= RxBytesRemaining */
18158c2ecf20Sopenharmony_ci				rxLen = edge_serial->rxBytesRemaining;
18168c2ecf20Sopenharmony_ci				/* Start another header next time */
18178c2ecf20Sopenharmony_ci				edge_serial->rxState = EXPECT_HDR1;
18188c2ecf20Sopenharmony_ci			}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci			bufferLength -= rxLen;
18218c2ecf20Sopenharmony_ci			edge_serial->rxBytesRemaining -= rxLen;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci			/* spit this data back into the tty driver if this
18248c2ecf20Sopenharmony_ci			   port is open */
18258c2ecf20Sopenharmony_ci			if (rxLen && edge_serial->rxPort < serial->num_ports) {
18268c2ecf20Sopenharmony_ci				port = serial->port[edge_serial->rxPort];
18278c2ecf20Sopenharmony_ci				edge_port = usb_get_serial_port_data(port);
18288c2ecf20Sopenharmony_ci				if (edge_port && edge_port->open) {
18298c2ecf20Sopenharmony_ci					dev_dbg(dev, "%s - Sending %d bytes to TTY for port %d\n",
18308c2ecf20Sopenharmony_ci						__func__, rxLen,
18318c2ecf20Sopenharmony_ci						edge_serial->rxPort);
18328c2ecf20Sopenharmony_ci					edge_tty_recv(edge_port->port, buffer,
18338c2ecf20Sopenharmony_ci							rxLen);
18348c2ecf20Sopenharmony_ci					edge_port->port->icount.rx += rxLen;
18358c2ecf20Sopenharmony_ci				}
18368c2ecf20Sopenharmony_ci			}
18378c2ecf20Sopenharmony_ci			buffer += rxLen;
18388c2ecf20Sopenharmony_ci			break;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci		case EXPECT_HDR3:	/* Expect 3rd byte of status header */
18418c2ecf20Sopenharmony_ci			edge_serial->rxHeader3 = *buffer;
18428c2ecf20Sopenharmony_ci			++buffer;
18438c2ecf20Sopenharmony_ci			--bufferLength;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci			/* We have all the header bytes, process the
18468c2ecf20Sopenharmony_ci			   status now */
18478c2ecf20Sopenharmony_ci			process_rcvd_status(edge_serial,
18488c2ecf20Sopenharmony_ci				edge_serial->rxStatusParam,
18498c2ecf20Sopenharmony_ci				edge_serial->rxHeader3);
18508c2ecf20Sopenharmony_ci			edge_serial->rxState = EXPECT_HDR1;
18518c2ecf20Sopenharmony_ci			break;
18528c2ecf20Sopenharmony_ci		}
18538c2ecf20Sopenharmony_ci	}
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci/*****************************************************************************
18588c2ecf20Sopenharmony_ci * process_rcvd_status
18598c2ecf20Sopenharmony_ci *	this function handles the any status messages received on the
18608c2ecf20Sopenharmony_ci *	bulk in pipe.
18618c2ecf20Sopenharmony_ci *****************************************************************************/
18628c2ecf20Sopenharmony_cistatic void process_rcvd_status(struct edgeport_serial *edge_serial,
18638c2ecf20Sopenharmony_ci						__u8 byte2, __u8 byte3)
18648c2ecf20Sopenharmony_ci{
18658c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
18668c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port;
18678c2ecf20Sopenharmony_ci	struct tty_struct *tty;
18688c2ecf20Sopenharmony_ci	struct device *dev;
18698c2ecf20Sopenharmony_ci	__u8 code = edge_serial->rxStatusCode;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	/* switch the port pointer to the one being currently talked about */
18728c2ecf20Sopenharmony_ci	if (edge_serial->rxPort >= edge_serial->serial->num_ports)
18738c2ecf20Sopenharmony_ci		return;
18748c2ecf20Sopenharmony_ci	port = edge_serial->serial->port[edge_serial->rxPort];
18758c2ecf20Sopenharmony_ci	edge_port = usb_get_serial_port_data(port);
18768c2ecf20Sopenharmony_ci	if (edge_port == NULL) {
18778c2ecf20Sopenharmony_ci		dev_err(&edge_serial->serial->dev->dev,
18788c2ecf20Sopenharmony_ci			"%s - edge_port == NULL for port %d\n",
18798c2ecf20Sopenharmony_ci					__func__, edge_serial->rxPort);
18808c2ecf20Sopenharmony_ci		return;
18818c2ecf20Sopenharmony_ci	}
18828c2ecf20Sopenharmony_ci	dev = &port->dev;
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	if (code == IOSP_EXT_STATUS) {
18858c2ecf20Sopenharmony_ci		switch (byte2) {
18868c2ecf20Sopenharmony_ci		case IOSP_EXT_STATUS_CHASE_RSP:
18878c2ecf20Sopenharmony_ci			/* we want to do EXT status regardless of port
18888c2ecf20Sopenharmony_ci			 * open/closed */
18898c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - Port %u EXT CHASE_RSP Data = %02x\n",
18908c2ecf20Sopenharmony_ci				__func__, edge_serial->rxPort, byte3);
18918c2ecf20Sopenharmony_ci			/* Currently, the only EXT_STATUS is Chase, so process
18928c2ecf20Sopenharmony_ci			 * here instead of one more call to one more subroutine
18938c2ecf20Sopenharmony_ci			 * If/when more EXT_STATUS, there'll be more work to do
18948c2ecf20Sopenharmony_ci			 * Also, we currently clear flag and close the port
18958c2ecf20Sopenharmony_ci			 * regardless of content of above's Byte3.
18968c2ecf20Sopenharmony_ci			 * We could choose to do something else when Byte3 says
18978c2ecf20Sopenharmony_ci			 * Timeout on Chase from Edgeport, like wait longer in
18988c2ecf20Sopenharmony_ci			 * block_until_chase_response, but for now we don't.
18998c2ecf20Sopenharmony_ci			 */
19008c2ecf20Sopenharmony_ci			edge_port->chaseResponsePending = false;
19018c2ecf20Sopenharmony_ci			wake_up(&edge_port->wait_chase);
19028c2ecf20Sopenharmony_ci			return;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci		case IOSP_EXT_STATUS_RX_CHECK_RSP:
19058c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s ========== Port %u CHECK_RSP Sequence = %02x =============\n",
19068c2ecf20Sopenharmony_ci				__func__, edge_serial->rxPort, byte3);
19078c2ecf20Sopenharmony_ci			/* Port->RxCheckRsp = true; */
19088c2ecf20Sopenharmony_ci			return;
19098c2ecf20Sopenharmony_ci		}
19108c2ecf20Sopenharmony_ci	}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	if (code == IOSP_STATUS_OPEN_RSP) {
19138c2ecf20Sopenharmony_ci		edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3);
19148c2ecf20Sopenharmony_ci		edge_port->maxTxCredits = edge_port->txCredits;
19158c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d\n",
19168c2ecf20Sopenharmony_ci			__func__, edge_serial->rxPort, byte2, edge_port->txCredits);
19178c2ecf20Sopenharmony_ci		handle_new_msr(edge_port, byte2);
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci		/* send the current line settings to the port so we are
19208c2ecf20Sopenharmony_ci		   in sync with any further termios calls */
19218c2ecf20Sopenharmony_ci		tty = tty_port_tty_get(&edge_port->port->port);
19228c2ecf20Sopenharmony_ci		if (tty) {
19238c2ecf20Sopenharmony_ci			change_port_settings(tty,
19248c2ecf20Sopenharmony_ci				edge_port, &tty->termios);
19258c2ecf20Sopenharmony_ci			tty_kref_put(tty);
19268c2ecf20Sopenharmony_ci		}
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci		/* we have completed the open */
19298c2ecf20Sopenharmony_ci		edge_port->openPending = false;
19308c2ecf20Sopenharmony_ci		edge_port->open = true;
19318c2ecf20Sopenharmony_ci		wake_up(&edge_port->wait_open);
19328c2ecf20Sopenharmony_ci		return;
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	/* If port is closed, silently discard all rcvd status. We can
19368c2ecf20Sopenharmony_ci	 * have cases where buffered status is received AFTER the close
19378c2ecf20Sopenharmony_ci	 * port command is sent to the Edgeport.
19388c2ecf20Sopenharmony_ci	 */
19398c2ecf20Sopenharmony_ci	if (!edge_port->open || edge_port->closePending)
19408c2ecf20Sopenharmony_ci		return;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	switch (code) {
19438c2ecf20Sopenharmony_ci	/* Not currently sent by Edgeport */
19448c2ecf20Sopenharmony_ci	case IOSP_STATUS_LSR:
19458c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Port %u LSR Status = %02x\n",
19468c2ecf20Sopenharmony_ci			__func__, edge_serial->rxPort, byte2);
19478c2ecf20Sopenharmony_ci		handle_new_lsr(edge_port, false, byte2, 0);
19488c2ecf20Sopenharmony_ci		break;
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	case IOSP_STATUS_LSR_DATA:
19518c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Port %u LSR Status = %02x, Data = %02x\n",
19528c2ecf20Sopenharmony_ci			__func__, edge_serial->rxPort, byte2, byte3);
19538c2ecf20Sopenharmony_ci		/* byte2 is LSR Register */
19548c2ecf20Sopenharmony_ci		/* byte3 is broken data byte */
19558c2ecf20Sopenharmony_ci		handle_new_lsr(edge_port, true, byte2, byte3);
19568c2ecf20Sopenharmony_ci		break;
19578c2ecf20Sopenharmony_ci	/*
19588c2ecf20Sopenharmony_ci	 *	case IOSP_EXT_4_STATUS:
19598c2ecf20Sopenharmony_ci	 *		dev_dbg(dev, "%s - Port %u LSR Status = %02x Data = %02x\n",
19608c2ecf20Sopenharmony_ci	 *			__func__, edge_serial->rxPort, byte2, byte3);
19618c2ecf20Sopenharmony_ci	 *		break;
19628c2ecf20Sopenharmony_ci	 */
19638c2ecf20Sopenharmony_ci	case IOSP_STATUS_MSR:
19648c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Port %u MSR Status = %02x\n",
19658c2ecf20Sopenharmony_ci			__func__, edge_serial->rxPort, byte2);
19668c2ecf20Sopenharmony_ci		/*
19678c2ecf20Sopenharmony_ci		 * Process this new modem status and generate appropriate
19688c2ecf20Sopenharmony_ci		 * events, etc, based on the new status. This routine
19698c2ecf20Sopenharmony_ci		 * also saves the MSR in Port->ShadowMsr.
19708c2ecf20Sopenharmony_ci		 */
19718c2ecf20Sopenharmony_ci		handle_new_msr(edge_port, byte2);
19728c2ecf20Sopenharmony_ci		break;
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci	default:
19758c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Unrecognized IOSP status code %u\n", __func__, code);
19768c2ecf20Sopenharmony_ci		break;
19778c2ecf20Sopenharmony_ci	}
19788c2ecf20Sopenharmony_ci}
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci/*****************************************************************************
19828c2ecf20Sopenharmony_ci * edge_tty_recv
19838c2ecf20Sopenharmony_ci *	this function passes data on to the tty flip buffer
19848c2ecf20Sopenharmony_ci *****************************************************************************/
19858c2ecf20Sopenharmony_cistatic void edge_tty_recv(struct usb_serial_port *port, unsigned char *data,
19868c2ecf20Sopenharmony_ci		int length)
19878c2ecf20Sopenharmony_ci{
19888c2ecf20Sopenharmony_ci	int cnt;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci	cnt = tty_insert_flip_string(&port->port, data, length);
19918c2ecf20Sopenharmony_ci	if (cnt < length) {
19928c2ecf20Sopenharmony_ci		dev_err(&port->dev, "%s - dropping data, %d bytes lost\n",
19938c2ecf20Sopenharmony_ci				__func__, length - cnt);
19948c2ecf20Sopenharmony_ci	}
19958c2ecf20Sopenharmony_ci	data += cnt;
19968c2ecf20Sopenharmony_ci	length -= cnt;
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	tty_flip_buffer_push(&port->port);
19998c2ecf20Sopenharmony_ci}
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_ci/*****************************************************************************
20038c2ecf20Sopenharmony_ci * handle_new_msr
20048c2ecf20Sopenharmony_ci *	this function handles any change to the msr register for a port.
20058c2ecf20Sopenharmony_ci *****************************************************************************/
20068c2ecf20Sopenharmony_cistatic void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr)
20078c2ecf20Sopenharmony_ci{
20088c2ecf20Sopenharmony_ci	struct  async_icount *icount;
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
20118c2ecf20Sopenharmony_ci			EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
20128c2ecf20Sopenharmony_ci		icount = &edge_port->port->icount;
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci		/* update input line counters */
20158c2ecf20Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_CTS)
20168c2ecf20Sopenharmony_ci			icount->cts++;
20178c2ecf20Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_DSR)
20188c2ecf20Sopenharmony_ci			icount->dsr++;
20198c2ecf20Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_CD)
20208c2ecf20Sopenharmony_ci			icount->dcd++;
20218c2ecf20Sopenharmony_ci		if (newMsr & EDGEPORT_MSR_DELTA_RI)
20228c2ecf20Sopenharmony_ci			icount->rng++;
20238c2ecf20Sopenharmony_ci		wake_up_interruptible(&edge_port->port->port.delta_msr_wait);
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	/* Save the new modem status */
20278c2ecf20Sopenharmony_ci	edge_port->shadowMSR = newMsr & 0xf0;
20288c2ecf20Sopenharmony_ci}
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci/*****************************************************************************
20328c2ecf20Sopenharmony_ci * handle_new_lsr
20338c2ecf20Sopenharmony_ci *	this function handles any change to the lsr register for a port.
20348c2ecf20Sopenharmony_ci *****************************************************************************/
20358c2ecf20Sopenharmony_cistatic void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
20368c2ecf20Sopenharmony_ci							__u8 lsr, __u8 data)
20378c2ecf20Sopenharmony_ci{
20388c2ecf20Sopenharmony_ci	__u8 newLsr = (__u8) (lsr & (__u8)
20398c2ecf20Sopenharmony_ci		(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK));
20408c2ecf20Sopenharmony_ci	struct async_icount *icount;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	edge_port->shadowLSR = lsr;
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	if (newLsr & LSR_BREAK) {
20458c2ecf20Sopenharmony_ci		/*
20468c2ecf20Sopenharmony_ci		 * Parity and Framing errors only count if they
20478c2ecf20Sopenharmony_ci		 * occur exclusive of a break being
20488c2ecf20Sopenharmony_ci		 * received.
20498c2ecf20Sopenharmony_ci		 */
20508c2ecf20Sopenharmony_ci		newLsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
20518c2ecf20Sopenharmony_ci	}
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci	/* Place LSR data byte into Rx buffer */
20548c2ecf20Sopenharmony_ci	if (lsrData)
20558c2ecf20Sopenharmony_ci		edge_tty_recv(edge_port->port, &data, 1);
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	/* update input line counters */
20588c2ecf20Sopenharmony_ci	icount = &edge_port->port->icount;
20598c2ecf20Sopenharmony_ci	if (newLsr & LSR_BREAK)
20608c2ecf20Sopenharmony_ci		icount->brk++;
20618c2ecf20Sopenharmony_ci	if (newLsr & LSR_OVER_ERR)
20628c2ecf20Sopenharmony_ci		icount->overrun++;
20638c2ecf20Sopenharmony_ci	if (newLsr & LSR_PAR_ERR)
20648c2ecf20Sopenharmony_ci		icount->parity++;
20658c2ecf20Sopenharmony_ci	if (newLsr & LSR_FRM_ERR)
20668c2ecf20Sopenharmony_ci		icount->frame++;
20678c2ecf20Sopenharmony_ci}
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci/****************************************************************************
20718c2ecf20Sopenharmony_ci * sram_write
20728c2ecf20Sopenharmony_ci *	writes a number of bytes to the Edgeport device's sram starting at the
20738c2ecf20Sopenharmony_ci *	given address.
20748c2ecf20Sopenharmony_ci *	If successful returns the number of bytes written, otherwise it returns
20758c2ecf20Sopenharmony_ci *	a negative error number of the problem.
20768c2ecf20Sopenharmony_ci ****************************************************************************/
20778c2ecf20Sopenharmony_cistatic int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
20788c2ecf20Sopenharmony_ci					__u16 length, const __u8 *data)
20798c2ecf20Sopenharmony_ci{
20808c2ecf20Sopenharmony_ci	int result;
20818c2ecf20Sopenharmony_ci	__u16 current_length;
20828c2ecf20Sopenharmony_ci	unsigned char *transfer_buffer;
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	dev_dbg(&serial->dev->dev, "%s - %x, %x, %d\n", __func__, extAddr, addr, length);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	transfer_buffer =  kmalloc(64, GFP_KERNEL);
20878c2ecf20Sopenharmony_ci	if (!transfer_buffer)
20888c2ecf20Sopenharmony_ci		return -ENOMEM;
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	/* need to split these writes up into 64 byte chunks */
20918c2ecf20Sopenharmony_ci	result = 0;
20928c2ecf20Sopenharmony_ci	while (length > 0) {
20938c2ecf20Sopenharmony_ci		if (length > 64)
20948c2ecf20Sopenharmony_ci			current_length = 64;
20958c2ecf20Sopenharmony_ci		else
20968c2ecf20Sopenharmony_ci			current_length = length;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci/*		dev_dbg(&serial->dev->dev, "%s - writing %x, %x, %d\n", __func__, extAddr, addr, current_length); */
20998c2ecf20Sopenharmony_ci		memcpy(transfer_buffer, data, current_length);
21008c2ecf20Sopenharmony_ci		result = usb_control_msg(serial->dev,
21018c2ecf20Sopenharmony_ci					usb_sndctrlpipe(serial->dev, 0),
21028c2ecf20Sopenharmony_ci					USB_REQUEST_ION_WRITE_RAM,
21038c2ecf20Sopenharmony_ci					0x40, addr, extAddr, transfer_buffer,
21048c2ecf20Sopenharmony_ci					current_length, 300);
21058c2ecf20Sopenharmony_ci		if (result < 0)
21068c2ecf20Sopenharmony_ci			break;
21078c2ecf20Sopenharmony_ci		length -= current_length;
21088c2ecf20Sopenharmony_ci		addr += current_length;
21098c2ecf20Sopenharmony_ci		data += current_length;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	kfree(transfer_buffer);
21138c2ecf20Sopenharmony_ci	return result;
21148c2ecf20Sopenharmony_ci}
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci/****************************************************************************
21188c2ecf20Sopenharmony_ci * rom_write
21198c2ecf20Sopenharmony_ci *	writes a number of bytes to the Edgeport device's ROM starting at the
21208c2ecf20Sopenharmony_ci *	given address.
21218c2ecf20Sopenharmony_ci *	If successful returns the number of bytes written, otherwise it returns
21228c2ecf20Sopenharmony_ci *	a negative error number of the problem.
21238c2ecf20Sopenharmony_ci ****************************************************************************/
21248c2ecf20Sopenharmony_cistatic int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
21258c2ecf20Sopenharmony_ci					__u16 length, const __u8 *data)
21268c2ecf20Sopenharmony_ci{
21278c2ecf20Sopenharmony_ci	int result;
21288c2ecf20Sopenharmony_ci	__u16 current_length;
21298c2ecf20Sopenharmony_ci	unsigned char *transfer_buffer;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci	transfer_buffer =  kmalloc(64, GFP_KERNEL);
21328c2ecf20Sopenharmony_ci	if (!transfer_buffer)
21338c2ecf20Sopenharmony_ci		return -ENOMEM;
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/* need to split these writes up into 64 byte chunks */
21368c2ecf20Sopenharmony_ci	result = 0;
21378c2ecf20Sopenharmony_ci	while (length > 0) {
21388c2ecf20Sopenharmony_ci		if (length > 64)
21398c2ecf20Sopenharmony_ci			current_length = 64;
21408c2ecf20Sopenharmony_ci		else
21418c2ecf20Sopenharmony_ci			current_length = length;
21428c2ecf20Sopenharmony_ci		memcpy(transfer_buffer, data, current_length);
21438c2ecf20Sopenharmony_ci		result = usb_control_msg(serial->dev,
21448c2ecf20Sopenharmony_ci					usb_sndctrlpipe(serial->dev, 0),
21458c2ecf20Sopenharmony_ci					USB_REQUEST_ION_WRITE_ROM, 0x40,
21468c2ecf20Sopenharmony_ci					addr, extAddr,
21478c2ecf20Sopenharmony_ci					transfer_buffer, current_length, 300);
21488c2ecf20Sopenharmony_ci		if (result < 0)
21498c2ecf20Sopenharmony_ci			break;
21508c2ecf20Sopenharmony_ci		length -= current_length;
21518c2ecf20Sopenharmony_ci		addr += current_length;
21528c2ecf20Sopenharmony_ci		data += current_length;
21538c2ecf20Sopenharmony_ci	}
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci	kfree(transfer_buffer);
21568c2ecf20Sopenharmony_ci	return result;
21578c2ecf20Sopenharmony_ci}
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci/****************************************************************************
21618c2ecf20Sopenharmony_ci * rom_read
21628c2ecf20Sopenharmony_ci *	reads a number of bytes from the Edgeport device starting at the given
21638c2ecf20Sopenharmony_ci *	address.
21648c2ecf20Sopenharmony_ci *	Returns zero on success or a negative error number.
21658c2ecf20Sopenharmony_ci ****************************************************************************/
21668c2ecf20Sopenharmony_cistatic int rom_read(struct usb_serial *serial, __u16 extAddr,
21678c2ecf20Sopenharmony_ci					__u16 addr, __u16 length, __u8 *data)
21688c2ecf20Sopenharmony_ci{
21698c2ecf20Sopenharmony_ci	int result;
21708c2ecf20Sopenharmony_ci	__u16 current_length;
21718c2ecf20Sopenharmony_ci	unsigned char *transfer_buffer;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	transfer_buffer =  kmalloc(64, GFP_KERNEL);
21748c2ecf20Sopenharmony_ci	if (!transfer_buffer)
21758c2ecf20Sopenharmony_ci		return -ENOMEM;
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci	/* need to split these reads up into 64 byte chunks */
21788c2ecf20Sopenharmony_ci	result = 0;
21798c2ecf20Sopenharmony_ci	while (length > 0) {
21808c2ecf20Sopenharmony_ci		if (length > 64)
21818c2ecf20Sopenharmony_ci			current_length = 64;
21828c2ecf20Sopenharmony_ci		else
21838c2ecf20Sopenharmony_ci			current_length = length;
21848c2ecf20Sopenharmony_ci		result = usb_control_msg(serial->dev,
21858c2ecf20Sopenharmony_ci					usb_rcvctrlpipe(serial->dev, 0),
21868c2ecf20Sopenharmony_ci					USB_REQUEST_ION_READ_ROM,
21878c2ecf20Sopenharmony_ci					0xC0, addr, extAddr, transfer_buffer,
21888c2ecf20Sopenharmony_ci					current_length, 300);
21898c2ecf20Sopenharmony_ci		if (result < current_length) {
21908c2ecf20Sopenharmony_ci			if (result >= 0)
21918c2ecf20Sopenharmony_ci				result = -EIO;
21928c2ecf20Sopenharmony_ci			break;
21938c2ecf20Sopenharmony_ci		}
21948c2ecf20Sopenharmony_ci		memcpy(data, transfer_buffer, current_length);
21958c2ecf20Sopenharmony_ci		length -= current_length;
21968c2ecf20Sopenharmony_ci		addr += current_length;
21978c2ecf20Sopenharmony_ci		data += current_length;
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci		result = 0;
22008c2ecf20Sopenharmony_ci	}
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	kfree(transfer_buffer);
22038c2ecf20Sopenharmony_ci	return result;
22048c2ecf20Sopenharmony_ci}
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci/****************************************************************************
22088c2ecf20Sopenharmony_ci * send_iosp_ext_cmd
22098c2ecf20Sopenharmony_ci *	Is used to send a IOSP message to the Edgeport device
22108c2ecf20Sopenharmony_ci ****************************************************************************/
22118c2ecf20Sopenharmony_cistatic int send_iosp_ext_cmd(struct edgeport_port *edge_port,
22128c2ecf20Sopenharmony_ci						__u8 command, __u8 param)
22138c2ecf20Sopenharmony_ci{
22148c2ecf20Sopenharmony_ci	unsigned char   *buffer;
22158c2ecf20Sopenharmony_ci	unsigned char   *currentCommand;
22168c2ecf20Sopenharmony_ci	int             length = 0;
22178c2ecf20Sopenharmony_ci	int             status = 0;
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	buffer = kmalloc(10, GFP_ATOMIC);
22208c2ecf20Sopenharmony_ci	if (!buffer)
22218c2ecf20Sopenharmony_ci		return -ENOMEM;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	currentCommand = buffer;
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	MAKE_CMD_EXT_CMD(&currentCommand, &length, edge_port->port->port_number,
22268c2ecf20Sopenharmony_ci			 command, param);
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	status = write_cmd_usb(edge_port, buffer, length);
22298c2ecf20Sopenharmony_ci	if (status) {
22308c2ecf20Sopenharmony_ci		/* something bad happened, let's free up the memory */
22318c2ecf20Sopenharmony_ci		kfree(buffer);
22328c2ecf20Sopenharmony_ci	}
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	return status;
22358c2ecf20Sopenharmony_ci}
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci/*****************************************************************************
22398c2ecf20Sopenharmony_ci * write_cmd_usb
22408c2ecf20Sopenharmony_ci *	this function writes the given buffer out to the bulk write endpoint.
22418c2ecf20Sopenharmony_ci *****************************************************************************/
22428c2ecf20Sopenharmony_cistatic int write_cmd_usb(struct edgeport_port *edge_port,
22438c2ecf20Sopenharmony_ci					unsigned char *buffer, int length)
22448c2ecf20Sopenharmony_ci{
22458c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial =
22468c2ecf20Sopenharmony_ci				usb_get_serial_data(edge_port->port->serial);
22478c2ecf20Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
22488c2ecf20Sopenharmony_ci	int status = 0;
22498c2ecf20Sopenharmony_ci	struct urb *urb;
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, length, buffer);
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	/* Allocate our next urb */
22548c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_ATOMIC);
22558c2ecf20Sopenharmony_ci	if (!urb)
22568c2ecf20Sopenharmony_ci		return -ENOMEM;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	atomic_inc(&CmdUrbs);
22598c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - ALLOCATE URB %p (outstanding %d)\n",
22608c2ecf20Sopenharmony_ci		__func__, urb, atomic_read(&CmdUrbs));
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, edge_serial->serial->dev,
22638c2ecf20Sopenharmony_ci			usb_sndbulkpipe(edge_serial->serial->dev,
22648c2ecf20Sopenharmony_ci					edge_serial->bulk_out_endpoint),
22658c2ecf20Sopenharmony_ci			buffer, length, edge_bulk_out_cmd_callback, edge_port);
22668c2ecf20Sopenharmony_ci
22678c2ecf20Sopenharmony_ci	edge_port->commandPending = true;
22688c2ecf20Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	if (status) {
22718c2ecf20Sopenharmony_ci		/* something went wrong */
22728c2ecf20Sopenharmony_ci		dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
22738c2ecf20Sopenharmony_ci			__func__, status);
22748c2ecf20Sopenharmony_ci		usb_free_urb(urb);
22758c2ecf20Sopenharmony_ci		atomic_dec(&CmdUrbs);
22768c2ecf20Sopenharmony_ci		return status;
22778c2ecf20Sopenharmony_ci	}
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci#if 0
22808c2ecf20Sopenharmony_ci	wait_event(&edge_port->wait_command, !edge_port->commandPending);
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	if (edge_port->commandPending) {
22838c2ecf20Sopenharmony_ci		/* command timed out */
22848c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - command timed out\n", __func__);
22858c2ecf20Sopenharmony_ci		status = -EINVAL;
22868c2ecf20Sopenharmony_ci	}
22878c2ecf20Sopenharmony_ci#endif
22888c2ecf20Sopenharmony_ci	return status;
22898c2ecf20Sopenharmony_ci}
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci/*****************************************************************************
22938c2ecf20Sopenharmony_ci * send_cmd_write_baud_rate
22948c2ecf20Sopenharmony_ci *	this function sends the proper command to change the baud rate of the
22958c2ecf20Sopenharmony_ci *	specified port.
22968c2ecf20Sopenharmony_ci *****************************************************************************/
22978c2ecf20Sopenharmony_cistatic int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
22988c2ecf20Sopenharmony_ci								int baudRate)
22998c2ecf20Sopenharmony_ci{
23008c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial =
23018c2ecf20Sopenharmony_ci				usb_get_serial_data(edge_port->port->serial);
23028c2ecf20Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
23038c2ecf20Sopenharmony_ci	unsigned char *cmdBuffer;
23048c2ecf20Sopenharmony_ci	unsigned char *currCmd;
23058c2ecf20Sopenharmony_ci	int cmdLen = 0;
23068c2ecf20Sopenharmony_ci	int divisor;
23078c2ecf20Sopenharmony_ci	int status;
23088c2ecf20Sopenharmony_ci	u32 number = edge_port->port->port_number;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	if (edge_serial->is_epic &&
23118c2ecf20Sopenharmony_ci	    !edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) {
23128c2ecf20Sopenharmony_ci		dev_dbg(dev, "SendCmdWriteBaudRate - NOT Setting baud rate for port, baud = %d\n",
23138c2ecf20Sopenharmony_ci			baudRate);
23148c2ecf20Sopenharmony_ci		return 0;
23158c2ecf20Sopenharmony_ci	}
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - baud = %d\n", __func__, baudRate);
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	status = calc_baud_rate_divisor(dev, baudRate, &divisor);
23208c2ecf20Sopenharmony_ci	if (status) {
23218c2ecf20Sopenharmony_ci		dev_err(dev, "%s - bad baud rate\n", __func__);
23228c2ecf20Sopenharmony_ci		return status;
23238c2ecf20Sopenharmony_ci	}
23248c2ecf20Sopenharmony_ci
23258c2ecf20Sopenharmony_ci	/* Alloc memory for the string of commands. */
23268c2ecf20Sopenharmony_ci	cmdBuffer =  kmalloc(0x100, GFP_ATOMIC);
23278c2ecf20Sopenharmony_ci	if (!cmdBuffer)
23288c2ecf20Sopenharmony_ci		return -ENOMEM;
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	currCmd = cmdBuffer;
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci	/* Enable access to divisor latch */
23338c2ecf20Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR, LCR_DL_ENABLE);
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci	/* Write the divisor itself */
23368c2ecf20Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLL, LOW8(divisor));
23378c2ecf20Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, DLM, HIGH8(divisor));
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci	/* Restore original value to disable access to divisor latch */
23408c2ecf20Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, number, LCR,
23418c2ecf20Sopenharmony_ci						edge_port->shadowLCR);
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
23448c2ecf20Sopenharmony_ci	if (status) {
23458c2ecf20Sopenharmony_ci		/* something bad happened, let's free up the memory */
23468c2ecf20Sopenharmony_ci		kfree(cmdBuffer);
23478c2ecf20Sopenharmony_ci	}
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci	return status;
23508c2ecf20Sopenharmony_ci}
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci/*****************************************************************************
23548c2ecf20Sopenharmony_ci * calc_baud_rate_divisor
23558c2ecf20Sopenharmony_ci *	this function calculates the proper baud rate divisor for the specified
23568c2ecf20Sopenharmony_ci *	baud rate.
23578c2ecf20Sopenharmony_ci *****************************************************************************/
23588c2ecf20Sopenharmony_cistatic int calc_baud_rate_divisor(struct device *dev, int baudrate, int *divisor)
23598c2ecf20Sopenharmony_ci{
23608c2ecf20Sopenharmony_ci	int i;
23618c2ecf20Sopenharmony_ci	__u16 custom;
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
23648c2ecf20Sopenharmony_ci		if (divisor_table[i].BaudRate == baudrate) {
23658c2ecf20Sopenharmony_ci			*divisor = divisor_table[i].Divisor;
23668c2ecf20Sopenharmony_ci			return 0;
23678c2ecf20Sopenharmony_ci		}
23688c2ecf20Sopenharmony_ci	}
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	/* We have tried all of the standard baud rates
23718c2ecf20Sopenharmony_ci	 * lets try to calculate the divisor for this baud rate
23728c2ecf20Sopenharmony_ci	 * Make sure the baud rate is reasonable */
23738c2ecf20Sopenharmony_ci	if (baudrate > 50 && baudrate < 230400) {
23748c2ecf20Sopenharmony_ci		/* get divisor */
23758c2ecf20Sopenharmony_ci		custom = (__u16)((230400L + baudrate/2) / baudrate);
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci		*divisor = custom;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - Baud %d = %d\n", __func__, baudrate, custom);
23808c2ecf20Sopenharmony_ci		return 0;
23818c2ecf20Sopenharmony_ci	}
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci	return -1;
23848c2ecf20Sopenharmony_ci}
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci/*****************************************************************************
23888c2ecf20Sopenharmony_ci * send_cmd_write_uart_register
23898c2ecf20Sopenharmony_ci *  this function builds up a uart register message and sends to the device.
23908c2ecf20Sopenharmony_ci *****************************************************************************/
23918c2ecf20Sopenharmony_cistatic int send_cmd_write_uart_register(struct edgeport_port *edge_port,
23928c2ecf20Sopenharmony_ci						__u8 regNum, __u8 regValue)
23938c2ecf20Sopenharmony_ci{
23948c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial =
23958c2ecf20Sopenharmony_ci				usb_get_serial_data(edge_port->port->serial);
23968c2ecf20Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
23978c2ecf20Sopenharmony_ci	unsigned char *cmdBuffer;
23988c2ecf20Sopenharmony_ci	unsigned char *currCmd;
23998c2ecf20Sopenharmony_ci	unsigned long cmdLen = 0;
24008c2ecf20Sopenharmony_ci	int status;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - write to %s register 0x%02x\n",
24038c2ecf20Sopenharmony_ci		(regNum == MCR) ? "MCR" : "LCR", __func__, regValue);
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	if (edge_serial->is_epic &&
24068c2ecf20Sopenharmony_ci	    !edge_serial->epic_descriptor.Supports.IOSPWriteMCR &&
24078c2ecf20Sopenharmony_ci	    regNum == MCR) {
24088c2ecf20Sopenharmony_ci		dev_dbg(dev, "SendCmdWriteUartReg - Not writing to MCR Register\n");
24098c2ecf20Sopenharmony_ci		return 0;
24108c2ecf20Sopenharmony_ci	}
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	if (edge_serial->is_epic &&
24138c2ecf20Sopenharmony_ci	    !edge_serial->epic_descriptor.Supports.IOSPWriteLCR &&
24148c2ecf20Sopenharmony_ci	    regNum == LCR) {
24158c2ecf20Sopenharmony_ci		dev_dbg(dev, "SendCmdWriteUartReg - Not writing to LCR Register\n");
24168c2ecf20Sopenharmony_ci		return 0;
24178c2ecf20Sopenharmony_ci	}
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	/* Alloc memory for the string of commands. */
24208c2ecf20Sopenharmony_ci	cmdBuffer = kmalloc(0x10, GFP_ATOMIC);
24218c2ecf20Sopenharmony_ci	if (cmdBuffer == NULL)
24228c2ecf20Sopenharmony_ci		return -ENOMEM;
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	currCmd = cmdBuffer;
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci	/* Build a cmd in the buffer to write the given register */
24278c2ecf20Sopenharmony_ci	MAKE_CMD_WRITE_REG(&currCmd, &cmdLen, edge_port->port->port_number,
24288c2ecf20Sopenharmony_ci			   regNum, regValue);
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	status = write_cmd_usb(edge_port, cmdBuffer, cmdLen);
24318c2ecf20Sopenharmony_ci	if (status) {
24328c2ecf20Sopenharmony_ci		/* something bad happened, let's free up the memory */
24338c2ecf20Sopenharmony_ci		kfree(cmdBuffer);
24348c2ecf20Sopenharmony_ci	}
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci	return status;
24378c2ecf20Sopenharmony_ci}
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci/*****************************************************************************
24418c2ecf20Sopenharmony_ci * change_port_settings
24428c2ecf20Sopenharmony_ci *	This routine is called to set the UART on the device to match the
24438c2ecf20Sopenharmony_ci *	specified new settings.
24448c2ecf20Sopenharmony_ci *****************************************************************************/
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty,
24478c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port, struct ktermios *old_termios)
24488c2ecf20Sopenharmony_ci{
24498c2ecf20Sopenharmony_ci	struct device *dev = &edge_port->port->dev;
24508c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial =
24518c2ecf20Sopenharmony_ci			usb_get_serial_data(edge_port->port->serial);
24528c2ecf20Sopenharmony_ci	int baud;
24538c2ecf20Sopenharmony_ci	unsigned cflag;
24548c2ecf20Sopenharmony_ci	__u8 mask = 0xff;
24558c2ecf20Sopenharmony_ci	__u8 lData;
24568c2ecf20Sopenharmony_ci	__u8 lParity;
24578c2ecf20Sopenharmony_ci	__u8 lStop;
24588c2ecf20Sopenharmony_ci	__u8 rxFlow;
24598c2ecf20Sopenharmony_ci	__u8 txFlow;
24608c2ecf20Sopenharmony_ci	int status;
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	if (!edge_port->open &&
24638c2ecf20Sopenharmony_ci	    !edge_port->openPending) {
24648c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - port not opened\n", __func__);
24658c2ecf20Sopenharmony_ci		return;
24668c2ecf20Sopenharmony_ci	}
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	cflag = tty->termios.c_cflag;
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci	switch (cflag & CSIZE) {
24718c2ecf20Sopenharmony_ci	case CS5:
24728c2ecf20Sopenharmony_ci		lData = LCR_BITS_5; mask = 0x1f;
24738c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 5\n", __func__);
24748c2ecf20Sopenharmony_ci		break;
24758c2ecf20Sopenharmony_ci	case CS6:
24768c2ecf20Sopenharmony_ci		lData = LCR_BITS_6; mask = 0x3f;
24778c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 6\n", __func__);
24788c2ecf20Sopenharmony_ci		break;
24798c2ecf20Sopenharmony_ci	case CS7:
24808c2ecf20Sopenharmony_ci		lData = LCR_BITS_7; mask = 0x7f;
24818c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 7\n", __func__);
24828c2ecf20Sopenharmony_ci		break;
24838c2ecf20Sopenharmony_ci	default:
24848c2ecf20Sopenharmony_ci	case CS8:
24858c2ecf20Sopenharmony_ci		lData = LCR_BITS_8;
24868c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - data bits = 8\n", __func__);
24878c2ecf20Sopenharmony_ci		break;
24888c2ecf20Sopenharmony_ci	}
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	lParity = LCR_PAR_NONE;
24918c2ecf20Sopenharmony_ci	if (cflag & PARENB) {
24928c2ecf20Sopenharmony_ci		if (cflag & CMSPAR) {
24938c2ecf20Sopenharmony_ci			if (cflag & PARODD) {
24948c2ecf20Sopenharmony_ci				lParity = LCR_PAR_MARK;
24958c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - parity = mark\n", __func__);
24968c2ecf20Sopenharmony_ci			} else {
24978c2ecf20Sopenharmony_ci				lParity = LCR_PAR_SPACE;
24988c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - parity = space\n", __func__);
24998c2ecf20Sopenharmony_ci			}
25008c2ecf20Sopenharmony_ci		} else if (cflag & PARODD) {
25018c2ecf20Sopenharmony_ci			lParity = LCR_PAR_ODD;
25028c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - parity = odd\n", __func__);
25038c2ecf20Sopenharmony_ci		} else {
25048c2ecf20Sopenharmony_ci			lParity = LCR_PAR_EVEN;
25058c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - parity = even\n", __func__);
25068c2ecf20Sopenharmony_ci		}
25078c2ecf20Sopenharmony_ci	} else {
25088c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - parity = none\n", __func__);
25098c2ecf20Sopenharmony_ci	}
25108c2ecf20Sopenharmony_ci
25118c2ecf20Sopenharmony_ci	if (cflag & CSTOPB) {
25128c2ecf20Sopenharmony_ci		lStop = LCR_STOP_2;
25138c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - stop bits = 2\n", __func__);
25148c2ecf20Sopenharmony_ci	} else {
25158c2ecf20Sopenharmony_ci		lStop = LCR_STOP_1;
25168c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - stop bits = 1\n", __func__);
25178c2ecf20Sopenharmony_ci	}
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci	/* figure out the flow control settings */
25208c2ecf20Sopenharmony_ci	rxFlow = txFlow = 0x00;
25218c2ecf20Sopenharmony_ci	if (cflag & CRTSCTS) {
25228c2ecf20Sopenharmony_ci		rxFlow |= IOSP_RX_FLOW_RTS;
25238c2ecf20Sopenharmony_ci		txFlow |= IOSP_TX_FLOW_CTS;
25248c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__);
25258c2ecf20Sopenharmony_ci	} else {
25268c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__);
25278c2ecf20Sopenharmony_ci	}
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	/* if we are implementing XON/XOFF, set the start and stop character
25308c2ecf20Sopenharmony_ci	   in the device */
25318c2ecf20Sopenharmony_ci	if (I_IXOFF(tty) || I_IXON(tty)) {
25328c2ecf20Sopenharmony_ci		unsigned char stop_char  = STOP_CHAR(tty);
25338c2ecf20Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci		if (!edge_serial->is_epic ||
25368c2ecf20Sopenharmony_ci		    edge_serial->epic_descriptor.Supports.IOSPSetXChar) {
25378c2ecf20Sopenharmony_ci			send_iosp_ext_cmd(edge_port,
25388c2ecf20Sopenharmony_ci					IOSP_CMD_SET_XON_CHAR, start_char);
25398c2ecf20Sopenharmony_ci			send_iosp_ext_cmd(edge_port,
25408c2ecf20Sopenharmony_ci					IOSP_CMD_SET_XOFF_CHAR, stop_char);
25418c2ecf20Sopenharmony_ci		}
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci		/* if we are implementing INBOUND XON/XOFF */
25448c2ecf20Sopenharmony_ci		if (I_IXOFF(tty)) {
25458c2ecf20Sopenharmony_ci			rxFlow |= IOSP_RX_FLOW_XON_XOFF;
25468c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
25478c2ecf20Sopenharmony_ci				__func__, start_char, stop_char);
25488c2ecf20Sopenharmony_ci		} else {
25498c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__);
25508c2ecf20Sopenharmony_ci		}
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci		/* if we are implementing OUTBOUND XON/XOFF */
25538c2ecf20Sopenharmony_ci		if (I_IXON(tty)) {
25548c2ecf20Sopenharmony_ci			txFlow |= IOSP_TX_FLOW_XON_XOFF;
25558c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
25568c2ecf20Sopenharmony_ci				__func__, start_char, stop_char);
25578c2ecf20Sopenharmony_ci		} else {
25588c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__);
25598c2ecf20Sopenharmony_ci		}
25608c2ecf20Sopenharmony_ci	}
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci	/* Set flow control to the configured value */
25638c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic ||
25648c2ecf20Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)
25658c2ecf20Sopenharmony_ci		send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
25668c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic ||
25678c2ecf20Sopenharmony_ci	    edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)
25688c2ecf20Sopenharmony_ci		send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
25728c2ecf20Sopenharmony_ci	edge_port->shadowLCR |= (lData | lParity | lStop);
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci	edge_port->validDataMask = mask;
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci	/* Send the updated LCR value to the EdgePort */
25778c2ecf20Sopenharmony_ci	status = send_cmd_write_uart_register(edge_port, LCR,
25788c2ecf20Sopenharmony_ci							edge_port->shadowLCR);
25798c2ecf20Sopenharmony_ci	if (status != 0)
25808c2ecf20Sopenharmony_ci		return;
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci	/* set up the MCR register and send it to the EdgePort */
25838c2ecf20Sopenharmony_ci	edge_port->shadowMCR = MCR_MASTER_IE;
25848c2ecf20Sopenharmony_ci	if (cflag & CBAUD)
25858c2ecf20Sopenharmony_ci		edge_port->shadowMCR |= (MCR_DTR | MCR_RTS);
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	status = send_cmd_write_uart_register(edge_port, MCR,
25888c2ecf20Sopenharmony_ci						edge_port->shadowMCR);
25898c2ecf20Sopenharmony_ci	if (status != 0)
25908c2ecf20Sopenharmony_ci		return;
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	/* Determine divisor based on baud rate */
25938c2ecf20Sopenharmony_ci	baud = tty_get_baud_rate(tty);
25948c2ecf20Sopenharmony_ci	if (!baud) {
25958c2ecf20Sopenharmony_ci		/* pick a default, any default... */
25968c2ecf20Sopenharmony_ci		baud = 9600;
25978c2ecf20Sopenharmony_ci	}
25988c2ecf20Sopenharmony_ci
25998c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
26008c2ecf20Sopenharmony_ci	status = send_cmd_write_baud_rate(edge_port, baud);
26018c2ecf20Sopenharmony_ci	if (status == -1) {
26028c2ecf20Sopenharmony_ci		/* Speed change was not possible - put back the old speed */
26038c2ecf20Sopenharmony_ci		baud = tty_termios_baud_rate(old_termios);
26048c2ecf20Sopenharmony_ci		tty_encode_baud_rate(tty, baud, baud);
26058c2ecf20Sopenharmony_ci	}
26068c2ecf20Sopenharmony_ci}
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci
26098c2ecf20Sopenharmony_ci/****************************************************************************
26108c2ecf20Sopenharmony_ci * unicode_to_ascii
26118c2ecf20Sopenharmony_ci *	Turns a string from Unicode into ASCII.
26128c2ecf20Sopenharmony_ci *	Doesn't do a good job with any characters that are outside the normal
26138c2ecf20Sopenharmony_ci *	ASCII range, but it's only for debugging...
26148c2ecf20Sopenharmony_ci *	NOTE: expects the unicode in LE format
26158c2ecf20Sopenharmony_ci ****************************************************************************/
26168c2ecf20Sopenharmony_cistatic void unicode_to_ascii(char *string, int buflen,
26178c2ecf20Sopenharmony_ci					__le16 *unicode, int unicode_size)
26188c2ecf20Sopenharmony_ci{
26198c2ecf20Sopenharmony_ci	int i;
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	if (buflen <= 0)	/* never happens, but... */
26228c2ecf20Sopenharmony_ci		return;
26238c2ecf20Sopenharmony_ci	--buflen;		/* space for nul */
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	for (i = 0; i < unicode_size; i++) {
26268c2ecf20Sopenharmony_ci		if (i >= buflen)
26278c2ecf20Sopenharmony_ci			break;
26288c2ecf20Sopenharmony_ci		string[i] = (char)(le16_to_cpu(unicode[i]));
26298c2ecf20Sopenharmony_ci	}
26308c2ecf20Sopenharmony_ci	string[i] = 0x00;
26318c2ecf20Sopenharmony_ci}
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci/****************************************************************************
26358c2ecf20Sopenharmony_ci * get_manufacturing_desc
26368c2ecf20Sopenharmony_ci *	reads in the manufacturing descriptor and stores it into the serial
26378c2ecf20Sopenharmony_ci *	structure.
26388c2ecf20Sopenharmony_ci ****************************************************************************/
26398c2ecf20Sopenharmony_cistatic void get_manufacturing_desc(struct edgeport_serial *edge_serial)
26408c2ecf20Sopenharmony_ci{
26418c2ecf20Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
26428c2ecf20Sopenharmony_ci	int response;
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci	dev_dbg(dev, "getting manufacturer descriptor\n");
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci	response = rom_read(edge_serial->serial,
26478c2ecf20Sopenharmony_ci				(EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16,
26488c2ecf20Sopenharmony_ci				(__u16)(EDGE_MANUF_DESC_ADDR & 0x0000ffff),
26498c2ecf20Sopenharmony_ci				EDGE_MANUF_DESC_LEN,
26508c2ecf20Sopenharmony_ci				(__u8 *)(&edge_serial->manuf_descriptor));
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	if (response < 0) {
26538c2ecf20Sopenharmony_ci		dev_err(dev, "error in getting manufacturer descriptor: %d\n",
26548c2ecf20Sopenharmony_ci				response);
26558c2ecf20Sopenharmony_ci	} else {
26568c2ecf20Sopenharmony_ci		char string[30];
26578c2ecf20Sopenharmony_ci		dev_dbg(dev, "**Manufacturer Descriptor\n");
26588c2ecf20Sopenharmony_ci		dev_dbg(dev, "  RomSize:        %dK\n",
26598c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.RomSize);
26608c2ecf20Sopenharmony_ci		dev_dbg(dev, "  RamSize:        %dK\n",
26618c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.RamSize);
26628c2ecf20Sopenharmony_ci		dev_dbg(dev, "  CpuRev:         %d\n",
26638c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.CpuRev);
26648c2ecf20Sopenharmony_ci		dev_dbg(dev, "  BoardRev:       %d\n",
26658c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.BoardRev);
26668c2ecf20Sopenharmony_ci		dev_dbg(dev, "  NumPorts:       %d\n",
26678c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.NumPorts);
26688c2ecf20Sopenharmony_ci		dev_dbg(dev, "  DescDate:       %d/%d/%d\n",
26698c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate[0],
26708c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate[1],
26718c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.DescDate[2]+1900);
26728c2ecf20Sopenharmony_ci		unicode_to_ascii(string, sizeof(string),
26738c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.SerialNumber,
26748c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.SerNumLength/2);
26758c2ecf20Sopenharmony_ci		dev_dbg(dev, "  SerialNumber: %s\n", string);
26768c2ecf20Sopenharmony_ci		unicode_to_ascii(string, sizeof(string),
26778c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.AssemblyNumber,
26788c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.AssemblyNumLength/2);
26798c2ecf20Sopenharmony_ci		dev_dbg(dev, "  AssemblyNumber: %s\n", string);
26808c2ecf20Sopenharmony_ci		unicode_to_ascii(string, sizeof(string),
26818c2ecf20Sopenharmony_ci		    edge_serial->manuf_descriptor.OemAssyNumber,
26828c2ecf20Sopenharmony_ci		    edge_serial->manuf_descriptor.OemAssyNumLength/2);
26838c2ecf20Sopenharmony_ci		dev_dbg(dev, "  OemAssyNumber:  %s\n", string);
26848c2ecf20Sopenharmony_ci		dev_dbg(dev, "  UartType:       %d\n",
26858c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.UartType);
26868c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IonPid:         %d\n",
26878c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.IonPid);
26888c2ecf20Sopenharmony_ci		dev_dbg(dev, "  IonConfig:      %d\n",
26898c2ecf20Sopenharmony_ci			edge_serial->manuf_descriptor.IonConfig);
26908c2ecf20Sopenharmony_ci	}
26918c2ecf20Sopenharmony_ci}
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci/****************************************************************************
26958c2ecf20Sopenharmony_ci * get_boot_desc
26968c2ecf20Sopenharmony_ci *	reads in the bootloader descriptor and stores it into the serial
26978c2ecf20Sopenharmony_ci *	structure.
26988c2ecf20Sopenharmony_ci ****************************************************************************/
26998c2ecf20Sopenharmony_cistatic void get_boot_desc(struct edgeport_serial *edge_serial)
27008c2ecf20Sopenharmony_ci{
27018c2ecf20Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
27028c2ecf20Sopenharmony_ci	int response;
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	dev_dbg(dev, "getting boot descriptor\n");
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	response = rom_read(edge_serial->serial,
27078c2ecf20Sopenharmony_ci				(EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16,
27088c2ecf20Sopenharmony_ci				(__u16)(EDGE_BOOT_DESC_ADDR & 0x0000ffff),
27098c2ecf20Sopenharmony_ci				EDGE_BOOT_DESC_LEN,
27108c2ecf20Sopenharmony_ci				(__u8 *)(&edge_serial->boot_descriptor));
27118c2ecf20Sopenharmony_ci
27128c2ecf20Sopenharmony_ci	if (response < 0) {
27138c2ecf20Sopenharmony_ci		dev_err(dev, "error in getting boot descriptor: %d\n",
27148c2ecf20Sopenharmony_ci				response);
27158c2ecf20Sopenharmony_ci	} else {
27168c2ecf20Sopenharmony_ci		dev_dbg(dev, "**Boot Descriptor:\n");
27178c2ecf20Sopenharmony_ci		dev_dbg(dev, "  BootCodeLength: %d\n",
27188c2ecf20Sopenharmony_ci			le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
27198c2ecf20Sopenharmony_ci		dev_dbg(dev, "  MajorVersion:   %d\n",
27208c2ecf20Sopenharmony_ci			edge_serial->boot_descriptor.MajorVersion);
27218c2ecf20Sopenharmony_ci		dev_dbg(dev, "  MinorVersion:   %d\n",
27228c2ecf20Sopenharmony_ci			edge_serial->boot_descriptor.MinorVersion);
27238c2ecf20Sopenharmony_ci		dev_dbg(dev, "  BuildNumber:    %d\n",
27248c2ecf20Sopenharmony_ci			le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
27258c2ecf20Sopenharmony_ci		dev_dbg(dev, "  Capabilities:   0x%x\n",
27268c2ecf20Sopenharmony_ci		      le16_to_cpu(edge_serial->boot_descriptor.Capabilities));
27278c2ecf20Sopenharmony_ci		dev_dbg(dev, "  UConfig0:       %d\n",
27288c2ecf20Sopenharmony_ci			edge_serial->boot_descriptor.UConfig0);
27298c2ecf20Sopenharmony_ci		dev_dbg(dev, "  UConfig1:       %d\n",
27308c2ecf20Sopenharmony_ci			edge_serial->boot_descriptor.UConfig1);
27318c2ecf20Sopenharmony_ci	}
27328c2ecf20Sopenharmony_ci}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci/****************************************************************************
27368c2ecf20Sopenharmony_ci * load_application_firmware
27378c2ecf20Sopenharmony_ci *	This is called to load the application firmware to the device
27388c2ecf20Sopenharmony_ci ****************************************************************************/
27398c2ecf20Sopenharmony_cistatic void load_application_firmware(struct edgeport_serial *edge_serial)
27408c2ecf20Sopenharmony_ci{
27418c2ecf20Sopenharmony_ci	struct device *dev = &edge_serial->serial->dev->dev;
27428c2ecf20Sopenharmony_ci	const struct ihex_binrec *rec;
27438c2ecf20Sopenharmony_ci	const struct firmware *fw;
27448c2ecf20Sopenharmony_ci	const char *fw_name;
27458c2ecf20Sopenharmony_ci	const char *fw_info;
27468c2ecf20Sopenharmony_ci	int response;
27478c2ecf20Sopenharmony_ci	__u32 Operaddr;
27488c2ecf20Sopenharmony_ci	__u16 build;
27498c2ecf20Sopenharmony_ci
27508c2ecf20Sopenharmony_ci	switch (edge_serial->product_info.iDownloadFile) {
27518c2ecf20Sopenharmony_ci		case EDGE_DOWNLOAD_FILE_I930:
27528c2ecf20Sopenharmony_ci			fw_info = "downloading firmware version (930)";
27538c2ecf20Sopenharmony_ci			fw_name	= "edgeport/down.fw";
27548c2ecf20Sopenharmony_ci			break;
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci		case EDGE_DOWNLOAD_FILE_80251:
27578c2ecf20Sopenharmony_ci			fw_info = "downloading firmware version (80251)";
27588c2ecf20Sopenharmony_ci			fw_name	= "edgeport/down2.fw";
27598c2ecf20Sopenharmony_ci			break;
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci		case EDGE_DOWNLOAD_FILE_NONE:
27628c2ecf20Sopenharmony_ci			dev_dbg(dev, "No download file specified, skipping download\n");
27638c2ecf20Sopenharmony_ci			return;
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci		default:
27668c2ecf20Sopenharmony_ci			return;
27678c2ecf20Sopenharmony_ci	}
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	response = request_ihex_firmware(&fw, fw_name,
27708c2ecf20Sopenharmony_ci				    &edge_serial->serial->dev->dev);
27718c2ecf20Sopenharmony_ci	if (response) {
27728c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to load image \"%s\" err %d\n",
27738c2ecf20Sopenharmony_ci		       fw_name, response);
27748c2ecf20Sopenharmony_ci		return;
27758c2ecf20Sopenharmony_ci	}
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	rec = (const struct ihex_binrec *)fw->data;
27788c2ecf20Sopenharmony_ci	build = (rec->data[2] << 8) | rec->data[3];
27798c2ecf20Sopenharmony_ci
27808c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s %d.%d.%d\n", fw_info, rec->data[0], rec->data[1], build);
27818c2ecf20Sopenharmony_ci
27828c2ecf20Sopenharmony_ci	edge_serial->product_info.FirmwareMajorVersion = rec->data[0];
27838c2ecf20Sopenharmony_ci	edge_serial->product_info.FirmwareMinorVersion = rec->data[1];
27848c2ecf20Sopenharmony_ci	edge_serial->product_info.FirmwareBuildNumber = cpu_to_le16(build);
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	for (rec = ihex_next_binrec(rec); rec;
27878c2ecf20Sopenharmony_ci	     rec = ihex_next_binrec(rec)) {
27888c2ecf20Sopenharmony_ci		Operaddr = be32_to_cpu(rec->addr);
27898c2ecf20Sopenharmony_ci		response = sram_write(edge_serial->serial,
27908c2ecf20Sopenharmony_ci				     Operaddr >> 16,
27918c2ecf20Sopenharmony_ci				     Operaddr & 0xFFFF,
27928c2ecf20Sopenharmony_ci				     be16_to_cpu(rec->len),
27938c2ecf20Sopenharmony_ci				     &rec->data[0]);
27948c2ecf20Sopenharmony_ci		if (response < 0) {
27958c2ecf20Sopenharmony_ci			dev_err(&edge_serial->serial->dev->dev,
27968c2ecf20Sopenharmony_ci				"sram_write failed (%x, %x, %d)\n",
27978c2ecf20Sopenharmony_ci				Operaddr >> 16, Operaddr & 0xFFFF,
27988c2ecf20Sopenharmony_ci				be16_to_cpu(rec->len));
27998c2ecf20Sopenharmony_ci			break;
28008c2ecf20Sopenharmony_ci		}
28018c2ecf20Sopenharmony_ci	}
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	dev_dbg(dev, "sending exec_dl_code\n");
28048c2ecf20Sopenharmony_ci	response = usb_control_msg (edge_serial->serial->dev,
28058c2ecf20Sopenharmony_ci				    usb_sndctrlpipe(edge_serial->serial->dev, 0),
28068c2ecf20Sopenharmony_ci				    USB_REQUEST_ION_EXEC_DL_CODE,
28078c2ecf20Sopenharmony_ci				    0x40, 0x4000, 0x0001, NULL, 0, 3000);
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci	release_firmware(fw);
28108c2ecf20Sopenharmony_ci}
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci
28138c2ecf20Sopenharmony_ci/****************************************************************************
28148c2ecf20Sopenharmony_ci * edge_startup
28158c2ecf20Sopenharmony_ci ****************************************************************************/
28168c2ecf20Sopenharmony_cistatic int edge_startup(struct usb_serial *serial)
28178c2ecf20Sopenharmony_ci{
28188c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial;
28198c2ecf20Sopenharmony_ci	struct usb_device *dev;
28208c2ecf20Sopenharmony_ci	struct device *ddev = &serial->dev->dev;
28218c2ecf20Sopenharmony_ci	int i;
28228c2ecf20Sopenharmony_ci	int response;
28238c2ecf20Sopenharmony_ci	bool interrupt_in_found;
28248c2ecf20Sopenharmony_ci	bool bulk_in_found;
28258c2ecf20Sopenharmony_ci	bool bulk_out_found;
28268c2ecf20Sopenharmony_ci	static const __u32 descriptor[3] = {	EDGE_COMPATIBILITY_MASK0,
28278c2ecf20Sopenharmony_ci						EDGE_COMPATIBILITY_MASK1,
28288c2ecf20Sopenharmony_ci						EDGE_COMPATIBILITY_MASK2 };
28298c2ecf20Sopenharmony_ci
28308c2ecf20Sopenharmony_ci	dev = serial->dev;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci	/* create our private serial structure */
28338c2ecf20Sopenharmony_ci	edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
28348c2ecf20Sopenharmony_ci	if (!edge_serial)
28358c2ecf20Sopenharmony_ci		return -ENOMEM;
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_ci	spin_lock_init(&edge_serial->es_lock);
28388c2ecf20Sopenharmony_ci	edge_serial->serial = serial;
28398c2ecf20Sopenharmony_ci	usb_set_serial_data(serial, edge_serial);
28408c2ecf20Sopenharmony_ci
28418c2ecf20Sopenharmony_ci	/* get the name for the device from the device */
28428c2ecf20Sopenharmony_ci	i = usb_string(dev, dev->descriptor.iManufacturer,
28438c2ecf20Sopenharmony_ci	    &edge_serial->name[0], MAX_NAME_LEN+1);
28448c2ecf20Sopenharmony_ci	if (i < 0)
28458c2ecf20Sopenharmony_ci		i = 0;
28468c2ecf20Sopenharmony_ci	edge_serial->name[i++] = ' ';
28478c2ecf20Sopenharmony_ci	usb_string(dev, dev->descriptor.iProduct,
28488c2ecf20Sopenharmony_ci	    &edge_serial->name[i], MAX_NAME_LEN+2 - i);
28498c2ecf20Sopenharmony_ci
28508c2ecf20Sopenharmony_ci	dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name);
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci	/* Read the epic descriptor */
28538c2ecf20Sopenharmony_ci	if (get_epic_descriptor(edge_serial) < 0) {
28548c2ecf20Sopenharmony_ci		/* memcpy descriptor to Supports structures */
28558c2ecf20Sopenharmony_ci		memcpy(&edge_serial->epic_descriptor.Supports, descriptor,
28568c2ecf20Sopenharmony_ci		       sizeof(struct edge_compatibility_bits));
28578c2ecf20Sopenharmony_ci
28588c2ecf20Sopenharmony_ci		/* get the manufacturing descriptor for this device */
28598c2ecf20Sopenharmony_ci		get_manufacturing_desc(edge_serial);
28608c2ecf20Sopenharmony_ci
28618c2ecf20Sopenharmony_ci		/* get the boot descriptor */
28628c2ecf20Sopenharmony_ci		get_boot_desc(edge_serial);
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci		get_product_info(edge_serial);
28658c2ecf20Sopenharmony_ci	}
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	/* set the number of ports from the manufacturing description */
28688c2ecf20Sopenharmony_ci	/* serial->num_ports = serial->product_info.NumPorts; */
28698c2ecf20Sopenharmony_ci	if ((!edge_serial->is_epic) &&
28708c2ecf20Sopenharmony_ci	    (edge_serial->product_info.NumPorts != serial->num_ports)) {
28718c2ecf20Sopenharmony_ci		dev_warn(ddev,
28728c2ecf20Sopenharmony_ci			"Device Reported %d serial ports vs. core thinking we have %d ports, email greg@kroah.com this information.\n",
28738c2ecf20Sopenharmony_ci			 edge_serial->product_info.NumPorts,
28748c2ecf20Sopenharmony_ci			 serial->num_ports);
28758c2ecf20Sopenharmony_ci	}
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci	dev_dbg(ddev, "%s - time 1 %ld\n", __func__, jiffies);
28788c2ecf20Sopenharmony_ci
28798c2ecf20Sopenharmony_ci	/* If not an EPiC device */
28808c2ecf20Sopenharmony_ci	if (!edge_serial->is_epic) {
28818c2ecf20Sopenharmony_ci		/* now load the application firmware into this device */
28828c2ecf20Sopenharmony_ci		load_application_firmware(edge_serial);
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_ci		dev_dbg(ddev, "%s - time 2 %ld\n", __func__, jiffies);
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci		/* Check current Edgeport EEPROM and update if necessary */
28878c2ecf20Sopenharmony_ci		update_edgeport_E2PROM(edge_serial);
28888c2ecf20Sopenharmony_ci
28898c2ecf20Sopenharmony_ci		dev_dbg(ddev, "%s - time 3 %ld\n", __func__, jiffies);
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci		/* set the configuration to use #1 */
28928c2ecf20Sopenharmony_ci/*		dev_dbg(ddev, "set_configuration 1\n"); */
28938c2ecf20Sopenharmony_ci/*		usb_set_configuration (dev, 1); */
28948c2ecf20Sopenharmony_ci	}
28958c2ecf20Sopenharmony_ci	dev_dbg(ddev, "  FirmwareMajorVersion  %d.%d.%d\n",
28968c2ecf20Sopenharmony_ci	    edge_serial->product_info.FirmwareMajorVersion,
28978c2ecf20Sopenharmony_ci	    edge_serial->product_info.FirmwareMinorVersion,
28988c2ecf20Sopenharmony_ci	    le16_to_cpu(edge_serial->product_info.FirmwareBuildNumber));
28998c2ecf20Sopenharmony_ci
29008c2ecf20Sopenharmony_ci	/* we set up the pointers to the endpoints in the edge_open function,
29018c2ecf20Sopenharmony_ci	 * as the structures aren't created yet. */
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_ci	response = 0;
29048c2ecf20Sopenharmony_ci
29058c2ecf20Sopenharmony_ci	if (edge_serial->is_epic) {
29068c2ecf20Sopenharmony_ci		struct usb_host_interface *alt;
29078c2ecf20Sopenharmony_ci
29088c2ecf20Sopenharmony_ci		alt = serial->interface->cur_altsetting;
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci		/* EPIC thing, set up our interrupt polling now and our read
29118c2ecf20Sopenharmony_ci		 * urb, so that the device knows it really is connected. */
29128c2ecf20Sopenharmony_ci		interrupt_in_found = bulk_in_found = bulk_out_found = false;
29138c2ecf20Sopenharmony_ci		for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
29148c2ecf20Sopenharmony_ci			struct usb_endpoint_descriptor *endpoint;
29158c2ecf20Sopenharmony_ci			int buffer_size;
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_ci			endpoint = &alt->endpoint[i].desc;
29188c2ecf20Sopenharmony_ci			buffer_size = usb_endpoint_maxp(endpoint);
29198c2ecf20Sopenharmony_ci			if (!interrupt_in_found &&
29208c2ecf20Sopenharmony_ci			    (usb_endpoint_is_int_in(endpoint))) {
29218c2ecf20Sopenharmony_ci				/* we found a interrupt in endpoint */
29228c2ecf20Sopenharmony_ci				dev_dbg(ddev, "found interrupt in\n");
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_ci				/* not set up yet, so do it now */
29258c2ecf20Sopenharmony_ci				edge_serial->interrupt_read_urb =
29268c2ecf20Sopenharmony_ci						usb_alloc_urb(0, GFP_KERNEL);
29278c2ecf20Sopenharmony_ci				if (!edge_serial->interrupt_read_urb) {
29288c2ecf20Sopenharmony_ci					response = -ENOMEM;
29298c2ecf20Sopenharmony_ci					break;
29308c2ecf20Sopenharmony_ci				}
29318c2ecf20Sopenharmony_ci
29328c2ecf20Sopenharmony_ci				edge_serial->interrupt_in_buffer =
29338c2ecf20Sopenharmony_ci					kmalloc(buffer_size, GFP_KERNEL);
29348c2ecf20Sopenharmony_ci				if (!edge_serial->interrupt_in_buffer) {
29358c2ecf20Sopenharmony_ci					response = -ENOMEM;
29368c2ecf20Sopenharmony_ci					break;
29378c2ecf20Sopenharmony_ci				}
29388c2ecf20Sopenharmony_ci				edge_serial->interrupt_in_endpoint =
29398c2ecf20Sopenharmony_ci						endpoint->bEndpointAddress;
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_ci				/* set up our interrupt urb */
29428c2ecf20Sopenharmony_ci				usb_fill_int_urb(
29438c2ecf20Sopenharmony_ci					edge_serial->interrupt_read_urb,
29448c2ecf20Sopenharmony_ci					dev,
29458c2ecf20Sopenharmony_ci					usb_rcvintpipe(dev,
29468c2ecf20Sopenharmony_ci						endpoint->bEndpointAddress),
29478c2ecf20Sopenharmony_ci					edge_serial->interrupt_in_buffer,
29488c2ecf20Sopenharmony_ci					buffer_size,
29498c2ecf20Sopenharmony_ci					edge_interrupt_callback,
29508c2ecf20Sopenharmony_ci					edge_serial,
29518c2ecf20Sopenharmony_ci					endpoint->bInterval);
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci				interrupt_in_found = true;
29548c2ecf20Sopenharmony_ci			}
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_ci			if (!bulk_in_found &&
29578c2ecf20Sopenharmony_ci				(usb_endpoint_is_bulk_in(endpoint))) {
29588c2ecf20Sopenharmony_ci				/* we found a bulk in endpoint */
29598c2ecf20Sopenharmony_ci				dev_dbg(ddev, "found bulk in\n");
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci				/* not set up yet, so do it now */
29628c2ecf20Sopenharmony_ci				edge_serial->read_urb =
29638c2ecf20Sopenharmony_ci						usb_alloc_urb(0, GFP_KERNEL);
29648c2ecf20Sopenharmony_ci				if (!edge_serial->read_urb) {
29658c2ecf20Sopenharmony_ci					response = -ENOMEM;
29668c2ecf20Sopenharmony_ci					break;
29678c2ecf20Sopenharmony_ci				}
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci				edge_serial->bulk_in_buffer =
29708c2ecf20Sopenharmony_ci					kmalloc(buffer_size, GFP_KERNEL);
29718c2ecf20Sopenharmony_ci				if (!edge_serial->bulk_in_buffer) {
29728c2ecf20Sopenharmony_ci					response = -ENOMEM;
29738c2ecf20Sopenharmony_ci					break;
29748c2ecf20Sopenharmony_ci				}
29758c2ecf20Sopenharmony_ci				edge_serial->bulk_in_endpoint =
29768c2ecf20Sopenharmony_ci						endpoint->bEndpointAddress;
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_ci				/* set up our bulk in urb */
29798c2ecf20Sopenharmony_ci				usb_fill_bulk_urb(edge_serial->read_urb, dev,
29808c2ecf20Sopenharmony_ci					usb_rcvbulkpipe(dev,
29818c2ecf20Sopenharmony_ci						endpoint->bEndpointAddress),
29828c2ecf20Sopenharmony_ci					edge_serial->bulk_in_buffer,
29838c2ecf20Sopenharmony_ci					usb_endpoint_maxp(endpoint),
29848c2ecf20Sopenharmony_ci					edge_bulk_in_callback,
29858c2ecf20Sopenharmony_ci					edge_serial);
29868c2ecf20Sopenharmony_ci				bulk_in_found = true;
29878c2ecf20Sopenharmony_ci			}
29888c2ecf20Sopenharmony_ci
29898c2ecf20Sopenharmony_ci			if (!bulk_out_found &&
29908c2ecf20Sopenharmony_ci			    (usb_endpoint_is_bulk_out(endpoint))) {
29918c2ecf20Sopenharmony_ci				/* we found a bulk out endpoint */
29928c2ecf20Sopenharmony_ci				dev_dbg(ddev, "found bulk out\n");
29938c2ecf20Sopenharmony_ci				edge_serial->bulk_out_endpoint =
29948c2ecf20Sopenharmony_ci						endpoint->bEndpointAddress;
29958c2ecf20Sopenharmony_ci				bulk_out_found = true;
29968c2ecf20Sopenharmony_ci			}
29978c2ecf20Sopenharmony_ci		}
29988c2ecf20Sopenharmony_ci
29998c2ecf20Sopenharmony_ci		if (response || !interrupt_in_found || !bulk_in_found ||
30008c2ecf20Sopenharmony_ci							!bulk_out_found) {
30018c2ecf20Sopenharmony_ci			if (!response) {
30028c2ecf20Sopenharmony_ci				dev_err(ddev, "expected endpoints not found\n");
30038c2ecf20Sopenharmony_ci				response = -ENODEV;
30048c2ecf20Sopenharmony_ci			}
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_ci			goto error;
30078c2ecf20Sopenharmony_ci		}
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci		/* start interrupt read for this edgeport this interrupt will
30108c2ecf20Sopenharmony_ci		 * continue as long as the edgeport is connected */
30118c2ecf20Sopenharmony_ci		response = usb_submit_urb(edge_serial->interrupt_read_urb,
30128c2ecf20Sopenharmony_ci								GFP_KERNEL);
30138c2ecf20Sopenharmony_ci		if (response) {
30148c2ecf20Sopenharmony_ci			dev_err(ddev, "%s - Error %d submitting control urb\n",
30158c2ecf20Sopenharmony_ci				__func__, response);
30168c2ecf20Sopenharmony_ci
30178c2ecf20Sopenharmony_ci			goto error;
30188c2ecf20Sopenharmony_ci		}
30198c2ecf20Sopenharmony_ci	}
30208c2ecf20Sopenharmony_ci	return response;
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_cierror:
30238c2ecf20Sopenharmony_ci	usb_free_urb(edge_serial->interrupt_read_urb);
30248c2ecf20Sopenharmony_ci	kfree(edge_serial->interrupt_in_buffer);
30258c2ecf20Sopenharmony_ci
30268c2ecf20Sopenharmony_ci	usb_free_urb(edge_serial->read_urb);
30278c2ecf20Sopenharmony_ci	kfree(edge_serial->bulk_in_buffer);
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci	kfree(edge_serial);
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci	return response;
30328c2ecf20Sopenharmony_ci}
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci/****************************************************************************
30368c2ecf20Sopenharmony_ci * edge_disconnect
30378c2ecf20Sopenharmony_ci *	This function is called whenever the device is removed from the usb bus.
30388c2ecf20Sopenharmony_ci ****************************************************************************/
30398c2ecf20Sopenharmony_cistatic void edge_disconnect(struct usb_serial *serial)
30408c2ecf20Sopenharmony_ci{
30418c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_ci	if (edge_serial->is_epic) {
30448c2ecf20Sopenharmony_ci		usb_kill_urb(edge_serial->interrupt_read_urb);
30458c2ecf20Sopenharmony_ci		usb_kill_urb(edge_serial->read_urb);
30468c2ecf20Sopenharmony_ci	}
30478c2ecf20Sopenharmony_ci}
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_ci
30508c2ecf20Sopenharmony_ci/****************************************************************************
30518c2ecf20Sopenharmony_ci * edge_release
30528c2ecf20Sopenharmony_ci *	This function is called when the device structure is deallocated.
30538c2ecf20Sopenharmony_ci ****************************************************************************/
30548c2ecf20Sopenharmony_cistatic void edge_release(struct usb_serial *serial)
30558c2ecf20Sopenharmony_ci{
30568c2ecf20Sopenharmony_ci	struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
30578c2ecf20Sopenharmony_ci
30588c2ecf20Sopenharmony_ci	if (edge_serial->is_epic) {
30598c2ecf20Sopenharmony_ci		usb_kill_urb(edge_serial->interrupt_read_urb);
30608c2ecf20Sopenharmony_ci		usb_free_urb(edge_serial->interrupt_read_urb);
30618c2ecf20Sopenharmony_ci		kfree(edge_serial->interrupt_in_buffer);
30628c2ecf20Sopenharmony_ci
30638c2ecf20Sopenharmony_ci		usb_kill_urb(edge_serial->read_urb);
30648c2ecf20Sopenharmony_ci		usb_free_urb(edge_serial->read_urb);
30658c2ecf20Sopenharmony_ci		kfree(edge_serial->bulk_in_buffer);
30668c2ecf20Sopenharmony_ci	}
30678c2ecf20Sopenharmony_ci
30688c2ecf20Sopenharmony_ci	kfree(edge_serial);
30698c2ecf20Sopenharmony_ci}
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_cistatic int edge_port_probe(struct usb_serial_port *port)
30728c2ecf20Sopenharmony_ci{
30738c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port;
30748c2ecf20Sopenharmony_ci
30758c2ecf20Sopenharmony_ci	edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL);
30768c2ecf20Sopenharmony_ci	if (!edge_port)
30778c2ecf20Sopenharmony_ci		return -ENOMEM;
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_ci	spin_lock_init(&edge_port->ep_lock);
30808c2ecf20Sopenharmony_ci	edge_port->port = port;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, edge_port);
30838c2ecf20Sopenharmony_ci
30848c2ecf20Sopenharmony_ci	return 0;
30858c2ecf20Sopenharmony_ci}
30868c2ecf20Sopenharmony_ci
30878c2ecf20Sopenharmony_cistatic int edge_port_remove(struct usb_serial_port *port)
30888c2ecf20Sopenharmony_ci{
30898c2ecf20Sopenharmony_ci	struct edgeport_port *edge_port;
30908c2ecf20Sopenharmony_ci
30918c2ecf20Sopenharmony_ci	edge_port = usb_get_serial_port_data(port);
30928c2ecf20Sopenharmony_ci	kfree(edge_port);
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	return 0;
30958c2ecf20Sopenharmony_ci}
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_cistatic struct usb_serial_driver edgeport_2port_device = {
30988c2ecf20Sopenharmony_ci	.driver = {
30998c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
31008c2ecf20Sopenharmony_ci		.name		= "edgeport_2",
31018c2ecf20Sopenharmony_ci	},
31028c2ecf20Sopenharmony_ci	.description		= "Edgeport 2 port adapter",
31038c2ecf20Sopenharmony_ci	.id_table		= edgeport_2port_id_table,
31048c2ecf20Sopenharmony_ci	.num_ports		= 2,
31058c2ecf20Sopenharmony_ci	.num_bulk_in		= 1,
31068c2ecf20Sopenharmony_ci	.num_bulk_out		= 1,
31078c2ecf20Sopenharmony_ci	.num_interrupt_in	= 1,
31088c2ecf20Sopenharmony_ci	.open			= edge_open,
31098c2ecf20Sopenharmony_ci	.close			= edge_close,
31108c2ecf20Sopenharmony_ci	.throttle		= edge_throttle,
31118c2ecf20Sopenharmony_ci	.unthrottle		= edge_unthrottle,
31128c2ecf20Sopenharmony_ci	.attach			= edge_startup,
31138c2ecf20Sopenharmony_ci	.disconnect		= edge_disconnect,
31148c2ecf20Sopenharmony_ci	.release		= edge_release,
31158c2ecf20Sopenharmony_ci	.port_probe		= edge_port_probe,
31168c2ecf20Sopenharmony_ci	.port_remove		= edge_port_remove,
31178c2ecf20Sopenharmony_ci	.ioctl			= edge_ioctl,
31188c2ecf20Sopenharmony_ci	.set_termios		= edge_set_termios,
31198c2ecf20Sopenharmony_ci	.tiocmget		= edge_tiocmget,
31208c2ecf20Sopenharmony_ci	.tiocmset		= edge_tiocmset,
31218c2ecf20Sopenharmony_ci	.get_serial		= get_serial_info,
31228c2ecf20Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
31238c2ecf20Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
31248c2ecf20Sopenharmony_ci	.write			= edge_write,
31258c2ecf20Sopenharmony_ci	.write_room		= edge_write_room,
31268c2ecf20Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
31278c2ecf20Sopenharmony_ci	.break_ctl		= edge_break,
31288c2ecf20Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
31298c2ecf20Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
31308c2ecf20Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
31318c2ecf20Sopenharmony_ci};
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_cistatic struct usb_serial_driver edgeport_4port_device = {
31348c2ecf20Sopenharmony_ci	.driver = {
31358c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
31368c2ecf20Sopenharmony_ci		.name		= "edgeport_4",
31378c2ecf20Sopenharmony_ci	},
31388c2ecf20Sopenharmony_ci	.description		= "Edgeport 4 port adapter",
31398c2ecf20Sopenharmony_ci	.id_table		= edgeport_4port_id_table,
31408c2ecf20Sopenharmony_ci	.num_ports		= 4,
31418c2ecf20Sopenharmony_ci	.num_bulk_in		= 1,
31428c2ecf20Sopenharmony_ci	.num_bulk_out		= 1,
31438c2ecf20Sopenharmony_ci	.num_interrupt_in	= 1,
31448c2ecf20Sopenharmony_ci	.open			= edge_open,
31458c2ecf20Sopenharmony_ci	.close			= edge_close,
31468c2ecf20Sopenharmony_ci	.throttle		= edge_throttle,
31478c2ecf20Sopenharmony_ci	.unthrottle		= edge_unthrottle,
31488c2ecf20Sopenharmony_ci	.attach			= edge_startup,
31498c2ecf20Sopenharmony_ci	.disconnect		= edge_disconnect,
31508c2ecf20Sopenharmony_ci	.release		= edge_release,
31518c2ecf20Sopenharmony_ci	.port_probe		= edge_port_probe,
31528c2ecf20Sopenharmony_ci	.port_remove		= edge_port_remove,
31538c2ecf20Sopenharmony_ci	.ioctl			= edge_ioctl,
31548c2ecf20Sopenharmony_ci	.set_termios		= edge_set_termios,
31558c2ecf20Sopenharmony_ci	.tiocmget		= edge_tiocmget,
31568c2ecf20Sopenharmony_ci	.tiocmset		= edge_tiocmset,
31578c2ecf20Sopenharmony_ci	.get_serial		= get_serial_info,
31588c2ecf20Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
31598c2ecf20Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
31608c2ecf20Sopenharmony_ci	.write			= edge_write,
31618c2ecf20Sopenharmony_ci	.write_room		= edge_write_room,
31628c2ecf20Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
31638c2ecf20Sopenharmony_ci	.break_ctl		= edge_break,
31648c2ecf20Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
31658c2ecf20Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
31668c2ecf20Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
31678c2ecf20Sopenharmony_ci};
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_cistatic struct usb_serial_driver edgeport_8port_device = {
31708c2ecf20Sopenharmony_ci	.driver = {
31718c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
31728c2ecf20Sopenharmony_ci		.name		= "edgeport_8",
31738c2ecf20Sopenharmony_ci	},
31748c2ecf20Sopenharmony_ci	.description		= "Edgeport 8 port adapter",
31758c2ecf20Sopenharmony_ci	.id_table		= edgeport_8port_id_table,
31768c2ecf20Sopenharmony_ci	.num_ports		= 8,
31778c2ecf20Sopenharmony_ci	.num_bulk_in		= 1,
31788c2ecf20Sopenharmony_ci	.num_bulk_out		= 1,
31798c2ecf20Sopenharmony_ci	.num_interrupt_in	= 1,
31808c2ecf20Sopenharmony_ci	.open			= edge_open,
31818c2ecf20Sopenharmony_ci	.close			= edge_close,
31828c2ecf20Sopenharmony_ci	.throttle		= edge_throttle,
31838c2ecf20Sopenharmony_ci	.unthrottle		= edge_unthrottle,
31848c2ecf20Sopenharmony_ci	.attach			= edge_startup,
31858c2ecf20Sopenharmony_ci	.disconnect		= edge_disconnect,
31868c2ecf20Sopenharmony_ci	.release		= edge_release,
31878c2ecf20Sopenharmony_ci	.port_probe		= edge_port_probe,
31888c2ecf20Sopenharmony_ci	.port_remove		= edge_port_remove,
31898c2ecf20Sopenharmony_ci	.ioctl			= edge_ioctl,
31908c2ecf20Sopenharmony_ci	.set_termios		= edge_set_termios,
31918c2ecf20Sopenharmony_ci	.tiocmget		= edge_tiocmget,
31928c2ecf20Sopenharmony_ci	.tiocmset		= edge_tiocmset,
31938c2ecf20Sopenharmony_ci	.get_serial		= get_serial_info,
31948c2ecf20Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
31958c2ecf20Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
31968c2ecf20Sopenharmony_ci	.write			= edge_write,
31978c2ecf20Sopenharmony_ci	.write_room		= edge_write_room,
31988c2ecf20Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
31998c2ecf20Sopenharmony_ci	.break_ctl		= edge_break,
32008c2ecf20Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
32018c2ecf20Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
32028c2ecf20Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
32038c2ecf20Sopenharmony_ci};
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_cistatic struct usb_serial_driver epic_device = {
32068c2ecf20Sopenharmony_ci	.driver = {
32078c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
32088c2ecf20Sopenharmony_ci		.name		= "epic",
32098c2ecf20Sopenharmony_ci	},
32108c2ecf20Sopenharmony_ci	.description		= "EPiC device",
32118c2ecf20Sopenharmony_ci	.id_table		= Epic_port_id_table,
32128c2ecf20Sopenharmony_ci	.num_ports		= 1,
32138c2ecf20Sopenharmony_ci	.num_bulk_in		= 1,
32148c2ecf20Sopenharmony_ci	.num_bulk_out		= 1,
32158c2ecf20Sopenharmony_ci	.num_interrupt_in	= 1,
32168c2ecf20Sopenharmony_ci	.open			= edge_open,
32178c2ecf20Sopenharmony_ci	.close			= edge_close,
32188c2ecf20Sopenharmony_ci	.throttle		= edge_throttle,
32198c2ecf20Sopenharmony_ci	.unthrottle		= edge_unthrottle,
32208c2ecf20Sopenharmony_ci	.attach			= edge_startup,
32218c2ecf20Sopenharmony_ci	.disconnect		= edge_disconnect,
32228c2ecf20Sopenharmony_ci	.release		= edge_release,
32238c2ecf20Sopenharmony_ci	.port_probe		= edge_port_probe,
32248c2ecf20Sopenharmony_ci	.port_remove		= edge_port_remove,
32258c2ecf20Sopenharmony_ci	.ioctl			= edge_ioctl,
32268c2ecf20Sopenharmony_ci	.set_termios		= edge_set_termios,
32278c2ecf20Sopenharmony_ci	.tiocmget		= edge_tiocmget,
32288c2ecf20Sopenharmony_ci	.tiocmset		= edge_tiocmset,
32298c2ecf20Sopenharmony_ci	.get_serial		= get_serial_info,
32308c2ecf20Sopenharmony_ci	.tiocmiwait		= usb_serial_generic_tiocmiwait,
32318c2ecf20Sopenharmony_ci	.get_icount		= usb_serial_generic_get_icount,
32328c2ecf20Sopenharmony_ci	.write			= edge_write,
32338c2ecf20Sopenharmony_ci	.write_room		= edge_write_room,
32348c2ecf20Sopenharmony_ci	.chars_in_buffer	= edge_chars_in_buffer,
32358c2ecf20Sopenharmony_ci	.break_ctl		= edge_break,
32368c2ecf20Sopenharmony_ci	.read_int_callback	= edge_interrupt_callback,
32378c2ecf20Sopenharmony_ci	.read_bulk_callback	= edge_bulk_in_callback,
32388c2ecf20Sopenharmony_ci	.write_bulk_callback	= edge_bulk_out_data_callback,
32398c2ecf20Sopenharmony_ci};
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
32428c2ecf20Sopenharmony_ci	&edgeport_2port_device, &edgeport_4port_device,
32438c2ecf20Sopenharmony_ci	&edgeport_8port_device, &epic_device, NULL
32448c2ecf20Sopenharmony_ci};
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table_combined);
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
32498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
32508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
32518c2ecf20Sopenharmony_ciMODULE_FIRMWARE("edgeport/boot.fw");
32528c2ecf20Sopenharmony_ciMODULE_FIRMWARE("edgeport/boot2.fw");
32538c2ecf20Sopenharmony_ciMODULE_FIRMWARE("edgeport/down.fw");
32548c2ecf20Sopenharmony_ciMODULE_FIRMWARE("edgeport/down2.fw");
3255