162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * mos7720.c
462306a36Sopenharmony_ci *   Controls the Moschip 7720 usb to dual port serial converter
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright 2006 Moschip Semiconductor Tech. Ltd.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Developed by:
962306a36Sopenharmony_ci * 	Vijaya Kumar <vijaykumar.gn@gmail.com>
1062306a36Sopenharmony_ci *	Ajay Kumar <naanuajay@yahoo.com>
1162306a36Sopenharmony_ci *	Gurudeva <ngurudeva@yahoo.com>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Cleaned up from the original by:
1462306a36Sopenharmony_ci *	Greg Kroah-Hartman <gregkh@suse.de>
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Originally based on drivers/usb/serial/io_edgeport.c which is:
1762306a36Sopenharmony_ci *	Copyright (C) 2000 Inside Out Networks, All rights reserved.
1862306a36Sopenharmony_ci *	Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/errno.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/tty.h>
2462306a36Sopenharmony_ci#include <linux/tty_driver.h>
2562306a36Sopenharmony_ci#include <linux/tty_flip.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/spinlock.h>
2862306a36Sopenharmony_ci#include <linux/serial.h>
2962306a36Sopenharmony_ci#include <linux/serial_reg.h>
3062306a36Sopenharmony_ci#include <linux/usb.h>
3162306a36Sopenharmony_ci#include <linux/usb/serial.h>
3262306a36Sopenharmony_ci#include <linux/uaccess.h>
3362306a36Sopenharmony_ci#include <linux/parport.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRIVER_AUTHOR "Aspire Communications pvt Ltd."
3662306a36Sopenharmony_ci#define DRIVER_DESC "Moschip USB Serial Driver"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* default urb timeout */
3962306a36Sopenharmony_ci#define MOS_WDR_TIMEOUT	5000
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MOS_MAX_PORT	0x02
4262306a36Sopenharmony_ci#define MOS_WRITE	0x0E
4362306a36Sopenharmony_ci#define MOS_READ	0x0D
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Interrupt Routines Defines	*/
4662306a36Sopenharmony_ci#define SERIAL_IIR_RLS	0x06
4762306a36Sopenharmony_ci#define SERIAL_IIR_RDA	0x04
4862306a36Sopenharmony_ci#define SERIAL_IIR_CTI	0x0c
4962306a36Sopenharmony_ci#define SERIAL_IIR_THR	0x02
5062306a36Sopenharmony_ci#define SERIAL_IIR_MS	0x00
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define NUM_URBS			16	/* URB Count */
5362306a36Sopenharmony_ci#define URB_TRANSFER_BUFFER_SIZE	32	/* URB Size */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* This structure holds all of the local serial port information */
5662306a36Sopenharmony_cistruct moschip_port {
5762306a36Sopenharmony_ci	__u8	shadowLCR;		/* last LCR value received */
5862306a36Sopenharmony_ci	__u8	shadowMCR;		/* last MCR value received */
5962306a36Sopenharmony_ci	__u8	shadowMSR;		/* last MSR value received */
6062306a36Sopenharmony_ci	char			open;
6162306a36Sopenharmony_ci	struct usb_serial_port	*port;	/* loop back to the owner */
6262306a36Sopenharmony_ci	struct urb		*write_urb_pool[NUM_URBS];
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define USB_VENDOR_ID_MOSCHIP		0x9710
6662306a36Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7720		0x7720
6762306a36Sopenharmony_ci#define MOSCHIP_DEVICE_ID_7715		0x7715
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = {
7062306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7720) },
7162306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7715) },
7262306a36Sopenharmony_ci	{ } /* terminating entry */
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* initial values for parport regs */
7962306a36Sopenharmony_ci#define DCR_INIT_VAL       0x0c	/* SLCTIN, nINIT */
8062306a36Sopenharmony_ci#define ECR_INIT_VAL       0x00	/* SPP mode */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cienum mos7715_pp_modes {
8362306a36Sopenharmony_ci	SPP = 0<<5,
8462306a36Sopenharmony_ci	PS2 = 1<<5,      /* moschip calls this 'NIBBLE' mode */
8562306a36Sopenharmony_ci	PPF = 2<<5,	 /* moschip calls this 'CB-FIFO mode */
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct mos7715_parport {
8962306a36Sopenharmony_ci	struct parport          *pp;	       /* back to containing struct */
9062306a36Sopenharmony_ci	struct kref             ref_count;     /* to instance of this struct */
9162306a36Sopenharmony_ci	bool                    msg_pending;   /* usb sync call pending */
9262306a36Sopenharmony_ci	struct completion       syncmsg_compl; /* usb sync call completed */
9362306a36Sopenharmony_ci	struct work_struct      work;          /* restore deferred writes */
9462306a36Sopenharmony_ci	struct usb_serial       *serial;       /* back to containing struct */
9562306a36Sopenharmony_ci	__u8	                shadowECR;     /* parallel port regs... */
9662306a36Sopenharmony_ci	__u8	                shadowDCR;
9762306a36Sopenharmony_ci	atomic_t                shadowDSR;     /* updated in int-in callback */
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* lock guards against dereferencing NULL ptr in parport ops callbacks */
10162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(release_lock);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const unsigned int dummy; /* for clarity in register access fns */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cienum mos_regs {
10862306a36Sopenharmony_ci	MOS7720_THR,		  /* serial port regs */
10962306a36Sopenharmony_ci	MOS7720_RHR,
11062306a36Sopenharmony_ci	MOS7720_IER,
11162306a36Sopenharmony_ci	MOS7720_FCR,
11262306a36Sopenharmony_ci	MOS7720_ISR,
11362306a36Sopenharmony_ci	MOS7720_LCR,
11462306a36Sopenharmony_ci	MOS7720_MCR,
11562306a36Sopenharmony_ci	MOS7720_LSR,
11662306a36Sopenharmony_ci	MOS7720_MSR,
11762306a36Sopenharmony_ci	MOS7720_SPR,
11862306a36Sopenharmony_ci	MOS7720_DLL,
11962306a36Sopenharmony_ci	MOS7720_DLM,
12062306a36Sopenharmony_ci	MOS7720_DPR,		  /* parallel port regs */
12162306a36Sopenharmony_ci	MOS7720_DSR,
12262306a36Sopenharmony_ci	MOS7720_DCR,
12362306a36Sopenharmony_ci	MOS7720_ECR,
12462306a36Sopenharmony_ci	MOS7720_SP1_REG,	  /* device control regs */
12562306a36Sopenharmony_ci	MOS7720_SP2_REG,	  /* serial port 2 (7720 only) */
12662306a36Sopenharmony_ci	MOS7720_PP_REG,
12762306a36Sopenharmony_ci	MOS7720_SP_CONTROL_REG,
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/*
13162306a36Sopenharmony_ci * Return the correct value for the Windex field of the setup packet
13262306a36Sopenharmony_ci * for a control endpoint message.  See the 7715 datasheet.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistatic inline __u16 get_reg_index(enum mos_regs reg)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	static const __u16 mos7715_index_lookup_table[] = {
13762306a36Sopenharmony_ci		0x00,		/* MOS7720_THR */
13862306a36Sopenharmony_ci		0x00,		/* MOS7720_RHR */
13962306a36Sopenharmony_ci		0x01,		/* MOS7720_IER */
14062306a36Sopenharmony_ci		0x02,		/* MOS7720_FCR */
14162306a36Sopenharmony_ci		0x02,		/* MOS7720_ISR */
14262306a36Sopenharmony_ci		0x03,		/* MOS7720_LCR */
14362306a36Sopenharmony_ci		0x04,		/* MOS7720_MCR */
14462306a36Sopenharmony_ci		0x05,		/* MOS7720_LSR */
14562306a36Sopenharmony_ci		0x06,		/* MOS7720_MSR */
14662306a36Sopenharmony_ci		0x07,		/* MOS7720_SPR */
14762306a36Sopenharmony_ci		0x00,		/* MOS7720_DLL */
14862306a36Sopenharmony_ci		0x01,		/* MOS7720_DLM */
14962306a36Sopenharmony_ci		0x00,		/* MOS7720_DPR */
15062306a36Sopenharmony_ci		0x01,		/* MOS7720_DSR */
15162306a36Sopenharmony_ci		0x02,		/* MOS7720_DCR */
15262306a36Sopenharmony_ci		0x0a,		/* MOS7720_ECR */
15362306a36Sopenharmony_ci		0x01,		/* MOS7720_SP1_REG */
15462306a36Sopenharmony_ci		0x02,		/* MOS7720_SP2_REG (7720 only) */
15562306a36Sopenharmony_ci		0x04,		/* MOS7720_PP_REG (7715 only) */
15662306a36Sopenharmony_ci		0x08,		/* MOS7720_SP_CONTROL_REG */
15762306a36Sopenharmony_ci	};
15862306a36Sopenharmony_ci	return mos7715_index_lookup_table[reg];
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * Return the correct value for the upper byte of the Wvalue field of
16362306a36Sopenharmony_ci * the setup packet for a control endpoint message.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic inline __u16 get_reg_value(enum mos_regs reg,
16662306a36Sopenharmony_ci				  unsigned int serial_portnum)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	if (reg >= MOS7720_SP1_REG)	/* control reg */
16962306a36Sopenharmony_ci		return 0x0000;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	else if (reg >= MOS7720_DPR)	/* parallel port reg (7715 only) */
17262306a36Sopenharmony_ci		return 0x0100;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	else			      /* serial port reg */
17562306a36Sopenharmony_ci		return (serial_portnum + 2) << 8;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/*
17962306a36Sopenharmony_ci * Write data byte to the specified device register.  The data is embedded in
18062306a36Sopenharmony_ci * the value field of the setup packet. serial_portnum is ignored for registers
18162306a36Sopenharmony_ci * not specific to a particular serial port.
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_cistatic int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
18462306a36Sopenharmony_ci			 enum mos_regs reg, __u8 data)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct usb_device *usbdev = serial->dev;
18762306a36Sopenharmony_ci	unsigned int pipe = usb_sndctrlpipe(usbdev, 0);
18862306a36Sopenharmony_ci	__u8 request = (__u8)0x0e;
18962306a36Sopenharmony_ci	__u8 requesttype = (__u8)0x40;
19062306a36Sopenharmony_ci	__u16 index = get_reg_index(reg);
19162306a36Sopenharmony_ci	__u16 value = get_reg_value(reg, serial_portnum) + data;
19262306a36Sopenharmony_ci	int status = usb_control_msg(usbdev, pipe, request, requesttype, value,
19362306a36Sopenharmony_ci				     index, NULL, 0, MOS_WDR_TIMEOUT);
19462306a36Sopenharmony_ci	if (status < 0)
19562306a36Sopenharmony_ci		dev_err(&usbdev->dev,
19662306a36Sopenharmony_ci			"mos7720: usb_control_msg() failed: %d\n", status);
19762306a36Sopenharmony_ci	return status;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/*
20162306a36Sopenharmony_ci * Read data byte from the specified device register.  The data returned by the
20262306a36Sopenharmony_ci * device is embedded in the value field of the setup packet.  serial_portnum is
20362306a36Sopenharmony_ci * ignored for registers that are not specific to a particular serial port.
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
20662306a36Sopenharmony_ci			enum mos_regs reg, __u8 *data)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct usb_device *usbdev = serial->dev;
20962306a36Sopenharmony_ci	unsigned int pipe = usb_rcvctrlpipe(usbdev, 0);
21062306a36Sopenharmony_ci	__u8 request = (__u8)0x0d;
21162306a36Sopenharmony_ci	__u8 requesttype = (__u8)0xc0;
21262306a36Sopenharmony_ci	__u16 index = get_reg_index(reg);
21362306a36Sopenharmony_ci	__u16 value = get_reg_value(reg, serial_portnum);
21462306a36Sopenharmony_ci	u8 *buf;
21562306a36Sopenharmony_ci	int status;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	buf = kmalloc(1, GFP_KERNEL);
21862306a36Sopenharmony_ci	if (!buf) {
21962306a36Sopenharmony_ci		*data = 0;
22062306a36Sopenharmony_ci		return -ENOMEM;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	status = usb_control_msg(usbdev, pipe, request, requesttype, value,
22462306a36Sopenharmony_ci				     index, buf, 1, MOS_WDR_TIMEOUT);
22562306a36Sopenharmony_ci	if (status == 1) {
22662306a36Sopenharmony_ci		*data = *buf;
22762306a36Sopenharmony_ci	} else {
22862306a36Sopenharmony_ci		dev_err(&usbdev->dev,
22962306a36Sopenharmony_ci			"mos7720: usb_control_msg() failed: %d\n", status);
23062306a36Sopenharmony_ci		if (status >= 0)
23162306a36Sopenharmony_ci			status = -EIO;
23262306a36Sopenharmony_ci		*data = 0;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	kfree(buf);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return status;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic inline int mos7715_change_mode(struct mos7715_parport *mos_parport,
24362306a36Sopenharmony_ci				      enum mos7715_pp_modes mode)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	mos_parport->shadowECR = mode;
24662306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
24762306a36Sopenharmony_ci		      mos_parport->shadowECR);
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void destroy_mos_parport(struct kref *kref)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct mos7715_parport *mos_parport =
25462306a36Sopenharmony_ci		container_of(kref, struct mos7715_parport, ref_count);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	kfree(mos_parport);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * This is the common top part of all parallel port callback operations that
26162306a36Sopenharmony_ci * send synchronous messages to the device.  This implements convoluted locking
26262306a36Sopenharmony_ci * that avoids two scenarios: (1) a port operation is called after usbserial
26362306a36Sopenharmony_ci * has called our release function, at which point struct mos7715_parport has
26462306a36Sopenharmony_ci * been destroyed, and (2) the device has been disconnected, but usbserial has
26562306a36Sopenharmony_ci * not called the release function yet because someone has a serial port open.
26662306a36Sopenharmony_ci * The shared release_lock prevents the first, and the mutex and disconnected
26762306a36Sopenharmony_ci * flag maintained by usbserial covers the second.  We also use the msg_pending
26862306a36Sopenharmony_ci * flag to ensure that all synchronous usb message calls have completed before
26962306a36Sopenharmony_ci * our release function can return.
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_cistatic int parport_prologue(struct parport *pp)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	spin_lock(&release_lock);
27662306a36Sopenharmony_ci	mos_parport = pp->private_data;
27762306a36Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {
27862306a36Sopenharmony_ci		/* release fn called, port struct destroyed */
27962306a36Sopenharmony_ci		spin_unlock(&release_lock);
28062306a36Sopenharmony_ci		return -1;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	mos_parport->msg_pending = true;   /* synch usb call pending */
28362306a36Sopenharmony_ci	reinit_completion(&mos_parport->syncmsg_compl);
28462306a36Sopenharmony_ci	spin_unlock(&release_lock);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* ensure writes from restore are submitted before new requests */
28762306a36Sopenharmony_ci	if (work_pending(&mos_parport->work))
28862306a36Sopenharmony_ci		flush_work(&mos_parport->work);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	mutex_lock(&mos_parport->serial->disc_mutex);
29162306a36Sopenharmony_ci	if (mos_parport->serial->disconnected) {
29262306a36Sopenharmony_ci		/* device disconnected */
29362306a36Sopenharmony_ci		mutex_unlock(&mos_parport->serial->disc_mutex);
29462306a36Sopenharmony_ci		mos_parport->msg_pending = false;
29562306a36Sopenharmony_ci		complete(&mos_parport->syncmsg_compl);
29662306a36Sopenharmony_ci		return -1;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return 0;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/*
30362306a36Sopenharmony_ci * This is the common bottom part of all parallel port functions that send
30462306a36Sopenharmony_ci * synchronous messages to the device.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_cistatic inline void parport_epilogue(struct parport *pp)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
30962306a36Sopenharmony_ci	mutex_unlock(&mos_parport->serial->disc_mutex);
31062306a36Sopenharmony_ci	mos_parport->msg_pending = false;
31162306a36Sopenharmony_ci	complete(&mos_parport->syncmsg_compl);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void deferred_restore_writes(struct work_struct *work)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	mos_parport = container_of(work, struct mos7715_parport, work);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	mutex_lock(&mos_parport->serial->disc_mutex);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* if device disconnected, game over */
32362306a36Sopenharmony_ci	if (mos_parport->serial->disconnected)
32462306a36Sopenharmony_ci		goto done;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
32762306a36Sopenharmony_ci		      mos_parport->shadowDCR);
32862306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
32962306a36Sopenharmony_ci		      mos_parport->shadowECR);
33062306a36Sopenharmony_cidone:
33162306a36Sopenharmony_ci	mutex_unlock(&mos_parport->serial->disc_mutex);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void parport_mos7715_write_data(struct parport *pp, unsigned char d)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
33962306a36Sopenharmony_ci		return;
34062306a36Sopenharmony_ci	mos7715_change_mode(mos_parport, SPP);
34162306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, (__u8)d);
34262306a36Sopenharmony_ci	parport_epilogue(pp);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic unsigned char parport_mos7715_read_data(struct parport *pp)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
34862306a36Sopenharmony_ci	unsigned char d;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
35162306a36Sopenharmony_ci		return 0;
35262306a36Sopenharmony_ci	read_mos_reg(mos_parport->serial, dummy, MOS7720_DPR, &d);
35362306a36Sopenharmony_ci	parport_epilogue(pp);
35462306a36Sopenharmony_ci	return d;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void parport_mos7715_write_control(struct parport *pp, unsigned char d)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
36062306a36Sopenharmony_ci	__u8 data;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci	data = ((__u8)d & 0x0f) | (mos_parport->shadowDCR & 0xf0);
36562306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR, data);
36662306a36Sopenharmony_ci	mos_parport->shadowDCR = data;
36762306a36Sopenharmony_ci	parport_epilogue(pp);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic unsigned char parport_mos7715_read_control(struct parport *pp)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
37362306a36Sopenharmony_ci	__u8 dcr;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	spin_lock(&release_lock);
37662306a36Sopenharmony_ci	mos_parport = pp->private_data;
37762306a36Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {
37862306a36Sopenharmony_ci		spin_unlock(&release_lock);
37962306a36Sopenharmony_ci		return 0;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	dcr = mos_parport->shadowDCR & 0x0f;
38262306a36Sopenharmony_ci	spin_unlock(&release_lock);
38362306a36Sopenharmony_ci	return dcr;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic unsigned char parport_mos7715_frob_control(struct parport *pp,
38762306a36Sopenharmony_ci						  unsigned char mask,
38862306a36Sopenharmony_ci						  unsigned char val)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
39162306a36Sopenharmony_ci	__u8 dcr;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	mask &= 0x0f;
39462306a36Sopenharmony_ci	val &= 0x0f;
39562306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
39662306a36Sopenharmony_ci		return 0;
39762306a36Sopenharmony_ci	mos_parport->shadowDCR = (mos_parport->shadowDCR & (~mask)) ^ val;
39862306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
39962306a36Sopenharmony_ci		      mos_parport->shadowDCR);
40062306a36Sopenharmony_ci	dcr = mos_parport->shadowDCR & 0x0f;
40162306a36Sopenharmony_ci	parport_epilogue(pp);
40262306a36Sopenharmony_ci	return dcr;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic unsigned char parport_mos7715_read_status(struct parport *pp)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	unsigned char status;
40862306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	spin_lock(&release_lock);
41162306a36Sopenharmony_ci	mos_parport = pp->private_data;
41262306a36Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {	/* release called */
41362306a36Sopenharmony_ci		spin_unlock(&release_lock);
41462306a36Sopenharmony_ci		return 0;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	status = atomic_read(&mos_parport->shadowDSR) & 0xf8;
41762306a36Sopenharmony_ci	spin_unlock(&release_lock);
41862306a36Sopenharmony_ci	return status;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic void parport_mos7715_enable_irq(struct parport *pp)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void parport_mos7715_disable_irq(struct parport *pp)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic void parport_mos7715_data_forward(struct parport *pp)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
43462306a36Sopenharmony_ci		return;
43562306a36Sopenharmony_ci	mos7715_change_mode(mos_parport, PS2);
43662306a36Sopenharmony_ci	mos_parport->shadowDCR &=  ~0x20;
43762306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
43862306a36Sopenharmony_ci		      mos_parport->shadowDCR);
43962306a36Sopenharmony_ci	parport_epilogue(pp);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void parport_mos7715_data_reverse(struct parport *pp)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci	mos7715_change_mode(mos_parport, PS2);
44962306a36Sopenharmony_ci	mos_parport->shadowDCR |= 0x20;
45062306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
45162306a36Sopenharmony_ci		      mos_parport->shadowDCR);
45262306a36Sopenharmony_ci	parport_epilogue(pp);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void parport_mos7715_init_state(struct pardevice *dev,
45662306a36Sopenharmony_ci				       struct parport_state *s)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	s->u.pc.ctr = DCR_INIT_VAL;
45962306a36Sopenharmony_ci	s->u.pc.ecr = ECR_INIT_VAL;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/* N.B. Parport core code requires that this function not block */
46362306a36Sopenharmony_cistatic void parport_mos7715_save_state(struct parport *pp,
46462306a36Sopenharmony_ci				       struct parport_state *s)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	spin_lock(&release_lock);
46962306a36Sopenharmony_ci	mos_parport = pp->private_data;
47062306a36Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {	/* release called */
47162306a36Sopenharmony_ci		spin_unlock(&release_lock);
47262306a36Sopenharmony_ci		return;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci	s->u.pc.ctr = mos_parport->shadowDCR;
47562306a36Sopenharmony_ci	s->u.pc.ecr = mos_parport->shadowECR;
47662306a36Sopenharmony_ci	spin_unlock(&release_lock);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/* N.B. Parport core code requires that this function not block */
48062306a36Sopenharmony_cistatic void parport_mos7715_restore_state(struct parport *pp,
48162306a36Sopenharmony_ci					  struct parport_state *s)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	spin_lock(&release_lock);
48662306a36Sopenharmony_ci	mos_parport = pp->private_data;
48762306a36Sopenharmony_ci	if (unlikely(mos_parport == NULL)) {	/* release called */
48862306a36Sopenharmony_ci		spin_unlock(&release_lock);
48962306a36Sopenharmony_ci		return;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci	mos_parport->shadowDCR = s->u.pc.ctr;
49262306a36Sopenharmony_ci	mos_parport->shadowECR = s->u.pc.ecr;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	schedule_work(&mos_parport->work);
49562306a36Sopenharmony_ci	spin_unlock(&release_lock);
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic size_t parport_mos7715_write_compat(struct parport *pp,
49962306a36Sopenharmony_ci					   const void *buffer,
50062306a36Sopenharmony_ci					   size_t len, int flags)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	int retval;
50362306a36Sopenharmony_ci	struct mos7715_parport *mos_parport = pp->private_data;
50462306a36Sopenharmony_ci	int actual_len;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (parport_prologue(pp) < 0)
50762306a36Sopenharmony_ci		return 0;
50862306a36Sopenharmony_ci	mos7715_change_mode(mos_parport, PPF);
50962306a36Sopenharmony_ci	retval = usb_bulk_msg(mos_parport->serial->dev,
51062306a36Sopenharmony_ci			      usb_sndbulkpipe(mos_parport->serial->dev, 2),
51162306a36Sopenharmony_ci			      (void *)buffer, len, &actual_len,
51262306a36Sopenharmony_ci			      MOS_WDR_TIMEOUT);
51362306a36Sopenharmony_ci	parport_epilogue(pp);
51462306a36Sopenharmony_ci	if (retval) {
51562306a36Sopenharmony_ci		dev_err(&mos_parport->serial->dev->dev,
51662306a36Sopenharmony_ci			"mos7720: usb_bulk_msg() failed: %d\n", retval);
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	return actual_len;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic struct parport_operations parport_mos7715_ops = {
52362306a36Sopenharmony_ci	.owner =		THIS_MODULE,
52462306a36Sopenharmony_ci	.write_data =		parport_mos7715_write_data,
52562306a36Sopenharmony_ci	.read_data =		parport_mos7715_read_data,
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	.write_control =	parport_mos7715_write_control,
52862306a36Sopenharmony_ci	.read_control =		parport_mos7715_read_control,
52962306a36Sopenharmony_ci	.frob_control =		parport_mos7715_frob_control,
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	.read_status =		parport_mos7715_read_status,
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	.enable_irq =		parport_mos7715_enable_irq,
53462306a36Sopenharmony_ci	.disable_irq =		parport_mos7715_disable_irq,
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	.data_forward =		parport_mos7715_data_forward,
53762306a36Sopenharmony_ci	.data_reverse =		parport_mos7715_data_reverse,
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	.init_state =		parport_mos7715_init_state,
54062306a36Sopenharmony_ci	.save_state =		parport_mos7715_save_state,
54162306a36Sopenharmony_ci	.restore_state =	parport_mos7715_restore_state,
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	.compat_write_data =	parport_mos7715_write_compat,
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	.nibble_read_data =	parport_ieee1284_read_nibble,
54662306a36Sopenharmony_ci	.byte_read_data =	parport_ieee1284_read_byte,
54762306a36Sopenharmony_ci};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci/*
55062306a36Sopenharmony_ci * Allocate and initialize parallel port control struct, initialize
55162306a36Sopenharmony_ci * the parallel port hardware device, and register with the parport subsystem.
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic int mos7715_parport_init(struct usb_serial *serial)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct mos7715_parport *mos_parport;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* allocate and initialize parallel port control struct */
55862306a36Sopenharmony_ci	mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
55962306a36Sopenharmony_ci	if (!mos_parport)
56062306a36Sopenharmony_ci		return -ENOMEM;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	mos_parport->msg_pending = false;
56362306a36Sopenharmony_ci	kref_init(&mos_parport->ref_count);
56462306a36Sopenharmony_ci	usb_set_serial_data(serial, mos_parport); /* hijack private pointer */
56562306a36Sopenharmony_ci	mos_parport->serial = serial;
56662306a36Sopenharmony_ci	INIT_WORK(&mos_parport->work, deferred_restore_writes);
56762306a36Sopenharmony_ci	init_completion(&mos_parport->syncmsg_compl);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* cycle parallel port reset bit */
57062306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x80);
57162306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_PP_REG, (__u8)0x00);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* initialize device registers */
57462306a36Sopenharmony_ci	mos_parport->shadowDCR = DCR_INIT_VAL;
57562306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_DCR,
57662306a36Sopenharmony_ci		      mos_parport->shadowDCR);
57762306a36Sopenharmony_ci	mos_parport->shadowECR = ECR_INIT_VAL;
57862306a36Sopenharmony_ci	write_mos_reg(mos_parport->serial, dummy, MOS7720_ECR,
57962306a36Sopenharmony_ci		      mos_parport->shadowECR);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* register with parport core */
58262306a36Sopenharmony_ci	mos_parport->pp = parport_register_port(0, PARPORT_IRQ_NONE,
58362306a36Sopenharmony_ci						PARPORT_DMA_NONE,
58462306a36Sopenharmony_ci						&parport_mos7715_ops);
58562306a36Sopenharmony_ci	if (mos_parport->pp == NULL) {
58662306a36Sopenharmony_ci		dev_err(&serial->interface->dev,
58762306a36Sopenharmony_ci			"Could not register parport\n");
58862306a36Sopenharmony_ci		kref_put(&mos_parport->ref_count, destroy_mos_parport);
58962306a36Sopenharmony_ci		return -EIO;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci	mos_parport->pp->private_data = mos_parport;
59262306a36Sopenharmony_ci	mos_parport->pp->modes = PARPORT_MODE_COMPAT | PARPORT_MODE_PCSPP;
59362306a36Sopenharmony_ci	mos_parport->pp->dev = &serial->interface->dev;
59462306a36Sopenharmony_ci	parport_announce_port(mos_parport->pp);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return 0;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci#endif	/* CONFIG_USB_SERIAL_MOS7715_PARPORT */
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/*
60162306a36Sopenharmony_ci * mos7720_interrupt_callback
60262306a36Sopenharmony_ci *	this is the callback function for when we have received data on the
60362306a36Sopenharmony_ci *	interrupt endpoint.
60462306a36Sopenharmony_ci */
60562306a36Sopenharmony_cistatic void mos7720_interrupt_callback(struct urb *urb)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	int result;
60862306a36Sopenharmony_ci	int length;
60962306a36Sopenharmony_ci	int status = urb->status;
61062306a36Sopenharmony_ci	struct device *dev = &urb->dev->dev;
61162306a36Sopenharmony_ci	__u8 *data;
61262306a36Sopenharmony_ci	__u8 sp1;
61362306a36Sopenharmony_ci	__u8 sp2;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	switch (status) {
61662306a36Sopenharmony_ci	case 0:
61762306a36Sopenharmony_ci		/* success */
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	case -ECONNRESET:
62062306a36Sopenharmony_ci	case -ENOENT:
62162306a36Sopenharmony_ci	case -ESHUTDOWN:
62262306a36Sopenharmony_ci		/* this urb is terminated, clean up */
62362306a36Sopenharmony_ci		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
62462306a36Sopenharmony_ci		return;
62562306a36Sopenharmony_ci	default:
62662306a36Sopenharmony_ci		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
62762306a36Sopenharmony_ci		goto exit;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	length = urb->actual_length;
63162306a36Sopenharmony_ci	data = urb->transfer_buffer;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* Moschip get 4 bytes
63462306a36Sopenharmony_ci	 * Byte 1 IIR Port 1 (port.number is 0)
63562306a36Sopenharmony_ci	 * Byte 2 IIR Port 2 (port.number is 1)
63662306a36Sopenharmony_ci	 * Byte 3 --------------
63762306a36Sopenharmony_ci	 * Byte 4 FIFO status for both */
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/* the above description is inverted
64062306a36Sopenharmony_ci	 * 	oneukum 2007-03-14 */
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	if (unlikely(length != 4)) {
64362306a36Sopenharmony_ci		dev_dbg(dev, "Wrong data !!!\n");
64462306a36Sopenharmony_ci		return;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	sp1 = data[3];
64862306a36Sopenharmony_ci	sp2 = data[2];
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if ((sp1 | sp2) & 0x01) {
65162306a36Sopenharmony_ci		/* No Interrupt Pending in both the ports */
65262306a36Sopenharmony_ci		dev_dbg(dev, "No Interrupt !!!\n");
65362306a36Sopenharmony_ci	} else {
65462306a36Sopenharmony_ci		switch (sp1 & 0x0f) {
65562306a36Sopenharmony_ci		case SERIAL_IIR_RLS:
65662306a36Sopenharmony_ci			dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n");
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci		case SERIAL_IIR_CTI:
65962306a36Sopenharmony_ci			dev_dbg(dev, "Serial Port 1: Receiver time out\n");
66062306a36Sopenharmony_ci			break;
66162306a36Sopenharmony_ci		case SERIAL_IIR_MS:
66262306a36Sopenharmony_ci			/* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */
66362306a36Sopenharmony_ci			break;
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		switch (sp2 & 0x0f) {
66762306a36Sopenharmony_ci		case SERIAL_IIR_RLS:
66862306a36Sopenharmony_ci			dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n");
66962306a36Sopenharmony_ci			break;
67062306a36Sopenharmony_ci		case SERIAL_IIR_CTI:
67162306a36Sopenharmony_ci			dev_dbg(dev, "Serial Port 2: Receiver time out\n");
67262306a36Sopenharmony_ci			break;
67362306a36Sopenharmony_ci		case SERIAL_IIR_MS:
67462306a36Sopenharmony_ci			/* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */
67562306a36Sopenharmony_ci			break;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ciexit:
68062306a36Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
68162306a36Sopenharmony_ci	if (result)
68262306a36Sopenharmony_ci		dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/*
68662306a36Sopenharmony_ci * mos7715_interrupt_callback
68762306a36Sopenharmony_ci *	this is the 7715's callback function for when we have received data on
68862306a36Sopenharmony_ci *	the interrupt endpoint.
68962306a36Sopenharmony_ci */
69062306a36Sopenharmony_cistatic void mos7715_interrupt_callback(struct urb *urb)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	int result;
69362306a36Sopenharmony_ci	int length;
69462306a36Sopenharmony_ci	int status = urb->status;
69562306a36Sopenharmony_ci	struct device *dev = &urb->dev->dev;
69662306a36Sopenharmony_ci	__u8 *data;
69762306a36Sopenharmony_ci	__u8 iir;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	switch (status) {
70062306a36Sopenharmony_ci	case 0:
70162306a36Sopenharmony_ci		/* success */
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	case -ECONNRESET:
70462306a36Sopenharmony_ci	case -ENOENT:
70562306a36Sopenharmony_ci	case -ESHUTDOWN:
70662306a36Sopenharmony_ci	case -ENODEV:
70762306a36Sopenharmony_ci		/* this urb is terminated, clean up */
70862306a36Sopenharmony_ci		dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
70962306a36Sopenharmony_ci		return;
71062306a36Sopenharmony_ci	default:
71162306a36Sopenharmony_ci		dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
71262306a36Sopenharmony_ci		goto exit;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	length = urb->actual_length;
71662306a36Sopenharmony_ci	data = urb->transfer_buffer;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Structure of data from 7715 device:
71962306a36Sopenharmony_ci	 * Byte 1: IIR serial Port
72062306a36Sopenharmony_ci	 * Byte 2: unused
72162306a36Sopenharmony_ci	 * Byte 2: DSR parallel port
72262306a36Sopenharmony_ci	 * Byte 4: FIFO status for both */
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (unlikely(length != 4)) {
72562306a36Sopenharmony_ci		dev_dbg(dev, "Wrong data !!!\n");
72662306a36Sopenharmony_ci		return;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	iir = data[0];
73062306a36Sopenharmony_ci	if (!(iir & 0x01)) {	/* serial port interrupt pending */
73162306a36Sopenharmony_ci		switch (iir & 0x0f) {
73262306a36Sopenharmony_ci		case SERIAL_IIR_RLS:
73362306a36Sopenharmony_ci			dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n");
73462306a36Sopenharmony_ci			break;
73562306a36Sopenharmony_ci		case SERIAL_IIR_CTI:
73662306a36Sopenharmony_ci			dev_dbg(dev, "Serial Port: Receiver time out\n");
73762306a36Sopenharmony_ci			break;
73862306a36Sopenharmony_ci		case SERIAL_IIR_MS:
73962306a36Sopenharmony_ci			/* dev_dbg(dev, "Serial Port: Modem status change\n"); */
74062306a36Sopenharmony_ci			break;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
74562306a36Sopenharmony_ci	{       /* update local copy of DSR reg */
74662306a36Sopenharmony_ci		struct usb_serial_port *port = urb->context;
74762306a36Sopenharmony_ci		struct mos7715_parport *mos_parport = port->serial->private;
74862306a36Sopenharmony_ci		if (unlikely(mos_parport == NULL))
74962306a36Sopenharmony_ci			return;
75062306a36Sopenharmony_ci		atomic_set(&mos_parport->shadowDSR, data[2]);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci#endif
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ciexit:
75562306a36Sopenharmony_ci	result = usb_submit_urb(urb, GFP_ATOMIC);
75662306a36Sopenharmony_ci	if (result)
75762306a36Sopenharmony_ci		dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci/*
76162306a36Sopenharmony_ci * mos7720_bulk_in_callback
76262306a36Sopenharmony_ci *	this is the callback function for when we have received data on the
76362306a36Sopenharmony_ci *	bulk in endpoint.
76462306a36Sopenharmony_ci */
76562306a36Sopenharmony_cistatic void mos7720_bulk_in_callback(struct urb *urb)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	int retval;
76862306a36Sopenharmony_ci	unsigned char *data ;
76962306a36Sopenharmony_ci	struct usb_serial_port *port;
77062306a36Sopenharmony_ci	int status = urb->status;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (status) {
77362306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
77462306a36Sopenharmony_ci		return;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	port = urb->context;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	dev_dbg(&port->dev, "Entering...%s\n", __func__);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	data = urb->transfer_buffer;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (urb->actual_length) {
78462306a36Sopenharmony_ci		tty_insert_flip_string(&port->port, data, urb->actual_length);
78562306a36Sopenharmony_ci		tty_flip_buffer_push(&port->port);
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (port->read_urb->status != -EINPROGRESS) {
78962306a36Sopenharmony_ci		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
79062306a36Sopenharmony_ci		if (retval)
79162306a36Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci/*
79662306a36Sopenharmony_ci * mos7720_bulk_out_data_callback
79762306a36Sopenharmony_ci *	this is the callback function for when we have finished sending serial
79862306a36Sopenharmony_ci *	data on the bulk out endpoint.
79962306a36Sopenharmony_ci */
80062306a36Sopenharmony_cistatic void mos7720_bulk_out_data_callback(struct urb *urb)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
80362306a36Sopenharmony_ci	int status = urb->status;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (status) {
80662306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status);
80762306a36Sopenharmony_ci		return;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	mos7720_port = urb->context;
81162306a36Sopenharmony_ci	if (!mos7720_port) {
81262306a36Sopenharmony_ci		dev_dbg(&urb->dev->dev, "NULL mos7720_port pointer\n");
81362306a36Sopenharmony_ci		return ;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (mos7720_port->open)
81762306a36Sopenharmony_ci		tty_port_tty_wakeup(&mos7720_port->port->port);
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic int mos77xx_calc_num_ports(struct usb_serial *serial,
82162306a36Sopenharmony_ci					struct usb_serial_endpoints *epds)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (product == MOSCHIP_DEVICE_ID_7715) {
82662306a36Sopenharmony_ci		/*
82762306a36Sopenharmony_ci		 * The 7715 uses the first bulk in/out endpoint pair for the
82862306a36Sopenharmony_ci		 * parallel port, and the second for the serial port. We swap
82962306a36Sopenharmony_ci		 * the endpoint descriptors here so that the first and
83062306a36Sopenharmony_ci		 * only registered port structure uses the serial-port
83162306a36Sopenharmony_ci		 * endpoints.
83262306a36Sopenharmony_ci		 */
83362306a36Sopenharmony_ci		swap(epds->bulk_in[0], epds->bulk_in[1]);
83462306a36Sopenharmony_ci		swap(epds->bulk_out[0], epds->bulk_out[1]);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci		return 1;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return 2;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct usb_serial *serial;
84562306a36Sopenharmony_ci	struct urb *urb;
84662306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
84762306a36Sopenharmony_ci	int response;
84862306a36Sopenharmony_ci	int port_number;
84962306a36Sopenharmony_ci	__u8 data;
85062306a36Sopenharmony_ci	int allocated_urbs = 0;
85162306a36Sopenharmony_ci	int j;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	serial = port->serial;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
85662306a36Sopenharmony_ci	if (mos7720_port == NULL)
85762306a36Sopenharmony_ci		return -ENODEV;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	usb_clear_halt(serial->dev, port->write_urb->pipe);
86062306a36Sopenharmony_ci	usb_clear_halt(serial->dev, port->read_urb->pipe);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* Initialising the write urb pool */
86362306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
86462306a36Sopenharmony_ci		urb = usb_alloc_urb(0, GFP_KERNEL);
86562306a36Sopenharmony_ci		mos7720_port->write_urb_pool[j] = urb;
86662306a36Sopenharmony_ci		if (!urb)
86762306a36Sopenharmony_ci			continue;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
87062306a36Sopenharmony_ci					       GFP_KERNEL);
87162306a36Sopenharmony_ci		if (!urb->transfer_buffer) {
87262306a36Sopenharmony_ci			usb_free_urb(mos7720_port->write_urb_pool[j]);
87362306a36Sopenharmony_ci			mos7720_port->write_urb_pool[j] = NULL;
87462306a36Sopenharmony_ci			continue;
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci		allocated_urbs++;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	if (!allocated_urbs)
88062306a36Sopenharmony_ci		return -ENOMEM;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	 /* Initialize MCS7720 -- Write Init values to corresponding Registers
88362306a36Sopenharmony_ci	  *
88462306a36Sopenharmony_ci	  * Register Index
88562306a36Sopenharmony_ci	  * 0 : MOS7720_THR/MOS7720_RHR
88662306a36Sopenharmony_ci	  * 1 : MOS7720_IER
88762306a36Sopenharmony_ci	  * 2 : MOS7720_FCR
88862306a36Sopenharmony_ci	  * 3 : MOS7720_LCR
88962306a36Sopenharmony_ci	  * 4 : MOS7720_MCR
89062306a36Sopenharmony_ci	  * 5 : MOS7720_LSR
89162306a36Sopenharmony_ci	  * 6 : MOS7720_MSR
89262306a36Sopenharmony_ci	  * 7 : MOS7720_SPR
89362306a36Sopenharmony_ci	  *
89462306a36Sopenharmony_ci	  * 0x08 : SP1/2 Control Reg
89562306a36Sopenharmony_ci	  */
89662306a36Sopenharmony_ci	port_number = port->port_number;
89762306a36Sopenharmony_ci	read_mos_reg(serial, port_number, MOS7720_LSR, &data);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	dev_dbg(&port->dev, "SS::%p LSR:%x\n", mos7720_port, data);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP1_REG, 0x02);
90262306a36Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP2_REG, 0x02);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
90562306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
90862306a36Sopenharmony_ci	mos7720_port->shadowLCR = 0x03;
90962306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
91062306a36Sopenharmony_ci		      mos7720_port->shadowLCR);
91162306a36Sopenharmony_ci	mos7720_port->shadowMCR = 0x0b;
91262306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
91362306a36Sopenharmony_ci		      mos7720_port->shadowMCR);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_SP_CONTROL_REG, 0x00);
91662306a36Sopenharmony_ci	read_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, &data);
91762306a36Sopenharmony_ci	data = data | (port->port_number + 1);
91862306a36Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, data);
91962306a36Sopenharmony_ci	mos7720_port->shadowLCR = 0x83;
92062306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
92162306a36Sopenharmony_ci		      mos7720_port->shadowLCR);
92262306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_THR, 0x0c);
92362306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
92462306a36Sopenharmony_ci	mos7720_port->shadowLCR = 0x03;
92562306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
92662306a36Sopenharmony_ci		      mos7720_port->shadowLCR);
92762306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	response = usb_submit_urb(port->read_urb, GFP_KERNEL);
93062306a36Sopenharmony_ci	if (response)
93162306a36Sopenharmony_ci		dev_err(&port->dev, "%s - Error %d submitting read urb\n",
93262306a36Sopenharmony_ci							__func__, response);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* initialize our port settings */
93562306a36Sopenharmony_ci	mos7720_port->shadowMCR = UART_MCR_OUT2; /* Must set to enable ints! */
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* send a open port command */
93862306a36Sopenharmony_ci	mos7720_port->open = 1;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return 0;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci/*
94462306a36Sopenharmony_ci * mos7720_chars_in_buffer
94562306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to know how many
94662306a36Sopenharmony_ci *	bytes of data we currently have outstanding in the port (data that has
94762306a36Sopenharmony_ci *	been written, but hasn't made it out the port yet)
94862306a36Sopenharmony_ci */
94962306a36Sopenharmony_cistatic unsigned int mos7720_chars_in_buffer(struct tty_struct *tty)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
95262306a36Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
95362306a36Sopenharmony_ci	int i;
95462306a36Sopenharmony_ci	unsigned int chars = 0;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
95762306a36Sopenharmony_ci		if (mos7720_port->write_urb_pool[i] &&
95862306a36Sopenharmony_ci		    mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
95962306a36Sopenharmony_ci			chars += URB_TRANSFER_BUFFER_SIZE;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %u\n", __func__, chars);
96262306a36Sopenharmony_ci	return chars;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic void mos7720_close(struct usb_serial_port *port)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct usb_serial *serial;
96862306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
96962306a36Sopenharmony_ci	int j;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	serial = port->serial;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
97462306a36Sopenharmony_ci	if (mos7720_port == NULL)
97562306a36Sopenharmony_ci		return;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j)
97862306a36Sopenharmony_ci		usb_kill_urb(mos7720_port->write_urb_pool[j]);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	/* Freeing Write URBs */
98162306a36Sopenharmony_ci	for (j = 0; j < NUM_URBS; ++j) {
98262306a36Sopenharmony_ci		if (mos7720_port->write_urb_pool[j]) {
98362306a36Sopenharmony_ci			kfree(mos7720_port->write_urb_pool[j]->transfer_buffer);
98462306a36Sopenharmony_ci			usb_free_urb(mos7720_port->write_urb_pool[j]);
98562306a36Sopenharmony_ci		}
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* While closing port, shutdown all bulk read, write  *
98962306a36Sopenharmony_ci	 * and interrupt read if they exists, otherwise nop   */
99062306a36Sopenharmony_ci	usb_kill_urb(port->write_urb);
99162306a36Sopenharmony_ci	usb_kill_urb(port->read_urb);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	write_mos_reg(serial, port->port_number, MOS7720_MCR, 0x00);
99462306a36Sopenharmony_ci	write_mos_reg(serial, port->port_number, MOS7720_IER, 0x00);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	mos7720_port->open = 0;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic int mos7720_break(struct tty_struct *tty, int break_state)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
100262306a36Sopenharmony_ci	unsigned char data;
100362306a36Sopenharmony_ci	struct usb_serial *serial;
100462306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	serial = port->serial;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
100962306a36Sopenharmony_ci	if (mos7720_port == NULL)
101062306a36Sopenharmony_ci		return -ENODEV;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	if (break_state == -1)
101362306a36Sopenharmony_ci		data = mos7720_port->shadowLCR | UART_LCR_SBC;
101462306a36Sopenharmony_ci	else
101562306a36Sopenharmony_ci		data = mos7720_port->shadowLCR & ~UART_LCR_SBC;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	mos7720_port->shadowLCR  = data;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	return write_mos_reg(serial, port->port_number, MOS7720_LCR,
102062306a36Sopenharmony_ci			     mos7720_port->shadowLCR);
102162306a36Sopenharmony_ci}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci/*
102462306a36Sopenharmony_ci * mos7720_write_room
102562306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to know how many
102662306a36Sopenharmony_ci *	bytes of data we can accept for a specific port.
102762306a36Sopenharmony_ci */
102862306a36Sopenharmony_cistatic unsigned int mos7720_write_room(struct tty_struct *tty)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
103162306a36Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
103262306a36Sopenharmony_ci	unsigned int room = 0;
103362306a36Sopenharmony_ci	int i;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* FIXME: Locking */
103662306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
103762306a36Sopenharmony_ci		if (mos7720_port->write_urb_pool[i] &&
103862306a36Sopenharmony_ci		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS)
103962306a36Sopenharmony_ci			room += URB_TRANSFER_BUFFER_SIZE;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - returns %u\n", __func__, room);
104362306a36Sopenharmony_ci	return room;
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cistatic int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
104762306a36Sopenharmony_ci				 const unsigned char *data, int count)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	int status;
105062306a36Sopenharmony_ci	int i;
105162306a36Sopenharmony_ci	int bytes_sent = 0;
105262306a36Sopenharmony_ci	int transfer_size;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
105562306a36Sopenharmony_ci	struct usb_serial *serial;
105662306a36Sopenharmony_ci	struct urb    *urb;
105762306a36Sopenharmony_ci	const unsigned char *current_position = data;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	serial = port->serial;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
106262306a36Sopenharmony_ci	if (mos7720_port == NULL)
106362306a36Sopenharmony_ci		return -ENODEV;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* try to find a free urb in the list */
106662306a36Sopenharmony_ci	urb = NULL;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	for (i = 0; i < NUM_URBS; ++i) {
106962306a36Sopenharmony_ci		if (mos7720_port->write_urb_pool[i] &&
107062306a36Sopenharmony_ci		    mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
107162306a36Sopenharmony_ci			urb = mos7720_port->write_urb_pool[i];
107262306a36Sopenharmony_ci			dev_dbg(&port->dev, "URB:%d\n", i);
107362306a36Sopenharmony_ci			break;
107462306a36Sopenharmony_ci		}
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (urb == NULL) {
107862306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
107962306a36Sopenharmony_ci		goto exit;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	if (urb->transfer_buffer == NULL) {
108362306a36Sopenharmony_ci		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
108462306a36Sopenharmony_ci					       GFP_ATOMIC);
108562306a36Sopenharmony_ci		if (!urb->transfer_buffer) {
108662306a36Sopenharmony_ci			bytes_sent = -ENOMEM;
108762306a36Sopenharmony_ci			goto exit;
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	memcpy(urb->transfer_buffer, current_position, transfer_size);
109362306a36Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, transfer_size,
109462306a36Sopenharmony_ci			      urb->transfer_buffer);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/* fill urb with data and submit  */
109762306a36Sopenharmony_ci	usb_fill_bulk_urb(urb, serial->dev,
109862306a36Sopenharmony_ci			  usb_sndbulkpipe(serial->dev,
109962306a36Sopenharmony_ci					port->bulk_out_endpointAddress),
110062306a36Sopenharmony_ci			  urb->transfer_buffer, transfer_size,
110162306a36Sopenharmony_ci			  mos7720_bulk_out_data_callback, mos7720_port);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	/* send it down the pipe */
110462306a36Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
110562306a36Sopenharmony_ci	if (status) {
110662306a36Sopenharmony_ci		dev_err_console(port, "%s - usb_submit_urb(write bulk) failed "
110762306a36Sopenharmony_ci			"with status = %d\n", __func__, status);
110862306a36Sopenharmony_ci		bytes_sent = status;
110962306a36Sopenharmony_ci		goto exit;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci	bytes_sent = transfer_size;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ciexit:
111462306a36Sopenharmony_ci	return bytes_sent;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic void mos7720_throttle(struct tty_struct *tty)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
112062306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
112162306a36Sopenharmony_ci	int status;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (mos7720_port == NULL)
112662306a36Sopenharmony_ci		return;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	if (!mos7720_port->open) {
112962306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
113062306a36Sopenharmony_ci		return;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, send the stop character */
113462306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
113562306a36Sopenharmony_ci		unsigned char stop_char = STOP_CHAR(tty);
113662306a36Sopenharmony_ci		status = mos7720_write(tty, port, &stop_char, 1);
113762306a36Sopenharmony_ci		if (status <= 0)
113862306a36Sopenharmony_ci			return;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
114262306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
114362306a36Sopenharmony_ci		mos7720_port->shadowMCR &= ~UART_MCR_RTS;
114462306a36Sopenharmony_ci		write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
114562306a36Sopenharmony_ci			      mos7720_port->shadowMCR);
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic void mos7720_unthrottle(struct tty_struct *tty)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
115262306a36Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
115362306a36Sopenharmony_ci	int status;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (mos7720_port == NULL)
115662306a36Sopenharmony_ci		return;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (!mos7720_port->open) {
115962306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
116062306a36Sopenharmony_ci		return;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* if we are implementing XON/XOFF, send the start character */
116462306a36Sopenharmony_ci	if (I_IXOFF(tty)) {
116562306a36Sopenharmony_ci		unsigned char start_char = START_CHAR(tty);
116662306a36Sopenharmony_ci		status = mos7720_write(tty, port, &start_char, 1);
116762306a36Sopenharmony_ci		if (status <= 0)
116862306a36Sopenharmony_ci			return;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/* if we are implementing RTS/CTS, toggle that line */
117262306a36Sopenharmony_ci	if (C_CRTSCTS(tty)) {
117362306a36Sopenharmony_ci		mos7720_port->shadowMCR |= UART_MCR_RTS;
117462306a36Sopenharmony_ci		write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
117562306a36Sopenharmony_ci			      mos7720_port->shadowMCR);
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci/* FIXME: this function does not work */
118062306a36Sopenharmony_cistatic int set_higher_rates(struct moschip_port *mos7720_port,
118162306a36Sopenharmony_ci			    unsigned int baud)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct usb_serial_port *port;
118462306a36Sopenharmony_ci	struct usb_serial *serial;
118562306a36Sopenharmony_ci	int port_number;
118662306a36Sopenharmony_ci	enum mos_regs sp_reg;
118762306a36Sopenharmony_ci	if (mos7720_port == NULL)
118862306a36Sopenharmony_ci		return -EINVAL;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	port = mos7720_port->port;
119162306a36Sopenharmony_ci	serial = port->serial;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	 /***********************************************
119462306a36Sopenharmony_ci	 *      Init Sequence for higher rates
119562306a36Sopenharmony_ci	 ***********************************************/
119662306a36Sopenharmony_ci	dev_dbg(&port->dev, "Sending Setting Commands ..........\n");
119762306a36Sopenharmony_ci	port_number = port->port_number;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
120062306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
120162306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
120262306a36Sopenharmony_ci	mos7720_port->shadowMCR = 0x0b;
120362306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
120462306a36Sopenharmony_ci		      mos7720_port->shadowMCR);
120562306a36Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x00);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/***********************************************
120862306a36Sopenharmony_ci	 *              Set for higher rates           *
120962306a36Sopenharmony_ci	 ***********************************************/
121062306a36Sopenharmony_ci	/* writing baud rate verbatum into uart clock field clearly not right */
121162306a36Sopenharmony_ci	if (port_number == 0)
121262306a36Sopenharmony_ci		sp_reg = MOS7720_SP1_REG;
121362306a36Sopenharmony_ci	else
121462306a36Sopenharmony_ci		sp_reg = MOS7720_SP2_REG;
121562306a36Sopenharmony_ci	write_mos_reg(serial, dummy, sp_reg, baud * 0x10);
121662306a36Sopenharmony_ci	write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG, 0x03);
121762306a36Sopenharmony_ci	mos7720_port->shadowMCR = 0x2b;
121862306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
121962306a36Sopenharmony_ci		      mos7720_port->shadowMCR);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	/***********************************************
122262306a36Sopenharmony_ci	 *              Set DLL/DLM
122362306a36Sopenharmony_ci	 ***********************************************/
122462306a36Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
122562306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
122662306a36Sopenharmony_ci		      mos7720_port->shadowLCR);
122762306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_DLL, 0x01);
122862306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_DLM, 0x00);
122962306a36Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
123062306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
123162306a36Sopenharmony_ci		      mos7720_port->shadowLCR);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	return 0;
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/* baud rate information */
123762306a36Sopenharmony_cistruct divisor_table_entry {
123862306a36Sopenharmony_ci	__u32  baudrate;
123962306a36Sopenharmony_ci	__u16  divisor;
124062306a36Sopenharmony_ci};
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci/* Define table of divisors for moschip 7720 hardware	   *
124362306a36Sopenharmony_ci * These assume a 3.6864MHz crystal, the standard /16, and *
124462306a36Sopenharmony_ci * MCR.7 = 0.						   */
124562306a36Sopenharmony_cistatic const struct divisor_table_entry divisor_table[] = {
124662306a36Sopenharmony_ci	{   50,		2304},
124762306a36Sopenharmony_ci	{   110,	1047},	/* 2094.545455 => 230450   => .0217 % over */
124862306a36Sopenharmony_ci	{   134,	857},	/* 1713.011152 => 230398.5 => .00065% under */
124962306a36Sopenharmony_ci	{   150,	768},
125062306a36Sopenharmony_ci	{   300,	384},
125162306a36Sopenharmony_ci	{   600,	192},
125262306a36Sopenharmony_ci	{   1200,	96},
125362306a36Sopenharmony_ci	{   1800,	64},
125462306a36Sopenharmony_ci	{   2400,	48},
125562306a36Sopenharmony_ci	{   4800,	24},
125662306a36Sopenharmony_ci	{   7200,	16},
125762306a36Sopenharmony_ci	{   9600,	12},
125862306a36Sopenharmony_ci	{   19200,	6},
125962306a36Sopenharmony_ci	{   38400,	3},
126062306a36Sopenharmony_ci	{   57600,	2},
126162306a36Sopenharmony_ci	{   115200,	1},
126262306a36Sopenharmony_ci};
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci/*****************************************************************************
126562306a36Sopenharmony_ci * calc_baud_rate_divisor
126662306a36Sopenharmony_ci *	this function calculates the proper baud rate divisor for the specified
126762306a36Sopenharmony_ci *	baud rate.
126862306a36Sopenharmony_ci *****************************************************************************/
126962306a36Sopenharmony_cistatic int calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	int i;
127262306a36Sopenharmony_ci	__u16 custom;
127362306a36Sopenharmony_ci	__u16 round1;
127462306a36Sopenharmony_ci	__u16 round;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - %d\n", __func__, baudrate);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
128062306a36Sopenharmony_ci		if (divisor_table[i].baudrate == baudrate) {
128162306a36Sopenharmony_ci			*divisor = divisor_table[i].divisor;
128262306a36Sopenharmony_ci			return 0;
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/* After trying for all the standard baud rates    *
128762306a36Sopenharmony_ci	 * Try calculating the divisor for this baud rate  */
128862306a36Sopenharmony_ci	if (baudrate > 75 &&  baudrate < 230400) {
128962306a36Sopenharmony_ci		/* get the divisor */
129062306a36Sopenharmony_ci		custom = (__u16)(230400L  / baudrate);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		/* Check for round off */
129362306a36Sopenharmony_ci		round1 = (__u16)(2304000L / baudrate);
129462306a36Sopenharmony_ci		round = (__u16)(round1 - (custom * 10));
129562306a36Sopenharmony_ci		if (round > 4)
129662306a36Sopenharmony_ci			custom++;
129762306a36Sopenharmony_ci		*divisor = custom;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci		dev_dbg(&port->dev, "Baud %d = %d\n", baudrate, custom);
130062306a36Sopenharmony_ci		return 0;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	dev_dbg(&port->dev, "Baud calculation Failed...\n");
130462306a36Sopenharmony_ci	return -EINVAL;
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci/*
130862306a36Sopenharmony_ci * send_cmd_write_baud_rate
130962306a36Sopenharmony_ci *	this function sends the proper command to change the baud rate of the
131062306a36Sopenharmony_ci *	specified port.
131162306a36Sopenharmony_ci */
131262306a36Sopenharmony_cistatic int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,
131362306a36Sopenharmony_ci				    int baudrate)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	struct usb_serial_port *port;
131662306a36Sopenharmony_ci	struct usb_serial *serial;
131762306a36Sopenharmony_ci	int divisor;
131862306a36Sopenharmony_ci	int status;
131962306a36Sopenharmony_ci	unsigned char number;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (mos7720_port == NULL)
132262306a36Sopenharmony_ci		return -1;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	port = mos7720_port->port;
132562306a36Sopenharmony_ci	serial = port->serial;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	number = port->port_number;
132862306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudrate);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* Calculate the Divisor */
133162306a36Sopenharmony_ci	status = calc_baud_rate_divisor(port, baudrate, &divisor);
133262306a36Sopenharmony_ci	if (status) {
133362306a36Sopenharmony_ci		dev_err(&port->dev, "%s - bad baud rate\n", __func__);
133462306a36Sopenharmony_ci		return status;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	/* Enable access to divisor latch */
133862306a36Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR | UART_LCR_DLAB;
133962306a36Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	/* Write the divisor */
134262306a36Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_DLL, (__u8)(divisor & 0xff));
134362306a36Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_DLM,
134462306a36Sopenharmony_ci		      (__u8)((divisor & 0xff00) >> 8));
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/* Disable access to divisor latch */
134762306a36Sopenharmony_ci	mos7720_port->shadowLCR = mos7720_port->shadowLCR & ~UART_LCR_DLAB;
134862306a36Sopenharmony_ci	write_mos_reg(serial, number, MOS7720_LCR, mos7720_port->shadowLCR);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	return status;
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci/*
135462306a36Sopenharmony_ci * change_port_settings
135562306a36Sopenharmony_ci *	This routine is called to set the UART on the device to match
135662306a36Sopenharmony_ci *      the specified new settings.
135762306a36Sopenharmony_ci */
135862306a36Sopenharmony_cistatic void change_port_settings(struct tty_struct *tty,
135962306a36Sopenharmony_ci				 struct moschip_port *mos7720_port,
136062306a36Sopenharmony_ci				 const struct ktermios *old_termios)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct usb_serial_port *port;
136362306a36Sopenharmony_ci	struct usb_serial *serial;
136462306a36Sopenharmony_ci	int baud;
136562306a36Sopenharmony_ci	unsigned cflag;
136662306a36Sopenharmony_ci	__u8 lData;
136762306a36Sopenharmony_ci	__u8 lParity;
136862306a36Sopenharmony_ci	__u8 lStop;
136962306a36Sopenharmony_ci	int status;
137062306a36Sopenharmony_ci	int port_number;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	if (mos7720_port == NULL)
137362306a36Sopenharmony_ci		return ;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	port = mos7720_port->port;
137662306a36Sopenharmony_ci	serial = port->serial;
137762306a36Sopenharmony_ci	port_number = port->port_number;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (!mos7720_port->open) {
138062306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
138162306a36Sopenharmony_ci		return;
138262306a36Sopenharmony_ci	}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	lStop = 0x00;	/* 1 stop bit */
138562306a36Sopenharmony_ci	lParity = 0x00;	/* No parity */
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	cflag = tty->termios.c_cflag;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	lData = UART_LCR_WLEN(tty_get_char_size(cflag));
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	/* Change the Parity bit */
139262306a36Sopenharmony_ci	if (cflag & PARENB) {
139362306a36Sopenharmony_ci		if (cflag & PARODD) {
139462306a36Sopenharmony_ci			lParity = UART_LCR_PARITY;
139562306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
139662306a36Sopenharmony_ci		} else {
139762306a36Sopenharmony_ci			lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
139862306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s - parity = even\n", __func__);
139962306a36Sopenharmony_ci		}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	} else {
140262306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - parity = none\n", __func__);
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (cflag & CMSPAR)
140662306a36Sopenharmony_ci		lParity = lParity | 0x20;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	/* Change the Stop bit */
140962306a36Sopenharmony_ci	if (cflag & CSTOPB) {
141062306a36Sopenharmony_ci		lStop = UART_LCR_STOP;
141162306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
141262306a36Sopenharmony_ci	} else {
141362306a36Sopenharmony_ci		lStop = 0x00;
141462306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci#define LCR_BITS_MASK		0x03	/* Mask for bits/char field */
141862306a36Sopenharmony_ci#define LCR_STOP_MASK		0x04	/* Mask for stop bits field */
141962306a36Sopenharmony_ci#define LCR_PAR_MASK		0x38	/* Mask for parity field */
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	/* Update the LCR with the correct value */
142262306a36Sopenharmony_ci	mos7720_port->shadowLCR &=
142362306a36Sopenharmony_ci		~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
142462306a36Sopenharmony_ci	mos7720_port->shadowLCR |= (lData | lParity | lStop);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	/* Disable Interrupts */
142862306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x00);
142962306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0x00);
143062306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_FCR, 0xcf);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* Send the updated LCR value to the mos7720 */
143362306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_LCR,
143462306a36Sopenharmony_ci		      mos7720_port->shadowLCR);
143562306a36Sopenharmony_ci	mos7720_port->shadowMCR = 0x0b;
143662306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
143762306a36Sopenharmony_ci		      mos7720_port->shadowMCR);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	/* set up the MCR register and send it to the mos7720 */
144062306a36Sopenharmony_ci	mos7720_port->shadowMCR = UART_MCR_OUT2;
144162306a36Sopenharmony_ci	if (cflag & CBAUD)
144262306a36Sopenharmony_ci		mos7720_port->shadowMCR |= (UART_MCR_DTR | UART_MCR_RTS);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	if (cflag & CRTSCTS) {
144562306a36Sopenharmony_ci		mos7720_port->shadowMCR |= (UART_MCR_XONANY);
144662306a36Sopenharmony_ci		/* To set hardware flow control to the specified *
144762306a36Sopenharmony_ci		 * serial port, in SP1/2_CONTROL_REG             */
144862306a36Sopenharmony_ci		if (port_number)
144962306a36Sopenharmony_ci			write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
145062306a36Sopenharmony_ci				      0x01);
145162306a36Sopenharmony_ci		else
145262306a36Sopenharmony_ci			write_mos_reg(serial, dummy, MOS7720_SP_CONTROL_REG,
145362306a36Sopenharmony_ci				      0x02);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	} else
145662306a36Sopenharmony_ci		mos7720_port->shadowMCR &= ~(UART_MCR_XONANY);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_MCR,
145962306a36Sopenharmony_ci		      mos7720_port->shadowMCR);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	/* Determine divisor based on baud rate */
146262306a36Sopenharmony_ci	baud = tty_get_baud_rate(tty);
146362306a36Sopenharmony_ci	if (!baud) {
146462306a36Sopenharmony_ci		/* pick a default, any default... */
146562306a36Sopenharmony_ci		dev_dbg(&port->dev, "Picked default baud...\n");
146662306a36Sopenharmony_ci		baud = 9600;
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	if (baud >= 230400) {
147062306a36Sopenharmony_ci		set_higher_rates(mos7720_port, baud);
147162306a36Sopenharmony_ci		/* Enable Interrupts */
147262306a36Sopenharmony_ci		write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
147362306a36Sopenharmony_ci		return;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
147762306a36Sopenharmony_ci	status = send_cmd_write_baud_rate(mos7720_port, baud);
147862306a36Sopenharmony_ci	/* FIXME: needs to write actual resulting baud back not just
147962306a36Sopenharmony_ci	   blindly do so */
148062306a36Sopenharmony_ci	if (cflag & CBAUD)
148162306a36Sopenharmony_ci		tty_encode_baud_rate(tty, baud, baud);
148262306a36Sopenharmony_ci	/* Enable Interrupts */
148362306a36Sopenharmony_ci	write_mos_reg(serial, port_number, MOS7720_IER, 0x0c);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (port->read_urb->status != -EINPROGRESS) {
148662306a36Sopenharmony_ci		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
148762306a36Sopenharmony_ci		if (status)
148862306a36Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
148962306a36Sopenharmony_ci	}
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci/*
149362306a36Sopenharmony_ci * mos7720_set_termios
149462306a36Sopenharmony_ci *	this function is called by the tty driver when it wants to change the
149562306a36Sopenharmony_ci *	termios structure.
149662306a36Sopenharmony_ci */
149762306a36Sopenharmony_cistatic void mos7720_set_termios(struct tty_struct *tty,
149862306a36Sopenharmony_ci				struct usb_serial_port *port,
149962306a36Sopenharmony_ci				const struct ktermios *old_termios)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	int status;
150262306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	if (mos7720_port == NULL)
150762306a36Sopenharmony_ci		return;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	if (!mos7720_port->open) {
151062306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - port not opened\n", __func__);
151162306a36Sopenharmony_ci		return;
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	/* change the port settings to the new ones specified */
151562306a36Sopenharmony_ci	change_port_settings(tty, mos7720_port, old_termios);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	if (port->read_urb->status != -EINPROGRESS) {
151862306a36Sopenharmony_ci		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
151962306a36Sopenharmony_ci		if (status)
152062306a36Sopenharmony_ci			dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci/*
152562306a36Sopenharmony_ci * get_lsr_info - get line status register info
152662306a36Sopenharmony_ci *
152762306a36Sopenharmony_ci * Purpose: Let user call ioctl() to get info when the UART physically
152862306a36Sopenharmony_ci * 	    is emptied.  On bus types like RS485, the transmitter must
152962306a36Sopenharmony_ci * 	    release the bus after transmitting. This must be done when
153062306a36Sopenharmony_ci * 	    the transmit shift register is empty, not be done when the
153162306a36Sopenharmony_ci * 	    transmit holding register is empty.  This functionality
153262306a36Sopenharmony_ci * 	    allows an RS485 driver to be written in user space.
153362306a36Sopenharmony_ci */
153462306a36Sopenharmony_cistatic int get_lsr_info(struct tty_struct *tty,
153562306a36Sopenharmony_ci		struct moschip_port *mos7720_port, unsigned int __user *value)
153662306a36Sopenharmony_ci{
153762306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
153862306a36Sopenharmony_ci	unsigned int result = 0;
153962306a36Sopenharmony_ci	unsigned char data = 0;
154062306a36Sopenharmony_ci	int port_number = port->port_number;
154162306a36Sopenharmony_ci	int count;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	count = mos7720_chars_in_buffer(tty);
154462306a36Sopenharmony_ci	if (count == 0) {
154562306a36Sopenharmony_ci		read_mos_reg(port->serial, port_number, MOS7720_LSR, &data);
154662306a36Sopenharmony_ci		if ((data & (UART_LSR_TEMT | UART_LSR_THRE))
154762306a36Sopenharmony_ci					== (UART_LSR_TEMT | UART_LSR_THRE)) {
154862306a36Sopenharmony_ci			dev_dbg(&port->dev, "%s -- Empty\n", __func__);
154962306a36Sopenharmony_ci			result = TIOCSER_TEMT;
155062306a36Sopenharmony_ci		}
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci	if (copy_to_user(value, &result, sizeof(int)))
155362306a36Sopenharmony_ci		return -EFAULT;
155462306a36Sopenharmony_ci	return 0;
155562306a36Sopenharmony_ci}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_cistatic int mos7720_tiocmget(struct tty_struct *tty)
155862306a36Sopenharmony_ci{
155962306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
156062306a36Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
156162306a36Sopenharmony_ci	unsigned int result = 0;
156262306a36Sopenharmony_ci	unsigned int mcr ;
156362306a36Sopenharmony_ci	unsigned int msr ;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	mcr = mos7720_port->shadowMCR;
156662306a36Sopenharmony_ci	msr = mos7720_port->shadowMSR;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	result = ((mcr & UART_MCR_DTR)  ? TIOCM_DTR : 0)   /* 0x002 */
156962306a36Sopenharmony_ci	  | ((mcr & UART_MCR_RTS)   ? TIOCM_RTS : 0)   /* 0x004 */
157062306a36Sopenharmony_ci	  | ((msr & UART_MSR_CTS)   ? TIOCM_CTS : 0)   /* 0x020 */
157162306a36Sopenharmony_ci	  | ((msr & UART_MSR_DCD)   ? TIOCM_CAR : 0)   /* 0x040 */
157262306a36Sopenharmony_ci	  | ((msr & UART_MSR_RI)    ? TIOCM_RI :  0)   /* 0x080 */
157362306a36Sopenharmony_ci	  | ((msr & UART_MSR_DSR)   ? TIOCM_DSR : 0);  /* 0x100 */
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	return result;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic int mos7720_tiocmset(struct tty_struct *tty,
157962306a36Sopenharmony_ci			    unsigned int set, unsigned int clear)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
158262306a36Sopenharmony_ci	struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
158362306a36Sopenharmony_ci	unsigned int mcr ;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	mcr = mos7720_port->shadowMCR;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (set & TIOCM_RTS)
158862306a36Sopenharmony_ci		mcr |= UART_MCR_RTS;
158962306a36Sopenharmony_ci	if (set & TIOCM_DTR)
159062306a36Sopenharmony_ci		mcr |= UART_MCR_DTR;
159162306a36Sopenharmony_ci	if (set & TIOCM_LOOP)
159262306a36Sopenharmony_ci		mcr |= UART_MCR_LOOP;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	if (clear & TIOCM_RTS)
159562306a36Sopenharmony_ci		mcr &= ~UART_MCR_RTS;
159662306a36Sopenharmony_ci	if (clear & TIOCM_DTR)
159762306a36Sopenharmony_ci		mcr &= ~UART_MCR_DTR;
159862306a36Sopenharmony_ci	if (clear & TIOCM_LOOP)
159962306a36Sopenharmony_ci		mcr &= ~UART_MCR_LOOP;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	mos7720_port->shadowMCR = mcr;
160262306a36Sopenharmony_ci	write_mos_reg(port->serial, port->port_number, MOS7720_MCR,
160362306a36Sopenharmony_ci		      mos7720_port->shadowMCR);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	return 0;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int mos7720_ioctl(struct tty_struct *tty,
160962306a36Sopenharmony_ci			 unsigned int cmd, unsigned long arg)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
161262306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
161562306a36Sopenharmony_ci	if (mos7720_port == NULL)
161662306a36Sopenharmony_ci		return -ENODEV;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	switch (cmd) {
161962306a36Sopenharmony_ci	case TIOCSERGETLSR:
162062306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
162162306a36Sopenharmony_ci		return get_lsr_info(tty, mos7720_port,
162262306a36Sopenharmony_ci					(unsigned int __user *)arg);
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	return -ENOIOCTLCMD;
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic int mos7720_startup(struct usb_serial *serial)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	struct usb_device *dev;
163162306a36Sopenharmony_ci	char data;
163262306a36Sopenharmony_ci	u16 product;
163362306a36Sopenharmony_ci	int ret_val;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	product = le16_to_cpu(serial->dev->descriptor.idProduct);
163662306a36Sopenharmony_ci	dev = serial->dev;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	if (product == MOSCHIP_DEVICE_ID_7715) {
163962306a36Sopenharmony_ci		struct urb *urb = serial->port[0]->interrupt_in_urb;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		urb->complete = mos7715_interrupt_callback;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
164462306a36Sopenharmony_ci		ret_val = mos7715_parport_init(serial);
164562306a36Sopenharmony_ci		if (ret_val < 0)
164662306a36Sopenharmony_ci			return ret_val;
164762306a36Sopenharmony_ci#endif
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci	/* start the interrupt urb */
165062306a36Sopenharmony_ci	ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
165162306a36Sopenharmony_ci	if (ret_val) {
165262306a36Sopenharmony_ci		dev_err(&dev->dev, "failed to submit interrupt urb: %d\n",
165362306a36Sopenharmony_ci			ret_val);
165462306a36Sopenharmony_ci	}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* LSR For Port 1 */
165762306a36Sopenharmony_ci	read_mos_reg(serial, 0, MOS7720_LSR, &data);
165862306a36Sopenharmony_ci	dev_dbg(&dev->dev, "LSR:%x\n", data);
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	return 0;
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic void mos7720_release(struct usb_serial *serial)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	usb_kill_urb(serial->port[0]->interrupt_in_urb);
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
166862306a36Sopenharmony_ci	/* close the parallel port */
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	if (le16_to_cpu(serial->dev->descriptor.idProduct)
167162306a36Sopenharmony_ci	    == MOSCHIP_DEVICE_ID_7715) {
167262306a36Sopenharmony_ci		struct mos7715_parport *mos_parport =
167362306a36Sopenharmony_ci			usb_get_serial_data(serial);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci		/* prevent NULL ptr dereference in port callbacks */
167662306a36Sopenharmony_ci		spin_lock(&release_lock);
167762306a36Sopenharmony_ci		mos_parport->pp->private_data = NULL;
167862306a36Sopenharmony_ci		spin_unlock(&release_lock);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci		/* wait for synchronous usb calls to return */
168162306a36Sopenharmony_ci		if (mos_parport->msg_pending)
168262306a36Sopenharmony_ci			wait_for_completion_timeout(&mos_parport->syncmsg_compl,
168362306a36Sopenharmony_ci					    msecs_to_jiffies(MOS_WDR_TIMEOUT));
168462306a36Sopenharmony_ci		/*
168562306a36Sopenharmony_ci		 * If delayed work is currently scheduled, wait for it to
168662306a36Sopenharmony_ci		 * complete. This also implies barriers that ensure the
168762306a36Sopenharmony_ci		 * below serial clearing is not hoisted above the ->work.
168862306a36Sopenharmony_ci		 */
168962306a36Sopenharmony_ci		cancel_work_sync(&mos_parport->work);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci		parport_remove_port(mos_parport->pp);
169262306a36Sopenharmony_ci		usb_set_serial_data(serial, NULL);
169362306a36Sopenharmony_ci		mos_parport->serial = NULL;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci		parport_del_port(mos_parport->pp);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci		kref_put(&mos_parport->ref_count, destroy_mos_parport);
169862306a36Sopenharmony_ci	}
169962306a36Sopenharmony_ci#endif
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_cistatic int mos7720_port_probe(struct usb_serial_port *port)
170362306a36Sopenharmony_ci{
170462306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL);
170762306a36Sopenharmony_ci	if (!mos7720_port)
170862306a36Sopenharmony_ci		return -ENOMEM;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	mos7720_port->port = port;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	usb_set_serial_port_data(port, mos7720_port);
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	return 0;
171562306a36Sopenharmony_ci}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cistatic void mos7720_port_remove(struct usb_serial_port *port)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	struct moschip_port *mos7720_port;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	mos7720_port = usb_get_serial_port_data(port);
172262306a36Sopenharmony_ci	kfree(mos7720_port);
172362306a36Sopenharmony_ci}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_cistatic struct usb_serial_driver moschip7720_2port_driver = {
172662306a36Sopenharmony_ci	.driver = {
172762306a36Sopenharmony_ci		.owner =	THIS_MODULE,
172862306a36Sopenharmony_ci		.name =		"moschip7720",
172962306a36Sopenharmony_ci	},
173062306a36Sopenharmony_ci	.description		= "Moschip 2 port adapter",
173162306a36Sopenharmony_ci	.id_table		= id_table,
173262306a36Sopenharmony_ci	.num_bulk_in		= 2,
173362306a36Sopenharmony_ci	.num_bulk_out		= 2,
173462306a36Sopenharmony_ci	.num_interrupt_in	= 1,
173562306a36Sopenharmony_ci	.calc_num_ports		= mos77xx_calc_num_ports,
173662306a36Sopenharmony_ci	.open			= mos7720_open,
173762306a36Sopenharmony_ci	.close			= mos7720_close,
173862306a36Sopenharmony_ci	.throttle		= mos7720_throttle,
173962306a36Sopenharmony_ci	.unthrottle		= mos7720_unthrottle,
174062306a36Sopenharmony_ci	.attach			= mos7720_startup,
174162306a36Sopenharmony_ci	.release		= mos7720_release,
174262306a36Sopenharmony_ci	.port_probe		= mos7720_port_probe,
174362306a36Sopenharmony_ci	.port_remove		= mos7720_port_remove,
174462306a36Sopenharmony_ci	.ioctl			= mos7720_ioctl,
174562306a36Sopenharmony_ci	.tiocmget		= mos7720_tiocmget,
174662306a36Sopenharmony_ci	.tiocmset		= mos7720_tiocmset,
174762306a36Sopenharmony_ci	.set_termios		= mos7720_set_termios,
174862306a36Sopenharmony_ci	.write			= mos7720_write,
174962306a36Sopenharmony_ci	.write_room		= mos7720_write_room,
175062306a36Sopenharmony_ci	.chars_in_buffer	= mos7720_chars_in_buffer,
175162306a36Sopenharmony_ci	.break_ctl		= mos7720_break,
175262306a36Sopenharmony_ci	.read_bulk_callback	= mos7720_bulk_in_callback,
175362306a36Sopenharmony_ci	.read_int_callback	= mos7720_interrupt_callback,
175462306a36Sopenharmony_ci};
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
175762306a36Sopenharmony_ci	&moschip7720_2port_driver, NULL
175862306a36Sopenharmony_ci};
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
176362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
176462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1765